Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help



Evaluation vs Simplification

Topic: advanced.evaluation_vs_simplification

Understand the critical differences between evaluation (computing numerical results with domain checking) and simplification (algebraic transformation) in MathHook's symbolic engine. Knowing when to use each operation is essential for correct mathematical computation.

Mathematical Definition

Evaluation maps expressions to numerical values: where is the set of expressions and is a variable substitution.

Simplification maps expressions to equivalent canonical forms:

Evaluation vs Simplification

MathHook provides two fundamental operations for working with expressions:

  1. Evaluation (evaluate(), evaluate_with_context()) - Compute numerical values
  2. Simplification (simplify()) - Algebraic reduction

The Key Principle

evaluate()simplify() - They serve different purposes and should not be used interchangeably.

AspectEvaluationSimplification
PurposeCompute numerical valuesReduce algebraic complexity
InputExpression (+ optional variables)Expression only
OutputNumerical result or errorSimpler symbolic form
Domain Checking✅ Yes (catches mathematical errors)❌ No
Substitution✅ Yes (with context)❌ No
Error HandlingResult<Expression, MathError>Expression

Core Concepts

Evaluation: Numerical Computation

Evaluation converts symbolic expressions into concrete numerical values with domain checking:

  • Domain Checking: Catches mathematical errors (sqrt(-1), log(0), division by zero)
  • Recursive Evaluation: Evaluates entire expression tree
  • Error Propagation: Errors bubble up from nested expressions

Simplification: Algebraic Reduction

Simplification transforms expressions into equivalent but simpler symbolic forms:

  • Algebraic Equivalence: Output is mathematically equivalent to input
  • No Domain Checking: Operates purely symbolically
  • Idempotency: Simplifying twice yields the same result

Decision Guide

Use evaluate() when:

  • You need a numerical result
  • You want domain validation
  • Expression contains only constants

Use evaluate_with_context() when:

  • Expression contains variables you need to substitute
  • You want control over evaluation behavior
  • You're solving equations or evaluating formulas

Use simplify() when:

  • You need algebraic reduction
  • You want to reduce expression complexity
  • You're preparing for symbolic operations

Common Pitfalls

❌ Expecting Numbers from simplify()

#![allow(unused)]
fn main() {
let x = symbol!(x);
let result = expr!(x + x).simplify();
// Returns: 2*x (still symbolic, NOT a number!)
}

❌ Using evaluate() Without Substitution

#![allow(unused)]
fn main() {
let x = symbol!(x);
let result = expr!(x + 1).evaluate().unwrap();
// Returns: x + 1 (symbolic, can't substitute without context)
}

❌ Ignoring Domain Errors

#![allow(unused)]
fn main() {
let result = expr!(sqrt(-1)).evaluate().unwrap(); // PANIC!
// Always handle Result properly
}

Examples

Basic Evaluation vs Simplification

Shows the fundamental difference between the two operations

Rust
#![allow(unused)]
fn main() {
use mathhook_core::{expr, symbol};

// Simplify: algebraic reduction
let x = symbol!(x);
let simplified = expr!(x + x + x).simplify();
assert_eq!(simplified, expr!(3 * x));  // Still symbolic

// Evaluate: numerical computation
let result = expr!(2 + 3).evaluate().unwrap();
assert_eq!(result, expr!(5));  // Numerical value

}
Python
from mathhook import symbol, expr

# Simplify: algebraic reduction
x = symbol('x')
simplified = (x + x + x).simplify()
# Result: 3*x (still symbolic)

# Evaluate: numerical computation
result = expr('2 + 3').evaluate()
# Result: 5 (numerical value)

JavaScript
const { symbol, expr } = require('mathhook');

// Simplify: algebraic reduction
const x = symbol('x');
const simplified = expr(x.add(x).add(x)).simplify();
// Result: 3*x (still symbolic)

// Evaluate: numerical computation
const result = expr('2 + 3').evaluate();
// Result: 5 (numerical value)

Evaluation with Variable Substitution

Using evaluate_with_context for variable substitution

Rust
#![allow(unused)]
fn main() {
use mathhook_core::core::expression::eval_numeric::EvalContext;
use mathhook_core::{expr, symbol};
use std::collections::HashMap;

let x = symbol!(x);
let y = symbol!(y);

// Create context with variable values
let mut vars = HashMap::new();
vars.insert("x".to_string(), expr!(3));
vars.insert("y".to_string(), expr!(4));
let ctx = EvalContext::numeric(vars);

// Evaluate x² + 2xy + y² at (x=3, y=4)
let formula = expr!(x^2 + 2*x*y + y^2);
let result = formula.evaluate_with_context(&ctx).unwrap();
assert_eq!(result, expr!(49)); // (3 + 4)² = 49

}
Python
from mathhook import symbol, expr, EvalContext

x = symbol('x')
y = symbol('y')

# Create context with variable values
ctx = EvalContext({'x': 3, 'y': 4})

# Evaluate x² + 2xy + y² at (x=3, y=4)
formula = x**2 + 2*x*y + y**2
result = formula.evaluate_with_context(ctx)
# Result: 49  (which is (3+4)²)

JavaScript
const { symbol, expr, EvalContext } = require('mathhook');

const x = symbol('x');
const y = symbol('y');

// Create context with variable values
const ctx = new EvalContext({x: 3, y: 4});

// Evaluate x² + 2xy + y² at (x=3, y=4)
const formula = x.pow(2).add(x.mul(y).mul(2)).add(y.pow(2));
const result = formula.evaluateWithContext(ctx);
// Result: 49  (which is (3+4)²)

Domain Error Handling

Evaluation catches mathematical domain errors

Rust
#![allow(unused)]
fn main() {
use mathhook_core::{expr, MathError};

// sqrt(-1) triggers domain error
match expr!(sqrt(-1)).evaluate() {
    Ok(result) => println!("Result: {}", result),
    Err(MathError::DomainError { operation, value, reason }) => {
        eprintln!("Domain error in {}: {} ({})", operation, value, reason);
    }
    Err(e) => eprintln!("Error: {:?}", e),
}

// log(0) triggers domain error
assert!(expr!(log(0)).evaluate().is_err());

// Division by zero
assert!(expr!(1 / 0).evaluate().is_err());

}
Python
from mathhook import expr, MathError

# sqrt(-1) triggers domain error
try:
    result = expr('sqrt(-1)').evaluate()
except MathError as e:
    print(f"Domain error: {e}")

# log(0) triggers domain error
try:
    result = expr('log(0)').evaluate()
except MathError as e:
    print(f"Domain error: {e}")

JavaScript
const { expr, MathError } = require('mathhook');

// sqrt(-1) triggers domain error
try {
    const result = expr('sqrt(-1)').evaluate();
} catch (e) {
    if (e instanceof MathError) {
        console.error(`Domain error: ${e.message}`);
    }
}

// log(0) triggers domain error - also throws
// Division by zero - also throws

Simplification for Algebraic Manipulation

Simplification applies algebraic identities without domain checking

Rust
#![allow(unused)]
fn main() {
use mathhook_core::{expr, symbol};

let x = symbol!(x);

// Combine like terms
assert_eq!(expr!(x + x + x).simplify(), expr!(3 * x));

// Remove identity elements
assert_eq!(expr!(x * 1).simplify(), expr!(x));
assert_eq!(expr!(x + 0).simplify(), expr!(x));

// Zero propagation
assert_eq!(expr!(0 * x).simplify(), expr!(0));

// Trigonometric identities
assert_eq!(expr!(sin(x)^2 + cos(x)^2).simplify(), expr!(1));

// Simplify doesn't check domain (stays symbolic)
let result = expr!(sqrt(x)).simplify(); // OK, stays sqrt(x)

}
Python
from mathhook import symbol, expr, simplify

x = symbol('x')

# Combine like terms
assert simplify(x + x + x) == 3*x

# Remove identity elements
assert simplify(x * 1) == x
assert simplify(x + 0) == x

# Zero propagation
assert simplify(0 * x) == 0

# Trigonometric identities
from mathhook import sin, cos
assert simplify(sin(x)**2 + cos(x)**2) == 1

JavaScript
const { symbol, expr, simplify, sin, cos } = require('mathhook');

const x = symbol('x');

// Combine like terms
console.log(simplify(x.add(x).add(x)));  // 3*x

// Remove identity elements
console.log(simplify(x.mul(1)));  // x
console.log(simplify(x.add(0)));  // x

// Zero propagation
console.log(simplify(x.mul(0)));  // 0

// Trigonometric identities
console.log(simplify(sin(x).pow(2).add(cos(x).pow(2))));  // 1

API Reference

  • Rust: mathhook_core::core::expression::eval_numeric
  • Python: mathhook.evaluation
  • JavaScript: mathhook.evaluation

See Also