@holgerengels/compute-engine
Version:
Symbolic computing and numeric evaluations for JavaScript and Node.js
1,431 lines (1,430 loc) • 80.6 kB
TypeScript
/* 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 <= 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 <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