Reverse Mode Differentiation: Ideal for functions with many inputs and one output (think neural nets).
Foolproof API: Lifetimes and phantom types ensure correctness at compile time, at no cost to the API consumer.
Scoped Variables: Keep your variables around and reuse them across computations. This allows for effective variable storage in model structures, bounded by the variable scope's lifetime.
Here’s a quick example:
use lib_auto::scalar::{Pullback,VarExt};use lib_auto::{Gradient,Tape};fnmain(){// Create a new tape (Wengert List) to store the nodes of our computationletmut tape:Tape<f64,Pullback> = Tape::new();// Define a scope to play around in; you can return the result of the computationlet() = tape.scope(|guard| {let x = guard.var(1.0);let y = x.mul(&x);// After locking a guard, we can only spawn more subcomputations, or collapse into gradientslet snap = guard.lock();let(_, grads) = snap.collapse();let wrt_y = y.deltas(&grads);println!("Value: {}, dy/dx: {}", y.value(), wrt_y[&x]);});}In this snippet, we create a variable, compute a function, and automatically derive its gradient...