Reverse mode automatic differentiation in Rust.
To use this in your crate, add the following to Cargo.toml
:
rust
[dependencies]
reverse = "0.1"
```rust use reverse::*;
fn main() { let graph = Graph::new(); let a = graph.addvar(2.5); let b = graph.addvar(14.); let c = (a.sin().powi(2) + b.ln() * 3.) - 5.; let gradients = c.grad();
asserteq!(gradients.wrt(&a), (2. * 2.5).sin()); asserteq!(gradients.wrt(&b), 3. / 14.); } ```
The main type is Var<'a>
, so you can define functions that take this in and the function will be differentiable. For example:
```rust use reverse::*;
fn main() { let graph = Graph::new(); let params = graph.addvars(&[5., 2., 0., 1.]); let result = difffn(¶ms); let gradients = result.grad(); println!("{:?}", gradients.wrt(¶ms)); }
fn diff_fn<'a>(params: &[Var<'a>]) -> Var<'a> { params[0].powf(params[1]) + params[2].sin() - params[3].asinh() / 2. } ```
There is an optional diff
feature that activates a convenience macro to transform certain functions so that they are differentiable. That is, functions that act on f64
s can be used without change on Var
s, and without needing to specify the type.
To use this, add the following to Cargo.toml
:
rust
reverse = { version = "0.1", features = ["diff"] }
Functions must have the type Fn(&[f64], &[&[f64]]) -> f64
, where the first argument contains the differentiable parameters and the second argument contains arbitrary arrays of data.
Here is an example of what the feature allows you to do:
```rust use reverse::*;
fn main() { let graph = Graph::new(); let a = graph.addvar(5.); let b = graph.addvar(2.);
// you can track gradients through the function as usual!
let res = addmul(&[a, b], &[&[4.]]);
let grad = res.grad();
assert_eq!(grad.wrt(&a), 1.);
assert_eq!(grad.wrt(&b), 4.);
}
// function must have these argument types but can be arbitrarily complex // apply computations to params and data as if they were f64s
fn addmul(params: &[f64], data: &[&[f64]]) -> f64 { params[0] + data[0][0] * params[1] } ```