|
limes 3.1.0
Composable Calculus Expressions for C++20
|
Can we build something for integration analogous to what autograd does for differentiation?
This question launched limes. Automatic differentiation (autograd) revolutionized machine learning by computing gradients automatically through expression graphs. Could we do the same for integrals?
The honest answer is **no**—not in the same way.
Differentiation is local. The chain rule decomposes perfectly: to differentiate f(g(x)), you only need ‘f’(g(x))andg'(x)` at a single point. The computation is algebraic.
Integration is global. There's no "chain rule" for integrals. To compute an integral, you fundamentally need to visit infinitely many points (approximated by quadrature). The computation is analytic.
This asymmetry means we can't build an "autointegrate" that automatically computes integrals by understanding expression structure the way autograd computes gradients.
Instead of mimicking autograd's mechanism, we take inspiration from its design philosophy: expressions as first-class composable objects.
The insight is:
Integrals are algebraic objects that compose according to mathematical laws.
These laws are well-known:
∫(af + bg) = a∫f + b∫g∫[a,c] = ∫[a,b] + ∫[b,c]∫∫f dxdy = ∫∫f dydx (when valid)∫∫f(x)g(y) dxdy = (∫f dx)(∫g dy)limes makes these laws explicit and exploitable in the API.
Most numerical integration libraries treat functions as black boxes:
The integrator has no idea what f does. It can only:
f at pointsThis works, but it's leaving information on the table.
limes builds expressions from primitives, making structure visible:
Now the system knows f is sin(x²). This enables:
Since f = sin(x²) is built from known primitives, we can differentiate symbolically:
The chain rule is applied at compile time. No numerical differentiation, no epsilon parameters, no round-off error accumulation.
Consider this double integral:
A black-box integrator would use nested quadrature: for each y point, integrate over x. That's O(n²) evaluations.
But limes can detect that f(x,y) = g(x)·h(y) where g(x) = exp(-x²) and h(y) = exp(-y²). The integral factors:
Now we need only O(n) evaluations for each 1D integral.
Integrals carry domain information that can be manipulated:
Different integrands need different methods. With expressions, we can (eventually) make intelligent choices:
limes follows these principles:
The expression (what to compute) is separate from the method (how to compute it):
Simple pieces that compose well beat monolithic features:
Extension through concepts enables generic programming:
Any type satisfying the concept works with the system.
Expressions are trees you can examine:
This makes debugging possible and the system pedagogically valuable.
limes draws from Alex Stepanov's approach to generic programming:
The goal is not just to compute integrals, but to express the mathematics of integration in C++.
The key insight of limes:
While we cannot automate integration like autograd automates differentiation, we CAN build a system where both differentiation and integration are first-class algebraic objects with rich structure that the computer can exploit.
Expressions beat black boxes when structure matters.