#jit #expression #cranelift #variables #compilation #differentiation #derivative

evalexpr-jit

JIT compilation and symbolic differentiation of evalexpr expressions with Cranelift

6 releases

0.2.0 Jan 17, 2025
0.1.4 Jan 16, 2025

#797 in Algorithms

Download history 265/week @ 2025-01-04 294/week @ 2025-01-11 89/week @ 2025-01-18 20/week @ 2025-02-01

419 downloads per month

MIT and AGPL-3.0-only

165KB
2.5K SLoC

EvalExpr-JIT

Crates.io Version Build Status

A high-performance mathematical expression evaluator with JIT compilation and automatic differentiation support. Builds on top of evalexpr and Cranelift.

This crate is still under development and the API is subject to change.

Features

  • šŸš€ JIT compilation for fast expression evaluation
  • šŸ“Š Automatic differentiation (up to any order)
  • šŸ”¢ Support for multiple variables with consistent ordering
  • šŸ§® Higher-order partial derivatives
  • šŸ“ Jacobian matrix computation
  • šŸ”„ Batch evaluation of equation systems

Check out the API Reference for more details.

Installation

Install the crate from crates.io:

cargo add evalexpr-jit

or add this to your Cargo.toml:

[dependencies]
evalexpr-jit = "0.1.2"  # Replace with actual version

Quick Start

Single Equation

The Equation struct provides a simple way to evaluate mathematical expressions and compute their derivatives. Variables are automatically detected from the expression and ordered alphabetically.

use evalexpr_jit::Equation;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create a single equation
    let eq = Equation::new("2*x + y^2".to_string())?;
    
    // Evaluate at point (x=1, y=2)
    let result = eq.eval(&[1.0, 2.0])?;
    assert_eq!(result, 6.0); // 2*1 + 2^2 = 6
    
    // Compute first-order derivative
    let dx = eq.derivative("x")?;
    let result = dx(&[1.0, 2.0]);
    assert_eq!(result, 2.0); // d/dx[2x + y^2] = 2
    
    // Compute higher-order mixed derivative
    let dxy = eq.derive_wrt(&["x", "y"])?;
    let result = dxy(&[1.0, 2.0]);
    assert_eq!(result, 0.0); // dĀ²/dxdy[2x + y^2] = 0
    
    Ok(())
}

System of Equations

The EquationSystem struct allows you to evaluate multiple equations simultaneously, sharing variables across equations for efficient computation. Variables are automatically collected from all equations and consistently ordered.

use evalexpr_jit::system::EquationSystem;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create a system of equations
    let system = EquationSystem::new(vec![
        "x^2*y".to_string(),  // f1
        "x*y^2".to_string(),  // f2
    ])?;
    
    // Evaluate at point (x=2, y=3)
    let results = system.eval(&[2.0, 3.0])?;
    assert_eq!(results.as_slice(), &[
        12.0,  // f1: 2^2 * 3 = 12
        18.0   // f2: 2 * 3^2 = 18
    ]); 
    
    // Get the sorted variable names
    println!("Variables: {:?}", system.sorted_variables()); // ["x", "y"]
    
    Ok(())
}

Advanced Usage

System Derivatives and Jacobian

use evalexpr_jit::system::EquationSystem;
use ndarray::Array2;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let system = EquationSystem::new(vec![
        "x^2*y".to_string(),  // f1
        "x*y^2".to_string(),  // f2
    ])?;

    // Compute Jacobian matrix at point (2,3)
    let jacobian = system.eval_jacobian(&[2.0, 3.0], None)?;
    assert_eq!(jacobian[0], vec![12.0, 4.0]);  // derivatives of f1 [āˆ‚f1/āˆ‚x, āˆ‚f1/āˆ‚y]
    assert_eq!(jacobian[1], vec![9.0, 12.0]);  // derivatives of f2 [āˆ‚f2/āˆ‚x, āˆ‚f2/āˆ‚y]

    // Create optimized Jacobian computer for specific variables
    let jacobian_fn = system.jacobian_wrt(&["x", "y"])?;
    let mut results = Array2::zeros((2, 2));
    jacobian_fn.eval_into_matrix(&[2.0, 3.0], &mut results)?;

    // Compute higher-order derivatives
    let d2 = system.derive_wrt(&["x", "y"])?;
    let mut results = vec![0.0; 2];
    d2.eval_into(&[2.0, 3.0], &mut results)?;
    assert_eq!(results, vec![
        4.0,  // dĀ²/dxdy[x^2*y] = 2x
        6.0   // dĀ²/dxdy[x*y^2] = 2y
    ]);

    Ok(())
}

Batch Evaluation

use evalexpr_jit::system::EquationSystem;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let system = EquationSystem::new(vec![
        "x^2*y".to_string(),
        "x*y^2".to_string(),
    ])?;

    // Evaluate multiple input sets in parallel
    let input_sets = vec![
        vec![2.0, 3.0],
        vec![1.0, 2.0],
        vec![3.0, 4.0],
    ];

    let results = system.eval_parallel(&input_sets)?;
    assert_eq!(results[0].as_slice(), &[12.0, 18.0]); // [2^2 * 3, 2 * 3^2]
    assert_eq!(results[1].as_slice(), &[2.0, 4.0]);   // [1^2 * 2, 1 * 2^2]
    assert_eq!(results[2].as_slice(), &[36.0, 48.0]); // [3^2 * 4, 3 * 4^2]

    Ok(())
}

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Dependencies

~10ā€“19MB
~295K SLoC