Compute Engine API Reference
Compute Engine
AngularUnit
type AngularUnit = "rad" | "deg" | "grad" | "turn";
When a unitless value is passed to or returned from a trigonometric function, the angular unit of the value.
| Angular Unit | Description |
|---|---|
rad | radians, 2π radians is a full circle |
deg | degrees, 360 degrees is a full circle |
grad | gradians, 400 gradians is a full circle |
turn | turns, 1 turn is a full circle |
To change the angular unit used by the Compute Engine, use:
ce.angularUnit = 'deg';
AssignValue
type AssignValue = KernelAssignValue<Expression, ExpressionInput, ComputeEngine>;
Assignable value for ce.assign().
ExpressionComputeEngine
Compute engine surface used by expression types.
This interface is augmented by types-engine.ts with the concrete
IComputeEngine members to avoid type-layer circular dependencies.
ExpressionComputeEngine.latexDictionary
latexDictionary: readonly OnlyFirst<
| DefaultEntry
| ExpressionEntry
| MatchfixEntry
| InfixEntry
| PostfixEntry
| PrefixEntry
| EnvironmentEntry
| SymbolEntry
| FunctionEntry, {} &
| DefaultEntry
| ExpressionEntry
| MatchfixEntry
| InfixEntry
| PostfixEntry
| PrefixEntry
| EnvironmentEntry
| SymbolEntry
| FunctionEntry>[];
ExpressionComputeEngine.decimalSeparator
decimalSeparator: string;
ExpressionComputeEngine.True
readonly True: Expression;
ExpressionComputeEngine.False
readonly False: Expression;
ExpressionComputeEngine.Pi
readonly Pi: Expression;
ExpressionComputeEngine.E
readonly E: Expression;
ExpressionComputeEngine.Nothing
readonly Nothing: Expression;
ExpressionComputeEngine.Zero
readonly Zero: Expression;
ExpressionComputeEngine.One
readonly One: Expression;
ExpressionComputeEngine.Half
readonly Half: Expression;
ExpressionComputeEngine.NegativeOne
readonly NegativeOne: Expression;
ExpressionComputeEngine.NaN
readonly NaN: Expression;
ExpressionComputeEngine.PositiveInfinity
readonly PositiveInfinity: Expression;
ExpressionComputeEngine.NegativeInfinity
readonly NegativeInfinity: Expression;
ExpressionComputeEngine.ComplexInfinity
readonly ComplexInfinity: Expression;
ExpressionComputeEngine.context
readonly context: EvalContext;
ExpressionComputeEngine.contextStack
contextStack: readonly EvalContext[];
ExpressionComputeEngine.timeLimit
timeLimit: number;
ExpressionComputeEngine.iterationLimit
iterationLimit: number;
ExpressionComputeEngine.recursionLimit
recursionLimit: number;
ExpressionComputeEngine.bignum()
bignum: (a) => Decimal;
ExpressionComputeEngine.complex()
complex: (a, b?) => Complex;
ExpressionComputeEngine.tolerance
tolerance: number;
ExpressionComputeEngine.angularUnit
angularUnit: AngularUnit;
ExpressionComputeEngine.costFunction()
costFunction: (expr) => number;
ExpressionComputeEngine.simplificationRules
simplificationRules: Rule[];
The rules used by .simplify() when no explicit rules option is passed.
Initialized to the built-in simplification rules.
Users can push() additional rules or replace the entire array.
ExpressionComputeEngine.strict
strict: boolean;
ExpressionComputeEngine.trace
trace: readonly string[];
A list of the function calls to the current evaluation context
ExpressionComputeEngine.precision
get precision(): number
set precision(p: number | "auto" | "machine"): void
ExpressionComputeEngine.chop()
chop(n)
chop(n): number
####### n
number
chop(n)
chop(n): 0 | Decimal
####### n
Decimal
chop(n)
chop(n): number | Decimal
####### n
number | Decimal
ExpressionComputeEngine.box()
box(expr, options?): Expression
####### expr
NumericValue | ExpressionInput
####### options?
####### form?
####### scope?
Scope
ExpressionComputeEngine.function()
function(name, ops, options?): Expression
####### name
string
####### ops
readonly ExpressionInput[]
####### options?
####### metadata?
####### form?
####### scope?
Scope
ExpressionComputeEngine.registerCompilationTarget()
registerCompilationTarget(name, target): void
Register a custom compilation target.
####### name
string
####### target
LanguageTarget<Expression>
ExpressionComputeEngine.getCompilationTarget()
getCompilationTarget(name): LanguageTarget<Expression>
Get a registered compilation target by name.
####### name
string
ExpressionComputeEngine.listCompilationTargets()
listCompilationTargets(): string[]
Return the names of all registered compilation targets.
ExpressionComputeEngine.unregisterCompilationTarget()
unregisterCompilationTarget(name): void
Remove a registered compilation target.
####### name
string
ExpressionComputeEngine.number()
number(value, options?): Expression
####### value
any
####### options?
####### metadata?
####### canonical?
ExpressionComputeEngine.symbol()
symbol(sym, options?): Expression
####### sym
string
####### options?
####### canonical?
####### metadata?
ExpressionComputeEngine.error()
error(message, where?): Expression
####### message
string | string[]
####### where?
string
ExpressionComputeEngine.typeError()
typeError(expectedType, actualType, where?): Expression
####### expectedType
####### actualType
####### where?
ExpressionComputeEngine.tuple()
tuple(elements)
tuple(...elements): Expression
####### elements
...readonly number[]
tuple(elements)
tuple(...elements): Expression
####### elements
...readonly Expression[]
ExpressionComputeEngine.type()
type(type): BoxedType
####### type
string | AlgebraicType | NegationType | CollectionType | ListType | SetType | RecordType | DictionaryType | TupleType | SymbolType | ExpressionType | NumericType | FunctionSignature | ValueType | TypeReference | BoxedType
ExpressionComputeEngine.rules()
rules(rules, options?): BoxedRuleSet
####### rules
Rule | BoxedRuleSet | readonly Rule | BoxedRule[]
####### options?
####### canonical?
boolean
ExpressionComputeEngine.getRuleSet()
getRuleSet(id?): BoxedRuleSet
####### id?
"harmonization" | "solve-univariate" | "standard-simplification"
ExpressionComputeEngine.parse()
parse(latex, options)
parse(latex, options?): null
####### latex
null
####### options?
Partial<ParseLatexOptions> & {
form: FormOption;
}
parse(latex, options)
parse(latex, options?): Expression
####### latex
string
####### options?
Partial<ParseLatexOptions> & {
form: FormOption;
}
parse(latex, options)
parse(latex, options?): Expression
####### latex
string
####### options?
Partial<ParseLatexOptions> & {
form: FormOption;
}
ExpressionComputeEngine.pushScope()
pushScope(scope?, name?): void
####### scope?
Scope
####### name?
string
ExpressionComputeEngine.popScope()
popScope(): void
ExpressionComputeEngine.assign()
assign(ids)
assign(ids): IComputeEngine
####### ids
assign(id, value)
assign(id, value): IComputeEngine
####### id
string
####### value
AssignValue
assign(arg1, arg2)
assign(arg1, arg2?): IComputeEngine
####### arg1
string | {}
####### arg2?
AssignValue
ExpressionComputeEngine.declareType()
declareType(name, type, options?): void
####### name
string
####### type
####### options?
####### alias?
boolean
ExpressionComputeEngine.declare()
declare(symbols)
declare(symbols): IComputeEngine
####### symbols
declare(id, def, scope)
declare(id, def, scope?): IComputeEngine
####### id
string
####### def
string | AlgebraicType | NegationType | CollectionType | ListType | SetType | RecordType | DictionaryType | TupleType | SymbolType | ExpressionType | NumericType | FunctionSignature | ValueType | TypeReference | Partial<OnlyFirst<ValueDefinition, BaseDefinition & {
holdUntil: "never" | "evaluate" | "N";
type: | string
| AlgebraicType
| NegationType
| CollectionType
| ListType
| SetType
| RecordType
| DictionaryType
| TupleType
| SymbolType
| ExpressionType
| NumericType
| FunctionSignature
| ValueType
| TypeReference
| BoxedType;
inferred: boolean;
value: | ExpressionInput
| (ce) => Expression;
eq: (a) => boolean;
neq: (a) => boolean;
cmp: (a) => ">" | "<" | "=";
collection: CollectionHandlers;
subscriptEvaluate: (subscript, options) => Expression;
} & Partial<BaseDefinition> & Partial<OperatorDefinitionFlags> & {
signature: | string
| AlgebraicType
| NegationType
| CollectionType
| ListType
| SetType
| RecordType
| DictionaryType
| TupleType
| SymbolType
| ExpressionType
| NumericType
| FunctionSignature
| ValueType
| TypeReference
| BoxedType;
type: (ops, options) =>
| string
| AlgebraicType
| NegationType
| CollectionType
| ListType
| SetType
| RecordType
| DictionaryType
| TupleType
| SymbolType
| ExpressionType
| NumericType
| FunctionSignature
| ValueType
| TypeReference
| BoxedType;
sgn: (ops, options) => Sign;
isPositive: boolean;
isNonNegative: boolean;
isNegative: boolean;
isNonPositive: boolean;
even: (ops, options) => boolean;
complexity: number;
canonical: (ops, options) => Expression;
evaluate: | Expression
| (ops, options) => Expression;
evaluateAsync: (ops, options) => Promise<Expression>;
evalDimension: (args, options) => Expression;
xcompile: (expr) => CompiledExpression;
eq: (a, b) => boolean;
neq: (a, b) => boolean;
collection: CollectionHandlers;
}>> | Partial<OnlyFirst<OperatorDefinition, BaseDefinition & {
holdUntil: "never" | "evaluate" | "N";
type: | string
| AlgebraicType
| NegationType
| CollectionType
| ListType
| SetType
| RecordType
| DictionaryType
| TupleType
| SymbolType
| ExpressionType
| NumericType
| FunctionSignature
| ValueType
| TypeReference
| BoxedType;
inferred: boolean;
value: | ExpressionInput
| (ce) => Expression;
eq: (a) => boolean;
neq: (a) => boolean;
cmp: (a) => ">" | "<" | "=";
collection: CollectionHandlers;
subscriptEvaluate: (subscript, options) => Expression;
} & Partial<BaseDefinition> & Partial<OperatorDefinitionFlags> & {
signature: | string
| AlgebraicType
| NegationType
| CollectionType
| ListType
| SetType
| RecordType
| DictionaryType
| TupleType
| SymbolType
| ExpressionType
| NumericType
| FunctionSignature
| ValueType
| TypeReference
| BoxedType;
type: (ops, options) =>
| string
| AlgebraicType
| NegationType
| CollectionType
| ListType
| SetType
| RecordType
| DictionaryType
| TupleType
| SymbolType
| ExpressionType
| NumericType
| FunctionSignature
| ValueType
| TypeReference
| BoxedType;
sgn: (ops, options) => Sign;
isPositive: boolean;
isNonNegative: boolean;
isNegative: boolean;
isNonPositive: boolean;
even: (ops, options) => boolean;
complexity: number;
canonical: (ops, options) => Expression;
evaluate: | Expression
| (ops, options) => Expression;
evaluateAsync: (ops, options) => Promise<Expression>;
evalDimension: (args, options) => Expression;
xcompile: (expr) => CompiledExpression;
eq: (a, b) => boolean;
neq: (a, b) => boolean;
collection: CollectionHandlers;
}>>
####### scope?
Scope
declare(arg1, arg2, arg3)
declare(arg1, arg2?, arg3?): IComputeEngine
####### arg1
string | {}
####### arg2?
string | AlgebraicType | NegationType | CollectionType | ListType | SetType | RecordType | DictionaryType | TupleType | SymbolType | ExpressionType | NumericType | FunctionSignature | ValueType | TypeReference | Partial<OnlyFirst<ValueDefinition, BaseDefinition & {
holdUntil: "never" | "evaluate" | "N";
type: | string
| AlgebraicType
| NegationType
| CollectionType
| ListType
| SetType
| RecordType
| DictionaryType
| TupleType
| SymbolType
| ExpressionType
| NumericType
| FunctionSignature
| ValueType
| TypeReference
| BoxedType;
inferred: boolean;
value: | ExpressionInput
| (ce) => Expression;
eq: (a) => boolean;
neq: (a) => boolean;
cmp: (a) => ">" | "<" | "=";
collection: CollectionHandlers;
subscriptEvaluate: (subscript, options) => Expression;
} & Partial<BaseDefinition> & Partial<OperatorDefinitionFlags> & {
signature: | string
| AlgebraicType
| NegationType
| CollectionType
| ListType
| SetType
| RecordType
| DictionaryType
| TupleType
| SymbolType
| ExpressionType
| NumericType
| FunctionSignature
| ValueType
| TypeReference
| BoxedType;
type: (ops, options) =>
| string
| AlgebraicType
| NegationType
| CollectionType
| ListType
| SetType
| RecordType
| DictionaryType
| TupleType
| SymbolType
| ExpressionType
| NumericType
| FunctionSignature
| ValueType
| TypeReference
| BoxedType;
sgn: (ops, options) => Sign;
isPositive: boolean;
isNonNegative: boolean;
isNegative: boolean;
isNonPositive: boolean;
even: (ops, options) => boolean;
complexity: number;
canonical: (ops, options) => Expression;
evaluate: | Expression
| (ops, options) => Expression;
evaluateAsync: (ops, options) => Promise<Expression>;
evalDimension: (args, options) => Expression;
xcompile: (expr) => CompiledExpression;
eq: (a, b) => boolean;
neq: (a, b) => boolean;
collection: CollectionHandlers;
}>> | Partial<OnlyFirst<OperatorDefinition, BaseDefinition & {
holdUntil: "never" | "evaluate" | "N";
type: | string
| AlgebraicType
| NegationType
| CollectionType
| ListType
| SetType
| RecordType
| DictionaryType
| TupleType
| SymbolType
| ExpressionType
| NumericType
| FunctionSignature
| ValueType
| TypeReference
| BoxedType;
inferred: boolean;
value: | ExpressionInput
| (ce) => Expression;
eq: (a) => boolean;
neq: (a) => boolean;
cmp: (a) => ">" | "<" | "=";
collection: CollectionHandlers;
subscriptEvaluate: (subscript, options) => Expression;
} & Partial<BaseDefinition> & Partial<OperatorDefinitionFlags> & {
signature: | string
| AlgebraicType
| NegationType
| CollectionType
| ListType
| SetType
| RecordType
| DictionaryType
| TupleType
| SymbolType
| ExpressionType
| NumericType
| FunctionSignature
| ValueType
| TypeReference
| BoxedType;
type: (ops, options) =>
| string
| AlgebraicType
| NegationType
| CollectionType
| ListType
| SetType
| RecordType
| DictionaryType
| TupleType
| SymbolType
| ExpressionType
| NumericType
| FunctionSignature
| ValueType
| TypeReference
| BoxedType;
sgn: (ops, options) => Sign;
isPositive: boolean;
isNonNegative: boolean;
isNegative: boolean;
isNonPositive: boolean;
even: (ops, options) => boolean;
complexity: number;
canonical: (ops, options) => Expression;
evaluate: | Expression
| (ops, options) => Expression;
evaluateAsync: (ops, options) => Promise<Expression>;
evalDimension: (args, options) => Expression;
xcompile: (expr) => CompiledExpression;
eq: (a, b) => boolean;
neq: (a, b) => boolean;
collection: CollectionHandlers;
}>>
####### arg3?
Scope
ExpressionComputeEngine.declareSequence()
declareSequence(name, def): IComputeEngine
Declare a sequence with a recurrence relation.
####### name
string
####### def
Example
// Fibonacci sequence
ce.declareSequence('F', {
base: { 0: 0, 1: 1 },
recurrence: 'F_{n-1} + F_{n-2}',
});
ce.parse('F_{10}').evaluate(); // → 55
ExpressionComputeEngine.getSequenceStatus()
getSequenceStatus(name): SequenceStatus
Get the status of a sequence definition.
####### name
string
Example
ce.parse('F_0 := 0').evaluate();
ce.getSequenceStatus('F');
// → { status: 'pending', hasBase: true, hasRecurrence: false, baseIndices: [0] }
ExpressionComputeEngine.getSequence()
getSequence(name): SequenceInfo
Get information about a defined sequence.
Returns undefined if the symbol is not a sequence.
####### name
string
ExpressionComputeEngine.listSequences()
listSequences(): string[]
List all defined sequences. Returns an array of sequence names.
ExpressionComputeEngine.isSequence()
isSequence(name): boolean
Check if a symbol is a defined sequence.
####### name
string
ExpressionComputeEngine.clearSequenceCache()
clearSequenceCache(name?): void
Clear the memoization cache for a sequence. If no name is provided, clears caches for all sequences.
####### name?
string
ExpressionComputeEngine.getSequenceCache()
getSequenceCache(name): Map<string | number, Expression>
Get the memoization cache for a sequence.
Returns a Map of index → value, or undefined if not a sequence or memoization is disabled.
For single-index sequences, keys are numbers. For multi-index sequences, keys are comma-separated strings (e.g., '5,2').
####### name
string
ExpressionComputeEngine.getSequenceTerms()
getSequenceTerms(
name,
start,
end,
step?): Expression[]
Generate a list of sequence terms from start to end (inclusive).
####### name
string
The sequence name
####### start
number
Starting index (inclusive)
####### end
number
Ending index (inclusive)
####### step?
number
Step size (default: 1)
Example
ce.declareSequence('F', { base: { 0: 0, 1: 1 }, recurrence: 'F_{n-1} + F_{n-2}' });
ce.getSequenceTerms('F', 0, 10);
// → [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
ExpressionComputeEngine.lookupOEIS()
lookupOEIS(terms, options?): Promise<OEISSequenceInfo[]>
Look up sequences in OEIS by their terms.
####### terms
(number | Expression)[]
Array of sequence terms to search for
####### options?
Optional configuration (timeout, maxResults)
Example
const results = await ce.lookupOEIS([0, 1, 1, 2, 3, 5, 8, 13]);
// → [{ id: 'A000045', name: 'Fibonacci numbers', ... }]
ExpressionComputeEngine.checkSequenceOEIS()
checkSequenceOEIS(name, count?, options?): Promise<{
matches: OEISSequenceInfo[];
terms: number[];
}>
Check if a defined sequence matches an OEIS sequence.
####### name
string
Name of the defined sequence
####### count?
number
Number of terms to check (default: 10)
####### options?
Optional configuration
Example
ce.declareSequence('F', { base: { 0: 0, 1: 1 }, recurrence: 'F_{n-1} + F_{n-2}' });
const result = await ce.checkSequenceOEIS('F', 10);
// → { matches: [{ id: 'A000045', name: 'Fibonacci numbers', ... }], terms: [0, 1, 1, ...] }
Boxed Expression
SimplifyOptions
type SimplifyOptions = {
rules: null | Rule | ReadonlyArray<BoxedRule | Rule> | BoxedRuleSet;
costFunction: (expr) => number;
strategy: "default" | "fu";
};
Options for Expression.simplify()
EvaluateOptions
type EvaluateOptions = KernelEvaluateOptions;
Options for evaluating boxed expressions.
This is the compute-engine-specialized form of the generic kernel type.
Expression
The Expression interface includes the methods and properties
applicable to all kinds of expression. For example it includes expr.symbol
which only applies to symbols or expr.ops which only applies to
function expressions.
When a property is not applicable to this Expression its value is
undefined. For example expr.symbol for a BoxedNumber is undefined.
This convention makes it convenient to manipulate expressions without having to check what kind of instance they are before manipulating them.
A boxed expression can represent a canonical or a non-canonical
expression. A non-canonical expression is a "raw" form of the
expression. For example, the non-canonical representation of \frac{10}{20}
is ["Divide", 10, 20]. The canonical representation of the same
expression is the boxed number 1/2.
The canonical representation of symbols and function expressions are bound to a definition. The definition contains metadata about the symbol or function operator, such as its type, its signature, and other attributes. The value of symbols are tracked in a separate table for each evaluation context.
The binding only occurs when the expression is constructed, if it is created as a canonical expression. If the expression is constructed as a non-canonical expression, no binding is done.
The value of an expression is a number, a string, a boolean or a tensor.
The value of number literals and strings are themselves.
A symbol can have a value associated with it, in which case the value of the symbol is the value associated with it.
Some symbols (unknowns) are purely symbolic and have no value associated with them.
Function expressions do not have a value associated with them.
For example, ["Add", 2, 3] has no value associated with it, it is a
symbolic expression.
Some properties of a Boxed Expression are only applicable if the expression
has a value associated with it. For example, expr.isNumber is only
applicable if the value of the expression is a number, that is if the
expression is a number literal or a symbol with a numeric value.
The following properties are applicable to expressions with a value:
expr.isNumber
To create a boxed expression:
ce.box() and ce.parse()
Use ce.box() or ce.parse().
Use ce.parse() to get a boxed expression from a LaTeX string.
Use ce.box() to get a boxed expression from a MathJSON expression.
By default, the result of these methods is a canonical expression. For example, if it is a rational literal, it is reduced to its canonical form. If it is a function expression:
- the arguments are put in canonical form
- the arguments of commutative functions are sorted
- invisible operators are made explicit
- a limited number of core simplifications are applied, for example rationals are reduced
- sequences are flattened:
["Add", 1, ["Sequence", 2, 3]]is transformed to["Add", 1, 2, 3] - associative functions are flattened:
["Add", 1, ["Add", 2, 3]]is transformed to["Add", 1, 2, 3] - symbols are not replaced with their values (unless they have
a
holdUntilflag set tonever).
ce.function()
This is a specialized version of ce.box() for creating a new function
expression.
The canonical handler of the operator is called.
Algebraic methods (expr.add(), expr.mul(), etc...)
The boxed expression have some algebraic methods, i.e. add(), mul(),
div(), pow(), etc. These methods are suitable for
internal calculations, although they may be used as part of the public
API as well.
- a runtime error is thrown if the expression is not canonical
- the arguments are not evaluated
- the canonical handler (of the corresponding operation) is not called
- some additional simplifications over canonicalization are applied.
For example number literals are combined.
However, the result is exact, and no approximation is made. Use
.N()to get an approximate value. This is equivalent to callingsimplify()on the expression (but without simplifying the arguments). - sequences were already flattened as part of the canonicalization process
For 'add()' and 'mul()', which take multiple arguments, separate functions
are provided that take an array of arguments. They are equivalent
to calling the boxed algebraic method, i.e. ce.Zero.add(1, 2, 3) and
add(1, 2, 3) are equivalent.
These methods are not equivalent to calling expr.evaluate() on the
expression: evaluate will replace symbols with their values, and
evaluate the expression.
For algebraic functions (add(), mul(), etc..), use the corresponding
canonicalization function, i.e. canonicalAdd(a, b) instead of
ce.function('Add', [a, b]).
Another option is to use the algebraic methods directly, i.e. a.add(b)
instead of ce.function('Add', [a, b]). However, the algebraic methods will
apply further simplifications which may or may not be desirable. For
example, number literals will be combined.
ce._fn()
This method is a low level method to create a new function expression which is typically invoked in the canonical handler of an operator definition.
The arguments are not modified. The expression is not put in canonical form. The canonical handler is not called.
A canonical flag can be set when calling this method, but it only asserts that the function expression is canonical. The caller is responsible for ensuring that is the case.
Canonical Handlers
Canonical handlers are responsible for:
- validating the signature: this can involve checking the number of arguments. It is recommended to avoid checking the type of non-literal arguments, since the type of symbols or function expressions may change. Similarly, the canonicalization process should not rely on the value of or assumptions about non-literal arguments.
- flattening sequences
- flattening arguments if the function is associative
- sort the arguments (if the function is commutative)
- calling
ce._fn()to create a new function expression
When the canonical handler is invoked, the arguments have been put in
canonical form unless the lazy flag is set to true.
Note that the result of a canonical handler should be a canonical expression,
but not all arguments need to be canonical. For example, the arguments of
["Declare", "x", 2] are not canonical, since x refers to the name
of the symbol, not its value.
Function Expression
Expression.operator
readonly operator: string;
The name of the operator of the expression.
For example, the name of the operator of ["Add", 2, 3] is "Add".
A string literal has a "String" operator.
A symbol has a "Symbol" operator.
A number has a "Number", "Real", "Rational" or "Integer" operator; amongst some others.
Practically speaking, for fully canonical and valid expressions, all of these are likely to
collapse to "Number".
Numeric Expression
Expression.isEven
readonly isEven: boolean;
If the value of this expression is not an integer return undefined.
Expression.isOdd
readonly isOdd: boolean;
If the value of this expression is not an integer return undefined.
Expression.re
readonly re: number;
Return the real part of the value of this expression, if a number.
Otherwise, return NaN (not a number).
Expression.im
readonly im: number;
If value of this expression is a number, return the imaginary part of the value. If the value is a real number, the imaginary part is 0.
Otherwise, return NaN (not a number).
Expression.bignumRe
readonly bignumRe: Decimal;
If the value of this expression is a number, return the real part of the
value as a BigNum.
If the value is not available as a bignum return undefined. That is,
the value is not upconverted to a bignum.
To get the real value either as a bignum or a number, use
expr.bignumRe ?? expr.re.
When using this pattern, the value is returned as a bignum if available,
otherwise as a number or NaN if the value is not a number.
Expression.bignumIm
readonly bignumIm: Decimal;
If the value of this expression is a number, return the imaginary part as
a BigNum.
It may be 0 if the number is real.
If the value of the expression is not a number or the value is not
available as a bignum return undefined. That is, the value is not
upconverted to a bignum.
To get the imaginary value either as a bignum or a number, use
expr.bignumIm ?? expr.im.
When using this pattern, the value is returned as a bignum if available, otherwise as a number or NaN if the value is not a number.
Expression.sgn
readonly sgn: Sign;
Return the sign of the expression.
Note that complex numbers have no natural ordering, so if the value is an
imaginary number (a complex number with a non-zero imaginary part),
this.sgn will return unsigned.
If a symbol, this does take assumptions into account, that is this.sgn
will return positive if the symbol is assumed to be positive
using ce.assume().
Non-canonical expressions return undefined.
Expression.isPositive
readonly isPositive: boolean;
The value of this expression is > 0, same as isGreaterEqual(0)
Expression.isNonNegative
readonly isNonNegative: boolean;
The value of this expression is >= 0, same as isGreaterEqual(0)
Expression.isNegative
readonly isNegative: boolean;
The value of this expression is < 0, same as isLess(0)
Expression.isNonPositive
readonly isNonPositive: boolean;
The value of this expression is <= 0, same as isLessEqual(0)
Expression.isNaN
readonly isNaN: boolean;
If true, the value of this expression is "Not a Number".
A value representing undefined result of computations, such as 0/0,
as per the floating point format standard IEEE-754.
Note that if isNaN is true, isNumber is also true (yes, NaN is a
number).
Expression.isInfinity
readonly isInfinity: boolean;
The numeric value of this expression is ±Infinity or ComplexInfinity.
Expression.isFinite
readonly isFinite: boolean;
This expression is a number, but not ±Infinity, ComplexInfinity or
NaN
Other
Expression.engine
readonly engine: ExpressionComputeEngine;
The Compute Engine instance associated with this expression provides a context in which to interpret it, such as definition of symbols and functions.
Expression.toLatex()
toLatex(options?): string
Serialize to a LaTeX string.
Note that lazy collections are eagerly evaluated.
Will ignore any LaTeX metadata.
####### options?
Partial<SerializeLatexOptions>
Expression.latex
LaTeX representation of this expression.
If the expression was parsed from LaTeX, the LaTeX representation is the same as the input LaTeX.
To customize the serialization, use expr.toLatex().
Note that lazy collections are eagerly evaluated.
Applicable to canonical and non-canonical expressions.
Expression.toMathJson()
toMathJson(options?): MathJsonExpression
Serialize to a MathJSON expression with specified options
####### options?
Readonly<Partial<JsonSerializationOptions>>
Expression.json
readonly json: MathJsonExpression;
MathJSON representation of this expression.
This representation always use shorthands when possible. Metadata is not included.
Numbers are converted to JavaScript numbers and may lose precision.
The expression is represented exactly and no sugaring is applied. For
example, ["Power", "x", 2] is not represented as ["Square", "x"].
For more control over the serialization, use expr.toMathJson().
Note that lazy collections are not eagerly evaluated.
Applicable to canonical and non-canonical expressions.
Expression.print()
print(): void
Output to the console a string representation of the expression.
Note that lazy collections are eagerly evaluated when printed.
Expression.verbatimLatex?
optional verbatimLatex: string;
If the expression was constructed from a LaTeX string, the verbatim LaTeX string it was parsed from.
Expression.isCanonical
If true, this expression is in a canonical form.
Expression.isStructural
If true, this expression is in a structural form.
The structural form of an expression is used when applying rules to
an expression. For example, a rational number is represented as a
function expression instead of a Expression object.
Expression.canonical
Return the canonical form of this expression.
If a function expression or symbol, they are first bound with a definition in the current scope.
When determining the canonical form the following operator definition flags are applied:
associative: $ f(a, f(b), c) \longrightarrow f(a, b, c) $idempotent: $ f(f(a)) \longrightarrow f(a) $involution: $ f(f(a)) \longrightarrow a $commutative: sort the arguments.
If this expression is already canonical, the value of canonical is
this.
The arguments of a canonical function expression may not all be
canonical, for example in the ["Declare", "i", 2] expression,
i is not canonical since it is used only as the name of a symbol, not
as a (potentially) existing symbol.
Partially canonical expressions, such as those produced through
CanonicalForm, also yield an expression which is marked as canonical.
This means that, likewise for partially canonical expressions, the
canonical property will return the self-same expression (and
'isCanonical' will also be true).
Expression.structural
Return the structural form of this expression.
Some expressions, such as rational numbers, are represented with
a Expression object. In some cases, for example when doing a
structural comparison of two expressions, it is useful to have a
structural representation of the expression where the rational numbers
is represented by a function expression instead.
If there is a structural representation of the expression, return it,
otherwise return this.
Expression.isValid
readonly isValid: boolean;
false if this expression or any of its subexpressions is an ["Error"]
expression.
Applicable to canonical and non-canonical expressions. For non-canonical expression, this may indicate a syntax error while parsing LaTeX. For canonical expression, this may indicate argument type mismatch, or missing or unexpected arguments.
Expression.isPure
readonly isPure: boolean;
If true, evaluating this expression has no side-effects (does not change the state of the Compute Engine).
If false, evaluating this expression may change the state of the Compute Engine or it may return a different value each time it is evaluated, even if the state of the Compute Engine is the same.
As an example, the ["Add", 2, 3] function expression is pure, but
the ["Random"] function expression is not pure.
For a function expression to be pure, the function itself (its operator) must be pure, and all of its arguments must be pure too.
A pure function expression may return a different value each time it is
evaluated if its arguments are not constant. For example, the
["Add", "x", 1] function expression is pure, but it is not
constant, because x is not constant.
Applicable to canonical expressions only
Expression.isConstant
readonly isConstant: boolean;
True if evaluating this expression always returns the same value.
If true and a function expression, implies that it is pure and that all of its arguments are constant.
Number literals, symbols with constant values, and pure numeric functions with constant arguments are all constant, i.e.:
42is constantPiis constant["Divide", "Pi", 2]is constantxis not constant, unless declared with a constant flag.["Add", "x", 2]is either constant only ifxis constant.
Expression.errors
readonly errors: readonly Expression[];
All the ["Error"] subexpressions.
If an expression includes an error, the expression is also an error.
In that case, the this.isValid property is false.
Applicable to canonical and non-canonical expressions.
Expression.getSubexpressions()
getSubexpressions(operator): readonly Expression[]
All the subexpressions matching the named operator, recursively.
Example:
const expr = ce.parse('a + b * c + d');
const subexpressions = expr.getSubexpressions('Add');
// -> `[['Add', 'a', 'b'], ['Add', 'c', 'd']]`
Applicable to canonical and non-canonical expressions.
####### operator
string
Expression.subexpressions
readonly subexpressions: readonly Expression[];
All the subexpressions in this expression, recursively
Example:
const expr = ce.parse('a + b * c + d');
const subexpressions = expr.subexpressions;
// -> `[['Add', 'a', 'b'], ['Add', 'c', 'd'], 'a', 'b', 'c', 'd']`
Applicable to canonical and non-canonical expressions.
Expression.symbols
readonly symbols: readonly string[];
All the symbols in the expression, recursively, including bound variables (e.g., summation/product index variables).
Use unknowns or freeVariables to get only the symbols that are free (not bound by a scoping construct).
const expr = ce.parse('a + b * c + d');
const symbols = expr.symbols;
// -> ['a', 'b', 'c', 'd']
Applicable to canonical and non-canonical expressions.
Expression.unknowns
readonly unknowns: readonly string[];
All the symbols used in the expression that do not have a value associated with them, i.e. they are declared but not defined.
Expression.freeVariables
readonly freeVariables: readonly string[];
The free variables of the expression: symbols that are not constants, not operators, not bound to a value, and not locally scoped (e.g., summation/product index variables are excluded).
This is an alias for unknowns.
Expression.toNumericValue()
toNumericValue(): [NumericValue, Expression]
Attempt to factor a numeric coefficient c and a rest out of a
canonical expression such that rest.mul(c) is equal to this.
Attempts to make rest a positive value (i.e. pulls out negative sign).
['Multiply', 2, 'x', 3, 'a']
-> [NumericValue(6), ['Multiply', 'x', 'a']]
['Divide', ['Multiply', 2, 'x'], ['Multiply', 3, 'y', 'a']]
-> [NumericValue({rational: [2, 3]}), ['Divide', 'x', ['Multiply, 'y', 'a']]]
Expression.numerator
Return this expression expressed as a numerator.
Expression.denominator
Return this expression expressed as a denominator.
Expression.numeratorDenominator
Return this expression expressed as a numerator and denominator.
Expression.toRational()
toRational(): [number, number]
Return the value of this expression as a pair of integer numerator and
denominator, or null if the expression is not a rational number.
- For a
BoxedNumberwith an exact rational value, extracts from the numeric representation. - For an integer, returns
[n, 1]. - For a
DivideorRationalfunction with integer operands, returns[num, den]. - For everything else, returns
null.
The returned rational is always in lowest terms.
ce.parse('\\frac{6}{4}').toRational() // [3, 2]
ce.parse('7').toRational() // [7, 1]
ce.parse('x + 1').toRational() // null
ce.number(1.5).toRational() // null (machine float)
Expression.factors()
factors(): readonly Expression[]
Return the multiplicative factors of this expression as a flat array.
This is a structural decomposition — it does not perform algebraic
factoring (use ce.function('Factor', [expr]) for that).
Multiply(a, b, c)returns[a, b, c]Negate(x)returns[-1, ...x.factors()]- Anything else returns
[expr]
ce.parse('2xyz').factors() // [2, x, y, z]
ce.parse('-3x').factors() // [-1, 3, x]
ce.parse('x + 1').factors() // [x + 1]
Expression.polynomialCoefficients()
polynomialCoefficients(variable?): readonly Expression[]
Return the coefficients of this expression as a polynomial in variable,
in descending order of degree. Returns undefined if the expression is
not a polynomial in the given variable.
If variable is omitted, auto-detects when the expression has exactly
one unknown. Returns undefined if there are zero or multiple unknowns.
ce.parse('x^2 + 2x + 1').polynomialCoefficients('x') // [1, 2, 1]
ce.parse('x^3 + 2x + 1').polynomialCoefficients('x') // [1, 0, 2, 1]
ce.parse('sin(x)').polynomialCoefficients('x') // undefined
ce.parse('x^2 + 5').polynomialCoefficients() // [1, 0, 5]
Subsumes isPolynomial:
const isPolynomial = expr.polynomialCoefficients('x') !== undefined;
Subsumes polynomialDegree:
const degree = expr.polynomialCoefficients('x')?.length - 1;
When variable is an array, the expression must be polynomial in ALL
listed variables. Coefficients are decomposed by the first variable;
remaining variables appear as symbolic coefficients.
ce.parse('x^2*y + 3x + y^2').polynomialCoefficients(['x', 'y'])
// → [y, 3, y²] (coefficients of x², x¹, x⁰)
####### variable?
string | string[]
Expression.polynomialRoots()
polynomialRoots(variable?): readonly Expression[]
Return the roots of this expression treated as a polynomial in variable.
Returns undefined if the expression is not a polynomial in the given
variable. Returns an empty array if no roots can be found.
If variable is omitted, auto-detects when the expression has exactly
one unknown.
ce.parse('x^2 - 5x + 6').polynomialRoots('x') // [2, 3]
ce.parse('x^2 + 1').polynomialRoots('x') // [] (no real roots)
ce.parse('sin(x)').polynomialRoots('x') // undefined
####### variable?
string
Expression.isScoped
readonly isScoped: boolean;
If true, the expression has its own local scope that can be used for local variables and arguments. Only true if the expression is a function expression.
Expression.localScope
If this expression has a local scope, return it.
Expression.subs()
subs(sub, options?): Expression
Replace all the symbols in the expression as indicated.
Note the same effect can be achieved with this.replace(), but
using this.subs() is more efficient and simpler, but limited
to replacing symbols.
The result is bound to the current scope, not to this.scope.
If options.canonical is not set, the result is canonical if this
is canonical.
Applicable to canonical and non-canonical expressions.
If this is a function, an empty substitution is given, and the computed value of canonical
does not differ from that of this expr.: then a call this method is analagous to requesting a
clone.
####### sub
Substitution<ExpressionInput>
####### options?
####### canonical?
Expression.map()
map(fn, options?): Expression
Recursively replace all the subexpressions in the expression as indicated.
To remove a subexpression, return an empty ["Sequence"] expression.
The canonical option is applied to each function subexpression after
the substitution is applied.
If no options.canonical is set, the result is canonical if this
is canonical.
Default: { canonical: this.isCanonical, recursive: true }
Applicable to canonical and non-canonical expressions.
####### fn
(expr) => Expression
####### options?
####### canonical
####### recursive?
boolean
Expression.replace()
replace(rules, options?): Expression
Transform the expression by applying one or more replacement rules:
-
If the expression matches the
matchpattern and theconditionpredicate is true, replace it with thereplacepattern. -
If no rules apply, return
null.
See also expr.subs() for a simple substitution of symbols.
Procedure for the determining the canonical-status of the input expression and replacements:
-
If
options.canonicalis set, the entire expr. is canonicalized to this degree: whether the replacement occurs at the top-level, or within/recursively. -
If otherwise, the direct replacement will be canonical if either the 'replaced' expression is canonical, or the given replacement (- is a Expression and -) is canonical. Notably also, if this replacement takes place recursively (not at the top-level), then exprs. containing the replaced expr. will still however have their (previous) canonical-status preserved... unless this expr. was previously non-canonical, and replacements have resulted in canonical operands. In this case, an expr. meeting this criteria will be updated to canonical status. (Canonicalization is opportunistic here, in other words).
Applicable to canonical and non-canonical expressions.
To match a specific symbol (not a wildcard pattern), the match must be
a Expression (e.g., { match: ce.box('x'), replace: ... }).
For simple symbol substitution, consider using subs() instead.
####### rules
Rule | BoxedRuleSet | Rule[]
####### options?
Partial<ReplaceOptions>
Expression.has()
has(v): boolean
True if the expression includes a symbol v or a function operator v.
Applicable to canonical and non-canonical expressions.
####### v
string | string[]
Expression.match()
match(pattern, options?): BoxedSubstitution<Expression>
If this expression matches pattern, return a substitution that makes
pattern equal to this. Otherwise return null.
If pattern includes wildcards (symbols that start
with _), the substitution will include a prop for each matching named
wildcard.
If this expression matches pattern but there are no named wildcards,
return the empty substitution, {}.
pattern can be:
- A string (LaTeX): single-character symbols are auto-converted to
wildcards (e.g.,
'ax^2+bx+c'treatsa,b,cas wildcards). Results use unprefixed keys ({a: 3}not{_a: 3}) and self-matches are filtered out.useVariationsandmatchMissingTermsdefault totrue. Unprefixed keys are accepted insubstitution. - A MathJSON array (e.g.,
['Add', '_a', '_b']): boxed automatically. - A BoxedExpression: used directly.
Read more about patterns and rules.
Applicable to canonical and non-canonical expressions.
####### pattern
####### options?
PatternMatchOptions<Expression>
Expression.wikidata
readonly wikidata: string;
Wikidata identifier.
If not a canonical expression, return undefined.
Expression.description
readonly description: string[];
An optional short description if a symbol or function expression.
May include markdown. Each string is a paragraph.
If not a canonical expression, return undefined.
Expression.url
readonly url: string;
An optional URL pointing to more information about the symbol or function operator.
If not a canonical expression, return undefined.
Expression.complexity
readonly complexity: number;
Expressions with a higher complexity score are sorted first in commutative functions
If not a canonical expression, return undefined.
Expression.baseDefinition
readonly baseDefinition: BoxedBaseDefinition;
For symbols and functions, a definition associated with the
expression. this.baseDefinition is the base class of symbol and function
definition.
If not a canonical expression, return undefined.
Expression.operatorDefinition
readonly operatorDefinition: BoxedOperatorDefinition;
For function expressions, the definition of the operator associated with
the expression. For symbols, the definition of the symbol if it is an
operator, for example "Sin".
If not a canonical expression or not a function expression,
its value is undefined.
Expression.valueDefinition
readonly valueDefinition: BoxedValueDefinition;
For symbols, a definition associated with the expression, if it is not an operator.
If not a canonical expression, or not a value, its value is undefined.
Expression.simplify()
simplify(options?): Expression
Return a simpler form of this expression.
A series of rewriting rules are applied repeatedly, until no more rules apply.
The values assigned to symbols and the assumptions about symbols may be
used, for example expr.isInteger or expr.isPositive.
No calculations involving decimal numbers (numbers that are not integers) are performed but exact calculations may be performed, for example:
\sin(\frac{\pi}{4}) \longrightarrow \frac{\sqrt{2}}{2}.
The result is canonical.
To manipulate symbolically non-canonical expressions, use expr.replace().
####### options?
Partial<SimplifyOptions>
Expression.evaluate()
evaluate(options?): Expression
Return the value of the canonical form of this expression.
A pure expression always returns the same value (provided that it remains constant / values of sub-expressions or symbols do not change), and has no side effects.
Evaluating an impure expression may return a varying value, and may have some side effects such as adjusting symbol assumptions.
To perform approximate calculations, use expr.N() instead,
or call with options.numericApproximation to true.
It is possible that the result of expr.evaluate() may be the same as
expr.simplify().
The result is in canonical form.
####### options?
Partial<EvaluateOptions>
Expression.evaluateAsync()
evaluateAsync(options?): Promise<Expression>
Asynchronous version of evaluate().
The options argument can include a signal property, which is an
AbortSignal object. If the signal is aborted, a CancellationError is thrown.
####### options?
Partial<EvaluateOptions>
Expression.N()
N(): Expression
Return a numeric approximation of the canonical form of this expression.
Any necessary calculations, including on decimal numbers (non-integers), are performed.
The calculations are performed according to the
precision property of the ComputeEngine.
To only perform exact calculations, use this.evaluate() instead.
If the function is not numeric, the result of this.N() is the same as
this.evaluate().
The result is in canonical form.
Expression.solve()
solve(vars?):
| readonly Expression[]
| Record<string, Expression>
| Record<string, Expression>[]
If this is an equation, solve the equation for the variables in vars.
Otherwise, solve the equation this = 0 for the variables in vars.
For univariate equations, returns an array of solutions (roots). For systems of linear equations (List of Equal expressions), returns an object mapping variable names to their values. For non-linear polynomial systems (like xy=6, x+y=5), returns an array of solution objects (multiple solutions possible).
// Univariate equation
const expr = ce.parse("x^2 + 2*x + 1 = 0");
console.log(expr.solve("x")); // Returns array of roots
// System of linear equations
const system = ce.parse("\\begin{cases}x+y=70\\\\2x-4y=80\\end{cases}");
console.log(system.solve(["x", "y"])); // Returns { x: 60, y: 10 }
// Non-linear polynomial system (product + sum)
const nonlinear = ce.parse("\\begin{cases}xy=6\\\\x+y=5\\end{cases}");
console.log(nonlinear.solve(["x", "y"])); // Returns [{ x: 2, y: 3 }, { x: 3, y: 2 }]
####### vars?
string | Iterable<string, any, any> | Expression | Iterable<Expression, any, any>
Expression.value
get value(): Expression
set value(value:
| string
| number
| boolean
| number[]
| Decimal
| OnlyFirst<{
re: number;
im: number;
}, {
re: number;
im: number;
} & {
num: number;
denom: number;
} & Expression>
| OnlyFirst<{
num: number;
denom: number;
}, {
re: number;
im: number;
} & {
num: number;
denom: number;
} & Expression>
| OnlyFirst<Expression, {
re: number;
im: number;
} & {
num: number;
denom: number;
} & Expression>): void
If this expression is a number literal, a string literal or a function literal, return the expression.
If the expression is a symbol, return the value of the symbol.
Otherwise, the expression is a symbolic expression, including an unknown
symbol, i.e. a symbol with no value, return undefined.
If the expression is a symbol, set the value of the symbol.
Will throw a runtime error if either not a symbol, or a symbol with the
constant flag set to true.
Setting the value of a symbol results in the forgetting of all assumptions about it in the current scope.
Expression.isCollection
isCollection: boolean;
Is true if the expression is a collection.
When isCollection is true, the expression:
- has an
each()method that returns a generator over the elements of the collection. - has a
sizeproperty that returns the number of elements in the collection. - has a
contains(other)method that returnstrueif theotherexpression is in the collection.
Expression.isIndexedCollection
isIndexedCollection: boolean;
Is true if this is an indexed collection, such as a list, a vector,
a matrix, a tuple, etc...
The elements of an indexed collection can be accessed by a one-based index.
When isIndexedCollection is true, the expression:
- has an
each(),size()andcontains(rhs)methods as for a collection. - has an
at(index: number)method that returns the element at the specified index. - has an
indexWhere(predicate: (element: Expression) => boolean)method that returns the index of the first element that matches the predicate.
Expression.isLazyCollection
isLazyCollection: boolean;
False if not a collection, or if the elements of the collection are not computed lazily.
The elements of a lazy collection are computed on demand, when
iterating over the collection using each().
Use ListFrom and related functions to create eager collections from
lazy collections.
Expression.each()
each(): Generator<Expression>
If this is a collection, return an iterator over the elements of the collection.
const expr = ce.parse('[1, 2, 3, 4]');
for (const e of expr.each()) {
console.log(e);
}
Expression.contains()
contains(rhs): boolean
If this is a collection, return true if the rhs expression is in the
collection.
Return undefined if the membership cannot be determined without
iterating over the collection.
####### rhs
Expression.subsetOf()
subsetOf(other, strict): boolean
Check if this collection is a subset of another collection.
####### other
The other collection to check against.
####### strict
boolean
If true, the subset relation is strict (i.e., proper subset).
Expression.count
If this is a collection, return the number of elements in the collection.
If the collection is infinite, return Infinity.
If the number of elements cannot be determined, return undefined, for
example, if the collection is lazy and not finite and the size cannot
be determined without iterating over the collection.
Expression.isFiniteCollection
isFiniteCollection: boolean;
If this is a finite collection, return true.
Expression.isEmptyCollection
isEmptyCollection: boolean;
If this is an empty collection, return true.
An empty collection has a size of 0.
Expression.at()
at(index): Expression
If this is an indexed collection, return the element at the specified index. The first element is at index 1.
If the index is negative, return the element at index size() + index + 1.
The last element is at index -1.
####### index
number
Expression.get()
get(key): Expression
If this is a keyed collection (map, record, tuple), return the value of the corresponding key.
If key is a Expression, it should be a string.
####### key
string | Expression
Expression.indexWhere()
indexWhere(predicate): number
If this is an indexed collection, return the index of the first element that matches the predicate.
####### predicate
(element) => boolean
Primitive Methods
Expression.valueOf()
valueOf(): string | number | boolean | number[] | number[][] | number[][][]
Return a JavaScript primitive value for the expression, based on
Object.valueOf().
This method is intended to make it easier to work with JavaScript primitives, for example when mixing JavaScript computations with symbolic computations from the Compute Engine.
If the expression is a machine number, a bignum, or a rational
that can be converted to a machine number, return a JavaScript number.
This conversion may result in a loss of precision.
If the expression is the symbol "True" or the symbol "False",
return true or false, respectively.
If the expression is a symbol with a numeric value, return the numeric value of the symbol.
If the expression is a string literal, return the string value.
If the expression is a tensor (list of number or multidimensional array or matrix), return an array of numbers, or an array of arrays of numbers, or an array of arrays of arrays of numbers.
If the expression is a function expression return a string representation of the expression.
Expression.[toPrimitive]()
toPrimitive: string | number
Similar toexpr.valueOf() but includes a hint.
####### hint
"string" | "number" | "default"
Expression.toString()
toString(): string
Return an ASCIIMath representation of the expression. This string is suitable to be output to the console for debugging, for example.
Based on Object.toString().
To get a LaTeX representation of the expression, use expr.latex.
Note that lazy collections are eagerly evaluated.
Used when coercing a Expression to a String.
Expression.toJSON()
toJSON(): MathJsonExpression
Used by JSON.stringify() to serialize this object to JSON.
Method version of expr.json.
Based on Object.toJSON().
Note that lazy collections are not eagerly evaluated.
Expression.is()
is(other, tolerance?): boolean
Smart equality check: structural first, then numeric evaluation fallback.
Symmetric: a.is(b) always equals b.is(a).
First tries an exact structural check (same as isSame()). If that fails
and the expression is constant (no free variables), evaluates numerically
and compares within engine.tolerance.
For literal numbers compared to primitives (number, bigint), behaves
identically to isSame() — no tolerance is applied. Tolerance only
applies to expressions that require evaluation (e.g., \\sin(\\pi)).
ce.parse('\\cos(\\frac{\\pi}{2})').is(0) // true — evaluates, within tolerance
ce.number(1e-17).is(0) // false — literal, no tolerance
ce.parse('x + 1').is(1) // false — has free variables
ce.parse('\\pi').is(3.14, 0.01) // true — within custom tolerance
After the structural check, attempts to expand both sides (distributing
products, applying the multinomial theorem, etc.) and re-checks
structural equality. This catches equivalences like (x+1)^2 vs
x^2+2x+1 even when the expression has free variables.
####### other
string | number | bigint | boolean | Expression
####### tolerance?
number
If provided, overrides engine.tolerance for the
numeric comparison. Has no effect when the comparison is structural
(i.e., when isSame() succeeds or the expression has free variables).
Relational Operator
Expression.isSame()
isSame(rhs): boolean
Fast exact structural/symbolic equality check.
Returns true if the expression is structurally identical to rhs.
For symbols with value bindings, follows the binding (e.g., if one = 1,
then ce.symbol('one').isSame(1) is true).
Accepts JavaScript primitives: number, bigint, boolean, string.
Does not evaluate expressions — purely structural.
ce.parse('1+x', {form: 'raw'}).isSame(ce.parse('x+1', {form: 'raw'})) is false.
See expr.is() for a smart check with numeric evaluation fallback,
and expr.isEqual() for full mathematical equality.
Applicable to canonical and non-canonical expressions.
####### rhs
string | number | bigint | boolean | Expression
Expression.isLess()
isLess(other): boolean
The value of both expressions are compared.
If the expressions cannot be compared, return undefined
####### other
number | Expression
Expression.isLessEqual()
isLessEqual(other): boolean
The value of both expressions are compared.
If the expressions cannot be compared, return undefined
####### other
number | Expression
Expression.isGreater()
isGreater(other): boolean
The value of both expressions are compared.
If the expressions cannot be compared, return undefined
####### other
number | Expression
Expression.isGreaterEqual()
isGreaterEqual(other): boolean
The value of both expressions are compared.
If the expressions cannot be compared, return undefined
####### other
number | Expression
Expression.isEqual()
isEqual(other): boolean
Mathematical equality (strong equality), that is the value
of this expression and the value of other are numerically equal.
Both expressions are evaluated and the result is compared numerically.
Numbers whose difference is less than engine.tolerance are
considered equal. This tolerance is set when the engine.precision is
changed to be such that the last two digits are ignored.
Evaluating the expressions may be expensive. Other options to consider to compare two expressions include:
expr.isSame(other)for a fast exact structural comparison (no evaluation)expr.is(other)for a smart check that tries structural first, then numeric evaluation fallback for constant expressions
Examples
let expr = ce.parse('2 + 2');
console.log(expr.isEqual(4)); // true
console.log(expr.isSame(4)); // false (structural only)
console.log(expr.is(4)); // true (evaluates, within tolerance)
expr = ce.parse('4');
console.log(expr.isEqual(4)); // true
console.log(expr.isSame(4)); // true
console.log(expr.is(4)); // true
####### other
number | Expression
Tensor Expression
Expression.shape
readonly shape: number[];
The shape describes the axes of the expression, where each axis represent a way to index the elements of the expression.
When the expression is a scalar (number), the shape is [].
When the expression is a vector of length n, the shape is [n].
When the expression is a n by m matrix, the shape is [n, m].
Expression.rank
readonly rank: number;
The rank refers to the number of dimensions (or axes) of the expression.
Return 0 for a scalar, 1 for a vector, 2 for a matrix, > 2 for a multidimensional matrix.
The rank is equivalent to the length of expr.shape
There are several definitions of rank in the literature. For example, the row rank of a matrix is the number of linearly independent rows. The rank can also refer to the number of non-zero singular values of a matrix.
Type Properties
Expression.type
get type(): BoxedType
set type(type:
| string
| AlgebraicType
| NegationType
| CollectionType
| ListType
| SetType
| RecordType
| DictionaryType
| TupleType
| SymbolType
| ExpressionType
| NumericType
| FunctionSignature
| ValueType
| TypeReference
| BoxedType): void
The type of the value of this expression.
If a symbol the type of the value of the symbol.
If a function expression, the type of the value of the function (the result type).
If a symbol with a "function" type (a function literal), returns the
signature.
If not valid, return "error".
If the type is not known, return "unknown".
Expression.isNumber
readonly isNumber: boolean;
true if the value of this expression is a number.
Note that in a fateful twist of cosmic irony, NaN ("Not a Number")
is a number.
If isNumber is true, this indicates that evaluating the expression
will return a number.
This does not indicate that the expression is a number literal. To check
if the expression is a number literal, use expr.isNumberLiteral.
For example, the expression ["Add", 1, "x"] is a number if "x" is a
number and expr.isNumber is true, but isNumberLiteral is false.
Expression.isInteger
readonly isInteger: boolean;
The value of this expression is an element of the set ℤ: ...,-2, -1, 0, 1, 2...
Note that ±∞ and NaN are not integers.
Expression.isRational
readonly isRational: boolean;
The value of this expression is an element of the set ℚ, p/q with p ∈ ℕ, q ∈ ℤ ⃰ q >= 1
Note that every integer is also a rational.
This is equivalent to this.type === "rational" || this.type === "integer"
Note that ±∞ and NaN are not rationals.
Expression.isReal
readonly isReal: boolean;
The value of this expression is a real number.
This is equivalent to this.type === "rational" || this.type === "integer" || this.type === "real"
Note that ±∞ and NaN are not real numbers.
NumberLiteralInterface
Narrowed interface for number literal expressions.
Obtained via isNumber().
NumberLiteralInterface.numericValue
readonly numericValue: number | NumericValue;
NumberLiteralInterface.isExact
readonly isExact: boolean;
NumberLiteralInterface.isNumberLiteral
readonly isNumberLiteral: true;
SymbolInterface
Narrowed interface for symbol expressions.
Obtained via isSymbol().
SymbolInterface.symbol
readonly symbol: string;
FunctionInterface
Narrowed interface for function expressions.
Obtained via isFunction().
FunctionInterface.isFunctionExpression
readonly isFunctionExpression: true;
FunctionInterface.ops
readonly ops: readonly Expression[];
FunctionInterface.nops
readonly nops: number;
FunctionInterface.op1
readonly op1: Expression;
FunctionInterface.op2
readonly op2: Expression;
FunctionInterface.op3
readonly op3: Expression;
StringInterface
Narrowed interface for string expressions.
Obtained via isString().
StringInterface.string
readonly string: string;
TensorInterface
Narrowed interface for tensor expressions.
Obtained via isTensor().
TensorInterface.tensor
readonly tensor: Tensor<keyof DataTypeMap>;
TensorInterface.shape
readonly shape: number[];
TensorInterface.rank
readonly rank: number;
CollectionInterface
Narrowed interface for collection expressions.
Obtained via isCollection().
Extended by
CollectionInterface.isCollection
readonly isCollection: true;
CollectionInterface.count
readonly count: number;
CollectionInterface.isFiniteCollection
readonly isFiniteCollection: boolean;
CollectionInterface.isEmptyCollection
readonly isEmptyCollection: boolean;
CollectionInterface.each()
each(): Generator<Expression>
IndexedCollectionInterface
Narrowed interface for indexed collection expressions (lists, vectors, matrices, tuples).
Obtained via isIndexedCollection().
Extends
IndexedCollectionInterface.isIndexedCollection
readonly isIndexedCollection: true;
IndexedCollectionInterface.indexWhere()
indexWhere(predicate): number
####### predicate
(element) => boolean
ExpressionInput
type ExpressionInput =
| number
| bigint
| string
| BigNum
| MathJsonNumberObject
| MathJsonStringObject
| MathJsonSymbolObject
| MathJsonFunctionObject
| MathJsonDictionaryObject
| readonly [MathJsonSymbol, ...ExpressionInput[]]
| Expression;
An expression input is a MathJSON expression which can include some engine expression terms.
This is convenient when creating new expressions from portions
of an existing Expression while avoiding unboxing and reboxing.
ReplaceOptions
type ReplaceOptions = {
recursive: boolean;
once: boolean;
useVariations: boolean;
matchPermutations: boolean;
iterationLimit: number;
canonical: CanonicalOptions;
};
Options for Expression.replace().
CanonicalForm
type CanonicalForm =
| "InvisibleOperator"
| "Number"
| "Multiply"
| "Add"
| "Power"
| "Divide"
| "Flatten"
| "Order";
Canonical normalization transforms.
CanonicalOptions
type CanonicalOptions =
| boolean
| CanonicalForm
| CanonicalForm[];
FormOption
type FormOption =
| "canonical"
| "structural"
| "raw"
| CanonicalForm
| CanonicalForm[];
Controls how expressions are created.
Metadata
type Metadata = {
latex: string;
wikidata: string;
};
Metadata that can be associated with a MathJSON expression.
Pattern Matching
Substitution
type Substitution<T> = KernelSubstitution<T>;
A substitution describes the values of the wildcards in a pattern so that the pattern is equal to a target expression.
A substitution can also be considered a more constrained version of a
rule whose match is always a symbol.
Type Parameters
• T = ExpressionInput
BoxedSubstitution
type BoxedSubstitution<T> = KernelBoxedSubstitution<T>;
Type Parameters
• T = Expression
PatternMatchOptions
type PatternMatchOptions<T> = KernelPatternMatchOptions<T>;
Control how a pattern is matched to an expression.
Type Parameters
• T = Expression
Rules
RuleReplaceFunction
type RuleReplaceFunction = KernelRuleReplaceFunction<Expression>;
Rule replacement callback specialized to boxed expressions.
RuleConditionFunction
type RuleConditionFunction = KernelRuleConditionFunction<Expression, ComputeEngine>;
Rule condition callback with access to the compute engine.
Rule
type Rule = KernelRule<Expression, ExpressionInput, ComputeEngine>;
Rule declaration specialized to boxed expression and compute engine types.
Assumptions
ExpressionMapInterface
type ExpressionMapInterface<U> = KernelExpressionMapInterface<U, Expression>;
Map-like interface keyed by boxed expressions.
Type Parameters
• U
Assumption
type Assumption = KernelAssumption<Expression, ComputeEngine>;
Assumption predicates bound to this compute engine.
AssumeResult
type AssumeResult =
| "internal-error"
| "not-a-predicate"
| "contradiction"
| "tautology"
| "ok";
Compiling
CompiledType
type CompiledType = boolean | number | string | object;
JSSource
type JSSource = string;
CompiledExpression
type CompiledExpression = {
evaluate: (scope) => number | Expression;
};
Definitions
ValueDefinition
type ValueDefinition = BaseDefinition & {
holdUntil: "never" | "evaluate" | "N";
type: | Type
| TypeString
| BoxedType;
inferred: boolean;
value: | LatexString
| ExpressionInput
| (ce) => Expression | null;
eq: (a) => boolean | undefined;
neq: (a) => boolean | undefined;
cmp: (a) => "=" | ">" | "<" | undefined;
collection: CollectionHandlers;
subscriptEvaluate: (subscript, options) => Expression | undefined;
};
A bound symbol (i.e. one with an associated definition) has either a type (e.g. ∀ x ∈ ℝ), a value (x = 5) or both (π: value = 3.14... type = 'real').
ValueDefinition.inferred
inferred: boolean;
If true, the type is inferred, and could be adjusted later as more information becomes available or if the symbol is explicitly declared.
ValueDefinition.value
value:
| LatexString
| ExpressionInput
| (ce) => Expression | null;
value can be a JS function since for some constants, such as
Pi, the actual value depends on the precision setting of the
ComputeEngine and possible other environment settings
ValueDefinition.subscriptEvaluate()?
optional subscriptEvaluate: (subscript, options) => Expression | undefined;
Custom evaluation handler for subscripted expressions of this symbol.
Called when evaluating Subscript(symbol, index).
subscript
The subscript expression (already evaluated)
options
Contains the compute engine and evaluation options
####### engine
ComputeEngine
####### numericApproximation?
boolean
SequenceDefinition
Definition for a sequence declared with ce.declareSequence().
A sequence is defined by base cases and a recurrence relation.
Example
// Fibonacci sequence
ce.declareSequence('F', {
base: { 0: 0, 1: 1 },
recurrence: 'F_{n-1} + F_{n-2}',
});
ce.parse('F_{10}').evaluate(); // → 55
SequenceDefinition.variable?
optional variable: string;
Index variable name for single-index sequences, default 'n'.
For multi-index sequences, use variables instead.
SequenceDefinition.variables?
optional variables: string[];
Index variable names for multi-index sequences.
Example: ['n', 'k'] for Pascal's triangle P\_{n,k}
If provided, this takes precedence over variable.
SequenceDefinition.base
base: Record<number | string, number | Expression>;
Base cases as index → value mapping.
For single-index sequences, use numeric keys:
base: { 0: 0, 1: 1 } // F_0 = 0, F_1 = 1
For multi-index sequences, use comma-separated string keys:
base: {
'0,0': 1, // Exact: P_{0,0} = 1
'n,0': 1, // Pattern: P_{n,0} = 1 for all n
'n,n': 1, // Pattern: P_{n,n} = 1 (diagonal)
}
Pattern keys use variable names to match any value. When the same variable appears multiple times (e.g., 'n,n'), the indices must be equal.
SequenceDefinition.recurrence
recurrence: string | Expression;
Recurrence relation as LaTeX string or Expression
SequenceDefinition.memoize?
optional memoize: boolean;
Whether to memoize computed values (default: true)
SequenceDefinition.domain?
optional domain:
| {
min: number;
max: number;
}
| Record<string, {
min: number;
max: number;
}>;
Valid index domain constraints.
For single-index sequences:
domain: { min: 0, max: 100 }
For multi-index sequences, use per-variable constraints:
domain: { n: { min: 0 }, k: { min: 0 } }
SequenceDefinition.constraints?
optional constraints: string | Expression;
Constraint expression for multi-index sequences. The expression should evaluate to a boolean/numeric value. If it evaluates to false or 0, the subscript is considered out of domain.
Example: 'k <= n' for Pascal's triangle (only valid when k ≤ n)
SequenceStatus
Status of a sequence definition.
SequenceStatus.status
status: "complete" | "pending" | "not-a-sequence";
Status of the sequence:
- 'complete': Both base case(s) and recurrence defined
- 'pending': Waiting for base case(s) or recurrence
- 'not-a-sequence': Symbol is not a sequence
SequenceStatus.baseIndices
baseIndices: (string | number)[];
Keys of defined base cases. For single-index: numeric indices (e.g., [0, 1]) For multi-index: string keys including patterns (e.g., ['0,0', 'n,0', 'n,n'])
SequenceStatus.variable?
optional variable: string;
Index variable name if recurrence is defined (single-index)
SequenceStatus.variables?
optional variables: string[];
Index variable names if recurrence is defined (multi-index)
SequenceInfo
Information about a defined sequence for introspection.
SequenceInfo.variable?
optional variable: string;
Index variable name for single-index sequences (e.g., "n")
SequenceInfo.variables?
optional variables: string[];
Index variable names for multi-index sequences (e.g., ["n", "k"])
SequenceInfo.baseIndices
baseIndices: (string | number)[];
Base case keys. For single-index: numeric indices For multi-index: string keys including patterns
SequenceInfo.domain
domain:
| {
min: number;
max: number;
}
| Record<string, {
min: number;
max: number;
}>;
Domain constraints.
For single-index: { min?, max? }
For multi-index: per-variable constraints
OperatorDefinition
type OperatorDefinition = Partial<BaseDefinition> & Partial<OperatorDefinitionFlags> & {
signature: | Type
| TypeString
| BoxedType;
type: (ops, options) =>
| Type
| TypeString
| BoxedType
| undefined;
sgn: (ops, options) => Sign | undefined;
isPositive: boolean;
isNonNegative: boolean;
isNegative: boolean;
isNonPositive: boolean;
even: (ops, options) => boolean | undefined;
complexity: number;
canonical: (ops, options) => Expression | null;
evaluate: | (ops, options) => Expression | undefined
| Expression;
evaluateAsync: (ops, options) => Promise<Expression | undefined>;
evalDimension: (args, options) => Expression;
xcompile: (expr) => CompiledExpression;
eq: (a, b) => boolean | undefined;
neq: (a, b) => boolean | undefined;
collection: CollectionHandlers;
};
Definition record for a function.
OperatorDefinition.signature?
optional signature:
| Type
| TypeString
| BoxedType;
The function signature, describing the type of the arguments and the return type.
If a type handler is provided, the return type of the function should
be a subtype of the return type in the signature.
OperatorDefinition.type()?
optional type: (ops, options) =>
| Type
| TypeString
| BoxedType
| undefined;
The type of the result (return type) based on the type of the arguments.
Should be a subtype of the type indicated by the signature.
For example, if the signature is (number) -> real, the type of the
result could be real or integer, but not complex.
Do not evaluate the arguments.
However, the type of the arguments can be used to determine the type of the result.
OperatorDefinition.sgn()?
optional sgn: (ops, options) => Sign | undefined;
Return the sign of the function expression.
If the sign cannot be determined, return undefined.
When determining the sign, only literal values and the values of symbols, if they are literals, should be considered.
Do not evaluate the arguments.
However, the type and sign of the arguments can be used to determine the sign.
OperatorDefinition.isPositive?
readonly optional isPositive: boolean;
The value of this expression is > 0, same as isGreater(0)
OperatorDefinition.isNonNegative?
readonly optional isNonNegative: boolean;
The value of this expression is >= 0, same as isGreaterEqual(0)
OperatorDefinition.isNegative?
readonly optional isNegative: boolean;
The value of this expression is < 0, same as isLess(0)
OperatorDefinition.isNonPositive?
readonly optional isNonPositive: boolean;
The value of this expression is <= 0, same as isLessEqual(0)
OperatorDefinition.even()?
optional even: (ops, options) => boolean | undefined;
Return true if the function expression is even, false if it is odd
and undefined if it is neither (for example if it is not a number,
or if it is a complex number).
OperatorDefinition.complexity?
optional complexity: number;
A number used to order arguments.
Argument with higher complexity are placed after arguments with lower complexity when ordered canonically in commutative functions.
- Additive functions: 1000-1999
- Multiplicative functions: 2000-2999
- Root and power functions: 3000-3999
- Log functions: 4000-4999
- Trigonometric functions: 5000-5999
- Hypertrigonometric functions: 6000-6999
- Special functions (factorial, Gamma, ...): 7000-7999
- Collections: 8000-8999
- Inert and styling: 9000-9999
- Logic: 10000-10999
- Relational: 11000-11999
Default: 100,000
OperatorDefinition.canonical()?
optional canonical: (ops, options) => Expression | null;
Return the canonical form of the expression with the arguments args.
The arguments (args) may not be in canonical form. If necessary, they
can be put in canonical form.
This handler should validate the type and number of the arguments (arity).
If a required argument is missing, it should be indicated with a
["Error", "'missing"] expression. If more arguments than expected
are present, this should be indicated with an
["Error", "'unexpected-argument'"] error expression
If the type of an argument is not compatible, it should be indicated
with an incompatible-type error.
["Sequence"] expressions are not folded and need to be handled
explicitly.
If the function is associative, idempotent or an involution, this handler should account for it. Notably, if it is commutative, the arguments should be sorted in canonical order.
Values of symbols should not be substituted, unless they have
a holdUntil attribute of "never".
The handler should not consider the value or any assumptions about any
of the arguments that are symbols or functions (i.e. arg.isZero,
arg.isInteger, etc...) since those may change over time.
The result of the handler should be a canonical expression.
If the arguments do not match, they should be replaced with an
appropriate ["Error"] expression. If the expression cannot be put in
canonical form, the handler should return null.
OperatorDefinition.evaluate?
optional evaluate:
| (ops, options) => Expression | undefined
| Expression;
Evaluate a function expression.
When the handler is invoked, the arguments have been evaluated, except
if the lazy option is set to true.
It is not necessary to further simplify or evaluate the arguments.
If performing numerical calculations and options.numericalApproximation
is false return an exact numeric value, for example return a rational
number or a square root, rather than a floating point approximation.
Use ce.number() to create the numeric value.
If the expression cannot be evaluated, due to the values, types, or
assumptions about its arguments, return undefined or
an ["Error"] expression.
OperatorDefinition.evaluateAsync()?
optional evaluateAsync: (ops, options) => Promise<Expression | undefined>;
An asynchronous version of evaluate.
OperatorDefinition.evalDimension()?
optional evalDimension: (args, options) => Expression;
Experimental
Dimensional analysis
OperatorDefinition.xcompile()?
optional xcompile: (expr) => CompiledExpression;
Return a compiled (optimized) expression.
BaseDefinition
Metadata common to both symbols and functions.
BaseDefinition.description
description: string | string[];
If a string, a short description, about one line long.
Otherwise, a list of strings, each string a paragraph.
May contain Markdown.
BaseDefinition.examples
examples: string | string[];
A list of examples of how to use this symbol or operator.
Each example is a string, which can be a MathJSON expression or LaTeX, bracketed by $ signs.
For example, ["Add", 1, 2] or $\\sin(\\pi/4)$.
BaseDefinition.wikidata
wikidata: string;
A short string representing an entry in a wikibase.
For example "Q167" is the wikidata entry
for the Pi constant.
BaseDefinition.isConstant?
readonly optional isConstant: boolean;
If true, the value or type of the definition cannot be changed
SymbolDefinition
type SymbolDefinition = OneOf<[ValueDefinition, OperatorDefinition]>;
A table mapping symbols to their definition.
Symbols should be valid MathJSON symbols. In addition, the following rules are recommended:
- Use only latin letters, digits and
-:/[a-zA-Z0-9-]+/ - The first character should be a letter:
/^[a-zA-Z]/ - Functions and symbols exported from a library should start with an uppercase letter
/^[A-Z]/
SymbolDefinitions
type SymbolDefinitions = Readonly<{}>;
LibraryDefinition
A library bundles symbol/operator definitions with their LaTeX dictionary entries and declares dependencies on other libraries.
Use with the libraries constructor option to load standard or custom
libraries:
const ce = new ComputeEngine({
libraries: ['core', 'arithmetic', {
name: 'custom',
requires: ['arithmetic'],
definitions: { G: { value: 6.674e-11, type: 'real', isConstant: true } },
}],
});
LibraryDefinition.requires?
optional requires: string[];
Libraries that must be loaded before this one
LibraryDefinition.definitions?
optional definitions: Readonly<{}> | Readonly<{}>[];
Symbol and operator definitions
LibraryDefinition.latexDictionary?
optional latexDictionary: readonly Partial<OnlyFirst<
| DefaultEntry
| ExpressionEntry
| MatchfixEntry
| InfixEntry
| PostfixEntry
| PrefixEntry
| EnvironmentEntry
| SymbolEntry
| FunctionEntry, {} &
| DefaultEntry
| ExpressionEntry
| MatchfixEntry
| InfixEntry
| PostfixEntry
| PrefixEntry
| EnvironmentEntry
| SymbolEntry
| FunctionEntry>>[];
LaTeX dictionary entries for parsing/serialization
BaseCollectionHandlers
These handlers are the primitive operations that can be performed on all collections, indexed or not.
Definitions
BaseCollectionHandlers.iterator()
iterator: (collection) => Iterator<Expression, undefined, any>;
Return an iterator that iterates over the elements of the collection.
The order in which the elements are returned is not defined. Requesting two iterators on the same collection may return the elements in a different order.
Other
BaseCollectionHandlers.count()
count: (collection) => number;
Return the number of elements in the collection.
An empty collection has a count of 0.
BaseCollectionHandlers.isEmpty()?
optional isEmpty: (collection) => boolean;
Optional flag to quickly check if the collection is empty, without having to count exactly how may elements it has (useful for lazy evaluation).
BaseCollectionHandlers.isFinite()?
optional isFinite: (collection) => boolean;
Optional flag to quickly check if the collection is finite, without having to count exactly how many elements it has (useful for lazy evaluation).
BaseCollectionHandlers.isLazy()?
optional isLazy: (collection) => boolean;
Return true if the collection is lazy, false otherwise.
If the collection is lazy, it means that the elements are not
computed until they are needed, for example when iterating over the
collection.
Default: true
BaseCollectionHandlers.contains()?
optional contains: (collection, target) => boolean;
Return true if the target expression is in the collection,
false otherwise.
Return undefined if the membership cannot be determined.
BaseCollectionHandlers.subsetOf()?
optional subsetOf: (collection, other, strict) => boolean;
Return true if all the elements of other are in collection.
Both collection and other are collections.
If strict is true, the subset must be strict, that is, collection must
have more elements than other.
Return undefined if the subset relation cannot be determined.
BaseCollectionHandlers.eltsgn()?
optional eltsgn: (collection) => Sign;
Return the sign of all the elements of the collection.
BaseCollectionHandlers.elttype()?
optional elttype: (collection) => Type;
Return the widest type of all the elements in the collection
IndexedCollectionHandlers
These additional collection handlers are applicable to indexed collections only.
The elements of an indexed collection can be accessed by index, and the order of the elements is defined.
IndexedCollectionHandlers.at()
at: (collection, index) => Expression;
Return the element at the specified index.
The first element is at(1), the last element is at(-1).
If the index is <0, return the element at index count() + index + 1.
The index can also be a string for example for records. The set of valid
keys is returned by the keys() handler.
If the index is invalid, return undefined.
IndexedCollectionHandlers.indexWhere()
indexWhere: (collection, predicate) => number;
Return the index of the first element that matches the predicate.
If no element matches the predicate, return undefined.
CollectionHandlers
type CollectionHandlers = BaseCollectionHandlers & Partial<IndexedCollectionHandlers>;
The collection handlers are the primitive operations that can be performed on collections, such as lists, sets, tuples, etc...
TaggedValueDefinition
type TaggedValueDefinition = {
value: BoxedValueDefinition;
};
The definition for a value, represented as a tagged object literal.
TaggedOperatorDefinition
type TaggedOperatorDefinition = {
operator: BoxedOperatorDefinition;
};
The definition for an operator, represented as a tagged object literal.
BoxedDefinition
type BoxedDefinition =
| TaggedValueDefinition
| TaggedOperatorDefinition;
A definition can be either a value or an operator.
It is collected in a tagged object literal, instead of being a simple union type, so that the type of the definition can be changed while keeping references to the definition in bound expressions.
BoxedBaseDefinition
Extends
Partial<BaseDefinition>
Extended by
BoxedBaseDefinition.collection?
optional collection: CollectionHandlers;
If this is the definition of a collection, the set of primitive operations that can be performed on this collection (counting the number of elements, enumerating it, etc...).
BoxedValueDefinition
Extends
BoxedValueDefinition.holdUntil
holdUntil: "never" | "evaluate" | "N";
If the symbol has a value, it is held as indicated in the table below. A green checkmark indicate that the symbol is substituted.
| Operation | "never" | "evaluate" | "N" |
|---|---|---|---|
canonical() | (X) | ||
evaluate() | (X) | (X) | |
"N()" | (X) | (X) | (X) |
Some examples:
ImaginaryUnithasholdUntil: 'never': it is substituted during canonicalizationxhasholdUntil: 'evaluate'(variables)PihasholdUntil: 'N'(special numeric constant)
Default: evaluate
BoxedValueDefinition.value
value: Expression;
The current value of the symbol. For constants, this is immutable. The definition object is the single source of truth — there is no separate evaluation-context values map.
BoxedValueDefinition.eq()?
optional eq: (a) => boolean;
BoxedValueDefinition.neq()?
optional neq: (a) => boolean;
BoxedValueDefinition.cmp()?
optional cmp: (a) => ">" | "<" | "=";
BoxedValueDefinition.inferredType
inferredType: boolean;
True if the type has been inferred. An inferred type can be updated as more information becomes available.
A type that is not inferred, but has been set explicitly, cannot be updated.
BoxedValueDefinition.type
type: BoxedType;
BoxedValueDefinition.subscriptEvaluate()?
optional subscriptEvaluate: (subscript, options) => Expression;
Custom evaluation handler for subscripted expressions of this symbol.
Called when evaluating Subscript(symbol, index).
OperatorDefinitionFlags
type OperatorDefinitionFlags = {
lazy: boolean;
scoped: boolean;
broadcastable: boolean;
associative: boolean;
commutative: boolean;
commutativeOrder: (a, b) => number | undefined;
idempotent: boolean;
involution: boolean;
pure: boolean;
};
An operator definition can have some flags to indicate specific properties of the operator.
BoxedOperatorDefinition
The definition includes information specific about an operator, such as handlers to canonicalize or evaluate a function expression with this operator.
Extends
BoxedOperatorDefinition.complexity
complexity: number;
BoxedOperatorDefinition.inferredSignature
inferredSignature: boolean;
If true, the signature was inferred from usage and may be modified as more information becomes available.
BoxedOperatorDefinition.signature
signature: BoxedType;
The type of the arguments and return value of this function
BoxedOperatorDefinition.type()?
optional type: (ops, options) =>
| string
| AlgebraicType
| NegationType
| CollectionType
| ListType
| SetType
| RecordType
| DictionaryType
| TupleType
| SymbolType
| ExpressionType
| NumericType
| FunctionSignature
| ValueType
| TypeReference
| BoxedType;
If present, this handler can be used to more precisely determine the return type based on the type of the arguments. The arguments themselves should not be evaluated, only their types should be used.
BoxedOperatorDefinition.sgn()?
optional sgn: (ops, options) => Sign;
If present, this handler can be used to determine the sign of the return value of the function, based on the sign and type of its arguments.
The arguments themselves should not be evaluated, only their types and sign should be used.
This can be used in some case for example to determine when certain simplifications are valid.
BoxedOperatorDefinition.eq()?
optional eq: (a, b) => boolean;
BoxedOperatorDefinition.neq()?
optional neq: (a, b) => boolean;
BoxedOperatorDefinition.canonical()?
optional canonical: (ops, options) => Expression;
BoxedOperatorDefinition.evaluate()?
optional evaluate: (ops, options) => Expression;
BoxedOperatorDefinition.evaluateAsync()?
optional evaluateAsync: (ops, options?) => Promise<Expression>;
BoxedOperatorDefinition.evalDimension()?
optional evalDimension: (ops, options) => Expression;
BoxedOperatorDefinition.compile()?
optional compile: (expr) => CompiledExpression;
EqHandlers
These handlers compare two expressions.
If only one of the handlers is provided, the other is derived from it.
Having both may be useful if comparing non-equality is faster than equality.
EqHandlers.eq()
eq: (a, b) => boolean;
EqHandlers.neq()
neq: (a, b) => boolean;
Hold
type Hold = "none" | "all" | "first" | "rest" | "last" | "most";
Latex Parsing and Serialization
LatexToken
type LatexToken = string | "<{>" | "<}>" | "<space>" | "<$>" | "<$$>";
A LatexToken is a token as returned by Parser.peek.
It can be one of the indicated tokens, or a string that starts with a `` for LaTeX commands, or a LaTeX character which includes digits, letters and punctuation.
LatexString
type LatexString = string;
A LatexString is a regular string of LaTeX, for example:
\frac{\pi}{2}
Delimiter
type Delimiter =
| "."
| ")"
| "("
| "]"
| "["
| "{"
| "}"
| "<"
| ">"
| "|"
| "||"
| "\lceil"
| "\rceil"
| "\lfloor"
| "\rfloor"
| "\llbracket"
| "\rrbracket";
Open and close delimiters that can be used with MatchfixEntry
record to define new LaTeX dictionary entries.
DelimiterScale
type DelimiterScale = "normal" | "scaled" | "big" | "none";
LibraryCategory
type LibraryCategory =
| "arithmetic"
| "calculus"
| "collections"
| "colors"
| "control-structures"
| "combinatorics"
| "core"
| "linear-algebra"
| "logic"
| "number-theory"
| "other"
| "physics"
| "polynomials"
| "relop"
| "statistics"
| "trigonometry"
| "units";
Precedence
type Precedence = number;
The precedence of an operator is a number that indicates the order in which operators are applied.
For example, in 1 + 2 * 3, the * operator has a higher precedence
than the + operator, so it is applied first.
The precedence ranges from 0 to 1000. The larger the number, the higher the precedence, the more "binding" the operator is.
Operator Precedence Table
| Precedence | Operators | Description |
|---|---|---|
| 880 | \lnot \neg ++ -- + - (prefix) | Prefix/postfix unary |
| 810 | ! ' !! ''' | Factorial, prime (postfix) |
| 800 | _ (subscript) | Subscript |
| 780 | \degree \prime | Degree, prime symbols |
| 740 | \% | Percent |
| 720 | / (inline division) | Inline division |
| 700 | ^ \overset \underset | Exponentiation, over/underscript |
| 650 | (invisible multiply) \cdot | Implicit multiplication |
| 600 | \div \frac | Division |
| 390 | \times * / | Multiplication |
| 350 | \cup \cap | Set union/intersection |
| 275 | + - (infix) | Addition, subtraction |
| 270 | \to \rightarrow \mapsto | Arrows |
| 265 | \setminus \smallsetminus : (range) | Set difference, range |
| 260 | := | Assignment |
| 255 | \ne | Not equal |
| 250 | \not\approxeq | Not approximately equal |
| 247 | \approx | Approximately |
| 245-246 | = < > \lt \gt \nless \ngtr | Equality, comparison |
| 241-244 | \le \leq \ge \geq >= | Less/greater or equal |
| 240 | \in \notin \subset \supset ... | Set membership/relations |
| 235 | \land \wedge \& | Logical AND |
| 232 | \veebar \barwedge (Xor, Nand, Nor) | Logical XOR, NAND, NOR |
| 230 | \lor \vee \parallel | Logical OR |
| 220 | \implies \Rightarrow \vdash \models | Implication, entailment |
| 219 | \iff \Leftrightarrow \equiv | Equivalence |
| 200 | \forall \exists \exists! | Quantifiers |
| 160 | \mid \vert (set builder) | Set builder notation |
| 19-20 | , ; \ldots | Sequence separators |
Key Relationships
- Comparisons bind tighter than logic:
x = 1 \lor y = 2parses as(x = 1) \lor (y = 2), notx = (1 \lor y) = 2 - AND binds tighter than OR:
a \land b \lor cparses as(a \land b) \lor c - Logic operators bind tighter than implication:
a \lor b \implies cparses as(a \lor b) \implies c
Some constants are defined below for common precedence values.
Note: MathML defines some operator precedence, but it has some issues and inconsistencies. However, whenever possible we adopted the MathML precedence.
The JavaScript operator precedence is documented here.
Terminator
type Terminator = {
minPrec: Precedence;
condition: (parser) => boolean;
};
This indicates a condition under which parsing should stop:
- an operator of a precedence higher than specified has been encountered
- the last token has been reached
- or if a condition is provided, the condition returns true
ParseHandler
type ParseHandler =
| ExpressionParseHandler
| SymbolParseHandler
| FunctionParseHandler
| EnvironmentParseHandler
| PostfixParseHandler
| InfixParseHandler
| MatchfixParseHandler;
Custom parsing handler.
When this handler is invoked the parser points right after the LaTeX fragment that triggered it.
Tokens can be consumed with parser.nextToken() and other parser methods
such as parser.parseGroup(), parser.parseOptionalGroup(), etc...
If it was in an infix or postfix context, lhs will represent the
left-hand side argument. In a prefix or matchfix context, lhs is null.
In a superfix (^) or subfix (_) context (that is if the first token of
the trigger is ^ or _), lhs is ["Superscript", lhs, rhs]
and ["Subscript", lhs, rhs], respectively.
The handler should return null if the tokens could not be parsed
(didn't match the syntax that was expected), or the matching expression
otherwise.
If the tokens were parsed but should be ignored, the handler should
return Nothing.
ExpressionParseHandler()
type ExpressionParseHandler = (parser, until?) => MathJsonExpression | null;
PrefixParseHandler()
type PrefixParseHandler = (parser, until?) => MathJsonExpression | null;
SymbolParseHandler()
type SymbolParseHandler = (parser, until?) => MathJsonExpression | null;
FunctionParseHandler()
type FunctionParseHandler = (parser, until?) => MathJsonExpression | null;
EnvironmentParseHandler()
type EnvironmentParseHandler = (parser, until?) => MathJsonExpression | null;
PostfixParseHandler()
type PostfixParseHandler = (parser, lhs, until?) => MathJsonExpression | null;
InfixParseHandler()
type InfixParseHandler = (parser, lhs, until) => MathJsonExpression | null;
MatchfixParseHandler()
type MatchfixParseHandler = (parser, body) => MathJsonExpression | null;
LatexArgumentType
type LatexArgumentType =
| "{expression}"
| "[expression]"
| "{text}"
| "[text]"
| "{unit}"
| "[unit]"
| "{glue}"
| "[glue]"
| "{string}"
| "[string]"
| "{color}"
| "[color]";
Trigger
type Trigger = {
latexTrigger: LatexString | LatexToken[];
symbolTrigger: MathJsonSymbol;
};
A trigger is the set of tokens that will make an entry in the
LaTeX dictionary eligible to parse the stream and generate an expression.
If the trigger matches, the parse handler is called, if available.
The trigger can be specified either as a LaTeX string (latexTrigger) or
as an symbol (symbolTrigger). A symbol match several
LaTeX expressions that are equivalent, for example \operatorname{gcd} or
\mathbin{gcd}, match the "gcd" symbol
matchfix operators use openTrigger and closeTrigger instead.
BaseEntry
type BaseEntry = {
name: MathJsonSymbol;
serialize: LatexString | SerializeHandler;
};
Maps a string of LaTeX tokens to a function or symbol and vice-versa.
DefaultEntry
type DefaultEntry = BaseEntry & Trigger & {
parse: | MathJsonExpression
| ExpressionParseHandler;
};
ExpressionEntry
type ExpressionEntry = BaseEntry & Trigger & {
kind: "expression";
parse: | MathJsonExpression
| ExpressionParseHandler;
precedence: Precedence;
};
MatchfixEntry
type MatchfixEntry = BaseEntry & {
kind: "matchfix";
openTrigger: Delimiter | LatexToken[];
closeTrigger: Delimiter | LatexToken[];
parse: MatchfixParseHandler;
};
MatchfixEntry.openTrigger
openTrigger: Delimiter | LatexToken[];
If kind is 'matchfix': the openTrigger and closeTrigger
properties are required.
MatchfixEntry.parse?
optional parse: MatchfixParseHandler;
When invoked, the parser is pointing after the close delimiter. The argument of the handler is the body, i.e. the content between the open delimiter and the close delimiter.
InfixEntry
type InfixEntry = BaseEntry & Trigger & {
kind: "infix";
associativity: "right" | "left" | "none" | "any";
precedence: Precedence;
parse: string | InfixParseHandler;
};
InfixEntry.kind
kind: "infix";
Infix position, with an operand before and an operand after: a ⊛ b.
Example: +, \times.
InfixEntry.associativity?
optional associativity: "right" | "left" | "none" | "any";
-
none: a ? b ? c -> syntax error -
any: a + b + c -> +(a, b, c) -
left: a / b / c -> /(/(a, b), c) -
right: a = b = c -> =(a, =(b, c)) -
any-associative operators have an unlimited number of arguments -
left,rightornoneassociative operators have two arguments
PostfixEntry
type PostfixEntry = BaseEntry & Trigger & {
kind: "postfix";
precedence: Precedence;
parse: string | PostfixParseHandler;
};
PostfixEntry.kind
kind: "postfix";
Postfix position, with an operand before: a ⊛
Example: !.
PrefixEntry
type PrefixEntry = BaseEntry & Trigger & {
kind: "prefix";
precedence: Precedence;
parse: string | PrefixParseHandler;
};
PrefixEntry.kind
kind: "prefix";
Prefix position, with an operand after: ⊛ a
Example: -, \not.
EnvironmentEntry
type EnvironmentEntry = BaseEntry & {
kind: "environment";
parse: EnvironmentParseHandler;
symbolTrigger: MathJsonSymbol;
};
A LaTeX dictionary entry for an environment, that is a LaTeX
construct using \begin{...}...\end{...}.
SymbolEntry
type SymbolEntry = BaseEntry & Trigger & {
kind: "symbol";
precedence: Precedence;
parse: | MathJsonExpression
| SymbolParseHandler;
};
SymbolEntry.precedence?
optional precedence: Precedence;
Used for appropriate wrapping (i.e. when to surround it with parens)
FunctionEntry
type FunctionEntry = BaseEntry & Trigger & {
kind: "function";
parse: | MathJsonExpression
| FunctionParseHandler;
arguments: "enclosure" | "implicit";
};
A function is a symbol followed by:
- some postfix operators such as
\prime - an optional list of arguments in an enclosure (parentheses)
For more complex situations, for example implicit arguments or
inverse functions postfix (i.e. ^-1), use a custom parse handler with a
entry of kind expression.
FunctionEntry.arguments?
optional arguments: "enclosure" | "implicit";
How arguments are parsed:
'enclosure'(default): arguments must be enclosed in parentheses, e.g.\max(a, b).'implicit': arguments can be provided with or without parentheses, e.g.\det Ais parsed as\det(A). Bare arguments are parsed at multiplication precedence, so\det 2A + 1is parsed as\det(2A) + 1.
LatexDictionaryEntry
type LatexDictionaryEntry = OneOf<[
| ExpressionEntry
| MatchfixEntry
| InfixEntry
| PostfixEntry
| PrefixEntry
| SymbolEntry
| FunctionEntry
| EnvironmentEntry
| DefaultEntry]>;
A dictionary entry is a record that maps a LaTeX token or string of tokens ( a trigger) to a MathJSON expression or to a parsing handler.
Set the ComputeEngine.latexDictionary property to an array of
dictionary entries to define custom LaTeX parsing and serialization.
ParseLatexOptions
type ParseLatexOptions = NumberFormat & {
strict: boolean;
skipSpace: boolean;
parseNumbers: "auto" | "rational" | "decimal" | "never";
getSymbolType: (symbol) =>
| BoxedType
| TypeString;
hasSubscriptEvaluate: (symbol) => boolean;
parseUnexpectedToken: (lhs, parser) => MathJsonExpression | null;
preserveLatex: boolean;
quantifierScope: "tight" | "loose";
timeDerivativeVariable: string;
};
The LaTeX parsing options can be used with the ce.parse() method.
ParseLatexOptions.strict
strict: boolean;
Controls the strictness of LaTeX parsing:
true: Strict LaTeX syntax required (e.g.,\sin{x},x^{n+1})false: Accept relaxed Math-ASCII/Typst-like syntax in addition to LaTeX (e.g.,sin(x),x^(n+1))
Default: true
ParseLatexOptions.skipSpace
skipSpace: boolean;
If true, ignore space characters in math mode.
Default: true
ParseLatexOptions.parseNumbers
parseNumbers: "auto" | "rational" | "decimal" | "never";
When parsing a decimal number, e.g. 3.1415:
"auto"or"decimal": if a decimal number, parse it as an approximate decimal number with a whole part and a fractional part"rational": if a decimal number, parse it as an exact rational number with a numerator and a denominator. If not a decimal number, parse it as a regular number."never": do not parse numbers, instead return each token making up the number (minus sign, digits, decimal marker, etc...).
Note: if the number includes repeating digits (e.g. 1.33(333)),
it will be parsed as a decimal number even if this setting is "rational".
Default: "auto"
ParseLatexOptions.getSymbolType()
getSymbolType: (symbol) =>
| BoxedType
| TypeString;
This handler is invoked when the parser encounters a that has not yet been declared.
The symbol argument is a valid symbol.
ParseLatexOptions.hasSubscriptEvaluate()?
optional hasSubscriptEvaluate: (symbol) => boolean;
This handler is invoked when the parser needs to determine if a symbol
has a custom subscript evaluation handler. If true, subscripts on this
symbol will be kept as Subscript expressions rather than being absorbed
into a compound symbol name.
ParseLatexOptions.parseUnexpectedToken()
parseUnexpectedToken: (lhs, parser) => MathJsonExpression | null;
This handler is invoked when the parser encounters an unexpected token.
The lhs argument is the left-hand side of the token, if any.
The handler can access the unexpected token with parser.peek. If
it is a token that should be recognized, the handler can consume it
by calling parser.nextToken().
The handler should return an expression or null if the token is not
recognized.
ParseLatexOptions.preserveLatex
preserveLatex: boolean;
If true, the expression will be decorated with the LaTeX fragments corresponding to each elements of the expression.
The top-level expression, that is the one returned by parse(), will
include the verbatim LaTeX input that was parsed. The sub-expressions
may contain a slightly different LaTeX, for example with consecutive spaces
replaced by one, with comments removed and with some low-level LaTeX
commands replaced, for example \egroup and \bgroup.
Default: false
ParseLatexOptions.quantifierScope
quantifierScope: "tight" | "loose";
Controls how quantifier scope is determined when parsing expressions
like \forall x. P(x) \rightarrow Q(x).
-
"tight": The quantifier binds only to the immediately following well-formed formula, stopping at logical connectives (\rightarrow,\implies,\land,\lor, etc.). This follows standard First-Order Logic conventions. Use explicit parentheses for wider scope:\forall x. (P(x) \rightarrow Q(x)). -
"loose": The quantifier scope extends to the end of the expression or until a lower-precedence operator is encountered.
Default: "tight"
Example
// With "tight" (default):
// \forall x. P(x) \rightarrow Q(x)
// parses as: (∀x. P(x)) → Q(x)
// With "loose":
// \forall x. P(x) \rightarrow Q(x)
// parses as: ∀x. (P(x) → Q(x))
ParseLatexOptions.timeDerivativeVariable
timeDerivativeVariable: string;
The variable used for time derivatives in Newton notation
(\dot{x}, \ddot{x}, etc.).
When parsing \dot{x}, it will be interpreted as ["D", "x", timeDerivativeVariable].
Default: "t"
Parser
An instance of Parser is provided to the parse handlers of custom
LaTeX dictionary entries.
Parser.options
readonly options: Readonly<ParseLatexOptions>;
Parser.inQuantifierScope
readonly inQuantifierScope: boolean;
True if currently parsing inside a quantifier body (ForAll, Exists, etc.)
Parser.atEnd
readonly atEnd: boolean;
True if the last token has been reached.
Consider also atTerminator().
Parser.atBoundary
Parser.hasSubscriptEvaluate()
hasSubscriptEvaluate(id): boolean
Check if a symbol has a custom subscript evaluation handler.
####### id
string
Parser.pushSymbolTable()
pushSymbolTable(): void
Parser.popSymbolTable()
popSymbolTable(): void
Parser.enterQuantifierScope()
enterQuantifierScope(): void
Enter a quantifier scope for parsing the body of ForAll, Exists, etc.
Parser.atTerminator()
atTerminator(t): boolean
Return true if the terminator condition is met or if the last token has been reached.
####### t
Parser.latex()
latex(start, end?): string
Return a string representation of the expression
between start and end (default: the whole expression)
####### start
number
####### end?
number
Parser.error()
error(code, fromToken): MathJsonExpression
Return an error expression with the specified code and arguments
####### code
string | [string, ...MathJsonExpression[]]
####### fromToken
number
Parser.skipSpace()
skipSpace(): boolean
If there are any space, advance the index until a non-space is encountered
Parser.skipVisualSpace()
skipVisualSpace(): void
Skip over "visual space" which
includes space tokens, empty groups {}, and commands such as \, and \!
Parser.match()
match(token): boolean
If the next token matches the target advance and return true. Otherwise return false
####### token
string
Parser.matchAll()
matchAll(tokens): boolean
Return true if the next tokens match the argument, an array of tokens, or null otherwise
####### tokens
string[]
Parser.matchAny()
matchAny(tokens): string
Return the next token if it matches any of the token in the argument or null otherwise
####### tokens
string[]
Parser.parseChar()
parseChar(): string
If the next token is a character, return it and advance the index
This includes plain characters (e.g. 'a', '+'...), characters
defined in hex (^^ and ^^^^), the \char and \unicode command.
Parser.parseGroup()
parseGroup(): MathJsonExpression
Parse an expression in a LaTeX group enclosed in curly brackets {}.
These are often used as arguments to LaTeX commands, for example
\frac{1}{2}.
Return null if none was found
Return Nothing if an empty group {} was found
Parser.parseToken()
parseToken(): MathJsonExpression
Some LaTeX commands (but not all) can accept arguments as single
tokens (i.e. without braces), for example ^2, \sqrt3 or \frac12
This argument will usually be a single token, but can be a sequence of
tokens (e.g. \sqrt\frac12 or \sqrt\operatorname{speed}).
The following tokens are excluded from consideration in order to fail
early when encountering a likely syntax error, for example x^(2)
instead of x^{2}. With ( in the list of excluded tokens, the
match will fail and the error can be recovered.
The excluded tokens include !"#$%&(),/;:?@[]|~", \left, \bigl, etc...
Parser.parseOptionalGroup()
parseOptionalGroup(): MathJsonExpression
Parse an expression enclosed in a LaTeX optional group enclosed in square brackets [].
Return null if none was found.
Parser.parseEnclosure()
parseEnclosure(): MathJsonExpression
Parse an enclosure (open paren/close paren, etc..) and return the expression inside the enclosure
Parser.parseStringGroup()
parseStringGroup(optional?): string
Some LaTeX commands have arguments that are not interpreted as
expressions, but as strings. For example, \begin{array}{ccc} (both
array and ccc are strings), \color{red} or \operatorname{lim sup}.
If the next token is the start of a group ({), return the content
of the group as a string. This may include white space, and it may need
to be trimmed at the start and end of the string.
LaTeX commands are typically not allowed inside a string group (for example,
\alpha would result in an error), but we do not enforce this.
If optional is true, this should be an optional group in square brackets
otherwise it is a regular group in braces.
####### optional?
boolean
Parser.parseSymbol()
parseSymbol(until?): MathJsonExpression
A symbol can be:
- a single-letter symbol:
x - a single LaTeX command:
\pi - a multi-letter symbol:
\operatorname{speed}
####### until?
Partial<Terminator>
Parser.parseTabular()
parseTabular(): MathJsonExpression[][]
Parse an expression in a tabular format, where rows are separated by \\
and columns by &.
Return rows of sparse columns: empty rows are indicated with Nothing,
and empty cells are also indicated with Nothing.
Parser.parseArguments()
parseArguments(kind?, until?): readonly MathJsonExpression[]
Parse an argument list, for example: (12, x+1) or \left(x\right)
- 'enclosure' : will look for arguments inside an enclosure (an open/close fence) (default)
- 'implicit': either an expression inside a pair of
(), or just a primary (i.e. we interpret\cos x + 1as\cos(x) + 1)
Return an array of expressions, one for each argument, or null if no
argument was found.
####### kind?
"enclosure" | "implicit"
####### until?
Parser.parsePostfixOperator()
parsePostfixOperator(lhs, until?): MathJsonExpression
Parse a postfix operator, such as ' or !.
Prefix, infix and matchfix operators are handled by parseExpression()
####### lhs
####### until?
Partial<Terminator>
Parser.parseExpression()
parseExpression(until?): MathJsonExpression
Parse an expression:
<expression> ::=
| <primary> ( <infix-op> <expression> )?
| <prefix-op> <expression>
<primary> :=
(<number> | <symbol> | <function-call> | <matchfix-expr>)
(<subsup> | <postfix-operator>)*
<matchfix-expr> :=
<matchfix-op-open> <expression> <matchfix-op-close>
<function-call> ::=
| <function><matchfix-op-group-open><expression>[',' <expression>]<matchfix-op-group-close>
This is the top-level parsing entry point.
Stop when an operator of precedence less than until.minPrec
or the sequence of tokens until.tokens is encountered
until is { minPrec:0 } by default.
####### until?
Partial<Terminator>
Parser.addBoundary()
addBoundary(boundary): void
Boundaries are used to detect the end of an expression.
They are used for unusual syntactic constructs, for example
\int \sin x dx where the dx is not an argument to the \sin
function, but a boundary of the integral.
They are also useful when handling syntax errors and recovery.
For example, \begin{bmatrix} 1 & 2 { \end{bmatrix} has an
extraneous {, but the parser will attempt to recover and continue
parsing when it encounters the \end{bmatrix} boundary.
####### boundary
string[]
Parser.removeBoundary()
removeBoundary(): void
Parser.matchBoundary()
matchBoundary(): boolean
Parser.boundaryError()
boundaryError(msg): MathJsonExpression
####### msg
string | [string, ...MathJsonExpression[]]
SerializeLatexOptions
type SerializeLatexOptions = NumberSerializationFormat & {
prettify: boolean;
materialization: boolean | number | [number, number];
invisibleMultiply: LatexString;
invisiblePlus: LatexString;
multiply: LatexString;
missingSymbol: LatexString;
applyFunctionStyle: (expr, level) => DelimiterScale;
groupStyle: (expr, level) => DelimiterScale;
rootStyle: (expr, level) => "radical" | "quotient" | "solidus";
fractionStyle: (expr, level) =>
| "quotient"
| "block-quotient"
| "inline-quotient"
| "inline-solidus"
| "nice-solidus"
| "reciprocal"
| "factor";
logicStyle: (expr, level) => "word" | "boolean" | "uppercase-word" | "punctuation";
powerStyle: (expr, level) => "root" | "solidus" | "quotient";
numericSetStyle: (expr, level) => "compact" | "regular" | "interval" | "set-builder";
dmsFormat: boolean;
angleNormalization: "none" | "0...360" | "-180...180";
};
The LaTeX serialization options can used with the expr.toLatex() method.
SerializeLatexOptions.prettify
prettify: boolean;
If true, prettify the LaTeX output.
For example, render \frac{a}{b}\frac{c}{d} as \frac{ac}{bd}
SerializeLatexOptions.materialization
materialization: boolean | number | [number, number];
Controls the materialization of the lazy collections.
- If
true, lazy collections are materialized, i.e. it is rendered as a LaTeX expression with all its elements. - If
false, the expression is not materialized, i.e. it is rendered as a LaTeX command with its arguments. - If a number is provided, it is the maximum number of elements that will be materialized.
- If a pair of numbers is provided, it is the number of elements of the head and the tail that will be materialized, respectively.
SerializeLatexOptions.invisibleMultiply
invisibleMultiply: LatexString;
LaTeX string used to render an invisible multiply, e.g. in '2x'.
If empty, both operands are concatenated, i.e. 2x.
Use \cdot to insert a \cdot operator between them, i.e. 2 \cdot x.
Empty by default.
SerializeLatexOptions.invisiblePlus
invisiblePlus: LatexString;
LaTeX string used to render mixed numbers e.g. '1 3/4'.
Leave it empty to join the main number and the fraction, i.e. render it
as 1\frac{3}{4}.
Use + to insert an explicit + operator between them,
i.e. 1+\frac{3}{4}
Empty by default.
SerializeLatexOptions.multiply
multiply: LatexString;
LaTeX string used to render an explicit multiply operator.
For example, \times, \cdot, etc...
Default: \times
SerializeLatexOptions.missingSymbol
missingSymbol: LatexString;
Serialize the expression ["Error", "'missing'"], with this LaTeX string
SerializeLatexOptions.dmsFormat?
optional dmsFormat: boolean;
When true, serialize angle quantities in degrees-minutes-seconds format. When false (default), use decimal degrees.
Default
false
Example
const ce = new ComputeEngine();
const angle = ce.box(['Quantity', 9.5, 'deg']);
// DMS format
angle.latex({ dmsFormat: true }); // "9°30'"
// Decimal format (default)
angle.latex({ dmsFormat: false }); // "9.5°"
// Full DMS notation
ce.box(['Quantity', 9.504166, 'deg'])
.latex({ dmsFormat: true }); // "9°30'15\""
SerializeLatexOptions.angleNormalization?
optional angleNormalization: "none" | "0...360" | "-180...180";
Normalize angles to a specific range during serialization. Useful for geographic coordinates and rotations.
Default
'none'
Example
const ce = new ComputeEngine();
// No normalization (show exact value)
ce.box(['Degrees', 370])
.latex({ angleNormalization: 'none' }); // "370°"
// Normalize to [0, 360) - useful for bearings
ce.box(['Degrees', 370])
.latex({ angleNormalization: '0...360' }); // "10°"
ce.box(['Degrees', -45])
.latex({ angleNormalization: '0...360' }); // "315°"
// Normalize to [-180, 180] - useful for longitude
ce.box(['Degrees', 190])
.latex({ angleNormalization: '-180...180' }); // "-170°"
// Combine with DMS format
ce.box(['Degrees', 370])
.latex({
dmsFormat: true,
angleNormalization: '0...360'
}); // "10°0'0\""
Serializer
An instance of Serializer is provided to the serialize handlers of custom
LaTeX dictionary entries.
Serializer.options
readonly options: Required<SerializeLatexOptions>;
Serializer.dictionary
readonly dictionary: SerializerDictionary;
Serializer.level
level: number;
"depth" of the expression:
- 0 for the root
- 1 for a subexpression of the root
- 2 for subexpressions of the subexpressions of the root
- etc...
This allows the serialized LaTeX to vary depending on the depth of the expression.
For example use \Bigl( for the top level, and \bigl( or ( for others.
Serializer.serialize()
serialize: (expr) => string;
Output a LaTeX string representing the expression
Serializer.wrap()
wrap: (expr, prec?) => string;
Add a group fence around the expression if it is
an operator of precedence less than or equal to prec.
Serializer.groupStyle()
groupStyle: (expr, level) => DelimiterScale;
Serializer.rootStyle()
rootStyle: (expr, level) => "radical" | "quotient" | "solidus";
Serializer.fractionStyle()
fractionStyle: (expr, level) =>
| "quotient"
| "block-quotient"
| "inline-quotient"
| "inline-solidus"
| "nice-solidus"
| "reciprocal"
| "factor";
Serializer.logicStyle()
logicStyle: (expr, level) => "boolean" | "word" | "uppercase-word" | "punctuation";
Serializer.powerStyle()
powerStyle: (expr, level) => "quotient" | "solidus" | "root";
Serializer.numericSetStyle()
numericSetStyle: (expr, level) => "compact" | "regular" | "interval" | "set-builder";
Serializer.serializeFunction()
serializeFunction(expr, def?): string
####### expr
####### def?
SerializerDictionaryEntry
Serializer.wrapString()
wrapString(s, style, delimiters?): string
Output s surrounded by delimiters.
If delimiters is not specified, use ()
####### s
string
####### style
####### delimiters?
string
Serializer.wrapArguments()
wrapArguments(expr): string
A string with the arguments of expr fenced appropriately and separated by commas.
####### expr
Serializer.wrapShort()
wrapShort(expr): string
Add a group fence around the expression if it is short (not a function)
####### expr
SerializeHandler()
type SerializeHandler = (serializer, expr) => string;
The serialize handler of a custom LaTeX dictionary entry can be
a function of this type.
Numerics
ExactNumericValueData
type ExactNumericValueData = {
rational: Rational;
radical: number;
};
The value is equal to (decimal * rational * sqrt(radical)) + im * i
NumericValueData
type NumericValueData = {
re: Decimal | number;
im: number;
};
NumericValueFactory()
type NumericValueFactory = (data) => NumericValue;
abstract NumericValue
new NumericValue()
new NumericValue(): NumericValue
NumericValue.im
im: number;
The imaginary part of this numeric value.
Can be negative, zero or positive.
NumericValue.type
NumericValue.isExact
True if numeric value is the product of a rational and the square root of an integer.
This includes: 3/4√5, -2, √2, etc...
But it doesn't include 0.5, 3.141592, etc...
NumericValue.asExact
If isExact(), returns an ExactNumericValue, otherwise returns undefined.
NumericValue.bignumRe
bignum version of .re, if available
NumericValue.bignumIm
NumericValue.numerator
NumericValue.denominator
NumericValue.isNaN
NumericValue.isPositiveInfinity
NumericValue.isNegativeInfinity
NumericValue.isComplexInfinity
NumericValue.isZero
NumericValue.isOne
NumericValue.isNegativeOne
NumericValue.isZeroWithTolerance()
isZeroWithTolerance(_tolerance): boolean
####### _tolerance
number | Decimal
NumericValue.neg()
abstract neg(): NumericValue
NumericValue.inv()
abstract inv(): NumericValue
NumericValue.pow()
abstract pow(n): NumericValue
####### n
number | NumericValue | {
re: number;
im: number;
}
NumericValue.sqrt()
abstract sqrt(): NumericValue
NumericValue.abs()
abstract abs(): NumericValue
NumericValue.exp()
abstract exp(): NumericValue
NumericValue.floor()
abstract floor(): NumericValue
NumericValue.ceil()
abstract ceil(): NumericValue
NumericValue.round()
abstract round(): NumericValue
NumericValue.valueOf()
valueOf(): string | number
Object.valueOf(): returns a primitive value, preferably a JavaScript number over a string, even if at the expense of precision
NumericValue.[toPrimitive]()
toPrimitive: string | number
Object.toPrimitive()
####### hint
"string" | "number" | "default"
NumericValue.print()
print(): void
Rational
type Rational =
| [SmallInteger, SmallInteger]
| [bigint, bigint];
A rational number is a number that can be expressed as the quotient or fraction p/q of two integers, a numerator p and a non-zero denominator q.
A rational can either be represented as a pair of small integers or a pair of big integers.
BigNum
type BigNum = Decimal;
BigNumFactory()
type BigNumFactory = (value) => Decimal;
IBigNum
IBigNum._BIGNUM_NAN
readonly _BIGNUM_NAN: Decimal;
IBigNum._BIGNUM_ZERO
readonly _BIGNUM_ZERO: Decimal;
IBigNum._BIGNUM_ONE
readonly _BIGNUM_ONE: Decimal;
IBigNum._BIGNUM_TWO
readonly _BIGNUM_TWO: Decimal;
IBigNum._BIGNUM_HALF
readonly _BIGNUM_HALF: Decimal;
IBigNum._BIGNUM_PI
readonly _BIGNUM_PI: Decimal;
IBigNum._BIGNUM_NEGATIVE_ONE
readonly _BIGNUM_NEGATIVE_ONE: Decimal;
Sign
type Sign =
| "zero"
| "positive"
| "negative"
| "non-negative"
| "non-positive"
| "not-zero"
| "unsigned";
OEIS
OEISSequenceInfo
Result from an OEIS lookup operation.
OEISOptions
Options for OEIS operations.
OEISOptions.maxResults?
optional maxResults: number;
Maximum number of results to return for lookups (default: 5)
Other
SymbolTable
type SymbolTable = {
parent: SymbolTable | null;
ids: {};
};
BoxedRuleSet
type BoxedRuleSet = KernelBoxedRuleSet<Expression, ComputeEngine>;
Collection of boxed rules.
EvalContext
type EvalContext = KernelEvalContext<Expression, BoxedDefinition>;
Evaluation context specialized to this engine/runtime model.
DictionaryInterface
Interface for dictionary-like structures.
Use isDictionary() to check if an expression is a dictionary.
DictionaryInterface.keys
DictionaryInterface.entries
DictionaryInterface.values
SemiBoxedExpression
type SemiBoxedExpression = ExpressionInput;
Deprecated
Use ExpressionInput instead.
Serialization
NumberFormat
type NumberFormat = {
positiveInfinity: LatexString;
negativeInfinity: LatexString;
notANumber: LatexString;
imaginaryUnit: LatexString;
decimalSeparator: LatexString;
digitGroupSeparator: | LatexString
| [LatexString, LatexString];
digitGroup: "lakh" | number | [number | "lakh", number];
exponentProduct: LatexString;
beginExponentMarker: LatexString;
endExponentMarker: LatexString;
truncationMarker: LatexString;
repeatingDecimal: "auto" | "vinculum" | "dots" | "parentheses" | "arc" | "none";
};
These options control how numbers are parsed and serialized.
NumberSerializationFormat
type NumberSerializationFormat = NumberFormat & {
fractionalDigits: "auto" | "max" | number;
notation: "auto" | "engineering" | "scientific" | "adaptiveScientific";
avoidExponentsInRange: undefined | null | [number, number];
};
NumberSerializationFormat.fractionalDigits
fractionalDigits: "auto" | "max" | number;
The maximum number of significant digits in serialized numbers.
"max": all availabe digits are serialized."auto": use the same precision as the compute engine.
Default: "auto"
JsonSerializationOptions
type JsonSerializationOptions = {
prettify: boolean;
exclude: string[];
shorthands: ("all" | "number" | "symbol" | "function" | "string" | "dictionary")[];
metadata: ("all" | "wikidata" | "latex")[];
repeatingDecimal: boolean;
fractionalDigits: "auto" | "max" | number;
};
Options to control serialization to MathJSON when using
Expression.toMathJson().
Tensors
DataTypeMap
type DataTypeMap = {
float64: number;
float32: number;
int32: number;
uint8: number;
complex128: Complex;
complex64: Complex;
bool: boolean;
expression: Expression;
};
Map of TensorDataType to JavaScript type.
TensorData
A record representing the type, shape and data of a tensor.
Extended by
TensorData.dtype
dtype: DT;
TensorData.shape
shape: number[];
TensorData.rank?
optional rank: number;
TensorData.data
data: DataTypeMap[DT][];
TensorField
TensorField.one
readonly one: T;
TensorField.zero
readonly zero: T;
TensorField.nan
readonly nan: T;
TensorField.cast()
cast(x, dtype)
cast(x, dtype): number
####### x
T
####### dtype
"float64"
cast(x, dtype)
cast(x, dtype): number
####### x
T
####### dtype
"float32"
cast(x, dtype)
cast(x, dtype): number
####### x
T
####### dtype
"int32"
cast(x, dtype)
cast(x, dtype): number
####### x
T
####### dtype
"uint8"
cast(x, dtype)
cast(x, dtype): any
####### x
T
####### dtype
"complex128"
cast(x, dtype)
cast(x, dtype): any
####### x
T
####### dtype
"complex64"
cast(x, dtype)
cast(x, dtype): boolean
####### x
T
####### dtype
"bool"
cast(x, dtype)
cast(x, dtype): Expression
####### x
T
####### dtype
"expression"
cast(x, dtype)
cast(x, dtype): number[]
####### x
T[]
####### dtype
"float64"
cast(x, dtype)
cast(x, dtype): number[]
####### x
T[]
####### dtype
"float32"
cast(x, dtype)
cast(x, dtype): number[]
####### x
T[]
####### dtype
"int32"
cast(x, dtype)
cast(x, dtype): number[]
####### x
T[]
####### dtype
"uint8"
cast(x, dtype)
cast(x, dtype): Complex[]
####### x
T[]
####### dtype
"complex128"
cast(x, dtype)
cast(x, dtype): Complex[]
####### x
T[]
####### dtype
"complex64"
cast(x, dtype)
cast(x, dtype): boolean[]
####### x
T[]
####### dtype
"bool"
cast(x, dtype)
cast(x, dtype): Expression[]
####### x
T[]
####### dtype
"expression"
cast(x, dtype)
cast(x, dtype): any
####### x
T | T[]
####### dtype
keyof DataTypeMap
Tensor
Extends
TensorData<DT>
Tensor.dtype
dtype: DT;
Tensor.shape
shape: number[];
Tensor.rank
rank: number;
Tensor.data
data: DataTypeMap[DT][];
Tensor.field
readonly field: TensorField<DT>;
Tensor.expression
readonly expression: Expression;
Tensor.array
readonly array: NestedArray<DataTypeMap[DT]>;
Tensor.isSquare
readonly isSquare: boolean;
Tensor.isSymmetric
readonly isSymmetric: boolean;
Tensor.isSkewSymmetric
readonly isSkewSymmetric: boolean;
Tensor.isDiagonal
readonly isDiagonal: boolean;
Tensor.isUpperTriangular
readonly isUpperTriangular: boolean;
Tensor.isLowerTriangular
readonly isLowerTriangular: boolean;
Tensor.isTriangular
readonly isTriangular: boolean;
Tensor.isIdentity
readonly isIdentity: boolean;
Tensor.isZero
readonly isZero: boolean;
Tensor.diagonal()
diagonal(axis1?, axis2?): DataTypeMap[DT][]
####### axis1?
number
####### axis2?
number
Tensor.trace()
trace(axis1?, axis2?): Tensor<DT> | DataTypeMap[DT]
####### axis1?
number
####### axis2?
number
Tensor.flatten()
flatten(): DataTypeMap[DT][]
Tensor.conjugateTranspose()
conjugateTranspose(axis1?, axis2?): Tensor<DT>
####### axis1?
number
####### axis2?
number
Tensor.determinant()
determinant(): DataTypeMap[DT]
Tensor.inverse()
inverse(): Tensor<DT>
Tensor.pseudoInverse()
pseudoInverse(): Tensor<DT>
Tensor.adjugateMatrix()
adjugateMatrix(): Tensor<DT>
Tensor.map1()
map1(fn, scalar): Tensor<DT>
####### fn
(lhs, rhs) => DataTypeMap[DT]
####### scalar
DataTypeMap[DT]
Type
BoxedType
new BoxedType()
new BoxedType(type, typeResolver?): BoxedType
####### type
string | AlgebraicType | NegationType | CollectionType | ListType | SetType | RecordType | DictionaryType | TupleType | SymbolType | ExpressionType | NumericType | FunctionSignature | ValueType | TypeReference
####### typeResolver?
BoxedType.unknown
static unknown: BoxedType;
BoxedType.number
static number: BoxedType;
BoxedType.non_finite_number
static non_finite_number: BoxedType;
BoxedType.finite_number
static finite_number: BoxedType;
BoxedType.finite_integer
static finite_integer: BoxedType;
BoxedType.finite_real
static finite_real: BoxedType;
BoxedType.string
static string: BoxedType;
BoxedType.dictionary
static dictionary: BoxedType;
BoxedType.setNumber
static setNumber: BoxedType;
BoxedType.setComplex
static setComplex: BoxedType;
BoxedType.setImaginary
static setImaginary: BoxedType;
BoxedType.setReal
static setReal: BoxedType;
BoxedType.setRational
static setRational: BoxedType;
BoxedType.setFiniteInteger
static setFiniteInteger: BoxedType;
BoxedType.setInteger
static setInteger: BoxedType;
BoxedType.type
type: Type;
BoxedType.isUnknown
BoxedType.toString()
toString(): string
BoxedType.toJSON()
toJSON(): string
BoxedType.valueOf()
valueOf(): string
MathJSON
MathJsonAttributes
type MathJsonAttributes = {
comment: string;
documentation: string;
latex: string;
wikidata: string;
wikibase: string;
openmathSymbol: string;
openmathCd: string;
sourceUrl: string;
sourceContent: string;
sourceOffsets: [number, number];
};
The following properties can be added to any MathJSON expression to provide additional information about the expression.
MathJsonSymbol
type MathJsonSymbol = string;
MathJsonNumberObject
type MathJsonNumberObject = {
num: "NaN" | "-Infinity" | "+Infinity" | string;
} & MathJsonAttributes;
A MathJSON numeric quantity.
The num string is made of:
- an optional
-minus sign - a string of decimal digits
- an optional fraction part (a
.decimal marker followed by decimal digits) - an optional repeating decimal pattern: a string of digits enclosed in parentheses
- an optional exponent part (a
eorEexponent marker followed by an optional-minus sign, followed by a string of digits)
It can also consist of the string NaN, -Infinity or +Infinity to
represent these respective values.
A MathJSON number may contain more digits or an exponent with a greater range than can be represented in an IEEE 64-bit floating-point.
For example:
-12.340.234e-561.(3)123456789123456789.123(4567)e999
MathJsonSymbolObject
type MathJsonSymbolObject = {
sym: MathJsonSymbol;
} & MathJsonAttributes;
MathJsonStringObject
type MathJsonStringObject = {
str: string;
} & MathJsonAttributes;
MathJsonFunctionObject
type MathJsonFunctionObject = {
fn: [MathJsonSymbol, ...MathJsonExpression[]];
} & MathJsonAttributes;
DictionaryValue
type DictionaryValue =
| boolean
| number
| string
| ExpressionObject
| ReadonlyArray<DictionaryValue>;
MathJsonDictionaryObject
type MathJsonDictionaryObject = {
dict: Record<string, DictionaryValue>;
} & MathJsonAttributes;
ExpressionObject
type ExpressionObject =
| MathJsonNumberObject
| MathJsonStringObject
| MathJsonSymbolObject
| MathJsonFunctionObject
| MathJsonDictionaryObject;
MathJsonExpression
type MathJsonExpression =
| ExpressionObject
| number
| MathJsonSymbol
| string
| readonly [MathJsonSymbol, ...MathJsonExpression[]];
A MathJSON expression is a recursive data structure.
The leaf nodes of an expression are numbers, strings and symbols. The dictionary and function nodes can contain expressions themselves.
Type
PrimitiveType
type PrimitiveType =
| NumericPrimitiveType
| "collection"
| "indexed_collection"
| "list"
| "set"
| "dictionary"
| "record"
| "dictionary"
| "tuple"
| "value"
| "scalar"
| "function"
| "symbol"
| "boolean"
| "string"
| "expression"
| "unknown"
| "error"
| "nothing"
| "never"
| "any";
A primitive type is a simple type that represents a concrete value.
-
any: the top typeexpressionerror: an invalid value, such as["Error", "missing"]nothing: the type of theNothingsymbol, the unit typenever: the bottom typeunknown: a value whose type is not known
-
expression:- a symbolic expression, such as
["Add", "x", 1] <value>symbol: a symbol, such asx.function: a function literal such as["Function", ["Add", "x", 1], "x"].
- a symbolic expression, such as
-
valuescalar<number>boolean: a boolean value:TrueorFalse.string: a string of characters.
collectionset: a collection of unique expressions, e.g.set<string>.record: a collection of specific key-value pairs, e.g.record<x: number, y: boolean>.dictionary: a collection of arbitrary key-value pairs e.g.dictionary<string, number>.indexed_collection: collections whose elements can be accessed by a numeric indexlist: a collection of expressions, possibly recursive, with optional dimensions, e.g.[number],[boolean^32],[number^(2x3)]. Used to represent a vector, a matrix or a tensor when the type of its elements is a numbertuple: a fixed-size collection of named or unnamed elements, e.g.tuple<number, boolean>,tuple<x: number, y: boolean>.
NumericPrimitiveType
type NumericPrimitiveType =
| "number"
| "finite_number"
| "complex"
| "finite_complex"
| "imaginary"
| "real"
| "finite_real"
| "rational"
| "finite_rational"
| "integer"
| "finite_integer"
| "non_finite_number";
number: any numeric value =complex+realplusNaNcomplex: a number with non-zero real and imaginary parts =finite_complexplusComplexInfinityfinite_complex: a finite complex number =imaginary+finite_realimaginary: a complex number with a real part of 0 (pure imaginary)finite_number: a finite numeric value =finite_complexfinite_real: a finite real number =finite_rational+finite_integerfinite_rational: a pure rational numberfinite_integer: a whole numberreal: a complex number with an imaginary part of 0 =finite_real+non_finite_numbernon_finite_number:PositiveInfinity,NegativeInfinityinteger: a whole number =finite_integer+non_finite_numberrational: a pure rational number (not an integer) =finite_rational+non_finite_number
NamedElement
type NamedElement = {
name: string;
type: Type;
};
FunctionSignature
type FunctionSignature = {
kind: "signature";
args: NamedElement[];
optArgs: NamedElement[];
variadicArg: NamedElement;
variadicMin: 0 | 1;
result: Type;
};
AlgebraicType
type AlgebraicType = {
kind: "union" | "intersection";
types: Type[];
};
NegationType
type NegationType = {
kind: "negation";
type: Type;
};
ValueType
type ValueType = {
kind: "value";
value: any;
};
RecordType
type RecordType = {
kind: "record";
elements: Record<string, Type>;
};
A record is a collection of key-value pairs.
The keys are strings. The set of keys is fixed.
For a record type to be a subtype of another record type, it must have a subset of the keys, and all their types must match (width subtyping).
DictionaryType
type DictionaryType = {
kind: "dictionary";
values: Type;
};
A dictionary is a collection of key-value pairs.
The keys are strings. The set of keys is also not defined as part of the type and can be modified at runtime.
A dictionary is suitable for use as cache or data storage.
CollectionType
type CollectionType = {
kind: "collection" | "indexed_collection";
elements: Type;
};
CollectionType is a generic collection of elements of a certain type.
- Indexed collections: List, Tuple
- Non-indexed: Set, Record, Dictionary
ListType
type ListType = {
kind: "list";
elements: Type;
dimensions: number[];
};
The elements of a list can be accessed by their one-based index.
All elements of a list have the same type, but it can be a broad type,
up to any.
The same element can be present in the list more than once.
A list can be multi-dimensional. For example, a list of integers with dimensions 2x3x4 is a 3D tensor with 2 layers, 3 rows and 4 columns.
SymbolType
type SymbolType = {
kind: "symbol";
name: string;
};
ExpressionType
type ExpressionType = {
kind: "expression";
operator: string;
};
NumericType
type NumericType = {
kind: "numeric";
type: NumericPrimitiveType;
lower: number;
upper: number;
};
SetType
type SetType = {
kind: "set";
elements: Type;
};
Each element of a set is unique (is not present in the set more than once). The elements of a set are not indexed.
TupleType
type TupleType = {
kind: "tuple";
elements: NamedElement[];
};
The elements of a tuple are indexed and may be named or unnamed. If one element is named, all elements must be named.
TypeReference
type TypeReference = {
kind: "reference";
name: string;
alias: boolean;
def: Type | undefined;
};
Nominal typing
Type
type Type =
| PrimitiveType
| AlgebraicType
| NegationType
| CollectionType
| ListType
| SetType
| RecordType
| DictionaryType
| TupleType
| SymbolType
| ExpressionType
| NumericType
| NumericPrimitiveType
| FunctionSignature
| ValueType
| TypeReference;
TypeString
type TypeString = string;
The type of a boxed expression indicates the kind of expression it is and the value it represents.
The type is represented either by a primitive type (e.g. number, complex, collection, etc.), or a compound type (e.g. tuple, function signature, etc.).
Types are described using the following BNF grammar:
<type> ::= <union_type> | "(" <type> ")"
<union_type> ::= <intersection_type> (" | " <intersection_type>)*
<intersection_type> ::= <primary_type> (" & " <primary_type>)*
<primary_type> ::= <primitive>
| <tuple_type>
| <signature>
| <list_type>
<primitive> ::= "any" | "unknown" | <value-type> | <symbolic-type> | <numeric-type>
<numeric-type> ::= "number" | "complex" | "imaginary" | "real" | "rational" | "integer"
<value-type> ::= "value" | <numeric-type> | "collection" | "boolean" | "string"
<symbolic-type> ::= "expression" | "function" | "symbol"
<tuple_type> ::= "tuple<" (<name> <type> "," <named_tuple_elements>*) ">"
| "tuple<" (<type> "," <unnamed_tuple_elements>*) ">" |
| "tuple<" <tuple_elements> ">"
<tuple_elements> ::= <unnamed_tuple_elements> | <named_tuple_elements>
<unnamed_tuple_elements> ::= <type> ("," <type>)*
<named_tuple_elements> ::= <name> <type> ("," <name> <type>)*
<signature> ::= <arguments> " -> " <type>
<arguments> ::= "()"
| <argument>
| "(" <argument-list> ")"
<argument> ::= <type>
| <name> <type>
<rest_argument> ::= "..." <type>
| <name> "..." <type>
<optional_argument> ::= <argument> "?"
<optional_arguments> ::= <optional_argument> ("," <optional_argument>)*
<required_arguments> ::= <argument> ("," <argument>)*
<argument-list> ::= <required_arguments> ("," <rest_argument>)?
| <required_arguments> <optional_arguments>?
| <optional_arguments>?
| <rest_argument>
<list_type> ::= "list<" <type> <dimensions>? ">"
<dimensions> ::= "^" <fixed_size>
| "^(" <multi_dimensional_size> ")"
<fixed_size> ::= <positive-integer_literal>
<multi_dimensional_size> ::= <positive-integer_literal> "x" <positive-integer_literal> ("x" <positive-integer_literal>)*
<map> ::= "map" | "map<" <map_elements> ">"
<map_elements> ::= <name> <type> ("," <name> <type>)*
<set> ::= "set<" <type> ">"
<collection ::= "collection<" <type> ">"
<name> ::= <identifier> ":"
<identifier> ::= [a-zA-Z_][a-zA-Z0-9_]*
<positive-integer_literal> ::= [1-9][0-9]*
Examples of types strings:
"number"-- a simple type primitive"(number, boolean)"-- a tuple type"(x: number, y:boolean)"-- a named tuple/record type. Either all arguments are named, or none are"collection<any>"-- an arbitrary collection type, with no length or element type restrictions"collection<integer>"-- a collection type where all the elements are integers"collection<(number, boolean)>"-- a collection of tuples"collection<(value:number, seen:boolean)>"-- a collection of named tuples"[boolean]^32"-- a collection type with a fixed size of 32 elements"[integer]^(2x3)"-- an integer matrix of 2 columns and 3 rows"[integer]^(2x3x4)"-- a tensor of dimensions 2x3x4"number -> number"-- a signature with a single argument"(x: number, number) -> number"-- a signature with a named argument"(number, y:number?) -> number"-- a signature with an optional named argument (can have several optional arguments, at the end)"(number, number+) -> number"-- a signature with a rest argument (can have only one, and no optional arguments if there is a rest argument)."() -> number"-- a signature with an empty argument list"number | boolean"-- a union type"(x: number) & (y: number)"-- an intersection type"number | ((x: number) & (y: number))"-- a union type with an intersection type"(number -> number) | number"-- a union type with a signature and a primitive type
TypeCompatibility
type TypeCompatibility = "covariant" | "contravariant" | "bivariant" | "invariant";
TypeResolver
type TypeResolver = {
get names: string[];
forward: (name) => TypeReference | undefined;
resolve: (name) => TypeReference | undefined;
};
A type resolver should return a definition for a given type name.