UNPKG

@holgerengels/compute-engine

Version:

Symbolic computing and numeric evaluations for JavaScript and Node.js

1,431 lines (1,430 loc) 80.6 kB
/* 0.26.0-alpha2 */ import { Complex } from 'complex-esm'; import type { Expression, MathJsonNumber, MathJsonString, MathJsonSymbol, MathJsonFunction, MathJsonIdentifier } from '../../math-json'; import type { SerializeLatexOptions, LatexDictionaryEntry, ParseLatexOptions } from '../latex-syntax/public'; import type { IndexedLatexDictionary } from '../latex-syntax/dictionary/definitions'; import { Rational } from '../numerics/rationals'; import { ExactNumericValueData, NumericValue, NumericValueData } from '../numeric-value/public'; import { BigNum, IBigNum } from '../numerics/bignum'; import { Type, TypeString } from '../../common/type/types'; import { AbstractTensor } from '../tensor/tensors'; import { OneOf } from '../../common/one-of'; /** * :::info[THEORY OF OPERATIONS] * * To create a boxed expression: * * ### `ce.box()` and `ce.parse()` * * Use `ce.box()` or `ce.parse()` to get a canonical expression. * - the arguments are put in canonical form * - invisible operators are made explicit * - a limited number of core simplifications are applied, * for example 0 is removed from additions * - 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]` * - the arguments of commutative functions are sorted * - identifiers are **not** replaced with their values * * ### 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. * * - the operation is performed on the canonical version of the expression * * - 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 identifiers with their values, and * evaluate the expression * * ### `ce._fn()` * * Use `ce._fn()` to create a new function expression. * * This is a low level method which is typically invoked in the canonical * handler of a function 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 the function, but it only * asserts that the function and its arguments are canonical. The caller * is responsible for ensuring that is the case. * * * ### `ce.function()` * * This is a specialized version of `ce.box()`. It is used to create a new * function expression. * * The arguments are put in canonical form and the canonical handler is called. * * 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. * * ### Canonical Handlers * * Canonical handlers are responsible for: * - validating the signature (type and number of arguments) * - flattening sequences * - flattening associative functions * - sort the arguments (if the function is commutative) * - calling `ce._fn()` to create a new function expression * - if the function definition has a hold, they should also put * their arguments in canonical form, if appropriate * * When the canonical handler is invoked, the arguments have been put in * canonical form according to the `hold` flag. * * Some canonical handlers are available as separate functions and can be * used directly, for example `canonicalAdd(a, b)` instead of * `ce.function('Add', [a, b])`. * * ::: */ export type Sign = /** The expression is equal to 0 */ 'zero' /** The expression is > 0 */ | 'positive' /** The expression is < 0 */ | 'negative' /** The expression is >= 0 and isPositive is either false or undefined*/ | 'non-negative' /** The expression is <= 0 and isNegative is either false or undefined*/ | 'non-positive' /** The expression is not equal to 0 (possibly with an imaginary part) and isPositive, isNegative, isUnsigned are all false or undefined */ | 'not-zero' /** The expression has no imaginary part and a non-zero real part and isPositive and isNegative are false or undefined*/ | 'real-not-zero' /** The expression has no imaginary part and isNotZero,isPositive,isNegative,isNonNegative,isNonPositive,isZero are either false or undefined*/ | 'real' /** The expression is NaN */ | 'nan' /** The expression is +∞ */ | 'positive-infinity' /** The expression is -∞ */ | 'negative-infinity' /** The expression is ~∞ */ | 'complex-infinity' /** The expression has an imaginary part or is NaN */ | 'unsigned'; /** * :::info[THEORY OF OPERATIONS] * * The `BoxedExpression` interface includes most of the member functions * applicable to any kind of expression, for example `get symbol()` or * `get ops()`. * * When a member function is not applicable to this `BoxedExpression`, * for example `get symbol()` on a `BoxedNumber`, it returns `null`. * * This convention makes it convenient to manipulate expressions without * having to check what kind of instance they are before manipulating them. * ::: * * To get a boxed expression from a LaTeX string use `ce.parse()`, or to * get a boxed expression from a MathJSON expression use `ce.box()`. * * @category Boxed Expression * */ export interface BoxedExpression { /** The Compute Engine associated with this expression provides * a context in which to interpret it, such as definition of symbols * and functions. * */ readonly engine: IComputeEngine; /** From `Object.valueOf()`, return a primitive value for the expression. * * If the expression is a machine number, or bignum or rational that can be * converted to a machine number, return a JavaScript `number`. * * If the expression is a symbol, return the name of the symbol as a `string`. * * Otherwise return a JavaScript primitive representation of the expression. * * @category Primitive Methods */ valueOf(): number | any | string | boolean; /** From `Object.toString()`, return a string representation of the * expression. This string is suitable to be output to the console * for debugging, for example. It is formatted as a ASCIIMath expression. * * To get a LaTeX representation of the expression, use `expr.latex`. * * Used when coercing a `BoxedExpression` to a `String`. * * @category Primitive Methods */ toString(): string; /** * Output to the console a string representation of the expression. * * @category Primitive Methods */ print(): void; /** Similar to`expr.valueOf()` but includes a hint. * * @category Primitive Methods */ [Symbol.toPrimitive](hint: 'number' | 'string' | 'default'): number | string | null; /** Used by `JSON.stringify()` to serialize this object to JSON. * * Method version of `expr.json`. * * @category Primitive Methods */ toJSON(): Expression; /** Serialize to a MathJSON expression with specified options*/ toMathJson(options?: Readonly<Partial<JsonSerializationOptions>>): Expression; /** Serialize to a LaTeX string. * * Will ignore any LaTeX metadata. */ toLatex(options?: Partial<SerializeLatexOptions>): LatexString; verbatimLatex?: string; /** If `true`, this expression is in a canonical form. */ get isCanonical(): boolean; /** For internal use only, set when a canonical expression is created. * @internal */ set isCanonical(val: boolean); /** If `true`, this expression is in a structural form. */ get isStructural(): boolean; /** 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. * ::: * */ readonly json: Expression; /** * The scope in which this expression has been defined. * * Is `null` when the expression is not canonical. */ readonly scope: RuntimeScope | null; /** * 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))`. * * @category Primitive Methods * */ is(rhs: any): boolean; /** @internal */ readonly hash: number; /** 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. * ::: * */ get latex(): LatexString; /** * * :::info[Note] * Applicable to canonical and non-canonical expressions. * ::: * @internal */ set latex(val: LatexString); /** 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. * ::: * * @category Symbol Expression * */ readonly symbol: string | null; /** * @category Symbol Expression * */ readonly tensor: null | AbstractTensor<'expression'>; /** If this expression is a string, return the value of the string. * Otherwise, return `null`. * * :::info[Note] * Applicable to canonical and non-canonical expressions. * ::: * @category String Expression * */ readonly string: string | null; /** All the subexpressions matching the named operator, recursively. * * :::info[Note] * Applicable to canonical and non-canonical expressions. * ::: * */ getSubexpressions(name: string): ReadonlyArray<BoxedExpression>; /** All the subexpressions in this expression, recursively * * :::info[Note] * Applicable to canonical and non-canonical expressions. * ::: * */ readonly subexpressions: ReadonlyArray<BoxedExpression>; /** * * All the symbols in the expression, recursively * * :::info[Note] * Applicable to canonical and non-canonical expressions. * ::: * */ readonly symbols: ReadonlyArray<string>; /** * All the identifiers used in the expression that do not have a value * associated with them, i.e. they are declared but not defined. */ readonly unknowns: ReadonlyArray<string>; /** * * All the identifiers (symbols and functions) in the expression that are * not a local variable or a parameter of that function. * */ readonly freeVariables: ReadonlyArray<string>; /** 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. * ::: * */ readonly errors: ReadonlyArray<BoxedExpression>; /** `true` 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. * ::: * * @category Symbol Expression * */ readonly isValid: boolean; /** * 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. * */ readonly operator: string; /** 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. * ::: * * @category Function Expression * */ readonly ops: null | ReadonlyArray<BoxedExpression>; /** 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. * ::: * * @category Function Expression * */ readonly nops: number; /** 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. * ::: * * @category Function Expression * * */ readonly op1: 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. * ::: * * @category Function Expression * * */ readonly op2: 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. * ::: * * @category Function Expression * * */ readonly op3: BoxedExpression; /** If true, the value of the expression never changes and evaluating it has * no side-effects. * * If false, the value of the expression may change, if the * value of other expression changes or for other reasons. * * If `this.isPure` is `false`, `this.value` is undefined. Call * `this.evaluate()` to determine the value of the expression instead. * * As an example, the `Random` function is not pure. * * :::info[Note] * Applicable to canonical and non-canonical expressions. * ::: */ readonly isPure: boolean; /** * True if the the value of the expression does not depend on the value of * any other expression. * * For example, a number literal, a symbol with a constant value. * - `2` is constant * - `Pi` is constant * - `["Add", "Pi", 2]` is constant * - `x` is not constant * - `["Add", "x", 2]` is not constant */ readonly isConstant: boolean; /** * Return the canonical form of this expression. * * If this is a function expression, a definition is associated with the * canonical expression. * * When determining the canonical form the following function 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`. * */ get canonical(): BoxedExpression; /** * 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`. * */ get structural(): 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. * ::: * */ subs(sub: Substitution, options?: { canonical?: CanonicalOptions; }): 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 }` */ map(fn: (expr: BoxedExpression) => BoxedExpression, options?: { canonical: CanonicalOptions; recursive?: boolean; }): 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. * ::: */ replace(rules: BoxedRuleSet | Rule | Rule[], options?: Partial<ReplaceOptions>): null | BoxedExpression; /** * True if the expression includes a symbol `v` or a function operator `v`. * * :::info[Note] * Applicable to canonical and non-canonical expressions. * ::: */ has(v: string | string[]): boolean; /** Structural/symbolic equality (weak equality). * * `ce.parse('1+x').isSame(ce.parse('x+1'))` is `false`. * * See `expr.isEqual()` for mathematical equality. * * :::info[Note] * Applicable to canonical and non-canonical expressions. * ::: * * @category Relational Operator */ isSame(rhs: BoxedExpression): boolean; /** * Return this expression expressed as a numerator and denominator. */ get numerator(): BoxedExpression; get denominator(): BoxedExpression; get numeratorDenominator(): [BoxedExpression, BoxedExpression]; /** * If this expression matches `pattern`, return a substitution that makes * `pattern` equal to `this`. Otherwise return `null`. * * If `pattern` includes wildcards (identifiers 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**](/compute-engine/guides/patterns-and-rules/). * * :::info[Note] * Applicable to canonical and non-canonical expressions. * ::: * */ match(pattern: BoxedExpression, options?: PatternMatchOptions): BoxedSubstitution | null; /** * "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). * * @category Numeric Expression * */ readonly isNaN: boolean | undefined; /** * The numeric value of this expression is `±Infinity` or Complex Infinity * * @category Numeric Expression */ readonly isInfinity: boolean | undefined; /** This expression is a number, but not `±Infinity`, 'ComplexInfinity` or * `NaN` * * @category Numeric Expression */ readonly isFinite: boolean | undefined; /** * @category Numeric Expression */ readonly isEven: boolean | undefined; /** * @category Numeric Expression */ readonly isOdd: boolean | undefined; /** * Return the value of this expression, if a number literal. * * Note it is possible for `this.numericValue` to be `null`, and for * `this.isNotZero` to be true. For example, when a symbol has been * defined with an assumption. * * Conversely, `this.isNumber` may be true even if `numericValue` is `null`, * example the symbol `Pi` return `true` for `isNumber` but `numericValue` is * `null`. Its value can be accessed with `.N().numericValue`. * * To check if an expression is a number literal, use `this.isNumberLiteral`. * If `this.isNumberLiteral` is `true`, `this.numericValue` is not `null` * * @category Numeric Expression * */ readonly numericValue: number | NumericValue | null; /** * Return `true` if this expression is a number literal, for example * `2`, `3.14`, `1/2`, `√2` etc. * * This is equivalent to checking if `this.numericValue` is not `null`. * * @category Numeric Expression * */ readonly isNumberLiteral: boolean; /** * Return `true` if this expression is a function expression. * * If `true`, `this.ops` is not `null`, and `this.operator` is the name * of the function. */ readonly isFunctionExpression: boolean; /** * If this expression is a number literal or a symbol with a value that * is a number literal, return the real part of the value. * * If the expression is not a number literal, or a symbol with a value * that is a number literal, return `NaN` (not a number). * * @category Numeric Expression */ readonly re: number; /** * If this expression is a number literal or a symbol with a value that * is a number literal, return the imaginary part of the value. If the value * is a real number, the imaginary part is 0. * * If the expression is not a number literal, or a symbol with a value * that is a number literal, return `NaN` (not a number). * * @category Numeric Expression */ readonly im: number; /** * If this expression is a number literal or a symbol with a value that * is a number literal, 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 * `this.bignumRe ?? this.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 literal or a symbol with a value that is a * number literal. * * @category Numeric Expression * */ readonly bignumRe: BigNum | undefined; /** * If this expression is a number literal, return the imaginary part as a * `BigNum`. * * It may be 0 if the number is real. * * If the expression is not a number literal 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 * `this.bignumIm ?? this.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 literal or a symbol with a value that is a * number literal. * * @category Numeric Expression */ readonly bignumIm: BigNum | undefined; /** * 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). * * For example: * * ['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']]] */ toNumericValue(): [NumericValue, BoxedExpression]; neg(): BoxedExpression; inv(): BoxedExpression; abs(): BoxedExpression; add(rhs: number | BoxedExpression): BoxedExpression; sub(rhs: BoxedExpression): BoxedExpression; mul(rhs: NumericValue | number | BoxedExpression): BoxedExpression; div(rhs: number | BoxedExpression): BoxedExpression; pow(exp: number | BoxedExpression): BoxedExpression; root(exp: number | BoxedExpression): BoxedExpression; sqrt(): BoxedExpression; ln(base?: number | BoxedExpression): BoxedExpression; /** * * The shape describes the axis 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]`. */ readonly shape: number[]; /** 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` */ readonly rank: number; /** * 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()`). * * @category Numeric Expression * */ readonly sgn: Sign | undefined; /** If the expressions cannot be compared, return `undefined` * * The numeric value of both expressions are compared. * * The expressions are evaluated before being compared, which may be * expensive. * * @category Relational Operator */ isLess(other: number | BoxedExpression): boolean | undefined; /** * The numeric value of both expressions are compared. * @category Relational Operator */ isLessEqual(other: number | BoxedExpression): boolean | undefined; /** * The numeric value of both expressions are compared. * @category Relational Operator */ isGreater(other: number | BoxedExpression): boolean | undefined; /** * The numeric value of both expressions are compared. * @category Relational Operator */ isGreaterEqual(other: number | BoxedExpression): boolean | undefined; /** The numeric value of this expression is > 0, same as `isGreater(0)` * * @category Numeric Expression */ readonly isPositive: boolean | undefined; /** The numeric value of this expression is >= 0, same as `isGreaterEqual(0)` * * @category Numeric Expression */ readonly isNonNegative: boolean | undefined; /** The numeric value of this expression is < 0, same as `isLess(0)` * * @category Numeric Expression */ readonly isNegative: boolean | undefined; /** The numeric value of this expression is &lt;= 0, same as `isLessEqual(0)` * * @category Numeric Expression */ readonly isNonPositive: boolean | undefined; /** Wikidata identifier. * * :::info[Note] * `undefined` if not a canonical expression. * ::: */ readonly wikidata: string | undefined; /** An optional short description if a symbol or function expression. * * May include markdown. Each string is a paragraph. * * :::info[Note] * `undefined` if not a canonical expression. * ::: * */ readonly description: undefined | string[]; /** An optional URL pointing to more information about the symbol or * function operator. * * :::info[Note] * `undefined` if not a canonical expression. * ::: * */ readonly url: string | undefined; /** Expressions with a higher complexity score are sorted * first in commutative functions * * :::info[Note] * `undefined` if not a canonical expression. * ::: */ readonly complexity: number | undefined; /** * For symbols and functions, a definition associated with the * expression. `this.baseDefinition` is the base class of symbol and function * definition. * * :::info[Note] * `undefined` if not a canonical expression. * ::: * */ readonly baseDefinition: BoxedBaseDefinition | undefined; /** * For functions, a definition associated with the expression. * * :::info[Note] * `undefined` if not a canonical expression or not a function. * ::: * */ readonly functionDefinition: BoxedFunctionDefinition | undefined; /** * For symbols, a definition associated with the expression. * * Return `undefined` if not a symbol * */ readonly symbolDefinition: BoxedSymbolDefinition | undefined; /** * * Infer the type of this expression. * * If the type of this expression is already known, return `false`. * * If the type was not set, set it to the inferred type, return `true` * If the type was previously inferred, adjust it by widening it, * return `true` * * @internal */ infer(t: Type): boolean; /** * Update the definition associated with this expression, using the * current scope (`ce.context`). * * @internal */ bind(): void; /** * * Reset the cached value associated with this expression. * * Use when the environment has changed, for example the numeric mode * or precision, to force the expression to be re-evaluated. * * @internal */ reset(): void; /** * 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()`. * */ simplify(options?: Partial<SimplifyOptions>): BoxedExpression; /** * Return the value of the canonical form of this expression. * * A pure expression always return the same value and has no side effects. * If `expr.isPure` is `true`, `expr.value` and `expr.evaluate()` are * synonyms. * * For an impure expression, `expr.value` is undefined. * * Evaluating an impure expression may have some side effects, for * example modifying the `ComputeEngine` environment, such as its set of * assumptions. * * The result may be a rational number or the product of a rational number * and the square root of an integer. * * To perform approximate calculations, use `expr.N()` instead, * or set `options.numericApproximation` to `true`. * * The result of `expr.evaluate()` may be the same as `expr.simplify()`. * * The result is in canonical form. * */ evaluate(options?: EvaluateOptions): 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. */ N(): BoxedExpression; /** * 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. * * * ```javascript * const expr = ce.parse('x^2 + y^2'); * const f = expr.compile('javascript'); * console.log(f({x: 2, y: 3})); * ``` */ compile(to?: 'javascript', options?: { optimize: ('simplify' | 'evaluate')[]; }): ((args: Record<string, any>) => any | undefined) | undefined; /** * If this is an equation, solve the equation for the variables in vars. * Otherwise, solve the equation `this = 0` for the variables in vars. * * * ```javascript * const expr = ce.parse('x^2 + 2*x + 1 = 0'); * console.log(expr.solve('x')); * ``` * * */ solve(vars: Iterable<string> | string | BoxedExpression | Iterable<BoxedExpression>): null | ReadonlyArray<BoxedExpression>; /** * Return a JavaScript primitive representing the value of this expression. * * Equivalent to `expr.N().valueOf()`. * */ get value(): number | boolean | string | object | undefined; /** * Only the value of variables can be changed (symbols that are not * constants). * * Throws a runtime error if a constant. * * :::info[Note] * If non-canonical, does nothing * ::: * */ set value(value: boolean | string | BigNum | { re: number; im: number; } | { num: number; denom: number; } | number[] | BoxedExpression | number | undefined); /** * * The type of the value of this expression. * * If a function expression, the type of the value of the function * (the result type). * * If a symbol the type of the value of the symbol. * * :::info[Note] * If not valid, return `"error"`. * If non-canonical, return `undefined`. * If the type is not known, return `"unknown"`. * ::: * */ get type(): Type; set type(type: Type); /** `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`. * * @category Type Properties */ readonly isNumber: boolean | undefined; /** * * The value of this expression is an element of the set ℤ: ...,-2, -1, 0, 1, 2... * * Note that ±∞ and NaN are not integers. * * @category Type Properties * */ readonly isInteger: boolean | undefined; /** 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. * * @category Type Properties * */ readonly isRational: boolean | undefined; /** * 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. * * @category Type Properties */ readonly isReal: boolean | undefined; /** 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. * * The evaluations may be expensive operations. Other options to consider * to compare two expressions include: * - `expr.isSame(other)` for a structural comparison * - `expr.is(other)` for a comparison of a number literal * * ## Examples * * ```js * 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) * * ``` * * @category Relational Operator */ isEqual(other: number | BoxedExpression): boolean | undefined; /** * Return true if the expression is a collection: a list, a vector, a matrix, a map, a tuple, etc... */ isCollection: boolean; /** * If this is a collection, return true if the `rhs` expression is in the * collection. * * Return `undefined` if the membership cannot be determined. */ contains(rhs: BoxedExpression): boolean | undefined; /** * If this is a collection, return the number of elements in the collection. * * If the collection is infinite, return `Infinity`. * */ get size(): number; /** * 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. * * ```js * const expr = ce.parse('[1, 2, 3, 4]'); * for (const e of expr.each()) { * console.log(e); * } * ``` */ each: (start?: number, count?: number) => Iterator<BoxedExpression, undefined>; /** 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`. * */ at(index: number): BoxedExpression | undefined; /** 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. * */ get(key: string | BoxedExpression): BoxedExpression | undefined; /** * If this is an indexable collection, return the index of the first element * that matches the target expression. */ indexOf(expr: BoxedExpression): number | undefined; } /** 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. * * @category Boxed Expression */ export type SemiBoxedExpression = number | bigint | string | BigNum | MathJsonNumber | MathJsonString | MathJsonSymbol | MathJsonFunction | readonly [MathJsonIdentifier, ...SemiBoxedExpression[]] | BoxedExpression | Complex; /** * @category Definitions * */ export interface BoxedBaseDefinition { name: string; wikidata?: string; description?: string | string[]; url?: string; /** * The scope this definition belongs to. * * This field is usually undefined, but its value is set by `getDefinition()` */ scope: RuntimeScope | undefined; collection?: Partial<CollectionHandlers>; /** When the environment changes, for example the numerical precision, * call `reset()` so that any cached values can be recalculated. */ reset(): void; } /** * 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. * * @category Definitions * */ export type EqHandlers = { eq: (a: BoxedExpression, b: BoxedExpression) => boolean | undefined; neq: (a: BoxedExpression, b: BoxedExpression) => boolean | 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. * * @category Definitions */ export type CollectionHandlers = { /** Return the number of elements in the collection. * * An empty collection has a size of 0. */ size: (collection: BoxedExpression) => number; /** * Return `true` if the target * expression is in the collection, `false` otherwise. */ contains: (collection: BoxedExpression, target: BoxedExpression) => boolean; /** 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. * * @category Definitions */ iterator: (collection: BoxedExpression, start?: number, count?: number) => Iterator<BoxedExpression, undefined>; /** * Return the element at the specified index. * * The first element is `at(1)`, the last element is `at(-1)`. * * If the index is &lt;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`. */ at: (collection: BoxedExpression, index: number | string) => undefined | BoxedExpression; /** * If the collection can be indexed by strings, return the valid values * for the index. */ keys: (collection: BoxedExpression) => undefined | Iterable<string>; /** * 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. */ indexOf: (collection: BoxedExpression, target: BoxedExpression, from?: number) => number | undefined; /** * Return `true` if all the elements 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`. */ subsetOf: (collection: BoxedExpression, target: BoxedExpression, strict: boolean) => boolean; /** Return the sign of all the elements of the collection. */ eltsgn: (collection: BoxedExpression) => Sign | undefined; /** Return the widest type of all the elements in the collection */ elttype: (collection: BoxedExpression) => Type | undefined; }; /** * A function definition can have some flags to indicate specific * properties of the function. * @category Definitions */ export type FunctionDefinitionFlags = { /** * If `true`, the arguments of the functions are held unevaluated. * * It will be up to the `evaluate()` handler to evaluate the arguments as * needed. This is conveninent to pass symbolic expressions as arguments * to functions without having to explicitly use a `Hold` expression. * * This also applies to the `canonical()` handler. * */ hold: boolean; /** If `true`, the function is applied element by element to lists, matrices * (`["List"]` or `["Tuple"]` expressions) and equations (relational * operators). * * **Default**: `false` */ threadable: boolean; /** If `true`, `["f", ["f", a], b]` simplifies to `["f", a, b]` * * **Default**: `false` */ associative: boolean; /** If `true`, `["f", a, b]` equals `["f", b, a]`. The canonical * version of the function will order the arguments. * * **Default**: `false` */ commutative: boolean; /** * 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. * */ commutativeOrder: ((a: BoxedExpression, b: BoxedExpression) => number) | undefined; /** If `true`, when the function is univariate, `["f", ["Multiply", x, c]]` * simplifies to `["Multiply", ["f", x], c]` where `c` is constant * * When the function is multivariate, multiplicativity is considered only on * the first argument: `["f", ["Multiply", x, y], z]` simplifies to * `["Multiply", ["f", x, z], ["f", y, z]]` * * Default: `false` */ /** If `true`, `["f", ["f", x]]` simplifies to `["f", x]`. * * **Default**: `false` */ idempotent: boolean; /** If `true`, `["f", ["f", x]]` simplifies to `x`. * * **Default**: `false` */ involution: boolean; /** If `true`, the value of this function is always the same for a given * set of arguments and it has no side effects. * * An expression using this function is pure if the function 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` */ pure: boolean; }; /** @category Compiling */ export type CompiledExpression = { evaluate?: (scope: { [symbol: string]: BoxedExpression; }) => number | BoxedExpression; }; /** @category Definitions */ export type Hold = 'none' | 'all' | 'first' | 'rest' | 'last' | 'most'; /** * @category Definitions * */ export type BoxedFunctionDefinition = BoxedBaseDefinition & FunctionDefinitionFlags & { complexity: number; hold: boolean; inferredSignature: boolean; signature: Type; type?: (ops: ReadonlyArray<BoxedExpression>, options: { engine: IComputeEngine; }) => Type | TypeString | undefined; sgn?: (ops: ReadonlyArray<BoxedExpression>, options: { engine: IComputeEngine; }) => Sign | undefined; eq?: (a: BoxedExpression, b: BoxedExpression) => boolean | undefined; neq?: (a: BoxedExpression, b: BoxedExpression) => boolean | undefined; canonical?: (ops: ReadonlyArray<BoxedExpression>, options: { engine: IComputeEngine; }) => BoxedExpression | null; evaluate?: (ops: ReadonlyArray<BoxedExpression>, options: EvaluateOptions & { engine: IComputeEngine; }) => BoxedExpression | undefined; evalDimension?: (ops: ReadonlyArray<BoxedExpression>, options: { engine: IComputeEngine; }) => BoxedExpression; compile?: (expr: BoxedExpressio