Merge pull request #1302 from demroz/develop
New library: Reverse-mode automatic differentiation
3
.gitignore
vendored
@@ -10,7 +10,7 @@ test/cuda
|
||||
**/.temps/*
|
||||
build/*
|
||||
.vscode/*
|
||||
*.svg
|
||||
#*.svg
|
||||
tools/bin/**
|
||||
.idea/*
|
||||
|
||||
@@ -28,3 +28,4 @@ cmake-build-debug/*
|
||||
build.ninja
|
||||
.ninja*
|
||||
a.out
|
||||
|
||||
|
||||
665
doc/differentiation/autodiff_reverse.qbk
Normal file
@@ -0,0 +1,665 @@
|
||||
[/ Copyright Maksym Zhelyeznyakov 2025-2026
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)]
|
||||
|
||||
[section:autodiff Reverse Mode Automatic Differentiation]
|
||||
[template autodiff_equation[name] '''<inlinemediaobject><imageobject><imagedata fileref="../equations/autodiff/'''[name]'''"></imagedata></imageobject></inlinemediaobject>''']
|
||||
[template autodiff_graph[name] '''<inlinemediaobject><imageobject><imagedata fileref="../graphs/autodiff/'''[name]'''"></imagedata></imageobject></inlinemediaobject>''']
|
||||
[h1:synopsis Synopsis]
|
||||
|
||||
#include <boost/math/differentiation/autodiff_reverse.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace math {
|
||||
namespace differentiation {
|
||||
namespace reverse_mode {
|
||||
|
||||
/* autodiff variable of type RealType (numeric types), stores derivatives up to DerivativeOrder
|
||||
* rvar inherits from a generic expression base class
|
||||
* expression<RealType, DerivativeOrder, rvar<RealType, DerivativeOrder>>
|
||||
* This is a Curiously Recurring Template Pattern(CRTP)
|
||||
* The purpose is so that rvar acts as a terminal node in an expression graph, and can be combined
|
||||
* with other expression-based types (sums, producs, etc..) to form expression graphs.
|
||||
*/
|
||||
template<typename RealType, size_t DerivativeOrder = 1>
|
||||
class rvar : public expression<RealType, DerivativeOrder, rvar<RealType, DerivativeOrder>> {
|
||||
// inner_t of rvar<RealType, N> = var<RealType, N-1>
|
||||
// used to store graphs of N-1 order derivatives
|
||||
// rvar_t<RealType, 0> decays to RealType
|
||||
using inner_t = rvar_t<RealType, DerivativeOrder - 1>;
|
||||
/* constructors */
|
||||
// default
|
||||
rvar();
|
||||
// construct from numeric type
|
||||
rvar(const RealType value);
|
||||
// copy
|
||||
rvar(const rvar<RealType, DerivativeOrder> other);
|
||||
|
||||
/* assignment operators */
|
||||
// from numeric type
|
||||
rvar &operator=(RealType v);
|
||||
// from another rvar
|
||||
rvar &operator=(const rvar<RealType, DerivativeOrder> &other);
|
||||
// from a chain of expression templates
|
||||
rvar &operator=(const expression<RealType, DerivativeOrder, E> &expr);
|
||||
|
||||
// calculate all derivatives in computational graph
|
||||
void backward();
|
||||
|
||||
// get RealType value in rvar
|
||||
RealType item() const;
|
||||
|
||||
// get accumulated adjoint of this variable
|
||||
// returns inner_t
|
||||
const inner_t &adjoint() const { return *node_->get_adjoint_ptr(); }
|
||||
inner_t &adjoint() { return *node_->get_adjoint_ptr(); }
|
||||
|
||||
// all arithmetic and comparison operators are overloaded
|
||||
template<class E>
|
||||
rvar<RealType, DerivativeOrder> &operator+=(const expression<RealType, DerivativeOrder, E> &expr);
|
||||
|
||||
template<class E>
|
||||
rvar<RealType, DerivativeOrder> &operator*=(const expression<RealType, DerivativeOrder, E> &expr)
|
||||
|
||||
|
||||
}
|
||||
|
||||
// gradient tape holds the computational graph
|
||||
/*
|
||||
* The expression graph is stored on a tape. The tape is closely related to the memory manegement
|
||||
* system in the library. BOOST_MATH_BUFFER_SIZE is set to 65536. It controls the block size of the
|
||||
* internal memory arena. Its a macro, and can be set at compile time.
|
||||
*/
|
||||
template<typename RealType, size_t DerivativeOrder, size_t buffer_size = BOOST_MATH_BUFFER_SIZE>
|
||||
class gradient_tape {
|
||||
|
||||
// clear tape
|
||||
void clear();
|
||||
|
||||
// sets all derivatives to zero
|
||||
void zero_grad();
|
||||
|
||||
// adds a checkpoint you can rewind to
|
||||
void add_checkpoint();
|
||||
|
||||
// rewinds tape to last checkpoint set
|
||||
void rewind_to_last_checkpoint();
|
||||
// index is "checkpoint" index. so // order which checkpoint was set
|
||||
void rewind_to_checkpoint_at(size_t index);
|
||||
// rewind to beginning of graph without clearing the tape
|
||||
void rewind();
|
||||
}
|
||||
|
||||
// tape access
|
||||
template<typename template<typename RealType, size_t DerivativeOrder>>
|
||||
inline gradient_tape<RealType, DerivativeOrder, BOOST_MATH_BUFFER_SIZE> &get_active_tape();
|
||||
|
||||
// standard math functions are overloaded using expression templates
|
||||
template<typename RealType, size_t DerivativeOrder, typename LHS, typename RHS>
|
||||
struct add_expr : public abstract_binary_expression<RealType, DerivativeOrder, LHS, RHS, add_expr<RealType, DerivativeOrder, LHS, RHS>>
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder, typename LHS, typename RHS> add_expr<RealType, DerivativeOrder, LHS, RHS> operator+(const expression<RealType, DerivativeOrder, LHS> &lhs,const expression<RealType, DerivativeOrder, RHS> &rhs);
|
||||
|
||||
// Standard math functions are overloaded and called via argument-dependent lookup (ADL).
|
||||
template<typename RealType, size_t DerivativeOrder, typename ARG>
|
||||
floor_expr<RealType, DerivativeOrder, ARG> floor(const expression<RealType,DerivativeOrder, ARG> &arg);
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder, typename ARG>
|
||||
cos_expr<RealType, DerivativeOrder, ARG> cos(const expression<RealType,DerivativeOrder, ARG> &arg);
|
||||
|
||||
// Helper gradient functions
|
||||
// rvar<T,0> decays into T
|
||||
// returns vector<rvar<T,order1_1-1>> of gradients
|
||||
template<typename T, size_t order_1, size_t order_2>
|
||||
auto grad(rvar<T, order_1> &f, std::vector<rvar<T, order_2> *> &x);
|
||||
template<typename T, size_t order_1, typename First, typename... Other>
|
||||
auto grad(rvar<T, order_1> &f, First first, Other... other)
|
||||
// returns a vector<vector<rvar<T, order_1 - 2>> representing the hessian matrix
|
||||
template<typename T, size_t order_1, size_t order_2>
|
||||
auto hess(rvar<T, order_1> &f, std::vector<rvar<T, order_2> *> &x)
|
||||
template<typename T, size_t order_1, typename First, typename... Other>
|
||||
auto hess(rvar<T, order_1> &f, First first, Other... other)
|
||||
// returns N-d nested vector<vector< ...rvar<T,order_1 - N> ...
|
||||
// representing the tensor generalization of the N-d gradient
|
||||
template<size_t N, typename T, size_t order_1, size_t order_2>
|
||||
auto grad_nd(rvar<T, order_1> &f, std::vector<rvar<T, order_2> *> &x)
|
||||
template<size_t N, typename ftype, typename First, typename... Other>
|
||||
auto grad_nd(ftype &f, First first, Other... other)
|
||||
// ...
|
||||
|
||||
} // namespace reverse_mode
|
||||
} // namespace differentiation
|
||||
} // namespace math
|
||||
} // namespace boost
|
||||
|
||||
[h1:description Description]
|
||||
|
||||
Reverse mode autodiff is a header-only C++ library the [@https://en.wikipedia.org/wiki/Automatic_differentiation
|
||||
automatic differentiation] (reverse mode) of mathematical functions. This implementation builds a computational graph known as a tape, which stores all operations and their corresponding derivatives. The total gradients are then computed by reverse accumulation via the chain rule.
|
||||
|
||||
Consider the following function
|
||||
|
||||
template<typename T>
|
||||
T f(x,y)
|
||||
{
|
||||
return x*y+sin(x);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
rvar<double,1> x = 1.0;
|
||||
rvar<double,1> y = 1.0;
|
||||
rvar<double,1> z = f(x,y);
|
||||
z.backward();
|
||||
}
|
||||
|
||||
and the associated computational graph.
|
||||
|
||||
[:[:[autodiff_graph autodiff_reverse_graph.svg]]]
|
||||
|
||||
Using reverse mode autodiff, the gradients are computed by traversing the graph backward:
|
||||
[:[:[autodiff_equation reverse_mode_autodiff_ex_eq.svg]]]
|
||||
|
||||
Some key points about reverse mode automatic differentiation:
|
||||
|
||||
1. Reverse mode auto-diff is exceptionally efficient for computing the gradient of functions mapping from high to low dimensional space, f : [real][super n][rarr][real]. Unlike finite differences or forward mode autodiff which scale with the number of input variables, reverse mode autodiff calculates the entire gradient vector in time proportional to the original function's evaluation time.
|
||||
|
||||
2. While forward-over-reverse is often the most efficient way to obtain Hessians or Jacobian–vector products, our implementation currently supports reverse-over-reverse only. This means higher-order derivatives are available, but at a higher computational cost.It is possible to compute higher order derivatives by applying reverse mode over reverse mode differentiation. The backward function builds a new computational tape over the gradient computation. This approach is conceptually sound, but it can become computationally expensive very quickly. Forward mode autodiff is generally preferrable when computing higher order derivatives.
|
||||
|
||||
3. While reverse mode is fast for computing the gradient, it has to store all the intermediate values from the forward pass to be used during the backward pass. This can be a significant memory overhead, especially for deep networks or complex functions.
|
||||
|
||||
[h1:overloaded-functions List of overloaded functions]
|
||||
[table
|
||||
[[Function] [argument types] [implementation] [return type] [left derivative] [right derivative]]
|
||||
[[ + ] [expression, expression] [x+y] [expression] [1.0] [1.0]]
|
||||
[[ + ] [expression, float] [x+y] [expresison] [1.0] []]
|
||||
[[ + ] [float, expression] [x+y] [expression] [1.0] []]
|
||||
|
||||
[[ \* ] [expression, expression] [x*y] [expression] [y] [x]]
|
||||
[[ \* ] [expression, float] [x*y] [expression] [y] []]
|
||||
[[ \* ] [float, expression] [x*y] [expression] [] [x]]
|
||||
|
||||
[[ - ] [expression, expression] [x-y] [expression] [1.0] [-1.0]]
|
||||
[[ - ] [expression, float] [x-y] [expression] [1.0] []]
|
||||
[[ - ] [float, expression] [x-y] [expression] [] [-1.0]]
|
||||
|
||||
[[ / ] [expression, expression] [x/y] [expression] [1/y] [-x/(y*y)]]
|
||||
[[ / ] [expression, float] [x/y] [expression] [1/y] []]
|
||||
[[ / ] [float, expression] [x/y] [expression] [] [-x/(y*y)]]
|
||||
|
||||
[[fabs] [expression] [std::fabs(x)] [expression] [x > 0.0 ? 1.0 : -1.0] []]
|
||||
[[abs] [expression] [fabs(x)] [expression] [x > 0.0 ? 1.0 : -1.0] []]
|
||||
[[ceil] [expression] [std::ceil(x)] [expression] [0.0] []]
|
||||
[[floor] [expression] [std::floor(x)] [expression] [0.0] []]
|
||||
[[trunc] [expression] [std::trunc(x)] [expression] [0.0] []]
|
||||
[[exp] [expression] [std::exp(x)] [expression] [exp(x)] []]
|
||||
|
||||
[[pow] [expression, expression] [std::pow(x,y)] [expression] [y*pow(x,y-1)] [pow(x,y)*log(x)]]
|
||||
[[pow] [expression, float] [std::pow(x,y)] [expression] [y*pow(x,y-1)] []]
|
||||
[[pow] [float. expression] [std::pow(x,y)] [expression] [] [pow(x,y)*log(x)]]
|
||||
|
||||
[[sqrt] [expression] [std::sqrt(x)] [expression] [1/(2 sqrt(x))] []]
|
||||
[[log] [expression] [std::log(x)] [expression] [1/x] []]
|
||||
[[cos] [expression] [std::cos(x)] [expression] [-sin(x)] []]
|
||||
[[sin] [expression] [std::sin(x)] [expression] [cos(x)] []]
|
||||
[[tan] [expression] [std::tan(x)] [expression] [1/(cos(x)*cos(x))] []]
|
||||
[[acos] [expression] [std::acos(x)] [expression] [-1.0/(sqrt(1-x*x))] []]
|
||||
[[asin] [expression] [std::asin(x)] [expression] [1.0/(sqrt(1-x*x))] []]
|
||||
[[atan] [expression] [std::atan(x)] [expression] [1.0/(1+x*x)] []]
|
||||
[[atan2] [expression,expression] [std::atan2(x,y)] [expression] [y/(x*x+y*y)] [-x/(x*x+y*y)]]
|
||||
[[atan2] [expression,float] [std::atan2(x,y)] [expression] [y/(x*x+y*y)] []]
|
||||
[[atan2] [float,expression] [std::atan2(x,y)] [expression] [] [-x/(x*x+y*y)]]
|
||||
|
||||
[[round] [expression] [std::round(x)] [expression] [0.0] []]
|
||||
[[cosh] [expression] [std::cosh(x)] [expression] [sinh(x)] []]
|
||||
[[sinh] [expression] [std::sinh(x)] [expression] [cosh(x)] []]
|
||||
[[tanh] [expression] [std::tanh(x)] [expression] [1/(cosh(x)*cosh(x))] []]
|
||||
[[log10] [expression] [std::log10(x)] [expression] [1/(xlog(10.0)] []]
|
||||
|
||||
[[acosh] [expression] [std::cosh(x)] [expression] [1.0/(sqrt(x-1)*sqrt(x+1))] []]
|
||||
[[asinh] [expression] [std::sinh(x)] [expression] [1/(sqrt(1+x*x))] []]
|
||||
[[atanh] [expression] [std::tanh(x)] [expression] [1/(1-x*x))] []]
|
||||
[[fmod] [expression,expression] [std::fmod(x,y)] [expression] [1.0] [-1.0/trunc(x/y)]]
|
||||
[[fmod] [expression,float] [std::fmod(x,y)] [expression] [1.0] []]
|
||||
[[fmod] [float,expression] [std::fmod(x,y)] [expression] [] [-1.0/trunc(x/y)]]
|
||||
|
||||
[[iround] [expression] [std::iround(x)] [int, this function breaks the autodiff chain] [] []]
|
||||
[[lround] [expression] [std::lround(x)] [long, this function breaks the autodiff chain] [] []]
|
||||
[[llround] [expression] [std::llround(x)] [long long, this function breaks the autodiff chain] [] []]
|
||||
|
||||
[[itrunc] [expression] [std::itrunc(x)] [int, this function breaks the autodiff chain] [] []]
|
||||
[[ltrunc] [expression] [std::ltrunc(x)] [long, this function breaks the autodiff chain] [] []]
|
||||
[[lltrunc] [expression] [std::lltrunc(x)] [long long, this function breaks the autodiff chain] [] []]
|
||||
|
||||
[[frexp] [expression, *int] [std::frexp(x.evaluate(),i); x/pow(2.0, *int) ] [expression] [1/pow(2.0,int)] []]
|
||||
[[ldexp] [expression, &int] [ x*pow(2,int) ] [expression] [pow(2,int)] []]
|
||||
]
|
||||
|
||||
[h1:expression_templates Example 1: Linear Regression]
|
||||
|
||||
Although autodiff is overkill for linear regression, its a useful example for demonstrating a typical gradient based optimization usecase.
|
||||
|
||||
#include <array>
|
||||
#include <boost/math/differentiation/autodiff_reverse.hpp>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
using namespace boost::math::differentiation::reverse_mode;
|
||||
double random_double(double min_val, double max_val)
|
||||
{
|
||||
static std::random_device rd;
|
||||
static std::mt19937 gen(rd());
|
||||
std::uniform_real_distribution<double> dist(min_val, max_val);
|
||||
return dist(gen);
|
||||
}
|
||||
|
||||
This function generates a random double within a specified range, used to create the true slope, intercept, and noisy data.
|
||||
|
||||
double noisy_linear_function(double intercept, double slope, double x)
|
||||
{
|
||||
return intercept + slope * x + random_double(-0.1, 0.1);
|
||||
}
|
||||
|
||||
Above is a simple data generating function that simulates some real-world data by adding a small amount of random noise to a perfect linear relationship.
|
||||
template<size_t N>
|
||||
rvar<double, 1> loss(std::array<double, N>& y_target, std::array<rvar<double, 1>, N>& y_fit)
|
||||
{
|
||||
rvar<double, 1> loss_v = make_rvar<double, 1>(0.0);
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
loss_v += pow(abs(y_target[i] - y_fit[i]), 2) / N;
|
||||
}
|
||||
return loss_v;
|
||||
}
|
||||
|
||||
The loss function calculates the mean-squared error. It takes true values of y, and the "model's" predicted y values and computes their squared difference.
|
||||
|
||||
template<size_t N>
|
||||
std::array<rvar<double, 1>, N> model(rvar<double, 1>& a, rvar<double, 1>& b, std::array<double, N>& x)
|
||||
{
|
||||
std::array<rvar<double, 1>, N> ret;
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
ret[i] = a * x[i] + b;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
This function represents the "linear model" we're fitting to. y = ax + b. It takes slow a and intercept b as rvars and the "x" as data. The returned array of rvars holds predicted y values and all calcuations are tracked by the gradient tape.
|
||||
|
||||
int main()
|
||||
{
|
||||
// 1. Generate noisy data with known slope and intercept
|
||||
double slope = random_double(-5, 5);
|
||||
double intercept = random_double(-5, 5);
|
||||
const size_t num_data_samples = 100;
|
||||
std::array<double, num_data_samples> noisy_data_x;
|
||||
std::array<double, num_data_samples> noisy_data_y;
|
||||
|
||||
for (size_t i = 0; i < num_data_samples; i++) {
|
||||
double x = random_double(-1, 1);
|
||||
double y = noisy_linear_function(intercept, slope, x);
|
||||
noisy_data_x[i] = x;
|
||||
noisy_data_y[i] = y;
|
||||
}
|
||||
|
||||
Above is the data generation stage. We generate a dataset of 100 (x,y) points where y us a linear function of plus some random noise. The "true" slop and intercept are stored to be compared in the final result.
|
||||
|
||||
// 2. Initialize guess variables
|
||||
double slope_guess = random_double(-5, 5);
|
||||
double intercept_guess = random_double(-5, 5);
|
||||
rvar<double, 1> a = make_rvar<double, 1>(slope_guess);
|
||||
rvar<double, 1> b = make_rvar<double, 1>(intercept_guess);
|
||||
|
||||
The initialization stage: the model's parameters a and b are initialized with random guesses.
|
||||
|
||||
// 3. Get the gradient tape and add a checkpoint
|
||||
gradient_tape<double, 1>& tape = get_active_tape<double, 1>();
|
||||
tape.add_checkpoint();
|
||||
|
||||
Tape management: `get_active_tape<double,1>` returns a reference to a global tape that stores the computational graph. Checkpoints are essential for gradient descent loops. They allow the tape to be "rewound" at the beginning of each iteration preventing it from growing infinitely.
|
||||
|
||||
// 4. Initial forward pass and loss calculation
|
||||
auto y_fit = model(a, b, noisy_data_x);
|
||||
rvar<double, 1> loss_v = loss(noisy_data_y, y_fit);
|
||||
|
||||
The model and loss functions are called. This is just to initialize y_fit and loss_v to be used inside the while loop.
|
||||
// 5. Gradient Descent Loop
|
||||
double learning_rate = 1e-3;
|
||||
|
||||
The learning rate controls how large a step we take in the direction of the
|
||||
negative gradient each iteration. Intuitively, it sets the "velocity" of
|
||||
descent toward a minimum.
|
||||
|
||||
Too high: the optimization may overshoot minima, oscillate, or even diverge.
|
||||
Too low: convergence will be very slow and may stall in shallow regions.
|
||||
|
||||
In practice, values in the range [1e-4, 1e-1] are common starting points,
|
||||
with 1e-3 being a typical safe default for many problems. The best choice
|
||||
depends on the scale of the data, the model, and the curvature of the loss
|
||||
landscape.
|
||||
|
||||
while (loss_v > 0.005) {
|
||||
tape.zero_grad(); // zero out all the adjoints
|
||||
|
||||
It is essentially to zero out the gradients at every iteration so as to not reaccumulate them. if you were to call `loss_v.backward()` a second time, the derivative calculations will be incorrect.
|
||||
|
||||
tape.rewind_to_last_checkpoint(); // Rewind the tape for a new iteration
|
||||
|
||||
Rewinds the tape to right before the model and loss calculates were done.
|
||||
y_fit = model(a, b, noisy_data_x);
|
||||
loss_v = loss(noisy_data_y, y_fit);
|
||||
loss_v.backward(); // backward pass
|
||||
Calls the model and computes the gradeints.
|
||||
a -= a.adjoint() * learning_rate; // Update 'a' by subtracting the gradient scaled by the learning rate
|
||||
b -= b.adjoint() * learning_rate; // Update 'b' similarly
|
||||
}
|
||||
updates `a` and `b` based on their gradients.
|
||||
|
||||
// 6. Print Results
|
||||
std::cout << "Autodiff Linear Regression Summary \n";
|
||||
std::cout << "learning rate : " << learning_rate << "\n";
|
||||
std::cout << "true slope: " << slope;
|
||||
std::cout << " regression: " << a.item() << "\n";
|
||||
|
||||
std::cout << "relative error (slope): " << relative_slope_error << "\n";
|
||||
std::cout << "absolute error (slope): " << slope_error << "\n";
|
||||
std::cout << "true intercept: " << intercept;
|
||||
std::cout << " regression: " << b.item() << "\n";
|
||||
std::cout << "absolute error (intercept): " << intercept_error << "\n";
|
||||
std::cout << "aelative error (intercept): " << relative_intercept_error << "\n";
|
||||
std::cout << "-------------------------------" << std::endl;
|
||||
}
|
||||
A sample output of the above code
|
||||
|
||||
Autodiff Linear Regression Summary
|
||||
learning rate : 0.001
|
||||
true slope: 1.15677 regression: 1.24685
|
||||
relative error (slope): 0.0778782
|
||||
absolute error (slope): 0.0900868
|
||||
true intercept: 2.23378 regression: 2.22309
|
||||
absolute error (intercept): 0.0106901
|
||||
aelative error (intercept): 0.00478563
|
||||
-------------------------------
|
||||
|
||||
[h2:example-black-scholes Example 2: Black-Scholes Option Pricing with Greeks]
|
||||
Below is effectively a rewrite of the forward mode autodiff Black Scholes example. Its a good example on how to compute higher oder gradients with reverse mode autodiff with helper functions like `grad()`, `hess()`, and `grad_nd()`
|
||||
|
||||
#include <boost/math/differentiation/autodiff_reverse.hpp>
|
||||
|
||||
using namespace boost::math::differentiation::reverse_mode;
|
||||
using namespace boost::math::constants;
|
||||
|
||||
template<typename Real>
|
||||
Real phi(Real const& x)
|
||||
{
|
||||
return one_div_root_two_pi<Real>() * exp(-0.5 * x * x);
|
||||
}
|
||||
|
||||
template<typename Real>
|
||||
Real Phi(Real const& x)
|
||||
{
|
||||
return 0.5 * erfc(-one_div_root_two<Real>() * x);
|
||||
}
|
||||
|
||||
enum class CP { call, put };
|
||||
|
||||
template<typename T>
|
||||
T black_scholes_option_price(CP cp, double K, T const& S, T const& sigma, T const& tau, T const& r)
|
||||
{
|
||||
using namespace std;
|
||||
auto const d1 = (log(S / K) + (r + sigma * sigma / 2) * tau) / (sigma * sqrt(tau));
|
||||
auto const d2 = (log(S / K) + (r - sigma * sigma / 2) * tau) / (sigma * sqrt(tau));
|
||||
switch (cp) {
|
||||
case CP::call:
|
||||
return S * Phi<T>(d1) - exp(-r * tau) * K * Phi<T>(d2);
|
||||
case CP::put:
|
||||
return exp(-r * tau) * K * Phi<T>(-d2) - S * Phi<T>(-d1);
|
||||
default:
|
||||
throw runtime_error("Invalid CP value.");
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
double const K = 100.0;
|
||||
double S_val = 105.0;
|
||||
double sigma_val = 5.0;
|
||||
double tau_val = 30.0 / 365;
|
||||
double r_val = 1.25 / 100;
|
||||
rvar<double, 3> S = make_rvar<double, 3>(S_val);
|
||||
rvar<double, 3> sigma = make_rvar<double, 3>(sigma_val);
|
||||
rvar<double, 3> tau = make_rvar<double, 3>(tau_val);
|
||||
rvar<double, 3> r = make_rvar<double, 3>(r_val);
|
||||
|
||||
rvar<double, 3> call_price
|
||||
= black_scholes_option_price<rvar<double, 3>>(CP::call, K, S, sigma, tau, r);
|
||||
rvar<double, 3> put_price
|
||||
= black_scholes_option_price<rvar<double, 3>>(CP::put, K, S, sigma, tau, r);
|
||||
|
||||
double const d1 = ((log(S_val / K) + (r_val + sigma_val * sigma_val / 2) * tau_val)
|
||||
/ (sigma_val * sqrt(tau_val)));
|
||||
double const d2 = ((log(S_val / K) + (r_val - sigma_val * sigma_val / 2) * tau_val)
|
||||
/ (sigma_val * sqrt(tau_val)));
|
||||
double const formula_call_delta = +Phi(+d1);
|
||||
double const formula_put_delta = -Phi(-d1);
|
||||
double const formula_vega = (S_val * phi(d1) * sqrt(tau_val));
|
||||
double const formula_call_theta = (-S_val * phi(d1) * sigma_val / (2 * sqrt(tau_val))
|
||||
- r_val * K * exp(-r_val * tau_val) * Phi(+d2));
|
||||
|
||||
double const formula_put_theta = (-S_val * phi(d1) * sigma_val / (2 * sqrt(tau_val))
|
||||
+ r_val * K * exp(-r_val * tau_val) * Phi(-d2));
|
||||
double const formula_call_rho = (+K * tau_val * exp(-r_val * tau_val) * Phi(+d2));
|
||||
double const formula_put_rho = (-K * tau_val * exp(-r_val * tau_val) * Phi(-d2));
|
||||
double const formula_gamma = (phi(d1) / (S_val * sigma_val * sqrt(tau_val)));
|
||||
double const formula_vanna = (-phi(d1) * d2 / sigma_val);
|
||||
double const formula_charm = (phi(d1) * (d2 * sigma_val * sqrt(tau_val) - 2 * r_val * tau_val)
|
||||
/ (2 * tau_val * sigma_val * sqrt(tau_val)));
|
||||
double const formula_vomma = (S_val * phi(d1) * sqrt(tau_val) * d1 * d2 / sigma_val);
|
||||
double const formula_veta = (-S_val * phi(d1) * sqrt(tau_val)
|
||||
* (r_val * d1 / (sigma_val * sqrt(tau_val))
|
||||
- (1 + d1 * d2) / (2 * tau_val)));
|
||||
double const formula_speed = (-phi(d1) * (d1 / (sigma_val * sqrt(tau_val)) + 1)
|
||||
/ (S_val * S_val * sigma_val * sqrt(tau_val)));
|
||||
double const formula_zomma = (phi(d1) * (d1 * d2 - 1)
|
||||
/ (S_val * sigma_val * sigma_val * sqrt(tau_val)));
|
||||
double const formula_color = (-phi(d1) / (2 * S_val * tau_val * sigma_val * sqrt(tau_val))
|
||||
* (1
|
||||
+ (2 * r_val * tau_val - d2 * sigma_val * sqrt(tau_val)) * d1
|
||||
/ (sigma_val * sqrt(tau_val))));
|
||||
double const formula_ultima = -formula_vega
|
||||
* ((d1 * d2 * (1 - d1 * d2) + d1 * d1 + d2 * d2)
|
||||
/ (sigma_val * sigma_val));
|
||||
|
||||
auto call_greeks = grad(call_price, &S, &sigma, &tau, &r);
|
||||
auto put_greeks = grad(put_price, &S, &sigma, &tau, &r);
|
||||
|
||||
`grad(f, &x, &y, ...)` returns a `vector<rvar<double,N-1>` correspond to the gradient vector of `f` w.r.t. `x`,`y`, etc...
|
||||
|
||||
auto call_greeks_2nd_order = hess(call_price, &S, &sigma, &tau, &r);
|
||||
auto put_greeks_2nd_order = hess(put_price, &S, &sigma, &tau, &r);
|
||||
|
||||
`hess(f, &x, &y, ...)` returns a `vector<vector<rvar<double,N-1>>` correspond to the hessian of `f` w.r.t. `x`,`y`, etc...
|
||||
|
||||
auto call_greeks_3rd_order = grad_nd<3>(call_price, &S, &sigma, &tau, &r);
|
||||
auto put_greeks_3rd_order = grad_nd<3>(put_price, &S, &sigma, &tau, &r);
|
||||
|
||||
`grad_nd<N>(f, &x, &y, ...)` returns a `vector<vector<vector<rvar<double,N-1>>...` correspond to the gradient tensor of `f` w.r.t. `x`,`y`, etc...
|
||||
|
||||
std::cout << std::setprecision(std::numeric_limits<double>::digits10)
|
||||
<< "autodiff black-scholes call price = " << call_price.item() << "\n"
|
||||
<< "autodiff black-scholes put price = " << put_price.item() << "\n"
|
||||
<< "\n ## First-order Greeks \n"
|
||||
<< "autodiff call delta = " << call_greeks[0].item() << "\n"
|
||||
<< "formula call delta = " << formula_call_delta << "\n"
|
||||
<< "autodiff call vega = " << call_greeks[1].item() << '\n'
|
||||
<< " formula call vega = " << formula_vega << '\n'
|
||||
<< "autodiff call theta = " << -call_greeks[2].item() << '\n'
|
||||
<< " formula call theta = " << formula_call_theta << '\n'
|
||||
<< "autodiff call rho = " << call_greeks[3].item() << 'n'
|
||||
<< " formula call rho = " << formula_call_rho << '\n'
|
||||
<< '\n'
|
||||
<< "autodiff put delta = " << put_greeks[0].item() << 'n'
|
||||
<< " formula put delta = " << formula_put_delta << '\n'
|
||||
<< "autodiff put vega = " << put_greeks[1].item() << '\n'
|
||||
<< " formula put vega = " << formula_vega << '\n'
|
||||
<< "autodiff put theta = " << -put_greeks[2].item() << '\n'
|
||||
<< " formula put theta = " << formula_put_theta << '\n'
|
||||
<< "autodiff put rho = " << put_greeks[3].item() << '\n'
|
||||
<< " formula put rho = " << formula_put_rho << '\n'
|
||||
|
||||
<< "\n## Second-order Greeks\n"
|
||||
<< "autodiff call gamma = " << call_greeks_2nd_order[0][0].item() << '\n'
|
||||
<< "autodiff put gamma = " << put_greeks_2nd_order[0][0].item() << '\n'
|
||||
<< " formula gamma = " << formula_gamma << '\n'
|
||||
<< "autodiff call vanna = " << call_greeks_2nd_order[0][1].item() << '\n'
|
||||
<< "autodiff put vanna = " << put_greeks_2nd_order[0][1].item() << '\n'
|
||||
<< " formula vanna = " << formula_vanna << '\n'
|
||||
<< "autodiff call charm = " << -call_greeks_2nd_order[0][2].item() << '\n'
|
||||
<< "autodiff put charm = " << -put_greeks_2nd_order[0][2].item() << '\n'
|
||||
<< " formula charm = " << formula_charm << '\n'
|
||||
<< "autodiff call vomma = " << call_greeks_2nd_order[1][1].item() << '\n'
|
||||
<< "autodiff put vomma = " << put_greeks_2nd_order[1][1].item() << '\n'
|
||||
<< " formula vomma = " << formula_vomma << '\n'
|
||||
<< "autodiff call veta = " << call_greeks_2nd_order[1][2].item() << '\n'
|
||||
<< "autodiff put veta = " << put_greeks_2nd_order[1][2].item() << '\n'
|
||||
<< " formula veta = " << formula_veta << '\n'
|
||||
|
||||
<< "\n## Third-order Greeks\n"
|
||||
<< "autodiff call speed = " << call_greeks_3rd_order[0][0][0] << '\n'
|
||||
<< "autodiff put speed = " << put_greeks_3rd_order[0][0][0] << '\n'
|
||||
<< " formula speed = " << formula_speed << '\n'
|
||||
<< "autodiff call zomma = " << call_greeks_3rd_order[0][0][1] << '\n'
|
||||
<< "autodiff put zomma = " << put_greeks_3rd_order[0][0][1] << '\n'
|
||||
<< " formula zomma = " << formula_zomma << '\n'
|
||||
<< "autodiff call color = " << call_greeks_3rd_order[0][0][2] << '\n'
|
||||
<< "autodiff put color = " << put_greeks_3rd_order[0][0][2] << '\n'
|
||||
<< " formula color = " << formula_color << '\n'
|
||||
<< "autodiff call ultima = " << call_greeks_3rd_order[1][1][1] << '\n'
|
||||
<< "autodiff put ultima = " << put_greeks_3rd_order[1][1][1] << '\n'
|
||||
<< " formula ultima = " << formula_ultima << '\n';
|
||||
return 0;
|
||||
}
|
||||
|
||||
Some notes on the implementations of `grad`, `hess`, and `grad_nd`. Internally these functions correctly clear the tape and zero out the gradients, so manual `tape.zero_grad()` isn't needed. They however return copies of the adjoint values in a vector, so there can be some performance overhead over using `f.backward()` directly.
|
||||
|
||||
`grad`, `hess` and `grad_nd` are each overloaded twice. You can call
|
||||
|
||||
grad(rvar<RealType, DerivativeOrder_1> &f, std::vector<rvar<RealType, DerivativeOrder_2> *> &x)
|
||||
|
||||
if you want to take a gradient with respect to many variables, or conveniently
|
||||
|
||||
grad(f, &x, &y)
|
||||
|
||||
if you have only a few variables you need to take the gradient with respect to.
|
||||
The order of operations for `grad` matters. Although something like below is generally safe:
|
||||
|
||||
auto gv = grad(f,&x,&y,&z);
|
||||
auto hv = grad(gv[0], &x, &y &z);
|
||||
|
||||
The code below isn't, and will produce incorrect results:
|
||||
|
||||
auto hv = hess(f,&x,&y,&z)
|
||||
auto g3v = grad(hv[0][0], &x,&y,&z)
|
||||
|
||||
or:
|
||||
|
||||
auto hv = hess(f,&x,&y,&z)
|
||||
auto g4v = hess(hv[0][0], &x,&y,&z)
|
||||
|
||||
When in doubt, its always preferrable to compute higher order derivatives with:
|
||||
|
||||
auto g4 = grad_nd<4>(f, &x,&y,&z)
|
||||
|
||||
[h2:expression-templates-and-auto Expression Templates and auto]
|
||||
Reverse mode autodiff is expression template based. This means that some care has to be taken into considerations when writing code that uses this library. For example consider the code below:
|
||||
|
||||
rvar<double, 1> x = 1.0;
|
||||
rvar<double, 1> y = 2.0;
|
||||
rvar<double, 1> z = 3.0;
|
||||
auto w = x+y*z;
|
||||
|
||||
The type of `w` is not `rvar<double,1>` but `add_expr<rvar<double,1>,mult_expr<rvar<double,1>,rvar<double,1>>>` This means that the code below will not compile:
|
||||
|
||||
template<typename T>
|
||||
T f(T x)
|
||||
{
|
||||
return x*x;
|
||||
}
|
||||
int main()
|
||||
{
|
||||
rvar<double, 1> x = 1.0;
|
||||
auto v = f(2*x);
|
||||
}
|
||||
due to a type mismatch. It is also preferred to use auto for type deduction as often as possible. Consider the following 2 functions:
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <boost/math/differentiation/autodiff_reverse.hpp>
|
||||
#include <cmath>
|
||||
|
||||
using namespace boost::math::differentiation::reverse_mode;
|
||||
template<typename T>
|
||||
T testfunc_noauto(T& x, T& y, T& z)
|
||||
{
|
||||
T w1 = log(1 + abs(x)) * exp(y) + z;
|
||||
T w2 = pow(x + y, 2.5) / z;
|
||||
T w3 = sqrt(1 + x * y) * z * z;
|
||||
T w4 = w1 + w2 + w3;
|
||||
T w5 = w4 * w2;
|
||||
return w5;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T testfunc_auto(T& x, T& y, T& z)
|
||||
{
|
||||
auto w1 = log(1 + abs(x)) * exp(y) + z;
|
||||
auto w2 = pow(x + y, 2.5) / z;
|
||||
auto w3 = sqrt(1 + x * y) * z * z;
|
||||
auto w4 = w1 + w2 + w3;
|
||||
auto w5 = w4 * w2;
|
||||
return w5;
|
||||
}
|
||||
|
||||
and the corresponding benchmark:
|
||||
|
||||
template<class Func>
|
||||
void BM_RVar(benchmark::State& state, Func f)
|
||||
{
|
||||
using T = rvar<double, 1>;
|
||||
auto& tape = get_active_tape<T, 1>();
|
||||
for (auto _ : state) {
|
||||
tape.clear();
|
||||
T x(1.0), y(2.0), z(3.0);
|
||||
auto result = f(x, y, z);
|
||||
benchmark::DoNotOptimize(result);
|
||||
result.backward();
|
||||
benchmark::DoNotOptimize(x.adjoint());
|
||||
benchmark::DoNotOptimize(y.adjoint());
|
||||
benchmark::DoNotOptimize(z.adjoint());
|
||||
}
|
||||
}
|
||||
BENCHMARK([](benchmark::State& st) { BM_RVar(st, testfunc_auto<rvar<double, 1>>); });
|
||||
BENCHMARK([](benchmark::State& st) { BM_RVar(st, testfunc_noauto<rvar<double, 1>>); });
|
||||
BENCHMARK_MAIN();
|
||||
|
||||
Running this benchmark on a 13th Gen Intel(R) Core(TM) i7-13650HX, compiled with `-O3`:
|
||||
|
||||
./benchmark --benchmark_filter=testfunc_auto
|
||||
|
||||
--------------------------------------------------------------------------------------------------------------------
|
||||
Benchmark Time CPU Iterations
|
||||
--------------------------------------------------------------------------------------------------------------------
|
||||
[](benchmark::State& st) { BM_RVar(st, testfunc_auto<rvar<double, 1>>); } 199546 ns 199503 ns 10000
|
||||
|
||||
and
|
||||
|
||||
./benchmark --benchmark_filter=testfunc_noauto
|
||||
|
||||
----------------------------------------------------------------------------------------------------------------------
|
||||
Benchmark Time CPU Iterations
|
||||
----------------------------------------------------------------------------------------------------------------------
|
||||
[](benchmark::State& st) { BM_RVar(st, testfunc_noauto<rvar<double, 1>>); } 375731 ns 375669 ns 10000
|
||||
|
||||
You get nearly 2x speedup on the code that uses auto.
|
||||
|
||||
[endsect]
|
||||
2
doc/equations/autodiff/reverse_mode_autodiff_ex_eq.svg
Normal file
|
After Width: | Height: | Size: 79 KiB |
1230
doc/graphs/autodiff/autodiff_reverse_graph.svg
Normal file
|
After Width: | Height: | Size: 65 KiB |
2255
doc/html/math_toolkit/autodiff0.html
Normal file
207
doc/html/math_toolkit/dist_ref/dists/holtsmark_dist.html
Normal file
@@ -0,0 +1,207 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>Holtsmark Distribution</title>
|
||||
<link rel="stylesheet" href="../../../math.css" type="text/css">
|
||||
<meta name="generator" content="DocBook XSL Stylesheets Vsnapshot">
|
||||
<link rel="home" href="../../../index.html" title="Math Toolkit 4.2.1">
|
||||
<link rel="up" href="../dists.html" title="Distributions">
|
||||
<link rel="prev" href="geometric_dist.html" title="Geometric Distribution">
|
||||
<link rel="next" href="hyperexponential_dist.html" title="Hyperexponential Distribution">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
|
||||
<table cellpadding="2" width="100%"><tr>
|
||||
<td valign="top"><img alt="Boost C++ Libraries" width="277" height="86" src="../../../../../../../boost.png"></td>
|
||||
<td align="center"><a href="../../../../../../../index.html">Home</a></td>
|
||||
<td align="center"><a href="../../../../../../../libs/libraries.htm">Libraries</a></td>
|
||||
<td align="center"><a href="http://www.boost.org/users/people.html">People</a></td>
|
||||
<td align="center"><a href="http://www.boost.org/users/faq.html">FAQ</a></td>
|
||||
<td align="center"><a href="../../../../../../../more/index.htm">More</a></td>
|
||||
</tr></table>
|
||||
<hr>
|
||||
<div class="spirit-nav">
|
||||
<a accesskey="p" href="geometric_dist.html"><img src="../../../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../dists.html"><img src="../../../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../../../index.html"><img src="../../../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="hyperexponential_dist.html"><img src="../../../../../../../doc/src/images/next.png" alt="Next"></a>
|
||||
</div>
|
||||
<div class="section">
|
||||
<div class="titlepage"><div><div><h4 class="title">
|
||||
<a name="math_toolkit.dist_ref.dists.holtsmark_dist"></a><a class="link" href="holtsmark_dist.html" title="Holtsmark Distribution">Holtsmark
|
||||
Distribution</a>
|
||||
</h4></div></div></div>
|
||||
<pre class="programlisting"><span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">math</span><span class="special">/</span><span class="identifier">distributions</span><span class="special">/</span><span class="identifier">holtsmark</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span></pre>
|
||||
<pre class="programlisting"><span class="keyword">template</span> <span class="special"><</span><span class="keyword">class</span> <span class="identifier">RealType</span> <span class="special">=</span> <span class="keyword">double</span><span class="special">,</span>
|
||||
<span class="keyword">class</span> <a class="link" href="../../../policy.html" title="Chapter 22. Policies: Controlling Precision, Error Handling etc">Policy</a> <span class="special">=</span> <a class="link" href="../../pol_ref/pol_ref_ref.html" title="Policy Class Reference">policies::policy<></a> <span class="special">></span>
|
||||
<span class="keyword">class</span> <span class="identifier">holtsmark_distribution</span><span class="special">;</span>
|
||||
|
||||
<span class="keyword">typedef</span> <span class="identifier">holtsmark_distribution</span><span class="special"><></span> <span class="identifier">holtsmark</span><span class="special">;</span>
|
||||
|
||||
<span class="keyword">template</span> <span class="special"><</span><span class="keyword">class</span> <span class="identifier">RealType</span><span class="special">,</span> <span class="keyword">class</span> <a class="link" href="../../../policy.html" title="Chapter 22. Policies: Controlling Precision, Error Handling etc">Policy</a><span class="special">></span>
|
||||
<span class="keyword">class</span> <span class="identifier">holtsmark_distribution</span>
|
||||
<span class="special">{</span>
|
||||
<span class="keyword">public</span><span class="special">:</span>
|
||||
<span class="keyword">typedef</span> <span class="identifier">RealType</span> <span class="identifier">value_type</span><span class="special">;</span>
|
||||
<span class="keyword">typedef</span> <span class="identifier">Policy</span> <span class="identifier">policy_type</span><span class="special">;</span>
|
||||
|
||||
<span class="identifier">BOOST_MATH_GPU_ENABLED</span> <span class="identifier">holtsmark_distribution</span><span class="special">(</span><span class="identifier">RealType</span> <span class="identifier">location</span> <span class="special">=</span> <span class="number">0</span><span class="special">,</span> <span class="identifier">RealType</span> <span class="identifier">scale</span> <span class="special">=</span> <span class="number">1</span><span class="special">);</span>
|
||||
|
||||
<span class="identifier">BOOST_MATH_GPU_ENABLED</span> <span class="identifier">RealType</span> <span class="identifier">location</span><span class="special">()</span><span class="keyword">const</span><span class="special">;</span>
|
||||
<span class="identifier">BOOST_MATH_GPU_ENABLED</span> <span class="identifier">RealType</span> <span class="identifier">scale</span><span class="special">()</span><span class="keyword">const</span><span class="special">;</span>
|
||||
<span class="special">};</span>
|
||||
</pre>
|
||||
<p>
|
||||
The <a href="http://en.wikipedia.org/wiki/holtsmark_distribution" target="_top">Holtsmark
|
||||
distribution</a> is named after Johan Peter Holtsmark. It is special
|
||||
case of a <a href="http://en.wikipedia.org/wiki/Stable_distribution" target="_top">stable
|
||||
distribution</a> with shape parameter α=3/2, β=0.
|
||||
</p>
|
||||
<p>
|
||||
<a href="http://en.wikipedia.org/wiki/Probability_distribution" target="_top">probability
|
||||
distribution function PDF</a> given by:
|
||||
</p>
|
||||
<div class="blockquote"><blockquote class="blockquote"><p>
|
||||
<span class="inlinemediaobject"><img src="../../../../equations/holtsmark_ref1.svg"></span>
|
||||
|
||||
</p></blockquote></div>
|
||||
<p>
|
||||
The location parameter μ is the location of the distribution, while the scale
|
||||
parameter [c] determines the width of the distribution. If the location
|
||||
is zero, and the scale 1, then the result is a standard holtsmark distribution.
|
||||
</p>
|
||||
<p>
|
||||
The distribution especially used in astrophysics for modeling gravitational
|
||||
bodies.
|
||||
</p>
|
||||
<p>
|
||||
The following graph shows how the distributions moves as the location parameter
|
||||
changes:
|
||||
</p>
|
||||
<div class="blockquote"><blockquote class="blockquote"><p>
|
||||
<span class="inlinemediaobject"><img src="../../../../graphs/holtsmark_pdf1.svg" align="middle"></span>
|
||||
|
||||
</p></blockquote></div>
|
||||
<p>
|
||||
While the following graph shows how the shape (scale) parameter alters
|
||||
the distribution:
|
||||
</p>
|
||||
<div class="blockquote"><blockquote class="blockquote"><p>
|
||||
<span class="inlinemediaobject"><img src="../../../../graphs/holtsmark_pdf2.svg" align="middle"></span>
|
||||
|
||||
</p></blockquote></div>
|
||||
<h5>
|
||||
<a name="math_toolkit.dist_ref.dists.holtsmark_dist.h0"></a>
|
||||
<span class="phrase"><a name="math_toolkit.dist_ref.dists.holtsmark_dist.member_functions"></a></span><a class="link" href="holtsmark_dist.html#math_toolkit.dist_ref.dists.holtsmark_dist.member_functions">Member
|
||||
Functions</a>
|
||||
</h5>
|
||||
<pre class="programlisting"><span class="identifier">BOOST_MATH_GPU_ENABLED</span> <span class="identifier">holtsmark_distribution</span><span class="special">(</span><span class="identifier">RealType</span> <span class="identifier">location</span> <span class="special">=</span> <span class="number">0</span><span class="special">,</span> <span class="identifier">RealType</span> <span class="identifier">scale</span> <span class="special">=</span> <span class="number">1</span><span class="special">);</span>
|
||||
</pre>
|
||||
<p>
|
||||
Constructs a holtsmark distribution, with location parameter <span class="emphasis"><em>location</em></span>
|
||||
and scale parameter <span class="emphasis"><em>scale</em></span>. When these parameters take
|
||||
their default values (location = 0, scale = 1) then the result is a Standard
|
||||
holtsmark Distribution.
|
||||
</p>
|
||||
<p>
|
||||
Requires scale > 0, otherwise calls <a class="link" href="../../error_handling.html#math_toolkit.error_handling.domain_error">domain_error</a>.
|
||||
</p>
|
||||
<pre class="programlisting"><span class="identifier">BOOST_MATH_GPU_ENABLED</span> <span class="identifier">RealType</span> <span class="identifier">location</span><span class="special">()</span><span class="keyword">const</span><span class="special">;</span>
|
||||
</pre>
|
||||
<p>
|
||||
Returns the location parameter of the distribution.
|
||||
</p>
|
||||
<pre class="programlisting"><span class="identifier">BOOST_MATH_GPU_ENABLED</span> <span class="identifier">RealType</span> <span class="identifier">scale</span><span class="special">()</span><span class="keyword">const</span><span class="special">;</span>
|
||||
</pre>
|
||||
<p>
|
||||
Returns the scale parameter of the distribution.
|
||||
</p>
|
||||
<h5>
|
||||
<a name="math_toolkit.dist_ref.dists.holtsmark_dist.h1"></a>
|
||||
<span class="phrase"><a name="math_toolkit.dist_ref.dists.holtsmark_dist.non_member_accessors"></a></span><a class="link" href="holtsmark_dist.html#math_toolkit.dist_ref.dists.holtsmark_dist.non_member_accessors">Non-member
|
||||
Accessors</a>
|
||||
</h5>
|
||||
<p>
|
||||
All the <a class="link" href="../nmp.html" title="Non-Member Properties">usual non-member accessor
|
||||
functions</a> that are generic to all distributions are supported:
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.cdf">Cumulative Distribution Function</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.pdf">Probability Density Function</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.quantile">Quantile</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.hazard">Hazard Function</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.chf">Cumulative Hazard Function</a>,
|
||||
__logcdf, __logpdf, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.mean">mean</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.median">median</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.mode">mode</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.variance">variance</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.sd">standard deviation</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.skewness">skewness</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.kurtosis">kurtosis</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.kurtosis_excess">kurtosis_excess</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.range">range</a> and <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.support">support</a>. For this distribution
|
||||
all non-member accessor functions are marked with <code class="computeroutput"><span class="identifier">BOOST_MATH_GPU_ENABLED</span></code>
|
||||
and can be run on both host and device.
|
||||
</p>
|
||||
<p>
|
||||
Note however that the holtsmark distribution does not have a skewness,
|
||||
kurtosis, etc. See <a class="link" href="../../pol_ref/assert_undefined.html" title="Mathematically Undefined Function Policies">mathematically
|
||||
undefined function</a> to control whether these should fail to compile
|
||||
with a BOOST_STATIC_ASSERTION_FAILURE, which is the default.
|
||||
</p>
|
||||
<p>
|
||||
Alternately, the functions <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.skewness">skewness</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.kurtosis">kurtosis</a> and
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.kurtosis_excess">kurtosis_excess</a>
|
||||
will all return a <a class="link" href="../../error_handling.html#math_toolkit.error_handling.domain_error">domain_error</a>
|
||||
if called.
|
||||
</p>
|
||||
<p>
|
||||
The domain of the random variable is [-[max_value], +[min_value]].
|
||||
</p>
|
||||
<h5>
|
||||
<a name="math_toolkit.dist_ref.dists.holtsmark_dist.h2"></a>
|
||||
<span class="phrase"><a name="math_toolkit.dist_ref.dists.holtsmark_dist.accuracy"></a></span><a class="link" href="holtsmark_dist.html#math_toolkit.dist_ref.dists.holtsmark_dist.accuracy">Accuracy</a>
|
||||
</h5>
|
||||
<p>
|
||||
The error is within 4 epsilon.
|
||||
</p>
|
||||
<p>
|
||||
Errors in the PDF at 64-bit double precision:
|
||||
</p>
|
||||
<p>
|
||||
<span class="inlinemediaobject"><img src="../../../../graphs/holtsmark_pdf_accuracy_64.png"></span>
|
||||
</p>
|
||||
<p>
|
||||
Errors in the CDF-complement at 64-bit double precision:
|
||||
</p>
|
||||
<p>
|
||||
<span class="inlinemediaobject"><img src="../../../../graphs/holtsmark_ccdf_accuracy_64.png"></span>
|
||||
</p>
|
||||
<h5>
|
||||
<a name="math_toolkit.dist_ref.dists.holtsmark_dist.h3"></a>
|
||||
<span class="phrase"><a name="math_toolkit.dist_ref.dists.holtsmark_dist.implementation"></a></span><a class="link" href="holtsmark_dist.html#math_toolkit.dist_ref.dists.holtsmark_dist.implementation">Implementation</a>
|
||||
</h5>
|
||||
<p>
|
||||
See references.
|
||||
</p>
|
||||
<h5>
|
||||
<a name="math_toolkit.dist_ref.dists.holtsmark_dist.h4"></a>
|
||||
<span class="phrase"><a name="math_toolkit.dist_ref.dists.holtsmark_dist.references"></a></span><a class="link" href="holtsmark_dist.html#math_toolkit.dist_ref.dists.holtsmark_dist.references">References</a>
|
||||
</h5>
|
||||
<div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; ">
|
||||
<li class="listitem">
|
||||
<a href="http://en.wikipedia.org/wiki/holtsmark_distribution" target="_top">Holtsmark
|
||||
Distribution</a>
|
||||
</li>
|
||||
<li class="listitem">
|
||||
T. Yoshimura, Numerical Evaluation and High Precision Approximation
|
||||
Formula for Holtsmark Distribution, DOI: 10.36227/techrxiv.172054657.73020014/v1,
|
||||
2024.
|
||||
</li>
|
||||
</ul></div>
|
||||
</div>
|
||||
<div class="copyright-footer">Copyright © 2006-2021 Nikhar Agrawal, Anton Bikineev, Matthew Borland,
|
||||
Paul A. Bristow, Marco Guazzone, Christopher Kormanyos, Hubert Holin, Bruno
|
||||
Lalande, John Maddock, Evan Miller, Jeremy Murphy, Matthew Pulver, Johan Råde,
|
||||
Gautam Sewani, Benjamin Sobotta, Nicholas Thompson, Thijs van den Berg, Daryle
|
||||
Walker and Xiaogang Zhang<p>
|
||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
file LICENSE_1_0.txt or copy at <a href="http://www.boost.org/LICENSE_1_0.txt" target="_top">http://www.boost.org/LICENSE_1_0.txt</a>)
|
||||
</p>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="spirit-nav">
|
||||
<a accesskey="p" href="geometric_dist.html"><img src="../../../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../dists.html"><img src="../../../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../../../index.html"><img src="../../../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="hyperexponential_dist.html"><img src="../../../../../../../doc/src/images/next.png" alt="Next"></a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
224
doc/html/math_toolkit/dist_ref/dists/landau_dist.html
Normal file
@@ -0,0 +1,224 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>Landau Distribution</title>
|
||||
<link rel="stylesheet" href="../../../math.css" type="text/css">
|
||||
<meta name="generator" content="DocBook XSL Stylesheets Vsnapshot">
|
||||
<link rel="home" href="../../../index.html" title="Math Toolkit 4.2.1">
|
||||
<link rel="up" href="../dists.html" title="Distributions">
|
||||
<link rel="prev" href="kolmogorov_smirnov_dist.html" title="Kolmogorov-Smirnov Distribution">
|
||||
<link rel="next" href="laplace_dist.html" title="Laplace Distribution">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
|
||||
<table cellpadding="2" width="100%"><tr>
|
||||
<td valign="top"><img alt="Boost C++ Libraries" width="277" height="86" src="../../../../../../../boost.png"></td>
|
||||
<td align="center"><a href="../../../../../../../index.html">Home</a></td>
|
||||
<td align="center"><a href="../../../../../../../libs/libraries.htm">Libraries</a></td>
|
||||
<td align="center"><a href="http://www.boost.org/users/people.html">People</a></td>
|
||||
<td align="center"><a href="http://www.boost.org/users/faq.html">FAQ</a></td>
|
||||
<td align="center"><a href="../../../../../../../more/index.htm">More</a></td>
|
||||
</tr></table>
|
||||
<hr>
|
||||
<div class="spirit-nav">
|
||||
<a accesskey="p" href="kolmogorov_smirnov_dist.html"><img src="../../../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../dists.html"><img src="../../../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../../../index.html"><img src="../../../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="laplace_dist.html"><img src="../../../../../../../doc/src/images/next.png" alt="Next"></a>
|
||||
</div>
|
||||
<div class="section">
|
||||
<div class="titlepage"><div><div><h4 class="title">
|
||||
<a name="math_toolkit.dist_ref.dists.landau_dist"></a><a class="link" href="landau_dist.html" title="Landau Distribution">Landau Distribution</a>
|
||||
</h4></div></div></div>
|
||||
<pre class="programlisting"><span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">math</span><span class="special">/</span><span class="identifier">distributions</span><span class="special">/</span><span class="identifier">landau</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span></pre>
|
||||
<pre class="programlisting"><span class="keyword">template</span> <span class="special"><</span><span class="keyword">class</span> <span class="identifier">RealType</span> <span class="special">=</span> <span class="keyword">double</span><span class="special">,</span>
|
||||
<span class="keyword">class</span> <a class="link" href="../../../policy.html" title="Chapter 22. Policies: Controlling Precision, Error Handling etc">Policy</a> <span class="special">=</span> <a class="link" href="../../pol_ref/pol_ref_ref.html" title="Policy Class Reference">policies::policy<></a> <span class="special">></span>
|
||||
<span class="keyword">class</span> <span class="identifier">landau_distribution</span><span class="special">;</span>
|
||||
|
||||
<span class="keyword">typedef</span> <span class="identifier">landau_distribution</span><span class="special"><></span> <span class="identifier">landau</span><span class="special">;</span>
|
||||
|
||||
<span class="keyword">template</span> <span class="special"><</span><span class="keyword">class</span> <span class="identifier">RealType</span><span class="special">,</span> <span class="keyword">class</span> <a class="link" href="../../../policy.html" title="Chapter 22. Policies: Controlling Precision, Error Handling etc">Policy</a><span class="special">></span>
|
||||
<span class="keyword">class</span> <span class="identifier">landau_distribution</span>
|
||||
<span class="special">{</span>
|
||||
<span class="keyword">public</span><span class="special">:</span>
|
||||
<span class="keyword">typedef</span> <span class="identifier">RealType</span> <span class="identifier">value_type</span><span class="special">;</span>
|
||||
<span class="keyword">typedef</span> <span class="identifier">Policy</span> <span class="identifier">policy_type</span><span class="special">;</span>
|
||||
|
||||
<span class="identifier">BOOST_MATH_GPU_ENABLED</span> <span class="identifier">landau_distribution</span><span class="special">(</span><span class="identifier">RealType</span> <span class="identifier">location</span> <span class="special">=</span> <span class="number">0</span><span class="special">,</span> <span class="identifier">RealType</span> <span class="identifier">scale</span> <span class="special">=</span> <span class="number">1</span><span class="special">);</span>
|
||||
|
||||
<span class="identifier">BOOST_MATH_GPU_ENABLED</span> <span class="identifier">RealType</span> <span class="identifier">location</span><span class="special">()</span><span class="keyword">const</span><span class="special">;</span>
|
||||
<span class="identifier">BOOST_MATH_GPU_ENABLED</span> <span class="identifier">RealType</span> <span class="identifier">scale</span><span class="special">()</span><span class="keyword">const</span><span class="special">;</span>
|
||||
<span class="identifier">BOOST_MATH_GPU_ENABLED</span> <span class="identifier">RealType</span> <span class="identifier">bias</span><span class="special">()</span><span class="keyword">const</span><span class="special">;</span>
|
||||
<span class="special">};</span>
|
||||
</pre>
|
||||
<p>
|
||||
The <a href="http://en.wikipedia.org/wiki/landau_distribution" target="_top">Landau
|
||||
distribution</a> is named after Lev Landau. It is special case of a
|
||||
<a href="http://en.wikipedia.org/wiki/Stable_distribution" target="_top">stable distribution</a>
|
||||
with shape parameter α=1, β=1.
|
||||
</p>
|
||||
<p>
|
||||
<a href="http://en.wikipedia.org/wiki/Probability_distribution" target="_top">probability
|
||||
distribution function PDF</a> given by:
|
||||
</p>
|
||||
<div class="blockquote"><blockquote class="blockquote"><p>
|
||||
<span class="inlinemediaobject"><img src="../../../../equations/landau_ref1.svg"></span>
|
||||
|
||||
</p></blockquote></div>
|
||||
<p>
|
||||
The location parameter μ is the location of the distribution, while the scale
|
||||
parameter [c] determines the width of the distribution, but unlike other
|
||||
scalable distributions, it has a peculiarity that changes the location
|
||||
of the distribution. If the location is zero, and the scale 1, then the
|
||||
result is a standard landau distribution.
|
||||
</p>
|
||||
<p>
|
||||
The distribution describe the statistical property of the energy loss by
|
||||
charged particles as they traversing a thin layer of matter.
|
||||
</p>
|
||||
<p>
|
||||
The following graph shows how the distributions moves as the location parameter
|
||||
changes:
|
||||
</p>
|
||||
<div class="blockquote"><blockquote class="blockquote"><p>
|
||||
<span class="inlinemediaobject"><img src="../../../../graphs/landau_pdf1.svg" align="middle"></span>
|
||||
|
||||
</p></blockquote></div>
|
||||
<p>
|
||||
While the following graph shows how the shape (scale) parameter alters
|
||||
the distribution:
|
||||
</p>
|
||||
<div class="blockquote"><blockquote class="blockquote"><p>
|
||||
<span class="inlinemediaobject"><img src="../../../../graphs/landau_pdf2.svg" align="middle"></span>
|
||||
|
||||
</p></blockquote></div>
|
||||
<h5>
|
||||
<a name="math_toolkit.dist_ref.dists.landau_dist.h0"></a>
|
||||
<span class="phrase"><a name="math_toolkit.dist_ref.dists.landau_dist.member_functions"></a></span><a class="link" href="landau_dist.html#math_toolkit.dist_ref.dists.landau_dist.member_functions">Member
|
||||
Functions</a>
|
||||
</h5>
|
||||
<pre class="programlisting"><span class="identifier">BOOST_MATH_GPU_ENABLED</span> <span class="identifier">landau_distribution</span><span class="special">(</span><span class="identifier">RealType</span> <span class="identifier">location</span> <span class="special">=</span> <span class="number">0</span><span class="special">,</span> <span class="identifier">RealType</span> <span class="identifier">scale</span> <span class="special">=</span> <span class="number">1</span><span class="special">);</span>
|
||||
</pre>
|
||||
<p>
|
||||
Constructs a landau distribution, with location parameter <span class="emphasis"><em>location</em></span>
|
||||
and scale parameter <span class="emphasis"><em>scale</em></span>. When these parameters take
|
||||
their default values (location = 0, scale = 1) then the result is a Standard
|
||||
landau Distribution.
|
||||
</p>
|
||||
<p>
|
||||
Requires scale > 0, otherwise calls <a class="link" href="../../error_handling.html#math_toolkit.error_handling.domain_error">domain_error</a>.
|
||||
</p>
|
||||
<pre class="programlisting"><span class="identifier">BOOST_MATH_GPU_ENABLED</span> <span class="identifier">RealType</span> <span class="identifier">location</span><span class="special">()</span><span class="keyword">const</span><span class="special">;</span>
|
||||
</pre>
|
||||
<p>
|
||||
Returns the location parameter of the distribution.
|
||||
</p>
|
||||
<pre class="programlisting"><span class="identifier">BOOST_MATH_GPU_ENABLED</span> <span class="identifier">RealType</span> <span class="identifier">scale</span><span class="special">()</span><span class="keyword">const</span><span class="special">;</span>
|
||||
</pre>
|
||||
<p>
|
||||
Returns the scale parameter of the distribution.
|
||||
</p>
|
||||
<pre class="programlisting"><span class="identifier">BOOST_MATH_GPU_ENABLED</span> <span class="identifier">RealType</span> <span class="identifier">bias</span><span class="special">()</span><span class="keyword">const</span><span class="special">;</span>
|
||||
</pre>
|
||||
<p>
|
||||
Returns the amount of translation by the scale parameter.
|
||||
</p>
|
||||
<div class="blockquote"><blockquote class="blockquote"><p>
|
||||
<span class="serif_italic">bias = - 2 / π log(c)</span>
|
||||
</p></blockquote></div>
|
||||
<h5>
|
||||
<a name="math_toolkit.dist_ref.dists.landau_dist.h1"></a>
|
||||
<span class="phrase"><a name="math_toolkit.dist_ref.dists.landau_dist.non_member_accessors"></a></span><a class="link" href="landau_dist.html#math_toolkit.dist_ref.dists.landau_dist.non_member_accessors">Non-member
|
||||
Accessors</a>
|
||||
</h5>
|
||||
<p>
|
||||
All the <a class="link" href="../nmp.html" title="Non-Member Properties">usual non-member accessor
|
||||
functions</a> that are generic to all distributions are supported:
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.cdf">Cumulative Distribution Function</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.pdf">Probability Density Function</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.quantile">Quantile</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.hazard">Hazard Function</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.chf">Cumulative Hazard Function</a>,
|
||||
__logcdf, __logpdf, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.mean">mean</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.median">median</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.mode">mode</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.variance">variance</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.sd">standard deviation</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.skewness">skewness</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.kurtosis">kurtosis</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.kurtosis_excess">kurtosis_excess</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.range">range</a> and <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.support">support</a>. For this distribution
|
||||
all non-member accessor functions are marked with <code class="computeroutput"><span class="identifier">BOOST_MATH_GPU_ENABLED</span></code>
|
||||
and can be run on both host and device.
|
||||
</p>
|
||||
<p>
|
||||
Note however that the landau distribution does not have a mean, standard
|
||||
deviation, etc. See <a class="link" href="../../pol_ref/assert_undefined.html" title="Mathematically Undefined Function Policies">mathematically
|
||||
undefined function</a> to control whether these should fail to compile
|
||||
with a BOOST_STATIC_ASSERTION_FAILURE, which is the default.
|
||||
</p>
|
||||
<p>
|
||||
Alternately, the functions <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.mean">mean</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.sd">standard deviation</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.variance">variance</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.skewness">skewness</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.kurtosis">kurtosis</a>
|
||||
and <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.kurtosis_excess">kurtosis_excess</a>
|
||||
will all return a <a class="link" href="../../error_handling.html#math_toolkit.error_handling.domain_error">domain_error</a>
|
||||
if called.
|
||||
</p>
|
||||
<p>
|
||||
The domain of the random variable is [-[max_value], +[min_value]].
|
||||
</p>
|
||||
<h5>
|
||||
<a name="math_toolkit.dist_ref.dists.landau_dist.h2"></a>
|
||||
<span class="phrase"><a name="math_toolkit.dist_ref.dists.landau_dist.accuracy"></a></span><a class="link" href="landau_dist.html#math_toolkit.dist_ref.dists.landau_dist.accuracy">Accuracy</a>
|
||||
</h5>
|
||||
<p>
|
||||
The error is within 4 epsilon except for the rapidly decaying left tail.
|
||||
</p>
|
||||
<p>
|
||||
Errors in the PDF at 64-bit double precision:
|
||||
</p>
|
||||
<p>
|
||||
<span class="inlinemediaobject"><img src="../../../../graphs/landau_pdf_accuracy_64.png"></span>
|
||||
</p>
|
||||
<p>
|
||||
Errors in the CDF at 64-bit double precision:
|
||||
</p>
|
||||
<p>
|
||||
<span class="inlinemediaobject"><img src="../../../../graphs/landau_cdf_accuracy_64.png"></span>
|
||||
</p>
|
||||
<p>
|
||||
Errors in the CDF-complement at 64-bit double precision:
|
||||
</p>
|
||||
<p>
|
||||
<span class="inlinemediaobject"><img src="../../../../graphs/landau_ccdf_accuracy_64.png"></span>
|
||||
</p>
|
||||
<h5>
|
||||
<a name="math_toolkit.dist_ref.dists.landau_dist.h3"></a>
|
||||
<span class="phrase"><a name="math_toolkit.dist_ref.dists.landau_dist.implementation"></a></span><a class="link" href="landau_dist.html#math_toolkit.dist_ref.dists.landau_dist.implementation">Implementation</a>
|
||||
</h5>
|
||||
<p>
|
||||
See references.
|
||||
</p>
|
||||
<h5>
|
||||
<a name="math_toolkit.dist_ref.dists.landau_dist.h4"></a>
|
||||
<span class="phrase"><a name="math_toolkit.dist_ref.dists.landau_dist.references"></a></span><a class="link" href="landau_dist.html#math_toolkit.dist_ref.dists.landau_dist.references">References</a>
|
||||
</h5>
|
||||
<div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; ">
|
||||
<li class="listitem">
|
||||
<a href="http://en.wikipedia.org/wiki/landau_distribution" target="_top">landau
|
||||
distribution</a>
|
||||
</li>
|
||||
<li class="listitem">
|
||||
T. Yoshimura, Numerical Evaluation and High Precision Approximation
|
||||
Formula for Landau Distribution, DOI: 10.36227/techrxiv.171822215.53612870/v2,
|
||||
2024.
|
||||
</li>
|
||||
</ul></div>
|
||||
</div>
|
||||
<div class="copyright-footer">Copyright © 2006-2021 Nikhar Agrawal, Anton Bikineev, Matthew Borland,
|
||||
Paul A. Bristow, Marco Guazzone, Christopher Kormanyos, Hubert Holin, Bruno
|
||||
Lalande, John Maddock, Evan Miller, Jeremy Murphy, Matthew Pulver, Johan Råde,
|
||||
Gautam Sewani, Benjamin Sobotta, Nicholas Thompson, Thijs van den Berg, Daryle
|
||||
Walker and Xiaogang Zhang<p>
|
||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
file LICENSE_1_0.txt or copy at <a href="http://www.boost.org/LICENSE_1_0.txt" target="_top">http://www.boost.org/LICENSE_1_0.txt</a>)
|
||||
</p>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="spirit-nav">
|
||||
<a accesskey="p" href="kolmogorov_smirnov_dist.html"><img src="../../../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../dists.html"><img src="../../../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../../../index.html"><img src="../../../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="laplace_dist.html"><img src="../../../../../../../doc/src/images/next.png" alt="Next"></a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
215
doc/html/math_toolkit/dist_ref/dists/mapairy_dist.html
Normal file
@@ -0,0 +1,215 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>Map-Airy Distribution</title>
|
||||
<link rel="stylesheet" href="../../../math.css" type="text/css">
|
||||
<meta name="generator" content="DocBook XSL Stylesheets Vsnapshot">
|
||||
<link rel="home" href="../../../index.html" title="Math Toolkit 4.2.1">
|
||||
<link rel="up" href="../dists.html" title="Distributions">
|
||||
<link rel="prev" href="lognormal_dist.html" title="Log Normal Distribution">
|
||||
<link rel="next" href="negative_binomial_dist.html" title="Negative Binomial Distribution">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
|
||||
<table cellpadding="2" width="100%"><tr>
|
||||
<td valign="top"><img alt="Boost C++ Libraries" width="277" height="86" src="../../../../../../../boost.png"></td>
|
||||
<td align="center"><a href="../../../../../../../index.html">Home</a></td>
|
||||
<td align="center"><a href="../../../../../../../libs/libraries.htm">Libraries</a></td>
|
||||
<td align="center"><a href="http://www.boost.org/users/people.html">People</a></td>
|
||||
<td align="center"><a href="http://www.boost.org/users/faq.html">FAQ</a></td>
|
||||
<td align="center"><a href="../../../../../../../more/index.htm">More</a></td>
|
||||
</tr></table>
|
||||
<hr>
|
||||
<div class="spirit-nav">
|
||||
<a accesskey="p" href="lognormal_dist.html"><img src="../../../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../dists.html"><img src="../../../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../../../index.html"><img src="../../../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="negative_binomial_dist.html"><img src="../../../../../../../doc/src/images/next.png" alt="Next"></a>
|
||||
</div>
|
||||
<div class="section">
|
||||
<div class="titlepage"><div><div><h4 class="title">
|
||||
<a name="math_toolkit.dist_ref.dists.mapairy_dist"></a><a class="link" href="mapairy_dist.html" title="Map-Airy Distribution">Map-Airy
|
||||
Distribution</a>
|
||||
</h4></div></div></div>
|
||||
<pre class="programlisting"><span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">math</span><span class="special">/</span><span class="identifier">distributions</span><span class="special">/</span><span class="identifier">mapairy</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span></pre>
|
||||
<pre class="programlisting"><span class="keyword">template</span> <span class="special"><</span><span class="keyword">class</span> <span class="identifier">RealType</span> <span class="special">=</span> <span class="keyword">double</span><span class="special">,</span>
|
||||
<span class="keyword">class</span> <a class="link" href="../../../policy.html" title="Chapter 22. Policies: Controlling Precision, Error Handling etc">Policy</a> <span class="special">=</span> <a class="link" href="../../pol_ref/pol_ref_ref.html" title="Policy Class Reference">policies::policy<></a> <span class="special">></span>
|
||||
<span class="keyword">class</span> <span class="identifier">mapairy_distribution</span><span class="special">;</span>
|
||||
|
||||
<span class="keyword">typedef</span> <span class="identifier">mapairy_distribution</span><span class="special"><></span> <span class="identifier">mapairy</span><span class="special">;</span>
|
||||
|
||||
<span class="keyword">template</span> <span class="special"><</span><span class="keyword">class</span> <span class="identifier">RealType</span><span class="special">,</span> <span class="keyword">class</span> <a class="link" href="../../../policy.html" title="Chapter 22. Policies: Controlling Precision, Error Handling etc">Policy</a><span class="special">></span>
|
||||
<span class="keyword">class</span> <span class="identifier">mapairy_distribution</span>
|
||||
<span class="special">{</span>
|
||||
<span class="keyword">public</span><span class="special">:</span>
|
||||
<span class="keyword">typedef</span> <span class="identifier">RealType</span> <span class="identifier">value_type</span><span class="special">;</span>
|
||||
<span class="keyword">typedef</span> <span class="identifier">Policy</span> <span class="identifier">policy_type</span><span class="special">;</span>
|
||||
|
||||
<span class="identifier">BOOST_MATH_GPU_ENABLED</span> <span class="identifier">mapairy_distribution</span><span class="special">(</span><span class="identifier">RealType</span> <span class="identifier">location</span> <span class="special">=</span> <span class="number">0</span><span class="special">,</span> <span class="identifier">RealType</span> <span class="identifier">scale</span> <span class="special">=</span> <span class="number">1</span><span class="special">);</span>
|
||||
|
||||
<span class="identifier">BOOST_MATH_GPU_ENABLED</span> <span class="identifier">RealType</span> <span class="identifier">location</span><span class="special">()</span><span class="keyword">const</span><span class="special">;</span>
|
||||
<span class="identifier">BOOST_MATH_GPU_ENABLED</span> <span class="identifier">RealType</span> <span class="identifier">scale</span><span class="special">()</span><span class="keyword">const</span><span class="special">;</span>
|
||||
<span class="special">};</span>
|
||||
</pre>
|
||||
<p>
|
||||
It is special case of a <a href="http://en.wikipedia.org/wiki/Stable_distribution" target="_top">stable
|
||||
distribution</a> with shape parameter α=3/2, β=1.
|
||||
</p>
|
||||
<p>
|
||||
This distribution is also defined as β = −1, which is inverted about the
|
||||
x-axis.
|
||||
</p>
|
||||
<p>
|
||||
<a href="http://en.wikipedia.org/wiki/Probability_distribution" target="_top">probability
|
||||
distribution function PDF</a> given by:
|
||||
</p>
|
||||
<div class="blockquote"><blockquote class="blockquote"><p>
|
||||
<span class="inlinemediaobject"><img src="../../../../equations/mapairy_ref1.svg"></span>
|
||||
|
||||
</p></blockquote></div>
|
||||
<p>
|
||||
The location parameter μ is the location of the distribution, while the scale
|
||||
parameter [c] determines the width of the distribution. If the location
|
||||
is zero, and the scale 1, then the result is a standard map-airy distribution.
|
||||
</p>
|
||||
<p>
|
||||
The distribution describes the probability distribution of the area under
|
||||
a Brownian excursion over a unit interval.
|
||||
</p>
|
||||
<p>
|
||||
The following graph shows how the distributions moves as the location parameter
|
||||
changes:
|
||||
</p>
|
||||
<div class="blockquote"><blockquote class="blockquote"><p>
|
||||
<span class="inlinemediaobject"><img src="../../../../graphs/mapairy_pdf1.svg" align="middle"></span>
|
||||
|
||||
</p></blockquote></div>
|
||||
<p>
|
||||
While the following graph shows how the shape (scale) parameter alters
|
||||
the distribution:
|
||||
</p>
|
||||
<div class="blockquote"><blockquote class="blockquote"><p>
|
||||
<span class="inlinemediaobject"><img src="../../../../graphs/mapairy_pdf2.svg" align="middle"></span>
|
||||
|
||||
</p></blockquote></div>
|
||||
<h5>
|
||||
<a name="math_toolkit.dist_ref.dists.mapairy_dist.h0"></a>
|
||||
<span class="phrase"><a name="math_toolkit.dist_ref.dists.mapairy_dist.member_functions"></a></span><a class="link" href="mapairy_dist.html#math_toolkit.dist_ref.dists.mapairy_dist.member_functions">Member
|
||||
Functions</a>
|
||||
</h5>
|
||||
<pre class="programlisting"><span class="identifier">BOOST_MATH_GPU_ENABLED</span> <span class="identifier">mapairy_distribution</span><span class="special">(</span><span class="identifier">RealType</span> <span class="identifier">location</span> <span class="special">=</span> <span class="number">0</span><span class="special">,</span> <span class="identifier">RealType</span> <span class="identifier">scale</span> <span class="special">=</span> <span class="number">1</span><span class="special">);</span>
|
||||
</pre>
|
||||
<p>
|
||||
Constructs a mapairy distribution, with location parameter <span class="emphasis"><em>location</em></span>
|
||||
and scale parameter <span class="emphasis"><em>scale</em></span>. When these parameters take
|
||||
their default values (location = 0, scale = 1) then the result is a Standard
|
||||
map-airy Distribution.
|
||||
</p>
|
||||
<p>
|
||||
Requires scale > 0, otherwise calls <a class="link" href="../../error_handling.html#math_toolkit.error_handling.domain_error">domain_error</a>.
|
||||
</p>
|
||||
<pre class="programlisting"><span class="identifier">BOOST_MATH_GPU_ENABLED</span> <span class="identifier">RealType</span> <span class="identifier">location</span><span class="special">()</span><span class="keyword">const</span><span class="special">;</span>
|
||||
</pre>
|
||||
<p>
|
||||
Returns the location parameter of the distribution.
|
||||
</p>
|
||||
<pre class="programlisting"><span class="identifier">BOOST_MATH_GPU_ENABLED</span> <span class="identifier">RealType</span> <span class="identifier">scale</span><span class="special">()</span><span class="keyword">const</span><span class="special">;</span>
|
||||
</pre>
|
||||
<p>
|
||||
Returns the scale parameter of the distribution.
|
||||
</p>
|
||||
<h5>
|
||||
<a name="math_toolkit.dist_ref.dists.mapairy_dist.h1"></a>
|
||||
<span class="phrase"><a name="math_toolkit.dist_ref.dists.mapairy_dist.non_member_accessors"></a></span><a class="link" href="mapairy_dist.html#math_toolkit.dist_ref.dists.mapairy_dist.non_member_accessors">Non-member
|
||||
Accessors</a>
|
||||
</h5>
|
||||
<p>
|
||||
All the <a class="link" href="../nmp.html" title="Non-Member Properties">usual non-member accessor
|
||||
functions</a> that are generic to all distributions are supported:
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.cdf">Cumulative Distribution Function</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.pdf">Probability Density Function</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.quantile">Quantile</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.hazard">Hazard Function</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.chf">Cumulative Hazard Function</a>,
|
||||
__logcdf, __logpdf, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.mean">mean</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.median">median</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.mode">mode</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.variance">variance</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.sd">standard deviation</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.skewness">skewness</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.kurtosis">kurtosis</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.kurtosis_excess">kurtosis_excess</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.range">range</a> and <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.support">support</a>. For this distribution
|
||||
all non-member accessor functions are marked with <code class="computeroutput"><span class="identifier">BOOST_MATH_GPU_ENABLED</span></code>
|
||||
and can be run on both host and device.
|
||||
</p>
|
||||
<p>
|
||||
Note however that the map-airy distribution does not have a skewness, kurtosis,
|
||||
etc. See <a class="link" href="../../pol_ref/assert_undefined.html" title="Mathematically Undefined Function Policies">mathematically
|
||||
undefined function</a> to control whether these should fail to compile
|
||||
with a BOOST_STATIC_ASSERTION_FAILURE, which is the default.
|
||||
</p>
|
||||
<p>
|
||||
Alternately, the functions <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.skewness">skewness</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.kurtosis">kurtosis</a> and
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.kurtosis_excess">kurtosis_excess</a>
|
||||
will all return a <a class="link" href="../../error_handling.html#math_toolkit.error_handling.domain_error">domain_error</a>
|
||||
if called.
|
||||
</p>
|
||||
<p>
|
||||
The domain of the random variable is [-[max_value], +[min_value]].
|
||||
</p>
|
||||
<h5>
|
||||
<a name="math_toolkit.dist_ref.dists.mapairy_dist.h2"></a>
|
||||
<span class="phrase"><a name="math_toolkit.dist_ref.dists.mapairy_dist.accuracy"></a></span><a class="link" href="mapairy_dist.html#math_toolkit.dist_ref.dists.mapairy_dist.accuracy">Accuracy</a>
|
||||
</h5>
|
||||
<p>
|
||||
The error is within 4 epsilon except for the rapidly decaying left tail.
|
||||
</p>
|
||||
<p>
|
||||
Errors in the PDF at 64-bit double precision:
|
||||
</p>
|
||||
<p>
|
||||
<span class="inlinemediaobject"><img src="../../../../graphs/mapairy_pdf_accuracy_64.png"></span>
|
||||
</p>
|
||||
<p>
|
||||
Errors in the CDF at 64-bit double precision:
|
||||
</p>
|
||||
<p>
|
||||
<span class="inlinemediaobject"><img src="../../../../graphs/mapairy_cdf_accuracy_64.png"></span>
|
||||
</p>
|
||||
<p>
|
||||
Errors in the CDF-complement at 64-bit double precision:
|
||||
</p>
|
||||
<p>
|
||||
<span class="inlinemediaobject"><img src="../../../../graphs/mapairy_ccdf_accuracy_64.png"></span>
|
||||
</p>
|
||||
<h5>
|
||||
<a name="math_toolkit.dist_ref.dists.mapairy_dist.h3"></a>
|
||||
<span class="phrase"><a name="math_toolkit.dist_ref.dists.mapairy_dist.implementation"></a></span><a class="link" href="mapairy_dist.html#math_toolkit.dist_ref.dists.mapairy_dist.implementation">Implementation</a>
|
||||
</h5>
|
||||
<p>
|
||||
See references.
|
||||
</p>
|
||||
<h5>
|
||||
<a name="math_toolkit.dist_ref.dists.mapairy_dist.h4"></a>
|
||||
<span class="phrase"><a name="math_toolkit.dist_ref.dists.mapairy_dist.references"></a></span><a class="link" href="mapairy_dist.html#math_toolkit.dist_ref.dists.mapairy_dist.references">References</a>
|
||||
</h5>
|
||||
<div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; ">
|
||||
<li class="listitem">
|
||||
<a href="https://mathworld.wolfram.com/Map-AiryDistribution.html" target="_top">Wolfram
|
||||
MathWorld: Map-Airy Distribution</a>
|
||||
</li>
|
||||
<li class="listitem">
|
||||
T. Yoshimura, Numerical Evaluation and High Precision Approximation
|
||||
Formula for Map-Airy Distribution, DOI: 10.36227/techrxiv.172053942.27675733/v1,
|
||||
2024.
|
||||
</li>
|
||||
</ul></div>
|
||||
</div>
|
||||
<div class="copyright-footer">Copyright © 2006-2021 Nikhar Agrawal, Anton Bikineev, Matthew Borland,
|
||||
Paul A. Bristow, Marco Guazzone, Christopher Kormanyos, Hubert Holin, Bruno
|
||||
Lalande, John Maddock, Evan Miller, Jeremy Murphy, Matthew Pulver, Johan Råde,
|
||||
Gautam Sewani, Benjamin Sobotta, Nicholas Thompson, Thijs van den Berg, Daryle
|
||||
Walker and Xiaogang Zhang<p>
|
||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
file LICENSE_1_0.txt or copy at <a href="http://www.boost.org/LICENSE_1_0.txt" target="_top">http://www.boost.org/LICENSE_1_0.txt</a>)
|
||||
</p>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="spirit-nav">
|
||||
<a accesskey="p" href="lognormal_dist.html"><img src="../../../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../dists.html"><img src="../../../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../../../index.html"><img src="../../../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="negative_binomial_dist.html"><img src="../../../../../../../doc/src/images/next.png" alt="Next"></a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
199
doc/html/math_toolkit/dist_ref/dists/saspoint5_dist.html
Normal file
@@ -0,0 +1,199 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>SαS Point5 Distribution</title>
|
||||
<link rel="stylesheet" href="../../../math.css" type="text/css">
|
||||
<meta name="generator" content="DocBook XSL Stylesheets Vsnapshot">
|
||||
<link rel="home" href="../../../index.html" title="Math Toolkit 4.2.1">
|
||||
<link rel="up" href="../dists.html" title="Distributions">
|
||||
<link rel="prev" href="rayleigh.html" title="Rayleigh Distribution">
|
||||
<link rel="next" href="skew_normal_dist.html" title="Skew Normal Distribution">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
|
||||
<table cellpadding="2" width="100%"><tr>
|
||||
<td valign="top"><img alt="Boost C++ Libraries" width="277" height="86" src="../../../../../../../boost.png"></td>
|
||||
<td align="center"><a href="../../../../../../../index.html">Home</a></td>
|
||||
<td align="center"><a href="../../../../../../../libs/libraries.htm">Libraries</a></td>
|
||||
<td align="center"><a href="http://www.boost.org/users/people.html">People</a></td>
|
||||
<td align="center"><a href="http://www.boost.org/users/faq.html">FAQ</a></td>
|
||||
<td align="center"><a href="../../../../../../../more/index.htm">More</a></td>
|
||||
</tr></table>
|
||||
<hr>
|
||||
<div class="spirit-nav">
|
||||
<a accesskey="p" href="rayleigh.html"><img src="../../../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../dists.html"><img src="../../../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../../../index.html"><img src="../../../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="skew_normal_dist.html"><img src="../../../../../../../doc/src/images/next.png" alt="Next"></a>
|
||||
</div>
|
||||
<div class="section">
|
||||
<div class="titlepage"><div><div><h4 class="title">
|
||||
<a name="math_toolkit.dist_ref.dists.saspoint5_dist"></a><a class="link" href="saspoint5_dist.html" title="SαS Point5 Distribution">SαS Point5
|
||||
Distribution</a>
|
||||
</h4></div></div></div>
|
||||
<pre class="programlisting"><span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">math</span><span class="special">/</span><span class="identifier">distributions</span><span class="special">/</span><span class="identifier">saspoint5</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span></pre>
|
||||
<pre class="programlisting"><span class="keyword">template</span> <span class="special"><</span><span class="keyword">class</span> <span class="identifier">RealType</span> <span class="special">=</span> <span class="keyword">double</span><span class="special">,</span>
|
||||
<span class="keyword">class</span> <a class="link" href="../../../policy.html" title="Chapter 22. Policies: Controlling Precision, Error Handling etc">Policy</a> <span class="special">=</span> <a class="link" href="../../pol_ref/pol_ref_ref.html" title="Policy Class Reference">policies::policy<></a> <span class="special">></span>
|
||||
<span class="keyword">class</span> <span class="identifier">saspoint5_distribution</span><span class="special">;</span>
|
||||
|
||||
<span class="keyword">typedef</span> <span class="identifier">saspoint5_distribution</span><span class="special"><></span> <span class="identifier">saspoint5</span><span class="special">;</span>
|
||||
|
||||
<span class="keyword">template</span> <span class="special"><</span><span class="keyword">class</span> <span class="identifier">RealType</span><span class="special">,</span> <span class="keyword">class</span> <a class="link" href="../../../policy.html" title="Chapter 22. Policies: Controlling Precision, Error Handling etc">Policy</a><span class="special">></span>
|
||||
<span class="keyword">class</span> <span class="identifier">saspoint5_distribution</span>
|
||||
<span class="special">{</span>
|
||||
<span class="keyword">public</span><span class="special">:</span>
|
||||
<span class="keyword">typedef</span> <span class="identifier">RealType</span> <span class="identifier">value_type</span><span class="special">;</span>
|
||||
<span class="keyword">typedef</span> <span class="identifier">Policy</span> <span class="identifier">policy_type</span><span class="special">;</span>
|
||||
|
||||
<span class="identifier">BOOST_MATH_GPU_ENABLED</span> <span class="identifier">saspoint5_distribution</span><span class="special">(</span><span class="identifier">RealType</span> <span class="identifier">location</span> <span class="special">=</span> <span class="number">0</span><span class="special">,</span> <span class="identifier">RealType</span> <span class="identifier">scale</span> <span class="special">=</span> <span class="number">1</span><span class="special">);</span>
|
||||
|
||||
<span class="identifier">BOOST_MATH_GPU_ENABLED</span> <span class="identifier">RealType</span> <span class="identifier">location</span><span class="special">()</span><span class="keyword">const</span><span class="special">;</span>
|
||||
<span class="identifier">BOOST_MATH_GPU_ENABLED</span> <span class="identifier">RealType</span> <span class="identifier">scale</span><span class="special">()</span><span class="keyword">const</span><span class="special">;</span>
|
||||
<span class="special">};</span>
|
||||
</pre>
|
||||
<p>
|
||||
It is special case of a <a href="http://en.wikipedia.org/wiki/Stable_distribution" target="_top">stable
|
||||
distribution</a> with shape parameter α=1/2, β=0.
|
||||
</p>
|
||||
<p>
|
||||
<a href="http://en.wikipedia.org/wiki/Probability_distribution" target="_top">probability
|
||||
distribution function PDF</a> given by:
|
||||
</p>
|
||||
<div class="blockquote"><blockquote class="blockquote"><p>
|
||||
<span class="inlinemediaobject"><img src="../../../../equations/saspoint5_ref1.svg"></span>
|
||||
|
||||
</p></blockquote></div>
|
||||
<p>
|
||||
The location parameter μ is the location of the distribution, while the scale
|
||||
parameter [c] determines the width of the distribution. If the location
|
||||
is zero, and the scale 1, then the result is a standard SαS Point5 distribution.
|
||||
</p>
|
||||
<p>
|
||||
This distribution has heavier tails than the Cauchy distribution.
|
||||
</p>
|
||||
<p>
|
||||
The following graph shows how the distributions moves as the location parameter
|
||||
changes:
|
||||
</p>
|
||||
<div class="blockquote"><blockquote class="blockquote"><p>
|
||||
<span class="inlinemediaobject"><img src="../../../../graphs/saspoint5_pdf1.svg" align="middle"></span>
|
||||
|
||||
</p></blockquote></div>
|
||||
<p>
|
||||
While the following graph shows how the shape (scale) parameter alters
|
||||
the distribution:
|
||||
</p>
|
||||
<div class="blockquote"><blockquote class="blockquote"><p>
|
||||
<span class="inlinemediaobject"><img src="../../../../graphs/saspoint5_pdf2.svg" align="middle"></span>
|
||||
|
||||
</p></blockquote></div>
|
||||
<h5>
|
||||
<a name="math_toolkit.dist_ref.dists.saspoint5_dist.h0"></a>
|
||||
<span class="phrase"><a name="math_toolkit.dist_ref.dists.saspoint5_dist.member_functions"></a></span><a class="link" href="saspoint5_dist.html#math_toolkit.dist_ref.dists.saspoint5_dist.member_functions">Member
|
||||
Functions</a>
|
||||
</h5>
|
||||
<pre class="programlisting"><span class="identifier">BOOST_MATH_GPU_ENABLED</span> <span class="identifier">saspoint5_distribution</span><span class="special">(</span><span class="identifier">RealType</span> <span class="identifier">location</span> <span class="special">=</span> <span class="number">0</span><span class="special">,</span> <span class="identifier">RealType</span> <span class="identifier">scale</span> <span class="special">=</span> <span class="number">1</span><span class="special">);</span>
|
||||
</pre>
|
||||
<p>
|
||||
Constructs a SαS Point5 distribution, with location parameter <span class="emphasis"><em>location</em></span>
|
||||
and scale parameter <span class="emphasis"><em>scale</em></span>. When these parameters take
|
||||
their default values (location = 0, scale = 1) then the result is a Standard
|
||||
SαS Point5 Distribution.
|
||||
</p>
|
||||
<p>
|
||||
Requires scale > 0, otherwise calls <a class="link" href="../../error_handling.html#math_toolkit.error_handling.domain_error">domain_error</a>.
|
||||
</p>
|
||||
<pre class="programlisting"><span class="identifier">BOOST_MATH_GPU_ENABLED</span> <span class="identifier">RealType</span> <span class="identifier">location</span><span class="special">()</span><span class="keyword">const</span><span class="special">;</span>
|
||||
</pre>
|
||||
<p>
|
||||
Returns the location parameter of the distribution.
|
||||
</p>
|
||||
<pre class="programlisting"><span class="identifier">BOOST_MATH_GPU_ENABLED</span> <span class="identifier">RealType</span> <span class="identifier">scale</span><span class="special">()</span><span class="keyword">const</span><span class="special">;</span>
|
||||
</pre>
|
||||
<p>
|
||||
Returns the scale parameter of the distribution.
|
||||
</p>
|
||||
<h5>
|
||||
<a name="math_toolkit.dist_ref.dists.saspoint5_dist.h1"></a>
|
||||
<span class="phrase"><a name="math_toolkit.dist_ref.dists.saspoint5_dist.non_member_accessors"></a></span><a class="link" href="saspoint5_dist.html#math_toolkit.dist_ref.dists.saspoint5_dist.non_member_accessors">Non-member
|
||||
Accessors</a>
|
||||
</h5>
|
||||
<p>
|
||||
All the <a class="link" href="../nmp.html" title="Non-Member Properties">usual non-member accessor
|
||||
functions</a> that are generic to all distributions are supported:
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.cdf">Cumulative Distribution Function</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.pdf">Probability Density Function</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.quantile">Quantile</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.hazard">Hazard Function</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.chf">Cumulative Hazard Function</a>,
|
||||
__logcdf, __logpdf, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.mean">mean</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.median">median</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.mode">mode</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.variance">variance</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.sd">standard deviation</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.skewness">skewness</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.kurtosis">kurtosis</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.kurtosis_excess">kurtosis_excess</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.range">range</a> and <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.support">support</a>. For this distribution
|
||||
all non-member accessor functions are marked with <code class="computeroutput"><span class="identifier">BOOST_MATH_GPU_ENABLED</span></code>
|
||||
and can be run on both host and device.
|
||||
</p>
|
||||
<p>
|
||||
Note however that the SαS Point5 distribution does not have a mean, standard
|
||||
deviation, etc. See <a class="link" href="../../pol_ref/assert_undefined.html" title="Mathematically Undefined Function Policies">mathematically
|
||||
undefined function</a> to control whether these should fail to compile
|
||||
with a BOOST_STATIC_ASSERTION_FAILURE, which is the default.
|
||||
</p>
|
||||
<p>
|
||||
Alternately, the functions <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.mean">mean</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.sd">standard deviation</a>,
|
||||
<a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.variance">variance</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.skewness">skewness</a>, <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.kurtosis">kurtosis</a>
|
||||
and <a class="link" href="../nmp.html#math_toolkit.dist_ref.nmp.kurtosis_excess">kurtosis_excess</a>
|
||||
will all return a <a class="link" href="../../error_handling.html#math_toolkit.error_handling.domain_error">domain_error</a>
|
||||
if called.
|
||||
</p>
|
||||
<p>
|
||||
The domain of the random variable is [-[max_value], +[min_value]].
|
||||
</p>
|
||||
<h5>
|
||||
<a name="math_toolkit.dist_ref.dists.saspoint5_dist.h2"></a>
|
||||
<span class="phrase"><a name="math_toolkit.dist_ref.dists.saspoint5_dist.accuracy"></a></span><a class="link" href="saspoint5_dist.html#math_toolkit.dist_ref.dists.saspoint5_dist.accuracy">Accuracy</a>
|
||||
</h5>
|
||||
<p>
|
||||
The error is within 4 epsilon.
|
||||
</p>
|
||||
<p>
|
||||
Errors in the PDF at 64-bit double precision:
|
||||
</p>
|
||||
<p>
|
||||
<span class="inlinemediaobject"><img src="../../../../graphs/saspoint5_pdf_accuracy_64.png"></span>
|
||||
</p>
|
||||
<p>
|
||||
Errors in the CDF-complement at 64-bit double precision:
|
||||
</p>
|
||||
<p>
|
||||
<span class="inlinemediaobject"><img src="../../../../graphs/saspoint5_ccdf_accuracy_64.png"></span>
|
||||
</p>
|
||||
<h5>
|
||||
<a name="math_toolkit.dist_ref.dists.saspoint5_dist.h3"></a>
|
||||
<span class="phrase"><a name="math_toolkit.dist_ref.dists.saspoint5_dist.implementation"></a></span><a class="link" href="saspoint5_dist.html#math_toolkit.dist_ref.dists.saspoint5_dist.implementation">Implementation</a>
|
||||
</h5>
|
||||
<p>
|
||||
See references.
|
||||
</p>
|
||||
<h5>
|
||||
<a name="math_toolkit.dist_ref.dists.saspoint5_dist.h4"></a>
|
||||
<span class="phrase"><a name="math_toolkit.dist_ref.dists.saspoint5_dist.references"></a></span><a class="link" href="saspoint5_dist.html#math_toolkit.dist_ref.dists.saspoint5_dist.references">References</a>
|
||||
</h5>
|
||||
<div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">
|
||||
T. Yoshimura, Numerical Evaluation and High Precision Approximation
|
||||
Formula for SαS Point5 Distribution, DOI: 10.36227/techrxiv.172055253.37208198/v1,
|
||||
2024.
|
||||
</li></ul></div>
|
||||
</div>
|
||||
<div class="copyright-footer">Copyright © 2006-2021 Nikhar Agrawal, Anton Bikineev, Matthew Borland,
|
||||
Paul A. Bristow, Marco Guazzone, Christopher Kormanyos, Hubert Holin, Bruno
|
||||
Lalande, John Maddock, Evan Miller, Jeremy Murphy, Matthew Pulver, Johan Råde,
|
||||
Gautam Sewani, Benjamin Sobotta, Nicholas Thompson, Thijs van den Berg, Daryle
|
||||
Walker and Xiaogang Zhang<p>
|
||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
file LICENSE_1_0.txt or copy at <a href="http://www.boost.org/LICENSE_1_0.txt" target="_top">http://www.boost.org/LICENSE_1_0.txt</a>)
|
||||
</p>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="spirit-nav">
|
||||
<a accesskey="p" href="rayleigh.html"><img src="../../../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../dists.html"><img src="../../../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../../../index.html"><img src="../../../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="skew_normal_dist.html"><img src="../../../../../../../doc/src/images/next.png" alt="Next"></a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
66
doc/html/math_toolkit/double_exponential/gpu_usage.html
Normal file
@@ -0,0 +1,66 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>GPU Usage</title>
|
||||
<link rel="stylesheet" href="../../math.css" type="text/css">
|
||||
<meta name="generator" content="DocBook XSL Stylesheets Vsnapshot">
|
||||
<link rel="home" href="../../index.html" title="Math Toolkit 4.2.1">
|
||||
<link rel="up" href="../double_exponential.html" title="Double-exponential quadrature">
|
||||
<link rel="prev" href="de_caveats.html" title="Caveats">
|
||||
<link rel="next" href="de_refes.html" title="References">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
|
||||
<table cellpadding="2" width="100%"><tr>
|
||||
<td valign="top"><img alt="Boost C++ Libraries" width="277" height="86" src="../../../../../../boost.png"></td>
|
||||
<td align="center"><a href="../../../../../../index.html">Home</a></td>
|
||||
<td align="center"><a href="../../../../../../libs/libraries.htm">Libraries</a></td>
|
||||
<td align="center"><a href="http://www.boost.org/users/people.html">People</a></td>
|
||||
<td align="center"><a href="http://www.boost.org/users/faq.html">FAQ</a></td>
|
||||
<td align="center"><a href="../../../../../../more/index.htm">More</a></td>
|
||||
</tr></table>
|
||||
<hr>
|
||||
<div class="spirit-nav">
|
||||
<a accesskey="p" href="de_caveats.html"><img src="../../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../double_exponential.html"><img src="../../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../../index.html"><img src="../../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="de_refes.html"><img src="../../../../../../doc/src/images/next.png" alt="Next"></a>
|
||||
</div>
|
||||
<div class="section">
|
||||
<div class="titlepage"><div><div><h3 class="title">
|
||||
<a name="math_toolkit.double_exponential.gpu_usage"></a><a class="link" href="gpu_usage.html" title="GPU Usage">GPU Usage</a>
|
||||
</h3></div></div></div>
|
||||
<pre class="programlisting"> <span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">math</span><span class="special">/</span><span class="identifier">quadrature</span><span class="special">/</span><span class="identifier">exp_sinh</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span>
|
||||
|
||||
<span class="keyword">namespace</span> <span class="identifier">boost</span><span class="special">{</span> <span class="keyword">namespace</span> <span class="identifier">math</span><span class="special">{</span> <span class="keyword">namespace</span> <span class="identifier">quadrature</span> <span class="special">{</span>
|
||||
|
||||
<span class="keyword">template</span> <span class="special"><</span><span class="keyword">class</span> <span class="identifier">F</span><span class="special">,</span> <span class="keyword">class</span> <span class="identifier">Real</span><span class="special">,</span> <span class="keyword">class</span> <span class="identifier">Policy</span> <span class="special">=</span> <span class="identifier">policies</span><span class="special">::</span><span class="identifier">policy</span><span class="special"><></span> <span class="special">></span>
|
||||
<span class="identifier">__device__</span> <span class="keyword">auto</span> <span class="identifier">exp_sinh_integrate</span><span class="special">(</span><span class="keyword">const</span> <span class="identifier">F</span><span class="special">&</span> <span class="identifier">f</span><span class="special">,</span> <span class="identifier">Real</span> <span class="identifier">a</span><span class="special">,</span> <span class="identifier">Real</span> <span class="identifier">b</span><span class="special">,</span> <span class="identifier">Real</span> <span class="identifier">tolerance</span><span class="special">,</span> <span class="identifier">Real</span><span class="special">*</span> <span class="identifier">error</span><span class="special">,</span> <span class="identifier">Real</span><span class="special">*</span> <span class="identifier">L1</span><span class="special">,</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">math</span><span class="special">::</span><span class="identifier">size_t</span><span class="special">*</span> <span class="identifier">levels</span><span class="special">)</span>
|
||||
|
||||
<span class="keyword">template</span> <span class="special"><</span><span class="keyword">class</span> <span class="identifier">F</span><span class="special">,</span> <span class="keyword">class</span> <span class="identifier">Real</span><span class="special">,</span> <span class="keyword">class</span> <span class="identifier">Policy</span> <span class="special">=</span> <span class="identifier">policies</span><span class="special">::</span><span class="identifier">policy</span><span class="special"><></span> <span class="special">></span>
|
||||
<span class="identifier">__device__</span> <span class="keyword">auto</span> <span class="identifier">exp_sinh_integrate</span><span class="special">(</span><span class="keyword">const</span> <span class="identifier">F</span><span class="special">&</span> <span class="identifier">f</span><span class="special">,</span> <span class="identifier">Real</span> <span class="identifier">tolerance</span><span class="special">,</span> <span class="identifier">Real</span><span class="special">*</span> <span class="identifier">error</span><span class="special">,</span> <span class="identifier">Real</span><span class="special">*</span> <span class="identifier">L1</span><span class="special">,</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">math</span><span class="special">::</span><span class="identifier">size_t</span><span class="special">*</span> <span class="identifier">levels</span><span class="special">)</span>
|
||||
|
||||
<span class="special">}}}</span>
|
||||
</pre>
|
||||
<p>
|
||||
Quadrature is additionally able to run on CUDA (NVCC and NVRTC) platforms.
|
||||
The major difference is outlined in the above function signatures. When used
|
||||
on device these are free standing functions instead of using OOP like on
|
||||
the host. The tables of abscissas and weights are stored in shared read only
|
||||
memory on the device instead of being initialized when the class is constructed.
|
||||
An example use case would be in the finite elements method computing a stiffness
|
||||
matrix since it would consist of many different functions.
|
||||
</p>
|
||||
</div>
|
||||
<div class="copyright-footer">Copyright © 2006-2021 Nikhar Agrawal, Anton Bikineev, Matthew Borland,
|
||||
Paul A. Bristow, Marco Guazzone, Christopher Kormanyos, Hubert Holin, Bruno
|
||||
Lalande, John Maddock, Evan Miller, Jeremy Murphy, Matthew Pulver, Johan Råde,
|
||||
Gautam Sewani, Benjamin Sobotta, Nicholas Thompson, Thijs van den Berg, Daryle
|
||||
Walker and Xiaogang Zhang<p>
|
||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
file LICENSE_1_0.txt or copy at <a href="http://www.boost.org/LICENSE_1_0.txt" target="_top">http://www.boost.org/LICENSE_1_0.txt</a>)
|
||||
</p>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="spirit-nav">
|
||||
<a accesskey="p" href="de_caveats.html"><img src="../../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../double_exponential.html"><img src="../../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../../index.html"><img src="../../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="de_refes.html"><img src="../../../../../../doc/src/images/next.png" alt="Next"></a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
121
doc/html/math_toolkit/gpu.html
Normal file
@@ -0,0 +1,121 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>Support for GPU programming in Boost.Math</title>
|
||||
<link rel="stylesheet" href="../math.css" type="text/css">
|
||||
<meta name="generator" content="DocBook XSL Stylesheets Vsnapshot">
|
||||
<link rel="home" href="../index.html" title="Math Toolkit 4.2.1">
|
||||
<link rel="up" href="../overview.html" title="Chapter 1. Overview">
|
||||
<link rel="prev" href="error_handling.html" title="Error Handling">
|
||||
<link rel="next" href="compilers_overview.html" title="Compilers">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
|
||||
<table cellpadding="2" width="100%"><tr>
|
||||
<td valign="top"><img alt="Boost C++ Libraries" width="277" height="86" src="../../../../../boost.png"></td>
|
||||
<td align="center"><a href="../../../../../index.html">Home</a></td>
|
||||
<td align="center"><a href="../../../../../libs/libraries.htm">Libraries</a></td>
|
||||
<td align="center"><a href="http://www.boost.org/users/people.html">People</a></td>
|
||||
<td align="center"><a href="http://www.boost.org/users/faq.html">FAQ</a></td>
|
||||
<td align="center"><a href="../../../../../more/index.htm">More</a></td>
|
||||
</tr></table>
|
||||
<hr>
|
||||
<div class="spirit-nav">
|
||||
<a accesskey="p" href="error_handling.html"><img src="../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../overview.html"><img src="../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../index.html"><img src="../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="compilers_overview.html"><img src="../../../../../doc/src/images/next.png" alt="Next"></a>
|
||||
</div>
|
||||
<div class="section">
|
||||
<div class="titlepage"><div><div><h2 class="title" style="clear: both">
|
||||
<a name="math_toolkit.gpu"></a><a class="link" href="gpu.html" title="Support for GPU programming in Boost.Math">Support for GPU programming in Boost.Math</a>
|
||||
</h2></div></div></div>
|
||||
<h5>
|
||||
<a name="math_toolkit.gpu.h0"></a>
|
||||
<span class="phrase"><a name="math_toolkit.gpu.gpu_support"></a></span><a class="link" href="gpu.html#math_toolkit.gpu.gpu_support">GPU
|
||||
Support</a>
|
||||
</h5>
|
||||
<p>
|
||||
Selected functions, distributions, tools, etc. support running on both host
|
||||
and devices. These functions will have the annotation <code class="computeroutput"><span class="identifier">BOOST_MATH_GPU_ENABLED</span></code>
|
||||
or <code class="computeroutput"><span class="identifier">BOOST_MATH_CUDA_ENABLED</span></code>
|
||||
next to their individual documentation. Functions marked with <code class="computeroutput"><span class="identifier">BOOST_MATH_GPU_ENABLED</span></code> are tested using CUDA
|
||||
(both NVCC and NVRTC) as well as SYCL to provide a wide range of support. Functions
|
||||
marked with <code class="computeroutput"><span class="identifier">BOOST_MATH_CUDA_ENABLED</span></code>
|
||||
are few, but due to its restrictions SYCL is unsupported.
|
||||
</p>
|
||||
<h5>
|
||||
<a name="math_toolkit.gpu.h1"></a>
|
||||
<span class="phrase"><a name="math_toolkit.gpu.policies"></a></span><a class="link" href="gpu.html#math_toolkit.gpu.policies">Policies</a>
|
||||
</h5>
|
||||
<p>
|
||||
The default policy on all devices is ignore error due to the lack of throwing
|
||||
ability. A user can specify their own policy like usual, but when the code
|
||||
is run on device it will be ignored.
|
||||
</p>
|
||||
<h5>
|
||||
<a name="math_toolkit.gpu.h2"></a>
|
||||
<span class="phrase"><a name="math_toolkit.gpu.how_to_build_with_device_support"></a></span><a class="link" href="gpu.html#math_toolkit.gpu.how_to_build_with_device_support">How
|
||||
to build with device support</a>
|
||||
</h5>
|
||||
<p>
|
||||
When compiling with CUDA or SYCL you will have to ensure that your code is
|
||||
being run inside of a kernel function. It is not enough to simply compile existing
|
||||
code with the NVCC compiler to run the code on the device. A simple CUDA kernel
|
||||
to run the Beta Distribution CDF on NVCC would be:
|
||||
</p>
|
||||
<pre class="programlisting"><span class="identifier">__global__</span> <span class="keyword">void</span> <span class="identifier">cuda_beta_dist</span><span class="special">(</span><span class="keyword">const</span> <span class="keyword">double</span><span class="special">*</span> <span class="identifier">in</span><span class="special">,</span> <span class="keyword">double</span><span class="special">*</span> <span class="identifier">out</span><span class="special">,</span> <span class="keyword">int</span> <span class="identifier">num_elements</span><span class="special">)</span>
|
||||
<span class="special">{</span>
|
||||
<span class="keyword">const</span> <span class="keyword">int</span> <span class="identifier">i</span> <span class="special">=</span> <span class="identifier">blockDim</span><span class="special">.</span><span class="identifier">x</span> <span class="special">*</span> <span class="identifier">blockIdx</span><span class="special">.</span><span class="identifier">x</span> <span class="special">+</span> <span class="identifier">threadIdx</span><span class="special">.</span><span class="identifier">x</span><span class="special">;</span>
|
||||
|
||||
<span class="keyword">if</span> <span class="special">(</span><span class="identifier">i</span> <span class="special"><</span> <span class="identifier">num_elements</span><span class="special">)</span>
|
||||
<span class="special">{</span>
|
||||
<span class="identifier">out</span><span class="special">[</span><span class="identifier">i</span><span class="special">]</span> <span class="special">=</span> <span class="identifier">cdf</span><span class="special">(</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">math</span><span class="special">::</span><span class="identifier">beta_distribution</span><span class="special"><</span><span class="keyword">double</span><span class="special">>(),</span> <span class="identifier">in</span><span class="special">[</span><span class="identifier">i</span><span class="special">]);</span>
|
||||
<span class="special">}</span>
|
||||
<span class="special">}</span>
|
||||
</pre>
|
||||
<p>
|
||||
And on CUDA on NVRTC:
|
||||
</p>
|
||||
<pre class="programlisting"><span class="keyword">const</span> <span class="keyword">char</span><span class="special">*</span> <span class="identifier">cuda_kernel</span> <span class="special">=</span> <span class="identifier">R</span><span class="string">"(
|
||||
#include <boost/math/distributions/beta.hpp>
|
||||
extern "</span><span class="identifier">C</span><span class="string">" __global__
|
||||
void test_beta_dist_kernel(const double* in, double* out, int num_elements)
|
||||
{
|
||||
const int i = blockDim.x * blockIdx.x + threadIdx.x;
|
||||
if (i < num_elements)
|
||||
{
|
||||
out[i] = boost::math::cdf(boost::math::beta_distribution<double>(), in[i]);
|
||||
}
|
||||
}
|
||||
)"</span><span class="special">;</span>
|
||||
</pre>
|
||||
<p>
|
||||
And lastly on SYCL:
|
||||
</p>
|
||||
<pre class="programlisting"><span class="keyword">void</span> <span class="identifier">sycl_beta_dist</span><span class="special">(</span><span class="keyword">const</span> <span class="keyword">double</span><span class="special">*</span> <span class="identifier">in</span><span class="special">,</span> <span class="keyword">double</span><span class="special">*</span> <span class="identifier">out</span><span class="special">,</span> <span class="keyword">int</span> <span class="identifier">num_elements</span><span class="special">,</span> <span class="identifier">sycl</span><span class="special">::</span><span class="identifier">queue</span><span class="special">&</span> <span class="identifier">q</span><span class="special">)</span>
|
||||
<span class="special">{</span>
|
||||
<span class="identifier">q</span><span class="special">.</span><span class="identifier">submit</span><span class="special">([&](</span><span class="identifier">sycl</span><span class="special">::</span><span class="identifier">handler</span><span class="special">&</span> <span class="identifier">h</span><span class="special">)</span> <span class="special">{</span>
|
||||
<span class="identifier">h</span><span class="special">.</span><span class="identifier">parallel_for</span><span class="special">(</span><span class="identifier">sycl</span><span class="special">::</span><span class="identifier">range</span><span class="special"><</span><span class="number">1</span><span class="special">>(</span><span class="identifier">num_elements</span><span class="special">),</span> <span class="special">[=](</span><span class="identifier">sycl</span><span class="special">::</span><span class="identifier">id</span><span class="special"><</span><span class="number">1</span><span class="special">></span> <span class="identifier">i</span><span class="special">)</span> <span class="special">{</span>
|
||||
<span class="identifier">out</span><span class="special">[</span><span class="identifier">i</span><span class="special">]</span> <span class="special">=</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">math</span><span class="special">::</span><span class="identifier">cdf</span><span class="special">(</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">math</span><span class="special">::</span><span class="identifier">beta_distribution</span><span class="special"><</span><span class="keyword">double</span><span class="special">>(),</span> <span class="identifier">in</span><span class="special">[</span><span class="identifier">i</span><span class="special">]);</span>
|
||||
<span class="special">});</span>
|
||||
<span class="special">});</span>
|
||||
<span class="special">}</span>
|
||||
</pre>
|
||||
<p>
|
||||
Once your kernel function has been written then use the framework mechanism
|
||||
for launching the kernel.
|
||||
</p>
|
||||
</div>
|
||||
<div class="copyright-footer">Copyright © 2006-2021 Nikhar Agrawal, Anton Bikineev, Matthew Borland,
|
||||
Paul A. Bristow, Marco Guazzone, Christopher Kormanyos, Hubert Holin, Bruno
|
||||
Lalande, John Maddock, Evan Miller, Jeremy Murphy, Matthew Pulver, Johan Råde,
|
||||
Gautam Sewani, Benjamin Sobotta, Nicholas Thompson, Thijs van den Berg, Daryle
|
||||
Walker and Xiaogang Zhang<p>
|
||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
file LICENSE_1_0.txt or copy at <a href="http://www.boost.org/LICENSE_1_0.txt" target="_top">http://www.boost.org/LICENSE_1_0.txt</a>)
|
||||
</p>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="spirit-nav">
|
||||
<a accesskey="p" href="error_handling.html"><img src="../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../overview.html"><img src="../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../index.html"><img src="../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="compilers_overview.html"><img src="../../../../../doc/src/images/next.png" alt="Next"></a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
49
doc/html/math_toolkit/logistic.html
Normal file
@@ -0,0 +1,49 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>Logistic Functions</title>
|
||||
<link rel="stylesheet" href="../math.css" type="text/css">
|
||||
<meta name="generator" content="DocBook XSL Stylesheets Vsnapshot">
|
||||
<link rel="home" href="../index.html" title="Math Toolkit 4.2.1">
|
||||
<link rel="up" href="../special.html" title="Chapter 8. Special Functions">
|
||||
<link rel="prev" href="sf_poly/jacobi.html" title="Jacobi Polynomials">
|
||||
<link rel="next" href="logistic/logit.html" title="logit">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
|
||||
<table cellpadding="2" width="100%"><tr>
|
||||
<td valign="top"><img alt="Boost C++ Libraries" width="277" height="86" src="../../../../../boost.png"></td>
|
||||
<td align="center"><a href="../../../../../index.html">Home</a></td>
|
||||
<td align="center"><a href="../../../../../libs/libraries.htm">Libraries</a></td>
|
||||
<td align="center"><a href="http://www.boost.org/users/people.html">People</a></td>
|
||||
<td align="center"><a href="http://www.boost.org/users/faq.html">FAQ</a></td>
|
||||
<td align="center"><a href="../../../../../more/index.htm">More</a></td>
|
||||
</tr></table>
|
||||
<hr>
|
||||
<div class="spirit-nav">
|
||||
<a accesskey="p" href="sf_poly/jacobi.html"><img src="../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../special.html"><img src="../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../index.html"><img src="../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="logistic/logit.html"><img src="../../../../../doc/src/images/next.png" alt="Next"></a>
|
||||
</div>
|
||||
<div class="section">
|
||||
<div class="titlepage"><div><div><h2 class="title" style="clear: both">
|
||||
<a name="math_toolkit.logistic"></a><a class="link" href="logistic.html" title="Logistic Functions">Logistic Functions</a>
|
||||
</h2></div></div></div>
|
||||
<div class="toc"><dl class="toc">
|
||||
<dt><span class="section"><a href="logistic/logit.html">logit</a></span></dt>
|
||||
<dt><span class="section"><a href="logistic/logistic_sigmoid.html">logistic_sigmoid</a></span></dt>
|
||||
</dl></div>
|
||||
</div>
|
||||
<div class="copyright-footer">Copyright © 2006-2021 Nikhar Agrawal, Anton Bikineev, Matthew Borland,
|
||||
Paul A. Bristow, Marco Guazzone, Christopher Kormanyos, Hubert Holin, Bruno
|
||||
Lalande, John Maddock, Evan Miller, Jeremy Murphy, Matthew Pulver, Johan Råde,
|
||||
Gautam Sewani, Benjamin Sobotta, Nicholas Thompson, Thijs van den Berg, Daryle
|
||||
Walker and Xiaogang Zhang<p>
|
||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
file LICENSE_1_0.txt or copy at <a href="http://www.boost.org/LICENSE_1_0.txt" target="_top">http://www.boost.org/LICENSE_1_0.txt</a>)
|
||||
</p>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="spirit-nav">
|
||||
<a accesskey="p" href="sf_poly/jacobi.html"><img src="../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../special.html"><img src="../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../index.html"><img src="../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="logistic/logit.html"><img src="../../../../../doc/src/images/next.png" alt="Next"></a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
71
doc/html/math_toolkit/logistic/logistic_sigmoid.html
Normal file
@@ -0,0 +1,71 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>logistic_sigmoid</title>
|
||||
<link rel="stylesheet" href="../../math.css" type="text/css">
|
||||
<meta name="generator" content="DocBook XSL Stylesheets Vsnapshot">
|
||||
<link rel="home" href="../../index.html" title="Math Toolkit 4.2.1">
|
||||
<link rel="up" href="../logistic.html" title="Logistic Functions">
|
||||
<link rel="prev" href="logit.html" title="logit">
|
||||
<link rel="next" href="../bessel.html" title="Bessel Functions">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
|
||||
<table cellpadding="2" width="100%"><tr>
|
||||
<td valign="top"><img alt="Boost C++ Libraries" width="277" height="86" src="../../../../../../boost.png"></td>
|
||||
<td align="center"><a href="../../../../../../index.html">Home</a></td>
|
||||
<td align="center"><a href="../../../../../../libs/libraries.htm">Libraries</a></td>
|
||||
<td align="center"><a href="http://www.boost.org/users/people.html">People</a></td>
|
||||
<td align="center"><a href="http://www.boost.org/users/faq.html">FAQ</a></td>
|
||||
<td align="center"><a href="../../../../../../more/index.htm">More</a></td>
|
||||
</tr></table>
|
||||
<hr>
|
||||
<div class="spirit-nav">
|
||||
<a accesskey="p" href="logit.html"><img src="../../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../logistic.html"><img src="../../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../../index.html"><img src="../../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="../bessel.html"><img src="../../../../../../doc/src/images/next.png" alt="Next"></a>
|
||||
</div>
|
||||
<div class="section">
|
||||
<div class="titlepage"><div><div><h3 class="title">
|
||||
<a name="math_toolkit.logistic.logistic_sigmoid"></a><a class="link" href="logistic_sigmoid.html" title="logistic_sigmoid">logistic_sigmoid</a>
|
||||
</h3></div></div></div>
|
||||
<h5>
|
||||
<a name="math_toolkit.logistic.logistic_sigmoid.h0"></a>
|
||||
<span class="phrase"><a name="math_toolkit.logistic.logistic_sigmoid.synopsis"></a></span><a class="link" href="logistic_sigmoid.html#math_toolkit.logistic.logistic_sigmoid.synopsis">Synopsis</a>
|
||||
</h5>
|
||||
<pre class="programlisting"><span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">math</span><span class="special">/</span><span class="identifier">special_functions</span><span class="special">/</span><span class="identifier">logistic_sigmoid</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span>
|
||||
|
||||
<span class="keyword">namespace</span> <span class="identifier">boost</span> <span class="special">{</span> <span class="keyword">namespace</span> <span class="identifier">math</span> <span class="special">{</span>
|
||||
|
||||
<span class="keyword">template</span> <span class="special"><</span><span class="keyword">typename</span> <span class="identifier">RealType</span><span class="special">,</span> <span class="keyword">typename</span> <span class="identifier">Policy</span><span class="special">></span>
|
||||
<span class="identifier">RealType</span> <span class="identifier">logistic_sigmoid</span><span class="special">(</span><span class="identifier">RealType</span> <span class="identifier">x</span><span class="special">,</span> <span class="keyword">const</span> <span class="identifier">Policy</span><span class="special">&);</span>
|
||||
|
||||
<span class="keyword">template</span> <span class="special"><</span><span class="keyword">typename</span> <span class="identifier">RealType</span><span class="special">></span>
|
||||
<span class="identifier">RealType</span> <span class="identifier">logistic_sigmoid</span><span class="special">(</span><span class="identifier">RealType</span> <span class="identifier">x</span><span class="special">)</span>
|
||||
|
||||
<span class="special">}}</span> <span class="comment">// namespaces</span>
|
||||
</pre>
|
||||
<h5>
|
||||
<a name="math_toolkit.logistic.logistic_sigmoid.h1"></a>
|
||||
<span class="phrase"><a name="math_toolkit.logistic.logistic_sigmoid.description"></a></span><a class="link" href="logistic_sigmoid.html#math_toolkit.logistic.logistic_sigmoid.description">Description</a>
|
||||
</h5>
|
||||
<p>
|
||||
Returns the <a href="https://en.wikipedia.org/wiki/Logistic_function" target="_top">logistic
|
||||
sigmoid function</a> This function is broadly useful, and is used to
|
||||
compute the CDF of the logistic distribution. It is also sometimes referred
|
||||
to as expit as it is the inverse of the logit function.
|
||||
</p>
|
||||
</div>
|
||||
<div class="copyright-footer">Copyright © 2006-2021 Nikhar Agrawal, Anton Bikineev, Matthew Borland,
|
||||
Paul A. Bristow, Marco Guazzone, Christopher Kormanyos, Hubert Holin, Bruno
|
||||
Lalande, John Maddock, Evan Miller, Jeremy Murphy, Matthew Pulver, Johan Råde,
|
||||
Gautam Sewani, Benjamin Sobotta, Nicholas Thompson, Thijs van den Berg, Daryle
|
||||
Walker and Xiaogang Zhang<p>
|
||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
file LICENSE_1_0.txt or copy at <a href="http://www.boost.org/LICENSE_1_0.txt" target="_top">http://www.boost.org/LICENSE_1_0.txt</a>)
|
||||
</p>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="spirit-nav">
|
||||
<a accesskey="p" href="logit.html"><img src="../../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../logistic.html"><img src="../../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../../index.html"><img src="../../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="../bessel.html"><img src="../../../../../../doc/src/images/next.png" alt="Next"></a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
70
doc/html/math_toolkit/logistic/logit.html
Normal file
@@ -0,0 +1,70 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>logit</title>
|
||||
<link rel="stylesheet" href="../../math.css" type="text/css">
|
||||
<meta name="generator" content="DocBook XSL Stylesheets Vsnapshot">
|
||||
<link rel="home" href="../../index.html" title="Math Toolkit 4.2.1">
|
||||
<link rel="up" href="../logistic.html" title="Logistic Functions">
|
||||
<link rel="prev" href="../logistic.html" title="Logistic Functions">
|
||||
<link rel="next" href="logistic_sigmoid.html" title="logistic_sigmoid">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
|
||||
<table cellpadding="2" width="100%"><tr>
|
||||
<td valign="top"><img alt="Boost C++ Libraries" width="277" height="86" src="../../../../../../boost.png"></td>
|
||||
<td align="center"><a href="../../../../../../index.html">Home</a></td>
|
||||
<td align="center"><a href="../../../../../../libs/libraries.htm">Libraries</a></td>
|
||||
<td align="center"><a href="http://www.boost.org/users/people.html">People</a></td>
|
||||
<td align="center"><a href="http://www.boost.org/users/faq.html">FAQ</a></td>
|
||||
<td align="center"><a href="../../../../../../more/index.htm">More</a></td>
|
||||
</tr></table>
|
||||
<hr>
|
||||
<div class="spirit-nav">
|
||||
<a accesskey="p" href="../logistic.html"><img src="../../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../logistic.html"><img src="../../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../../index.html"><img src="../../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="logistic_sigmoid.html"><img src="../../../../../../doc/src/images/next.png" alt="Next"></a>
|
||||
</div>
|
||||
<div class="section">
|
||||
<div class="titlepage"><div><div><h3 class="title">
|
||||
<a name="math_toolkit.logistic.logit"></a><a class="link" href="logit.html" title="logit">logit</a>
|
||||
</h3></div></div></div>
|
||||
<h5>
|
||||
<a name="math_toolkit.logistic.logit.h0"></a>
|
||||
<span class="phrase"><a name="math_toolkit.logistic.logit.synopsis"></a></span><a class="link" href="logit.html#math_toolkit.logistic.logit.synopsis">Synopsis</a>
|
||||
</h5>
|
||||
<pre class="programlisting"><span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">math</span><span class="special">/</span><span class="identifier">special_functions</span><span class="special">/</span><span class="identifier">logit</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span>
|
||||
|
||||
<span class="keyword">namespace</span> <span class="identifier">boost</span> <span class="special">{</span> <span class="keyword">namespace</span> <span class="identifier">math</span> <span class="special">{</span>
|
||||
|
||||
<span class="keyword">template</span> <span class="special"><</span><span class="keyword">typename</span> <span class="identifier">RealType</span><span class="special">,</span> <span class="keyword">typename</span> <span class="identifier">Policy</span><span class="special">></span>
|
||||
<span class="identifier">RealType</span> <span class="identifier">logit</span><span class="special">(</span><span class="identifier">RealType</span> <span class="identifier">x</span><span class="special">,</span> <span class="keyword">const</span> <span class="identifier">Policy</span><span class="special">&);</span>
|
||||
|
||||
<span class="keyword">template</span> <span class="special"><</span><span class="keyword">typename</span> <span class="identifier">RealType</span><span class="special">></span>
|
||||
<span class="identifier">RealType</span> <span class="identifier">logit</span><span class="special">(</span><span class="identifier">RealType</span> <span class="identifier">x</span><span class="special">)</span>
|
||||
|
||||
<span class="special">}}</span> <span class="comment">// namespaces</span>
|
||||
</pre>
|
||||
<h5>
|
||||
<a name="math_toolkit.logistic.logit.h1"></a>
|
||||
<span class="phrase"><a name="math_toolkit.logistic.logit.description"></a></span><a class="link" href="logit.html#math_toolkit.logistic.logit.description">Description</a>
|
||||
</h5>
|
||||
<p>
|
||||
Returns the <a href="https://en.wikipedia.org/wiki/Logit" target="_top">logit function</a>
|
||||
This function is broadly useful, and is used to compute the Quantile of the
|
||||
logistic distribution. The inverse of this function is the logistic_sigmoid.
|
||||
</p>
|
||||
</div>
|
||||
<div class="copyright-footer">Copyright © 2006-2021 Nikhar Agrawal, Anton Bikineev, Matthew Borland,
|
||||
Paul A. Bristow, Marco Guazzone, Christopher Kormanyos, Hubert Holin, Bruno
|
||||
Lalande, John Maddock, Evan Miller, Jeremy Murphy, Matthew Pulver, Johan Råde,
|
||||
Gautam Sewani, Benjamin Sobotta, Nicholas Thompson, Thijs van den Berg, Daryle
|
||||
Walker and Xiaogang Zhang<p>
|
||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
file LICENSE_1_0.txt or copy at <a href="http://www.boost.org/LICENSE_1_0.txt" target="_top">http://www.boost.org/LICENSE_1_0.txt</a>)
|
||||
</p>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="spirit-nav">
|
||||
<a accesskey="p" href="../logistic.html"><img src="../../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../logistic.html"><img src="../../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../../index.html"><img src="../../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="logistic_sigmoid.html"><img src="../../../../../../doc/src/images/next.png" alt="Next"></a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -774,6 +774,7 @@ and as a CD ISBN 0-9504833-2-X 978-0-9504833-2-0, Classification 519.2-dc22.
|
||||
[include quadrature/wavelet_transforms.qbk]
|
||||
[include differentiation/numerical_differentiation.qbk]
|
||||
[include differentiation/autodiff.qbk]
|
||||
[include differentiation/autodiff_reverse.qbk]
|
||||
[include differentiation/lanczos_smoothing.qbk]
|
||||
[endmathpart]
|
||||
|
||||
|
||||
@@ -153,6 +153,8 @@ test-suite examples :
|
||||
[ run ooura_fourier_integrals_cosine_example.cpp : : : [ requires cxx11_hdr_mutex cxx11_inline_namespaces cxx11_auto_declarations cxx17_std_apply ] ]
|
||||
[ run ooura_fourier_integrals_multiprecision_example.cpp : : : [ check-target-builds ../config//has_float128 "GCC libquadmath and __float128 support" : <linkflags>-lquadmath ] [ requires cxx11_hdr_mutex cxx11_inline_namespaces cxx11_auto_declarations cxx17_std_apply ] ]
|
||||
[ run reciprocal_fibonacci_constant.cpp : : : [ check-target-builds ../config//has_mpfr "MPFR Support" : <linkflags>"-lmpfr -lgmp" : <build>no ] ]
|
||||
[ run reverse_mode_linear_regression_example.cpp ]
|
||||
[ run autodiff_reverse_black_scholes.cpp ]
|
||||
;
|
||||
|
||||
run root_elliptic_finding.cpp /boost/timer : : : release <link>static [ requires cxx11_unified_initialization_syntax cxx11_defaulted_functions ] <target-os>freebsd:<linkflags>"-lrt" <target-os>linux:<linkflags>"-lrt -lpthread" ;
|
||||
|
||||
10044
example/airy_ai_float.svg
Normal file
|
After Width: | Height: | Size: 718 KiB |
10044
example/airy_ai_float_white.svg
Normal file
|
After Width: | Height: | Size: 718 KiB |
10038
example/airy_ai_float_white_no_envelope.svg
Normal file
|
After Width: | Height: | Size: 510 KiB |
153
example/autodiff_reverse_black_scholes.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
// Copyright Maksym Zhelyenzyakov 2025-2026.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
#include <boost/math/differentiation/autodiff_reverse.hpp>
|
||||
|
||||
using namespace boost::math::differentiation::reverse_mode;
|
||||
using namespace boost::math::constants;
|
||||
|
||||
template<typename Real>
|
||||
Real phi(Real const& x)
|
||||
{
|
||||
return one_div_root_two_pi<Real>() * exp(-0.5 * x * x);
|
||||
}
|
||||
|
||||
template<typename Real>
|
||||
Real Phi(Real const& x)
|
||||
{
|
||||
return 0.5 * erfc(-one_div_root_two<Real>() * x);
|
||||
}
|
||||
|
||||
enum class CP { call, put };
|
||||
|
||||
template<typename T>
|
||||
T black_scholes_option_price(CP cp, double K, T const& S, T const& sigma, T const& tau, T const& r)
|
||||
{
|
||||
using namespace std;
|
||||
auto const d1 = (log(S / K) + (r + sigma * sigma / 2) * tau) / (sigma * sqrt(tau));
|
||||
auto const d2 = (log(S / K) + (r - sigma * sigma / 2) * tau) / (sigma * sqrt(tau));
|
||||
switch (cp) {
|
||||
case CP::call:
|
||||
return S * Phi<T>(d1) - exp(-r * tau) * K * Phi<T>(d2);
|
||||
case CP::put:
|
||||
return exp(-r * tau) * K * Phi<T>(-d2) - S * Phi<T>(-d1);
|
||||
default:
|
||||
throw runtime_error("Invalid CP value.");
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
double const K = 100.0;
|
||||
double S_val = 105.0;
|
||||
double sigma_val = 5.0;
|
||||
double tau_val = 30.0 / 365;
|
||||
double r_val = 1.25 / 100;
|
||||
rvar<double, 3> S = make_rvar<double, 3>(S_val);
|
||||
rvar<double, 3> sigma = make_rvar<double, 3>(sigma_val);
|
||||
rvar<double, 3> tau = make_rvar<double, 3>(tau_val);
|
||||
rvar<double, 3> r = make_rvar<double, 3>(r_val);
|
||||
|
||||
rvar<double, 3> call_price
|
||||
= black_scholes_option_price<rvar<double, 3>>(CP::call, K, S, sigma, tau, r);
|
||||
rvar<double, 3> put_price
|
||||
= black_scholes_option_price<rvar<double, 3>>(CP::put, K, S, sigma, tau, r);
|
||||
|
||||
double const d1 = ((log(S_val / K) + (r_val + sigma_val * sigma_val / 2) * tau_val)
|
||||
/ (sigma_val * sqrt(tau_val)));
|
||||
double const d2 = ((log(S_val / K) + (r_val - sigma_val * sigma_val / 2) * tau_val)
|
||||
/ (sigma_val * sqrt(tau_val)));
|
||||
double const formula_call_delta = +Phi(+d1);
|
||||
double const formula_put_delta = -Phi(-d1);
|
||||
double const formula_vega = (S_val * phi(d1) * sqrt(tau_val));
|
||||
double const formula_call_theta = (-S_val * phi(d1) * sigma_val / (2 * sqrt(tau_val))
|
||||
- r_val * K * exp(-r_val * tau_val) * Phi(+d2));
|
||||
|
||||
double const formula_put_theta = (-S_val * phi(d1) * sigma_val / (2 * sqrt(tau_val))
|
||||
+ r_val * K * exp(-r_val * tau_val) * Phi(-d2));
|
||||
double const formula_call_rho = (+K * tau_val * exp(-r_val * tau_val) * Phi(+d2));
|
||||
double const formula_put_rho = (-K * tau_val * exp(-r_val * tau_val) * Phi(-d2));
|
||||
double const formula_gamma = (phi(d1) / (S_val * sigma_val * sqrt(tau_val)));
|
||||
double const formula_vanna = (-phi(d1) * d2 / sigma_val);
|
||||
double const formula_charm = (phi(d1) * (d2 * sigma_val * sqrt(tau_val) - 2 * r_val * tau_val)
|
||||
/ (2 * tau_val * sigma_val * sqrt(tau_val)));
|
||||
double const formula_vomma = (S_val * phi(d1) * sqrt(tau_val) * d1 * d2 / sigma_val);
|
||||
double const formula_veta = (-S_val * phi(d1) * sqrt(tau_val)
|
||||
* (r_val * d1 / (sigma_val * sqrt(tau_val))
|
||||
- (1 + d1 * d2) / (2 * tau_val)));
|
||||
double const formula_speed = (-phi(d1) * (d1 / (sigma_val * sqrt(tau_val)) + 1)
|
||||
/ (S_val * S_val * sigma_val * sqrt(tau_val)));
|
||||
double const formula_zomma = (phi(d1) * (d1 * d2 - 1)
|
||||
/ (S_val * sigma_val * sigma_val * sqrt(tau_val)));
|
||||
double const formula_color = (-phi(d1) / (2 * S_val * tau_val * sigma_val * sqrt(tau_val))
|
||||
* (1
|
||||
+ (2 * r_val * tau_val - d2 * sigma_val * sqrt(tau_val)) * d1
|
||||
/ (sigma_val * sqrt(tau_val))));
|
||||
double const formula_ultima = -formula_vega
|
||||
* ((d1 * d2 * (1 - d1 * d2) + d1 * d1 + d2 * d2)
|
||||
/ (sigma_val * sigma_val));
|
||||
|
||||
auto call_greeks = grad(call_price, &S, &sigma, &tau, &r);
|
||||
auto put_greeks = grad(put_price, &S, &sigma, &tau, &r);
|
||||
|
||||
auto call_greeks_2nd_order = grad_nd<2>(call_price, &S, &sigma, &tau, &r);
|
||||
auto put_greeks_2nd_order = grad_nd<2>(put_price, &S, &sigma, &tau, &r);
|
||||
|
||||
auto call_greeks_3rd_order = grad_nd<3>(call_price, &S, &sigma, &tau, &r);
|
||||
auto put_greeks_3rd_order = grad_nd<3>(put_price, &S, &sigma, &tau, &r);
|
||||
|
||||
std::cout << std::setprecision(std::numeric_limits<double>::digits10)
|
||||
<< "autodiff black-scholes call price = " << call_price.item() << "\n"
|
||||
<< "autodiff black-scholes put price = " << put_price.item() << "\n"
|
||||
<< "\n ## First-order Greeks \n"
|
||||
<< "autodiff call delta = " << call_greeks[0].item() << "\n"
|
||||
<< "formula call delta = " << formula_call_delta << "\n"
|
||||
<< "autodiff call vega = " << call_greeks[1].item() << '\n'
|
||||
<< " formula call vega = " << formula_vega << '\n'
|
||||
<< "autodiff call theta = " << -call_greeks[2].item() << '\n'
|
||||
<< " formula call theta = " << formula_call_theta << '\n'
|
||||
<< "autodiff call rho = " << call_greeks[3].item() << 'n'
|
||||
<< " formula call rho = " << formula_call_rho << '\n'
|
||||
<< '\n'
|
||||
<< "autodiff put delta = " << put_greeks[0].item() << 'n'
|
||||
<< " formula put delta = " << formula_put_delta << '\n'
|
||||
<< "autodiff put vega = " << put_greeks[1].item() << '\n'
|
||||
<< " formula put vega = " << formula_vega << '\n'
|
||||
<< "autodiff put theta = " << -put_greeks[2].item() << '\n'
|
||||
<< " formula put theta = " << formula_put_theta << '\n'
|
||||
<< "autodiff put rho = " << put_greeks[3].item() << '\n'
|
||||
<< " formula put rho = " << formula_put_rho << '\n'
|
||||
|
||||
<< "\n## Second-order Greeks\n"
|
||||
<< "autodiff call gamma = " << call_greeks_2nd_order[0][0].item() << '\n'
|
||||
<< "autodiff put gamma = " << put_greeks_2nd_order[0][0].item() << '\n'
|
||||
<< " formula gamma = " << formula_gamma << '\n'
|
||||
<< "autodiff call vanna = " << call_greeks_2nd_order[0][1].item() << '\n'
|
||||
<< "autodiff put vanna = " << put_greeks_2nd_order[0][1].item() << '\n'
|
||||
<< " formula vanna = " << formula_vanna << '\n'
|
||||
<< "autodiff call charm = " << -call_greeks_2nd_order[0][2].item() << '\n'
|
||||
<< "autodiff put charm = " << -put_greeks_2nd_order[0][2].item() << '\n'
|
||||
<< " formula charm = " << formula_charm << '\n'
|
||||
<< "autodiff call vomma = " << call_greeks_2nd_order[1][1].item() << '\n'
|
||||
<< "autodiff put vomma = " << put_greeks_2nd_order[1][1].item() << '\n'
|
||||
<< " formula vomma = " << formula_vomma << '\n'
|
||||
<< "autodiff call veta = " << call_greeks_2nd_order[1][2].item() << '\n'
|
||||
<< "autodiff put veta = " << put_greeks_2nd_order[1][2].item() << '\n'
|
||||
<< " formula veta = " << formula_veta << '\n'
|
||||
|
||||
<< "\n## Third-order Greeks\n"
|
||||
<< "autodiff call speed = " << call_greeks_3rd_order[0][0][0] << '\n'
|
||||
<< "autodiff put speed = " << put_greeks_3rd_order[0][0][0] << '\n'
|
||||
<< " formula speed = " << formula_speed << '\n'
|
||||
<< "autodiff call zomma = " << call_greeks_3rd_order[0][0][1] << '\n'
|
||||
<< "autodiff put zomma = " << put_greeks_3rd_order[0][0][1] << '\n'
|
||||
<< " formula zomma = " << formula_zomma << '\n'
|
||||
<< "autodiff call color = " << call_greeks_3rd_order[0][0][2] << '\n'
|
||||
<< "autodiff put color = " << put_greeks_3rd_order[0][0][2] << '\n'
|
||||
<< " formula color = " << formula_color << '\n'
|
||||
<< "autodiff call ultima = " << call_greeks_3rd_order[1][1][1] << '\n'
|
||||
<< "autodiff put ultima = " << put_greeks_3rd_order[1][1][1] << '\n'
|
||||
<< " formula ultima = " << formula_ultima << '\n';
|
||||
return 0;
|
||||
}
|
||||
100
example/reverse_mode_linear_regression_example.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
// Copyright Maksym Zhelyenzyakov 2025-2026.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
#include <array>
|
||||
#include <boost/math/differentiation/autodiff_reverse.hpp>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
using namespace boost::math::differentiation::reverse_mode;
|
||||
double random_double(double min_val, double max_val)
|
||||
{
|
||||
static std::random_device rd;
|
||||
static std::mt19937 gen(rd());
|
||||
std::uniform_real_distribution<double> dist(min_val, max_val);
|
||||
return dist(gen);
|
||||
}
|
||||
|
||||
template<size_t N>
|
||||
rvar<double, 1> loss(std::array<double, N>& y_target, std::array<rvar<double, 1>, N>& y_fit)
|
||||
{
|
||||
rvar<double, 1> loss_v = make_rvar<double, 1>(0.0);
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
loss_v += pow(abs(y_target[i] - y_fit[i]), 2) / N;
|
||||
}
|
||||
return loss_v;
|
||||
}
|
||||
double noisy_linear_function(double intercept, double slope, double x)
|
||||
{
|
||||
return intercept + slope * x + random_double(-0.1, 0.1);
|
||||
}
|
||||
|
||||
template<size_t N>
|
||||
std::array<rvar<double, 1>, N> model(rvar<double, 1>& a,
|
||||
rvar<double, 1>& b,
|
||||
std::array<double, N>& x)
|
||||
{
|
||||
std::array<rvar<double, 1>, N> ret;
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
ret[i] = a * x[i] + b;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
int main()
|
||||
{
|
||||
double slope = random_double(-5, 5);
|
||||
double intercept = random_double(-5, 5);
|
||||
|
||||
const size_t num_data_samples = 100;
|
||||
/**/
|
||||
std::array<double, num_data_samples> noisy_data_x;
|
||||
std::array<double, num_data_samples> noisy_data_y;
|
||||
for (size_t i = 0; i < num_data_samples; i++) {
|
||||
double x = random_double(-1, 1);
|
||||
double y = noisy_linear_function(intercept, slope, x);
|
||||
noisy_data_x[i] = x;
|
||||
noisy_data_y[i] = y;
|
||||
}
|
||||
|
||||
double slope_guess = random_double(-5, 5);
|
||||
double intercept_guess = random_double(-5, 5);
|
||||
|
||||
rvar<double, 1> a = make_rvar<double, 1>(slope_guess);
|
||||
rvar<double, 1> b = make_rvar<double, 1>(intercept_guess);
|
||||
|
||||
gradient_tape<double, 1>& tape = get_active_tape<double, 1>();
|
||||
tape.add_checkpoint();
|
||||
|
||||
auto y_fit = model(a, b, noisy_data_x);
|
||||
rvar<double, 1> loss_v = loss(noisy_data_y, y_fit);
|
||||
|
||||
double learning_rate = 1e-3;
|
||||
while (loss_v > 0.005) {
|
||||
tape.rewind_to_last_checkpoint();
|
||||
y_fit = model(a, b, noisy_data_x);
|
||||
loss_v = loss(noisy_data_y, y_fit);
|
||||
auto gv = grad(loss_v, &a, &b);
|
||||
a -= gv[0] * learning_rate;
|
||||
b -= gv[1] * learning_rate;
|
||||
}
|
||||
|
||||
double slope_error = std::abs(slope - a.item());
|
||||
double intercept_error = std::abs(intercept - b.item());
|
||||
double relative_slope_error = slope_error / std::abs(slope);
|
||||
double relative_intercept_error = intercept_error / std::abs(intercept);
|
||||
|
||||
std::cout << "Autodiff Linear Regression Summary \n";
|
||||
std::cout << "learning rate : " << learning_rate << "\n";
|
||||
std::cout << "true slope: " << slope;
|
||||
std::cout << " regression: " << a.item() << "\n";
|
||||
|
||||
std::cout << "relative error (slope): " << relative_slope_error << "\n";
|
||||
std::cout << "absolute error (slope): " << slope_error << "\n";
|
||||
std::cout << "true intercept: " << intercept;
|
||||
std::cout << " regression: " << b.item() << "\n";
|
||||
std::cout << "absolute error (intercept): " << intercept_error << "\n";
|
||||
std::cout << "aelative error (intercept): " << relative_intercept_error << "\n";
|
||||
std::cout << "-------------------------------" << std::endl;
|
||||
}
|
||||
785
include/boost/math/differentiation/autodiff_reverse.hpp
Normal file
@@ -0,0 +1,785 @@
|
||||
// Copyright Maksym Zhelyenzyakov 2025-2026.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef BOOST_MATH_DIFFERENTIATION_AUTODIFF_HPP
|
||||
#define BOOST_MATH_DIFFERENTIATION_AUTODIFF_HPP
|
||||
|
||||
#include <boost/math/constants/constants.hpp>
|
||||
#include <boost/math/differentiation/detail/reverse_mode_autodiff_basic_operator_overloads.hpp>
|
||||
#include <boost/math/differentiation/detail/reverse_mode_autodiff_comparison_operator_overloads.hpp>
|
||||
#include <boost/math/differentiation/detail/reverse_mode_autodiff_erf_overloads.hpp>
|
||||
#include <boost/math/differentiation/detail/reverse_mode_autodiff_expression_template_base.hpp>
|
||||
#include <boost/math/differentiation/detail/reverse_mode_autodiff_memory_management.hpp>
|
||||
#include <boost/math/differentiation/detail/reverse_mode_autodiff_stl_overloads.hpp>
|
||||
#include <boost/math/special_functions/acosh.hpp>
|
||||
#include <boost/math/special_functions/asinh.hpp>
|
||||
#include <boost/math/special_functions/atanh.hpp>
|
||||
#include <boost/math/special_functions/digamma.hpp>
|
||||
#include <boost/math/special_functions/erf.hpp>
|
||||
#include <boost/math/special_functions/lambert_w.hpp>
|
||||
#include <boost/math/special_functions/polygamma.hpp>
|
||||
#include <boost/math/special_functions/round.hpp>
|
||||
#include <boost/math/special_functions/trunc.hpp>
|
||||
#include <boost/math/tools/config.hpp>
|
||||
#include <boost/math/tools/promotion.hpp>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#define BOOST_MATH_BUFFER_SIZE 65536
|
||||
|
||||
namespace boost {
|
||||
namespace math {
|
||||
namespace differentiation {
|
||||
namespace reverse_mode {
|
||||
|
||||
/* forward declarations for utitlity functions */
|
||||
template<typename RealType, size_t DerivativeOrder, class DerivedExpression>
|
||||
struct expression;
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder>
|
||||
class rvar;
|
||||
|
||||
template<typename RealType,
|
||||
size_t DerivativeOrder,
|
||||
typename LHS,
|
||||
typename RHS,
|
||||
typename ConcreteBinaryOperation>
|
||||
struct abstract_binary_expression;
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder, typename ARG, typename ConcreteBinaryOperation>
|
||||
struct abstract_unary_expression;
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder>
|
||||
class gradient_node; // forward declaration for tape
|
||||
// manages nodes in computational graph
|
||||
template<typename RealType, size_t DerivativeOrder, size_t buffer_size = BOOST_MATH_BUFFER_SIZE>
|
||||
class gradient_tape
|
||||
{
|
||||
/** @brief tape (graph) management class for autodiff
|
||||
* holds all the data structures for autodiff */
|
||||
private:
|
||||
/* type decays to order - 1 to support higher order derivatives */
|
||||
using inner_t = rvar_t<RealType, DerivativeOrder - 1>;
|
||||
/* adjoints are the overall derivative, and derivatives are the "local"
|
||||
* derivative */
|
||||
detail::flat_linear_allocator<inner_t, buffer_size> adjoints_;
|
||||
detail::flat_linear_allocator<inner_t, buffer_size> derivatives_;
|
||||
detail::flat_linear_allocator<gradient_node<RealType, DerivativeOrder>, buffer_size>
|
||||
gradient_nodes_;
|
||||
detail::flat_linear_allocator<gradient_node<RealType, DerivativeOrder> *, buffer_size>
|
||||
argument_nodes_;
|
||||
|
||||
// compile time check if emplace_back calls on zero
|
||||
template<size_t n>
|
||||
gradient_node<RealType, DerivativeOrder> *fill_node_at_compile_time(
|
||||
std::true_type, gradient_node<RealType, DerivativeOrder> *node_ptr)
|
||||
{
|
||||
node_ptr->derivatives_ = derivatives_.template emplace_back_n<n>();
|
||||
node_ptr->argument_nodes_ = argument_nodes_.template emplace_back_n<n>();
|
||||
return node_ptr;
|
||||
}
|
||||
|
||||
template<size_t n>
|
||||
gradient_node<RealType, DerivativeOrder> *fill_node_at_compile_time(
|
||||
std::false_type, gradient_node<RealType, DerivativeOrder> *node_ptr)
|
||||
{
|
||||
node_ptr->derivatives_ = nullptr;
|
||||
node_ptr->argument_adjoints_ = nullptr;
|
||||
node_ptr->argument_nodes_ = nullptr;
|
||||
return node_ptr;
|
||||
}
|
||||
|
||||
public:
|
||||
/* gradient node stores iterators to its data memebers
|
||||
* (adjoint/derivative/arguments) so that in case flat linear allocator
|
||||
* reaches its block boundary and needs more memory for that node, the
|
||||
* iterator can be invoked to access it */
|
||||
using adjoint_iterator = typename detail::flat_linear_allocator<inner_t, buffer_size>::iterator;
|
||||
using derivatives_iterator =
|
||||
typename detail::flat_linear_allocator<inner_t, buffer_size>::iterator;
|
||||
using gradient_nodes_iterator =
|
||||
typename detail::flat_linear_allocator<gradient_node<RealType, DerivativeOrder>,
|
||||
buffer_size>::iterator;
|
||||
using argument_nodes_iterator =
|
||||
typename detail::flat_linear_allocator<gradient_node<RealType, DerivativeOrder> *,
|
||||
buffer_size>::iterator;
|
||||
|
||||
gradient_tape() { clear(); };
|
||||
|
||||
gradient_tape(const gradient_tape &) = delete;
|
||||
gradient_tape &operator=(const gradient_tape &) = delete;
|
||||
gradient_tape(gradient_tape &&other) = delete;
|
||||
gradient_tape operator=(gradient_tape &&other) = delete;
|
||||
~gradient_tape() noexcept { clear(); }
|
||||
void clear()
|
||||
{
|
||||
adjoints_.clear();
|
||||
derivatives_.clear();
|
||||
gradient_nodes_.clear();
|
||||
argument_nodes_.clear();
|
||||
}
|
||||
|
||||
// no derivatives or arguments
|
||||
gradient_node<RealType, DerivativeOrder> *emplace_leaf_node()
|
||||
{
|
||||
gradient_node<RealType, DerivativeOrder> *node = &*gradient_nodes_.emplace_back();
|
||||
node->adjoint_ = adjoints_.emplace_back();
|
||||
node->derivatives_ = derivatives_iterator(); // nullptr;
|
||||
node->argument_nodes_ = argument_nodes_iterator(); // nullptr;
|
||||
|
||||
return node;
|
||||
};
|
||||
|
||||
// single argument, single derivative
|
||||
gradient_node<RealType, DerivativeOrder> *emplace_active_unary_node()
|
||||
{
|
||||
gradient_node<RealType, DerivativeOrder> *node = &*gradient_nodes_.emplace_back();
|
||||
node->n_ = 1;
|
||||
node->adjoint_ = adjoints_.emplace_back();
|
||||
node->derivatives_ = derivatives_.emplace_back();
|
||||
|
||||
return node;
|
||||
};
|
||||
|
||||
// arbitrary number of arguments/derivatives (compile time)
|
||||
template<size_t n>
|
||||
gradient_node<RealType, DerivativeOrder> *emplace_active_multi_node()
|
||||
{
|
||||
gradient_node<RealType, DerivativeOrder> *node = &*gradient_nodes_.emplace_back();
|
||||
node->n_ = n;
|
||||
node->adjoint_ = adjoints_.emplace_back();
|
||||
// emulate if constexpr
|
||||
return fill_node_at_compile_time<n>(std::integral_constant<bool, (n > 0)>{}, node);
|
||||
}
|
||||
|
||||
// same as above at runtime
|
||||
gradient_node<RealType, DerivativeOrder> *emplace_active_multi_node(size_t n)
|
||||
{
|
||||
gradient_node<RealType, DerivativeOrder> *node = &*gradient_nodes_.emplace_back();
|
||||
node->n_ = n;
|
||||
node->adjoint_ = adjoints_.emplace_back();
|
||||
if (n > 0) {
|
||||
node->derivatives_ = derivatives_.emplace_back_n(n);
|
||||
node->argument_nodes_ = argument_nodes_.emplace_back_n(n);
|
||||
}
|
||||
return node;
|
||||
};
|
||||
/* manual reset button for all adjoints */
|
||||
void zero_grad()
|
||||
{
|
||||
const RealType zero = RealType(0.0);
|
||||
adjoints_.fill(zero);
|
||||
}
|
||||
|
||||
// return type is an iterator
|
||||
auto begin() { return gradient_nodes_.begin(); }
|
||||
auto end() { return gradient_nodes_.end(); }
|
||||
auto find(gradient_node<RealType, DerivativeOrder> *node)
|
||||
{
|
||||
return gradient_nodes_.find(node);
|
||||
};
|
||||
void add_checkpoint()
|
||||
{
|
||||
gradient_nodes_.add_checkpoint();
|
||||
adjoints_.add_checkpoint();
|
||||
derivatives_.add_checkpoint();
|
||||
argument_nodes_.add_checkpoint();
|
||||
};
|
||||
|
||||
auto last_checkpoint() { return gradient_nodes_.last_checkpoint(); };
|
||||
auto first_checkpoint() { return gradient_nodes_.last_checkpoint(); };
|
||||
auto checkpoint_at(size_t index) { return gradient_nodes_.get_checkpoint_at(index); };
|
||||
void rewind_to_last_checkpoint()
|
||||
{
|
||||
gradient_nodes_.rewind_to_last_checkpoint();
|
||||
adjoints_.rewind_to_last_checkpoint();
|
||||
derivatives_.rewind_to_last_checkpoint();
|
||||
argument_nodes_.rewind_to_last_checkpoint();
|
||||
};
|
||||
void rewind_to_checkpoint_at(size_t index) // index is "checkpoint" index. so
|
||||
// order which checkpoint was set
|
||||
{
|
||||
gradient_nodes_.rewind_to_checkpoint_at(index);
|
||||
adjoints_.rewind_to_checkpoint_at(index);
|
||||
derivatives_.rewind_to_checkpoint_at(index);
|
||||
argument_nodes_.rewind_to_checkpoint_at(index);
|
||||
}
|
||||
|
||||
// rewind to beginning of computational graph
|
||||
void rewind()
|
||||
{
|
||||
gradient_nodes_.rewind();
|
||||
adjoints_.rewind();
|
||||
derivatives_.rewind();
|
||||
argument_nodes_.rewind();
|
||||
}
|
||||
|
||||
// random acces
|
||||
gradient_node<RealType, DerivativeOrder> &operator[](size_t i) { return gradient_nodes_[i]; }
|
||||
const gradient_node<RealType, DerivativeOrder> &operator[](size_t i) const
|
||||
{
|
||||
return gradient_nodes_[i];
|
||||
}
|
||||
};
|
||||
// class rvar;
|
||||
template<typename RealType, size_t DerivativeOrder> // no CRTP, just storage
|
||||
class gradient_node
|
||||
{
|
||||
/*
|
||||
* @brief manages adjoints, derivatives, and stores points to argument
|
||||
* adjoints pointers to arguments aren't needed here
|
||||
* */
|
||||
public:
|
||||
using adjoint_iterator = typename gradient_tape<RealType, DerivativeOrder>::adjoint_iterator;
|
||||
using derivatives_iterator =
|
||||
typename gradient_tape<RealType, DerivativeOrder>::derivatives_iterator;
|
||||
using argument_nodes_iterator =
|
||||
typename gradient_tape<RealType, DerivativeOrder>::argument_nodes_iterator;
|
||||
|
||||
private:
|
||||
size_t n_;
|
||||
using inner_t = rvar_t<RealType, DerivativeOrder - 1>;
|
||||
/* these are iterators in case
|
||||
* flat linear allocator is at capacity, and needs to allocate a new block of
|
||||
* memory. */
|
||||
adjoint_iterator adjoint_;
|
||||
derivatives_iterator derivatives_;
|
||||
argument_nodes_iterator argument_nodes_;
|
||||
|
||||
public:
|
||||
friend class gradient_tape<RealType, DerivativeOrder>;
|
||||
friend class rvar<RealType, DerivativeOrder>;
|
||||
|
||||
gradient_node() = default;
|
||||
explicit gradient_node(const size_t n)
|
||||
: n_(n)
|
||||
, adjoint_(nullptr)
|
||||
, derivatives_(nullptr)
|
||||
{}
|
||||
explicit gradient_node(const size_t n,
|
||||
RealType *adjoint,
|
||||
RealType *derivatives,
|
||||
rvar<RealType, DerivativeOrder> **arguments)
|
||||
: n_(n)
|
||||
, adjoint_(adjoint)
|
||||
, derivatives_(derivatives)
|
||||
{ static_cast<void>(arguments); }
|
||||
|
||||
inner_t get_adjoint_v() const { return *adjoint_; }
|
||||
inner_t get_derivative_v(size_t arg_id) const { return derivatives_[static_cast<ptrdiff_t>(arg_id)]; };
|
||||
inner_t get_argument_adjoint_v(size_t arg_id) const
|
||||
{
|
||||
return *argument_nodes_[static_cast<ptrdiff_t>(arg_id)]->adjoint_;
|
||||
}
|
||||
|
||||
adjoint_iterator get_adjoint_ptr() { return adjoint_; }
|
||||
adjoint_iterator get_adjoint_ptr() const { return adjoint_; };
|
||||
void update_adjoint_v(inner_t value) { *adjoint_ = value; };
|
||||
void update_derivative_v(size_t arg_id, inner_t value) { derivatives_[static_cast<ptrdiff_t>(arg_id)] = value; };
|
||||
void update_argument_adj_v(size_t arg_id, inner_t value)
|
||||
{
|
||||
argument_nodes_[static_cast<ptrdiff_t>(arg_id)]->update_adjoint_v(value);
|
||||
};
|
||||
void update_argument_ptr_at(size_t arg_id, gradient_node<RealType, DerivativeOrder> *node_ptr)
|
||||
{
|
||||
argument_nodes_[static_cast<ptrdiff_t>(arg_id)] = node_ptr;
|
||||
}
|
||||
|
||||
void backward()
|
||||
{
|
||||
if (!n_) // leaf node
|
||||
return;
|
||||
|
||||
using boost::math::differentiation::reverse_mode::fabs;
|
||||
using std::fabs;
|
||||
if (!adjoint_ || fabs(*adjoint_) < 2 * std::numeric_limits<RealType>::epsilon())
|
||||
return;
|
||||
|
||||
if (!argument_nodes_) // no arguments
|
||||
return;
|
||||
|
||||
if (!derivatives_) // no derivatives
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < n_; ++i) {
|
||||
auto adjoint = get_adjoint_v();
|
||||
auto derivative = get_derivative_v(i);
|
||||
auto argument_adjoint = get_argument_adjoint_v(i);
|
||||
update_argument_adj_v(i, argument_adjoint + derivative * adjoint);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/****************************************************************************************************************/
|
||||
template<typename RealType, size_t DerivativeOrder>
|
||||
inline gradient_tape<RealType, DerivativeOrder, BOOST_MATH_BUFFER_SIZE> &get_active_tape()
|
||||
{
|
||||
static BOOST_MATH_THREAD_LOCAL gradient_tape<RealType, DerivativeOrder, BOOST_MATH_BUFFER_SIZE>
|
||||
tape;
|
||||
return tape;
|
||||
}
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder = 1>
|
||||
class rvar : public expression<RealType, DerivativeOrder, rvar<RealType, DerivativeOrder>>
|
||||
{
|
||||
private:
|
||||
using inner_t = rvar_t<RealType, DerivativeOrder - 1>;
|
||||
friend class gradient_node<RealType, DerivativeOrder>;
|
||||
inner_t value_;
|
||||
gradient_node<RealType, DerivativeOrder> *node_ = nullptr;
|
||||
template<typename, size_t>
|
||||
friend class rvar;
|
||||
/*****************************************************************************************/
|
||||
/**
|
||||
* @brief implementation helpers for get_value_at
|
||||
*/
|
||||
template<size_t target_order, size_t current_order>
|
||||
struct get_value_at_impl
|
||||
{
|
||||
static_assert(target_order <= current_order, "Requested depth exceeds variable order.");
|
||||
|
||||
/** @return value_ at rvar_t<T,current_order - 1>
|
||||
*/
|
||||
static auto &get(rvar<RealType, current_order> &v)
|
||||
{
|
||||
return get_value_at_impl<target_order, current_order - 1>::get(v.get_value());
|
||||
}
|
||||
/** @return const value_ at rvar_t<T,current_order - 1>
|
||||
*/
|
||||
static const auto &get(const rvar<RealType, current_order> &v)
|
||||
{
|
||||
return get_value_at_impl<target_order, current_order - 1>::get(v.get_value());
|
||||
}
|
||||
};
|
||||
|
||||
/** @brief base case specialization for target_order == current order
|
||||
*/
|
||||
template<size_t target_order>
|
||||
struct get_value_at_impl<target_order, target_order>
|
||||
{
|
||||
/** @return value_ at rvar_t<T,target_order>
|
||||
*/
|
||||
static auto &get(rvar<RealType, target_order> &v) { return v; }
|
||||
/** @return const value_ at rvar_t<T,target_order>
|
||||
*/
|
||||
static const auto &get(const rvar<RealType, target_order> &v) { return v; }
|
||||
};
|
||||
/*****************************************************************************************/
|
||||
void make_leaf_node()
|
||||
{
|
||||
gradient_tape<RealType, DerivativeOrder, BOOST_MATH_BUFFER_SIZE> &tape
|
||||
= get_active_tape<RealType, DerivativeOrder>();
|
||||
node_ = tape.emplace_leaf_node();
|
||||
}
|
||||
|
||||
void make_unary_node()
|
||||
{
|
||||
gradient_tape<RealType, DerivativeOrder, BOOST_MATH_BUFFER_SIZE> &tape
|
||||
= get_active_tape<RealType, DerivativeOrder>();
|
||||
node_ = tape.emplace_active_unary_node();
|
||||
}
|
||||
|
||||
void make_multi_node(size_t n)
|
||||
{
|
||||
gradient_tape<RealType, DerivativeOrder, BOOST_MATH_BUFFER_SIZE> &tape
|
||||
= get_active_tape<RealType, DerivativeOrder>();
|
||||
node_ = tape.emplace_active_multi_node(n);
|
||||
}
|
||||
|
||||
template<size_t n>
|
||||
void make_multi_node()
|
||||
{
|
||||
gradient_tape<RealType, DerivativeOrder, BOOST_MATH_BUFFER_SIZE> &tape
|
||||
= get_active_tape<RealType, DerivativeOrder>();
|
||||
node_ = tape.template emplace_active_multi_node<n>();
|
||||
}
|
||||
|
||||
template<typename E>
|
||||
void make_rvar_from_expr(const expression<RealType, DerivativeOrder, E> &expr)
|
||||
{
|
||||
make_multi_node<detail::count_rvars<E, DerivativeOrder>>();
|
||||
expr.template propagatex<0>(node_, inner_t(1.0));
|
||||
}
|
||||
RealType get_item_impl(std::true_type) const
|
||||
{
|
||||
return value_.get_item_impl(std::integral_constant<bool, (DerivativeOrder - 1 > 1)>{});
|
||||
}
|
||||
|
||||
RealType get_item_impl(std::false_type) const { return value_; }
|
||||
|
||||
public:
|
||||
using value_type = RealType;
|
||||
static constexpr size_t DerivativeOrder_v = DerivativeOrder;
|
||||
rvar()
|
||||
: value_()
|
||||
{
|
||||
make_leaf_node();
|
||||
}
|
||||
rvar(const RealType value)
|
||||
: value_(inner_t{value})
|
||||
{
|
||||
make_leaf_node();
|
||||
}
|
||||
|
||||
rvar &operator=(RealType v)
|
||||
{
|
||||
value_ = inner_t(v);
|
||||
if (node_ == nullptr) {
|
||||
make_leaf_node();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
rvar(const rvar<RealType, DerivativeOrder> &other) = default;
|
||||
rvar &operator=(const rvar<RealType, DerivativeOrder> &other) = default;
|
||||
|
||||
template<size_t arg_index>
|
||||
void propagatex(gradient_node<RealType, DerivativeOrder> *node, inner_t adj) const
|
||||
{
|
||||
node->update_derivative_v(arg_index, adj);
|
||||
node->update_argument_ptr_at(arg_index, node_);
|
||||
}
|
||||
|
||||
template<class E>
|
||||
rvar(const expression<RealType, DerivativeOrder, E> &expr)
|
||||
{
|
||||
value_ = expr.evaluate();
|
||||
make_rvar_from_expr(expr);
|
||||
}
|
||||
template<class E>
|
||||
rvar &operator=(const expression<RealType, DerivativeOrder, E> &expr)
|
||||
{
|
||||
value_ = expr.evaluate();
|
||||
make_rvar_from_expr(expr);
|
||||
return *this;
|
||||
}
|
||||
/***************************************************************************************************/
|
||||
template<class E>
|
||||
rvar<RealType, DerivativeOrder> &operator+=(const expression<RealType, DerivativeOrder, E> &expr)
|
||||
{
|
||||
*this = *this + expr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class E>
|
||||
rvar<RealType, DerivativeOrder> &operator*=(const expression<RealType, DerivativeOrder, E> &expr)
|
||||
{
|
||||
*this = *this * expr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class E>
|
||||
rvar<RealType, DerivativeOrder> &operator-=(const expression<RealType, DerivativeOrder, E> &expr)
|
||||
{
|
||||
*this = *this - expr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class E>
|
||||
rvar<RealType, DerivativeOrder> &operator/=(const expression<RealType, DerivativeOrder, E> &expr)
|
||||
{
|
||||
*this = *this / expr;
|
||||
return *this;
|
||||
}
|
||||
/***************************************************************************************************/
|
||||
rvar<RealType, DerivativeOrder> &operator+=(const RealType &v)
|
||||
{
|
||||
*this = *this + v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
rvar<RealType, DerivativeOrder> &operator*=(const RealType &v)
|
||||
{
|
||||
*this = *this * v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
rvar<RealType, DerivativeOrder> &operator-=(const RealType &v)
|
||||
{
|
||||
*this = *this - v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
rvar<RealType, DerivativeOrder> &operator/=(const RealType &v)
|
||||
{
|
||||
*this = *this / v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/***************************************************************************************************/
|
||||
const inner_t &adjoint() const { return *node_->get_adjoint_ptr(); }
|
||||
inner_t &adjoint() { return *node_->get_adjoint_ptr(); }
|
||||
|
||||
const inner_t &evaluate() const { return value_; };
|
||||
inner_t &get_value() { return value_; };
|
||||
|
||||
explicit operator RealType() const { return item(); }
|
||||
|
||||
explicit operator int() const { return static_cast<int>(item()); }
|
||||
explicit operator long() const { return static_cast<long>(item()); }
|
||||
explicit operator long long() const { return static_cast<long long>(item()); }
|
||||
|
||||
/**
|
||||
* @brief same as evaluate but returns proper depth for higher order derivatives
|
||||
* @return value_ at depth N
|
||||
*/
|
||||
template<size_t N>
|
||||
auto &get_value_at()
|
||||
{
|
||||
static_assert(N <= DerivativeOrder, "Requested depth exceeds variable order.");
|
||||
return get_value_at_impl<N, DerivativeOrder>::get(*this);
|
||||
}
|
||||
/** @brief same as above but const
|
||||
*/
|
||||
template<size_t N>
|
||||
const auto &get_value_at() const
|
||||
{
|
||||
static_assert(N <= DerivativeOrder, "Requested depth exceeds variable order.");
|
||||
return get_value_at_impl<N, DerivativeOrder>::get(*this);
|
||||
}
|
||||
|
||||
RealType item() const
|
||||
{
|
||||
return get_item_impl(std::integral_constant<bool, (DerivativeOrder > 1)>{});
|
||||
}
|
||||
|
||||
void backward()
|
||||
{
|
||||
gradient_tape<RealType, DerivativeOrder, BOOST_MATH_BUFFER_SIZE> &tape
|
||||
= get_active_tape<RealType, DerivativeOrder>();
|
||||
auto it = tape.find(node_);
|
||||
it->update_adjoint_v(inner_t(1.0));
|
||||
while (it != tape.begin()) {
|
||||
it->backward();
|
||||
--it;
|
||||
}
|
||||
it->backward();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder>
|
||||
std::ostream &operator<<(std::ostream &os, const rvar<RealType, DerivativeOrder> var)
|
||||
{
|
||||
os << "rvar<" << DerivativeOrder << ">(" << var.item() << "," << var.adjoint() << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder, typename E>
|
||||
std::ostream &operator<<(std::ostream &os, const expression<RealType, DerivativeOrder, E> &expr)
|
||||
{
|
||||
rvar<RealType, DerivativeOrder> tmp = expr;
|
||||
os << "rvar<" << DerivativeOrder << ">(" << tmp.item() << "," << tmp.adjoint() << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder>
|
||||
rvar<RealType, DerivativeOrder> make_rvar(const RealType v)
|
||||
{
|
||||
static_assert(DerivativeOrder > 0, "rvar order must be >= 1");
|
||||
return rvar<RealType, DerivativeOrder>(v);
|
||||
}
|
||||
template<typename RealType, size_t DerivativeOrder, typename E>
|
||||
rvar<RealType, DerivativeOrder> make_rvar(const expression<RealType, DerivativeOrder, E> &expr)
|
||||
{
|
||||
static_assert(DerivativeOrder > 0, "rvar order must be >= 1");
|
||||
return rvar<RealType, DerivativeOrder>(expr);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
/** @brief helper overload for grad implementation.
|
||||
* @return vector<rvar<T,order-1> of gradients of the autodiff graph.
|
||||
* specialization for autodiffing through autodiff. i.e. being able to
|
||||
* compute higher order grads
|
||||
*/
|
||||
template<typename RealType, size_t DerivativeOrder>
|
||||
struct grad_op_impl
|
||||
{
|
||||
std::vector<rvar<RealType, DerivativeOrder - 1>> operator()(
|
||||
rvar<RealType, DerivativeOrder> &f, std::vector<rvar<RealType, DerivativeOrder> *> &x)
|
||||
{
|
||||
auto &tape = get_active_tape<RealType, DerivativeOrder>();
|
||||
tape.zero_grad();
|
||||
f.backward();
|
||||
|
||||
std::vector<rvar<RealType, DerivativeOrder - 1>> gradient_vector;
|
||||
gradient_vector.reserve(x.size());
|
||||
|
||||
for (auto &xi : x) {
|
||||
gradient_vector.emplace_back(xi->adjoint());
|
||||
}
|
||||
return gradient_vector;
|
||||
}
|
||||
};
|
||||
/** @brief helper overload for grad implementation.
|
||||
* @return vector<T> of gradients of the autodiff graph.
|
||||
* base specialization for order 1 autodiff
|
||||
*/
|
||||
template<typename T>
|
||||
struct grad_op_impl<T, 1>
|
||||
{
|
||||
std::vector<T> operator()(rvar<T, 1> &f, std::vector<rvar<T, 1> *> &x)
|
||||
{
|
||||
gradient_tape<T, 1, BOOST_MATH_BUFFER_SIZE> &tape = get_active_tape<T, 1>();
|
||||
tape.zero_grad();
|
||||
f.backward();
|
||||
std::vector<T> gradient_vector;
|
||||
gradient_vector.reserve(x.size());
|
||||
for (auto &xi : x) {
|
||||
gradient_vector.push_back(xi->adjoint());
|
||||
}
|
||||
return gradient_vector;
|
||||
}
|
||||
};
|
||||
|
||||
/** @brief helper overload for higher order autodiff
|
||||
* @return nested vector representing N-d tensor of
|
||||
* higher order derivatives
|
||||
*/
|
||||
template<size_t N,
|
||||
typename RealType,
|
||||
size_t DerivativeOrder_1,
|
||||
size_t DerivativeOrder_2,
|
||||
typename Enable = void>
|
||||
struct grad_nd_impl
|
||||
{
|
||||
auto operator()(rvar<RealType, DerivativeOrder_1> &f,
|
||||
std::vector<rvar<RealType, DerivativeOrder_2> *> &x)
|
||||
{
|
||||
static_assert(N > 1, "N must be greater than 1 for this template");
|
||||
|
||||
auto current_grad = grad(f, x); // vector<rvar<T,DerivativeOrder_1-1>> or vector<T>
|
||||
|
||||
std::vector<decltype(grad_nd_impl<N - 1, RealType, DerivativeOrder_1 - 1, DerivativeOrder_2>()(
|
||||
current_grad[0], x))>
|
||||
result;
|
||||
result.reserve(current_grad.size());
|
||||
|
||||
for (auto &g_i : current_grad) {
|
||||
result.push_back(
|
||||
grad_nd_impl<N - 1, RealType, DerivativeOrder_1 - 1, DerivativeOrder_2>()(g_i, x));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
/** @brief spcialization for order = 1,
|
||||
* @return vector<rvar<T,DerivativeOrder_1-1>> gradients */
|
||||
template<typename RealType, size_t DerivativeOrder_1, size_t DerivativeOrder_2>
|
||||
struct grad_nd_impl<1, RealType, DerivativeOrder_1, DerivativeOrder_2>
|
||||
{
|
||||
auto operator()(rvar<RealType, DerivativeOrder_1> &f,
|
||||
std::vector<rvar<RealType, DerivativeOrder_2> *> &x)
|
||||
{
|
||||
return grad(f, x);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ptr>
|
||||
struct rvar_order;
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder>
|
||||
struct rvar_order<rvar<RealType, DerivativeOrder> *>
|
||||
{
|
||||
static constexpr size_t value = DerivativeOrder;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @brief grad computes gradient with respect to vector of pointers x
|
||||
* @param f -> computational graph
|
||||
* @param x -> variables gradients to record. Note ALL gradients of the graph
|
||||
* are computed simultaneously, only the ones w.r.t. x are returned
|
||||
* @return vector<rvar<T,DerivativeOrder_1 - 1> of gradients. in the case of DerivativeOrder_1 = 1
|
||||
* rvar<T,DerivativeOrder_1-1> decays to T
|
||||
*
|
||||
* safe to call recursively with grad(grad(grad...
|
||||
*/
|
||||
template<typename RealType, size_t DerivativeOrder_1, size_t DerivativeOrder_2>
|
||||
auto grad(rvar<RealType, DerivativeOrder_1> &f, std::vector<rvar<RealType, DerivativeOrder_2> *> &x)
|
||||
{
|
||||
static_assert(DerivativeOrder_1 <= DerivativeOrder_2,
|
||||
"variable differentiating w.r.t. must have order >= function order");
|
||||
std::vector<rvar<RealType, DerivativeOrder_1> *> xx;
|
||||
xx.reserve(x.size());
|
||||
for (auto &xi : x)
|
||||
xx.push_back(&(xi->template get_value_at<DerivativeOrder_1>()));
|
||||
return detail::grad_op_impl<RealType, DerivativeOrder_1>{}(f, xx);
|
||||
}
|
||||
/** @brief variadic overload of above
|
||||
*/
|
||||
template<typename RealType, size_t DerivativeOrder_1, typename First, typename... Other>
|
||||
auto grad(rvar<RealType, DerivativeOrder_1> &f, First first, Other... other)
|
||||
{
|
||||
constexpr size_t DerivativeOrder_2 = detail::rvar_order<First>::value;
|
||||
static_assert(DerivativeOrder_1 <= DerivativeOrder_2,
|
||||
"variable differentiating w.r.t. must have order >= function order");
|
||||
std::vector<rvar<RealType, DerivativeOrder_2> *> x_vec = {first, other...};
|
||||
return grad(f, x_vec);
|
||||
}
|
||||
|
||||
/** @brief computes hessian matrix of computational graph w.r.t.
|
||||
* vector of variables x.
|
||||
* @return std::vector<std::vector<rvar<T,DerivativeOrder_1-2>> hessian matrix
|
||||
* rvar<T,2> decays to T
|
||||
*
|
||||
* NOT recursion safe, cannot do hess(hess(
|
||||
*/
|
||||
template<typename RealType, size_t DerivativeOrder_1, size_t DerivativeOrder_2>
|
||||
auto hess(rvar<RealType, DerivativeOrder_1> &f, std::vector<rvar<RealType, DerivativeOrder_2> *> &x)
|
||||
{
|
||||
return detail::grad_nd_impl<2, RealType, DerivativeOrder_1, DerivativeOrder_2>{}(f, x);
|
||||
}
|
||||
/** @brief variadic overload of above
|
||||
*/
|
||||
template<typename RealType, size_t DerivativeOrder_1, typename First, typename... Other>
|
||||
auto hess(rvar<RealType, DerivativeOrder_1> &f, First first, Other... other)
|
||||
{
|
||||
constexpr size_t DerivativeOrder_2 = detail::rvar_order<First>::value;
|
||||
std::vector<rvar<RealType, DerivativeOrder_2> *> x_vec = {first, other...};
|
||||
return hess(f, x_vec);
|
||||
}
|
||||
|
||||
/** @brief comput N'th gradient of computational graph w.r.t. x
|
||||
* @return vector<vector<.... up N nestings representing tensor
|
||||
* of gradients of order N
|
||||
*
|
||||
* NOT recursively safe, cannot do grad_nd(grad_nd(... etc...
|
||||
*/
|
||||
template<size_t N, typename RealType, size_t DerivativeOrder_1, size_t DerivativeOrder_2>
|
||||
auto grad_nd(rvar<RealType, DerivativeOrder_1> &f,
|
||||
std::vector<rvar<RealType, DerivativeOrder_2> *> &x)
|
||||
{
|
||||
static_assert(DerivativeOrder_1 >= N, "Function order must be at least N");
|
||||
static_assert(DerivativeOrder_2 >= DerivativeOrder_1,
|
||||
"Variable order must be at least function order");
|
||||
|
||||
return detail::grad_nd_impl<N, RealType, DerivativeOrder_1, DerivativeOrder_2>()(f, x);
|
||||
}
|
||||
|
||||
/** @brief variadic overload of above
|
||||
*/
|
||||
template<size_t N, typename ftype, typename First, typename... Other>
|
||||
auto grad_nd(ftype &f, First first, Other... other)
|
||||
{
|
||||
using RealType = typename ftype::value_type;
|
||||
constexpr size_t DerivativeOrder_1 = detail::rvar_order<ftype *>::value;
|
||||
constexpr size_t DerivativeOrder_2 = detail::rvar_order<First>::value;
|
||||
std::vector<rvar<RealType, DerivativeOrder_2> *> x_vec = {first, other...};
|
||||
return detail::grad_nd_impl<N, RealType, DerivativeOrder_1, DerivativeOrder_1>{}(f, x_vec);
|
||||
}
|
||||
} // namespace reverse_mode
|
||||
} // namespace differentiation
|
||||
} // namespace math
|
||||
} // namespace boost
|
||||
namespace std {
|
||||
|
||||
// copied from forward mode
|
||||
template<typename RealType, size_t DerivativeOrder>
|
||||
class numeric_limits<boost::math::differentiation::reverse_mode::rvar<RealType, DerivativeOrder>>
|
||||
: public numeric_limits<typename boost::math::differentiation::reverse_mode::
|
||||
rvar<RealType, DerivativeOrder>::value_type>
|
||||
{};
|
||||
} // namespace std
|
||||
#endif
|
||||
@@ -0,0 +1,418 @@
|
||||
// Copyright Maksym Zhelyenzyakov 2025-2026.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef REVERSE_MODE_AUTODOFF_BASIC_OPERATOR_OVERLOADS_HPP
|
||||
#define REVERSE_MODE_AUTODOFF_BASIC_OPERATOR_OVERLOADS_HPP
|
||||
|
||||
#include <boost/math/differentiation/detail/reverse_mode_autodiff_expression_template_base.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace math {
|
||||
namespace differentiation {
|
||||
namespace reverse_mode {
|
||||
/****************************************************************************************************************/
|
||||
template<typename RealType, size_t DerivativeOrder, typename LHS, typename RHS>
|
||||
struct add_expr : public abstract_binary_expression<RealType,
|
||||
DerivativeOrder,
|
||||
LHS,
|
||||
RHS,
|
||||
add_expr<RealType, DerivativeOrder, LHS, RHS>>
|
||||
{
|
||||
/* @brief addition
|
||||
* rvar+rvar
|
||||
* */
|
||||
using inner_t = rvar_t<RealType, DerivativeOrder - 1>;
|
||||
// Explicitly define constructor to forward to base class
|
||||
explicit add_expr(const expression<RealType, DerivativeOrder, LHS> &left_hand_expr,
|
||||
const expression<RealType, DerivativeOrder, RHS> &right_hand_expr)
|
||||
: abstract_binary_expression<RealType,
|
||||
DerivativeOrder,
|
||||
LHS,
|
||||
RHS,
|
||||
add_expr<RealType, DerivativeOrder, LHS, RHS>>(left_hand_expr,
|
||||
right_hand_expr)
|
||||
{}
|
||||
|
||||
inner_t evaluate() const { return this->lhs.evaluate() + this->rhs.evaluate(); }
|
||||
static const inner_t left_derivative(const inner_t & /*l*/,
|
||||
const inner_t & /*r*/,
|
||||
const inner_t & /*v*/)
|
||||
{
|
||||
return inner_t(1.0);
|
||||
}
|
||||
static const inner_t right_derivative(const inner_t & /*l*/,
|
||||
const inner_t & /*r*/,
|
||||
const inner_t & /*v*/)
|
||||
{
|
||||
return inner_t(1.0);
|
||||
}
|
||||
};
|
||||
template<typename RealType, size_t DerivativeOrder, typename ARG>
|
||||
struct add_const_expr
|
||||
: public abstract_unary_expression<RealType,
|
||||
DerivativeOrder,
|
||||
ARG,
|
||||
add_const_expr<RealType, DerivativeOrder, ARG>>
|
||||
{
|
||||
/* @brief
|
||||
* rvar+float or float+rvar
|
||||
* */
|
||||
using inner_t = rvar_t<RealType, DerivativeOrder - 1>;
|
||||
explicit add_const_expr(const expression<RealType, DerivativeOrder, ARG> &arg_expr,
|
||||
const RealType v)
|
||||
: abstract_unary_expression<RealType,
|
||||
DerivativeOrder,
|
||||
ARG,
|
||||
add_const_expr<RealType, DerivativeOrder, ARG>>(arg_expr, v){};
|
||||
inner_t evaluate() const { return this->arg.evaluate() + inner_t(this->constant); }
|
||||
static const inner_t derivative(const inner_t & /*argv*/,
|
||||
const inner_t & /*v*/,
|
||||
const RealType & /*constant*/)
|
||||
{
|
||||
return inner_t(1.0);
|
||||
}
|
||||
};
|
||||
/****************************************************************************************************************/
|
||||
template<typename RealType, size_t DerivativeOrder, typename LHS, typename RHS>
|
||||
struct mult_expr : public abstract_binary_expression<RealType,
|
||||
DerivativeOrder,
|
||||
LHS,
|
||||
RHS,
|
||||
mult_expr<RealType, DerivativeOrder, LHS, RHS>>
|
||||
{
|
||||
/* @brief multiplication
|
||||
* rvar * rvar
|
||||
* */
|
||||
using inner_t = rvar_t<RealType, DerivativeOrder - 1>;
|
||||
explicit mult_expr(const expression<RealType, DerivativeOrder, LHS> &left_hand_expr,
|
||||
const expression<RealType, DerivativeOrder, RHS> &right_hand_expr)
|
||||
: abstract_binary_expression<RealType,
|
||||
DerivativeOrder,
|
||||
LHS,
|
||||
RHS,
|
||||
mult_expr<RealType, DerivativeOrder, LHS, RHS>>(left_hand_expr,
|
||||
right_hand_expr)
|
||||
{}
|
||||
|
||||
inner_t evaluate() const { return this->lhs.evaluate() * this->rhs.evaluate(); };
|
||||
static const inner_t left_derivative(const inner_t & /*l*/,
|
||||
const inner_t &r,
|
||||
const inner_t & /*v*/) noexcept
|
||||
{
|
||||
return r;
|
||||
};
|
||||
static const inner_t right_derivative(const inner_t &l,
|
||||
const inner_t & /*r*/,
|
||||
const inner_t & /*v*/) noexcept
|
||||
{
|
||||
return l;
|
||||
};
|
||||
};
|
||||
template<typename RealType, size_t DerivativeOrder, typename ARG>
|
||||
struct mult_const_expr
|
||||
: public abstract_unary_expression<RealType,
|
||||
DerivativeOrder,
|
||||
ARG,
|
||||
mult_const_expr<RealType, DerivativeOrder, ARG>>
|
||||
{
|
||||
/* @brief
|
||||
* rvar+float or float+rvar
|
||||
* */
|
||||
using inner_t = rvar_t<RealType, DerivativeOrder - 1>;
|
||||
|
||||
explicit mult_const_expr(const expression<RealType, DerivativeOrder, ARG> &arg_expr,
|
||||
const RealType v)
|
||||
: abstract_unary_expression<RealType,
|
||||
DerivativeOrder,
|
||||
ARG,
|
||||
mult_const_expr<RealType, DerivativeOrder, ARG>>(arg_expr, v){};
|
||||
|
||||
inner_t evaluate() const { return this->arg.evaluate() * inner_t(this->constant); }
|
||||
static const inner_t derivative(const inner_t & /*argv*/,
|
||||
const inner_t & /*v*/,
|
||||
const RealType &constant)
|
||||
{
|
||||
return inner_t(constant);
|
||||
}
|
||||
};
|
||||
/****************************************************************************************************************/
|
||||
template<typename RealType, size_t DerivativeOrder, typename LHS, typename RHS>
|
||||
struct sub_expr : public abstract_binary_expression<RealType,
|
||||
DerivativeOrder,
|
||||
LHS,
|
||||
RHS,
|
||||
sub_expr<RealType, DerivativeOrder, LHS, RHS>>
|
||||
{
|
||||
/* @brief addition
|
||||
* rvar-rvar
|
||||
* */
|
||||
using inner_t = rvar_t<RealType, DerivativeOrder - 1>;
|
||||
// Explicitly define constructor to forward to base class
|
||||
explicit sub_expr(const expression<RealType, DerivativeOrder, LHS> &left_hand_expr,
|
||||
const expression<RealType, DerivativeOrder, RHS> &right_hand_expr)
|
||||
: abstract_binary_expression<RealType,
|
||||
DerivativeOrder,
|
||||
LHS,
|
||||
RHS,
|
||||
sub_expr<RealType, DerivativeOrder, LHS, RHS>>(left_hand_expr,
|
||||
right_hand_expr)
|
||||
{}
|
||||
|
||||
inner_t evaluate() const { return this->lhs.evaluate() - this->rhs.evaluate(); }
|
||||
static const inner_t left_derivative(const inner_t & /*l*/,
|
||||
const inner_t & /*r*/,
|
||||
const inner_t & /*v*/)
|
||||
{
|
||||
return inner_t(1.0);
|
||||
}
|
||||
static const inner_t right_derivative(const inner_t & /*l*/,
|
||||
const inner_t & /*r*/,
|
||||
const inner_t & /*v*/)
|
||||
{
|
||||
return inner_t(-1.0);
|
||||
}
|
||||
};
|
||||
|
||||
/****************************************************************************************************************/
|
||||
template<typename RealType, size_t DerivativeOrder, typename LHS, typename RHS>
|
||||
struct div_expr : public abstract_binary_expression<RealType,
|
||||
DerivativeOrder,
|
||||
LHS,
|
||||
RHS,
|
||||
div_expr<RealType, DerivativeOrder, LHS, RHS>>
|
||||
{
|
||||
/* @brief multiplication
|
||||
* rvar / rvar
|
||||
* */
|
||||
using inner_t = rvar_t<RealType, DerivativeOrder - 1>;
|
||||
// Explicitly define constructor to forward to base class
|
||||
explicit div_expr(const expression<RealType, DerivativeOrder, LHS> &left_hand_expr,
|
||||
const expression<RealType, DerivativeOrder, RHS> &right_hand_expr)
|
||||
: abstract_binary_expression<RealType,
|
||||
DerivativeOrder,
|
||||
LHS,
|
||||
RHS,
|
||||
div_expr<RealType, DerivativeOrder, LHS, RHS>>(left_hand_expr,
|
||||
right_hand_expr)
|
||||
{}
|
||||
|
||||
inner_t evaluate() const { return this->lhs.evaluate() / this->rhs.evaluate(); };
|
||||
static const inner_t left_derivative(const inner_t & /*l*/,
|
||||
const inner_t &r,
|
||||
const inner_t & /*v*/)
|
||||
{
|
||||
return static_cast<RealType>(1.0) / r;
|
||||
};
|
||||
static const inner_t right_derivative(const inner_t &l, const inner_t &r, const inner_t & /*v*/)
|
||||
{
|
||||
return -l / (r * r);
|
||||
};
|
||||
};
|
||||
template<typename RealType, size_t DerivativeOrder, typename ARG>
|
||||
struct div_by_const_expr
|
||||
: public abstract_unary_expression<RealType,
|
||||
DerivativeOrder,
|
||||
ARG,
|
||||
div_by_const_expr<RealType, DerivativeOrder, ARG>>
|
||||
{
|
||||
/* @brief
|
||||
* rvar/float
|
||||
* */
|
||||
using inner_t = rvar_t<RealType, DerivativeOrder - 1>;
|
||||
|
||||
explicit div_by_const_expr(const expression<RealType, DerivativeOrder, ARG> &arg_expr,
|
||||
const RealType v)
|
||||
: abstract_unary_expression<RealType,
|
||||
DerivativeOrder,
|
||||
ARG,
|
||||
div_by_const_expr<RealType, DerivativeOrder, ARG>>(arg_expr,
|
||||
v){};
|
||||
|
||||
inner_t evaluate() const { return this->arg.evaluate() / inner_t(this->constant); }
|
||||
static const inner_t derivative(const inner_t & /*argv*/,
|
||||
const inner_t & /*v*/,
|
||||
const RealType &constant)
|
||||
{
|
||||
return inner_t(1.0 / constant);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder, typename ARG>
|
||||
struct const_div_by_expr
|
||||
: public abstract_unary_expression<RealType,
|
||||
DerivativeOrder,
|
||||
ARG,
|
||||
const_div_by_expr<RealType, DerivativeOrder, ARG>>
|
||||
{
|
||||
/** @brief
|
||||
* float/rvar
|
||||
* */
|
||||
using inner_t = rvar_t<RealType, DerivativeOrder - 1>;
|
||||
|
||||
explicit const_div_by_expr(const expression<RealType, DerivativeOrder, ARG> &arg_expr,
|
||||
const RealType v)
|
||||
: abstract_unary_expression<RealType,
|
||||
DerivativeOrder,
|
||||
ARG,
|
||||
const_div_by_expr<RealType, DerivativeOrder, ARG>>(arg_expr,
|
||||
v){};
|
||||
|
||||
inner_t evaluate() const { return inner_t(this->constant) / this->arg.evaluate(); }
|
||||
static const inner_t derivative(const inner_t &argv,
|
||||
const inner_t & /*v*/,
|
||||
const RealType &constant)
|
||||
{
|
||||
return -inner_t{constant} / (argv * argv);
|
||||
}
|
||||
};
|
||||
/****************************************************************************************************************/
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder, typename LHS, typename RHS>
|
||||
mult_expr<RealType, DerivativeOrder, LHS, RHS> operator*(
|
||||
const expression<RealType, DerivativeOrder, LHS> &lhs,
|
||||
const expression<RealType, DerivativeOrder, RHS> &rhs)
|
||||
{
|
||||
return mult_expr<RealType, DerivativeOrder, LHS, RHS>(lhs, rhs);
|
||||
}
|
||||
|
||||
/** @brief type promotion is handled by casting the numeric type to
|
||||
* the type inside expression. This is to avoid converting the
|
||||
* entire tape in case you have something like double * rvar<float>
|
||||
* */
|
||||
template<typename RealType2,
|
||||
typename RealType1,
|
||||
size_t DerivativeOrder,
|
||||
typename ARG,
|
||||
typename = typename std::enable_if<!detail::is_expression<RealType2>::value>::type>
|
||||
mult_const_expr<RealType1, DerivativeOrder, ARG> operator*(
|
||||
const expression<RealType1, DerivativeOrder, ARG> &arg, const RealType2 &v)
|
||||
{
|
||||
return mult_const_expr<RealType1, DerivativeOrder, ARG>(arg, static_cast<RealType1>(v));
|
||||
}
|
||||
template<typename RealType2,
|
||||
typename RealType1,
|
||||
size_t DerivativeOrder,
|
||||
typename ARG,
|
||||
typename = typename std::enable_if<!detail::is_expression<RealType2>::value>::type>
|
||||
mult_const_expr<RealType1, DerivativeOrder, ARG> operator*(
|
||||
const RealType2 &v, const expression<RealType1, DerivativeOrder, ARG> &arg)
|
||||
{
|
||||
return mult_const_expr<RealType1, DerivativeOrder, ARG>(arg, static_cast<RealType1>(v));
|
||||
}
|
||||
/****************************************************************************************************************/
|
||||
/* + */
|
||||
template<typename RealType, size_t DerivativeOrder, typename LHS, typename RHS>
|
||||
add_expr<RealType, DerivativeOrder, LHS, RHS> operator+(
|
||||
const expression<RealType, DerivativeOrder, LHS> &lhs,
|
||||
const expression<RealType, DerivativeOrder, RHS> &rhs)
|
||||
{
|
||||
return add_expr<RealType, DerivativeOrder, LHS, RHS>(lhs, rhs);
|
||||
}
|
||||
template<typename RealType2,
|
||||
typename RealType1,
|
||||
size_t DerivativeOrder,
|
||||
typename ARG,
|
||||
typename = typename std::enable_if<!detail::is_expression<RealType2>::value>::type>
|
||||
add_const_expr<RealType1, DerivativeOrder, ARG> operator+(
|
||||
const expression<RealType1, DerivativeOrder, ARG> &arg, const RealType2 &v)
|
||||
{
|
||||
return add_const_expr<RealType1, DerivativeOrder, ARG>(arg, static_cast<RealType1>(v));
|
||||
}
|
||||
template<typename RealType2,
|
||||
typename RealType1,
|
||||
size_t DerivativeOrder,
|
||||
typename ARG,
|
||||
typename = typename std::enable_if<!detail::is_expression<RealType2>::value>::type>
|
||||
add_const_expr<RealType1, DerivativeOrder, ARG> operator+(
|
||||
const RealType2 &v, const expression<RealType1, DerivativeOrder, ARG> &arg)
|
||||
{
|
||||
return add_const_expr<RealType1, DerivativeOrder, ARG>(arg, static_cast<RealType1>(v));
|
||||
}
|
||||
/****************************************************************************************************************/
|
||||
/* - overload */
|
||||
/** @brief
|
||||
* negation (-1.0*rvar) */
|
||||
template<typename RealType, size_t DerivativeOrder, typename ARG>
|
||||
mult_const_expr<RealType, DerivativeOrder, ARG> operator-(
|
||||
const expression<RealType, DerivativeOrder, ARG> &arg)
|
||||
{
|
||||
return mult_const_expr<RealType, DerivativeOrder, ARG>(arg, static_cast<RealType>(-1.0));
|
||||
}
|
||||
|
||||
/** @brief
|
||||
* subtraction rvar-rvar */
|
||||
template<typename RealType, size_t DerivativeOrder, typename LHS, typename RHS>
|
||||
sub_expr<RealType, DerivativeOrder, LHS, RHS> operator-(
|
||||
const expression<RealType, DerivativeOrder, LHS> &lhs,
|
||||
const expression<RealType, DerivativeOrder, RHS> &rhs)
|
||||
{
|
||||
return sub_expr<RealType, DerivativeOrder, LHS, RHS>(lhs, rhs);
|
||||
}
|
||||
|
||||
/** @brief
|
||||
* subtraction float - rvar */
|
||||
template<typename RealType2,
|
||||
typename RealType1,
|
||||
size_t DerivativeOrder,
|
||||
typename ARG,
|
||||
typename = typename std::enable_if<!detail::is_expression<RealType2>::value>::type>
|
||||
add_const_expr<RealType1, DerivativeOrder, ARG> operator-(
|
||||
const expression<RealType1, DerivativeOrder, ARG> &arg, const RealType2 &v)
|
||||
{
|
||||
/* rvar - float = rvar + (-float) */
|
||||
return add_const_expr<RealType1, DerivativeOrder, ARG>(arg, static_cast<RealType1>(-v));
|
||||
}
|
||||
|
||||
/** @brief
|
||||
* subtraction float - rvar
|
||||
* @return add_expr<neg_expr<ARG>>
|
||||
*/
|
||||
template<typename RealType2,
|
||||
typename RealType1,
|
||||
size_t DerivativeOrder,
|
||||
typename ARG,
|
||||
typename = typename std::enable_if<!detail::is_expression<RealType2>::value>::type>
|
||||
auto operator-(const RealType2 &v, const expression<RealType1, DerivativeOrder, ARG> &arg)
|
||||
{
|
||||
auto neg = -arg;
|
||||
return neg + static_cast<RealType1>(v);
|
||||
}
|
||||
/****************************************************************************************************************/
|
||||
/* / */
|
||||
template<typename RealType, size_t DerivativeOrder, typename LHS, typename RHS>
|
||||
div_expr<RealType, DerivativeOrder, LHS, RHS> operator/(
|
||||
const expression<RealType, DerivativeOrder, LHS> &lhs,
|
||||
const expression<RealType, DerivativeOrder, RHS> &rhs)
|
||||
{
|
||||
return div_expr<RealType, DerivativeOrder, LHS, RHS>(lhs, rhs);
|
||||
}
|
||||
|
||||
template<typename RealType2,
|
||||
typename RealType1,
|
||||
size_t DerivativeOrder,
|
||||
typename ARG,
|
||||
typename = typename std::enable_if<!detail::is_expression<RealType2>::value>::type>
|
||||
const_div_by_expr<RealType1, DerivativeOrder, ARG> operator/(
|
||||
const RealType2 &v, const expression<RealType1, DerivativeOrder, ARG> &arg)
|
||||
{
|
||||
return const_div_by_expr<RealType1, DerivativeOrder, ARG>(arg, static_cast<RealType1>(v));
|
||||
}
|
||||
|
||||
template<typename RealType2,
|
||||
typename RealType1,
|
||||
size_t DerivativeOrder,
|
||||
typename ARG,
|
||||
typename = typename std::enable_if<!detail::is_expression<RealType2>::value>::type>
|
||||
div_by_const_expr<RealType1, DerivativeOrder, ARG> operator/(
|
||||
const expression<RealType1, DerivativeOrder, ARG> &arg, const RealType2 &v)
|
||||
{
|
||||
return div_by_const_expr<RealType1, DerivativeOrder, ARG>(arg, static_cast<RealType1>(v));
|
||||
}
|
||||
|
||||
} // namespace reverse_mode
|
||||
} // namespace differentiation
|
||||
} // namespace math
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,167 @@
|
||||
// Copyright Maksym Zhelyenzyakov 2025-2026.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef REVERSE_MODE_AUTODIFF_COMPARISON_OPERATOR_OVERLOADS_HPP
|
||||
#define REVERSE_MODE_AUTODIFF_COMPARISON_OPERATOR_OVERLOADS_HPP
|
||||
#include <boost/math/differentiation/detail/reverse_mode_autodiff_expression_template_base.hpp>
|
||||
namespace boost {
|
||||
namespace math {
|
||||
namespace differentiation {
|
||||
namespace reverse_mode {
|
||||
template<typename RealType, size_t DerivativeOrder1, size_t DerivativeOrder2, class LhsExpr, class RhsExpr>
|
||||
bool operator==(const expression<RealType, DerivativeOrder1, LhsExpr> &lhs,
|
||||
const expression<RealType, DerivativeOrder2, RhsExpr> &rhs)
|
||||
{
|
||||
return lhs.evaluate() == rhs.evaluate();
|
||||
}
|
||||
|
||||
template<typename RealType2,
|
||||
typename RealType1,
|
||||
size_t DerivativeOrder,
|
||||
class ArgExpr,
|
||||
typename = typename std::enable_if<!detail::is_expression<RealType2>::value>::type>
|
||||
bool operator==(const expression<RealType1, DerivativeOrder, ArgExpr> &lhs, const RealType2 &rhs)
|
||||
{
|
||||
return lhs.evaluate() == static_cast<RealType1>(rhs);
|
||||
}
|
||||
template<typename RealType2,
|
||||
typename RealType1,
|
||||
size_t DerivativeOrder,
|
||||
class ArgExpr,
|
||||
typename = typename std::enable_if<!detail::is_expression<RealType2>::value>::type>
|
||||
bool operator==(const RealType2 &lhs, const expression<RealType1, DerivativeOrder, ArgExpr> &rhs)
|
||||
{
|
||||
return lhs == rhs.evaluate();
|
||||
}
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder1, size_t DerivativeOrder2, class LhsExpr, class RhsExpr>
|
||||
bool operator!=(const expression<RealType, DerivativeOrder1, LhsExpr> &lhs,
|
||||
const expression<RealType, DerivativeOrder2, RhsExpr> &rhs)
|
||||
{
|
||||
return lhs.evaluate() != rhs.evaluate();
|
||||
}
|
||||
template<typename RealType2,
|
||||
typename RealType1,
|
||||
size_t DerivativeOrder,
|
||||
class ArgExpr,
|
||||
typename = typename std::enable_if<!detail::is_expression<RealType2>::value>::type>
|
||||
bool operator!=(const expression<RealType1, DerivativeOrder, ArgExpr> &lhs, const RealType2 &rhs)
|
||||
{
|
||||
return lhs.evaluate() != rhs;
|
||||
}
|
||||
template<typename RealType2,
|
||||
typename RealType1,
|
||||
size_t DerivativeOrder,
|
||||
class ArgExpr,
|
||||
typename = typename std::enable_if<!detail::is_expression<RealType2>::value>::type>
|
||||
bool operator!=(const RealType2 &lhs, const expression<RealType1, DerivativeOrder, ArgExpr> &rhs)
|
||||
{
|
||||
return lhs != rhs.evaluate();
|
||||
}
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder1, size_t DerivativeOrder2, class LhsExpr, class RhsExpr>
|
||||
bool operator<(const expression<RealType, DerivativeOrder1, LhsExpr> &lhs,
|
||||
const expression<RealType, DerivativeOrder2, RhsExpr> &rhs)
|
||||
{
|
||||
return lhs.evaluate() < rhs.evaluate();
|
||||
}
|
||||
template<typename RealType2,
|
||||
typename RealType1,
|
||||
size_t DerivativeOrder,
|
||||
class ArgExpr,
|
||||
typename = typename std::enable_if<!detail::is_expression<RealType2>::value>::type>
|
||||
bool operator<(const expression<RealType1, DerivativeOrder, ArgExpr> &lhs, const RealType2 &rhs)
|
||||
{
|
||||
return lhs.evaluate() < rhs;
|
||||
}
|
||||
template<typename RealType2,
|
||||
typename RealType1,
|
||||
size_t DerivativeOrder,
|
||||
class ArgExpr,
|
||||
typename = typename std::enable_if<!detail::is_expression<RealType2>::value>::type>
|
||||
bool operator<(const RealType2 &lhs, const expression<RealType1, DerivativeOrder, ArgExpr> &rhs)
|
||||
{
|
||||
return lhs < rhs.evaluate();
|
||||
}
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder1, size_t DerivativeOrder2, class LhsExpr, class RhsExpr>
|
||||
bool operator>(const expression<RealType, DerivativeOrder1, LhsExpr> &lhs,
|
||||
const expression<RealType, DerivativeOrder2, RhsExpr> &rhs)
|
||||
{
|
||||
return lhs.evaluate() > rhs.evaluate();
|
||||
}
|
||||
template<typename RealType2,
|
||||
typename RealType1,
|
||||
size_t DerivativeOrder,
|
||||
class ArgExpr,
|
||||
typename = typename std::enable_if<!detail::is_expression<RealType2>::value>::type>
|
||||
bool operator>(const expression<RealType1, DerivativeOrder, ArgExpr> &lhs, const RealType2 &rhs)
|
||||
{
|
||||
return lhs.evaluate() > rhs;
|
||||
}
|
||||
template<typename RealType2,
|
||||
typename RealType1,
|
||||
size_t DerivativeOrder,
|
||||
class ArgExpr,
|
||||
typename = typename std::enable_if<!detail::is_expression<RealType2>::value>::type>
|
||||
bool operator>(const RealType2 &lhs, const expression<RealType1, DerivativeOrder, ArgExpr> &rhs)
|
||||
{
|
||||
return lhs > rhs.evaluate();
|
||||
}
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder1, size_t DerivativeOrder2, class LhsExpr, class RhsExpr>
|
||||
bool operator<=(const expression<RealType, DerivativeOrder1, LhsExpr> &lhs,
|
||||
const expression<RealType, DerivativeOrder2, RhsExpr> &rhs)
|
||||
{
|
||||
return lhs.evaluate() <= rhs.evaluate();
|
||||
}
|
||||
template<typename RealType2,
|
||||
typename RealType1,
|
||||
size_t DerivativeOrder,
|
||||
class ArgExpr,
|
||||
typename = typename std::enable_if<!detail::is_expression<RealType2>::value>::type>
|
||||
bool operator<=(const expression<RealType1, DerivativeOrder, ArgExpr> &lhs, const RealType2 &rhs)
|
||||
{
|
||||
return lhs.evaluate() <= rhs;
|
||||
}
|
||||
template<typename RealType2,
|
||||
typename RealType1,
|
||||
size_t DerivativeOrder,
|
||||
class ArgExpr,
|
||||
typename = typename std::enable_if<!detail::is_expression<RealType2>::value>::type>
|
||||
bool operator<=(const RealType2 &lhs, const expression<RealType1, DerivativeOrder, ArgExpr> &rhs)
|
||||
{
|
||||
return lhs <= rhs.evaluate();
|
||||
}
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder1, size_t DerivativeOrder2, class LhsExpr, class RhsExpr>
|
||||
bool operator>=(const expression<RealType, DerivativeOrder1, LhsExpr> &lhs,
|
||||
const expression<RealType, DerivativeOrder2, RhsExpr> &rhs)
|
||||
{
|
||||
return lhs.evaluate() >= rhs.evaluate();
|
||||
}
|
||||
template<typename RealType2,
|
||||
typename RealType1,
|
||||
size_t DerivativeOrder,
|
||||
class ArgExpr,
|
||||
typename = typename std::enable_if<!detail::is_expression<RealType2>::value>::type>
|
||||
bool operator>=(const expression<RealType1, DerivativeOrder, ArgExpr> &lhs, const RealType2 &rhs)
|
||||
{
|
||||
return lhs.evaluate() >= rhs;
|
||||
}
|
||||
template<typename RealType2,
|
||||
typename RealType1,
|
||||
size_t DerivativeOrder,
|
||||
class ArgExpr,
|
||||
typename = typename std::enable_if<!detail::is_expression<RealType2>::value>::type>
|
||||
bool operator>=(const RealType2 &lhs, const expression<RealType1, DerivativeOrder, ArgExpr> &rhs)
|
||||
{
|
||||
return lhs >= rhs.evaluate();
|
||||
}
|
||||
} // namespace reverse_mode
|
||||
} // namespace differentiation
|
||||
} // namespace math
|
||||
} // namespace boost
|
||||
#endif // REVERSE_MODE_AUTODIFF_COMPARISON_OPERATOR_OVERLOADS_HPP
|
||||
@@ -0,0 +1,228 @@
|
||||
// Copyright Maksym Zhelyenzyakov 2025-2026.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef REVERSE_MODE_AUTODIFF_ERF_OVERLOADS_HPP
|
||||
#define REVERSE_MODE_AUTODIFF_ERF_OVERLOADS_HPP
|
||||
|
||||
#include <boost/math/constants/constants.hpp>
|
||||
#include <boost/math/differentiation/detail/reverse_mode_autodiff_expression_template_base.hpp>
|
||||
#include <boost/math/differentiation/detail/reverse_mode_autodiff_stl_overloads.hpp>
|
||||
#include <boost/math/differentiation/detail/reverse_mode_autodiff_utilities.hpp>
|
||||
#include <boost/math/special_functions/erf.hpp>
|
||||
#include <boost/multiprecision/cpp_bin_float.hpp>
|
||||
namespace boost {
|
||||
namespace math {
|
||||
namespace differentiation {
|
||||
namespace reverse_mode {
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder, typename ARG>
|
||||
struct erf_expr;
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder, typename ARG>
|
||||
struct erfc_expr;
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder, typename ARG>
|
||||
struct erf_inv_expr;
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder, typename ARG>
|
||||
struct erfc_inv_expr;
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder, typename ARG>
|
||||
erf_expr<RealType, DerivativeOrder, ARG> erf(const expression<RealType, DerivativeOrder, ARG> &arg)
|
||||
{
|
||||
return erf_expr<RealType, DerivativeOrder, ARG>(arg, 0.0);
|
||||
}
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder, typename ARG>
|
||||
erfc_expr<RealType, DerivativeOrder, ARG> erfc(const expression<RealType, DerivativeOrder, ARG> &arg)
|
||||
{
|
||||
return erfc_expr<RealType, DerivativeOrder, ARG>(arg, 0.0);
|
||||
}
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder, typename ARG>
|
||||
erf_inv_expr<RealType, DerivativeOrder, ARG> erf_inv(
|
||||
const expression<RealType, DerivativeOrder, ARG> &arg)
|
||||
{
|
||||
return erf_inv_expr<RealType, DerivativeOrder, ARG>(arg, 0.0);
|
||||
}
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder, typename ARG>
|
||||
erfc_inv_expr<RealType, DerivativeOrder, ARG> erfc_inv(
|
||||
const expression<RealType, DerivativeOrder, ARG> &arg)
|
||||
{
|
||||
return erfc_inv_expr<RealType, DerivativeOrder, ARG>(arg, 0.0);
|
||||
}
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder, typename ARG>
|
||||
struct erf_expr : public abstract_unary_expression<RealType,
|
||||
DerivativeOrder,
|
||||
ARG,
|
||||
erf_expr<RealType, DerivativeOrder, ARG>>
|
||||
{
|
||||
/** @brief erf(x)
|
||||
*
|
||||
* d/dx erf(x) = 2*exp(x^2)/sqrt(pi)
|
||||
*
|
||||
* */
|
||||
using inner_t = rvar_t<RealType, DerivativeOrder - 1>;
|
||||
|
||||
explicit erf_expr(const expression<RealType, DerivativeOrder, ARG> &arg_expr, const RealType v)
|
||||
: abstract_unary_expression<RealType,
|
||||
DerivativeOrder,
|
||||
ARG,
|
||||
erf_expr<RealType, DerivativeOrder, ARG>>(arg_expr, v){};
|
||||
|
||||
inner_t evaluate() const
|
||||
{
|
||||
return detail::if_functional_dispatch<(DerivativeOrder > 1)>(
|
||||
[this](auto &&x) { return reverse_mode::erf(std::forward<decltype(x)>(x)); },
|
||||
[this](auto &&x) { return boost::math::erf(std::forward<decltype(x)>(x)); },
|
||||
this->arg.evaluate());
|
||||
}
|
||||
static const inner_t derivative(const inner_t &argv,
|
||||
const inner_t & /*v*/,
|
||||
const RealType & /*constant*/)
|
||||
{
|
||||
BOOST_MATH_STD_USING
|
||||
return static_cast<RealType>(2.0) * exp(-argv * argv) / sqrt(constants::pi<RealType>());
|
||||
}
|
||||
};
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder, typename ARG>
|
||||
struct erfc_expr : public abstract_unary_expression<RealType,
|
||||
DerivativeOrder,
|
||||
ARG,
|
||||
erfc_expr<RealType, DerivativeOrder, ARG>>
|
||||
{
|
||||
/** @brief erfc(x)
|
||||
*
|
||||
* d/dx erf(x) = -2*exp(x^2)/sqrt(pi)
|
||||
*
|
||||
* */
|
||||
|
||||
using inner_t = rvar_t<RealType, DerivativeOrder - 1>;
|
||||
|
||||
explicit erfc_expr(const expression<RealType, DerivativeOrder, ARG> &arg_expr, const RealType v)
|
||||
: abstract_unary_expression<RealType,
|
||||
DerivativeOrder,
|
||||
ARG,
|
||||
erfc_expr<RealType, DerivativeOrder, ARG>>(arg_expr, v){};
|
||||
|
||||
inner_t evaluate() const
|
||||
{
|
||||
return detail::if_functional_dispatch<((DerivativeOrder > 1))>(
|
||||
[this](auto &&x) { return reverse_mode::erfc(std::forward<decltype(x)>(x)); },
|
||||
[this](auto &&x) { return boost::math::erfc(std::forward<decltype(x)>(x)); },
|
||||
this->arg.evaluate());
|
||||
}
|
||||
static const inner_t derivative(const inner_t &argv,
|
||||
const inner_t & /*v*/,
|
||||
const RealType & /*constant*/)
|
||||
{
|
||||
BOOST_MATH_STD_USING
|
||||
return static_cast<RealType>(-2.0) * exp(-argv * argv) / sqrt(constants::pi<RealType>());
|
||||
}
|
||||
};
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder, typename ARG>
|
||||
struct erf_inv_expr : public abstract_unary_expression<RealType,
|
||||
DerivativeOrder,
|
||||
ARG,
|
||||
erf_inv_expr<RealType, DerivativeOrder, ARG>>
|
||||
{
|
||||
/** @brief erf(x)
|
||||
*
|
||||
* d/dx erf(x) = 2*exp(x^2)/sqrt(pi)
|
||||
*
|
||||
* */
|
||||
|
||||
using inner_t = rvar_t<RealType, DerivativeOrder - 1>;
|
||||
|
||||
explicit erf_inv_expr(const expression<RealType, DerivativeOrder, ARG> &arg_expr,
|
||||
const RealType v)
|
||||
: abstract_unary_expression<RealType,
|
||||
DerivativeOrder,
|
||||
ARG,
|
||||
erf_inv_expr<RealType, DerivativeOrder, ARG>>(arg_expr, v){};
|
||||
|
||||
inner_t evaluate() const
|
||||
{
|
||||
return detail::if_functional_dispatch<((DerivativeOrder > 1))>(
|
||||
[this](auto &&x) { return reverse_mode::erf_inv(std::forward<decltype(x)>(x)); },
|
||||
[this](auto &&x) { return boost::math::erf_inv(std::forward<decltype(x)>(x)); },
|
||||
this->arg.evaluate());
|
||||
}
|
||||
static const inner_t derivative(const inner_t &argv,
|
||||
const inner_t & /*v*/,
|
||||
const RealType & /*constant*/)
|
||||
{
|
||||
BOOST_MATH_STD_USING
|
||||
return detail::if_functional_dispatch<((DerivativeOrder > 1))>(
|
||||
[](auto &&x) {
|
||||
return static_cast<RealType>(0.5) * sqrt(constants::pi<RealType>())
|
||||
* reverse_mode::exp(
|
||||
reverse_mode::pow(reverse_mode::erf_inv(x), static_cast<RealType>(2.0)));
|
||||
},
|
||||
[](auto &&x) {
|
||||
return static_cast<RealType>(0.5) * sqrt(constants::pi<RealType>())
|
||||
* exp(pow(boost::math::erf_inv(x), static_cast<RealType>(2.0)));
|
||||
},
|
||||
argv);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder, typename ARG>
|
||||
struct erfc_inv_expr
|
||||
: public abstract_unary_expression<RealType,
|
||||
DerivativeOrder,
|
||||
ARG,
|
||||
erfc_inv_expr<RealType, DerivativeOrder, ARG>>
|
||||
{
|
||||
/** @brief erfc(x)
|
||||
*
|
||||
* d/dx erf(x) = -2*exp(x^2)/sqrt(pi)
|
||||
*
|
||||
* */
|
||||
|
||||
using inner_t = rvar_t<RealType, DerivativeOrder - 1>;
|
||||
|
||||
explicit erfc_inv_expr(const expression<RealType, DerivativeOrder, ARG> &arg_expr,
|
||||
const RealType v)
|
||||
: abstract_unary_expression<RealType,
|
||||
DerivativeOrder,
|
||||
ARG,
|
||||
erfc_inv_expr<RealType, DerivativeOrder, ARG>>(arg_expr, v){};
|
||||
|
||||
inner_t evaluate() const
|
||||
{
|
||||
return detail::if_functional_dispatch<((DerivativeOrder > 1))>(
|
||||
[this](auto &&x) { return reverse_mode::erfc_inv(std::forward<decltype(x)>(x)); },
|
||||
[this](auto &&x) { return boost::math::erfc_inv(std::forward<decltype(x)>(x)); },
|
||||
this->arg.evaluate());
|
||||
}
|
||||
static const inner_t derivative(const inner_t &argv,
|
||||
const inner_t & /*v*/,
|
||||
const RealType & /*constant*/)
|
||||
{
|
||||
BOOST_MATH_STD_USING
|
||||
return detail::if_functional_dispatch<((DerivativeOrder > 1))>(
|
||||
[](auto &&x) {
|
||||
return static_cast<RealType>(-0.5) * sqrt(constants::pi<RealType>())
|
||||
* reverse_mode::exp(reverse_mode::pow(reverse_mode::erfc_inv(x),
|
||||
static_cast<RealType>(2.0)));
|
||||
},
|
||||
[](auto &&x) {
|
||||
return static_cast<RealType>(-0.5) * sqrt(constants::pi<RealType>())
|
||||
* exp(pow(boost::math::erfc_inv(x), static_cast<RealType>(2.0)));
|
||||
},
|
||||
argv);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace reverse_mode
|
||||
} // namespace differentiation
|
||||
} // namespace math
|
||||
} // namespace boost
|
||||
|
||||
#endif // REVERSE_MODE_AUTODIFF_ERF_OVERLOADS_HPP
|
||||
@@ -0,0 +1,258 @@
|
||||
// Copyright Maksym Zhelyenzyakov 2025-2026.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef REVERSE_MODE_AUTODIFF_EXPRESSION_TEMPLATE_BASE_HPP
|
||||
#define REVERSE_MODE_AUTODIFF_EXPRESSION_TEMPLATE_BASE_HPP
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
namespace boost {
|
||||
namespace math {
|
||||
namespace differentiation {
|
||||
namespace reverse_mode {
|
||||
|
||||
/* forward declarations for utitlity functions */
|
||||
struct expression_base
|
||||
{};
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder, class DerivedExpression>
|
||||
struct expression;
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder>
|
||||
class rvar;
|
||||
|
||||
template<typename RealType,
|
||||
size_t DerivativeOrder,
|
||||
typename LHS,
|
||||
typename RHS,
|
||||
typename ConcreteBinaryOperation>
|
||||
struct abstract_binary_expression;
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder, typename ARG, typename ConcreteUnaryOperation>
|
||||
|
||||
struct abstract_unary_expression;
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder>
|
||||
class gradient_node; // forward declaration for tape
|
||||
|
||||
namespace detail {
|
||||
template<typename...>
|
||||
using void_t = void;
|
||||
// Check if T has a 'value_type' alias
|
||||
template<typename T, typename Enable = void>
|
||||
struct has_value_type : std::false_type
|
||||
{};
|
||||
template<typename T>
|
||||
struct has_value_type<T, void_t<typename T::value_type>> : std::true_type
|
||||
{};
|
||||
template<typename T, typename Enable = void>
|
||||
struct has_binary_sub_types : std::false_type
|
||||
{};
|
||||
template<typename T>
|
||||
struct has_binary_sub_types<T, void_t<typename T::lhs_type, typename T::rhs_type>> : std::true_type
|
||||
{};
|
||||
template<typename T, typename Enable = void>
|
||||
struct has_unary_sub_type : std::false_type
|
||||
{};
|
||||
template<typename T>
|
||||
struct has_unary_sub_type<T, void_t<typename T::arg_type>> : std::true_type
|
||||
{};
|
||||
|
||||
template<typename T, size_t order, typename Enable = void>
|
||||
struct count_rvar_impl
|
||||
{
|
||||
static constexpr std::size_t value = 0;
|
||||
};
|
||||
template<typename RealType, size_t DerivativeOrder>
|
||||
struct count_rvar_impl<rvar<RealType, DerivativeOrder>, DerivativeOrder>
|
||||
{
|
||||
static constexpr std::size_t value = 1;
|
||||
};
|
||||
|
||||
template<typename RealType, std::size_t DerivativeOrder>
|
||||
struct count_rvar_impl<
|
||||
RealType,
|
||||
DerivativeOrder,
|
||||
std::enable_if_t<has_binary_sub_types<RealType>::value
|
||||
&& !std::is_same<RealType, rvar<typename RealType::value_type, DerivativeOrder>>::value
|
||||
&& !has_unary_sub_type<RealType>::value>>
|
||||
{
|
||||
static constexpr std::size_t value
|
||||
= count_rvar_impl<typename RealType::lhs_type, DerivativeOrder>::value
|
||||
+ count_rvar_impl<typename RealType::rhs_type, DerivativeOrder>::value;
|
||||
};
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder>
|
||||
struct count_rvar_impl<
|
||||
RealType,
|
||||
DerivativeOrder,
|
||||
typename std::enable_if_t<
|
||||
has_unary_sub_type<RealType>::value
|
||||
&& !std::is_same<RealType, rvar<typename RealType::value_type, DerivativeOrder>>::value
|
||||
&& !has_binary_sub_types<RealType>::value>>
|
||||
{
|
||||
static constexpr std::size_t value
|
||||
= count_rvar_impl<typename RealType::arg_type, DerivativeOrder>::value;
|
||||
};
|
||||
template<typename RealType, size_t DerivativeOrder>
|
||||
constexpr std::size_t count_rvars = detail::count_rvar_impl<RealType, DerivativeOrder>::value;
|
||||
|
||||
template<typename T>
|
||||
struct is_expression : std::is_base_of<expression_base, typename std::decay<T>::type>
|
||||
{};
|
||||
|
||||
template<typename RealType, size_t N>
|
||||
struct rvar_type_impl
|
||||
{
|
||||
using type = rvar<RealType, N>;
|
||||
};
|
||||
|
||||
template<typename RealType>
|
||||
struct rvar_type_impl<RealType, 0>
|
||||
{
|
||||
using type = RealType;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<typename T, size_t N>
|
||||
using rvar_t = typename detail::rvar_type_impl<T, N>::type;
|
||||
|
||||
template<typename RealType, size_t DerivativeOrder, class DerivedExpression>
|
||||
struct expression : expression_base
|
||||
{
|
||||
/* @brief
|
||||
* base expression class
|
||||
* */
|
||||
|
||||
using value_type = RealType;
|
||||
static constexpr size_t order_v = DerivativeOrder;
|
||||
using derived_type = DerivedExpression;
|
||||
|
||||
static constexpr size_t num_literals = 0;
|
||||
using inner_t = rvar_t<RealType, DerivativeOrder - 1>;
|
||||
inner_t evaluate() const { return static_cast<const DerivedExpression *>(this)->evaluate(); }
|
||||
|
||||
template<size_t arg_index>
|
||||
void propagatex(gradient_node<RealType, DerivativeOrder> *node, inner_t adj) const
|
||||
{
|
||||
return static_cast<const DerivedExpression *>(this)->template propagatex<arg_index>(node,
|
||||
adj);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename RealType,
|
||||
size_t DerivativeOrder,
|
||||
typename LHS,
|
||||
typename RHS,
|
||||
typename ConcreteBinaryOperation>
|
||||
struct abstract_binary_expression
|
||||
: public expression<
|
||||
RealType,
|
||||
DerivativeOrder,
|
||||
abstract_binary_expression<RealType, DerivativeOrder, LHS, RHS, ConcreteBinaryOperation>>
|
||||
{
|
||||
using lhs_type = LHS;
|
||||
using rhs_type = RHS;
|
||||
using value_type = RealType;
|
||||
using inner_t = rvar_t<RealType, DerivativeOrder - 1>;
|
||||
const lhs_type lhs;
|
||||
const rhs_type rhs;
|
||||
|
||||
explicit abstract_binary_expression(
|
||||
const expression<RealType, DerivativeOrder, LHS> &left_hand_expr,
|
||||
const expression<RealType, DerivativeOrder, RHS> &right_hand_expr)
|
||||
: lhs(static_cast<const LHS &>(left_hand_expr))
|
||||
, rhs(static_cast<const RHS &>(right_hand_expr)){};
|
||||
|
||||
inner_t evaluate() const
|
||||
{
|
||||
return static_cast<const ConcreteBinaryOperation *>(this)->evaluate();
|
||||
};
|
||||
|
||||
template<size_t arg_index>
|
||||
void propagatex(gradient_node<RealType, DerivativeOrder> *node, inner_t adj) const
|
||||
{
|
||||
const inner_t lv = lhs.evaluate();
|
||||
const inner_t rv = rhs.evaluate();
|
||||
const inner_t v = evaluate();
|
||||
const inner_t partial_l = ConcreteBinaryOperation::left_derivative(lv, rv, v);
|
||||
const inner_t partial_r = ConcreteBinaryOperation::right_derivative(lv, rv, v);
|
||||
|
||||
constexpr size_t num_lhs_args = detail::count_rvars<LHS, DerivativeOrder>;
|
||||
constexpr size_t num_rhs_args = detail::count_rvars<RHS, DerivativeOrder>;
|
||||
|
||||
propagate_lhs<num_lhs_args, arg_index>(node, adj * partial_l);
|
||||
propagate_rhs<num_rhs_args, arg_index + num_lhs_args>(node, adj * partial_r);
|
||||
}
|
||||
|
||||
private:
|
||||
/* everything here just emulates c++17 if constexpr */
|
||||
|
||||
template<std::size_t num_args,
|
||||
std::size_t arg_index_,
|
||||
typename std::enable_if<(num_args > 0), int>::type = 0>
|
||||
void propagate_lhs(gradient_node<RealType, DerivativeOrder> *node, inner_t adj) const
|
||||
{
|
||||
lhs.template propagatex<arg_index_>(node, adj);
|
||||
}
|
||||
|
||||
template<std::size_t num_args,
|
||||
std::size_t arg_index_,
|
||||
typename std::enable_if<(num_args == 0), int>::type = 0>
|
||||
void propagate_lhs(gradient_node<RealType, DerivativeOrder> *, inner_t) const
|
||||
{}
|
||||
|
||||
template<std::size_t num_args,
|
||||
std::size_t arg_index_,
|
||||
typename std::enable_if<(num_args > 0), int>::type = 0>
|
||||
void propagate_rhs(gradient_node<RealType, DerivativeOrder> *node, inner_t adj) const
|
||||
{
|
||||
rhs.template propagatex<arg_index_>(node, adj);
|
||||
}
|
||||
|
||||
template<std::size_t num_args,
|
||||
std::size_t arg_index_,
|
||||
typename std::enable_if<(num_args == 0), int>::type = 0>
|
||||
void propagate_rhs(gradient_node<RealType, DerivativeOrder> *, inner_t) const
|
||||
{}
|
||||
};
|
||||
template<typename RealType, size_t DerivativeOrder, typename ARG, typename ConcreteUnaryOperation>
|
||||
|
||||
struct abstract_unary_expression
|
||||
: public expression<
|
||||
RealType,
|
||||
DerivativeOrder,
|
||||
abstract_unary_expression<RealType, DerivativeOrder, ARG, ConcreteUnaryOperation>>
|
||||
{
|
||||
using arg_type = ARG;
|
||||
using value_type = RealType;
|
||||
using inner_t = rvar_t<RealType, DerivativeOrder - 1>;
|
||||
const arg_type arg;
|
||||
const RealType constant;
|
||||
explicit abstract_unary_expression(const expression<RealType, DerivativeOrder, ARG> &arg_expr,
|
||||
const RealType &constant)
|
||||
: arg(static_cast<const ARG &>(arg_expr))
|
||||
, constant(constant){};
|
||||
inner_t evaluate() const
|
||||
{
|
||||
return static_cast<const ConcreteUnaryOperation *>(this)->evaluate();
|
||||
};
|
||||
|
||||
template<size_t arg_index>
|
||||
void propagatex(gradient_node<RealType, DerivativeOrder> *node, inner_t adj) const
|
||||
{
|
||||
inner_t argv = arg.evaluate();
|
||||
inner_t v = evaluate();
|
||||
inner_t partial_arg = ConcreteUnaryOperation::derivative(argv, v, constant);
|
||||
|
||||
arg.template propagatex<arg_index>(node, adj * partial_arg);
|
||||
}
|
||||
};
|
||||
} // namespace reverse_mode
|
||||
} // namespace differentiation
|
||||
} // namespace math
|
||||
} // namespace boost
|
||||
|
||||
#endif // REVERSE_MODE_AUTODIFF_EXPRESSION_TEMPLATE_BASE_HPP
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright Maksym Zhelyenzyakov 2025-2026.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef BOOST_REVERSE_MODE_AUTODIFF_HELPER_FUNCTIONS_HPP
|
||||
#define BOOST_REVERSE_MODE_AUTODIFF_HELPER_FUNCTIONS_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
namespace boost {
|
||||
namespace math {
|
||||
namespace differentiation {
|
||||
namespace reverse_mode {} // namespace reverse_mode
|
||||
} // namespace differentiation
|
||||
} // namespace math
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,434 @@
|
||||
// Copyright Maksym Zhelyenzyakov 2025-2026.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef REVERSE_MODE_AUTODIFF_MEMORY_MANAGEMENT_HPP
|
||||
#define REVERSE_MODE_AUTODIFF_MEMORY_MANAGEMENT_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <boost/math/tools/assert.hpp>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
namespace boost {
|
||||
namespace math {
|
||||
namespace differentiation {
|
||||
namespace reverse_mode {
|
||||
namespace detail {
|
||||
template<typename allocator_type, size_t buffer_size>
|
||||
class flat_linear_allocator_iterator
|
||||
{
|
||||
/**
|
||||
* @brief enables iterating over linear allocator with
|
||||
* c++ iterators
|
||||
*/
|
||||
public:
|
||||
using raw_allocator_type = std::remove_const_t<allocator_type>;
|
||||
using value_type = typename allocator_type::value_type;
|
||||
using pointer = typename allocator_type::value_type *;
|
||||
using const_ptr_type = const value_type *;
|
||||
using reference = typename allocator_type::value_type &;
|
||||
using const_reference_type = const value_type &;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
using difference_type = ptrdiff_t;
|
||||
|
||||
private:
|
||||
const allocator_type *storage_ = nullptr;
|
||||
size_t index_ = 0;
|
||||
size_t begin_ = 0;
|
||||
size_t end_ = 0;
|
||||
|
||||
public:
|
||||
flat_linear_allocator_iterator() = default;
|
||||
|
||||
explicit flat_linear_allocator_iterator(allocator_type *storage, size_t index)
|
||||
: storage_(storage)
|
||||
, index_(index)
|
||||
, begin_(0)
|
||||
, end_(storage->size())
|
||||
{}
|
||||
|
||||
explicit flat_linear_allocator_iterator(allocator_type *storage,
|
||||
size_t index,
|
||||
size_t begin,
|
||||
size_t end)
|
||||
: storage_(storage)
|
||||
, index_(index)
|
||||
, begin_(begin)
|
||||
, end_(end)
|
||||
{}
|
||||
|
||||
explicit flat_linear_allocator_iterator(const allocator_type *storage, size_t index)
|
||||
: storage_(storage)
|
||||
, index_(index)
|
||||
, begin_(0)
|
||||
, end_(storage->size())
|
||||
{}
|
||||
|
||||
explicit flat_linear_allocator_iterator(const allocator_type *storage,
|
||||
size_t index,
|
||||
size_t begin,
|
||||
size_t end)
|
||||
: storage_(storage)
|
||||
, index_(index)
|
||||
, begin_(begin)
|
||||
, end_(end)
|
||||
{}
|
||||
reference operator*()
|
||||
{
|
||||
BOOST_MATH_ASSERT(index_ >= begin_ && index_ < end_);
|
||||
return (*storage_->data_[index_ / buffer_size])[index_ % buffer_size];
|
||||
}
|
||||
|
||||
const_reference_type operator*() const
|
||||
{
|
||||
BOOST_MATH_ASSERT(index_ >= begin_ && index_ < end_);
|
||||
return (*storage_->data_[index_ / buffer_size])[index_ % buffer_size];
|
||||
}
|
||||
|
||||
pointer operator->()
|
||||
{
|
||||
BOOST_MATH_ASSERT(index_ >= begin_ && index_ < end_);
|
||||
return &operator*();
|
||||
}
|
||||
|
||||
const_ptr_type operator->() const
|
||||
{
|
||||
BOOST_MATH_ASSERT(index_ >= begin_ && index_ < end_);
|
||||
return &operator*();
|
||||
}
|
||||
flat_linear_allocator_iterator &operator++()
|
||||
{
|
||||
++index_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
flat_linear_allocator_iterator operator++(int)
|
||||
{
|
||||
auto tmp = *this;
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
flat_linear_allocator_iterator &operator--()
|
||||
{
|
||||
--index_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
flat_linear_allocator_iterator operator--(int)
|
||||
{
|
||||
auto tmp = *this;
|
||||
--(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator==(const flat_linear_allocator_iterator &other) const
|
||||
{
|
||||
return index_ == other.index_ && storage_ == other.storage_;
|
||||
}
|
||||
|
||||
bool operator!=(const flat_linear_allocator_iterator &other) const { return !(*this == other); }
|
||||
|
||||
flat_linear_allocator_iterator operator+(difference_type n) const
|
||||
{
|
||||
return flat_linear_allocator_iterator(storage_, index_ + static_cast<size_t>(n), begin_, end_);
|
||||
}
|
||||
|
||||
flat_linear_allocator_iterator &operator+=(difference_type n)
|
||||
{
|
||||
index_ += n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
flat_linear_allocator_iterator operator-(difference_type n) const
|
||||
{
|
||||
return flat_linear_allocator_iterator(storage_, index_ - n, begin_, end_);
|
||||
}
|
||||
flat_linear_allocator_iterator &operator-=(difference_type n)
|
||||
{
|
||||
index_ -= n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
difference_type operator-(const flat_linear_allocator_iterator &other) const
|
||||
{
|
||||
return static_cast<difference_type>(index_) - static_cast<difference_type>(other.index_);
|
||||
}
|
||||
|
||||
reference operator[](difference_type n) { return *(*this + n); }
|
||||
|
||||
const_reference_type operator[](difference_type n) const { return *(*this + n); }
|
||||
|
||||
bool operator<(const flat_linear_allocator_iterator &other) const
|
||||
{
|
||||
return index_ < other.index_;
|
||||
}
|
||||
|
||||
bool operator>(const flat_linear_allocator_iterator &other) const
|
||||
{
|
||||
return index_ > other.index_;
|
||||
}
|
||||
|
||||
bool operator<=(const flat_linear_allocator_iterator &other) const
|
||||
{
|
||||
return index_ <= other.index_;
|
||||
}
|
||||
|
||||
bool operator>=(const flat_linear_allocator_iterator &other) const
|
||||
{
|
||||
return index_ >= other.index_;
|
||||
}
|
||||
|
||||
bool operator!() const noexcept { return storage_ == nullptr; }
|
||||
};
|
||||
/* memory management helps for tape */
|
||||
template<typename RealType, size_t buffer_size>
|
||||
class flat_linear_allocator
|
||||
{
|
||||
/** @brief basically a vector<array<T*, size>>
|
||||
* intended to work like a vector that allocates memory in chunks
|
||||
* and doesn't invalidate references
|
||||
* */
|
||||
public:
|
||||
// store vector of unique pointers to arrays
|
||||
// to avoid vector reference invalidation
|
||||
using buffer_type = std::array<RealType, buffer_size>;
|
||||
using buffer_ptr = std::unique_ptr<std::array<RealType, buffer_size>>;
|
||||
|
||||
private:
|
||||
std::vector<buffer_ptr> data_;
|
||||
size_t total_size_ = 0;
|
||||
std::vector<size_t> checkpoints_; //{0};
|
||||
|
||||
public:
|
||||
friend class flat_linear_allocator_iterator<flat_linear_allocator<RealType, buffer_size>,
|
||||
buffer_size>;
|
||||
friend class flat_linear_allocator_iterator<const flat_linear_allocator<RealType, buffer_size>,
|
||||
buffer_size>;
|
||||
using value_type = RealType;
|
||||
using iterator
|
||||
= flat_linear_allocator_iterator<flat_linear_allocator<RealType, buffer_size>, buffer_size>;
|
||||
using const_iterator
|
||||
= flat_linear_allocator_iterator<const flat_linear_allocator<RealType, buffer_size>,
|
||||
buffer_size>;
|
||||
|
||||
size_t buffer_id() const noexcept { return total_size_ / buffer_size; }
|
||||
size_t item_id() const noexcept { return total_size_ % buffer_size; }
|
||||
|
||||
private:
|
||||
void allocate_buffer()
|
||||
{
|
||||
data_.emplace_back(std::make_unique<buffer_type>());
|
||||
}
|
||||
|
||||
public:
|
||||
flat_linear_allocator() { allocate_buffer(); }
|
||||
flat_linear_allocator(const flat_linear_allocator &) = delete;
|
||||
flat_linear_allocator &operator=(const flat_linear_allocator &) = delete;
|
||||
flat_linear_allocator(flat_linear_allocator &&) = delete;
|
||||
flat_linear_allocator &operator=(flat_linear_allocator &&) = delete;
|
||||
~flat_linear_allocator()
|
||||
{
|
||||
destroy_all();
|
||||
data_.clear();
|
||||
}
|
||||
|
||||
void destroy_all()
|
||||
{
|
||||
for (size_t i = 0; i < total_size_; ++i) {
|
||||
size_t bid = i / buffer_size;
|
||||
size_t iid = i % buffer_size;
|
||||
(*data_[bid])[iid].~RealType();
|
||||
}
|
||||
}
|
||||
/** @brief
|
||||
* helper functions to clear tape and create block in tape
|
||||
*/
|
||||
void clear()
|
||||
{
|
||||
data_.clear();
|
||||
total_size_ = 0;
|
||||
checkpoints_.clear();
|
||||
allocate_buffer();
|
||||
}
|
||||
|
||||
// doesn't delete anything, only sets the current index to zero
|
||||
void reset() { total_size_ = 0; }
|
||||
void rewind() { total_size_ = 0; };
|
||||
|
||||
// adds current index as a checkpoint to be able to walk back to
|
||||
void add_checkpoint()
|
||||
{
|
||||
if (total_size_ > 0) {
|
||||
checkpoints_.push_back(total_size_ - 1);
|
||||
} else {
|
||||
checkpoints_.push_back(0);
|
||||
}
|
||||
};
|
||||
|
||||
/** @brief clears all checkpoints
|
||||
* */
|
||||
void reset_checkpoints() { checkpoints_.clear(); }
|
||||
|
||||
void rewind_to_last_checkpoint() { total_size_ = checkpoints_.back(); }
|
||||
void rewind_to_checkpoint_at(size_t index) { total_size_ = checkpoints_[index]; }
|
||||
|
||||
void fill(const RealType &val)
|
||||
{
|
||||
for (size_t i = 0; i < total_size_; ++i) {
|
||||
size_t bid = i / buffer_size;
|
||||
size_t iid = i % buffer_size;
|
||||
(*data_[bid])[iid] = val;
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief emplaces back object at the end of the
|
||||
* data structure, calls default constructor */
|
||||
iterator emplace_back()
|
||||
{
|
||||
if (item_id() == 0 && total_size_ != 0) {
|
||||
allocate_buffer();
|
||||
}
|
||||
size_t bid = buffer_id();
|
||||
size_t iid = item_id();
|
||||
|
||||
RealType *ptr = &(*data_[bid])[iid];
|
||||
new (ptr) RealType();
|
||||
++total_size_;
|
||||
return iterator(this, total_size_ - 1);
|
||||
};
|
||||
|
||||
/** @brief, emplaces back object at end of data structure,
|
||||
* passes arguments to constructor */
|
||||
template<typename... Args>
|
||||
iterator emplace_back(Args &&...args)
|
||||
{
|
||||
if (item_id() == 0 && total_size_ != 0) {
|
||||
allocate_buffer();
|
||||
}
|
||||
BOOST_MATH_ASSERT(buffer_id() < data_.size());
|
||||
BOOST_MATH_ASSERT(item_id() < buffer_size);
|
||||
RealType *ptr = &(*data_[buffer_id()])[item_id()];
|
||||
new (ptr) RealType(std::forward<Args>(args)...);
|
||||
++total_size_;
|
||||
return iterator(this, total_size_ - 1);
|
||||
}
|
||||
/** @brief default constructs n objects at end of
|
||||
* data structure, n known at compile time */
|
||||
template<size_t n>
|
||||
iterator emplace_back_n()
|
||||
{
|
||||
size_t bid = buffer_id();
|
||||
size_t iid = item_id();
|
||||
if (iid + n < buffer_size) {
|
||||
RealType *ptr = &(*data_[bid])[iid];
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
new (ptr + i) RealType();
|
||||
}
|
||||
total_size_ += n;
|
||||
return iterator(this, total_size_ - n, total_size_ - n, total_size_);
|
||||
} else {
|
||||
size_t allocs_in_curr_buffer = buffer_size - iid;
|
||||
size_t allocs_in_next_buffer = n - (buffer_size - iid);
|
||||
RealType *ptr = &(*data_[bid])[iid];
|
||||
for (size_t i = 0; i < allocs_in_curr_buffer; ++i) {
|
||||
new (ptr + i) RealType();
|
||||
}
|
||||
allocate_buffer();
|
||||
bid = data_.size() - 1;
|
||||
iid = 0;
|
||||
total_size_ += n;
|
||||
|
||||
RealType *ptr2 = &(*data_[bid])[iid];
|
||||
for (size_t i = 0; i < allocs_in_next_buffer; i++) {
|
||||
new (ptr2 + i) RealType();
|
||||
}
|
||||
return iterator(this, total_size_ - n, total_size_ - n, total_size_);
|
||||
}
|
||||
}
|
||||
/** @brief default constructs n objects at end of
|
||||
* data structure, n known at run time
|
||||
*/
|
||||
iterator emplace_back_n(size_t n)
|
||||
{
|
||||
size_t bid = buffer_id();
|
||||
size_t iid = item_id();
|
||||
if (iid + n < buffer_size) {
|
||||
RealType *ptr = &(*data_[bid])[iid];
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
new (ptr + i) RealType();
|
||||
}
|
||||
total_size_ += n;
|
||||
return iterator(this, total_size_ - n, total_size_ - n, total_size_);
|
||||
} else {
|
||||
size_t allocs_in_curr_buffer = buffer_size - iid;
|
||||
size_t allocs_in_next_buffer = n - (buffer_size - iid);
|
||||
RealType *ptr = &(*data_[bid])[iid];
|
||||
for (size_t i = 0; i < allocs_in_curr_buffer; ++i) {
|
||||
new (ptr + i) RealType();
|
||||
}
|
||||
allocate_buffer();
|
||||
bid = data_.size() - 1;
|
||||
iid = 0;
|
||||
total_size_ += n;
|
||||
|
||||
RealType *ptr2 = &(*data_[bid])[iid];
|
||||
for (size_t i = 0; i < allocs_in_next_buffer; i++) {
|
||||
new (ptr2 + i) RealType();
|
||||
}
|
||||
return iterator(this, total_size_ - n, total_size_ - n, total_size_);
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief number of elements */
|
||||
size_t size() const { return total_size_; }
|
||||
|
||||
/** @brief total capacity */
|
||||
size_t capacity() const { return data_.size() * buffer_size; }
|
||||
|
||||
/** @brief iterator helpers */
|
||||
iterator begin() { return iterator(this, 0); }
|
||||
iterator end() { return iterator(this, total_size_); }
|
||||
const_iterator begin() const { return const_iterator(this, 0); }
|
||||
const_iterator end() const { return const_iterator(this, total_size_); }
|
||||
|
||||
iterator last_checkpoint() { return iterator(this, checkpoints_.back(), 0, total_size_); }
|
||||
iterator first_checkpoint() { return iterator(this, checkpoints_[0], 0, total_size_); };
|
||||
iterator checkpoint_at(size_t index)
|
||||
{
|
||||
return iterator(this, checkpoints_[index], 0, total_size_);
|
||||
};
|
||||
|
||||
/** @brief searches for item in allocator
|
||||
* only used to find gradient nodes for propagation */
|
||||
iterator find(const RealType *const item)
|
||||
{
|
||||
return std::find_if(begin(), end(), [&](const RealType &val) { return &val == item; });
|
||||
}
|
||||
/** @brief vector like access,
|
||||
* currently unused anywhere but very useful for debugging
|
||||
*/
|
||||
RealType &operator[](std::size_t i)
|
||||
{
|
||||
BOOST_MATH_ASSERT(i < total_size_);
|
||||
return (*data_[i / buffer_size])[i % buffer_size];
|
||||
}
|
||||
const RealType &operator[](std::size_t i) const
|
||||
{
|
||||
BOOST_MATH_ASSERT(i < total_size_);
|
||||
return (*data_[i / buffer_size])[i % buffer_size];
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
} // namespace reverse_mode
|
||||
} // namespace differentiation
|
||||
} // namespace math
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright Maksym Zhelyenzyakov 2025-2026.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef REVERSE_MODE_AUTODIFF_UTILITIES_HPP
|
||||
#define REVERSE_MODE_AUTODIFF_UTILITIES_HPP
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace math {
|
||||
namespace differentiation {
|
||||
namespace reverse_mode {
|
||||
namespace detail {
|
||||
|
||||
template<bool Condition>
|
||||
struct if_functional_dispatch_impl;
|
||||
|
||||
template<>
|
||||
struct if_functional_dispatch_impl<true>
|
||||
{
|
||||
template<typename F1, typename F2, typename... Args>
|
||||
static decltype(auto) call(F1&& f1, F2&&, Args&&... args)
|
||||
{
|
||||
return std::forward<F1>(f1)(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct if_functional_dispatch_impl<false>
|
||||
{
|
||||
template<typename F1, typename F2, typename... Args>
|
||||
static decltype(auto) call(F1&&, F2&& f2, Args&&... args)
|
||||
{
|
||||
return std::forward<F2>(f2)(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template<bool Condition, typename F1, typename F2, typename... Args>
|
||||
decltype(auto) if_functional_dispatch(F1&& f1, F2&& f2, Args&&... args)
|
||||
{
|
||||
return if_functional_dispatch_impl<Condition>::call(std::forward<F1>(f1),
|
||||
std::forward<F2>(f2),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
} // namespace detail
|
||||
} // namespace reverse_mode
|
||||
} // namespace differentiation
|
||||
} // namespace math
|
||||
} // namespace boost
|
||||
#endif // REVERSE_MODE_AUTODIFF_UTILITIES_HPP
|
||||
@@ -94,7 +94,6 @@ inline typename tools::promote_args<T>::type sin_pi(T x)
|
||||
{
|
||||
return boost::math::sin_pi(x, policies::policy<>());
|
||||
}
|
||||
|
||||
} // namespace math
|
||||
} // namespace boost
|
||||
|
||||
|
||||
@@ -1360,6 +1360,11 @@ test-suite autodiff :
|
||||
[ compile compile_test/diff_lanczos_smoothing_incl_test.cpp : <toolset>gcc-mingw:<cxxflags>-Wa,-mbig-obj <debug-symbols>off <toolset>msvc:<cxxflags>/bigobj release [ requires cxx17_if_constexpr cxx17_std_apply ] $(float128_type) [ check-target-builds ../config//is_cygwin_run "Cygwin CI run" : <build>no ] [ requires cxx11_inline_namespaces ] ]
|
||||
;
|
||||
|
||||
test-suite autodiff_reverse_specific :
|
||||
# This rule compiles and runs your specific test file.
|
||||
# The arguments replicate the common settings for Boost.Math tests.
|
||||
[ run my_autodiff_reverse_test.cpp /boost/test//boost_unit_test_framework : : : <toolset>gcc-mingw:<cxxflags>-Wa,-mbig-obj <debug-symbols>off <toolset>msvc:<cxxflags>/bigobj release $(float128_type) [ check-target-builds ../config//is_cygwin_run "Cygwin CI run" : <build>no ] [ requires cxx11_inline_namespaces ] ]
|
||||
;
|
||||
#
|
||||
# These tests are run by default when you invoke the Jamfile, but
|
||||
# they are deliberately NOT run from the CI scripts as they soak up
|
||||
@@ -1380,11 +1385,19 @@ test-suite long-running-tests :
|
||||
[ run test_pFq_precision.cpp ../tools//mpfr ../tools//gmp /boost/test//boost_unit_test_framework /boost/system//boost_system /boost/chrono//boost_chrono : : : [ check-target-builds ../config//has_mpfr : : <build>no ] [ requires cxx11_hdr_initializer_list cxx11_auto_declarations cxx11_lambdas cxx11_unified_initialization_syntax cxx11_smart_ptr ] release <toolset>clang:<cxxflags>-Wno-literal-range ]
|
||||
[ run test_constant_generate.cpp : : : release <define>USE_CPP_FLOAT=1 <exception-handling>off:<build>no ]
|
||||
;
|
||||
|
||||
build-project ../example ;
|
||||
# Expect policy_ref_snips13 to fail (message about no Cauchy Mean).
|
||||
|
||||
|
||||
test-suite "test_reverse_mode_autodiff"
|
||||
:
|
||||
[ run test_reverse_mode_autodiff_flat_linear_allocator.cpp ]
|
||||
[ run test_reverse_mode_autodiff_constructors.cpp ]
|
||||
[ run test_reverse_mode_autodiff_comparison_operators.cpp ]
|
||||
[ run test_reverse_mode_autodiff_stl_support.cpp ]
|
||||
[ run test_reverse_mode_autodiff_basic_math_ops.cpp ]
|
||||
[ run test_reverse_mode_autodiff_error_functions.cpp ]
|
||||
;
|
||||
rule get_float128_tests
|
||||
{
|
||||
local result ;
|
||||
|
||||
10044
test/airy_ai_float.svg
Normal file
|
After Width: | Height: | Size: 719 KiB |
10044
test/airy_ai_float_white.svg
Normal file
|
After Width: | Height: | Size: 719 KiB |
10038
test/airy_ai_float_white_no_envelope.svg
Normal file
|
After Width: | Height: | Size: 510 KiB |
157
test/test_autodiff_reverse.hpp
Normal file
@@ -0,0 +1,157 @@
|
||||
// Copyright Maksym Zhelyenzyakov 2025-2026.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef TEST_AUTODIFF_REVERSE_HPP
|
||||
#define TEST_AUTODIFF_REVERSE_HPP
|
||||
#ifndef BOOST_TEST_MODULE
|
||||
#define BOOST_TEST_MODULE test_autodiff
|
||||
#endif
|
||||
#define BOOST_MP_NO_QUAD
|
||||
#define BOOST_MATH_DISABLE_FLOAT128
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/math/differentiation/autodiff_reverse.hpp>
|
||||
#include <boost/math/tools/config.hpp>
|
||||
#include <boost/mp11/function.hpp>
|
||||
#include <boost/mp11/integral.hpp>
|
||||
#include <boost/mp11/list.hpp>
|
||||
#include <boost/mp11/utility.hpp>
|
||||
#include <boost/multiprecision/cpp_bin_float.hpp>
|
||||
#include <boost/multiprecision/cpp_dec_float.hpp>
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
#include <cfenv>
|
||||
#include <cstdlib>
|
||||
#include <random>
|
||||
|
||||
using boost::multiprecision::cpp_bin_float_50;
|
||||
|
||||
namespace mp11 = boost::mp11;
|
||||
namespace bmp = boost::multiprecision;
|
||||
namespace rdiff_detail = boost::math::differentiation::reverse_mode::detail;
|
||||
namespace rdiff = boost::math::differentiation::reverse_mode;
|
||||
#if defined(BOOST_USE_VALGRIND) || defined(BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS)
|
||||
using bin_float_types = mp11::mp_list<float>;
|
||||
#elif defined(__STDCPP_FLOAT32_T__) && defined(__STDCPP_FLOAT64_T__)
|
||||
using bin_float_types = mp11::mp_list<std::float32_t, std::float64_t>;
|
||||
#else
|
||||
using bin_float_types = mp11::mp_list<float, double, long double>;
|
||||
#endif
|
||||
|
||||
#if !defined(BOOST_VERSION) || BOOST_VERSION < 107000 || defined(BOOST_USE_VALGRIND) \
|
||||
|| defined(BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS) || defined(BOOST_NO_STRESS_TEST) \
|
||||
|| defined(BOOST_MATH_STANDALONE)
|
||||
using multiprecision_float_types = mp11::mp_list<>;
|
||||
#else
|
||||
#define BOOST_AUTODIFF_TESTING_INCLUDE_MULTIPRECISION
|
||||
using multiprecision_float_types = mp11::mp_list<bmp::cpp_bin_float_50>;
|
||||
#endif
|
||||
|
||||
using all_float_types = mp11::mp_append<bin_float_types, multiprecision_float_types>;
|
||||
|
||||
using namespace boost::math::differentiation;
|
||||
#endif // TEST_AUTODIFF_REVERSE_HPP
|
||||
template<typename T>
|
||||
using is_multiprecision_t = mp11::mp_or<bmp::is_number<T>, bmp::is_number_expression<T>>;
|
||||
|
||||
template<bool IfValue, typename ThenType, typename ElseType>
|
||||
using if_c = mp11::mp_eval_if_c<IfValue, ThenType, mp11::mp_identity_t, ElseType>;
|
||||
|
||||
template<typename IfType, typename ThenType, typename ElseType>
|
||||
using if_t = if_c<IfType::value, ThenType, ElseType>;
|
||||
/**
|
||||
* struct to emit pseudo-random values from a given interval.
|
||||
* Endpoints are closed or open depending on whether or not they're infinite).
|
||||
*/
|
||||
/**
|
||||
* Simple struct to hold constants that are used in each test
|
||||
* since BOOST_AUTO_TEST_CASE_TEMPLATE doesn't support fixtures.
|
||||
*/
|
||||
template<typename T, std::size_t OrderValue>
|
||||
struct test_constants_t
|
||||
{
|
||||
static constexpr auto n_samples
|
||||
= if_t<mp11::mp_or<bmp::is_number<T>, bmp::is_number_expression<T>>,
|
||||
mp11::mp_int<10>,
|
||||
mp11::mp_int<25>>::value;
|
||||
static constexpr auto order = OrderValue;
|
||||
static constexpr T pct_epsilon() noexcept
|
||||
{
|
||||
return (is_multiprecision_t<T>::value ? 2 : 1) * std::numeric_limits<T>::epsilon() * 100;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, std::size_t Order = 5>
|
||||
using test_constants = test_constants_t<T, Order>;
|
||||
|
||||
template<typename T>
|
||||
struct RandomSample
|
||||
{
|
||||
using numeric_limits_t = std::numeric_limits<T>;
|
||||
using is_integer_t = mp11::mp_bool<std::numeric_limits<T>::is_integer>;
|
||||
|
||||
using distribution_param_t
|
||||
= if_t<is_multiprecision_t<T>,
|
||||
if_t<is_integer_t, if_c<numeric_limits_t::is_signed, int64_t, uint64_t>, long double>,
|
||||
T>;
|
||||
static_assert((std::numeric_limits<T>::is_integer
|
||||
&& std::numeric_limits<distribution_param_t>::is_integer)
|
||||
|| (!std::numeric_limits<T>::is_integer
|
||||
&& !std::numeric_limits<distribution_param_t>::is_integer),
|
||||
"T and distribution_param_t must either both be integral or "
|
||||
"both be not integral");
|
||||
|
||||
using dist_t = if_t<is_integer_t,
|
||||
std::uniform_int_distribution<distribution_param_t>,
|
||||
std::uniform_real_distribution<distribution_param_t>>;
|
||||
|
||||
struct get_integral_endpoint
|
||||
{
|
||||
template<typename V>
|
||||
constexpr distribution_param_t operator()(V finish) const noexcept
|
||||
{
|
||||
return static_cast<distribution_param_t>(finish);
|
||||
}
|
||||
};
|
||||
|
||||
struct get_real_endpoint
|
||||
{
|
||||
template<typename V>
|
||||
constexpr distribution_param_t operator()(V finish) const noexcept
|
||||
{
|
||||
return std::nextafter(static_cast<distribution_param_t>(finish),
|
||||
(std::numeric_limits<distribution_param_t>::max)());
|
||||
}
|
||||
};
|
||||
|
||||
using get_endpoint_t = if_t<is_integer_t, get_integral_endpoint, get_real_endpoint>;
|
||||
|
||||
template<typename U, typename V>
|
||||
RandomSample(U start, V finish)
|
||||
: rng_(std::random_device{}())
|
||||
, dist_(static_cast<distribution_param_t>(start), get_endpoint_t{}(finish))
|
||||
{}
|
||||
|
||||
T next() noexcept { return static_cast<T>(dist_(rng_)); }
|
||||
T normalize(const T& x) noexcept { return x / ((dist_.max)() - (dist_.min)()); }
|
||||
|
||||
std::mt19937 rng_;
|
||||
dist_t dist_;
|
||||
};
|
||||
static_assert(
|
||||
std::is_same<RandomSample<float>::dist_t, std::uniform_real_distribution<float>>::value, "");
|
||||
static_assert(
|
||||
std::is_same<RandomSample<int64_t>::dist_t, std::uniform_int_distribution<int64_t>>::value, "");
|
||||
static_assert(std::is_same<RandomSample<bmp::uint512_t>::dist_t,
|
||||
std::uniform_int_distribution<uint64_t>>::value,
|
||||
"");
|
||||
static_assert(std::is_same<RandomSample<bmp::cpp_bin_float_50>::dist_t,
|
||||
std::uniform_real_distribution<long double>>::value,
|
||||
"");
|
||||
|
||||
template<typename T>
|
||||
constexpr T boost_close_tol(T scale_factor = 1e5)
|
||||
{
|
||||
return std::numeric_limits<T>::epsilon() * scale_factor;
|
||||
}
|
||||
409
test/test_reverse_mode_autodiff_basic_math_ops.cpp
Normal file
@@ -0,0 +1,409 @@
|
||||
// Copyright Maksym Zhelyenzyakov 2025-2026.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "test_autodiff_reverse.hpp"
|
||||
#include <vector>
|
||||
BOOST_AUTO_TEST_SUITE(test_basic_math_ops)
|
||||
|
||||
using namespace rdiff;
|
||||
template<typename T>
|
||||
T test_add(T x[5])
|
||||
{
|
||||
return x[0] + x[1] + x[2] + x[3] + x[4];
|
||||
}
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(addition, T, all_float_types)
|
||||
{
|
||||
RandomSample<T> rng{-100, 100};
|
||||
T x1_v = rng.next();
|
||||
T x2_v = rng.next();
|
||||
T test_rvar_p_rvar_v = x1_v + x2_v;
|
||||
|
||||
rvar<T, 1> x1 = x1_v;
|
||||
rvar<T, 1> x2 = x2_v;
|
||||
|
||||
rvar<T, 1> test_rvar_p_rvar = x1 + x2;
|
||||
rvar<T, 1> test_rvar_p_float = x1 + x2_v;
|
||||
rvar<T, 1> test_float_p_rvar = x1_v + x2;
|
||||
BOOST_REQUIRE_EQUAL(test_rvar_p_rvar_v, test_rvar_p_rvar.item());
|
||||
BOOST_REQUIRE_EQUAL(test_rvar_p_rvar_v, test_rvar_p_float.item());
|
||||
BOOST_REQUIRE_EQUAL(test_float_p_rvar.item(), test_rvar_p_rvar_v);
|
||||
|
||||
gradient_tape<T, 1>& tape = get_active_tape<T, 1>();
|
||||
tape.zero_grad();
|
||||
test_rvar_p_rvar.backward();
|
||||
BOOST_REQUIRE_EQUAL(x1.adjoint(), 1.0);
|
||||
BOOST_REQUIRE_EQUAL(x2.adjoint(), 1.0);
|
||||
|
||||
tape.zero_grad();
|
||||
rvar<T, 1> z = x1 + x1 + x1 + x1 + x1;
|
||||
z.backward();
|
||||
BOOST_REQUIRE_EQUAL(x1.adjoint(), 5.0);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(multiplication, T, all_float_types)
|
||||
{
|
||||
RandomSample<T> rng{-100, 100};
|
||||
T x1_v = rng.next();
|
||||
T x2_v = rng.next();
|
||||
T test_rvar_p_rvar_v = x1_v * x2_v;
|
||||
|
||||
rvar<T, 1> x1 = x1_v;
|
||||
rvar<T, 1> x2 = x2_v;
|
||||
|
||||
rvar<T, 1> test_rvar_p_rvar = x1 * x2;
|
||||
rvar<T, 1> test_rvar_p_float = x1 * x2_v;
|
||||
rvar<T, 1> test_float_p_rvar = x1_v * x2;
|
||||
|
||||
BOOST_REQUIRE_EQUAL(test_rvar_p_rvar_v, test_rvar_p_rvar.item());
|
||||
BOOST_REQUIRE_EQUAL(test_rvar_p_rvar_v, test_rvar_p_float.item());
|
||||
BOOST_REQUIRE_EQUAL(test_float_p_rvar.item(), test_rvar_p_rvar_v);
|
||||
|
||||
gradient_tape<T, 1>& tape = get_active_tape<T, 1>();
|
||||
tape.zero_grad();
|
||||
test_rvar_p_rvar.backward();
|
||||
BOOST_REQUIRE_EQUAL(x1.adjoint(), x2_v);
|
||||
BOOST_REQUIRE_EQUAL(x2.adjoint(), x1_v);
|
||||
|
||||
tape.zero_grad();
|
||||
auto z = x1 * x1 * x1 * x1 * x1;
|
||||
rvar<T, 1> zz(z);
|
||||
zz.backward();
|
||||
BOOST_REQUIRE_CLOSE(x1.adjoint(), 5 * x1_v * x1_v * x1_v * x1_v, boost_close_tol<T>());
|
||||
tape.clear();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(minus, T, all_float_types)
|
||||
{
|
||||
RandomSample<T> rng{-100, 100};
|
||||
T x1_v = rng.next();
|
||||
T x2_v = rng.next();
|
||||
T test_rvar_p_rvar_v = x1_v - x2_v;
|
||||
|
||||
rvar<T, 1> x1 = x1_v;
|
||||
rvar<T, 1> x2 = x2_v;
|
||||
|
||||
rvar<T, 1> test_rvar_p_rvar = x1 - x2;
|
||||
rvar<T, 1> test_rvar_p_float = x1 - x2_v;
|
||||
rvar<T, 1> test_float_p_rvar = x1_v - x2;
|
||||
|
||||
BOOST_REQUIRE_EQUAL(test_rvar_p_rvar_v, test_rvar_p_rvar.item());
|
||||
BOOST_REQUIRE_EQUAL(test_rvar_p_rvar_v, test_rvar_p_float.item());
|
||||
BOOST_REQUIRE_EQUAL(test_float_p_rvar.item(), test_rvar_p_rvar_v);
|
||||
|
||||
gradient_tape<T, 1>& tape = get_active_tape<T, 1>();
|
||||
tape.zero_grad();
|
||||
test_rvar_p_rvar.backward();
|
||||
BOOST_REQUIRE_EQUAL(x1.adjoint(), 1.0);
|
||||
BOOST_REQUIRE_EQUAL(x2.adjoint(), -1.0);
|
||||
|
||||
tape.zero_grad();
|
||||
auto z = -x1 - x1 - x1 - x1 - x1;
|
||||
rvar<T, 1> zz(z);
|
||||
zz.backward();
|
||||
BOOST_REQUIRE_CLOSE(x1.adjoint(), -5, boost_close_tol<T>());
|
||||
tape.clear();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(division, T, all_float_types)
|
||||
{
|
||||
RandomSample<T> rng{-1, 1};
|
||||
T x1_v = rng.next();
|
||||
T x2_v = rng.next();
|
||||
T test_rvar_p_rvar_v = x1_v / x2_v;
|
||||
|
||||
rvar<T, 1> x1 = x1_v;
|
||||
rvar<T, 1> x2 = x2_v;
|
||||
|
||||
rvar<T, 1> test_rvar_p_rvar = x1 / x2;
|
||||
rvar<T, 1> test_rvar_p_float = x1 / x2_v;
|
||||
rvar<T, 1> test_float_p_rvar = x1_v / x2;
|
||||
|
||||
BOOST_REQUIRE_EQUAL(test_rvar_p_rvar_v, test_rvar_p_rvar.item());
|
||||
BOOST_REQUIRE_EQUAL(test_rvar_p_rvar_v, test_rvar_p_float.item());
|
||||
BOOST_REQUIRE_EQUAL(test_float_p_rvar.item(), test_rvar_p_rvar_v);
|
||||
|
||||
gradient_tape<T, 1>& tape = get_active_tape<T, 1>();
|
||||
tape.zero_grad();
|
||||
test_rvar_p_rvar.backward();
|
||||
BOOST_REQUIRE_CLOSE(x1.adjoint(), 1.0 / x2_v, boost_close_tol<T>());
|
||||
BOOST_REQUIRE_CLOSE(x2.adjoint(), -x1_v / (x2_v * x2_v), boost_close_tol<T>());
|
||||
|
||||
tape.zero_grad();
|
||||
auto z = 2.0 / x1 / x1 / x1 / x1 / x1;
|
||||
rvar<T, 1> zz(z);
|
||||
zz.backward();
|
||||
BOOST_REQUIRE_CLOSE(x1.adjoint(),
|
||||
-10 / (x1_v * x1_v * x1_v * x1_v * x1_v * x1_v),
|
||||
boost_close_tol<T>());
|
||||
tape.clear();
|
||||
}
|
||||
|
||||
/* gradient test function */
|
||||
template<typename T>
|
||||
T f(T x, T y)
|
||||
{
|
||||
return (x * y) / ((x + y) * (x - y));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::vector<T> grad_f_a(T x, T y)
|
||||
{
|
||||
T f_x = static_cast<T>(y * (-pow(x, 2) - pow(y, 2))
|
||||
/ (pow(x, 4) - 2 * pow(x, 2) * pow(y, 2) + pow(y, 4)));
|
||||
T f_y = static_cast<T>(x * (pow(x, 2) + pow(y, 2))
|
||||
/ (pow(x, 4) - 2 * pow(x, 2) * pow(y, 2) + pow(y, 4)));
|
||||
return {f_x, f_y};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::vector<std::vector<T>> hess_f_a(T x, T y)
|
||||
{
|
||||
T f_xx = static_cast<T>(
|
||||
2 * x * y * (pow(x, 2) + 3 * pow(y, 2))
|
||||
/ (pow(x, 6) - 3 * pow(x, 4) * pow(y, 2) + 3 * pow(x, 2) * pow(y, 4) - pow(y, 6)));
|
||||
T f_xy = static_cast<T>(
|
||||
(-pow(x, 4) - 6 * pow(x, 2) * pow(y, 2) - pow(y, 4))
|
||||
/ (pow(x, 6) - 3 * pow(x, 4) * pow(y, 2) + 3 * pow(x, 2) * pow(y, 4) - pow(y, 6)));
|
||||
T f_yx = static_cast<T>(
|
||||
(-pow(x, 4) - 6 * pow(x, 2) * pow(y, 2) - pow(y, 4))
|
||||
/ (pow(x, 6) - 3 * pow(x, 4) * pow(y, 2) + 3 * pow(x, 2) * pow(y, 4) - pow(y, 6)));
|
||||
T f_yy = static_cast<T>(
|
||||
2 * x * y * (3 * pow(x, 2) + pow(y, 2))
|
||||
/ (pow(x, 6) - 3 * pow(x, 4) * pow(y, 2) + 3 * pow(x, 2) * pow(y, 4) - pow(y, 6)));
|
||||
return {{f_xx, f_xy}, {f_yx, f_yy}};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::vector<std::vector<std::vector<T>>> df_3_a(T x, T y)
|
||||
{
|
||||
T f_xxx = static_cast<T>(6 * y * (-pow(x, 4) - 6 * pow(x, 2) * pow(y, 2) - pow(y, 4))
|
||||
/ (pow(x, 8) - 4 * pow(x, 6) * pow(y, 2) + 6 * pow(x, 4) * pow(y, 4)
|
||||
- 4 * pow(x, 2) * pow(y, 6) + pow(y, 8)));
|
||||
T f_xxy = static_cast<T>(2 * x * (pow(x, 4) + 14 * pow(x, 2) * pow(y, 2) + 9 * pow(y, 4))
|
||||
/ (pow(x, 8) - 4 * pow(x, 6) * pow(y, 2) + 6 * pow(x, 4) * pow(y, 4)
|
||||
- 4 * pow(x, 2) * pow(y, 6) + pow(y, 8)));
|
||||
T f_xyx = static_cast<T>(2 * x * (pow(x, 4) + 14 * pow(x, 2) * pow(y, 2) + 9 * pow(y, 4))
|
||||
/ (pow(x, 8) - 4 * pow(x, 6) * pow(y, 2) + 6 * pow(x, 4) * pow(y, 4)
|
||||
- 4 * pow(x, 2) * pow(y, 6) + pow(y, 8)));
|
||||
T f_xyy = static_cast<T>(2 * y * (-9 * pow(x, 4) - 14 * pow(x, 2) * pow(y, 2) - pow(y, 4))
|
||||
/ (pow(x, 8) - 4 * pow(x, 6) * pow(y, 2) + 6 * pow(x, 4) * pow(y, 4)
|
||||
- 4 * pow(x, 2) * pow(y, 6) + pow(y, 8)));
|
||||
T f_yxx = static_cast<T>(2 * x * (pow(x, 4) + 14 * pow(x, 2) * pow(y, 2) + 9 * pow(y, 4))
|
||||
/ (pow(x, 8) - 4 * pow(x, 6) * pow(y, 2) + 6 * pow(x, 4) * pow(y, 4)
|
||||
- 4 * pow(x, 2) * pow(y, 6) + pow(y, 8)));
|
||||
T f_yxy = static_cast<T>(2 * y * (-9 * pow(x, 4) - 14 * pow(x, 2) * pow(y, 2) - pow(y, 4))
|
||||
/ (pow(x, 8) - 4 * pow(x, 6) * pow(y, 2) + 6 * pow(x, 4) * pow(y, 4)
|
||||
- 4 * pow(x, 2) * pow(y, 6) + pow(y, 8)));
|
||||
T f_yyx = static_cast<T>(2 * y * (-9 * pow(x, 4) - 14 * pow(x, 2) * pow(y, 2) - pow(y, 4))
|
||||
/ (pow(x, 8) - 4 * pow(x, 6) * pow(y, 2) + 6 * pow(x, 4) * pow(y, 4)
|
||||
- 4 * pow(x, 2) * pow(y, 6) + pow(y, 8)));
|
||||
T f_yyy = static_cast<T>(6 * x * (pow(x, 4) + 6 * pow(x, 2) * pow(y, 2) + pow(y, 4))
|
||||
/ (pow(x, 8) - 4 * pow(x, 6) * pow(y, 2) + 6 * pow(x, 4) * pow(y, 4)
|
||||
- 4 * pow(x, 2) * pow(y, 6) + pow(y, 8)));
|
||||
return {{{f_xxx, f_xxy}, {f_xyx, f_xyy}}, {{f_yxx, f_yxy}, {f_yyx, f_yyy}}};
|
||||
}
|
||||
template<typename T>
|
||||
std::vector<std::vector<std::vector<std::vector<T>>>> df_4_a(T x, T y)
|
||||
{
|
||||
T f_xxxx = static_cast<T>(24 * x * y * (pow(x, 4) + 10 * pow(x, 2) * pow(y, 2) + 5 * pow(y, 4))
|
||||
/ (pow(x, 10) - 5 * pow(x, 8) * pow(y, 2) + 10 * pow(x, 6) * pow(y, 4)
|
||||
- 10 * pow(x, 4) * pow(y, 6) + 5 * pow(x, 2) * pow(y, 8)
|
||||
- pow(y, 10)));
|
||||
T f_xxxy = static_cast<T>(
|
||||
6 * (-pow(x, 6) - 25 * pow(x, 4) * pow(y, 2) - 35 * pow(x, 2) * pow(y, 4) - 3 * pow(y, 6))
|
||||
/ (pow(x, 10) - 5 * pow(x, 8) * pow(y, 2) + 10 * pow(x, 6) * pow(y, 4)
|
||||
- 10 * pow(x, 4) * pow(y, 6) + 5 * pow(x, 2) * pow(y, 8) - pow(y, 10)));
|
||||
T f_xxyx = static_cast<T>(
|
||||
6 * (-pow(x, 6) - 25 * pow(x, 4) * pow(y, 2) - 35 * pow(x, 2) * pow(y, 4) - 3 * pow(y, 6))
|
||||
/ (pow(x, 10) - 5 * pow(x, 8) * pow(y, 2) + 10 * pow(x, 6) * pow(y, 4)
|
||||
- 10 * pow(x, 4) * pow(y, 6) + 5 * pow(x, 2) * pow(y, 8) - pow(y, 10)));
|
||||
T f_xxyy = static_cast<T>(
|
||||
(72 * pow(x, 5) * y + 240 * pow(x, 3) * pow(y, 3) + 72 * x * pow(y, 5))
|
||||
/ (pow(x, 10) - 5 * pow(x, 8) * pow(y, 2) + 10 * pow(x, 6) * pow(y, 4)
|
||||
- 10 * pow(x, 4) * pow(y, 6) + 5 * pow(x, 2) * pow(y, 8) - pow(y, 10)));
|
||||
T f_xyxx = static_cast<T>(
|
||||
6 * (-pow(x, 6) - 25 * pow(x, 4) * pow(y, 2) - 35 * pow(x, 2) * pow(y, 4) - 3 * pow(y, 6))
|
||||
/ (pow(x, 10) - 5 * pow(x, 8) * pow(y, 2) + 10 * pow(x, 6) * pow(y, 4)
|
||||
- 10 * pow(x, 4) * pow(y, 6) + 5 * pow(x, 2) * pow(y, 8) - pow(y, 10)));
|
||||
T f_xyxy = static_cast<T>(
|
||||
(72 * pow(x, 5) * y + 240 * pow(x, 3) * pow(y, 3) + 72 * x * pow(y, 5))
|
||||
/ (pow(x, 10) - 5 * pow(x, 8) * pow(y, 2) + 10 * pow(x, 6) * pow(y, 4)
|
||||
- 10 * pow(x, 4) * pow(y, 6) + 5 * pow(x, 2) * pow(y, 8) - pow(y, 10)));
|
||||
T f_xyyx = static_cast<T>(
|
||||
(72 * pow(x, 5) * y + 240 * pow(x, 3) * pow(y, 3) + 72 * x * pow(y, 5))
|
||||
/ (pow(x, 10) - 5 * pow(x, 8) * pow(y, 2) + 10 * pow(x, 6) * pow(y, 4)
|
||||
- 10 * pow(x, 4) * pow(y, 6) + 5 * pow(x, 2) * pow(y, 8) - pow(y, 10)));
|
||||
T f_xyyy = static_cast<T>(
|
||||
6 * (-3 * pow(x, 6) - 35 * pow(x, 4) * pow(y, 2) - 25 * pow(x, 2) * pow(y, 4) - pow(y, 6))
|
||||
/ (pow(x, 10) - 5 * pow(x, 8) * pow(y, 2) + 10 * pow(x, 6) * pow(y, 4)
|
||||
- 10 * pow(x, 4) * pow(y, 6) + 5 * pow(x, 2) * pow(y, 8) - pow(y, 10)));
|
||||
T f_yxxx = static_cast<T>(
|
||||
6 * (-pow(x, 6) - 25 * pow(x, 4) * pow(y, 2) - 35 * pow(x, 2) * pow(y, 4) - 3 * pow(y, 6))
|
||||
/ (pow(x, 10) - 5 * pow(x, 8) * pow(y, 2) + 10 * pow(x, 6) * pow(y, 4)
|
||||
- 10 * pow(x, 4) * pow(y, 6) + 5 * pow(x, 2) * pow(y, 8) - pow(y, 10)));
|
||||
T f_yxxy = static_cast<T>(
|
||||
(72 * pow(x, 5) * y + 240 * pow(x, 3) * pow(y, 3) + 72 * x * pow(y, 5))
|
||||
/ (pow(x, 10) - 5 * pow(x, 8) * pow(y, 2) + 10 * pow(x, 6) * pow(y, 4)
|
||||
- 10 * pow(x, 4) * pow(y, 6) + 5 * pow(x, 2) * pow(y, 8) - pow(y, 10)));
|
||||
T f_yxyx = static_cast<T>(
|
||||
(72 * pow(x, 5) * y + 240 * pow(x, 3) * pow(y, 3) + 72 * x * pow(y, 5))
|
||||
/ (pow(x, 10) - 5 * pow(x, 8) * pow(y, 2) + 10 * pow(x, 6) * pow(y, 4)
|
||||
- 10 * pow(x, 4) * pow(y, 6) + 5 * pow(x, 2) * pow(y, 8) - pow(y, 10)));
|
||||
T f_yxyy = static_cast<T>(
|
||||
6 * (-3 * pow(x, 6) - 35 * pow(x, 4) * pow(y, 2) - 25 * pow(x, 2) * pow(y, 4) - pow(y, 6))
|
||||
/ (pow(x, 10) - 5 * pow(x, 8) * pow(y, 2) + 10 * pow(x, 6) * pow(y, 4)
|
||||
- 10 * pow(x, 4) * pow(y, 6) + 5 * pow(x, 2) * pow(y, 8) - pow(y, 10)));
|
||||
T f_yyxx = static_cast<T>(
|
||||
(72 * pow(x, 5) * y + 240 * pow(x, 3) * pow(y, 3) + 72 * x * pow(y, 5))
|
||||
/ (pow(x, 10) - 5 * pow(x, 8) * pow(y, 2) + 10 * pow(x, 6) * pow(y, 4)
|
||||
- 10 * pow(x, 4) * pow(y, 6) + 5 * pow(x, 2) * pow(y, 8) - pow(y, 10)));
|
||||
T f_yyxy = static_cast<T>(
|
||||
6 * (-3 * pow(x, 6) - 35 * pow(x, 4) * pow(y, 2) - 25 * pow(x, 2) * pow(y, 4) - pow(y, 6))
|
||||
/ (pow(x, 10) - 5 * pow(x, 8) * pow(y, 2) + 10 * pow(x, 6) * pow(y, 4)
|
||||
- 10 * pow(x, 4) * pow(y, 6) + 5 * pow(x, 2) * pow(y, 8) - pow(y, 10)));
|
||||
T f_yyyx = static_cast<T>(
|
||||
6 * (-3 * pow(x, 6) - 35 * pow(x, 4) * pow(y, 2) - 25 * pow(x, 2) * pow(y, 4) - pow(y, 6))
|
||||
/ (pow(x, 10) - 5 * pow(x, 8) * pow(y, 2) + 10 * pow(x, 6) * pow(y, 4)
|
||||
- 10 * pow(x, 4) * pow(y, 6) + 5 * pow(x, 2) * pow(y, 8) - pow(y, 10)));
|
||||
T f_yyyy = static_cast<T>(24 * x * y * (5 * pow(x, 4) + 10 * pow(x, 2) * pow(y, 2) + pow(y, 4))
|
||||
/ (pow(x, 10) - 5 * pow(x, 8) * pow(y, 2) + 10 * pow(x, 6) * pow(y, 4)
|
||||
- 10 * pow(x, 4) * pow(y, 6) + 5 * pow(x, 2) * pow(y, 8)
|
||||
- pow(y, 10)));
|
||||
return {{{{f_xxxx, f_xxxy}, {f_xxyx, f_xxyy}}, {{f_xyxx, f_xyxy}, {f_xyyx, f_xyyy}}},
|
||||
{{{f_yxxx, f_yxxy}, {f_yxyx, f_yxyy}}, {{f_yyxx, f_yyxy}, {f_yyyx, f_yyyy}}}};
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(first_derivative, T, all_float_types)
|
||||
{
|
||||
//RandomSample<T> rng{-1, 1};
|
||||
T x = 1.1224; //rng.next();
|
||||
T y = 5.213414; //rng.next();
|
||||
|
||||
rvar<T, 1> x_ad = x;
|
||||
rvar<T, 1> y_ad = y;
|
||||
|
||||
//T fv = f(x, y);
|
||||
rvar<T, 1> f_ad = f(x_ad, y_ad);
|
||||
auto grad_f_analytical = grad_f_a(x, y);
|
||||
/* intended use case */
|
||||
f_ad.backward();
|
||||
BOOST_REQUIRE_CLOSE(x_ad.adjoint(), grad_f_analytical[0], boost_close_tol<T>());
|
||||
BOOST_REQUIRE_CLOSE(y_ad.adjoint(), grad_f_analytical[1], boost_close_tol<T>());
|
||||
|
||||
gradient_tape<T, 1, BOOST_MATH_BUFFER_SIZE>& tape = get_active_tape<T, 1>();
|
||||
tape.zero_grad();
|
||||
|
||||
/* grad test */
|
||||
auto grad_func_test_grad = grad(f_ad, &x_ad, &y_ad);
|
||||
/* grad_nd test */
|
||||
auto grad_nd_func_test_grad = grad_nd<1>(f_ad, &x_ad, &y_ad);
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
BOOST_REQUIRE_CLOSE(grad_func_test_grad[i], grad_f_analytical[i], boost_close_tol<T>());
|
||||
BOOST_REQUIRE_CLOSE(grad_nd_func_test_grad[i], grad_f_analytical[i], boost_close_tol<T>());
|
||||
}
|
||||
}
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(second_derivative_and_hessian, T, all_float_types)
|
||||
{
|
||||
//RandomSample<T> rng{1, 1};
|
||||
T x = 1.0; //; rng.next();
|
||||
T y = 1.5; //rng.next();
|
||||
|
||||
rvar<T, 2> x_ad = x;
|
||||
rvar<T, 2> y_ad = y;
|
||||
|
||||
//T fv = f(x, y);
|
||||
rvar<T, 2> f_ad = f(x_ad, y_ad);
|
||||
gradient_tape<T, 2, BOOST_MATH_BUFFER_SIZE>& tape = get_active_tape<T, 2>();
|
||||
tape.zero_grad();
|
||||
|
||||
auto hess_analytical = hess_f_a(x, y);
|
||||
auto hess_func_test = hess(f_ad, &x_ad, &y_ad);
|
||||
auto grad_nd_func_test = grad_nd<2>(f_ad, &x_ad, &y_ad);
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
BOOST_REQUIRE_CLOSE(hess_func_test[i][j], hess_analytical[i][j], boost_close_tol<T>());
|
||||
BOOST_REQUIRE_CLOSE(hess_func_test[i][j], grad_nd_func_test[i][j], boost_close_tol<T>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(order_3_der, T, all_float_types)
|
||||
{
|
||||
//RandomSample<T> rng{1, 2};
|
||||
T x = 1; //rng.next();
|
||||
T y = 2; //rng.next();
|
||||
|
||||
rvar<T, 3> x_ad = x;
|
||||
rvar<T, 3> y_ad = y;
|
||||
|
||||
//T fv = f(x, y);
|
||||
rvar<T, 3> f_ad = f(x_ad, y_ad);
|
||||
gradient_tape<T, 3, BOOST_MATH_BUFFER_SIZE>& tape = get_active_tape<T, 3>();
|
||||
tape.zero_grad();
|
||||
|
||||
auto df3 = df_3_a(x, y);
|
||||
auto grad_ad = grad(f_ad, &x_ad, &y_ad);
|
||||
|
||||
std::vector<std::vector<std::vector<T>>> grad_tensor;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
auto df_hess = hess(grad_ad[i], &x_ad, &y_ad);
|
||||
grad_tensor.push_back(df_hess);
|
||||
}
|
||||
auto grad_nd_func_test = grad_nd<3>(f_ad, &x_ad, &y_ad);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
for (int k = 0; k < 2; k++) {
|
||||
BOOST_REQUIRE_CLOSE(grad_tensor[i][j][k], df3[i][j][k], boost_close_tol<T>());
|
||||
BOOST_REQUIRE_CLOSE(grad_nd_func_test[i][j][k], df3[i][j][k], boost_close_tol<T>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(fourth_derivative, T, all_float_types)
|
||||
{
|
||||
//RandomSample<T> rng{1, 2};
|
||||
// this derivative is a bit unstable and so the tests
|
||||
// are difficult to get to pass for floats and doubles (need long doubles)
|
||||
// but its stable enough at x=1 y=2
|
||||
T x_val = 1; //rng.next();
|
||||
T y_val = 2; //rng.next();
|
||||
|
||||
rvar<T, 4> x_ad = x_val;
|
||||
rvar<T, 4> y_ad = y_val;
|
||||
rvar<T, 4> f_ad = f(x_ad, y_ad);
|
||||
|
||||
auto df4 = df_4_a(x_val, y_val);
|
||||
auto gf = grad(f_ad, &x_ad, &y_ad);
|
||||
std::array<std::array<std::array<std::array<T, 2>, 2>, 2>, 2> ggggf;
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
auto hess1 = grad(gf[i], &x_ad, &y_ad);
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
auto hess2 = grad(hess1[j], &x_ad, &y_ad);
|
||||
for (int k = 0; k < 2; ++k) {
|
||||
auto hess3 = grad(hess2[k], &x_ad, &y_ad);
|
||||
for (int l = 0; l < 2; ++l) {
|
||||
ggggf[i][j][k][l] = hess3[l];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
auto grad_nd_func_test = grad_nd<4>(f_ad, &x_ad, &y_ad);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
for (int k = 0; k < 2; k++) {
|
||||
for (int l = 0; l < 2; l++) {
|
||||
BOOST_REQUIRE_CLOSE(ggggf[i][j][k][l], df4[i][j][k][l], boost_close_tol<T>());
|
||||
BOOST_REQUIRE_CLOSE(grad_nd_func_test[i][j][k][l],
|
||||
df4[i][j][k][l],
|
||||
boost_close_tol<T>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
303
test/test_reverse_mode_autodiff_comparison_operators.cpp
Normal file
@@ -0,0 +1,303 @@
|
||||
// Copyright Maksym Zhelyenzyakov 2025-2026.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
#include "test_autodiff_reverse.hpp"
|
||||
BOOST_AUTO_TEST_SUITE(test_comparison_operators)
|
||||
using namespace rdiff;
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(equals_rvar_and_rvar, T, all_float_types)
|
||||
{
|
||||
T a = T(3.14);
|
||||
T b = T(3.14);
|
||||
T c = T(2.71);
|
||||
|
||||
rvar<T, 1> var1 = rvar<T, 1>(a);
|
||||
rvar<T, 2> var2 = rvar<T, 2>(b);
|
||||
rvar<T, 3> var3 = rvar<T, 3>(a);
|
||||
rvar<T, 4> var4 = rvar<T, 4>(c);
|
||||
|
||||
BOOST_CHECK(var1 == var2);
|
||||
BOOST_CHECK(!(var1 == var4));
|
||||
BOOST_CHECK(var2 == var3);
|
||||
BOOST_CHECK(!(var3 == var4));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(equals_rvar_and_scalar, T, all_float_types)
|
||||
{
|
||||
T a = T(3.14);
|
||||
T b = T(3.14);
|
||||
T c = T(2.71);
|
||||
|
||||
rvar<T, 1> var1 = rvar<T, 1>(a);
|
||||
rvar<T, 2> var2 = rvar<T, 2>(a);
|
||||
rvar<T, 3> var3 = rvar<T, 3>(a);
|
||||
rvar<T, 4> var4 = rvar<T, 4>(a);
|
||||
|
||||
BOOST_CHECK(var1 == b);
|
||||
BOOST_CHECK(!(var1 == c));
|
||||
BOOST_CHECK(b == var1);
|
||||
BOOST_CHECK(!(c == var1));
|
||||
|
||||
BOOST_CHECK(var2 == b);
|
||||
BOOST_CHECK(!(var2 == c));
|
||||
BOOST_CHECK(b == var2);
|
||||
BOOST_CHECK(!(c == var2));
|
||||
|
||||
BOOST_CHECK(var3 == b);
|
||||
BOOST_CHECK(!(var3 == c));
|
||||
BOOST_CHECK(b == var3);
|
||||
BOOST_CHECK(!(c == var3));
|
||||
|
||||
BOOST_CHECK(var4 == b);
|
||||
BOOST_CHECK(!(var4 == c));
|
||||
BOOST_CHECK(b == var4);
|
||||
BOOST_CHECK(!(c == var4));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(inequality_rvar_and_rvar, T, all_float_types)
|
||||
{
|
||||
T a = T(3.14);
|
||||
T b = T(3.14);
|
||||
T c = T(2.71);
|
||||
|
||||
rvar<T, 1> var1 = rvar<T, 1>(a);
|
||||
rvar<T, 2> var2 = rvar<T, 2>(b);
|
||||
rvar<T, 3> var3 = rvar<T, 3>(a);
|
||||
rvar<T, 4> var4 = rvar<T, 4>(c);
|
||||
|
||||
BOOST_CHECK(var1 != var4);
|
||||
BOOST_CHECK(!(var1 != var2));
|
||||
BOOST_CHECK(var2 != var4);
|
||||
BOOST_CHECK(!(var3 != var2));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(inequality_rvar_and_scalar, T, all_float_types)
|
||||
{
|
||||
T a = T(3.14);
|
||||
T b = T(3.14);
|
||||
T c = T(2.71);
|
||||
|
||||
rvar<T, 1> var1 = rvar<T, 1>(a);
|
||||
rvar<T, 2> var2 = rvar<T, 2>(a);
|
||||
rvar<T, 3> var3 = rvar<T, 3>(a);
|
||||
rvar<T, 4> var4 = rvar<T, 4>(a);
|
||||
|
||||
BOOST_CHECK(var1 != c);
|
||||
BOOST_CHECK(!(var1 != b));
|
||||
BOOST_CHECK(c != var1);
|
||||
BOOST_CHECK(!(b != var1));
|
||||
|
||||
BOOST_CHECK(var2 != c);
|
||||
BOOST_CHECK(!(var2 != b));
|
||||
BOOST_CHECK(c != var2);
|
||||
BOOST_CHECK(!(b != var2));
|
||||
|
||||
BOOST_CHECK(var3 != c);
|
||||
BOOST_CHECK(!(var3 != b));
|
||||
BOOST_CHECK(c != var3);
|
||||
BOOST_CHECK(!(b != var3));
|
||||
|
||||
BOOST_CHECK(var4 != c);
|
||||
BOOST_CHECK(!(var4 != b));
|
||||
BOOST_CHECK(c != var4);
|
||||
BOOST_CHECK(!(b != var4));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(less_than_rvar_and_rvar, T, all_float_types)
|
||||
{
|
||||
T a = T(3.14);
|
||||
T b = T(3.14);
|
||||
T c = T(2.71);
|
||||
|
||||
rvar<T, 1> var1 = rvar<T, 1>(a);
|
||||
rvar<T, 2> var2 = rvar<T, 2>(b);
|
||||
rvar<T, 3> var3 = rvar<T, 3>(c);
|
||||
rvar<T, 4> var4 = rvar<T, 4>(c);
|
||||
|
||||
BOOST_CHECK(var3 < var1);
|
||||
BOOST_CHECK(!(var1 < var2));
|
||||
BOOST_CHECK(!(var1 < var3));
|
||||
BOOST_CHECK(!(var3 < var4));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(greater_than_rvar_and_rvar, T, all_float_types)
|
||||
{
|
||||
T a = T(3.14);
|
||||
T b = T(3.14);
|
||||
T c = T(2.71);
|
||||
|
||||
rvar<T, 1> var1 = rvar<T, 1>(a);
|
||||
rvar<T, 2> var2 = rvar<T, 2>(b);
|
||||
rvar<T, 3> var3 = rvar<T, 3>(c);
|
||||
rvar<T, 4> var4 = rvar<T, 4>(c);
|
||||
|
||||
BOOST_CHECK(var1 > var3);
|
||||
BOOST_CHECK(!(var1 > var2));
|
||||
BOOST_CHECK(!(var3 > var1));
|
||||
BOOST_CHECK(!(var3 > var4));
|
||||
}
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(less_than_rvar_and_scalar, T, all_float_types)
|
||||
{
|
||||
T a = T(3.14);
|
||||
T b = T(3.14);
|
||||
T c = T(2.71);
|
||||
T d = T(4.0);
|
||||
|
||||
rvar<T, 1> var1 = rvar<T, 1>(a);
|
||||
rvar<T, 2> var2 = rvar<T, 2>(a);
|
||||
rvar<T, 3> var3 = rvar<T, 3>(c);
|
||||
rvar<T, 4> var4 = rvar<T, 4>(c);
|
||||
|
||||
BOOST_CHECK(var3 < a);
|
||||
BOOST_CHECK(!(var1 < b));
|
||||
BOOST_CHECK(c < var1);
|
||||
BOOST_CHECK(!(d < var2));
|
||||
|
||||
BOOST_CHECK(var4 < a);
|
||||
BOOST_CHECK(!(var2 < b));
|
||||
BOOST_CHECK(c < var2);
|
||||
BOOST_CHECK(!(d < var1));
|
||||
|
||||
BOOST_CHECK(var3 < b);
|
||||
BOOST_CHECK(var4 < b);
|
||||
BOOST_CHECK(c < var1);
|
||||
BOOST_CHECK(!(d < var3));
|
||||
|
||||
BOOST_CHECK(var3 < a);
|
||||
BOOST_CHECK(!(var2 < a));
|
||||
BOOST_CHECK(!(c < var4));
|
||||
BOOST_CHECK(!(d < var1));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(greater_than_rvar_and_scalar, T, all_float_types)
|
||||
{
|
||||
T a = T(3.14);
|
||||
T b = T(3.14);
|
||||
T c = T(2.71);
|
||||
|
||||
rvar<T, 1> var1 = rvar<T, 1>(a);
|
||||
rvar<T, 2> var2 = rvar<T, 2>(a);
|
||||
rvar<T, 3> var3 = rvar<T, 3>(c);
|
||||
rvar<T, 4> var4 = rvar<T, 4>(c);
|
||||
|
||||
BOOST_CHECK(var1 > c);
|
||||
BOOST_CHECK(!(var1 > b));
|
||||
BOOST_CHECK(b > var3);
|
||||
BOOST_CHECK(!(c > var4));
|
||||
|
||||
BOOST_CHECK(var2 > c);
|
||||
BOOST_CHECK(!(var2 > b));
|
||||
BOOST_CHECK(b > var4);
|
||||
BOOST_CHECK(!(c > var3));
|
||||
|
||||
BOOST_CHECK(var1 > c);
|
||||
BOOST_CHECK(!(var1 > b));
|
||||
BOOST_CHECK(a > var4);
|
||||
BOOST_CHECK(!(c > var3));
|
||||
|
||||
BOOST_CHECK(var2 > c);
|
||||
BOOST_CHECK(!(var2 > b));
|
||||
BOOST_CHECK(a > var4);
|
||||
BOOST_CHECK(!(c > var3));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(less_than_or_equal_rvar_and_rvar, T, all_float_types)
|
||||
{
|
||||
T a = T(3.14);
|
||||
T b = T(3.14);
|
||||
T c = T(2.71);
|
||||
|
||||
rvar<T, 1> var1 = rvar<T, 1>(a);
|
||||
rvar<T, 2> var2 = rvar<T, 2>(b);
|
||||
rvar<T, 3> var3 = rvar<T, 3>(c);
|
||||
rvar<T, 4> var4 = rvar<T, 4>(c);
|
||||
|
||||
BOOST_CHECK(var3 <= var1);
|
||||
BOOST_CHECK(var1 <= var2);
|
||||
BOOST_CHECK(!(var1 <= var3));
|
||||
BOOST_CHECK(var3 <= var4);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(less_than_or_equal_rvar_and_scalar, T, all_float_types)
|
||||
{
|
||||
T a = T(3.14);
|
||||
T b = T(3.14);
|
||||
T c = T(2.71);
|
||||
T d = T(4.0);
|
||||
|
||||
rvar<T, 1> var1 = rvar<T, 1>(a);
|
||||
rvar<T, 2> var2 = rvar<T, 2>(a);
|
||||
rvar<T, 3> var3 = rvar<T, 3>(c);
|
||||
rvar<T, 4> var4 = rvar<T, 4>(c);
|
||||
|
||||
BOOST_CHECK(var3 <= a);
|
||||
BOOST_CHECK(var1 <= b);
|
||||
BOOST_CHECK(c <= var1);
|
||||
BOOST_CHECK(!(d <= var2));
|
||||
|
||||
BOOST_CHECK(var4 <= a);
|
||||
BOOST_CHECK(var2 <= b);
|
||||
BOOST_CHECK(c <= var2);
|
||||
BOOST_CHECK(!(d <= var1));
|
||||
|
||||
BOOST_CHECK(var3 <= b);
|
||||
BOOST_CHECK(var4 <= b);
|
||||
BOOST_CHECK(c <= var1);
|
||||
BOOST_CHECK(!(d <= var3));
|
||||
|
||||
BOOST_CHECK(var3 <= a);
|
||||
BOOST_CHECK(var2 <= a);
|
||||
BOOST_CHECK(c <= var4);
|
||||
BOOST_CHECK(!(d <= var1));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(greater_than_or_equal_rvar_and_rvar, T, all_float_types)
|
||||
{
|
||||
T a = T(3.14);
|
||||
T b = T(3.14);
|
||||
T c = T(2.71);
|
||||
|
||||
rvar<T, 1> var1 = rvar<T, 1>(a);
|
||||
rvar<T, 2> var2 = rvar<T, 2>(b);
|
||||
rvar<T, 3> var3 = rvar<T, 3>(c);
|
||||
rvar<T, 4> var4 = rvar<T, 4>(c);
|
||||
|
||||
BOOST_CHECK(var1 >= var3);
|
||||
BOOST_CHECK(var1 >= var2);
|
||||
BOOST_CHECK(!(var3 >= var1));
|
||||
BOOST_CHECK(var3 >= var4);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(greater_than_or_equal_rvar_and_scalar, T, all_float_types)
|
||||
{
|
||||
T a = T(3.14);
|
||||
T b = T(3.14);
|
||||
T c = T(2.71);
|
||||
|
||||
rvar<T, 1> var1 = rvar<T, 1>(a);
|
||||
rvar<T, 2> var2 = rvar<T, 2>(a);
|
||||
rvar<T, 3> var3 = rvar<T, 3>(c);
|
||||
rvar<T, 4> var4 = rvar<T, 4>(c);
|
||||
|
||||
BOOST_CHECK(var1 >= c);
|
||||
BOOST_CHECK(var1 >= b);
|
||||
BOOST_CHECK(b >= var3);
|
||||
BOOST_CHECK(c >= var4);
|
||||
|
||||
BOOST_CHECK(var2 >= c);
|
||||
BOOST_CHECK(var2 >= b);
|
||||
BOOST_CHECK(b >= var4);
|
||||
BOOST_CHECK(c >= var3);
|
||||
|
||||
BOOST_CHECK(var1 >= c);
|
||||
BOOST_CHECK(var1 >= b);
|
||||
BOOST_CHECK(a >= var4);
|
||||
BOOST_CHECK(c >= var3);
|
||||
|
||||
BOOST_CHECK(var2 >= c);
|
||||
BOOST_CHECK(var2 >= b);
|
||||
BOOST_CHECK(a >= var4);
|
||||
BOOST_CHECK(c >= var3);
|
||||
}
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
288
test/test_reverse_mode_autodiff_constructors.cpp
Normal file
@@ -0,0 +1,288 @@
|
||||
// Copyright Maksym Zhelyenzyakov 2025-2026.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "test_autodiff_reverse.hpp"
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(explicit_rvar_constructors)
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(rvar_constructors_and_utils, T, all_float_types)
|
||||
{
|
||||
RandomSample<T> rng{-100, 100};
|
||||
/* raw constructors */
|
||||
T x_value = rng.next();
|
||||
rdiff::rvar<T, 1> x0(x_value);
|
||||
rdiff::rvar<T, 2> x1(x_value);
|
||||
rdiff::rvar<T, 3> x2(x_value);
|
||||
|
||||
/* raw eval */
|
||||
BOOST_CHECK_EQUAL(x_value, x0.evaluate());
|
||||
BOOST_CHECK_EQUAL(x_value, x1.evaluate().evaluate());
|
||||
BOOST_CHECK_EQUAL(x_value, x2.evaluate().evaluate().evaluate());
|
||||
|
||||
/* get item helper */
|
||||
BOOST_CHECK_EQUAL(x_value, x0.item());
|
||||
BOOST_CHECK_EQUAL(x_value, x1.item());
|
||||
BOOST_CHECK_EQUAL(x_value, x2.item());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(make_rvar_constructors, T, all_float_types)
|
||||
{
|
||||
RandomSample<T> rng{-100, 100};
|
||||
T x_value = rng.next();
|
||||
|
||||
rdiff::rvar<T,1> x1 = rdiff::make_rvar<T,1>(x_value);
|
||||
rdiff::rvar<T,2> x2 = rdiff::make_rvar<T,2>(x_value);
|
||||
rdiff::rvar<T,3> x3 = rdiff::make_rvar<T,3>(x_value);
|
||||
BOOST_CHECK_EQUAL(x_value, x1.item());
|
||||
BOOST_CHECK_EQUAL(x_value, x2.item());
|
||||
BOOST_CHECK_EQUAL(x_value, x3.item());
|
||||
|
||||
T result = x_value+x_value;
|
||||
auto z1 = x1+x1;
|
||||
auto z2 = x2+x2;
|
||||
auto z3 = x3+x3;
|
||||
|
||||
rdiff::rvar<T,1> y1 = rdiff::make_rvar(z1);
|
||||
rdiff::rvar<T,2> y2 = rdiff::make_rvar(z2);
|
||||
rdiff::rvar<T,3> y3 = rdiff::make_rvar(z3);
|
||||
|
||||
BOOST_CHECK_EQUAL(y1.item(),result);
|
||||
BOOST_CHECK_EQUAL(y2.item(),result);
|
||||
BOOST_CHECK_EQUAL(y3.item(),result);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(copy_construction, T, all_float_types)
|
||||
{
|
||||
RandomSample<T> rng{-100, 100};
|
||||
T original_value_1 = rng.next();
|
||||
|
||||
/* copy constructor checks */
|
||||
rdiff::rvar<T, 1> original_rvar_1(original_value_1);
|
||||
rdiff::rvar<T, 1> copied_rvar_1 = original_rvar_1;
|
||||
|
||||
BOOST_CHECK_EQUAL(copied_rvar_1.item(), original_value_1);
|
||||
|
||||
/* change original, copy stays same */
|
||||
T new_value_1 = rng.next();
|
||||
original_rvar_1 = new_value_1;
|
||||
BOOST_CHECK_EQUAL(copied_rvar_1.item(), original_value_1);
|
||||
BOOST_CHECK_EQUAL(original_rvar_1.item(), new_value_1);
|
||||
|
||||
T original_value_2 = rng.next();
|
||||
rdiff::rvar<T, 2> original_rvar_2(original_value_2);
|
||||
rdiff::rvar<T, 2> copied_rvar_2 = original_rvar_2;
|
||||
|
||||
BOOST_CHECK_EQUAL(copied_rvar_2.item(), original_value_2);
|
||||
|
||||
T new_value_2 = rng.next();
|
||||
original_rvar_2 = new_value_2;
|
||||
BOOST_CHECK_EQUAL(copied_rvar_2.item(), original_value_2);
|
||||
BOOST_CHECK_EQUAL(original_rvar_2.item(), new_value_2);
|
||||
|
||||
T original_value_3 = rng.next();
|
||||
rdiff::rvar<T, 3> original_rvar_3(original_value_3);
|
||||
rdiff::rvar<T, 3> copied_rvar_3 = original_rvar_3;
|
||||
|
||||
BOOST_CHECK_EQUAL(copied_rvar_3.item(), original_value_3);
|
||||
|
||||
T new_value_3 = rng.next();
|
||||
original_rvar_3 = new_value_3;
|
||||
BOOST_CHECK_EQUAL(copied_rvar_3.item(), original_value_3);
|
||||
BOOST_CHECK_EQUAL(original_rvar_3.item(), new_value_3);
|
||||
}
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(copy_assignment, T, all_float_types)
|
||||
{
|
||||
RandomSample<T> rng{-100, 100};
|
||||
T source_value_1 = rng.next();
|
||||
T initial_target_value_1 = rng.next();
|
||||
rdiff::rvar<T, 1> source_rvar_1(source_value_1);
|
||||
rdiff::rvar<T, 1> target_rvar_1(initial_target_value_1);
|
||||
|
||||
target_rvar_1 = source_rvar_1;
|
||||
|
||||
BOOST_CHECK_EQUAL(target_rvar_1.item(), source_value_1);
|
||||
|
||||
T new_source_value_1 = rng.next();
|
||||
source_rvar_1 = new_source_value_1;
|
||||
BOOST_CHECK_EQUAL(target_rvar_1.item(), source_value_1);
|
||||
BOOST_CHECK_EQUAL(source_rvar_1.item(), new_source_value_1);
|
||||
|
||||
rdiff::rvar<T, 1> self_assign_rvar_1(rng.next());
|
||||
T value_before_self_assign_1 = self_assign_rvar_1.item();
|
||||
self_assign_rvar_1 = self_assign_rvar_1;
|
||||
BOOST_CHECK_EQUAL(self_assign_rvar_1.item(), value_before_self_assign_1);
|
||||
|
||||
T source_value_2 = rng.next();
|
||||
T initial_target_value_2 = rng.next();
|
||||
rdiff::rvar<T, 2> source_rvar_2(source_value_2);
|
||||
rdiff::rvar<T, 2> target_rvar_2(initial_target_value_2);
|
||||
|
||||
target_rvar_2 = source_rvar_2;
|
||||
|
||||
BOOST_CHECK_EQUAL(target_rvar_2.item(), source_value_2);
|
||||
|
||||
T new_source_value_2 = rng.next();
|
||||
source_rvar_2 = new_source_value_2;
|
||||
BOOST_CHECK_EQUAL(target_rvar_2.item(), source_value_2);
|
||||
BOOST_CHECK_EQUAL(source_rvar_2.item(), new_source_value_2);
|
||||
|
||||
T source_value_3 = rng.next();
|
||||
T initial_target_value_3 = rng.next();
|
||||
rdiff::rvar<T, 3> source_rvar_3(source_value_3);
|
||||
rdiff::rvar<T, 3> target_rvar_3(initial_target_value_3);
|
||||
|
||||
target_rvar_3 = source_rvar_3;
|
||||
|
||||
BOOST_CHECK_EQUAL(target_rvar_3.item(), source_value_3);
|
||||
|
||||
T new_source_value_3 = rng.next();
|
||||
source_rvar_3 = new_source_value_3;
|
||||
BOOST_CHECK_EQUAL(target_rvar_3.item(), source_value_3);
|
||||
BOOST_CHECK_EQUAL(source_rvar_3.item(), new_source_value_3);
|
||||
}
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(inplace_addition, T, all_float_types)
|
||||
{
|
||||
using namespace rdiff;
|
||||
RandomSample<T> rng{-1, 1};
|
||||
T x1_v = rng.next();
|
||||
T x2_v = rng.next();
|
||||
T expected = x1_v + x2_v;
|
||||
|
||||
rvar<T, 1> x1 = x1_v;
|
||||
rvar<T, 1> x2 = x2_v;
|
||||
|
||||
rvar<T, 1> orig_x1 = x1;
|
||||
rvar<T, 1> orig_x2 = x2;
|
||||
|
||||
x1 += x2;
|
||||
|
||||
BOOST_REQUIRE_EQUAL(x1.item(), expected);
|
||||
|
||||
gradient_tape<T, 1>& tape = get_active_tape<T, 1>();
|
||||
tape.zero_grad();
|
||||
x1.backward();
|
||||
|
||||
BOOST_REQUIRE_CLOSE(orig_x1.adjoint(), 1.0, 300 * std::numeric_limits<T>::epsilon());
|
||||
BOOST_REQUIRE_CLOSE(orig_x2.adjoint(), 1.0, 300 * std::numeric_limits<T>::epsilon());
|
||||
tape.clear();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(inplace_subtraction, T, all_float_types)
|
||||
{
|
||||
using namespace rdiff;
|
||||
RandomSample<T> rng{-1, 1};
|
||||
T x1_v = rng.next();
|
||||
T x2_v = rng.next();
|
||||
T expected = x1_v - x2_v;
|
||||
|
||||
rvar<T, 1> x1 = x1_v;
|
||||
rvar<T, 1> x2 = x2_v;
|
||||
|
||||
rvar<T, 1> orig_x1 = x1;
|
||||
rvar<T, 1> orig_x2 = x2;
|
||||
|
||||
x1 -= x2;
|
||||
|
||||
BOOST_REQUIRE_EQUAL(x1.item(), expected);
|
||||
|
||||
gradient_tape<T, 1>& tape = get_active_tape<T, 1>();
|
||||
tape.zero_grad();
|
||||
x1.backward();
|
||||
|
||||
BOOST_REQUIRE_CLOSE(orig_x1.adjoint(), 1.0, 300 * std::numeric_limits<T>::epsilon());
|
||||
BOOST_REQUIRE_CLOSE(orig_x2.adjoint(), -1.0, 300 * std::numeric_limits<T>::epsilon());
|
||||
tape.clear();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(inplace_multiplication, T, all_float_types)
|
||||
{
|
||||
using namespace rdiff;
|
||||
RandomSample<T> rng{-1, 1};
|
||||
T x1_v = rng.next();
|
||||
T x2_v = rng.next();
|
||||
T expected = x1_v * x2_v;
|
||||
|
||||
rvar<T, 1> x1 = x1_v;
|
||||
rvar<T, 1> x2 = x2_v;
|
||||
|
||||
rvar<T, 1> orig_x1 = x1;
|
||||
rvar<T, 1> orig_x2 = x2;
|
||||
|
||||
x1 *= x2;
|
||||
|
||||
BOOST_REQUIRE_EQUAL(x1.item(), expected);
|
||||
|
||||
gradient_tape<T, 1>& tape = get_active_tape<T, 1>();
|
||||
tape.zero_grad();
|
||||
x1.backward();
|
||||
|
||||
BOOST_REQUIRE_CLOSE(orig_x1.adjoint(), x2_v, 300 * std::numeric_limits<T>::epsilon());
|
||||
BOOST_REQUIRE_CLOSE(orig_x2.adjoint(), x1_v, 300 * std::numeric_limits<T>::epsilon());
|
||||
tape.clear();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(inplace_division, T, all_float_types)
|
||||
{
|
||||
using namespace rdiff;
|
||||
RandomSample<T> rng{-1, 1};
|
||||
T x1_v = rng.next();
|
||||
T x2_v = rng.next() + 1e-2;
|
||||
|
||||
T expected = x1_v / x2_v;
|
||||
|
||||
rvar<T, 1> x1 = x1_v;
|
||||
rvar<T, 1> x2 = x2_v;
|
||||
|
||||
rvar<T, 1> orig_x1 = x1;
|
||||
rvar<T, 1> orig_x2 = x2;
|
||||
|
||||
x1 /= x2;
|
||||
|
||||
BOOST_REQUIRE_EQUAL(x1.item(), expected);
|
||||
|
||||
gradient_tape<T, 1>& tape = get_active_tape<T, 1>();
|
||||
tape.zero_grad();
|
||||
x1.backward();
|
||||
|
||||
BOOST_REQUIRE_CLOSE(orig_x1.adjoint(), 1.0 / x2_v, 300 * std::numeric_limits<T>::epsilon());
|
||||
BOOST_REQUIRE_CLOSE(orig_x2.adjoint(),
|
||||
-x1_v / (x2_v * x2_v),
|
||||
300 * std::numeric_limits<T>::epsilon());
|
||||
tape.clear();
|
||||
}
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(test_rvar_ostream_output, T, all_float_types)
|
||||
{
|
||||
using namespace rdiff;
|
||||
rvar<T, 1> x = T{2.0};
|
||||
rvar<T, 1> y = T{3.0};
|
||||
|
||||
rvar<T, 1> z = x * y;
|
||||
z.backward();
|
||||
|
||||
std::ostringstream oss_x, oss_y, oss_z;
|
||||
oss_x << x;
|
||||
oss_y << y;
|
||||
oss_z << z;
|
||||
|
||||
BOOST_CHECK_EQUAL(oss_x.str(), "rvar<1>(2,3)");
|
||||
BOOST_CHECK_EQUAL(oss_y.str(), "rvar<1>(3,2)");
|
||||
BOOST_CHECK_EQUAL(oss_z.str(), "rvar<1>(6,1)");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(boost_math_tools_numeric_traits, T, all_float_types)
|
||||
{
|
||||
using namespace rdiff;
|
||||
using rvar_t = rvar<T, 1>;
|
||||
|
||||
BOOST_CHECK_EQUAL(boost::math::tools::digits<rvar_t>(), boost::math::tools::digits<T>());
|
||||
BOOST_CHECK_EQUAL(boost::math::tools::max_value<rvar_t>(), boost::math::tools::max_value<T>());
|
||||
BOOST_CHECK_EQUAL(boost::math::tools::min_value<rvar_t>(), boost::math::tools::min_value<T>());
|
||||
BOOST_CHECK_EQUAL(boost::math::tools::log_max_value<rvar_t>(),
|
||||
boost::math::tools::log_max_value<T>());
|
||||
BOOST_CHECK_EQUAL(boost::math::tools::log_min_value<rvar_t>(),
|
||||
boost::math::tools::log_min_value<T>());
|
||||
BOOST_CHECK_EQUAL(boost::math::tools::epsilon<rvar_t>(), boost::math::tools::epsilon<T>());
|
||||
}
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
114
test/test_reverse_mode_autodiff_error_functions.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
// Copyright Maksym Zhelyenzyakov 2025-2026.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
#include "test_autodiff_reverse.hpp"
|
||||
#include <boost/math/tools/test_value.hpp>
|
||||
#include <boost/utility/identity_type.hpp>
|
||||
#include <cmath>
|
||||
BOOST_AUTO_TEST_SUITE(erf_support)
|
||||
|
||||
using namespace rdiff;
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(test_erf, T, all_float_types)
|
||||
{
|
||||
std::array<T, 5> answers{
|
||||
{BOOST_MATH_TEST_VALUE(
|
||||
T, 0.99997790950300141455862722387041767962015229291260075034275901649691099714918),
|
||||
BOOST_MATH_TEST_VALUE(T, 0.0001392530519467478538904180310183558561119961779459213),
|
||||
BOOST_MATH_TEST_VALUE(T, -0.0008355183116804871233425081861101351366719770676755276),
|
||||
BOOST_MATH_TEST_VALUE(T, 0.004734603766189427032274213054624099107807870050161323),
|
||||
BOOST_MATH_TEST_VALUE(T, -0.02506554935041461370027524558330405410015931203026583)}};
|
||||
const T eps = 0.01;
|
||||
constexpr int ord = 4;
|
||||
T cx = 3.0;
|
||||
|
||||
rvar<T, ord> x = cx;
|
||||
rvar<T, ord> y = rdiff::erf(x);
|
||||
auto g1 = grad_nd<1>(y, &x);
|
||||
auto g2 = grad_nd<2>(y, &x);
|
||||
auto g3 = grad_nd<3>(y, &x);
|
||||
auto g4 = grad_nd<4>(y, &x);
|
||||
|
||||
BOOST_CHECK_CLOSE(y.item(), answers[0], eps);
|
||||
BOOST_CHECK_CLOSE(g1[0], answers[1], eps);
|
||||
BOOST_CHECK_CLOSE(g2[0][0], answers[2], eps);
|
||||
BOOST_CHECK_CLOSE(g3[0][0][0], answers[3], eps);
|
||||
BOOST_CHECK_CLOSE(g4[0][0][0][0], answers[4], eps);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(test_erfc, T, all_float_types)
|
||||
{
|
||||
std::array<T, 5> answers{
|
||||
{BOOST_MATH_TEST_VALUE(T, 0.00002209049699858544137277612958232037984770708739924966),
|
||||
BOOST_MATH_TEST_VALUE(T, -0.0001392530519467478538904180310183558561119961779459213),
|
||||
BOOST_MATH_TEST_VALUE(T, 0.0008355183116804871233425081861101351366719770676755276),
|
||||
BOOST_MATH_TEST_VALUE(T, -0.004734603766189427032274213054624099107807870050161323),
|
||||
BOOST_MATH_TEST_VALUE(T, 0.02506554935041461370027524558330405410015931203026583)}};
|
||||
const T eps = 0.01;
|
||||
constexpr int ord = 4;
|
||||
T cx = 3.0;
|
||||
|
||||
rvar<T, ord> x = cx;
|
||||
rvar<T, ord> y = rdiff::erfc(x);
|
||||
auto g1 = grad_nd<1>(y, &x);
|
||||
auto g2 = grad_nd<2>(y, &x);
|
||||
auto g3 = grad_nd<3>(y, &x);
|
||||
auto g4 = grad_nd<4>(y, &x);
|
||||
|
||||
BOOST_CHECK_CLOSE(y.item(), answers[0], eps);
|
||||
BOOST_CHECK_CLOSE(g1[0], answers[1], eps);
|
||||
BOOST_CHECK_CLOSE(g2[0][0], answers[2], eps);
|
||||
BOOST_CHECK_CLOSE(g3[0][0][0], answers[3], eps);
|
||||
BOOST_CHECK_CLOSE(g4[0][0][0][0], answers[4], eps);
|
||||
}
|
||||
|
||||
using namespace rdiff;
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(test_erf_inv, T, all_float_types)
|
||||
{
|
||||
std::array<T, 5> answers{{BOOST_MATH_TEST_VALUE(T, 0.4769362762044699),
|
||||
BOOST_MATH_TEST_VALUE(T, 1.11258481897195),
|
||||
BOOST_MATH_TEST_VALUE(T, 1.1807463499934),
|
||||
BOOST_MATH_TEST_VALUE(T, 5.26058253926122),
|
||||
BOOST_MATH_TEST_VALUE(T, 28.44125004446708)}};
|
||||
const T eps = 0.01;
|
||||
constexpr int ord = 4;
|
||||
T cx = 0.5;
|
||||
|
||||
rvar<T, ord> x = cx;
|
||||
rvar<T, ord> y = rdiff::erf_inv(x);
|
||||
auto g1 = grad_nd<1>(y, &x);
|
||||
auto g2 = grad_nd<2>(y, &x);
|
||||
auto g3 = grad_nd<3>(y, &x);
|
||||
auto g4 = grad_nd<4>(y, &x);
|
||||
|
||||
BOOST_CHECK_CLOSE(y.item(), answers[0], eps);
|
||||
BOOST_CHECK_CLOSE(g1[0], answers[1], eps);
|
||||
BOOST_CHECK_CLOSE(g2[0][0], answers[2], eps);
|
||||
BOOST_CHECK_CLOSE(g3[0][0][0], answers[3], eps);
|
||||
BOOST_CHECK_CLOSE(g4[0][0][0][0], answers[4], eps);
|
||||
}
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(test_erfc_inv, T, all_float_types)
|
||||
{
|
||||
std::array<T, 5> answers{{BOOST_MATH_TEST_VALUE(T, 0.4769362762044699),
|
||||
BOOST_MATH_TEST_VALUE(T, -1.11258481897195),
|
||||
BOOST_MATH_TEST_VALUE(T, 1.1807463499934),
|
||||
BOOST_MATH_TEST_VALUE(T, -5.260582539261222),
|
||||
BOOST_MATH_TEST_VALUE(T, 28.44125004446708)}};
|
||||
const T eps = 0.01;
|
||||
constexpr int ord = 4;
|
||||
T cx = 0.5;
|
||||
|
||||
rvar<T, ord> x = cx;
|
||||
rvar<T, ord> y = rdiff::erfc_inv(x);
|
||||
auto g1 = grad_nd<1>(y, &x);
|
||||
auto g2 = grad_nd<2>(y, &x);
|
||||
auto g3 = grad_nd<3>(y, &x);
|
||||
auto g4 = grad_nd<4>(y, &x);
|
||||
|
||||
BOOST_CHECK_CLOSE(y.item(), answers[0], eps);
|
||||
BOOST_CHECK_CLOSE(g1[0], answers[1], eps);
|
||||
BOOST_CHECK_CLOSE(g2[0][0], answers[2], eps);
|
||||
BOOST_CHECK_CLOSE(g3[0][0][0], answers[3], eps);
|
||||
BOOST_CHECK_CLOSE(g4[0][0][0][0], answers[4], eps);
|
||||
}
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
118
test/test_reverse_mode_autodiff_flat_linear_allocator.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
// Copyright Maksym Zhelyenzyakov 2025-2026.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
#include "test_autodiff_reverse.hpp"
|
||||
#include <vector>
|
||||
BOOST_AUTO_TEST_SUITE(test_flat_linear_allocator)
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(flat_linear_allocator_constructors, T, all_float_types)
|
||||
{
|
||||
size_t buffer_size = 16;
|
||||
RandomSample<T> rng{-1, 1};
|
||||
rdiff_detail::flat_linear_allocator<T, 16> float_allocator{};
|
||||
for (size_t i = 0; i < 2 * buffer_size - buffer_size / 2; i++) {
|
||||
float_allocator.emplace_back(rng.next());
|
||||
}
|
||||
|
||||
BOOST_CHECK_EQUAL(float_allocator.size(),
|
||||
static_cast<size_t>(2 * buffer_size - buffer_size / 2));
|
||||
BOOST_CHECK_EQUAL(float_allocator.capacity(), static_cast<size_t>(2 * buffer_size));
|
||||
|
||||
float_allocator.clear();
|
||||
BOOST_CHECK_EQUAL(float_allocator.size(), static_cast<size_t>(0));
|
||||
BOOST_CHECK_EQUAL(float_allocator.capacity(), buffer_size);
|
||||
|
||||
for (size_t i = 0; i < 2 * buffer_size - buffer_size / 2; i++) {
|
||||
float_allocator.emplace_back(rng.next());
|
||||
}
|
||||
float_allocator.reset();
|
||||
BOOST_CHECK_EQUAL(float_allocator.size(), static_cast<size_t>(0));
|
||||
BOOST_CHECK_EQUAL(float_allocator.capacity(), 2 * buffer_size);
|
||||
|
||||
for (size_t i = 0; i < 2 * buffer_size - buffer_size / 2; i++) {
|
||||
float_allocator.emplace_back(rng.next());
|
||||
}
|
||||
T fill_value = T(0.25);
|
||||
float_allocator.fill(fill_value);
|
||||
for (size_t i = 0; i < float_allocator.size(); i++) {
|
||||
BOOST_CHECK_EQUAL(float_allocator[i], fill_value);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(flat_linear_allocator_test_emplace, T, all_float_types)
|
||||
{
|
||||
size_t buffer_size = 16;
|
||||
RandomSample<T> rng{-1, 1};
|
||||
rdiff_detail::flat_linear_allocator<T, 16> float_allocator{};
|
||||
std::vector<T> test_vector;
|
||||
|
||||
for (size_t i = 0; i < 2 * buffer_size - 1; i++) {
|
||||
test_vector.push_back(rng.next());
|
||||
float_allocator.emplace_back(test_vector[i]);
|
||||
}
|
||||
|
||||
auto it1 = float_allocator.template emplace_back_n<4>();
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
T literal = rng.next();
|
||||
test_vector.push_back(literal);
|
||||
*(it1 + i) = literal;
|
||||
}
|
||||
|
||||
auto it2 = float_allocator.emplace_back_n(buffer_size);
|
||||
for (size_t i = 0; i < buffer_size; i++) {
|
||||
T literal = rng.next();
|
||||
test_vector.push_back(literal);
|
||||
*(it2 + i) = literal;
|
||||
}
|
||||
|
||||
auto vit = test_vector.begin();
|
||||
auto alloc_it = float_allocator.begin();
|
||||
for (; vit != test_vector.end(); vit++, alloc_it++) {
|
||||
BOOST_CHECK_EQUAL(
|
||||
*vit,
|
||||
*alloc_it); // should be ok to check floats like this since they are expected to be the same.
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < test_vector.size(); i++) {
|
||||
BOOST_CHECK_EQUAL(test_vector[i], float_allocator[i]); // check random access aswell;
|
||||
}
|
||||
|
||||
BOOST_CHECK_EQUAL(test_vector.size(), float_allocator.size());
|
||||
}
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(flat_linear_allocator_test_checkpointing, T, all_float_types)
|
||||
{
|
||||
size_t buffer_size = 16;
|
||||
RandomSample<T> rng{-1, 1};
|
||||
rdiff_detail::flat_linear_allocator<T, 16> float_allocator{};
|
||||
std::vector<T> test_vector;
|
||||
std::vector<size_t> checkpoint_indices{2, 11, 15, 16, 17, 28};
|
||||
std::vector<T> expected_value_at_checkpoint;
|
||||
|
||||
size_t ckp_id = 0;
|
||||
for (size_t i = 0; i < 2 * buffer_size; i++) {
|
||||
T literal = rng.next();
|
||||
float_allocator.emplace_back(literal);
|
||||
if (ckp_id < checkpoint_indices.size() && i == checkpoint_indices[ckp_id]) {
|
||||
float_allocator.add_checkpoint();
|
||||
expected_value_at_checkpoint.push_back(literal);
|
||||
++ckp_id;
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < checkpoint_indices.size(); i++) {
|
||||
auto it = float_allocator.checkpoint_at(i);
|
||||
BOOST_CHECK_EQUAL(*it, expected_value_at_checkpoint[i]);
|
||||
}
|
||||
|
||||
auto first_ckp = float_allocator.first_checkpoint();
|
||||
auto last_ckp = float_allocator.last_checkpoint();
|
||||
|
||||
BOOST_CHECK_EQUAL(*first_ckp, expected_value_at_checkpoint[0]);
|
||||
BOOST_CHECK_EQUAL(*last_ckp, expected_value_at_checkpoint.back());
|
||||
|
||||
float_allocator.rewind_to_last_checkpoint();
|
||||
BOOST_CHECK_EQUAL(float_allocator.size(), checkpoint_indices.back());
|
||||
BOOST_CHECK_EQUAL(float_allocator.capacity(), 2 * buffer_size);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||