Automatic Differentiation
 
Loading...
Searching...
No Matches
integrate_1d_adjoint.hpp
Go to the documentation of this file.
1#ifndef STAN_MATH_REV_FUNCTOR_INTEGRATE_1D_ADJOINT_HPP
2#define STAN_MATH_REV_FUNCTOR_INTEGRATE_1D_ADJOINT_HPP
3
13#include <cstddef>
14#include <ostream>
15#include <tuple>
16#include <utility>
17
18namespace stan {
19namespace math {
20namespace internal {
21
55template <typename F, typename T_a, typename T_b, typename Integrator,
56 typename... Args>
57inline return_type_t<T_a, T_b, Args...> integrate_1d_adjoint(
58 const char* function, const F& f, const T_a& a, const T_b& b,
59 Integrator&& integrator, std::ostream* msgs, const Args&... args) {
60 const double a_val = value_of(a);
61 const double b_val = value_of(b);
62 if (unlikely(a_val == b_val)) {
63 if (is_inf(a_val)) {
64 throw_domain_error(function, "Integration endpoints are both", a_val, "",
65 "");
66 }
67 return var(0.0);
68 }
69 auto args_val_tuple = std::make_tuple(value_of(args)...);
70 auto eval_f = [&](const auto& x, const auto& xc) {
71 return stan::math::apply(
72 [&](auto&&... val_args) { return f(x, xc, msgs, val_args...); },
73 args_val_tuple);
74 };
75
76 const double integral = integrator(eval_f);
77 var ret(integral);
78 if constexpr (is_var_v<T_a>) {
79 double partial_a = 0.0;
80 if (!is_inf(a_val)) {
81 partial_a = -eval_f(a_val, 0.0);
82 }
84 [ret, a, partial_a]() { a.adj() += partial_a * ret.adj(); });
85 }
86 if constexpr (is_var_v<T_b>) {
87 double partial_b = 0.0;
88 if (!is_inf(b_val)) {
89 partial_b = eval_f(b_val, 0.0);
90 }
92 [ret, b, partial_b]() { b.adj() += partial_b * ret.adj(); });
93 }
94
95 // Argument adjoints.
96 if constexpr (is_any_var_scalar_v<Args...>) {
97 auto args_adj = make_zeroed_arena(std::forward_as_tuple(args...));
98 {
99 nested_rev_autodiff argument_nest;
100 auto args_copy = deep_copy_vargs<var>(std::forward_as_tuple(args...));
101 auto args_copy_filter = filter_var_scalar_types(args_copy);
102 auto integrate_grad = [&](auto&& target) -> double {
103 return integrator([&](const auto& x, const auto& xc) {
104 argument_nest.set_zero_all_adjoints();
105 nested_rev_autodiff gradient_nest;
107 [&](auto&&... local_args) {
108 return f(x, xc, msgs, local_args...);
109 },
110 args_copy);
111 fx.grad();
112 double gradient = target.adj();
113 if (is_nan(gradient) && fx.val() == 0) {
114 gradient = 0.0;
115 }
116 return gradient;
117 });
118 };
119 std::size_t param_index = 0;
120 auto assign_grad = [&](auto&& adj, auto&& target) {
121 adj = integrate_grad(target);
122 if (unlikely(is_nan(adj))) {
123 throw_domain_error("gradient_of_f", "The gradient of f", param_index,
124 "is nan for parameter ", "");
125 }
126 ++param_index;
127 };
129 [&](auto&& adj_leaf, auto&& var_leaf) {
130 using leaf_t = std::decay_t<decltype(var_leaf)>;
131 if constexpr (is_std_vector_v<leaf_t>) {
132 for (size_t j = 0; j < var_leaf.size(); ++j) {
133 assign_grad(adj_leaf[j], var_leaf[j]);
134 }
135 } else if constexpr (is_eigen_v<leaf_t>) {
136 for (Eigen::Index j = 0; j < var_leaf.size(); ++j) {
137 assign_grad(adj_leaf.coeffRef(j), var_leaf.coeff(j));
138 }
139 } else { // scalar var leaf
140 assign_grad(adj_leaf, var_leaf);
141 }
142 },
143 args_adj, args_copy_filter);
144 }
145 auto args_filter = filter_var_scalar_types(std::forward_as_tuple(args...));
146 reverse_pass_collect_adjoints(ret, args_filter, std::move(args_adj));
147 }
148 return ret;
149}
150
151} // namespace internal
152} // namespace math
153} // namespace stan
154
155#endif
void set_zero_all_adjoints()
Reset all adjoint values in this nested stack to zero.
A class following the RAII idiom to start and recover nested autodiff scopes.
#define unlikely(x)
typename return_type< Ts... >::type return_type_t
Convenience type for the return type of the specified template parameters.
return_type_t< T_a, T_b, Args... > integrate_1d_adjoint(const char *function, const F &f, const T_a &a, const T_b &b, Integrator &&integrator, std::ostream *msgs, const Args &... args)
Build the reverse-mode result of a one-dimensional adaptive quadrature.
constexpr decltype(auto) filter_var_scalar_types(T &&t)
Filter a tuple and return a tuple with references to the types with a var scalar type.
void reverse_pass_collect_adjoints(var ret, Output &&output, Input &&input)
Collects adjoints from a tuple or std::vector of tuples.
constexpr auto make_zeroed_arena(Input &&input)
Creates an arena type that is the same type as the input and initialized with zeros.
void iter_tuple_nested(F &&f, Types &&... args)
Iterate and nest into a tuple or std::vector to apply f to each matrix or scalar type.
bool is_nan(T &&x)
Returns 1 if the input's value is NaN and 0 otherwise.
Definition is_nan.hpp:22
void gradient(const F &f, const Eigen::Matrix< T, Eigen::Dynamic, 1 > &x, T &fx, Eigen::Matrix< T, Eigen::Dynamic, 1 > &grad_fx)
Calculate the value and the gradient of the specified function at the specified argument.
Definition gradient.hpp:40
void reverse_pass_callback(F &&functor)
Puts a callback on the autodiff stack to be called in reverse pass.
T value_of(const fvar< T > &v)
Return the value of the specified variable.
Definition value_of.hpp:18
void throw_domain_error(const char *function, const char *name, const T &y, const char *msg1, const char *msg2)
Throw a domain error with a consistently formatted message.
var_value< double > var
Definition var.hpp:1187
int is_inf(const fvar< T > &x)
Returns 1 if the input's value is infinite and 0 otherwise.
Definition is_inf.hpp:21
constexpr decltype(auto) apply(F &&f, Tuple &&t, PreArgs &&... pre_args)
Definition apply.hpp:51
constexpr bool is_any_var_scalar_v
Definition is_var.hpp:50
The lgamma implementation in stan-math is based on either the reentrant safe lgamma_r implementation ...