limes (Latin: "limit", "boundary") — a C++20 header-only library for composable calculus expressions with symbolic differentiation and numerical integration.
limes makes calculus operations first-class composable objects. Build mathematical expressions, differentiate them symbolically via chain rule, and integrate them numerically with your choice of method—all with a clean, fluent API.
#include <limes/limes.hpp>
using namespace limes::expr;
auto x = arg<0>;
auto f = sin(x * x); // f(x) = sin(x²)
auto df = derivative(f).wrt<0>(); // df/dx = 2x·cos(x²)
auto I = integral(f).over<0>(0.0, 1.0); // ∫₀¹ sin(x²) dx
auto result = I.eval(); // ≈ 0.3103- Symbolic differentiation via chain rule — derivatives computed at compile time
- Fluent builder API —
derivative(f).wrt<0>(),integral(f).over<0>(a, b) - Named variables —
var(0, "x")for readableto_string()output - N-dimensional integration — nested integrals with dependent bounds
- Box integration — Monte Carlo over rectangular regions
- Separable composition —
I * Jfor independent integrals
- Multiple quadrature rules — Gauss-Legendre, Gauss-Kronrod, Clenshaw-Curtis, Tanh-Sinh
- Adaptive integration — automatic interval subdivision with error estimation
- High-precision accumulators — Kahan, Neumaier, Klein compensated summation
- Integration methods as objects —
gauss<7>(),monte_carlo(10000),adaptive()
using namespace limes::expr;
auto x = arg<0>;
auto I = integral(x * x).over<0>(0.0, 1.0); // ∫₀¹ x² dx
auto result = I.eval(); // = 1/3auto x = arg<0>;
auto f = exp(sin(x)); // e^(sin(x))
auto df = derivative(f).wrt<0>(); // cos(x)·e^(sin(x))
// Evaluate at x = 0
std::array<double, 1> args{0.0};
double slope = df.eval(args); // = 1.0auto [x, y] = vars_xy(); // Named variables for debugging
auto f = sin(x) * cos(y);
std::cout << f.to_string(); // "(* (sin x) (cos y))"using namespace limes::methods;
auto I = integral(sin(x)).over<0>(0.0, pi);
// Choose your method
auto r1 = I.eval(); // Default adaptive
auto r2 = I.eval(gauss<15>()); // 15-point Gauss-Legendre
auto r3 = I.eval(adaptive(1e-12)); // Adaptive with toleranceauto x = arg<0>;
auto y = arg<1>;
// Double integral: ∫₀¹∫₀ˣ xy dy dx
auto I = integral(x * y)
.over<1>(0.0, x) // y from 0 to x
.over<0>(0.0, 1.0); // x from 0 to 1
auto result = I.eval(); // = 1/8auto x = arg<0>;
auto y = arg<1>;
// Volume under paraboloid over unit square
auto I = integral(x*x + y*y)
.over_box({0.0, 1.0}, {0.0, 1.0});
auto result = I.eval(monte_carlo(100000)); // ≈ 2/3
// With constraint (unit circle)
auto disk = integral(One<double>{})
.over_box({-1.0, 1.0}, {-1.0, 1.0})
.where([](double x, double y) { return x*x + y*y <= 1.0; });
auto area = disk.eval(monte_carlo(100000)); // ≈ πauto x = arg<0>;
auto y = arg<1>;
// Independent integrals
auto I = integral(sin(x)).over<0>(0.0, pi); // = 2
auto J = integral(exp(y)).over<1>(0.0, 1.0); // = e - 1
// Multiply: evaluates each independently, then multiplies
auto IJ = I * J; // ProductIntegral
auto result = IJ.eval(); // = 2(e - 1) ≈ 3.44limes is header-only. Copy include/limes to your project or use CMake:
find_package(limes REQUIRED)
target_link_libraries(your_target PRIVATE limes::limes)limes/
├── limes.hpp # Main entry point
├── expr/ # Expression layer (user-facing API)
│ ├── expr.hpp # Aggregated expression header
│ ├── nodes/ # Expression nodes (Const, Var, Binary, Unary, ...)
│ ├── derivative.hpp # Symbolic differentiation
│ ├── derivative_builder.hpp # derivative(f).wrt<D>() API
│ ├── integral.hpp # Integration expressions
│ ├── box_integral.hpp # N-dimensional box integration
│ ├── product_integral.hpp # Separable integral composition
│ └── analysis.hpp # Variable set analysis
├── methods/ # Integration method objects
│ └── methods.hpp # gauss<N>(), monte_carlo(), adaptive()
└── algorithms/ # Numerical backend
├── concepts/ # Field, Accumulator, QuadratureRule
├── accumulators/ # Kahan, Neumaier, Klein
├── quadrature/ # Gauss-Legendre, Kronrod, Clenshaw-Curtis
└── integrators/ # Adaptive, Romberg, Tanh-Sinh
limes(alias:li) — Root namespacelimes::expr— Expression layer (composable calculus)limes::methods— Integration method objectslimes::algorithms— Low-level numerical backendlimes::algorithms::quadrature— Quadrature ruleslimes::algorithms::accumulators— Compensated summation
limes is inspired by Stepanov's approach to generic programming:
- Expressions as algebraic objects — Integrals compose according to mathematical laws (linearity, Fubini, separability)
- Separation of concerns — The what (expression) is separate from the how (numerical method)
- Concepts over inheritance — Clear, minimal requirements enable generic programming
- Compile-time where possible — Symbolic differentiation and expression simplification at compile time
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
ctest --test-dir build --output-on-failure- C++20 compiler (GCC 10+, Clang 12+, MSVC 19.29+)
- CMake 3.20+
- Google Test (for tests, fetched automatically)
Full documentation is available at: GitHub Pages
Build the API documentation locally with Doxygen:
cmake -S . -B build -DBUILD_DOCS=ON
cmake --build build --target docs
# Open build/docs/html/index.html in your browser| Guide | Description |
|---|---|
| Motivation | Why limes? The problem it solves |
| Tutorial | Step-by-step introduction |
| Examples | Complete worked examples |
| Algorithms | Numerical method details |
| Design | Full API design and philosophy |
| About | Author and project info |
MIT