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:
- Parses the function body into a symbolic expression tree.
- Differentiates each component analytically using standard calculus rules.
- Simplifies the result (constant folding, identity laws, CSE, etc.).
- Applies greedy commutative/associative reordering to expose further common sub-expressions.
- Emits the gradient function with shared sub-expressions hoisted into
letbindings.
§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 ofx[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 f64casts, 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. powiexponents 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 elementiis∂f/∂x[i]in closed form.