Math.js is an extensive math library for JavaScript and Node.js featuring a flexible expression parser, symbolic computation, and support for numbers, big numbers, complex numbers, fractions, units, and matrices.
84
This document covers Math.js's powerful expression parser and evaluation system, including parsing mathematical expressions, compiling for performance, working with Abstract Syntax Trees (AST), symbolic operations, and advanced expression manipulation.
import {
// Core parsing and evaluation
evaluate, parse, compile,
// Parser creation
parser,
// Symbolic operations
derivative, simplify, simplifyConstant, simplifyCore, rationalize,
resolve, leafCount,
// AST Node constructors
AccessorNode, ArrayNode, AssignmentNode, BlockNode, ConditionalNode,
ConstantNode, FunctionAssignmentNode, FunctionNode, IndexNode,
ObjectNode, OperatorNode, ParenthesisNode, RangeNode,
RelationalNode, SymbolNode,
// Utility
symbolicEqual
} from 'mathjs'evaluate(expr: string | string[], scope?: object): any{ .api }
// Simple arithmetic
evaluate('2 + 3') // 5
evaluate('2 * 3 + 4') // 10 (follows order of operations)
evaluate('sqrt(16)') // 4
// Complex expressions
evaluate('sin(pi/2) + cos(0)') // 2
evaluate('2^3 * (4 + 1)') // 40
// With variables (scope)
evaluate('a * x + b', { a: 2, b: 3, x: 4 }) // 11
// Multiple expressions
evaluate(['a = 3', 'b = 4', 'a * b']) // [3, 4, 12]
// Matrix and array operations in expressions
evaluate('[[1,2],[3,4]] * [5,6]') // Matrix-vector multiplication
evaluate('sum([1,2,3,4,5])') // 15Math.js supports a rich expression syntax:
// Arithmetic operators
evaluate('5 + 3') // Addition: 8
evaluate('5 - 3') // Subtraction: 2
evaluate('5 * 3') // Multiplication: 15
evaluate('5 / 3') // Division: 1.667...
evaluate('5 ^ 3') // Exponentiation: 125
evaluate('5 % 3') // Modulo: 2
// Comparison operators
evaluate('5 > 3') // true
evaluate('5 < 3') // false
evaluate('5 >= 5') // true
evaluate('5 == 5') // true
evaluate('5 != 3') // true
// Logical operators
evaluate('true and false') // false
evaluate('true or false') // true
evaluate('not true') // false
// Bitwise operators (with integers)
evaluate('5 & 3') // Bitwise AND: 1
evaluate('5 | 3') // Bitwise OR: 7
evaluate('5 xor 3') // Bitwise XOR: 6
evaluate('~5') // Bitwise NOT: -6
evaluate('5 << 2') // Left shift: 20
evaluate('5 >> 1') // Right shift: 2parse(expr: string, options?: ParseOptions): MathNode
parse(exprs: string[], options?: ParseOptions): MathNode[]{ .api }
interface ParseOptions {
nodes?: Record<string, MathNode>
}// Parse to Abstract Syntax Tree
const node = parse('2 * x + 1')
console.log(node.type) // 'OperatorNode'
console.log(node.op) // '+'
// Parse multiple expressions
const nodes = parse(['x = 5', 'y = x^2', 'y + 1'])
nodes.forEach(node => console.log(node.type))
// With custom nodes
const customNodes = {
myFunction: new FunctionNode('myCustomFunction', [])
}
const nodeWithCustom = parse('myFunction(x)', { nodes: customNodes })// Navigate the AST
const expr = parse('a * (b + c)')
console.log(expr.type) // 'OperatorNode'
console.log(expr.fn) // 'multiply'
console.log(expr.args.length) // 2
// First argument is 'a'
console.log(expr.args[0].type) // 'SymbolNode'
console.log(expr.args[0].name) // 'a'
// Second argument is '(b + c)'
console.log(expr.args[1].type) // 'ParenthesisNode'
console.log(expr.args[1].content.type) // 'OperatorNode'
// Evaluate AST with scope
const result = expr.evaluate({ a: 2, b: 3, c: 4 }) // 14compile(expr: string | MathNode): EvalFunction{ .api }
interface EvalFunction {
evaluate(scope?: object): any
}// Compile expression for repeated evaluation
const compiled = compile('a * x^2 + b * x + c')
// Evaluate multiple times (much faster than parsing each time)
const results = [
compiled.evaluate({ a: 1, b: 2, c: 3, x: 0 }), // 3
compiled.evaluate({ a: 1, b: 2, c: 3, x: 1 }), // 6
compiled.evaluate({ a: 1, b: 2, c: 3, x: 2 }), // 11
compiled.evaluate({ a: 1, b: 2, c: 3, x: 3 }) // 18
]
// Compile from parsed node
const node = parse('sin(x) + cos(y)')
const compiledFromNode = compile(node)
compiledFromNode.evaluate({ x: pi/2, y: 0 }) // 2
// Performance comparison
const expr = 'sqrt(x^2 + y^2)'
const compiled_expr = compile(expr)
// Fast: compile once, evaluate many times
const fastResults = points.map(({x, y}) => compiled_expr.evaluate({x, y}))
// Slow: parse and evaluate each time
const slowResults = points.map(({x, y}) => evaluate(expr, {x, y}))parser(): Parser{ .api }
interface Parser {
evaluate(expr: string): any
get(variable: string): any
getAll(): object
set(variable: string, value: any): void
remove(variable: string): void
clear(): void
}// Create parser instance with persistent scope
const mathParser = parser()
// Set variables
mathParser.set('a', 3)
mathParser.set('b', 4)
// Evaluate with persistent scope
mathParser.evaluate('c = a^2 + b^2') // 25
mathParser.evaluate('sqrt(c)') // 5
// Get variables
mathParser.get('c') // 25
mathParser.getAll() // { a: 3, b: 4, c: 25 }
// Remove and clear
mathParser.remove('c')
mathParser.clear() // Remove all variables
// Multi-line expressions
mathParser.evaluate(`
x = 5
y = x^2
z = sqrt(y)
`) // 5 (returns last expression result)SymbolNode(name: string): SymbolNode
ConstantNode(value: any): ConstantNode{ .api }
// Create symbol (variable reference)
const xSymbol = new SymbolNode('x')
xSymbol.evaluate({ x: 5 }) // 5
// Create constant
const constPi = new ConstantNode(pi)
constPi.evaluate() // π
// Create complex constants
const constComplex = new ConstantNode(complex(2, 3))
const constMatrix = new ConstantNode(matrix([[1, 2], [3, 4]]))OperatorNode(op: string, fn: string, args: MathNode[], implicit?: boolean): OperatorNode{ .api }
// Create binary operation: x + y
const addNode = new OperatorNode('+', 'add', [
new SymbolNode('x'),
new SymbolNode('y')
])
addNode.evaluate({ x: 3, y: 4 }) // 7
// Create unary operation: -x
const negNode = new OperatorNode('-', 'unaryMinus', [
new SymbolNode('x')
], false)
negNode.evaluate({ x: 5 }) // -5
// Implicit multiplication: 2x
const implicitMult = new OperatorNode('*', 'multiply', [
new ConstantNode(2),
new SymbolNode('x')
], true) // implicit = trueFunctionNode(fn: string | MathNode, args: MathNode[]): FunctionNode{ .api }
// Create function call: sin(x)
const sinNode = new FunctionNode('sin', [
new SymbolNode('x')
])
sinNode.evaluate({ x: pi/2 }) // 1
// Function with multiple arguments: pow(x, y)
const powNode = new FunctionNode('pow', [
new SymbolNode('x'),
new ConstantNode(2)
])
powNode.evaluate({ x: 3 }) // 9
// Custom function reference
const customFn = new SymbolNode('myFunction')
const customCall = new FunctionNode(customFn, [new SymbolNode('x')])AssignmentNode(object: MathNode, index?: IndexNode, value: MathNode): AssignmentNode{ .api }
// Simple assignment: x = 5
const assign = new AssignmentNode(
new SymbolNode('x'),
undefined,
new ConstantNode(5)
)
const scope = {}
assign.evaluate(scope) // 5, and scope.x = 5
// Array element assignment: arr[2] = 10
const arrAssign = new AssignmentNode(
new SymbolNode('arr'),
new IndexNode([new ConstantNode(2)]),
new ConstantNode(10)
)
arrAssign.evaluate({ arr: [1, 2, 3, 4] }) // Sets arr[2] = 10ArrayNode(items: MathNode[]): ArrayNode
ObjectNode(properties: Record<string, MathNode>): ObjectNode{ .api }
// Create array: [1, 2, x]
const arrayNode = new ArrayNode([
new ConstantNode(1),
new ConstantNode(2),
new SymbolNode('x')
])
arrayNode.evaluate({ x: 3 }) // [1, 2, 3]
// Create object: {a: 1, b: x+1}
const objectNode = new ObjectNode({
a: new ConstantNode(1),
b: new OperatorNode('+', 'add', [
new SymbolNode('x'),
new ConstantNode(1)
])
})
objectNode.evaluate({ x: 2 }) // {a: 1, b: 3}ConditionalNode(condition: MathNode, trueExpr: MathNode, falseExpr: MathNode): ConditionalNode{ .api }
// Create ternary: x > 0 ? x : -x (absolute value)
const absNode = new ConditionalNode(
new OperatorNode('>', 'larger', [
new SymbolNode('x'),
new ConstantNode(0)
]),
new SymbolNode('x'),
new OperatorNode('-', 'unaryMinus', [new SymbolNode('x')])
)
absNode.evaluate({ x: 5 }) // 5
absNode.evaluate({ x: -3 }) // 3RangeNode(start: MathNode, end: MathNode, step?: MathNode): RangeNode{ .api }
// Create range: 1:5
const rangeNode = new RangeNode(
new ConstantNode(1),
new ConstantNode(5)
)
rangeNode.evaluate() // [1, 2, 3, 4, 5]
// Range with step: 0:2:10
const stepRange = new RangeNode(
new ConstantNode(0),
new ConstantNode(10),
new ConstantNode(2)
)
stepRange.evaluate() // [0, 2, 4, 6, 8, 10]derivative(expr: MathNode | string, variable: MathNode | string, options?: DerivativeOptions): MathNode{ .api }
interface DerivativeOptions {
simplify?: boolean
}// Basic differentiation
derivative('x^2', 'x') // Returns AST for '2*x'
derivative('sin(x)', 'x') // Returns AST for 'cos(x)'
derivative('a*x + b', 'x') // Returns AST for 'a'
// Multi-variable expressions
derivative('x^2 + y^2', 'x') // Returns AST for '2*x'
derivative('x*y + y^2', 'y') // Returns AST for 'x + 2*y'
// Complex expressions
const expr = 'sin(x^2) * cos(y)'
const dfdx = derivative(expr, 'x') // 2*x*cos(x^2)*cos(y)
const dfdy = derivative(expr, 'y') // -sin(x^2)*sin(y)
// Chain rule example
derivative('sin(cos(x))', 'x') // -cos(cos(x))*sin(x)
// Product rule example
derivative('x^2 * sin(x)', 'x') // 2*x*sin(x) + x^2*cos(x)
// Evaluate derivative at point
const f_prime = derivative('x^3 - 2*x', 'x')
f_prime.evaluate({ x: 2 }) // 3*4 - 2 = 10simplify(expr: MathNode | string, rules?: MathNode[] | string[], scope?: object, options?: SimplifyOptions): MathNode{ .api }
// Basic simplification
simplify('2 + 3') // ConstantNode(5)
simplify('x + x') // OperatorNode for '2*x'
simplify('x * 1') // SymbolNode('x')
simplify('0 + x') // SymbolNode('x')
// Algebraic simplification
simplify('(x + 1)^2') // x^2 + 2*x + 1
simplify('x^2 - 1') // (x-1)*(x+1) (if factorization rules applied)
// Trigonometric simplification
simplify('sin(x)^2 + cos(x)^2') // 1
simplify('2*sin(x)*cos(x)') // sin(2*x)
// Custom rules
const rules = ['n1*n2 -> n1*n2', 'x + x -> 2*x']
simplify('a + a + b + b', rules) // 2*a + 2*b
// With scope (substitute known values)
simplify('a*x + b*x', {}, { a: 2, b: 3 }) // 5*xsimplifyConstant(expr: MathNode | string, options?: SimplifyOptions): MathNode
simplifyCore(expr: MathNode | string, options?: SimplifyOptions): MathNode{ .api }
// Simplify only constant expressions
simplifyConstant('2 + 3 * x') // 5 + 3*x (only 2+3 simplified)
simplifyConstant('sin(0) + cos(pi)') // 0 + (-1) = -1
// Core simplification (basic algebraic rules)
simplifyCore('x + 0') // x
simplifyCore('x * 1') // x
simplifyCore('x * 0') // 0
simplifyCore('x + x') // 2*xrationalize(expr: MathNode | string, optional?: object, detailed?: boolean): MathNode{ .api }
// Convert to rational form
rationalize('0.125') // 1/8
rationalize('0.333333') // 1/3 (approximately)
rationalize('sqrt(2)/2') // More rational form
// Rationalize denominators
rationalize('1/sqrt(2)') // sqrt(2)/2
rationalize('1/(1+sqrt(2))') // -1 + sqrt(2)resolve(node: MathNode, scope?: object): MathNode{ .api }
// Resolve variables in expression
const expr = parse('a * x + b')
const resolved = resolve(expr, { a: 2, b: 3 })
// Returns AST equivalent to '2 * x + 3'
resolved.evaluate({ x: 4 }) // 11leafCount(expr: MathNode | string): number{ .api }
// Count leaf nodes (complexity measure)
leafCount('x') // 1
leafCount('x + y') // 2
leafCount('x^2 + 2*x + 1') // 5 (x, 2, x, 2, 1)
// Useful for measuring expression complexity
const expr1 = 'x + 1'
const expr2 = '(x + 1)^2'
leafCount(expr1) // 2
leafCount(expr2) // 4 (after expansion)symbolicEqual(expr1: MathNode | string, expr2: MathNode | string, options?: SymbolicEqualOptions): boolean{ .api }
// Test if expressions are symbolically equivalent
symbolicEqual('x + 1', '1 + x') // true (commutative)
symbolicEqual('2*x', 'x + x') // true
symbolicEqual('(x+1)^2', 'x^2 + 2*x + 1') // true
symbolicEqual('sin(x)^2 + cos(x)^2', '1') // true
// Different but not simplified
symbolicEqual('x + 0', 'x') // true
symbolicEqual('x * 1', 'x') // true// Units are first-class citizens in expressions
evaluate('5 meter + 3 meter') // 8 meter
evaluate('10 km/h * 2 hours') // 20 km
evaluate('5 kg * 9.8 m/s^2') // 49 N (automatically converts)
// Unit conversions in expressions
evaluate('5 km to mile') // ~3.1 mile
evaluate('to(100 fahrenheit, celsius)') // ~37.8 celsius
// Complex unit calculations
evaluate('(60 mile/hour) * (2 hour) to km') // ~193 km
evaluate('force = 10 newton; area = 2 cm^2; force/area to bar') // Pressure calculation// Matrix operations in expressions
evaluate('[[1,2],[3,4]] * [[5,6],[7,8]]') // Matrix multiplication
evaluate('inv([[1,2],[3,4]])') // Matrix inverse
evaluate('det([[1,2],[3,4]])') // Determinant: -2
// Element access and modification
evaluate('A = [[1,2,3],[4,5,6]]; A[1,2]') // 5 (0-indexed)
evaluate('A[0,:] = [10,20,30]; A') // Modify first row
// Array operations
evaluate('sum([1,2,3,4,5])') // 15
evaluate('mean([1,2,3,4,5])') // 3
evaluate('std([1,2,3,4,5])') // ~1.58// Define functions within expressions
const parser_instance = parser()
// Simple function
parser_instance.evaluate('f(x) = x^2 + 1')
parser_instance.evaluate('f(3)') // 10
// Multi-parameter functions
parser_instance.evaluate('g(x,y) = sqrt(x^2 + y^2)')
parser_instance.evaluate('g(3,4)') // 5
// Recursive functions (be careful with stack overflow)
parser_instance.evaluate('factorial(n) = n <= 1 ? 1 : n * factorial(n-1)')
parser_instance.evaluate('factorial(5)') // 120
// Functions with conditionals
parser_instance.evaluate('abs(x) = x >= 0 ? x : -x')
parser_instance.evaluate('abs(-5)') // 5import { ArgumentsError, SyntaxError } from 'mathjs'
try {
evaluate('1 / 0') // May return Infinity or throw
} catch (error) {
console.log('Division by zero')
}
try {
evaluate('unknown_function(5)')
} catch (error) {
if (error instanceof Error) {
console.log('Undefined function:', error.message)
}
}
try {
parse('1 + + 2') // Syntax error
} catch (error) {
console.log('Parse error:', error.message)
}
// Validate expressions before evaluation
function safeEvaluate(expr, scope = {}) {
try {
const node = parse(expr)
return node.evaluate(scope)
} catch (error) {
return { error: error.message }
}
}// Pre-compile frequently used expressions
const expressions = {
quadratic: compile('a*x^2 + b*x + c'),
distance: compile('sqrt((x2-x1)^2 + (y2-y1)^2)'),
interest: compile('P * (1 + r/n)^(n*t)')
}
// Batch evaluation
function evaluateQuadratic(coefficients, xValues) {
return xValues.map(x =>
expressions.quadratic.evaluate({ ...coefficients, x })
)
}
// Memoization for expensive symbolic operations
const memoizedSimplify = (() => {
const cache = new Map()
return (expr) => {
const key = typeof expr === 'string' ? expr : expr.toString()
if (!cache.has(key)) {
cache.set(key, simplify(expr))
}
return cache.get(key)
}
})()// Custom AST transformations
function optimizeExpression(node) {
// Traverse and transform AST
if (node.type === 'OperatorNode') {
if (node.op === '*' && node.args.some(arg =>
arg.type === 'ConstantNode' && arg.value === 0
)) {
return new ConstantNode(0) // x * 0 = 0
}
if (node.op === '+' && node.args.some(arg =>
arg.type === 'ConstantNode' && arg.value === 0
)) {
// Remove zero terms
return node.args.find(arg => !(arg.type === 'ConstantNode' && arg.value === 0))
}
}
// Recursively optimize children
if (node.args) {
node.args = node.args.map(optimizeExpression)
}
return node
}// Use expressions in chain operations
const result = chain('x^2 + 2*x + 1')
.parse()
.simplify()
.evaluate({ x: 3 })
.done() // 16
// Complex chain with symbolic operations
const derivative_result = chain('sin(x^2)')
.parse()
.derivative('x')
.simplify()
.evaluate({ x: pi/2 })
.done()// Register custom functions for use in expressions
const math = create(all)
math.import({
myCustomFunction: (x) => x^2 + 1,
factorial: factorial // Re-export built-in
})
evaluate('myCustomFunction(5)', {}, { math }) // 26// Population growth model
const exponentialGrowth = compile('P0 * e^(r * t)')
const logisticGrowth = compile('K / (1 + ((K - P0) / P0) * e^(-r * t))')
// Physical equations
const kineticEnergy = compile('0.5 * m * v^2')
const gravitationalForce = compile('G * m1 * m2 / r^2')
// Economic calculations
const compoundInterest = compile('P * (1 + r/n)^(n*t)')
const presentValue = compile('FV / (1 + r)^t')// Newton's method for root finding
function newtonMethod(expr, guess, tolerance = 1e-10) {
const f = compile(expr)
const f_prime = compile(derivative(expr, 'x'))
let x = guess
let iterations = 0
while (iterations < 100) {
const fx = f.evaluate({ x })
const fpx = f_prime.evaluate({ x })
if (Math.abs(fx) < tolerance) break
x = x - fx / fpx
iterations++
}
return x
}
// Find root of x^2 - 2 = 0 (sqrt(2))
const root = newtonMethod('x^2 - 2', 1) // ≈ 1.414// Extract variables from expression
function extractVariables(expr) {
const node = parse(expr)
const variables = new Set()
node.traverse((node, path, parent) => {
if (node.type === 'SymbolNode' && !isFunction(node.name)) {
variables.add(node.name)
}
})
return Array.from(variables)
}
// Analyze expression complexity
function analyzeExpression(expr) {
const node = parse(expr)
return {
leafCount: leafCount(node),
variables: extractVariables(expr),
hasConstants: node.toString().match(/\d+/) !== null,
hasFunctions: node.toString().match(/\w+\(/) !== null,
complexity: leafCount(node) + extractVariables(expr).length
}
}Install with Tessl CLI
npx tessl i tessl/npm-mathjsdocs
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10