Skip to content

Autodifferentiation

Status: stub

Full autodiff documentation is planned for Phase 2. This stub covers the key concepts and the operator.

Spec source: 05_AUTODIFF.csl

Sigil’s differentiation operator takes a function and returns its derivative function:

fn f(x: f32) -> f32 { x * x + 2.0 * x }
let df = ∂f; // type: fn(f32) -> f32
let val = df(3.0); // = 2*3 + 2 = 8.0 (exact, not numeric)

is a compile-time transformation — df is a new function generated by the compiler that computes the exact derivative. There is no numerical approximation, no epsilon, no tape overhead at runtime.

A function is differentiable by if:

  1. Its return type implements Differentiable
  2. Its arguments implement Differentiable
  3. It is !pure (or has only !pure effects in the positions being differentiated)

The third requirement is why the effect system and autodiff are coupled. Differentiating an impure function is not well-defined — you can’t take the derivative of a side effect.

Sigil’s compiler selects the differentiation mode (forward or reverse) based on the ratio of inputs to outputs:

  • Forward mode (JVP) — efficient when |inputs| ≤ |outputs|. The compiler generates a Jacobian-vector product.
  • Reverse mode (VJP) — efficient when |outputs| ≤ |inputs|. The compiler generates a vector-Jacobian product.

For scalar functions, this distinction doesn’t matter. For gradient computation in ML-style optimization (many inputs, scalar output), reverse mode is automatic.

Full documentation in Phase 2. See also: §C.2 Jets & Higher-Order AD.