10 releases (6 breaking)

new 0.7.0 Feb 3, 2025
0.6.0 Jan 26, 2025
0.5.0 Jan 25, 2025
0.4.0 Jan 25, 2025
0.1.0 Jan 15, 2025

#319 in Rust patterns

Download history 318/week @ 2025-01-13 389/week @ 2025-01-20 93/week @ 2025-01-27

800 downloads per month

Custom license

55KB
1.5K SLoC

aad - Automatic Adjoint Differentiation Library

crates.io docs.rs License

A pure Rust automatic differentiation library using reverse-mode adjoint differentiation.

Features

  • Supports both f32 and f64: Generic implementation works with any floating-point type implementing num_traits::Float.
  • Reverse-mode autodiff: Efficiently compute gradients for scalar-valued functions with many inputs.
  • Operator overloading: Use standard mathematical operators with variables.
  • High Performance: Optimized for minimal runtime overhead.
    • Benchmarks show competitive performance, often outperforming alternatives in gradient computation ( see Benchmarks).
  • Type-agnostic functions: Write generic mathematical code using the FloatLike trait.
  • Derive macros: Automatically generate differentiable functions with #[autodiff] macro (requires derive feature).

Installation

Add to your Cargo.toml:

[dependencies]
aad = { version = "0.5.0", features = ["derive"] }

Usage

Basic Example

use aad::Tape;

fn main() {
    // Initialize computation tape
    let tape = Tape::default();

    // Create variables
    let [x, y] = tape.create_variables(&[2.0_f64, 3.0_f64]);

    // Build computation graph
    let z = (x + y) * x.sin();

    // Forward pass
    println!("z = {:.2}", z.value()); // z = 4.55

    // Reverse pass
    let gradients = z.compute_gradients();
    println!("Gradients: dx = {:.2}, dy = {:.2}",
             gradients.get_gradients(&[x, y]));
    // Gradients: dx = -1.17, dy = 0.91
}

Using Macros for Automatic Differentiation

Enable the derive feature and use #[autodiff] to automatically differentiate functions:

use aad::{Tape, autodiff};

#[autodiff]
fn f(x: f64, y: f64) -> f64 {
    5.0 + 2.0 * x + y / 3.0
}

fn main() {
    let tape = Tape::default();
    let [x, y] = tape.create_variables(&[2.0_f64, 3.0_f64]);

    // Compute value and gradients
    let z = f(x, y);
    let gradients = z.compute_gradients();

    println!("Result: {:.2}", z.value());    // 5.0 + 4.0 + 1.0 = 10.00
    println!("Gradients: dx = {:.2}, dy = {:.2}",
             gradients.get_gradients(&[x, y]));
    // Gradients: dx = 2.00, dy = 0.33
}

Benchmarks

Run benchmarks with:

cargo bench --features benchmarks

Detailed results

License

MIT License - see LICENSE

Dependencies

~0.1–10MB
~105K SLoC