Rookfighter / autodiff-cpp

A single header-only C++ library for automatic / algorithmic differentiation.
MIT License
13 stars 2 forks source link
algorithmic-differentation automatic-differentiation cpp header-only library

autodiff-cpp

Cpp11 License CMake

autodiff-cpp is a single header-only C++ library for algorithmic (automatic) differentiation.

Install

Simply copy the single header file into your project and add it to your include path, then you are good to go.

If CMake is your preferred tooling you can install the library with the following command:

cd path/to/repo
cmake -B out/ -S .
cmake --install out/

You can then simply use the library in your CMake projects by finding the package:

find_package(adcpp REQUIRED)
add_executable(myproject main.cpp)
target_link_libraries(myproject adcpp::adcpp)

Usage

Choose if you either want to use algorithmic differentiation in backward or forward mode. The result and precision are the same in both cases but the differentiation mode makes a difference performance-wise.

Forward mode is linear in the number of input arguments of your function. If your function requires n input arguments, then the runtime behavior is O(n). Basically the function has to be evaluated n times to calculate the full gradient / Jacobian.

Backward mode is linear in the number of output values of your function. If your function produces m output arguments, then the runtime behavior is O(m). Basically the function has to be evaluated m times to calculate the full gradient / Jacobian.

Both modes can compute the following arithmetic expressions:

Foward Mode

#include <iostream>
#include <adcpp.h>

using namespace adcpp;

// define your function with adcpp numbers
static fwd::Double myfunc(const fwd::Double &x,
    const fwd::Double &y)
{
    // constants have to be wrapped in adcpp numbers
    return fwd::Double(2) * fwd::pow(y * fwd::sin(x) + fwd::exp(x / y), 2);
}

int main(const int argc, const char **argv)
{
    if(argc != 3)
    {
        std::cout << "Usage: forward_diff <xval> <yval>" << std::endl;
        return 1;
    }

    // parse command line arguments as regular doubles
    double xval = std::stod(argv[1]);
    double yval = std::stod(argv[2]);

    // Define x and y as adcpp Double
    // The first parameter defines its value and the second value defines its
    // gradient.
    // Set the gradient of x to 1, so we can calculate the partial derivative
    // of our function w.r.t. to x.
    fwd::Double x = fwd::Double(xval, 1);
    fwd::Double y = fwd::Double(yval, 0);
    // Evaluate the function with respect to x.
    fwd::Double fx = myfunc(x, y);

    // Set the gradient of y to 1, so we can calculate the partial derivative
    // of our function w.r.t. to y.
    x = fwd::Double(xval, 0);
    y = fwd::Double(yval, 1);
    // Evaluate the function with respect to y.
    fwd::Double fy = myfunc(x, y);

    // Print the results.
    // value() and gradient() are accessors for the gradient and computed value
    // of a function.
    std::cout << "Result:" << std::endl
        << "x = " << xval << ", y = " << yval << std::endl
        << "f = " << fx.value()
        << ", fx = " << fx.gradient()
        << ", fy = " << fy.gradient() << std::endl;
    return 0;
}

Backward Mode

#include <adcpp.h>
#include <iostream>
#include <string>

using namespace adcpp;

// define your function with adcpp numbers
static bwd::Double myfuncA(const bwd::Double &x,
    const bwd::Double &y)
{
    // constants have to be wrapped in adcpp numbers
    return bwd::Double(2) * bwd::pow(y * bwd::sin(x) + bwd::exp(x / y), 2);
}

// define a second function with adcpp numbers
static bwd::Double myfuncB(const bwd::Double &x,
    const bwd::Double &y)
{
    return bwd::sqrt(x * x / bwd::exp(y)) ;
}

int main(const int argc, const char **argv)
{
    if(argc != 3)
    {
        std::cout << "Usage: forward_diff <xval> <yval>" << std::endl;
        return 1;
    }

    // parse command line arguments as regular doubles
    double xval = std::stod(argv[1]);
    double yval = std::stod(argv[2]);

    // Define x and y as adcpp Double in backward mode.
    bwd::Double x = bwd::Double(xval);
    bwd::Double y = bwd::Double(yval);
    // Evaluate the functions.
    bwd::Double fA = myfuncA(x, y);
    bwd::Double fB = myfuncB(x, y);

    // Compute the derivative of all input parameters with respect to the
    // given function
    bwd::Double::DerivativeMap derivative;
    fA.derivative(derivative);

    // Print the results.
    // value() is an accessors for the computed value of a function.
    // The derivative variable contains the derivative of different parameters
    // w.r.t. the function.
    // Call value on final function value to retrieve its result.
    // Use derivative on the variable of which you want partial derivatives for
    // the function.
    std::cout << "Result (A):" << std::endl
        << "x = " << xval << ", y = " << yval << std::endl
        << "f = " << fA.value()
        << ", fx = " << derivative(x)
        << ", fy = " << derivative(y) << std::endl;

    // Calculate the derivatives for a different function.
    // The derivative variable can be reused.
    fB.derivative(derivative);

    std::cout << "Result (B):" << std::endl
        << "x = " << xval << ", y = " << yval << std::endl
        << "f = " << fB.value()
        << ", fx = " << derivative(x)
        << ", fy = " << derivative(y) << std::endl;
    return 0;
}