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.
rad
: radians, 2π radians is a full circledeg
: degrees, 360 degrees is a full circlegrad
: gradians, 400 gradians is a full circleturn
: turns, 1 turn is a full circle
AssignValue
type AssignValue =
| boolean
| number
| bigint
| SemiBoxedExpression
| (args, options) => BoxedExpression
| undefined;
EvalContext
type EvalContext = {
lexicalScope: Scope;
assumptions: ExpressionMapInterface<boolean>;
values: Record<string, BoxedExpression | undefined>;
name: undefined | string;
};
An evaluation context is a set of bindings mapping symbols to their values. It also includes a reference to the lexical scope of the context, as well as a set of assumptions about the values of the symbols.
Eval contexts are arranged in a stack structure. When a new context is created, it is pushed on the top of the stack.
A new eval context is created when a function expression that needs to track its own local variables and named arguments is evaluated. This kind of function is a "scoped" function, meaning that it has its own local variables and named arguments.
For example, the Sum
function creates a new eval context to track the local
variable used as the index of the sum.
The eval context stack is used to resolve the value of symbols.
When a scoped recursive function is called, a new context is created for each recursive call.
In contrast, the lexical scope is used to resolve the metadata about symbols, such as their type, whether they are constant, etc... A new scope is not created for recursive calls, since the metadata does not change, only the values of the symbols change.
The name of the eval context is used to print a "stack trace" for debugging.
Boxed Expression
BoxedExpression
The BoxedExpression
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 BoxedExpression
its value is
null
. For example expr.symbol
for a BoxedNumber
is null
.
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
holdUntil
flag 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
BoxedExpression.isFunctionExpression
readonly isFunctionExpression: boolean;
Return true
if this expression is a function expression.
If true
, expr.ops
is not null
, and expr.operator
is the name
of the function.
BoxedExpression.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"
.
BoxedExpression.ops
readonly ops: readonly BoxedExpression[];
The list of operands of the function.
If the expression is not a function, return null
.
Applicable to canonical and non-canonical expressions.
BoxedExpression.nops
readonly nops: number;
If this expression is a function, the number of operands, otherwise 0.
Note that a function can have 0 operands, so to check if this expression
is a function, check if this.ops !== null
instead.
Applicable to canonical and non-canonical expressions.
BoxedExpression.op1
readonly op1: BoxedExpression;
First operand, i.e.this.ops[0]
.
If there is no first operand, return the symbol Nothing
.
Applicable to canonical and non-canonical expressions.
BoxedExpression.op2
readonly op2: BoxedExpression;
Second operand, i.e.this.ops[1]
If there is no second operand, return the symbol Nothing
.
Applicable to canonical and non-canonical expressions.
BoxedExpression.op3
readonly op3: BoxedExpression;
Third operand, i.e. this.ops[2]
If there is no third operand, return the symbol Nothing
.
Applicable to canonical and non-canonical expressions.
Numeric Expression
BoxedExpression.isNumberLiteral
readonly isNumberLiteral: boolean;
Return true
if this expression is a number literal, for example
2
, 3.14
, 1/2
, √2
etc.
When true
, expr.numericValue
is not null
.
BoxedExpression.numericValue
readonly numericValue: number | NumericValue;
Return the value of this expression, if a number literal.
Note it is possible for expr.numericValue
to be null
, and for
expr.isNotZero
to be true. For example, when a symbol has been
defined with an assumption.
Conversely, expr.isNumber
may be true even if expr.numericValue
is
null
, for example the symbol Pi
return true
for isNumber
but
expr.numericValue
is null
(it's a symbol, not a number literal).
Its value can be accessed with expr.value
.
To check if an expression is a number literal, use expr.isNumberLiteral
.
If expr.isNumberLiteral
is true
, expr.numericValue
is not null
.
BoxedExpression.isEven
readonly isEven: boolean;
If the value of this expression is not an integer return undefined
.
BoxedExpression.isOdd
readonly isOdd: boolean;
If the value of this expression is not an integer return undefined
.
BoxedExpression.re
readonly re: number;
Return the real part of the value of this expression, if a number.
Otherwise, return NaN
(not a number).
BoxedExpression.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).
BoxedExpression.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.
BoxedExpression.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.
BoxedExpression.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
.
BoxedExpression.isPositive
readonly isPositive: boolean;
The value of this expression is > 0, same as isGreaterEqual(0)
BoxedExpression.isNonNegative
readonly isNonNegative: boolean;
The value of this expression is >= 0, same as isGreaterEqual(0)
BoxedExpression.isNegative
readonly isNegative: boolean;
The value of this expression is < 0, same as isLess(0)
BoxedExpression.isNonPositive
readonly isNonPositive: boolean;
The value of this expression is <= 0, same as isLessEqual(0)
BoxedExpression.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).
BoxedExpression.isInfinity
readonly isInfinity: boolean;
The numeric value of this expression is ±Infinity
or ComplexInfinity.
BoxedExpression.isFinite
readonly isFinite: boolean;
This expression is a number, but not ±Infinity
, ComplexInfinity
or
NaN
Other
BoxedExpression.engine
readonly engine: ComputeEngine;
The Compute Engine instance associated with this expression provides a context in which to interpret it, such as definition of symbols and functions.
BoxedExpression.toLatex()
toLatex(options?): string
Serialize to a LaTeX string.
Will ignore any LaTeX metadata.
####### options?
Partial
<SerializeLatexOptions
>
BoxedExpression.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()
.
Applicable to canonical and non-canonical expressions.
BoxedExpression.toMathJson()
toMathJson(options?): Expression
Serialize to a MathJSON expression with specified options
####### options?
Readonly
<Partial
<JsonSerializationOptions
>>
BoxedExpression.json
readonly json: Expression;
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()
.
Applicable to canonical and non-canonical expressions.
BoxedExpression.print()
print(): void
Output to the console a string representation of the expression.
BoxedExpression.verbatimLatex?
optional verbatimLatex: string;
If the expression was constructed from a LaTeX string, the verbatim LaTeX string it was parsed from.
BoxedExpression.isCanonical
If true
, this expression is in a canonical form.
BoxedExpression.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 BoxedExpression
object.
BoxedExpression.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).
BoxedExpression.structural
Return the structural form of this expression.
Some expressions, such as rational numbers, are represented with
a BoxedExpression
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
.
BoxedExpression.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.
BoxedExpression.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
BoxedExpression.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.:
42
is constantPi
is constant["Divide", "Pi", 2]
is constantx
is not constant, unless declared with a constant flag.["Add", "x", 2]
is either constant only ifx
is constant.
BoxedExpression.errors
readonly errors: readonly BoxedExpression[];
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.
BoxedExpression.getSubexpressions()
getSubexpressions(operator): readonly BoxedExpression[]
All the subexpressions matching the named operator, recursively.
Applicable to canonical and non-canonical expressions.
####### operator
string
BoxedExpression.subexpressions
readonly subexpressions: readonly BoxedExpression[];
All the subexpressions in this expression, recursively
Applicable to canonical and non-canonical expressions.
BoxedExpression.symbols
readonly symbols: readonly string[];
All the symbols in the expression, recursively
Applicable to canonical and non-canonical expressions.
BoxedExpression.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.
BoxedExpression.toNumericValue()
toNumericValue(): [NumericValue, BoxedExpression]
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']]]
BoxedExpression.mul()
mul(rhs): BoxedExpression
Multiplication
####### rhs
number
| NumericValue
| BoxedExpression
BoxedExpression.ln()
ln(base?): BoxedExpression
Logarithm (natural by default)
####### base?
number
| BoxedExpression
BoxedExpression.numerator
Return this expression expressed as a numerator.
BoxedExpression.denominator
Return this expression expressed as a denominator.
BoxedExpression.numeratorDenominator
Return this expression expressed as a numerator and denominator.
BoxedExpression.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.
BoxedExpression.localScope
If this expression has a local scope, return it.
BoxedExpression.subs()
subs(sub, options?): BoxedExpression
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.
####### sub
Substitution
<SemiBoxedExpression
>
####### options?
####### canonical
BoxedExpression.map()
map(fn, options?): BoxedExpression
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
) => BoxedExpression
####### options?
####### canonical
####### recursive
boolean
BoxedExpression.replace()
replace(rules, options?): BoxedExpression
Transform the expression by applying one or more replacement rules:
-
If the expression matches the
match
pattern and thecondition
predicate is true, replace it with thereplace
pattern. -
If no rules apply, return
null
.
See also expr.subs()
for a simple substitution of symbols.
If options.canonical
is not set, the result is canonical if this
is canonical.
Applicable to canonical and non-canonical expressions.
####### rules
BoxedRuleSet
| Rule
| Rule
[]
####### options?
Partial
<ReplaceOptions
>
BoxedExpression.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
[]
BoxedExpression.match()
match(pattern, options?): BoxedSubstitution
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, {}
.
Read more about patterns and rules.
Applicable to canonical and non-canonical expressions.
####### pattern
####### options?
BoxedExpression.wikidata
readonly wikidata: string;
Wikidata identifier.
If not a canonical expression, return undefined
.
BoxedExpression.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
.
BoxedExpression.url
readonly url: string;
An optional URL pointing to more information about the symbol or function operator.
If not a canonical expression, return undefined
.
BoxedExpression.complexity
readonly complexity: number;
Expressions with a higher complexity score are sorted first in commutative functions
If not a canonical expression, return undefined
.
BoxedExpression.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
.
BoxedExpression.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
.
BoxedExpression.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
.
BoxedExpression.simplify()
simplify(options?): BoxedExpression
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
>
BoxedExpression.expand()
expand(): BoxedExpression
Expand the expression: distribute multiplications over additions, and expand powers.
BoxedExpression.evaluate()
evaluate(options?): BoxedExpression
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
>
BoxedExpression.evaluateAsync()
evaluateAsync(options?): Promise<BoxedExpression>
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
>
BoxedExpression.N()
N(): BoxedExpression
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.
BoxedExpression.compile()
compile(options?): (args?) => CompiledType
Compile the expression to a JavaScript function.
The function takes an object as argument, with the keys being the symbols in the expression, and returns the value of the expression.
const expr = ce.parse("x^2 + y^2");
const f = expr.compile();
console.log(f({x: 2, y: 3}));
####### options?
####### to
"javascript"
####### functions
Record
<string
, string
| (...any
) => any
>
####### vars
Record
<string
, CompiledType
>
####### imports
(...any
) => any
[]
####### preamble
string
BoxedExpression.solve()
solve(vars?): readonly BoxedExpression[]
If this is an equation, solve the equation for the variables in vars.
Otherwise, solve the equation this = 0
for the variables in vars.
const expr = ce.parse("x^2 + 2*x + 1 = 0");
console.log(expr.solve("x"));
####### vars?
string
| Iterable
<string
> | BoxedExpression
| Iterable
<BoxedExpression
>
BoxedExpression.value
get value(): BoxedExpression
set value(value:
| string
| number
| boolean
| number[]
| Decimal
| OnlyFirst<{
re: number;
im: number;
}, {
re: number;
im: number;
} & {
num: number;
denom: number;
} & BoxedExpression>
| OnlyFirst<{
num: number;
denom: number;
}, {
re: number;
im: number;
} & {
num: number;
denom: number;
} & BoxedExpression>
| OnlyFirst<BoxedExpression, {
re: number;
im: number;
} & {
num: number;
denom: number;
} & BoxedExpression>): 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.
BoxedExpression.isCollection
isCollection: boolean;
Return true if the expression is a collection: a list, a vector, a matrix, a map, a tuple, etc...
BoxedExpression.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.
####### rhs
BoxedExpression.size
If this is a collection, return the number of elements in the collection.
If the collection is infinite, return Infinity
.
BoxedExpression.each()
each: (start?, count?) => Iterator<BoxedExpression, undefined>;
If this is a collection, return an iterator over the elements of the collection.
If start
is not specified, start from the first element.
If count
is not specified or negative, return all the elements from start
to the end.
const expr = ce.parse('[1, 2, 3, 4]');
for (const e of expr.each()) {
console.log(e);
}
BoxedExpression.at()
at(index): BoxedExpression
If this is an indexable collection, return the element at the specified index.
If the index is negative, return the element at index size() + index + 1
.
####### index
number
BoxedExpression.get()
get(key): BoxedExpression
If this is a map or a tuple, return the value of the corresponding key.
If key
is a BoxedExpression
, it should be a string.
####### key
string
| BoxedExpression
BoxedExpression.indexOf()
indexOf(expr): number
If this is an indexable collection, return the index of the first element that matches the target expression.
####### expr
Primitive Methods
BoxedExpression.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.
BoxedExpression.[toPrimitive]()
toPrimitive: string | number
Similar toexpr.valueOf()
but includes a hint.
####### hint
"string"
| "number"
| "default"
BoxedExpression.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
.
Used when coercing a BoxedExpression
to a String
.
BoxedExpression.toJSON()
toJSON(): Expression
Used by JSON.stringify()
to serialize this object to JSON.
Method version of expr.json
.
Based on Object.toJSON()
.
BoxedExpression.is()
is(other): boolean
Equivalent to BoxedExpression.isSame()
but the argument can be
a JavaScript primitive. For example, expr.is(2)
is equivalent to
expr.isSame(ce.number(2))
.
####### other
string
| number
| bigint
| boolean
| BoxedExpression
Relational Operator
BoxedExpression.isSame()
isSame(rhs): boolean
Structural/symbolic equality (weak equality).
ce.parse('1+x', {canonical: false}).isSame(ce.parse('x+1', {canonical: false}))
is false
.
See expr.isEqual()
for mathematical equality.
Applicable to canonical and non-canonical expressions.
####### rhs
BoxedExpression.isLess()
isLess(other): boolean
The value of both expressions are compared.
If the expressions cannot be compared, return undefined
####### other
number
| BoxedExpression
BoxedExpression.isLessEqual()
isLessEqual(other): boolean
The value of both expressions are compared.
If the expressions cannot be compared, return undefined
####### other
number
| BoxedExpression
BoxedExpression.isGreater()
isGreater(other): boolean
The value of both expressions are compared.
If the expressions cannot be compared, return undefined
####### other
number
| BoxedExpression
BoxedExpression.isGreaterEqual()
isGreaterEqual(other): boolean
The value of both expressions are compared.
If the expressions cannot be compared, return undefined
####### other
number
| BoxedExpression
BoxedExpression.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 structural comparison which does not involve evaluating the expressions.expr.is(other)
for a comparison of a number literal
Examples
let expr = ce.parse('2 + 2');
console.log(expr.isEqual(4)); // true
console.log(expr.isSame(ce.parse(4))); // false
console.log(expr.is(4)); // false
expr = ce.parse('4');
console.log(expr.isEqual(4)); // true
console.log(expr.isSame(ce.parse(4))); // true
console.log(expr.is(4)); // true (fastest)
####### other
number
| BoxedExpression
String Expression
BoxedExpression.string
readonly string: string;
If this expression is a string, return the value of the string.
Otherwise, return null
.
Applicable to canonical and non-canonical expressions.
Symbol Expression
BoxedExpression.symbol
readonly symbol: string;
If this expression is a symbol, return the name of the symbol as a string.
Otherwise, return null
.
Applicable to canonical and non-canonical expressions.
Tensor Expression
BoxedExpression.tensor
readonly tensor: Tensor<any>;
If this expression is a tensor, return the tensor data.
Otherwise, return null
.
Applicable to canonical and non-canonical expressions.
BoxedExpression.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]
.
BoxedExpression.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
BoxedExpression.type
get type(): BoxedType
set type(type:
| string
| AlgebraicType
| NegationType
| CollectionType
| ListType
| SetType
| MapType
| TupleType
| 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"
.
BoxedExpression.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
.
BoxedExpression.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.
BoxedExpression.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.
BoxedExpression.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.
SemiBoxedExpression
type SemiBoxedExpression =
| number
| bigint
| string
| BigNum
| MathJsonNumberObject
| MathJsonStringObject
| MathJsonSymbolObject
| MathJsonFunctionObject
| readonly [MathJsonSymbol, ...SemiBoxedExpression[]]
| BoxedExpression;
A semi boxed expression is a MathJSON expression which can include some boxed terms.
This is convenient when creating new expressions from portions
of an existing BoxedExpression
while avoiding unboxing and reboxing.
ReplaceOptions
type ReplaceOptions = {
recursive: boolean;
once: boolean;
useVariations: boolean;
iterationLimit: number;
canonical: CanonicalOptions;
};
ReplaceOptions.recursive
recursive: boolean;
If true
, apply replacement rules to all sub-expressions.
If false
, only consider the top-level expression.
Default: false
ReplaceOptions.once
once: boolean;
If true
, stop after the first rule that matches.
If false
, apply all the remaining rules even after the first match.
Default: false
ReplaceOptions.useVariations
useVariations: boolean;
If true
the rule will use some equivalent variations to match.
For example when useVariations
is true:
x
matchesa + x
with a = 0x
matchesax
with a = 1- etc...
Setting this to true
can save time by condensing multiple rules
into one. This can be particularly useful when describing equations
solutions. However, it can lead to infinite recursion and should be
used with caution.
ReplaceOptions.iterationLimit
iterationLimit: number;
If iterationLimit
> 1, the rules will be repeatedly applied
until no rules apply, up to maxIterations
times.
Note that if once
is true, iterationLimit
has no effect.
Default: 1
ReplaceOptions.canonical
canonical: CanonicalOptions;
Indicate if the expression should be canonicalized after the replacement. If not provided, the expression is canonicalized if the expression that matched the pattern is canonical.
SimplifyOptions
type SimplifyOptions = {
rules: | null
| Rule
| ReadonlyArray<BoxedRule | Rule>
| BoxedRuleSet;
costFunction: (expr) => number;
};
Options for BoxedExpression.simplify()
SimplifyOptions.rules?
optional rules:
| null
| Rule
| ReadonlyArray<BoxedRule | Rule>
| BoxedRuleSet;
The set of rules to apply. If null
, use no rules. If not provided,
use the default simplification rules.
SimplifyOptions.costFunction()?
optional costFunction: (expr) => number;
Use this cost function to determine if a simplification is worth it.
If not provided, ce.costFunction
, the cost function of the engine is
used.
CanonicalForm
type CanonicalForm =
| "InvisibleOperator"
| "Number"
| "Multiply"
| "Add"
| "Power"
| "Divide"
| "Flatten"
| "Order";
When provided, canonical forms are used to put an expression in a "standard" form.
Each canonical form applies some transformation to an expression. When specified as an array, each transformation is done in the order in which it was provided.
InvisibleOperator
: replace use of theInvisibleOperator
with another operation, such as multiplication (i.e.2x
or function application (f(x)
). Also replaces ['InvisibleOperator', real, imaginary] instances with complex (imaginary) numbers.Number
: replace all numeric values with their canonical representation, for example, reduce rationals and replace complex numbers with no imaginary part with a real number.Multiply
: replace negation with multiplication by -1, remove 1 from multiplications, simplify signs (-y \times -x
->x \times y
), complex numbers are promoted (['Multiply', 2, 'ImaginaryUnit'] ->["Complex", 0, 2]
)Add
: replaceSubtract
withAdd
, removes 0 in addition, promote complex numbers (["Add", "a", ["Complex", 0, "b"] ->["Complex", "a", "b"]
)Power
: simplifyPower
expression, for example,x^{-1}
->\frac{1}{x}
,x^0
->1
,x^1
->x
,1^x
->1
,x^{\frac{1}{2}}
->\sqrt{x}
,a^b^c
->a^{bc}
...Divide
: replace with aRational
number if numerator and denominator are integers, simplify, e.g.\frac{x}{1}
->x
...Flatten
: remove any unnecessaryDelimiter
expression, and flatten any associative functions, for example["Add", ["Add", "a", "b"], "c"]
->["Add", "a", "b", "c"]
Order
: when applicable, sort the arguments in a specific order, for example for addition and multiplication.
CanonicalOptions
type CanonicalOptions =
| boolean
| CanonicalForm
| CanonicalForm[];
EvaluateOptions
type EvaluateOptions = {
numericApproximation: boolean;
signal: AbortSignal;
withArguments: Record<MathJsonSymbol, BoxedExpression>;
};
Options for BoxedExpression.evaluate()
Metadata
type Metadata = {
latex: string;
wikidata: string;
};
Metadata that can be associated with an MathJSON expression.
Pattern Matching
PatternMatchOptions
type PatternMatchOptions = {
substitution: BoxedSubstitution;
recursive: boolean;
useVariations: boolean;
};
Control how a pattern is matched to an expression.
-
substitution
: if present, assumes these values for the named wildcards, and ensure that subsequent occurrence of the same wildcard have the same value. -
recursive
: if true, match recursively, otherwise match only the top level. -
useVariations
: if false, only match expressions that are structurally identical. If true, match expressions that are structurally identical or equivalent.For example, when true,
["Add", '_a', 2]
matches2
, with a value of_a
of0
. If false, the expression does not match. Default:false
Substitution<T>
type Substitution<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 = SemiBoxedExpression
BoxedSubstitution
type BoxedSubstitution = Substitution<BoxedExpression>;
Rules
RuleReplaceFunction()
type RuleReplaceFunction = (expr, wildcards) => BoxedExpression | undefined;
Given an expression and set of wildcards, return a new expression.
For example:
{
match: '_x',
replace: (expr, {_x}) => { return ['Add', 1, _x] }
}
RuleConditionFunction()
type RuleConditionFunction = (wildcards, ce) => boolean;
RuleFunction()
type RuleFunction = (expr) =>
| undefined
| BoxedExpression
| RuleStep;
RuleStep
type RuleStep = {
value: BoxedExpression;
because: string;
};
RuleSteps
type RuleSteps = RuleStep[];
Rule
type Rule =
| string
| RuleFunction
| {
match: | LatexString
| SemiBoxedExpression
| BoxedExpression;
replace: | LatexString
| SemiBoxedExpression
| RuleReplaceFunction
| RuleFunction;
condition: | LatexString
| RuleConditionFunction;
useVariations: boolean;
id: string;
onBeforeMatch: (rule, expr) => void;
onMatch: (rule, expr, replace) => void;
};
A rule describes how to modify an expressions that matches a pattern match
into a new expression replace
.
x-1
( \to )1-x
(x+1)(x-1)
( \to ) `x^2-1
The patterns can be expressed as LaTeX strings or a MathJSON expressions.
As a shortcut, a rule can be defined as a LaTeX string: x-1 -> 1-x
.
The expression to the left of ->
is the match
and the expression to the
right is the replace
. When using LaTeX strings, single character variables
are assumed to be wildcards.
When using MathJSON expressions, anonymous wildcards (_
) will match any
expression. Named wildcards (_x
, _a
, etc...) will match any expression
and bind the expression to the wildcard name.
In addition the sequence wildcard (__1
, __a
, etc...) will match
a sequence of one or more expressions, and bind the sequence to the
wildcard name.
Sequence wildcards are useful when the number of elements in the sequence
is not known in advance. For example, in a sum, the number of terms is
not known in advance. ["Add", 0, __a
] will match two or more terms and
the __a
wildcard will be a sequence of the matchign terms.
If exact
is false, the rule will match variants.
For example 'x' will match 'a + x', 'x' will match 'ax', etc...
For simplification rules, you generally want exact
to be true, but
to solve equations, you want it to be false. Default to true.
When set to false, infinite recursion is possible.
BoxedRule
type BoxedRule = {
match: undefined | BoxedExpression;
replace: | BoxedExpression
| RuleReplaceFunction
| RuleFunction;
condition: undefined | RuleConditionFunction;
useVariations: boolean;
id: string;
onBeforeMatch: (rule, expr) => void;
onMatch: (rule, expr, replace) => void;
};
If the match
property is undefined
, all expressions match this rule
and condition
should also be undefined
. The replace
property should
be a BoxedExpression
or a RuleFunction
, and further filtering can be
done in the replace
function.
BoxedRuleSet
type BoxedRuleSet = {
rules: ReadonlyArray<BoxedRule>;
};
To create a BoxedRuleSet use the ce.rules()
method.
Do not create a BoxedRuleSet
directly.
Assumptions
Assumption
Assumption.isPositive
isPositive: boolean;
Assumption.isNonNegative
isNonNegative: boolean;
Assumption.isNegative
isNegative: boolean;
Assumption.isNonPositive
isNonPositive: boolean;
Assumption.isNumber
isNumber: boolean;
Assumption.isInteger
isInteger: boolean;
Assumption.isRational
isRational: boolean;
Assumption.isReal
isReal: boolean;
Assumption.isComplex
isComplex: boolean;
Assumption.isImaginary
isImaginary: boolean;
Assumption.isFinite
isFinite: boolean;
Assumption.isInfinite
isInfinite: boolean;
Assumption.isNaN
isNaN: boolean;
Assumption.isZero
isZero: boolean;
Assumption.toExpression()
toExpression(ce, x): BoxedExpression
####### ce
ComputeEngine
####### x
string
ExpressionMapInterface<U>
ExpressionMapInterface.clear()
clear(): void
ExpressionMapInterface.[iterator]()
iterator: IterableIterator<[BoxedExpression, U]>
ExpressionMapInterface.entries()
entries(): IterableIterator<[BoxedExpression, U]>
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 | BoxedExpression;
};
Definitions
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";
ValueDefinition
type ValueDefinition = BaseDefinition & {
holdUntil: "never" | "evaluate" | "N";
type: | Type
| TypeString
| BoxedType;
inferred: boolean;
value: | LatexString
| SemiBoxedExpression
| (ce) => BoxedExpression | null;
eq: (a) => boolean | undefined;
neq: (a) => boolean | undefined;
cmp: (a) => "=" | ">" | "<" | undefined;
collection: Partial<CollectionHandlers>;
};
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
| SemiBoxedExpression
| (ce) => BoxedExpression | 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
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) => BoxedExpression | null;
evaluate: | (ops, options) => BoxedExpression | undefined
| BoxedExpression;
evaluateAsync: (ops, options) => Promise<BoxedExpression | undefined>;
evalDimension: (args, options) => BoxedExpression;
compile: (expr) => CompiledExpression;
eq: (a, b) => boolean | undefined;
neq: (a, b) => boolean | undefined;
collection: Partial<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) => BoxedExpression | 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) => BoxedExpression | undefined
| BoxedExpression;
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<BoxedExpression | undefined>;
An asynchronous version of evaluate
.
OperatorDefinition.evalDimension()?
optional evalDimension: (args, options) => BoxedExpression;
Experimental
Dimensional analysis
OperatorDefinition.compile()?
optional compile: (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.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<{}>;
CollectionHandlers
type CollectionHandlers = {
size: (collection) => number;
contains: (collection, target) => boolean;
iterator: (collection, start?, count?) => Iterator<BoxedExpression, undefined>;
at: (collection, index) => undefined | BoxedExpression;
keys: (collection) => undefined | Iterable<string>;
indexOf: (collection, target, from?) => number | undefined;
subsetOf: (collection, target, strict) => boolean;
eltsgn: (collection) => Sign | undefined;
elttype: (collection) => Type | undefined;
};
These handlers are the primitive operations that can be performed on collections.
There are two types of collections:
-
finite collections, such as lists, tuples, sets, matrices, etc... The
size()
handler of finite collections returns the number of elements -
infinite collections, such as sequences, ranges, etc... The
size()
handler of infinite collections returnsInfinity
Infinite collections are not indexable: they have noat()
handler.
Definitions
CollectionHandlers.iterator()
iterator: (collection, start?, count?) => Iterator<BoxedExpression, undefined>;
Return an iterator
- start is optional and is a 1-based index.
- if start is not specified, start from index 1
- count is optional and is the number of elements to return
- if count is not specified or negative, return all the elements from start to the end
If there is a keys()
handler, there is no iterator()
handler.
Other
CollectionHandlers.size()
size: (collection) => number;
Return the number of elements in the collection.
An empty collection has a size of 0.
CollectionHandlers.contains()
contains: (collection, target) => boolean;
Return true
if the target
expression is in the collection, false
otherwise.
CollectionHandlers.at()
at: (collection, index) => undefined | BoxedExpression;
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 size() + index + 1
.
The index can also be a string for example for maps. The set of valid keys
is returned by the keys()
handler.
If the index is invalid, return undefined
.
CollectionHandlers.keys()
keys: (collection) => undefined | Iterable<string>;
If the collection can be indexed by strings, return the valid values for the index.
CollectionHandlers.indexOf()
indexOf: (collection, target, from?) => number | undefined;
Return the index of the first element that matches the target expression.
The comparison is done using the target.isEqual()
method.
If the expression is not found, return undefined
.
If the expression is found, return the index, 1-based.
Return the index of the first match.
from
is the starting index for the search. If negative, start from
the end and search backwards.
CollectionHandlers.subsetOf()
subsetOf: (collection, target, strict) => boolean;
Return true
if all theelements of target
are in expr
.
Both expr
and target
are collections.
If strict is true
, the subset must be strict, that is, expr
must
have more elements than target
.
CollectionHandlers.eltsgn()
eltsgn: (collection) => Sign | undefined;
Return the sign of all the elements of the collection.
CollectionHandlers.elttype()
elttype: (collection) => Type | undefined;
Return the widest type of all the elements in the collection
BoxedDefinition
type BoxedDefinition =
| TaggedValueDefinition
| TaggedOperatorDefinition;
A boxed 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: Partial<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:
ImaginaryUnit
hasholdUntil: 'never'
: it is substituted during canonicalizationx
hasholdUntil: 'evaluate'
(variables)Pi
hasholdUntil: 'N'
(special numeric constant)
Default: evaluate
BoxedValueDefinition.value
readonly value: BoxedExpression;
This is either the initial value of the symbol (i.e. when a new evaluation context is created), or its constant value, if a constant. Otherwise, the current value is tracked in the evaluation context.
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;
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.
OperatorDefinitionFlags.lazy
lazy: boolean;
If true
, the arguments to this operator are not automatically
evaluated. The default is false
(the arguments are evaluated).
This can be useful for example for operators that take symbolic
expressions as arguments, such as Declare
or Integrate
.
This is also useful for operators that take an argument that is potentially an infinite collection.
It will be up to the evaluate()
handler to evaluate the arguments as
needed. This is convenient to pass symbolic expressions as arguments
to operators without having to explicitly use a Hold
expression.
This also applies to the canonical()
handler.
OperatorDefinitionFlags.scoped
scoped: boolean;
If true
, the operator requires a new lexical scope when canonicalized.
This will allow it to declare variables that are not visible outside
the function expression using the operator.
Default: false
OperatorDefinitionFlags.broadcastable
broadcastable: boolean;
If true
, the operator is applied element by element to lists, matrices
(["List"]
or ["Tuple"]
expressions) and equations (relational
operators).
Default: false
OperatorDefinitionFlags.associative
associative: boolean;
If true
, ["f", ["f", a], b]
simplifies to ["f", a, b]
Default: false
OperatorDefinitionFlags.commutative
commutative: boolean;
If true
, ["f", a, b]
equals ["f", b, a]
. The canonical
version of the function will order the arguments.
Default: false
OperatorDefinitionFlags.commutativeOrder
commutativeOrder: (a, b) => number | undefined;
If commutative
is true
, the order of the arguments is determined by
this function.
If the function is not provided, the arguments are ordered by the default order of the arguments.
OperatorDefinitionFlags.idempotent
idempotent: boolean;
If true
, ["f", ["f", x]]
simplifies to ["f", x]
.
Default: false
OperatorDefinitionFlags.involution
involution: boolean;
If true
, ["f", ["f", x]]
simplifies to x
.
Default: false
OperatorDefinitionFlags.pure
pure: boolean;
If true
, the value of this operator is always the same for a given
set of arguments and it has no side effects.
An expression using this operator is pure if the operator and all its arguments are pure.
For example Sin
is pure, Random
isn't.
This information may be used to cache the value of expressions.
Default: true
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
| MapType
| TupleType
| 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) => BoxedExpression;
BoxedOperatorDefinition.evaluate()?
optional evaluate: (ops, options) => BoxedExpression;
BoxedOperatorDefinition.evaluateAsync()?
optional evaluateAsync: (ops, options?) => Promise<BoxedExpression>;
BoxedOperatorDefinition.evalDimension()?
optional evalDimension: (ops, options) => BoxedExpression;
BoxedOperatorDefinition.compile()?
optional compile: (expr) => CompiledExpression;
Scope
type Scope = {
parent: Scope | null;
bindings: Map<string, BoxedDefinition>;
};
A lexical scope is a table mapping symbols to their definitions. The symbols are the names of the variables, unknowns and functions in the scope.
The lexical scope is used to resolve the metadata about symbols, such as their type, whether they are constant, etc...
It does not resolve the values of the symbols, since those depend on the evaluation context. For example, the local variables of a recursive function will have the same lexical scope, but different values in each evaluation context.
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 =
| "algebra"
| "arithmetic"
| "calculus"
| "collections"
| "control-structures"
| "combinatorics"
| "complex"
| "core"
| "data-structures"
| "dimensions"
| "domains"
| "linear-algebra"
| "logic"
| "numeric"
| "other"
| "physics"
| "polynomials"
| "relop"
| "sets"
| "statistics"
| "styling"
| "symbols"
| "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 range from 0 to 1000. The larger the number, the higher the precedence, the more "binding" the operator is.
Here are some rough ranges for the precedence:
- 800: prefix and postfix operators:
\lnot
etc...POSTFIX_PRECEDENCE
= 810:!
,'
- 700: some arithmetic operators
EXPONENTIATION_PRECEDENCE
= 700:^
- 600: some binary operators
DIVISION_PRECEDENCE
= 600:\div
- 500: not used
- 400: not used
- 300: some logic and arithmetic operators:
\land
,\lor
,\times
, etc...MULTIPLICATION_PRECEDENCE
= 390:\times
- 200: arithmetic operators, inequalities:
ADDITION_PRECEDENCE
= 275:+
-
ARROW_PRECEDENCE
= 270:\to
\rightarrow
ASSIGNMENT_PRECEDENCE
= 260::=
COMPARISON_PRECEDENCE
= 245:\lt
\gt
- 241:
\leq
- 100: not used
- 0:
,
,;
, etc...
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?) => Expression | null;
PrefixParseHandler()
type PrefixParseHandler = (parser, until?) => Expression | null;
SymbolParseHandler()
type SymbolParseHandler = (parser, until?) => Expression | null;
FunctionParseHandler()
type FunctionParseHandler = (parser, until?) => Expression | null;
EnvironmentParseHandler()
type EnvironmentParseHandler = (parser, until?) => Expression | null;
PostfixParseHandler()
type PostfixParseHandler = (parser, lhs, until?) => Expression | null;
InfixParseHandler()
type InfixParseHandler = (parser, lhs, until) => Expression | null;
MatchfixParseHandler()
type MatchfixParseHandler = (parser, body) => Expression | 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.
BaseEntry.name?
optional name: MathJsonSymbol;
Map a MathJSON symbol to this entry.
Each entry should have at least a name
or a parse
handler.
An entry with no name
cannot be serialized: the name
is used to map
a MathJSON function or symbol name to the appropriate entry for
serializing.
However, an entry with no name
can be used to define a synonym (for
example for the symbol \varnothing
which is a synonym for \emptyset
).
If no parse
handler is provided, only the trigger is used to select this
entry. Otherwise, if the trigger of the entry matches the current
token, the parse
handler is invoked.
BaseEntry.serialize?
optional serialize:
| LatexString
| SerializeHandler;
Transform an expression into a LaTeX string.
If no serialize
handler is provided, the trigger is used.
DefaultEntry
type DefaultEntry = BaseEntry & Trigger & {
parse: | Expression
| ExpressionParseHandler;
};
ExpressionEntry
type ExpressionEntry = BaseEntry & Trigger & {
kind: "expression";
parse: | Expression
| 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
,right
ornone
associative 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: | Expression
| 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: | Expression
| FunctionParseHandler;
};
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
.
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 & {
skipSpace: boolean;
parseNumbers: "auto" | "rational" | "decimal" | "never";
getSymbolType: (symbol) => BoxedType;
parseUnexpectedToken: (lhs, parser) => Expression | null;
preserveLatex: boolean;
};
The LaTeX parsing options can be used with the ce.parse()
method.
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;
This handler is invoked when the parser encounters a that has not yet been declared.
The symbol
argument is a valid symbol.
ParseLatexOptions.parseUnexpectedToken()
parseUnexpectedToken: (lhs, parser) => Expression | 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
Parser
An instance of Parser
is provided to the parse
handlers of custom
LaTeX dictionary entries.
Parser.options
readonly options: Required<ParseLatexOptions>;
Parser.atEnd
readonly atEnd: boolean;
True if the last token has been reached.
Consider also atTerminator()
.
Parser.atBoundary
Parser.pushSymbolTable()
pushSymbolTable(): void
Parser.popSymbolTable()
popSymbolTable(): void
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): Expression
Return an error expression with the specified code and arguments
####### code
string
| [string
, ...Expression[]
]
####### 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.matchChar()
matchChar(): 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(): Expression
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(): Expression
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(): Expression
Parse an expression enclosed in a LaTeX optional group enclosed in square brackets []
.
Return null
if none was found.
Parser.parseEnclosure()
parseEnclosure(): Expression
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?): Expression
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(): Expression[][]
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 Expression[]
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 + 1
as\cos(x) + 1
)
Return an array of expressions, one for each argument, or null
if no
argument was found.
####### kind?
"implicit"
| "enclosure"
####### until?
Parser.parsePostfixOperator()
parsePostfixOperator(lhs, until?): Expression
Parse a postfix operator, such as '
or !
.
Prefix, infix and matchfix operators are handled by parseExpression()
####### lhs
####### until?
Partial
<Terminator
>
Parser.parseExpression()
parseExpression(until?): Expression
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
SerializeLatexOptions
type SerializeLatexOptions = NumberSerializationFormat & {
prettify: boolean;
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";
};
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.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
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: IndexedLatexDictionary;
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) => "interval" | "compact" | "regular" | "set-builder";
Serializer.serializeFunction()
serializeFunction(expr, def?): string
####### expr
####### def?
IndexedLatexDictionaryEntry
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
Sign
type Sign =
| "zero"
| "positive"
| "negative"
| "non-negative"
| "non-positive"
| "not-zero"
| "unsigned";
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;
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;
Other
TaggedValueDefinition
type TaggedValueDefinition = {
value: BoxedValueDefinition;
};
TaggedOperatorDefinition
type TaggedOperatorDefinition = {
operator: BoxedOperatorDefinition;
};
SymbolTable
type SymbolTable = {
parent: SymbolTable | null;
ids: {};
};
BigNumFactory()
type BigNumFactory = (value) => Decimal;
Serialization
JsonSerializationOptions
type JsonSerializationOptions = {
prettify: boolean;
exclude: string[];
shorthands: ("all" | "number" | "symbol" | "function" | "string")[];
metadata: ("all" | "wikidata" | "latex")[];
repeatingDecimal: boolean;
fractionalDigits: "auto" | "max" | number;
};
Options to control the serialization to MathJSON when using BoxedExpression.toMathJson()
.
JsonSerializationOptions.prettify
prettify: boolean;
If true, the serialization applies some transformations to make
the JSON more readable. For example, ["Power", "x", 2]
is serialized
as ["Square", "x"]
.
JsonSerializationOptions.exclude
exclude: string[];
A list of space separated function names that should be excluded from the JSON output.
Those functions are replaced with an equivalent, for example, Square
with
Power
, etc...
Possible values include Sqrt
, Root
, Square
, Exp
, Subtract
,
Rational
, Complex
Default: []
(none)
JsonSerializationOptions.shorthands
shorthands: ("all" | "number" | "symbol" | "function" | "string")[];
A list of space separated keywords indicating which MathJSON expressions can use a shorthand.
Default: ["all"]
JsonSerializationOptions.metadata
metadata: ("all" | "wikidata" | "latex")[];
A list of space separated keywords indicating which metadata should be included in the MathJSON. If metadata is included, shorthand notation is not used.
Default: []
(none)
JsonSerializationOptions.repeatingDecimal
repeatingDecimal: boolean;
If true, repeating decimals are detected and serialized accordingly For example:
1.3333333333333333
( \to )1.(3)
0.142857142857142857142857142857142857142857142857142
( \to )0.(1428571)
Default: true
JsonSerializationOptions.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"
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.
NumberFormat.decimalSeparator
decimalSeparator: LatexString;
A string representing the decimal separator, the string separating the whole portion of a number from the fractional portion, i.e. the "." in "3.1415".
Some countries use a comma rather than a dot. In this case it is
recommended to use "{,}"
as the separator: the surrounding brackets
ensure there is no additional gap after the comma.
Default: "."
NumberFormat.digitGroupSeparator
digitGroupSeparator:
| LatexString
| [LatexString, LatexString];
A string representing the separator between groups of digits, to make numbers with many digits easier to read.
If a single string is provided, it is used to group digits in the whole and the fractional part of the number. If two strings are provided, the first is used for the whole part and the second for the fractional part.
Caution: some values may lead to unexpected results.
For example, if the digitGroupSeparator
is ,
(comma) the expression
\operatorname{Hypot}(1,2)
will parse as ["Hypot", 1.2]
rather than
["Hypot", 1, 2]
. You can however use {,}
which will avoid this issue
and display with correct spacing.
Default: "\\,"
(thin space, 3/18mu) (Resolution 7 of the 1948 CGPM)
NumberFormat.digitGroup
digitGroup: "lakh" | number | [number | "lakh", number];
Maximum length of digits between digit group separators.
If a single number is provided, it is used for the whole and the fractional part of the number. If two numbers are provided, the first is used for the whole part and the second for the fractional part.
If '"lakh"
' is provided, the number is grouped in groups of 2 digits,
except for the last group which has 3 digits. For example: 1,00,00,000
.
Default: 3
NumberSerializationFormat
type NumberSerializationFormat = NumberFormat & {
fractionalDigits: "auto" | "max" | number;
notation: "auto" | "engineering" | "scientific";
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"
Tensors
DataTypeMap
type DataTypeMap = {
float64: number;
float32: number;
int32: number;
uint8: number;
complex128: Complex;
complex64: Complex;
bool: boolean;
expression: BoxedExpression;
};
Map of TensorDataType
to JavaScript type.
TensorData<DT>
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<T>
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): BoxedExpression
####### 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): BoxedExpression[]
####### x
T
[]
####### dtype
"expression"
cast(x, dtype)
cast(x, dtype): any
####### x
T
| T
[]
####### dtype
keyof DataTypeMap
Tensor<DT>
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: BoxedExpression;
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.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): BoxedType
####### type
string
| AlgebraicType
| NegationType
| CollectionType
| ListType
| SetType
| MapType
| TupleType
| FunctionSignature
| ValueType
| TypeReference
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.type
type: Type;
BoxedType.isUnknown
BoxedType.matches()
matches(other): boolean
####### other
string
| AlgebraicType
| NegationType
| CollectionType
| ListType
| SetType
| MapType
| TupleType
| FunctionSignature
| ValueType
| TypeReference
| BoxedType
BoxedType.is()
is(other): boolean
####### other
string
| AlgebraicType
| NegationType
| CollectionType
| ListType
| SetType
| MapType
| TupleType
| FunctionSignature
| ValueType
| TypeReference
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];
};
MathJsonAttributes.comment?
optional comment: string;
A human readable string to annotate this expression, since JSON does not allow comments in its encoding
MathJsonAttributes.documentation?
optional documentation: string;
A Markdown-encoded string providing documentation about this expression.
MathJsonAttributes.latex?
optional latex: string;
A visual representation of this expression as a LaTeX string.
This can be useful to preserve non-semantic details, for example parentheses in an expression or styling attributes.
MathJsonAttributes.wikidata?
optional wikidata: string;
A short string referencing an entry in a wikibase.
For example:
"Q167"
is the wikidata entry
for the Pi
constant.
MathJsonAttributes.wikibase?
optional wikibase: string;
A base URL for the wikidata
key.
A full URL can be produced by concatenating this key with the wikidata
key. This key applies to this node and all its children.
The default value is "https://www.wikidata.org/wiki/"
MathJsonAttributes.openmathSymbol?
optional openmathSymbol: string;
A short string indicating an entry in an OpenMath Content Dictionary.
For example: arith1/#abs
.
MathJsonAttributes.openmathCd?
optional openmathCd: string;
A base URL for an OpenMath content dictionary. This key applies to this node and all its children.
The default value is "http://www.openmath.org/cd".
MathJsonAttributes.sourceUrl?
optional sourceUrl: string;
A URL to the source code from which this expression was generated.
MathJsonAttributes.sourceContent?
optional sourceContent: string;
The source code from which this expression was generated.
It could be a LaTeX expression, or some other source language.
MathJsonAttributes.sourceOffsets?
optional sourceOffsets: [number, number];
A character offset in sourceContent
or sourceUrl
from which this
expression was generated.
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
e
orE
exponent marker followed by an optional-
minus sign, followed by a string of digits)
It can also consist of the value NaN
, -Infinity
and +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.34
0.234e-56
1.(3)
123456789123456789.123(4567)e999
MathJsonSymbolObject
type MathJsonSymbolObject = {
sym: MathJsonSymbol;
} & MathJsonAttributes;
MathJsonStringObject
type MathJsonStringObject = {
str: string;
} & MathJsonAttributes;
MathJsonFunctionObject
type MathJsonFunctionObject = {
fn: [MathJsonSymbol, ...Expression[]];
} & MathJsonAttributes;
ExpressionObject
type ExpressionObject =
| MathJsonNumberObject
| MathJsonStringObject
| MathJsonSymbolObject
| MathJsonFunctionObject;
Expression
type Expression =
| ExpressionObject
| number
| MathJsonSymbol
| string
| readonly [MathJsonSymbol, ...Expression[]];
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 =
| NumericType
| "collection"
| "list"
| "set"
| "map"
| "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 typeexpression
error
: an invalid value, such as["Error", "missing"]
nothing
: the type of theNothing
symbol, 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
-
value
scalar
<number>
boolean
: a boolean value:True
orFalse
.string
: a string of characters.
collection
list
: 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 numberset
: a collection of unique expressions, e.g.set<string>
.tuple
: a fixed-size collection of named or unnamed elements, e.g.tuple<number, boolean>
,tuple<x: number, y: boolean>
.map
: a set key-value pairs, e.g.map<x: number, y: boolean>
.
NumericType
type NumericType =
| "number"
| "finite_number"
| "complex"
| "finite_complex"
| "imaginary"
| "real"
| "finite_real"
| "rational"
| "finite_rational"
| "integer"
| "finite_integer"
| "non_finite_number";
number
: any numeric value =complex
+real
plusNaN
complex
: a number with non-zero real and imaginary parts =finite_complex
plusComplexInfinity
finite_complex
: a finite complex number =imaginary
+finite_real
imaginary
: a complex number with a real part of 0 (pure imaginary)finite_number
: a finite numeric value =finite_complex
finite_real
: a finite real number =finite_rational
+finite_integer
finite_rational
: a pure rational numberfinite_integer
: a whole numberreal
: a complex number with an imaginary part of 0 =finite_real
+non_finite_number
non_finite_number
:PositiveInfinity
,NegativeInfinity
integer
: a whole number =finite_integer
+non_finite_number
rational
: 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[];
restArg: NamedElement;
result: Type;
};
AlgebraicType
type AlgebraicType = {
kind: "union" | "intersection";
types: Type[];
};
NegationType
type NegationType = {
kind: "negation";
type: Type;
};
ValueType
type ValueType = {
kind: "value";
value: any;
};
MapType
type MapType = {
kind: "map";
elements: Record<string, Type>;
};
Map is a non-indexable collection of key/value pairs.
An element of a map whose type is a subtype of nothing
is optional.
For example, in {x: number, y: boolean | nothing}
the element y
is optional.
CollectionType
type CollectionType = {
kind: "collection";
elements: Type;
};
Collection, List, Set, Tuple and Map are collections.
CollectionType
is a generic collection of elements of a certain type.
ListType
type ListType = {
kind: "list";
elements: Type;
dimensions: number[];
};
The elements of a list are ordered.
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.
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 ordered.
TupleType
type TupleType = {
kind: "tuple";
elements: NamedElement[];
};
Type
type Type =
| PrimitiveType
| AlgebraicType
| NegationType
| CollectionType
| ListType
| SetType
| MapType
| TupleType
| 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 = (name) => Type | undefined;