Automatic Differentiation
 
Loading...
Searching...
No Matches
solve_powell.hpp
Go to the documentation of this file.
1#ifndef STAN_MATH_REV_FUNCTOR_SOLVE_POWELL_HPP
2#define STAN_MATH_REV_FUNCTOR_SOLVE_POWELL_HPP
3
12#include <unsupported/Eigen/NonLinearOptimization>
13#include <iostream>
14#include <string>
15#include <vector>
16
17namespace stan {
18namespace math {
19
46template <typename F, typename T, typename... Args,
47 require_eigen_vector_t<T>* = nullptr>
48T& solve_powell_call_solver(const F& f, T& x, std::ostream* const msgs,
49 const double relative_tolerance,
50 const double function_tolerance,
51 const int64_t max_num_steps, const Args&... args) {
52 // Construct the solver
54 Eigen::HybridNonLinearSolver<hybrj_functor_solver<F>> solver(hfs);
55
56 // Compute theta_dbl
57 solver.parameters.xtol = relative_tolerance;
58 solver.parameters.maxfev = max_num_steps;
59 solver.solve(x);
60
61 // Check if the max number of steps has been exceeded
62 if (solver.nfev >= max_num_steps) {
63 [max_num_steps]() STAN_COLD_PATH {
64 throw_domain_error("algebra_solver", "maximum number of iterations",
65 max_num_steps, "(", ") was exceeded in the solve.");
66 }();
67 }
68
69 // Check solution is a root
70 double system_norm = f(x).stableNorm();
71 if (system_norm > function_tolerance) {
72 [function_tolerance, system_norm]() STAN_COLD_PATH {
73 std::ostringstream message;
74 message << "the norm of the algebraic function is " << system_norm
75 << " but should be lower than the function "
76 << "tolerance:";
77 throw_domain_error("algebra_solver", message.str().c_str(),
78 function_tolerance, "",
79 ". Consider decreasing the relative tolerance and "
80 "increasing max_num_steps.");
81 }();
82 }
83
84 return x;
85}
86
125template <typename F, typename T, typename... Args,
126 require_eigen_vector_t<T>* = nullptr,
127 require_all_st_arithmetic<Args...>* = nullptr>
128Eigen::VectorXd solve_powell_tol(const F& f, const T& x,
129 const double relative_tolerance,
130 const double function_tolerance,
131 const int64_t max_num_steps,
132 std::ostream* const msgs,
133 const Args&... args) {
134 auto x_ref = eval(value_of(x));
135 auto args_vals_tuple = std::make_tuple(to_ref(args)...);
136
137 auto f_wrt_x = [&args_vals_tuple, &f, msgs](const auto& x) {
138 return math::apply(
139 [&x, &f, msgs](const auto&... args) { return f(x, msgs, args...); },
140 args_vals_tuple);
141 };
142
143 check_nonzero_size("solve_powell", "initial guess", x_ref);
144 check_finite("solve_powell", "initial guess", x_ref);
145 check_nonnegative("alegbra_solver_powell", "relative_tolerance",
146 relative_tolerance);
147 check_nonnegative("solve_powell", "function_tolerance", function_tolerance);
148 check_positive("solve_powell", "max_num_steps", max_num_steps);
149 check_matching_sizes("solve_powell", "the algebraic system's output",
150 f_wrt_x(x_ref), "the vector of unknowns, x,", x_ref);
151
152 // Solve the system
153 return solve_powell_call_solver(f_wrt_x, x_ref, msgs, relative_tolerance,
154 function_tolerance, max_num_steps);
155}
156
186template <typename F, typename T, typename... T_Args,
187 require_eigen_vector_t<T>* = nullptr>
188Eigen::Matrix<stan::return_type_t<T_Args...>, Eigen::Dynamic, 1> solve_powell(
189 const F& f, const T& x, std::ostream* const msgs, const T_Args&... args) {
190 double relative_tolerance = 1e-10;
191 double function_tolerance = 1e-6;
192 int64_t max_num_steps = 200;
193 const auto& args_ref_tuple = std::make_tuple(to_ref(args)...);
194 return math::apply(
195 [&](const auto&... args_refs) {
196 return solve_powell_tol(f, x, relative_tolerance, function_tolerance,
197 max_num_steps, msgs, args_refs...);
198 },
199 args_ref_tuple);
200}
201
249template <typename F, typename T1, typename T2,
251Eigen::Matrix<value_type_t<T2>, Eigen::Dynamic, 1> algebra_solver(
252 const F& f, const T1& x, const T2& y, const std::vector<double>& dat,
253 const std::vector<int>& dat_int, std::ostream* msgs = nullptr,
254 const double relative_tolerance = 1e-10,
255 const double function_tolerance = 1e-6,
256 const int64_t max_num_steps = 1e+3) {
257 return solve_powell_tol(algebra_solver_adapter<F>(f), x, relative_tolerance,
258 function_tolerance, max_num_steps, msgs, y, dat,
259 dat_int);
260}
261
322template <typename F, typename T, typename... T_Args,
323 require_eigen_vector_t<T>* = nullptr,
324 require_any_st_var<T_Args...>* = nullptr>
325Eigen::Matrix<var, Eigen::Dynamic, 1> solve_powell_tol(
326 const F& f, const T& x, const double relative_tolerance,
327 const double function_tolerance, const int64_t max_num_steps,
328 std::ostream* const msgs, const T_Args&... args) {
329 auto x_ref = eval(value_of(x));
330 auto arena_args_tuple = make_chainable_ptr(std::make_tuple(eval(args)...));
331 auto args_vals_tuple = math::apply(
332 [&](const auto&... args) {
333 return std::make_tuple(to_ref(value_of(args))...);
334 },
335 *arena_args_tuple);
336
337 auto f_wrt_x = [&args_vals_tuple, &f, msgs](const auto& x) {
338 return math::apply(
339 [&x, &f, msgs](const auto&... args) { return f(x, msgs, args...); },
340 args_vals_tuple);
341 };
342
343 check_nonzero_size("solve_powell", "initial guess", x_ref);
344 check_finite("solve_powell", "initial guess", x_ref);
345 check_nonnegative("alegbra_solver_powell", "relative_tolerance",
346 relative_tolerance);
347 check_nonnegative("solve_powell", "function_tolerance", function_tolerance);
348 check_positive("solve_powell", "max_num_steps", max_num_steps);
349 check_matching_sizes("solve_powell", "the algebraic system's output",
350 f_wrt_x(x_ref), "the vector of unknowns, x,", x_ref);
351
352 // Solve the system
353 solve_powell_call_solver(f_wrt_x, x_ref, msgs, relative_tolerance,
354 function_tolerance, max_num_steps);
355
356 Eigen::MatrixXd Jf_x;
357 Eigen::VectorXd f_x;
358
359 jacobian(f_wrt_x, x_ref, f_x, Jf_x);
360
361 using ret_type = Eigen::Matrix<var, Eigen::Dynamic, -1>;
362 auto Jf_x_T_lu_ptr
363 = make_unsafe_chainable_ptr(Jf_x.transpose().partialPivLu()); // Lu
364
365 arena_t<ret_type> ret = x_ref;
366
367 reverse_pass_callback([f, ret, arena_args_tuple, Jf_x_T_lu_ptr,
368 msgs]() mutable {
369 Eigen::VectorXd eta = -Jf_x_T_lu_ptr->solve(ret.adj().eval());
370
371 // Contract with Jacobian of f with respect to y using a nested reverse
372 // autodiff pass.
373 {
375 Eigen::VectorXd ret_val = ret.val();
376 auto x_nrad_ = math::apply(
377 [&](const auto&... args) { return eval(f(ret_val, msgs, args...)); },
378 *arena_args_tuple);
379 x_nrad_.adj() = eta;
380 grad();
381 }
382 });
383
384 return ret_type(ret);
385}
386
387} // namespace math
388} // namespace stan
389
390#endif
A class following the RAII idiom to start and recover nested autodiff scopes.
#define STAN_COLD_PATH
require_all_t< std::is_arithmetic< scalar_type_t< std::decay_t< Types > > >... > require_all_st_arithmetic
Require all of the scalar types satisfy std::is_arithmetic.
require_t< is_eigen_vector< std::decay_t< T > > > require_eigen_vector_t
Require type satisfies is_eigen_vector.
require_all_t< is_eigen_vector< std::decay_t< Types > >... > require_all_eigen_vector_t
Require all of the types satisfy is_eigen_vector.
typename return_type< Ts... >::type return_type_t
Convenience type for the return type of the specified template parameters.
require_any_t< is_var< scalar_type_t< std::decay_t< Types > > >... > require_any_st_var
Require any of the scalar types satisfy is_var.
Definition is_var.hpp:131
void jacobian(const F &f, const Eigen::Matrix< T, Eigen::Dynamic, 1 > &x, Eigen::Matrix< T, Eigen::Dynamic, 1 > &fx, Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > &J)
Definition jacobian.hpp:11
Eigen::Matrix< value_type_t< T2 >, Eigen::Dynamic, 1 > algebra_solver(const F &f, const T1 &x, const T2 &y, const std::vector< double > &dat, const std::vector< int > &dat_int, std::ostream *msgs=nullptr, const double relative_tolerance=1e-10, const double function_tolerance=1e-6, const int64_t max_num_steps=1e+3)
Return the solution to the specified system of algebraic equations given an initial guess,...
void check_nonnegative(const char *function, const char *name, const T_y &y)
Check if y is non-negative.
static constexpr double e()
Return the base of the natural logarithm.
Definition constants.hpp:20
auto make_unsafe_chainable_ptr(T &&obj)
Store the given object in a chainable_object so it is destructed only when the chainable stack memory...
void reverse_pass_callback(F &&functor)
Puts a callback on the autodiff stack to be called in reverse pass.
T eval(T &&arg)
Inputs which have a plain_type equal to the own time are forwarded unmodified (for Eigen expressions ...
Definition eval.hpp:20
T value_of(const fvar< T > &v)
Return the value of the specified variable.
Definition value_of.hpp:18
Eigen::Matrix< stan::return_type_t< T_Args... >, Eigen::Dynamic, 1 > solve_powell(const F &f, const T &x, std::ostream *const msgs, const T_Args &... args)
Return the solution to the specified system of algebraic equations given an initial guess,...
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.
auto make_chainable_ptr(T &&obj)
Store the given object in a chainable_object so it is destructed only when the chainable stack memory...
void check_matching_sizes(const char *function, const char *name1, const T_y1 &y1, const char *name2, const T_y2 &y2)
Check if two structures at the same size.
T & solve_powell_call_solver(const F &f, T &x, std::ostream *const msgs, const double relative_tolerance, const double function_tolerance, const int64_t max_num_steps, const Args &... args)
Solve algebraic equations using Powell solver.
ref_type_t< T && > to_ref(T &&a)
This evaluates expensive Eigen expressions.
Definition to_ref.hpp:17
void check_finite(const char *function, const char *name, const T_y &y)
Return true if all values in y are finite.
var_value< double > var
Definition var.hpp:1187
void check_nonzero_size(const char *function, const char *name, const T_y &y)
Check if the specified matrix/vector is of non-zero size.
void check_positive(const char *function, const char *name, const T_y &y)
Check if y is positive.
static void grad()
Compute the gradient for all variables starting from the end of the AD tape.
Definition grad.hpp:26
constexpr decltype(auto) apply(F &&f, Tuple &&t, PreArgs &&... pre_args)
Definition apply.hpp:52
Eigen::VectorXd solve_powell_tol(const F &f, const T &x, const double relative_tolerance, const double function_tolerance, const int64_t max_num_steps, std::ostream *const msgs, const Args &... args)
Return the solution to the specified system of algebraic equations given an initial guess,...
typename internal::arena_type_impl< std::decay_t< T > >::type arena_t
Determines a type that can be used in place of T that does any dynamic allocations on the AD stack.
The lgamma implementation in stan-math is based on either the reentrant safe lgamma_r implementation ...
Adapt the non-variadic algebra_solver_newton and algebra_solver_powell arguemts to the variadic algeb...
A functor with the required operators to call Eigen's algebraic solver.