active library

dual

Started 2026 Makefile

Resources & Distribution

Source Code

Package Registries

dual

Forward-mode automatic differentiation via dual numbers for C++20.

Overview

Dual numbers are a simple yet powerful technique for computing exact derivatives. The key insight: if we extend our number system with an element epsilon where epsilon^2 = 0, then evaluating f(x + epsilon) yields f(x) + epsilon * f'(x). The derivative emerges automatically from the algebra.

This library provides:

  • dual<T> - Dual numbers for first derivatives
  • dual2<T> - Nested duals for second derivatives
  • jet<T, N> - Taylor jets for arbitrary-order derivatives
  • Numerical differentiation - Finite difference schemes for comparison/verification

Quick Start

#include <dual/dual.hpp>
#include <iostream>

int main() {
    using namespace dual;

    // Create a dual variable at x = 2
    auto x = dual<double>::variable(2.0);

    // Compute f(x) = x^3 - 3x + 1
    auto f = x*x*x - 3.0*x + 1.0;

    std::cout << "f(2) = " << f.value() << "\n";       // 3.0
    std::cout << "f'(2) = " << f.derivative() << "\n"; // 9.0
}

Installation

Header-only

Copy the include/dual directory to your project and add it to your include path.

CMake

add_subdirectory(dual)
target_link_libraries(your_target PRIVATE dual::dual)

System-wide

cmake -B build -DCMAKE_INSTALL_PREFIX=/usr/local
cmake --build build
cmake --install build

Building and Testing

cmake -B build
cmake --build build
ctest --test-dir build --output-on-failure

API Reference

dual

The core dual number type.

// Create a variable for differentiation
auto x = dual<double>::variable(3.0);  // x = 3, dx = 1

// Create a constant
auto c = dual<double>::constant(2.0);  // c = 2, dc = 0

// Access values
double val = x.value();       // 3.0
double deriv = x.derivative(); // 1.0

// Arithmetic operators: +, -, *, /
auto y = sin(x*x) + exp(-x);

// Convenience function
auto [value, deriv] = differentiate([](auto x) { return x*x; }, 3.0);

Mathematical Functions

All standard math functions are supported with correct derivative propagation:

  • Basic: sqrt, cbrt, abs
  • Exponential: exp, exp2, expm1, log, log2, log10, log1p
  • Trigonometric: sin, cos, tan, asin, acos, atan, atan2
  • Hyperbolic: sinh, cosh, tanh, asinh, acosh, atanh
  • Power: pow, hypot
  • Special: erf, erfc

Higher-Order Derivatives

Second derivatives with dual2:

auto result = differentiate2([](auto x) { return sin(x); }, 1.0);
// result.value  = sin(1)
// result.first  = cos(1)
// result.second = -sin(1)

Arbitrary order with jets:

// Compute f, f', f'', f''', f'''' at x = 1
auto derivs = derivatives<4>([](auto x) { return exp(x); }, 1.0);
// All derivatives of e^x at x=1 equal e

Numerical Differentiation

For verification or black-box functions:

using namespace dual::numerical;

// Various finite difference schemes
auto fd = forward_difference(f, x);      // O(h)
auto cd = central_difference(f, x);      // O(h^2)
auto fp = five_point_stencil(f, x);      // O(h^4)

// Gradient of multivariate function
auto grad = gradient(f, {x, y, z});

// Jacobian and Hessian
auto jac = jacobian(f, x);
auto hess = hessian(f, x);

// Compare automatic vs numerical
auto cmp = compare_derivatives(f, x);
assert(cmp.agrees());

Forward vs Reverse Mode

This library implements forward mode AD:

  • Pros: Simple, no memory overhead, exact derivatives
  • Cons: Cost is O(n) for n input variables

For functions with many inputs and few outputs (like neural network loss functions), reverse mode (backpropagation) is more efficient. See gradator for a reverse-mode implementation.

Use CaseBest Mode
f: R -> R^n (one input, many outputs)Forward
f: R^n -> R (many inputs, one output)Reverse
Jacobian-vector productsForward
Vector-Jacobian productsReverse

Requirements

  • C++20 compiler with concepts support
  • CMake 3.20+ (for building)
  • Google Test (optional, for testing)

License

MIT License - see LICENSE file for details.

Discussion