File indexing completed on 2026-08-17 08:48:09
0001
0002
0003
0004
0005
0006 #ifndef REVERSE_MODE_AUTODIFF_EXPRESSION_TEMPLATE_BASE_HPP
0007 #define REVERSE_MODE_AUTODIFF_EXPRESSION_TEMPLATE_BASE_HPP
0008 #include <cstddef>
0009 #include <type_traits>
0010 namespace boost {
0011 namespace math {
0012 namespace differentiation {
0013 namespace reverse_mode {
0014
0015
0016 struct expression_base
0017 {};
0018
0019 template<typename RealType, size_t DerivativeOrder, class DerivedExpression>
0020 struct expression;
0021
0022 template<typename RealType, size_t DerivativeOrder>
0023 class rvar;
0024
0025 template<typename RealType,
0026 size_t DerivativeOrder,
0027 typename LHS,
0028 typename RHS,
0029 typename ConcreteBinaryOperation>
0030 struct abstract_binary_expression;
0031
0032 template<typename RealType, size_t DerivativeOrder, typename ARG, typename ConcreteUnaryOperation>
0033
0034 struct abstract_unary_expression;
0035
0036 template<typename RealType, size_t DerivativeOrder>
0037 class gradient_node;
0038
0039 namespace detail {
0040 template<typename...>
0041 using void_t = void;
0042
0043 template<typename T, typename Enable = void>
0044 struct has_value_type : std::false_type
0045 {};
0046 template<typename T>
0047 struct has_value_type<T, void_t<typename T::value_type>> : std::true_type
0048 {};
0049 template<typename T, typename Enable = void>
0050 struct has_binary_sub_types : std::false_type
0051 {};
0052 template<typename T>
0053 struct has_binary_sub_types<T, void_t<typename T::lhs_type, typename T::rhs_type>> : std::true_type
0054 {};
0055 template<typename T, typename Enable = void>
0056 struct has_unary_sub_type : std::false_type
0057 {};
0058 template<typename T>
0059 struct has_unary_sub_type<T, void_t<typename T::arg_type>> : std::true_type
0060 {};
0061
0062 template<typename T, size_t order, typename Enable = void>
0063 struct count_rvar_impl
0064 {
0065 static constexpr std::size_t value = 0;
0066 };
0067 template<typename RealType, size_t DerivativeOrder>
0068 struct count_rvar_impl<rvar<RealType, DerivativeOrder>, DerivativeOrder>
0069 {
0070 static constexpr std::size_t value = 1;
0071 };
0072
0073 template<typename RealType, std::size_t DerivativeOrder>
0074 struct count_rvar_impl<
0075 RealType,
0076 DerivativeOrder,
0077 std::enable_if_t<has_binary_sub_types<RealType>::value
0078 && !std::is_same<RealType, rvar<typename RealType::value_type, DerivativeOrder>>::value
0079 && !has_unary_sub_type<RealType>::value>>
0080 {
0081 static constexpr std::size_t value
0082 = count_rvar_impl<typename RealType::lhs_type, DerivativeOrder>::value
0083 + count_rvar_impl<typename RealType::rhs_type, DerivativeOrder>::value;
0084 };
0085
0086 template<typename RealType, size_t DerivativeOrder>
0087 struct count_rvar_impl<
0088 RealType,
0089 DerivativeOrder,
0090 typename std::enable_if_t<
0091 has_unary_sub_type<RealType>::value
0092 && !std::is_same<RealType, rvar<typename RealType::value_type, DerivativeOrder>>::value
0093 && !has_binary_sub_types<RealType>::value>>
0094 {
0095 static constexpr std::size_t value
0096 = count_rvar_impl<typename RealType::arg_type, DerivativeOrder>::value;
0097 };
0098 template<typename RealType, size_t DerivativeOrder>
0099 constexpr std::size_t count_rvars = detail::count_rvar_impl<RealType, DerivativeOrder>::value;
0100
0101 template<typename T>
0102 struct is_expression : std::is_base_of<expression_base, typename std::decay<T>::type>
0103 {};
0104
0105 template<typename RealType, size_t N>
0106 struct rvar_type_impl
0107 {
0108 using type = rvar<RealType, N>;
0109 };
0110
0111 template<typename RealType>
0112 struct rvar_type_impl<RealType, 0>
0113 {
0114 using type = RealType;
0115 };
0116
0117 }
0118
0119 template<typename T, size_t N>
0120 using rvar_t = typename detail::rvar_type_impl<T, N>::type;
0121
0122 template<typename RealType, size_t DerivativeOrder, class DerivedExpression>
0123 struct expression : expression_base
0124 {
0125
0126
0127
0128
0129 using value_type = RealType;
0130 static constexpr size_t order_v = DerivativeOrder;
0131 using derived_type = DerivedExpression;
0132
0133 static constexpr size_t num_literals = 0;
0134 using inner_t = rvar_t<RealType, DerivativeOrder - 1>;
0135 inner_t evaluate() const { return static_cast<const DerivedExpression *>(this)->evaluate(); }
0136
0137 template<size_t arg_index>
0138 void propagatex(gradient_node<RealType, DerivativeOrder> *node, inner_t adj) const
0139 {
0140 return static_cast<const DerivedExpression *>(this)->template propagatex<arg_index>(node,
0141 adj);
0142 }
0143 };
0144
0145 template<typename RealType,
0146 size_t DerivativeOrder,
0147 typename LHS,
0148 typename RHS,
0149 typename ConcreteBinaryOperation>
0150 struct abstract_binary_expression
0151 : public expression<
0152 RealType,
0153 DerivativeOrder,
0154 abstract_binary_expression<RealType, DerivativeOrder, LHS, RHS, ConcreteBinaryOperation>>
0155 {
0156 using lhs_type = LHS;
0157 using rhs_type = RHS;
0158 using value_type = RealType;
0159 using inner_t = rvar_t<RealType, DerivativeOrder - 1>;
0160 const lhs_type lhs;
0161 const rhs_type rhs;
0162
0163 explicit abstract_binary_expression(
0164 const expression<RealType, DerivativeOrder, LHS> &left_hand_expr,
0165 const expression<RealType, DerivativeOrder, RHS> &right_hand_expr)
0166 : lhs(static_cast<const LHS &>(left_hand_expr))
0167 , rhs(static_cast<const RHS &>(right_hand_expr)){};
0168
0169 inner_t evaluate() const
0170 {
0171 return static_cast<const ConcreteBinaryOperation *>(this)->evaluate();
0172 };
0173
0174 template<size_t arg_index>
0175 void propagatex(gradient_node<RealType, DerivativeOrder> *node, inner_t adj) const
0176 {
0177 const inner_t lv = lhs.evaluate();
0178 const inner_t rv = rhs.evaluate();
0179 const inner_t v = evaluate();
0180 const inner_t partial_l = ConcreteBinaryOperation::left_derivative(lv, rv, v);
0181 const inner_t partial_r = ConcreteBinaryOperation::right_derivative(lv, rv, v);
0182
0183 constexpr size_t num_lhs_args = detail::count_rvars<LHS, DerivativeOrder>;
0184 constexpr size_t num_rhs_args = detail::count_rvars<RHS, DerivativeOrder>;
0185
0186 propagate_lhs<num_lhs_args, arg_index>(node, adj * partial_l);
0187 propagate_rhs<num_rhs_args, arg_index + num_lhs_args>(node, adj * partial_r);
0188 }
0189
0190 private:
0191
0192
0193 template<std::size_t num_args,
0194 std::size_t arg_index_,
0195 typename std::enable_if<(num_args > 0), int>::type = 0>
0196 void propagate_lhs(gradient_node<RealType, DerivativeOrder> *node, inner_t adj) const
0197 {
0198 lhs.template propagatex<arg_index_>(node, adj);
0199 }
0200
0201 template<std::size_t num_args,
0202 std::size_t arg_index_,
0203 typename std::enable_if<(num_args == 0), int>::type = 0>
0204 void propagate_lhs(gradient_node<RealType, DerivativeOrder> *, inner_t) const
0205 {}
0206
0207 template<std::size_t num_args,
0208 std::size_t arg_index_,
0209 typename std::enable_if<(num_args > 0), int>::type = 0>
0210 void propagate_rhs(gradient_node<RealType, DerivativeOrder> *node, inner_t adj) const
0211 {
0212 rhs.template propagatex<arg_index_>(node, adj);
0213 }
0214
0215 template<std::size_t num_args,
0216 std::size_t arg_index_,
0217 typename std::enable_if<(num_args == 0), int>::type = 0>
0218 void propagate_rhs(gradient_node<RealType, DerivativeOrder> *, inner_t) const
0219 {}
0220 };
0221 template<typename RealType, size_t DerivativeOrder, typename ARG, typename ConcreteUnaryOperation>
0222
0223 struct abstract_unary_expression
0224 : public expression<
0225 RealType,
0226 DerivativeOrder,
0227 abstract_unary_expression<RealType, DerivativeOrder, ARG, ConcreteUnaryOperation>>
0228 {
0229 using arg_type = ARG;
0230 using value_type = RealType;
0231 using inner_t = rvar_t<RealType, DerivativeOrder - 1>;
0232 const arg_type arg;
0233 const RealType constant;
0234 explicit abstract_unary_expression(const expression<RealType, DerivativeOrder, ARG> &arg_expr,
0235 const RealType &constant)
0236 : arg(static_cast<const ARG &>(arg_expr))
0237 , constant(constant){};
0238 inner_t evaluate() const
0239 {
0240 return static_cast<const ConcreteUnaryOperation *>(this)->evaluate();
0241 };
0242
0243 template<size_t arg_index>
0244 void propagatex(gradient_node<RealType, DerivativeOrder> *node, inner_t adj) const
0245 {
0246 inner_t argv = arg.evaluate();
0247 inner_t v = evaluate();
0248 inner_t partial_arg = ConcreteUnaryOperation::derivative(argv, v, constant);
0249
0250 arg.template propagatex<arg_index>(node, adj * partial_arg);
0251 }
0252 };
0253 }
0254 }
0255 }
0256 }
0257
0258 #endif