Skip to main content

Crate symdiff

Crate symdiff 

Source
Expand description

Compile-time symbolic differentiation via a proc-macro attribute.

The gradient macro differentiates a Rust function analytically at compile time and emits a companion {fn}_gradient that returns the closed-form gradient as a fixed-size array. No numerical approximation, no runtime overhead beyond the arithmetic itself.

§How it works

At compile time the macro:

  1. Parses the function body into a symbolic expression tree.
  2. Differentiates each component analytically using standard calculus rules.
  3. Simplifies the result (constant folding, identity laws, CSE, etc.).
  4. Applies greedy commutative/associative reordering to expose further common sub-expressions.
  5. Emits the gradient function with shared sub-expressions hoisted into let bindings.

§Example

use symdiff::gradient;

#[gradient(dim = 2)]
fn rosenbrock(x: &[f64]) -> f64 {
    let a = 1.0 - x[0];
    let b = x[1] - x[0].powi(2);
    a.powi(2) + 100.0 * b.powi(2)
}

// Generates rosenbrock unchanged, plus:
// fn rosenbrock_gradient(x: &[f64]) -> [f64; 2] {
//     let tmp0 = x[0].powi(1);   // shared sub-expressions hoisted
//     ...
//     [∂f/∂x[0], ∂f/∂x[1]]
// }

let g = rosenbrock_gradient(&[1.0, 1.0]);
assert_eq!(g, [0.0, 0.0]); // minimum of the Rosenbrock function

§Attribute parameters

  • dim: usize — number of gradient components; must match the number of x[i] indices used in the function body (required)
  • max_passes: usize — maximum simplification passes; default 10 (optional)

§Supported syntax

The function body may contain let bindings followed by a bare tail expression. Within expressions, the following are supported:

  • Variables: x[0], x[1], … (integer literal indices only)
  • Numeric literals: 1, 2.0, etc.
  • Arithmetic: +, -, *, /, unary -
  • Methods: powi(n) (integer literal exponent), sin, cos, ln, exp, sqrt
  • Transparent: parentheses, as f64 casts, block expressions

Anything else (closures, function calls, loops, …) causes a compile-time panic with a descriptive message.

§Limitations

  • The input slice parameter must be named x.
  • powi exponents must be integer literals, not variables.
  • Conditional expressions and loops cannot be differentiated symbolically.

Attribute Macros§

gradient
Emit the original function unchanged, plus {fn}_gradient(x: &[f64]) -> [f64; dim] where element i is ∂f/∂x[i] in closed form.