tang.

math for physical reality.

Dual<f64> f(x) = x · sin(x)
x = 0.00
val = 0.00
dual = 0.00

Evaluating f(x) = x·sin(x) with Dual<f64> computes the value and exact derivative in a single pass — no finite differences, no symbolic engine. The green tangent line is the dual field: the slope that falls out for free.

use tang::*;
use tang_la::{DVec, DMat, Svd};
use tang_ad::{Tape, grad};
use tang_optim::Adam;

// Dual numbers give you exact derivatives for free
let x = Dual::new(2.0, 1.0);
let y = (x * x).sin();  // y.dual = cos(x²) · 2x

// Reverse-mode AD for large parameter spaces
let loss = |x: &[f64]| x[0]*x[0] + x[1]*x[1] + x[2]*x[2];
let g = grad(loss, &[1.0, 2.0, 3.0]);  // [2, 4, 6]

// Dense linear algebra — LU, SVD, Cholesky, QR, Eigen
let a = DMat::from_fn(3, 3, |i, j| if i == j { 2.0 } else { -1.0 });
let svd = Svd::new(&a);

// The same Scalar trait flows through everything
let q = Quat::axis_angle(Dir3::Z, core::f64::consts::FRAC_PI_2);
let v = q.rotate(Vec3::new(1.0, 0.0, 0.0));  // ≈ (0, 1, 0)

// the-scalar-trick

One trait. Three powers.

Write your math once with S: Scalar. Swap the type to change what it does.

Compute
fn spring_energy<S: Scalar>(
    k: S, x: S
) -> S {
    k * x * x * S::from_f64(0.5)
}

spring_energy(10.0_f64, 0.3)
// → 0.45
Differentiate
// same function, different type
let x = Dual::var(0.3);

spring_energy(10.0.into(), x)
// → Dual {
//     val:  0.45,  // energy
//     dual: 3.0,   // ∂E/∂x = kx
// }
Bound (planned)
// interval arithmetic
let x = Interval::new(0.28, 0.32);

spring_energy(10.0.into(), x)
// → Interval {
//     lo: 0.392,
//     hi: 0.512,
// }

One trait. Same code. Exact derivatives. No tape, no graph tracing.

// the-stack

8 crates. One coherent stack.

Each layer builds on the last. Use only what you need.

tang-train
top
let mut model = Sequential::new(vec![
    Box::new(Linear::new(784, 128, 42)),
    Box::new(ReLU),
    Box::new(Linear::new(128, 10, 43)),
]);
let pred = model.forward(&input);
let loss = cross_entropy_loss(&pred, &targets);
tang-optim
// nonlinear least-squares: min ||r(x)||²
let lm = LevenbergMarquardt::new();
let x = lm.minimize(&x0, residual_fn, jacobian_fn);

// or gradient-based with Adam
let mut opt = Adam::new(0.001);
opt.step(&mut params, &grads);
tang-tensor
let x = Tensor::from_fn(Shape::from_slice(&[32, 784]), |idx| /* ... */);
let w = Tensor::zeros(Shape::from_slice(&[784, 128]));
let h = x.matmul(&w);         // [32, 128]
let flat = h.reshape(Shape::from_slice(&[4096]));
tang-gpu
let device = GpuDevice::new().await;
let a_gpu = device.upload(&sparse_a);
let y = device.spmv(&a_gpu, &x); // GPU SpMV kernel
tang-sparse
let mut coo = CooMatrix::new(1000, 1000);
coo.push(0, 0, 2.0);  coo.push(0, 1, -1.0);
let csr = coo.to_csr();        // compressed sparse row
let y = csr.spmv(&x);          // O(nnz) mat-vec
tang-ad
let tape = Tape::new();
let (a, b) = (tape.var(3.0), tape.var(5.0));
let loss = &a * &b + (&a).sin();
let grads = loss.backward();   // ∂loss/∂a, ∂loss/∂b — one pass

// or: one-liner gradient, jacobian, hessian
let g = grad(|x| &x[0] * &x[1], &[3.0, 5.0]); // [5.0, 3.0]
tang-la
let a = DMat::from_fn(100, 100, |i, j| if i == j { 2.0 } else if (i as i64 - j as i64).abs() == 1 { -1.0 } else { 0.0 });
let x = Lu::new(&a).unwrap().solve(&b);  // direct solve
let svd = Svd::new(&a);                  // A = UΣVᵀ
let eig = SymmetricEigen::new(&a);       // eigenvalues + vectors
tang
foundation
// one function, three scalar types
fn spring<S: Scalar>(k: S, x: S) -> S { k * x * x * S::from_f64(0.5) }

spring(10.0_f64, 0.3);                     // → 0.45
spring(Dual::cst(10.0), Dual::var(0.3));    // → 0.45 + 3.0ε (exact derivative)

let q = Quat::from_axis_angle(Vec3::z(), PI / 4.0);
let t = Transform::new(Mat3::from_quat(q), Vec3::new(1.0, 2.0, 3.0));

// benchmarks

Measured, not marketed.

Apple M-series, single-threaded. cargo bench -p tang-bench

view benchmark source →

Geometry & physics primitives (f64 unless noted)

Lower is better. glam uses f32 (SIMD).

tang nalgebra glam (f32)

Differentiable physics

tang AD gives exact derivatives. Finite differences are ε-approximate and require step-size tuning.

tang AD (exact) finite diff (approximate)

The speed comparison is secondary — the real win is machine-precision derivatives with zero tuning. Finite differences break down for stiff systems.

Dense linear algebra (f64, tang vs nalgebra)

tang is pure generic Rust — works with Dual<f64>, any Scalar. nalgebra dispatches to BLAS-style routines. For peak f64, enable the faer feature.

tang nalgebra

// use-cases

Built for real systems.

Geometry Kernels

BRep, NURBS, mesh ops. The types CAD needs.

let n = Vec3::new(0.0, 0.0, 1.0);
let t = Transform::rotation(
    Quat::axis_angle(Dir3::Z, PI/4.0)
);

Physics Engines

Spatial algebra, screw theory, inertia tensors.

let i = SpatialInertia::new(
    mass, com, inertia
);
let f = i.apply(&accel);

Robotics

Differentiable FK/IK, exact Jacobians for free.

// exact Jacobian via Dual
let j = jacobian(
    |q| forward_kinematics(q),
    &joint_angles
);

ML / Differentiable Programming

Same types train your nets and run your sim.

let g = grad(
    |x| mse_loss(&predict(x), &target),
    &params
);

// migration

Coming from nalgebra?

Drop-in aliases. Minimal call-site changes.

Type mapping

nalgebra tang
Vector3<f64>Vec3<f64>
Point3<f64>Point3<f64>
Unit<Vector3>Dir3<f64>
Matrix4<f64>Mat4<f64>
UnitQuaternionQuat<f64>
DVector<f64>DVec<f64>
DMatrix<f64>DMat<f64>

API compatibility

// nalgebra style — works
v.dot(&w);
v.cross(&w);

// tang style — also works
v.dot(w);
v.cross(w);
nalgebra tang
Vec3::zeros()Vec3::zero() + alias
v.norm_squared()v.norm_sq() + alias
Unit::new_normalize(v)Dir3::new(v) + alias
m.try_inverse()m.try_inverse()
m.symmetric_eigen()m.symmetric_eigen()
a.clone().lu().solve(&b)a.clone().lu().solve(&b)

// targets

Runs everywhere.

no_std Embedded
Every crate is #![no_std] with alloc. Run tang on microcontrollers, in kernels, anywhere without a standard library. No heavyweight dependencies — core types are hand-rolled #[repr(C)].
wasm32 Browser & Edge
Compiles to WebAssembly with zero changes. The same geometry kernel that runs natively also runs in the browser at near-native speed. Used by vcad for client-side CAD.
wgpu GPU Compute
tang-gpu provides wgpu compute shaders for sparse matrix-vector products, batch operations, and tensor kernels. Cross-platform GPU acceleration on Vulkan, Metal, DX12, and WebGPU.