CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-mathjs

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

1.47x
Overview
Eval results
Files

expressions.mddocs/

Expression Parser and Evaluation

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

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'

Basic Expression Evaluation

Direct Evaluation

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])') // 15

Expression Syntax

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

Parsing to AST

Parse Function

parse(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 })

Working with AST Nodes

// 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 }) // 14

Compilation for Performance

Compile Function

compile(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 Instance

Creating Parser

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)

AST Node Types and Construction

Symbol and Constant Nodes

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]]))

Operator Nodes

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 = true

Function Nodes

FunctionNode(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')])

Assignment Nodes

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] = 10

Array and Object Nodes

ArrayNode(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}

Conditional Nodes

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 }) // 3

Range Nodes

RangeNode(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]

Symbolic Operations

Symbolic Differentiation

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 = 10

Expression Simplification

simplify(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*x

Specific Simplification Functions

simplifyConstant(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*x

Rationalization

rationalize(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)

Advanced Expression Manipulation

Expression Resolution

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 }) // 11

Leaf Count

leafCount(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)

Symbolic Equality

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

Working with Units in Expressions

// 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 and Array Expressions

// 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

Function Definition in Expressions

// 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)') // 5

Error Handling in Expressions

import { 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 }
  }
}

Performance Optimization

Expression Compilation Strategies

// 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)
  }
})()

Tree Transformation and Optimization

// 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
}

Integration with Math.js Ecosystem

Chain Interface with Expressions

// 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()

Custom Function Registration

// 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

Common Expression Patterns

Mathematical Modeling

// 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')

Numerical Methods Integration

// 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

Expression Analysis

// 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-mathjs

docs

arithmetic.md

data-types.md

expressions.md

index.md

matrices.md

probability.md

statistics.md

trigonometry.md

units.md

tile.json