Lightweight automatic differentiation and error propagation library
C++MIT
A minimal engine for automatic differentiation
Cascade is a lightweight C++ library for automatic differentiation and error propagation. It provides a streamlined engine to compute gradients of arbitrary functions and propagate uncertainties in the inputs to the outputs. The library simplifies the implementation of gradient-based optimization algorithms and the error analysis in scientific computing and engineering applications.
How to build
Create a build folder in the root directory and cd it. Build the library and tests executable with:
./example_derivatives
Value of f: 11.4487
Gradient of f: (12.2532 4.45102 -2.09071)
example_covariances.cpp
#include"cascade.h"
#include<iostream>usingnamespacecascade;intmain()
{
// Create variables by providing their values and standard deviations (default to 0.0)
Var x = {2.1, 1.5};
Var y = {-3.5, 2.5};
Var z = {5.7, 1.4};
// Set the covariances between them (default to 0.0)Var::setCovariance(x, y, 0.5);
Var::setCovariance(x, z, 1.8);
Var::setCovariance(y, z, -1.0);
// Compute a function of the variables
Var f = (x + y) * cos(z) * x;
bool changed = Var::setCovariance(f, x, 1.0);
if (!changed)
{
std::cout << "Covariance involving a functional variable cannot be set" << std::endl;
}
// Computing covariances involving functional variables triggers backpropagation calls
std::cout << Var::covariance(f, x) << std::endl;
std::cout << Var::covariance(f, y) << std::endl;
std::cout << Var::covariance(f, z) << std::endl;
std::cout << Var::covariance(f, f) << std::endl;
std::cout << f.sigma() * f.sigma() << std::endl;
return0;
}
./example_covariances
Covariance involving a functional variable cannot be set
-0.723107
12.8668
-3.87443
28.4044
28.4044
Background
Automatic differentiation
Cascade uses reverse mode automatic differentiation, most commonly known as backpropagation, to compute exact derivatives of arbitrary piecewise differentiable functions. The key concept behind this technique is compositionality. Each function defines an acyclic computational graph where its nodes store the intermediate values of the operations that make up the result, and each edge stores the derivative of a parent node with respect to a child node. One can then efficiently apply the chain rule on the function by sorting the graph nodes in a topological order and allowing the derivatives to flow backwards, from the output to the inputs. For example, the graph of $x^2 \sin{y} + e^{x/z}$ has the form:
Here each node is given a name and is already indexed following a topological order. The partial derivative of the function with respect to $x$ corresponds to adding all the paths from the output node to the $x$ node, where the value of each path is the product of the values of its edges:
If you have a set of $n$ distinct variables with values $\mu_1, \ldots, \mu_n$, which come with some errors that you can estimate, $\sigma_1, \ldots, \sigma_n$, together with the size of the correlations between the variables, $\sigma_{ij}$, you can model your data as a sample of a random vector $\boldsymbol{X} = (X_1, \ldots, X_n)$ with expected value
Whenever you compute a value $Y$ that is a function of these variables, say $Y = f(\boldsymbol{X})$, their uncertainties will be propagated to the result, so that it will also be a random variable. An estimation of the error associated with this new variable can be computed by linearizing the function around the expected value of the input, $\boldsymbol{\mu}$:
The same idea can also be used to compute the covariance between two variables that are both functions of $\boldsymbol{X}$. For instance, if $Z = g(\boldsymbol{X})$: