Skip to content

Files

Latest commit

May 14, 2025
1dad595 · May 14, 2025

History

History
21968 lines (14605 loc) · 576 KB

kb-compute-engine.md

File metadata and controls

21968 lines (14605 loc) · 576 KB
title slug
Compiling Expressions
/compute-engine/guides/compiling/
The Compute Engine **LaTeX expressions** can compile expressions to **JavaScript functions**!

Introduction

Some expressions can take a long time to evaluate numerically, for example if they contain a large number of terms or involve a loop \((\sum\) or \(\prod\)).

In this case, it is useful to compile the expression into a JavaScript function that can be evaluated much faster.

For example this approximation: $ \pi \approx \textstyle\sqrt{6\sum^{10^6}_{n=1}\frac{1}{n^2}} $

const expr = ce.parse("\\sqrt{6\\sum^{10^2}_{n=1}\\frac{1}{n^2}}");

// Numerical evaluation using the Compute Engine
console.time('evaluate');
console.timeEnd('evaluate', expr.evaluate());

// Compilation to a JavaScript function and execution
console.time('compile');
const fn = expr.compile();
console.timeEnd('compile', fn());

Compiling

To get a compiled version of an expression use the expr.compile() method:

const expr = ce.parse("2\\prod_{n=1}^{\\infty} \\frac{4n^2}{4n^2-1}");
const fn = expr.compile();

To evaluate the compiled expression call the function returned by expr.compile():

console.log(fn());
// ➔ 3.141592653589793

If the expression cannot be compiled, the compile() method will throw an error.

Arguments

The function returned by expr.compile() can be called with an object literal containing the value of the arguments:

const expr = ce.parse("n^2");
const fn = expr.compile();

for (let i = 1; i < 10; i++) console.log(fn({ n: i }));

To get a list of the unknows of an expression use the expr.unknowns property:

console.log(ce.parse("n^2").unknowns);

console.log(ce.parse("a^2+b^3").unknowns);

Limitations

The calculations are only performed using machine precision numbers.

Complex numbers, arbitrary precision numbers, and symbolic calculations are not supported.

Some functions are not supported.

If the expression cannot be compiled, the compile() method will throw an error. The expression can be numerically evaluated as a fallback:

function compileOrEvaluate(expr) {
  try {
    const fn = expr.compile();
    return   fn() + " (compiled)";
  } catch (e) {
    return   expr.N().numericValue + " (evaluated)";
  }
}

  // `expr.compile()` can handle this expression
  console.log(compileOrEvaluate(ce.parse("\\frac{\\sqrt{5}+1}{2}")));


  // `expr.compile()` cannot handle complex numbers, so it throws
  // and we fall back to numerical evaluation with expr.N()
  console.log(compileOrEvaluate(ce.parse("-i\\sqrt{-1}")));


title: Execution Constraints slug: /compute-engine/guides/execution-constraints/ date: Last Modified

Execution Constraints

Symbol Description
timeLimit
iterationLimit
--- title: Styling slug: /compute-engine/reference/styling/ ---

The functions in this section produce a visual difference that is not material to the interpretation of an expression such as text color and size or other typographic variations.

They are inert and the value of a ["Function", _expr_] expression is expr.

expr

expr, delim

Visually group expressions with an open delimiter, a close delimiter and separators between elements of the expression.

When serializing to LaTeX, render expr wrapped in delimiters.

The Delimiter function is inert and the value of a ["Delimiter", _expr_] expression is expr.

expr is a function expression, usually a ["Sequence"]. It should not be a symbol or a number.

delim is an optional string:

  • when it is a single character it is a separator
  • when it is two characters, the first is the opening delimiter and the second is the closing delimiter
  • when it is three characters, the first is the opening delimiter, the second is the separator, and the third is the closing delimiter

The delimiters are rendered to LaTeX.

The open and close delimiters are a single character, one of: ()[]{}<>|‖⌈⌉⌊⌋⌜⌝⌞⌟⎰⎱". The open and close delimiters do not have to match. For example, "')]'" is a valid delimiter.

If an open or close delimiter is ., it is ignored.

The separator delimiter is also a single character, one of ,;.&:|- or U+00B7 (middle dot), U+2022 (bullet) or U+2026 (ellipsis).

If no delim is provided, a default delimiter is used based on the type of expr:

  • ["Sequence"] -> (,)
  • ["Tuple"], ["Single"], ["Pair"], ["Triple"] -> (,)
  • ["List"] -> [,]
  • ["Set"] -> {,}

width

When serializing to LaTeX, widthis the dimension of the spacing, in 1/18 em.

The Spacing function is inert and the value of a ["Spacing", _expr_] expression is expr.

expr, dictionary

  • expran expression
  • dictionarya dictionary with one or more of the following keys:
    • _"display"_:
      • "inline" for \textstyle
      • "block" for \displaystyle
      • "script" for \scriptstyle
      • "scriptscript" for \scriptscriptstyle
    • _"size"_: 1...10. Size 5 is normal, size 1 is smallest
    • _"color"_

The Style function is inert and the value of a ["Style", _expr_] expression is expr.

Read more about formatting of **matrixes** and **vectors**

title: Complex slug: /compute-engine/reference/complex/

Constants

Symbol Description
ImaginaryUnit $$ \imaginaryI $$ The imaginary unit, solution of x 2 + 1 = 0

Functions

z

Evaluate to the real part of a complex number.

["Real", ["Complex", 3, 4]]
// ➔ 3

z

Evaluate to the imaginary part of a complex number. If z is a real number, the imaginary part is zero.

["Imaginary", ["Complex", 3, 4]]
// ➔ 4

["Imaginary", "Pi"]
// ➔ 0

z

Evaluate to the complex conjugate of a complex number. The conjugates of complex numbers give the mirror image of the complex number about the real axis.

z = z \imaginaryI z

["Conjugate", ["Complex", 3, 4]]
// ➔ ["Complex", 3, -4]

z

Evaluate to the magnitude of a complex number.

The magnitude of a complex number is the distance from the origin to the point representing the complex number in the complex plane.

| z | = ( z ) 2 + ( z ) 2

["Abs", ["Complex", 3, 4]]
// ➔ 5

z

Evaluate to the argument of a complex number.

The argument of a complex number is the angle between the positive real axis and the line joining the origin to the point representing the complex number in the complex plane.

arg z = tan 1 z z

["Arg", ["Complex", 3, 4]]
// ➔ 0.9272952180016122

z

Return a tuple of the magnitude and argument of a complex number.

This corresponds to the polar representation of a complex number.

["AbsArg", ["Complex", 3, 4]]
// ➔ [5, 0.9272952180016122]

3 + 4 \imaginaryI = 5 ( cos 0.9272 + \imaginaryI sin 0.9272 ) = 5 \exponentialE 0.9272

z, n

Retrurn a list of the nth roots of a number z.

The complex roots of a number are the solutions of the equation z n = a .

// The three complex roots of unity (1)
["ComplexRoots", 1, 3]
// ➔ [1, -1/2 + sqrt(3)/2, -1/2 - sqrt(3)/2]

title: Compute Engine API Reference sidebar_label: API Reference slug: /compute-engine/api/ toc_max_heading_level: 3

import APIFilter from '@site/src/components/APIFilter'; import MemberCard from '@site/src/components/MemberCard';

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 circle
  • deg: degrees, 360 degrees is a full circle
  • grad: gradians, 400 gradians is a full circle
  • turn: turns, 1 turn is a full circle

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

:::info[THEORY OF OPERATIONS]

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. :::

:::info[THEORY OF OPERATIONS] 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.

:::

:::info[THEORY OF OPERATIONS] 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 to never).

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 calling simplify() 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.

:::info[Note] 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.

:::info[Note] 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.

:::info[Note] 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.

:::info[Note] 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.

:::info[Note] 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().

:::info[Note] 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().

:::info[Note] 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.

:::info[Note] 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.

:::info[Note] 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.

:::info[Note] 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 constant
  • Pi is constant
  • ["Divide", "Pi", 2] is constant
  • x is not constant, unless declared with a constant flag.
  • ["Add", "x", 2] is either constant only if x 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.

:::info[Note] Applicable to canonical and non-canonical expressions. :::

BoxedExpression.getSubexpressions()
getSubexpressions(operator): readonly BoxedExpression[]

All the subexpressions matching the named operator, recursively.

:::info[Note] Applicable to canonical and non-canonical expressions. :::

####### operator

string

BoxedExpression.subexpressions
readonly subexpressions: readonly BoxedExpression[];

All the subexpressions in this expression, recursively

:::info[Note] Applicable to canonical and non-canonical expressions. :::

BoxedExpression.symbols
readonly symbols: readonly string[];

All the symbols in the expression, recursively

:::info[Note] 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.neg()
neg(): BoxedExpression

Negate (additive inverse)

BoxedExpression.inv()
inv(): BoxedExpression

Inverse (multiplicative inverse)

BoxedExpression.abs()
abs(): BoxedExpression

Absolute value

BoxedExpression.add()
add(rhs): BoxedExpression

Addition

####### rhs

number | BoxedExpression

BoxedExpression.sub()
sub(rhs): BoxedExpression

Subtraction

####### rhs

BoxedExpression

BoxedExpression.mul()
mul(rhs): BoxedExpression

Multiplication

####### rhs

number | NumericValue | BoxedExpression

BoxedExpression.div()
div(rhs): BoxedExpression

Division

####### rhs

number | BoxedExpression

BoxedExpression.pow()
pow(exp): BoxedExpression

Power

####### exp

number | BoxedExpression

BoxedExpression.root()
root(exp): BoxedExpression

Exponentiation

####### exp

number | BoxedExpression

BoxedExpression.sqrt()
sqrt(): BoxedExpression

Square root

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.

:::info[Note] Applicable to canonical and non-canonical expressions. :::

####### sub

Substitution<SemiBoxedExpression>

####### options?

####### canonical

CanonicalOptions

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 }

:::info[Note] Applicable to canonical and non-canonical expressions. :::

####### fn

(expr) => BoxedExpression

####### options?

####### canonical

CanonicalOptions

####### 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 the condition predicate is true, replace it with the replace 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.

:::info[Note] 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.

:::info[Note] 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.

:::info[Note] Applicable to canonical and non-canonical expressions. :::

####### pattern

BoxedExpression

####### options?

PatternMatchOptions

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

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

BoxedExpression

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.

:::info[Note] Applicable to canonical and non-canonical expressions. :::

####### rhs

BoxedExpression

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.

:::info[Note] 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.

:::info[Note] 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.

:::info[Note] 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

:::info[Note] 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 matches a + x with a = 0
  • x matches ax 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 the InvisibleOperator 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: replace Subtract with Add, removes 0 in addition, promote complex numbers (["Add", "a", ["Complex", 0, "b"] -> ["Complex", "a", "b"])
  • Power: simplify Power 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 a Rational number if numerator and denominator are integers, simplify, e.g. \frac{x}{1} -> x...
  • Flatten: remove any unnecessary Delimiter 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] matches 2, with a value of _a of 0. 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.matches()
matches(t): boolean

####### t

string | BoxedType

Assumption.isGreater()
isGreater(other): boolean

####### other

BoxedExpression

Assumption.isGreaterEqual()
isGreaterEqual(other): boolean

####### other

BoxedExpression

Assumption.isLess()
isLess(other): boolean

####### other

BoxedExpression

Assumption.isLessEqual()
isLessEqual(other): boolean

####### other

BoxedExpression

Assumption.isEqual()
isEqual(other): boolean

####### other

BoxedExpression

Assumption.toExpression()
toExpression(ce, x): BoxedExpression

####### ce

ComputeEngine

####### x

string

ExpressionMapInterface<U>

ExpressionMapInterface.has()
has(expr): boolean

####### expr

BoxedExpression

ExpressionMapInterface.get()
get(expr): U

####### expr

BoxedExpression

ExpressionMapInterface.set()
set(expr, value): void

####### expr

BoxedExpression

####### value

U

ExpressionMapInterface.delete()
delete(expr): void

####### expr

BoxedExpression

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.

:::info[Note] 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.url
url: string;

A URL pointing to more information about this symbol or operator.

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 returns Infinity Infinite collections are not indexable: they have no at() 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

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 has holdUntil: 'never': it is substituted during canonicalization
  • x has holdUntil: 'evaluate' (variables)
  • Pi has holdUntil: '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;

:::info[THEORY OF OPERATIONS]

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 or none 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.index
index: number;

The index of the current token

Parser.atEnd
readonly atEnd: boolean;

True if the last token has been reached. Consider also atTerminator().

Parser.peek
readonly peek: string;

Return the next token, without advancing the index

Parser.atBoundary

Parser.getSymbolType()
getSymbolType(id): BoxedType

####### id

string

Parser.pushSymbolTable()
pushSymbolTable(): void

Parser.popSymbolTable()
popSymbolTable(): void

Parser.addSymbol()
addSymbol(id, type): void

####### id

string

####### type

string | BoxedType

Parser.atTerminator()
atTerminator(t): boolean

Return true if the terminator condition is met or if the last token has been reached.

####### t

Terminator

Parser.nextToken()
nextToken(): string

Return the next token and advance the index

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?

Terminator

Parser.parsePostfixOperator()
parsePostfixOperator(lhs, until?): Expression

Parse a postfix operator, such as ' or !.

Prefix, infix and matchfix operators are handled by parseExpression()

####### lhs

Expression

####### 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.parseNumber()
parseNumber(): Expression

Parse a number.

Parser.addBoundary()
addBoundary(boundary): void

Boundaries are used to detect the end of an expression.

They are used for unusual syntactic constructs, for example \int \sin x dx where the dx is not an argument to the \sin function, but a boundary of the integral.

They are also useful when handling syntax errors and recovery.

For example, \begin{bmatrix} 1 & 2 { \end{bmatrix} has an extraneous {, but the parser will attempt to recover and continue parsing when it encounters the \end{bmatrix} boundary.

####### boundary

string[]

Parser.removeBoundary()
removeBoundary(): void

Parser.matchBoundary()
matchBoundary(): boolean

Parser.boundaryError()
boundaryError(msg): Expression

####### msg

string | [string, ...Expression[]]

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.applyFunctionStyle()
applyFunctionStyle: (expr, level) => DelimiterScale;

Styles

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

Expression

####### def?

IndexedLatexDictionaryEntry

Serializer.serializeSymbol()
serializeSymbol(expr): string

####### expr

Expression

Serializer.wrapString()
wrapString(s, style, delimiters?): string

Output s surrounded by delimiters.

If delimiters is not specified, use ()

####### s

string

####### style

DelimiterScale

####### delimiters?

string

Serializer.wrapArguments()
wrapArguments(expr): string

A string with the arguments of expr fenced appropriately and separated by commas.

####### expr

Expression

Serializer.wrapShort()
wrapShort(expr): string

Add a group fence around the expression if it is short (not a function)

####### expr

Expression

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.re

The real part of this numeric value.

Can be negative, 0 or positive.

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.sgn()
abstract sgn(): -1 | 0 | 1

The sign of complex numbers is undefined

NumericValue.N()
abstract N(): NumericValue

Return a non-exact representation of the numeric value

NumericValue.neg()
abstract neg(): NumericValue

NumericValue.inv()
abstract inv(): NumericValue

NumericValue.add()
abstract add(other): NumericValue

####### other

number | NumericValue

NumericValue.sub()
abstract sub(other): NumericValue

####### other

NumericValue

NumericValue.mul()
abstract mul(other): NumericValue

####### other

number | Decimal | NumericValue

NumericValue.div()
abstract div(other): NumericValue

####### other

number | NumericValue

NumericValue.pow()
abstract pow(n): NumericValue

####### n

number | NumericValue | { re: number; im: number; }

NumericValue.root()
abstract root(n): NumericValue

####### n

number

NumericValue.sqrt()
abstract sqrt(): NumericValue

NumericValue.gcd()
abstract gcd(other): NumericValue

####### other

NumericValue

NumericValue.abs()
abstract abs(): NumericValue

NumericValue.ln()
abstract ln(base?): NumericValue

####### base?

number

NumericValue.exp()
abstract exp(): NumericValue

NumericValue.floor()
abstract floor(): NumericValue

NumericValue.ceil()
abstract ceil(): NumericValue

NumericValue.round()
abstract round(): NumericValue

NumericValue.eq()
abstract eq(other): boolean

####### other

number | NumericValue

NumericValue.lt()
abstract lt(other): boolean

####### other

number | NumericValue

NumericValue.lte()
abstract lte(other): boolean

####### other

number | NumericValue

NumericValue.gt()
abstract gt(other): boolean

####### other

number | NumericValue

NumericValue.gte()
abstract gte(other): boolean

####### other

number | 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.toJSON()
toJSON(): any

Object.toJSON

NumericValue.print()
print(): void

SmallInteger

type SmallInteger = IsInteger<number>;

A SmallInteger is an integer < 1e6

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;

IBigNum.bignum()
bignum(value): Decimal

####### value

string | number | bigint | 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.

TensorDataType

type TensorDataType = keyof DataTypeMap;

The type of the cells in a tensor.

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

TensorField.expression()
expression(x): BoxedExpression

####### x

T

TensorField.isZero()
isZero(x): boolean

####### x

T

TensorField.isOne()
isOne(x): boolean

####### x

T

TensorField.equals()
equals(lhs, rhs): boolean

####### lhs

T

####### rhs

T

TensorField.add()
add(lhs, rhs): T

####### lhs

T

####### rhs

T

TensorField.addn()
addn(...xs): T

####### xs

...T[]

TensorField.neg()
neg(x): T

####### x

T

TensorField.sub()
sub(lhs, rhs): T

####### lhs

T

####### rhs

T

TensorField.mul()
mul(lhs, rhs): T

####### lhs

T

####### rhs

T

TensorField.muln()
muln(...xs): T

####### xs

...T[]

TensorField.div()
div(lhs, rhs): T

####### lhs

T

####### rhs

T

TensorField.pow()
pow(rhs, n): T

####### rhs

T

####### n

number

TensorField.conjugate()
conjugate(x): T

####### x

T

Tensor<DT>

Extends

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.at()
at(...indices): DataTypeMap[DT]

####### indices

...number[]

Tensor.diagonal()
diagonal(axis1?, axis2?): DataTypeMap[DT][]

####### axis1?

number

####### axis2?

number

Tensor.trace()
trace(axis1?, axis2?): DataTypeMap[DT]

####### axis1?

number

####### axis2?

number

Tensor.reshape()
reshape(...shape): Tensor<DT>

####### shape

...number[]

Tensor.flatten()
flatten(): DataTypeMap[DT][]

Tensor.upcast()
upcast<DT>(dtype): Tensor<DT>

• DT extends keyof DataTypeMap

####### dtype

DT

Tensor.transpose()
transpose(axis1?, axis2?): Tensor<DT>

####### axis1?

number

####### axis2?

number

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.minor()
minor(axis1, axis2): DataTypeMap[DT]

####### axis1

number

####### axis2

number

Tensor.map1()
map1(fn, scalar): Tensor<DT>

####### fn

(lhs, rhs) => DataTypeMap[DT]

####### scalar

DataTypeMap[DT]

Tensor.map2()
map2(fn, rhs): Tensor<DT>

####### fn

(lhs, rhs) => DataTypeMap[DT]

####### rhs

Tensor<DT>

Tensor.add()
add(other): Tensor<DT>

####### other

Tensor<DT> | DataTypeMap[DT]

Tensor.subtract()
subtract(other): Tensor<DT>

####### other

Tensor<DT> | DataTypeMap[DT]

Tensor.multiply()
multiply(other): Tensor<DT>

####### other

Tensor<DT> | DataTypeMap[DT]

Tensor.divide()
divide(other): Tensor<DT>

####### other

Tensor<DT> | DataTypeMap[DT]

Tensor.power()
power(other): Tensor<DT>

####### other

Tensor<DT> | DataTypeMap[DT]

Tensor.equals()
equals(other): boolean

####### other

Tensor<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.[toPrimitive]()
toPrimitive: string

####### hint

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 or E 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 type

    • expression
    • error: an invalid value, such as ["Error", "missing"]
    • nothing: the type of the Nothing symbol, the unit type
    • never: the bottom type
    • unknown: a value whose type is not known
  • expression:

    • a symbolic expression, such as ["Add", "x", 1]
    • <value>
    • symbol: a symbol, such as x.
    • function: a function literal such as ["Function", ["Add", "x", 1], "x"].
  • value

    • scalar
      • <number>
      • boolean: a boolean value: True or False.
      • 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 number
      • set: 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 plus NaN
  • complex: a number with non-zero real and imaginary parts = finite_complex plus ComplexInfinity
  • 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 number
  • finite_integer: a whole number
  • real: 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[];
};

TypeReference

type TypeReference = {
  kind: "reference";
  ref: string;
};

Nominal typing

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;
--- title: Trigonometry slug: /compute-engine/reference/trigonometry/ date: Last Modified ---

Constants

Symbol Value
Degrees $$ \frac{\pi}{180} = 0.017453292519943295769236907\ldots $$
Pi $$ \pi \approx 3.14159265358979323\ldots $$

Trigonometric Functions

Function Inverse Hyperbolic Area Hyperbolic
Sin Arcsin Sinh Arsinh
Cos Arccos Cosh Arcosh
Tan Arctan
Arctan2
Tanh Artanh
Cot Acot Coth Arcoth
Sec Asec Sech Asech
Csc Acsc Csch Acsch
Function
FromPolarCoordinates Converts $$ (\operatorname{radius}, \operatorname{angle}) \longrightarrow (x, y)$$
ToPolarCoordinates Converts
The following macros are not allowed: operatorname

$$(x, y) \longrightarrow (\operatorname{radius}, \operatorname{angle})$$
Hypot
The following macros are not allowed: operatorname

$$\operatorname{Hypot}(x,y) = \sqrt{x^2+y^2}$$
Haversine $$ \operatorname{Haversine}(z) = \sin(\frac{z}{2})^2 $$
The Haversine function was important in navigation because it appears in the haversine formula, which is used to reasonably accurately compute distances on an astronomic spheroid given angular positions (e.g., longitude and latitude).
InverseHaversine
The following macros are not allowed: operatorname

$$\operatorname{InverseHaversine}(z) = 2 \operatorname{Arcsin}(\sqrt{z})$$
--- title: Logic slug: /compute-engine/reference/logic/ date: Last Modified ---

Constants

Symbol LaTeX Notation
True \top $$ \top $$
’’ \mathsf{T} $$ \mathsf{T}$$
’’ \operator{True} $$ \operatorname{True}$$
False \bot $$ \bot $$
’’ \mathsf{F} $$ \mathsf{F}$$
’’ \operatorname{False} $$ \operatorname{False}$$

Logical Operators

Symbol LaTeX Notation
And p \land q $$ p \land q$$ Conjunction
’’ p \operatorname{and} q $$ p \operatorname{and} q$$
Or p \lor q $$ p \lor q$$ Disjunction
’’ p \operatorname{or} q $$ p \operatorname{or} q$$
Xor p \veebar q $$ p \veebar q$$ Exclusive Or
Nand p \barwedge q $$ p \barwedge q$$ Not And
Nor p \char"22BD q $$ p \char"22BD q$$ Not Or
Not \lnot p $$ \lnot p$$ Negation
’’ \operatorname{not} p $$ \operatorname{not} p$$
Equivalent p \iff q $$ p \iff q$$
’’ p \Leftrightarrow q $$ p \Leftrightarrow q$$
Implies p \implies q p q
’’ p \Rightarrow q p q
Proves p \vdash q p q
Entails p \vDash q p q
Satisfies p \models q p q

Quantifiers

condition, predicate

The ForAll function represents the universal quantifier.

The condition is the variable or variables that are being quantified over or the set of elements that the variable can take.

The predicate is the statement that is being quantified.

The condition and the predicate are separated by a comma, a colon, or a vertical bar. The predicate can also be enclosed in parentheses after the condition.

["ForAll", "x", ["Greater", ["Add", "x", 1], "x"]]

["ForAll", ["Element", "x", "RealNumbers"], ["Greater", ["Square", "x"], 0]]

condition, predicate

The Exists function represents the existential quantifier.

The condition is the variable or variables that are being quantified over, and the predicate is the statement that is being quantified.

The condition and the predicate are separated by a comma, a colon, or a vertical bar. The predicate can also be enclosed in parentheses after the condition.

["Exists", "x", ["Equal", ["Square", "x"], 1]]

["Exists", ["Element", "x", "RealNumbers"], ["Equal", ["Square", "x"], 1]]

condition, predicate

The ExistsUnique function represents the unique existential quantifier.

--- title: Core slug: /compute-engine/reference/core/

The functions described in this section are part of the **core** of the Compute Engine.

Constants

The symbols below are inert constants. They are used as tags and have no value other than themselves.

Symbol Description
All All the possible values apply
None None of the possible values apply
Nothing An optional expression is not present. Used in sparse list to indicate skipped elements.
Undefined The result is not defined.
Note that for numbers, the equivalent is NaN (Not a Number)
["List", 2, "Nothing", 3]

Declaring, Assigning and Assuming

A ["Declare"] expression is used to declare a symbol in the current scope.

Once a symbol has been declared, its value can be changed using an ["Assign"] expression.

An ["Assume"] expression is used to assert a predicate about an expression. It is used to provide additional information to the system, for example to indicate that a variable is positive.

symbol, type_

symbol, type, value

Declare a new symbol in the current scope, and set its value and type.

If the symbol already has a definition in the current scope, evaluate to an error, otherwise evaluate to value.

This is equivalent to let in JavaScript or var in Python.

To change the value of an existing symbol, use an ["Assign"] expression.

Declare is not a pure function since it changes the state of the Compute Engine.

Read more about using ce.declare() to declare a new symbol.

symbol, value

Set the value of symbol to value.

If symbol has not been declared in the current scope, consider parent scopes until a definition for the symbol is found.

If a definition is found, change the value of the symbol to value if the value is compatible with the type of the symbol: once set, the type of a symbol cannot be changed.

If there is no definition for the symbol, add a new definition in the current scope, and use the value to infer the type of the symbol.

This is equivalent to = in may programming languages.

Assign is not a pure function.

Read more about using Assign to change the value of a symbol or function.

predicate

The predicate is an expression that evaluates to True or False.

The symbols in the predicate expression may be free, i.e. they may not have been declared yet. Asserting an assumption does not declare the symbols in the predicate.

The predicate can take the form of:

  • an equality: ["Assume", ["Equal", "x", 3]]
  • an inequality: ["Assume", ["Greater", "x", 0]]
  • a membership expression: ["Assume", ["Element", "x", "Integers"]]

Assign is not a pure function since it changes the state of the Compute Engine.

Structural Operations

The following functions can be applied to non-canonical expressions. The do not depend on the canonical form, but reflect the structure of the expression.

symbol_

Evaluate to a dictionary expression containing information about a symbol such as its type, its attributes, its value, etc...

expression

Evaluate to the head of expression

["Head", ["Add", 2, 3]]

// ➔ "Add"

expression

Evaluate to a sequence of the arguments of expression.

["Tail", ["Add", 2, 3]]
// ➔ ["Sequence", 2, 3]

Tail can be used to change the head of an expression, for example:

["Multiply", ["Tail", ["Add", 2, 3]]]
// ➔ ["Multiply", 2, 3]

expression

Tag an expression that should be kept in an unevaluated form

expression

Evaluate to its argument

In the mathematical sense, this is an operator (a function that takes a function as an argument and returns a function).

Inspecting an Expression

The following functions can be used to obtain information about an expression.

expression

Evaluate to the domain of expression

["Domain", 2.4531]

// ➔ "RealNumbers"

expression1, expression2

Evaluate to True if the two expressions are structurally identical, otherwise evaluate to False.

["IsSame", ["Add", 2, 3], ["Add", 2, 3]]
// ➔ True

To compare two expressions for mathematical equality, use Equal.

To compare two expressions structurally, but ignoring the order of the arguments of commutative functions, use CanonicalForm.

See Comparing Expressions for other options to compare two expressions, such as the Equal function.

Transforming an Expression

expression

Apply a sequence of definitions to an expression in order to reduce, simplify and calculate its value. Overrides Hold and hold attributes of a function.

Evaluate only performs exact calculations. To perform numerical approximations, use N.

Read more about exact calculations and approximate calculations.

expression

Apply the distributive law if the expression is a product or power of sums.

For example: a(b + c) = ab + ac

  • Expand the terms of the expression if it is a sum or negate.
  • If the expression is a fraction, expand the numerator.
["Expand", ["Power", ["Add", "x", 1], 2]]
// ➔ ["Add", 1, ["Multiply", 2, "x"], ["Power", "x", 2]]

expression

Expand an expression, recursively.

["ExpandAll", ["Power", ["Multiply", ["Add", "x", 1], 3], 2]]
// ➔ ["Add", 1, ["Multiply", 6, "x"], ["Multiply", 6, ["Power", "x", 2]], ["Power", "x", 3]]

expression

Factor an expression.

["Factor", ["Add", ["Multiply", 2, "x"], ["Multiply", 2, "y"]]]
// ➔ ["Multiply", 2, ["Add", "x", "y"]]

expression

Combine the terms of a sum of fractions into a single fraction.

["Together", ["Add", ["Divide", 1, 2], ["Divide", 1, 3]]]
// ➔ ["Divide", 5, 6]

expression

The Simplify function applies a sequence of transformations to an expression in order to reduce, simplify and calculate its value.

expression

expression, form-1, form-2, ...

If expression is already canonical, this function has no effect.

If there are no form-n arguments, the expression is transformed to its canonical form.

If some form-n arguments are provided, they indicate one or more canonical transformations to apply to the expression. The following canonical forms are supported:

  • Order: If expression is a commutative function, sort the arguments according to the canonical order of the arguments of the function.
["CanonicalForm", ["Add", 3, 2, 1], "Order"]
// -> ["Add", 1, 2, 3]

This can be useful to compare two non-canonical expressions for equality, for example:

["IsSame",
  ["Add", 1, "x"], 
  ["Add", "x", 1]
]
// -> False

["IsSame", 
  ["CanonicalForm", ["Add", 1, "x"], "Order"], 
  ["CanonicalForm", ["Add", "x", 1], "Order"]
]
// -> True
  • Flatten: Simplify associative expressions, remove any unnecessary delimiters indicating the order of operations, flattens any Sequence expressions.
["CanonicalForm", ["Add", 1, ["Add", 2, 3]], "Flatten"]
// -> ["Add", 1, 2, 3]

["CanonicalForm", ["Add", 1, ["Delimiter", ["Sequence", 2, 3]]], "Flatten"] 
// -> ["Add", 1, 2, 3]

["CanonicalForm", ["Add", 1, ["Sequence", 2, 3]], "Flatten"]
// -> ["Add", 1, 2, 3]
  • Number: Transform some number forms, for example ["Add", 2, ["Multiply", 3, "ImaginaryI"]] to ["Complex", 2, 3], simplify and normalize numerator and denominator of rational numbers, etc...

  • InvisibleOperator: Remove any invisible operators that may be contained in the expression and replace them with Multiply or function application, depending on the context

["CanonicalForm", ["InvisibleOperator", "2", "x"], "InvisibleOperator"]
// -> ["Multiply", 2, "x"]
  • Multiply: If expression is a Multiply function, simplify it by combining the coefficients and the factors, transform product to a Power expression when possible.
["CanonicalForm", ["Multiply", 2, 3, "x"], "Multiply"]
// -> ["Multiply", 6, "x"]
  • Add: If expression is an Add function, remove any 0, transform sum into multiplication when possible. If expression is a Subtract transform it into an Add. If expression is a Negate transform it into a Multiply or negate number literals.

  • Power: Transform Exp, Square, Sqrt, Root function to a Power expression;

["CanonicalForm", ["Exp", "x"], "Power"]

```json example
["CanonicalForm", ["Power", 2, 3], "Power"]
// -> ["Power", 8]

To compare the input from a mathfield with an expected answer, you could use:

const correct = ce.parse(mf.value, {canonical: "Order"})
    .isSame(ce.parse("1+x"))

Both 1+x and x+1 will return true, but 2-1+x will return false.

Note: see also the options for the canonical option of ce.parse() and ce.box() which can also be used to specify a custom canonical form:

const correct = ce.parse(mf.value, {canonical: "Order"})
    .isSame(ce.parse("x+1"))

expression

Evaluate to a numerical approximation of the expression.

["N", "Pi"]

// ➔ 3.141592653589793

Core Functions

error-code, context

Tag an expression that could not be interpreted correctly. It may have a syntax error, a reference to an unknown symbol or some other problem.

The first argument, error-code is either a string, or an ["ErrorCode"] expression.

The context is an optional expression that provides additional information about the error.

symbol

Evaluate to the inverse function of its argument for example Arcsin for Sin.

[["InverseFunction", "Sin"], "x"]

In the mathematical sense, this is an operator (a function that takes a function as an argument and returns a function).

expression

Evaluate to a string made from the concatenation of the arguments converted to strings

["String", "x", 2]

// ➔ "'x2'"

expression

Evaluate to a new symbol made of a concatenation of the arguments.

["Symbol", "x", 2]

// ➔ "x2"

The symbol is not declared, it remains a free variable. To declare the symbol use Declare.

["Declare", ["Symbol", "x", 2], "RealNumbers"]

Parsing and Serializing Latex

string

If expr is a ["LatexString"] expression, evaluate to a MathJSON expression corresponding to the LaTeX string.

["Parse", ["LatexString", "'\frac{\pi}{2}'"]]

// ➔ ["Divide", "Pi", 2]

expression

Evaluate to a LatexString which is the expression serialized to LaTeX

string

Tag a string as a LaTeX string

Superscripts and Subscripts

These functions are all inert functions, that is they evaluate to themselves.

Function Description
Subminus $$ x_- $$
Subplus $$ x_+$$
Subscript $$ x_{n} $$
Substar $$ x_*$$
Superdagger $$ x^\dagger$$
Superminus $$ x^-$$
Superplus $$ x^+$$
Superstar $$ x^*$$ When the argument is a complex number, indicate the conjugate.
--- title: Changelog - Compute Engine sidebar_label: Changelog slug: /compute-engine/changelog/ toc_max_heading_level: 2 ---

Compute Engine Changelog

import ChangeLog from '@site/src/components/ChangeLog';

## Coming Soon

Breaking Changes

  • The expr.value property reflects the value of the expression if it is a number literal or a symbol with a literal value. If you previously used the expr.value property to get the value of an expression, you should now use the expr.N().valueOf() method instead. The valueOf() method is suitable for interoperability with JavaScript, but it may result in a loss of precision for numbers with more than 15 digits.

  • BoxedExpr.sgn now returns undefined for complex numbers, or symbols with a complex-number value.

  • The ce.assign() method previously accepted ce.assign("f(x, y)", ce.parse("x+y")). This is now deprecated. Use ce.assign("f", ce.parse("(x, y) \\mapsto x+y") instead.

  • It was previously possible to invoke expr.evaluate() or expr.N() on a non-canonical expression. This will now return the expression itself.

    To evaluate a non-canonical expression, use expr.canonical.evaluate() or expr.canonical.N().

    That's also the case for the methods numeratorDenominator(), numerator(), and denominator().

    In addition, invoking the methods inv(), abs(), add(), mul(), div(), pow(), root(), ln() will throw an error if the expression is not canonical.

New Features and Improvements

  • The symbol type can be refined to match a specific symbol. For example symbol<True>. The type expression can be refined to match expressions with a specific operator, for example expression<Add> is a type that matches expressions with the Add operator. The numeric types can be refined with a lower and upper bound. For example integer<0..10> is a type that matches integers between 0 and 10. The type real<1..> matches real numbers greater than 1 and rational<..0> matches non-positive rational numbers.

  • The bindings of symbols and function expressions is now consistently done during canonicalization.

  • It was previously not possible to change the type of an identifier from a function to a value or vice versa. This is now possible.

  • Added a rule to solve the equation a^x + b = 0

  • The LaTeX parser now supports the \placeholder[]{}, \phantom{}, \hphantom{}, \vphantom{}, \mathstrut, \strut and \smash{} commands.

  • The range of recognized sign values, i.e. as returned from BoxedExpression.sgn has been simplified (e.g. '...-infinity' and 'nan' have been removed)

  • The Power canonical-form is less aggressive - only carrying-out ops. as listed in doc. - is much more careful in its consideration of operand types & values... (for example, typically, exponents are required to be numbers: e.g. x^1 will simplify, but x^y (where y===0), or x^{1+0}, will not)

Issues Resolved

  • Ensure expression LaTeX serialization is based on MathJSON generated with matching 'pretty' formatting (or not), therefore resulting in LaTeX with less prettification, where prettify == false (#daef87f)

  • Symbols declare with a constant flag are now not marked as 'inferred'

  • Some BoxedSymbols properties now more consistently return 'undefined', instead of a 'boolean' (i.e. because the symbol is non-bound)

  • Some expr.root() computations

  • Canonical-forms

    • Fixes the Number form
    • Forms (at least, 'Number', 'Power') do not mistakenly fully canonicalize operands
    • This (partial canonicalization) now substitutes symbols (constants) with a holdUntil value of never during/prior-to canonicalization (i.e. just like for full canonicalization)

0.29.1 2025-03-31

  • #231 During evaluation, some numbers, for example 10e-15 were incorrectly rounded to 0.

0.28.0 2025-02-06

Issues Resolved

  • #211 More consistent canonicalization and serialization of exact numeric values of the form (a√b)/c.

  • #219 The invisibleOperator canonicalization previously also canonicalized some multiplication.

  • #218 Improved performance of parsing invisible operators, including fixing some cases where the parsing was incorrect.

  • #216 Correctly parse subscripts with a single character, for example x_1.

  • #216 Parse some non-standard integral signs, for example \int x \cdot \differentialD x (both the \cdot and the \differentialD are non-standard).

  • #210 Numeric approximation of odd nth roots of negative numbers evaluate correctly.

  • #153 Correctly parse integrals with \limits, e.g. \int\limits_0^1 x^2 \mathrm{d} x.

  • Correctly serialize to ASCIIMath Delimiter expressions.

  • When inferring the type of numeric values do not constrain them to be real. As a result:

    ce.assign('a', ce.parse('i'));
    ce.parse('a+1').evaluate().print();

    now returns 1 + i instead of throwing a type error.

  • Correctly parse and evaluate unary and binary \pm and \mp operators.

New Features and Improvements

  • expr.isEqual() will now return true/false if the expressions include the same unknowns and are structurally equal after expansion and simplifications. For example:

    console.info(ce.parse('(x+1)^2').isEqual(ce.parse('x^2+2x+1')));
    // -> true

Asynchronous Operations

Some computations can be time-consuming, for example, computing a very large factorial. To prevent the browser from freezing, the Compute Engine can now perform some operations asynchronously.

To perform an asynchronous operation, use the expr.evaluateAsync method. For example:

try {
  const fact = ce.parse('(70!)!');
  const factResult = await fact.evaluateAsync();
  factResult.print();
} catch (e) {
  console.error(e);
}

It is also possible to interrupt an operation, for example by providing a pause/cancel button that the user can press. To do so, use an AbortController object and a signal. For example:

const abort = new AbortController();
const signal = abort.signal;
setTimeout(() => abort.abort(), 500);
try {
  const fact = ce.parse('(70!)!');
  const factResult = await fact.evaluateAsync({ signal });
  factResult.print();
} catch (e) {
  console.error(e);
}

In the example above, we trigger an abort after 500ms.

It is also possible to control how long an operation can run by setting the ce.timeLimit property with a value in milliseconds. For example:

ce.timeLimit = 1000;
try {
  const fact = ce.parse('(70!)!');
  fact.evaluate().print();
} catch (e) {
  console.error(e);
}

The time limit applies to either the synchronous or asynchronous evaluation.

The default time limit is 2,000ms (2 seconds).

When an operation is canceled either because of a timeout or an abort, a CancellationError is thrown.

0.27.0 2024-12-02

  • #217 Correctly parse LaTeX expressions that include a command followed by a * such as \\pi*2.

  • #217 Correctly calculate the angle of trigonometric expressions with an expression containing a reference to Pi, for example \\sin(\\pi^2).

  • The Factorial function will now time out if the argument is too large. The timeout is signaled by throwing a CancellationError.

  • When specifying exp.toMathJSON({shorthands:[]}), i.e., not to use shorthands in the MathJSON, actually avoid using shorthands.

  • Correctly use custom multiply, plus, etc. for LaTeX serialization.

  • When comparing two numeric values, the tolerance is now used to determine if the values are equal. The tolerance can be set with the ce.tolerance property.

  • When comparing two expressions with isEqual() the values are compared structurally when necessary, or with a stochastic test when the expressions are too complex to compare structurally.

  • Correctly serialize nested superscripts, e.g. x^{y^z}.

  • The result of evaluating a Hold expression is now the expression itself.

  • To prevent evaluation of an expression temporarily, use the Unevaluated function. The result of evaluating an Unevaluated expression is its argument.

  • The type of a Hold expression was incorrectly returned as string. It now returns the type of its argument.

  • The statistics function (Mean, Median, Variance, StandardDeviation, Kurtosis, Skewness, Mode, Quartiles and InterQuartileRange) now accept as argument either a collection or a sequence of values.

    ce.parse("\\mathrm{Mean}([7, 2, 11])").evaluate().print();
    // -> 20/3
    ce.parse("\\mathrm{Mean}(7, 2, 11)").evaluate().print();
    // -> 20/3
  • The Variance and StandardDeviation functions now have variants for population statistics, PopulationVariance and PopulationStandardDeviation. The default is to use sample statistics.

    ce.parse("\\mathrm{PopulationVariance}([7, 2, 11])").evaluate().print();
    // -> 13.555
    ce.parse("\\mathrm{Variance}([7, 2, 11])").evaluate().print();
    // -> 20.333
  • The statistics function can now be compiled to JavaScript:

    const code = ce.parse("\\mathrm{Mean}(7, 2, 11)").compile();
    console.log(code());
    // -> 13.555
  • The statistics function calculate either using machine numbers or bignums depending on the precision. The precision can be set with the precision property of the Compute Engine.

  • The argument of compiled function is now optional.

  • Compiled expressions can now reference external JavaScript functions. For example:

    ce.defineFunction('Foo', {
      signature: 'number -> number',
      evaluate: ([x]) => ce.box(['Add', x, 1]),
    });
    
    const fn = ce.box(['Foo', 3]).compile({
      functions: { Foo: (x) => x + 1 },
    })!;
    
    console.info(fn());
    // -> 4
    ce.defineFunction('Foo', {
      signature: 'number -> number',
      evaluate: ([x]) => ce.box(['Add', x, 1]),
    });
    
    function foo(x) {
      return x + 1;
    }
    
    const fn = ce.box(['Foo', 3]).compile({
      functions: { Foo: foo },
    })!;
    
    console.info(fn());
    // -> 4

    Additionally, functions can be implicitly imported (in case they are needed by other JavaScript functions):

    ce.defineFunction('Foo', {
      signature: 'number -> number',
      evaluate: ([x]) => ce.box(['Add', x, 1]),
    });
    
    function bar(x, y) {
      return x + y;
    }
    
    function foo(x) {
      return bar(x, 1);
    }
    
    
    const fn = ce.box(['Foo', 3]).compile({
      functions: { Foo: 'foo' },
      imports: [foo, bar],
    })!;
    
    console.info(fn());
    // -> 4
  • Compiled expression can now include an arbitrary preamble (JavaScript source) that is executed before the compiled function is executed. This can be used to define additional functions or constants.

    ce.defineFunction('Foo', {
      signature: 'number -> number',
      evaluate: ([x]) => ce.box(['Add', x, 1]),
    });
    
    const code = ce.box(['Foo', 3]).compile({
      preamble: "function Foo(x) { return x + 1};",
    });
  • The hold function definition flag has been renamed to lazy

0.26.4 2024-10-17

  • #201 Identifiers of the form A_\text{1} were not parsed correctly.
  • #202 Fixed serialization of integrals and bigops.

0.26.3 2024-10-17

  • Correctly account for fractionalDigits when formatting numbers.
  • #191 Correctly handle \\lnot\\forall and \\lnot\\exists.
  • #206 The square root of 1000000 was canonicalized to 0.
  • #207 When a square root with a literal base greater than 1e6 was preceded by a non-integer literal number, the literal number was ignored during canonicalization.
  • #208 #204 Correctly evaluate numeric approximation of roots, e.g. \\sqrt[3]{125}.
  • #205 1/ln(0) was incorrectly evaluated to 1. It now returns 0.

0.26.1 2024-10-04

Issues Resolved

  • #194 Correctly handle the precedence of unary negate, for example in -5^{\frac12} or -5!.
  • When using a function definition with ce.declare(), do not generate a runtime error.

New Features and Improvements

  • Added .expand() method to boxed expression. This method expands the expression, for example ce.parse("(x+1)^2").expand() will return x^2 + 2x + 1.

0.26.0 2024-10-01

Breaking Changes

  • The property expr.head has been deprecated. Use expr.operator instead. expr.head is still supported in this version but will be removed in a future update.

  • The MathJSON utility functions head() and op() have been renamed to operator() and operand() respectively.

  • The methods for algebraic operations (add, div, mul, etc...) have been moved from the Compute Engine to the Boxed Expression class. Instead of calling ce.add(a, b), call a.add(b).

    Those methods also behave more consistently: they apply some additional simplication rules over canonicalization. For example, while ce.parse('1 + 2') return ["Add", 1, 2], ce.box(1).add(2) will return 3.

  • The ce.numericMode option has been removed. Instead, set the ce.precision property to the desired precision. Set the precision to "machine" for machine precision calculations (about 15 digits). Set it to "auto" for a default of 21 digits. Set it to a number for a greater fixed precision.

  • The MathJSON Dictionary element has been deprecated. Use a Dictionary expression instead.

  • The ExtendedRealNumbers, ExtendedComplexNumbers domains have been deprecated. Use the RealNumbers and ComplexNumbers domains instead.

  • The "Domain" expression has been deprecated. Use types instead (see below).

  • Some BoxedExpression properties have been removed:

    • Instead of expr.isZero, use expr.is(0).
    • Instead of expr.isNotZero, use !expr.is(0).
    • Instead of expr.isOne, use expr.is(1).
    • Instead of expr.isNegativeOne, use expr.is(-1).
  • The signature of ce.declare() has changed. In particular, the N handler has been replaced with evaluate.

// Before
ce.declare('Mean', {
  N: (ce: IComputeEngine): BoxedExpression => {
    return ce.number(1);
  },
});

// Now
ce.declare('Mean', { evaluate: (ops, { engine }) => ce.number(1) });

New Features and Improvements

  • New Simplification Engine

    The way expressions are simplified has been completely rewritten. The new engine is more powerful and more flexible.

    The core API remains the same: to simplify an expression, use expr.simplify().

    To use a custom set of rules, pass the rules as an argument to simplify():

    expr.simplify({rules: [
      "|x:<0| -> -x",
      "|x:>=0| -> x",
    ]});

    There are a few changes to the way rules are represented. The priority property has been removed. Instead, rules are applied in the order in which they are defined.

    A rule can also now be a function that takes an expression and returns a new expression. For example:

    expr.simplify({rules: [
      (expr) => {
        if (expr.operator !== 'Abs') return undefined;
        const x = expr.args[0];
        return x.isNegative ? x.negate() : expr;
      }
    ]});

    This can be used to perform more complex transformations at the cost of more verbose JavaScript code.

    The algorithm for simplification has been simplified. It attempts to apply each rule in the rule set in turn, then restarts the process until no more rules can be applied or the result of applying a rule returns a previously seen expression.

    Function definitions previously included a simplify handler that could be used to perform simplifications specific to this function. This has been removed. Instead, use a rule that matches the function and returns the simplified expression.

  • Types

    Previously, an expression was associated with a domain such as RealNumbers or ComplexNumbers. This has been replaced with a more flexible system of types.

    A type is a set of values that an expression can take. For example, the type real is the set of real numbers, the type integer is the set of integers,

    The type of an expression can be set with the type property. For example:

    const expr = ce.parse('\\sqrt{-1}');
    console.info(expr.type); // -> imaginary

    The type of a symbol can be set when declaring the symbol. For example:

    ce.declare('x', 'imaginary');

    In addition to primitive types, the type system supports more complex types such union types, intersection types, and function types.

    For example, the type real|imaginary is the union of the real and imaginary numbers.

    When declaring a function, the type of the arguments and the return value can be specified. For example, to declare a function f that takes two integers and returns a real number:

    ce.declare('f', '(integer, integer) -> real');

    The sets of numbers are defined as follows:

    • number - any number, real or complex, including NaN and infinity
    • non_finite_number - NaN or infinity
    • real
    • finite_real - finite real numbers (exclude NaN and infinity)
    • imaginary - imaginary numbers (complex numbers with a real part of 0)
    • finite_imaginary
    • complex - complex numbers with a real and imaginary part not equal to 0
    • finite_complex
    • rational
    • finite_rational
    • integer
    • finite_integer

    To check the type of an expression, use the isSubtypeOf() method. For example:

    let expr = ce.parse('5');
    console.info(expr.type.isSubtypeOf('rational')); // -> true
    console.info(expr.type.isSubtypeOf('integer')); // -> true
    
    expr = ce.parse('\\frac{1}{2}');
    console.info(expr.type.isSubtypeOf('rational')); // -> true
    console.info(expr.type.isSubtypeOf('integer')); // -> false

    As a shortcut, the properties isReal, isRational, isInteger are available on boxed expressions. For example:

    let expr = ce.parse('5');
    console.info(expr.isInteger); // -> true
    console.info(expr.isRational); // -> true

    They are equivalent to expr.type.isSubtypeOf('integer') and expr.type.isSubtypeOf('rational') respectively.

    To check if a number has a non-zero imaginary part, use:

    let expr = ce.parse('5i');
    console.info(expr.isNumber && expr.isReal === false); // -> true
  • Collections

    Support for collections has been improved. Collections include List, Set, Tuple, Range, Interval, Linspace and Dictionary.

    It is now possible to check if an element is contained in a collection using an Element expression. For example:

    let expr = ce.parse('[1, 2, 3]');
    ce.box(['Element', 3, expr]).print(); // -> True
    ce.box(['Element', 5, expr]).print(); // -> False

    To check if a collection is a subset of another collection, use the Subset expression. For example:

    ce.box(['Subset', 'Integers', 'RealNumbers']).print(); // -> True

    Collections can also be compared for equality. For example:

    let set1 = ce.parse('\\lbrace 1, 2, 3 \\rbrace');
    let set2 = ce.parse('\\lbrace 3, 2, 1 \\rbrace');
    console.info(set1.isEqual(set2)); // -> true

    There are also additional convenience methods on boxed expressions:

    • expr.isCollection
    • expr.contains(element)
    • expr.size
    • expr.isSubsetOf(other)
    • expr.indexOf(element)
    • expr.at(index)
    • expr.each()
    • expr.get(key)
  • Exact calculations

    The Compute Engine has a new backed for numerical calculations. The new backed can handle arbitrary precision calculations, including real and complex numbers. It can also handle exact calculations, preserving calculations with rationals and radicals (square root of integers). For example 1/2 + 1/3 is evaluated to 5/6 instead of 0.8(3).

    To get an approximate result, use the N() method, for example ce.parse("\\frac12 + \\frac13").N().

    Previously the result of calculations was not always an exact number but returned a numerical approximation instead.

    This has now been improved by introducing a NumericValue type that encapsulates exact numbers and by doing all calculations in this type. Previously the calculations were handled manually in the various evaluation functions. This made the code complicated and error prone.

    A NumericValue is made of:

    • an imaginary part, represented as a fixed-precision number
    • a real part, represented either as a fixed or arbitrary precision number or as the product of a rational number and the square root of an integer.

    For example:

    • 234.567
    • 1/2
    • 3√5
    • √7/3
    • 4-3i

    While this is a significant change internally, the external API remains the same. The result of calculations should be more predictable and more accurate.

    One change to the public API is that the expr.numericValue property is now either a machine precision number or a NumericValue object.

  • Rule Wildcards

    When defining a rule as a LaTeX expression, single character identifiers are interpreted as wildcards. For example, the rule x + x -> 2x will match any expression with two identical terms. The wildcard corresponding to x is _x.

    It is now possible to define sequence wildcards and optional sequence wildcards. Sequence wildcards match 1 or more expressions, while optional sequence wildcards match 0 or more expressions.

    They are indicated in LaTeX as ...x and ...x? respectively. For example:

    expr.simplify("x + ...y -> 2x");

    If expr is a + b + c the rule will match and return 2a

    expr.simplify("x + ...y? -> 3x");

    If expr is a + b + c the rule will match and return 3a. If expr is a the rule will match and return 3a.

  • Conditional Rules

    Rules can now include conditions that are evaluated at runtime. If the condition is not satisfied, the rules does not apply.

    For example, to simplify the expression |x|:

    expr.simplify({rules: [
      "|x_{>=0}| -> x",
      "|x_{<0}| -> -x",
    ]});

    The condition is indicated as a subscript of the wildcard. The condition can be one of:

    • boolean - a boolean value, True or False

    • string - a string of characters

    • number - a number literal

    • symbol

    • expression

    • numeric - an expression that has a numeric value, i.e. 2√3, 1/2, 3.14

    • integer - an integer value, -2, -1, 0, 1, 2, 3, ...

    • natural - a natural number, 0, 1, 2, 3, ...

    • real - real numbers, including integers

    • imaginary - imaginary numbers, i.e. 2i, 3√-1 (not including real numbers)

    • complex - complex numbers, including real and imaginary

    • rational - rational numbers, 1/2, 3/4, 5/6, ...

    • irrational - irrational numbers, √2, √3, π, ...

    • algebraic - algebraic numbers, rational and irrational

    • transcendental - transcendental numbers, π, e, ...

    • positive - positive real numbers, > 0

    • negative - negative real numbers, < 0

    • nonnegative - nonnegative real numbers, >= 0

    • nonpositive - nonpositive real numbers, <= 0

    • even - even integers, 0, 2, 4, 6, ...

    • odd - odd integers, 1, 3, 5, 7, ...

    • prime :A000040 - prime numbers, 2, 3, 5, 7, 11, ...

    • composite :A002808 - composite numbers, 4, 6, 8, 9, 10, ...

    • notzero - a value that is not zero

    • notone - a value that is not one

    • finite - a finite value, not infinite

    • infinite

    • constant

    • variable

    • function

    • operator

    • relation - an equation or inequality

    • equation

    • inequality

    • vector - a tensor of rank 1

    • matrix - a tensor of rank 2

    • list - a collection of values

    • set - a collection of unique values

    • tuple - a fixed length list

    • single - a tuple of length 1

    • pair - a tuple of length 2

    • triple - a tuple of length 3

    • collection - a list, set, or tuple

    • tensor - a nested list of values of the same type

    • scalar - not a tensor or list

    or one of the following expressions:

    • >0' -> positive,
    • \gt0' -> positive,
    • <0' -> negative,
    • \lt0' -> negative,
    • >=0' -> nonnegative,
    • \geq0' -> nonnegative,
    • <=0' -> nonpositive,
    • \leq0' -> nonpositive,
    • !=0' -> notzero,
    • \neq0' -> notzero,
    • !=1' -> notone,
    • \neq1' -> notone,
    • \in\Z' -> integer,
    • \in\mathbb{Z}' -> integer,
    • \in\N' -> natural,
    • \in\mathbb{N}' -> natural,
    • \in\R' -> real,
    • \in\mathbb{R}' -> real,
    • \in\C' -> complex,
    • \in\mathbb{C}' -> complex,
    • \in\Q' -> rational,
    • \in\mathbb{Q}' -> rational,
    • \in\Z^+' -> integer,positive,
    • \in\Z^-' -> intger,negative,
    • \in\Z^*' -> nonzero,
    • \in\R^+' -> positive,
    • \in\R^-' -> negative,
    • \in\R^*' -> real,nonzero,
    • \in\N^*' -> integer,positive,
    • \in\N_0' -> integer,nonnegative,
    • \in\R\backslash\Q' -> irrational,

    More complex conditions can be specified following a semi-colon, for example:

    expr.simplify({x -> 2x; x < 10});

    Note that this syntax complements the existing rule syntax, and can be used together with the existing, more verbose, rule syntax.

    expr.simplify({rules: [
      {match: "x + x", replace: "2x", condition: "x < 10"}
    ]});

    This advanced syntax can specify more complex conditions, for example above the rule will only apply if x is less than 10.

  • Improved results for Expand. In some cases the expression was not fully expanded. For example, 4x(3x+2)-5(5x-4) now returns 12x^2 - 17x + 20. Previously it returned 4x(3x+2)+25x-20.

  • AsciiMath serialization The expr.toString() method now returns a serialization of the expression using the AsciiMath format.

    The serialization to AsciiMath can be customized using the toAsciiMath() method. For example:

    console.log(ce.box(['Sigma', 2]).toAsciiMath({functions: {Sigma: 'sigma'}}));
    // -> sigma(2)
  • The tolerance can now be specified with a value of "auto" which will use the precision to determine a reasonable tolerance. The tolerance is used when comparing two numbers for equality. The tolerance can be specified with the ce.tolerance property or in the Compute Engine constructor.

  • Boxed expressions have some additional properties:

    • expr.isNumberLiteral - true if the expression is a number literal.This is equivalent to checking if expr.numericValue is not null.
    • expr.re - the real part of the expression, if it is a number literal, undefined if not a number literal.
    • expr.im - the imaginary part of the expression, if it is a number literal, undefined if not a number literal.
    • expr.bignumRe - the real part of the expression as a bignum, if it is a number literal, undefined if not a number literal or a bignum representation is not available.
    • expr.bignumIm - the imaginary part of the expression as a bignum, if it is a number literal, undefined if not a number literal or if a bignum representation is not available.
    • expr.root() to get the root of the expression. For example, expr.root(3) will return the cube root of the expression.
    • Additionally, the relational operators (expr.isLess(), expr.isEqual(), etc...) now accept a number argument. For example, expr.isGreater(1) will return true if the expression is greater than 1.
  • Added LaTeX syntax to index collections. If a is a collection:

    • a[i] is parsed as ["At", "a", "i"].
    • a[i,j] is parsed as ["At", "a", "i", "j"].
    • a_i is parsed as ["At", "a", "i"].
    • a_{i,j} is parsed as ["At", "a", "i", "j"].
  • Added support for Kronecker delta notation, i.e. \delta_{ij}, which is parsed as ["KroneckerDelta", "i", "j"] and is equal to 1 if i = j and 0 otherwise.

    When a single index is provided the value of the function is 1 if the index is 0 and 0 otherwise

    When multiple index are provided, the value of the function is 1 if all the indexes are equal and 0 otherwise.

  • Added support for Iverson Bracket notation, i.e. [a = b], which is parsed as ["Boole", ["Equal", "a", "b"]] and is equal to 1 if its argument is true and 0 otherwise. The argument is expected to be a relational expression.

  • Implemented Unique and Tally on collections. Unique returns a collection with only the unique elements of the input collection, and Tally returns a collection with the count of each unique element.

    console.log(ce.box(['Unique', ['List', 1, 2, 3, 1, 2, 3, 4, 5]]).value);
    // -> [1, 2, 3, 4, 5]
    
    console.log(ce.box(['Tally', ['List', 1, 2, 3, 1, 2, 3, 4, 5]]).value);
    // -> [['List', 1, 2, 3, 4, 5], ['List', 2, 2, 2, 1, 1]]
  • Implemented the Map, Filter and Tabulate functions. These functions can be used to transform collections, for example:

    // Using LaTeX
    console.log(ce.parse('\\mathrm{Map}([3, 5, 7], x \\mapsto x^2)').toString());
    // -> [9, 25, 49]
    
    // Using boxed expressions
    console.log(
      ce.box(['Map', ['List', 3, 5, 7], ['Square', '_']]).value
    );
    // -> [9, 25, 49]
    
    console.log(ce.box(['Tabulate',['Square', '_'], 5]).value);
    // -> [1, 4, 9, 16, 25]

    Tabulate can be used with multiple indexes. For example, to generate a 4x4 unit matrix:

    console.log(ce.box(['Tabulate', ['If', ['Equal', '_1', '_2'], 1, 0]], 4, 4).value);
    // -> [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]
    
    // Using the Kronecker delta notation:
    console.log(ce.parse('\\mathrm{Tabulate}(i, j \\mapsto \\delta_{ij}, 4, 4)').value);
    // -> [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]
  • Added Random function. ["Random"] returns a real pseudo-random number betwen 0 and 1. ["Random", 10] returns an integer between 0 and 9, ["Random", 5, 10] returns an integer between 5 and 10.

  • Extended the definition of expr.isConstant. Previously, it only applied to symbols, e.g. Pi. Now it apply to all expressions. expr.isConstant is true if the expression is a number literal, a symbol with a constant value, or a pure function with constant arguments.

  • The boxed expression properties isPositive, isNegative, isNonNegative, isNonPositive, isZero, isNotZero now return a useful value for most function expressions. For example, ce.parse('|x + 1|').isPositive is true.

    If the value cannot be determined, the property will return undefined. For example, ce.parse('|x + 1|').isZero is undefined.

    If the expression is not a real number, the property will return NaN. For example, ce.parse('i').isPositive is NaN.

  • Added Choose function to compute binomial coefficients, i.e. Choose(5, 2) is equal to 10.

  • The fallback for non-constructible complex values of trigonometric functions is now implemented via rules.

  • The canonical order of the arguments has changed and should be more consistent and predictable. In particular, for polynomials, the monomial order is now degrevlex.

  • Canonical expressions can now include a Root expression. For example, the canonical form of \\sqrt[3]{5} is ["Root", 5, 3]. Previously, these were represented as ["Power", 5, ["Divide", 1, 3]].

  • The function definitions no longer have a N handler. Instead the evaluate handler has an optional {numericApproximation} argument.

Issues Resolved

  • #188 Throw an error when invalid expressions are boxed, for example ce.box(["Add", ["3"]]).

  • Some LaTeX renderer can't render \/, so use / instead.

  • When definitions are added to the LaTeX dictionary, they now take precedence over the built-in definitions. This allows users to override the built-in definitions.

  • Improved parsing of functions, including when a mixture of named and positional arguments are used.

  • #175 Matching some patterns when the target had not enough operands would result in a runtime error.

0.25.1 2024-06-27

Issues Resolved

  • #174 Fixed some simplifications, such as \frac{a^n}{a^m} = a^{n-m)

New Features

  • Rules can be defined using a new shorthand syntax, where each rule is a string of LaTeX:

    expr.simplify(["\\frac{x}{x} -> 1", "x + x -> 2x"]);

Single letter variables are assumed to be wildcards, so x is interpreted as the wildcard _x.

Additionally, the expanded form can also include LaTeX strings. The previous syntax using expressions can still be used, and the new and old syntax can be mixed.

For example:

expr.simplify([
  {
    match: "\\frac{x}{x}",
    replace: "1"
  },
  {
    match: ["Add", "x", "x"],
    replace: "2x"
  }
]);

The condition function can also be expressed as a LaTeX string.

  expr.simplify([ { match: "\\frac{x}{x}", replace: 1, condition: "x != 0" }, ]);

The shorthand syntax can be used any where a ruleset is expected, including with the ce.rule() function.

  • A new ce.getRuleSet() method gives access to the built-in rules.
  • #171 The Subtract and Divide function can now accept an arbitrary number of arguments. For example, ["Subtract", 1, 2, 3] is equivalent to ["Subtract", ["Subtract", 1, 2], 3].

0.25.0 2024-06-25

Breaking Changes

  • The canonical form of expressions has changed. It is now more consistent and simpler and should produce more predictable results.

    For example, previously ce.parse("1-x^2") would produce ["Subtract", 1, ["Square", "x"]].

    While this is a readable form, it introduces some complications when manipulating the expression: both the Subtract and Square functions have to be handled, in addition to Add and Power.

    The new canonical form of this expression is ["Add", 1, ["Negate", ["Power", "x", 2]]]. It is a bit more verbose, but it is simpler to manipulate.

  • The ce.serialize() method has been replaced with expr.toLatex() and expr.toMathJson(). The ce.latexOptions and ce.jsonSerializationOptions properties have been removed. Instead, pass the formating options directly to the toLatex() and toMathJson() methods. The ce.parse() method now takes an optional argument to specify the format of the input string.

  • The default JSON serialization of an expression has changed.

    Previously, the default JSON serialization, accessed via the .json property, had some transformations applied to it (sugaring) to make the JSON more human readable.

    For example, ce.parse("\frac12").json would return the symbol "Half" instead of ["Divide", 1, 2].

    However, this could lead to some confusion when manipulating the JSON directly. Since the JSON is intended to be used by machine more than humans, these additional transformations have been removed.

    The expr.json property now returns the JSON representing the expression, without any transformations.

    To get a version of JSON with some transformations applied use the ce.toMathJson() function.

    expr = ce.box(["Subtract", 1, ["Square", "x"]]);
    console.log(expr.json);
    // -> ["Add", 1, ["Negate", ["Power", "x", 2]]]
    expr.toMathJson()
    // -> ["Subtract", 1, ["Square", "x"]]
    expr.toMathJson({exclude: "Square"})
    // -> ["Subtract", 1, ["Power", "x", 2]]

    In practice, the impact of both of these changes should be minimal. If you were manipulating expressions using BoxedExpression, the new canonical form should make it easier to manipulate expressions. You can potentially simplify your code by removing special cases for functions such as Square and Subtract.

    If you were using the JSON serialization directly, you may also be able to simplify you code since the default output from expr.json is now more consistent and simpler.

  • The name of some number formatting options has changed. The number formatting options are an optional argument of ce.parse() and ce.toLatex(). See the
    NumberFormat and NumberSerializationFormat types.

  • The values +infinity, -infinity and NaN are now represented preferably with the symbols PositiveInfinity, NegativeInfinity and NaN respectively. Previously they were represented with numeric values, i.e. {num: "+Infinity"}, {num: "-Infinity"} and {num: "NaN"}. The numeric values are still supported, but the symbols are preferred.

  • The method expr.isNothing has been removed. Instead, use expr.symbol === "Nothing".

New Features

  • When serializing to LaTeX, the output can be "prettified". This involves modifying the LaTeX output to make it more pleasant to read, for example:

    • a+\\frac{-b}{c} -> a-\\frac{b}{c}
    • a\\times b^{-1} -> \\frac{a}{b}
    • \\frac{a}{b}\\frac{c}{d} -> \\frac{a\\cdot c}{b\\cdot d}
    • --2 -> 2

    This is on by default and can be turned off by setting the prettify option to false. For example:

    ce.parse("a+\\frac{-b}{c}").toLatex({prettify: true})
    // -> "a-\\frac{b}{c}"
    ce.parse("a+\\frac{-b}{c}").toLatex({prettify: false})
    // -> "a+\\frac{-b}{c}"
  • Numbers can have a different digit group length for the whole and fractional part of a number. For example, ce.toLatex(ce.parse("1234.5678"), {digitGroup: [3, 0]}) will return 1\,234.5678.

  • Numbers can now be formatted using South-East Asian Numbering System, i.e. lakh and crore. For example:

    ce.toLatex(ce.parse("12345678"), {digitGroup: "lakh"})
    // -> "1,23,45,678"
  • Expressions with Integrate functions can now be compiled to JavaScript. The compiled function can be used to evaluate the integral numerically. For example:

    const f = ce.parse("\\int_0^1 x^2 dx");
    const compiled = f.compile();
    console.log(compiled()); // -> 0.33232945619482307
  • #82 Support for angular units. The default is radians, but degrees can be used by setting ce.angularUnit = "deg". Other possible values are "grad" and "turn". This affects how unitless numbers with a trigonometric function are interpreted. For example, sin(90) will return 1 when ce.angularUnit is "deg", 0.8939966636005579 when ce.angularUnit is "grad" and 0 when ce.angularUnit is "turn".

  • Added expr.map(fn) method to apply a function to each subexpression of an expression. This can be useful to apply custom canonical forms and compare two expressions.

  • An optional canonical form can now be specified with the ce.function().

Issues Resolved

  • #173 Parsing 1++2 would result in an expression with a PreIncrement function. It is now correctly parsed as ["Add", 1, 2].
  • #161 Power expressions would not be processed when their argument was a Divide expression.
  • #165 More aggressive simplification of expressions with exponent greater than 3.
  • #169 Calculating a constant integral (and integral that did not depend on the variable) would result in a runtime error.
  • #164 Negative mixed fractions (e.g. -1\frac23) are now parsed correctly.
  • #162 Numeric evaluation of expressions with large exponents could result in machine precision numbers instead of bignum numbers.
  • #155 The expression ["Subtract", ["Multiply", 0.5, "x"], ["Divide", "x", 2]] will now evaluate to 0.
  • #154 In some cases, parsing implicit argument of trig function return more natural results, for example \cos a \sin b is now parsed as (\cos a)(\sin b) and not \cos (a \sin b).
  • #147 The associativity of some operators, including / was not applied correctly, resulting in unexpected results. For example, 1/2/3 would be parsed as ["Divide", 1, ["Divide", 2, 3]] instead of ["Divide", ["Divide", 1, 2], 3].
  • #146 When parsing an expression like x(x+1) where x is an undeclared symbol, do not infer that x is a function. Instead, infer that x is a variable and that the expression is a product.
  • #145 The expression ["Or", "False", "False"], that is when all the arguments are False, is now evaluates to False.
  • Fixed canonical form of e^x^2, and more generally apply power rule in more cases.
  • Added missing "Sech" and "Csch" functions.
  • The digit grouping serializing would place the separator in the wrong place for some numbers.
  • The avoidExponentsInRange formating option would not always avoid exponents in the specified range.

0.24.0 2024-02-23

Issues Resolved

  • Fix parsing of very deeply nested expressions.
  • Correctly apply rules to deeply nested expressions.
  • expr.print() now correctly prints the expression when using the minified version of the library.
  • expr.isEqual() now correctly compares equalities and inequalities.
  • expr.match() has been improved and works correctly in more cases. The signature of the match function has been changed so that the pattern is the first argument, i.e. instead of pattern.match(expr) use expr.match(pattern).
  • Fix expr.print() when using the minified version of the library.
  • #142 Accept complex expressions as the subcript of \ln and \log in LaTeX.
  • #139 Parse quantifiers \forall and \exists in LaTeX.

0.23.1 2024-01-27

Issues Resolved

  • Using a custom canonical order of "Multiply" would not distribute the Negate function.
  • #141 The canonical form "Order" was applied to non-commutative functions.

0.23.0 2024-01-01

New Features

  • Added ExpandAll function to expand an expression recursively.
  • Added Factor function to factor an expression.
  • Added Together function to combine rational expressions into a single fraction.

Issues Resolved

  • The expression \frac5 7 is now parsed correctly as \frac{5}{7} instead of \frac{5}{}7.
  • Do not sugar non-canonical expression. Previously, ce.parse('\\frac{1}{2}', {canonical: false}) would return Half instead of ['Divide', '1', '2'].
  • #132 Attempting to set a value to 0 with ce.defineSymbol("count", {value: 0}) would fail: the symbol would be undefined.
  • Correctly evaluate power expressions in some cases, for example (\sqrt2 + \sqrt2)^2.
  • Comparison of expressions containing non-exact numbers could fail. For example: 2(13.1+3.1x) and 26.2+6.2x would not be considered equal.

Improvements

  • Significant improvements to symbolic computation. Now, boxing, canonicalization and evaluation are more consistent and produce more predictable results.
  • Adedd the \neg command, synonym for \lnot -> Not.
  • Relational expressions (inequalities, etc...) are now properly factored.
  • Integers are now factored when simplifying, i.e. 2x = 4x -> x = 2x.

0.22.0 2023-11-13

Breaking Changes

  • Rule Syntax

    The syntax to describe rules has changed. The syntax for a rule was previously a tuple [lhs, rhs, {condition} ]. The new syntax is an object with the properties match, replace and condition. For example:

    • previous syntax: [["Add", "_x", "_x"], ["Multiply", 2, "_x"]]
    • new syntax: {match: ["Add", "_x", "_x"], replace: ["Multiply", 2, "_x"]}

    The condition property is optional, and is either a boxed function or a JavaScript function. For example, to add a condition that checks that _x is a number literal:

    {
      match: ["Add", "_x", "_x"],
      replace: ["Multiply", 2, "_x"],
      condition: ({_x}) => _x.isNumberLiteral
    }
  • CanonicalForm

    The CanonicalOrder function has been replaced by the more flexible CanonicalForm function. The CanonicalForm function takes an expression and a list of transformations to apply. To apply the same transformations as CanonicalOrder, use:

    ['CanonicalForm', expr, 'Order']

    These canonical forms can also be specified with box() and parse() options:

    ce.box(expr, { canonical: "Order" });
    ce.parse("x^2 + 2x + 1", { canonical: "Order" });

Work In Progress

  • Linear algebra functions: Rank, Shape,Reshape, Flatten, Determinant, Trace, Transpose, ConjugateTranspose, Inverse. See the Linear Algebra reference guide. Some of these function may not yet return correct result in all cases.

New Features

  • Added a expr.print() method as a synonym for console.log(expr.toString()).
  • Added an exact option (false by default) to the expr.match() pattern matching method. When true some additional patterns are automatically recognized, for example, x will match ["Multiply", '_a', 'x'] when exact is false, but not when exact is true.

Improvements

  • The equation solver used by expr.solve() has been improved and can now solve more equations.
  • The pattern matching engine has been improved and can now match more expressions, including sequences for commutative functions.

0.21.0 2023-11-02

New Features

  • #125 Parse and serialize environemnts, i.e. \begin{matrix} 1 & 2 \\ 3 & 4 \end{matrix} will be parsed as ["Matrix", ["List", ["List", 1, 2], ["List", 3, 4]]].

    A new section on Linear Algebra has some details on the supported formats.

    The linear algebra operations are limited at the moment, but will be expanded in the future.

  • Added IsSame function, which is the function expression corresponding to expr.isSame().

  • Added CanonicalOrder function, which sorts the arguments of commutative functions into canonical order. This is useful to compare two non-canonical expressions for equality.

ce.box(["CanonicalOrder", ["Add", 1, "x"]]).isSame(
  ce.box(["CanonicalOrder", ["Add", "x", 1]])
);
// -> true

Issue Resolved

  • When evaluating a sum (\sum) with a bound that is not a number, return the sum expression instead of an error.

0.20.2 2023-10-31

Issues Resolved

  • Fixed numerical evaluation of integrals and limits when parsed from LaTeX.
console.info(ce.parse("\\lim_{x \\to 0} \\frac{\\sin(x)}{x}").value);
// -> 1

console.info(ce.parse("\\int_{0}^{2} x^2 dx").value);
// -> 2.6666666666666665

0.20.1 2023-10-31

Issues Resolved

  • Fixed evaluation of functions with multiple arguments
  • Fixed compilation of some function assignments
  • Improved serialization of function assignment

0.20.0 2023-10-30

Breaking Changes

  • Architectural changes: the invisible operator is used to represent the multiplication of two adjacent symbols, i.e. 2x. It was previously handled during parsing, but it is now handled during canonicalization. This allows more complex syntactic structures to be handled correctly, for example f(x) := 2x: previously, the left-hand-side argument would have been parsed as a function application, while in this case it should be interpreted as a function definition.

    A new InvisibleOperator function has been added to support this.

    The applyInvisibleOperator parsing option has been removed. To support custom invisible operators, use the InvisibleOperator function.

Issues Resolved

  • #25 Correctly parse chained relational operators, i.e. a < b <= c
  • #126 Logic operators only accepted up to two arguments.
  • #127 Correctly compile Log with bases other than 10.
  • Correctly parse numbers with repeating patterns but no fractional digits, i.e. 0.(1234)
  • Correctly parse |1+|a|+2|

New Features and Improvements

  • Function assignment can now be done with this syntax: f(x) := 2x+1. This syntax is equivalent to f := x -> 2x+1.
  • Implement the Mod and Congruent function.
  • Correctly parse 11 \bmod 5 (Mod) and 26\equiv 11 \pmod5 (Congruent)
  • Better handle empty argument lists, i.e. f()
  • When a function is used before being declared, infer that the symbol is a function, e.g. f(12) will infer that f is a function (and not a variable f multiplied by 12)
  • When a constant is followed by some parentheses, don't assume this is a function application, e.g. \pi(3+n) is now parsed as ["Multiply", "Pi", ["Add", 3, "n"]] instead of ["Pi", ["Add", 3, "n"]]
  • Improved parsing of nested lists, sequences and sets.
  • Improved error messages when syntax errors are encountered during LaTeX parsing.
  • When parsing with the canonical option set to false, preserve more closely the original LaTeX syntax.
  • When parsing text strings, convert some LaTeX commands to Unicode, including spacing commands. As a result, ce.parse("\\text{dead\;beef}_{16}") correctly gets evaluated to 3,735,928,559.

0.19.1 2023-10-26

Issues Resolved

  • Assigning a function to an indentifier works correctly now, i.e.
ce.parse("\\operatorname{f} := x \\mapsto 2x").evaluate();

0.19.0 2023-10-25

Breaking Changes

  • The domain property of the function definition signature is deprecated and replaced with the params, optParams, restParam and result properties instead. The domain property is still supported for backward compatibility, but will be removed in a future version.

Issues Resolved

  • When invoking a declared function in a numeric operation, correctly infer the result type.
["Assign", "f", ["Add", "_", 1]]
["Add", ["f", 1], 1]
// -> 3

Previously a domain error was returned, now f is inferred to have a numeric return type.

  • Fixed a runtime error when inverting a fraction, i.e. \frac{3}{4}^{-1}
  • The tangent of π/2 now correctly returns ComplexInfinity.
  • The exact values of some constructible trigonometric operations (e.g. \tan 18\degree = \frac{\sqrt{25-10\sqrt5}}{5}) returned incorrect results. The unit test case was incorrect and did not detect the problem. The unit test case has been fixed and the returned values are now correct.

New Features

  • Implemented Union and Intersection of collections, for example:
["Intersection", ["List", 3, 5, 7], ["List", 2, 5, 9]]
// -> ["Set", 5]

["Union", ["List", 3, 5, 7], ["List", 2, 5, 9]]
// -> ["Set", 3, 5, 7, 2, 9]
  • Parse ranges, for example 1..5 or 1, 3..10. Ranges are collections and can be used anywhere collections can be used.

  • The functions Sum, Product, Min, Max, and the statistics functions (Mean, Median, Variance, etc...) now handle collection arguments: collections:

    • ["Range"], ["Interval"], ["Linspace"] expressions
    • ["List"] or ["Set"] expressions
    • ["Tuple"], ["Pair"], ["Pair"], ["Triple"] expressions
    • ["Sequence"] expressions
  • Most mathematical functions are now threadable, that is their arguments can be collections, for example:

["Sin", ["List", 0, 1, 5]]
// -> ["List", 0, 0.8414709848078965, -0.9589242746631385]

["Add", ["List", 1, 2], ["List", 3, 4]]
// -> ["List", 4, 6]
  • Added GCD and LCM functions
["GCD", 10, 5, 15]
// -> 5

["LCM", 10, 5, 15]
// -> 30
  • Added Numerator, Denominator, NumeratorDenominator functions. These functions can be used on non-canonical expressions.

  • Added Head and Tail functions which can be used on non-canonical expressions.

  • Added display-quotient and inline-quotient style for formatting of division expressions in LaTeX.

Improvements

  • Improved parsing of \degree command
ce.parse("30\\degree)
// -> ["Divide", "Pi", 6]
  • Improved interoperability with JavaScript: expr.value will return a JavaScript primitive (number, boolean, string, etc...) when possible. This is a more succinct version of expr.N().valueOf().

0.18.1 2023-10-16

Issues Resolved

  • Parsing of whole numbers while in rational mode would return incorrect results.
  • The ND function to evaluate derivatives numerically now return correct values.
ce.parse("\\mathrm{ND}(x \\mapsto 3x^2+5x+7, 2)").N();
// -> 17.000000000001

Improvements

  • Speed up NIntegrate by temporarily switching the numeric mode to machine while computing the Monte Carlo approximation.

0.18.0 2023-10-16

New Features

  • Expanded LaTeX dictionary with \max, \min, \sup, \inf and \lim functions
  • Added Supremum and Infimum functions
  • Compilation of Block expressions, local variables, return statements and conditionals If.
  • Added numerical evaluation of limits with Limit functions and NLimit functions, using a Richardson Extrapolation.
console.info(ce.parse("\\lim_{x\\to0} \\frac{\\sin x}{x}").N().json);
// -> 1

console.info(
  ce.box(["NLimit", ["Divide", ["Sin", "_"], "_"], 0]).evaluate().json
);
// -> 1

console.info(ce.parse("\\lim_{x\\to \\infty} \\cos \\frac{1}{x}").N().json);
// -> 1
  • Added Assign and Declare functions to assign values to symbols and declare symbols with a domain.

  • Block evaluations with local variables work now. For example:

ce.box(["Block", ["Assign", "c", 5], ["Multiply", "c", 2]]).evaluate().json;
// -> 10
  • When decimal numbers are parsed they are interpreted as inexact numbers by default, i.e. "1.2" -> {num: "1.2"}. To force the number to be interpreted as a rational number, set ce.latexOptions.parseNumbers = "rational". In that case, "1.2" -> ["Rational", 12, 10], an exact number.

    While regular decimals are considered "inexact" numbers (i.e. they are assumed to be an approximation), rationals are assumed to be exact. In most cases, the safest thing to do is to consider decimal numbers as inexact to avoid introducing errors in calculations. If you know that the decimal numbers you parse are exact, you can use this option to consider them as exact numbers.

Improvements

  • LaTeX parser: empty superscripts are now ignored, e.g. 4^{} is interpreted as 4.

0.17.0 2023-10-12

Breaking Changes

  • The Nothing domain has been renamed to NothingDomain
  • The Functions, Maybe, Sequence, Dictionary, List and Tuple domain constructors have been renamed to FunctionOf, OptArg, VarArg, DictionaryOf, ListOf and TupleOf, respectively.
  • Domains no longer require a ["Domain"] expression wrapper, so for example ce.box("Pi").domain returns "TranscendentalNumbers" instead of ["Domain", "TranscendentalNumbers"].
  • The VarArg domain constructor now indicates the presence of 0 or more arguments, instead of 1 or more arguments.
  • The MaybeBooleans domain has been dropped. Use ["Union", "Booleans", "NothingDomain"] instead.
  • The ce.defaultDomain has been dropped. The domain of a symbol is now determined by the context in which it is used, or by the ce.assume() method. In some circumstances, the domain of a symbol can be undefined.

New Features

  • Symbolic derivatives of expressions can be calculated using the D function. For example, ce.box(["D", ce.parse("x^2 + 3x + 1"), "x"]).evaluate().latex returns "2x + 3".

Improvements

  • Some frequently used expressions are now available as predefined constants, for example ce.Pi, ce.True and ce.Numbers.
  • Improved type checking and inference, especially for functions with complicated or non-numeric signatures.

Bugs Fixed

  • Invoking a function repeatedly would invoke the function in the original scope rather than using a new scope for each invocation.

0.16.0 2023-09-29

Breaking Changes

  • The methods ce.let() and ce.set() have been renamed to ce.declare() and ce.assign() respectively.
  • The method ce.assume() requires a predicate.
  • The signatures of ce.assume() and ce.ask() have been simplified.
  • The signature of ce.pushScope() has been simplified.
  • The expr.freeVars property has been renamed to expr.unknowns. It returns the identifiers used in the expression that do not have a value associated with them. The expr.freeVariables property now return the identifiers used in the expression that are defined outside of the local scope and are not arguments of the function, if a function.

New Features

  • Domain Inference when the domain of a symbol is not set explicitly (for example with ce.declare()), the domain is inferred from the value of the symbol or from the context of its usage.

  • Added Assume, Identity, Which, Parse, N, Evaluate, Simplify, Domain.

  • Assignments in LaTeX: x \\coloneq 42 produce ["Assign", "x", 42]

  • Added ErfInv (inverse error function)

  • Added Factorial2 (double factorial)

Functions

  • Functions can now be defined:

    • using ce.assign() or ce.declare()
    • evaluating LaTeX: (x, y) \mapsto x^2 + y^2
    • evaluating MathJSON: ["Function", ["Add", ["Power", "x", 2], ["Power", "y", 2]]], "x", "y"]
  • Function can be applied using \operatorname{apply} or the operators \rhd and \lhd:

    • \operatorname{apply}(f, x)
    • f \rhd x
    • x \lhd f

See Adding New Definitions and Functions.

Control Structures

  • Added FixedPoint, Block, If, Loop
  • Added Break, Continue and Return statements

See Control Structures

Calculus

  • Added numeric approximation of derivatives, using an 8-th order centered difference approximation, with the ND function.
  • Added numeric approximation of integrals, using a Monte Carlo method with rebasing for improper integrals, with the NIntegrate function
  • Added symbolic calculation of derivatives with the D function.

Collections

Added support for collections such as lists, tuples, ranges, etc...

See Collections

Collections can be used to represent various data structures, such as lists, vectors, matrixes and more.

They can be iterated, sliced, filtered, mapped, etc...

["Length", ["List", 19, 23, 5]]
// -> 3

["IsEmpty", ["Range", 1, 10]]
// -> "False"

["Take", ["Linspace", 0, 100, 50], 4]
// -> ["List", 0, 2, 4, 6]

["Map", ["List", 1, 2, 3], ["Function", "x", ["Power", "x", 2]]]
// -> ["List", 1, 4, 9]

["Exclude", ["List", 33, 45, 12, 89, 65], -2, 2]
// -> ["List", 33, 12, 65]


["First", ["List", 33, 45, 12, 89, 65]]
// -> 33

Improvements

  • The documentation has been significantly rewritten with help from an AI-powered writing assistant.

Issues Resolved

  • The LaTeX string returned in ["Error"] expression was incorrectly tagged as Latex instead of LatexString.

0.15.0 2023-09-14

Improvements

  • The ce.serialize() function now takes an optional canonical argument. Set it to false to prevent some transformations that are done to produce more readable LaTeX, but that may not match exactly the MathJSON. For example, by default ce.serialize(["Power", "x", -1]) returns \frac{1}{x} while ce.serialize(["Power", "x", -1], {canonical: false}) returns x^{-1}.
  • Improved parsing of delimiters, i.e. \left(, \right], etc...
  • Added complex functions Real, Imaginary, Arg, Conjugate, AbsArg. See Complex
  • Added parsing and evaluation of \Re, \Im, \arg, ^\star (Conjugate).
  • #104 Added the ["ComplexRoots", x, n] function which returns the nthroot of x.
  • Added parsing and evaluation of statistics functions Mean, Median, StandardDeviation, Variance, Skewness, Kurtosis, Quantile, Quartiles, InterquartileRange, Mode, Count, Erf, Erfc. See Statistics

0.14.0 2023-09-13

Breaking Changes

  • The entries in the LaTeX syntax dictionary can now have LaTeX triggers (latexTrigger) or triggers based on identifiers (symbolTrigger). The former replaces the trigger property. The latter is new. An entry with a triggerIdentifier of average will match \operatorname{average}, \mathrm{average} and other variants.
  • The ce.latexOptions and ce.jsonSerializationOptions properties are more robust. They can be modified directly or one of their properties can be modified.

Improvements

  • Added more functions and symbols supported by expr.compile():

    • Factorial postfix operator 5!
    • Gamma function \Gamma(2)
    • LogGamma function \operatorname{LogGamma}(2)
    • Gcd function \operatorname{gcd}(20, 5)
    • Lcm function \operatorname{lcm}(20, 5)
    • Chop function \operatorname{chop}(0.00000000001)
    • Half constant \frac{1}{2}
    • 'MachineEpsilon' constant
    • GoldenRatio constant
    • CatalanConstant constant
    • EulerGamma constant \gamma
    • Max function \operatorname{max}(1, 2, 3)
    • Min function \operatorname{min}(13, 5, 7)
    • Relational operators: Less, Greater, LessEqual, GreaterEqual, 'Equal', 'NotEqual'
    • Some logical operators and constants: And, Or, Not, True, False
  • More complex identifiers syntax are recognized, including \mathbin{}, \mathord{}, etc... \operatorname{} is the recommended syntax, though: it will display the identifier in upright font and with the propert spacing, and is properly enclosing. Some commands, such as \mathrm{} are not properly enclosing: two adjacent \mathrm{} command could be merged into one.

  • Environments are now parsed and serialized correctly.

  • When parsing LaTeX, function application is properly handled in more cases, including custom functions, e.g. f(x)

  • When parsing LaTeX, multiple arguments are properly handled, e.g. f(x, y)

  • Add LaTeX syntax for logical operators:

    • And: \land, \operatorname{and} (infix or function)
    • Or: \lor, \operatorname{or} (infix or function)
    • Not: \lnot, \operatorname{not} (prefix or function)
    • Xor: \veebar (infix)
    • Nand: \barwedge (infix)
    • Nor: ^^^^22BD (infix)
    • Implies: \implies (infix)
    • Equivalent: \iff (infix)
  • When a postfix operator is defined in the LaTeX syntax dictionary of the form ^ plus a single token, a definition with braces is added automatically so that both forms will be recognized.

  • Extended the LaTeX dictionary with:

    • floor
    • ceil
    • round
    • sgn
    • exp
    • abs
    • gcd
    • lcm
    • apply
  • Properly handle inverse and derivate notations, e.g. \sin^{-1}(x), \sin'(x), \cos''(x), \cos^{(4)}(x)or even\sin^{-1}''(x)`

0.13.0 2023-09-09

New Features

  • Compilation Some expressions can be compiled to Javascript. This is useful to evaluate an expression many times, for example in a loop. The compiled expression is faster to evaluate than the original expression. To get the compiled expression, use expr.compile(). Read more at Compiling

Issues Resolved and Improvements

  • Fixed parsing and serialization of extended LaTeX synonyms for e and i.
  • Fixed serialization of Half.
  • Fixed serialization of Which
  • Improved serialization of ["Delimiter"] expressions.

0.12.7 2023-09-08

Improvements

  • Made customization of the LaTeX dictionary simpler. The ce.latexDictionary property can be used to access and modify the dictionary. The documentation has been updated.

0.12.6 2023-09-08

Breaking Changes

  • New API for the Parser class.

Improvements and Bux Fixes

  • The ComputeEngine now exports the bignum() and complex() methods that can be used to create bignum and complex numbers from strings or numbers. The methods isBigNum() and isComplex() have also been added to check if a value is a bignum (Decimal) or complex (Complex) number, for example as returned by expr.numericValue.
  • #69 \leq was incorrectly parsed as Equals instead of LessEqual
  • #94 The \exp command was not parsed correctly.
  • Handle PlusMinus in infix and prefix position, i.e. a\pm b and \pm a.
  • Improved parsing, serialization
  • Improved simplification
  • Improved evaluation of Sum and Product
  • Support complex identifiers (i.e. non-latin scripts, emojis).
  • Fixed serialization of mixed numbers.

0.12.1 2022-12-01

Work around unpckg.com issue with libraries using BigInt.

0.12.0 2022-11-27

Breaking Changes

  • The expr.symbols property return an array of string. Previously it returned an array of BoxedExpression.

Improvements

  • Rewrote the rational computation engine to use JavaScript bigint instead of Decimal instances. Performance improvements of up to 100x.
  • expr.freeVars provides the free variables in an expression.
  • Improved performance of prime factorization of big num by x100.
  • Added ["RandomExpression"]
  • Improved accuracy of some operations, for example expr.parse("1e999 + 1").simplify()

Issues Resolved

  • When ce.numericMode === "auto", square roots of negative numbers would return an expression instead of a complex number.
  • The formatting of LaTeX numbers when using ce.latexOptions.notation = "engineering" or "scientific" was incorrect.
  • The trig functions no longer "simplify" to the less simple exponential formulas.
  • The canonical order of polynomials now orders non-lexicographic terms of degree 1 last, i.e. "ax^2+ bx+ c" instead of "x + ax^2 + bx".
  • Fixed evaluation of inverse functions
  • Fixed expr.isLess, expr.isGreater, expr.isLessEqual, expr.isGreaterEqual and ["Min"], ["Max"]

0.11.0 2022-11-18

Breaking Changes

  • The signature of ce.defineSymbol(), ce.defineFunction() and ce.pushScope() have changed

Improvements

  • When a constant should be held or substituted with its value can now be more precisely controlled. The hold symbol attribute is now holdUntil and can specify at which stage the substitution should take place.

Issues Resolved

  • Some constants would return a value as bignum or complex even when the numericMode did not allow it.
  • Changing the value or domain of a symbol is now correctly taken into account. Changes can be made with ce.assume(), ce.set() or expr.value.
  • When a symbol does not have a value associated with it, assumptions about it (e.g. "x > 0") are now correctly tracked and reflected.

0.10.0 2022-11-17

Breaking Changes

  • expr.isLiteral has been removed. Use expr.numericValue !== null and expr.string !== null instead.

Issues Resolved

  • Calling ce.forget() would not affect expressions that previously referenced the symbol.

Improvements

  • More accurate calculations of some trig functions when using bignums.
  • Improved performance when changing a value with ce.set(). Up to 10x faster when evaluating a simple polynomial in a loop.
  • ce.strict can be set to false to bypass some domain and validity checks.

0.9.0 2022-11-15

Breaking Changes

  • The head of a number expression is always Number. Use expr.domain to be get more specific info about what kind of number this is.
  • By default, ce.box() and ce.parse() return a canonical expression. A flag can be used if a non-canonical expression is desired.
  • The API surface of BoxedExpression has been reduced. The properties machineValue, bignumValue, asFloat, asSmallInteger, asRational etc... have been replaced with a single numericValue property.
  • parseUnknownSymbol is now parseUnknownIdentifier

Improvements

  • Support angles in degrees with 30\degree, 30^\circ and \ang{30}.

  • More accurate error expressions, for example if there is a missing closing delimiter an ["Error", ["ErrorCode", "'expected-closing-delimiter'", "')'"]] is produced.

  • ["Expand"] handles more cases

  • The trig functions can now have a regular exponent, i.e.\cos^2(x) in addition to -1 for inverse, and a combination of \prime, \doubleprime and ' for derivatives.

  • ce.assume() handle more expressions and can be used to define new symbols by domain or value.

  • Better error message when parsing, e.g. \sqrt(2) (instead of \sqrt{2})

  • Better simplification for square root expressions:

    • \sqrt{25x^2} -> 5x
  • Improved evaluation of ["Power"] expressions, including for negative arguments and non-integer exponents and complex arguments and exponents.

  • Added Arccot, Arcoth, Arcsch, Arcscc, Arsech and Arccsc

  • expr.solve() returns result for polynomials of order up to 2.

  • The pattern.match() function now work correctly for commutative functions, i.e. ce.pattern(['Add', '_a', 'x']).match(ce.parse('x+y')) -> {"_a": "y"}

  • Added ce.let() and ce.set() to declare and assign values to identifiers.

  • Preserve exact calculations involving rationals or square root of rationals.

    • \sqrt{\frac{49}{25}} -> \frac{7}{5}
  • Addition and multiplication provide more consistent results for evaluate() and N(). Evaluate returns an exact result when possible.

    • EXACT
      • 2 + 5 -> 7
      • 2 + 5/7 -> 19/7
      • 2 + √2 -> 2 + √2
      • 2 + √(5/7) -> 2 + √(5/7)
      • 5/7 + 9/11 -> 118/77
      • 5/7 + √2 -> 5/7 + √2
      • 10/14 + √(18/9) -> 5/7 + √2
      • √2 + √5 -> √2 + √5
      • √2 + √2 -> 2√2
      • sin(2) -> sin(2)
      • sin(π/3) -> √3/2
    • APPROXIMATE
      • 2 + 2.1 -> 4.1
      • 2 + √2.1 -> 3.44914
      • 5/7 + √2.1 -> 2.16342
      • sin(2) + √2.1 -> 2.35844
  • More consistent behavior of the auto numeric mode: calculations are done with bignum and complex in most cases.

  • JsonSerializationOptions has a new option to specify the numeric precision in the MathJSON serialization.

  • Shorthand numbers can now be strings if they do not fit in a float-64:

// Before
["Rational", { "num": "1234567890123456789"}, { "num": "2345678901234567889"}]

// Now
["Rational", "1234567890123456789", "2345678901234567889"]
  • \sum is now correctly parsed and evaluated. This includes creating a local scope with the index and expression value of the sum.

Bugs Fixed

  • The parsing and evaluation of log functions could produce unexpected results
  • The \gamma command now correctly maps to ["Gamma"]
  • Fixed numeric evaluation of the ["Gamma"] function when using bignum
  • #57 Substituting 0 (i.e. with expr.subs({})) did not work.
  • #60 Correctly parse multi-char symbols with underscore, i.e. \mathrm{V_a}
  • Parsing a number with repeating decimals and an exponent would drop the exponent.
  • Correct calculation of complex square roots
    • \sqrt{-49} -> 7i
  • Calculations were not always performed as bignum in "auto" numeric mode if the precision was less than 15. Now, if the numeric mode is "auto", calculations are done as bignum or complex numbers.
  • If an identifier contained multiple strings of digits, it would not be rendered to LaTeX correctly, e.g. V20_20.
  • Correctly return isReal for real numbers

0.8.0 2022-10-02

Breaking Changes

  • Corrected the implementation of expr.toJSON(), expr.valueOf() and added the esoteric [Symbol.toPrimitive]() method. These are used by JavaScript when interacting with other primitive types. A major change is that expr.toJSON() now returns an Expression as an object literal, and not a string serialization of the Expression.

  • Changed from "decimal" to "bignum". "Decimal" is a confusing name, since it is used to represent both integers and floating point numbers. Its key characteristic is that it is an arbitrary precision number, aka "bignum". This affects ce.numericMode which now uses bignum instead of decimal', expr.decimalValue->expr.bignumValue, decimalValue()-> bignumValue()`

Bugs Fixed

  • Numerical evaluation of expressions containing complex numbers when in decimal or auto mode produced incorrect results. Example: e^{i\\pi}

0.7.0 2022-09-30

Breaking Changes

  • The ce.latexOptions.preserveLatex default value is now false
  • The first argument of the ["Error"] expression (default value) has been dropped. The first argument is now an error code, either as a string or an ["ErrorCode"] expression.

Features

  • Much improved LaTeX parser, in particular when parsing invalid LaTeX. The parser now avoids throwing, but will return a partial expression with ["Error"] subexpressions indicating where the problems were.
  • Implemented new domain computation system (similar to type systems in programming languages)
  • Added support for multiple signatures per function (ad-hoc polymorphism)
  • Added FixedPoint, Loop, Product, Sum, Break, Continue, Block, If, Let, Set, Function, Apply, Return
  • Added Min, Max, Clamp
  • Parsing of \sum, \prod, \int.
  • Added parsing of log functions, \lb, \ln, \ln_{10}, \ln_2, etc...
  • Added expr.subexpressions, expr.getSubexpressions(), expr.errors, expr.symbols, expr.isValid`.
  • Symbols can now be used to represent functions, i.e. ce.box('Sin').domain correctly returns ["Domain", "Function"].
  • Correctly handle rational numbers with a numerator or denominator outside the range of a 64-bit float.
  • Instead of a Missing symbol an ["Error", "'missing'"] expression is used.
  • Name binding is now done lazily
  • Correctly handle MathJSON numbers with repeating decimals, e.g. 1.(3).
  • Correctly evaluate inverse functions, e.g. ce.parse('\\sin^{-1}(.5)).N()
  • Fixed some LaTeX serialization issues

Read more at Core Reference and [Arithmetic Reference] (https://cortexjs.io/compute-engine/reference/arithmetic/)

Bugs Fixed

  • #43 If the input of ce.parse() is an empty string, return an empty string for expr.latex or expr.json.latex: that is, ensure verbatim LaTeX round-tripping
  • Evaluating some functions, such as \arccos would result in a crash
  • Correctly handle parsing of multi-token decimal markers, e.g. {,}

0.6.0 2022-04-18

Improvements

  • Parse more cases of tabular environments
  • Handle simplify and evaluate of inert functions by default
  • Avoid unnecessary wrapping of functions when serializing LaTeX
  • Parse arguments of LaTeX commands (e.g. \vec{})
  • #42 Export static ComputeEngine.getLatexDictionary
  • Parse multi-character constants and variables, e.g. \mathit{speed} and \mathrm{radius}
  • Parse/serialize some LaTeX styling commands: \displaystyle, \tiny and more

0.5.0 2022-04-05

Improvements

  • Correctly parse tabular content (for example in \begin{pmatrix}...\end{pmatrix}
  • Correctly parse LaTeX groups, i.e. {...}
  • Ensure constructible trigonometric values are canonical
  • Correct and simplify evaluation loop for simplify(), evaluate() and N().
  • #41 Preserve the parsed LaTeX verbatim for top-level expressions
  • #40 Correctly calculate the synthetic LaTeX metadata for numbers
  • Only require Node LTS (16.14.2)
  • Improved documentation, including Dark Mode support

0.4.4

Release Date: 2022-03-27

Improvements

  • Added option to specify custom LaTeX dictionaries in ComputeEngine constructor
  • expr.valueOf returns rational numbers as [number, number] when applicable
  • The non-ESM builds (compute-engine.min.js) now targets vintage JavaScript for improved compatibility with outdated toolchains (e.g. Webpack 4) and environments. The ESM build (compute-engine.min.esm.js) targets evergreen JavaScript (currently ECMAScript 2020).

0.4.3

Release Date: 2022-03-21

Transition Guide from 0.4.2

The API has changed substantially between 0.4.2 and 0.4.3, however adapting code to the new API is very straightforward.

The two major changes are the introduction of the BoxedExpression class and the removal of top level functions.

Boxed Expression

The BoxedExpression class is a immutable box (wrapper) that encapsulates a MathJSON Expression. It provides some member functions that can be used to manipulate the expression, for example expr.simplify() or expr.evaluate().

The boxed expresson itself is immutable. For example, calling expr.simplify() will return a new, simplified, expression, without modifying expr.

To create a "boxed" expression from a "raw" MathJSON expression, use ce.box(). To create a boxed expression from a LaTeX string, use ce.parse().

To access the "raw" MathJSON expression, use the expr.json property. To serialize the expression to LaTeX, use the expr.latex property.

The top level functions such as parse() and evaluate() are now member functions of the ComputeEngine class or the BoxedExpression class.

There are additional member functions to examine the content of a boxed expression. For example, expr.symbol will return null if the expression is not a MathJSON symbol, otherwise it will return the name of the symbol as a string. Similarly, expr.ops return the arguments (operands) of a function, expr.asFloat return null if the expression does not have a numeric value that can be represented by a float, a number otherwise, etc...

Canonical Form

Use expr.canonical to obtain the canonical form of an expression rather than the ce.format() method.

The canonical form is less aggressive in its attempt to simplify than what was performed by ce.format().

The canonical form still accounts for distributive and associative functions, and will collapse some integer constants. However, in some cases it may be necessary to invoke expr.simplify() in order to get the same results as ce.format(expr).

Rational and Division

In addition to machine floating points, arbitrary precision numbers and complex numbers, the Compute Engine now also recognize and process rational numbers.

This is mostly an implementation detail, although you may see ["Rational", 3, 4], for example, in the value of a expr.json property.

If you do not want rational numbers represented in the value of the .json property, you can exclude the Rational function from the serialization of JSON (see below) in which case Divide will be used instead.

Note also that internally (as a result of boxing), Divide is represented as a product of a power with a negative exponent. This makes some pattern detection and simplifications easier. However, when the .json property is accessed, product of powers with a negative exponents are converted to a Divide, unless you have included Divide as an excluded function for serialization.

Similarly, Subtract is converted internally to Add, but may be serialized unless excluded.

Parsing and Serialization Customization

Rather than using a separate instance of the LatexSyntax class to customize the parsing or serialization, use a ComputeEngine instance and its ce.parse() method and the expr.latex property.

Custom dictionaries (to parse/serialize custom LaTeX syntax) can be passed as an argument to the ComputeEngine constructor.

For more advanced customizations, use ce.latexOptions = {...}. For example, to change the formatting options of numbers, how the invisible operator is interpreted, how unknown commands and symbols are interpreted, etc...

Note that there are also now options available for the "serialization" to MathJSON, i.e. when the expr.json property is used. It is possible to control for example if metadata should be included, if shorthand forms are allowed, or whether some functions should be avoided (Divide, Sqrt, Subtract, etc...). These options can be set using ce.jsonSerializationOptions = {...}.

Comparing Expressions

There are more options to compare two expressions.

Previously, match() could be used to check if one expression matched another as a pattern.

If match() returned null, the first expression could not be matched to the second. If it returned an object literal, the two expressions matched.

The top-level match() function is replaced by the expr.match() method. However, there are two other options that may offer better results:

  • expr.isSame(otherExpr) return true if expr and otherExpr are structurally identical. Structural identity is closely related to the concept of pattern matching, that is ["Add", 1, "x"] and ["Add", "x", 1] are not the same, since the order of the arguments is different. It is useful for example to compare some input to an answer that is expected to have a specific form.
  • expr.isEqual(otherExpr) return true if expr and otherExpr are mathematically identical. For example ce.parse("1+1").isEqual(ce.parse("2")) will return true. This is useful if the specific structure of the expression is not important.

It is also possible to evaluate a boolean expression with a relational operator, such as Equal:

console.log(ce.box(["Equal", expr, 2]).evaluate().symbol);
// -> "True"

console.log(expr.isEqual(ce.box(2)));
// -> true

Before / After

Before After
expr = ["Add", 1, 2] expr = ce.box(["Add", 1, 2])
expr = ce.evaluate(expr) expr = expr.evaluate()
console.log(expr) console.log(expr.json)
expr = new LatexSyntax().parse("x^2+1") expr = ce.parse("x^2+1")
new LatexSyntax().serialize(expr) expr.latex
ce.simplify(expr) expr.simplify()
await ce.evaluate(expr) expr.evaluate()
ce.N(expr) expr.N()
ce.domain(expr) expr.domain
ce.format(expr...) expr.canonical
expr.simplify()

0.3.0

Release Date: 2021-06-18

Improvements

  • In LaTeX, parse \operatorname{foo} as the MathJSON symbol "foo".
--- title: Compute Engine Demo slug: /compute-engine/demo/ date: Last Modified hide_table_of_contents: true ---

import Mathfield from '@site/src/components/Mathfield'; import { useEffect, useState } from 'react'; import { clsx } from 'clsx'; import CodePlayground from '@site/src/components/CodePlayground'; import ConsoleMarkup from '@site/src/components/ConsoleMarkup';

export function ToggleButton ({toggle, className, style}) { return <button className={clsx(className, "toggle")} style={style} onClick={() => toggle()}> ; }

export function InfoSection({ label, children, secondary }) { const [visible, setVisible] = useState(false); return (

{label &&
{label}
}
{children} {secondary && <ToggleButton className={visible ? "is-on" : ""} toggle={()=>{setVisible(!visible)}}/>}
{visible && secondary}
); }

export function Console({children}) { return

{children}
; }

export function ExampleSelector({ onSelect, index }) {
const examples = [ { latex: "\frac{165}{315}", json: '["Divide", 165, 315]', preamble: To create a boxed expression from a MathJSON expression, use the \ce.box()` function.

By default expressions are put in canonical form. The canonical form of fractions is their reduced form.`, template: "as-json" },

{ 
  latex: 'e^{i\\pi}', 
  preamble: `To create a boxed expression from a LaTeX string, 

use the `ce.parse()` function.

The `expr.N()` function evaluates the expression numerically, including complex numbers.`, template: "N" },

{ 
  latex: '\\sqrt{6\\sum_{n=1}^{\\infty} \\frac{1}{n^2}}', 
  preamble: 'Evaluate a sum',
  template: 'eval-async'
},

{ 
  latex: '2\\prod_{n=1}^{\\infty} \\frac{4n^2}{4n^2-1}', 
  preamble: 'Evaluate a product',
  template: 'eval-async'
},

{ 
  latex: '\\mathrm{Expand}((a+b)^5)', 
  preamble: `Symbolically expand an expression.

Use the `latex` property to get the result in LaTeX format.`, template: 'eval-string' },

{ 
  label: 'Rational',
  json: '["Rational", "Pi"]', 
  preamble: `The \`Rational\` function with a single argument

approximates a number as a rational.` },

{ 
  latex: '\\cos\\left(\\frac{\\pi}{6}\\right)',
  preamble: 'Exact trigonometric values',
  template: 'eval-string'
},

{ 
  latex: '\\sin(30\\degree)', 
  preamble: 'Use degrees unit in trig functions',
  template: 'eval-string'
},

{
  latex: `\\begin{cases}

0 & n = 0\\ 1 & n = 1\\ n^2+1 & n \geq 2
\end{cases}`, label: "Piecewise" },

{ 
  latex: '\\lfloor \\pi \\rfloor = \\lceil \\exponentialE \\rceil', 
  preamble: "Evaluate boolean expressions",
  template: "eval-string"
},

{
  latex: '(x = \\pi) + 2',
  label: "Errors",
  preamble: 'Syntax errors are represented as \`Error\` functions'
}

];

useEffect(() => { onSelect(examples[index], index)}, []);

return (

{ examples.map((x, i) => <div key={x.label ?? x.latex ?? x.json} className={clsx("example-cell", {"active": i === index} )} onClick={() => onSelect(x, i)} > {x.label ? x.label: $$${x.latex}$$}
) } ); }

export function ComputeEngineDemo() {

const TEMPLATES = { evaluate: $0 const expr = $1; console.info(expr.evaluate().json); , "as-json": $0 const expr = $1; console.info(expr.json); , "eval-string": $0 const expr = $1; console.info(expr.evaluate()); ,
"eval-latex": $0 const expr = $1; console.info(expr.evaluate().latex); , N: $0 const expr = $1; console.info(expr.N()); , "eval-async": $0 const expr = $1; console.info(await expr.evaluateAsync());

};

let [index, setIndex] = useState(0); let [value, setValue] = useState('');

const handleSelect = (example, exampleIndex) => { let code = TEMPLATES[example.template] ?? TEMPLATES["evaluate"];

if (example.preamble) {
  const lines = example.preamble.split('\n');
  const preamble = lines.map(x => `// ${x}`).join('\n') + '\n';

  code = code.replace('$0', preamble);
} else {
  code = code.replace('$0', '');
}

// Prioritize JSON over LaTeX if we have both. The LaTeX will be used
// as a label.
if (example.json) {
  code = code.replace("$1", `ce.box(${example.json})`);
} else if (example.latex) {
  if (example.latex.includes('\n')) {
    const escaped = example.latex.replace(/\\/g, '\\\\');
    code = code.replace('$1', `ce.parse(\`${escaped}\`)` );
  } else {
    code = code.replace('$1', `ce.parse(${JSON.stringify(example.latex)})`);
  }
}  

setValue(code);
setIndex(exampleIndex);

};

return (

); }

Compute Engine Demo

<style>{` .example-cells { display: flex; flex-wrap: nowrap; justify-content: flex-start; gap: .5em; width: 100%; overflow: auto hidden; margin-bottom: 1rem; border-radius: 16px; border: 1px solid var(--neutral-700); padding: 16px; background-color: var(--neutral-900); font-size: 1em; box-shadow: inset 2px 2px 4px 0px var(--neutral-800), inset -2px -2px 4px 0px var(--neutral-800); } .example-cell { display: flex; align-items: center; justify-content: center; padding: .25rem 0.5rem; margin: 0; min-width: fit-content; white-space: nowrap; overflow: hidden; text-align: center; border: 1px solid var(--callout-border-color); background-color: var(--neutral-1000); border-radius: 8px; color: var(--primary-color-dark); cursor: pointer; scale: 1; transition: scale 0.2s; } [data-theme="dark"] .example-cell { background-color: var(--neutral-800); color: var(--neutral-400); } .example-cell.active { color: white; background-color: var(--primary-color); border-color: var(--primary-color-dark); } .example-cell.active:hover { color: white; background-color: var(--primary-color); border-color: var(--primary-color-dark); scale: 1.1; transition: scale 0.2s; } .example-cell:hover { color: var(--neutral-100); background-color: var(--callout-background); border-color: var(--primary-color); scale: 1.1; transition: scale 0.2s; } .example-cell:active { color: var(--neutral-900); background: var(--primary-color); border-color: var(--primary-color-dark); } [data-theme="dark"] .example-cell:active { color: var(--neutral-200); } .info-section { margin-top: 1em; padding: 0; border-radius: 8px; } .info-section .label { font-size: 1.25em; margin-bottom: 0.5em; font-weight: bold; padding: 0; } button.toggle { display: flex; align-items: center; appearance: none; border: none; background: none; border-radius: 4px; width: 36px; height: 36px; color: #777; margin-left: 8px; color: var(--primary-color-dark); } button.toggle:hover, button.toggle:active { background: var(--primary-color-light); color: var(--primary-color-dark); } button.toggle.is-on { color: #fff; background: var(--primary-color); } button.toggle svg { width: 24px; height: 24px; } .info-section-row { margin: 0; margin-top: 0.5em; display: flex; align-items: center; justify-content: space-between; width: 100%; } `}</style>

title: Evaluation of Expressions slug: /compute-engine/guides/evaluate/ preamble:

Evaluation

Evaluating an expression is the process of determining the value of the expression. This involves looking up the definitions of symbols and functions, evaluating the arguments of functions, and applying the function to the arguments.

Evaluation Methods

To evaluate an expression, use the expr.evaluate() method.

const expr = ce.parse('2 + 2');
expr.evaluate().print();

The expr.value property does not evaluate the expression. If the expression is a literal, it returns the literal value. If the expression is a symbol, it looks up the symbol in the current scope and returns its value.

ce.box('x').value = 314;
console.info(ce.parse('42').value)
console.info(ce.parse('x').value)
console.info(ce.parse('2 + 2').value)

The expr.N() method is a shorthand for expr.evaluate({numericApproximation: true}).

const expr = ce.parse('2\\pi');
expr.evaluate().print();
expr.N().print();

Compilation

An expression can be evaluated by compiling it to JavaScript using the expr.compile() method. The method returns a JavaScript function that can be called to evaluate the expression.

const f = ce.parse('2\\pi').compile();
console.log(f());
Read more about **compiling expressions**

Asynchronous Evaluation

Some computations can be time-consuming. For example, computing a very large factorial. To prevent the browser from freezing, the Compute Engine can perform some operations asynchronously.

To perform an asynchronous evaluation, use the expr.evaluateAsync() method.

try {
  const fact = ce.parse('(70!)!');
  const factResult = await fact.evaluateAsync();
  factResult.print();
} catch (e) {
  console.error(e);
}

The expr.evaluateAsync() method returns a Promise that resolves to the result of the evaluation. It accepts the same numericApproximation options as expr.evaluate().

It is also possible to interrupt an evaluation, for example by providing the user with a pause/cancel button.

To make an evaluation interruptible, use an AbortController object and a signal.

For example, to interrupt an evaluation after 500ms:

const abort = new AbortController();
const signal = abort.signal;
setTimeout(() => abort.abort(), 500);
try {
  const fact = ce.parse('(70!)!');
  const factResult = await fact.evaluateAsync({ signal });
  factResult.print();
} catch (e) {
  console.error(e);
}

:::html
<div class="stack">
  <div class="row">
    <button id="evaluate-button">Evaluate</button>
    <button id="cancel-button" disabled>Cancel</button>
  </div>

  <div id="output"></div>
</div>
:::js
const abort = new AbortController();

document.getElementById('cancel-button').addEventListener('click', 
  () => abort.abort()
);

document.getElementById('evaluate-button').addEventListener('click', async () => {
  try {
    document.getElementById('evaluate-button').disabled = true;
    document.getElementById('cancel-button').disabled = false;

    const fact = ce.parse('(70!)!');
    const factResult = await fact.evaluateAsync({ signal: abort.signal });
    document.getElementById('output').textContent = factResult.toString();
    
    document.getElementById('evaluate-button').disabled = false;
    document.getElementById('cancel-button').disabled = true;
  } catch (e) {
    document.getElementById('evaluate-button').disabled = false;
    document.getElementById('cancel-button').disabled = true;
    console.error(e);
  }
});

To set a time limit for an operation, use the ce.timeLimit option, which is a number of milliseconds.

ce.timeLimit = 1000;
try {
  const fact = ce.parse('(70!)!');
  fact.evaluate().print();
} catch (e) {
  console.error(e);
}

The time limit applies to both the synchronous or asynchronous evaluation.

The default time limit is 2,000ms (2 seconds).

When an operation is canceled either because of a timeout or an abort, a CancellationError is thrown.

Lexical Scopes and Evaluation Contexts

The Compute Engine supports lexical scoping.

A lexical scope is a region of the code where a symbol is defined. Each scope has its own bindings table, which is a mapping of symbols to their definitions. The definition includes the type of the symbol, whether it is a constant and other properties.

An evaluation context is a snapshot of the current state of the Compute Engine. It includes the values of all symbols currently in scope and a chain of lexical scopes.

Evaluation contexts are arranged in a stack, with the current (top-most) evaluation context available with ce.context.

Evaluation contexts are created automatically, for example when a new scope is created, or each time a recursive function is called.

Some functions may create their own scope. These functions have the scoped flag set to true. For example, the Block function creates a new scope when it is called: any declarations made in the block are only visible within the block. Similarly, the Sum function creates a new scope so that the index variable is not visible outside the sum.

Binding

Name Binding is the process of associating a symbol with a definition.

Name Binding should not be confused with value binding which is the process of associating a value to a symbol.

To bind a symbol to a definition, the Compute Engine looks up the bindings table of the current scope. If the symbol is not found in the table, the parent scope is searched, and so on until a definition is found.

If no definition is found, the symbol is declared with a type of unknown, or a more specific type if the context allows it.

If the symbol is found, the definition record is used to evaluate the expression.

The definition record contains information about the symbol, such as its type, whether it is a constant, and how to evaluate it.

There are two kind of definition records:

  1. Value Definition Record: This record contains information about the symbol, such as its type and whether it is a constant.
  2. Operator Definition Record: This record contains information about the operator, such as its signature and how to evaluate it.

Name binding is done during canonicalization. If name binding failed, the isValid property of the expression is false.

To get a list of the errors in an expression use the expr.errors property.

Read more about the errors

Default Scopes

The Compute Engine has a set of default scopes that are used to look up symbols. These scopes are created automatically when the Compute Engine is initialized. The default scopes include the system scope, and the global scope.

The system scope contains the definitions of all the built-in functions and operators. The global scope is initially empty, but can be used to store user-defined functions and symbols.

The global scope is the default scope used when the Compute Engine is initialized.

Additional scopes can be created using the ce.pushScope() method.

Creating New Scopes

To add a new scope use ce.pushScope().

ce.assign('x', 100); // "x" is defined in the current scope
ce.pushScope();
ce.assign('x', 500); // "x" is defined in the new scope
ce.box('x').print(); // 500
ce.popScope();
ce.box('x').print(); // 100

To exit a scope use ce.popScope().

This will invalidate any definitions associated with the scope, and restore the symbol table from previous scopes that may have been shadowed by the current scope.

Evaluation Loop

:::info

This is an advanced topic. You don't need to know the details of how the evaluation loop works, unless you're interested in extending the standard library and providing your own function definitions.

:::

Each symbol is bound to a definition within a lexical scope during canonicalization. This usually happens when calling ce.box() or ce.parse(), or if accessing the .canonical property of a non-canonical expression.

When a function is evaluated, the following steps are followed:

  1. If the expression is not canonical, it cannot be evaluated and an error is thrown. The expression must be canonicalized first.

  2. Each argument of the function are evaluated, left to right, unless the function has a lazy flag. If the function is lazy, the arguments are not evaluated.

    1. An argument can be held, in which case it is not evaluated. Held arguments can be useful when you need to pass a symbolic expression to a function. If it wasn't held, the result of evaluating the expression would be used, not the symbolic expression.

      Alternatively, using the Hold function will prevent its argument from being evaluated. Conversely, the ReleaseHold function will force an evaluation.

    2. If an argument is a ["Sequence"] expression, treat each argument of the sequence expression as if it was an argument of the function. If the sequence is empty, ignore the argument.

  3. If the function is associative, flatten its arguments as necessary. \[ f(f(a, b), c) \to f(a, b, c) \]

  4. Apply the function to the arguments

  5. Return the result in canonical form.


title: Assumptions slug: /compute-engine/guides/assumptions/

Assumptions are statements about symbols that are assumed to be true. For example, the assumption that \\(x\\) is a positive real number can be used to simplify \\(|x|\\) to \\(x\\).

Assumptions can be used to describe the range of values that a symbol can take.

The assumptions knowledge base is then used by the Compute Engine to simplify expressions.

Read more about **Simplifying Expressions**

For example, the assumption that \(x\) is positive is used to simplify \(\sqrt{x^2}\) to \(x\).

ce.assume(["Greater", "x", 0]);
ce.parse("\\sqrt{x^2}").simplify().print();
// ➔ x

Assumptions can be used for other operations as well, such as solving equations or integrating.

Defining New Assumptions

To make an assumption about a symbol, use the ce.assume() method.

For example, to indicate \(\beta \neq 0\):

ce.assume(ce.parse("\\beta \\neq 0"));

Each call to ce.assume() replaces the previous assumptions about the symbol.

Assumptions Lifecycle

Assumptions are stored in a knowledge base which is tied to the current evaluation context.

Any assumptions made in the current context are automatically inherited by all child contexts.

When a context is exited, all assumptions made in that context are forgotten.

To remove previous assumptions, use ce.forget().

  • Invoking ce.forget() with no arguments will remove all assumptions.
  • Passing an array of symbol names will remove assumptions about each of the symbols.
  • Passing a symbol name will only remove assumptions about that particular symbol.
ce.declare("x", "number");
ce.assume(ce.parse("x > 0"));
ce.is(ce.parse("x > 2"));
// ➔  true

ce.forget("x");

ce.is(ce.parse("x > 2"));
// ➔  undefined

To temporarily define a series of assumptions, create a new scope.

ce.declare("x", "number");
ce.is(ce.parse("x > 2"));
// ➔ undefined

ce.pushScope();

ce.assume(ce.parse("x > 0"));
ce.is(ce.parse("x > 2"));
// ➔  true

ce.popScope(); // all assumptions made in the current scope are forgotten

ce.is(ce.parse("x > 2"));
// ➔  undefined

Assumption Propositions

The argument of the ce.assume() method is a proposition. A proposition is a statement that can be either true or false. Assumption propositions can take the following forms:

Operator
Element
NotElement
Indicate the domain of a symbol
Less
LessEqual
Greater
GreaterEqual
Inequality. Both sides are assumed to be RealNumbers
Equal
NotEqual
Equality

Some propositions can be described in several equivalent ways. You can use whichever form you prefer. Similarly, when querying the knowledge base later, you can use any form you'd like.

ce.assume(ce.parse("x > 0"));

// Equivalent to...
ce.assume(ce.parse("0 < x"));

Verifying Assumptions

To test if a particular assumption is valid call the ce.verify() method.

ce.verify(ce.parse("x > 0"));

The method ce.verify() return true if the assumption is true, false if it is not, and undefined if it cannot be determined.

While ce.verify() is appropriate to get boolean answers, more complex queries can also be made.

To query the assumptions knowledge base call the ce.ask() method.

The argument of ce.ask() can be a pattern, and it returns an array of matches as Substitution objects.

// "x is a positive integer"
ce.assume(ce.parse("x > 0"));

// "What is x greater than?"
ce.ask(["Greater", "x", "_val"]);

//  -> [{"val": 0}] "It is greater than 0"
Read more about **Patterns and Rules**

title: MathJSON Format slug: /math-json/ description: MathJSON is a lightweight data interchange format for mathematical notation hide_title: true sidebar_class_name: "sdk-icon"

import Mathfield from '@site/src/components/Mathfield'; import ConsoleMarkup from '@site/src/components/ConsoleMarkup'; import {useState, useEffect} from 'react'; import {BrowserOnly} from '@docusaurus/BrowserOnly';

export function setupComputeEngine() { if (window.ce !== undefined) return; // If we're not ready, try again in 50ms if (!("ComputeEngine" in window)) { setTimeout(setupComputeEngine, 50); return; } const [value, setValue] = useState(children); const [json, setJson] = useState({}); window.ce = new ComputeEngine.ComputeEngine(); setJson(window.ce?.parse(value).json); } export function MathJSONOutput({children}) { const [value, setValue] = useState(children); const [json, setJson] = useState({}); useEffect(setupComputeEngine, []); // We need to use useEffect so that the MathfieldElement is available // when this code runs (in the client). useEffect(() => { setupComputeEngine(); setJson(window.ce?.parse(value).json); }, [value]); return<> <Mathfield style={{ width: "100%", borderRadius: "8px", padding: "8px", marginBottom: "1em" }} onChange={(ev) => setValue(ev.target.value)} >{value} </>; }

# MathJSON A lightweight data interchange format for mathematical notation.
Math MathJSON
n 1 + n ["Divide", "n", ["Add", 1, "n"]]
Double exponent: use braces to clarify

$$\sin^{-1}^\prime(x)$$
["Apply", ["Derivative", ["InverseFunction", "Sin"]], "x"]

MathJSON is built on the JSON format. Its focus is on interoperability between software programs to facilitate the exchange of mathematical data and the building of scientific software through the integration of software components communicating with a common format.

It is human-readable, while being easy for machines to generate and parse. It is simple enough that it can be generated, consumed and manipulated using any programming languages.

MathJSON can be transformed from (parsing) and to (serialization) other formats.

:::info[Demo] Type an expression in the mathfield below to see its MathJSON representation. {e^{i\\pi}+1=0} :::

The Cortex Compute Engine library provides an implementation in JavaScript/TypeScript of utilities that parse LaTeX to MathJSON, serialize MathJSON to LaTeX, and provide a collection of functions for symbolic manipulation and numeric evaluations of MathJSON expressions.

Try a demo of the **Compute Engine** Read more about the **Compute Engine** LaTeX syntax parsing and serializing

Mathematical notation is used in a broad array of fields, from elementary school arithmetic, engineering, applied mathematics to physics and more. New notations are invented regularly and MathJSON endeavors to be flexible and extensible to account for those notations.

The Compute Engine includes a standard library of functions and symbols which can be extended with custom libraries.

Read more about the **MathJSON Standard Library**

MathJSON is not intended to be suitable as a visual representation of arbitrary mathematical notations, and as such is not a replacement for LaTeX or MathML.

Structure of a MathJSON Expression

A MathJSON expression is a combination of numbers, symbols, strings and functions.

Number

3.14
314e-2
{"num": "3.14159265358979323846264338327950288419716939937510"}
{"num": "-Infinity"}

Symbol

"x"
"Pi"
"🍎"
"半径"
{"sym": "Pi", "wikidata": "Q167"}

String

"'Diameter of a circle'"
{"str": "Srinivasa Ramanujan"}

Function

["Add", 1, "x"]
{"fn": [{"sym": "Add"}, {"num": "1"}, {"sym": "x"}]}

Numbers, symbols, strings and functions are expressed either as object literals with a "num" "str" "sym" or "fn" key, respectively, or using a shorthand notation as a a JSON number, string or array.

The shorthand notation is more concise and easier to read, but it cannot include metadata properties.

Numbers

A MathJSON number is either:

  • an object literal with a "num" key
  • a JSON number
  • a JSON string starting with +, - or the digits 0-9. Using a string is useful to represent numbers with a higher precision or greater range than JSON numbers.

Numbers as Object Literals

Numbers may be represented as an object literal with a "num" key. The value of the key is a string representation of the number.

{
  "num": <string>
}

The string representing a number follows the JSON syntax for number, with the following differences:

  • The range or precision of MathJSON numbers may be greater than the range and precision supported by IEEE 754 64-bit float.
{ "num": "1.1238976755823478721365872345683247563245876e-4567" }
  • The string values "NaN" "+Infinity" and "-Infinity" are used to represent respectively an undefined result as per IEEE 754, + , and .
{ "num": "+Infinity" }
  • If the string includes the pattern /\([0-9]+\)/, that is a series of one or more digits enclosed in parentheses, that pattern is interpreted as repeating digits.
{ "num": "1.(3)" }
{ "num": "0.(142857)" }
{ "num": "0.(142857)e7" }
  • The following characters in a string representing a number are ignored:
U+0009 TAB
U+000A LINE FEED
U+000B VERTICAL TAB
U+000C FORM FEED
U+000D CARRIAGE RETURN
U+0020 SPACE
U+00A0 UNBREAKABLE SPACE

Numbers as Number Literals

When a number is compatible with the JSON representation of numbers and has no metadata, a JSON number literal may be used.

Specifically:

  • the number fits in a 64-bit binary floating point, as per IEEE 754-2008, with a 53-bit significand (about 15 digits of precision) and 11-bit exponent. If negative, its range is from 1.797693134862315 10 + 308 to 2.225073858507201 10 308 and if positive from 2.225073858507201 10 308 to 1.797693134862315 10 + 308
  • the number is finite: it is not +Infinity -Infinity or NaN.
0
-234.534e-46

The numeric values below may not be represented as JSON number literals:

// Exponent out of bounds
{ "num": "5.78e400" }

// Too many digits
{ "num": "3.14159265358979323846264338327950288419716" }

// Non-finite numeric value
{ "num": "-Infinity" }

Numbers as String Literals

An alternate representation of a number with no extra metadata is as a string following the format described above.

This allows for a shorthand representation of numbers with a higher precision or greater range than JSON numbers.

"3.14159265358979323846264338327950288419716"
"+Infinity"

Strings

A MathJSON string is either:

  • an object literal with a "str" key
  • a JSON string that starts and ends with U+0027 ' APOSTROPHE .

Strings may contain any character represented by a Unicode scalar value (a codepoint in the [0...0x10FFFF] range, except for [0xD800...0xDFFF]), but the following characters must be escaped as indicated:

Codepoint Name Escape Sequence
U+0000 to U+001F \u0000 to \u001f
U+0008 BACKSPACE \b or \u0008
U+0009 TAB \t or \u0009
U+000A LINE FEED \n or \u000a
U+000C FORM FEED \f or \u000c
U+000D CARRIAGE RETURN \r or \u000d
U+0027 APOSTROPHE \' or \u0027
U+005C REVERSE SOLIDUS (backslash) \\ or \u005c

The encoding of the string follows the encoding of the JSON payload: UTF-8, UTF-16LE, UTF-16BE, etc...

"'Alan Turing'"

Functions

A MathJSON function expression is either:

  • an object literal with a "fn" key.
  • a JSON array

Function expressions in the context of MathJSON may be used to represent mathematical functions but are more generally used to represent the application of a function to some arguments.

The function expression ["Add", 2, 3] applies the function named Add to the arguments 2 and 3.

Functions as Object Literal

The default representation of function expressions is an object literal with a "fn" key. The value of the fn key is an array representing the function operator (its name) and its arguments (its operands).

{
  "fn": [Operator, ...Operands[]]
}

For example:

  • 2 + x : { "fn": ["Add", 2, "x"] }
  • sin ( 2 x + π ) : { "fn": ["Sin", ["Add", ["Multiply", 2, "x"], "Pi"]] }
  • x 2 3 x + 5 : { "fn": ["Add", ["Power", "x", 2], ["Multiply", -3, "x"], 5] }

Functions as JSON Arrays

If a function expression has no extra metadata it may be represented as a JSON array.

For example these two expressions are equivalent:

{ "fn": ["Cos", ["Add", "x", 1]] }

["Cos", ["Add", "x", 1]]

:::note An array representing a function must have at least one element, the operator of the function. Therefore [] is not a valid expression. :::

Function Operator

The operator of the function expression is the first element in the array. Its presence is required. It indicates the name of the function: this is what the function is about.

The operator is a symbol following the conventions for function names (see below).

// Apply the function "Sin" to the argument "x"
["Sin", "x"]

// Apply "Cos" to a function expression
["Cos", ["Divide", "Pi", 2]]

Following the operator are zero or more arguments (or operands), which are expressions.

:::warning[CAUTION] The arguments of a function are expressions. To represent an argument which is a list, use a ["List"] expression, do not use a JSON array. :::

The expression corresponding to $ \sin^{-1}(x) $ is:

["Apply", ["InverseFunction", "Sin"], "x"]

The operator of this expression is "Apply" and its argument are the expressions ["InverseFunction", "Sin"] and "x".

Shorthands

The following shorthands are allowed:

  • A ["Dictionary"] expression may be represented as a string starting with U+007B { LEFT CURLY BRACKET and ending with U+007D } RIGHT CURLY BRACKET. The string must be a valid JSON object literal.
  • A ["List"] expression may be represented as a string starting with U+005B [ LEFT SQUARE BRACKET and ending with U+005D ] RIGHT SQUARE BRACKET. The string must be a valid JSON array.
"{\"x\": 2, \"y\": 3}"
// ➔ ["Dictionary", ["Tuple", "x", 2], ["Tuple", "y", 3]]

"[1, 2, 3]"
// ➔ ["List", 1, 2, 3]

Symbols

A MathJSON symbol is either:

  • an object literal with a "sym" key
  • a JSON string

Symbols are JSON strings that represent the names of symbols, variables, constants, wildcards and functions.

Before they are used, JSON escape sequences (such as \u sequences, \\, etc.) are decoded.

The symbols are then normalized to the Unicode Normalization Form C (NFC). They are stored internally and compared using the Unicode NFC.

For example, these four JSON strings represent the same symbol:

  • "Å"
  • "A\u030a" U+0041 A‌ LATIN CAPITAL LETTER + U+030A ̊ COMBINING RING ABOVE
  • "\u00c5" U+00C5 Å LATIN CAPITAL LETTER A WITH RING ABOVE
  • "\u0041\u030a" U+0041 A‌ LATIN CAPITAL LETTER A + U+030A ̊ COMBINING RING ABOVE

Symbols conform to a profile of UAX31-R1-1 with the following modifications:

  • The character U+005F _ LOW LINE is added to the Start character set
  • The characters should belong to a recommended script
  • A symbol can be a sequence of one or more emojis. Characters that have both the Emoji and XIDC property are only considered emojis when they are preceded with emoji modifiers. The definition below is based on Unicode TR51 but modified to exclude invalid symbols.

Symbols match either the NON_EMOJI_SYMBOL or the EMOJI_SYMBOL regex patterns below:

const NON_EMOJI_SYMBOL = /^[\p{XIDS}_]\p{XIDC}*$/u;

(from Unicode TR51)

or

const VS16 = "\\u{FE0F}"; // Variation Selector-16, forces emoji presentation
const KEYCAP = "\\u{20E3}"; // Combining Enclosing Keycap
const ZWJ = "\\u{200D}"; // Zero Width Joiner

const FLAG_SEQUENCE = "\\p{RI}\\p{RI}";

const TAG_MOD = `(?:[\\u{E0020}-\\u{E007E}]+\\u{E007F})`;
const EMOJI_MOD = `(?:\\p{EMod}|${VS16}${KEYCAP}?|${TAG_MOD})`;
const EMOJI_NOT_SYMBOL = `(?:(?=\\P{XIDC})\\p{Emoji})`;
const ZWJ_ELEMENT = `(?:${EMOJI_NOT_SYMBOL}${EMOJI_MOD}*|\\p{Emoji}${EMOJI_MOD}+|${FLAG_SEQUENCE})`;
const POSSIBLE_EMOJI = `(?:${ZWJ_ELEMENT})(${ZWJ}${ZWJ_ELEMENT})*`;
const EMOJI_SYMBOL = new RegExp(`^(?:${POSSIBLE_EMOJI})+$`, "u");

In summary, when using Latin characters, symbols can start with a letter or an underscore, followed by zero or more letters, digits and underscores.

Carefully consider when to use non-latin characters. Use non-latin characters for whole words, for example: "半径" (radius), "מְהִירוּת" (speed), "直徑" (diameter) or "सतह" (surface).

Avoid mixing Unicode characters from different scripts in the same symbols.

Do not include bidi markers such as U+200E LTR* or U+200F RTL in symbols. LTR and RTL marks should be added as needed by the client displaying the symbol. They should be ignored when parsing symbols.

Avoid visual ambiguity issues that might arise with some Unicode characters. For example:

  • prefer using "gamma" rather than U+0194 ɣ LATIN SMALL LETTER GAMMA or U+03B3 γ GREEK SMALL LETTER GAMMA
  • prefer using "Sum" rather than U+2211 N-ARY SUMMATION, which can be visually confused with U+03A3 Σ GREEK CAPITAL LETTER SIGMA.

The following naming convention for wildcards, variables, constants and function names are recommendations.

Wildcards Naming Convention

Symbols that begin with U+005F _ LOW LINE (underscore) should be used to denote wildcards and other placeholders.

For example, they may be used to denote the positional parameter in a function expression. They may also denote placeholders and captured expression in patterns.

Wildcard
"_" Wildcard for a single expression or for the first positional argument
"_1" Wildcard for a positional argument
"_ _" Wildcard for a sequence of 1 or more expression
"___" Wildcard for a sequence of 0 or more expression
"_a" Capturing an expression as a wildcard named a

Variables Naming Convention

  • If a variable is made of several words, use camelCase. For example "newDeterminant"

  • Prefer clarity over brevity and avoid obscure abbreviations.

    Use "newDeterminant" rather than "newDet" or "nDet"

Constants Naming Convention

  • If using latin characters, the first character of a constant should be an uppercase letter A-Z

  • If the name of a constant is made up of several words, use PascalCase. For example "SpeedOfLight"

Function Names Naming Convention

  • The name of the functions in the MathJSON Standard Library starts with an uppercase letter A-Z. For example "Sin", "Fold".

  • The name of your own functions can start with a lowercase or uppercase letter.

  • If the name of a function is made up of several words, use PascalCase or camelCase. For example "InverseFunction" or "inverseFunction".

LaTeX Rendering Conventions

The following recommendations may be followed by clients displaying MathJSON symbols with LaTeX, or parsing LaTeX to MathJSON symbols.

These recommendations do not affect computation or manipulation of expressions following these conventions.

  • A symbol may be composed of a main body, some modifiers, some style variants, some subscripts and superscripts. For example:
    • "alpha_0__prime" α 0
    • "x_vec" $$ \vec{x} $$
    • "Re_fraktur" Re .
  • Subscripts are indicated by an underscore _ and superscripts by a double-underscore __. There may be more than one superscript or subscript, but they get concatenated. For example "a_b__c_q__p" -> a_{b, q}^{c, p} \( a_{b, q}^{c, p} \).
  • Modifiers after a superscript or subscript apply to the closest preceding superscript or subscript. For example "a_b_prime" -> a_{b^{\prime}}

Modifiers include:

Modifier LaTeX
_deg \degree \( x\degree \)
_prime {}^\prime \( x^{\prime} \)
_dprime {}^\doubleprime \( x^{\doubleprime} \)
_ring \mathring{} \( \mathring{x} \)
_hat \hat{} \( \hat{x} \)
_tilde \tilde{} \( \tilde{x} \)
_vec \vec{} \( \vec{x} \)
_bar \overline{} \( \overline{x} \)
_underbar \underline{} \( \underline{x} \)
_dot \dot{} \( \dot{x} \)
_ddot \ddot{} \( \ddot{x} \)
_tdot \dddot{} \( \dddot{x} \)
_qdot \ddddot{} \( \dddodt{x} \)
_operator \operatorname{} \( \operatorname{x} \)
_upright \mathrm{} \( \mathrm{x} \)
_italic \mathit{} \( \mathit{x} \)
_bold \mathbf{} \( \mathbf{x} \)
_doublestruck \mathbb{} \( \mathbb{x} \)
_fraktur \mathfrak{} \( \mathfrak{x} \)
_script \mathscr{} \( \mathscr{x} \)
  • The following common names, when they appear as the body or in a subscript/superscript of a symbol, may be replaced with a corresponding LaTeX command:
Common Names LaTeX
alpha \alpha \( \alpha \)
beta \beta \( \beta \)
gamma \gamma \( \gamma \)
delta \delta \( \delta \)
epsilon \epsilon \( \epsilon \)
epsilonSymbol \varepsilon \( \varepsilon \)
zeta \zeta \( \zeta \)
eta \eta \( \eta \)
theta \theta \( \theta \)
thetaSymbol \vartheta \( \vartheta \)
iota \iota \( \iota \)
kappa \kappa \( \kappa \)
kappaSymbol \varkappa \( \varkappa \)
mu \mu \( \mu \)
nu \nu \( \nu \)
xi \xi \( \xi \)
omicron \omicron \( \omicron \)
piSymbol \varpi \( \varpi \)
rho \rho \( \rho \)
rhoSymbol \varrho \( \varrho \)
sigma \sigma \( \sigma \)
finalSigma \varsigma \( \varsigma \)
tau \tau \( \tau \)
phi \phi \( \phi \)
phiLetter \varphi \( \varphi \)
upsilon \upsilon \( \upsilon \)
chi \chi \( \chi \)
psi \psi \( \psi \)
omega \omega \( \omega \)
Alpha \Alpha \( \Alpha \)
Beta \Beta \( \Beta \)
Gamma \Gamma \( \Gamma \)
Delta \Delta \( \Delta \)
Epsilon \Epsilon \( \Epsilon \)
Zeta \Zeta \( \Zeta \)
Eta \Eta \( \Eta \)
Theta \Theta \( \Theta \)
Iota \Iota \( \Iota \)
Kappa \Kappa \( \Kappa \)
Lambda \Lambda \( \Lambda \)
Mu \Mu \( \Mu \)
Nu \Nu \( \Nu \)
Xi \Xi \( \Xi \)
Omicron \Omicron \( \Omicron \)
Pi \Pi \( \Pi \)
Rho \Rho \( \Rho \)
Sigma \Sigma \( \Sigma \)
Tau \Tau \( \Tau \)
Phi \Phi \( \Phi \)
Upsilon \Upsilon \( \Upsilon \)
Chi \Chi \( \Chi \)
Psi \Psi \( \Psi \)
Omega \Omega \( \Omega \)
digamma \digamma \( \digamma \)
aleph \aleph \( \aleph \)
lambda \lambda \( \lambda \)
bet \beth \( \beth \)
gimel \gimel \( \gimel \)
dalet \dalet \( \dalet \)
ell \ell \( \ell \)
turnedCapitalF \Finv \( \Finv \)
turnedCapitalG \Game \( \Game \)
weierstrass \wp \( \wp \)
eth \eth \( \eth \)
invertedOhm \mho \( \mho \)
hBar \hbar \( \hbar \)
hSlash \hslash \( \hslash \)
blacksquare \hslash \( \hslash \)
bottom \bot \( \bot \)
bullet \bullet \( \bullet \)
circle \circ \( \circ \)
diamond \diamond \( \diamond \)
times \times \( \times \)
top \top \( \top \)
square \square \( \square \)
star \star \( \star \)
  • The following names, when used as a subscript or superscript, may be replaced with a corresponding LaTeX command:
Subscript/Supscript LaTeX
plus {}_{+} / {}^{+} \( x_{+} x^+\)
minus {}_{-} /{}^{-} \( x_{-} x^-\)
pm {}_\pm /{}^\pm \( x_{\pm} x^\pm \)
ast {}_\ast /{}^\ast \( {x}_\ast x^\ast \)
dag {}_\dag /{}^\dag \( {x}_\dag x^\dag \)
ddag {}_\ddag {}^\ddag \( {x}_\ddag x^\ddag \)
hash {}_\# {}^\# \( {x}_# x^#\)
  • Multi-letter symbols may be rendered with a \mathit{}, \mathrm{} or \operatorname{} command.

  • Symbol fragments ending in digits may be rendered with a corresponding subscript.

Symbol LaTeX
time \mathrm{time} \( \mathrm{time} \)
speed_italic \mathit{speed} \( \mathit{speed} \)
P_blackboard__plus \mathbb{P}^{+} $$ \mathbb{P}^+ $$
alpha \alpha \( \alpha \)
mu0 \mu_{0} \( \mu_0 \)
m56 m_{56} \( m_{56} \)
c_max \mathrm{c_{max}} \( \mathrm{c_{max}} \)

Metadata

MathJSON object literals may be annotated with supplemental information.

A number represented as a JSON number literal, a symbol or string represented as a JSON string literal, or a function represented as a JSON array must be transformed into the equivalent object literal to be annotated.

The following metadata keys are recommended:

Key Note
wikidata A short string indicating an entry in a wikibase.
This information can be used to disambiguate the meaning of a symbol. Unless otherwise specified, the entry in this key refers to an entry in the wikidata.org wikibase
comment A human readable plain string to annotate an expression, since JSON does not allow comments in its encoding
documentation A Markdown-encoded string providing documentation about this expression.
latex A visual representation in LaTeX of the expression.
This can be useful to preserve non-semantic details, for example parentheses in an expression or styling attributes
sourceUrl A URL to the source of this expression
sourceContent The source from which this expression was generated.
It could be a LaTeX expression, or some other source language.
sourceOffsets A pair of character offsets in sourceContent or sourceUrl from which this expression was produced
hash A string representing a digest of this expression.
{
  "sym": "Pi",
  "comment": "The ratio of the circumference of a circle to its diameter",
  "wikidata": "Q167",
  "latex": "\\pi"
}

{
  "sym": "Pi",
  "comment": "The greek letter ∏",
  "wikidata": "Q168",
}

MathJSON Standard Library

This document defines the structure of MathJSON expression. The MathJSON Standard Library defines a recommended vocabulary to use in MathJSON expressions.

Before considering inventing your own vocabulary, check if the MathJSON Standard Library already provides relevant definitions.

The MathJSON Standard Library includes definitions for:

Topic
Arithmetic Add Multiply Power Exp Log ExponentialE ImaginaryUnit...
Calculus D Derivative Integrate...
Collections List Reverse Filter...
Complex Real Conjugate ComplexRoots...
Control Structures If Block Loop ...
Core Declare Assign Error LatexString...
Functions Function Apply Return ...
Logic And Or Not True False ForAll ...
Sets Union Intersection EmptySet RealNumbers Integers ...
Special Functions Gamma Factorial...
Statistics StandardDeviation Mean Erf...
Styling Delimiter Style...
Trigonometry Pi Cos Sin Tan...

When defining a new function, avoid using a name already defined in the Standard Library.

Read more about **Adding New Definitions** --- title: Calculus slug: /compute-engine/reference/calculus/ --- Calculus is the mathematical study of continuous change.

It has two main branches: differential calculus and integral calculus. These two branches are related by the fundamental theorem of calculus:

$$\int_a^b f(x) \,\mathrm{d}x = F(b) - F(a)$$

...where $ F $ is an antiderivative of $ f $, that is $ F' = f $.

To calculate the derivative of a function, use the D function to calculate a symbolic derivative or ND to calculate a numerical approximation

To calculate the integral (antiderivative) of a function, use the Integrate function to calculate a symbolic integral or NIntegrate to calculate a numerical approximation.

To calculate the limit of a function, use the Limit function.

Derivative

The derivative of a function is a measure of how the function changes as its input changes. It is the ratio of the change in the value of a function to the change in its input value. The derivative of a function $$ f(x) $$ with respect to its input $$ x $$ is denoted by:

The derivative of a function $$ f(x) $$ is defined as:

$$f'(x) = \lim_{h \to 0} \frac{f(x + h) - f(x)}{h}$$

where $$ \lim_{h \to 0} \frac{f(x + h) - f(x)}{h} $$ is the limit of the ratio of the change in the value of the function to the change in its input value as $$ h $$ approaches $$ 0 $$.

The limit is taken as $$ h $$ approaches $$ 0 $$ because the derivative is the instantaneous rate of change of the function at a point, and the change in the input value must be infinitesimally small to be instantaneous.

Reference

Lagrange Notation

LaTeX MathJSON
f'(x) ["Derivative", "f", "x"]
f\prime(x) ["Derivative", "f", "x"]
f^{\prime}(x) ["Derivative", "f", "x"]
f''(x) ["Derivative", "f", "x", "x"]
f\prime\prime(x) ["Derivative", "f", "x", "x"]
f^{\prime\prime}(x) ["Derivative", "f", "x", "x"]
f\doubleprime(x) ["Derivative", "f", "x", "x"]
f^{(n)}(x) ["Derivative", "f", "x", n]

The Derivative function represents a derivative of a function with respect to a single variable. The D function is used to calculate the symbolic derivative of a function with respect to one or more variables. The ND function is used to calculate a numerical approximation of the derivative of a function.

f: number -> number, var: symbol

The D function represents the partial derivative of a function f with respect to the variable var.

["D", "f", "x"]

f, ...var: symbol

Multiple variables can be specified to compute the partial derivative of a multivariate function.

["D", "f", "x", "y"]

A variable can be repeated to compute the second derivative of a function.

["D", "f", "x", "x"]

f: number -> number, x: number

The ND function returns a numerical approximation of the partial derivative of a function f at the point x .

["ND", "Sin", 1]
// ➔ 0.5403023058681398

Note: ["ND", "Sin", 1] is equivalent to ["Apply", ["D", "Sin"], 1].

f: number -> number

The Derivative function represents the derivative of a function f .

["Apply", ["Derivative", "f"], "x"]

f: number -> number, n: integer

When an argument n is present it represents the n-th derivative of a function expr.

["Apply", ["Derivative", "f", n], "x"]

Derivative is an operator in the mathematical sense, that is, a function that takes a function as an argument and returns a function.

The Derivative function is used to represent the derivative of a function in a symbolic form. It is not used to calculate the derivative of a function. To calculate the derivative of a function, use the D function or ND to calculate a numerical approximation.

    ["Derivative", "f", "x"]
is equivalent to
    ["D", ["f", "x"], "x"]

Integral

The integral of a function $$ f(x) $$ is denoted by:

:::info[Note] The commands \! and \, adjust the spacing. The \! command reduces the space between the integral sign and the integrand, while the \, command increases the space before the differential operator d.

The \mathrm command is used to typeset the differential operator d in an upright font.

These typesetting conventions are part of the ISO 80000-2:2009 standard for mathematical notation, but are not universally adopted. :::

The indefinite integral of a function $$ f(x) $$ is the family of all antiderivatives of a function:

$$\int f(x) \,\mathrm{d}x = F(x) + C$$

where F ( x ) is the antiderivative of f ( x ) , meaning F ( x ) = f ( x ) and C is the constant of integration, accounting for the fact that there are many functions that can have the same derivative, differing only by a constant.

A definite integral of a function $$ f(x) $$ is the signed area under the curve of the function between two points $$ a $$ and $$ b $$:

$$\int_a^b f(x) \,\mathrm{d}x = F(b) - F(a)$$

The \limits command controls the placement of the limits of integration.

A double integral of a function $$ f(x, y) $$ is the signed volume under the surface of the function between two points $$ a $$ and $$ b $$ in the x-direction and two points $$ c $$ and $$ d $$ in the y-direction:

The \iint command is used to typeset the double integral symbol.

To calculate the symbolic integral of a function, use the Integrate function.

To calculate a numerical approximation of the integral of a function, use the NIntegrate function.

Reference

_f_: function

Evaluates to a symbolic indefinite integral of a function $$ f $$.

["Integrate", "Sin"]

The argument f, the integrand, is a function literal, which can be expressed in different ways:

  • As a symbol whose value is a function: ["Integrate", "f"]
  • As a symbol for a built-in function: ["Integrate", "Sin"]
  • As a ["Function"] expression: ["Integrate", ["Function", ["Sin", "x"], "x"]]
  • As a shorthand function literal: ["Integrate", ["Power", "_", 2]]
  • As an expression with unknowns: ["Integrate", ["Power", "x", 2]]

f: function, ...var:symbol

Symbolic indefinite integral of a function $$ f $$ with respect to a variable $$ x $$.

["Integrate", ["Sin", "x"], "x"]

Symbolic indefinite integral of a function $$ f $$ with respect to a variable $ x $ and $ y $.

["Integrate", 
  ["Add", ["Sin", ["Power", "x", 2]], ["Power", "y", 2]], 
  "x", "y"
]

Symbolic indefinite integral of a function $$ f $$ with respect to a variable $$ x $$, applied twice.

["Integrate", ["Sin", "x"], "x", "x"]

f: function, ...limits:tuple

A definite integral of a function $$ f $$. The function is evaluated symbolically as:

$$\int_a^b f(x) \,\mathrm{d}x = F(b) - F(a)$$

where $$ F $$ is the antiderivative of $$ f $$.

The limits tuples indicate the variable of integration and the limits of integration.

The first element of the tuple is the variable of integration, and the second and third elements are the lower and upper limits of integration, respectively.

["Integrate", 
  ["Power", "x", 2], 
  ["Tuple", "x", 0, 2]
]

The variable of integration can be omitted if it is the same as the argument of the function.

["Integrate", 
  ["Power", "x", 2], 
  ["Tuple", 0, 2]
]

Double integrals can be computed by specifying more than one limit.

["Integrate", 
  ["Add", ["Power", "x", 2], ["Power", "y", 2]], 
  ["Tuple", "x", 0, 2], 
  ["Tuple", "y", 1, 3]
]

Some functions do not have a closed form for their antiderivative, and the integral cannot be computed symbolically. In this case, the Integrate function returns a symbolic representation of the integral. Use NIntegrate to compute a numerical approximation of the integral.

_f_:function, ..._limits_:tuple

Calculate a numerical approximation of the definite integral of a function.

["NIntegrate", ["Power", "x", 2], ["Tuple", 0, 2]]
// -> 2.6666666666666665

The limits tuples indicate the variable of integration and the limits of integration.

The first element of the tuple is the variable of integration, and the second and third elements are the lower and upper limits of integration, respectively.

The variable of integration can be omitted if it is the same as the argument of the function.

["NIntegrate", ["Power", "x", 2], ["Tuple", 0, 2]]
// -> 2.6666666666666665

A double integral can be computed by specifying more than one limit.

["NIntegrate", 
  ["Add", ["Power", "x", 2], ["Power", "y", 2]], 
  ["Tuple", 0, 2], 
  ["Tuple", 1, 3]
]
// -> 20.666666666666668

The numerical approximation is computed using a Monte Carlo method.

Limit

The limit of a function $$ f(x) $$ as $$ x $$ approaches a value $$ a $$ is the value that $$ f(x) $$ approaches as $$ x $$ gets arbitrarily close to $$ a $$.

It is denoted by:

["Limit", ["f", "x"], "a"]

f: function, value: number

Evaluate the function f as it approaches the value value.

["Limit", ["Divide", ["Sin", "_"], "_"], 0]

["Limit", ["Function", ["Divide", ["Sin", "x"], "x"], "x"], 0]

This function evaluates to a numerical approximation when using expr.N(). To get a numerical evaluation with expr.evaluate(), use NLimit.

f: function, value: number

Evaluate the function f as it approaches the value value.

["NLimit", ["Divide", ["Sin", "_"], "_"], 0]
// ➔ 1

["NLimit", ["Function", ["Divide", ["Sin", "x"], "x"], "x"], 0]
// ➔ 1

The numerical approximation is computed using a Richardson extrapolation algorithm.


title: MathJSON Standard Library slug: /compute-engine/standard-library/ hide_table_of_contents: true

The MathJSON standard library defines the vocabulary used by a MathJSON expression.

This library defines the meaning of the symbols used in a MathJSON expression. It is independent of the syntax used to parse/serialize from another language such as LaTeX.

It includes definitions such as:

  • "Pi is a transcendental number whose value is approximately 3.14159265..."
  • "The Add function is associative, commutative, pure, idempotent and can be applied to an arbitrary number of Real or Complex numbers".

Topics

The MathJSON Standard Library is organized by topics, each topic is a separate page in the documentation.

Topic
Arithmetic Add Multiply Power Exp Log ExponentialE ImaginaryUnit...
Calculus D Derivative Integrate...
Collections List Reverse Filter...
Complex Real Conjugate, ComplexRoots...
Control Structures If Block Loop ...
Core Declare Assign Error LatexString...
Functions Function Apply Return ...
Logic And Or Not True False ForAll ...
Sets Union Intersection EmptySet RealNumbers Integers ...
Special Functions Gamma Factorial...
Statistics StandardDeviation Mean Erf...
Styling Delimiter Style...
Trigonometry Pi Cos Sin Tan...

Extending the MathJSON Standard Library

The MathJSON Standard Library can be extended by defining new functions:

// Declare that the symbol "f" is a function, 
// but without giving it a definition
ce.declare("f", "function");

// Define a new function `double` that returns twice its input
ce.assign("double(x)", ["Multiply", "x", 2]);

// LaTeX can be used for the definition as well...
ce.assign("half(x)", ce.parse("\\frac{x}{2}"));
Read more about Augmenting the Standard Library

You can also customize the LaTeX syntax, that is how to parse and serialize LaTeX to MathJSON.

Read more about Parsing and Serializing LaTeX --- title: Patterns and Rules slug: /compute-engine/guides/patterns-and-rules/ ---

Recognizing patterns and applying rules is a powerful symbolic computing tool to identify and manipulate the structure of expressions.

Wildcards

Wildcards are placeholders symbols in an expression. They start with a _.

The "_" universal wildcard matches anything that is in the corresponding position in an expression.

The "__" wildcard matches any sequence of 1 or more expressions in its corresponding position. It is useful to capture the arguments of a function.

The "___" wildcard matches any sequence of 0 or more expressions in its corresponding position.

A wildcard symbol may include a name which is used to capture the matching expression, for example _a.

When using a named wildcard, all instances of the named wildcard must match. In contrast, an un-named wildcard (a universal wildcard such as "_" "__" or "___") can be used multiple times to match different values.

Patterns

A pattern is an expression which can include one or more placeholders in the form of wildcards.

Patterns are similar to Regular Expressions in traditional programming languages but they are tailored to deal with MathJSON expressions instead of strings.

Given a pattern and an expression the goal of pattern matching is to find a substitution for all the wildcards such that the pattern becomes the expression.

An expression is said to match a pattern if there exists a set of values such that replacing the wildcards with those values is equal to the expression. This set of values is called a substitution.

For example, the pattern ["Add", 3, "_c"] becomes the expression ["Add", 3, "x"] by replacing the wildcard "_c" with "x". The substitution is therefore {_c : "x"}.

On the other hand, the expression ["Divide", "x", 2] does not match the pattern ["Add", 3, "_c"]: no substitution exists to transform the pattern into the expression by substituting the wildcards.

Matching an Expression to a Pattern

To check if an expression matches a pattern, use the _expression_.match(_pattern_) method.

If there is no match, the method returns null.

If there is a match, a Substitution object literal with keys corresponding to the matching named wildcards is returned.

If no named wildcards are used and there is a match, an empty object literal is returned.

For convenience, the pattern argument can be an unboxed MathJSON expression.

Commutativity

The commutativity of functions is taken into account when matching patterns.

const pattern = ce.box(["Add", "_", "x"]);
console.log("x+1 ➔", ce.box(["Add", 1, "x"]).match(pattern));
// ➔ { } : the expression matches the pattern

console.log("x+42 ➔", ce.box(["Add", "x", 42]).match(pattern));
// ➔ { } : the expression matches the pattern by commutativity

console.log("5*x ➔", ce.box(["Multiply", 5, "x"]).match(pattern));
// ➔ null : the expression does not match the pattern

Exact Matching

By default, the expr.match() method will match some variants of the same expression, for example x+_a and x are considered to match (with the substitution {_a : 0}).

const pattern = ce.box(["Add", "x", "_a"]);
const expr = ce.box("x");

console.log("x ➔", expr.match(pattern));
// ➔ { _a: 0 } : the expression matches the pattern

To prevent the matching of variants, set the exact property to true.

const pattern = ce.box(["Add", "x", "_a"]);
const expr = ce.box("x");

console.log("exact: x ➔", expr.match(pattern, {exact: true}));
// ➔ null : the expression does not match the pattern

The variants can be applied to the whole expression or to sub-expressions.

const pattern = ce.box(["Add", ["Multiply", "_a", "x"], "_b"]);

console.log("x ➔", ce.box("x").match(pattern));
// ➔ { _a: 1, _b: 0 } : the expression matches the pattern

Recursive Matching

By default, the expr.match() method does not consider sub-expressions: it is not recursive.

const pattern = ce.box(["Add", "_", "x"]);
const expr = ce.box(["Multiply", 2, ["Add", 1, "x"]]);

console.log("2(1+x) ➔", expr.match(pattern));
// ➔ null : the expression does not match the pattern

To match sub-expressions recursively, set the recursive property to true.

const pattern = ce.box(["Add", "_", "x"]);
const expr = ce.box(["Multiply", 2, ["Add", 1, "x"]]);

console.log("recursive: 2(1+x) ➔", expr.match(pattern, {recursive: true}));
// ➔ { } : the expression matches the pattern

Repeated Named Wildcards

If a named wildcard is referenced multiple times in a pattern, all its values must match.

console.log(ce.box(["Add", 1, "x"]).match(ce.box(["Add", '_a', '_a'])));
// ➔ null

console.log(ce.box(["Add", "x", "x"]).match(ce.box(["Add", '_a', '_a'])));
// ➔ { _a: "x" }

Capturing the Head of Functions

Wildcards can be used to capture the head of functions:

console.log(ce.box(["Add", 1, "x"]).match(ce.box(["_f", "__args"])));
// ➔ { _f: "Add", __args: ["Sequence", [1, "x"]] }

Substitution

The return value of the expr.match() function is a Substitution object: a mapping from wildcards to expressions.

If there is no match, expr.match() returns null.

To apply a substitution to a pattern, and therefore recover the expression it was derived from, use the subs() function.

const expr = ce.box(["Add", 1, "x"]);
const pattern = ce.box(["Add", 1, "_a"]);
const subs = expr.match(pattern);
console.log(subs);
// ➔ { _a: "x" }

pattern.subs(subs).print();
// ➔ ["Add", 1, "x"]

Applying Rewrite Rules

A rewrite rule is an object with three properties:

  • match: a matching pattern
  • replace: a substitution pattern
  • condition: an optional condition predicate

To apply a set of rules to an expression, call the expr.replace() function.

Each rule in the ruleset is applied to the expression in turn. If a rule matches, the expression is replaced by the substitution pattern of the rule.

const squareRule = {
  match: ["Multiply", "_x", "_x"],
  replace: ["Square", "_x"],
};

const expr = ce.box(["Multiply", 7, 7], { canonical: false });
(expr.replace(squareRule) ?? expr).print();
// ➔ ["Square", 7]

The expr.replace() function continues applying all the rules in the ruleset until no rules are applicable.

If expr is not canonical, the result of the replace operation is not canonical either.

Simplifying an Expression

The expr.simplify() function applies a collection of built-in rewrite rules.

You can define your own rules and apply them using expr.replace().

Substituting Symbols

If a pattern does not contain any named wildcards and only symbols, the expr.subs() function can be used to replace all occurrences of matching symbols.

const expr = ce.box(["Add", ["Multiply", "a", "x"], "b"]);

expr.replace([
    { match: "a", replace: 2 }, 
    { match: "b", replace: 3 }
  ], 
  { recursive: true }
)?.print();
// ➔ 2x + 3

expr.subs({"a": 2, "b": 3}).print();
// ➔ 2x + 3

title: Changelog - Compute Engine sidebar_label: Changelog slug: /compute-engine/changelog/ toc_max_heading_level: 2

Compute Engine Changelog

import ChangeLog from '@site/src/components/ChangeLog';

--- title: Linear Algebra slug: /compute-engine/reference/linear-algebra/ --- [Linear algebra](https://en.wikipedia.org/wiki/Linear_algebra) is the branch of mathematics that studies vector spaces and linear transformations between them like adding and scaling. It uses matrixes to represent linear maps. Linear algebra is widely used in science and engineering.

In the Compute Engine matrices are represented as lists of lists.

For example the matrix above is represented as the following list of lists:

["List", ["List", 1, 3, ["List", 5, 0]]]

An axis is a dimension of a tensor. A vector has one axis, a matrix has two axes, a tensor (multi-dimensional matrix) has more than two axes.

The shape of a tensor is the length of each of its axes. For example, a matrix with 3 rows and 4 columns has a shape of (3, 4).

The rank of a tensor is the number of axes of the tensor. For example, a matrix has a rank of 2. Note that rank is sometimes used with a different meaning. For example, the rank of a matrix can also be defined as the maximum number of linearly independent rows or columns of the matrix. In the Compute Engine, rank is always used with the meaning of the number of axes.

In the Compute Engine, matrices are stored in row-major order. This means that the first element of the outer axis is the first row of the matrix, the second element of the list is the second row of the matrix, etc.

Since matrixes are List collections, some collection operations can also be applied to them such as At, Fold and Map.

An extension of linear algebra is tensor algebra which studies tensors, which are multidimensional arrays.

For example, a grayscale image can be represented by a matrix of grayscale values. But a color image is represented by a rank 3 tensor, an array of RGB triplets. Tensors are also represented as nested lists.

The Compute Engine provides a number of functions for working with matrices.

Representing Matrices

Vectors (row vectors) are represented as lists, that is an expression with the head List.

Matrixes are represented as lists of lists.

Tensors (multi-dimensional matrixes) are represented as nested lists.

:::info[Note] Tensors are represented internally using an optimized format that is more efficient than nested lists. Because of this, some operations on tensors such as Reshape and Transpose can be done in O(1) time. :::

Vector is a convenience function that interprets a list of elements as a column vector.

Matrix is an optional "tag" inert function that is used to influence the visual representation of a matrix. It has not impact on the value of the matrix.

In LaTeX notation, a matrix is represented with "environments" (with command \begin and \end) such as pmatrix or bmatrix.:

In LaTeX, each column is separated by an & and each row is separated by \.

x-1, ...x-2

Vector interprets the elements x-1... as a column vector

This is essentially a shortcut for ["Matrix", ["List", ["List", _x-1_], ["List, _x-2_], ...]]].

["Vector", 1, 3, 5, 0]

A row vector can be represented with a simple list or a tuple.

["List", 1, 3, 5, 0]

matrix

matrix, delimiters, columns-format

Matrix is an inert function: its value is the value of its first argument. It influences the visual representation of a matrix.

matrix is a matrix represented by a list of rows. Each row is represented by a list of elements.

delimiters is an optional string of two characters. The first character represent the opening delimiter and the second character represents the closing delimiter.

The delimiters can be any of the following characters:

  • (, ), [, ], {, }, <, >
  • (U+27E6), (U+27E7)
  • |, (U+2016)
  • \
  • (U+2308), (U+2309), (U+230A), (U+230B)
  • (U+231C), (U+231D), (U+231E), (U+231F)
  • (U+23B0), (U+23B1).

In addition, the character . can be used to indicate no delimiter.

Some commom combinations may be represented using some standard LaTeX environments:

Delimiters LaTeX Environment Example
() pmatrix $$ \begin{pmatrix} a & b \ c & d \end{pmatrix} $$
[] bmatrix $$ \begin{bmatrix} a & b \ c & d \end{bmatrix} $$
{} Bmatrix $$ \begin{Bmatrix} a & b \ c & d \end{Bmatrix} $$
` `
‖‖ Vmatrix $$ \begin{Vmatrix} a & b \ c & d \end{Vmatrix} $$
{. dcases $$ \begin{dcases} a & b \ c & d \end{dcases} $$
.} rcases $$ \begin{rcases} a & b \ c & d \end{rcases} $$

columns_format is an optional string indicating the format of each column. A character = indicates a centered column, < indicates a left-aligned column, and > indicates a right-aligned column.

A character of | indicate a solid line between two columns and : indicate a dashed lines between two columns.

Matrix Properties

matrix

Returns the shape of a matrix, a tuple of the lengths of the matrix along each of its axis.

A list (or vector) has a single axis. A matrix has two axes. A tensor has more than two axes.

For a scalar, Shape returns an empty tuple.

["Shape", 5]
// ➔ ["Tuple"]

["Shape", ["List", 5, 2, 10, 18]]
// ➔ ["Tuple", 4]

["Shape", ["List", ["List", 5, 2, 10, 18], ["List", 1, 2, 3]]]
// ➔ ["Tuple", 2, 4]

Note: The shape of a matrix is also sometimes called "dimensions". However, this terminology is ambiguous because the word "dimension" is also used to refer to the length of a matrix along a specific axis.

matrix

Returns the number of axes of a matrix.

A scalar (a number, for example) has rank 0.

A vector or list has rank 1.

A matrix has rank 2, a tensor has rank 3, etc.

The rank is the length of the shape of the tensor.

["Rank", 5]
// ➔ 0

["Rank", ["List", 5, 2, 10, 18]]
// ➔ 1

["Rank", ["List", ["List", 5, 2, 10], ["List", 1, 2, 3]]]
// ➔ 2

Accessing the content of Tensors

matrix, index-1, index-2, ...

Returns the element of the matrix at the specified indexes.

index-1, ... is a sequence of integers, one for each axis of the matrix.

Indexes start at 1. Negative indexes count elements from the end. A negative index is equivalent to adding the length of the axis to the index. So -1 is the last element of the axis, -2 is the second to last element, etc.

["At", ["List", 5, 2, 10, 18], 3]
// ➔ 10

["At", ["List", ["List", 5, 2, 10, 18], ["List", 1, 2, 3]], 2, 3]
// ➔ 3

["At", ["List", ["List", 5, 2, 10, 18], ["List", 1, 2, 3]], 2, -1]
// ➔ 3

In a list (or vector), there is only one axis, so there is only one index.

In a matrix, the first index is the row, the second is the column.

In LaTeX, accessing the element of a matrix is done with a subscript or square brackets following a matrix.

Transforming Matrixes

matrix

Returns a list of all the elements of the matrix, recursively, in row-major order.

This function can also be applied to any collection.

Only elements with the same head as the collection are flattened. Matrixes have a head of List, so only other List elements are flattened.

["Flatten", ["List", ["List", 5, 2, 10, 18], ["List", 1, 2, 3]]]
// ➔ ["List", 5, 2, 10, 18, 1, 2, 3]

Flatten is similar to the APL , Ravel operator or numpy.ravel Numpy.

matrix, shape

Returns a matrix with the specified shape.

matrix can be a list, a matrix, a tensor or a collection.

shape is a tuple of integers, one for each axis of the result matrix.

Reshape can be used to convert a list or collection into a matrix.

["Reshape", ["Range", 9], ["Tuple", 3, 3]]
// ➔ ["List", ["List", 1, 2, 3], ["List", 4, 5, 6], ["List", 7, 8, 9]]

This is similar to the APL Reshape operator or numpy.reshape Numpy.

The result may have fewer or more elements than the original tensor.

When reshaping, the elements are taken from the original tensor in row-major order, that is the order of elements as returned by Flatten.

If the result has fewer elements, the elements are dropped from the end of the element list. If the result has more elements, the lists of elements is filled cyclically.

This is the same behavior as APL, but other environment may behave differently. For example, by default Mathematica ArrayReshape will fill the missing elements with zeros.

matrix

Returns the transpose of the matrix.

["Transpose", ["List", ["List", 1, 2, 3], ["List", 4, 5, 6]]]
// ➔ ["List", ["List", 1, 4], ["List", 2, 5], ["List", 3, 6]]

tensor, axis-1, axis-2

Swap the two specified axes of the tensor. Note that axis indexes start at 1.

matrix

Returns the conjugate transpose of the matrix, that is the transpose of the matrix with all its (complex) elements conjugated. Also known as the Hermitian transpose.

["ConjugateTranspose", ["List", ["List", 1, 2, 3], ["List", 4, 5, 6]]]
// ➔ ["List", ["List", 1, 4], ["List", 2, 5], ["List", 3, 6]]

matrix, axis-1, axis-2

Swap the two specified axes of the matrix. Note that axis indexes start at 1. In addition, all the (complex) elements of the tensor are conjugated.

matrix

Returns the inverse of the matrix.

["Inverse", ["List", ["List", 1, 2], ["List", 3, 4]]]
// ➔ ["List", ["List", -2, 1], ["List", 1.5, -0.5]]

matrix

Returns the Moore-Penrose pseudoinverse of the matrix.

["PseudoInverse", ["List", ["List", 1, 2], ["List", 3, 4]]]
// ➔ ["List", ["List", -2, 1], ["List", 1.5, -0.5]]

matrix

Returns the diagonal of the matrix, that is the list of all the elements on the diagonal of the matrix.

["Diagonal", ["List", ["List", 1, 2], ["List", 3, 4]]]
// ➔ ["List", 1, 4]

Calculating with Matrixes

matrix

Returns the determinant of the matrix.

["Determinant", ["List", ["List", 1, 2], ["List", 3, 4]]]
// ➔ -2

matrix

Returns the adjugate matrix of the input matrix, that is the inverse of the cofactor matrix.

The cofactor matrix is a matrix of the determinants of the minors of the matrix multiplied by $$ (-1)^{i+j} $$. That is, for each element of the matrix, the cofactor is the determinant of the matrix without the row and column of the element.

["AdjugateMatrix", ["List", ["List", 1, 2], ["List", 3, 4]]]
// ➔ ["List", ["List", 4, -2], ["List", -3, 1]]

matrix

Returns the trace of the matrix, the sum of the elements on the diagonal of the matrix. The trace is only defined for square matrices. The trace is also the sum of the eigenvalues of the matrix.

["Trace", ["List", ["List", 1, 2], ["List", 3, 4]]]
// ➔ 5

title: Sets slug: /compute-engine/reference/sets/

A **set** is a collection of distinct elements.

The Compute Engine standard library includes definitions for common numeric sets. Checking if a value belongs to a set is done using the Element expression, or the (\in) command in LaTeX.

ce.box(['Element', 3.14, 'NegativeIntegers']).evaluate().print();
// ➔ False

ce.parse("42 \\in \\Z").evaluate().print();
// ➔ True

Checking if an element is in a set is equivalent to checking if the type of the element matches the type associated with the set.

const x = ce.box(42);

x.type;
// ➔ "finite_integer"

x.type.matches("integer");
// ➔ true

x.isInteger;
// ➔ true

ce.box(['Element', x, 'Integers']).evaluate().print();
// ➔ True

ce.parse("42 \\in \\Z").evaluate().print();
// ➔ True

Constants

Symbol Notation   Definition
EmptySet \varnothing or \emptyset $$ \varnothing $$ or $$ \emptyset $$ A set that has no elements
Numbers \mathrm{Numbers} $$ \mathrm{Numbers} $$ Any number, real, imaginary, or complex
ComplexNumbers \C $$ \C $$ Real or imaginary numbers
ExtendedComplexNumbers \overline\C $$ \overline\C $$ Real or imaginary numbers, including + , and ~
ImaginaryNumbers \imaginaryI\R $$ \imaginaryI\R $$ Complex numbers with a non-zero imaginary part and no real part
RealNumbers \R $$ \R $$ Numbers that form the unique Dedekind-complete ordered field $$ \left( \mathbb{R} ; + ; \cdot ; \lt \right) $$, up to an isomorphism (does not include ± )
ExtendedRealNumbers \overline\R $$ \overline\R $$ Real numbers extended to include ±
Integers \Z $$ \Z$$ Whole numbers and their additive inverse { 3 , 2 , 1 , 0 , 1 , 2 , 3 }
ExtendedIntegers \overline\Z $$ \overline\Z$$ Integers extended to include ±
RationalNumbers \Q $$ \Q $$ Numbers which can be expressed as the quotient $$ \nicefrac{p}{q}$$ of two integers p , q Z .
ExtendedRationalNumbers \overline\Q $$ \overline\Q$$ Rational numbers extended to include ±
NegativeNumbers \R_{<0} $$ \R_{<0} $$ Real numbers $$ \lt 0 $$
NonPositiveNumbers \R_{\leq0} $$ \R_{\leq0} $$ Real numbers $$ \leq 0 $$
NonNegativeNumbers \R_{\geq0} $$ \R_{\geq0} $$ Real numbers $$ \geq 0 $$
PositiveNumbers \R_{>0} $$ \R_{>0} $$ Real numbers $$ \gt 0$$
NegativeIntegers \Z_{<0} $$ \Z_{<0} $$ Integers $$ \lt 0$$, { 3 , 2 , 1 }
NonPositiveIntegers \Z_{\le0} $$ \Z_{\le0} $$ Integers $$ \leq 0 , \lbrace \ldots -3, -2, -1, 0\rbrace$$
NonNegativeIntegers \N $$ \N $$ Integers $$ \geq 0 , \lbrace 0, 1, 2, 3\ldots\rbrace$$
PositiveIntegers \N^* $$ \N^* $$ Integers $$ \gt 0 , \lbrace 1, 2, 3\ldots\rbrace$$

Functions

New sets can be defined using one of the following operators.

Function Operation
CartesianProduct $$ \operatorname{A} \times \operatorname{B} $$ A.k.a the product set, the set direct product or cross product. Q173740
Complement $$ \operatorname{A}^\complement $$ The set of elements that are not in $$ \operatorname{A} . I f \operatorname{A}$$ is a numeric type, the universe is assumed to be the set of all numbers. Q242767
Intersection $$ \operatorname{A} \cap \operatorname{B} $$ The set of elements that are in
The following macros are not allowed: operatorname

$$\operatorname{A}$$
and in
The following macros are not allowed: operatorname

$$\operatorname{B}$$
Q185837
Union $$ \operatorname{A} \cup \operatorname{B} $$ The set of elements that are in
The following macros are not allowed: operatorname

$$\operatorname{A}$$
or in
The following macros are not allowed: operatorname

$$\operatorname{B}$$
Q173740
Set { 1 , 2 , 3 } Set builder notation
SetMinus $$ \operatorname{A} \setminus \operatorname{B} $$ Q18192442
SymmetricDifference $$ \operatorname{A} \triangle \operatorname{B} $$ Disjunctive union = $$ (\operatorname{A} \setminus \operatorname{B}) \cup (\operatorname{B} \setminus \operatorname{A})$$ Q1147242

Relations

To check the membership of an element in a set or the relationship between two sets using the following operators.

Function Notation  
Element $$ x \in \operatorname{A} $$ x \in \operatorname{A}
NotElement $$ x \not\in \operatorname{A} $$ x \not\in \operatorname{A}
NotSubset $$ \operatorname{A} \nsubset \operatorname{B} $$ \operatorname{A} \nsubset \operatorname{B}
NotSuperset $$ \operatorname{A} \nsupset \operatorname{B} $$ \operatorname{A} \nsupset \operatorname{B}
Subset $$ \operatorname{A} \subset \operatorname{B} $$
$$ \operatorname{A} \subsetneq \operatorname{B} $$
$$ \operatorname{A} \varsubsetneqq \operatorname{B} $$
\operatorname{A} \subset \operatorname{B}
\operatorname{A} \subsetneq \operatorname{B}
\operatorname{A} \varsubsetneqq \operatorname{B}
SubsetEqual $$ \operatorname{A} \subseteq \operatorname{B} $$ \operatorname{A} \subseteq \operatorname{B}
Superset $$ \operatorname{A} \supset \operatorname{B} $$
$$ \operatorname{A} \supsetneq \operatorname{B} $$
$$ \operatorname{A} \varsupsetneq \operatorname{B} $$
\operatorname{A} \supset \operatorname{B}
\operatorname{A} \supsetneq \operatorname{B}
\operatorname{A} \varsupsetneq \operatorname{B}
SupersetEqual $$ \operatorname{A} \supseteq \operatorname{B} $$ \operatorname{A} \supseteq \operatorname{B}
--- title: Custom Functions and Symbols slug: /compute-engine/guides/augmenting/ ---

The MathJSON Standard Library is a collection of definitions for symbols such as Pi, Add, Sin, Power, List, etc...

In this guide we discuss how to augment the MathJSON Standard Library with your own symbols.

You may also be interested in **augmenting the LaTeX dictionary** which defines how LaTeX is parsed from and serialized to MathJSON.

This is useful if you want to add support for custom LaTeX macros that you'd like to parse to MathJSON.

Introduction

When an symbol such as Pi or Sin is encountered in an expression, the Compute Engine will look up its definition in the set of known symbols, including the Standard Library.

Automatic Declaration

If a matching definition is found, it will be bound to the symbol and used later to evaluate the expression.

If no definition is found, an automatic declaration will be made of the symbol with a type unknown or a more specific type if the context allows it.

Learn more about **types**.

To provide a more explicit definition for the symbol, you can define it using a LaTeX expression, or an explicit declaration using the ce.declare() method.

Declarations are Scoped

The declaration of a symbol is done within a lexical scope. A scope is a hierarchical collection of definitions.

Read more about **scopes**

Definitions Using LaTeX

The simplest way to define a new symbol is to use LaTeX.

For example, to define a new symbol m with a value of 42 , use the following LaTeX expression:

ce.parse("m := 42").evaluate();
ce.parse("m").value.print();
// ➔ 42

Note: the assignment expression must be evaluated to take effect.

To define a new function f that multiplies its argument by 2 , use the following LaTeX expression:

ce.parse("f(x) := 2x").evaluate();
ce.parse("f(3)").evaluate().print();
// ➔ 6

The \mapsto operator is an alternative syntax to define a function:

ce.parse("f := x \\mapsto 2x").evaluate();
ce.parse("f(3)").evaluate().print();
// ➔ 6

To define multiletter symbols, use the \operatorname{} command:

ce.parse('\\operatorname{double}(x) := 2x').evaluate().print();
ce.parse('\\operatorname{double}(3)').evaluate().print();
// ➔ 6

Note: you can also use the \mathrm{} or \mathit{} commands to wrap multiletter symbols.

The LaTeX identifiers are mapped to MathJSON symbols. For example, the LaTeX \operatorname{double} is mapped to the MathJSON symbol double.

console.info(ce.parse('\\operatorname{double}(3)').json);
// ➔ ["double", 3]

Explicit Declarations

To have more control over the definition of a symbol use the ce.declare() method.

When declaring a symbol, you can specify the type of the symbol, its value and other properties.

// Declaring a symbol "m"
ce.declare("m",  "integer");

// Declaring a function "f"
ce.declare("f", {
  signature: "number -> number",
  evaluate: ce.parse("x \\mapsto 2x"),
});

Declaring a Symbol

To declare a symbol use the ce.declare() method with the name of the symmbol as the first argument and a type as the second argument.

ce.declare("n", "integer");

The type specifies the valid values of the symbol. For example, boolean, integer, rational, function, string etc.. Learn more about types.

Alternatively, you can provide an object literal with the additional properties value, type, isConstant, and more.

ce.declare("m", {
  type: "integer",
  value: 42,
});

If you do not provide a type property for a symbol, the type will be inferred from the value of the symbol. If no type and no value are provided, the type of the symbol will be unknown.

As a shorthand, a symbol can be declared by assigning it a value using ce.assign():

ce.assign("m", 42);

If the symbol was not previously defined, this is equivalent to:

ce.declare("m", { value: 42 });

Alternatively, you can set the value of a symbol using the value property:

ce.box("m").value = 42;

To prevent the value of a symbol from being changed, set the constant property to true:

ce.declare("m_e", {
  value: 9.1e-31,
  constant: true,
});

Declaring a Function

To declare a function, associate an evaluate handler, which is the body of the function, with a symbol.

ce.declare("double", { 
  evaluate: ce.parse("x \\mapsto 2x") 
});

:::caution[Caution] The first argument of declare() is a MathJSON symbol, not a LaTeX command. For example, use double instead of \operatorname{double}. :::

The evaluate handler can be either a MathJSON expression as above or a JavaScript function.

ce.declare("double", { evaluate: ([x]) => x.mul(2) });

The signature of the evaluate handler is (args[], options), where:

  • args: an array of the arguments that have been applied to the function. Each argument is a boxed expression. The array may be empty if there are no arguments.
  • options: an object literal which includes an engine property that is the Compute Engine instance that is evaluating the expression and a numericApproximation property that is true if the result should be a numeric approximation.

Since args is an array, you can use destructuring to get the arguments:

ce.declare("double", { evaluate: (args) => args[0].mul(2) });

// or
ce.declare("double", { evaluate: ([x]) => x.mul(2) });

In addition to the evaluate handler the function definition can include a signature type that describes the arguments and return value of the function.

ce.declare("double", {
  signature: "number -> number",
  evaluate: ([x]) => x.mul(2),
});

See FunctionDefinition for more details on the other handlers and properties that can be provided when defining a function.

To define a function without specifying a body for it, specify the signature of the function as the second argument of ce.declare() or use the "function" type.

ce.declare("double", "function");

Functions that do not have an evaluate handler or a function literal as a value remain unchanged when evaluated.

You can set the body of the function later using ce.assign():

When using ce.assign() to define a function, the value can be a JavaScript function, a MathJSON expression or a LaTeX expression.

ce.assign("double", ([x]) => x.mul(2));

ce.assign("double", ["Function", ["Multiply", "x", 2], "x"]);

ce.assign("double",ce.parse("x \\mapsto 2x"));
Learn more about the standard operator to manipulate **functions**.

Overloading Functions

Overloading is the ability to define multiple functions with the same name.

To overload a function, use the ce.declare() methods.

For example, to overload the Sqrt function to return NaN for non-real numbers, use the following code:

const originalSqrtDefinition = ce.box('Sqrt').operatorDefinition!;
ce.declare('Sqrt', {
  ...originalSqrtDefinition,
  evaluate: (x, options) => {
    const y = originalSqrtDefinition.evaluate!(x, options);
    return y?.isReal ? y : ce.NaN;
  },
});

In general, re-declaring a function in the same scope is not allowed and will throw an error. However, the standard functions are in a system scope so a new declaration in the global scope or a child scope will override the original declaration.

Defining Multiple Functions and Symbols

To define multiple functions and symbols, use the ce.declare() method with a dictionary of definitions.

:::info[Note] The keys to ce.declare() (m, f, etc...) are MathJSON symbols, not LaTeX commands. For example, if you have a symbol α, use alpha, not \alpha :::

ce.declare({
  m: { type: "number", value: 5 },
  f: { type: "function" },
  g: { type: "function" },
  Smallfrac: {
    signature: "(number, number) -> number",
    evaluate: ([x,y]) => x.div(y),
  },
});

To assign multiple functions and symbols, use the ce.assign() method with a dictionary of values.

ce.assign({
  "m": 10,
  "f": ce.parse("x \\mapsto x^2 + x + 41"),
  "g": ce.parse("t \\mapsto t^3 + t^2 + 17"),
});

title: Symbolic Computing slug: /compute-engine/guides/symbolic-computing/ date: Last Modified

The Compute Engine essentially performs computation by applying rewriting rules to a MathJSON expression.

There are three common transformations that can be applied to an expression:

Transformation
expr.simplify() Eliminate constants and common sub-expressions. Use available assumptions to determine which rules are applicable. Limit calculations to exact results.
expr.evaluate() Calculate the exact value of an expression. Replace symbols with their value.
expr.N() Calculate a numeric approximation of an expression using floating point numbers.

A key difference between expr.evaluate() and expr.N() is that the former will use the exact value of symbols, while the latter will use their numeric approximation. An exact value is a rational number, an integer, the square root of an integer and some constants such as \(\pi\) or \(e\). A numeric approximation is a floating point number.

  expr.simplify() expr.evaluate() expr.N()
Use assumptions on symbols
Exact calculations
Floating-point approximations

Read more about Simplify

Read more about Evaluate

Read more about Numerical Evaluation

Other operations can be performed on an expression: comparing it to a pattern, replacing part of it, and applying conditional rewrite rules.

const expr = ce.parse('3x^2 + 2x^2 + x + 5');
console.log(expr, '=', expr.simplify());

Comparing Expressions

There are two useful ways to compare symbolic expressions:

  • structural equality
  • mathematical equality

Structural Equality: isSame()

Structural equality (or syntactic equality) considers the symbolic structure used to represent an expression.

The symbolic structure of an expression is the tree of symbols and functions that make up the expression.

For example, the symbolic structure of \(2 + 1\) is a sum of two terms, the first term is the number 2 and the second term is the number 1.

The symbolic structure of \(3\) is a number 3.

The symbolic structure of \(2 + 1\) and \(3\) are different, even though they represent the same mathematical object.

The lhs.isSame(rhs) function returns true if lhs and rhs are structurally exactly identical, that is each sub-expression is recursively identical in lhs and rhs.

  • \(1 + 1 \) and \( 2 \) are not structurally equal, one is a sum of two integers, the other is an integer
  • \( (x + 1)^2 \) and \( x^2 + 2x + 1 \) are not structural equal, one is a power of a sum, the other a sum of terms.
const a = ce.parse('2 + 1');
const b = ce.parse('3');
console.log('isSame?', a.isSame(b));

By default, when parsing or boxing an expression, they are put in canonical form. For example, fractions are automatically reduced to their simplest form, and arguments are sorted in a standard way.

The expressions \( \frac{1}{10} \) and \( \frac{2}{20} \) are structurally equal because they get put into a canonical form when parsed, in which the fractions are reduced.

Similarly, \( x^2 - 3x + 4 \) and \( 4 - 3x + x^2 \) are structurally equal (isSame returns true) because the arguments of the sum are sorted in a standard way.

To compare two expressions without canonicalizing them, parse or box them with the canonical option set to false.

const a = ce.parse('\\frac{1}{10}');
const b = ce.parse('\\frac{2}{20}');
console.log('Canonical isSame?', a.isSame(b));

const aPrime = ce.parse('\\frac{1}{10}', {canonical: false});
const bPrime = ce.parse('\\frac{2}{20}', {canonical: false});
console.log('Non-canonical isSame?', aPrime.isSame(bPrime));

In some cases you may want to compare two expressions with a weak form of canonicalization, for example to ignore the order of the arguments of a sum.

You can achieve this by comparing the expressions in their canonical order:

ce.box(["CanonicalForm", ["Add", 1, "x"], "Order"]).isSame(
  ["CanonicalForm", ["Add", "x", 1], "Order"]
)

Mathematical Equality: isEqual()

It turns out that comparing two arbitrary mathematical expressions is a complex problem.

In fact, Richardson's Theorem proves that it is impossible to determine if two symbolic expressions are identical in general.

However, there are many cases where it is possible to make a comparison between two expressions to check if they represent the same mathematical object.

The lhs.isEqual(rhs) function return true if lhs and rhs represent the same mathematical object.

If lhs and rhs are numeric expressions, they are evaluated before being compared. They are considered equal if the absolute value of the difference between them is less than ce.tolerance.

The expressions \( x^2 - 3x + 4 \) and \( 4 - 3x + x^2 \) will be considered equal (isEqual returns true) because the difference between them is zero, i.e. \( (x^2 - 3x + 4) - (4 - 3x + x^2) \) is zero once the expression has been simplified.

Note that unlike expr.isSame(), expr.isEqual() can return true, false or undefined. The latter value indicates that there is not enough information to determine if the two expressions are mathematically equal.

const a = ce.parse('1 + 2');
const b = ce.parse('3');
console.log('isEqual?', a.isEqual(b));

Other Comparisons

lhs === rhs If true, same box expression instances
lhs.isSame(rhs) Structural equality
lhs.isEqual(rhs) Mathematical equality
lhs.match(rhs) !== null Pattern match
lhs.is(rhs) Synonym for lhs.isSame(rhs), but the argument of is() can be a boolean or number.
ce.box(["Equal", lhs, rhs]).evaluate() Synonym for lhs.isEqual(rhs)
ce.box(["Same", lhs, rhs]).evaluate() Synonym for lhs.isSame(rhs)

Replacing a Symbol in an Expresssion

To replace a symbol in an expression use the subs() function.

The argument of the subs() function is an object literal. Each key/value pairs is a symbol and the value to be substituted with. The value can be either a number or a boxed expression.


let expr = ce.parse('\\sqrt{\\frac{1}{x+1}}');
console.log(expr.json);

expr = expr.subs({x: 3});

console.log("Substitute x -> 3\n", expr.json);
console.log("Numerical Evaluation:", expr.N());

Other Symbolic Manipulation

There are a number of operations that can be performed on an expression:

  • creating an expression from a raw MathJSON expression or from a LaTeX string
  • simplifying an expression
  • evaluating an expression
  • applying a substitution to an expression
  • applying conditional rewrite rules to an expression
  • checking if an expression matches a pattern
  • checking if an expression is a number, a symbol, a function, etc...
  • checking if an expression is zero, positive, negative, etc...
  • checking if an expression is an integer, a rational, etc...
  • and more...

We've introduced some of these operations in this guide, but there are many more that are available.

Read more about Expressions, their properties and methods

To check if an expression matches a pattern, apply a substitution to some elements in an expression or apply conditional rewriting rules to an expression.

Read more about Patterns and Rules for these operations

title: Functions slug: /compute-engine/reference/functions/

Functions are first-class values in the Compute Engine. This means that functions can be passed as arguments to other functions, returned from functions, and assigned to variables.

The standard library can be extended with your own functions.

Read more about adding new definitions to the Compute Engine.
Term Definition
Function Expression An expression representing a function application where a function (or operator) is evaluated with arguments (the arguments are applied to the function). For example ["Add", 1, 2] applies the arguments 1 and 2 to the operator Add.
Function Signature A type describing the function's inputs and outputs, e.g. (real, integer) -> boolean
Function Literal A first-class function value, defined using a construct like ["Function", body, params...], which may or may not capture variables from its lexical scope.
Shorthand Function Literal A compact way to write a function literal using placeholders (e.g. _ or _2) instead of explicitly listing parameters, e.g. ["Add", "_", 1] desugared to ["Function", ["Add", "_", 1], "_"].
Closure A function literal that captures one or more free variables from its defining lexical scope, preserving their values at the time of definition. The capture happens by simply referencing the variables in the function body.
Anonymous Function A function that is not bound to a symbol, often used as an argument to other functions.

Function Literals

A function literal is a first-class function value, defined using a ["Function"] expression. It can be passed as an argument to other functions, returned from a function, or assigned to a variable.

The ["Function"] expression takes a body and a list of parameters.

["Sum", ["Function", ["Multiply", "x", 2], "x"]]

To specify a function literal with LaTeX use the \mapsto command:

["Function", ["Multiply", "x", 2], "x"]
["Function", ["Add", ["Multiply", "x", 2], "y"], "x", "y"]

The examples in this section define functions as a simple expression, but function literals can include more complex control structures, including blocks, local variables, loops and conditionals.

For example, here's a simple "clamp" function, using a ["Block"] expression.

["Function",
  ["Block",
    ["Assign", "x", ["Max", "x", "min"]],
    ["Min", "x", "max"]
  ],
  "x", "min", "max"
]

Learn more about control structures.

Shorthand Function Literals

A shorthand function literal is a compact way to write a function literal without explicitly listing parameters.

A shorthand function literal can use either wildcards (_, _2, etc...) or unknowns (symbols that have no value) as implicit parameters.

The shorthand function literal is desugared to a function literal.

For example the shorthand function literal ["Multiply", "_", 2] is desugared to ["Function", ["Multiply", "_", 2], "_"].

To use wildcards in LaTeX, they must be wrapped with an \operatorname command except for \_.

["Add", "_", "_2"]

The shorthand function literal ["Multiply", "x", 2] which uses the unknown x is equivalent to the function literal ["Function", ["Multiply", "x", 2], "x"]. When using this form, make sure that the symbol x is not defined in the current scope.

A symbol which is the name of an operator (for example Sin) is also a valid function literal shorthand.

This expression will apply the Sin function to the elements of xs.

["Map", "xs", "Sin"]

It is equivalent to ["Map", "xs", ["Sin", "_"]] which is desugared to ["Map", "xs", ["Function", ["Sin", "_"], "_"]].

Anonymous Functions

A function that is not bound to a symbol is called an anonymous function.

Anonymous functions are frequently used as arguments to other functions.

In the example below, the second argument of the Map function is an anonymous function expressed as a function literal that multiplies its argument by 2.

["Map", "xs", ["Function", ["Multiply", "x", 2], "x"]]

The same function can be expressed using a shorthand function literal, which uses _ as a wildcard for the parameter.

["Map", "xs", ["Multiply", "_", 2]]

Evaluating a Function Literal

To apply a function literal to some arguments use an ["Apply"] expression.

["Apply", ["Function", ["Add", 2, "x"], "x"], 11]
// ➔ 22

["Apply", ["Add", 2, "_"], 4]
// ➔ 6

["Apply", "Power", 2, 3]
// ➔ 8

The first argument of Apply is a function literal. The rest of the arguments are the arguments that will be applied to the function literal.

Closures

A closure is a function literal that captures one or more free variables from its defining lexical scope, preserving their values at the time of definition. The capture happens by simply referencing the variables in the function body. For example, the following function literal captures the variable a from its lexical scope:

["Function", ["Add", "a", "_"], "_"]

This function literal captures the value of a at the time of definition, and when the function is applied, it will use that value of a in the computation.

["Block"
  ["Assign", "f",
    ["Block",
      ["Declare", "a", "integer"],
      ["Assign", "a", 10],
      ["Function", ["Add", "a", "_"], "_"]
    ]
  ]
  ["Declare", "a", "integer"],
  ["Assign", "a", 100]
  ["f", 1]
]
// ➔ 1 + 10

Note that the value of a is 3 when the function is defined, and it is 10 when the function is applied. The function will always use the value of a that was in scope when the function was defined, not the value of a at the time the function is applied. In fact, the out a is a different variable which is unrelated to the a in the scope of the function, but with the same name.

Operating on Functions

body

body, arg-1, arg-2, ...

Create a function literal which can be used as an anonymous function.

The arg-n arguments are symbols which are the bound variables (parameters) of the function literal.

:::info[Note] The Function operator has the lazy flag set to true, which means that neither the body nor the parameters are evaluated until the function is applied to some arguments. :::

The body is an expression that is evaluated when the function is applied to some arguments.

To apply some arguments to a function expression, use ["Apply"].

["Function", ["Multiply", "x", 2], "x"]
["Function", ["Add", ["Multiply", "x", 2], "y"], "x", "y"]

symbol, fn

Assign the function literal fn to the symbol symbol.

Assign is not a pure function, as it changes the state of the Compute Engine.

The fn is a function literal, which can be created using the ["Function"] expression or the shorthand function literal.

["Assign", "double", ["Function", ["Multiply", "x", 2], "x"]]

function, expr-1, ...expr-n

Apply a list of arguments to a function. The function is a function literal, or a symbol whose value is a function literal.

The expr-n arguments are the arguments of the function literal.

["Apply", ["Multiply", "_", "_"], 3]
// ➔ 9

["Apply", ["Function", ["Multiply", "x", "x"], "x"], 3]
// ➔ 9

With LaTeX, the \lhd and \rhd commands can be used to apply a function to a single argument on the left or right respectively.

["Apply", "f", ["Apply", "g", "x"]]

title: Parsing and Serializing LaTeX sidebar_label: LaTeX Syntax slug: /compute-engine/guides/latex-syntax/ date: Last Modified

The Compute Engine manipulates MathJSON expressions. It can also convert LaTeX strings to MathJSON expressions (**parsing**) and output MathJSON expressions as LaTeX string (**serializing**)

:::info[Note] In this documentation, functions such as ce.box() and ce.parse() require a ComputeEngine instance which is denoted by a ce. prefix.
Functions that apply to a boxed expression, such as expr.simplify() are denoted with a expr. prefix. :::

To create a new instance of the Compute Engine, use the new ComputeEngine() constructor.

const ce = new ComputeEngine();

To input math using an interactive mathfield, use the Mathfield library.

A <math-field> DOM element works like a <textarea> in HTML, but for math. It provides its content as a LaTeX string, ready to be used with the Compute Engine.

Read more about the **mathfield element**

All the mathfields on the page share a Compute Engine instance, which is available as MathfieldElement.computeEngine.

const ce = MathfieldElement.computeEngine;

You can associate a customized compute engine with the mathfields in the document:

const ce = new ComputeEngine();
MathfieldElement.computeEngine = ce;
console.log(mfe.expression.json);

To parse a LaTeX string as a MathJSON expression, call the ce.parse() function.

console.log(ce.parse("5x + 1").json);
// ➔  ["Add", ["Multiply", 5, "x"], 1]

By default, ce.parse() return a canonical expression. To get a non-canonical expression instead, use the {canonical: false} option: The non-canonical form is closer to the literal LaTeX input.

ce.parse("\\frac{7}{-4}").json;
// ➔  ["Rational", -7, 4]

ce.parse("\\frac{7}{-4}", { canonical: false }).json;
// ➔  ["Divide", 7, -4]

The Compute Engine Natural Parser

Unlike a programming language, mathematical notation is surprisingly ambiguous and full of idiosyncrasies. Mathematicians frequently invent new notations, or have their own preferences to represent even common concepts.

The Compute Engine Natural Parser interprets expressions using the notation you are already familiar with. Write as you would on a blackboard, and get back a semantic representation as an expression ready to be processed.

LaTeX MathJSON
$$ \sin 3t + \cos 2t $$
\sin 3t + \cos 2t
["Add", ["Sin", ["Multiply", 3, "t"]], ["Cos", ["Multiply", 2, "t"]]]
$$ \int \frac{dx}{x} $$
\int \frac{dx}{x}
["Integrate", ["Divide", 1, "x"], "x"]
$$ 123.4(567) $$
123.4(567)
123.4(567)
$$ 123.4\overline{567} $$
123.4\overline{567}
123.4(567)
$$ \vert a+\vert b\vert+c\vert $$
|a+|b|+c|
["Abs", ["Add", "a", ["Abs", "b"], "c"]]
$$ \vert\vert a\vert\vert+\vert b\vert $$
||a||+|b|
["Add", ["Norm", "a"], ["Abs", "b"]]

The Compute Engine Natural Parser will apply maximum effort to parse the input string as LaTeX, even if it includes errors. If errors are encountered, the resulting expression will have its expr.isValid property set to false. An ["Error"] expression will be produced where a problem was encountered. To get the list of all the errors in an expression, use expr.errors which will return an array of ["Error"] expressions.

Read more about the **errors** that can be returned.

Serializing to LaTeX

To serialize an expression to a LaTeX string, read the expr.latex property.

console.log(ce.box(["Add", ["Power", "x", 3], 2]).latex);
// ➔  "x^3 + 2"

Customizing Parsing

The LaTeX parsing can be customized by providing a ParseLatexOptions object as the second argument to the ce.parse() function.

Customizing the Parsing of Numbers

See the Number Formatting section for details on how to customize the parsing of numbers. Most of the same options are available for parsing as for serialization.

Other Parsing Options

Key Description
skipSpace If true, ignore space characters in a math zone. Default is true.
parseNumbers 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"
preserveLatex If true, the expression will be decorated with the LaTeX fragments corresponding to each element 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
ce.parse('x + 0.5', { parseNumbers: "rational" }).print();
// ➔ x + 1/2

getSymbolType

This handler is invoked when the parser encounters a symbol that has not yet been declared.

The symbol argument is a valid symbol.

The handler should return the type of the symbol.

console.info(ce.parse("f(x)", {
  getSymbolType: (symbol) => {
    if (symbol === "f") {
      return "function";
    }
    return "unknown";
  },
}).json);

parseUnexpectedToken

This handler is invoked when the parser encounters a token that it does not know how to handle.

The lhs argument is the previous 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.

console.info(ce.parse("3\\frac{1}{\\foo}", {
  parseUnexpectedToken: (lhs, parser) => {
    if (parser.peek === '\\foo') {
      parser.nextToken();
      return "foo";
    }
    return null;
  },
}).json);

Customizing Serialization

While expr.latex provides a simple, default serialization to LaTeX, it may not always be the most suitable for your needs.

To customize the serialization to LaTeX, use the expr.toLatex() method.

The argument of the expr.toLatex() method is a SerializeLatexOptions object that can be used to customize the serialization. The keys are explained in the sections below.

Number Formatting

Key Description
fractionalDigits The number of decimal places to use when formatting numbers. Use "max" to include all available digits and "auto" to use the same precision as for evaluation. Default is "auto".
notation The notation to use for numbers. Use "auto", "scientific", or "engineering". Default is "auto".
avoidExponentsInRange A tuple of two values representing a range of exponents. If the exponent for the number is within this range, a decimal notation is used. Otherwise, the number is displayed with an exponent. Default is [-6, 20].
digitGroupSeparator The LaTeX string used to separate group of digits, for example thousands. Default is "\,". To turn off group separators, set to "". If a string tuple is provide, the first string is used to group digits in the whole part and the second string to group digits in the fractional part.
digitGroupSize The number of digits in a group. If set to "lakh" the digits are in groups of 2, except for the last group which has 3 digits. If a tupe is provided, the first element is used for the whole part and the second element for the fractional part. Default is 3.
exponentProduct A LaTeX string inserted before an exponent, if necessary. Default is "\cdot".
beginExponentMarker A LaTeX string used as template to format an exponent. Default value is "10^{".
endExponentMarker A LaTeX string used as template to format an exponent. Default value is "}".
truncationMarker A LaTeX string used to indicate that a number has more precision than what is displayed. Default is "\ldots".
repeatingDecimal The decoration around repeating digits. Valid values are "auto", "vinculum", "dots", "parentheses", "arc" and "none". Default is "auto".
console.log(ce.parse("\\pi").N().toLatex({ 
    fractionalDigits: 6,
}));
console.log(ce.box(700).toLatex({
  notation: "scientific",
  avoidExponentsInRange: null,
  exponentProduct: "\\times"
}));
// ➔ "7\times10^{2}"

console.log(ce.box(123456.789).toLatex({
  notation: "scientific",
  avoidExponentsInRange: null,
  exponentProduct: "\\times",
}));
// ➔ "1.234\,567\,89\times10^{5}"

Customizing the Decimal Separator

The world is about evenly split between using a dot or a comma as a decimal marker.

By default, the ComputeEngine is configured to use a dot, i.e. $ 3.1415 $.

To use a comma as a decimal marker, set the decimalSeparator option:

console.log(ce.box(3.141).toLatex({ 
    decimalSeparator: "{,}"
}));

Note that in LaTeX, in order to get the correct spacing around the comma, it must be surrounded by curly brackets.

Special Numbers and Symbols

Key Description
positiveInfinity The LaTeX string used to represent positive infinity. Default is "\infty" $ \infty $.
negativeInfinity The LaTeX string used to represent negative infinity. Default is "-\infty" $ -\infty $.
imaginaryUnit The LaTeX string used to represent the imaginary unit symbol. Default is "\imaginaryI" $ \imaginaryI $
notANumber The LaTeX string to represent the number NaN. Default value is "\operatorname{NaN}" $ \operatorname{NaN} $.
prettify If true, the output will be formatted to be more human-readable. Default is false.
invisibleMultiply A LaTeX string to use as an invisible multiply operator between expressions. Use "\cdot" to use a $ \cdot $. Default is "".
invisiblePlus A LaTeX string to use as an invisible plus operator between expressions, for example with mixed numbers. Leave it empty to join the main number and the fraction. Use "+" to insert an explicit $ + $ operator between them. Default is "".
multiply A LaTeX string to use as a multiply operator between expressions. Use "\cdot" to use a $ \cdot $. Default is "\times" $ \times $.
missingSymbol A LaTeX string to use when a symbol is missing. Default is "\placeholder{}" $ \placeholder{} $.
console.log(ce.parse("3\\frac{1}{4}").toLatex({ 
    invisiblePlus: "+"
}));

Customizing the Serialization Style

In addition, the keys applyFunctionStyle, groupStyle, powerStyle, rootStyle, fractionStyle, logicStyle and numericSetStyle can be used to customize the serialization of specific types of expressions.

For example, a group can be indicated by simple parentheses, or by a \left...\right command. A fraction can be indicated by a \frac{}{} command or by a {}{}^{-1}.

The Compute Engine includes some built-in defaults, but they can be customized as desired. These style options are functions that take an expression fragment and return a string indicating the desired style.

For example to always represent fractions with a solidus (forward slash) use:

console.log(ce.parse("\\frac{3}{5}").toLatex({
  fractionStyle: () => "quotient"
}));

console.log(ce.parse("\\frac{3}{5}").toLatex({
  fractionStyle: () => "inline-solidus"
}));

The style option handler has two arguments:

  • the expression fragment being styled
  • the depth/level of the expression in the overall expression

For example, to serialize fractions deeper than level 0 as an inline solidus:


console.log(ce.parse("\\frac{a}{b}+\\sqrt{\\frac{c}{d}}").toLatex({
  fractionStyle: (expr, level) =>
     level > 0 ? "inline-solidus" : "quotient"
}));

Function Application

To customize the serialization of function application, use the applyFunctionStyle style option handler.

console.log(ce.parse("\\sin x").toLatex({
  applyFunctionStyle: () => "big"
}));
"paren" \sin(x) sin ( x )
"leftright" \sin\left(x\right) sin ( x )
"big" \sin\bigl(x\bigr) sin ( x )
"none" \sin x sin x

Group

To customize the serialization of groups, use the groupStyle style option handler.

console.log(ce.parse("(a+b)", {canonical: false}).toLatex({
  groupStyle: () => "big"
}));
"paren" x(a+b) x ( a + b )
"leftright" x\left(a+b\right) x ( a + b )
"big" x\bigl(a+b\bigr) x ( a + b )
"none" x a+b $$ x a+b$$

Root

To customize how roots are serialized, use the rootStyle style option handler.

console.log(ce.parse("\\sqrt{2}").toLatex({
  rootStyle: () => "solidus"
}));
"radical"
"quotient"
"solidus"

Fraction

To customize how fractions are serialized, use the fractionStyle style option handler.

console.log(ce.parse("\\frac{3}{5}").toLatex({
  fractionStyle: () => "nice-solidus"
}));
"quotient"
"inline-solidus"
"nice-solidus"
"reciprocal"
"factor"

Logic

To customize how logic expressions are serialized, use the logicStyle style option handler.

console.log(ce.parse("p\\land q").toLatex({
  logicStyle: () => "word"
}));
"word" a \text{ and } b a  and  b
"boolean"
"uppercase-word" p \text{ AND } q $ p \text{ AND } q $
"punctuation"

Power

To customize how powers are serialized, use the powerStyle style option handler.

console.log(ce.parse("x^2").toLatex({
  powerStyle: () => "solidus"
}));
"root"
"solidus"
"quotient"

Numeric Sets

To customize how numeric sets are serialized, use the numericSetStyle style option handler.

console.log(ce.parse("x \\in \\Z").toLatex({
  numericSetStyle: () => "interval"
}));
"compact"
"regular"
"interval"
"set-builder"

Customizing the LaTeX Dictionary

The MathJSON format is independent of any source or target language (LaTeX, MathASCII, Python, etc...) or of any specific interpretation of the symbols used in a MathJSON expression ("Pi", "Sin", etc...).

A LaTeX dictionary defines how a MathJSON expression can be expressed as a LaTeX string (serialization) or constructed from a LaTeX string (parsing).

The Compute Engine includes a default LaTeX dictionary to parse and serialize common math expressions.

It includes definitions such as:

  • "The Power function is represented as "x^{n}""
  • "The Divide function is represented as "\frac{x}{y}"".

Note that the dictionary will include LaTeX commands as triggers. LaTeX commands are usually prefixed with a backslash, such as \frac or \pm. It will also reference MathJSON symbols. MathJSON symbols are usually capitalized, such as Divide or PlusMinus and are not prefixed with a backslash.

To extend the LaTeX syntax update the latexDictionary property of the Compute Engine

ce.latexDictionary = [
  // Include all the entries from the default dictionary...
  ...ce.latexDictionary,
  // ...and add the `\smoll{}{}` command
  {
    // The parse handler below will be invoked when this LaTeX command 
    // is encountered
    latexTrigger: '\\smoll',
    parse: (parser) => {
      // We're expecting two arguments, so we're calling
      // `parseGroup()` twice. If `parseGroup()` returns `null`,
      // we assume that the argument is missing.
      return [
        "Divide",
        parser.parseGroup() ?? ["Error", "'missing'"],
        parser.parseGroup() ?? ["Error", "'missing'"],
      ];
    },
  },
];

console.log(ce.parse('\\smoll{1}{5}').json);
// The "Divide" get represented as a "Rational" by default when
// both arguments are integers.
// ➔ ["Rational", 1, 5]

To override an existing entry, create a new array that includes the default entries and add your own entry at the end of the array.

Entries at the end of the array will override earlier entries. When parsing an expression, the first entry (starting at the bottom) whose trigger matches is selected.

ce.latexDictionary = [
  ...ce.latexDictionary,
  // The entry below will override the default entry for the `\times` command
  {
    latexTrigger: ['\\times'],
    name: 'CrossProduct',
    kind: 'infix',
    associativity: 'none'
    precedence: 390,
  },
];

:::caution Do not modify the ce.latexDictionary array, or the entries in the array, directly. Instead, create a new array that includes the entries from the
default dictionary, and add your own entries. Later entries will override earlier ones, so you can replace or modify existing entries by providing a new definition for them. :::

The precedence property is used to determine the order of operations when parsing expressions, but it does not impact whether an entry is used for parsing. Only the latexTrigger or symbolTrigger properties are used to determine if an entry is used for parsing.

Note that latexTrigger can be an array of tokens. However, the tokens are not interpreted as alternatives. The array is treated as a sequence of tokens that must be matched in order.

LaTeX Dictionary Entries

Each entry in the LaTeX dictionary is an object with the following properties:

  • kind

    The kind of expression associated with this entry.

    Valid values are prefix, postfix, infix, expression, function, symbol, environment and matchfix.

    If not provided, the default is expression.

    The meaning of the values and how to use them is explained below.

    Note that it is possible to provide multiple entries with the same latexTrigger or symbolTrigger but with different kind properties. For example, the + operator is both an infix (binary) and a prefix (unary) operator.

  • latexTrigger

    A LaTeX fragment that will trigger the entry. For example, ^{+} or \mathbb{D}.

  • symbolTrigger

    A string, usually wrapped in a LaTeX command, that will trigger the entry.

    For example, if symbolTrigger is floor, the LaTeX command \mathrm{floor} or \operatorname{floor} will trigger the entry.

    Only one of latexTrigger or symbolTrigger should be provided.

    If kind is "environment", only symbolTrigger is valid, and it represents the name of the environment.

    If kind is matchfix, both openTrigger and closeTrigger must be provided instead.

  • parse

    A handler that will be invoked when the trigger is encountered in the LaTeX input.

    It will be passed a parser object that can be used to parse the input.

    The parse handler is invoked when the preconditions for the entry are met. For example, an infix entry will only be invoked if the trigger is encountered in the LaTeX input and there is a left-hand side to the operator.

    The signature of the parse handler will vary depending on the kind. For example, for an entry of kind infix the left-hand side argument will be passed to the parse handler. See below for more info about parsing for each kind.

    The parse handler should return a MathJSON expression or null if the expression is not recognized. When null is returned, the Compute Engine Natural Parser will backtrack and attempt to find another handler that matches the current token. If there can be no ambiguity and the expression is not recognized, the parse handler should return an ["Error"] expression. In general, it is better to return null and let the Compute Engine Natural Parser attempt to find another handler that matches the current token. If none is found, an ["Error"] expression will be returned.

  • serialize

    A handler that will be invoked when the expr.latex property is read. It will be passed a Serializer object that can be used to serialize the expression. The serialize handler should return a LaTeX string. See below for more info about serialization.

    If a serialize handler is provided, the name property must be provided as well.

  • name

    The name of the MathJSON symbol associated with this entry.

    If provided, a default parse handler will be used that is equivalent to: parse: name.

    It is possible to have multiple definitions with the same triggers, but the name property must be unique. The record with the name property will be used to serialize the expression. A serialize handler is invalid if the name property is not provided.

    The name property must be unique. However, multiple entries can have different triggers that produce the same expression. This is useful for synonyms, such as \operatorname{floor} and \lfloor...\rfloor.

Expressions

The most general type of entry is one of kind expression. If no kind property is provided, the kind is assumed to be expression.

For entries of kind expression the parse handler is invoked when the trigger is encountered in the LaTeX input. The parse handler is passed a parser object that can be used to parse the input.

The kind expression is suitable for a simple symbol, for example a mathematical constant. It can also be used for more complex constructs, such as to parse a series of tokens representing an integral expression. In this case, the parse handler would be responsible for parsing the entire expression and would use the parser object to parse the tokens.

If the tokens are not recognized, the parse handler should return null and the parser will continue to look for another handler that matches the current token.

Functions

The function kind is a special case of expression where the expression is a function, possibly using multi-character symbols, as in \operatorname{concat}.

Unlike an expression entry, after the parse handler is invoked, the parser will look for a pair of parentheses to parse the arguments of the function and apply them to the function.

The parse handler should return the symbol corresponding to the function, such as Concatenate. As a shortcut, the parse handler can be provided as an Expression. For example:

{
  kind: "function",
  symbolTrigger: "concat",
  parse: "Concatenate"
}

Operators: prefix, infix, postfix

The prefix, infix and postfix kinds are used for operators.

Entries for prefix, infix and postfix operators must include a precedence property. The precedence property is a number that indicates the precedence of the operator. The higher the number, the higher the precedence, that is the more "binding" the operator is.

For example, the precedence of the Add operator is 275 (ADDITION_PRECEDENCE), while the precedence of the Multiply operator is 390 (MULTIPLICATION_PRECEDENCE).

In 1 + 2 * 3, the Multiply operator has a higher precedence than the Add operator, so it is applied first.

The precedence range is an integer from 0 to 1000.

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
  • 300: some logic and arithmetic operators: \land, \lor 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
  • 0: ,, ;, etc...

The infix kind is used for binary operators (operators with a left-hand-side and right-hand-side).

The parse handler will be passed a parser object and the left-hand side of the operator, for postfix and infix operators.

The parser object can be used to parse the right-hand side of the expression.

{
  kind: "infix",
  latexTrigger: '\\oplus',
  precedence: ADDITION_PRECEDENCE,
  parse: (parser, lhs) => {
    return ["Concatenate", lhs, parser.parseExpression()];
  },
}

The prefix kind is used for unary operators.

The parse handler will be passed a parser object.

{
  kind: "prefix",
  latexTrigger: '\\neg',
  precedence: ADDITION_PRECEDENCE,
  parse: (parser, lhs) => {
    return ["Negate", lhs];
  },
}

The postfix kind is used for postfix operators. The parse handler will be passed a parser object and the left-hand side of the operator.

{
  kind: "postfix",
  latexTrigger: '\\!',
  parse: (parser, lhs) => {
    return ["Factorial", lhs];
  },
}

Environment

The environment kind is used for LaTeX environments.

The `symbolTrigger property in that case is the name of the environment.

The parse handler wil be passed a parser object. The parseTabular() method can be used to parse the rows and columns of the environment. It returns a two dimensional array of expressions.

The parse handler should return a MathJSON expression.

{
  kind: "environment",
  symbolTrigger: "matrix",
  parse: (parser) => {
    const content = parser.parseTabular();
    return ["Matrix", ["List", content.map(row => ["List", row.map(cell => cell)])]];
  },
}

Matchfix

The matchfix kind is used for LaTeX commands that are used to enclose an expression.

The openTrigger and closeTrigger indicate the LaTeX commands that enclose the expression. The parse handler is passed a parser object and the "body" (the expression between the open and close delimiters). The parse handler should return a MathJSON expression.

{
  kind: "matchfix",
  openTrigger: '\\lvert',
  closeTrigger: '\\rvert',
  parse: (parser, body) => {
    return ["Abs", body];
  },
}

Parsing

When parsing a LaTeX string, the first step is to tokenize the string according to the LaTeX syntax. For example, the input string \frac{ab}{10} will result in the tokens ["\\frac", "{", "a", "b", "}", "{", "1", "0", "}"].

Note that each LaTeX command is a single token, but that digits and ordinary letters are each separate tokens.

The parse handler is invoked when the trigger is encountered in the LaTeX token strings.

A common case is to return from the parse handler a MathJSON symbol.

For example, let's say you wanted to map the LaTeX command \div to the MathJSON Divide symbol. You would write:

{
  latexTrigger: '\\div',
  parse: (parser) => {
    return "Divide";
  },
}

As a shortcut, you can also write:

{
  latexTrigger: '\\div',
  parse: () => "Divide"
}

Or even more succintly:

{
  latexTrigger: '\\div',
  parse: "Divide"
}

The LaTeX \div(1, 2) would then produce the MathJSON expression ["Divide", 1, 2]. Note that the arguments are provided as comma-separated, parenthesized expressions, not as LaTeX arguments in curly brackets.

If you need to parse some more complex LaTeX syntax, you can use the parser argument of the parse handler. The parser object has numerous methods to help you parse the LaTeX string:

  • parser.peek is the current token.
  • parser.index is the index of the current token. If backtracking is necessary, it is possible to set the index to a previous value.
  • parser.nextToken() returns the next token and advances the index.
  • parser.skipSpace() in LaTeX math mode, skip over "space" which includes space tokens, and empty groups {}. Whether space tokens are skipped or not depends on the skipSpace option.
  • parser.skipVisualSpace() skip over "visual space" which includes space tokens, empty groups {}, and commands such as \, and \!.
  • parser.match(token: LatexToken) return true if the next token matches the argument, or null otherwise.
  • parser.matchAll(tokens) return true if the next tokens match the argument, an array of tokens, or null otherwise.
  • parser.matchAny(tokens: LatexToken[]) return the next token if it matches any of the token in the argument or null otherwise.
  • parser.matchChar() return the next token if it is a plain character (e.g. "a", '+'...), or the character corresponding to a hex literal (^^ and ^^^^) or the \char and \unicode commands
  • parser.parseGroup() return an expression if the next token is a group begin token { followed by a sequence of LaTeX tokens until a group end token } is encountered, or null otherwise.
  • parser.parseToken() return an expression if the next token can be parsed as a MathJSON expression, or null otherwise. This is useful when the argument of a LaTeX command can be a single token, for example for \sqrt5. Some, but not all, LaTeX commands accept a single token as an argument.
  • parser.parseOptionalGroup() return an expression if the next token is an optional group begin token [ followed by a sequence of LaTeX tokens until an optional group end token ] is encountered, or null otherwise.
  • parser.parseExpression() return an expression if the next tokens can be parsed as a MathJSON expression, or null otherwise. After this call, there may be some tokens left to parse.
  • parser.parseArguments() return an array of expressions if the next tokens can be parsed as a sequence of MathJSON expressions separated by a comma, or null otherwise. This is useful to parse the argument of a function. For example with f(x, y, z), the arguments would be [x, y, z].

If the parse() handler returns null, the parser will continue to look for another handler that matches the current token.

Note there is a pattern in the names of the methods of the parser. The match prefix means that the method will return the next token if it matches the argument, or null otherwise. These methods are more primitive. The parse prefix indicates that the method will return a MathJSON expression or null.

The most common usage is to call parser.parseGroup() to parse a group of tokens as an argument to a LaTeX command.

For example:

{
  latexTrigger: '\\div',
  parse: (parser) => {
    return ["Divide", parser.parseGroup(), parser.parseGroup()];
  },
}

In this case, the LaTeX input \div{1}{2} would produce the MathJSON expression ["Divide", 1, 2] (note the use of the curly brackets, rather than the parentheses in the LaTeX input).

If we wanted instead to treat the \div command as a binary operator, we could write:

{
  latexTrigger: '\\div',
  kind: "infix",
  parse: (parser, lhs) => {
    return ["Divide", lhs, parser.parseExpression()];
  },
}

By using the kind: "infix" option, the parser will automatically insert the left-hand side of the operator as the first argument to the parse handler.

Serializing

When serializing a MathJSON expression to a LaTeX string, the serialize() handler is invoked. You must specify a name property to associate the serialization handler with a MathJSON symbol.

{
  name: "Concatenate",
  latexTrigger: "\\oplus",
  serialize: (serializer, expr) =>
    "\\oplus" + serializer.wrapArguments(expr),
  evaluate: (ce, args) => {
    let result = '';
    for (const arg of args) {
      val = arg.numericValue;
      if (val === null || ce.isComplex(val) || Array.isArray(val)) return null;
      if (ce.isBignum(val)) {
        if (!val.isInteger() || val.isNegative()) return null;
        result += val.toString();
      } else if (typeof val === "number") {
        if (!Number.isInteger(val) || val < 0) return null;
        result += val.toString();
      }
    }
    return ce.parse(result);
  },
}

In the example above, the LaTeX command \oplus is associated with the Concatenate function. The serialize() handler will be invoked when the expr.latex property is read.

Note that we did not provide a parse() handler: if a name property is provided, a default parse handler will be used that is equivalent to: parse: name.

It is possible to have multiple definitions with the same triggers, but the name property must be unique. The record with the name property will be used to serialize the expression. A serialize handler is invalid if the name property is not provided.

Using a New Function with a Mathfield

You may also want to use your new function with a mathfield.

First you need to define a LaTeX macro so that the mathfield knows how to render this command. Let's define the \smallfrac macro.

const mfe = document.querySelector("math-field");

mfe.macros = {
  ...mfe.macros,
  smallfrac: {
    args: 2,
    def: "{}^{#1}\\!\\!/\\!{}_{#2}",
  },
};

The content of the def property is a LaTeX fragment that will be used to render the \\smallfrac command.

The #1 token in def is a reference to the first argument and #2 to the second one.

You may also want to define an inline shortcut to make it easier to input the command.

With the code below, we define a shortcut "smallfrac".

When typed, the shortcut is replaced with the associated LaTeX.

The #@ token represents the argument to the left of the shortcut, and the #? token represents a placeholder to be filled by the user.

mfe.inlineShortcuts = {
  ...mfe.inlineShortcuts,
  smallfrac: "\\smallfrac{#@}{#?}",
};
Learn more about **Key Bindings and Inline Shortcuts**

You can now parse the input from a mathfield using:

console.log(ce.parse(mfe.value).json);

Alternatively, the customized compute engine can be associated with the mathfields in the document:

MathfieldElement.computeEngine = ce;
console.log(mfe.getValue("math-json"));

title: Canonical Form slug: /compute-engine/guides/canonical-form/ layout: single date: Last Modified sidebar:

  • nav: "universal" toc: true render_math_in_document: true

Many mathematical objects can be represented by several equivalent expressions. A **canonical form** is a unique representation of an object that is chosen as the standard representation.

For example, the expressions in each row below represent the same mathematical object:

\(215.3465\) \(2.15346\operatorname{e}2\) \(2.15346 \times 10^2\)
\(1 - x\) \(-x + 1\) \(1 + (-x)\)
\(-2x^{-1}\) \(-\frac{2}{x}\) \(\frac{-2}{x}\)

The Compute Engine stores expressions internally in a canonical form to make it easier to work with symbolic expressions.

The canonical form is intended to be “stable”, that is it does not depend on the values of non-constant symbols or on assumptions about a symbol or expression.

The type of symbols can be used during canonicalization of expressions referencing the symbol, as the type can only be narrowed later and thus would not change the result of the canonicalization. The value of variables (non-constant symbols) is never used during canonicalization as it could be changed later.

ce.assign("x", -2);
console.info(ce.parse("\\frac{10}{x}").json);
// ➔ ["Rational", 10, "x"]
// and not `["Rational", -10, ["Negate", "x"]]` or `5`

Future versions of the Compute Engine could have different canonical forms, however a given version of the Compute Engine will always produce the same canonical form for a given expression, given the same type information about symbols and the same dictionary.

To check if an expression is canonical use expr.isCanonical.

To obtain the canonical representation of a non-canonical expression, use the expr.canonical property.

If the expression is already canonical, expr.canonical immediately returns expr.

The return value of expr.simplify(), expr.evaluate() and expr.N() are canonical expressions.

The ce.box() and ce.parse() functions return a canonical expression by default, which is the desirable behavior in most cases.

To get a non-canonical version of an expression use of ce.parse(s, {canonical: false}) or ce.box(expr, {canonical: false}).

You can further customize the canonical form of an expression by using the ["CanonicalForm"] function or by specifying the form you want to use. See below for more details.

The non-canonical version will be closer to the literal LaTeX input, which may be desirable to compare a "raw" user input with an expected answer.

console.info(ce.parse('\\frac{30}{-50}').json);
// ➔ ["Rational", -3, 5]
// The canonical version moves the sign to the numerator 
// and reduces the numerator and denominator

console.info(ce.parse('\\frac{30}{-50}', { canonical: false }).json);
// ➔ ["Divide", 30, -50]
// The non-canonical version does not change the arguments,
// so this is interpreted as a regular fraction ("Divide"), 
// not as a rational number.

The value of expr.json (the plain JSON representation of an expression) may not be in canonical form: some "sugaring" is applied to the internal representation before being returned, for example ["Power", "x", 2] is returned as ["Square", "x"].

To customize how an expression is serialized to plain JSON use expr.toMathJson().

const expr = ce.parse("\\frac{3}{5}");
console.log(expr.toMathJson());
// ➔ ["Rational", 3, 5]

console.log(expr.expr.toMathJson({ exclude: ["Rational"] }));
// ➔ ["Divide", 3, 5]
// We have excluded `["Rational"]` expressions, so it 
// is interepreted as a division instead.
const expr = ce.parse("\\frac{10}{30}", { canonical: false });
console.log(expr.json);
// ➔ ["Divide", 10, 30]

console.log(expr.isCanonical);
// ➔ false

console.log(expr.canonical.json);
// ➔ ["Rational", 1, 3]

Canonical Form and Validity

The canonical form of an expression may not be valid. A canonical expression may include ["Error"] expressions, for example, indicating missing arguments, excess arguments, or arguments of the wrong type.

For example the canonical form of ["Ln"] is ["Ln", ["Error", "'missing'"]] and it is not a valid expression.

To check if an expression is valid use expr.isValid.

To get a list of errors in an expression use expr.errors.

const expr = ce.parse("Ln");
console.log(expr.json);
// ➔ ["Ln", ["Error", "'missing'"]]
// The canonical form of `Ln` is not valid

console.log(expr.isCanonical);
// ➔ true

console.log(expr.isValid);
// ➔ false

console.log(expr.errors);
// ➔ [["Error", "'missing'"]]

Canonical Form Transformations

The canonical form used by the Compute Engine follows common conventions. However, it is not always "the simplest" way to represent an expression.

Calculating the canonical form of an expression involves applying some rewriting rules to an expression to put sums, products, numbers, roots, etc... in canonical form. In that sense, it is similar to simplifying an expression with expr.simplify(), but it is more conservative in the transformations it applies.

Below is a list of some of the transformations applied to obtain the canonical form:

  • Literal Numbers
    • Rationals are reduced, e.g. \( \frac{6}{4} \to \frac{3}{2}\)
    • The denominator of rationals is made positive, e.g. \(\frac{5}{-11} \to \frac{-5}{11}\)
    • A rational with a denominator of 1 is replaced with the numerator, e.g. \(\frac{19}{1} \to 19\)
    • Complex numbers with no imaginary component are replaced with the real component
  • Add
    • Literal 0 is removed
    • Sum of a literal and the product of a literal with the imaginary unit are replaced with a complex number.
    • Associativity is applied
    • Arguments are sorted
  • Multiply
    • Literal 1 is removed
    • Product of a literal and the imaginary unit are replaced with a complex number.
    • Literal -1 multiplied by an expression is replaced with the negation of the expression.
    • Signs are simplified: (-x)(-y) -> xy
    • Associativity is applied
    • Arguments are sorted
  • Negate
    • Literal numbers are negated
    • Negate of a negation is removed
  • Power
    • \(x^n)^m \to x^{nm}\)
    • \(x^{\tilde\infty} \to \operatorname{NaN}\)
    • \(x^0 \to 1\)
    • \(x^1 \to x\)
    • \((\pm 1)^{-1} \to -1\)
    • \((\pm\infty)^{-1} \to 0\)
    • \(0^{\infty} \to \tilde\infty\)
    • \((\pm 1)^{\pm \infty} \to \operatorname{NaN}\)
    • \(\infty^{\infty} \to \infty\)
    • \(\infty^{-\infty} \to 0\)
    • \((-\infty)^{\pm \infty} \to \operatorname{NaN}\)
  • Square: ["Power", "x", 2] \(\to\) ["Square", "x"]
  • Sqrt: ["Sqrt", "x"] \(\to\)["Power", "x", "Half"]
  • Root: ["Root", "x", 3] \(\to\) ["Power", "x", ["Rational", 1, 3]]
  • Subtract
    • Replaced with addition, e.g. ["Subtract", "a", "b"] \(\to\) ["Add", ["Negate", "b"], "a"]
  • Other functions:
    • Simplified if idempotent: \( f(f(x)) \to f(x) \)
    • Simplified if an involution: \( f(f(x)) \to x \)
    • Simplified if associative: \( f(a, f(b, c)) \to f(a, b, c) \)

Custom Canonical Forms

The full canonical form of an expression is not always the most convenient representation for a given application. For example, if you want to check the answers from a quiz, you may want to compare the user input with a canonical form that is closer to the user input.

To get the non-canonical form, use ce.box(expr, { canonical: false }) or ce.parse(s, { canonical: false }).

console.log(ce.parse("2(0+x\\times x-1)", {canonical: false}).json);

To get the full canonical form, use ce.box(expr, { canonical: true }) or ce.parse(s, { canonical: true }). The canonical option can be omitted as it is true by default.

console.log(ce.parse("2(0+x\\times x-1)", {canonical: true}).json);

console.log(ce.parse("2(0+x\\times x-1)").json);

To get a custom canonical form of an expression, use the ["CanonicalForm"] function or specify the form you want to use with the canonical option of ce.box() and ce.parse().

To order the arguments in a canonical order, use ce.box(expr, { canonical: "Order" }) or ce.parse(s, { canonical: "Order" }).

ce.parse("0+1+x+2+\\sqrt{5}", 
  {canonical: "Order"}
).print();

Note in particular that the 0 is preserved in the expression, which is not the case in the full canonical form.

There are other forms that can be used to customize the canonical form of an expression. See the documentation of ["CanonicalForm"] for more details.

For example:

const latex = "3(2+x)";
ce.parse(latex, {canonical: false}).print();

ce.parse(latex, {canonical: ["InvisibleOperator"]}).print();

ce.parse(latex, 
  {canonical: ["InvisibleOperator", "Add", "Order", ]}
).print();

title: Numeric Evaluation slug: /compute-engine/guides/numeric-evaluation/ date: Last Modified

To obtain an exact numeric evaluation of an expression use `expr.evaluate()`. To obtain a numeric approximation use `expr.N()`.

Exact Evaluation

An evaluation with expr.evaluate() preserves exact values.

Exact values are:

Type Examples
Integers 42 , 1234
Rationals \nicefrac 3 4 , \nicefrac 11 7
Square Roots of Integers 2 , 3 5
Constants e (ExponentialE), π (Pi)
console.log(ce.parse('1/3 + 1/4').evaluate());

console.log(ce.parse('\\sqrt{2} + \\sqrt{3} + \\sqrt{75}').evaluate());

Numeric Approximation

To force the evaluation of an expression to be a numeric approximation, use expr.N().

console.log(ce.parse('1/3 + 1/4').N());
console.log(ce.parse('\\sqrt{2} + \\sqrt{3} + \\sqrt{75}').N());

When using expr.evaluate(), if one of the arguments is not an exact value the expression is automatically evaluated as a numeric approximation.

console.log(ce.parse('1/3 + 1/4 + 1.24').evaluate());

JavaScript Interoperability

The result of expr.evaluate() and expr.N() is a boxed expression.

The numericValue property of this expression is either a machine number (a JavaScript number), a NumericValue object or null if the expression is not a number.

While a NumericValue object can represent arbitrary precision numbers, for use with JavaScript, a reduced precision approximation can be accessed using the re (for the real part of the number) and im (for the imaginary part) properties.

const expr = ce.parse('1/3 + 1/4');
console.log(expr.N().re);
// ➔ 0.5833333333333334

Another way to obtain a JavaScript compatible representation of an expression is to use the valueOf() method of the boxed expression.

const expr = ce.parse('1/3 + 1/4');
console.log(expr.N().valueOf());
// ➔ 0.5833333333333334

The valueOf() property of a boxed expression can be used in JavaScript expressions.

const expr = ce.parse('1/3 + 1/4');
console.log(expr.N().valueOf() + 1);


Unlike the `.re` property, the `.value` property can also return a `boolean`
or a `string`, depending on the value of the expression.



**To get a boxed number from a JavaScript number**, use `ce.box()` or `ce.number()`.

```live
const boxed = ce.box(1.5);
console.log(boxed.valueOf());

Numeric Precision

The number of significant digits used in numeric calculations is controlled by the ce.precision property of the ComputeEngine instance.

ce.precision = 30;
console.log(ce.parse('\\pi').N().json);

The default precision is 21.

To set the precision to the default value, use ce.precision = "auto".

Machine Precision

If the precision is 15 or less, the Compute Engine uses a 64-bit binary floating point format for its internal calculations.

This format is implemented in hardware and well suited to do fast computations. It uses a fixed amount of memory and represent significant digits in base-2 with about 15 digits of precision and with a minimum value of $ \pm5\times 10^{-324} $ and a maximum value of $ \pm1.7976931348623157\times 10^{+308} $

To change the precision to machine precision, use ce.precision = "machine".

With this precision, some calculations that have a value very close to 0 may return 0 and some calculations that have a value greater than the maximum value representable by a machine number may return $ \pm\infty $.

:::warning Some numeric evaluations using machine numbers cannot produce exact results.

ce.precision = 'machine';
console.log(ce.parse('0.1 + 0.2').N().json);
// ➔ "0.30000000000000004"

While $ 0.1 $ and $ 0.2 $ look like "round numbers" in base-10, they can only be represented by an approximation in base-2, which introduces cascading errors when manipulating them.

Read **"What Every Computer Scientist Should Know About Floating-Point Arithmetic"** by David Goldberg

:::

Arbitrary Precision

If ce.precision is greater than 15, numbers are represented as bignum numbers, a string of base-10 digits and an exponent.

Bignum numbers have a minimum value of $ \pm 10^{-9,000,000,000,000,000} $ and a maximum value of $ \pm9.99999\ldots \times 10^{+9,000,000,000,000,000} $.

ce.precision = 21;
console.log(ce.parse('0.1 + 0.2').N().json);
// ➔ "0.3"

Trigonometric operations are accurate for precision up to 1,000.

Serialization

The precision property affects how the computations are performed, but not how they are serialized.

To change the number of digits when serializing to LaTeX, use expr.toLatex({ precision: 6 }) to set it to 6 significant digits, for example.

The LaTeX precision is adjusted automatically when the precision is changed so that the display precision is never greater than the computation precision.

When the precision is greater than 15, the return value of expr.N().json may be a MathJSON number that looks like this:

{
  "num": "3.141592653589793238462643383279502884197169399375105820974944
  5923078164062862089986280348253421170679821480865132823066470938446095
  5058223172535940812848111745028410270193852110555964462294895493038196
  4428810975665933446128475648233786783165271201909145648566923460348610
  4543266482133936072602491412737245870066063155881748815209209628292540
  9173643678925903600113305305488204665213841469519415116094330572703657
  5959195309218611738193261179310511854807446237996274956735188575272489
  1227938180119491298336733624406566430860213949463952247371907021798609
  4370277053921717629317675238467481846766940513200056812714526356082778
  5771342757789609173637178721468440901224953430146549585371050792279689
  2589235420199561121290219608640344181598136297747713099605187072113499
  9999837297804995105973173281609631859502445945534690830264252230825334
  4685035261931188171010003137838752886587533208381420617177669147303598
  2534904287554687311595628638823537875937519577818577805321712268066130
  01927876611195909216420199"
}

Repeated Evaluation

To repeatedly evaluate an expression use ce.assign() to change the value of variables. ce.assign() changes the value associated with one or more variables in the current scope.

const expr = ce.parse("3x^2+4x+2");

for (let x = 0; x < 1; x += 0.01) {
  ce.assign('x', x);
  console.log(`f(${x}) = ${expr.evaluate()}`);
}

You can also use expr.subs(), but this will create a brand new expression on each iteration, and will be much slower.

const expr = ce.parse("3x^2+4x+2");

for (let x = 0; x < 1; x += 0.01) {
  console.log(`f(${x}) = ${expr.subs({ x }).evaluate()}`);
}

To reset a variable to be unbound to a value use ce.assign()

ce.assign("x", null);

console.log(ce.parse("3x^2+4x+2").N());
// ➔ "3x^2+4x+c"

To change the value of a variable set its value property:

ce.symbol("x").value = 5;

ce.symbol("x").value = undefined;

Compiling

If performance is important, the expression can be compiled to a JavaScript

To get a compiled version of an expression use the expr.compile() method:

const expr = ce.parse("3x^2+4x+2");
const fn = expr.compile();
for (const x = 0; x < 1; x += 0.01) console.log(fn({ x }));

:::info[Note] The syntax {x} is a shortcut for {"x": x}, in other words it defines an argument named "x" (which is used in the definition of the expression expr) with the value of the JavaScript variable x (which is used in the for loop). :::

This will usually result in a much faster evaluation than using expr.N() but this approach has some limitations.

Read more about Compiling Expressions to JavaScript

Simplifying Before Evaluating

When using expr.N(), no rewriting of the expression is done before it is evaluated.

Because of the limitations of machine numbers, this may produce surprising results.

For example:

ce.precision = "machine";
const x = ce.parse("0.1 + 0.2").N();
console.log(ce.box(["Subtract", x, x]).N());
// ➔ 2.7755575615628914e-17

However, the result of $ x - x $ from ce.simplify() is $ 0 $ since the simplification is done symbolically, before any floating point calculations are made.

const x = ce.parse('0.1 + 0.2').N();
console.log(ce.parse('x - x').simplify());
// ➔ 0

In some cases, it may be advantageous to invoke expr.simplify() before using expr.N().

Tolerance

Two numbers that are sufficiently close to each other are considered equal.

To control how close two numbers have to be before they are considered equal, set the tolerance property of a ComputeEngine instance.

By default, the tolerance is $ 10^{-10} $.

The tolerance is accounted for by the Chop function to determine when to replace a number of a small magnitude with the exact integer 0.

It is also used when doing some comparison to zero: a number whose absolute value is smaller than the tolerance will be considered equal to 0.

Numeric Functions

The topics below from the MathJSON Standard Library can provide numeric evaluations for their numeric functions:

Topic Symbols/Functions
Arithmetic Add Multiply Power Exp Log ExponentialE ImaginaryUnit...
Calculus Derivative Integrate...
Complex Real Conjugate, ComplexRoots...
Special Functions Gamma Factorial...
Statistics StandardDeviation Mean Erf...
Trigonometry Pi Cos Sin Tan...
--- title: Simplify slug: /compute-engine/guides/simplify/ --- A complicated mathematical expression can often be transformed into a form that is easier to understand.

The expr.simplify() function tries expanding, factoring and applying many other transformations to find a simple a simpler form of a symbolic expression.

Before the transformation rules are applied, the expression is put into a canonical form.

When a function is simplified, its arguments are simplified as well, unless the argument is "held". Which arguments are held is specified by the hold property of the function definition. In addition, any argument wrapped with a Hold function will be held, that is, not simplified. Conversely, a held argument wrapped with a ReleaseHold function will not be held, and it will be simplified.

Defining "Simpler"

An expression may be represented by several equivalent forms.

For example \( (x + 4)(x-5) \) and \(x^2 -x -20\) represent the same expression.

Determining which is "the simplest" depends on how the complexity is measured.

By default, the complexity of an expression is measured by counting the number of operations in the expression, and giving an increasing cost to:

  • integers with fewer digits
  • integers with more digits
  • other numeric values
  • add, multiply, divide
  • subtract and negate
  • square root and root
  • exp
  • power and log
  • trigonometric function
  • inverse trigonometric function
  • hyperbolic functions
  • inverse hyperbolic functions
  • other functions

To influence how the complexity of an expression is measured, set the costFunction property of the compute engine to a function assigning a cost to an expression.

Numeric Simplifications

The expr.simplify() function will apply some numeric simplifications, such as combining small integer and rational values, simplifying division by 1, addition or subtraction of 0, etc...

It avoids making any simplification that could result in a loss of precision.

For example, \( 10^{300} + 1\) cannot be simplified without losing the least significant digit, so expr.simplify() will return the expression unmodified.

Using Assumptions

Assumptions are additional information available about some symbols, for example \( x > 0 \) or \(n \in \N\).

Some transformations are only applicable if some assumptions can be verified.

For example, if no assumptions about \(x \) is available the expression \( \sqrt{x^2} \) cannot be simplified. However, if an assumption that \( x \geq 0 \) is available, then the expression can be simplified to \( x \).

Read more about Assumptions

title: Types slug: /compute-engine/guides/types/

In the Compute Engine, the **type** of an expression is the set of the possible values of that expression.

The Compute Engine uses a type system to ensure that operations are performed on the correct types of values.

A type is represented by a type expression, which is a string with a specific syntax. For example:

  • "integer"
  • "boolean"
  • "matrix<3x3>"
  • "integer & !0"
  • "integer -> integer"

A type expression is either a primitive type represented by an identifier such as "integer" or "boolean" or a constructed type.

To check the type of an expression, use the expr.type property.

console.log(ce.parse("3.14").type);

The type of a symbol can be declared explicitly, or it can be inferred from the context in which it is used, such as the value that is assigned to it or the operation that is performed on it.

To explicitly declare the type of a symbol, use the ce.declare() function.

ce.declare("n", "integer");
ce.parse("n").type;
// ➔ "integer"

Alternatively, to declare the type of a symbol you can evaluate a ["Declare"] expression

ce.box(["Declare", "n", "'integer'"]).evaluate();
ce.parse("n").type;
// ➔ "integer"

Type Hierarchy

The type system is based on the concept of subtyping, which allows for a hierarchy of types, where a type can be a subtype of another type. This allows for more flexible and expressive type definitions, as well as better error checking and type inference.

any
├── error
├── nothing
├── never
├── unknown
└── expression
    ├── symbol
    ├── function
    └── value
        ├── scalar
        │   ├── boolean
        │   ├── string
        │   └── number
        │     └── complex
        │         ├── imaginary
        │         └── real
        │             └── rational
        │                 └── integer
        └── collection
            ├── tuple
            ├── set
            ├── map
            └── list
                └── tensor
                    ├── vector
                    └── matrix

Note: this diagram is simplified and does not accurately reflect the finite vs non-finite distinction for the numeric types.

The unknown type is a placeholder for an expression whose type has not yet been determined, typically during type inference or partial evaluation. It is compatible with all types, and all types are compatible with it. It serves as a wildcard in type matching and can be replaced or refined as more information becomes available.

Primitive Types

A primitive type is a type that is not defined in terms of other types.

The Compute Engine supports the following primitive types:

Type Description
any The universal type, it contains all possible values. It has the following sub-types: error, nothing, never, unknown and expression. No other type matches any
error The type of an invalid expression, such as ["Error"]
nothing The type whose only member is the symbol Nothing; the unit type
never The type that has no values; the empty type or bottom type
unknown The type of an expression whose type is not known. An expression whose type is unknown can have its type modified (narrowed or broadened) at any time. Every other type matches unknown
expression A symbolic expression that represents a mathematical object, such as ["Add", 1, "x"], a symbol, a function or a value
symbol A named object, for example a constant or variable in an expression such as x or alpha
function A function literal: an expression that applies some arguments to a body to produce a result, such as ["Function", ["Add", "x", 1], "x"]
value A constant value, such as 1, True, 'hello' or Pi: a scalar or a collection
collection A collection of values: a list, a set, a tuple, or a map
scalar A single value: a boolean, a string, or a number
boolean The symbol True or False
string A string of Unicode characters
number A numeric value

Comparison of Special Types

Type Description Assignable To Assignable From
any All possible values
unknown Undetermined type, placeholder for inference
never No values at all (bottom type)
nothing Singleton unit type (Nothing) Only Nothing
error Invalid or ill-formed expression

Numeric Types

The type number represent all numeric values, including NaN.

More specific types of numeric values are represented by subtypes of number.

Some numeric types have a variant that excludes non-finite values, such as PositiveInfinity, NegativeInfinity and ComplexInfinity.

Type Description
number All numeric values: a real or complex number or NaN
non_finite_number The values + and (PositiveInfinity and NegativeInfinity)
complex A number with non-zero real and imaginary parts, such as 2 + 3 i , including ~ (ComplexInfinity)
imaginary A pure imaginary number, such as 3 i
real A real number, such as 2.5 , including ±
rational A number that can be expressed as the quotient of two integers such as \nicefrac 3 4 , including ± .
integer A whole number, such as 42 , including ± .
finite_number A real or complex number, except ± and ~
finite_complex A complex number, except ± and ~
finite_real A real number, except ±
finite_rational A rational number, except ±
finite_integer An integer, except ±

Here is the type of various numeric values:

Value Type
42 finite_integer
3.14 finite_real
\nicefrac 1 2 finite_rational
3 i imaginary
2 + 3 i finite_complex
non_finite_number
NaN number

The Compute Engine Standard Library includes definitions for sets that correspond to some numeric types.

Read more about the **sets** included in the Standard Library

Collection Types

A collection type is the type of an object that contains multiple values.

The Compute Engine supports the following collection types: set, tuple,
list (including vector, matrix and tensor), map and collection.

Set

A set is an unordered collection of unique values.

The type of a set is represented by the type expression set<T>, where T is the type of the elements of the set.

ce.parse("\\{1, 2, 3\\}").type
// ➔ "set<finite_integer>"

Tuple

A tuple is an ordered collection of values, representing a fixed number of elements.

The type of a tuple is represented by the type expression tuple<T1, T2, ...>, where T1, T2, ... are the types of the elements of the tuple.

ce.parse("(1, 2, 3)").type
// ➔ "tuple<finite_integer, finite_integer, finite_integer>"

The elements of a tuple can be named: tuple<x: integer, y: integer>. If an element is named, all elements must be named and the names must be unique.

The name of the elements of a tuple must use the letters a to z or A to Z, the digits 0 to 9 or the underscore _ and must start with a letter or an underscore.

For two tuples to be compatible, each element must have the same type and the names must match.

ce.parse("(x: 1, y: 2)").type.matches("tuple<x: integer, y: integer>");
// ➔ true
ce.parse("(x: 1, y: 2)").type.matches("tuple<a: integer, b: integer>");
// ➔ false

List, Vector, Matrix and Tensor

A list is an ordered collection of values, used to represent vectors, matrices, and sequences.

The type of a list is represented by the type expression list<T>, where T is the type of the elements of the list.

ce.parse("[1, 2, 3]").type.toString();
// ➔ "list<number>"

The shorthand list is equivalent to list<any>, a list of values of any type.

ce.parse("[1, 2, 3]").matches("list");
// ➔ true

The shorthand vector is a list of numbers, equivalent to list<number>.

ce.parse("[1, 2, 3]").matches("vector");
// ➔ true

A vector<n> is a list of n numbers.

ce.parse("[1, 2, 3]").type.matches("vector<3>");
// ➔ true

A vector<T^n> is a list of n elements of type T.

ce.parse("[1, 2, 3]").type.matches("vector<integer^3>");
// ➔ true

Similarly, a matrix is a list of lists.

  • The shorthand matrix is matrix<number^?x?>, a matrix of elements of type T, a list of lists of numbers, of rank 2 but of any dimensions. The ? symbol is a wildcard that matches any number of elements.
  • matrix<T>: A matrix of elements of type T, of any dimensions.
  • matrix<nxm>: A matrix of n rows and m columns (e.g. matrix<3x3>)
  • matrix<T^nxm>: A matrix of n rows and m columns of elements of type T (e.g. matrix<boolean^3x3>)

And finally, a tensor is a multi-dimensional array of any values, of any rank, and tensor<T> is a tensor of elements of type T.

Map

A map is a collection of key-value pairs, used to represent a dictionary, also known as an associative array, a hash table or a record.

The keys of a map must use the letters a to z or A to Z, the digits 0 to 9 or the underscore _. Keys containing other characters must be enclosed in backticks.

Keys must be unique within a map, but they are not ordered.

The type of a map is represented by the type expression map<K1: T1, K2: T2, ...>, where K1, K2, ... are the keys and T1, T2, ... are the types of the values.

For example: map<red: integer, green: integer, blue: integer> is a map that contains three elements with keys red, green and blue, and values of type integer.

For a map A to be compatible with a map B, the keys of A must be a subset of the keys of B and the values of A must be compatible with the values of B.

ce.type("map<red: integer, green: integer>")
  .matches("map<red: integer, green: integer>");
// ➔ true

ce.type("map<red: integer, green: integer>")
  .matches("map<red: integer, green: integer, blue: integer>");
// ➔ false

ce.type("map<red: integer, green: integer, blue: integer>")
  .matches("map<red: integer, green: integer>");
// ➔ true

The type map matches any map.

Collection

The type collection represent any collection of values, such as a list, a set, a tuple, or a map.

The type collection<T> is a collection of values of type T.

The collection type is an abstract type that is not directly instantiated. It can be used to check if an expression is a collection of values, without specifying the exact type of the collection.

Function Signature

A function signature is the type of functions literals.

A function signature is represented by the type expression (T1) -> T2, where T1 is the type of the input values of the function literal and T2 is the type of the output value, or return type, of the function literal.

Return Types

If the function never returns, the function signature is (T) -> never.

If the function does not return a value, the function signature is (T) -> nothing.

Arguments

If there is a single input argument, the parentheses can be omitted: T1 -> T2. For example, real -> integer is the type of functions that map real numbers to integers.

If there are no input arguments, use () -> T, for example () -> integer is the type of functions that return an integer and have no input arguments.

If there are multiple input arguments, the function signature is (T1, T2, ...) -> T, for example (integer, integer) -> integer is the type of functions that map two integers to an integer.

Named Arguments

Optionally, the input arguments can be named, for example: (x: integer, y: integer) -> integer.

The name of the argument must use the letters a to z and A to Z, the digits 0 to 9 or the underscore _ and must start with a letter or an underscore.

Optional Argument

An optional argument is indicated by a question mark after the type.

For example (integer, integer?) -> integer indicates a function literal accepting one or two integers as input and returning an integer.

If there are any optional arguments, they must be at the end of the argument list.

ce.type("(integer, integer?) -> number").matches("(integer) -> number");
// ➔ true

Rest Argument

A function signature can include a variable number of arguments, also known as a rest argument, indicated by an ellipsis ... before the type of the last argument.

For example (string, ...integer) -> integer is a function that accepts a string as a first argument followed by any number of integers and returns an integer.

To indicate that the function accepts a variable number of arguments of any type, use ...any.

ce.type("(integer, ...integer) -> number").matches("(integer, integer) -> number");
// ➔ true

Function Type

The type function matches any function literal, it is a shorthand for (...any) -> unknown.

Value Type

A value type is a type that represents a single value.

A value can be:

  • a boolean: true or false
  • a number, such as 42, -3.14, or 6.022e23
  • a string, such as "yellow",

Value types can be used in conjunction with a union to represent a type that can be one of multiple values, for example:

  • 0 | 1 is the type of values that are either 0 or 1.
  • integer | false is the type of values that are integers or False.
  • "red" | "green" | "blue" is the type of values that are either of the strings "red", "green" or "blue".

Other Constructed Types

Types can be combined to form new types using the following operations:

Union

A union is the type of values that are in either of two types.

The type of a union is represented by the type expression T1 | T2, where T1 and T2 are the types of the values.

For example, number | boolean is the type of values that are numbers or booleans.

Intersection

An intersection is the type of values that are in both of two types.

The type of an intersection is represented by the type expression T1 & T2, where T1 and T2 are the types of the values.

For example, map<length: integer> & map<size: integer> is the type of values that are dictionaries with both a length and a size key.

Negation

A negation is the type of values that are not of a given type.

A type negation is represented by the type expression !T, where T is a type.

For example, !integer is the type of values that are not integers.

The type integer & !0 is the type of values that are integers but not 0.

Matching Types

Two types can be evaluated for their compatibility. A type A is compatible with a type B (or matches it) if all values of type A are also values of type B. In other words, if A is a non-strict subtype of B.

To check if two types are compatible, use the type.matches() method.

ce.type("integer").matches("number");
// ➔ true

ce.type("number").matches("integer");
// ➔ false

ce.parse("3.14").type.matches("real");
// ➔ true

:::warning Do not check for type compatibility by comparing the type strings directly.

Type strings may represent refined or derived types (e.g. real vs finite_real), so use .matches() for compatibility checks instead of strict equality.

ce.parse("3.14").type === "real";
// ➔ false (the type is actually "finite_real")

ce.parse("3.14").type.matches("real");
// ➔ true

:::

Compatibility of Complex Types

Maps

Maps are compatible if they have the same keys and the values are compatible.

ce.parse("{red: 1, green: 2}").type
  .matches("map<red: integer, green: integer>");
// ➔ true

Width subtyping is supported for maps, meaning that a map with more keys is compatible with a map with fewer keys.

ce.parse("{red: 1, green: 2, blue: 3}").type
  .matches("map<red: integer, green: integer>");
// ➔ true

Tuples

Tuples are compatible if they have the same length and the elements are compatible.

ce.parse("(1, 2, 3)").type
  .matches("tuple<integer, integer, integer>");
// ➔ true

If the elements of a tuple are named, the names must match.

ce.parse("(x: 1, y: 2)").type
  .matches("tuple<x: integer, y: integer>");
// ➔ true

ce.parse("(x: 1, y: 2)").type
  .matches("tuple<a: integer, b: integer>");
// ➔ false

Lists

Lists are compatible if they have the same length and the elements are compatible.

ce.parse("[1, 2, 3]").type
  .matches("list<finite_integer>");
// ➔ true

Function Literals

Function literals are compatible if the input types are compatible and the output types are compatible.

ce.type("integer -> integer")
  .matches("number -> number");
// ➔ true

The name of the arguments of a function signature is not taken into account when checking for compatibility.

ce.type("x: integer -> integer")
  .matches("integer -> integer");
// ➔ true

Checking the Type of a Numeric Value

The properties expr.isNumber, expr.isInteger, expr.isRational and expr.isReal are shortcuts to check if the type of an expression matches the types "number", "integer", "rational" and "real" respectively.

console.info(ce.box(3.14).type);
// ➔ "finite_real"

console.info(ce.box(3.14).type.matches("finite_real")) 
// ➔ true

console.info(ce.box(3.14).type.matches("real")) 
// ➔ true

console.info(ce.box(3.14).isReal) 
// ➔ true

console.info(ce.box(3.14).type.matches("integer")) 
// ➔ false

console.info(ce.box(3.14).isInteger) 
// ➔ false

Defining New Types

To define new types use the ce.declareType() function.

For example, to defines a new type point that is a tuple of two integers, x and y:

ce.declareType("point", "tuple<x: integer, y: integer>");

The type is defined in the current lexical scope.

Nominal vs Structural Types

By default, types are nominal, meaning that to be compatible, they must have the same name and not just the same structure.

ce.type("tuple<x: integer, y: integer>")
  .matches("point");
// ➔ false

To make a type structural, use the ce.declareType() function with the alias option. Two structural types are compatible if they have the same structure, regardless of their names.

ce.declareType("pointData", "tuple<x: integer, y: integer>", { alias: true });
ce.type("tuple<x: integer, y: integer>")
  .matches("pointData");
// ➔ true

Recursive Types

A recursive type is a type that refers to itself in its definition.

For example, a binary tree can be defined as a tuple of a value and two subtrees:

ce.declareType("tree", "tuple<value: integer, left: tree, right: tree>");

A set of types can be mutually recursive, meaning that they can refer to each other in their definitions. In this case, you can use a type before declaring it by prefacing if with the type keyword.

For example, a definition of a JSON object could be:

ce.declareType("json_value", `
    nothing
  | boolean
  | number
  | string
  | type json_array
  | type json_object
`);
ce.declareType("json_object", "map<string, type json>");
ce.declareType("json_array", "list<type json>");
ce.declareType("json", "json_object | json_array");

When using type json_array or type json_object, the type is not yet defined, but it will be defined later in the code. This allows you to use the type before declaring it, but it is not necessary to use the type keyword if the type is already defined.


title: Arithmetic slug: /compute-engine/reference/arithmetic/

Constants

Symbol Value
ExponentialE 2.7182818284 Euler's number
MachineEpsilon $$ 2^{−52}$$ The difference between 1 and the next larger floating point number.
See Machine Epsilon on Wikipedia
CatalanConstant 0.9159655941 $$ \sum_{n=0}^{\infty} \frac{(-1)^{n}}{(2n+1)^2} $$
See Catalan's Constant on Wikipedia
GoldenRatio 1.6180339887 $$ \frac{1+\sqrt{5}}{2} $$
See Golden Ratio on Wikipedia
EulerGamma 0.5772156649 See Euler-Mascheroni Constant on Wikipedia
See also **Trigonometry** for `Pi` and related constants See also **Complex** for `ImaginaryUnit`

Functions

Arithmetic Functions

Function Notation
Add $$ a + b$$ Addition
Subtract $$ a - b$$ Subtraction
Negate a Additive inverse
Multiply $$ a\times b $$ Multiplication
Divide $$ \frac{a}{b} $$ Divide
Power $$ a^b $$ Exponentiation
Root x n = x 1 n nth root
Sqrt x = x 1 2 Square root
Square x 2

Sums and Products

xs: collection

Evaluate to a sum of all the elements in collection. If all the elements are numbers, the result is a number. Otherwise it is an ["Add"] expression.

["Sum", ["List", 5, 7, 11]]
// ➔ 23

["Sum", ["List", 5, 7, "x" , "y"]]
// ➔ ["Add", 12, "x", "y"]

Note this is equivalent to:

["Reduce", ["List", 5, 7, 11], "Add"]

body: function, ...bounds: tuple

Evaluate to the sum of body for each value in bounds.

["Sum", ["Add", "i", 1], ["Tuple", "i", 1, 10]]
// ➔ 65

xs: collection

Evaluate to a product of all the elements in collection.

If all the elements are numbers, the result is a number. Otherwise it is a ["Multiply"] expression.

["Product", ["List", 5, 7, 11]]
// ➔ 385

["Product", ["List", 5, "x", 11]]
// ➔ ["Multiply", 55, "x"]

Note this is equivalent to:

["Reduce", ["List", 5, 7, 11], "Multiply"]

f: function, bounds:tuple

Return the product of bodyfor each value in bounds.

["Product", ["Add", "x", 1], ["Tuple", "x", 1, 10]]
// ➔ 39916800

Transcendental Functions

Function Notation
Exp \exponentialE x Exponential function
Ln ln ( x ) Logarithm function, the natural logarithm, the inverse of Exp
Log log b ( x ) ["Log", <v>, <b>]
Logarithm of base b, default 10
Lb log 2 ( x ) Binary logarithm function, the base-2 logarithm
Lg log 10 ( x ) Common logarithm, the base-10 logarithm
LogOnePlus ln ( x + 1 )
See also **Trigonometry** for trigonometric functions See also **Complex** for complex functions See also **Statistics** for statistics functions and functions on lists

Rounding

Function Notation
Abs | x | Absolute value, magnitude
Ceil x Rounds a number up to the next largest integer
Floor x Round a number to the greatest integer less than the input value
Chop Replace real numbers that are very close to 0 (less than 10 10 ) with 0
Round

Other Relational Operators

a, b, modulus

Evaluate to True if a is congruent to b modulo modulus.

["Congruent", 26, 11, 5]
// ➔ True

Other Functions

value:integer

value:integer, base

Format an integer in a specific base, such as hexadecimal or binary.

If no base is specified, use base-10.

The sign of integer is ignored.

  • value should be an integer.
  • base should be an integer from 2 to 36.
["Latex", ["BaseForm", 42, 16]]

// ➔ (\text(2a))_{16}
Latex(BaseForm(42, 16))
// ➔ (\text(2a))_{16}
String(BaseForm(42, 16))
// ➔ "'0x2a'"

value

value, lower, upper

  • If value is less than lower, evaluate to lower
  • If value is greater than upper, evaluate to upper
  • Otherwise, evaluate to value

If lowerand upperare not provided, they take the default values of -1 and +1.

["Clamp", 0.42]
// ➔ 1
["Clamp", 4.2]
// ➔ 1
["Clamp", -5, 0, "+Infinity"]
// ➔ 0
["Clamp", 100, 0, 11]
// ➔ 11

x1, x2, ...

list

If all the arguments are real numbers, excluding NaN, evaluate to the largest of the arguments.

Otherwise, simplify the expression by removing values that are smaller than or equal to the largest real number.

["Max", 5, 2, -1]
// ➔ 5
["Max", 0, 7.1, "NaN", "x", 3]
// ➔ ["Max", 7.1, "NaN", "x"]

x1, x2, ...

list

If all the arguments are real numbers, excluding NaN, evaluate to the smallest of the arguments.

Otherwise, simplify the expression by removing values that are greater than or equal to the smallest real number.

["Min", 5, 2, -1]
// ➔ -1
["Min", 0, 7.1, "x", 3]
// ➔ ["Min", 0, "x"]

a, b

Evaluate to the Euclidian division (modulus) of a by b.

When a and b are positive integers, this is equivalent to the % operator in JavaScript, and returns the remainder of the division of a by b.

However, when a and b are not positive integers, the result is different.

The result is always the same sign as b, or 0.

["Mod", 7, 5]
// ➔ 2

["Mod", -7, 5]
// ➔ 3

["Mod", 7, -5]
// ➔ -3

["Mod", -7, -5]
// ➔ -2

n

Evaluate to a rational approximating the value of the number n.

["Rational", 0.42]
// ➔ ["Rational", 21, 50]

numerator, denominator

Represent a rational number equal to numeratorover denominator.

expr

Return the numerator of expr.

Note that expr may be a non-canonical form.

["Numerator", ["Rational", 4, 5]]
// ➔ 4

expr

Return the denominator of expr.

Note that expr may be a non-canonical form.

["Denominator", ["Rational", 4, 5]]
// ➔ 5

expr

Return the numerator and denominator of expr as a sequence.

Note that expr may be a non-canonical form.

["NumeratorDenominator", ["Rational", 4, 5]]
// ➔ ["Sequence", 4, 5]

The sequence can be used with another function, for example GCD to check if the fraction is in its canonical form:

["GCD", ["NumeratorDenominator", ["Rational", 4, 5]]]
// ➔ 1

["GCD", ["NumeratorDenominator", ["Rational", 8, 10]]]
// ➔ 2

Relational Operators

Function Notation
Equal $$ x = y $$ Mathematical relationship asserting that two quantities have the same value
NotEqual $$ x \ne y $$
Greater $$ x \gt y $$
GreaterEqual $$ x \geq y $$
Less $$ x \lt y $$
LessEqual $$ x \leq y $$

See below for additonal relational operators: Congruent, etc...

--- title: Introduction - Compute Engine sidebar_label: Introduction hide_title: true slug: /compute-engine/ date: Last Modified description: The Compute Engine is a JavaScript/TypeScript library for symbolic computing and numeric evaluation of mathematical expressions. sidebar_class_name: "compass-icon" --- # Compute Engine A JavaScript library for symbolic computing and numeric evaluation of mathematical expressions.

:::info[Note] To use Compute Engine, you must write JavaScript or TypeScript code. This guide assumes you’re familiar with these languages. :::

console.log("exp(i*pi) =", ce.parse("e^{i\\pi}").evaluate());
const expr = ce.parse("(a+b)^2");
ce.box(["Expand", expr]).evaluate().print();
const lhs = ce.parse("1 + x(1 + 2x) + 2x");
const rhs = ce.parse("2x^2 + 3x + 1");
console.log(lhs, lhs.isEqual(rhs) ? "=" : "≠", rhs);

The Compute Engine is for educators, students, scientists, and engineers who need to run technical computing apps in browsers or server-side JavaScript environments like Node.js.

The Compute Engine manipulates math expressions represented with the MathJSON format.

The expression \(x^2 + 2x + 1\) is represented in MathJSON as:

["Add", ["Power", "x", 2], ["Multiply", 2, "x"], 1]

The Compute Engine can:

Try the **interactive demo** now

Getting Started

The easiest way to get started is to load the Compute Engine JavaScript module from a CDN, then create a ComputeEngine instance.

Using JavaScript Modules

To load the Compute Engine module from the jsdelivr CDN, use a <script> tag with the type="module" attribute and an import statement.

<script type="module">
  import { ComputeEngine } from "https://esm.run/@cortex-js/compute-engine";

  const ce = new ComputeEngine();
  ce.parse("e^{i\\pi}").evaluate().print();
  // ➔ "-1"
</script>

Alternatively, you can use the unpkg CDN to load the module:

import { ComputeEngine } from 
  "https://unpkg.com/@cortex-js/compute-engine?module";

The ESM (module) version is also available in the npm package as /compute-engine.min.esm.js

Using Vintage JavaScript

If you are using a vintage environment, or if your toolchain does not support modern JavaScript features, use the UMD version.

For example, WebPack 4 does not support the optional chaining operator, using the UMD version will make use of polyfills as necessary.

To load the UMD version, use a <script> tag with the src attribute.

<script 
  src="https://cdn.jsdelivr.net/npm/@cortex-js/compute-engine/compute-engine.min.js">
</script>
<script>
  window.onload = function() {
    const ce = new ComputeEngine.ComputeEngine();
    console.log(ce.parse("e^{i\\pi}").evaluate());
    // ➔ "-1"
  }
</script>

Alternatively, use the unpkg CDN to load the library:

<script src="//unpkg.com/@cortex-js/compute-engine"></script>

The UMD version is also available in the npm package in /compute-engine.min.js

Other Versions

A non-minified module which may be useful for debugging is available in the npm package as /compute-engine.esm.js.

MathJSON Standard Library

The operators in a MathJSON expression are defined in libraries. The MathJSON Standard Library is a collection of functions and symbols that are available by default to a ComputeEngine instance.

Topic
Arithmetic Add Multiply Power Exp Log ExponentialE ImaginaryUnit...
Calculus D Derivative Integrate...
Collections List Reverse Filter...
Complex Real Conjugate, ComplexRoots...
Control Structures If Block Loop ...
Core Declare Assign Error LatexString...
Functions Function Apply Return ...
Logic And Or Not True False ...
Sets Union Intersection EmptySet RealNumbers Integers ...
Special Functions Gamma Factorial...
Statistics StandardDeviation Mean Erf...
Styling Delimiter Style...
Trigonometry Pi Cos Sin Tan...

:::info[Note] In this guide, the ce. prefix in ce.box() or ce.parse() indicates that the function is a method of the ComputeEngine class.

To create a new ComputeEngine instance use ce = new ComputeEngine()

The expr. prefix in expr.evaluate() or expr.simplify() indicates that the function is a method of the BoxedExpression class.

To create a new boxed expression use expr = ce.parse() or expr = ce.box()

:::

Read more about the **MathJSON Standard Library**

You can add your own definitions to the built-in definitions from the MathJSON Standard Library.

Read more about **Extending the MathJSON Standard Library**

If you use a custom LaTeX syntax, such as macros, you can add your own definitions to the LaTeX dictionary, which defines how to parse and serialize LaTeX to MathJSON.

Read more about **Parsing and Serializing LaTeX** --- title: Statistics slug: /compute-engine/reference/statistics/ ---

For the following functions, when the input is a collection, it can take the following forms:

  • Multiple arguments, e.g. ["Mean", 1, 2, 3]
  • A list of numbers, e.g. ["Mean", ["List", 1, 2, 3]]
  • A matrix, e.g. ["Mean", ["List", ["List", 1, 2], ["List", 3, 4]]]
  • A range, e.g. ["Mean", ["Range", 1, 10]]
  • A linear space: ["Mean", ["Linspace", 1, 5, 10]]

Functions

collection

Evaluate to the arithmetic mean of a collection of numbers.

The arithmetic mean is the average of the list of numbers. The mean is calculated by dividing the sum of the numbers by the number of numbers in the list.

The formula for the mean of a list of numbers is x ¯ = 1 n i = 1 n x i , where n is the number of numbers in the list, and x i is the i -th number in the list.

["Mean", ["List", 7, 8, 3.1, 12, 77]]
// 21.02

collection

Evaluate to the median of a collection of numbers.

The median is the value separating the higher half from the lower half of a data sample. For a list of numbers sorted in ascending order, the median is the middle value of the list. If the list has an odd number of elements, the median is the middle element. If the list has an even number of elements, the median is the average of the two middle elements.

["Median", ["List", 1, 2, 3, 4, 5]]
// 3

collection

Evaluate to the mode of a collection of numbers.

The mode is the value that appears most often in a list of numbers. A list of numbers can have more than one mode. If there are two modes, the list is called bimodal. For example $$ \lbrack 2, 5, 5, 3, 2\rbrack$$. If there are three modes, the list is called trimodal. If there are more than three modes, the list is called multimodal.

collection

Evaluate to the variance of a collection of numbers.

The variance is a measure of the amount of variation or dispersion of a set of values. A low variance indicates that the values tend to be close to the mean of the set, while a high variance indicates that the values are spread out over a wider range.

The formula for the variance of a list of numbers is

1 n i = 1 n ( x i μ ) 2

where μ is the mean of the list.

collection

Evaluate to the standard deviation of a collection of numbers.

The standard deviation is a measure of the amount of variation or dispersion of a set of values. A low standard deviation indicates that the values tend to be close to the mean of the set, while a high standard deviation indicates that the values are spread out over a wider range.

The formula for the standard deviation of a collection of numbers is

1 n i = 1 n ( x i μ ) 2

where μ is the mean of the list.

collection

Evaluate to the skewness of a list of numbers.

The skewness is a measure of the asymmetry of the distribution of a real-valued random variable about its mean. The skewness value can be positive or negative, or undefined.

The formula for the skewness of a collection of numbers is: 1 n i = 1 n ( x i μ σ ) 3

where μ is the mean of the collection, and σ is the standard deviation of the collection.

collection

Evaluate to the kurtosis of a collection of numbers.

The kurtosis is a measure of the "tailedness" of the distribution of a real-valued random variable. The kurtosis value can be positive or negative, or undefined.

The formula for the kurtosis of a collection of numbers is

1 n i = 1 n ( x i μ σ ) 4

where μ is the mean of the list, and σ is the standard deviation of the list.

collection, q:number

Evaluate to the quantile of a collection of numbers.

The quantile is a value that divides a collection of numbers into equal-sized groups. The quantile is a generalization of the median, which divides a collection of numbers into two equal-sized groups.

So,

The following macros are not allowed: operatorname

$$\operatorname{median} = \operatorname{quantile}(0.5)$$
.

collection

Evaluate to the quartiles of a collection of numbers.

The quartiles are the three points that divide a collection of numbers into four equal groups, each group comprising a quarter of the collection.

collection

Evaluate to the interquartile range (IRQ) of a collection of numbers.

The interquartile range is the difference between the third quartile and the first quartile.

z:complex

Evaluate to the error function of a complex number.

The error function is an odd function ( $$ \operatorname{erf} -z = - \operatorname{erf} z$$ ) that is used in statistics to calculate probabilities of normally distributed events.

The formula for the error function of a complex number is:

The following macros are not allowed: operatorname

$$ \operatorname{erf} z = \frac{2}{\sqrt{\pi}} \int_0^z e^{-t^2} dt$$

where z is a complex number.

z:complex

Evaluate to the complementary error function of a complex number.

It is defined as $$ \operatorname{erfc} z = 1 - \operatorname {erf} z $$.

x:real

Evaluate to the inverse error function of a real number $$ -1 < x < 1 $$

It is defined as $$ \operatorname{erf} \left(\operatorname{erf} ^{-1}x\right) = x $$.


title: Expressions slug: /compute-engine/guides/expressions/ layout: single date: Last Modified sidebar:

  • nav: "universal" toc: true

The Compute Engine produces and manipulates [symbolic expressions]() such as numbers, constants, variables and functions.

In the Compute Engine, expressions are represented internally using the MathJSON format.

They are wrapped in a JavaScript object, a process called boxing, and the resulting expressions are Boxed Expressions.

Boxed Expressions improve performance by implementing caching to avoid repetitive calculations. They also ensure that expressions are valid and in a standard format.

Unlike the plain data types used by JSON, Boxed Expressions allow an IDE, such as VSCode Studio, to provide hints in the editor regarding the methods and properties available for a given expression.

Boxed Expressions can be created from a LaTeX string or from a raw MathJSON expression.

Boxing

To create a BoxedExpression from a MathJSON expression use the ce.box() method.

The input of ce.box() can be:

  • a MathJSON expression
  • a BoxedExpression (in which case it is returned as-is)
  • a SemiBoxedExpression, that is a MathJSON expression with some of its subexpressions already boxed.

The result is an instance of a BoxedExpression.

let expr = ce.box(1.729e3);
console.log(expr.re);
// ➔ 1729

console.log(expr.isPositive);
// ➔ true

expr = ce.box({ num: "+Infinity" });
console.log(expr.latex);
// ➔ "+\infty"

expr = ce.box(["Add", 3, "x"]);
console.log(expr.operator);
// ➔ "Add"

To create a Boxed Expression from a LaTeX string use the ce.parse() function.

const expr = ce.parse("3 + x + y");
console.log(expr.operator);
// ➔ "Add"

console.log(expr.json);
// ➔ ["Add", 3, "x", "y"]

To get a Boxed Expression representing the content of a mathfield use the mf.expression property:

const mf = document.getElementById("input");
mf.value = "\\frac{10}{5}";
const expr = mf.expression;
console.log(expr.evaluate());
// ➔ 2

Canonical Expressions

The canonical form of an expression is a conventional way of writing an expression.

For example, the canonical form of a fraction of two integers is a reduced rational number, written as a tuple of two integers, such that the GCD of the numerator and denominator is 1, and the denominator is positive.

const expr = ce.parse("\\frac{30}{-50}");
console.log(expr.json);
// ➔ ["Rational", -3, 5]

The canonical form of a rational with a denominator of 1 is an integer.

const expr = ce.parse("\\frac{17}{1}");
console.log(expr.json);
// ➔ 17

To determine if a non-canonical expression is a reduced (canonical) rational number check that the GCD of the numerator and denominator is 1.

const input = ce.parse("\\frac{30}{50}", {canonical: false});
console.info(ce.box(
  ["GCD", ["NumeratorDenominator", input]]
).evaluate().valueOf() === 1);
// ➔ false

The canonical form of an addition or multiplication will have its arguments ordered in a canonical way.

const expr = ce.parse("2+x+\\pi+\\sqrt2+1");
console.log(expr.json);
// ➔ ["Add", "Pi", ["Sqrt", 2], "x", 1, 2]
Read more about the **Canonical Form**

By default, ce.box() and ce.parse() produce a canonical expression.

To get a non-canonical expression instead, use ce.box(expr, {canonical: false}) or ce.parse(latex, {canonical: false}).

When using ce.parse(), the non-canonical form sticks closer to the original LaTeX input. When using ce.box(), the non-canonical form matches the input MathJSON.

const latex = "\\frac{30}{-50}";

ce.parse(latex);
// canonical form ➔ ["Rational", -3, 5]

ce.parse(latex, { canonical: false });
// non-canonical form ➔ ["Divide", 30, -50]

ce.box(["Divide", 30, -50], { canonical: false });
// non-canonical form ➔ ["Divide", 30, -50]

To obtain the canonical representation of a non-canonical expression use expr.canonical.

A non-canonical expression may include errors as a result of parsing from LaTeX, if the LaTeX input contained LaTeX syntax errors.

A canonical expression may include additional errors compared to a non-canonical expression, for example ["Divide", 2, 5, 6] (three arguments instead of two), ["Add", 2, "True"] (mismatched argument type, expected a number but got a boolean).

The canonical form of an expression which is not valid will include one or more ["Error"] expressions indicating the nature of the problem.

To check if an expression contains errors use expr.isValid. The expr.errors property returns a list of all the ["Error"] subexpressions.

When doing this check on a canonical expression it takes into consideration not only possible syntax errors, but also semantic errors (incorrect number or type of arguments, etc...).

String Representation

The expr.toString() method returns an AsciiMath string representation of the expression.

let expr = ce.parse("3x^2+\\sqrt{2}");
console.log(expr.toString());

When used in a context where a string is expected, the expr.toString() method is called automatically.

let expr = ce.parse("3x^2+\\sqrt{2}");
console.log(expr);

To output an AsciiMath representation of the expression to the console use expr.print().

let expr = ce.parse("\\frac{1+\\sqrt{5}}{2}");
expr.print();

To obtain a LaTeX representation of the expression use expr.latex or expr.toLatex() for additional formatting options.

let expr = ce.parse("3x^2+\\sqrt{2}");
console.log(expr.latex);

Unboxing

To access the MathJSON expression of a boxed expression as plain JSON use the expr.json property. This property is an "unboxed" version of the expression.

const expr = ce.box(["Add", 3, "x"]);
console.log(expr.json);
// ➔ ["Add", 3, "x"]

To customize the format of the MathJSON expression returned by expr.json use the ce.toMathJson() method.

Use this option to control:

  • which metadata, if any, should be included
  • whether to use shorthand notation
  • to exclude some functions

See JsonSerializationOptions for more info about the formatting options available.

const expr = ce.parse("2 + \\frac{q}{p}");
console.log("expr.json:", expr.json);

console.log("expr.toMathJson():", expr.toMathJson({
  exclude: ["Divide"], // Don't use `Divide` functions,
  // use `Multiply`/`Power` instead
  shorthands: [], // Don't use any shorthands
}));

Mutability

Unless otherwise specified, expressions are immutable.

The functions that manipulate Boxed Expressions, such as expr.simplify(), expr.evaluate(), expr.N() return a new Boxed Expression, without modifying expr.

However, the properties of the expression may change, since some of them may depend on contextual information which can change over time.

For example, ce.box('n').isPositive may return undefined if nothing is known about the symbol n. But if an assumption about the symbol is made later, or a value assigned to it, then ce.box('n').isPositive may take a different value.

const expr = ce.box("n");
console.log(expr.isPositive);
// ➔ undefined

ce.assume(ce.parse("n > 0"));
console.log(expr.isPositive);
// ➔ true

What doesn't change is the fact that expr represents the symbol "n".

Pure Expressions

A pure expression is an expression that produces no side effect (doesn't change the state of the Compute Engine) and always evaluates to the same value when the same arguments are applied to it.

The Sin function is pure: it evaluates to the same value when the same arguments are applied to it.

On the other hand, the Random function is not pure: by its nature it evaluates to a different value on every evaluation.

Numbers, symbols and strings are pure. A function expression is pure if the function itself is pure, and all its arguments are pure as well.

To check if an expression is pure use expr.isPure.

Checking the Kind of Expression

To identify if an expression is a number literal, a symbol, a function expression or a string use the following boolean expressions:

Kind Boolean Expression
Number Literal expr.isNumberLiteral
Function Expression expr.isFunctionExpression
Symbol expr.symbol !== null
String expr.string !== null

Accessing the Value of an Expression

To access the value of an expression as a MathJSON expression use expr.json.

To access the value of an expression as a JavaScript primitive use expr.valueOf(). The result is a JavaScript primitive, such as a number, string or boolean. When converting to a number, the result may have lost precision if the original expression had more than 15 digits of precision.

To access the value of an expression as a JavaScript number use expr.re. The result is the real part of the number, as a JavaScript number, or NaN if the expression is not a number. Use expr.im to get the imaginary part.

In general, expressions need to be evaluated before they can be converted to a JavaScript primitive. For example, ce.parse("2 + 3").valueOf() will return "2 + 3", while ce.parse("2 + 3").evaluate().valueOf() will return 5.

If the expression is a number literal or a symbol with a numeric value, the expr.value property will return the value of the expression as BoxedExpression or undefined if the expression is not a number.

Errors

Sometimes, things go wrong.

If a boxed expression is not valid, the expr.isValid property will be set to false, and the expr.errors property will contain a list of all the ["Error"] subexpressions.

When something goes wrong the Compute Engine uses an ["Error", <cause>, <location>] expression.

The <cause> argument provides details about the nature of the problem. This can be either a string or an ["ErrorCode"] expression if there are additional arguments to the error.

For example if the problem is that an argument of a function expression is a boolean when a number was expected, an expression such as ["Error", ["ErrorCode", "'incompatible-type'", "'number'", "'boolean'"]] could be returned.

The <location> argument indicates the context of the error. This can be a ["Latex"] expression when the problem occurred while parsing a LaTeX string, or another expression if the problem was detected later.

Parsing Errors

When parsing a LaTeX expression, the Compute Engine uses the maximum effort doctrine. That is, even partially complete expressions are parsed, and as much of the input as possible is reflected in the MathJSON result.

If required operands are missing (the denominator of a fraction, for example), a ["Error", ""missing""] error expression is inserted where the missing operand should have been.

Problems that occur while parsing a LaTeX string will usually indicate a LaTeX syntax error or typo: missing }, mistyped command name, etc...

Semantic Errors

Some errors are not caught until an expression is bound, that is until an attempt is made to associate its symbols to definitions. This could include errors such as missing or mismatched arguments.

Some errors that could be considered LaTeX syntax errors may not surface until binding occurs.

For example \frac{1}{2=x} (instead of \frac{1}{2}=x) will be parsed as ["Divide", 1, ["Equal", 2, x]]. The fact that the second argument of the "Divide" function is a boolean and not a number will not be detected until the definition for "Divide" has been located.

Name binding is done lazily, not upon boxing. To force the binding to occur, request the canonical version of the expression.

To check if an expression includes an ["Error"] subexpression check the expr.isValid property.

To get the list of all the ["Error"] subexpression use the expr.errors property.

Error Code Meaning
syntax-error the parsing could not continue
missing an expression was expected
unexpected-argument too many arguments provided
expected-argument not enough arguments provided
expected-expression an expression was expected inside an enclosure (parentheses)
unexpected-command the command is unknown, or not applicable in the current parsing context
unexpected-token the character does not apply to the current parsing context
incompatible-type the type of the provided argument does not match the expected type
invalid-symbol the symbol cannot be used (see MathJSON Symbols)
expected-closing-delimiter a closing } was expected, but is missing
unexpected-closing-delimiter a closing } was encountered, but not expected
expected-environment-name the name of an environment should be provided with a \begin or \end command
unknown-environment the environment name provided cannot be parsed
unbalanced-environment the named used with the \begin and \end commands should match
unexpected-operator the operator does not apply to the current parsing context. Could be an infix or postfix operator without a rhs.
unexpected-digit the string included some characters outside of the range of expected digits
expected-string-argument the argument was expected to be a string
unexpected-base the base is outside of the expected range (2..36)
iteration-limit-exceeded a loop has reached the maximum iteration limit
console.log(ce.parse("\\oops").json);
// ➔ ["Error", ["ErrorCode","'unexpected-command'","'\\oops'"], ["Latex","'\\oops'"]

console.log(ce.parse("\\oops{bar}+2").json);
// ➔  ["Add",
//        ["Error",
//          ["ErrorCode","'unexpected-command'","'\\oops'"],
//          ["Latex","'\\oops{bar}'"]
//        ],
//        2
//    ]

console.log(ce.parse("\\begin{oops}\\end{oops}").json);
// ➔ ["Error",["ErrorCode","'unknown-environment'",""oops""],["Latex","'\\\\begin{oops}\\\\end{oops}'"]

console.log(ce.parse("1+\\sqrt").json);
// ➔ ["Add", 1 ,["Sqrt", ["Error", ""missing""]]]

console.log(ce.parse("1+\\frac{2}").json);
// ➔ ["Add", 1, ["Divide", 2, ["Error",""missing""]]]

console.log(ce.parse("1+(2=2)+2").json);
// ➔ ["Add", 1, ["Delimiter", ["Equal", 2, 2]]]

console.log(ce.parse("1+(2=2)+3").canonical.json);
// ➔ ["Add",
//      1,
//      ["Error",
//          ["ErrorCode", "'incompatible-domain'", "Numbers", "Booleans"],
//          ["Delimiter", ["Equal", 2, 2]]
//      ],
//      3
//    ]

console.log(ce.parse("\\times 3").json);
// ➔ ["Sequence", ["Error", ["ErrorCode", "'unexpected-operator'", "'\\times'"], ["Latex","'\\times'"]], 3]

console.log(ce.parse("x__+1").json);
// ➔ ["Add", ["Subscript", "x", ["Error","'syntax-error'", ["Latex","'_'"]]], 1]

console.log(ce.parse("x_{a").json);
// ➔ ["Subscript", "x", ["Error", "'expected-closing-delimiter'", ["Latex","'{a'"]]]


console.log(ce.parse("x@2").json);
// ➔ ["Sequence", "x", ["Error", ["ErrorCode", "'unexpected-token'", "'@'"], ["Latex", "'@2'"]]]

title: Collections slug: /compute-engine/reference/collections/ date: Last Modified

In the Compute Engine, **collections** are used to represent data structures. They group together multiple elements into one unit. Each element in a collection is a [**Boxed Expression**](/compute-engine/guides/expressions/).

Collections are immutable. They cannot be modified. Operations on collections return new collections.

A ["List"] expression can represent an heterogeneous collection of elements.

["List", 42, 3.14, "x", "y"]

List of numbers can be used to represent vectors.

["List", 1, 2, 3]

A matrix is represented using a List of List of numbers.

["List", ["List", 1, 2, 3], ["List", 4, 5, 6], ["List", 7, 8, 9]]

Lists of lists can also be represented using a ; separator:

And matrixes can be represented using LaTeX environments:

See also the **Linear Algebra** section for operations on vectors, matrixes and tensors.

Another common collection is the Range which is used to represent a sequence of numbers from a lower bound to an upper bound. Both lower and upper bounds are included in the range.

["Range", 1, 10]

Collections operations such as IsEmpty, Extract, IndexOf can be applied to any collection types.

["Extract", ["List", 2, 5, 7], 2]
// ➔ ["List", 5]
["Extract", ["Range", 2, 10], 5]
// ➔ ["List", 7]

Creating Collections

This section contains functions that return a collection, but whose arguments are not collections. They are used to create collections.

x-1, ...x-2

A List is an ordered, indexable collection of elements. An element in a list may be repeated.

The visual presentation of a List expression can be customized using the Delimiter function.

ce.box(["List", 5, 2, 10, 18]).latex;
// ➔ "\lbrack 5, 2, 10, 18 \rbrack"

ce.box(["Delimiter", ["List", 5, 2, 10, 18], "<;>"]).latex;
// ➔ "\langle5; 2; 10; 18\rangle"
MathJSON LaTeX
["List", "x", "y", 7, 11] $$ \lbrack x, y, 7, 11\rbrack $$
["List", "x", "Nothing", "y"] $$ \lbrack x,,y\rbrack $$

upper

lower, upper

lower, upper, step

A sequence of numbers, starting with lower, ending with upper, and incrementing by step.

If the step is not specified, it is assumed to be 1.

If there is a single argument, it is assumed to be the upper bound, and the lower bound is assumed to be 1.

["Range", 3, 9]
// ➔ ["List", 3, 4, 5, 6, 7, 8, 9]

["Range", 7]
// ➔ ["List", 1, 2, 3, 4, 5, 6, 7]

["Range", 1, 10, 2]
// ➔ ["List", 1, 3, 5, 7, 9]

["Range", 10, 1, -1]
// ➔ ["List", 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

upper

lower, upper

lower, upper, count

Short for "linearly spaced", from the (MATLAB function of the same name)[mathworks.com/help/matlab/ref/linspace.html].

A sequence of numbers. Similar to Range but the number of elements in the collection is specified with count instead of a step value.

If the count is not specified, it is assumed to be 50.

If there is a single argument, it is assumed to be the upper bound, and the lower bound is assumed to be 1.

["Linspace", 3, 9]
// ➔ ["List", 3, 3.163265306122449, 3.326530612244898, 3.489795918367347, 3.653061224489796, 3.816326530612245, 3.979591836734694, 4.142857142857143, 4.3061224489795915, 4.469387755102041, 4.63265306122449, 4.795918367346939, 4.959183673469388, 5.122448979591837, 5.285714285714286, 5.448979591836735, 5.612244897959184, 5.775510204081633, 5.938775510204081, 6.1020408163265305, 6.26530612244898, 6.428571428571429, 6.591836734693878, 6.755102040816326, 6.918367346938775, 7.081632653061225, 7.244897959183673, 7.408163265306122, 7.571428571428571, 7.73469387755102, 7.8979591836734695, 8.061224489795919, 8.224489795918368, 8.387755102040817, 8.551020408163266, 8.714285714285714, 8.877551020408163, 9.040816326530612, 9.204081632653061, 9.36734693877551, 9.53061224489796, 9.693877551020408, 9.857142857142858, 10]

["Linspace", 7]
// ➔ ["List", 1, 1.1428571428571428, 1.2857142857142858, 1.4285714285714286, 1.5714285714285714, 1.7142857142857142, 1.8571428571428572, 2]

["Linspace", 1, 10, 5]
// ➔ ["List", 1, 3.25, 5.5, 7.75, 10]

["Linspace", 10, 1, 10]
// ➔ ["List", 10, 9.11111111111111, 8.222222222222221, 7.333333333333333, 6.444444444444445, 5.555555555555555, 4.666666666666666, 3.7777777777777777, 2.888888888888889, 2]

expr-1, ...expr-2

An unordered collection of unique elements.

["Set", 12, 15, 17]

dimensions, value

dimensions, function

Create a list of the specified dimensions.

If a value is provided, the elements of the list are all set to that value.

If a function is provided, the elements of the list are computed by applying the function to the index of the element.

If dimensions is a number, a list of that length is created.

["Fill", 3, 0]
// ➔ ["List", 0, 0, 0]

If dimension is a tuple, a matrix of the specified dimensions is created.

["Fill", ["Tuple", 2, 3], 0]
// ➔ ["List", ["List", 0, 0, 0], ["List", 0, 0, 0]]

If a function is specified, it is applied to the index of the element to compute the value of the element.

["Fill", ["Tuple", 2, 3], ["Function", ["Add", "i", "j"], "i", "j"]]
// ➔ ["List", ["List", 0, 1, 2], ["List", 1, 2, 3]]

expr

An infinite collection of the same element.

["Repeat", 0]
// ➔ ["List", 0, 0, 0, ...]

Use Take to get a finite number of elements.

["Take", ["Repeat", 42], 3]
// ➔ ["List", 42, 42, 42]

Note: ["Repeat", n] is equivalent to ["Cycle", ["List", n]].

seed

A collection that repeats the elements of the seed collection. The input collection must be finite.

["Cycle", ["List", 5, 7, 2]]
// ➔ ["List", 5, 7, 2, 5, 7, 2, 5, 7, ...]

["Cycle", ["Range", 3]]
// ➔ ["List", 1, 2, 3, 1, 2, 3, 1, 2, ...]

Use Take to get a finite number of elements.

["Take", ["Cycle", ["List", 5, 7, 2]], 5]
// ➔ ["List", 5, 7, 2, 5, 7]

function

function, initial

An infinite collection of the results of applying function to the initial value.

If the initial value is not specified, it is assumed to be 0

["Iterate", ["Function", ["Multiply", "_", 2]], 1]


// ➔ ["List", 1, 2, 4, 8, 16, ...]

Use Take to get a finite number of elements.

["Take", ["Iterate", ["Function", ["Add", "_", 2]], 7], 5]
// ➔ ["List", 7, 9, 11, 13, 15]

Transforming Collections

This section contains functions whose argument is a collection and which return a collection made of a subset of the elements of the input.

collection, n

Return a list without the first n elements.

If n is negative, it returns a list without the last n elements.

["Drop", ["List", 5, 2, 10, 18], 2]
// ➔ ["List", 10, 18]

["Drop", ["List", 5, 2], -2]
// ➔ ["List", 5, 2]

collection, index

collection, index1, index2

collection, range

Exclude is the opposite of Extract. It returns a list of the elements that are not at the specified indexes.

The elements are returned in the same order as they appear in the collection.

["Exclude", ["List", 5, 2, 10, 18], 2]
// ➔ ["List", 5, 10, 18]

["Exclude", ["List", 5, 2, 10, 18], -2, 1]
// ➔ ["List", 2, 18]

["Exclude", ["List", 5, 2, 10, 18], ["Range", 2, 3]]
// ➔ ["List", 5, 2]

["Exclude", ["List", 5, 2, 10, 18], ["Range", 1, -1, 2]]

// ➔ ["List", 2, 18]

An index may be repeated, but the corresponding element will only be dropped once.

["Exclude", ["List", 5, 2, 10, 18], 3, 3, 1]
// ➔ ["List", 2, 18]

collection, index

collection, index1, index2

collection, range

Returns a list of the elements at the specified indexes.

Extract is a flexible function that can be used to extract a single element, a range of elements, or a list of elements.

Extract always return a list, even if the result is a single element. If no elements match, an empty list is returned.

["Extract", ["List", 5, 2, 10, 18], 2]
// ➔ ["List", 10]

["Extract", ["List", 5, 2, 10, 18], -2, 1]
// ➔ ["List", 10, 5]


["Extract", ["List", 5, 2, 10, 18], 17]
// ➔ ["List"]

When using a range, it is specified as a Range expression.

// Elements 2 to 3
["Extract", ["List", 5, 2, 10, 18], ["Range", 2, 4]]
// ➔ ["List", 2, 10, 18]

// From start to end, every other element
["Extract", ["List", 5, 2, 10, 18], ["Range", 1, -1, 2]]
// ➔ ["List", 5, 10]

The elements are returned in the order in which they're specified. Using negative indexes (or ranges) reverses the order of the elements.

// From last to first = reverse
["Extract", ["List", 5, 2, 10, 18], ["Range", -1, 1]]
// ➔ ["List", 18, 10, 2, 5]

// From last to first = reverse
["Extract", ""desserts"", ["Range", -1, 1]]
// ➔ ""stressed""

An index can be repeated to extract the same element multiple times.

["Extract", ["List", 5, 2, 10, 18], 3, 3, 1]
// ➔ ["List", 10, 10, 5]

collection

Return the first element of the collection.

It's equivalent to ["Take", _collection_, 1].

["First", ["List", 5, 2, 10, 18]]
// ➔ 5

["First", ["Tuple", "x", "y"]]
// ➔ "x"

collection-1, collection-2, ...

Returns a collection that contains the elements of the first collection followed by the elements of the second collection.

All the collections should have the same head.

["Join", ["List", 5, 2, 10, 18], ["List", 1, 2, 3]]
// ➔ ["List", 5, 2, 10, 18, 1, 2, 3]

collection, n

Return a list of the first n elements of the collection.

If n is negative, it returns the last n elements.

["Take", ["List", 5, 2, 10, 18], 2]
// ➔ ["List", 5, 2]

["Take", ["List", 5, 2, 10, 18], -2]
// ➔ ["List", 18, 10]

collection

Return the last element of the collection.

["Last", ["List", 5, 2, 10, 18]]
// ➔ 18

collection, n

Return the last n elements of the collection.

["Last", ["List", 5, 2, 10, 18], 2]
// ➔ ["List", 10, 18]

collection

Return everything but the last element of the collection.

It's equivalent to ["Drop", _collection_, -1].

["Most", ["List", 5, 2, 10, 18]]
// ➔ ["List", 5, 2, 10]

collection

Return everything but the first element of the collection.

It's equivalent to ["Drop", _collection_, 1].

["Rest", ["List", 5, 2, 10, 18]]
// ➔ ["List", 2, 10, 18]

collection

Return the collection in reverse order.

["Reverse", ["List", 5, 2, 10, 18]]
// ➔ ["List", 18, 10, 2, 5]

It's equivalent to ["Extract", _collection_, ["Tuple", -1, 1]].

collection, count

Returns a collection where the elements are rotated to the left by the specified count.

["RotateLeft", ["List", 5, 2, 10, 18], 2]
// ➔ ["List", 10, 18, 5, 2]

collection, count

Returns a collection where the elements are rotated to the right by the specified count.

["RotateRight", ["List", 5, 2, 10, 18], 2]
// ➔ ["List", 10, 18, 5, 2]

collection

Return the second element of the collection.

["Second", ["Tuple", "x", "y"]]
// ➔ "y"

collection

Return the collection in random order.

["Shuffle", ["List", 5, 2, 10, 18]]
// ➔ ["List", 10, 18, 5, 5]

collection

collection, order-function

Return the collection in sorted order.

["Sort", ["List", 5, 2, 10, 18]]
// ➔ ["List", 2, 5, 10, 18]

collection

Returns a list of the elements in collection without duplicates.

This is equivalent to the first element of the result of Tally: ["First", ["Tally", _collection_]].

["Unique", ["List", 5, 2, 10, 18, 5, 2, 5]]
// ➔ ["List", 5, 2, 10, 18]

Operating On Collections

The section contains functions whose argument is a collection, but whose return value is not a collection.

collection, index

collection, index1, index2, ...

Returns the element at the specified index.

There can be multiple indexes, up to the rank of the collection.

["At", ["List", 5, 2, 10, 18], 2]
// ➔ 10

["At", ["List", 5, 2, 10, 18], -2]
// ➔ 10

["At", ["List", ["List", 1, 2], ["List", 3, 4]], 2, 1]
// ➔ 3

collection, function

Returns a collection where function is applied to each element of the collection. Only the elements for which the function returns "True" are kept.

["Filter", ["List", 5, 2, 10, 18], ["Function", ["Less", "_", 10]]]
// ➔ ["List", 5, 2]

collection, fn

collection, fn, initial

Returns a collection where the reducing function fn is applied to each element of the collection.

Fold performs a left fold operation: the reducing function is applied to the first two elements, then to the result of the previous application and the next element, etc...

When an initial value is provided, the reducing function is applied to the initial value and the first element of the collection, then to the result of the previous application and the next element, etc...

[
  "Fold",
  ["List", 5, 2, 10, 18]
  ["Function", ["Add", "_1", "_2"]],
]
// ➔ 35

The name of a function can be used as a shortcut for a function that takes two arguments.

["Fold", ["List", 5, 2, 10, 18], "Add"]
// ➔ 35
See also the **`FixedPoint` function** which operates without a collection.

collection

Returns the number of elements in the collection.

When the collection is a matrix (list of lists), Length returns the number of rows.

["Length", ["List", 5, 2, 10, 18]]
// ➔ 4

When the collection is a string, Length returns the number of characters in the string.

["Length", { "str": "Hello" }]
// ➔ 5

collection

Returns the symbol True if the collection is empty.

["IsEmpty", ["List", 5, 2, 10, 18]]
// ➔ "False"

["IsEmpty", ["List"]]
// ➔ "True"

["IsEmpty", "x"]
// ➔ "True"


["IsEmpty", {str: "Hello"]
// ➔ "False"

collection, function

Returns a collection where function is applied to each element of the input collection.

["Map", ["Function", ["Add", "x", 1], "x"], ["List", 5, 2, 10, 18]]
// ➔ ["List", 6, 3, 11, 19]
["Map", ["List", 5, 2, 10, 18], ["Function", ["Add", "_", 1]]]
// ➔ ["List", 6, 3, 11, 19]

collection

collection, order-function

Return the indexes of the collection in sorted order.

["Ordering", ["List", 5, 2, 10, 18]]
// ➔ ["List", 2, 1, 3, 4]

To get the values in sorted order, use Extract:

["Assign", "l", ["List", 5, 2, 10, 18]]
["Extract", "l", ["Ordering", "l"]]
// ➔ ["List", 2, 5, 10, 18]

// Same as Sort:
["Sort", "l"]
// ➔ ["List", 2, 5, 10, 18]

collection

Returns a tuples of two lists:

  • The first list contains the unique elements of the collection.
  • The second list contains the number of times each element appears in the collection.
["Tally", ["List", 5, 2, 10, 18, 5, 2, 5]]
// ➔ ["Tuple", ["List", 5, 2, 10, 18], ["List", 3, 2, 1, 1]]

collection-1, collection-2, ...

Returns a collection of tuples where the first element of each tuple is the first element of the first collection, the second element of each tuple is the second element of the second collection, etc.

The length of the resulting collection is the length of the shortest collection.

["Zip", ["List", 1, 2, 3], ["List", 4, 5, 6]]
// ➔ ["List", ["Tuple", 1, 4], ["Tuple", 2, 5], ["Tuple", 3, 6]]
--- title: Compute Engine API Reference sidebar_label: API Reference slug: /compute-engine/api/ toc_max_heading_level: 3 --- import APIFilter from '@site/src/components/APIFilter'; import MemberCard from '@site/src/components/MemberCard';

Compute Engine API Reference


title: Control Structures slug: /compute-engine/reference/control-structures/

Control Structures define how a sequence of expressions is evaluated.

Overview

The flow of a program is controlled by control structures. Control structures are expressions that define how a sequence of expressions is evaluated.

There are three kind of control structures:

  • Sequential: Block, the most common where expressions are evaluated one after the other
  • Conditional If or Which, where expressions are evaluated depending on the value of a condition
  • Iterative Loop or FixedPoint, where expressions are evaluated repeatedly

Sequential Control Structure

expr-1, ...expr-n

A ["Block"] expression is a sequence of expressions that are evaluated sequentially.

A new scope is created for the ["Block"] expression. The scope is destroyed when the ["Block"] expression is finished evaluating. This means that variables defined in the ["Block"] expression are not accessible outside of the ["Block"] expression.

The value of the ["Block"] expression is the value of the last expression expr-n.

If one of the expression in the block is a ["Return"] expression, a ["Break"] expression or a ["Continue"] expression, no more expressions are evaluated and the value of the ["Block"] is this expression.

["Block"] expressions can be nested as necessary.

["Block", ["Assign", "c", 5], ["Multiply", "c", 2]]


// ➔ 10

Conditional Control Structure

condition, expr-1

If the value of conditionis the symbol True, the value of the ["If"] expression is expr-1, otherwise Nothing.

condition, expr-1, expr-2

If the value of conditionis the symbol True, the value of the ["If"] expression is expr-1, otherwise expr-2.

Here's an example of a function that returns the absoluve value of a number:

["Function", ["If", ["Greater", "n", 0], "n", ["Negate", "n"]], "n"]

["If"] expressions can be nested as necessary.

condition-1, expr-1, ...condition-n, expr-n

The value of the ["Which"] expression is the value of the first expression expr-n for which the corresponding condition condition-n is True.

["Block",
  ["Assign", "n", -10]
  ["Which", ["Greater", "n", 0], "n", ["Negate", "n"], "n"]
]
// ➔ 10

A ["Which"] expression is equivalent to the following ["If"] expression:

["If", ["Equal", condition-1, "True"], expr-1,
    ["If", ["Equal", condition-2, "True"], _expr-2,
    ... ["If", ["Equal", condition-n, "True"],
          expr-n,
          "Nothing"
    ]
  ]
]

A ["Which"] expression is equivalent to a switch statement in JavaScript or the Which[] function in Mathematica.

Loops

body

Repeatedly evaluate bodyuntil the value of bodyis a ["Break"] expression, or a ["Return"] expression.

  • ["Break"] exits the loop immediately. The value of the ["Loop"] expression is the value of the ["Break"] expression.
  • ["Return"] exits the loop and returns the value of the ["Return"] expression.

To exit the loop, a ["Break"] or ["Return"] expression must be evaluated.

Loop with only a body argument is equivalent to a while(true) in JavaScript or a While[True, ...] in Mathematica.

body, collection

Iterates over the elements of collection and evaluates body with an implicit argument _ whose value is the current element. The value of the ["Loop"] expression is the value of the last iteration of the loop, or the value of the ["Break"] expression if the loop was exited with a ["Break"] expression.

["Loop", ["Print", ["Square", "_"]], ["Range", 5]]
// ➔ 1 4 9 16 25
["Loop", ["Function", ["Print", ["Square", "x"], "x"]], ["Range", 5]]
// ➔ 1 4 9 16 25

Loop with a body and collection to iterate is equivalent to a forEach() in JavaScript. It is somewhat similar to a Do[...] in Mathematica.

body, initial-value

body, initial-value, max-iterations

Assumes bodyis an expression using an implicit argument _.

Apply bodyto initial-value, then apply bodyto the result until the result no longer changes.

To determine if a fixed point has been reached and the loop should terminate, the previous and current values are compared with Equal.

Inside body, use a ["Break"] expression to exit the loop immediately or Return to exit the enclosing ["Function"] expression.

See also the **`Fold` function** which operates on a collection Read more about the `Product` and `Sum` functions which are specialized version of loops. Read more about operations on collection such as `Map` and `Fold` which are functional programming constructs that can be used to replace loops.

Controlling the Flow of Execution

To exit a function, use Return.

To control the flow of a loop expression, use Break and Continue.

value

Interupts the evaluation of a ["Function"] expression. The value of the ["Function"] expression is value.

The ["Return"] expression is useful when used with functions that have multiple exit points, conditional logic, loops, etc...

Here's a contrived example of a function that returns the sign of a number:

[
  "Function",
  [
    "Block",
    ["If", ["Greater", "x", 0], ["Return", 1]],
    ["If", ["Less", "x", 0], ["Return", -1]],
    0
  ],
  "x"
]
Read more about **functions**.

value

When in a loop exit the loop immediately. The final value of the loop is valueor Nothing if not provided.

value

When in a loop, skip to the next iteration of the loop. The value of the iteration is value or Nothing if not provided.


title: Symbols slug: /compute-engine/guides/symbols/ layout: single date: Last Modified toc: true

A **symbol** is a named object in the Compute Engine. It has a type and may hold a value. A symbol without a value represents a mathematical unknown in an expression.

To change the value or type of a symbol, use the value and type properties of the symbol.

A symbol does not have to be declared before it can be used. The type of a symbol will be inferred based on its usage or its value. If its type cannot be inferred, the type will be unknown.

const n = ce.box("n");
n.value = 5;
console.log("n =", n.value.toString(), ":", n.type);

To get a list of all the symbols in an expression use expr.symbols.

Read more about **adding definitions** for symbols and functions

Scope

Symbols are defined within a lexical scope.

Read more about **scopes**

Unknowns and Constants

A symbol that has been declared, but has no values associated with it, is said to be an unknown.

A symbol whose value cannot be changed is a constant. Constants are identified by a special flag in their definition.

To check if a symbol is a constant, use the expr.isConstant property.

console.log(ce.box("x").isConstant);
// ➔ false

console.log(ce.box("Pi").isConstant);
// ➔ true

:::warning

The value of constants may depend on settings of the Compute Engine. For example, the value of Pi is determined based on the value of the precision property. The values of constants in scope when the precision setting is changed will be updated.

:::

ce.precision = 4;
const smallPi = ce.box("Pi"); // π with 4 digits
console.log(smallPi.latex);
// ➔ 3.1415

ce.precision = 10;
const bigPi = ce.box("Pi"); // π with 10 digits
console.log(bigPi.latex);
// ➔ 3.1415926535

ce.precision = 100; // Future computations will be done with 100 digits

console.log("pi = ", smallPi, "=", bigPi);
// ➔ pi  = 3.1415 = 3.1415926535

Automatic Declaration

An unknown symbol is automatically declared when it is first used in an expression.

The symbol has a type of unknown and no value associated with it, so the symbol will be an unknown.

const symbol = ce.box("m"); // m for mystery
console.log(symbol.type);
// ➔ "unknown"

symbol.value = 5;
console.log(symbol.type);
// ➔ "finite_integer"

If the type of a symbol is inferred from its usage, the type can be adjusted later as further information is provided. However, if the type is provided in the declaration, the type cannot be changed later.

Forgetting a Symbol

To reset what is known about a symbol use the ce.forget() function.

The ce.forget() function will remove any assumptions associated with a symbol, and remove its value. Howeve, the symbol will remained declared, since other expressions may depend on it.

To forget about a specific symbol, pass the name of the symbol as an argument to ce.forget().

To forget about all the symbols in the current scope, use ce.forget() without any arguments.

:::info[Note] Note that only symbols in the current scope are forgotten. If assumptions about the symbol existed in a previous scope, those assumptions will be in effect when returning to the previous scope. :::

title: Special Functions slug: /compute-engine/reference/special-functions/

n

["Factorial", 5]
// -> 120

n

The double factorial of n: $$ n!! = n \cdot (n-2) \cdot (n-4) \times \cdots$$, that is the product of all the positive integers up to n that have the same parity (odd or even) as n.

["Factorial2", 5]
// -> 15

It can also be written in terms of the $$ \Gamma $$ function:

n ! ! = 2 n 2 + 1 4 ( 1 cos ( π n ) ) π 1 4 ( cos ( π n ) 1 ) Γ ( n 2 + 1 )

This is not the same as the factorial of the factorial of n (i.e. ( ( n ! ) ! ) ).

Reference

z

The Gamma Function is an extension of the factorial function, with its argument shifted by 1, to real and complex numbers.

The following macros are not allowed: operatorname

$$
\operatorname{\Gamma}\left(z\right) = \int\limits_{0}^{\infty} t^{z-1}
\mathrm{e}^{-t} , \mathrm{d}t
$$

["Gamma", 5]
// 24

z

This function is called gammaln in MatLab and SciPy and LogGamma in Mathematica.

See also Statistics for the Error Functions