From fc65777e7d04c922923ff8cf13ff81950803275b Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Tue, 9 Mar 2021 21:32:32 -0800 Subject: [PATCH 01/88] New implementation of algebraic_solver_vari compiles. --- .../rev/functor/algebra_solver_newton.hpp | 25 ++- .../rev/functor/algebra_solver_powell.hpp | 202 +++++++++++------- 2 files changed, 138 insertions(+), 89 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index f3a04386789..07a382dd784 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -129,22 +129,29 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_newton( const auto& x_eval = x.eval(); const auto& y_eval = y.eval(); + const auto& x_val = (value_of(x_eval)).eval(); + const auto& y_val = (value_of(y_eval)).eval(); Eigen::VectorXd theta_dbl = algebra_solver_newton( f, x_eval, value_of(y_eval), dat, dat_int, msgs, scaling_step_size, function_tolerance, max_num_steps); - typedef system_functor Fy; - typedef system_functor Fs; - typedef hybrj_functor_solver Fx; - Fx fx(Fs(), f, value_of(x_eval), value_of(y_eval), dat, dat_int, msgs); + typedef system_functor Fsx; + typedef hybrj_functor_solver Fx; + Fx fx(Fsx(), f, value_of(x_eval), value_of(y_eval), dat, dat_int, msgs); + + typedef system_functor Fsy; + typedef hybrj_functor_solver Fy; + Fsy fy(f, value_of(x_eval), value_of(y_eval), dat, dat_int, msgs); // Construct vari - auto* vi0 = new algebra_solver_vari, Fx>( - Fy(), f, value_of(x_eval), y_eval, dat, dat_int, theta_dbl, fx, msgs); + algebra_solver_vari, Fx>* vi0 + = new algebra_solver_vari, Fx>( + fy, y_val, fx, theta_dbl); Eigen::Matrix theta(x.size()); - theta(0) = var(vi0->theta_[0]); - for (int i = 1; i < x.size(); ++i) - theta(i) = var(vi0->theta_[i]); + theta(0) = var(vi0->x_[0]); + for (int i = 1; i < x.size(); ++i) { + theta(i) = var(vi0->x_[i]); + } return theta; } diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index a36705c70ac..0dd1b32ad31 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -21,58 +22,76 @@ namespace math { * function theorem. The call to Jacobian() occurs outside the call to * chain() -- this prevents malloc issues. */ -template +template struct algebra_solver_vari : public vari { - /** vector of parameters */ - vari** y_; /** number of parameters */ - int y_size_; + const int y_size_; + /** value of parameters */ + const Eigen::Matrix& y_val_; + /** System functor (f_) w.r.t. inputs (y_) */ + Fy fy_; + /** array of parameters */ + vari** y_; /** number of unknowns */ - int x_size_; - /** vector of solution */ - vari** theta_; - /** Jacobian of the solution w.r.t parameters */ - double* Jx_y_; + const int x_size_; + /** value of unknowns */ + const Eigen::VectorXd& x_val_; + /** System functor (f_) w.r.t. outputs (x_) */ + Fx fx_; + /** array of unknowns */ + vari** x_; - algebra_solver_vari(const Fs& fs, const F& f, const Eigen::VectorXd& x, - const Eigen::Matrix& y, - const std::vector& dat, - const std::vector& dat_int, - const Eigen::VectorXd& theta_dbl, Fx& fx, - std::ostream* msgs) - : vari(theta_dbl(0)), - y_(ChainableStack::instance_->memalloc_.alloc_array(y.size())), + algebra_solver_vari(Fy& fy, const Eigen::Matrix& y, Fx& fx, const Eigen::VectorXd& x) + : vari(x(0)), y_size_(y.size()), + y_val_(y), + fy_(fy), + y_(ChainableStack::instance_->memalloc_.alloc_array(y_size_)), x_size_(x.size()), - theta_( - ChainableStack::instance_->memalloc_.alloc_array(x_size_)), - Jx_y_(ChainableStack::instance_->memalloc_.alloc_array( - x_size_ * y_size_)) { + x_val_(x), + fx_(fx), + x_(ChainableStack::instance_->memalloc_.alloc_array(x_size_)) + { using Eigen::Map; using Eigen::MatrixXd; for (int i = 0; i < y.size(); ++i) { y_[i] = y(i).vi_; } - theta_[0] = this; + x_[0] = this; for (int i = 1; i < x.size(); ++i) { - theta_[i] = new vari(theta_dbl(i), false); + x_[i] = new vari(x(i), false); } - - // Compute the Jacobian and store in array, using the - // implicit function theorem, i.e. Jx_y = Jf_y / Jf_x - using f_y = hybrj_functor_solver; - Map(&Jx_y_[0], x_size_, y_size_) - = -mdivide_left(fx.get_jacobian(theta_dbl), - f_y(fs, f, theta_dbl, value_of(y), dat, dat_int, msgs) - .get_jacobian(value_of(y))); } void chain() { + using Eigen::Matrix; + using Eigen::MatrixXd; + using Eigen::VectorXd; + using Eigen::Dynamic; + + // Compute (transpose of) specificities with respect to x. + VectorXd x_bar_(x_size_); // TODO: Is this zeroing out memory? + for (int i = 0; i < x_size_; ++i) { + x_bar_[i] = x_[i]->adj_; + } + + // Contract specificities with inverse Jacobian of f with respect to x. + MatrixXd Jf_x = fx_.get_jacobian(x_val_); + VectorXd eta_ = - Jf_x.partialPivLu().solve(x_bar_); + + // Contract with Jacobian of f with respect to y using a nested reverse + // autodiff pass. + Matrix y_nrad_ = y_val_; + { + stan::math::nested_rev_autodiff(); + auto x_nrad_ = fy_(y_nrad_); + x_nrad_.adj() = eta_; + stan::math::grad(); + } + for (int j = 0; j < y_size_; j++) { - for (int i = 0; i < x_size_; i++) { - y_[j]->adj_ += theta_[i]->adj_ * Jx_y_[j * x_size_ + i]; - } + y_[j]->adj_ += y_nrad_.adj()[j]; } } }; @@ -98,7 +117,7 @@ struct algebra_solver_vari : public vari { * @param[in] f Functor that evaluates the system of equations. * @param[in] x Vector of starting values. * @param[in] y parameter vector for the equation system. The function - * is overloaded to treat y as a vector of doubles or of a + * is overloaded to treat y as a vector of doubles or as a * a template type T. * @param[in] dat continuous data vector for the equation system. * @param[in] dat_int integer data vector for the equation system. @@ -134,10 +153,7 @@ Eigen::VectorXd algebra_solver_powell( algebra_solver_check(x_val, y, dat, dat_int, function_tolerance, max_num_steps); check_nonnegative("alegbra_solver", "relative_tolerance", relative_tolerance); - // if (relative_tolerance < 0) - // invalid_argument("algebra_solver", "relative_tolerance,", - // function_tolerance, "", - // ", must be greater than or equal to 0"); + // Create functor for algebraic system using Fs = system_functor; @@ -149,32 +165,10 @@ Eigen::VectorXd algebra_solver_powell( check_matching_sizes("algebra_solver", "the algebraic system's output", fx.get_value(x_val), "the vector of unknowns, x,", x); - // Compute theta_dbl - Eigen::VectorXd theta_dbl = x_val; - solver.parameters.xtol = relative_tolerance; - solver.parameters.maxfev = max_num_steps; - solver.solve(theta_dbl); - - // Check if the max number of steps has been exceeded - if (solver.nfev >= max_num_steps) { - throw_domain_error("algebra_solver", "maximum number of iterations", - max_num_steps, "(", ") was exceeded in the solve."); - } - - // Check solution is a root - double system_norm = fx.get_value(theta_dbl).stableNorm(); - if (system_norm > function_tolerance) { - std::ostringstream message; - message << "the norm of the algebraic function is " << system_norm - << " but should be lower than the function " - << "tolerance:"; - throw_domain_error("algebra_solver", message.str().c_str(), - function_tolerance, "", - ". Consider decreasing the relative tolerance and " - "increasing max_num_steps."); - } - - return theta_dbl; + // Solve the system + return algebra_solver_powell_(solver, fx, x, y, dat, dat_int, msgs, + relative_tolerance, function_tolerance, + max_num_steps); } /** @@ -234,27 +228,38 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_powell( const auto& y_eval = y.eval(); const auto& x_val = (value_of(x_eval)).eval(); const auto& y_val = (value_of(y_eval)).eval(); - Eigen::VectorXd theta_dbl = algebra_solver_powell( - f, x_eval, y_val, dat, dat_int, 0, relative_tolerance, function_tolerance, - max_num_steps); - using Fy = system_functor; + algebra_solver_check(x_val, y, dat, dat_int, function_tolerance, + max_num_steps); + check_nonnegative("alegbra_solver", "relative_tolerance", relative_tolerance); - // TODO(charlesm93): a similar object gets constructed inside - // the call to algebra_solver. Cache the previous result - // and use it here (if possible). - using Fs = system_functor; - using Fx = hybrj_functor_solver; - Fx fx(Fs(), f, x_val, y_val, dat, dat_int, msgs); + // Construct the Powell solver + using Fsx = system_functor; + using Fx = hybrj_functor_solver; + Fx fx(Fsx(), f, x_val, y_val, dat, dat_int, msgs); + Eigen::HybridNonLinearSolver solver(fx); + + // Check dimension unknowns equals dimension of system output + check_matching_sizes("algebra_solver", "the algebraic system's output", + fx.get_value(x_val), "the vector of unknowns, x,", x); + + // Solve the system + Eigen::VectorXd theta_dbl = algebra_solver_powell_( + solver, fx, x_eval, y_val, dat, dat_int, 0, relative_tolerance, + function_tolerance, max_num_steps); + + using Fsy = system_functor; + using Fy = hybrj_functor_solver; + Fsy fy(f, x_val, y_val, dat, dat_int, msgs); // Construct vari - algebra_solver_vari, Fx>* vi0 - = new algebra_solver_vari, Fx>( - Fy(), f, x_val, y_eval, dat, dat_int, theta_dbl, fx, msgs); + algebra_solver_vari, Fx>* vi0 + = new algebra_solver_vari, Fx>( + fy, y_val, fx, theta_dbl); Eigen::Matrix theta(x.size()); - theta(0) = var(vi0->theta_[0]); + theta(0) = var(vi0->x_[0]); for (int i = 1; i < x.size(); ++i) { - theta(i) = var(vi0->theta_[i]); + theta(i) = var(vi0->x_[i]); } return theta; @@ -316,6 +321,43 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver( function_tolerance, max_num_steps); } +template * = nullptr> +Eigen::VectorXd algebra_solver_powell_( + S& solver, const F& fx, const T& x, const Eigen::VectorXd& y, + const std::vector& dat, const std::vector& dat_int, + std::ostream* msgs, double relative_tolerance, + double function_tolerance, long int max_num_steps ) { // NOLINT(runtime/int) + const auto& x_eval = x.eval(); + const auto& x_val = (value_of(x_eval)).eval(); + + // Compute theta_dbl + Eigen::VectorXd theta_dbl = x_val; + solver.parameters.xtol = relative_tolerance; + solver.parameters.maxfev = max_num_steps; + solver.solve(theta_dbl); + + // Check if the max number of steps has been exceeded + if (solver.nfev >= max_num_steps) { + throw_domain_error("algebra_solver", "maximum number of iterations", + max_num_steps, "(", ") was exceeded in the solve."); + } + + // Check solution is a root + double system_norm = fx.get_value(theta_dbl).stableNorm(); + if (system_norm > function_tolerance) { + std::ostringstream message; + message << "the norm of the algebraic function is " << system_norm + << " but should be lower than the function " + << "tolerance:"; + throw_domain_error("algebra_solver", message.str().c_str(), + function_tolerance, "", + ". Consider decreasing the relative tolerance and " + "increasing max_num_steps."); + } + + return theta_dbl; +} + } // namespace math } // namespace stan From 06045c627589712397b94af1640397f8845e0e07 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Tue, 9 Mar 2021 22:49:50 -0800 Subject: [PATCH 02/88] Compiling and most tests passing. --- stan/math/rev/functor/algebra_solver_powell.hpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 0dd1b32ad31..1c72d1c04c7 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -36,10 +36,11 @@ struct algebra_solver_vari : public vari { const int x_size_; /** value of unknowns */ const Eigen::VectorXd& x_val_; - /** System functor (f_) w.r.t. outputs (x_) */ Fx fx_; /** array of unknowns */ vari** x_; + /** Jacobian of f w.r.t. outputs (x_) */ + const Eigen::MatrixXd Jf_x_; algebra_solver_vari(Fy& fy, const Eigen::Matrix& y, Fx& fx, const Eigen::VectorXd& x) : vari(x(0)), @@ -49,17 +50,18 @@ struct algebra_solver_vari : public vari { y_(ChainableStack::instance_->memalloc_.alloc_array(y_size_)), x_size_(x.size()), x_val_(x), + Jf_x_(fx_.get_jacobian(x_val_)), fx_(fx), x_(ChainableStack::instance_->memalloc_.alloc_array(x_size_)) { using Eigen::Map; using Eigen::MatrixXd; - for (int i = 0; i < y.size(); ++i) { + for (int i = 0; i < y_size_; ++i) { y_[i] = y(i).vi_; } x_[0] = this; - for (int i = 1; i < x.size(); ++i) { + for (int i = 1; i < x_size_; ++i) { x_[i] = new vari(x(i), false); } } @@ -77,8 +79,7 @@ struct algebra_solver_vari : public vari { } // Contract specificities with inverse Jacobian of f with respect to x. - MatrixXd Jf_x = fx_.get_jacobian(x_val_); - VectorXd eta_ = - Jf_x.partialPivLu().solve(x_bar_); + VectorXd eta_ = - Jf_x_.transpose().fullPivLu().solve(x_bar_); // Contract with Jacobian of f with respect to y using a nested reverse // autodiff pass. From 1cb5249a3e89f8dfea1048675b84dde01b613e8e Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Wed, 10 Mar 2021 14:52:20 -0800 Subject: [PATCH 03/88] algebra_solver_vari chain method calls itself recursively. --- .../rev/functor/algebra_solver_powell.hpp | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 1c72d1c04c7..c314f43ad25 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -27,7 +27,7 @@ struct algebra_solver_vari : public vari { /** number of parameters */ const int y_size_; /** value of parameters */ - const Eigen::Matrix& y_val_; + const Eigen::Matrix y_val_; /** System functor (f_) w.r.t. inputs (y_) */ Fy fy_; /** array of parameters */ @@ -36,13 +36,15 @@ struct algebra_solver_vari : public vari { const int x_size_; /** value of unknowns */ const Eigen::VectorXd& x_val_; + /** Hybrj functor solver (f_) w.r.t. outputs (x_) */ Fx fx_; /** array of unknowns */ vari** x_; /** Jacobian of f w.r.t. outputs (x_) */ const Eigen::MatrixXd Jf_x_; - algebra_solver_vari(Fy& fy, const Eigen::Matrix& y, Fx& fx, const Eigen::VectorXd& x) + algebra_solver_vari(Fy& fy, const Eigen::Matrix& y, + Fx& fx, const Eigen::VectorXd& x) : vari(x(0)), y_size_(y.size()), y_val_(y), @@ -50,12 +52,13 @@ struct algebra_solver_vari : public vari { y_(ChainableStack::instance_->memalloc_.alloc_array(y_size_)), x_size_(x.size()), x_val_(x), - Jf_x_(fx_.get_jacobian(x_val_)), + Jf_x_(fx.get_jacobian(x_val_)), fx_(fx), x_(ChainableStack::instance_->memalloc_.alloc_array(x_size_)) { using Eigen::Map; using Eigen::MatrixXd; + for (int i = 0; i < y_size_; ++i) { y_[i] = y(i).vi_; } @@ -83,16 +86,15 @@ struct algebra_solver_vari : public vari { // Contract with Jacobian of f with respect to y using a nested reverse // autodiff pass. - Matrix y_nrad_ = y_val_; { stan::math::nested_rev_autodiff(); - auto x_nrad_ = fy_(y_nrad_); + Matrix y_nrad_ = y_val_; + auto x_nrad_ = stan::math::eval(fy_(y_nrad_)); x_nrad_.adj() = eta_; stan::math::grad(); - } - - for (int j = 0; j < y_size_; j++) { - y_[j]->adj_ += y_nrad_.adj()[j]; + for (int j = 0; j < y_size_; j++) { + y_[j]->adj_ += y_nrad_.adj()[j]; + } } } }; From 09550511af3d905098152275b3849677ea50b8cf Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Fri, 12 Mar 2021 12:15:49 -0800 Subject: [PATCH 04/88] Fix improper initialization of nested reverse mode stack. --- stan/math/rev/functor/algebra_solver_powell.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index c314f43ad25..dcbcbc19a83 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -87,7 +87,7 @@ struct algebra_solver_vari : public vari { // Contract with Jacobian of f with respect to y using a nested reverse // autodiff pass. { - stan::math::nested_rev_autodiff(); + stan::math::nested_rev_autodiff rev; Matrix y_nrad_ = y_val_; auto x_nrad_ = stan::math::eval(fy_(y_nrad_)); x_nrad_.adj() = eta_; From fe45cbb554effdae99e5184560873c430498ead7 Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Fri, 12 Mar 2021 20:40:20 +0000 Subject: [PATCH 05/88] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- .../rev/functor/algebra_solver_newton.hpp | 4 ++-- .../rev/functor/algebra_solver_powell.hpp | 23 +++++++++---------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index 07a382dd784..9d6efdf976b 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -145,8 +145,8 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_newton( // Construct vari algebra_solver_vari, Fx>* vi0 - = new algebra_solver_vari, Fx>( - fy, y_val, fx, theta_dbl); + = new algebra_solver_vari, Fx>(fy, y_val, fx, + theta_dbl); Eigen::Matrix theta(x.size()); theta(0) = var(vi0->x_[0]); for (int i = 1; i < x.size(); ++i) { diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index dcbcbc19a83..a8015d9eec4 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -54,8 +54,7 @@ struct algebra_solver_vari : public vari { x_val_(x), Jf_x_(fx.get_jacobian(x_val_)), fx_(fx), - x_(ChainableStack::instance_->memalloc_.alloc_array(x_size_)) - { + x_(ChainableStack::instance_->memalloc_.alloc_array(x_size_)) { using Eigen::Map; using Eigen::MatrixXd; @@ -70,19 +69,19 @@ struct algebra_solver_vari : public vari { } void chain() { + using Eigen::Dynamic; using Eigen::Matrix; using Eigen::MatrixXd; using Eigen::VectorXd; - using Eigen::Dynamic; // Compute (transpose of) specificities with respect to x. - VectorXd x_bar_(x_size_); // TODO: Is this zeroing out memory? + VectorXd x_bar_(x_size_); // TODO: Is this zeroing out memory? for (int i = 0; i < x_size_; ++i) { x_bar_[i] = x_[i]->adj_; } // Contract specificities with inverse Jacobian of f with respect to x. - VectorXd eta_ = - Jf_x_.transpose().fullPivLu().solve(x_bar_); + VectorXd eta_ = -Jf_x_.transpose().fullPivLu().solve(x_bar_); // Contract with Jacobian of f with respect to y using a nested reverse // autodiff pass. @@ -93,7 +92,7 @@ struct algebra_solver_vari : public vari { x_nrad_.adj() = eta_; stan::math::grad(); for (int j = 0; j < y_size_; j++) { - y_[j]->adj_ += y_nrad_.adj()[j]; + y_[j]->adj_ += y_nrad_.adj()[j]; } } } @@ -157,7 +156,6 @@ Eigen::VectorXd algebra_solver_powell( max_num_steps); check_nonnegative("alegbra_solver", "relative_tolerance", relative_tolerance); - // Create functor for algebraic system using Fs = system_functor; using Fx = hybrj_functor_solver; @@ -257,8 +255,8 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_powell( // Construct vari algebra_solver_vari, Fx>* vi0 - = new algebra_solver_vari, Fx>( - fy, y_val, fx, theta_dbl); + = new algebra_solver_vari, Fx>(fy, y_val, fx, + theta_dbl); Eigen::Matrix theta(x.size()); theta(0) = var(vi0->x_[0]); for (int i = 1; i < x.size(); ++i) { @@ -324,12 +322,13 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver( function_tolerance, max_num_steps); } -template * = nullptr> +template * = nullptr> Eigen::VectorXd algebra_solver_powell_( S& solver, const F& fx, const T& x, const Eigen::VectorXd& y, const std::vector& dat, const std::vector& dat_int, - std::ostream* msgs, double relative_tolerance, - double function_tolerance, long int max_num_steps ) { // NOLINT(runtime/int) + std::ostream* msgs, double relative_tolerance, double function_tolerance, + long int max_num_steps) { // NOLINT(runtime/int) const auto& x_eval = x.eval(); const auto& x_val = (value_of(x_eval)).eval(); From 53e637c4386067b8d2938c54906846485e77978e Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Mon, 22 Mar 2021 18:48:13 -0700 Subject: [PATCH 06/88] Minor changes. Remove consts, and initialize objects that need to be destructed when they go out of scope in the arena. --- stan/math/rev/functor/algebra_solver_newton.hpp | 2 +- stan/math/rev/functor/algebra_solver_powell.hpp | 13 +++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index 07a382dd784..4d12587317c 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -137,7 +137,7 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_newton( typedef system_functor Fsx; typedef hybrj_functor_solver Fx; - Fx fx(Fsx(), f, value_of(x_eval), value_of(y_eval), dat, dat_int, msgs); + Fx fx(Fsx(), f, x_val, y_val, dat, dat_int, msgs); typedef system_functor Fsy; typedef hybrj_functor_solver Fy; diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index dcbcbc19a83..c23fbd6993a 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -25,19 +25,17 @@ namespace math { template struct algebra_solver_vari : public vari { /** number of parameters */ - const int y_size_; + int y_size_; /** value of parameters */ - const Eigen::Matrix y_val_; + arena_t> y_val_; /** System functor (f_) w.r.t. inputs (y_) */ Fy fy_; /** array of parameters */ vari** y_; /** number of unknowns */ - const int x_size_; + int x_size_; /** value of unknowns */ - const Eigen::VectorXd& x_val_; - /** Hybrj functor solver (f_) w.r.t. outputs (x_) */ - Fx fx_; + arena_t x_val_; /** array of unknowns */ vari** x_; /** Jacobian of f w.r.t. outputs (x_) */ @@ -53,7 +51,6 @@ struct algebra_solver_vari : public vari { x_size_(x.size()), x_val_(x), Jf_x_(fx.get_jacobian(x_val_)), - fx_(fx), x_(ChainableStack::instance_->memalloc_.alloc_array(x_size_)) { using Eigen::Map; @@ -76,7 +73,7 @@ struct algebra_solver_vari : public vari { using Eigen::Dynamic; // Compute (transpose of) specificities with respect to x. - VectorXd x_bar_(x_size_); // TODO: Is this zeroing out memory? + VectorXd x_bar_(x_size_); for (int i = 0; i < x_size_; ++i) { x_bar_[i] = x_[i]->adj_; } From f33250cd141e754be154a97c8cfd4c3011aab0fb Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Mon, 22 Mar 2021 19:42:15 -0700 Subject: [PATCH 07/88] Fix gradient being uniformly zero. --- stan/math/rev/functor/algebra_solver_newton.hpp | 2 +- stan/math/rev/functor/algebra_solver_powell.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index 4d12587317c..b8801b41a1c 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -146,7 +146,7 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_newton( // Construct vari algebra_solver_vari, Fx>* vi0 = new algebra_solver_vari, Fx>( - fy, y_val, fx, theta_dbl); + fy, y_eval, fx, theta_dbl); Eigen::Matrix theta(x.size()); theta(0) = var(vi0->x_[0]); for (int i = 1; i < x.size(); ++i) { diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index c23fbd6993a..ec1c9dce7c4 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -255,7 +255,7 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_powell( // Construct vari algebra_solver_vari, Fx>* vi0 = new algebra_solver_vari, Fx>( - fy, y_val, fx, theta_dbl); + fy, y_eval, fx, theta_dbl); Eigen::Matrix theta(x.size()); theta(0) = var(vi0->x_[0]); for (int i = 1; i < x.size(); ++i) { From 7ea35df6bcc688dad2ff7fd891f4ace47ae53c4f Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Mon, 22 Mar 2021 19:44:06 -0700 Subject: [PATCH 08/88] All tests (except for degenerate_eq_test) passing. --- stan/math/rev/functor/algebra_solver_powell.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index ec1c9dce7c4..e9fdd8cd6d1 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -89,9 +89,6 @@ struct algebra_solver_vari : public vari { auto x_nrad_ = stan::math::eval(fy_(y_nrad_)); x_nrad_.adj() = eta_; stan::math::grad(); - for (int j = 0; j < y_size_; j++) { - y_[j]->adj_ += y_nrad_.adj()[j]; - } } } }; From a4f16055a6fe24ba48ed3d11c6e89d71c9c96d11 Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Tue, 23 Mar 2021 02:47:36 +0000 Subject: [PATCH 09/88] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- .../rev/functor/algebra_solver_powell.hpp | 65 +++++++++---------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 660bf82cf65..564b06ca090 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -52,51 +52,50 @@ struct algebra_solver_vari : public vari { x_val_(x), Jf_x_(fx.get_jacobian(x_val_)), <<<<<<< HEAD - x_(ChainableStack::instance_->memalloc_.alloc_array(x_size_)) - { + x_(ChainableStack::instance_->memalloc_.alloc_array(x_size_)){ ======= fx_(fx), x_(ChainableStack::instance_->memalloc_.alloc_array(x_size_)) { >>>>>>> origin/feature/issue-2401-alg-solver-adjoint - using Eigen::Map; - using Eigen::MatrixXd; + using Eigen::Map; + using Eigen::MatrixXd; - for (int i = 0; i < y_size_; ++i) { - y_[i] = y(i).vi_; - } + for (int i = 0; i < y_size_; ++i) { + y_[i] = y(i).vi_; + } - x_[0] = this; - for (int i = 1; i < x_size_; ++i) { - x_[i] = new vari(x(i), false); - } + x_[0] = this; + for (int i = 1; i < x_size_; ++i) { + x_[i] = new vari(x(i), false); } +} void chain() { - using Eigen::Dynamic; - using Eigen::Matrix; - using Eigen::MatrixXd; - using Eigen::VectorXd; + using Eigen::Dynamic; + using Eigen::Matrix; + using Eigen::MatrixXd; + using Eigen::VectorXd; - // Compute (transpose of) specificities with respect to x. - VectorXd x_bar_(x_size_); - for (int i = 0; i < x_size_; ++i) { - x_bar_[i] = x_[i]->adj_; - } + // Compute (transpose of) specificities with respect to x. + VectorXd x_bar_(x_size_); + for (int i = 0; i < x_size_; ++i) { + x_bar_[i] = x_[i]->adj_; + } - // Contract specificities with inverse Jacobian of f with respect to x. - VectorXd eta_ = -Jf_x_.transpose().fullPivLu().solve(x_bar_); + // Contract specificities with inverse Jacobian of f with respect to x. + VectorXd eta_ = -Jf_x_.transpose().fullPivLu().solve(x_bar_); - // Contract with Jacobian of f with respect to y using a nested reverse - // autodiff pass. - { - stan::math::nested_rev_autodiff rev; - Matrix y_nrad_ = y_val_; - auto x_nrad_ = stan::math::eval(fy_(y_nrad_)); - x_nrad_.adj() = eta_; - stan::math::grad(); - } + // Contract with Jacobian of f with respect to y using a nested reverse + // autodiff pass. + { + stan::math::nested_rev_autodiff rev; + Matrix y_nrad_ = y_val_; + auto x_nrad_ = stan::math::eval(fy_(y_nrad_)); + x_nrad_.adj() = eta_; + stan::math::grad(); } -}; +} +}; // namespace math /** * Return the solution to the specified system of algebraic @@ -360,7 +359,7 @@ Eigen::VectorXd algebra_solver_powell_( return theta_dbl; } -} // namespace math +} // namespace stan } // namespace stan #endif From 21ec967803e981c775689224b8be3c60a2ad6645 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Mon, 22 Mar 2021 19:56:24 -0700 Subject: [PATCH 10/88] Fix one additional linting issue. --- stan/math/rev/functor/algebra_solver_powell.hpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 660bf82cf65..c121c82ca83 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -51,13 +51,8 @@ struct algebra_solver_vari : public vari { x_size_(x.size()), x_val_(x), Jf_x_(fx.get_jacobian(x_val_)), -<<<<<<< HEAD x_(ChainableStack::instance_->memalloc_.alloc_array(x_size_)) { -======= - fx_(fx), - x_(ChainableStack::instance_->memalloc_.alloc_array(x_size_)) { ->>>>>>> origin/feature/issue-2401-alg-solver-adjoint using Eigen::Map; using Eigen::MatrixXd; From 88fda196dc355f6f88e3e3f4a64d9f312bec3bb7 Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Tue, 23 Mar 2021 03:07:19 +0000 Subject: [PATCH 11/88] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- .../rev/functor/algebra_solver_powell.hpp | 63 +++++++++---------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index f7d8168807b..2442f6f86ff 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -51,46 +51,45 @@ struct algebra_solver_vari : public vari { x_size_(x.size()), x_val_(x), Jf_x_(fx.get_jacobian(x_val_)), - x_(ChainableStack::instance_->memalloc_.alloc_array(x_size_)) - { - using Eigen::Map; - using Eigen::MatrixXd; + x_(ChainableStack::instance_->memalloc_.alloc_array(x_size_)) { + using Eigen::Map; + using Eigen::MatrixXd; - for (int i = 0; i < y_size_; ++i) { - y_[i] = y(i).vi_; - } + for (int i = 0; i < y_size_; ++i) { + y_[i] = y(i).vi_; + } - x_[0] = this; - for (int i = 1; i < x_size_; ++i) { - x_[i] = new vari(x(i), false); + x_[0] = this; + for (int i = 1; i < x_size_; ++i) { + x_[i] = new vari(x(i), false); + } } -} void chain() { - using Eigen::Dynamic; - using Eigen::Matrix; - using Eigen::MatrixXd; - using Eigen::VectorXd; + using Eigen::Dynamic; + using Eigen::Matrix; + using Eigen::MatrixXd; + using Eigen::VectorXd; - // Compute (transpose of) specificities with respect to x. - VectorXd x_bar_(x_size_); - for (int i = 0; i < x_size_; ++i) { - x_bar_[i] = x_[i]->adj_; - } + // Compute (transpose of) specificities with respect to x. + VectorXd x_bar_(x_size_); + for (int i = 0; i < x_size_; ++i) { + x_bar_[i] = x_[i]->adj_; + } - // Contract specificities with inverse Jacobian of f with respect to x. - VectorXd eta_ = -Jf_x_.transpose().fullPivLu().solve(x_bar_); + // Contract specificities with inverse Jacobian of f with respect to x. + VectorXd eta_ = -Jf_x_.transpose().fullPivLu().solve(x_bar_); - // Contract with Jacobian of f with respect to y using a nested reverse - // autodiff pass. - { - stan::math::nested_rev_autodiff rev; - Matrix y_nrad_ = y_val_; - auto x_nrad_ = stan::math::eval(fy_(y_nrad_)); - x_nrad_.adj() = eta_; - stan::math::grad(); + // Contract with Jacobian of f with respect to y using a nested reverse + // autodiff pass. + { + stan::math::nested_rev_autodiff rev; + Matrix y_nrad_ = y_val_; + auto x_nrad_ = stan::math::eval(fy_(y_nrad_)); + x_nrad_.adj() = eta_; + stan::math::grad(); + } } -} }; // namespace math /** @@ -355,7 +354,7 @@ Eigen::VectorXd algebra_solver_powell_( return theta_dbl; } -} // namespace stan +} // namespace math } // namespace stan #endif From 4b2097811d519ce7e897f005d099be740deca3c8 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Mon, 22 Mar 2021 20:41:13 -0700 Subject: [PATCH 12/88] Revert to passing y_eval instead of y_val to algebra_solver_var constructor. --- stan/math/rev/functor/algebra_solver_newton.hpp | 2 +- stan/math/rev/functor/algebra_solver_powell.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index 4edb122204b..19e5c3df94c 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -145,7 +145,7 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_newton( // Construct vari algebra_solver_vari, Fx>* vi0 - = new algebra_solver_vari, Fx>(fy, y_val, fx, + = new algebra_solver_vari, Fx>(fy, y_eval, fx, theta_dbl); Eigen::Matrix theta(x.size()); theta(0) = var(vi0->x_[0]); diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 2442f6f86ff..726f8602fa7 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -249,7 +249,7 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_powell( // Construct vari algebra_solver_vari, Fx>* vi0 - = new algebra_solver_vari, Fx>(fy, y_val, fx, + = new algebra_solver_vari, Fx>(fy, y_eval, fx, theta_dbl); Eigen::Matrix theta(x.size()); theta(0) = var(vi0->x_[0]); From 03d39d1106d87bad78562cb94de698a2d7cdcf45 Mon Sep 17 00:00:00 2001 From: Ben Date: Tue, 23 Mar 2021 16:48:37 -0400 Subject: [PATCH 13/88] Lots of updates to the algebra solver! --- .../rev/functor/algebra_solver_newton.hpp | 4 +- .../rev/functor/algebra_solver_powell.hpp | 81 ++++++++++++++----- stan/math/rev/functor/algebra_system.hpp | 13 +-- .../math/rev/functor/algebra_solver_test.cpp | 15 +++- .../math/rev/functor/util_algebra_solver.hpp | 13 +-- 5 files changed, 83 insertions(+), 43 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index 9d6efdf976b..7b7f3b63a35 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -126,7 +126,7 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_newton( const std::vector& dat_int, std::ostream* msgs = nullptr, double scaling_step_size = 1e-3, double function_tolerance = 1e-6, long int max_num_steps = 200) { // NOLINT(runtime/int) - +/* const auto& x_eval = x.eval(); const auto& y_eval = y.eval(); const auto& x_val = (value_of(x_eval)).eval(); @@ -153,7 +153,7 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_newton( theta(i) = var(vi0->x_[i]); } - return theta; + return theta;*/ } } // namespace math diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index a8015d9eec4..a1526c29f85 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -150,7 +150,7 @@ Eigen::VectorXd algebra_solver_powell( std::ostream* msgs = nullptr, double relative_tolerance = 1e-10, double function_tolerance = 1e-6, long int max_num_steps = 1e+3) { // NOLINT(runtime/int) - const auto& x_eval = x.eval(); + /*const auto& x_eval = x.eval(); const auto& x_val = (value_of(x_eval)).eval(); algebra_solver_check(x_val, y, dat, dat_int, function_tolerance, max_num_steps); @@ -169,7 +169,7 @@ Eigen::VectorXd algebra_solver_powell( // Solve the system return algebra_solver_powell_(solver, fx, x, y, dat, dat_int, msgs, relative_tolerance, function_tolerance, - max_num_steps); + max_num_steps);*/ } /** @@ -226,19 +226,24 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_powell( double relative_tolerance = 1e-10, double function_tolerance = 1e-6, long int max_num_steps = 1e+3) { // NOLINT(runtime/int) const auto& x_eval = x.eval(); - const auto& y_eval = y.eval(); + auto arena_y = to_arena(y); + auto arena_dat = to_arena(dat); + auto arena_dat_int = to_arena(dat_int); const auto& x_val = (value_of(x_eval)).eval(); - const auto& y_val = (value_of(y_eval)).eval(); + const auto& y_val = (value_of(arena_y)).eval(); algebra_solver_check(x_val, y, dat, dat_int, function_tolerance, max_num_steps); check_nonnegative("alegbra_solver", "relative_tolerance", relative_tolerance); // Construct the Powell solver - using Fsx = system_functor; - using Fx = hybrj_functor_solver; - Fx fx(Fsx(), f, x_val, y_val, dat, dat_int, msgs); - Eigen::HybridNonLinearSolver solver(fx); + + auto myfunc = [&](const auto& x) { + return f(x, y_val, dat, dat_int, msgs); + }; + + hybrj_functor_solver fx(myfunc); + Eigen::HybridNonLinearSolver solver(fx); // Check dimension unknowns equals dimension of system output check_matching_sizes("algebra_solver", "the algebraic system's output", @@ -249,21 +254,53 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_powell( solver, fx, x_eval, y_val, dat, dat_int, 0, relative_tolerance, function_tolerance, max_num_steps); - using Fsy = system_functor; - using Fy = hybrj_functor_solver; - Fsy fy(f, x_val, y_val, dat, dat_int, msgs); - - // Construct vari - algebra_solver_vari, Fx>* vi0 - = new algebra_solver_vari, Fx>(fy, y_val, fx, - theta_dbl); - Eigen::Matrix theta(x.size()); - theta(0) = var(vi0->x_[0]); - for (int i = 1; i < x.size(); ++i) { - theta(i) = var(vi0->x_[i]); - } + Eigen::MatrixXd Jf_x; + Eigen::VectorXd f_x; + + jacobian(myfunc, theta_dbl, f_x, Jf_x); + + using ret_type = Eigen::Matrix; + auto arena_Jf_x = to_arena(Jf_x); + + arena_t ret = theta_dbl; + + reverse_pass_callback([f, ret, arena_y, arena_dat, arena_dat_int, arena_Jf_x, msgs]() mutable { + using Eigen::Dynamic; + using Eigen::Matrix; + using Eigen::MatrixXd; + using Eigen::VectorXd; + + // Contract specificities with inverse Jacobian of f with respect to x. + std::cout << "ret_adj: " << ret.adj().transpose() << std::endl; + std::cout << "Jfx: " << arena_Jf_x << std::endl; + VectorXd ret_adj = ret.adj(); + VectorXd eta = -arena_Jf_x.transpose().fullPivLu().solve(ret_adj); + + std::cout << "eta: " << eta.transpose() << std::endl; + + // Contract with Jacobian of f with respect to y using a nested reverse + // autodiff pass. + { + stan::math::nested_rev_autodiff rev; + Matrix y_nrad_ = arena_y.val(); + + std::cout << "y_val: " << arena_y.val().transpose() << std::endl; + VectorXd ret_val = ret.val(); + std::cout << "ret_val: " << ret_val.transpose() << std::endl; + auto x_nrad_ = stan::math::eval(f(ret_val, y_nrad_, arena_dat, arena_dat_int, msgs)); + std::cout << "x_nrad: " << x_nrad_.val().transpose() << std::endl; + //auto x_nrad_ = stan::math::eval(fy_(y_nrad_)); + x_nrad_.adj() = eta; + std::cout << "y_adj1: " << arena_y.adj().transpose() << std::endl; + stan::math::grad(); + std::cout << "y_adj2: " << arena_y.adj().transpose() << std::endl; + std::cout << "y_nrad_: " << y_nrad_.adj().transpose() << std::endl; + arena_y.adj() += y_nrad_.adj(); + std::cout << "y_adj3: " << arena_y.adj().transpose() << std::endl; + } + }); - return theta; + return ret_type(ret); } /** diff --git a/stan/math/rev/functor/algebra_system.hpp b/stan/math/rev/functor/algebra_system.hpp index f165ae56624..1e6cb9f7a52 100644 --- a/stan/math/rev/functor/algebra_system.hpp +++ b/stan/math/rev/functor/algebra_system.hpp @@ -97,21 +97,16 @@ struct nlo_functor { * @tparam T0 scalar type for unknowns * @tparam T1 scalar type for auxiliary parameters */ -template +template struct hybrj_functor_solver : nlo_functor { /** Wrapper around algebraic system */ S fs_; - /** number of unknowns */ - int x_size_; + /** Jacobian of algebraic function wrt unknowns */ Eigen::MatrixXd J_; - hybrj_functor_solver(const S& fs, const F& f, - const Eigen::Matrix& x, - const Eigen::Matrix& y, - const std::vector& dat, - const std::vector& dat_int, std::ostream* msgs) - : fs_(f, x, y, dat, dat_int, msgs), x_size_(x.size()) {} + hybrj_functor_solver(const S& fs) + : fs_(fs) {} /** * Computes the value the algebraic function, f, when pluging in the diff --git a/test/unit/math/rev/functor/algebra_solver_test.cpp b/test/unit/math/rev/functor/algebra_solver_test.cpp index 9cba2eb9f86..b7ba89e88d0 100644 --- a/test/unit/math/rev/functor/algebra_solver_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_test.cpp @@ -44,7 +44,7 @@ class algebra_solver_simple_eq_test : public ::testing::Test { Eigen::MatrixXd J; }; -class algebra_solver_simple_eq_nopara_test : public ::testing::Test { +/*class algebra_solver_simple_eq_nopara_test : public ::testing::Test { protected: void SetUp() override { x = stan::math::to_vector({1, 1}); } @@ -122,7 +122,7 @@ class degenerate_eq_test : public ::testing::Test { Eigen::MatrixXd J2; std::vector dat; std::vector dat_int; -}; +};*/ ////////////////////////////////////////////////////////////////////////// // Tests for powell solver. @@ -136,16 +136,22 @@ TEST_F(algebra_solver_simple_eq_test, powell) { Eigen::Matrix theta = simple_eq_test(simple_eq_functor(), y, is_newton); + stan::math::set_zero_all_adjoints(); AVEC y_vec = createAVEC(y(0), y(1), y(2)); VEC g; theta(k).grad(y_vec, g); - for (int i = 0; i < n_y; i++) + std::cout << "Solution: " << theta.val() << std::endl; + + for (int i = 0; i < n_y; i++) { + std::cout << "i: " << i << ", k: " << k << std::endl; + std::cout << "J: " << J(k, i) << ", g: " << g[i] << std::endl; EXPECT_EQ(J(k, i), g[i]); + } } } -TEST_F(algebra_solver_simple_eq_test, powell_tuned) { +/*TEST_F(algebra_solver_simple_eq_test, powell_tuned) { using stan::math::var; bool is_newton = false; for (int k = 0; k < n_x; k++) { @@ -589,3 +595,4 @@ TEST_F(degenerate_eq_test, newton_guess_saddle_point_dbl) { y_scale, dat, dat_int), std::runtime_error, msg); } +*/ diff --git a/test/unit/math/rev/functor/util_algebra_solver.hpp b/test/unit/math/rev/functor/util_algebra_solver.hpp index 1ae2c426ce1..2e2df39c69d 100644 --- a/test/unit/math/rev/functor/util_algebra_solver.hpp +++ b/test/unit/math/rev/functor/util_algebra_solver.hpp @@ -33,13 +33,14 @@ Eigen::Matrix general_algebra_solver( /* define algebraic functions which get solved. */ struct simple_eq_functor { - template - inline Eigen::Matrix, Eigen::Dynamic, 1> - operator()(const Eigen::Matrix& x, - const Eigen::Matrix& y, - const std::vector& dat, const std::vector& dat_int, + template + inline Eigen::Matrix, Eigen::Dynamic, 1> + operator()(const T1& x, + const T2& y, + const T3& dat, + const T4& dat_int, std::ostream* pstream__) const { - Eigen::Matrix, Eigen::Dynamic, 1> z(2); + Eigen::Matrix, Eigen::Dynamic, 1> z(2); z(0) = x(0) - y(0) * y(1); z(1) = x(1) - y(2); return z; From 4848150b454d421fba117d6e25ae8d1df8f48ea8 Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Tue, 23 Mar 2021 21:06:09 +0000 Subject: [PATCH 14/88] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- .../rev/functor/algebra_solver_newton.hpp | 48 ++++++------ .../rev/functor/algebra_solver_powell.hpp | 76 +++++++++---------- stan/math/rev/functor/algebra_system.hpp | 3 +- .../math/rev/functor/util_algebra_solver.hpp | 5 +- 4 files changed, 64 insertions(+), 68 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index fcd787431d5..c6830b9f447 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -126,34 +126,34 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_newton( const std::vector& dat_int, std::ostream* msgs = nullptr, double scaling_step_size = 1e-3, double function_tolerance = 1e-6, long int max_num_steps = 200) { // NOLINT(runtime/int) -/* - const auto& x_eval = x.eval(); - const auto& y_eval = y.eval(); - const auto& x_val = (value_of(x_eval)).eval(); - const auto& y_val = (value_of(y_eval)).eval(); - Eigen::VectorXd theta_dbl = algebra_solver_newton( - f, x_eval, value_of(y_eval), dat, dat_int, msgs, scaling_step_size, - function_tolerance, max_num_steps); + /* + const auto& x_eval = x.eval(); + const auto& y_eval = y.eval(); + const auto& x_val = (value_of(x_eval)).eval(); + const auto& y_val = (value_of(y_eval)).eval(); + Eigen::VectorXd theta_dbl = algebra_solver_newton( + f, x_eval, value_of(y_eval), dat, dat_int, msgs, scaling_step_size, + function_tolerance, max_num_steps); - typedef system_functor Fsx; - typedef hybrj_functor_solver Fx; - Fx fx(Fsx(), f, x_val, y_val, dat, dat_int, msgs); + typedef system_functor Fsx; + typedef hybrj_functor_solver Fx; + Fx fx(Fsx(), f, x_val, y_val, dat, dat_int, msgs); - typedef system_functor Fsy; - typedef hybrj_functor_solver Fy; - Fsy fy(f, value_of(x_eval), value_of(y_eval), dat, dat_int, msgs); + typedef system_functor Fsy; + typedef hybrj_functor_solver Fy; + Fsy fy(f, value_of(x_eval), value_of(y_eval), dat, dat_int, msgs); - // Construct vari - algebra_solver_vari, Fx>* vi0 - = new algebra_solver_vari, Fx>(fy, y_eval, fx, - theta_dbl); - Eigen::Matrix theta(x.size()); - theta(0) = var(vi0->x_[0]); - for (int i = 1; i < x.size(); ++i) { - theta(i) = var(vi0->x_[i]); - } + // Construct vari + algebra_solver_vari, Fx>* vi0 + = new algebra_solver_vari, Fx>(fy, y_eval, fx, + theta_dbl); + Eigen::Matrix theta(x.size()); + theta(0) = var(vi0->x_[0]); + for (int i = 1; i < x.size(); ++i) { + theta(i) = var(vi0->x_[i]); + } - return theta;*/ + return theta;*/ } } // namespace math diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 034650e6d45..774707247a3 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -232,9 +232,7 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_powell( // Construct the Powell solver - auto myfunc = [&](const auto& x) { - return f(x, y_val, dat, dat_int, msgs); - }; + auto myfunc = [&](const auto& x) { return f(x, y_val, dat, dat_int, msgs); }; hybrj_functor_solver fx(myfunc); Eigen::HybridNonLinearSolver solver(fx); @@ -258,41 +256,43 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_powell( arena_t ret = theta_dbl; - reverse_pass_callback([f, ret, arena_y, arena_dat, arena_dat_int, arena_Jf_x, msgs]() mutable { - using Eigen::Dynamic; - using Eigen::Matrix; - using Eigen::MatrixXd; - using Eigen::VectorXd; - - // Contract specificities with inverse Jacobian of f with respect to x. - std::cout << "ret_adj: " << ret.adj().transpose() << std::endl; - std::cout << "Jfx: " << arena_Jf_x << std::endl; - VectorXd ret_adj = ret.adj(); - VectorXd eta = -arena_Jf_x.transpose().fullPivLu().solve(ret_adj); - - std::cout << "eta: " << eta.transpose() << std::endl; - - // Contract with Jacobian of f with respect to y using a nested reverse - // autodiff pass. - { - stan::math::nested_rev_autodiff rev; - Matrix y_nrad_ = arena_y.val(); - - std::cout << "y_val: " << arena_y.val().transpose() << std::endl; - VectorXd ret_val = ret.val(); - std::cout << "ret_val: " << ret_val.transpose() << std::endl; - auto x_nrad_ = stan::math::eval(f(ret_val, y_nrad_, arena_dat, arena_dat_int, msgs)); - std::cout << "x_nrad: " << x_nrad_.val().transpose() << std::endl; - //auto x_nrad_ = stan::math::eval(fy_(y_nrad_)); - x_nrad_.adj() = eta; - std::cout << "y_adj1: " << arena_y.adj().transpose() << std::endl; - stan::math::grad(); - std::cout << "y_adj2: " << arena_y.adj().transpose() << std::endl; - std::cout << "y_nrad_: " << y_nrad_.adj().transpose() << std::endl; - arena_y.adj() += y_nrad_.adj(); - std::cout << "y_adj3: " << arena_y.adj().transpose() << std::endl; - } - }); + reverse_pass_callback( + [f, ret, arena_y, arena_dat, arena_dat_int, arena_Jf_x, msgs]() mutable { + using Eigen::Dynamic; + using Eigen::Matrix; + using Eigen::MatrixXd; + using Eigen::VectorXd; + + // Contract specificities with inverse Jacobian of f with respect to x. + std::cout << "ret_adj: " << ret.adj().transpose() << std::endl; + std::cout << "Jfx: " << arena_Jf_x << std::endl; + VectorXd ret_adj = ret.adj(); + VectorXd eta = -arena_Jf_x.transpose().fullPivLu().solve(ret_adj); + + std::cout << "eta: " << eta.transpose() << std::endl; + + // Contract with Jacobian of f with respect to y using a nested reverse + // autodiff pass. + { + stan::math::nested_rev_autodiff rev; + Matrix y_nrad_ = arena_y.val(); + + std::cout << "y_val: " << arena_y.val().transpose() << std::endl; + VectorXd ret_val = ret.val(); + std::cout << "ret_val: " << ret_val.transpose() << std::endl; + auto x_nrad_ = stan::math::eval( + f(ret_val, y_nrad_, arena_dat, arena_dat_int, msgs)); + std::cout << "x_nrad: " << x_nrad_.val().transpose() << std::endl; + // auto x_nrad_ = stan::math::eval(fy_(y_nrad_)); + x_nrad_.adj() = eta; + std::cout << "y_adj1: " << arena_y.adj().transpose() << std::endl; + stan::math::grad(); + std::cout << "y_adj2: " << arena_y.adj().transpose() << std::endl; + std::cout << "y_nrad_: " << y_nrad_.adj().transpose() << std::endl; + arena_y.adj() += y_nrad_.adj(); + std::cout << "y_adj3: " << arena_y.adj().transpose() << std::endl; + } + }); return ret_type(ret); } diff --git a/stan/math/rev/functor/algebra_system.hpp b/stan/math/rev/functor/algebra_system.hpp index 1e6cb9f7a52..c88c5b27783 100644 --- a/stan/math/rev/functor/algebra_system.hpp +++ b/stan/math/rev/functor/algebra_system.hpp @@ -105,8 +105,7 @@ struct hybrj_functor_solver : nlo_functor { /** Jacobian of algebraic function wrt unknowns */ Eigen::MatrixXd J_; - hybrj_functor_solver(const S& fs) - : fs_(fs) {} + hybrj_functor_solver(const S& fs) : fs_(fs) {} /** * Computes the value the algebraic function, f, when pluging in the diff --git a/test/unit/math/rev/functor/util_algebra_solver.hpp b/test/unit/math/rev/functor/util_algebra_solver.hpp index 2e2df39c69d..2ebb8a7131f 100644 --- a/test/unit/math/rev/functor/util_algebra_solver.hpp +++ b/test/unit/math/rev/functor/util_algebra_solver.hpp @@ -35,10 +35,7 @@ Eigen::Matrix general_algebra_solver( struct simple_eq_functor { template inline Eigen::Matrix, Eigen::Dynamic, 1> - operator()(const T1& x, - const T2& y, - const T3& dat, - const T4& dat_int, + operator()(const T1& x, const T2& y, const T3& dat, const T4& dat_int, std::ostream* pstream__) const { Eigen::Matrix, Eigen::Dynamic, 1> z(2); z(0) = x(0) - y(0) * y(1); From b8804d389e4f35be575a121a3b1a8e108b8cf0cb Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 24 Mar 2021 13:48:09 -0400 Subject: [PATCH 15/88] Variadic stuff --- .../rev/functor/algebra_solver_powell.hpp | 73 ++++++++++--------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 034650e6d45..a2d0a377b08 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -211,29 +212,31 @@ Eigen::VectorXd algebra_solver_powell( * @throw std::domain_error if the norm of the solution exceeds * the function tolerance. */ -template * = nullptr, - require_st_var* = nullptr> -Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_powell( - const F& f, const T1& x, const T2& y, const std::vector& dat, - const std::vector& dat_int, std::ostream* msgs = nullptr, - double relative_tolerance = 1e-10, double function_tolerance = 1e-6, - long int max_num_steps = 1e+3) { // NOLINT(runtime/int) +template * = nullptr, + require_any_st_var* = nullptr> +Eigen::Matrix algebra_solver_powell_impl( + const F& f, const T1& x, std::ostream* msgs, + double relative_tolerance, double function_tolerance, + long int max_num_steps, const T_Args&... args) { // NOLINT(runtime/int) const auto& x_eval = x.eval(); - auto arena_y = to_arena(y); - auto arena_dat = to_arena(dat); - auto arena_dat_int = to_arena(dat_int); + + auto arena_args_tuple = std::make_tuple(to_arena(args)...); + const auto& x_val = (value_of(x_eval)).eval(); - const auto& y_val = (value_of(arena_y)).eval(); - algebra_solver_check(x_val, y, dat, dat_int, function_tolerance, - max_num_steps); + auto args_vals_tuple = std::make_tuple(eval(value_of(args))...); + + //algebra_solver_check(x_val, y, dat, dat_int, function_tolerance, + // max_num_steps); check_nonnegative("alegbra_solver", "relative_tolerance", relative_tolerance); // Construct the Powell solver auto myfunc = [&](const auto& x) { - return f(x, y_val, dat, dat_int, msgs); + return apply([&](const auto&... args) { + return f(x, msgs, args...); + }, args_vals_tuple); }; hybrj_functor_solver fx(myfunc); @@ -245,7 +248,7 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_powell( // Solve the system Eigen::VectorXd theta_dbl = algebra_solver_powell_( - solver, fx, x_eval, y_val, dat, dat_int, 0, relative_tolerance, + solver, fx, x_eval, 0, relative_tolerance, function_tolerance, max_num_steps); Eigen::MatrixXd Jf_x; @@ -258,45 +261,44 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_powell( arena_t ret = theta_dbl; - reverse_pass_callback([f, ret, arena_y, arena_dat, arena_dat_int, arena_Jf_x, msgs]() mutable { + reverse_pass_callback([f, ret, arena_args_tuple, arena_Jf_x, msgs]() mutable { using Eigen::Dynamic; using Eigen::Matrix; using Eigen::MatrixXd; using Eigen::VectorXd; // Contract specificities with inverse Jacobian of f with respect to x. - std::cout << "ret_adj: " << ret.adj().transpose() << std::endl; - std::cout << "Jfx: " << arena_Jf_x << std::endl; VectorXd ret_adj = ret.adj(); VectorXd eta = -arena_Jf_x.transpose().fullPivLu().solve(ret_adj); - std::cout << "eta: " << eta.transpose() << std::endl; - // Contract with Jacobian of f with respect to y using a nested reverse // autodiff pass. { - stan::math::nested_rev_autodiff rev; - Matrix y_nrad_ = arena_y.val(); + nested_rev_autodiff rev; - std::cout << "y_val: " << arena_y.val().transpose() << std::endl; VectorXd ret_val = ret.val(); - std::cout << "ret_val: " << ret_val.transpose() << std::endl; - auto x_nrad_ = stan::math::eval(f(ret_val, y_nrad_, arena_dat, arena_dat_int, msgs)); - std::cout << "x_nrad: " << x_nrad_.val().transpose() << std::endl; - //auto x_nrad_ = stan::math::eval(fy_(y_nrad_)); + auto x_nrad_ = apply([&](const auto&...args) { + return eval(f(ret_val, msgs, args...)); + }, arena_args_tuple); x_nrad_.adj() = eta; - std::cout << "y_adj1: " << arena_y.adj().transpose() << std::endl; - stan::math::grad(); - std::cout << "y_adj2: " << arena_y.adj().transpose() << std::endl; - std::cout << "y_nrad_: " << y_nrad_.adj().transpose() << std::endl; - arena_y.adj() += y_nrad_.adj(); - std::cout << "y_adj3: " << arena_y.adj().transpose() << std::endl; + grad(); } }); return ret_type(ret); } +template * = nullptr> +Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_powell( + const F& f, const T1& x, const T2& y, const std::vector& dat, + const std::vector& dat_int, std::ostream* msgs = nullptr, + double relative_tolerance = 1e-10, double function_tolerance = 1e-6, + long int max_num_steps = 1e+3) { // NOLINT(runtime/int) + return algebra_solver_powell_impl(f, x, y, dat, dat_int, msgs, relative_tolerance, + function_tolerance, max_num_steps); +} + /** * Return the solution to the specified system of algebraic * equations given an initial guess, and parameters and data, @@ -356,8 +358,7 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver( template * = nullptr> Eigen::VectorXd algebra_solver_powell_( - S& solver, const F& fx, const T& x, const Eigen::VectorXd& y, - const std::vector& dat, const std::vector& dat_int, + S& solver, const F& fx, const T& x, std::ostream* msgs, double relative_tolerance, double function_tolerance, long int max_num_steps) { // NOLINT(runtime/int) const auto& x_eval = x.eval(); From a68bd4ed7f90f67354fa67c23c13dc9200a8ca40 Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Wed, 24 Mar 2021 20:28:05 +0000 Subject: [PATCH 16/88] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- .../rev/functor/algebra_solver_powell.hpp | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 87126aef787..f09458d5f59 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -216,9 +216,9 @@ template * = nullptr, require_any_st_var* = nullptr> Eigen::Matrix algebra_solver_powell_impl( - const F& f, const T1& x, std::ostream* msgs, - double relative_tolerance, double function_tolerance, - long int max_num_steps, const T_Args&... args) { // NOLINT(runtime/int) + const F& f, const T1& x, std::ostream* msgs, double relative_tolerance, + double function_tolerance, long int max_num_steps, + const T_Args&... args) { // NOLINT(runtime/int) const auto& x_eval = x.eval(); auto arena_args_tuple = std::make_tuple(to_arena(args)...); @@ -227,7 +227,7 @@ Eigen::Matrix algebra_solver_powell_impl( auto args_vals_tuple = std::make_tuple(eval(value_of(args))...); - //algebra_solver_check(x_val, y, dat, dat_int, function_tolerance, + // algebra_solver_check(x_val, y, dat, dat_int, function_tolerance, // max_num_steps); check_nonnegative("alegbra_solver", "relative_tolerance", relative_tolerance); @@ -235,9 +235,8 @@ Eigen::Matrix algebra_solver_powell_impl( <<<<<<< HEAD auto myfunc = [&](const auto& x) { - return apply([&](const auto&... args) { - return f(x, msgs, args...); - }, args_vals_tuple); + return apply([&](const auto&... args) { return f(x, msgs, args...); }, + args_vals_tuple); }; ======= auto myfunc = [&](const auto& x) { return f(x, y_val, dat, dat_int, msgs); }; @@ -251,9 +250,9 @@ Eigen::Matrix algebra_solver_powell_impl( fx.get_value(x_val), "the vector of unknowns, x,", x); // Solve the system - Eigen::VectorXd theta_dbl = algebra_solver_powell_( - solver, fx, x_eval, 0, relative_tolerance, - function_tolerance, max_num_steps); + Eigen::VectorXd theta_dbl + = algebra_solver_powell_(solver, fx, x_eval, 0, relative_tolerance, + function_tolerance, max_num_steps); Eigen::MatrixXd Jf_x; Eigen::VectorXd f_x; @@ -281,9 +280,9 @@ Eigen::Matrix algebra_solver_powell_impl( nested_rev_autodiff rev; VectorXd ret_val = ret.val(); - auto x_nrad_ = apply([&](const auto&...args) { - return eval(f(ret_val, msgs, args...)); - }, arena_args_tuple); + auto x_nrad_ = apply( + [&](const auto&... args) { return eval(f(ret_val, msgs, args...)); }, + arena_args_tuple); x_nrad_.adj() = eta; grad(); } @@ -299,8 +298,9 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_powell( const std::vector& dat_int, std::ostream* msgs = nullptr, double relative_tolerance = 1e-10, double function_tolerance = 1e-6, long int max_num_steps = 1e+3) { // NOLINT(runtime/int) - return algebra_solver_powell_impl(f, x, y, dat, dat_int, msgs, relative_tolerance, - function_tolerance, max_num_steps); + return algebra_solver_powell_impl(f, x, y, dat, dat_int, msgs, + relative_tolerance, function_tolerance, + max_num_steps); } /** @@ -362,8 +362,8 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver( template * = nullptr> Eigen::VectorXd algebra_solver_powell_( - S& solver, const F& fx, const T& x, - std::ostream* msgs, double relative_tolerance, double function_tolerance, + S& solver, const F& fx, const T& x, std::ostream* msgs, + double relative_tolerance, double function_tolerance, long int max_num_steps) { // NOLINT(runtime/int) const auto& x_eval = x.eval(); const auto& x_val = (value_of(x_eval)).eval(); From 19bea289a052ac6ea9e54d314f6f9a0923235793 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Wed, 31 Mar 2021 09:27:50 -0700 Subject: [PATCH 17/88] Add adapter. --- .../rev/functor/algebra_solver_powell.hpp | 105 ++++-------------- 1 file changed, 22 insertions(+), 83 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index f09458d5f59..56dc8119619 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -17,82 +17,6 @@ namespace stan { namespace math { -/** - * The vari class for the algebraic solver. We compute the Jacobian of - * the solutions with respect to the parameters using the implicit - * function theorem. The call to Jacobian() occurs outside the call to - * chain() -- this prevents malloc issues. - */ -template -struct algebra_solver_vari : public vari { - /** number of parameters */ - int y_size_; - /** value of parameters */ - arena_t> y_val_; - /** System functor (f_) w.r.t. inputs (y_) */ - Fy fy_; - /** array of parameters */ - vari** y_; - /** number of unknowns */ - int x_size_; - /** value of unknowns */ - arena_t x_val_; - /** array of unknowns */ - vari** x_; - /** Jacobian of f w.r.t. outputs (x_) */ - const Eigen::MatrixXd Jf_x_; - - algebra_solver_vari(Fy& fy, const Eigen::Matrix& y, - Fx& fx, const Eigen::VectorXd& x) - : vari(x(0)), - y_size_(y.size()), - y_val_(y), - fy_(fy), - y_(ChainableStack::instance_->memalloc_.alloc_array(y_size_)), - x_size_(x.size()), - x_val_(x), - Jf_x_(fx.get_jacobian(x_val_)), - x_(ChainableStack::instance_->memalloc_.alloc_array(x_size_)) { - using Eigen::Map; - using Eigen::MatrixXd; - - for (int i = 0; i < y_size_; ++i) { - y_[i] = y(i).vi_; - } - - x_[0] = this; - for (int i = 1; i < x_size_; ++i) { - x_[i] = new vari(x(i), false); - } - } - - void chain() { - using Eigen::Dynamic; - using Eigen::Matrix; - using Eigen::MatrixXd; - using Eigen::VectorXd; - - // Compute (transpose of) specificities with respect to x. - VectorXd x_bar_(x_size_); - for (int i = 0; i < x_size_; ++i) { - x_bar_[i] = x_[i]->adj_; - } - - // Contract specificities with inverse Jacobian of f with respect to x. - VectorXd eta_ = -Jf_x_.transpose().fullPivLu().solve(x_bar_); - - // Contract with Jacobian of f with respect to y using a nested reverse - // autodiff pass. - { - stan::math::nested_rev_autodiff rev; - Matrix y_nrad_ = y_val_; - auto x_nrad_ = stan::math::eval(fy_(y_nrad_)); - x_nrad_.adj() = eta_; - stan::math::grad(); - } - } -}; // namespace math - /** * Return the solution to the specified system of algebraic * equations given an initial guess, and parameters and data, @@ -232,15 +156,10 @@ Eigen::Matrix algebra_solver_powell_impl( check_nonnegative("alegbra_solver", "relative_tolerance", relative_tolerance); // Construct the Powell solver - -<<<<<<< HEAD auto myfunc = [&](const auto& x) { return apply([&](const auto&... args) { return f(x, msgs, args...); }, args_vals_tuple); }; -======= - auto myfunc = [&](const auto& x) { return f(x, y_val, dat, dat_int, msgs); }; ->>>>>>> 4848150b454d421fba117d6e25ae8d1df8f48ea8 hybrj_functor_solver fx(myfunc); Eigen::HybridNonLinearSolver solver(fx); @@ -291,6 +210,26 @@ Eigen::Matrix algebra_solver_powell_impl( return ret_type(ret); } +/** + * Adapt the non-variadic algebra solver arguments to the variadic + * algebra_solver_powell_impl or algebra_solver_newton_impl interface. + * + * @param F Type of function to adapt. + */ +template +struct algebra_solver_adapter { + const F& f_; + + explicit algebra_solver_adapter(const F& f) : f_(f) {} + + template + auto operator()(const T1& x, std::ostream* msgs, const T2& y, + const std::vector& dat, + const std::vector& dat_int) const { + return f_(x, y, dat, dat_int, msgs); + } +}; + template * = nullptr> Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_powell( @@ -298,9 +237,9 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_powell( const std::vector& dat_int, std::ostream* msgs = nullptr, double relative_tolerance = 1e-10, double function_tolerance = 1e-6, long int max_num_steps = 1e+3) { // NOLINT(runtime/int) - return algebra_solver_powell_impl(f, x, y, dat, dat_int, msgs, + return algebra_solver_powell_impl(algebra_solver_adapter(f), x, msgs, relative_tolerance, function_tolerance, - max_num_steps); + max_num_steps, y, dat, dat_int); } /** From 0f2600470245501b17ecc2eea5299a92bc5a3b75 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Wed, 31 Mar 2021 15:36:28 -0700 Subject: [PATCH 18/88] Fix templating issue and remove print statements. --- stan/math/rev/functor/algebra_solver_powell.hpp | 5 ++--- test/unit/math/rev/functor/algebra_solver_test.cpp | 4 ---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 56dc8119619..ccf40a41dcd 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -222,10 +222,9 @@ struct algebra_solver_adapter { explicit algebra_solver_adapter(const F& f) : f_(f) {} - template + template auto operator()(const T1& x, std::ostream* msgs, const T2& y, - const std::vector& dat, - const std::vector& dat_int) const { + const T3& dat, const T4& dat_int) const { return f_(x, y, dat, dat_int, msgs); } }; diff --git a/test/unit/math/rev/functor/algebra_solver_test.cpp b/test/unit/math/rev/functor/algebra_solver_test.cpp index b7ba89e88d0..588fad1d05e 100644 --- a/test/unit/math/rev/functor/algebra_solver_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_test.cpp @@ -141,11 +141,7 @@ TEST_F(algebra_solver_simple_eq_test, powell) { VEC g; theta(k).grad(y_vec, g); - std::cout << "Solution: " << theta.val() << std::endl; - for (int i = 0; i < n_y; i++) { - std::cout << "i: " << i << ", k: " << k << std::endl; - std::cout << "J: " << J(k, i) << ", g: " << g[i] << std::endl; EXPECT_EQ(J(k, i), g[i]); } } From 7b859029d01f494c95cdf8bb5ffdfb6c160d40f6 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Wed, 31 Mar 2021 15:44:29 -0700 Subject: [PATCH 19/88] Add back specialization for when y is a vector of double instead of var. --- .../rev/functor/algebra_solver_powell.hpp | 73 +++++++++++-------- 1 file changed, 44 insertions(+), 29 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index ccf40a41dcd..0cf0cd781dc 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -17,6 +17,25 @@ namespace stan { namespace math { +/** + * Adapt the non-variadic algebra solver arguments to the variadic + * algebra_solver_powell_impl or algebra_solver_newton_impl interface. + * + * @param F Type of function to adapt. + */ +template +struct algebra_solver_adapter { + const F& f_; + + explicit algebra_solver_adapter(const F& f) : f_(f) {} + + template + auto operator()(const T1& x, std::ostream* msgs, const T2& y, + const T3& dat, const T4& dat_int) const { + return f_(x, y, dat, dat_int, msgs); + } +}; + /** * Return the solution to the specified system of algebraic * equations given an initial guess, and parameters and data, @@ -69,26 +88,40 @@ Eigen::VectorXd algebra_solver_powell( std::ostream* msgs = nullptr, double relative_tolerance = 1e-10, double function_tolerance = 1e-6, long int max_num_steps = 1e+3) { // NOLINT(runtime/int) - /*const auto& x_eval = x.eval(); + return algebra_solver_powell_impl(algebra_solver_adapter(f), x, msgs, + relative_tolerance, function_tolerance, + max_num_steps, y, dat, dat_int); +} + +template * = nullptr, + require_any_st_var* = nullptr> +Eigen::VectorXd algebra_solver_powell_impl( + const F& f, const T1& x, std::ostream* msgs, double relative_tolerance, + double function_tolerance, long int max_num_steps, const Eigen::VectorXd& y, + const T_Args&... args) { // NOLINT(runtime/int) + const auto& x_eval = x.eval(); const auto& x_val = (value_of(x_eval)).eval(); - algebra_solver_check(x_val, y, dat, dat_int, function_tolerance, - max_num_steps); + auto args_vals_tuple = std::make_tuple(eval(value_of(args))...); + check_nonnegative("alegbra_solver", "relative_tolerance", relative_tolerance); - // Create functor for algebraic system - using Fs = system_functor; - using Fx = hybrj_functor_solver; - Fx fx(Fs(), f, x_val, y, dat, dat_int, msgs); - Eigen::HybridNonLinearSolver solver(fx); + // Construct the Powell solver + auto myfunc = [&](const auto& x) { + return apply([&](const auto&... args) { return f(x, msgs, y, args...); }, + args_vals_tuple); + }; + + hybrj_functor_solver fx(myfunc); + Eigen::HybridNonLinearSolver solver(fx); // Check dimension unknowns equals dimension of system output check_matching_sizes("algebra_solver", "the algebraic system's output", fx.get_value(x_val), "the vector of unknowns, x,", x); // Solve the system - return algebra_solver_powell_(solver, fx, x, y, dat, dat_int, msgs, - relative_tolerance, function_tolerance, - max_num_steps);*/ + return algebra_solver_powell_(solver, fx, x_eval, 0, relative_tolerance, + function_tolerance, max_num_steps); } /** @@ -210,24 +243,6 @@ Eigen::Matrix algebra_solver_powell_impl( return ret_type(ret); } -/** - * Adapt the non-variadic algebra solver arguments to the variadic - * algebra_solver_powell_impl or algebra_solver_newton_impl interface. - * - * @param F Type of function to adapt. - */ -template -struct algebra_solver_adapter { - const F& f_; - - explicit algebra_solver_adapter(const F& f) : f_(f) {} - - template - auto operator()(const T1& x, std::ostream* msgs, const T2& y, - const T3& dat, const T4& dat_int) const { - return f_(x, y, dat, dat_int, msgs); - } -}; template * = nullptr> From 8421de0d61235e54d88ada15541009adf53a9083 Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Wed, 31 Mar 2021 22:59:12 +0000 Subject: [PATCH 20/88] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- stan/math/rev/functor/algebra_solver_powell.hpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 0cf0cd781dc..9b16ebcba6e 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -30,8 +30,8 @@ struct algebra_solver_adapter { explicit algebra_solver_adapter(const F& f) : f_(f) {} template - auto operator()(const T1& x, std::ostream* msgs, const T2& y, - const T3& dat, const T4& dat_int) const { + auto operator()(const T1& x, std::ostream* msgs, const T2& y, const T3& dat, + const T4& dat_int) const { return f_(x, y, dat, dat_int, msgs); } }; @@ -121,7 +121,7 @@ Eigen::VectorXd algebra_solver_powell_impl( // Solve the system return algebra_solver_powell_(solver, fx, x_eval, 0, relative_tolerance, - function_tolerance, max_num_steps); + function_tolerance, max_num_steps); } /** @@ -243,7 +243,6 @@ Eigen::Matrix algebra_solver_powell_impl( return ret_type(ret); } - template * = nullptr> Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_powell( From d2c7a964447891dafe40ddf2def523c9319a92b4 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Wed, 31 Mar 2021 16:12:15 -0700 Subject: [PATCH 21/88] Add back additional tests. (Message tests are broken now?) --- .../rev/functor/algebra_solver_powell.hpp | 7 ++- .../math/rev/functor/algebra_solver_test.cpp | 11 ++-- .../math/rev/functor/util_algebra_solver.hpp | 50 ++++++++----------- 3 files changed, 31 insertions(+), 37 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 0cf0cd781dc..f34fcff07f5 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -94,21 +94,20 @@ Eigen::VectorXd algebra_solver_powell( } template * = nullptr, - require_any_st_var* = nullptr> + require_all_eigen_vector_t* = nullptr> Eigen::VectorXd algebra_solver_powell_impl( const F& f, const T1& x, std::ostream* msgs, double relative_tolerance, double function_tolerance, long int max_num_steps, const Eigen::VectorXd& y, const T_Args&... args) { // NOLINT(runtime/int) const auto& x_eval = x.eval(); const auto& x_val = (value_of(x_eval)).eval(); - auto args_vals_tuple = std::make_tuple(eval(value_of(args))...); + auto args_vals_tuple = std::make_tuple(y, eval(value_of(args))...); check_nonnegative("alegbra_solver", "relative_tolerance", relative_tolerance); // Construct the Powell solver auto myfunc = [&](const auto& x) { - return apply([&](const auto&... args) { return f(x, msgs, y, args...); }, + return apply([&](const auto&... args) { return f(x, msgs, args...); }, args_vals_tuple); }; diff --git a/test/unit/math/rev/functor/algebra_solver_test.cpp b/test/unit/math/rev/functor/algebra_solver_test.cpp index 588fad1d05e..571ddb7c12a 100644 --- a/test/unit/math/rev/functor/algebra_solver_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_test.cpp @@ -44,7 +44,7 @@ class algebra_solver_simple_eq_test : public ::testing::Test { Eigen::MatrixXd J; }; -/*class algebra_solver_simple_eq_nopara_test : public ::testing::Test { +class algebra_solver_simple_eq_nopara_test : public ::testing::Test { protected: void SetUp() override { x = stan::math::to_vector({1, 1}); } @@ -71,6 +71,7 @@ class algebra_solver_non_linear_eq_test : public ::testing::Test { Eigen::MatrixXd J; }; +/* class error_message_test : public ::testing::Test { protected: void SetUp() override { @@ -81,6 +82,7 @@ class error_message_test : public ::testing::Test { Eigen::VectorXd y_2; Eigen::VectorXd y_3; }; +*/ class max_steps_test : public ::testing::Test { protected: @@ -122,7 +124,7 @@ class degenerate_eq_test : public ::testing::Test { Eigen::MatrixXd J2; std::vector dat; std::vector dat_int; -};*/ +}; ////////////////////////////////////////////////////////////////////////// // Tests for powell solver. @@ -147,7 +149,7 @@ TEST_F(algebra_solver_simple_eq_test, powell) { } } -/*TEST_F(algebra_solver_simple_eq_test, powell_tuned) { +TEST_F(algebra_solver_simple_eq_test, powell_tuned) { using stan::math::var; bool is_newton = false; for (int k = 0; k < n_x; k++) { @@ -223,6 +225,7 @@ TEST_F(algebra_solver_non_linear_eq_test, powell_dbl) { EXPECT_FLOAT_EQ(y_dbl(2), theta(2)); } +/* TEST_F(error_message_test, powell) { using stan::math::var; bool is_newton = false; @@ -234,6 +237,7 @@ TEST_F(error_message_test, powell_dbl) { bool is_newton = false; error_conditions_test(non_linear_eq_functor(), y_3, is_newton); } +*/ TEST(unsolvable_test, powell) { using stan::math::var; @@ -368,6 +372,7 @@ TEST(MathMatrixRevMat, system_functor_constructor) { ////////////////////////////////////////////////////////////////////////// // Tests for newton solver. +/* TEST_F(algebra_solver_simple_eq_test, newton) { using stan::math::var; bool is_newton = true; diff --git a/test/unit/math/rev/functor/util_algebra_solver.hpp b/test/unit/math/rev/functor/util_algebra_solver.hpp index 2ebb8a7131f..d907c7e1b14 100644 --- a/test/unit/math/rev/functor/util_algebra_solver.hpp +++ b/test/unit/math/rev/functor/util_algebra_solver.hpp @@ -45,13 +45,11 @@ struct simple_eq_functor { }; struct simple_eq_functor_nopara { - template - inline Eigen::Matrix, Eigen::Dynamic, 1> - operator()(const Eigen::Matrix& x, - const Eigen::Matrix& y, - const std::vector& dat, const std::vector& dat_int, + template + inline Eigen::Matrix, Eigen::Dynamic, 1> + operator()(const T1&x, const T2&y, const T3& dat, const T4& dat_int, std::ostream* pstream__) const { - Eigen::Matrix, Eigen::Dynamic, 1> z(2); + Eigen::Matrix, Eigen::Dynamic, 1> z(2); z(0) = x(0) - dat[0] * dat[1]; z(1) = x(1) - dat[2]; return z; @@ -59,13 +57,11 @@ struct simple_eq_functor_nopara { }; struct non_linear_eq_functor { - template - inline Eigen::Matrix, Eigen::Dynamic, 1> - operator()(const Eigen::Matrix& x, - const Eigen::Matrix& y, - const std::vector& dat, const std::vector& dat_int, + template + inline Eigen::Matrix, Eigen::Dynamic, 1> + operator()(const T1&x, const T2&y, const T3& dat, const T4& dat_int, std::ostream* pstream__) const { - Eigen::Matrix, Eigen::Dynamic, 1> z(3); + Eigen::Matrix, Eigen::Dynamic, 1> z(3); z(0) = x(2) - y(2); z(1) = x(0) * x(1) - y(0) * y(1); z(2) = x(2) / x(0) + y(2) / y(0); @@ -74,13 +70,11 @@ struct non_linear_eq_functor { }; struct non_square_eq_functor { - template - inline Eigen::Matrix, Eigen::Dynamic, 1> - operator()(const Eigen::Matrix& x, - const Eigen::Matrix& y, - const std::vector& dat, const std::vector& dat_int, + template + inline Eigen::Matrix, Eigen::Dynamic, 1> + operator()(const T1&x, const T2&y, const T3& dat, const T4& dat_int, std::ostream* pstream__) const { - Eigen::Matrix, Eigen::Dynamic, 1> z(2); + Eigen::Matrix, Eigen::Dynamic, 1> z(2); z(0) = x(0) - y(0); z(1) = x(1) * x(2) - y(1); return z; @@ -88,13 +82,11 @@ struct non_square_eq_functor { }; struct unsolvable_eq_functor { - template - inline Eigen::Matrix, Eigen::Dynamic, 1> - operator()(const Eigen::Matrix& x, - const Eigen::Matrix& y, - const std::vector& dat, const std::vector& dat_int, + template + inline Eigen::Matrix, Eigen::Dynamic, 1> + operator()(const T1& x, const T2& y, const T3& dat, const T4& dat_int, std::ostream* pstream__) const { - Eigen::Matrix, Eigen::Dynamic, 1> z(2); + Eigen::Matrix, Eigen::Dynamic, 1> z(2); z(0) = x(0) * x(0) + y(0); z(1) = x(1) * x(1) + y(1); return z; @@ -103,13 +95,11 @@ struct unsolvable_eq_functor { // Degenerate roots: each solution can either be y(0) or y(1). struct degenerate_eq_functor { - template - inline Eigen::Matrix, Eigen::Dynamic, 1> - operator()(const Eigen::Matrix& x, - const Eigen::Matrix& y, - const std::vector& dat, const std::vector& dat_int, + template + inline Eigen::Matrix, Eigen::Dynamic, 1> + operator()(const T1& x, const T2& y, const T3& dat, const T4& dat_int, std::ostream* pstream__) const { - Eigen::Matrix, Eigen::Dynamic, 1> z(2); + Eigen::Matrix, Eigen::Dynamic, 1> z(2); z(0) = (x(0) - y(0)) * (x(1) - y(1)); z(1) = (x(0) - y(1)) * (x(1) - y(0)); return z; From dd5e119ade13624c1a423cfc5b5b59ff98932111 Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Wed, 31 Mar 2021 23:15:41 +0000 Subject: [PATCH 22/88] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- test/unit/math/rev/functor/util_algebra_solver.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/unit/math/rev/functor/util_algebra_solver.hpp b/test/unit/math/rev/functor/util_algebra_solver.hpp index d907c7e1b14..4ea3e070b18 100644 --- a/test/unit/math/rev/functor/util_algebra_solver.hpp +++ b/test/unit/math/rev/functor/util_algebra_solver.hpp @@ -47,7 +47,7 @@ struct simple_eq_functor { struct simple_eq_functor_nopara { template inline Eigen::Matrix, Eigen::Dynamic, 1> - operator()(const T1&x, const T2&y, const T3& dat, const T4& dat_int, + operator()(const T1& x, const T2& y, const T3& dat, const T4& dat_int, std::ostream* pstream__) const { Eigen::Matrix, Eigen::Dynamic, 1> z(2); z(0) = x(0) - dat[0] * dat[1]; @@ -59,7 +59,7 @@ struct simple_eq_functor_nopara { struct non_linear_eq_functor { template inline Eigen::Matrix, Eigen::Dynamic, 1> - operator()(const T1&x, const T2&y, const T3& dat, const T4& dat_int, + operator()(const T1& x, const T2& y, const T3& dat, const T4& dat_int, std::ostream* pstream__) const { Eigen::Matrix, Eigen::Dynamic, 1> z(3); z(0) = x(2) - y(2); @@ -72,7 +72,7 @@ struct non_linear_eq_functor { struct non_square_eq_functor { template inline Eigen::Matrix, Eigen::Dynamic, 1> - operator()(const T1&x, const T2&y, const T3& dat, const T4& dat_int, + operator()(const T1& x, const T2& y, const T3& dat, const T4& dat_int, std::ostream* pstream__) const { Eigen::Matrix, Eigen::Dynamic, 1> z(2); z(0) = x(0) - y(0); From fe4191338c16f9df6cc7760fd3a344c322f5243b Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 2 Apr 2021 12:45:26 -0700 Subject: [PATCH 23/88] Make algebra_solver_newton double variadic --- .../rev/functor/algebra_solver_newton.hpp | 22 ++++- stan/math/rev/functor/kinsol_data.hpp | 86 ++++++++----------- stan/math/rev/functor/kinsol_solve.hpp | 21 ++--- .../math/rev/functor/algebra_solver_test.cpp | 12 +-- 4 files changed, 73 insertions(+), 68 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index c6830b9f447..ff534ac4de1 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -15,7 +15,18 @@ namespace stan { namespace math { +template +struct algebra_solver_adapter2 { + const F& f_; + explicit algebra_solver_adapter2(const F& f) : f_(f) {} + + template + auto operator()(const T1& x, std::ostream* msgs, const T2& y, const T3& dat, + const T4& dat_int) const { + return f_(x, y, dat, dat_int, msgs); + } +}; /** * Return the solution to the specified system of algebraic * equations given an initial guess, and parameters and data, @@ -70,8 +81,15 @@ Eigen::VectorXd algebra_solver_newton( value_of(f(x_eval, y, dat, dat_int, msgs)), "the vector of unknowns, x,", x); - return kinsol_solve(f, value_of(x_eval), y, dat, dat_int, 0, - scaling_step_size, function_tolerance, max_num_steps); + return kinsol_solve(algebra_solver_adapter2(f), + value_of(x_eval), + scaling_step_size, + function_tolerance, + max_num_steps, + 1, + 10, + KIN_LINESEARCH, + msgs, y, dat, dat_int); } /** diff --git a/stan/math/rev/functor/kinsol_data.hpp b/stan/math/rev/functor/kinsol_data.hpp index 5dd9b288338..f668a389f37 100644 --- a/stan/math/rev/functor/kinsol_data.hpp +++ b/stan/math/rev/functor/kinsol_data.hpp @@ -10,35 +10,11 @@ #include #include #include +#include namespace stan { namespace math { -/** - * Default Jacobian builder using reverse-mode autodiff. - */ -struct kinsol_J_f { - template - inline int operator()(const F& f, const Eigen::VectorXd& x, - const Eigen::VectorXd& y, - const std::vector& dat, - const std::vector& dat_int, std::ostream* msgs, - const double x_sun[], SUNMatrix J) const { - size_t N = x.size(); - const std::vector x_vec(x_sun, x_sun + N); - system_functor system(f, x, y, dat, dat_int, msgs); - Eigen::VectorXd fx; - Eigen::MatrixXd Jac; - jacobian(system, to_vector(x_vec), fx, Jac); - - for (int j = 0; j < Jac.cols(); j++) - for (int i = 0; i < Jac.rows(); i++) - SM_ELEMENT_D(J, i, j) = Jac(i, j); - - return 0; - } -}; - /** * KINSOL algebraic system data holder. * Based on cvodes_ode_data. @@ -47,18 +23,15 @@ struct kinsol_J_f { * @tparam F2 functor type for jacobian function. Default is 0. * If 0, use rev mode autodiff to compute the Jacobian. */ -template +template class kinsol_system_data { const F1& f_; - const F2& J_f_; const Eigen::VectorXd& x_; - const Eigen::VectorXd& y_; const size_t N_; - const std::vector& dat_; - const std::vector& dat_int_; std::ostream* msgs_; + std::tuple args_tuple; - typedef kinsol_system_data system_data; + typedef kinsol_system_data system_data; public: N_Vector nv_x_; @@ -67,17 +40,12 @@ class kinsol_system_data { void* kinsol_memory_; /* Constructor */ - kinsol_system_data(const F1& f, const F2& J_f, const Eigen::VectorXd& x, - const Eigen::VectorXd& y, const std::vector& dat, - const std::vector& dat_int, std::ostream* msgs) + kinsol_system_data(const F1& f, const Eigen::VectorXd& x, std::ostream* msgs, const Args&... args) : f_(f), - J_f_(J_f), x_(x), - y_(y), N_(x.size()), - dat_(dat), - dat_int_(dat_int), msgs_(msgs), + args_tuple(args...), nv_x_(N_VMake_Serial(N_, &to_array_1d(x_)[0])), J_(SUNDenseMatrix(N_, N_)), LS_(SUNLinSol_Dense(nv_x_, J_)), @@ -91,17 +59,17 @@ class kinsol_system_data { } /* Implements the user-defined function passed to KINSOL. */ - static int kinsol_f_system(N_Vector x, N_Vector f, void* user_data) { + static int kinsol_f_system(N_Vector x, N_Vector f_eval, void* user_data) { const system_data* explicit_system = static_cast(user_data); - Eigen::VectorXd x_eigen( - Eigen::Map(NV_DATA_S(x), explicit_system->N_)); + Eigen::VectorXd x_eigen(Eigen::Map(NV_DATA_S(x), explicit_system->N_)); + + Eigen::Map f_eval_map(N_VGetArrayPointer(f_eval), explicit_system->N_); - Eigen::Map(N_VGetArrayPointer(f), explicit_system->N_) - = explicit_system->f_(x_eigen, explicit_system->y_, - explicit_system->dat_, explicit_system->dat_int_, - explicit_system->msgs_); + f_eval_map = apply([&](const auto&... args) { + return explicit_system->f_(x_eigen, explicit_system->msgs_, args...); + }, explicit_system->args_tuple); return 0; } @@ -118,14 +86,32 @@ class kinsol_system_data { * https://computation.llnl.gov/sites/default/files/public/kin_guide-dev.pdf, * page 55. */ - static int kinsol_jacobian(N_Vector x, N_Vector f, SUNMatrix J, + static int kinsol_jacobian(N_Vector x, N_Vector f_eval, SUNMatrix J, void* user_data, N_Vector tmp1, N_Vector tmp2) { const system_data* explicit_system = static_cast(user_data); - return explicit_system->J_f_(explicit_system->f_, explicit_system->x_, - explicit_system->y_, explicit_system->dat_, - explicit_system->dat_int_, - explicit_system->msgs_, NV_DATA_S(x), J); + + Eigen::VectorXd x_eigen(Eigen::Map(NV_DATA_S(x), explicit_system->N_)); + Eigen::Map f_eval_map(N_VGetArrayPointer(f_eval), explicit_system->N_); + + auto myfunc = [&](const auto& x) { + return apply([&](const auto&... args) { + return explicit_system->f_(x, explicit_system->msgs_, args...); + }, explicit_system->args_tuple); + }; + + Eigen::MatrixXd Jf_x; + Eigen::VectorXd f_x; + + jacobian(myfunc, x_eigen, f_x, Jf_x); + + f_eval_map = f_x; + + for (int j = 0; j < Jf_x.cols(); j++) + for (int i = 0; i < Jf_x.rows(); i++) + SM_ELEMENT_D(J, i, j) = Jf_x(i, j); + + return 0; } }; diff --git a/stan/math/rev/functor/kinsol_solve.hpp b/stan/math/rev/functor/kinsol_solve.hpp index 3da9c825938..ecbe9ea4f2b 100644 --- a/stan/math/rev/functor/kinsol_solve.hpp +++ b/stan/math/rev/functor/kinsol_solve.hpp @@ -55,18 +55,19 @@ namespace math { * @throw std::runtime_error if Kinsol returns a * negative flag that is not due to hitting max_num_steps. */ -template +template Eigen::VectorXd kinsol_solve( - const F1& f, const Eigen::VectorXd& x, const Eigen::VectorXd& y, - const std::vector& dat, const std::vector& dat_int, - std::ostream* msgs = nullptr, double scaling_step_tol = 1e-3, - double function_tolerance = 1e-6, - long int max_num_steps = 200, // NOLINT(runtime/int) - bool custom_jacobian = 1, const F2& J_f = kinsol_J_f(), - int steps_eval_jacobian = 10, int global_line_search = KIN_LINESEARCH) { + const F1& f, const Eigen::VectorXd& x, + double scaling_step_tol, // = 1e-3 + double function_tolerance, // = 1e-6 + long int max_num_steps, // NOLINT(runtime/int) = 200 + bool custom_jacobian, // = 1 + int steps_eval_jacobian, // = 10 + int global_line_search, // = KIN_LINESEARCH + std::ostream* msgs, const Args&... args) { int N = x.size(); - typedef kinsol_system_data system_data; - system_data kinsol_data(f, J_f, x, y, dat, dat_int, msgs); + typedef kinsol_system_data system_data; + system_data kinsol_data(f, x, msgs, args...); check_flag_sundials(KINInit(kinsol_data.kinsol_memory_, &system_data::kinsol_f_system, kinsol_data.nv_x_), diff --git a/test/unit/math/rev/functor/algebra_solver_test.cpp b/test/unit/math/rev/functor/algebra_solver_test.cpp index 571ddb7c12a..b74e7150184 100644 --- a/test/unit/math/rev/functor/algebra_solver_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_test.cpp @@ -129,7 +129,7 @@ class degenerate_eq_test : public ::testing::Test { ////////////////////////////////////////////////////////////////////////// // Tests for powell solver. -TEST_F(algebra_solver_simple_eq_test, powell) { +/*TEST_F(algebra_solver_simple_eq_test, powell) { using stan::math::var; bool is_newton = false; for (int k = 0; k < n_x; k++) { @@ -223,7 +223,7 @@ TEST_F(algebra_solver_non_linear_eq_test, powell_dbl) { EXPECT_FLOAT_EQ(-y_dbl(0), theta(0)); EXPECT_FLOAT_EQ(-y_dbl(1), theta(1)); EXPECT_FLOAT_EQ(y_dbl(2), theta(2)); -} +}*/ /* TEST_F(error_message_test, powell) { @@ -239,7 +239,7 @@ TEST_F(error_message_test, powell_dbl) { } */ -TEST(unsolvable_test, powell) { +/*TEST(unsolvable_test, powell) { using stan::math::var; Eigen::Matrix y(2); y << 1, 1; @@ -368,7 +368,7 @@ TEST(MathMatrixRevMat, system_functor_constructor) { system_functor fs(f, x, y, dat, dat_int, msgs); EXPECT_EQ(fs.f_, f); -} +}*/ ////////////////////////////////////////////////////////////////////////// // Tests for newton solver. @@ -408,14 +408,14 @@ TEST_F(algebra_solver_simple_eq_test, newton_tuned) { for (int i = 0; i < n_y; i++) EXPECT_EQ(J(k, i), g[i]); } -} +}*/ TEST_F(algebra_solver_simple_eq_test, newton_dbl) { bool is_newton = true; Eigen::VectorXd theta = simple_eq_test(simple_eq_functor(), y_dbl, is_newton); } -TEST_F(algebra_solver_simple_eq_test, newton_tuned_dbl) { +/*TEST_F(algebra_solver_simple_eq_test, newton_tuned_dbl) { bool is_newton = true; Eigen::VectorXd theta = simple_eq_test(simple_eq_functor(), y_dbl, is_newton, true, scale_step, xtol, ftol, maxfev); From 28fd6e4554ddf2958088a9a0a9915ad5d409eaa8 Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Fri, 2 Apr 2021 20:06:29 +0000 Subject: [PATCH 24/88] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- .../rev/functor/algebra_solver_newton.hpp | 12 ++------ stan/math/rev/functor/kinsol_data.hpp | 29 ++++++++++++------- stan/math/rev/functor/kinsol_solve.hpp | 12 ++++---- 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index ff534ac4de1..ac991c444c8 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -81,15 +81,9 @@ Eigen::VectorXd algebra_solver_newton( value_of(f(x_eval, y, dat, dat_int, msgs)), "the vector of unknowns, x,", x); - return kinsol_solve(algebra_solver_adapter2(f), - value_of(x_eval), - scaling_step_size, - function_tolerance, - max_num_steps, - 1, - 10, - KIN_LINESEARCH, - msgs, y, dat, dat_int); + return kinsol_solve(algebra_solver_adapter2(f), value_of(x_eval), + scaling_step_size, function_tolerance, max_num_steps, 1, + 10, KIN_LINESEARCH, msgs, y, dat, dat_int); } /** diff --git a/stan/math/rev/functor/kinsol_data.hpp b/stan/math/rev/functor/kinsol_data.hpp index f668a389f37..3878b08f93e 100644 --- a/stan/math/rev/functor/kinsol_data.hpp +++ b/stan/math/rev/functor/kinsol_data.hpp @@ -40,7 +40,8 @@ class kinsol_system_data { void* kinsol_memory_; /* Constructor */ - kinsol_system_data(const F1& f, const Eigen::VectorXd& x, std::ostream* msgs, const Args&... args) + kinsol_system_data(const F1& f, const Eigen::VectorXd& x, std::ostream* msgs, + const Args&... args) : f_(f), x_(x), N_(x.size()), @@ -63,13 +64,17 @@ class kinsol_system_data { const system_data* explicit_system = static_cast(user_data); - Eigen::VectorXd x_eigen(Eigen::Map(NV_DATA_S(x), explicit_system->N_)); + Eigen::VectorXd x_eigen( + Eigen::Map(NV_DATA_S(x), explicit_system->N_)); - Eigen::Map f_eval_map(N_VGetArrayPointer(f_eval), explicit_system->N_); + Eigen::Map f_eval_map(N_VGetArrayPointer(f_eval), + explicit_system->N_); - f_eval_map = apply([&](const auto&... args) { + f_eval_map = apply( + [&](const auto&... args) { return explicit_system->f_(x_eigen, explicit_system->msgs_, args...); - }, explicit_system->args_tuple); + }, + explicit_system->args_tuple); return 0; } @@ -91,13 +96,17 @@ class kinsol_system_data { const system_data* explicit_system = static_cast(user_data); - Eigen::VectorXd x_eigen(Eigen::Map(NV_DATA_S(x), explicit_system->N_)); - Eigen::Map f_eval_map(N_VGetArrayPointer(f_eval), explicit_system->N_); + Eigen::VectorXd x_eigen( + Eigen::Map(NV_DATA_S(x), explicit_system->N_)); + Eigen::Map f_eval_map(N_VGetArrayPointer(f_eval), + explicit_system->N_); auto myfunc = [&](const auto& x) { - return apply([&](const auto&... args) { - return explicit_system->f_(x, explicit_system->msgs_, args...); - }, explicit_system->args_tuple); + return apply( + [&](const auto&... args) { + return explicit_system->f_(x, explicit_system->msgs_, args...); + }, + explicit_system->args_tuple); }; Eigen::MatrixXd Jf_x; diff --git a/stan/math/rev/functor/kinsol_solve.hpp b/stan/math/rev/functor/kinsol_solve.hpp index ecbe9ea4f2b..d62a44d31b0 100644 --- a/stan/math/rev/functor/kinsol_solve.hpp +++ b/stan/math/rev/functor/kinsol_solve.hpp @@ -58,12 +58,12 @@ namespace math { template Eigen::VectorXd kinsol_solve( const F1& f, const Eigen::VectorXd& x, - double scaling_step_tol, // = 1e-3 - double function_tolerance, // = 1e-6 - long int max_num_steps, // NOLINT(runtime/int) = 200 - bool custom_jacobian, // = 1 - int steps_eval_jacobian, // = 10 - int global_line_search, // = KIN_LINESEARCH + double scaling_step_tol, // = 1e-3 + double function_tolerance, // = 1e-6 + long int max_num_steps, // NOLINT(runtime/int) = 200 + bool custom_jacobian, // = 1 + int steps_eval_jacobian, // = 10 + int global_line_search, // = KIN_LINESEARCH std::ostream* msgs, const Args&... args) { int N = x.size(); typedef kinsol_system_data system_data; From e93899b3a582833f20548ad4a7faaca3f5ddda0b Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Tue, 13 Apr 2021 17:16:21 -0700 Subject: [PATCH 25/88] Finish Newton solver. This commit does three things: 1. Both versions of `algebra_solver_newton` now are wrappers around a variadic implementation. 2. The variadic implementation uses a callback on the reverse pass to propagate the derivatives. 3. An adapter, needed to connect the old and new interfaces (which eliminates the need to make changes to the existing tests) is added to `algebra_solver_adapter.hpp`. There are a couple of remaining tasks: 1. Clean up and standardize variable names. (It would be preferrable if `x` were not the name of the output.) 2. Write documentation, if appropriate, for the internal implemenations. 3. Specification currently relies on the fact that `y` is available in the old interface to check whether or not it's a double. How can we check that none of the arguments passed is a `var` in the future? --- .../prim/functor/algebra_solver_adapter.hpp | 27 +++ .../rev/functor/algebra_solver_newton.hpp | 134 ++++++++----- .../rev/functor/algebra_solver_powell.hpp | 177 ++++++++---------- .../math/rev/functor/algebra_solver_test.cpp | 18 +- 4 files changed, 198 insertions(+), 158 deletions(-) create mode 100644 stan/math/prim/functor/algebra_solver_adapter.hpp diff --git a/stan/math/prim/functor/algebra_solver_adapter.hpp b/stan/math/prim/functor/algebra_solver_adapter.hpp new file mode 100644 index 00000000000..8fc92a000c1 --- /dev/null +++ b/stan/math/prim/functor/algebra_solver_adapter.hpp @@ -0,0 +1,27 @@ +#ifndef STAN_MATH_PRIM_FUNCTOR_ALGEBRA_SOLVER_ADAPTER_HPP +#define STAN_MATH_PRIM_FUNCTOR_ALGEBRA_SOLVER_ADAPTER_HPP + +#include +#include + +/** + * Adapt the non-variadic algebra_solver_newton and algebra_solver_powell + * arguemts to the variadic algebra_solver_newton_impl and + * algebra_solver_powell_impl interfaces. + * + * @tparam F type of function to adapt. + */ +template +struct algebra_solver_adapter { + const F& f_; + + explicit algebra_solver_adapter(const F& f) : f_(f) {} + + template + auto operator()(const T1& x, std::ostream* msgs, const T2& y, const T3& dat, + const T4& dat_int) const { + return f_(x, y, dat, dat_int, msgs); + } +}; + +#endif diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index ac991c444c8..c6a80539c2a 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -15,18 +16,23 @@ namespace stan { namespace math { -template -struct algebra_solver_adapter2 { - const F& f_; - - explicit algebra_solver_adapter2(const F& f) : f_(f) {} - - template - auto operator()(const T1& x, std::ostream* msgs, const T2& y, const T3& dat, - const T4& dat_int) const { - return f_(x, y, dat, dat_int, msgs); - } -}; + +/** Implementation of ordinary newton solver. */ +template * = nullptr> +Eigen::VectorXd algebra_solver_newton_impl( + const F& f, const T& x, std::ostream* msgs, double scaling_step_size, + double function_tolerance, long int max_num_steps, const Eigen::VectorXd& y, + const T_Args&... args) { // NOLINT(runtime/int) + const auto& x_eval = x.eval(); + const auto& x_val = (value_of(x_eval)).eval(); + auto args_vals_tuple = std::make_tuple(y, eval(value_of(args))...); + + return kinsol_solve(f, value_of(x_eval), scaling_step_size, + function_tolerance, max_num_steps, 1, 10, KIN_LINESEARCH, + msgs, y, args...); +} + /** * Return the solution to the specified system of algebraic * equations given an initial guess, and parameters and data, @@ -73,17 +79,72 @@ Eigen::VectorXd algebra_solver_newton( double function_tolerance = 1e-6, long int max_num_steps = 200) { // NOLINT(runtime/int) const auto& x_eval = x.eval(); - algebra_solver_check(x_eval, y, dat, dat_int, function_tolerance, - max_num_steps); - check_nonnegative("algebra_solver", "scaling_step_size", scaling_step_size); + return algebra_solver_newton_impl(algebra_solver_adapter(f), x, msgs, + scaling_step_size, function_tolerance, + max_num_steps, y, dat, dat_int); +} + +/** Implementation of autodiff newton solver. */ +template * = nullptr, + require_any_st_var* = nullptr> +Eigen::Matrix algebra_solver_newton_impl( + const F& f, const T& x, std::ostream* msgs, double scaling_step_size, + double function_tolerance, long int max_num_steps, + const T_Args&... args) { // NOLINT(runtime/int) + const auto& x_eval = x.eval(); + auto arena_args_tuple = std::make_tuple(to_arena(args)...); + const auto& x_val = (value_of(x_eval)).eval(); + auto args_vals_tuple = std::make_tuple(eval(value_of(args))...); + + // Solve the system + Eigen::VectorXd theta_dbl = apply([&](const auto&... vals) { + return kinsol_solve( + f, value_of(x_eval), scaling_step_size, function_tolerance, max_num_steps, + 1, 10, KIN_LINESEARCH, msgs, vals...); + }, args_vals_tuple); + + // Evaluate and store the Jacobian. + auto myfunc = [&](const auto& x) { + return apply([&](const auto&... args) { return f(x, msgs, args...); }, + args_vals_tuple); + }; - check_matching_sizes("algebra_solver", "the algebraic system's output", - value_of(f(x_eval, y, dat, dat_int, msgs)), - "the vector of unknowns, x,", x); + Eigen::MatrixXd Jf_x; + Eigen::VectorXd f_x; - return kinsol_solve(algebra_solver_adapter2(f), value_of(x_eval), - scaling_step_size, function_tolerance, max_num_steps, 1, - 10, KIN_LINESEARCH, msgs, y, dat, dat_int); + jacobian(myfunc, theta_dbl, f_x, Jf_x); + + using ret_type = Eigen::Matrix; + auto arena_Jf_x = to_arena(Jf_x); + + arena_t ret = theta_dbl; + + reverse_pass_callback([f, ret, arena_args_tuple, arena_Jf_x, msgs]() mutable { + using Eigen::Dynamic; + using Eigen::Matrix; + using Eigen::MatrixXd; + using Eigen::VectorXd; + + // Contract specificities with inverse Jacobian of f with respect to x. + VectorXd ret_adj = ret.adj(); + VectorXd eta = -arena_Jf_x.transpose().fullPivLu().solve(ret_adj); + + // Contract with Jacobian of f with respect to y using a nested reverse + // autodiff pass. + { + nested_rev_autodiff rev; + + VectorXd ret_val = ret.val(); + auto x_nrad_ = apply( + [&](const auto&... args) { return eval(f(ret_val, msgs, args...)); }, + arena_args_tuple); + x_nrad_.adj() = eta; + grad(); + } + }); + + return ret_type(ret); } /** @@ -138,34 +199,9 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_newton( const std::vector& dat_int, std::ostream* msgs = nullptr, double scaling_step_size = 1e-3, double function_tolerance = 1e-6, long int max_num_steps = 200) { // NOLINT(runtime/int) - /* - const auto& x_eval = x.eval(); - const auto& y_eval = y.eval(); - const auto& x_val = (value_of(x_eval)).eval(); - const auto& y_val = (value_of(y_eval)).eval(); - Eigen::VectorXd theta_dbl = algebra_solver_newton( - f, x_eval, value_of(y_eval), dat, dat_int, msgs, scaling_step_size, - function_tolerance, max_num_steps); - - typedef system_functor Fsx; - typedef hybrj_functor_solver Fx; - Fx fx(Fsx(), f, x_val, y_val, dat, dat_int, msgs); - - typedef system_functor Fsy; - typedef hybrj_functor_solver Fy; - Fsy fy(f, value_of(x_eval), value_of(y_eval), dat, dat_int, msgs); - - // Construct vari - algebra_solver_vari, Fx>* vi0 - = new algebra_solver_vari, Fx>(fy, y_eval, fx, - theta_dbl); - Eigen::Matrix theta(x.size()); - theta(0) = var(vi0->x_[0]); - for (int i = 1; i < x.size(); ++i) { - theta(i) = var(vi0->x_[i]); - } - - return theta;*/ + return algebra_solver_newton_impl(algebra_solver_adapter(f), x, msgs, + scaling_step_size, function_tolerance, + max_num_steps, y, dat, dat_int); } } // namespace math diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index f655b5eb83c..7364475ef4f 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -17,86 +18,11 @@ namespace stan { namespace math { -/** - * Adapt the non-variadic algebra solver arguments to the variadic - * algebra_solver_powell_impl or algebra_solver_newton_impl interface. - * - * @param F Type of function to adapt. - */ -template -struct algebra_solver_adapter { - const F& f_; - - explicit algebra_solver_adapter(const F& f) : f_(f) {} - - template - auto operator()(const T1& x, std::ostream* msgs, const T2& y, const T3& dat, - const T4& dat_int) const { - return f_(x, y, dat, dat_int, msgs); - } -}; - -/** - * Return the solution to the specified system of algebraic - * equations given an initial guess, and parameters and data, - * which get passed into the algebraic system. - * Use Powell's dogleg solver. - * - * The user can also specify the relative tolerance - * (xtol in Eigen's code), the function tolerance, - * and the maximum number of steps (maxfev in Eigen's code). - * - * Throw an exception if the norm of f(x), where f is the - * output of the algebraic system and x the proposed solution, - * is greater than the function tolerance. We here use the - * norm as a metric to measure how far we are from the origin (0). - * - * @tparam F type of equation system function. - * @tparam T type of initial guess vector. - * - * @param[in] f Functor that evaluates the system of equations. - * @param[in] x Vector of starting values. - * @param[in] y parameter vector for the equation system. The function - * is overloaded to treat y as a vector of doubles or as a - * a template type T. - * @param[in] dat continuous data vector for the equation system. - * @param[in] dat_int integer data vector for the equation system. - * @param[in, out] msgs the print stream for warning messages. - * @param[in] relative_tolerance determines the convergence criteria - * for the solution. - * @param[in] function_tolerance determines whether roots are acceptable. - * @param[in] max_num_steps maximum number of function evaluations. - * @return theta Vector of solutions to the system of equations. - * @throw std::invalid_argument if x has size zero. - * @throw std::invalid_argument if x has non-finite elements. - * @throw std::invalid_argument if y has non-finite elements. - * @throw std::invalid_argument if dat has non-finite elements. - * @throw std::invalid_argument if dat_int has non-finite elements. - * @throw std::invalid_argument if relative_tolerance is strictly - * negative. - * @throw std::invalid_argument if function_tolerance is strictly - * negative. - * @throw std::invalid_argument if max_num_steps is not positive. - * @throw std::domain_error solver exceeds max_num_steps. - * @throw std::domain_error if the norm of the solution exceeds - * the function tolerance. - */ -template * = nullptr> -Eigen::VectorXd algebra_solver_powell( - const F& f, const T& x, const Eigen::VectorXd& y, - const std::vector& dat, const std::vector& dat_int, - std::ostream* msgs = nullptr, double relative_tolerance = 1e-10, - double function_tolerance = 1e-6, - long int max_num_steps = 1e+3) { // NOLINT(runtime/int) - return algebra_solver_powell_impl(algebra_solver_adapter(f), x, msgs, - relative_tolerance, function_tolerance, - max_num_steps, y, dat, dat_int); -} - -template * = nullptr> +/** Implementation of ordinary powell solver. */ +template * = nullptr> Eigen::VectorXd algebra_solver_powell_impl( - const F& f, const T1& x, std::ostream* msgs, double relative_tolerance, + const F& f, const T& x, std::ostream* msgs, double relative_tolerance, double function_tolerance, long int max_num_steps, const Eigen::VectorXd& y, const T_Args&... args) { // NOLINT(runtime/int) const auto& x_eval = x.eval(); @@ -133,18 +59,19 @@ Eigen::VectorXd algebra_solver_powell_impl( * (xtol in Eigen's code), the function tolerance, * and the maximum number of steps (maxfev in Eigen's code). * - * Overload the previous definition to handle the case where y - * is a vector of parameters (var). The overload calls the - * algebraic solver defined above and builds a vari object on - * top, using the algebra_solver_vari class. + * Throw an exception if the norm of f(x), where f is the + * output of the algebraic system and x the proposed solution, + * is greater than the function tolerance. We here use the + * norm as a metric to measure how far we are from the origin (0). * - * @tparam F type of equation system function - * @tparam T1 type of elements in the x vector - * @tparam T2 type of elements in the y vector + * @tparam F type of equation system function. + * @tparam T type of initial guess vector. * * @param[in] f Functor that evaluates the system of equations. - * @param[in] x Vector of starting values (initial guess). - * @param[in] y parameter vector for the equation system. + * @param[in] x Vector of starting values. + * @param[in] y parameter vector for the equation system. The function + * is overloaded to treat y as a vector of doubles or as a + * a template type T. * @param[in] dat continuous data vector for the equation system. * @param[in] dat_int integer data vector for the equation system. * @param[in, out] msgs the print stream for warning messages. @@ -157,8 +84,7 @@ Eigen::VectorXd algebra_solver_powell_impl( * @throw std::invalid_argument if x has non-finite elements. * @throw std::invalid_argument if y has non-finite elements. * @throw std::invalid_argument if dat has non-finite elements. - * @throw std::invalid_argument if dat_int has non-finite - * elements. + * @throw std::invalid_argument if dat_int has non-finite elements. * @throw std::invalid_argument if relative_tolerance is strictly * negative. * @throw std::invalid_argument if function_tolerance is strictly @@ -168,23 +94,31 @@ Eigen::VectorXd algebra_solver_powell_impl( * @throw std::domain_error if the norm of the solution exceeds * the function tolerance. */ -template * = nullptr, +template * = nullptr> +Eigen::VectorXd algebra_solver_powell( + const F& f, const T& x, const Eigen::VectorXd& y, + const std::vector& dat, const std::vector& dat_int, + std::ostream* msgs = nullptr, double relative_tolerance = 1e-10, + double function_tolerance = 1e-6, + long int max_num_steps = 1e+3) { // NOLINT(runtime/int) + return algebra_solver_powell_impl(algebra_solver_adapter(f), x, msgs, + relative_tolerance, function_tolerance, + max_num_steps, y, dat, dat_int); +} + +/** Implementation of autodiff powell solver. */ +template * = nullptr, require_any_st_var* = nullptr> Eigen::Matrix algebra_solver_powell_impl( - const F& f, const T1& x, std::ostream* msgs, double relative_tolerance, + const F& f, const T& x, std::ostream* msgs, double relative_tolerance, double function_tolerance, long int max_num_steps, const T_Args&... args) { // NOLINT(runtime/int) const auto& x_eval = x.eval(); - auto arena_args_tuple = std::make_tuple(to_arena(args)...); - const auto& x_val = (value_of(x_eval)).eval(); - auto args_vals_tuple = std::make_tuple(eval(value_of(args))...); - // algebra_solver_check(x_val, y, dat, dat_int, function_tolerance, - // max_num_steps); check_nonnegative("alegbra_solver", "relative_tolerance", relative_tolerance); // Construct the Powell solver @@ -241,7 +175,51 @@ Eigen::Matrix algebra_solver_powell_impl( return ret_type(ret); } - +/** + * Return the solution to the specified system of algebraic + * equations given an initial guess, and parameters and data, + * which get passed into the algebraic system. + * Use Powell's dogleg solver. + * + * The user can also specify the relative tolerance + * (xtol in Eigen's code), the function tolerance, + * and the maximum number of steps (maxfev in Eigen's code). + * + * Overload the previous definition to handle the case where y + * is a vector of parameters (var). The overload calls the + * algebraic solver defined above and builds a vari object on + * top, using the algebra_solver_vari class. + * + * @tparam F type of equation system function + * @tparam T1 type of elements in the x vector + * @tparam T2 type of elements in the y vector + * + * @param[in] f Functor that evaluates the system of equations. + * @param[in] x Vector of starting values (initial guess). + * @param[in] y parameter vector for the equation system. + * @param[in] dat continuous data vector for the equation system. + * @param[in] dat_int integer data vector for the equation system. + * @param[in, out] msgs the print stream for warning messages. + * @param[in] relative_tolerance determines the convergence criteria + * for the solution. + * @param[in] function_tolerance determines whether roots are acceptable. + * @param[in] max_num_steps maximum number of function evaluations. + * @return theta Vector of solutions to the system of equations. + * @throw std::invalid_argument if x has size zero. + * @throw std::invalid_argument if x has non-finite elements. + * @throw std::invalid_argument if y has non-finite elements. + * @throw std::invalid_argument if dat has non-finite elements. + * @throw std::invalid_argument if dat_int has non-finite + * elements. + * @throw std::invalid_argument if relative_tolerance is strictly + * negative. + * @throw std::invalid_argument if function_tolerance is strictly + * negative. + * @throw std::invalid_argument if max_num_steps is not positive. + * @throw std::domain_error solver exceeds max_num_steps. + * @throw std::domain_error if the norm of the solution exceeds + * the function tolerance. + */ template * = nullptr> Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_powell( @@ -254,6 +232,7 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_powell( max_num_steps, y, dat, dat_int); } + /** * Return the solution to the specified system of algebraic * equations given an initial guess, and parameters and data, diff --git a/test/unit/math/rev/functor/algebra_solver_test.cpp b/test/unit/math/rev/functor/algebra_solver_test.cpp index b74e7150184..36e79a59958 100644 --- a/test/unit/math/rev/functor/algebra_solver_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_test.cpp @@ -71,7 +71,6 @@ class algebra_solver_non_linear_eq_test : public ::testing::Test { Eigen::MatrixXd J; }; -/* class error_message_test : public ::testing::Test { protected: void SetUp() override { @@ -82,7 +81,6 @@ class error_message_test : public ::testing::Test { Eigen::VectorXd y_2; Eigen::VectorXd y_3; }; -*/ class max_steps_test : public ::testing::Test { protected: @@ -129,7 +127,7 @@ class degenerate_eq_test : public ::testing::Test { ////////////////////////////////////////////////////////////////////////// // Tests for powell solver. -/*TEST_F(algebra_solver_simple_eq_test, powell) { +TEST_F(algebra_solver_simple_eq_test, powell) { using stan::math::var; bool is_newton = false; for (int k = 0; k < n_x; k++) { @@ -223,7 +221,7 @@ TEST_F(algebra_solver_non_linear_eq_test, powell_dbl) { EXPECT_FLOAT_EQ(-y_dbl(0), theta(0)); EXPECT_FLOAT_EQ(-y_dbl(1), theta(1)); EXPECT_FLOAT_EQ(y_dbl(2), theta(2)); -}*/ +} /* TEST_F(error_message_test, powell) { @@ -239,7 +237,7 @@ TEST_F(error_message_test, powell_dbl) { } */ -/*TEST(unsolvable_test, powell) { +TEST(unsolvable_test, powell) { using stan::math::var; Eigen::Matrix y(2); y << 1, 1; @@ -368,11 +366,10 @@ TEST(MathMatrixRevMat, system_functor_constructor) { system_functor fs(f, x, y, dat, dat_int, msgs); EXPECT_EQ(fs.f_, f); -}*/ +} ////////////////////////////////////////////////////////////////////////// // Tests for newton solver. -/* TEST_F(algebra_solver_simple_eq_test, newton) { using stan::math::var; bool is_newton = true; @@ -408,14 +405,14 @@ TEST_F(algebra_solver_simple_eq_test, newton_tuned) { for (int i = 0; i < n_y; i++) EXPECT_EQ(J(k, i), g[i]); } -}*/ +} TEST_F(algebra_solver_simple_eq_test, newton_dbl) { bool is_newton = true; Eigen::VectorXd theta = simple_eq_test(simple_eq_functor(), y_dbl, is_newton); } -/*TEST_F(algebra_solver_simple_eq_test, newton_tuned_dbl) { +TEST_F(algebra_solver_simple_eq_test, newton_tuned_dbl) { bool is_newton = true; Eigen::VectorXd theta = simple_eq_test(simple_eq_functor(), y_dbl, is_newton, true, scale_step, xtol, ftol, maxfev); @@ -467,6 +464,7 @@ TEST_F(algebra_solver_non_linear_eq_test, newton_dbl) { EXPECT_FLOAT_EQ(y_dbl(2), theta(2)); } +/* TEST_F(error_message_test, newton) { using stan::math::var; bool is_newton = true; @@ -478,6 +476,7 @@ TEST_F(error_message_test, newton_dbl) { bool is_newton = true; error_conditions_test(non_linear_eq_functor(), y_3, is_newton); } +*/ TEST_F(max_steps_test, newton) { bool is_newton = true; @@ -596,4 +595,3 @@ TEST_F(degenerate_eq_test, newton_guess_saddle_point_dbl) { y_scale, dat, dat_int), std::runtime_error, msg); } -*/ From 5694e578829d6964ca497e613e8924da1ea1f5c3 Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Wed, 14 Apr 2021 00:23:37 +0000 Subject: [PATCH 26/88] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- stan/math/rev/functor/algebra_solver_newton.hpp | 14 ++++++++------ stan/math/rev/functor/algebra_solver_powell.hpp | 1 - 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index c6a80539c2a..272fdb312cb 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -23,7 +23,7 @@ template algebra_solver_newton_impl( auto args_vals_tuple = std::make_tuple(eval(value_of(args))...); // Solve the system - Eigen::VectorXd theta_dbl = apply([&](const auto&... vals) { - return kinsol_solve( - f, value_of(x_eval), scaling_step_size, function_tolerance, max_num_steps, - 1, 10, KIN_LINESEARCH, msgs, vals...); - }, args_vals_tuple); + Eigen::VectorXd theta_dbl = apply( + [&](const auto&... vals) { + return kinsol_solve(f, value_of(x_eval), scaling_step_size, + function_tolerance, max_num_steps, 1, 10, + KIN_LINESEARCH, msgs, vals...); + }, + args_vals_tuple); // Evaluate and store the Jacobian. auto myfunc = [&](const auto& x) { diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 7364475ef4f..334fb1c562e 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -232,7 +232,6 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_powell( max_num_steps, y, dat, dat_int); } - /** * Return the solution to the specified system of algebraic * equations given an initial guess, and parameters and data, From a4ed898713594b34fb9d999ca8a538a5becfdb46 Mon Sep 17 00:00:00 2001 From: Ben Date: Sat, 17 Apr 2021 12:31:16 -0700 Subject: [PATCH 27/88] Added more input checks (Issue #2401) --- .../rev/functor/algebra_solver_newton.hpp | 17 +++++++++++-- .../rev/functor/algebra_solver_powell.hpp | 17 ++++++++++--- stan/math/rev/functor/algebra_system.hpp | 17 ------------- .../math/rev/functor/algebra_solver_test.cpp | 4 --- .../math/rev/functor/util_algebra_solver.hpp | 25 +++---------------- 5 files changed, 32 insertions(+), 48 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index 272fdb312cb..75c33836878 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -19,7 +19,8 @@ namespace math { /** Implementation of ordinary newton solver. */ template * = nullptr> + require_eigen_vector_t* = nullptr, + require_all_st_arithmetic* = nullptr> Eigen::VectorXd algebra_solver_newton_impl( const F& f, const T& x, std::ostream* msgs, double scaling_step_size, double function_tolerance, long int max_num_steps, const Eigen::VectorXd& y, @@ -28,6 +29,12 @@ Eigen::VectorXd algebra_solver_newton_impl( const auto& x_val = (value_of(x_eval)).eval(); auto args_vals_tuple = std::make_tuple(y, eval(value_of(args))...); + check_nonzero_size("algebra_solver_newton", "initial guess", x); + check_finite("algebra_solver_newton", "initial guess", x); + check_nonnegative("algebra_solver_newton", "scaling_step_size", scaling_step_size); + check_nonnegative("algebra_solver_newton", "function_tolerance", function_tolerance); + check_positive("algebra_solver_newton", "max_num_steps", max_num_steps); + return kinsol_solve(f, value_of(x_eval), scaling_step_size, function_tolerance, max_num_steps, 1, 10, KIN_LINESEARCH, msgs, y, args...); @@ -86,7 +93,7 @@ Eigen::VectorXd algebra_solver_newton( /** Implementation of autodiff newton solver. */ template * = nullptr, + require_eigen_vector_t* = nullptr, require_any_st_var* = nullptr> Eigen::Matrix algebra_solver_newton_impl( const F& f, const T& x, std::ostream* msgs, double scaling_step_size, @@ -97,6 +104,12 @@ Eigen::Matrix algebra_solver_newton_impl( const auto& x_val = (value_of(x_eval)).eval(); auto args_vals_tuple = std::make_tuple(eval(value_of(args))...); + check_nonzero_size("algebra_solver_newton", "initial guess", x); + check_finite("algebra_solver_newton", "initial guess", x); + check_nonnegative("algebra_solver_newton", "scaling_step_size", scaling_step_size); + check_nonnegative("algebra_solver_newton", "function_tolerance", function_tolerance); + check_positive("algebra_solver_newton", "max_num_steps", max_num_steps); + // Solve the system Eigen::VectorXd theta_dbl = apply( [&](const auto&... vals) { diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 334fb1c562e..0ca7af95bb6 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -20,7 +20,8 @@ namespace math { /** Implementation of ordinary powell solver. */ template * = nullptr> + require_eigen_vector_t* = nullptr, + require_all_st_arithmetic* = nullptr> Eigen::VectorXd algebra_solver_powell_impl( const F& f, const T& x, std::ostream* msgs, double relative_tolerance, double function_tolerance, long int max_num_steps, const Eigen::VectorXd& y, @@ -29,7 +30,11 @@ Eigen::VectorXd algebra_solver_powell_impl( const auto& x_val = (value_of(x_eval)).eval(); auto args_vals_tuple = std::make_tuple(y, eval(value_of(args))...); - check_nonnegative("alegbra_solver", "relative_tolerance", relative_tolerance); + check_nonzero_size("algebra_solver_powell", "initial guess", x); + check_finite("algebra_solver_powell", "initial guess", x); + check_nonnegative("alegbra_solver_powell", "relative_tolerance", relative_tolerance); + check_nonnegative("algebra_solver_powell", "function_tolerance", function_tolerance); + check_positive("algebra_solver_powell", "max_num_steps", max_num_steps); // Construct the Powell solver auto myfunc = [&](const auto& x) { @@ -108,7 +113,7 @@ Eigen::VectorXd algebra_solver_powell( /** Implementation of autodiff powell solver. */ template * = nullptr, + require_eigen_vector_t* = nullptr, require_any_st_var* = nullptr> Eigen::Matrix algebra_solver_powell_impl( const F& f, const T& x, std::ostream* msgs, double relative_tolerance, @@ -119,7 +124,11 @@ Eigen::Matrix algebra_solver_powell_impl( const auto& x_val = (value_of(x_eval)).eval(); auto args_vals_tuple = std::make_tuple(eval(value_of(args))...); - check_nonnegative("alegbra_solver", "relative_tolerance", relative_tolerance); + check_nonzero_size("algebra_solver_powell", "initial guess", x); + check_finite("algebra_solver_powell", "initial guess", x); + check_nonnegative("alegbra_solver_powell", "relative_tolerance", relative_tolerance); + check_nonnegative("algebra_solver_powell", "function_tolerance", function_tolerance); + check_positive("algebra_solver_powell", "max_num_steps", max_num_steps); // Construct the Powell solver auto myfunc = [&](const auto& x) { diff --git a/stan/math/rev/functor/algebra_system.hpp b/stan/math/rev/functor/algebra_system.hpp index c88c5b27783..caac096ed3c 100644 --- a/stan/math/rev/functor/algebra_system.hpp +++ b/stan/math/rev/functor/algebra_system.hpp @@ -151,23 +151,6 @@ struct hybrj_functor_solver : nlo_functor { Eigen::VectorXd get_value(const Eigen::VectorXd& iv) const { return fs_(iv); } }; -template -void algebra_solver_check(const Eigen::Matrix& x, - const Eigen::Matrix y, - const std::vector& dat, - const std::vector& dat_int, - double function_tolerance, - long int max_num_steps) { // NOLINT(runtime/int) - check_nonzero_size("algebra_solver", "initial guess", x); - check_finite("algebra_solver", "initial guess", x); - check_finite("algebra_solver", "parameter vector", y); - check_finite("algebra_solver", "continuous data", dat); - check_finite("algebra_solver", "integer data", dat_int); - - check_nonnegative("algebra_solver", "function_tolerance", function_tolerance); - check_positive("algebra_solver", "max_num_steps", max_num_steps); -} - } // namespace math } // namespace stan diff --git a/test/unit/math/rev/functor/algebra_solver_test.cpp b/test/unit/math/rev/functor/algebra_solver_test.cpp index 36e79a59958..e89cef3ca52 100644 --- a/test/unit/math/rev/functor/algebra_solver_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_test.cpp @@ -223,7 +223,6 @@ TEST_F(algebra_solver_non_linear_eq_test, powell_dbl) { EXPECT_FLOAT_EQ(y_dbl(2), theta(2)); } -/* TEST_F(error_message_test, powell) { using stan::math::var; bool is_newton = false; @@ -235,7 +234,6 @@ TEST_F(error_message_test, powell_dbl) { bool is_newton = false; error_conditions_test(non_linear_eq_functor(), y_3, is_newton); } -*/ TEST(unsolvable_test, powell) { using stan::math::var; @@ -464,7 +462,6 @@ TEST_F(algebra_solver_non_linear_eq_test, newton_dbl) { EXPECT_FLOAT_EQ(y_dbl(2), theta(2)); } -/* TEST_F(error_message_test, newton) { using stan::math::var; bool is_newton = true; @@ -476,7 +473,6 @@ TEST_F(error_message_test, newton_dbl) { bool is_newton = true; error_conditions_test(non_linear_eq_functor(), y_3, is_newton); } -*/ TEST_F(max_steps_test, newton) { bool is_newton = true; diff --git a/test/unit/math/rev/functor/util_algebra_solver.hpp b/test/unit/math/rev/functor/util_algebra_solver.hpp index 4ea3e070b18..f6e969de98b 100644 --- a/test/unit/math/rev/functor/util_algebra_solver.hpp +++ b/test/unit/math/rev/functor/util_algebra_solver.hpp @@ -168,16 +168,17 @@ inline void error_conditions_test(const F& f, std::vector dat_int; std::stringstream err_msg; - err_msg << "algebra_solver: size of the algebraic system's output (2) " + err_msg << "size of the algebraic system's output (2) " << "and size of the vector of unknowns, x, (3) must match in size"; std::string msg = err_msg.str(); EXPECT_THROW_MSG( + // CHECK: Should this test run on the Newton solver too? algebra_solver_powell(non_square_eq_functor(), x, y, dat, dat_int), std::invalid_argument, msg); Eigen::VectorXd x_bad(static_cast(0)); std::stringstream err_msg2; - err_msg2 << "algebra_solver: initial guess has size 0, but " + err_msg2 << "initial guess has size 0, but " << "must have a non-zero size"; std::string msg2 = err_msg2.str(); EXPECT_THROW_MSG(general_algebra_solver(is_newton, f, x_bad, y, dat, dat_int), @@ -189,25 +190,7 @@ inline void error_conditions_test(const F& f, EXPECT_THROW_MSG( general_algebra_solver(is_newton, f, x_bad_inf, y, dat, dat_int), std::domain_error, - "algebra_solver: initial guess[1] is inf, but must " - "be finite!"); - - typedef Eigen::Matrix matrix; - matrix y_bad_inf(3); - y_bad_inf << inf, 1, 1; - EXPECT_THROW_MSG( - general_algebra_solver(is_newton, f, x, y_bad_inf, dat, dat_int), - std::domain_error, - "algebra_solver: parameter vector[1] is inf, but must " - "be finite!"); - - std::vector dat_bad_inf(1); - dat_bad_inf[0] = inf; - EXPECT_THROW_MSG( - general_algebra_solver(is_newton, f, x, y, dat_bad_inf, dat_int), - std::domain_error, - "algebra_solver: continuous data[1] is inf, but must " - "be finite!"); + "initial guess[1] is inf, but must be finite!"); if (!is_newton) { EXPECT_THROW_MSG(general_algebra_solver(is_newton, f, x, y, dat, dat_int, 0, From 360e9e0bf1b58aef47e556566c857661234a1c45 Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Sat, 17 Apr 2021 19:32:40 +0000 Subject: [PATCH 28/88] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- stan/math/rev/functor/algebra_solver_newton.hpp | 12 ++++++++---- stan/math/rev/functor/algebra_solver_powell.hpp | 12 ++++++++---- test/unit/math/rev/functor/util_algebra_solver.hpp | 5 ++--- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index 75c33836878..1c23548f302 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -31,8 +31,10 @@ Eigen::VectorXd algebra_solver_newton_impl( check_nonzero_size("algebra_solver_newton", "initial guess", x); check_finite("algebra_solver_newton", "initial guess", x); - check_nonnegative("algebra_solver_newton", "scaling_step_size", scaling_step_size); - check_nonnegative("algebra_solver_newton", "function_tolerance", function_tolerance); + check_nonnegative("algebra_solver_newton", "scaling_step_size", + scaling_step_size); + check_nonnegative("algebra_solver_newton", "function_tolerance", + function_tolerance); check_positive("algebra_solver_newton", "max_num_steps", max_num_steps); return kinsol_solve(f, value_of(x_eval), scaling_step_size, @@ -106,8 +108,10 @@ Eigen::Matrix algebra_solver_newton_impl( check_nonzero_size("algebra_solver_newton", "initial guess", x); check_finite("algebra_solver_newton", "initial guess", x); - check_nonnegative("algebra_solver_newton", "scaling_step_size", scaling_step_size); - check_nonnegative("algebra_solver_newton", "function_tolerance", function_tolerance); + check_nonnegative("algebra_solver_newton", "scaling_step_size", + scaling_step_size); + check_nonnegative("algebra_solver_newton", "function_tolerance", + function_tolerance); check_positive("algebra_solver_newton", "max_num_steps", max_num_steps); // Solve the system diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 0ca7af95bb6..7b35116a627 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -32,8 +32,10 @@ Eigen::VectorXd algebra_solver_powell_impl( check_nonzero_size("algebra_solver_powell", "initial guess", x); check_finite("algebra_solver_powell", "initial guess", x); - check_nonnegative("alegbra_solver_powell", "relative_tolerance", relative_tolerance); - check_nonnegative("algebra_solver_powell", "function_tolerance", function_tolerance); + check_nonnegative("alegbra_solver_powell", "relative_tolerance", + relative_tolerance); + check_nonnegative("algebra_solver_powell", "function_tolerance", + function_tolerance); check_positive("algebra_solver_powell", "max_num_steps", max_num_steps); // Construct the Powell solver @@ -126,8 +128,10 @@ Eigen::Matrix algebra_solver_powell_impl( check_nonzero_size("algebra_solver_powell", "initial guess", x); check_finite("algebra_solver_powell", "initial guess", x); - check_nonnegative("alegbra_solver_powell", "relative_tolerance", relative_tolerance); - check_nonnegative("algebra_solver_powell", "function_tolerance", function_tolerance); + check_nonnegative("alegbra_solver_powell", "relative_tolerance", + relative_tolerance); + check_nonnegative("algebra_solver_powell", "function_tolerance", + function_tolerance); check_positive("algebra_solver_powell", "max_num_steps", max_num_steps); // Construct the Powell solver diff --git a/test/unit/math/rev/functor/util_algebra_solver.hpp b/test/unit/math/rev/functor/util_algebra_solver.hpp index f6e969de98b..bec90820196 100644 --- a/test/unit/math/rev/functor/util_algebra_solver.hpp +++ b/test/unit/math/rev/functor/util_algebra_solver.hpp @@ -172,7 +172,7 @@ inline void error_conditions_test(const F& f, << "and size of the vector of unknowns, x, (3) must match in size"; std::string msg = err_msg.str(); EXPECT_THROW_MSG( - // CHECK: Should this test run on the Newton solver too? + // CHECK: Should this test run on the Newton solver too? algebra_solver_powell(non_square_eq_functor(), x, y, dat, dat_int), std::invalid_argument, msg); @@ -189,8 +189,7 @@ inline void error_conditions_test(const F& f, x_bad_inf << inf, 1, 1; EXPECT_THROW_MSG( general_algebra_solver(is_newton, f, x_bad_inf, y, dat, dat_int), - std::domain_error, - "initial guess[1] is inf, but must be finite!"); + std::domain_error, "initial guess[1] is inf, but must be finite!"); if (!is_newton) { EXPECT_THROW_MSG(general_algebra_solver(is_newton, f, x, y, dat, dat_int, 0, From 0a40184eb1aeb19cd41bf1da8ccc6aea2774f5dd Mon Sep 17 00:00:00 2001 From: Ben Date: Sat, 17 Apr 2021 13:02:06 -0700 Subject: [PATCH 29/88] Clean up how x is used (Issue #2401) --- .../rev/functor/algebra_solver_newton.hpp | 76 +++--------------- .../rev/functor/algebra_solver_powell.hpp | 80 +++---------------- 2 files changed, 20 insertions(+), 136 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index 75c33836878..d7f743d8c0d 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -25,72 +25,20 @@ Eigen::VectorXd algebra_solver_newton_impl( const F& f, const T& x, std::ostream* msgs, double scaling_step_size, double function_tolerance, long int max_num_steps, const Eigen::VectorXd& y, const T_Args&... args) { // NOLINT(runtime/int) - const auto& x_eval = x.eval(); - const auto& x_val = (value_of(x_eval)).eval(); - auto args_vals_tuple = std::make_tuple(y, eval(value_of(args))...); + const auto& x_val = to_ref(value_of(x)); + auto args_vals_tuple = std::make_tuple(y, to_ref(args)...); - check_nonzero_size("algebra_solver_newton", "initial guess", x); - check_finite("algebra_solver_newton", "initial guess", x); + check_nonzero_size("algebra_solver_newton", "initial guess", x_val); + check_finite("algebra_solver_newton", "initial guess", x_val); check_nonnegative("algebra_solver_newton", "scaling_step_size", scaling_step_size); check_nonnegative("algebra_solver_newton", "function_tolerance", function_tolerance); check_positive("algebra_solver_newton", "max_num_steps", max_num_steps); - return kinsol_solve(f, value_of(x_eval), scaling_step_size, + return kinsol_solve(f, x_val, scaling_step_size, function_tolerance, max_num_steps, 1, 10, KIN_LINESEARCH, msgs, y, args...); } -/** - * Return the solution to the specified system of algebraic - * equations given an initial guess, and parameters and data, - * which get passed into the algebraic system. Use the - * KINSOL solver from the SUNDIALS suite. - * - * The user can also specify the scaled step size, the function - * tolerance, and the maximum number of steps. - * - * @tparam F type of equation system function. - * @tparam T type of initial guess vector. - * - * @param[in] f Functor that evaluated the system of equations. - * @param[in] x Vector of starting values. - * @param[in] y Parameter vector for the equation system. The function - * is overloaded to treat y as a vector of doubles or of a - * a template type T. - * @param[in] dat Continuous data vector for the equation system. - * @param[in] dat_int Integer data vector for the equation system. - * @param[in, out] msgs The print stream for warning messages. - * @param[in] scaling_step_size Scaled-step stopping tolerance. If - * a Newton step is smaller than the scaling step - * tolerance, the code breaks, assuming the solver is no - * longer making significant progress (i.e. is stuck) - * @param[in] function_tolerance determines whether roots are acceptable. - * @param[in] max_num_steps maximum number of function evaluations. - * * @throw std::invalid_argument if x has size zero. - * @throw std::invalid_argument if x has non-finite elements. - * @throw std::invalid_argument if y has non-finite elements. - * @throw std::invalid_argument if dat has non-finite elements. - * @throw std::invalid_argument if dat_int has non-finite elements. - * @throw std::invalid_argument if scaled_step_size is strictly - * negative. - * @throw std::invalid_argument if function_tolerance is strictly - * negative. - * @throw std::invalid_argument if max_num_steps is not positive. - * @throw std::domain_error if solver exceeds max_num_steps. - */ -template * = nullptr> -Eigen::VectorXd algebra_solver_newton( - const F& f, const T& x, const Eigen::VectorXd& y, - const std::vector& dat, const std::vector& dat_int, - std::ostream* msgs = nullptr, double scaling_step_size = 1e-3, - double function_tolerance = 1e-6, - long int max_num_steps = 200) { // NOLINT(runtime/int) - const auto& x_eval = x.eval(); - return algebra_solver_newton_impl(algebra_solver_adapter(f), x, msgs, - scaling_step_size, function_tolerance, - max_num_steps, y, dat, dat_int); -} - /** Implementation of autodiff newton solver. */ template * = nullptr, @@ -99,13 +47,12 @@ Eigen::Matrix algebra_solver_newton_impl( const F& f, const T& x, std::ostream* msgs, double scaling_step_size, double function_tolerance, long int max_num_steps, const T_Args&... args) { // NOLINT(runtime/int) - const auto& x_eval = x.eval(); + const auto& x_val = to_ref(value_of(x)); auto arena_args_tuple = std::make_tuple(to_arena(args)...); - const auto& x_val = (value_of(x_eval)).eval(); - auto args_vals_tuple = std::make_tuple(eval(value_of(args))...); + auto args_vals_tuple = std::make_tuple(to_ref(value_of(args))...); - check_nonzero_size("algebra_solver_newton", "initial guess", x); - check_finite("algebra_solver_newton", "initial guess", x); + check_nonzero_size("algebra_solver_newton", "initial guess", x_val); + check_finite("algebra_solver_newton", "initial guess", x_val); check_nonnegative("algebra_solver_newton", "scaling_step_size", scaling_step_size); check_nonnegative("algebra_solver_newton", "function_tolerance", function_tolerance); check_positive("algebra_solver_newton", "max_num_steps", max_num_steps); @@ -113,7 +60,7 @@ Eigen::Matrix algebra_solver_newton_impl( // Solve the system Eigen::VectorXd theta_dbl = apply( [&](const auto&... vals) { - return kinsol_solve(f, value_of(x_eval), scaling_step_size, + return kinsol_solve(f, x_val, scaling_step_size, function_tolerance, max_num_steps, 1, 10, KIN_LINESEARCH, msgs, vals...); }, @@ -207,8 +154,7 @@ Eigen::Matrix algebra_solver_newton_impl( * @throw std::domain_error if solver exceeds max_num_steps. */ template * = nullptr, - require_st_var* = nullptr> + require_all_eigen_vector_t* = nullptr> Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_newton( const F& f, const T1& x, const T2& y, const std::vector& dat, const std::vector& dat_int, std::ostream* msgs = nullptr, diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 0ca7af95bb6..9f4dbb275d7 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -26,12 +26,11 @@ Eigen::VectorXd algebra_solver_powell_impl( const F& f, const T& x, std::ostream* msgs, double relative_tolerance, double function_tolerance, long int max_num_steps, const Eigen::VectorXd& y, const T_Args&... args) { // NOLINT(runtime/int) - const auto& x_eval = x.eval(); - const auto& x_val = (value_of(x_eval)).eval(); + const auto& x_val = to_ref(value_of(x)); auto args_vals_tuple = std::make_tuple(y, eval(value_of(args))...); - check_nonzero_size("algebra_solver_powell", "initial guess", x); - check_finite("algebra_solver_powell", "initial guess", x); + check_nonzero_size("algebra_solver_powell", "initial guess", x_val); + check_finite("algebra_solver_powell", "initial guess", x_val); check_nonnegative("alegbra_solver_powell", "relative_tolerance", relative_tolerance); check_nonnegative("algebra_solver_powell", "function_tolerance", function_tolerance); check_positive("algebra_solver_powell", "max_num_steps", max_num_steps); @@ -50,67 +49,10 @@ Eigen::VectorXd algebra_solver_powell_impl( fx.get_value(x_val), "the vector of unknowns, x,", x); // Solve the system - return algebra_solver_powell_(solver, fx, x_eval, 0, relative_tolerance, + return algebra_solver_powell_(solver, fx, x_val, 0, relative_tolerance, function_tolerance, max_num_steps); } -/** - * Return the solution to the specified system of algebraic - * equations given an initial guess, and parameters and data, - * which get passed into the algebraic system. - * Use Powell's dogleg solver. - * - * The user can also specify the relative tolerance - * (xtol in Eigen's code), the function tolerance, - * and the maximum number of steps (maxfev in Eigen's code). - * - * Throw an exception if the norm of f(x), where f is the - * output of the algebraic system and x the proposed solution, - * is greater than the function tolerance. We here use the - * norm as a metric to measure how far we are from the origin (0). - * - * @tparam F type of equation system function. - * @tparam T type of initial guess vector. - * - * @param[in] f Functor that evaluates the system of equations. - * @param[in] x Vector of starting values. - * @param[in] y parameter vector for the equation system. The function - * is overloaded to treat y as a vector of doubles or as a - * a template type T. - * @param[in] dat continuous data vector for the equation system. - * @param[in] dat_int integer data vector for the equation system. - * @param[in, out] msgs the print stream for warning messages. - * @param[in] relative_tolerance determines the convergence criteria - * for the solution. - * @param[in] function_tolerance determines whether roots are acceptable. - * @param[in] max_num_steps maximum number of function evaluations. - * @return theta Vector of solutions to the system of equations. - * @throw std::invalid_argument if x has size zero. - * @throw std::invalid_argument if x has non-finite elements. - * @throw std::invalid_argument if y has non-finite elements. - * @throw std::invalid_argument if dat has non-finite elements. - * @throw std::invalid_argument if dat_int has non-finite elements. - * @throw std::invalid_argument if relative_tolerance is strictly - * negative. - * @throw std::invalid_argument if function_tolerance is strictly - * negative. - * @throw std::invalid_argument if max_num_steps is not positive. - * @throw std::domain_error solver exceeds max_num_steps. - * @throw std::domain_error if the norm of the solution exceeds - * the function tolerance. - */ -template * = nullptr> -Eigen::VectorXd algebra_solver_powell( - const F& f, const T& x, const Eigen::VectorXd& y, - const std::vector& dat, const std::vector& dat_int, - std::ostream* msgs = nullptr, double relative_tolerance = 1e-10, - double function_tolerance = 1e-6, - long int max_num_steps = 1e+3) { // NOLINT(runtime/int) - return algebra_solver_powell_impl(algebra_solver_adapter(f), x, msgs, - relative_tolerance, function_tolerance, - max_num_steps, y, dat, dat_int); -} - /** Implementation of autodiff powell solver. */ template * = nullptr, @@ -119,13 +61,12 @@ Eigen::Matrix algebra_solver_powell_impl( const F& f, const T& x, std::ostream* msgs, double relative_tolerance, double function_tolerance, long int max_num_steps, const T_Args&... args) { // NOLINT(runtime/int) - const auto& x_eval = x.eval(); + const auto& x_val = to_ref(value_of(x)); auto arena_args_tuple = std::make_tuple(to_arena(args)...); - const auto& x_val = (value_of(x_eval)).eval(); auto args_vals_tuple = std::make_tuple(eval(value_of(args))...); - check_nonzero_size("algebra_solver_powell", "initial guess", x); - check_finite("algebra_solver_powell", "initial guess", x); + check_nonzero_size("algebra_solver_powell", "initial guess", x_val); + check_finite("algebra_solver_powell", "initial guess", x_val); check_nonnegative("alegbra_solver_powell", "relative_tolerance", relative_tolerance); check_nonnegative("algebra_solver_powell", "function_tolerance", function_tolerance); check_positive("algebra_solver_powell", "max_num_steps", max_num_steps); @@ -145,7 +86,7 @@ Eigen::Matrix algebra_solver_powell_impl( // Solve the system Eigen::VectorXd theta_dbl - = algebra_solver_powell_(solver, fx, x_eval, 0, relative_tolerance, + = algebra_solver_powell_(solver, fx, x_val, 0, relative_tolerance, function_tolerance, max_num_steps); Eigen::MatrixXd Jf_x; @@ -303,11 +244,8 @@ Eigen::VectorXd algebra_solver_powell_( S& solver, const F& fx, const T& x, std::ostream* msgs, double relative_tolerance, double function_tolerance, long int max_num_steps) { // NOLINT(runtime/int) - const auto& x_eval = x.eval(); - const auto& x_val = (value_of(x_eval)).eval(); - // Compute theta_dbl - Eigen::VectorXd theta_dbl = x_val; + Eigen::VectorXd theta_dbl = x; solver.parameters.xtol = relative_tolerance; solver.parameters.maxfev = max_num_steps; solver.solve(theta_dbl); From f49c28f3f23cea8acdb81fc680dfbb59d3fe253f Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Sat, 17 Apr 2021 20:05:44 +0000 Subject: [PATCH 30/88] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- .../rev/functor/algebra_solver_newton.hpp | 23 +++++++++++-------- .../rev/functor/algebra_solver_powell.hpp | 12 ++++++---- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index d7f743d8c0d..fbdee0f0c52 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -30,13 +30,14 @@ Eigen::VectorXd algebra_solver_newton_impl( check_nonzero_size("algebra_solver_newton", "initial guess", x_val); check_finite("algebra_solver_newton", "initial guess", x_val); - check_nonnegative("algebra_solver_newton", "scaling_step_size", scaling_step_size); - check_nonnegative("algebra_solver_newton", "function_tolerance", function_tolerance); + check_nonnegative("algebra_solver_newton", "scaling_step_size", + scaling_step_size); + check_nonnegative("algebra_solver_newton", "function_tolerance", + function_tolerance); check_positive("algebra_solver_newton", "max_num_steps", max_num_steps); - return kinsol_solve(f, x_val, scaling_step_size, - function_tolerance, max_num_steps, 1, 10, KIN_LINESEARCH, - msgs, y, args...); + return kinsol_solve(f, x_val, scaling_step_size, function_tolerance, + max_num_steps, 1, 10, KIN_LINESEARCH, msgs, y, args...); } /** Implementation of autodiff newton solver. */ @@ -53,16 +54,18 @@ Eigen::Matrix algebra_solver_newton_impl( check_nonzero_size("algebra_solver_newton", "initial guess", x_val); check_finite("algebra_solver_newton", "initial guess", x_val); - check_nonnegative("algebra_solver_newton", "scaling_step_size", scaling_step_size); - check_nonnegative("algebra_solver_newton", "function_tolerance", function_tolerance); + check_nonnegative("algebra_solver_newton", "scaling_step_size", + scaling_step_size); + check_nonnegative("algebra_solver_newton", "function_tolerance", + function_tolerance); check_positive("algebra_solver_newton", "max_num_steps", max_num_steps); // Solve the system Eigen::VectorXd theta_dbl = apply( [&](const auto&... vals) { - return kinsol_solve(f, x_val, scaling_step_size, - function_tolerance, max_num_steps, 1, 10, - KIN_LINESEARCH, msgs, vals...); + return kinsol_solve(f, x_val, scaling_step_size, function_tolerance, + max_num_steps, 1, 10, KIN_LINESEARCH, msgs, + vals...); }, args_vals_tuple); diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 9f4dbb275d7..965fa93d9cf 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -31,8 +31,10 @@ Eigen::VectorXd algebra_solver_powell_impl( check_nonzero_size("algebra_solver_powell", "initial guess", x_val); check_finite("algebra_solver_powell", "initial guess", x_val); - check_nonnegative("alegbra_solver_powell", "relative_tolerance", relative_tolerance); - check_nonnegative("algebra_solver_powell", "function_tolerance", function_tolerance); + check_nonnegative("alegbra_solver_powell", "relative_tolerance", + relative_tolerance); + check_nonnegative("algebra_solver_powell", "function_tolerance", + function_tolerance); check_positive("algebra_solver_powell", "max_num_steps", max_num_steps); // Construct the Powell solver @@ -67,8 +69,10 @@ Eigen::Matrix algebra_solver_powell_impl( check_nonzero_size("algebra_solver_powell", "initial guess", x_val); check_finite("algebra_solver_powell", "initial guess", x_val); - check_nonnegative("alegbra_solver_powell", "relative_tolerance", relative_tolerance); - check_nonnegative("algebra_solver_powell", "function_tolerance", function_tolerance); + check_nonnegative("alegbra_solver_powell", "relative_tolerance", + relative_tolerance); + check_nonnegative("algebra_solver_powell", "function_tolerance", + function_tolerance); check_positive("algebra_solver_powell", "max_num_steps", max_num_steps); // Construct the Powell solver From f92741f17d00ee0dc7df80497239dc7140a0b3eb Mon Sep 17 00:00:00 2001 From: Ben Date: Sat, 17 Apr 2021 13:34:22 -0700 Subject: [PATCH 31/88] Removed system functor test --- stan/math/rev/functor/algebra_system.hpp | 53 ------------------- .../math/rev/functor/algebra_solver_test.cpp | 18 ------- 2 files changed, 71 deletions(-) diff --git a/stan/math/rev/functor/algebra_system.hpp b/stan/math/rev/functor/algebra_system.hpp index caac096ed3c..f87ff6cdbca 100644 --- a/stan/math/rev/functor/algebra_system.hpp +++ b/stan/math/rev/functor/algebra_system.hpp @@ -12,59 +12,6 @@ namespace stan { namespace math { -/** - * A functor that allows us to treat either x or y as - * the independent variable. If x_is_iv = true, than the - * Jacobian is computed w.r.t x, else it is computed - * w.r.t y. - * - * @tparam F type for algebraic system functor - * @tparam T0 type for unknowns - * @tparam T1 type for auxiliary parameters - * @tparam x_is_iv true if x is the independent variable - */ -template -struct system_functor { - /** algebraic system functor */ - F f_; - /** unknowns */ - Eigen::Matrix x_; - /** auxiliary parameters */ - Eigen::Matrix y_; - /** real data */ - std::vector dat_; - /** integer data */ - std::vector dat_int_; - /** stream message */ - std::ostream* msgs_; - - system_functor() {} - - system_functor(const F& f, const Eigen::Matrix& x, - const Eigen::Matrix& y, - const std::vector& dat, - const std::vector& dat_int, std::ostream* msgs) - : f_(f), x_(x), y_(y), dat_(dat), dat_int_(dat_int), msgs_(msgs) {} - - /** - * An operator that takes in an independent variable. The - * independent variable is either passed as the unknown x, - * or the auxiliary parameter y. The x_is_iv template parameter - * allows us to determine whether the jacobian is computed - * with respect to x or y. - * @tparam T the scalar type of the independent variable - */ - template - inline Eigen::Matrix operator()( - const Eigen::Matrix& iv) const { - if (x_is_iv) { - return f_(iv, y_, dat_, dat_int_, msgs_); - } else { - return f_(x_, iv, dat_, dat_int_, msgs_); - } - } -}; - /** * A structure which gets passed to Eigen's dogleg * algebraic solver. diff --git a/test/unit/math/rev/functor/algebra_solver_test.cpp b/test/unit/math/rev/functor/algebra_solver_test.cpp index e89cef3ca52..e66b2c340b6 100644 --- a/test/unit/math/rev/functor/algebra_solver_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_test.cpp @@ -347,24 +347,6 @@ TEST_F(degenerate_eq_test, powell_guess_saddle_point_dbl) { EXPECT_FLOAT_EQ(100, theta(1)); } -// unit test to demo issue #696 -// system functor init bug issue #696 -TEST(MathMatrixRevMat, system_functor_constructor) { - using stan::math::system_functor; - - Eigen::VectorXd y(2); - y << 5, 8; - Eigen::VectorXd x(2); - x << 10, 1; - std::vector dat{0.0, 0.0}; - std::vector dat_int{0, 0}; - std::ostream* msgs = 0; - int f = 99; - - system_functor fs(f, x, y, dat, dat_int, msgs); - - EXPECT_EQ(fs.f_, f); -} ////////////////////////////////////////////////////////////////////////// // Tests for newton solver. From e7d9b1d67af008771346d0ab46f038683a0e50ac Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Tue, 27 Apr 2021 11:23:11 -0700 Subject: [PATCH 32/88] Switch to using Cholesky factors to do matrix solve. --- stan/math/rev/functor/algebra_solver_newton.hpp | 2 +- stan/math/rev/functor/algebra_solver_powell.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index fbdee0f0c52..450e9094ebf 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -93,7 +93,7 @@ Eigen::Matrix algebra_solver_newton_impl( // Contract specificities with inverse Jacobian of f with respect to x. VectorXd ret_adj = ret.adj(); - VectorXd eta = -arena_Jf_x.transpose().fullPivLu().solve(ret_adj); + VectorXd eta = -arena_Jf_x.transpose().llt().solve(ret_adj); // Contract with Jacobian of f with respect to y using a nested reverse // autodiff pass. diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 965fa93d9cf..9c837717fc3 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -111,7 +111,7 @@ Eigen::Matrix algebra_solver_powell_impl( // Contract specificities with inverse Jacobian of f with respect to x. VectorXd ret_adj = ret.adj(); - VectorXd eta = -arena_Jf_x.transpose().fullPivLu().solve(ret_adj); + VectorXd eta = -arena_Jf_x.transpose().llt().solve(ret_adj); // Contract with Jacobian of f with respect to y using a nested reverse // autodiff pass. From 0532d678d641db306c68559198245d6988f9458a Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Tue, 27 Apr 2021 12:01:59 -0700 Subject: [PATCH 33/88] Switch to LU decomp for matrix solve in general case. --- stan/math/rev/functor/algebra_solver_newton.hpp | 2 +- stan/math/rev/functor/algebra_solver_powell.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index 450e9094ebf..77bdb6060be 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -93,7 +93,7 @@ Eigen::Matrix algebra_solver_newton_impl( // Contract specificities with inverse Jacobian of f with respect to x. VectorXd ret_adj = ret.adj(); - VectorXd eta = -arena_Jf_x.transpose().llt().solve(ret_adj); + VectorXd eta = -arena_Jf_x.transpose().lu().solve(ret_adj); // Contract with Jacobian of f with respect to y using a nested reverse // autodiff pass. diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 9c837717fc3..f871897e52d 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -111,7 +111,7 @@ Eigen::Matrix algebra_solver_powell_impl( // Contract specificities with inverse Jacobian of f with respect to x. VectorXd ret_adj = ret.adj(); - VectorXd eta = -arena_Jf_x.transpose().llt().solve(ret_adj); + VectorXd eta = -arena_Jf_x.transpose().lu().solve(ret_adj); // Contract with Jacobian of f with respect to y using a nested reverse // autodiff pass. From 29185c6ce21d66700524309e588fceed7d8ae628 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Mon, 17 May 2021 14:41:36 -0700 Subject: [PATCH 34/88] Fix variable names. --- .../rev/functor/algebra_solver_newton.hpp | 20 ++--- .../rev/functor/algebra_solver_powell.hpp | 89 ++++++++++--------- stan/math/rev/functor/kinsol_data.hpp | 4 +- 3 files changed, 58 insertions(+), 55 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index 77bdb6060be..766cc7d3fbe 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -25,18 +25,18 @@ Eigen::VectorXd algebra_solver_newton_impl( const F& f, const T& x, std::ostream* msgs, double scaling_step_size, double function_tolerance, long int max_num_steps, const Eigen::VectorXd& y, const T_Args&... args) { // NOLINT(runtime/int) - const auto& x_val = to_ref(value_of(x)); + const auto& x_ref = to_ref(value_of(x)); auto args_vals_tuple = std::make_tuple(y, to_ref(args)...); - check_nonzero_size("algebra_solver_newton", "initial guess", x_val); - check_finite("algebra_solver_newton", "initial guess", x_val); + check_nonzero_size("algebra_solver_newton", "initial guess", x_ref); + check_finite("algebra_solver_newton", "initial guess", x_ref); check_nonnegative("algebra_solver_newton", "scaling_step_size", scaling_step_size); check_nonnegative("algebra_solver_newton", "function_tolerance", function_tolerance); check_positive("algebra_solver_newton", "max_num_steps", max_num_steps); - return kinsol_solve(f, x_val, scaling_step_size, function_tolerance, + return kinsol_solve(f, x_ref, scaling_step_size, function_tolerance, max_num_steps, 1, 10, KIN_LINESEARCH, msgs, y, args...); } @@ -48,12 +48,12 @@ Eigen::Matrix algebra_solver_newton_impl( const F& f, const T& x, std::ostream* msgs, double scaling_step_size, double function_tolerance, long int max_num_steps, const T_Args&... args) { // NOLINT(runtime/int) - const auto& x_val = to_ref(value_of(x)); + const auto& x_ref = to_ref(value_of(x)); auto arena_args_tuple = std::make_tuple(to_arena(args)...); auto args_vals_tuple = std::make_tuple(to_ref(value_of(args))...); - check_nonzero_size("algebra_solver_newton", "initial guess", x_val); - check_finite("algebra_solver_newton", "initial guess", x_val); + check_nonzero_size("algebra_solver_newton", "initial guess", x_ref); + check_finite("algebra_solver_newton", "initial guess", x_ref); check_nonnegative("algebra_solver_newton", "scaling_step_size", scaling_step_size); check_nonnegative("algebra_solver_newton", "function_tolerance", @@ -63,14 +63,14 @@ Eigen::Matrix algebra_solver_newton_impl( // Solve the system Eigen::VectorXd theta_dbl = apply( [&](const auto&... vals) { - return kinsol_solve(f, x_val, scaling_step_size, function_tolerance, + return kinsol_solve(f, x_ref, scaling_step_size, function_tolerance, max_num_steps, 1, 10, KIN_LINESEARCH, msgs, vals...); }, args_vals_tuple); // Evaluate and store the Jacobian. - auto myfunc = [&](const auto& x) { + auto f_wrt_x = [&](const auto& x) { return apply([&](const auto&... args) { return f(x, msgs, args...); }, args_vals_tuple); }; @@ -78,7 +78,7 @@ Eigen::Matrix algebra_solver_newton_impl( Eigen::MatrixXd Jf_x; Eigen::VectorXd f_x; - jacobian(myfunc, theta_dbl, f_x, Jf_x); + jacobian(f_wrt_x, theta_dbl, f_x, Jf_x); using ret_type = Eigen::Matrix; auto arena_Jf_x = to_arena(Jf_x); diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index f871897e52d..a791d4eb770 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -18,6 +18,41 @@ namespace stan { namespace math { +/** Function for internal use that actually calls the powell solver. */ +template * = nullptr> +Eigen::VectorXd algebra_solver_powell_call_solver_( + S& solver, const F& fx, const T& x, std::ostream* msgs, + double relative_tolerance, double function_tolerance, + long int max_num_steps) { // NOLINT(runtime/int) + // Compute theta_dbl + Eigen::VectorXd theta_dbl = x; + solver.parameters.xtol = relative_tolerance; + solver.parameters.maxfev = max_num_steps; + solver.solve(theta_dbl); + + // Check if the max number of steps has been exceeded + if (solver.nfev >= max_num_steps) { + throw_domain_error("algebra_solver", "maximum number of iterations", + max_num_steps, "(", ") was exceeded in the solve."); + } + + // Check solution is a root + double system_norm = fx.get_value(theta_dbl).stableNorm(); + if (system_norm > function_tolerance) { + std::ostringstream message; + message << "the norm of the algebraic function is " << system_norm + << " but should be lower than the function " + << "tolerance:"; + throw_domain_error("algebra_solver", message.str().c_str(), + function_tolerance, "", + ". Consider decreasing the relative tolerance and " + "increasing max_num_steps."); + } + + return theta_dbl; +} + /** Implementation of ordinary powell solver. */ template * = nullptr, @@ -38,12 +73,12 @@ Eigen::VectorXd algebra_solver_powell_impl( check_positive("algebra_solver_powell", "max_num_steps", max_num_steps); // Construct the Powell solver - auto myfunc = [&](const auto& x) { + auto f_wrt_x = [&](const auto& x) { return apply([&](const auto&... args) { return f(x, msgs, args...); }, args_vals_tuple); }; - hybrj_functor_solver fx(myfunc); + hybrj_functor_solver fx(f_wrt_x); Eigen::HybridNonLinearSolver solver(fx); // Check dimension unknowns equals dimension of system output @@ -51,8 +86,9 @@ Eigen::VectorXd algebra_solver_powell_impl( fx.get_value(x_val), "the vector of unknowns, x,", x); // Solve the system - return algebra_solver_powell_(solver, fx, x_val, 0, relative_tolerance, - function_tolerance, max_num_steps); + return algebra_solver_powell_call_solver_( + solver, fx, x_val, 0, relative_tolerance, function_tolerance, + max_num_steps); } /** Implementation of autodiff powell solver. */ @@ -76,12 +112,12 @@ Eigen::Matrix algebra_solver_powell_impl( check_positive("algebra_solver_powell", "max_num_steps", max_num_steps); // Construct the Powell solver - auto myfunc = [&](const auto& x) { + auto f_wrt_x = [&](const auto& x) { return apply([&](const auto&... args) { return f(x, msgs, args...); }, args_vals_tuple); }; - hybrj_functor_solver fx(myfunc); + hybrj_functor_solver fx(f_wrt_x); Eigen::HybridNonLinearSolver solver(fx); // Check dimension unknowns equals dimension of system output @@ -90,13 +126,14 @@ Eigen::Matrix algebra_solver_powell_impl( // Solve the system Eigen::VectorXd theta_dbl - = algebra_solver_powell_(solver, fx, x_val, 0, relative_tolerance, - function_tolerance, max_num_steps); + = algebra_solver_powell_call_solver_( + solver, fx, x_val, 0, relative_tolerance, function_tolerance, + max_num_steps); Eigen::MatrixXd Jf_x; Eigen::VectorXd f_x; - jacobian(myfunc, theta_dbl, f_x, Jf_x); + jacobian(f_wrt_x, theta_dbl, f_x, Jf_x); using ret_type = Eigen::Matrix; auto arena_Jf_x = to_arena(Jf_x); @@ -242,40 +279,6 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver( function_tolerance, max_num_steps); } -template * = nullptr> -Eigen::VectorXd algebra_solver_powell_( - S& solver, const F& fx, const T& x, std::ostream* msgs, - double relative_tolerance, double function_tolerance, - long int max_num_steps) { // NOLINT(runtime/int) - // Compute theta_dbl - Eigen::VectorXd theta_dbl = x; - solver.parameters.xtol = relative_tolerance; - solver.parameters.maxfev = max_num_steps; - solver.solve(theta_dbl); - - // Check if the max number of steps has been exceeded - if (solver.nfev >= max_num_steps) { - throw_domain_error("algebra_solver", "maximum number of iterations", - max_num_steps, "(", ") was exceeded in the solve."); - } - - // Check solution is a root - double system_norm = fx.get_value(theta_dbl).stableNorm(); - if (system_norm > function_tolerance) { - std::ostringstream message; - message << "the norm of the algebraic function is " << system_norm - << " but should be lower than the function " - << "tolerance:"; - throw_domain_error("algebra_solver", message.str().c_str(), - function_tolerance, "", - ". Consider decreasing the relative tolerance and " - "increasing max_num_steps."); - } - - return theta_dbl; -} - } // namespace math } // namespace stan diff --git a/stan/math/rev/functor/kinsol_data.hpp b/stan/math/rev/functor/kinsol_data.hpp index 3878b08f93e..573f2128c8b 100644 --- a/stan/math/rev/functor/kinsol_data.hpp +++ b/stan/math/rev/functor/kinsol_data.hpp @@ -101,7 +101,7 @@ class kinsol_system_data { Eigen::Map f_eval_map(N_VGetArrayPointer(f_eval), explicit_system->N_); - auto myfunc = [&](const auto& x) { + auto f_wrt_x = [&](const auto& x) { return apply( [&](const auto&... args) { return explicit_system->f_(x, explicit_system->msgs_, args...); @@ -112,7 +112,7 @@ class kinsol_system_data { Eigen::MatrixXd Jf_x; Eigen::VectorXd f_x; - jacobian(myfunc, x_eigen, f_x, Jf_x); + jacobian(f_wrt_x, x_eigen, f_x, Jf_x); f_eval_map = f_x; From 81cfdb132ae4968e6c1c0d182189132fe0940087 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Mon, 17 May 2021 14:48:25 -0700 Subject: [PATCH 35/88] Fix CppLint errors. --- stan/math/rev/functor/algebra_solver_newton.hpp | 4 ++-- stan/math/rev/functor/algebra_solver_powell.hpp | 4 ++-- stan/math/rev/functor/algebra_system.hpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index 766cc7d3fbe..9820f05006a 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -23,7 +23,7 @@ template * = nullptr> Eigen::VectorXd algebra_solver_newton_impl( const F& f, const T& x, std::ostream* msgs, double scaling_step_size, - double function_tolerance, long int max_num_steps, const Eigen::VectorXd& y, + double function_tolerance, int64_t max_num_steps, const Eigen::VectorXd& y, const T_Args&... args) { // NOLINT(runtime/int) const auto& x_ref = to_ref(value_of(x)); auto args_vals_tuple = std::make_tuple(y, to_ref(args)...); @@ -46,7 +46,7 @@ template * = nullptr> Eigen::Matrix algebra_solver_newton_impl( const F& f, const T& x, std::ostream* msgs, double scaling_step_size, - double function_tolerance, long int max_num_steps, + double function_tolerance, int64_t max_num_steps, const T_Args&... args) { // NOLINT(runtime/int) const auto& x_ref = to_ref(value_of(x)); auto arena_args_tuple = std::make_tuple(to_arena(args)...); diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index a791d4eb770..d0a6bf14303 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -59,7 +59,7 @@ template * = nullptr> Eigen::VectorXd algebra_solver_powell_impl( const F& f, const T& x, std::ostream* msgs, double relative_tolerance, - double function_tolerance, long int max_num_steps, const Eigen::VectorXd& y, + double function_tolerance, int64_t max_num_steps, const Eigen::VectorXd& y, const T_Args&... args) { // NOLINT(runtime/int) const auto& x_val = to_ref(value_of(x)); auto args_vals_tuple = std::make_tuple(y, eval(value_of(args))...); @@ -97,7 +97,7 @@ template * = nullptr> Eigen::Matrix algebra_solver_powell_impl( const F& f, const T& x, std::ostream* msgs, double relative_tolerance, - double function_tolerance, long int max_num_steps, + double function_tolerance, int64_t max_num_steps, const T_Args&... args) { // NOLINT(runtime/int) const auto& x_val = to_ref(value_of(x)); auto arena_args_tuple = std::make_tuple(to_arena(args)...); diff --git a/stan/math/rev/functor/algebra_system.hpp b/stan/math/rev/functor/algebra_system.hpp index f87ff6cdbca..c0d67f54f8e 100644 --- a/stan/math/rev/functor/algebra_system.hpp +++ b/stan/math/rev/functor/algebra_system.hpp @@ -52,7 +52,7 @@ struct hybrj_functor_solver : nlo_functor { /** Jacobian of algebraic function wrt unknowns */ Eigen::MatrixXd J_; - hybrj_functor_solver(const S& fs) : fs_(fs) {} + explicit hybrj_functor_solver(const S& fs) : fs_(fs) {} /** * Computes the value the algebraic function, f, when pluging in the From 665c9d879cb6dbd35a5ee59d0a5e6b5b33198030 Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Tue, 18 May 2021 00:22:24 +0000 Subject: [PATCH 36/88] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- stan/math/rev/functor/algebra_solver_powell.hpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index d0a6bf14303..b55cf72ca99 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -86,9 +86,9 @@ Eigen::VectorXd algebra_solver_powell_impl( fx.get_value(x_val), "the vector of unknowns, x,", x); // Solve the system - return algebra_solver_powell_call_solver_( - solver, fx, x_val, 0, relative_tolerance, function_tolerance, - max_num_steps); + return algebra_solver_powell_call_solver_(solver, fx, x_val, 0, + relative_tolerance, + function_tolerance, max_num_steps); } /** Implementation of autodiff powell solver. */ @@ -125,10 +125,9 @@ Eigen::Matrix algebra_solver_powell_impl( fx.get_value(x_val), "the vector of unknowns, x,", x); // Solve the system - Eigen::VectorXd theta_dbl - = algebra_solver_powell_call_solver_( - solver, fx, x_val, 0, relative_tolerance, function_tolerance, - max_num_steps); + Eigen::VectorXd theta_dbl = algebra_solver_powell_call_solver_( + solver, fx, x_val, 0, relative_tolerance, function_tolerance, + max_num_steps); Eigen::MatrixXd Jf_x; Eigen::VectorXd f_x; From 8f1873bb5415be0c09434be6cf9b12f15fd4dac0 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Sun, 20 Jun 2021 13:32:54 -0700 Subject: [PATCH 37/88] Clean up includes. --- stan/math/rev/functor/algebra_solver_newton.hpp | 2 -- stan/math/rev/functor/algebra_solver_powell.hpp | 1 - 2 files changed, 3 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index 9820f05006a..05a44414070 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -3,10 +3,8 @@ #include #include -#include #include #include -#include #include #include #include diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index d0a6bf14303..6720e2bef03 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include From 4da599ebbabe0abb9acd8b6ab55841d506e7fa31 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Sun, 20 Jun 2021 13:33:25 -0700 Subject: [PATCH 38/88] Make naming more consistent. --- stan/math/rev/functor/kinsol_data.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stan/math/rev/functor/kinsol_data.hpp b/stan/math/rev/functor/kinsol_data.hpp index 573f2128c8b..ac06faa0e13 100644 --- a/stan/math/rev/functor/kinsol_data.hpp +++ b/stan/math/rev/functor/kinsol_data.hpp @@ -29,7 +29,7 @@ class kinsol_system_data { const Eigen::VectorXd& x_; const size_t N_; std::ostream* msgs_; - std::tuple args_tuple; + std::tuple args_tuple_; typedef kinsol_system_data system_data; @@ -46,7 +46,7 @@ class kinsol_system_data { x_(x), N_(x.size()), msgs_(msgs), - args_tuple(args...), + args_tuple_(args...), nv_x_(N_VMake_Serial(N_, &to_array_1d(x_)[0])), J_(SUNDenseMatrix(N_, N_)), LS_(SUNLinSol_Dense(nv_x_, J_)), @@ -74,7 +74,7 @@ class kinsol_system_data { [&](const auto&... args) { return explicit_system->f_(x_eigen, explicit_system->msgs_, args...); }, - explicit_system->args_tuple); + explicit_system->args_tuple_); return 0; } @@ -106,7 +106,7 @@ class kinsol_system_data { [&](const auto&... args) { return explicit_system->f_(x, explicit_system->msgs_, args...); }, - explicit_system->args_tuple); + explicit_system->args_tuple_); }; Eigen::MatrixXd Jf_x; From 1388c05872c31520a84b90d55f240da60d44eef6 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Sun, 20 Jun 2021 13:34:05 -0700 Subject: [PATCH 39/88] Make fixed point algebra solver variadic. This commit updates the fixed point solver so that variadic arguments can be passed to it. It also uses a more efficient method of propagating adjoints, in line with the other solvers. This required restructuring some aspects of the code slightly; it is now organized to mimic the code for the Newton solver as closely as possible. This has resulted in a number of tests no longer being applicable. I've commented them out for now, and will go through more carefully in the future. --- stan/math/rev/functor/algebra_solver_fp.hpp | 382 ++++++++---------- .../rev/functor/algebra_solver_fp_test.cpp | 100 +++-- 2 files changed, 217 insertions(+), 265 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_fp.hpp b/stan/math/rev/functor/algebra_solver_fp.hpp index 3fe66b399f5..59d588cbb85 100644 --- a/stan/math/rev/functor/algebra_solver_fp.hpp +++ b/stan/math/rev/functor/algebra_solver_fp.hpp @@ -7,10 +7,10 @@ #include #include #include -#include -#include -#include #include +#include +#include +#include #include #include #include @@ -30,25 +30,16 @@ namespace math { * * @tparam F functor type for system function. */ -template +template struct KinsolFixedPointEnv { /** RHS functor. */ const F& f_; - /** val of params for @c y_ to refer to when - params are @c var type */ - const Eigen::VectorXd y_dummy; - /** ref to val of params */ - const Eigen::VectorXd& y_; /** system size */ const size_t N_; - /** nb. of params */ - const size_t M_; - /** real data */ - const std::vector& x_r_; - /** integer data */ - const std::vector& x_i_; /** message stream */ std::ostream* msgs_; + /** arguments and parameters */ + std::tuple args_tuple_; /** KINSOL memory block */ void* mem_; /** NVECTOR for unknowns */ @@ -58,48 +49,14 @@ struct KinsolFixedPointEnv { /** NVECTOR for scaling f */ N_Vector nv_f_scal_; - /** Constructor when y is data */ - template - KinsolFixedPointEnv(const F& f, const Eigen::Matrix& x, - const Eigen::VectorXd& y, const std::vector& x_r, - const std::vector& x_i, std::ostream* msgs, + KinsolFixedPointEnv(const F& f, const Eigen::MatrixXd x, const std::vector& u_scale, - const std::vector& f_scale) + const std::vector& f_scale, + std::ostream* msgs, const Args&... args) : f_(f), - y_dummy(), - y_(y), N_(x.size()), - M_(y.size()), - x_r_(x_r), - x_i_(x_i), - msgs_(msgs), - mem_(KINCreate()), - nv_x_(N_VNew_Serial(N_)), - nv_u_scal_(N_VNew_Serial(N_)), - nv_f_scal_(N_VNew_Serial(N_)) { - for (int i = 0; i < N_; ++i) { - NV_Ith_S(nv_x_, i) = stan::math::value_of(x(i)); - NV_Ith_S(nv_u_scal_, i) = stan::math::value_of(u_scale[i]); - NV_Ith_S(nv_f_scal_, i) = stan::math::value_of(f_scale[i]); - } - } - - /** Constructor when y is param */ - template - KinsolFixedPointEnv(const F& f, const Eigen::Matrix& x, - const Eigen::Matrix& y, - const std::vector& x_r, - const std::vector& x_i, std::ostream* msgs, - const std::vector& u_scale, - const std::vector& f_scale) - : f_(f), - y_dummy(stan::math::value_of(y)), - y_(y_dummy), - N_(x.size()), - M_(y.size()), - x_r_(x_r), - x_i_(x_i), msgs_(msgs), + args_tuple_(args...), mem_(KINCreate()), nv_x_(N_VNew_Serial(N_)), nv_u_scal_(N_VNew_Serial(N_)), @@ -120,21 +77,99 @@ struct KinsolFixedPointEnv { /** Implements the user-defined function passed to KINSOL. */ static int kinsol_f_system(N_Vector x, N_Vector f, void* user_data) { - auto g = static_cast*>(user_data); + auto g = + static_cast*>(user_data); Eigen::VectorXd x_eigen(Eigen::Map(NV_DATA_S(x), g->N_)); - Eigen::Map(N_VGetArrayPointer(f), g->N_) - = g->f_(x_eigen, g->y_, g->x_r_, g->x_i_, g->msgs_); + Eigen::Map f_map(N_VGetArrayPointer(f), g->N_); + + f_map = apply( + [&](const auto&... args) { + return g->f_(x_eigen, g->msgs_, args...); + }, + g->args_tuple_); return 0; } }; /** - * Calculate Jacobian Jxy(Jacobian of unknown x w.r.t. the * param y) - * given the solution. Specifically, for + * Solve FP using KINSOL + * + * @param x initial point and final solution. + * @param env KINSOL solution environment + * @param f_tol Function tolerance + * @param max_num_steps max nb. of iterations. + */ +template +Eigen::VectorXd kinsol_solve_fp( + const F& f, + const Eigen::VectorXd& x, + double function_tolerance, + double max_num_steps, + const std::vector& u_scale, + const std::vector& f_scale, + std::ostream* msgs, + const Args&... args) { + KinsolFixedPointEnv env( + f, x, u_scale, f_scale, msgs, args...); + int N = env.N_; + void* mem = env.mem_; + const int default_anderson_depth = 4; + int anderson_depth = std::min(N, default_anderson_depth); + Eigen::VectorXd x_solution(N); + + check_flag_sundials(KINSetNumMaxIters(mem, max_num_steps), + "KINSetNumMaxIters"); + check_flag_sundials(KINSetMAA(mem, anderson_depth), "KINSetMAA"); + check_flag_sundials(KINInit(mem, &env.kinsol_f_system, env.nv_x_), + "KINInit"); + check_flag_sundials(KINSetFuncNormTol(mem, function_tolerance), + "KINSetFuncNormTol"); + check_flag_sundials(KINSetUserData(mem, static_cast(&env)), + "KINSetUserData"); + + check_flag_kinsol( + KINSol(mem, env.nv_x_, KIN_FP, env.nv_u_scal_, env.nv_f_scal_), + max_num_steps); + + for (int i = 0; i < N; i++) + x_solution(i) = NV_Ith_S(env.nv_x_, i); + + return x_solution; +} + +/** Implementation of ordinary fixed point solver. */ +template * = nullptr, + require_all_st_arithmetic* = nullptr> +Eigen::VectorXd algebra_solver_fp_impl( + const F& f, const T& x, const double function_tolerance, + const int max_num_steps, const std::vector& u_scale, + const std::vector& f_scale, std::ostream* msgs, const Args&... args) { + const auto& x_ref = to_ref(value_of(x)); + + check_nonzero_size("algebra_solver_fp", "initial guess", x_ref); + check_finite("algebra_solver_fp", "initial guess", x_ref); + check_nonnegative("algebra_solver_fp", "u_scale", u_scale); + check_nonnegative("algebra_solver_fp", "f_scale", f_scale); + check_nonnegative("algebra_solver_fp", "function_tolerance", + function_tolerance); + check_positive("algebra_solver_fp", "max_num_steps", max_num_steps); + check_matching_sizes("algebra_solver_fp", "the algebraic system's output", + value_of(f(x_ref, msgs, args...)), + "the vector of unknowns, x,", x_ref); + + return kinsol_solve_fp(f, x_ref, function_tolerance, max_num_steps, u_scale, + f_scale, msgs, args...); +} + +/* Implementation of autodiff fixed point solver. The Jacobian Jxy(Jacobian of + * unknown x w.r.t. the * param y) is calculated given the solution as follows. + * Since * * x - f(x, y) = 0 * - * we have (Jpq = Jacobian matrix dq/dq) + * we have (Jpq being the Jacobian matrix dq/dq) * * Jxy - Jfx * Jxy = Jfy * @@ -142,168 +177,81 @@ struct KinsolFixedPointEnv { * * (I - Jfx) * Jxy = Jfy * - * Jfx and Jfy are obtained through one AD evaluation of f - * w.r.t combined vector [x, y]. - */ -struct FixedPointADJac { - /** - * Calculate Jacobian Jxy. - * - * @tparam F RHS functor type - * @param x fixed point solution - * @param y RHS parameters - * @param env KINSOL working environment, see doc for @c KinsolFixedPointEnv. - */ - template - inline Eigen::Matrix operator()( - const Eigen::VectorXd& x, const Eigen::Matrix& y, - KinsolFixedPointEnv& env) { - using stan::math::precomputed_gradients; - using stan::math::to_array_1d; - using stan::math::var; - - auto g = [&env](const Eigen::Matrix& par_) { - Eigen::Matrix x_(par_.head(env.N_)); - Eigen::Matrix y_(par_.tail(env.M_)); - return env.f_(x_, y_, env.x_r_, env.x_i_, env.msgs_); - }; - - Eigen::VectorXd theta(x.size() + y.size()); - for (int i = 0; i < env.N_; ++i) { - theta(i) = x(i); - } - for (int i = 0; i < env.M_; ++i) { - theta(i + env.N_) = env.y_(i); - } - Eigen::Matrix fx; - Eigen::Matrix J_theta; - stan::math::jacobian(g, theta, fx, J_theta); - Eigen::MatrixXd A(J_theta.block(0, 0, env.N_, env.N_)); - Eigen::MatrixXd b(J_theta.block(0, env.N_, env.N_, env.M_)); - A = Eigen::MatrixXd::Identity(env.N_, env.N_) - A; - Eigen::MatrixXd Jxy = A.colPivHouseholderQr().solve(b); - std::vector gradients(env.M_); - Eigen::Matrix x_sol(env.N_); - std::vector yv(to_array_1d(y)); - for (int i = 0; i < env.N_; ++i) { - gradients = to_array_1d(Eigen::VectorXd(Jxy.row(i))); - x_sol[i] = precomputed_gradients(x(i), yv, gradients); - } - return x_sol; - } -}; - -/** - * Fixed point solver for problem of form + * Let eta be the adjoint with respect to x; then to calculate * - * x = F(x; theta) + * eta * Jxy * - * with x as unknowns and theta parameters. + * we solve * - * The solution for FP iteration - * doesn't involve derivatives but only data types. + * (eta * (I - Jfx)^(-1)) * Jfy * - * @tparam fp_env_type solver environment setup that handles - * workspace & auxiliary data encapsulation & RAII, namely - * the work environment. Currently only support KINSOL's - * dense matrix. - * @tparam fp_jac_type functor type for calculating the - * jacobian. Currently only support @c - * FixedPointADJac that obtain dense Jacobian - * through QR decomposition. + * (This is virtually identical to the Powell and Newton solvers, except Jfx + * has been replaced by I - Jfx.) */ -template -struct FixedPointSolver; +template * = nullptr, + require_any_st_var* = nullptr> +Eigen::Matrix algebra_solver_fp_impl( + const F& f, const T& x, const double function_tolerance, + const int max_num_steps, const std::vector& u_scale, + const std::vector& f_scale, std::ostream* msgs, const Args&... args) { + const auto& x_val = to_ref(value_of(x)); + auto arena_args_tuple = std::make_tuple(to_arena(args)...); + auto args_vals_tuple = std::make_tuple(eval(value_of(args))...); -/** - * Specialization for fixed point solver when using KINSOL. - * - * @tparam F RHS functor for fixed point iteration. - * @tparam fp_jac_type functor type for calculating the jacobian - */ -template -struct FixedPointSolver, fp_jac_type> { - /** - * Solve FP using KINSOL - * - * @param x initial point and final solution. - * @param env KINSOL solution environment - * @param f_tol Function tolerance - * @param max_num_steps max nb. of iterations. - */ - void kinsol_solve_fp(Eigen::VectorXd& x, KinsolFixedPointEnv& env, - double f_tol, int max_num_steps) { - int N = env.N_; - void* mem = env.mem_; + auto f_wrt_x = [&](const auto& x) { + return apply([&](const auto&... args) { return f(x, msgs, args...); }, + args_vals_tuple); + }; - const int default_anderson_depth = 4; - int anderson_depth = std::min(N, default_anderson_depth); + // FP solution + Eigen::VectorXd theta_dbl = apply( + [&](const auto&... vals) { + return kinsol_solve_fp(f, x_val, function_tolerance, max_num_steps, + u_scale, f_scale, msgs, vals...); + }, + args_vals_tuple); - check_flag_sundials(KINSetNumMaxIters(mem, max_num_steps), - "KINSetNumMaxIters"); - check_flag_sundials(KINSetMAA(mem, anderson_depth), "KINSetMAA"); - check_flag_sundials(KINInit(mem, &env.kinsol_f_system, env.nv_x_), - "KINInit"); - check_flag_sundials(KINSetFuncNormTol(mem, f_tol), "KINSetFuncNormTol"); - check_flag_sundials(KINSetUserData(mem, static_cast(&env)), - "KINSetUserData"); + Eigen::MatrixXd Jf_x; + Eigen::VectorXd f_x; - check_flag_kinsol( - KINSol(mem, env.nv_x_, KIN_FP, env.nv_u_scal_, env.nv_f_scal_), - max_num_steps); + jacobian(f_wrt_x, theta_dbl, f_x, Jf_x); + int N = x.size(); + Jf_x -= Eigen::MatrixXd::Identity(x.size(), x.size()); - for (int i = 0; i < N; ++i) { - x(i) = NV_Ith_S(env.nv_x_, i); - } - } + using ret_type = Eigen::Matrix; + auto arena_Jf_x = to_arena(Jf_x); - /** - * Solve data-only FP problem so no need to calculate jacobian. - * - * @tparam T1 type of init point of iterations - * - * @param x initial point and final solution. - * @param y RHS functor parameters - * @param env KINSOL solution environment - * @param f_tol Function tolerance - * @param max_num_steps max nb. of iterations. - */ - template - Eigen::Matrix solve(const Eigen::Matrix& x, - const Eigen::Matrix& y, - KinsolFixedPointEnv& env, double f_tol, - int max_num_steps) { - Eigen::VectorXd xd(stan::math::value_of(x)); - kinsol_solve_fp(xd, env, f_tol, max_num_steps); - return xd; - } + arena_t ret = theta_dbl; - /** - * Solve FP problem and calculate jacobian. - * - * @tparam T1 type of init point of iterations - * - * @param x initial point and final solution. - * @param y RHS functor parameters - * @param env KINSOL solution environment - * @param f_tol Function tolerance - * @param max_num_steps max nb. of iterations. - */ - template - Eigen::Matrix solve( - const Eigen::Matrix& x, - const Eigen::Matrix& y, - KinsolFixedPointEnv& env, double f_tol, int max_num_steps) { - using stan::math::value_of; - using stan::math::var; + reverse_pass_callback([f, ret, arena_args_tuple, arena_Jf_x, msgs]() mutable { + using Eigen::Dynamic; + using Eigen::Matrix; + using Eigen::MatrixXd; + using Eigen::VectorXd; - // FP solution - Eigen::VectorXd xd(solve(x, Eigen::VectorXd(), env, f_tol, max_num_steps)); + // Contract specificities with inverse Jacobian of f with respect to x. + VectorXd ret_adj = ret.adj(); + VectorXd eta = -arena_Jf_x.transpose().lu().solve(ret_adj); + + // Contract with Jacobian of f with respect to y using a nested reverse + // autodiff pass. + { + nested_rev_autodiff rev; + + VectorXd ret_val = ret.val(); + auto x_nrad_ = apply( + [&](const auto&... args) { return eval(f(ret_val, msgs, args...)); }, + arena_args_tuple); + x_nrad_.adj() = eta; + grad(); + } + }); + + return ret_type(ret); +} - fp_jac_type jac_sol; - return jac_sol(xd, y, env); - } -}; /** * Return a fixed pointer to the specified system of algebraic @@ -364,22 +312,14 @@ struct FixedPointSolver, fp_jac_type> { template Eigen::Matrix algebra_solver_fp( const F& f, const Eigen::Matrix& x, - const Eigen::Matrix& y, const std::vector& x_r, - const std::vector& x_i, const std::vector& u_scale, + const Eigen::Matrix& y, const std::vector& dat, + const std::vector& dat_int, const std::vector& u_scale, const std::vector& f_scale, std::ostream* msgs = nullptr, - double f_tol = 1e-8, + double function_tolerance = 1e-8, int max_num_steps = 200) { // NOLINT(runtime/int) - algebra_solver_check(x, y, x_r, x_i, f_tol, max_num_steps); - check_nonnegative("algebra_solver", "u_scale", u_scale); - check_nonnegative("algebra_solver", "f_scale", f_scale); - check_matching_sizes("algebra_solver", "the algebraic system's output", - value_of(f(x, y, x_r, x_i, msgs)), - "the vector of unknowns, x,", x); - - KinsolFixedPointEnv env(f, x, y, x_r, x_i, msgs, u_scale, - f_scale); // NOLINT - FixedPointSolver, FixedPointADJac> fp; - return fp.solve(x, y, env, f_tol, max_num_steps); + return algebra_solver_fp_impl(algebra_solver_adapter(f), x, function_tolerance, + max_num_steps, u_scale, f_scale, msgs, y, dat, + dat_int); } } // namespace math diff --git a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp index 4e30a9b92fe..8b55a9a5959 100644 --- a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp @@ -12,8 +12,8 @@ using stan::math::algebra_solver_fp; using stan::math::finite_diff_gradient_auto; -using stan::math::FixedPointADJac; -using stan::math::FixedPointSolver; +// using stan::math::FixedPointADJac; +// using stan::math::FixedPointSolver; using stan::math::KinsolFixedPointEnv; using stan::math::to_array_1d; using stan::math::to_var; @@ -31,13 +31,11 @@ struct FP_exp_func_test : public ::testing::Test { * RHS functor */ struct FP_exp_func { - template - inline Eigen::Matrix, -1, 1> operator()( - const Eigen::Matrix& x, - const Eigen::Matrix& y, - const std::vector& x_r, const std::vector& x_i, - std::ostream* pstream__) const { - using scalar = stan::return_type_t; + template + inline Eigen::Matrix, Eigen::Dynamic, 1> + operator()(const T1& x, const T2& y, const T3& x_r, const T4& x_i, + std::ostream* pstream__) const { + using scalar = stan::return_type_t; Eigen::Matrix z(1); z(0) = stan::math::exp(-y(0) * x(0)); return z; @@ -65,6 +63,7 @@ struct FP_exp_func_test : public ::testing::Test { u_scale{1.0}, f_scale{1.0} {} + /* auto fd_functor(int i) { auto f_fd = [this, i](const Eigen::VectorXd& y_) { KinsolFixedPointEnv env(f, x, y_, x_r, x_i, msgs, u_scale, @@ -76,6 +75,7 @@ struct FP_exp_func_test : public ::testing::Test { }; return f_fd; } + */ }; /* @@ -90,13 +90,11 @@ struct FP_2d_func_test : public ::testing::Test { * RHS functor */ struct FP_2d_func { - template - inline Eigen::Matrix, -1, 1> operator()( - const Eigen::Matrix& x, - const Eigen::Matrix& y, - const std::vector& x_r, const std::vector& x_i, + template + inline Eigen::Matrix, -1, 1> operator()( + const T1& x, const T2&y, const T3& x_r, const T4& x_i, std::ostream* pstream__) const { - using scalar = stan::return_type_t; + using scalar = stan::return_type_t; Eigen::Matrix z(2); z(0) = y(0) * sqrt(x(1)); z(1) = y(1) * sqrt(y(2) - x(0) * x(0)); @@ -125,6 +123,7 @@ struct FP_2d_func_test : public ::testing::Test { u_scale{1.0, 1.0}, f_scale{1.0, 1.0} {} + /* auto fd_functor(int i) { auto f_fd = [this, i](const Eigen::VectorXd& y_) { KinsolFixedPointEnv env(f, x, y_, x_r, x_i, msgs, u_scale, @@ -136,6 +135,7 @@ struct FP_2d_func_test : public ::testing::Test { }; return f_fd; } + */ }; /* @@ -154,13 +154,11 @@ struct FP_degenerated_func_test : public ::testing::Test { * RHS functor */ struct FP_degenerated_func { - template - inline Eigen::Matrix, -1, 1> operator()( - const Eigen::Matrix& x, - const Eigen::Matrix& y, - const std::vector& x_r, const std::vector& x_i, - std::ostream* pstream__) const { - using scalar = stan::return_type_t; + template + inline Eigen::Matrix, Eigen::Dynamic, 1> + operator()(const T1& x, const T2& y, const T3& dat, const T4& dat_int, + std::ostream* pstream__) const { + using scalar = stan::return_type_t; Eigen::Matrix z(2); z(0) = y(0) + (x(0) - y(0)) * y(1) / x(1); z(1) = y(0) + (x(1) - y(0)) * y(1) / x(0); @@ -189,6 +187,7 @@ struct FP_degenerated_func_test : public ::testing::Test { u_scale{1.0, 1.0}, f_scale{1.0, 1.0} {} + /* auto fd_functor(int i) { auto f_fd = [this, i](const Eigen::VectorXd& y_) { KinsolFixedPointEnv env(f, x, y_, x_r, x_i, msgs, @@ -203,6 +202,7 @@ struct FP_degenerated_func_test : public ::testing::Test { }; return f_fd; } + */ }; /* @@ -217,13 +217,11 @@ struct FP_direct_prod_func_test : public ::testing::Test { * RHS functor */ struct FP_direct_prod_func { - template - inline Eigen::Matrix, -1, 1> operator()( - const Eigen::Matrix& x, - const Eigen::Matrix& y, - const std::vector& x_r, const std::vector& x_i, - std::ostream* pstream__) const { - using scalar = stan::return_type_t; + template + inline Eigen::Matrix, Eigen::Dynamic, 1> + operator()(const T1&x, const T2& y, const T3& dat, const T4& dat_int, + std::ostream* pstream__) const { + using scalar = stan::return_type_t; const size_t n = x.size(); Eigen::Matrix z(n); const size_t m = 10; @@ -248,16 +246,14 @@ struct FP_direct_prod_func_test : public ::testing::Test { * Newton root functor */ struct FP_direct_prod_newton_func { - template - inline Eigen::Matrix, -1, 1> operator()( - const Eigen::Matrix& x, - const Eigen::Matrix& y, - const std::vector& x_r, const std::vector& x_i, - std::ostream* pstream__) const { - using scalar = stan::return_type_t; + template + inline Eigen::Matrix, Eigen::Dynamic, 1> + operator()(const T1& x, const T2& y, const T3& dat, const T4& dat_int, + std::ostream* pstream__) const { + using scalar = stan::return_type_t; const size_t n = x.size(); Eigen::Matrix z(n); - z = FP_direct_prod_func()(x, y, x_r, x_i, pstream__); + z = FP_direct_prod_func()(x, y, dat, dat_int, pstream__); for (size_t i = 0; i < n; ++i) { z(i) = x(i) - z(i); } @@ -294,6 +290,7 @@ struct FP_direct_prod_func_test : public ::testing::Test { } } + /* auto fd_functor(int i) { auto f_fd = [this, i](const Eigen::VectorXd& y_) { KinsolFixedPointEnv env(f, x, y_, x_r, x_i, msgs, @@ -308,8 +305,10 @@ struct FP_direct_prod_func_test : public ::testing::Test { }; return f_fd; } + */ }; +/* TEST_F(FP_exp_func_test, solve) { KinsolFixedPointEnv env(f, x, y, x_r, x_i, msgs, u_scale, f_scale); @@ -342,7 +341,9 @@ TEST_F(FP_2d_func_test, solve) { EXPECT_FLOAT_EQ(res(0), 0.7861513777574); EXPECT_FLOAT_EQ(res(1), 0.6180339887499); } +*/ +/* TEST_F(FP_exp_func_test, gradient) { KinsolFixedPointEnv env(f, x, y, x_r, x_i, msgs, u_scale, f_scale); @@ -364,7 +365,9 @@ TEST_F(FP_exp_func_test, gradient) { finite_diff_gradient_auto(f_fd, y, fx, grad_fx); EXPECT_FLOAT_EQ(grad_fx(0), yp(0).adj()); } +*/ +/* TEST_F(FP_2d_func_test, gradient) { KinsolFixedPointEnv env(f, x, y, x_r, x_i, msgs, u_scale, f_scale); @@ -390,7 +393,9 @@ TEST_F(FP_2d_func_test, gradient) { } } } +*/ +/* TEST_F(FP_2d_func_test, gradient_with_var_init_point) { KinsolFixedPointEnv env(f, x, y, x_r, x_i, msgs, u_scale, f_scale); @@ -416,7 +421,9 @@ TEST_F(FP_2d_func_test, gradient_with_var_init_point) { } } } +*/ +/* TEST_F(FP_2d_func_test, algebra_solver_fp) { double f_tol = 1.e-12; int max_num_steps = 100; @@ -447,7 +454,9 @@ TEST_F(FP_2d_func_test, algebra_solver_fp) { } } } +*/ +/* TEST_F(FP_degenerated_func_test, algebra_solver_fp) { double f_tol = 1.e-12; int max_num_steps = 100; @@ -478,13 +487,14 @@ TEST_F(FP_degenerated_func_test, algebra_solver_fp) { } } } +*/ TEST_F(FP_degenerated_func_test, scaling_vector_as_params) { double f_tol = 1.e-12; int max_num_steps = 100; - std::vector u_scale_v(to_var(u_scale)); - std::vector f_scale_v(to_var(f_scale)); + const std::vector u_scale_v(to_var(u_scale)); + const std::vector f_scale_v(to_var(f_scale)); Eigen::Matrix xd = algebra_solver_fp( f, x, y, x_r, x_i, u_scale, f_scale, 0, f_tol, max_num_steps); // NOLINT EXPECT_FLOAT_EQ(xd(0), 5.0); @@ -533,7 +543,7 @@ TEST_F(FP_2d_func_test, exception_handling) { { std::stringstream err_msg; - err_msg << "algebra_solver: initial guess has size 0"; + err_msg << "algebra_solver_fp: initial guess has size 0"; std::string msg = err_msg.str(); x = Eigen::VectorXd(); EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, x_r, x_i, u_scale, f_scale, 0, @@ -543,9 +553,10 @@ TEST_F(FP_2d_func_test, exception_handling) { x << 0.1, 0.1; } + /* { std::stringstream err_msg; - err_msg << "algebra_solver: continuous data[1] is inf"; + err_msg << "algebra_solver_fp: continuous data[1] is inf"; std::string msg = err_msg.str(); x_r.push_back(std::numeric_limits::infinity()); EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, x_r, x_i, u_scale, f_scale, 0, @@ -553,10 +564,11 @@ TEST_F(FP_2d_func_test, exception_handling) { std::domain_error, msg); x_r.clear(); } + */ { std::stringstream err_msg; - err_msg << "algebra_solver: u_scale[1] is -1"; + err_msg << "algebra_solver_fp: u_scale[1] is -1"; std::string msg = err_msg.str(); u_scale[0] = -1.0; EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, x_r, x_i, u_scale, f_scale, 0, @@ -567,7 +579,7 @@ TEST_F(FP_2d_func_test, exception_handling) { { std::stringstream err_msg; - err_msg << "algebra_solver: f_scale[1] is -1"; + err_msg << "algebra_solver_fp: f_scale[1] is -1"; std::string msg = err_msg.str(); f_scale[0] = -1.0; EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, x_r, x_i, u_scale, f_scale, 0, @@ -578,7 +590,7 @@ TEST_F(FP_2d_func_test, exception_handling) { { std::stringstream err_msg; - err_msg << "algebra_solver: function_tolerance is -0.1"; + err_msg << "algebra_solver_fp: function_tolerance is -0.1"; std::string msg = err_msg.str(); f_tol = -0.1; EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, x_r, x_i, u_scale, f_scale, 0, From 2db3f10972b0bf8eea14ba70582d2754a243bae4 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Sun, 20 Jun 2021 13:42:11 -0700 Subject: [PATCH 40/88] Make calculation consistent with explanation and fix test typo. --- stan/math/rev/functor/algebra_solver_fp.hpp | 4 ++-- test/unit/math/rev/functor/algebra_solver_fp_test.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_fp.hpp b/stan/math/rev/functor/algebra_solver_fp.hpp index 59d588cbb85..bab83fcbf8a 100644 --- a/stan/math/rev/functor/algebra_solver_fp.hpp +++ b/stan/math/rev/functor/algebra_solver_fp.hpp @@ -218,7 +218,7 @@ Eigen::Matrix algebra_solver_fp_impl( jacobian(f_wrt_x, theta_dbl, f_x, Jf_x); int N = x.size(); - Jf_x -= Eigen::MatrixXd::Identity(x.size(), x.size()); + Jf_x = Eigen::MatrixXd::Identity(x.size(), x.size()) - Jf_x; using ret_type = Eigen::Matrix; auto arena_Jf_x = to_arena(Jf_x); @@ -233,7 +233,7 @@ Eigen::Matrix algebra_solver_fp_impl( // Contract specificities with inverse Jacobian of f with respect to x. VectorXd ret_adj = ret.adj(); - VectorXd eta = -arena_Jf_x.transpose().lu().solve(ret_adj); + VectorXd eta = arena_Jf_x.transpose().lu().solve(ret_adj); // Contract with Jacobian of f with respect to y using a nested reverse // autodiff pass. diff --git a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp index 8b55a9a5959..438c3e53bf7 100644 --- a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp @@ -601,7 +601,7 @@ TEST_F(FP_2d_func_test, exception_handling) { { std::stringstream err_msg; - err_msg << "algebra_solver: size of the algebraic system's output"; + err_msg << "algebra_solver_fp: size of the algebraic system's output"; std::string msg = err_msg.str(); x = Eigen::VectorXd::Zero(4); EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, x_r, x_i, u_scale, f_scale, 0, From 706eb0781d168d14d5019d1d16797a742d04f091 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Sun, 20 Jun 2021 15:06:39 -0700 Subject: [PATCH 41/88] Better templating. --- stan/math/rev/functor/algebra_solver_fp.hpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_fp.hpp b/stan/math/rev/functor/algebra_solver_fp.hpp index bab83fcbf8a..91f494e245f 100644 --- a/stan/math/rev/functor/algebra_solver_fp.hpp +++ b/stan/math/rev/functor/algebra_solver_fp.hpp @@ -309,14 +309,13 @@ Eigen::Matrix algebra_solver_fp_impl( * @throw boost::math::evaluation_error (which is a subclass of * std::runtime_error) if solver exceeds max_num_steps. */ -template +template * = nullptr> Eigen::Matrix algebra_solver_fp( - const F& f, const Eigen::Matrix& x, - const Eigen::Matrix& y, const std::vector& dat, + const F& f, const T1& x, const T2& y, const std::vector& dat, const std::vector& dat_int, const std::vector& u_scale, const std::vector& f_scale, std::ostream* msgs = nullptr, - double function_tolerance = 1e-8, - int max_num_steps = 200) { // NOLINT(runtime/int) + double function_tolerance = 1e-8, int max_num_steps = 200) { return algebra_solver_fp_impl(algebra_solver_adapter(f), x, function_tolerance, max_num_steps, u_scale, f_scale, msgs, y, dat, dat_int); From 9753b979762db9c6a585e935e5390547d8f464c8 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Sun, 20 Jun 2021 15:27:25 -0700 Subject: [PATCH 42/88] Get all tests compiling. --- stan/math/rev/functor/algebra_solver_fp.hpp | 8 +- .../rev/functor/algebra_solver_fp_test.cpp | 209 +++++++----------- 2 files changed, 81 insertions(+), 136 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_fp.hpp b/stan/math/rev/functor/algebra_solver_fp.hpp index 91f494e245f..e3df5081cf6 100644 --- a/stan/math/rev/functor/algebra_solver_fp.hpp +++ b/stan/math/rev/functor/algebra_solver_fp.hpp @@ -309,10 +309,10 @@ Eigen::Matrix algebra_solver_fp_impl( * @throw boost::math::evaluation_error (which is a subclass of * std::runtime_error) if solver exceeds max_num_steps. */ -template * = nullptr> -Eigen::Matrix algebra_solver_fp( - const F& f, const T1& x, const T2& y, const std::vector& dat, +template +Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_fp( + const F& f, const Eigen::Matrix& x, + const Eigen::Matrix& y, const std::vector& dat, const std::vector& dat_int, const std::vector& u_scale, const std::vector& f_scale, std::ostream* msgs = nullptr, double function_tolerance = 1e-8, int max_num_steps = 200) { diff --git a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp index 438c3e53bf7..14746327d97 100644 --- a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp @@ -12,8 +12,6 @@ using stan::math::algebra_solver_fp; using stan::math::finite_diff_gradient_auto; -// using stan::math::FixedPointADJac; -// using stan::math::FixedPointSolver; using stan::math::KinsolFixedPointEnv; using stan::math::to_array_1d; using stan::math::to_var; @@ -45,8 +43,8 @@ struct FP_exp_func_test : public ::testing::Test { FP_exp_func f; Eigen::VectorXd x; Eigen::VectorXd y; - std::vector x_r; - std::vector x_i; + std::vector dat; + std::vector dat_int; std::ostream* msgs; std::vector u_scale; std::vector f_scale; @@ -57,25 +55,21 @@ struct FP_exp_func_test : public ::testing::Test { : f(), x(stan::math::to_vector(std::vector{0.5})), y(stan::math::to_vector(std::vector{1.0})), - x_r(), - x_i(), + dat(), + dat_int(), msgs(nullptr), u_scale{1.0}, f_scale{1.0} {} - /* auto fd_functor(int i) { auto f_fd = [this, i](const Eigen::VectorXd& y_) { - KinsolFixedPointEnv env(f, x, y_, x_r, x_i, msgs, u_scale, - f_scale); - FixedPointSolver, FixedPointADJac> fp; - double f_tol = 1.e-12; + double function_tolerance = 1.e-12; int max_num_steps = 100; - return fp.solve(x, y_, env, f_tol, max_num_steps)(0); + return algebra_solver_fp(f, x, y_, dat, dat_int, u_scale, f_scale, msgs, + function_tolerance, max_num_steps)(0); }; return f_fd; } - */ }; /* @@ -105,8 +99,8 @@ struct FP_2d_func_test : public ::testing::Test { FP_2d_func f; Eigen::VectorXd x; Eigen::VectorXd y; - std::vector x_r; - std::vector x_i; + std::vector dat; + std::vector dat_int; std::ostream* msgs; std::vector u_scale; std::vector f_scale; @@ -117,25 +111,21 @@ struct FP_2d_func_test : public ::testing::Test { : f(), x(stan::math::to_vector(std::vector{0.1, 0.1})), y(stan::math::to_vector(std::vector{1.0, 1.0, 1.0})), - x_r(), - x_i(), + dat(), + dat_int(), msgs(nullptr), u_scale{1.0, 1.0}, f_scale{1.0, 1.0} {} - /* auto fd_functor(int i) { auto f_fd = [this, i](const Eigen::VectorXd& y_) { - KinsolFixedPointEnv env(f, x, y_, x_r, x_i, msgs, u_scale, - f_scale); - FixedPointSolver, FixedPointADJac> fp; - double f_tol = 1.e-12; + double function_tolerance = 1.e-12; int max_num_steps = 100; - return fp.solve(x, y_, env, f_tol, max_num_steps)(i); + return algebra_solver_fp(f, x, y_, dat, dat_int, u_scale, f_scale, msgs, + function_tolerance, max_num_steps)(i); }; return f_fd; } - */ }; /* @@ -169,8 +159,8 @@ struct FP_degenerated_func_test : public ::testing::Test { FP_degenerated_func f; Eigen::VectorXd x; Eigen::VectorXd y; - std::vector x_r; - std::vector x_i; + std::vector dat; + std::vector dat_int; std::ostream* msgs; std::vector u_scale; std::vector f_scale; @@ -181,28 +171,21 @@ struct FP_degenerated_func_test : public ::testing::Test { : f(), x(stan::math::to_vector(std::vector{5.0, 100.0})), y(stan::math::to_vector(std::vector{5.0, 100.0})), - x_r(), - x_i(), + dat(), + dat_int(), msgs(nullptr), u_scale{1.0, 1.0}, f_scale{1.0, 1.0} {} - /* auto fd_functor(int i) { auto f_fd = [this, i](const Eigen::VectorXd& y_) { - KinsolFixedPointEnv env(f, x, y_, x_r, x_i, msgs, - u_scale, // NOLINT - f_scale); - FixedPointSolver, - FixedPointADJac> - fp; // NOLINT - double f_tol = 1.e-12; + double function_tolerance = 1.e-12; int max_num_steps = 100; - return fp.solve(x, y_, env, f_tol, max_num_steps)(i); + return algebra_solver_fp(f, x, y_, dat, dat_int, u_scale, f_scale, msgs, + function_tolerance, max_num_steps)(i); }; return f_fd; } - */ }; /* @@ -266,8 +249,8 @@ struct FP_direct_prod_func_test : public ::testing::Test { const int n; Eigen::VectorXd x; Eigen::VectorXd y; - std::vector x_r; - std::vector x_i; + std::vector dat; + std::vector dat_int; std::ostream* msgs; std::vector u_scale; std::vector f_scale; @@ -280,8 +263,8 @@ struct FP_direct_prod_func_test : public ::testing::Test { n(400), x(stan::math::to_vector(std::vector(n, 0.2))), y(stan::math::to_vector(std::vector(n, 1.0))), - x_r(), - x_i(), + dat(), + dat_int(), msgs(nullptr), u_scale(n, 1.0), f_scale(n, 1.0) { @@ -290,34 +273,24 @@ struct FP_direct_prod_func_test : public ::testing::Test { } } - /* auto fd_functor(int i) { auto f_fd = [this, i](const Eigen::VectorXd& y_) { - KinsolFixedPointEnv env(f, x, y_, x_r, x_i, msgs, - u_scale, // NOLINT - f_scale); - FixedPointSolver, - FixedPointADJac> - fp; // NOLINT - double f_tol = 1.e-12; + double function_tolerance = 1.e-12; int max_num_steps = 100; - return fp.solve(x, y_, env, f_tol, max_num_steps)(i); + return algebra_solver_fp(f, x, y_, dat, dat_int, u_scale, f_scale, msgs, + function_tolerance, max_num_steps)(i); }; return f_fd; } - */ }; -/* TEST_F(FP_exp_func_test, solve) { - KinsolFixedPointEnv env(f, x, y, x_r, x_i, msgs, u_scale, - f_scale); - FixedPointSolver, FixedPointADJac> fp; - double f_tol = 1.e-12; + double function_tolerance = 1.e-12; int max_num_steps = 100; { Eigen::Matrix res - = fp.solve(x, y, env, f_tol, max_num_steps); // NOLINT + = algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, msgs, + function_tolerance, max_num_steps); EXPECT_FLOAT_EQ(res(0), 0.567143290409); } @@ -325,37 +298,32 @@ TEST_F(FP_exp_func_test, solve) { x(0) = 0.1; y(0) = 0.8; Eigen::Matrix res - = fp.solve(x, y, env, f_tol, max_num_steps); // NOLINT + = algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, msgs, + function_tolerance, max_num_steps); // NOLINT EXPECT_FLOAT_EQ(res(0), 0.612584823501); } } TEST_F(FP_2d_func_test, solve) { - KinsolFixedPointEnv env(f, x, y, x_r, x_i, msgs, u_scale, - f_scale); - FixedPointSolver, FixedPointADJac> fp; - double f_tol = 1.e-12; + double function_tolerance = 1.e-12; int max_num_steps = 100; Eigen::Matrix res - = fp.solve(x, y, env, f_tol, max_num_steps); // NOLINT + = algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, msgs, + function_tolerance, max_num_steps); EXPECT_FLOAT_EQ(res(0), 0.7861513777574); EXPECT_FLOAT_EQ(res(1), 0.6180339887499); } -*/ -/* TEST_F(FP_exp_func_test, gradient) { - KinsolFixedPointEnv env(f, x, y, x_r, x_i, msgs, u_scale, - f_scale); - FixedPointSolver, FixedPointADJac> fp; - double f_tol = 1.e-12; + double function_tolerance = 1.e-12; int max_num_steps = 100; Eigen::Matrix yp(to_var(y)); x(0) = 0.1; y(0) = 0.8; Eigen::Matrix x_sol - = fp.solve(x, yp, env, f_tol, max_num_steps); // NOLINT + = algebra_solver_fp(f, x, yp, dat, dat_int, u_scale, f_scale, msgs, + function_tolerance, max_num_steps); EXPECT_FLOAT_EQ(value_of(x_sol(0)), 0.612584823501); stan::math::set_zero_all_adjoints(); x_sol(0).grad(); @@ -365,78 +333,70 @@ TEST_F(FP_exp_func_test, gradient) { finite_diff_gradient_auto(f_fd, y, fx, grad_fx); EXPECT_FLOAT_EQ(grad_fx(0), yp(0).adj()); } -*/ -/* TEST_F(FP_2d_func_test, gradient) { - KinsolFixedPointEnv env(f, x, y, x_r, x_i, msgs, u_scale, - f_scale); - FixedPointSolver, FixedPointADJac> fp; - double f_tol = 1.e-12; + double function_tolerance = 1.e-12; int max_num_steps = 100; Eigen::Matrix yp(to_var(y)); Eigen::Matrix x_sol - = fp.solve(x, yp, env, f_tol, max_num_steps); // NOLINT + = value_of(algebra_solver_fp(f, x, yp, dat, dat_int, u_scale, f_scale, msgs, + function_tolerance, max_num_steps)); EXPECT_FLOAT_EQ(value_of(x_sol(0)), 0.7861513777574); EXPECT_FLOAT_EQ(value_of(x_sol(1)), 0.6180339887499); double fx; Eigen::VectorXd grad_fx; - for (int i = 0; i < env.N_; ++i) { + for (int i = 0; i < x.size(); ++i) { stan::math::set_zero_all_adjoints(); x_sol(i).grad(); auto f_fd = fd_functor(i); finite_diff_gradient_auto(f_fd, y, fx, grad_fx); - for (int j = 0; j < env.M_; ++j) { + for (int j = 0; j < y.size(); ++j) { EXPECT_FLOAT_EQ(grad_fx(j), yp(j).adj()); } } } -*/ -/* TEST_F(FP_2d_func_test, gradient_with_var_init_point) { - KinsolFixedPointEnv env(f, x, y, x_r, x_i, msgs, u_scale, - f_scale); - FixedPointSolver, FixedPointADJac> fp; - double f_tol = 1.e-12; + double function_tolerance = 1.e-12; int max_num_steps = 100; Eigen::Matrix yp(to_var(y)); Eigen::Matrix xp(to_var(x)); - Eigen::Matrix x_sol = fp.solve(xp, yp, env, f_tol, max_num_steps); + Eigen::Matrix x_sol = + algebra_solver_fp(f, xp, yp, dat, dat_int, u_scale, f_scale, msgs, + function_tolerance, max_num_steps); EXPECT_FLOAT_EQ(value_of(x_sol(0)), 0.7861513777574); EXPECT_FLOAT_EQ(value_of(x_sol(1)), 0.6180339887499); double fx; Eigen::VectorXd grad_fx; - for (int i = 0; i < env.N_; ++i) { + for (int i = 0; i < x.size(); ++i) { stan::math::set_zero_all_adjoints(); x_sol(i).grad(); auto f_fd = fd_functor(i); finite_diff_gradient_auto(f_fd, y, fx, grad_fx); - for (int j = 0; j < env.M_; ++j) { + for (int j = 0; j < y.size(); ++j) { EXPECT_FLOAT_EQ(grad_fx(j), yp(j).adj()); } } } -*/ -/* TEST_F(FP_2d_func_test, algebra_solver_fp) { - double f_tol = 1.e-12; + double function_tolerance = 1.e-12; int max_num_steps = 100; - Eigen::Matrix xd = algebra_solver_fp( - f, x, y, x_r, x_i, u_scale, f_scale, 0, f_tol, max_num_steps); // NOLINT + Eigen::Matrix xd = + algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, 0, + function_tolerance, max_num_steps); EXPECT_FLOAT_EQ(xd(0), 0.7861513777574); EXPECT_FLOAT_EQ(xd(1), 0.6180339887499); Eigen::Matrix yp(to_var(y)); Eigen::Matrix xv - = algebra_solver_fp(f, x, yp, x_r, x_i, u_scale, f_scale, 0, f_tol, - max_num_steps); // NOLINT + = algebra_solver_fp(f, x, yp, dat, dat_int, u_scale, f_scale, 0, + function_tolerance, max_num_steps); EXPECT_FLOAT_EQ(value_of(xv(0)), 0.7861513777574); EXPECT_FLOAT_EQ(value_of(xv(1)), 0.6180339887499); @@ -454,22 +414,21 @@ TEST_F(FP_2d_func_test, algebra_solver_fp) { } } } -*/ -/* TEST_F(FP_degenerated_func_test, algebra_solver_fp) { - double f_tol = 1.e-12; + double function_tolerance = 1.e-12; int max_num_steps = 100; - Eigen::Matrix xd = algebra_solver_fp( - f, x, y, x_r, x_i, u_scale, f_scale, 0, f_tol, max_num_steps); // NOLINT + Eigen::Matrix xd = + algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, 0, + function_tolerance, max_num_steps); EXPECT_FLOAT_EQ(xd(0), 5.0); EXPECT_FLOAT_EQ(xd(1), 5.0); Eigen::Matrix yp(to_var(y)); Eigen::Matrix xv - = algebra_solver_fp(f, x, yp, x_r, x_i, u_scale, f_scale, 0, f_tol, - max_num_steps); // NOLINT + = algebra_solver_fp(f, x, yp, dat, dat_int, u_scale, f_scale, 0, + function_tolerance, max_num_steps); EXPECT_FLOAT_EQ(value_of(xv(0)), 5.0); EXPECT_FLOAT_EQ(value_of(xv(1)), 5.0); @@ -487,7 +446,6 @@ TEST_F(FP_degenerated_func_test, algebra_solver_fp) { } } } -*/ TEST_F(FP_degenerated_func_test, scaling_vector_as_params) { double f_tol = 1.e-12; @@ -496,7 +454,7 @@ TEST_F(FP_degenerated_func_test, scaling_vector_as_params) { const std::vector u_scale_v(to_var(u_scale)); const std::vector f_scale_v(to_var(f_scale)); Eigen::Matrix xd = algebra_solver_fp( - f, x, y, x_r, x_i, u_scale, f_scale, 0, f_tol, max_num_steps); // NOLINT + f, x, y, dat, dat_int, u_scale, f_scale, 0, f_tol, max_num_steps); // NOLINT EXPECT_FLOAT_EQ(xd(0), 5.0); EXPECT_FLOAT_EQ(xd(1), 5.0); } @@ -507,9 +465,9 @@ TEST_F(FP_direct_prod_func_test, algebra_solver_fp) { Eigen::Matrix yp(to_var(y)); Eigen::Matrix xv_fp = algebra_solver_fp( - f, x, yp, x_r, x_i, u_scale, f_scale, 0, f_tol, max_num_steps); // NOLINT + f, x, yp, dat, dat_int, u_scale, f_scale, 0, f_tol, max_num_steps); Eigen::Matrix xv_newton = algebra_solver_newton( - f_newton, x, yp, x_r, x_i, 0, 1.e-3, f_tol, max_num_steps); // NOLINT + f_newton, x, yp, dat, dat_int, 0, 1.e-3, f_tol, max_num_steps); for (int i = 0; i < n; ++i) { EXPECT_FLOAT_EQ(value_of(xv_fp(i)), value_of(xv_newton(i))); } @@ -536,8 +494,8 @@ TEST_F(FP_2d_func_test, exception_handling) { err_msg << "algebra_solver: maximum number of iterations (4) was exceeded " "in the solve."; std::string msg = err_msg.str(); - EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, x_r, x_i, u_scale, f_scale, 0, - f_tol, max_num_steps), // NOLINT + EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, + 0, f_tol, max_num_steps), // NOLINT std::domain_error, msg); } @@ -546,33 +504,20 @@ TEST_F(FP_2d_func_test, exception_handling) { err_msg << "algebra_solver_fp: initial guess has size 0"; std::string msg = err_msg.str(); x = Eigen::VectorXd(); - EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, x_r, x_i, u_scale, f_scale, 0, - f_tol, max_num_steps), // NOLINT + EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, + 0, f_tol, max_num_steps), // NOLINT std::invalid_argument, msg); x = Eigen::VectorXd(2); x << 0.1, 0.1; } - /* - { - std::stringstream err_msg; - err_msg << "algebra_solver_fp: continuous data[1] is inf"; - std::string msg = err_msg.str(); - x_r.push_back(std::numeric_limits::infinity()); - EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, x_r, x_i, u_scale, f_scale, 0, - f_tol, max_num_steps), // NOLINT - std::domain_error, msg); - x_r.clear(); - } - */ - { std::stringstream err_msg; err_msg << "algebra_solver_fp: u_scale[1] is -1"; std::string msg = err_msg.str(); u_scale[0] = -1.0; - EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, x_r, x_i, u_scale, f_scale, 0, - f_tol, max_num_steps), // NOLINT + EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, + 0, f_tol, max_num_steps), // NOLINT std::domain_error, msg); u_scale[0] = 1.0; } @@ -582,8 +527,8 @@ TEST_F(FP_2d_func_test, exception_handling) { err_msg << "algebra_solver_fp: f_scale[1] is -1"; std::string msg = err_msg.str(); f_scale[0] = -1.0; - EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, x_r, x_i, u_scale, f_scale, 0, - f_tol, max_num_steps), // NOLINT + EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, + 0, f_tol, max_num_steps), // NOLINT std::domain_error, msg); f_scale[0] = 1.0; } @@ -593,8 +538,8 @@ TEST_F(FP_2d_func_test, exception_handling) { err_msg << "algebra_solver_fp: function_tolerance is -0.1"; std::string msg = err_msg.str(); f_tol = -0.1; - EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, x_r, x_i, u_scale, f_scale, 0, - f_tol, max_num_steps), // NOLINT + EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, + 0, f_tol, max_num_steps), // NOLINT std::domain_error, msg); f_tol = 1.e-8; } @@ -604,8 +549,8 @@ TEST_F(FP_2d_func_test, exception_handling) { err_msg << "algebra_solver_fp: size of the algebraic system's output"; std::string msg = err_msg.str(); x = Eigen::VectorXd::Zero(4); - EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, x_r, x_i, u_scale, f_scale, 0, - f_tol, max_num_steps), // NOLINT + EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, + 0, f_tol, max_num_steps), // NOLINT std::invalid_argument, msg); x = Eigen::VectorXd(2); x << 0.1, 0.1; From a73a4d3cebf4f938d1dccd0b218e7e2be7a35660 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Mon, 21 Jun 2021 16:29:49 -0400 Subject: [PATCH 43/88] All tests passing except one. --- test/unit/math/rev/functor/algebra_solver_fp_test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp index 14746327d97..d58377d5465 100644 --- a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp @@ -340,8 +340,8 @@ TEST_F(FP_2d_func_test, gradient) { Eigen::Matrix yp(to_var(y)); Eigen::Matrix x_sol - = value_of(algebra_solver_fp(f, x, yp, dat, dat_int, u_scale, f_scale, msgs, - function_tolerance, max_num_steps)); + = algebra_solver_fp(f, x, yp, dat, dat_int, u_scale, f_scale, msgs, + function_tolerance, max_num_steps); EXPECT_FLOAT_EQ(value_of(x_sol(0)), 0.7861513777574); EXPECT_FLOAT_EQ(value_of(x_sol(1)), 0.6180339887499); From 6664afe3effac6baf8358540227068c4f8e61805 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Mon, 21 Jun 2021 17:05:43 -0400 Subject: [PATCH 44/88] Fix issue in last test. --- test/unit/math/rev/functor/algebra_solver_fp_test.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp index d58377d5465..7ff0ffdc7bf 100644 --- a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp @@ -317,10 +317,11 @@ TEST_F(FP_2d_func_test, solve) { TEST_F(FP_exp_func_test, gradient) { double function_tolerance = 1.e-12; int max_num_steps = 100; - Eigen::Matrix yp(to_var(y)); x(0) = 0.1; y(0) = 0.8; + Eigen::Matrix yp(to_var(y)); + Eigen::Matrix x_sol = algebra_solver_fp(f, x, yp, dat, dat_int, u_scale, f_scale, msgs, function_tolerance, max_num_steps); From a1a9a70ada7abdc77f55892c588dae3faaebd051 Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Mon, 21 Jun 2021 21:08:50 +0000 Subject: [PATCH 45/88] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- stan/math/rev/functor/algebra_solver_fp.hpp | 56 ++++++++----------- .../rev/functor/algebra_solver_fp_test.cpp | 27 ++++----- 2 files changed, 37 insertions(+), 46 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_fp.hpp b/stan/math/rev/functor/algebra_solver_fp.hpp index e3df5081cf6..b0ae8644096 100644 --- a/stan/math/rev/functor/algebra_solver_fp.hpp +++ b/stan/math/rev/functor/algebra_solver_fp.hpp @@ -51,8 +51,8 @@ struct KinsolFixedPointEnv { KinsolFixedPointEnv(const F& f, const Eigen::MatrixXd x, const std::vector& u_scale, - const std::vector& f_scale, - std::ostream* msgs, const Args&... args) + const std::vector& f_scale, std::ostream* msgs, + const Args&... args) : f_(f), N_(x.size()), msgs_(msgs), @@ -77,15 +77,13 @@ struct KinsolFixedPointEnv { /** Implements the user-defined function passed to KINSOL. */ static int kinsol_f_system(N_Vector x, N_Vector f, void* user_data) { - auto g = - static_cast*>(user_data); + auto g = static_cast*>( + user_data); Eigen::VectorXd x_eigen(Eigen::Map(NV_DATA_S(x), g->N_)); Eigen::Map f_map(N_VGetArrayPointer(f), g->N_); f_map = apply( - [&](const auto&... args) { - return g->f_(x_eigen, g->msgs_, args...); - }, + [&](const auto&... args) { return g->f_(x_eigen, g->msgs_, args...); }, g->args_tuple_); return 0; } @@ -100,17 +98,13 @@ struct KinsolFixedPointEnv { * @param max_num_steps max nb. of iterations. */ template -Eigen::VectorXd kinsol_solve_fp( - const F& f, - const Eigen::VectorXd& x, - double function_tolerance, - double max_num_steps, - const std::vector& u_scale, - const std::vector& f_scale, - std::ostream* msgs, - const Args&... args) { - KinsolFixedPointEnv env( - f, x, u_scale, f_scale, msgs, args...); +Eigen::VectorXd kinsol_solve_fp(const F& f, const Eigen::VectorXd& x, + double function_tolerance, double max_num_steps, + const std::vector& u_scale, + const std::vector& f_scale, + std::ostream* msgs, const Args&... args) { + KinsolFixedPointEnv env(f, x, u_scale, f_scale, msgs, + args...); int N = env.N_; void* mem = env.mem_; const int default_anderson_depth = 4; @@ -120,8 +114,7 @@ Eigen::VectorXd kinsol_solve_fp( check_flag_sundials(KINSetNumMaxIters(mem, max_num_steps), "KINSetNumMaxIters"); check_flag_sundials(KINSetMAA(mem, anderson_depth), "KINSetMAA"); - check_flag_sundials(KINInit(mem, &env.kinsol_f_system, env.nv_x_), - "KINInit"); + check_flag_sundials(KINInit(mem, &env.kinsol_f_system, env.nv_x_), "KINInit"); check_flag_sundials(KINSetFuncNormTol(mem, function_tolerance), "KINSetFuncNormTol"); check_flag_sundials(KINSetUserData(mem, static_cast(&env)), @@ -138,8 +131,7 @@ Eigen::VectorXd kinsol_solve_fp( } /** Implementation of ordinary fixed point solver. */ -template * = nullptr, require_all_st_arithmetic* = nullptr> Eigen::VectorXd algebra_solver_fp_impl( @@ -188,8 +180,7 @@ Eigen::VectorXd algebra_solver_fp_impl( * (This is virtually identical to the Powell and Newton solvers, except Jfx * has been replaced by I - Jfx.) */ -template * = nullptr, require_any_st_var* = nullptr> Eigen::Matrix algebra_solver_fp_impl( @@ -207,11 +198,11 @@ Eigen::Matrix algebra_solver_fp_impl( // FP solution Eigen::VectorXd theta_dbl = apply( - [&](const auto&... vals) { - return kinsol_solve_fp(f, x_val, function_tolerance, max_num_steps, - u_scale, f_scale, msgs, vals...); - }, - args_vals_tuple); + [&](const auto&... vals) { + return kinsol_solve_fp(f, x_val, function_tolerance, max_num_steps, + u_scale, f_scale, msgs, vals...); + }, + args_vals_tuple); Eigen::MatrixXd Jf_x; Eigen::VectorXd f_x; @@ -252,7 +243,6 @@ Eigen::Matrix algebra_solver_fp_impl( return ret_type(ret); } - /** * Return a fixed pointer to the specified system of algebraic * equations of form @@ -316,9 +306,9 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_fp( const std::vector& dat_int, const std::vector& u_scale, const std::vector& f_scale, std::ostream* msgs = nullptr, double function_tolerance = 1e-8, int max_num_steps = 200) { - return algebra_solver_fp_impl(algebra_solver_adapter(f), x, function_tolerance, - max_num_steps, u_scale, f_scale, msgs, y, dat, - dat_int); + return algebra_solver_fp_impl(algebra_solver_adapter(f), x, + function_tolerance, max_num_steps, u_scale, + f_scale, msgs, y, dat, dat_int); } } // namespace math diff --git a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp index 7ff0ffdc7bf..5d657d84a34 100644 --- a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp @@ -86,7 +86,7 @@ struct FP_2d_func_test : public ::testing::Test { struct FP_2d_func { template inline Eigen::Matrix, -1, 1> operator()( - const T1& x, const T2&y, const T3& x_r, const T4& x_i, + const T1& x, const T2& y, const T3& x_r, const T4& x_i, std::ostream* pstream__) const { using scalar = stan::return_type_t; Eigen::Matrix z(2); @@ -202,7 +202,7 @@ struct FP_direct_prod_func_test : public ::testing::Test { struct FP_direct_prod_func { template inline Eigen::Matrix, Eigen::Dynamic, 1> - operator()(const T1&x, const T2& y, const T3& dat, const T4& dat_int, + operator()(const T1& x, const T2& y, const T3& dat, const T4& dat_int, std::ostream* pstream__) const { using scalar = stan::return_type_t; const size_t n = x.size(); @@ -365,9 +365,9 @@ TEST_F(FP_2d_func_test, gradient_with_var_init_point) { Eigen::Matrix yp(to_var(y)); Eigen::Matrix xp(to_var(x)); - Eigen::Matrix x_sol = - algebra_solver_fp(f, xp, yp, dat, dat_int, u_scale, f_scale, msgs, - function_tolerance, max_num_steps); + Eigen::Matrix x_sol + = algebra_solver_fp(f, xp, yp, dat, dat_int, u_scale, f_scale, msgs, + function_tolerance, max_num_steps); EXPECT_FLOAT_EQ(value_of(x_sol(0)), 0.7861513777574); EXPECT_FLOAT_EQ(value_of(x_sol(1)), 0.6180339887499); @@ -388,9 +388,9 @@ TEST_F(FP_2d_func_test, algebra_solver_fp) { double function_tolerance = 1.e-12; int max_num_steps = 100; - Eigen::Matrix xd = - algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, 0, - function_tolerance, max_num_steps); + Eigen::Matrix xd + = algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, 0, + function_tolerance, max_num_steps); EXPECT_FLOAT_EQ(xd(0), 0.7861513777574); EXPECT_FLOAT_EQ(xd(1), 0.6180339887499); @@ -420,9 +420,9 @@ TEST_F(FP_degenerated_func_test, algebra_solver_fp) { double function_tolerance = 1.e-12; int max_num_steps = 100; - Eigen::Matrix xd = - algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, 0, - function_tolerance, max_num_steps); + Eigen::Matrix xd + = algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, 0, + function_tolerance, max_num_steps); EXPECT_FLOAT_EQ(xd(0), 5.0); EXPECT_FLOAT_EQ(xd(1), 5.0); @@ -454,8 +454,9 @@ TEST_F(FP_degenerated_func_test, scaling_vector_as_params) { const std::vector u_scale_v(to_var(u_scale)); const std::vector f_scale_v(to_var(f_scale)); - Eigen::Matrix xd = algebra_solver_fp( - f, x, y, dat, dat_int, u_scale, f_scale, 0, f_tol, max_num_steps); // NOLINT + Eigen::Matrix xd + = algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, 0, f_tol, + max_num_steps); // NOLINT EXPECT_FLOAT_EQ(xd(0), 5.0); EXPECT_FLOAT_EQ(xd(1), 5.0); } From 12f2949301c1ccbd396321e278a7ba5ff3ff0853 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Mon, 21 Jun 2021 17:12:49 -0400 Subject: [PATCH 46/88] Remove unnecessary NOLINTs --- .../math/rev/functor/algebra_solver_fp_test.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp index 5d657d84a34..4d5eb6a36f7 100644 --- a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp @@ -299,7 +299,7 @@ TEST_F(FP_exp_func_test, solve) { y(0) = 0.8; Eigen::Matrix res = algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, msgs, - function_tolerance, max_num_steps); // NOLINT + function_tolerance, max_num_steps); EXPECT_FLOAT_EQ(res(0), 0.612584823501); } } @@ -456,7 +456,7 @@ TEST_F(FP_degenerated_func_test, scaling_vector_as_params) { const std::vector f_scale_v(to_var(f_scale)); Eigen::Matrix xd = algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, 0, f_tol, - max_num_steps); // NOLINT + max_num_steps); EXPECT_FLOAT_EQ(xd(0), 5.0); EXPECT_FLOAT_EQ(xd(1), 5.0); } @@ -497,7 +497,7 @@ TEST_F(FP_2d_func_test, exception_handling) { "in the solve."; std::string msg = err_msg.str(); EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, - 0, f_tol, max_num_steps), // NOLINT + 0, f_tol, max_num_steps), std::domain_error, msg); } @@ -507,7 +507,7 @@ TEST_F(FP_2d_func_test, exception_handling) { std::string msg = err_msg.str(); x = Eigen::VectorXd(); EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, - 0, f_tol, max_num_steps), // NOLINT + 0, f_tol, max_num_steps), std::invalid_argument, msg); x = Eigen::VectorXd(2); x << 0.1, 0.1; @@ -519,7 +519,7 @@ TEST_F(FP_2d_func_test, exception_handling) { std::string msg = err_msg.str(); u_scale[0] = -1.0; EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, - 0, f_tol, max_num_steps), // NOLINT + 0, f_tol, max_num_steps), std::domain_error, msg); u_scale[0] = 1.0; } @@ -530,7 +530,7 @@ TEST_F(FP_2d_func_test, exception_handling) { std::string msg = err_msg.str(); f_scale[0] = -1.0; EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, - 0, f_tol, max_num_steps), // NOLINT + 0, f_tol, max_num_steps), std::domain_error, msg); f_scale[0] = 1.0; } @@ -541,7 +541,7 @@ TEST_F(FP_2d_func_test, exception_handling) { std::string msg = err_msg.str(); f_tol = -0.1; EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, - 0, f_tol, max_num_steps), // NOLINT + 0, f_tol, max_num_steps), std::domain_error, msg); f_tol = 1.e-8; } @@ -552,7 +552,7 @@ TEST_F(FP_2d_func_test, exception_handling) { std::string msg = err_msg.str(); x = Eigen::VectorXd::Zero(4); EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, - 0, f_tol, max_num_steps), // NOLINT + 0, f_tol, max_num_steps), std::invalid_argument, msg); x = Eigen::VectorXd(2); x << 0.1, 0.1; From 177f695fcea7f1ace156d8614a5b92d91154b087 Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Mon, 21 Jun 2021 21:13:33 +0000 Subject: [PATCH 47/88] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- test/unit/math/rev/functor/algebra_solver_fp_test.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp index 4d5eb6a36f7..9271ebc9889 100644 --- a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp @@ -454,9 +454,8 @@ TEST_F(FP_degenerated_func_test, scaling_vector_as_params) { const std::vector u_scale_v(to_var(u_scale)); const std::vector f_scale_v(to_var(f_scale)); - Eigen::Matrix xd - = algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, 0, f_tol, - max_num_steps); + Eigen::Matrix xd = algebra_solver_fp( + f, x, y, dat, dat_int, u_scale, f_scale, 0, f_tol, max_num_steps); EXPECT_FLOAT_EQ(xd(0), 5.0); EXPECT_FLOAT_EQ(xd(1), 5.0); } From 4f7b442299e0c3b8722ca164cdd0a6a63699ce19 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Mon, 21 Jun 2021 20:22:58 -0400 Subject: [PATCH 48/88] Properly template expression function signature. --- test/expressions/expression_test_helpers.hpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/test/expressions/expression_test_helpers.hpp b/test/expressions/expression_test_helpers.hpp index 7528f2ef833..4e12393280d 100644 --- a/test/expressions/expression_test_helpers.hpp +++ b/test/expressions/expression_test_helpers.hpp @@ -195,13 +195,12 @@ struct test_functor { }; struct simple_eq_functor { - template - inline Eigen::Matrix, Eigen::Dynamic, 1> - operator()(const Eigen::Matrix& x, - const Eigen::Matrix& y, - const std::vector& dat, const std::vector& dat_int, - std::ostream* pstream__) const { - Eigen::Matrix, Eigen::Dynamic, 1> z(1); + template * = nullptr, + require_all_eigen_vector_t* = nullptr> + inline Eigen::Matrix, Eigen::Dynamic, 1> + operator()(const T1& x, const T2& y, const std::vector& dat, + const std::vector& dat_int, std::ostream* pstream__) const { + Eigen::Matrix, Eigen::Dynamic, 1> z(1); z(0) = x(0) - y(0); return z; } From 8afb387a444d4edd03cfa5150f98041c45be0167 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Tue, 22 Jun 2021 00:33:06 -0400 Subject: [PATCH 49/88] Fully template expression so it can be applied with arena types. --- stan/math/rev/functor/algebra_solver_powell.hpp | 7 ++++--- test/expressions/expression_test_helpers.hpp | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 95c6e3ed8f8..c9adad4d980 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -134,11 +134,12 @@ Eigen::Matrix algebra_solver_powell_impl( jacobian(f_wrt_x, theta_dbl, f_x, Jf_x); using ret_type = Eigen::Matrix; - auto arena_Jf_x = to_arena(Jf_x); + using lu_type = Eigen::PartialPivLU>; + plain_type_t* Jf_x_lu_ptr = make_chainable_ptr((lu_type) Jf_x.transpose().lu()); arena_t ret = theta_dbl; - reverse_pass_callback([f, ret, arena_args_tuple, arena_Jf_x, msgs]() mutable { + reverse_pass_callback([f, ret, arena_args_tuple, Jf_x_lu_ptr, msgs]() mutable { using Eigen::Dynamic; using Eigen::Matrix; using Eigen::MatrixXd; @@ -146,7 +147,7 @@ Eigen::Matrix algebra_solver_powell_impl( // Contract specificities with inverse Jacobian of f with respect to x. VectorXd ret_adj = ret.adj(); - VectorXd eta = -arena_Jf_x.transpose().lu().solve(ret_adj); + VectorXd eta = -Jf_x_lu_ptr->solve(ret_adj); // Contract with Jacobian of f with respect to y using a nested reverse // autodiff pass. diff --git a/test/expressions/expression_test_helpers.hpp b/test/expressions/expression_test_helpers.hpp index 4e12393280d..0c6dc72ad83 100644 --- a/test/expressions/expression_test_helpers.hpp +++ b/test/expressions/expression_test_helpers.hpp @@ -195,11 +195,12 @@ struct test_functor { }; struct simple_eq_functor { - template * = nullptr, + template * = nullptr, require_all_eigen_vector_t* = nullptr> inline Eigen::Matrix, Eigen::Dynamic, 1> - operator()(const T1& x, const T2& y, const std::vector& dat, - const std::vector& dat_int, std::ostream* pstream__) const { + operator()(const T1& x, const T2& y, const T3& dat, const T4& dat_int, + std::ostream* pstream__) const { Eigen::Matrix, Eigen::Dynamic, 1> z(1); z(0) = x(0) - y(0); return z; From fbe6a8d80c1f187066179e981d474e02b4680bef Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Tue, 22 Jun 2021 04:34:11 +0000 Subject: [PATCH 50/88] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- stan/math/rev/functor/algebra_solver_powell.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index c9adad4d980..a3a3769c1f6 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -135,11 +135,13 @@ Eigen::Matrix algebra_solver_powell_impl( using ret_type = Eigen::Matrix; using lu_type = Eigen::PartialPivLU>; - plain_type_t* Jf_x_lu_ptr = make_chainable_ptr((lu_type) Jf_x.transpose().lu()); + plain_type_t* Jf_x_lu_ptr + = make_chainable_ptr((lu_type)Jf_x.transpose().lu()); arena_t ret = theta_dbl; - reverse_pass_callback([f, ret, arena_args_tuple, Jf_x_lu_ptr, msgs]() mutable { + reverse_pass_callback([f, ret, arena_args_tuple, Jf_x_lu_ptr, + msgs]() mutable { using Eigen::Dynamic; using Eigen::Matrix; using Eigen::MatrixXd; From 901b603f57b4219f6d3db0525008fa4747c76be4 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Tue, 22 Jun 2021 00:42:27 -0400 Subject: [PATCH 51/88] Revert "Fully template expression so it can be applied with arena types." This reverts commit 8afb387a444d4edd03cfa5150f98041c45be0167. (I accidentally included a WIP change to the Powell solver.) --- test/expressions/expression_test_helpers.hpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/expressions/expression_test_helpers.hpp b/test/expressions/expression_test_helpers.hpp index 0c6dc72ad83..4e12393280d 100644 --- a/test/expressions/expression_test_helpers.hpp +++ b/test/expressions/expression_test_helpers.hpp @@ -195,12 +195,11 @@ struct test_functor { }; struct simple_eq_functor { - template * = nullptr, + template * = nullptr, require_all_eigen_vector_t* = nullptr> inline Eigen::Matrix, Eigen::Dynamic, 1> - operator()(const T1& x, const T2& y, const T3& dat, const T4& dat_int, - std::ostream* pstream__) const { + operator()(const T1& x, const T2& y, const std::vector& dat, + const std::vector& dat_int, std::ostream* pstream__) const { Eigen::Matrix, Eigen::Dynamic, 1> z(1); z(0) = x(0) - y(0); return z; From bdb74b31538467207f4a16622fb990573d75e372 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Tue, 22 Jun 2021 00:44:09 -0400 Subject: [PATCH 52/88] Fully template the simple equation functor signature. --- test/expressions/expression_test_helpers.hpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/expressions/expression_test_helpers.hpp b/test/expressions/expression_test_helpers.hpp index 4e12393280d..0c6dc72ad83 100644 --- a/test/expressions/expression_test_helpers.hpp +++ b/test/expressions/expression_test_helpers.hpp @@ -195,11 +195,12 @@ struct test_functor { }; struct simple_eq_functor { - template * = nullptr, + template * = nullptr, require_all_eigen_vector_t* = nullptr> inline Eigen::Matrix, Eigen::Dynamic, 1> - operator()(const T1& x, const T2& y, const std::vector& dat, - const std::vector& dat_int, std::ostream* pstream__) const { + operator()(const T1& x, const T2& y, const T3& dat, const T4& dat_int, + std::ostream* pstream__) const { Eigen::Matrix, Eigen::Dynamic, 1> z(1); z(0) = x(0) - y(0); return z; From 4d472f86e012bfd3b8a031e949d7d4d196a5f77b Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Tue, 22 Jun 2021 00:52:58 -0400 Subject: [PATCH 53/88] (Actually) revert the mistaken change introduced before. --- stan/math/rev/functor/algebra_solver_powell.hpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index a3a3769c1f6..95c6e3ed8f8 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -134,14 +134,11 @@ Eigen::Matrix algebra_solver_powell_impl( jacobian(f_wrt_x, theta_dbl, f_x, Jf_x); using ret_type = Eigen::Matrix; - using lu_type = Eigen::PartialPivLU>; - plain_type_t* Jf_x_lu_ptr - = make_chainable_ptr((lu_type)Jf_x.transpose().lu()); + auto arena_Jf_x = to_arena(Jf_x); arena_t ret = theta_dbl; - reverse_pass_callback([f, ret, arena_args_tuple, Jf_x_lu_ptr, - msgs]() mutable { + reverse_pass_callback([f, ret, arena_args_tuple, arena_Jf_x, msgs]() mutable { using Eigen::Dynamic; using Eigen::Matrix; using Eigen::MatrixXd; @@ -149,7 +146,7 @@ Eigen::Matrix algebra_solver_powell_impl( // Contract specificities with inverse Jacobian of f with respect to x. VectorXd ret_adj = ret.adj(); - VectorXd eta = -Jf_x_lu_ptr->solve(ret_adj); + VectorXd eta = -arena_Jf_x.transpose().lu().solve(ret_adj); // Contract with Jacobian of f with respect to y using a nested reverse // autodiff pass. From 70f02f80565814d8d7b3da58ea94bff9a0125cfa Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Tue, 22 Jun 2021 16:22:43 -0400 Subject: [PATCH 54/88] Try to prevent double eval. --- stan/math/rev/functor/algebra_solver_powell.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 95c6e3ed8f8..71cf8453b42 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -155,7 +155,7 @@ Eigen::Matrix algebra_solver_powell_impl( VectorXd ret_val = ret.val(); auto x_nrad_ = apply( - [&](const auto&... args) { return eval(f(ret_val, msgs, args...)); }, + [&](const auto&... args) { return f(ret_val, msgs, args...); }, arena_args_tuple); x_nrad_.adj() = eta; grad(); From 6a58089bace00ca71662dfc13127336d59c59c7e Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 25 Jun 2021 16:47:53 -0700 Subject: [PATCH 55/88] Switched to make_chainable_ptr in powell solver (Issue #2401) --- .../rev/functor/algebra_solver_powell.hpp | 8 +++--- .../math/rev/functor/algebra_solver_test.cpp | 25 +++++++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 71cf8453b42..1109bea3769 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -134,11 +134,11 @@ Eigen::Matrix algebra_solver_powell_impl( jacobian(f_wrt_x, theta_dbl, f_x, Jf_x); using ret_type = Eigen::Matrix; - auto arena_Jf_x = to_arena(Jf_x); - + auto Jf_xT_lu_ptr = make_chainable_ptr(Jf_x.transpose().fullPivHouseholderQr()); // Lu + arena_t ret = theta_dbl; - reverse_pass_callback([f, ret, arena_args_tuple, arena_Jf_x, msgs]() mutable { + reverse_pass_callback([f, ret, arena_args_tuple, Jf_xT_lu_ptr, msgs]() mutable { using Eigen::Dynamic; using Eigen::Matrix; using Eigen::MatrixXd; @@ -146,7 +146,7 @@ Eigen::Matrix algebra_solver_powell_impl( // Contract specificities with inverse Jacobian of f with respect to x. VectorXd ret_adj = ret.adj(); - VectorXd eta = -arena_Jf_x.transpose().lu().solve(ret_adj); + VectorXd eta = -Jf_xT_lu_ptr->solve(ret_adj); // Contract with Jacobian of f with respect to y using a nested reverse // autodiff pass. diff --git a/test/unit/math/rev/functor/algebra_solver_test.cpp b/test/unit/math/rev/functor/algebra_solver_test.cpp index e66b2c340b6..02cf0846f98 100644 --- a/test/unit/math/rev/functor/algebra_solver_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_test.cpp @@ -30,6 +30,10 @@ class algebra_solver_simple_eq_test : public ::testing::Test { x_var = stan::math::to_vector({1, 1}); } + void TearDown() override { + stan::math::recover_memory(); + } + int n_x; int n_y; Eigen::VectorXd y_dbl; @@ -48,6 +52,10 @@ class algebra_solver_simple_eq_nopara_test : public ::testing::Test { protected: void SetUp() override { x = stan::math::to_vector({1, 1}); } + void TearDown() override { + stan::math::recover_memory(); + } + int n_x = 2; Eigen::VectorXd x; std::vector dat = {5, 4, 2}; @@ -63,6 +71,11 @@ class algebra_solver_non_linear_eq_test : public ::testing::Test { J_ << -1, 0, 0, 0, -1, 0, 0, 0, 1; J = J_; } + + void TearDown() override { + stan::math::recover_memory(); + } + int n_x = 3; int n_y = 3; double err = 1e-11; @@ -78,6 +91,10 @@ class error_message_test : public ::testing::Test { y_3 = stan::math::to_vector({4, 6, 3}); } + void TearDown() override { + stan::math::recover_memory(); + } + Eigen::VectorXd y_2; Eigen::VectorXd y_3; }; @@ -89,6 +106,10 @@ class max_steps_test : public ::testing::Test { y_var = stan::math::to_vector({1, 1, 1}); } + void TearDown() override { + stan::math::recover_memory(); + } + Eigen::VectorXd y; Eigen::Matrix y_var; }; @@ -110,6 +131,10 @@ class degenerate_eq_test : public ::testing::Test { J2 = J_; } + void TearDown() override { + stan::math::recover_memory(); + } + int n_x = 2; int n_y = 2; double tolerance = 1e-10; From dec5dc4e55ae2c6f86795e3e6a070022bc163959 Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 25 Jun 2021 17:25:56 -0700 Subject: [PATCH 56/88] Don't double evaluate expressions (Issue #2401) --- stan/math/rev/functor/algebra_solver_fp.hpp | 4 +++- stan/math/rev/functor/algebra_solver_newton.hpp | 4 +++- stan/math/rev/functor/algebra_solver_powell.hpp | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_fp.hpp b/stan/math/rev/functor/algebra_solver_fp.hpp index b0ae8644096..5d00bea01dd 100644 --- a/stan/math/rev/functor/algebra_solver_fp.hpp +++ b/stan/math/rev/functor/algebra_solver_fp.hpp @@ -189,7 +189,9 @@ Eigen::Matrix algebra_solver_fp_impl( const std::vector& f_scale, std::ostream* msgs, const Args&... args) { const auto& x_val = to_ref(value_of(x)); auto arena_args_tuple = std::make_tuple(to_arena(args)...); - auto args_vals_tuple = std::make_tuple(eval(value_of(args))...); + auto args_vals_tuple = apply([&](const auto&... args) { + return std::make_tuple(eval(value_of(args))...); + }, arena_args_tuple); auto f_wrt_x = [&](const auto& x) { return apply([&](const auto&... args) { return f(x, msgs, args...); }, diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index 05a44414070..5eb22b125e2 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -48,7 +48,9 @@ Eigen::Matrix algebra_solver_newton_impl( const T_Args&... args) { // NOLINT(runtime/int) const auto& x_ref = to_ref(value_of(x)); auto arena_args_tuple = std::make_tuple(to_arena(args)...); - auto args_vals_tuple = std::make_tuple(to_ref(value_of(args))...); + auto args_vals_tuple = apply([&](const auto&... args) { + return std::make_tuple(eval(value_of(args))...); + }, arena_args_tuple); check_nonzero_size("algebra_solver_newton", "initial guess", x_ref); check_finite("algebra_solver_newton", "initial guess", x_ref); diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 1109bea3769..ef16eed0667 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -100,7 +100,9 @@ Eigen::Matrix algebra_solver_powell_impl( const T_Args&... args) { // NOLINT(runtime/int) const auto& x_val = to_ref(value_of(x)); auto arena_args_tuple = std::make_tuple(to_arena(args)...); - auto args_vals_tuple = std::make_tuple(eval(value_of(args))...); + auto args_vals_tuple = apply([&](const auto&... args) { + return std::make_tuple(eval(value_of(args))...); + }, arena_args_tuple); check_nonzero_size("algebra_solver_powell", "initial guess", x_val); check_finite("algebra_solver_powell", "initial guess", x_val); From e7317f9a54760dcead50f1cfa7720ede55c7be7b Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Sat, 26 Jun 2021 03:38:35 +0000 Subject: [PATCH 57/88] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- stan/math/rev/functor/algebra_solver_fp.hpp | 8 +-- .../rev/functor/algebra_solver_newton.hpp | 8 +-- .../rev/functor/algebra_solver_powell.hpp | 54 ++++++++++--------- .../math/rev/functor/algebra_solver_test.cpp | 24 +++------ 4 files changed, 45 insertions(+), 49 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_fp.hpp b/stan/math/rev/functor/algebra_solver_fp.hpp index 5d00bea01dd..60b096ed49d 100644 --- a/stan/math/rev/functor/algebra_solver_fp.hpp +++ b/stan/math/rev/functor/algebra_solver_fp.hpp @@ -189,9 +189,11 @@ Eigen::Matrix algebra_solver_fp_impl( const std::vector& f_scale, std::ostream* msgs, const Args&... args) { const auto& x_val = to_ref(value_of(x)); auto arena_args_tuple = std::make_tuple(to_arena(args)...); - auto args_vals_tuple = apply([&](const auto&... args) { - return std::make_tuple(eval(value_of(args))...); - }, arena_args_tuple); + auto args_vals_tuple = apply( + [&](const auto&... args) { + return std::make_tuple(eval(value_of(args))...); + }, + arena_args_tuple); auto f_wrt_x = [&](const auto& x) { return apply([&](const auto&... args) { return f(x, msgs, args...); }, diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index 5eb22b125e2..a2e956f036d 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -48,9 +48,11 @@ Eigen::Matrix algebra_solver_newton_impl( const T_Args&... args) { // NOLINT(runtime/int) const auto& x_ref = to_ref(value_of(x)); auto arena_args_tuple = std::make_tuple(to_arena(args)...); - auto args_vals_tuple = apply([&](const auto&... args) { - return std::make_tuple(eval(value_of(args))...); - }, arena_args_tuple); + auto args_vals_tuple = apply( + [&](const auto&... args) { + return std::make_tuple(eval(value_of(args))...); + }, + arena_args_tuple); check_nonzero_size("algebra_solver_newton", "initial guess", x_ref); check_finite("algebra_solver_newton", "initial guess", x_ref); diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index ef16eed0667..774ed90a43e 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -100,9 +100,11 @@ Eigen::Matrix algebra_solver_powell_impl( const T_Args&... args) { // NOLINT(runtime/int) const auto& x_val = to_ref(value_of(x)); auto arena_args_tuple = std::make_tuple(to_arena(args)...); - auto args_vals_tuple = apply([&](const auto&... args) { - return std::make_tuple(eval(value_of(args))...); - }, arena_args_tuple); + auto args_vals_tuple = apply( + [&](const auto&... args) { + return std::make_tuple(eval(value_of(args))...); + }, + arena_args_tuple); check_nonzero_size("algebra_solver_powell", "initial guess", x_val); check_finite("algebra_solver_powell", "initial guess", x_val); @@ -136,33 +138,35 @@ Eigen::Matrix algebra_solver_powell_impl( jacobian(f_wrt_x, theta_dbl, f_x, Jf_x); using ret_type = Eigen::Matrix; - auto Jf_xT_lu_ptr = make_chainable_ptr(Jf_x.transpose().fullPivHouseholderQr()); // Lu - + auto Jf_xT_lu_ptr + = make_chainable_ptr(Jf_x.transpose().fullPivHouseholderQr()); // Lu + arena_t ret = theta_dbl; - reverse_pass_callback([f, ret, arena_args_tuple, Jf_xT_lu_ptr, msgs]() mutable { - using Eigen::Dynamic; - using Eigen::Matrix; - using Eigen::MatrixXd; - using Eigen::VectorXd; + reverse_pass_callback( + [f, ret, arena_args_tuple, Jf_xT_lu_ptr, msgs]() mutable { + using Eigen::Dynamic; + using Eigen::Matrix; + using Eigen::MatrixXd; + using Eigen::VectorXd; - // Contract specificities with inverse Jacobian of f with respect to x. - VectorXd ret_adj = ret.adj(); - VectorXd eta = -Jf_xT_lu_ptr->solve(ret_adj); + // Contract specificities with inverse Jacobian of f with respect to x. + VectorXd ret_adj = ret.adj(); + VectorXd eta = -Jf_xT_lu_ptr->solve(ret_adj); - // Contract with Jacobian of f with respect to y using a nested reverse - // autodiff pass. - { - nested_rev_autodiff rev; + // Contract with Jacobian of f with respect to y using a nested reverse + // autodiff pass. + { + nested_rev_autodiff rev; - VectorXd ret_val = ret.val(); - auto x_nrad_ = apply( - [&](const auto&... args) { return f(ret_val, msgs, args...); }, - arena_args_tuple); - x_nrad_.adj() = eta; - grad(); - } - }); + VectorXd ret_val = ret.val(); + auto x_nrad_ = apply( + [&](const auto&... args) { return f(ret_val, msgs, args...); }, + arena_args_tuple); + x_nrad_.adj() = eta; + grad(); + } + }); return ret_type(ret); } diff --git a/test/unit/math/rev/functor/algebra_solver_test.cpp b/test/unit/math/rev/functor/algebra_solver_test.cpp index 02cf0846f98..40add575bd1 100644 --- a/test/unit/math/rev/functor/algebra_solver_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_test.cpp @@ -30,9 +30,7 @@ class algebra_solver_simple_eq_test : public ::testing::Test { x_var = stan::math::to_vector({1, 1}); } - void TearDown() override { - stan::math::recover_memory(); - } + void TearDown() override { stan::math::recover_memory(); } int n_x; int n_y; @@ -52,9 +50,7 @@ class algebra_solver_simple_eq_nopara_test : public ::testing::Test { protected: void SetUp() override { x = stan::math::to_vector({1, 1}); } - void TearDown() override { - stan::math::recover_memory(); - } + void TearDown() override { stan::math::recover_memory(); } int n_x = 2; Eigen::VectorXd x; @@ -72,9 +68,7 @@ class algebra_solver_non_linear_eq_test : public ::testing::Test { J = J_; } - void TearDown() override { - stan::math::recover_memory(); - } + void TearDown() override { stan::math::recover_memory(); } int n_x = 3; int n_y = 3; @@ -91,9 +85,7 @@ class error_message_test : public ::testing::Test { y_3 = stan::math::to_vector({4, 6, 3}); } - void TearDown() override { - stan::math::recover_memory(); - } + void TearDown() override { stan::math::recover_memory(); } Eigen::VectorXd y_2; Eigen::VectorXd y_3; @@ -106,9 +98,7 @@ class max_steps_test : public ::testing::Test { y_var = stan::math::to_vector({1, 1, 1}); } - void TearDown() override { - stan::math::recover_memory(); - } + void TearDown() override { stan::math::recover_memory(); } Eigen::VectorXd y; Eigen::Matrix y_var; @@ -131,9 +121,7 @@ class degenerate_eq_test : public ::testing::Test { J2 = J_; } - void TearDown() override { - stan::math::recover_memory(); - } + void TearDown() override { stan::math::recover_memory(); } int n_x = 2; int n_y = 2; From d3d5fbb68ac688d810626e66fae1f932bfb5595c Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Tue, 29 Jun 2021 15:01:02 -0400 Subject: [PATCH 58/88] Fix small signature issues. --- stan/math/rev/functor/algebra_solver_newton.hpp | 5 ++--- stan/math/rev/functor/algebra_solver_powell.hpp | 7 ++++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index 05a44414070..4c0ce5183bc 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -21,10 +21,9 @@ template * = nullptr> Eigen::VectorXd algebra_solver_newton_impl( const F& f, const T& x, std::ostream* msgs, double scaling_step_size, - double function_tolerance, int64_t max_num_steps, const Eigen::VectorXd& y, + double function_tolerance, int64_t max_num_steps, const T_Args&... args) { // NOLINT(runtime/int) const auto& x_ref = to_ref(value_of(x)); - auto args_vals_tuple = std::make_tuple(y, to_ref(args)...); check_nonzero_size("algebra_solver_newton", "initial guess", x_ref); check_finite("algebra_solver_newton", "initial guess", x_ref); @@ -35,7 +34,7 @@ Eigen::VectorXd algebra_solver_newton_impl( check_positive("algebra_solver_newton", "max_num_steps", max_num_steps); return kinsol_solve(f, x_ref, scaling_step_size, function_tolerance, - max_num_steps, 1, 10, KIN_LINESEARCH, msgs, y, args...); + max_num_steps, 1, 10, KIN_LINESEARCH, msgs, args...); } /** Implementation of autodiff newton solver. */ diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 71cf8453b42..e3951d1f21a 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -58,10 +58,11 @@ template * = nullptr> Eigen::VectorXd algebra_solver_powell_impl( const F& f, const T& x, std::ostream* msgs, double relative_tolerance, - double function_tolerance, int64_t max_num_steps, const Eigen::VectorXd& y, + double function_tolerance, int64_t max_num_steps, const T_Args&... args) { // NOLINT(runtime/int) const auto& x_val = to_ref(value_of(x)); - auto args_vals_tuple = std::make_tuple(y, eval(value_of(args))...); + auto arena_args_tuple = std::make_tuple(to_arena(args)...); + auto args_vals_tuple = std::make_tuple(eval(value_of(args))...); check_nonzero_size("algebra_solver_powell", "initial guess", x_val); check_finite("algebra_solver_powell", "initial guess", x_val); @@ -155,7 +156,7 @@ Eigen::Matrix algebra_solver_powell_impl( VectorXd ret_val = ret.val(); auto x_nrad_ = apply( - [&](const auto&... args) { return f(ret_val, msgs, args...); }, + [&](const auto&... args) { return eval(f(ret_val, msgs, args...)); }, arena_args_tuple); x_nrad_.adj() = eta; grad(); From 82df06268ad044623d2548f69bcf169c401270ab Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Tue, 29 Jun 2021 16:38:49 -0400 Subject: [PATCH 59/88] Make first set of requested changes. --- stan/math/rev/functor/algebra_solver_fp.hpp | 13 +-- .../rev/functor/algebra_solver_newton.hpp | 13 +-- .../rev/functor/algebra_solver_powell.hpp | 90 ++++++++----------- 3 files changed, 46 insertions(+), 70 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_fp.hpp b/stan/math/rev/functor/algebra_solver_fp.hpp index 60b096ed49d..00a50481aa9 100644 --- a/stan/math/rev/functor/algebra_solver_fp.hpp +++ b/stan/math/rev/functor/algebra_solver_fp.hpp @@ -191,7 +191,7 @@ Eigen::Matrix algebra_solver_fp_impl( auto arena_args_tuple = std::make_tuple(to_arena(args)...); auto args_vals_tuple = apply( [&](const auto&... args) { - return std::make_tuple(eval(value_of(args))...); + return std::make_tuple(to_ref(value_of(args))...); }, arena_args_tuple); @@ -221,21 +221,16 @@ Eigen::Matrix algebra_solver_fp_impl( arena_t ret = theta_dbl; reverse_pass_callback([f, ret, arena_args_tuple, arena_Jf_x, msgs]() mutable { - using Eigen::Dynamic; - using Eigen::Matrix; - using Eigen::MatrixXd; - using Eigen::VectorXd; - // Contract specificities with inverse Jacobian of f with respect to x. - VectorXd ret_adj = ret.adj(); - VectorXd eta = arena_Jf_x.transpose().lu().solve(ret_adj); + Eigen::VectorXd ret_adj = ret.adj(); + Eigen::VectorXd eta = arena_Jf_x.transpose().lu().solve(ret_adj); // Contract with Jacobian of f with respect to y using a nested reverse // autodiff pass. { nested_rev_autodiff rev; - VectorXd ret_val = ret.val(); + Eigen::VectorXd ret_val = ret.val(); auto x_nrad_ = apply( [&](const auto&... args) { return eval(f(ret_val, msgs, args...)); }, arena_args_tuple); diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index ea32c45c29b..2dd3cd5779a 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -49,7 +49,7 @@ Eigen::Matrix algebra_solver_newton_impl( auto arena_args_tuple = std::make_tuple(to_arena(args)...); auto args_vals_tuple = apply( [&](const auto&... args) { - return std::make_tuple(eval(value_of(args))...); + return std::make_tuple(to_ref(value_of(args))...); }, arena_args_tuple); @@ -87,21 +87,16 @@ Eigen::Matrix algebra_solver_newton_impl( arena_t ret = theta_dbl; reverse_pass_callback([f, ret, arena_args_tuple, arena_Jf_x, msgs]() mutable { - using Eigen::Dynamic; - using Eigen::Matrix; - using Eigen::MatrixXd; - using Eigen::VectorXd; - // Contract specificities with inverse Jacobian of f with respect to x. - VectorXd ret_adj = ret.adj(); - VectorXd eta = -arena_Jf_x.transpose().lu().solve(ret_adj); + Eigen::VectorXd ret_adj = ret.adj(); + Eigen::VectorXd eta = -arena_Jf_x.transpose().lu().solve(ret_adj); // Contract with Jacobian of f with respect to y using a nested reverse // autodiff pass. { nested_rev_autodiff rev; - VectorXd ret_val = ret.val(); + Eigen::VectorXd ret_val = ret.val(); auto x_nrad_ = apply( [&](const auto&... args) { return eval(f(ret_val, msgs, args...)); }, arena_args_tuple); diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index d2e2238e226..6dad24488c6 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -18,12 +18,17 @@ namespace stan { namespace math { /** Function for internal use that actually calls the powell solver. */ -template * = nullptr> Eigen::VectorXd algebra_solver_powell_call_solver_( - S& solver, const F& fx, const T& x, std::ostream* msgs, - double relative_tolerance, double function_tolerance, - long int max_num_steps) { // NOLINT(runtime/int) + const F& f, const T& x, std::ostream* msgs, + const double relative_tolerance, const double function_tolerance, + const long int max_num_steps, const Args&... args) { + + // Constr + hybrj_functor_solver hfs(f); + Eigen::HybridNonLinearSolver solver(hfs); + // Compute theta_dbl Eigen::VectorXd theta_dbl = x; solver.parameters.xtol = relative_tolerance; @@ -37,7 +42,7 @@ Eigen::VectorXd algebra_solver_powell_call_solver_( } // Check solution is a root - double system_norm = fx.get_value(theta_dbl).stableNorm(); + double system_norm = f(theta_dbl).stableNorm(); if (system_norm > function_tolerance) { std::ostringstream message; message << "the norm of the algebraic function is " << system_norm @@ -53,16 +58,19 @@ Eigen::VectorXd algebra_solver_powell_call_solver_( } /** Implementation of ordinary powell solver. */ -template * = nullptr, - require_all_st_arithmetic* = nullptr> + require_all_st_arithmetic* = nullptr> Eigen::VectorXd algebra_solver_powell_impl( - const F& f, const T& x, std::ostream* msgs, double relative_tolerance, - double function_tolerance, int64_t max_num_steps, - const T_Args&... args) { // NOLINT(runtime/int) + const F& f, const T& x, std::ostream* msgs, const double relative_tolerance, + const double function_tolerance, const int64_t max_num_steps, + const Args&... args) { const auto& x_val = to_ref(value_of(x)); - auto arena_args_tuple = std::make_tuple(to_arena(args)...); - auto args_vals_tuple = std::make_tuple(eval(value_of(args))...); + + // Curry the input function w.r.t. y + auto f_wrt_x = [&](const auto& x) { + return f(x, msgs, args...); + }; check_nonzero_size("algebra_solver_powell", "initial guess", x_val); check_finite("algebra_solver_powell", "initial guess", x_val); @@ -71,24 +79,12 @@ Eigen::VectorXd algebra_solver_powell_impl( check_nonnegative("algebra_solver_powell", "function_tolerance", function_tolerance); check_positive("algebra_solver_powell", "max_num_steps", max_num_steps); - - // Construct the Powell solver - auto f_wrt_x = [&](const auto& x) { - return apply([&](const auto&... args) { return f(x, msgs, args...); }, - args_vals_tuple); - }; - - hybrj_functor_solver fx(f_wrt_x); - Eigen::HybridNonLinearSolver solver(fx); - - // Check dimension unknowns equals dimension of system output check_matching_sizes("algebra_solver", "the algebraic system's output", - fx.get_value(x_val), "the vector of unknowns, x,", x); + f_wrt_x(x), "the vector of unknowns, x,", x); // Solve the system - return algebra_solver_powell_call_solver_(solver, fx, x_val, 0, - relative_tolerance, - function_tolerance, max_num_steps); + return algebra_solver_powell_call_solver_( + f_wrt_x, x_val, msgs, relative_tolerance, function_tolerance, max_num_steps); } /** Implementation of autodiff powell solver. */ @@ -103,10 +99,16 @@ Eigen::Matrix algebra_solver_powell_impl( auto arena_args_tuple = std::make_tuple(to_arena(args)...); auto args_vals_tuple = apply( [&](const auto&... args) { - return std::make_tuple(eval(value_of(args))...); + return std::make_tuple(to_ref(value_of(args))...); }, arena_args_tuple); + // Curry the input function w.r.t. y + auto f_wrt_x = [&](const auto& x) { + return apply([&](const auto&... args) { return f(x, msgs, args...); }, + args_vals_tuple); + }; + check_nonzero_size("algebra_solver_powell", "initial guess", x_val); check_finite("algebra_solver_powell", "initial guess", x_val); check_nonnegative("alegbra_solver_powell", "relative_tolerance", @@ -114,23 +116,12 @@ Eigen::Matrix algebra_solver_powell_impl( check_nonnegative("algebra_solver_powell", "function_tolerance", function_tolerance); check_positive("algebra_solver_powell", "max_num_steps", max_num_steps); - - // Construct the Powell solver - auto f_wrt_x = [&](const auto& x) { - return apply([&](const auto&... args) { return f(x, msgs, args...); }, - args_vals_tuple); - }; - - hybrj_functor_solver fx(f_wrt_x); - Eigen::HybridNonLinearSolver solver(fx); - - // Check dimension unknowns equals dimension of system output check_matching_sizes("algebra_solver", "the algebraic system's output", - fx.get_value(x_val), "the vector of unknowns, x,", x); + f_wrt_x(x), "the vector of unknowns, x,", x); // Solve the system Eigen::VectorXd theta_dbl = algebra_solver_powell_call_solver_( - solver, fx, x_val, 0, relative_tolerance, function_tolerance, + f_wrt_x, x_val, msgs, relative_tolerance, function_tolerance, max_num_steps); Eigen::MatrixXd Jf_x; @@ -146,22 +137,17 @@ Eigen::Matrix algebra_solver_powell_impl( reverse_pass_callback( [f, ret, arena_args_tuple, Jf_xT_lu_ptr, msgs]() mutable { - using Eigen::Dynamic; - using Eigen::Matrix; - using Eigen::MatrixXd; - using Eigen::VectorXd; - // Contract specificities with inverse Jacobian of f with respect to x. - VectorXd ret_adj = ret.adj(); - VectorXd eta = -Jf_xT_lu_ptr->solve(ret_adj); + Eigen::VectorXd ret_adj = ret.adj(); + Eigen::VectorXd eta = -Jf_xT_lu_ptr->solve(ret_adj); // Contract with Jacobian of f with respect to y using a nested reverse // autodiff pass. { nested_rev_autodiff rev; - VectorXd ret_val = ret.val(); + Eigen::VectorXd ret_val = ret.val(); auto x_nrad_ = apply( - [&](const auto&... args) { return f(ret_val, msgs, args...); }, + [&](const auto&... args) { return eval(f(ret_val, msgs, args...)); }, arena_args_tuple); x_nrad_.adj() = eta; grad(); @@ -220,8 +206,8 @@ template , Eigen::Dynamic, 1> algebra_solver_powell( const F& f, const T1& x, const T2& y, const std::vector& dat, const std::vector& dat_int, std::ostream* msgs = nullptr, - double relative_tolerance = 1e-10, double function_tolerance = 1e-6, - long int max_num_steps = 1e+3) { // NOLINT(runtime/int) + const double relative_tolerance = 1e-10, const double function_tolerance = 1e-6, + const long int max_num_steps = 1e+3) { return algebra_solver_powell_impl(algebra_solver_adapter(f), x, msgs, relative_tolerance, function_tolerance, max_num_steps, y, dat, dat_int); From d178dfaa3177fc32083e0c86d0e28fba3a4e2da9 Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Tue, 29 Jun 2021 20:42:54 +0000 Subject: [PATCH 60/88] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- .../rev/functor/algebra_solver_powell.hpp | 63 ++++++++++--------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 6dad24488c6..3d3ac4b1cbb 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -21,10 +21,9 @@ namespace math { template * = nullptr> Eigen::VectorXd algebra_solver_powell_call_solver_( - const F& f, const T& x, std::ostream* msgs, - const double relative_tolerance, const double function_tolerance, - const long int max_num_steps, const Args&... args) { - + const F& f, const T& x, std::ostream* msgs, const double relative_tolerance, + const double function_tolerance, const long int max_num_steps, + const Args&... args) { // Constr hybrj_functor_solver hfs(f); Eigen::HybridNonLinearSolver solver(hfs); @@ -61,16 +60,16 @@ Eigen::VectorXd algebra_solver_powell_call_solver_( template * = nullptr, require_all_st_arithmetic* = nullptr> -Eigen::VectorXd algebra_solver_powell_impl( - const F& f, const T& x, std::ostream* msgs, const double relative_tolerance, - const double function_tolerance, const int64_t max_num_steps, - const Args&... args) { +Eigen::VectorXd algebra_solver_powell_impl(const F& f, const T& x, + std::ostream* msgs, + const double relative_tolerance, + const double function_tolerance, + const int64_t max_num_steps, + const Args&... args) { const auto& x_val = to_ref(value_of(x)); // Curry the input function w.r.t. y - auto f_wrt_x = [&](const auto& x) { - return f(x, msgs, args...); - }; + auto f_wrt_x = [&](const auto& x) { return f(x, msgs, args...); }; check_nonzero_size("algebra_solver_powell", "initial guess", x_val); check_finite("algebra_solver_powell", "initial guess", x_val); @@ -83,8 +82,9 @@ Eigen::VectorXd algebra_solver_powell_impl( f_wrt_x(x), "the vector of unknowns, x,", x); // Solve the system - return algebra_solver_powell_call_solver_( - f_wrt_x, x_val, msgs, relative_tolerance, function_tolerance, max_num_steps); + return algebra_solver_powell_call_solver_(f_wrt_x, x_val, msgs, + relative_tolerance, + function_tolerance, max_num_steps); } /** Implementation of autodiff powell solver. */ @@ -135,24 +135,24 @@ Eigen::Matrix algebra_solver_powell_impl( arena_t ret = theta_dbl; - reverse_pass_callback( - [f, ret, arena_args_tuple, Jf_xT_lu_ptr, msgs]() mutable { - // Contract specificities with inverse Jacobian of f with respect to x. - Eigen::VectorXd ret_adj = ret.adj(); - Eigen::VectorXd eta = -Jf_xT_lu_ptr->solve(ret_adj); + reverse_pass_callback([f, ret, arena_args_tuple, Jf_xT_lu_ptr, + msgs]() mutable { + // Contract specificities with inverse Jacobian of f with respect to x. + Eigen::VectorXd ret_adj = ret.adj(); + Eigen::VectorXd eta = -Jf_xT_lu_ptr->solve(ret_adj); - // Contract with Jacobian of f with respect to y using a nested reverse - // autodiff pass. - { - nested_rev_autodiff rev; - Eigen::VectorXd ret_val = ret.val(); - auto x_nrad_ = apply( - [&](const auto&... args) { return eval(f(ret_val, msgs, args...)); }, - arena_args_tuple); - x_nrad_.adj() = eta; - grad(); - } - }); + // Contract with Jacobian of f with respect to y using a nested reverse + // autodiff pass. + { + nested_rev_autodiff rev; + Eigen::VectorXd ret_val = ret.val(); + auto x_nrad_ = apply( + [&](const auto&... args) { return eval(f(ret_val, msgs, args...)); }, + arena_args_tuple); + x_nrad_.adj() = eta; + grad(); + } + }); return ret_type(ret); } @@ -206,7 +206,8 @@ template , Eigen::Dynamic, 1> algebra_solver_powell( const F& f, const T1& x, const T2& y, const std::vector& dat, const std::vector& dat_int, std::ostream* msgs = nullptr, - const double relative_tolerance = 1e-10, const double function_tolerance = 1e-6, + const double relative_tolerance = 1e-10, + const double function_tolerance = 1e-6, const long int max_num_steps = 1e+3) { return algebra_solver_powell_impl(algebra_solver_adapter(f), x, msgs, relative_tolerance, function_tolerance, From 86ce197b9095b6202046b5920269188810652a54 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Tue, 29 Jun 2021 20:26:15 -0400 Subject: [PATCH 61/88] Respond to all comments except LU factorization. --- stan/math/rev/functor/algebra_solver_fp.hpp | 210 ++++++++++++++---- .../rev/functor/algebra_solver_newton.hpp | 133 +++++++++-- .../rev/functor/algebra_solver_powell.hpp | 172 ++++++++++++-- stan/math/rev/functor/kinsol_data.hpp | 12 +- stan/math/rev/functor/kinsol_solve.hpp | 14 +- 5 files changed, 445 insertions(+), 96 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_fp.hpp b/stan/math/rev/functor/algebra_solver_fp.hpp index 00a50481aa9..a838ec2c6cc 100644 --- a/stan/math/rev/functor/algebra_solver_fp.hpp +++ b/stan/math/rev/functor/algebra_solver_fp.hpp @@ -29,6 +29,15 @@ namespace math { * auxiliary data that will be used for functor evaluation. * * @tparam F functor type for system function. + * @tparam T_u type of scaling vector for unknowns. We allow + * it to be @c var because scaling could be parameter + * dependent. Internally these params are converted to data + * because scaling is applied. + * @tparam T_f type of scaling vector for residual. We allow + * it to be @c var because scaling could be parameter + * dependent. Internally these params are converted to data + * because scaling is applied. + * @tparam Args types of additional parameters to the equation system functor. */ template struct KinsolFixedPointEnv { @@ -51,7 +60,7 @@ struct KinsolFixedPointEnv { KinsolFixedPointEnv(const F& f, const Eigen::MatrixXd x, const std::vector& u_scale, - const std::vector& f_scale, std::ostream* msgs, + const std::vector& f_scale, std::ostream* const msgs, const Args&... args) : f_(f), N_(x.size()), @@ -76,7 +85,7 @@ struct KinsolFixedPointEnv { } /** Implements the user-defined function passed to KINSOL. */ - static int kinsol_f_system(N_Vector x, N_Vector f, void* user_data) { + static int kinsol_f_system(const N_Vector x, const N_Vector f, void* const user_data) { auto g = static_cast*>( user_data); Eigen::VectorXd x_eigen(Eigen::Map(NV_DATA_S(x), g->N_)); @@ -90,19 +99,31 @@ struct KinsolFixedPointEnv { }; /** - * Solve FP using KINSOL - * + * Private interface for solving fixed point problems using KINSOL. Users should + * call the KINSOL fixed point solver through `algebra_solver_fp` or + * `algebra_solver_fp_impl`. + * + * @tparam F type of the equation system functor f + * @tparam T_u type of scaling vector for unknowns. We allow + * it to be @c var because scaling could be parameter + * dependent. Internally these params are converted to data + * because scaling is applied. + * @tparam T_f type of scaling vector for residual. We allow + * it to be @c var because scaling could be parameter + * dependent. Internally these params are converted to data + * because scaling is applied. * @param x initial point and final solution. * @param env KINSOL solution environment * @param f_tol Function tolerance * @param max_num_steps max nb. of iterations. */ -template -Eigen::VectorXd kinsol_solve_fp(const F& f, const Eigen::VectorXd& x, - double function_tolerance, double max_num_steps, +template * = nullptr> +Eigen::VectorXd kinsol_solve_fp(const F& f, const T& x, + const double function_tolerance, const double max_num_steps, const std::vector& u_scale, const std::vector& f_scale, - std::ostream* msgs, const Args&... args) { + std::ostream* const msgs, const Args&... args) { KinsolFixedPointEnv env(f, x, u_scale, f_scale, msgs, args...); int N = env.N_; @@ -130,14 +151,67 @@ Eigen::VectorXd kinsol_solve_fp(const F& f, const Eigen::VectorXd& x, return x_solution; } -/** Implementation of ordinary fixed point solver. */ +/** + * Return a fixed pointer to the specified system of algebraic + * equations of form + * \[ + * x = F(x; theta) + * \] + * given an initial guess \(x\), and parameters \(theta\) and data. Use the + * KINSOL solver from the SUNDIALS suite. + * + * The user can also specify the scaling controls, the function + * tolerance, and the maximum number of steps. + * + * This function is overloaded to handle both constant and var-type parameters. + * This overload handles var parameters, and checks the input, calls the + * algebraic solver, and appropriately handles derivative propagation through + * the `reverse_pass_callback`. + * + * @tparam F type of equation system function. + * @tparam T type of initial guess vector. + * @tparam T_u type of scaling vector for unknowns. We allow + * it to be @c var because scaling could be parameter + * dependent. Internally these params are converted to data + * because scaling is applied. + * @tparam T_f type of scaling vector for residual. We allow + * it to be @c var because scaling could be parameter + * dependent. Internally these params are converted to data + * because scaling is applied. + * + * @param[in] f functor that evaluated the system of equations. + * @param[in] x vector of starting values. + * @param[in, out] msgs the print stream for warning messages. + * @param[in] u_scale diagonal scaling matrix elements \(Du\) + * such that \(Du x\) has all components roughly the same + * magnitude when \(x\) is close to a solution. + * (ref. KINSOL user guide chap.2 sec. "Scaling") + * @param[in] f_scale diagonal scaling matrix elements such + * that \(Df (x - f(x))\) has all components roughly the same + * magnitude when \(x\) is not too close to a solution. + * (ref. KINSOL user guide chap.2 sec. "Scaling") + * @param[in] function_tolerance Function-norm stopping tolerance. + * @param[in] max_num_steps maximum number of function evaluations. + * @param[in] args additional parameters to the equation system functor. + * @pre f returns finite values when passed any value of x and the given args. + * @throw std::invalid_argument if x has size zero. + * @throw std::invalid_argument if x has non-finite elements. + * @throw std::invalid_argument if scaled_step_size is strictly + * negative. + * @throw std::invalid_argument if function_tolerance is strictly + * negative. + * @throw std::invalid_argument if max_num_steps is not positive. + * @throw boost::math::evaluation_error (which is a subclass of + * std::runtime_error) if solver exceeds max_num_steps. + */ template * = nullptr, require_all_st_arithmetic* = nullptr> Eigen::VectorXd algebra_solver_fp_impl( - const F& f, const T& x, const double function_tolerance, - const int max_num_steps, const std::vector& u_scale, - const std::vector& f_scale, std::ostream* msgs, const Args&... args) { + const F& f, const T& x, std::ostream* const msgs, + const std::vector& u_scale, const std::vector& f_scale, + const double function_tolerance, const int max_num_steps, + const Args&... args) { const auto& x_ref = to_ref(value_of(x)); check_nonzero_size("algebra_solver_fp", "initial guess", x_ref); @@ -155,38 +229,90 @@ Eigen::VectorXd algebra_solver_fp_impl( f_scale, msgs, args...); } -/* Implementation of autodiff fixed point solver. The Jacobian Jxy(Jacobian of - * unknown x w.r.t. the * param y) is calculated given the solution as follows. - * Since - * - * x - f(x, y) = 0 - * - * we have (Jpq being the Jacobian matrix dq/dq) - * - * Jxy - Jfx * Jxy = Jfy - * - * therefore Jxy can be solved from system - * - * (I - Jfx) * Jxy = Jfy +/** + * Return a fixed pointer to the specified system of algebraic + * equations of form + * \[ + * x = F(x; theta) + * \] + * given an initial guess \(x\), and parameters \(theta\) and data. Use the + * KINSOL solver from the SUNDIALS suite. * - * Let eta be the adjoint with respect to x; then to calculate + * The user can also specify the scaling controls, the function + * tolerance, and the maximum number of steps. * - * eta * Jxy + * This function is overloaded to handle both constant and var-type parameters. + * This overload handles var parameters, and checks the input, calls the + * algebraic solver, and appropriately handles derivative propagation through + * the `reverse_pass_callback`. * + * The Jacobian \(J_{xy}\) (i.e., Jacobian of unknown \(x\) w.r.t. the parameter + * \(y\)) is calculated given the solution as follows. Since + * \[ + * x - f(x, y) = 0, + * \] + * we have (\(J_{pq}\) being the Jacobian matrix \(\tfrac {dq} {dq}\)) + * \[ + * J_{xy} - J_{fx} J_{xy} = J_{fy}, + * \] + * and therefore \(J_{xy}\) can be solved from system + * \[ + * (I - J_{fx}) * J_{xy} = J_{fy}. + * \] + * Let \(eta\) be the adjoint with respect to \(x\); then to calculate + * \[ + * \eta J_{xy}, + * \] * we solve + * \[ + * (\eta * (I - J_{fx})^{-1}) * J_{fy}. + * \] + * (This is virtually identical to the Powell and Newton solvers, except + * \(-J_{fx}\) has been replaced by \((I - J_{fx}\).) * - * (eta * (I - Jfx)^(-1)) * Jfy + * @tparam F type of equation system function. + * @tparam T type of initial guess vector. + * @tparam T_u type of scaling vector for unknowns. We allow + * it to be @c var because scaling could be parameter + * dependent. Internally these params are converted to data + * because scaling is applied. + * @tparam T_f type of scaling vector for residual. We allow + * it to be @c var because scaling could be parameter + * dependent. Internally these params are converted to data + * because scaling is applied. * - * (This is virtually identical to the Powell and Newton solvers, except Jfx - * has been replaced by I - Jfx.) + * @param[in] f functor that evaluated the system of equations. + * @param[in] x vector of starting values. + * @param[in, out] msgs the print stream for warning messages. + * @param[in] u_scale diagonal scaling matrix elements \(Du\) + * such that \(Du x\) has all components roughly the same + * magnitude when \(x\) is close to a solution. + * (ref. KINSOL user guide chap.2 sec. "Scaling") + * @param[in] f_scale diagonal scaling matrix elements such + * that \(Df (x - f(x))\) has all components roughly the same + * magnitude when \(x\) is not too close to a solution. + * (ref. KINSOL user guide chap.2 sec. "Scaling") + * @param[in] function_tolerance Function-norm stopping tolerance. + * @param[in] max_num_steps maximum number of function evaluations. + * @param[in] args additional parameters to the equation system functor. + * @pre f returns finite values when passed any value of x and the given args. + * @throw std::invalid_argument if x has size zero. + * @throw std::invalid_argument if x has non-finite elements. + * @throw std::invalid_argument if scaled_step_size is strictly + * negative. + * @throw std::invalid_argument if function_tolerance is strictly + * negative. + * @throw std::invalid_argument if max_num_steps is not positive. + * @throw boost::math::evaluation_error (which is a subclass of + * std::runtime_error) if solver exceeds max_num_steps. */ template * = nullptr, require_any_st_var* = nullptr> Eigen::Matrix algebra_solver_fp_impl( - const F& f, const T& x, const double function_tolerance, - const int max_num_steps, const std::vector& u_scale, - const std::vector& f_scale, std::ostream* msgs, const Args&... args) { + const F& f, const T& x, std::ostream* const msgs, const std::vector& u_scale, + const std::vector& f_scale, const double function_tolerance, + const int max_num_steps, const Args&... args) { const auto& x_val = to_ref(value_of(x)); auto arena_args_tuple = std::make_tuple(to_arena(args)...); auto args_vals_tuple = apply( @@ -254,6 +380,9 @@ Eigen::Matrix algebra_solver_fp_impl( * The user can also specify the scaling controls, the function * tolerance, and the maximum number of steps. * + * Signature to maintain backward compatibility, will be removed + * in the future. + * * @tparam F type of equation system function. * @tparam T type of initial guess vector. The final solution * type doesn't depend on initial guess type, @@ -272,8 +401,8 @@ Eigen::Matrix algebra_solver_fp_impl( * @param[in] y Parameter vector for the equation system. The function * is overloaded to treat y as a vector of doubles or of a * a template type T. - * @param[in] x_r Continuous data vector for the equation system. - * @param[in] x_i Integer data vector for the equation system. + * @param[in] dat Continuous data vector for the equation system. + * @param[in] dat_int Integer data vector for the equation system. * @param[in, out] msgs The print stream for warning messages. * @param[in] u_scale diagonal scaling matrix elements Du * such that Du*x has all components roughly the same @@ -285,9 +414,10 @@ Eigen::Matrix algebra_solver_fp_impl( * (ref. KINSOL user guide chap.2 sec. "Scaling") * @param[in] f_tol Function-norm stopping tolerance. * @param[in] max_num_steps maximum number of function evaluations. + * @pre f returns finite values when passed any value of x and the given y, dat, + * and dat_int. * @throw std::invalid_argument if x has size zero. * @throw std::invalid_argument if x has non-finite elements. - * @throw std::invalid_argument if y has non-finite elements. * @throw std::invalid_argument if dat has non-finite elements. * @throw std::invalid_argument if dat_int has non-finite elements. * @throw std::invalid_argument if scaled_step_size is strictly @@ -303,11 +433,11 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_fp( const F& f, const Eigen::Matrix& x, const Eigen::Matrix& y, const std::vector& dat, const std::vector& dat_int, const std::vector& u_scale, - const std::vector& f_scale, std::ostream* msgs = nullptr, + const std::vector& f_scale, std::ostream* const msgs = nullptr, double function_tolerance = 1e-8, int max_num_steps = 200) { - return algebra_solver_fp_impl(algebra_solver_adapter(f), x, - function_tolerance, max_num_steps, u_scale, - f_scale, msgs, y, dat, dat_int); + return algebra_solver_fp_impl(algebra_solver_adapter(f), x, msgs, + u_scale, f_scale, function_tolerance, + max_num_steps, y, dat, dat_int); } } // namespace math diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index 2dd3cd5779a..c900b8ffcc2 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -15,14 +15,51 @@ namespace stan { namespace math { -/** Implementation of ordinary newton solver. */ -template std::invalid_argument if x has size zero. + * @throw std::invalid_argument if x has non-finite elements. + * @throw std::invalid_argument if scaled_step_size is strictly + * negative. + * @throw std::invalid_argument if function_tolerance is strictly + * negative. + * @throw std::invalid_argument if max_num_steps is not positive. + * @throw std::domain_error if solver exceeds max_num_steps. + */ +template * = nullptr, - require_all_st_arithmetic* = nullptr> + require_all_st_arithmetic* = nullptr> Eigen::VectorXd algebra_solver_newton_impl( - const F& f, const T& x, std::ostream* msgs, double scaling_step_size, - double function_tolerance, int64_t max_num_steps, - const T_Args&... args) { // NOLINT(runtime/int) + const F& f, const T& x, std::ostream* const msgs, const double scaling_step_size, + const double function_tolerance, const int64_t max_num_steps, + const Args&... args) { const auto& x_ref = to_ref(value_of(x)); check_nonzero_size("algebra_solver_newton", "initial guess", x_ref); @@ -37,14 +74,74 @@ Eigen::VectorXd algebra_solver_newton_impl( max_num_steps, 1, 10, KIN_LINESEARCH, msgs, args...); } -/** Implementation of autodiff newton solver. */ +/** + * Return the solution to the specified system of algebraic + * equations given an initial guess, and parameters and data, + * which get passed into the algebraic system. Use the + * KINSOL solver from the SUNDIALS suite. + * + * The user can also specify the scaled step size, the function + * tolerance, and the maximum number of steps. + * + * This function is overloaded to handle both constant and var-type parameters. + * This overload handles var parameters, and checks the input, calls the + * algebraic solver, and appropriately handles derivative propagation through + * the `reverse_pass_callback`. + * + * The Jacobian \(J_{xy}\) (i.e., Jacobian of unknown \(x\) w.r.t. the parameter + * \(y\)) is calculated given the solution as follows. Since + * \[ + * f(x, y) = 0, + * \] + * we have (\(J_{pq}\) being the Jacobian matrix \(\tfrac {dq} {dq}\)) + * \[ + * - J_{fx} J_{xy} = J_{fy}, + * \] + * and therefore \(J_{xy}\) can be solved from system + * \[ + * - J_{fx} J_{xy} = J_{fy}. + * \] + * Let \(eta\) be the adjoint with respect to \(x\); then to calculate + * \[ + * \eta J_{xy}, + * \] + * we solve + * \[ + * - (\eta J_{fx}^{-1}) J_{fy}. + * \] + * + * @tparam F type of equation system function. + * @tparam T type of initial guess vector. + * @tparam Args types of additional parameters to the equation system functor + * + * @param[in] f Functor that evaluated the system of equations. + * @param[in] x Vector of starting values. + * @param[in, out] msgs The print stream for warning messages. + * @param[in] scaling_step_size Scaled-step stopping tolerance. If + * a Newton step is smaller than the scaling step + * tolerance, the code breaks, assuming the solver is no + * longer making significant progress (i.e. is stuck) + * @param[in] function_tolerance determines whether roots are acceptable. + * @param[in] max_num_steps maximum number of function evaluations. + * @param[in] args Additional parameters to the equation system functor. + * @return theta Vector of solutions to the system of equations. + * @pre f returns finite values when passed any value of x and the given args. + * @throw std::invalid_argument if x has size zero. + * @throw std::invalid_argument if x has non-finite elements. + * @throw std::invalid_argument if scaled_step_size is strictly + * negative. + * @throw std::invalid_argument if function_tolerance is strictly + * negative. + * @throw std::invalid_argument if max_num_steps is not positive. + * @throw std::domain_error if solver exceeds max_num_steps. + */ template * = nullptr, require_any_st_var* = nullptr> Eigen::Matrix algebra_solver_newton_impl( - const F& f, const T& x, std::ostream* msgs, double scaling_step_size, - double function_tolerance, int64_t max_num_steps, - const T_Args&... args) { // NOLINT(runtime/int) + const F& f, const T& x, std::ostream* const msgs, const double scaling_step_size, + const double function_tolerance, const int64_t max_num_steps, + const T_Args&... args) { const auto& x_ref = to_ref(value_of(x)); auto arena_args_tuple = std::make_tuple(to_arena(args)...); auto args_vals_tuple = apply( @@ -117,19 +214,15 @@ Eigen::Matrix algebra_solver_newton_impl( * The user can also specify the scaled step size, the function * tolerance, and the maximum number of steps. * - * Overload the previous definition to handle the case where y - * is a vector of parameters (var). The overload calls the - * algebraic solver defined above and builds a vari object on - * top, using the algebra_solver_vari class. + * Signature to maintain backward compatibility, will be removed + * in the future. * * @tparam F type of equation system function. * @tparam T type of initial guess vector. * * @param[in] f Functor that evaluated the system of equations. * @param[in] x Vector of starting values. - * @param[in] y Parameter vector for the equation system. The function - * is overloaded to treat y as a vector of doubles or of a - * a template type T. + * @param[in] y Parameter vector for the equation system. * @param[in] dat Continuous data vector for the equation system. * @param[in] dat_int Integer data vector for the equation system. * @param[in, out] msgs The print stream for warning messages. @@ -156,9 +249,9 @@ template * = nullptr> Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_newton( const F& f, const T1& x, const T2& y, const std::vector& dat, - const std::vector& dat_int, std::ostream* msgs = nullptr, - double scaling_step_size = 1e-3, double function_tolerance = 1e-6, - long int max_num_steps = 200) { // NOLINT(runtime/int) + const std::vector& dat_int, std::ostream* const msgs = nullptr, + const double scaling_step_size = 1e-3, const double function_tolerance = 1e-6, + const long int max_num_steps = 200) { // NOLINT(runtime/int) return algebra_solver_newton_impl(algebra_solver_adapter(f), x, msgs, scaling_step_size, function_tolerance, max_num_steps, y, dat, dat_int); diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 3d3ac4b1cbb..8b551948d15 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -17,14 +17,40 @@ namespace stan { namespace math { -/** Function for internal use that actually calls the powell solver. */ +/** + * Private interface for calling the Powell solver. Users should call the Powell + * solver through `algebra_solver_powell` or `algebra_solver_powell_impl`. + * + * @tparam F type of equation system function, curried with respect to inputs + * @tparam T type of elements in the x vector + * @tparam Args types of additional parameters to the equation system functor + * + * @param[in] f Functor that evaluates the system of equations, curried with + * respect to its input (i.e., "y") values. + * @param[in] x Vector of starting values (initial guess). + * @param[in, out] msgs the print stream for warning messages. + * @param[in] relative_tolerance determines the convergence criteria + * for the solution. + * @param[in] function_tolerance determines whether roots are acceptable. + * @param[in] max_num_steps maximum number of function evaluations. + * @return theta_dbl Double vector of solutions to the system of equations. + * @pre x has size greater than zero. + * @pre x has only finite elements. + * @pre f returns finite values when passed any value of x and the given args. + * @pre relative_tolerance is non-negative. + * @pre function_tolerance is non-negative. + * @pre max_num_steps is positive. + * @throw std::domain_error solver exceeds max_num_steps. + * @throw std::domain_error if the norm of the solution exceeds + * the function tolerance. + */ template * = nullptr> Eigen::VectorXd algebra_solver_powell_call_solver_( - const F& f, const T& x, std::ostream* msgs, const double relative_tolerance, - const double function_tolerance, const long int max_num_steps, + const F& f, const T& x, std::ostream* const msgs, const double relative_tolerance, + const double function_tolerance, const int64_t max_num_steps, const Args&... args) { - // Constr + // Construct the solver hybrj_functor_solver hfs(f); Eigen::HybridNonLinearSolver solver(hfs); @@ -56,16 +82,53 @@ Eigen::VectorXd algebra_solver_powell_call_solver_( return theta_dbl; } -/** Implementation of ordinary powell solver. */ +/** + * Return the solution to the specified system of algebraic + * equations given an initial guess, and parameters and data, + * which get passed into the algebraic system. + * Use Powell's dogleg solver. + * + * The user can also specify the relative tolerance + * (xtol in Eigen's code), the function tolerance, + * and the maximum number of steps (maxfev in Eigen's code). + * + * This function is overloaded to handle both constant and var-type parameters. + * This overload handles non-var parameters, and checks the input and calls the + * algebraic solver only. + * + * @tparam F type of equation system function + * @tparam T type of elements in the x vector + * @tparam Args types of additional parameters to the equation system functor + * + * @param[in] f Functor that evaluates the system of equations. + * @param[in] x Vector of starting values (initial guess). + * @param[in, out] msgs the print stream for warning messages. + * @param[in] relative_tolerance determines the convergence criteria + * for the solution. + * @param[in] function_tolerance determines whether roots are acceptable. + * @param[in] max_num_steps maximum number of function evaluations. + * @param[in] args additional parameters to the equation system functor. + * @return theta Vector of solutions to the system of equations. + * @pre f returns finite values when passed any value of x and the given args. + * @throw std::invalid_argument if x has size zero. + * @throw std::invalid_argument if x has non-finite elements. + * elements. + * @throw std::invalid_argument if relative_tolerance is strictly + * negative. + * @throw std::invalid_argument if function_tolerance is strictly + * negative. + * @throw std::invalid_argument if max_num_steps is not positive. + * @throw std::domain_error solver exceeds max_num_steps. + * @throw std::domain_error if the norm of the solution exceeds + * the function tolerance. + */ template * = nullptr, require_all_st_arithmetic* = nullptr> -Eigen::VectorXd algebra_solver_powell_impl(const F& f, const T& x, - std::ostream* msgs, - const double relative_tolerance, - const double function_tolerance, - const int64_t max_num_steps, - const Args&... args) { +Eigen::VectorXd algebra_solver_powell_impl( + const F& f, const T& x, std::ostream* const msgs, const double relative_tolerance, + const double function_tolerance, const int64_t max_num_steps, + const Args&... args) { const auto& x_val = to_ref(value_of(x)); // Curry the input function w.r.t. y @@ -87,14 +150,76 @@ Eigen::VectorXd algebra_solver_powell_impl(const F& f, const T& x, function_tolerance, max_num_steps); } -/** Implementation of autodiff powell solver. */ +/** + * Return the solution to the specified system of algebraic + * equations given an initial guess, and parameters and data, + * which get passed into the algebraic system. + * Use Powell's dogleg solver. + * + * The user can also specify the relative tolerance + * (xtol in Eigen's code), the function tolerance, + * and the maximum number of steps (maxfev in Eigen's code). + * + * This function is overloaded to handle both constant and var-type parameters. + * This overload handles var parameters, and checks the input, calls the + * algebraic solver, and appropriately handles derivative propagation through + * the `reverse_pass_callback`. + * + * The Jacobian \(J_{xy}\) (i.e., Jacobian of unknown \(x\) w.r.t. the parameter + * \(y\)) is calculated given the solution as follows. Since + * \[ + * f(x, y) = 0, + * \] + * we have (\(J_{pq}\) being the Jacobian matrix \(\tfrac {dq} {dq}\)) + * \[ + * - J_{fx} J_{xy} = J_{fy}, + * \] + * and therefore \(J_{xy}\) can be solved from system + * \[ + * - J_{fx} J_{xy} = J_{fy}. + * \] + * Let \(eta\) be the adjoint with respect to \(x\); then to calculate + * \[ + * \eta J_{xy}, + * \] + * we solve + * \[ + * - (\eta J_{fx}^{-1}) J_{fy}. + * \] + * + * @tparam F type of equation system function + * @tparam T type of elements in the x vector + * @tparam Args types of additional parameters to the equation system functor + * + * @param[in] f Functor that evaluates the system of equations. + * @param[in] x Vector of starting values (initial guess). + * @param[in, out] msgs the print stream for warning messages. + * @param[in] relative_tolerance determines the convergence criteria + * for the solution. + * @param[in] function_tolerance determines whether roots are acceptable. + * @param[in] max_num_steps maximum number of function evaluations. + * @param[in] args Additional parameters to the equation system functor. + * @return theta Vector of solutions to the system of equations. + * @pre f returns finite values when passed any value of x and the given args. + * @throw std::invalid_argument if x has size zero. + * @throw std::invalid_argument if x has non-finite elements. + * elements. + * @throw std::invalid_argument if relative_tolerance is strictly + * negative. + * @throw std::invalid_argument if function_tolerance is strictly + * negative. + * @throw std::invalid_argument if max_num_steps is not positive. + * @throw std::domain_error solver exceeds max_num_steps. + * @throw std::domain_error if the norm of the solution exceeds + * the function tolerance. + */ template * = nullptr, require_any_st_var* = nullptr> Eigen::Matrix algebra_solver_powell_impl( - const F& f, const T& x, std::ostream* msgs, double relative_tolerance, - double function_tolerance, int64_t max_num_steps, - const T_Args&... args) { // NOLINT(runtime/int) + const F& f, const T& x, std::ostream* const msgs, const double relative_tolerance, + const double function_tolerance, const int64_t max_num_steps, + const T_Args&... args) { const auto& x_val = to_ref(value_of(x)); auto arena_args_tuple = std::make_tuple(to_arena(args)...); auto args_vals_tuple = apply( @@ -156,6 +281,7 @@ Eigen::Matrix algebra_solver_powell_impl( return ret_type(ret); } + /** * Return the solution to the specified system of algebraic * equations given an initial guess, and parameters and data, @@ -166,10 +292,8 @@ Eigen::Matrix algebra_solver_powell_impl( * (xtol in Eigen's code), the function tolerance, * and the maximum number of steps (maxfev in Eigen's code). * - * Overload the previous definition to handle the case where y - * is a vector of parameters (var). The overload calls the - * algebraic solver defined above and builds a vari object on - * top, using the algebra_solver_vari class. + * Signature to maintain backward compatibility, will be removed + * in the future. * * @tparam F type of equation system function * @tparam T1 type of elements in the x vector @@ -205,10 +329,10 @@ template * = nullptr> Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_powell( const F& f, const T1& x, const T2& y, const std::vector& dat, - const std::vector& dat_int, std::ostream* msgs = nullptr, + const std::vector& dat_int, std::ostream* const msgs = nullptr, const double relative_tolerance = 1e-10, const double function_tolerance = 1e-6, - const long int max_num_steps = 1e+3) { + const int64_t max_num_steps = 1e+3) { return algebra_solver_powell_impl(algebra_solver_adapter(f), x, msgs, relative_tolerance, function_tolerance, max_num_steps, y, dat, dat_int); @@ -242,6 +366,8 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_powell( * @param[in] function_tolerance determines whether roots are acceptable. * @param[in] max_num_steps maximum number of function evaluations. * @return theta Vector of solutions to the system of equations. + * @pre f returns finite values when passed any value of x and the given y, dat, + * and dat_int. * @throw std::invalid_argument if x has size zero. * @throw std::invalid_argument if x has non-finite elements. * @throw std::invalid_argument if y has non-finite elements. @@ -264,8 +390,8 @@ template , Eigen::Dynamic, 1> algebra_solver( const F& f, const T1& x, const T2& y, const std::vector& dat, const std::vector& dat_int, std::ostream* msgs = nullptr, - double relative_tolerance = 1e-10, double function_tolerance = 1e-6, - long int max_num_steps = 1e+3) { // NOLINT(runtime/int) + const double relative_tolerance = 1e-10, const double function_tolerance = 1e-6, + const int64_t max_num_steps = 1e+3) { return algebra_solver_powell(f, x, y, dat, dat_int, msgs, relative_tolerance, function_tolerance, max_num_steps); } diff --git a/stan/math/rev/functor/kinsol_data.hpp b/stan/math/rev/functor/kinsol_data.hpp index ac06faa0e13..3c10b0cd15c 100644 --- a/stan/math/rev/functor/kinsol_data.hpp +++ b/stan/math/rev/functor/kinsol_data.hpp @@ -28,8 +28,8 @@ class kinsol_system_data { const F1& f_; const Eigen::VectorXd& x_; const size_t N_; - std::ostream* msgs_; - std::tuple args_tuple_; + std::ostream* const msgs_; + const std::tuple args_tuple_; typedef kinsol_system_data system_data; @@ -40,7 +40,7 @@ class kinsol_system_data { void* kinsol_memory_; /* Constructor */ - kinsol_system_data(const F1& f, const Eigen::VectorXd& x, std::ostream* msgs, + kinsol_system_data(const F1& f, const Eigen::VectorXd& x, std::ostream* const msgs, const Args&... args) : f_(f), x_(x), @@ -60,7 +60,7 @@ class kinsol_system_data { } /* Implements the user-defined function passed to KINSOL. */ - static int kinsol_f_system(N_Vector x, N_Vector f_eval, void* user_data) { + static int kinsol_f_system(const N_Vector x, const N_Vector f_eval, void* const user_data) { const system_data* explicit_system = static_cast(user_data); @@ -91,8 +91,8 @@ class kinsol_system_data { * https://computation.llnl.gov/sites/default/files/public/kin_guide-dev.pdf, * page 55. */ - static int kinsol_jacobian(N_Vector x, N_Vector f_eval, SUNMatrix J, - void* user_data, N_Vector tmp1, N_Vector tmp2) { + static int kinsol_jacobian(const N_Vector x, const N_Vector f_eval, const SUNMatrix J, + void* const user_data, const N_Vector tmp1, const N_Vector tmp2) { const system_data* explicit_system = static_cast(user_data); diff --git a/stan/math/rev/functor/kinsol_solve.hpp b/stan/math/rev/functor/kinsol_solve.hpp index d62a44d31b0..53f04a37fc0 100644 --- a/stan/math/rev/functor/kinsol_solve.hpp +++ b/stan/math/rev/functor/kinsol_solve.hpp @@ -58,13 +58,13 @@ namespace math { template Eigen::VectorXd kinsol_solve( const F1& f, const Eigen::VectorXd& x, - double scaling_step_tol, // = 1e-3 - double function_tolerance, // = 1e-6 - long int max_num_steps, // NOLINT(runtime/int) = 200 - bool custom_jacobian, // = 1 - int steps_eval_jacobian, // = 10 - int global_line_search, // = KIN_LINESEARCH - std::ostream* msgs, const Args&... args) { + const double scaling_step_tol, // = 1e-3 + const double function_tolerance, // = 1e-6 + const long int max_num_steps, // = 200 + const bool custom_jacobian, // = 1 + const int steps_eval_jacobian, // = 10 + const int global_line_search, // = KIN_LINESEARCH + std::ostream* const msgs, const Args&... args) { int N = x.size(); typedef kinsol_system_data system_data; system_data kinsol_data(f, x, msgs, args...); From f05ab116171210f96af4d8565ee2fab1e99fc462 Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Wed, 30 Jun 2021 02:16:10 +0000 Subject: [PATCH 62/88] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- stan/math/rev/functor/algebra_solver_fp.hpp | 33 +++++++++++-------- .../rev/functor/algebra_solver_newton.hpp | 19 ++++++----- .../rev/functor/algebra_solver_powell.hpp | 25 +++++++------- stan/math/rev/functor/kinsol_data.hpp | 12 ++++--- stan/math/rev/functor/kinsol_solve.hpp | 17 +++++----- 5 files changed, 59 insertions(+), 47 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_fp.hpp b/stan/math/rev/functor/algebra_solver_fp.hpp index a838ec2c6cc..471c37782b7 100644 --- a/stan/math/rev/functor/algebra_solver_fp.hpp +++ b/stan/math/rev/functor/algebra_solver_fp.hpp @@ -85,7 +85,8 @@ struct KinsolFixedPointEnv { } /** Implements the user-defined function passed to KINSOL. */ - static int kinsol_f_system(const N_Vector x, const N_Vector f, void* const user_data) { + static int kinsol_f_system(const N_Vector x, const N_Vector f, + void* const user_data) { auto g = static_cast*>( user_data); Eigen::VectorXd x_eigen(Eigen::Map(NV_DATA_S(x), g->N_)); @@ -102,7 +103,7 @@ struct KinsolFixedPointEnv { * Private interface for solving fixed point problems using KINSOL. Users should * call the KINSOL fixed point solver through `algebra_solver_fp` or * `algebra_solver_fp_impl`. - * + * * @tparam F type of the equation system functor f * @tparam T_u type of scaling vector for unknowns. We allow * it to be @c var because scaling could be parameter @@ -120,7 +121,8 @@ struct KinsolFixedPointEnv { template * = nullptr> Eigen::VectorXd kinsol_solve_fp(const F& f, const T& x, - const double function_tolerance, const double max_num_steps, + const double function_tolerance, + const double max_num_steps, const std::vector& u_scale, const std::vector& f_scale, std::ostream* const msgs, const Args&... args) { @@ -207,11 +209,13 @@ Eigen::VectorXd kinsol_solve_fp(const F& f, const T& x, template * = nullptr, require_all_st_arithmetic* = nullptr> -Eigen::VectorXd algebra_solver_fp_impl( - const F& f, const T& x, std::ostream* const msgs, - const std::vector& u_scale, const std::vector& f_scale, - const double function_tolerance, const int max_num_steps, - const Args&... args) { +Eigen::VectorXd algebra_solver_fp_impl(const F& f, const T& x, + std::ostream* const msgs, + const std::vector& u_scale, + const std::vector& f_scale, + const double function_tolerance, + const int max_num_steps, + const Args&... args) { const auto& x_ref = to_ref(value_of(x)); check_nonzero_size("algebra_solver_fp", "initial guess", x_ref); @@ -310,9 +314,10 @@ template * = nullptr, require_any_st_var* = nullptr> Eigen::Matrix algebra_solver_fp_impl( - const F& f, const T& x, std::ostream* const msgs, const std::vector& u_scale, - const std::vector& f_scale, const double function_tolerance, - const int max_num_steps, const Args&... args) { + const F& f, const T& x, std::ostream* const msgs, + const std::vector& u_scale, const std::vector& f_scale, + const double function_tolerance, const int max_num_steps, + const Args&... args) { const auto& x_val = to_ref(value_of(x)); auto arena_args_tuple = std::make_tuple(to_arena(args)...); auto args_vals_tuple = apply( @@ -435,9 +440,9 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_fp( const std::vector& dat_int, const std::vector& u_scale, const std::vector& f_scale, std::ostream* const msgs = nullptr, double function_tolerance = 1e-8, int max_num_steps = 200) { - return algebra_solver_fp_impl(algebra_solver_adapter(f), x, msgs, - u_scale, f_scale, function_tolerance, - max_num_steps, y, dat, dat_int); + return algebra_solver_fp_impl(algebra_solver_adapter(f), x, msgs, u_scale, + f_scale, function_tolerance, max_num_steps, y, + dat, dat_int); } } // namespace math diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index c900b8ffcc2..2004481fca2 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -56,10 +56,12 @@ namespace math { template * = nullptr, require_all_st_arithmetic* = nullptr> -Eigen::VectorXd algebra_solver_newton_impl( - const F& f, const T& x, std::ostream* const msgs, const double scaling_step_size, - const double function_tolerance, const int64_t max_num_steps, - const Args&... args) { +Eigen::VectorXd algebra_solver_newton_impl(const F& f, const T& x, + std::ostream* const msgs, + const double scaling_step_size, + const double function_tolerance, + const int64_t max_num_steps, + const Args&... args) { const auto& x_ref = to_ref(value_of(x)); check_nonzero_size("algebra_solver_newton", "initial guess", x_ref); @@ -139,9 +141,9 @@ template * = nullptr, require_any_st_var* = nullptr> Eigen::Matrix algebra_solver_newton_impl( - const F& f, const T& x, std::ostream* const msgs, const double scaling_step_size, - const double function_tolerance, const int64_t max_num_steps, - const T_Args&... args) { + const F& f, const T& x, std::ostream* const msgs, + const double scaling_step_size, const double function_tolerance, + const int64_t max_num_steps, const T_Args&... args) { const auto& x_ref = to_ref(value_of(x)); auto arena_args_tuple = std::make_tuple(to_arena(args)...); auto args_vals_tuple = apply( @@ -250,7 +252,8 @@ template , Eigen::Dynamic, 1> algebra_solver_newton( const F& f, const T1& x, const T2& y, const std::vector& dat, const std::vector& dat_int, std::ostream* const msgs = nullptr, - const double scaling_step_size = 1e-3, const double function_tolerance = 1e-6, + const double scaling_step_size = 1e-3, + const double function_tolerance = 1e-6, const long int max_num_steps = 200) { // NOLINT(runtime/int) return algebra_solver_newton_impl(algebra_solver_adapter(f), x, msgs, scaling_step_size, function_tolerance, diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 8b551948d15..78e8a663064 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -47,9 +47,9 @@ namespace math { template * = nullptr> Eigen::VectorXd algebra_solver_powell_call_solver_( - const F& f, const T& x, std::ostream* const msgs, const double relative_tolerance, - const double function_tolerance, const int64_t max_num_steps, - const Args&... args) { + const F& f, const T& x, std::ostream* const msgs, + const double relative_tolerance, const double function_tolerance, + const int64_t max_num_steps, const Args&... args) { // Construct the solver hybrj_functor_solver hfs(f); Eigen::HybridNonLinearSolver solver(hfs); @@ -125,10 +125,12 @@ Eigen::VectorXd algebra_solver_powell_call_solver_( template * = nullptr, require_all_st_arithmetic* = nullptr> -Eigen::VectorXd algebra_solver_powell_impl( - const F& f, const T& x, std::ostream* const msgs, const double relative_tolerance, - const double function_tolerance, const int64_t max_num_steps, - const Args&... args) { +Eigen::VectorXd algebra_solver_powell_impl(const F& f, const T& x, + std::ostream* const msgs, + const double relative_tolerance, + const double function_tolerance, + const int64_t max_num_steps, + const Args&... args) { const auto& x_val = to_ref(value_of(x)); // Curry the input function w.r.t. y @@ -217,9 +219,9 @@ template * = nullptr, require_any_st_var* = nullptr> Eigen::Matrix algebra_solver_powell_impl( - const F& f, const T& x, std::ostream* const msgs, const double relative_tolerance, - const double function_tolerance, const int64_t max_num_steps, - const T_Args&... args) { + const F& f, const T& x, std::ostream* const msgs, + const double relative_tolerance, const double function_tolerance, + const int64_t max_num_steps, const T_Args&... args) { const auto& x_val = to_ref(value_of(x)); auto arena_args_tuple = std::make_tuple(to_arena(args)...); auto args_vals_tuple = apply( @@ -390,7 +392,8 @@ template , Eigen::Dynamic, 1> algebra_solver( const F& f, const T1& x, const T2& y, const std::vector& dat, const std::vector& dat_int, std::ostream* msgs = nullptr, - const double relative_tolerance = 1e-10, const double function_tolerance = 1e-6, + const double relative_tolerance = 1e-10, + const double function_tolerance = 1e-6, const int64_t max_num_steps = 1e+3) { return algebra_solver_powell(f, x, y, dat, dat_int, msgs, relative_tolerance, function_tolerance, max_num_steps); diff --git a/stan/math/rev/functor/kinsol_data.hpp b/stan/math/rev/functor/kinsol_data.hpp index 3c10b0cd15c..6cb0c57bc5a 100644 --- a/stan/math/rev/functor/kinsol_data.hpp +++ b/stan/math/rev/functor/kinsol_data.hpp @@ -40,8 +40,8 @@ class kinsol_system_data { void* kinsol_memory_; /* Constructor */ - kinsol_system_data(const F1& f, const Eigen::VectorXd& x, std::ostream* const msgs, - const Args&... args) + kinsol_system_data(const F1& f, const Eigen::VectorXd& x, + std::ostream* const msgs, const Args&... args) : f_(f), x_(x), N_(x.size()), @@ -60,7 +60,8 @@ class kinsol_system_data { } /* Implements the user-defined function passed to KINSOL. */ - static int kinsol_f_system(const N_Vector x, const N_Vector f_eval, void* const user_data) { + static int kinsol_f_system(const N_Vector x, const N_Vector f_eval, + void* const user_data) { const system_data* explicit_system = static_cast(user_data); @@ -91,8 +92,9 @@ class kinsol_system_data { * https://computation.llnl.gov/sites/default/files/public/kin_guide-dev.pdf, * page 55. */ - static int kinsol_jacobian(const N_Vector x, const N_Vector f_eval, const SUNMatrix J, - void* const user_data, const N_Vector tmp1, const N_Vector tmp2) { + static int kinsol_jacobian(const N_Vector x, const N_Vector f_eval, + const SUNMatrix J, void* const user_data, + const N_Vector tmp1, const N_Vector tmp2) { const system_data* explicit_system = static_cast(user_data); diff --git a/stan/math/rev/functor/kinsol_solve.hpp b/stan/math/rev/functor/kinsol_solve.hpp index 53f04a37fc0..935658cd248 100644 --- a/stan/math/rev/functor/kinsol_solve.hpp +++ b/stan/math/rev/functor/kinsol_solve.hpp @@ -56,15 +56,14 @@ namespace math { * negative flag that is not due to hitting max_num_steps. */ template -Eigen::VectorXd kinsol_solve( - const F1& f, const Eigen::VectorXd& x, - const double scaling_step_tol, // = 1e-3 - const double function_tolerance, // = 1e-6 - const long int max_num_steps, // = 200 - const bool custom_jacobian, // = 1 - const int steps_eval_jacobian, // = 10 - const int global_line_search, // = KIN_LINESEARCH - std::ostream* const msgs, const Args&... args) { +Eigen::VectorXd kinsol_solve(const F1& f, const Eigen::VectorXd& x, + const double scaling_step_tol, // = 1e-3 + const double function_tolerance, // = 1e-6 + const long int max_num_steps, // = 200 + const bool custom_jacobian, // = 1 + const int steps_eval_jacobian, // = 10 + const int global_line_search, // = KIN_LINESEARCH + std::ostream* const msgs, const Args&... args) { int N = x.size(); typedef kinsol_system_data system_data; system_data kinsol_data(f, x, msgs, args...); From ff3a35d353afe8e6587d34e53a7dc7054f4f00a7 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Wed, 30 Jun 2021 00:19:16 -0400 Subject: [PATCH 63/88] Fix use of long int type. --- stan/math/rev/functor/kinsol_solve.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stan/math/rev/functor/kinsol_solve.hpp b/stan/math/rev/functor/kinsol_solve.hpp index 935658cd248..df65621e858 100644 --- a/stan/math/rev/functor/kinsol_solve.hpp +++ b/stan/math/rev/functor/kinsol_solve.hpp @@ -59,10 +59,10 @@ template Eigen::VectorXd kinsol_solve(const F1& f, const Eigen::VectorXd& x, const double scaling_step_tol, // = 1e-3 const double function_tolerance, // = 1e-6 - const long int max_num_steps, // = 200 + const int64_t max_num_steps, // = 200 const bool custom_jacobian, // = 1 const int steps_eval_jacobian, // = 10 - const int global_line_search, // = KIN_LINESEARCH + const int global_line_search, // = KIN_LINESEARCH std::ostream* const msgs, const Args&... args) { int N = x.size(); typedef kinsol_system_data system_data; From 49268560da94930563ac63d5cc12f0744beb6ec6 Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Wed, 30 Jun 2021 04:27:21 +0000 Subject: [PATCH 64/88] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- stan/math/rev/functor/kinsol_solve.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stan/math/rev/functor/kinsol_solve.hpp b/stan/math/rev/functor/kinsol_solve.hpp index df65621e858..8d03912adf6 100644 --- a/stan/math/rev/functor/kinsol_solve.hpp +++ b/stan/math/rev/functor/kinsol_solve.hpp @@ -62,7 +62,7 @@ Eigen::VectorXd kinsol_solve(const F1& f, const Eigen::VectorXd& x, const int64_t max_num_steps, // = 200 const bool custom_jacobian, // = 1 const int steps_eval_jacobian, // = 10 - const int global_line_search, // = KIN_LINESEARCH + const int global_line_search, // = KIN_LINESEARCH std::ostream* const msgs, const Args&... args) { int N = x.size(); typedef kinsol_system_data system_data; From d7d9356b0a3c3f02869d5b1637d0c20b142e0a0b Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Wed, 30 Jun 2021 16:50:51 -0400 Subject: [PATCH 65/88] Cleanup algebra solvers - Moves arithmetic only solver versions to prim/functor - Makes make_unsafe_chainable_ptr() for handling the partial pivot LU - Reduces the memory usage in the solvers - General cleanup --- stan/math/prim/functor.hpp | 2 + stan/math/prim/functor/algebra_solver_fp.hpp | 307 +++++++++++++++++ .../prim/functor/algebra_solver_powell.hpp | 277 +++++++++++++++ stan/math/rev/core/chainable_object.hpp | 56 +++ stan/math/rev/functor/algebra_solver_fp.hpp | 319 +----------------- .../rev/functor/algebra_solver_newton.hpp | 9 +- .../rev/functor/algebra_solver_powell.hpp | 271 +-------------- .../rev/functor/algebra_solver_fp_test.cpp | 2 +- 8 files changed, 675 insertions(+), 568 deletions(-) create mode 100644 stan/math/prim/functor/algebra_solver_fp.hpp create mode 100644 stan/math/prim/functor/algebra_solver_powell.hpp diff --git a/stan/math/prim/functor.hpp b/stan/math/prim/functor.hpp index 0ec5c343ff7..1c2632dfae2 100644 --- a/stan/math/prim/functor.hpp +++ b/stan/math/prim/functor.hpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/stan/math/prim/functor/algebra_solver_fp.hpp b/stan/math/prim/functor/algebra_solver_fp.hpp new file mode 100644 index 00000000000..a0b2273d631 --- /dev/null +++ b/stan/math/prim/functor/algebra_solver_fp.hpp @@ -0,0 +1,307 @@ +#ifndef STAN_MATH_PRIM_FUNCTOR_FP_SOLVER_HPP +#define STAN_MATH_PRIM_FUNCTOR_FP_SOLVER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace stan { +namespace math { + +/** + * KINSOL algebraic system data holder that handles + * construction & destruction of SUNDIALS data, as well as + * auxiliary data that will be used for functor evaluation. + * + * @tparam F functor type for system function. + * @tparam T_u type of scaling vector for unknowns. We allow + * it to be @c var because scaling could be parameter + * dependent. Internally these params are converted to data + * because scaling is applied. + * @tparam T_f type of scaling vector for residual. We allow + * it to be @c var because scaling could be parameter + * dependent. Internally these params are converted to data + * because scaling is applied. + * @tparam Args types of additional parameters to the equation system functor. + */ +template +struct KinsolFixedPointEnv { + /** RHS functor. */ + const F& f_; + /** system size */ + const size_t N_; + /** message stream */ + std::ostream* msgs_; + /** arguments and parameters */ + std::tuple args_tuple_; + /** KINSOL memory block */ + void* mem_; + /** NVECTOR for unknowns */ + N_Vector nv_x_; + /** NVECTOR for scaling u */ + N_Vector nv_u_scal_; + /** NVECTOR for scaling f */ + N_Vector nv_f_scal_; + + KinsolFixedPointEnv(const F& f, const Eigen::MatrixXd x, + const std::vector& u_scale, + const std::vector& f_scale, std::ostream* const msgs, + const Args&... args) + : f_(f), + N_(x.size()), + msgs_(msgs), + args_tuple_(args...), + mem_(KINCreate()), + nv_x_(N_VNew_Serial(N_)), + nv_u_scal_(N_VNew_Serial(N_)), + nv_f_scal_(N_VNew_Serial(N_)) { + for (int i = 0; i < N_; ++i) { + NV_Ith_S(nv_x_, i) = stan::math::value_of(x(i)); + } + for (int i = 0; i < N_; ++i) { + NV_Ith_S(nv_u_scal_, i) = stan::math::value_of(u_scale[i]); + } + for (int i = 0; i < N_; ++i) { + NV_Ith_S(nv_f_scal_, i) = stan::math::value_of(f_scale[i]); + } + } + + ~KinsolFixedPointEnv() { + N_VDestroy_Serial(nv_x_); + N_VDestroy_Serial(nv_u_scal_); + N_VDestroy_Serial(nv_f_scal_); + KINFree(&mem_); + } + + /** Implements the user-defined function passed to KINSOL. */ + static int kinsol_f_system(const N_Vector x, const N_Vector f, + void* const user_data) { + auto g = static_cast*>( + user_data); + Eigen::Map(N_VGetArrayPointer(f), g->N_) = apply( + [&x, &g](const auto&... args) { + return g->f_(Eigen::Map(NV_DATA_S(x), g->N_), g->msgs_, args...); + }, + g->args_tuple_); + return 0; + } +}; + +/** + * Private interface for solving fixed point problems using KINSOL. Users should + * call the KINSOL fixed point solver through `algebra_solver_fp` or + * `algebra_solver_fp_impl`. + * + * @tparam F type of the equation system functor f + * @tparam T_u type of scaling vector for unknowns. We allow + * it to be @c var because scaling could be parameter + * dependent. Internally these params are converted to data + * because scaling is applied. + * @tparam T_f type of scaling vector for residual. We allow + * it to be @c var because scaling could be parameter + * dependent. Internally these params are converted to data + * because scaling is applied. + * @param x initial point and final solution. + * @param env KINSOL solution environment + * @param f_tol Function tolerance + * @param max_num_steps max nb. of iterations. + */ +template * = nullptr> +Eigen::VectorXd kinsol_solve_fp(const F& f, const T& x, + const double function_tolerance, + const double max_num_steps, + const std::vector& u_scale, + const std::vector& f_scale, + std::ostream* const msgs, const Args&... args) { + KinsolFixedPointEnv env(f, x, u_scale, f_scale, msgs, + args...); + const int N = x.size(); + constexpr int default_anderson_depth = 4; + const int anderson_depth = std::min(N, default_anderson_depth); + + check_flag_sundials(KINSetNumMaxIters(env.mem_, max_num_steps), + "KINSetNumMaxIters"); + check_flag_sundials(KINSetMAA(env.mem_, anderson_depth), "KINSetMAA"); + check_flag_sundials(KINInit(env.mem_, &env.kinsol_f_system, env.nv_x_), + "KINInit"); + check_flag_sundials(KINSetFuncNormTol(env.mem_, function_tolerance), + "KINSetFuncNormTol"); + check_flag_sundials(KINSetUserData(env.mem_, static_cast(&env)), + "KINSetUserData"); + + check_flag_kinsol( + KINSol(env.mem_, env.nv_x_, KIN_FP, env.nv_u_scal_, env.nv_f_scal_), + max_num_steps); + + return Eigen::Map(N_VGetArrayPointer(env.nv_x_), N); +} + +/** + * Return a fixed pointer to the specified system of algebraic + * equations of form + * \[ + * x = F(x; theta) + * \] + * given an initial guess \(x\), and parameters \(theta\) and data. Use the + * KINSOL solver from the SUNDIALS suite. + * + * The user can also specify the scaling controls, the function + * tolerance, and the maximum number of steps. + * + * This function is overloaded to handle both constant and var-type parameters. + * This overload handles var parameters, and checks the input, calls the + * algebraic solver, and appropriately handles derivative propagation through + * the `reverse_pass_callback`. + * + * @tparam F type of equation system function. + * @tparam T type of initial guess vector. + * @tparam T_u type of scaling vector for unknowns. We allow + * it to be @c var because scaling could be parameter + * dependent. Internally these params are converted to data + * because scaling is applied. + * @tparam T_f type of scaling vector for residual. We allow + * it to be @c var because scaling could be parameter + * dependent. Internally these params are converted to data + * because scaling is applied. + * + * @param[in] f functor that evaluated the system of equations. + * @param[in] x vector of starting values. + * @param[in, out] msgs the print stream for warning messages. + * @param[in] u_scale diagonal scaling matrix elements \(Du\) + * such that \(Du x\) has all components roughly the same + * magnitude when \(x\) is close to a solution. + * (ref. KINSOL user guide chap.2 sec. "Scaling") + * @param[in] f_scale diagonal scaling matrix elements such + * that \(Df (x - f(x))\) has all components roughly the same + * magnitude when \(x\) is not too close to a solution. + * (ref. KINSOL user guide chap.2 sec. "Scaling") + * @param[in] function_tolerance Function-norm stopping tolerance. + * @param[in] max_num_steps maximum number of function evaluations. + * @param[in] args additional parameters to the equation system functor. + * @pre f returns finite values when passed any value of x and the given args. + * @throw std::invalid_argument if x has size zero. + * @throw std::invalid_argument if x has non-finite elements. + * @throw std::invalid_argument if scaled_step_size is strictly + * negative. + * @throw std::invalid_argument if function_tolerance is strictly + * negative. + * @throw std::invalid_argument if max_num_steps is not positive. + * @throw boost::math::evaluation_error (which is a subclass of + * std::runtime_error) if solver exceeds max_num_steps. + */ +template * = nullptr, + require_all_st_arithmetic* = nullptr> +Eigen::VectorXd algebra_solver_fp_impl(const F& f, const T& x, + std::ostream* const msgs, + const std::vector& u_scale, + const std::vector& f_scale, + const double function_tolerance, + const int max_num_steps, + const Args&... args) { + const auto& x_ref = to_ref(value_of(x)); + + check_nonzero_size("algebra_solver_fp", "initial guess", x_ref); + check_finite("algebra_solver_fp", "initial guess", x_ref); + check_nonnegative("algebra_solver_fp", "u_scale", u_scale); + check_nonnegative("algebra_solver_fp", "f_scale", f_scale); + check_nonnegative("algebra_solver_fp", "function_tolerance", + function_tolerance); + check_positive("algebra_solver_fp", "max_num_steps", max_num_steps); + check_matching_sizes("algebra_solver_fp", "the algebraic system's output", + value_of(f(x_ref, msgs, args...)), + "the vector of unknowns, x,", x_ref); + + return kinsol_solve_fp(f, x_ref, function_tolerance, max_num_steps, u_scale, + f_scale, msgs, args...); +} + +/** + * Return a fixed pointer to the specified system of algebraic + * equations of form + * + * x = F(x; theta) + * + * given an initial guess x, and parameters theta and data. Use the + * KINSOL solver from the SUNDIALS suite. + * + * The user can also specify the scaling controls, the function + * tolerance, and the maximum number of steps. + * + * Signature to maintain backward compatibility, will be removed + * in the future. + * + * @tparam F type of equation system function. + * @tparam T type of initial guess vector. The final solution + * type doesn't depend on initial guess type, + * but we allow initial guess to be either data or param. + * @tparam T_u type of scaling vector for unknowns. We allow + * it to be @c var because scaling could be parameter + * dependent. Internally these params are converted to data + * because scaling is applied. + * @tparam T_f type of scaling vector for residual. We allow + * it to be @c var because scaling could be parameter + * dependent. Internally these params are converted to data + * because scaling is applied. + * + * @param[in] f Functor that evaluated the system of equations. + * @param[in] x Vector of starting values. + * @param[in] y Parameter vector for the equation system. The function + * is overloaded to treat y as a vector of doubles or of a + * a template type T. + * @param[in] dat Continuous data vector for the equation system. + * @param[in] dat_int Integer data vector for the equation system. + * @param[in, out] msgs The print stream for warning messages. + * @param[in] u_scale diagonal scaling matrix elements Du + * such that Du*x has all components roughly the same + * magnitude when x is close to a solution. + * (ref. KINSOL user guide chap.2 sec. "Scaling") + * @param[in] f_scale diagonal scaling matrix elements such + * that Df*(x-f(x)) has all components roughly the same + * magnitude when x is not too close to a solution. + * (ref. KINSOL user guide chap.2 sec. "Scaling") + * @param[in] f_tol Function-norm stopping tolerance. + * @param[in] max_num_steps maximum number of function evaluations. + * @pre f returns finite values when passed any value of x and the given y, dat, + * and dat_int. + * @throw std::invalid_argument if x has size zero. + * @throw std::invalid_argument if x has non-finite elements. + * @throw std::invalid_argument if dat has non-finite elements. + * @throw std::invalid_argument if dat_int has non-finite elements. + * @throw std::invalid_argument if scaled_step_size is strictly + * negative. + * @throw std::invalid_argument if function_tolerance is strictly + * negative. + * @throw std::invalid_argument if max_num_steps is not positive. + * @throw boost::math::evaluation_error (which is a subclass of + * std::runtime_error) if solver exceeds max_num_steps. + */ +template * = nullptr, + require_eigen_vector_t* = nullptr> +Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_fp( + const F& f, const T1& x, const T2& y, const std::vector& dat, + const std::vector& dat_int, const std::vector& u_scale, + const std::vector& f_scale, std::ostream* const msgs = nullptr, + double function_tolerance = 1e-8, int max_num_steps = 200) { + return algebra_solver_fp_impl(algebra_solver_adapter(f), to_ref(x), msgs, u_scale, + f_scale, function_tolerance, max_num_steps, to_ref(y), + dat, dat_int); +} + +} // namespace math +} // namespace stan + +#endif diff --git a/stan/math/prim/functor/algebra_solver_powell.hpp b/stan/math/prim/functor/algebra_solver_powell.hpp new file mode 100644 index 00000000000..9fbcae4fe1e --- /dev/null +++ b/stan/math/prim/functor/algebra_solver_powell.hpp @@ -0,0 +1,277 @@ +#ifndef STAN_MATH_PRIM_FUNCTOR_ALGEBRA_SOLVER_POWELL_HPP +#define STAN_MATH_PRIM_FUNCTOR_ALGEBRA_SOLVER_POWELL_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace stan { +namespace math { + +/** + * Private interface for calling the Powell solver. Users should call the Powell + * solver through `algebra_solver_powell` or `algebra_solver_powell_impl`. + * + * @tparam F type of equation system function, curried with respect to inputs + * @tparam T type of elements in the x vector + * @tparam Args types of additional parameters to the equation system functor + * + * @param[in] f Functor that evaluates the system of equations, curried with + * respect to its input (i.e., "y") values. + * @param[in] x Vector of starting values (initial guess). + * @param[in, out] msgs the print stream for warning messages. + * @param[in] relative_tolerance determines the convergence criteria + * for the solution. + * @param[in] function_tolerance determines whether roots are acceptable. + * @param[in] max_num_steps maximum number of function evaluations. + * @return theta_dbl Double vector of solutions to the system of equations. + * @pre x has size greater than zero. + * @pre x has only finite elements. + * @pre f returns finite values when passed any value of x and the given args. + * @pre relative_tolerance is non-negative. + * @pre function_tolerance is non-negative. + * @pre max_num_steps is positive. + * @throw std::domain_error solver exceeds max_num_steps. + * @throw std::domain_error if the norm of the solution exceeds + * the function tolerance. + */ +template * = nullptr> +T& algebra_solver_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) { + // Construct the solver + hybrj_functor_solver hfs(f); + Eigen::HybridNonLinearSolver solver(hfs); + + // Compute theta_dbl + solver.parameters.xtol = relative_tolerance; + solver.parameters.maxfev = max_num_steps; + solver.solve(x); + + // Check if the max number of steps has been exceeded + if (solver.nfev >= max_num_steps) { + [&]() STAN_COLD_PATH { + throw_domain_error("algebra_solver", "maximum number of iterations", + max_num_steps, "(", ") was exceeded in the solve."); + }(); + } + + // Check solution is a root + double system_norm = f(x).stableNorm(); + if (system_norm > function_tolerance) { + [&]() STAN_COLD_PATH { + std::ostringstream message; + message << "the norm of the algebraic function is " << system_norm + << " but should be lower than the function " + << "tolerance:"; + throw_domain_error("algebra_solver", message.str().c_str(), + function_tolerance, "", + ". Consider decreasing the relative tolerance and " + "increasing max_num_steps."); + }(); + } + + return x; +} + +/** + * Return the solution to the specified system of algebraic + * equations given an initial guess, and parameters and data, + * which get passed into the algebraic system. + * Use Powell's dogleg solver. + * + * The user can also specify the relative tolerance + * (xtol in Eigen's code), the function tolerance, + * and the maximum number of steps (maxfev in Eigen's code). + * + * This function is overloaded to handle both constant and var-type parameters. + * This overload handles non-var parameters, and checks the input and calls the + * algebraic solver only. + * + * @tparam F type of equation system function + * @tparam T type of elements in the x vector + * @tparam Args types of additional parameters to the equation system functor + * + * @param[in] f Functor that evaluates the system of equations. + * @param[in] x Vector of starting values (initial guess). + * @param[in, out] msgs the print stream for warning messages. + * @param[in] relative_tolerance determines the convergence criteria + * for the solution. + * @param[in] function_tolerance determines whether roots are acceptable. + * @param[in] max_num_steps maximum number of function evaluations. + * @param[in] args additional parameters to the equation system functor. + * @return theta Vector of solutions to the system of equations. + * @pre f returns finite values when passed any value of x and the given args. + * @throw std::invalid_argument if x has size zero. + * @throw std::invalid_argument if x has non-finite elements. + * elements. + * @throw std::invalid_argument if relative_tolerance is strictly + * negative. + * @throw std::invalid_argument if function_tolerance is strictly + * negative. + * @throw std::invalid_argument if max_num_steps is not positive. + * @throw std::domain_error solver exceeds max_num_steps. + * @throw std::domain_error if the norm of the solution exceeds + * the function tolerance. + */ +template * = nullptr, + require_all_st_arithmetic* = nullptr> +Eigen::VectorXd algebra_solver_powell_impl(const F& f, const T& x, + std::ostream* const msgs, + const double relative_tolerance, + const double function_tolerance, + const int64_t max_num_steps, + const Args&... args) { + const auto& x_ref = to_ref(x); + auto x_val = to_ref(value_of(x_ref)); + + // Curry the input function w.r.t. y + auto f_wrt_x = [&f, msgs, &args...](const auto& x) { return f(x, msgs, args...); }; + + check_nonzero_size("algebra_solver_powell", "initial guess", x_val); + check_finite("algebra_solver_powell", "initial guess", x_val); + check_nonnegative("alegbra_solver_powell", "relative_tolerance", + relative_tolerance); + check_nonnegative("algebra_solver_powell", "function_tolerance", + function_tolerance); + check_positive("algebra_solver_powell", "max_num_steps", max_num_steps); + check_matching_sizes("algebra_solver", "the algebraic system's output", + f_wrt_x(x_ref), "the vector of unknowns, x,", x_ref); + + // Solve the system + return algebra_solver_powell_call_solver(f_wrt_x, x_val, msgs, + relative_tolerance, + function_tolerance, max_num_steps); +} + +/** + * Return the solution to the specified system of algebraic + * equations given an initial guess, and parameters and data, + * which get passed into the algebraic system. + * Use Powell's dogleg solver. + * + * The user can also specify the relative tolerance + * (xtol in Eigen's code), the function tolerance, + * and the maximum number of steps (maxfev in Eigen's code). + * + * Signature to maintain backward compatibility, will be removed + * in the future. + * + * @tparam F type of equation system function + * @tparam T1 type of elements in the x vector + * @tparam T2 type of elements in the y vector + * + * @param[in] f Functor that evaluates the system of equations. + * @param[in] x Vector of starting values (initial guess). + * @param[in] y parameter vector for the equation system. + * @param[in] dat continuous data vector for the equation system. + * @param[in] dat_int integer data vector for the equation system. + * @param[in, out] msgs the print stream for warning messages. + * @param[in] relative_tolerance determines the convergence criteria + * for the solution. + * @param[in] function_tolerance determines whether roots are acceptable. + * @param[in] max_num_steps maximum number of function evaluations. + * @return theta Vector of solutions to the system of equations. + * @throw std::invalid_argument if x has size zero. + * @throw std::invalid_argument if x has non-finite elements. + * @throw std::invalid_argument if y has non-finite elements. + * @throw std::invalid_argument if dat has non-finite elements. + * @throw std::invalid_argument if dat_int has non-finite + * elements. + * @throw std::invalid_argument if relative_tolerance is strictly + * negative. + * @throw std::invalid_argument if function_tolerance is strictly + * negative. + * @throw std::invalid_argument if max_num_steps is not positive. + * @throw std::domain_error solver exceeds max_num_steps. + * @throw std::domain_error if the norm of the solution exceeds + * the function tolerance. + */ +template * = nullptr> +Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_powell( + const F& f, const T1& x, const T2& y, const std::vector& dat, + const std::vector& dat_int, std::ostream* const msgs = nullptr, + const double relative_tolerance = 1e-10, + const double function_tolerance = 1e-6, + const int64_t max_num_steps = 1e+3) { + return algebra_solver_powell_impl(algebra_solver_adapter(f), x, msgs, + relative_tolerance, function_tolerance, + max_num_steps, y, dat, dat_int); +} + +/** + * Return the solution to the specified system of algebraic + * equations given an initial guess, and parameters and data, + * which get passed into the algebraic system. + * Use Powell's dogleg solver. + * + * The user can also specify the relative tolerance + * (xtol in Eigen's code), the function tolerance, + * and the maximum number of steps (maxfev in Eigen's code). + * + * Signature to maintain backward compatibility, will be removed + * in the future. + * + * @tparam F type of equation system function + * @tparam T1 type of elements in the x vector + * @tparam T2 type of elements in the y vector + * + * @param[in] f Functor that evaluates the system of equations. + * @param[in] x Vector of starting values (initial guess). + * @param[in] y parameter vector for the equation system. + * @param[in] dat continuous data vector for the equation system. + * @param[in] dat_int integer data vector for the equation system. + * @param[in, out] msgs the print stream for warning messages. + * @param[in] relative_tolerance determines the convergence criteria + * for the solution. + * @param[in] function_tolerance determines whether roots are acceptable. + * @param[in] max_num_steps maximum number of function evaluations. + * @return theta Vector of solutions to the system of equations. + * @pre f returns finite values when passed any value of x and the given y, dat, + * and dat_int. + * @throw std::invalid_argument if x has size zero. + * @throw std::invalid_argument if x has non-finite elements. + * @throw std::invalid_argument if y has non-finite elements. + * @throw std::invalid_argument if dat has non-finite elements. + * @throw std::invalid_argument if dat_int has non-finite + * elements. + * @throw std::invalid_argument if relative_tolerance is strictly + * negative. + * @throw std::invalid_argument if function_tolerance is strictly + * negative. + * @throw std::invalid_argument if max_num_steps is not positive. + * @throw boost::math::evaluation_error (which is a subclass of + * std::domain_error) if solver exceeds max_num_steps. + * @throw boost::math::evaluation_error (which is a subclass of + * std::domain_error) if the norm of the solution exceeds the + * function tolerance. + */ +template * = nullptr> +Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver( + const F& f, const T1& x, const T2& y, const std::vector& dat, + const std::vector& 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 algebra_solver_powell(f, x, y, dat, dat_int, msgs, relative_tolerance, + function_tolerance, max_num_steps); +} + +} // namespace math +} // namespace stan + +#endif diff --git a/stan/math/rev/core/chainable_object.hpp b/stan/math/rev/core/chainable_object.hpp index 50232476354..951370c5f55 100644 --- a/stan/math/rev/core/chainable_object.hpp +++ b/stan/math/rev/core/chainable_object.hpp @@ -62,6 +62,62 @@ auto make_chainable_ptr(T&& obj) { return &ptr->get(); } +/** + * `unsafe_chainable_object` hold another object and is useful for connecting + * the lifetime of a specific object to the chainable stack. This class + * differs from `chainable_object` in that this class does not evaluate + * expressions. + * + * `unsafe_chainable_object` objects should only be allocated with `new`. + * `unsafe_chainable_object` objects allocated on the stack will result + * in a double free (`obj_` will get destructed once when the + * `unsafe_chainable_object` leaves scope and once when the chainable + * stack memory is recovered). + * + * @tparam T type of object to hold + */ +template +class unsafe_chainable_object : public chainable_alloc { + private: + std::decay_t obj_; + + public: + /** + * Construct chainable object from another object + * + * @tparam S type of object to hold (must have the same plain type as `T`) + */ + template , plain_type_t>* = nullptr> + explicit unsafe_chainable_object(S&& obj) : obj_(std::forward(obj)) {} + + /** + * Return a reference to the underlying object + * + * @return reference to underlying object + */ + inline auto& get() noexcept { return obj_; } + inline const auto& get() const noexcept { return obj_; } +}; + +/** + * Store the given object in a `chainable_object` so it is destructed + * only when the chainable stack memory is recovered and return + * a pointer to the underlying object This function + * differs from `make_chainable_object` in that this class does not evaluate + * expressions. + * + * @tparam T type of object to hold + * @param obj object to hold + * @return pointer to object held in `chainable_object` + */ +template +auto make_unsafe_chainable_ptr(T&& obj) { + auto ptr = new unsafe_chainable_object(std::forward(obj)); + return &ptr->get(); +} + + } // namespace math } // namespace stan #endif diff --git a/stan/math/rev/functor/algebra_solver_fp.hpp b/stan/math/rev/functor/algebra_solver_fp.hpp index 471c37782b7..c04adada888 100644 --- a/stan/math/rev/functor/algebra_solver_fp.hpp +++ b/stan/math/rev/functor/algebra_solver_fp.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -23,216 +24,6 @@ namespace stan { namespace math { -/** - * KINSOL algebraic system data holder that handles - * construction & destruction of SUNDIALS data, as well as - * auxiliary data that will be used for functor evaluation. - * - * @tparam F functor type for system function. - * @tparam T_u type of scaling vector for unknowns. We allow - * it to be @c var because scaling could be parameter - * dependent. Internally these params are converted to data - * because scaling is applied. - * @tparam T_f type of scaling vector for residual. We allow - * it to be @c var because scaling could be parameter - * dependent. Internally these params are converted to data - * because scaling is applied. - * @tparam Args types of additional parameters to the equation system functor. - */ -template -struct KinsolFixedPointEnv { - /** RHS functor. */ - const F& f_; - /** system size */ - const size_t N_; - /** message stream */ - std::ostream* msgs_; - /** arguments and parameters */ - std::tuple args_tuple_; - /** KINSOL memory block */ - void* mem_; - /** NVECTOR for unknowns */ - N_Vector nv_x_; - /** NVECTOR for scaling u */ - N_Vector nv_u_scal_; - /** NVECTOR for scaling f */ - N_Vector nv_f_scal_; - - KinsolFixedPointEnv(const F& f, const Eigen::MatrixXd x, - const std::vector& u_scale, - const std::vector& f_scale, std::ostream* const msgs, - const Args&... args) - : f_(f), - N_(x.size()), - msgs_(msgs), - args_tuple_(args...), - mem_(KINCreate()), - nv_x_(N_VNew_Serial(N_)), - nv_u_scal_(N_VNew_Serial(N_)), - nv_f_scal_(N_VNew_Serial(N_)) { - for (int i = 0; i < N_; ++i) { - NV_Ith_S(nv_x_, i) = stan::math::value_of(x(i)); - NV_Ith_S(nv_u_scal_, i) = stan::math::value_of(u_scale[i]); - NV_Ith_S(nv_f_scal_, i) = stan::math::value_of(f_scale[i]); - } - } - - ~KinsolFixedPointEnv() { - N_VDestroy_Serial(nv_x_); - N_VDestroy_Serial(nv_u_scal_); - N_VDestroy_Serial(nv_f_scal_); - KINFree(&mem_); - } - - /** Implements the user-defined function passed to KINSOL. */ - static int kinsol_f_system(const N_Vector x, const N_Vector f, - void* const user_data) { - auto g = static_cast*>( - user_data); - Eigen::VectorXd x_eigen(Eigen::Map(NV_DATA_S(x), g->N_)); - Eigen::Map f_map(N_VGetArrayPointer(f), g->N_); - - f_map = apply( - [&](const auto&... args) { return g->f_(x_eigen, g->msgs_, args...); }, - g->args_tuple_); - return 0; - } -}; - -/** - * Private interface for solving fixed point problems using KINSOL. Users should - * call the KINSOL fixed point solver through `algebra_solver_fp` or - * `algebra_solver_fp_impl`. - * - * @tparam F type of the equation system functor f - * @tparam T_u type of scaling vector for unknowns. We allow - * it to be @c var because scaling could be parameter - * dependent. Internally these params are converted to data - * because scaling is applied. - * @tparam T_f type of scaling vector for residual. We allow - * it to be @c var because scaling could be parameter - * dependent. Internally these params are converted to data - * because scaling is applied. - * @param x initial point and final solution. - * @param env KINSOL solution environment - * @param f_tol Function tolerance - * @param max_num_steps max nb. of iterations. - */ -template * = nullptr> -Eigen::VectorXd kinsol_solve_fp(const F& f, const T& x, - const double function_tolerance, - const double max_num_steps, - const std::vector& u_scale, - const std::vector& f_scale, - std::ostream* const msgs, const Args&... args) { - KinsolFixedPointEnv env(f, x, u_scale, f_scale, msgs, - args...); - int N = env.N_; - void* mem = env.mem_; - const int default_anderson_depth = 4; - int anderson_depth = std::min(N, default_anderson_depth); - Eigen::VectorXd x_solution(N); - - check_flag_sundials(KINSetNumMaxIters(mem, max_num_steps), - "KINSetNumMaxIters"); - check_flag_sundials(KINSetMAA(mem, anderson_depth), "KINSetMAA"); - check_flag_sundials(KINInit(mem, &env.kinsol_f_system, env.nv_x_), "KINInit"); - check_flag_sundials(KINSetFuncNormTol(mem, function_tolerance), - "KINSetFuncNormTol"); - check_flag_sundials(KINSetUserData(mem, static_cast(&env)), - "KINSetUserData"); - - check_flag_kinsol( - KINSol(mem, env.nv_x_, KIN_FP, env.nv_u_scal_, env.nv_f_scal_), - max_num_steps); - - for (int i = 0; i < N; i++) - x_solution(i) = NV_Ith_S(env.nv_x_, i); - - return x_solution; -} - -/** - * Return a fixed pointer to the specified system of algebraic - * equations of form - * \[ - * x = F(x; theta) - * \] - * given an initial guess \(x\), and parameters \(theta\) and data. Use the - * KINSOL solver from the SUNDIALS suite. - * - * The user can also specify the scaling controls, the function - * tolerance, and the maximum number of steps. - * - * This function is overloaded to handle both constant and var-type parameters. - * This overload handles var parameters, and checks the input, calls the - * algebraic solver, and appropriately handles derivative propagation through - * the `reverse_pass_callback`. - * - * @tparam F type of equation system function. - * @tparam T type of initial guess vector. - * @tparam T_u type of scaling vector for unknowns. We allow - * it to be @c var because scaling could be parameter - * dependent. Internally these params are converted to data - * because scaling is applied. - * @tparam T_f type of scaling vector for residual. We allow - * it to be @c var because scaling could be parameter - * dependent. Internally these params are converted to data - * because scaling is applied. - * - * @param[in] f functor that evaluated the system of equations. - * @param[in] x vector of starting values. - * @param[in, out] msgs the print stream for warning messages. - * @param[in] u_scale diagonal scaling matrix elements \(Du\) - * such that \(Du x\) has all components roughly the same - * magnitude when \(x\) is close to a solution. - * (ref. KINSOL user guide chap.2 sec. "Scaling") - * @param[in] f_scale diagonal scaling matrix elements such - * that \(Df (x - f(x))\) has all components roughly the same - * magnitude when \(x\) is not too close to a solution. - * (ref. KINSOL user guide chap.2 sec. "Scaling") - * @param[in] function_tolerance Function-norm stopping tolerance. - * @param[in] max_num_steps maximum number of function evaluations. - * @param[in] args additional parameters to the equation system functor. - * @pre f returns finite values when passed any value of x and the given args. - * @throw std::invalid_argument if x has size zero. - * @throw std::invalid_argument if x has non-finite elements. - * @throw std::invalid_argument if scaled_step_size is strictly - * negative. - * @throw std::invalid_argument if function_tolerance is strictly - * negative. - * @throw std::invalid_argument if max_num_steps is not positive. - * @throw boost::math::evaluation_error (which is a subclass of - * std::runtime_error) if solver exceeds max_num_steps. - */ -template * = nullptr, - require_all_st_arithmetic* = nullptr> -Eigen::VectorXd algebra_solver_fp_impl(const F& f, const T& x, - std::ostream* const msgs, - const std::vector& u_scale, - const std::vector& f_scale, - const double function_tolerance, - const int max_num_steps, - const Args&... args) { - const auto& x_ref = to_ref(value_of(x)); - - check_nonzero_size("algebra_solver_fp", "initial guess", x_ref); - check_finite("algebra_solver_fp", "initial guess", x_ref); - check_nonnegative("algebra_solver_fp", "u_scale", u_scale); - check_nonnegative("algebra_solver_fp", "f_scale", f_scale); - check_nonnegative("algebra_solver_fp", "function_tolerance", - function_tolerance); - check_positive("algebra_solver_fp", "max_num_steps", max_num_steps); - check_matching_sizes("algebra_solver_fp", "the algebraic system's output", - value_of(f(x_ref, msgs, args...)), - "the vector of unknowns, x,", x_ref); - - return kinsol_solve_fp(f, x_ref, function_tolerance, max_num_steps, u_scale, - f_scale, msgs, args...); -} - /** * Return a fixed pointer to the specified system of algebraic * equations of form @@ -318,24 +109,24 @@ Eigen::Matrix algebra_solver_fp_impl( const std::vector& u_scale, const std::vector& f_scale, const double function_tolerance, const int max_num_steps, const Args&... args) { - const auto& x_val = to_ref(value_of(x)); auto arena_args_tuple = std::make_tuple(to_arena(args)...); + auto args_vals_tuple = apply( - [&](const auto&... args) { - return std::make_tuple(to_ref(value_of(args))...); - }, + [](const auto&... args) { return std::make_tuple(value_of(args)...); }, arena_args_tuple); - auto f_wrt_x = [&](const auto& x) { - return apply([&](const auto&... args) { return f(x, msgs, args...); }, - args_vals_tuple); + auto f_wrt_x = [&msgs, &f, &args_vals_tuple](const auto& x) { + return apply( + [&x, &msgs, &f](const auto&... args) { return f(x, msgs, args...); }, + args_vals_tuple); }; // FP solution Eigen::VectorXd theta_dbl = apply( - [&](const auto&... vals) { - return kinsol_solve_fp(f, x_val, function_tolerance, max_num_steps, - u_scale, f_scale, msgs, vals...); + [&f, function_tolerance, &u_scale, &f_scale, &msgs, + max_num_steps, x_val = value_of(x)](const auto&... vals) { + return kinsol_solve_fp(f, x_val, function_tolerance, + max_num_steps, u_scale, f_scale, msgs, vals...); }, args_vals_tuple); @@ -343,27 +134,23 @@ Eigen::Matrix algebra_solver_fp_impl( Eigen::VectorXd f_x; jacobian(f_wrt_x, theta_dbl, f_x, Jf_x); - int N = x.size(); - Jf_x = Eigen::MatrixXd::Identity(x.size(), x.size()) - Jf_x; using ret_type = Eigen::Matrix; - auto arena_Jf_x = to_arena(Jf_x); - arena_t ret = theta_dbl; - reverse_pass_callback([f, ret, arena_args_tuple, arena_Jf_x, msgs]() mutable { + auto Jf_xT_lu_ptr + = make_unsafe_chainable_ptr((Eigen::MatrixXd::Identity(x.size(), x.size()) - Jf_x).transpose().eval().partialPivLu()); // Lu + + reverse_pass_callback([f, ret, arena_args_tuple, Jf_xT_lu_ptr, msgs]() mutable { // Contract specificities with inverse Jacobian of f with respect to x. - Eigen::VectorXd ret_adj = ret.adj(); - Eigen::VectorXd eta = arena_Jf_x.transpose().lu().solve(ret_adj); + Eigen::VectorXd eta = Jf_xT_lu_ptr->solve(ret.adj().eval()); // Contract with Jacobian of f with respect to y using a nested reverse // autodiff pass. { nested_rev_autodiff rev; - - Eigen::VectorXd ret_val = ret.val(); auto x_nrad_ = apply( - [&](const auto&... args) { return eval(f(ret_val, msgs, args...)); }, + [&](const auto&... args) { return eval(f(ret.val(), msgs, args...)); }, arena_args_tuple); x_nrad_.adj() = eta; grad(); @@ -373,78 +160,6 @@ Eigen::Matrix algebra_solver_fp_impl( return ret_type(ret); } -/** - * Return a fixed pointer to the specified system of algebraic - * equations of form - * - * x = F(x; theta) - * - * given an initial guess x, and parameters theta and data. Use the - * KINSOL solver from the SUNDIALS suite. - * - * The user can also specify the scaling controls, the function - * tolerance, and the maximum number of steps. - * - * Signature to maintain backward compatibility, will be removed - * in the future. - * - * @tparam F type of equation system function. - * @tparam T type of initial guess vector. The final solution - * type doesn't depend on initial guess type, - * but we allow initial guess to be either data or param. - * @tparam T_u type of scaling vector for unknowns. We allow - * it to be @c var because scaling could be parameter - * dependent. Internally these params are converted to data - * because scaling is applied. - * @tparam T_f type of scaling vector for residual. We allow - * it to be @c var because scaling could be parameter - * dependent. Internally these params are converted to data - * because scaling is applied. - * - * @param[in] f Functor that evaluated the system of equations. - * @param[in] x Vector of starting values. - * @param[in] y Parameter vector for the equation system. The function - * is overloaded to treat y as a vector of doubles or of a - * a template type T. - * @param[in] dat Continuous data vector for the equation system. - * @param[in] dat_int Integer data vector for the equation system. - * @param[in, out] msgs The print stream for warning messages. - * @param[in] u_scale diagonal scaling matrix elements Du - * such that Du*x has all components roughly the same - * magnitude when x is close to a solution. - * (ref. KINSOL user guide chap.2 sec. "Scaling") - * @param[in] f_scale diagonal scaling matrix elements such - * that Df*(x-f(x)) has all components roughly the same - * magnitude when x is not too close to a solution. - * (ref. KINSOL user guide chap.2 sec. "Scaling") - * @param[in] f_tol Function-norm stopping tolerance. - * @param[in] max_num_steps maximum number of function evaluations. - * @pre f returns finite values when passed any value of x and the given y, dat, - * and dat_int. - * @throw std::invalid_argument if x has size zero. - * @throw std::invalid_argument if x has non-finite elements. - * @throw std::invalid_argument if dat has non-finite elements. - * @throw std::invalid_argument if dat_int has non-finite elements. - * @throw std::invalid_argument if scaled_step_size is strictly - * negative. - * @throw std::invalid_argument if function_tolerance is strictly - * negative. - * @throw std::invalid_argument if max_num_steps is not positive. - * @throw boost::math::evaluation_error (which is a subclass of - * std::runtime_error) if solver exceeds max_num_steps. - */ -template -Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_fp( - const F& f, const Eigen::Matrix& x, - const Eigen::Matrix& y, const std::vector& dat, - const std::vector& dat_int, const std::vector& u_scale, - const std::vector& f_scale, std::ostream* const msgs = nullptr, - double function_tolerance = 1e-8, int max_num_steps = 200) { - return algebra_solver_fp_impl(algebra_solver_adapter(f), x, msgs, u_scale, - f_scale, function_tolerance, max_num_steps, y, - dat, dat_int); -} - } // namespace math } // namespace stan diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index 2004481fca2..94d77bae89b 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -181,14 +181,13 @@ Eigen::Matrix algebra_solver_newton_impl( jacobian(f_wrt_x, theta_dbl, f_x, Jf_x); using ret_type = Eigen::Matrix; - auto arena_Jf_x = to_arena(Jf_x); - arena_t ret = theta_dbl; + auto Jf_xT_lu_ptr + = make_unsafe_chainable_ptr(Jf_x.transpose().partialPivLu()); // Lu - reverse_pass_callback([f, ret, arena_args_tuple, arena_Jf_x, msgs]() mutable { + reverse_pass_callback([f, ret, arena_args_tuple, Jf_xT_lu_ptr, msgs]() mutable { // Contract specificities with inverse Jacobian of f with respect to x. - Eigen::VectorXd ret_adj = ret.adj(); - Eigen::VectorXd eta = -arena_Jf_x.transpose().lu().solve(ret_adj); + Eigen::VectorXd eta = -Jf_xT_lu_ptr->solve(ret.adj().eval()); // Contract with Jacobian of f with respect to y using a nested reverse // autodiff pass. diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 78e8a663064..883360bdafb 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -17,141 +18,6 @@ namespace stan { namespace math { -/** - * Private interface for calling the Powell solver. Users should call the Powell - * solver through `algebra_solver_powell` or `algebra_solver_powell_impl`. - * - * @tparam F type of equation system function, curried with respect to inputs - * @tparam T type of elements in the x vector - * @tparam Args types of additional parameters to the equation system functor - * - * @param[in] f Functor that evaluates the system of equations, curried with - * respect to its input (i.e., "y") values. - * @param[in] x Vector of starting values (initial guess). - * @param[in, out] msgs the print stream for warning messages. - * @param[in] relative_tolerance determines the convergence criteria - * for the solution. - * @param[in] function_tolerance determines whether roots are acceptable. - * @param[in] max_num_steps maximum number of function evaluations. - * @return theta_dbl Double vector of solutions to the system of equations. - * @pre x has size greater than zero. - * @pre x has only finite elements. - * @pre f returns finite values when passed any value of x and the given args. - * @pre relative_tolerance is non-negative. - * @pre function_tolerance is non-negative. - * @pre max_num_steps is positive. - * @throw std::domain_error solver exceeds max_num_steps. - * @throw std::domain_error if the norm of the solution exceeds - * the function tolerance. - */ -template * = nullptr> -Eigen::VectorXd algebra_solver_powell_call_solver_( - const F& f, const T& x, std::ostream* const msgs, - const double relative_tolerance, const double function_tolerance, - const int64_t max_num_steps, const Args&... args) { - // Construct the solver - hybrj_functor_solver hfs(f); - Eigen::HybridNonLinearSolver solver(hfs); - - // Compute theta_dbl - Eigen::VectorXd theta_dbl = x; - solver.parameters.xtol = relative_tolerance; - solver.parameters.maxfev = max_num_steps; - solver.solve(theta_dbl); - - // Check if the max number of steps has been exceeded - if (solver.nfev >= max_num_steps) { - throw_domain_error("algebra_solver", "maximum number of iterations", - max_num_steps, "(", ") was exceeded in the solve."); - } - - // Check solution is a root - double system_norm = f(theta_dbl).stableNorm(); - if (system_norm > function_tolerance) { - std::ostringstream message; - message << "the norm of the algebraic function is " << system_norm - << " but should be lower than the function " - << "tolerance:"; - throw_domain_error("algebra_solver", message.str().c_str(), - function_tolerance, "", - ". Consider decreasing the relative tolerance and " - "increasing max_num_steps."); - } - - return theta_dbl; -} - -/** - * Return the solution to the specified system of algebraic - * equations given an initial guess, and parameters and data, - * which get passed into the algebraic system. - * Use Powell's dogleg solver. - * - * The user can also specify the relative tolerance - * (xtol in Eigen's code), the function tolerance, - * and the maximum number of steps (maxfev in Eigen's code). - * - * This function is overloaded to handle both constant and var-type parameters. - * This overload handles non-var parameters, and checks the input and calls the - * algebraic solver only. - * - * @tparam F type of equation system function - * @tparam T type of elements in the x vector - * @tparam Args types of additional parameters to the equation system functor - * - * @param[in] f Functor that evaluates the system of equations. - * @param[in] x Vector of starting values (initial guess). - * @param[in, out] msgs the print stream for warning messages. - * @param[in] relative_tolerance determines the convergence criteria - * for the solution. - * @param[in] function_tolerance determines whether roots are acceptable. - * @param[in] max_num_steps maximum number of function evaluations. - * @param[in] args additional parameters to the equation system functor. - * @return theta Vector of solutions to the system of equations. - * @pre f returns finite values when passed any value of x and the given args. - * @throw std::invalid_argument if x has size zero. - * @throw std::invalid_argument if x has non-finite elements. - * elements. - * @throw std::invalid_argument if relative_tolerance is strictly - * negative. - * @throw std::invalid_argument if function_tolerance is strictly - * negative. - * @throw std::invalid_argument if max_num_steps is not positive. - * @throw std::domain_error solver exceeds max_num_steps. - * @throw std::domain_error if the norm of the solution exceeds - * the function tolerance. - */ -template * = nullptr, - require_all_st_arithmetic* = nullptr> -Eigen::VectorXd algebra_solver_powell_impl(const F& f, const T& x, - std::ostream* const msgs, - const double relative_tolerance, - const double function_tolerance, - const int64_t max_num_steps, - const Args&... args) { - const auto& x_val = to_ref(value_of(x)); - - // Curry the input function w.r.t. y - auto f_wrt_x = [&](const auto& x) { return f(x, msgs, args...); }; - - check_nonzero_size("algebra_solver_powell", "initial guess", x_val); - check_finite("algebra_solver_powell", "initial guess", x_val); - check_nonnegative("alegbra_solver_powell", "relative_tolerance", - relative_tolerance); - check_nonnegative("algebra_solver_powell", "function_tolerance", - function_tolerance); - check_positive("algebra_solver_powell", "max_num_steps", max_num_steps); - check_matching_sizes("algebra_solver", "the algebraic system's output", - f_wrt_x(x), "the vector of unknowns, x,", x); - - // Solve the system - return algebra_solver_powell_call_solver_(f_wrt_x, x_val, msgs, - relative_tolerance, - function_tolerance, max_num_steps); -} - /** * Return the solution to the specified system of algebraic * equations given an initial guess, and parameters and data, @@ -222,7 +88,8 @@ Eigen::Matrix algebra_solver_powell_impl( const F& f, const T& x, std::ostream* const msgs, const double relative_tolerance, const double function_tolerance, const int64_t max_num_steps, const T_Args&... args) { - const auto& x_val = to_ref(value_of(x)); + const auto& x_ref = to_ref(x); + auto x_val = to_ref(value_of(x_ref)); auto arena_args_tuple = std::make_tuple(to_arena(args)...); auto args_vals_tuple = apply( [&](const auto&... args) { @@ -231,8 +98,8 @@ Eigen::Matrix algebra_solver_powell_impl( arena_args_tuple); // Curry the input function w.r.t. y - auto f_wrt_x = [&](const auto& x) { - return apply([&](const auto&... args) { return f(x, msgs, args...); }, + auto f_wrt_x = [&args_vals_tuple, &f, msgs](const auto& x) { + return apply([&x, &f, msgs](const auto&... args) { return f(x, msgs, args...); }, args_vals_tuple); }; @@ -244,29 +111,28 @@ Eigen::Matrix algebra_solver_powell_impl( function_tolerance); check_positive("algebra_solver_powell", "max_num_steps", max_num_steps); check_matching_sizes("algebra_solver", "the algebraic system's output", - f_wrt_x(x), "the vector of unknowns, x,", x); + f_wrt_x(x_ref), "the vector of unknowns, x,", x_ref); // Solve the system - Eigen::VectorXd theta_dbl = algebra_solver_powell_call_solver_( + algebra_solver_powell_call_solver( f_wrt_x, x_val, msgs, relative_tolerance, function_tolerance, max_num_steps); Eigen::MatrixXd Jf_x; Eigen::VectorXd f_x; - jacobian(f_wrt_x, theta_dbl, f_x, Jf_x); + jacobian(f_wrt_x, x_val, f_x, Jf_x); using ret_type = Eigen::Matrix; auto Jf_xT_lu_ptr - = make_chainable_ptr(Jf_x.transpose().fullPivHouseholderQr()); // Lu + = make_unsafe_chainable_ptr(Jf_x.transpose().partialPivLu()); // Lu - arena_t ret = theta_dbl; + arena_t ret = x_val; reverse_pass_callback([f, ret, arena_args_tuple, Jf_xT_lu_ptr, msgs]() mutable { // Contract specificities with inverse Jacobian of f with respect to x. - Eigen::VectorXd ret_adj = ret.adj(); - Eigen::VectorXd eta = -Jf_xT_lu_ptr->solve(ret_adj); + Eigen::VectorXd eta = -Jf_xT_lu_ptr->solve(ret.adj().eval()); // Contract with Jacobian of f with respect to y using a nested reverse // autodiff pass. @@ -284,121 +150,6 @@ Eigen::Matrix algebra_solver_powell_impl( return ret_type(ret); } -/** - * Return the solution to the specified system of algebraic - * equations given an initial guess, and parameters and data, - * which get passed into the algebraic system. - * Use Powell's dogleg solver. - * - * The user can also specify the relative tolerance - * (xtol in Eigen's code), the function tolerance, - * and the maximum number of steps (maxfev in Eigen's code). - * - * Signature to maintain backward compatibility, will be removed - * in the future. - * - * @tparam F type of equation system function - * @tparam T1 type of elements in the x vector - * @tparam T2 type of elements in the y vector - * - * @param[in] f Functor that evaluates the system of equations. - * @param[in] x Vector of starting values (initial guess). - * @param[in] y parameter vector for the equation system. - * @param[in] dat continuous data vector for the equation system. - * @param[in] dat_int integer data vector for the equation system. - * @param[in, out] msgs the print stream for warning messages. - * @param[in] relative_tolerance determines the convergence criteria - * for the solution. - * @param[in] function_tolerance determines whether roots are acceptable. - * @param[in] max_num_steps maximum number of function evaluations. - * @return theta Vector of solutions to the system of equations. - * @throw std::invalid_argument if x has size zero. - * @throw std::invalid_argument if x has non-finite elements. - * @throw std::invalid_argument if y has non-finite elements. - * @throw std::invalid_argument if dat has non-finite elements. - * @throw std::invalid_argument if dat_int has non-finite - * elements. - * @throw std::invalid_argument if relative_tolerance is strictly - * negative. - * @throw std::invalid_argument if function_tolerance is strictly - * negative. - * @throw std::invalid_argument if max_num_steps is not positive. - * @throw std::domain_error solver exceeds max_num_steps. - * @throw std::domain_error if the norm of the solution exceeds - * the function tolerance. - */ -template * = nullptr> -Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_powell( - const F& f, const T1& x, const T2& y, const std::vector& dat, - const std::vector& dat_int, std::ostream* const msgs = nullptr, - const double relative_tolerance = 1e-10, - const double function_tolerance = 1e-6, - const int64_t max_num_steps = 1e+3) { - return algebra_solver_powell_impl(algebra_solver_adapter(f), x, msgs, - relative_tolerance, function_tolerance, - max_num_steps, y, dat, dat_int); -} - -/** - * Return the solution to the specified system of algebraic - * equations given an initial guess, and parameters and data, - * which get passed into the algebraic system. - * Use Powell's dogleg solver. - * - * The user can also specify the relative tolerance - * (xtol in Eigen's code), the function tolerance, - * and the maximum number of steps (maxfev in Eigen's code). - * - * Signature to maintain backward compatibility, will be removed - * in the future. - * - * @tparam F type of equation system function - * @tparam T1 type of elements in the x vector - * @tparam T2 type of elements in the y vector - * - * @param[in] f Functor that evaluates the system of equations. - * @param[in] x Vector of starting values (initial guess). - * @param[in] y parameter vector for the equation system. - * @param[in] dat continuous data vector for the equation system. - * @param[in] dat_int integer data vector for the equation system. - * @param[in, out] msgs the print stream for warning messages. - * @param[in] relative_tolerance determines the convergence criteria - * for the solution. - * @param[in] function_tolerance determines whether roots are acceptable. - * @param[in] max_num_steps maximum number of function evaluations. - * @return theta Vector of solutions to the system of equations. - * @pre f returns finite values when passed any value of x and the given y, dat, - * and dat_int. - * @throw std::invalid_argument if x has size zero. - * @throw std::invalid_argument if x has non-finite elements. - * @throw std::invalid_argument if y has non-finite elements. - * @throw std::invalid_argument if dat has non-finite elements. - * @throw std::invalid_argument if dat_int has non-finite - * elements. - * @throw std::invalid_argument if relative_tolerance is strictly - * negative. - * @throw std::invalid_argument if function_tolerance is strictly - * negative. - * @throw std::invalid_argument if max_num_steps is not positive. - * @throw boost::math::evaluation_error (which is a subclass of - * std::domain_error) if solver exceeds max_num_steps. - * @throw boost::math::evaluation_error (which is a subclass of - * std::domain_error) if the norm of the solution exceeds the - * function tolerance. - */ -template * = nullptr> -Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver( - const F& f, const T1& x, const T2& y, const std::vector& dat, - const std::vector& 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 algebra_solver_powell(f, x, y, dat, dat_int, msgs, relative_tolerance, - function_tolerance, max_num_steps); -} - } // namespace math } // namespace stan diff --git a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp index 9271ebc9889..b4c9cd3c142 100644 --- a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp @@ -1,7 +1,7 @@ #include -#include #include #include +#include #include #include #include From df179ad0dc8b9a12c706c9029523dc08f3188b42 Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Sun, 11 Jul 2021 22:00:48 +0000 Subject: [PATCH 66/88] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- stan/math/prim/functor/algebra_solver_fp.hpp | 9 ++-- .../prim/functor/algebra_solver_powell.hpp | 36 ++++++++------- stan/math/rev/core/chainable_object.hpp | 1 - stan/math/rev/functor/algebra_solver_fp.hpp | 46 +++++++++++-------- .../rev/functor/algebra_solver_newton.hpp | 3 +- .../rev/functor/algebra_solver_powell.hpp | 10 ++-- 6 files changed, 57 insertions(+), 48 deletions(-) diff --git a/stan/math/prim/functor/algebra_solver_fp.hpp b/stan/math/prim/functor/algebra_solver_fp.hpp index a0b2273d631..1cedcf5bec3 100644 --- a/stan/math/prim/functor/algebra_solver_fp.hpp +++ b/stan/math/prim/functor/algebra_solver_fp.hpp @@ -91,7 +91,8 @@ struct KinsolFixedPointEnv { user_data); Eigen::Map(N_VGetArrayPointer(f), g->N_) = apply( [&x, &g](const auto&... args) { - return g->f_(Eigen::Map(NV_DATA_S(x), g->N_), g->msgs_, args...); + return g->f_(Eigen::Map(NV_DATA_S(x), g->N_), + g->msgs_, args...); }, g->args_tuple_); return 0; @@ -296,9 +297,9 @@ Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_fp( const std::vector& dat_int, const std::vector& u_scale, const std::vector& f_scale, std::ostream* const msgs = nullptr, double function_tolerance = 1e-8, int max_num_steps = 200) { - return algebra_solver_fp_impl(algebra_solver_adapter(f), to_ref(x), msgs, u_scale, - f_scale, function_tolerance, max_num_steps, to_ref(y), - dat, dat_int); + return algebra_solver_fp_impl(algebra_solver_adapter(f), to_ref(x), msgs, + u_scale, f_scale, function_tolerance, + max_num_steps, to_ref(y), dat, dat_int); } } // namespace math diff --git a/stan/math/prim/functor/algebra_solver_powell.hpp b/stan/math/prim/functor/algebra_solver_powell.hpp index 9fbcae4fe1e..0ff64826ec1 100644 --- a/stan/math/prim/functor/algebra_solver_powell.hpp +++ b/stan/math/prim/functor/algebra_solver_powell.hpp @@ -46,10 +46,11 @@ namespace math { */ template * = nullptr> -T& algebra_solver_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) { +T& algebra_solver_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) { // Construct the solver hybrj_functor_solver hfs(f); Eigen::HybridNonLinearSolver solver(hfs); @@ -62,8 +63,8 @@ T& algebra_solver_powell_call_solver( // Check if the max number of steps has been exceeded if (solver.nfev >= max_num_steps) { [&]() STAN_COLD_PATH { - throw_domain_error("algebra_solver", "maximum number of iterations", - max_num_steps, "(", ") was exceeded in the solve."); + throw_domain_error("algebra_solver", "maximum number of iterations", + max_num_steps, "(", ") was exceeded in the solve."); }(); } @@ -71,14 +72,14 @@ T& algebra_solver_powell_call_solver( double system_norm = f(x).stableNorm(); if (system_norm > function_tolerance) { [&]() STAN_COLD_PATH { - std::ostringstream message; - message << "the norm of the algebraic function is " << system_norm - << " but should be lower than the function " - << "tolerance:"; - throw_domain_error("algebra_solver", message.str().c_str(), - function_tolerance, "", - ". Consider decreasing the relative tolerance and " - "increasing max_num_steps."); + std::ostringstream message; + message << "the norm of the algebraic function is " << system_norm + << " but should be lower than the function " + << "tolerance:"; + throw_domain_error("algebra_solver", message.str().c_str(), + function_tolerance, "", + ". Consider decreasing the relative tolerance and " + "increasing max_num_steps."); }(); } @@ -138,7 +139,8 @@ Eigen::VectorXd algebra_solver_powell_impl(const F& f, const T& x, auto x_val = to_ref(value_of(x_ref)); // Curry the input function w.r.t. y - auto f_wrt_x = [&f, msgs, &args...](const auto& x) { return f(x, msgs, args...); }; + auto f_wrt_x + = [&f, msgs, &args...](const auto& x) { return f(x, msgs, args...); }; check_nonzero_size("algebra_solver_powell", "initial guess", x_val); check_finite("algebra_solver_powell", "initial guess", x_val); @@ -152,8 +154,8 @@ Eigen::VectorXd algebra_solver_powell_impl(const F& f, const T& x, // Solve the system return algebra_solver_powell_call_solver(f_wrt_x, x_val, msgs, - relative_tolerance, - function_tolerance, max_num_steps); + relative_tolerance, + function_tolerance, max_num_steps); } /** diff --git a/stan/math/rev/core/chainable_object.hpp b/stan/math/rev/core/chainable_object.hpp index 951370c5f55..0631d88354c 100644 --- a/stan/math/rev/core/chainable_object.hpp +++ b/stan/math/rev/core/chainable_object.hpp @@ -117,7 +117,6 @@ auto make_unsafe_chainable_ptr(T&& obj) { return &ptr->get(); } - } // namespace math } // namespace stan #endif diff --git a/stan/math/rev/functor/algebra_solver_fp.hpp b/stan/math/rev/functor/algebra_solver_fp.hpp index c04adada888..d5f7926af6a 100644 --- a/stan/math/rev/functor/algebra_solver_fp.hpp +++ b/stan/math/rev/functor/algebra_solver_fp.hpp @@ -123,10 +123,10 @@ Eigen::Matrix algebra_solver_fp_impl( // FP solution Eigen::VectorXd theta_dbl = apply( - [&f, function_tolerance, &u_scale, &f_scale, &msgs, - max_num_steps, x_val = value_of(x)](const auto&... vals) { - return kinsol_solve_fp(f, x_val, function_tolerance, - max_num_steps, u_scale, f_scale, msgs, vals...); + [&f, function_tolerance, &u_scale, &f_scale, &msgs, max_num_steps, + x_val = value_of(x)](const auto&... vals) { + return kinsol_solve_fp(f, x_val, function_tolerance, max_num_steps, + u_scale, f_scale, msgs, vals...); }, args_vals_tuple); @@ -138,24 +138,30 @@ Eigen::Matrix algebra_solver_fp_impl( using ret_type = Eigen::Matrix; arena_t ret = theta_dbl; - auto Jf_xT_lu_ptr - = make_unsafe_chainable_ptr((Eigen::MatrixXd::Identity(x.size(), x.size()) - Jf_x).transpose().eval().partialPivLu()); // Lu + auto Jf_xT_lu_ptr = make_unsafe_chainable_ptr( + (Eigen::MatrixXd::Identity(x.size(), x.size()) - Jf_x) + .transpose() + .eval() + .partialPivLu()); // Lu - reverse_pass_callback([f, ret, arena_args_tuple, Jf_xT_lu_ptr, msgs]() mutable { - // Contract specificities with inverse Jacobian of f with respect to x. - Eigen::VectorXd eta = Jf_xT_lu_ptr->solve(ret.adj().eval()); + reverse_pass_callback( + [f, ret, arena_args_tuple, Jf_xT_lu_ptr, msgs]() mutable { + // Contract specificities with inverse Jacobian of f with respect to x. + Eigen::VectorXd eta = Jf_xT_lu_ptr->solve(ret.adj().eval()); - // Contract with Jacobian of f with respect to y using a nested reverse - // autodiff pass. - { - nested_rev_autodiff rev; - auto x_nrad_ = apply( - [&](const auto&... args) { return eval(f(ret.val(), msgs, args...)); }, - arena_args_tuple); - x_nrad_.adj() = eta; - grad(); - } - }); + // Contract with Jacobian of f with respect to y using a nested reverse + // autodiff pass. + { + nested_rev_autodiff rev; + auto x_nrad_ = apply( + [&](const auto&... args) { + return eval(f(ret.val(), msgs, args...)); + }, + arena_args_tuple); + x_nrad_.adj() = eta; + grad(); + } + }); return ret_type(ret); } diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index 94d77bae89b..c5020599cf9 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -185,7 +185,8 @@ Eigen::Matrix algebra_solver_newton_impl( auto Jf_xT_lu_ptr = make_unsafe_chainable_ptr(Jf_x.transpose().partialPivLu()); // Lu - reverse_pass_callback([f, ret, arena_args_tuple, Jf_xT_lu_ptr, msgs]() mutable { + reverse_pass_callback([f, ret, arena_args_tuple, Jf_xT_lu_ptr, + msgs]() mutable { // Contract specificities with inverse Jacobian of f with respect to x. Eigen::VectorXd eta = -Jf_xT_lu_ptr->solve(ret.adj().eval()); diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 883360bdafb..75b1027b5d7 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -99,8 +99,9 @@ Eigen::Matrix algebra_solver_powell_impl( // Curry the input function w.r.t. y auto f_wrt_x = [&args_vals_tuple, &f, msgs](const auto& x) { - return apply([&x, &f, msgs](const auto&... args) { return f(x, msgs, args...); }, - args_vals_tuple); + return apply( + [&x, &f, msgs](const auto&... args) { return f(x, msgs, args...); }, + args_vals_tuple); }; check_nonzero_size("algebra_solver_powell", "initial guess", x_val); @@ -114,9 +115,8 @@ Eigen::Matrix algebra_solver_powell_impl( f_wrt_x(x_ref), "the vector of unknowns, x,", x_ref); // Solve the system - algebra_solver_powell_call_solver( - f_wrt_x, x_val, msgs, relative_tolerance, function_tolerance, - max_num_steps); + algebra_solver_powell_call_solver(f_wrt_x, x_val, msgs, relative_tolerance, + function_tolerance, max_num_steps); Eigen::MatrixXd Jf_x; Eigen::VectorXd f_x; From 8d864cbbf6743fe879942fc40bf9dbb858f123d9 Mon Sep 17 00:00:00 2001 From: Ben Date: Sun, 11 Jul 2021 15:56:51 -0700 Subject: [PATCH 67/88] Updated newton test --- stan/math/prim/functor/algebra_solver_fp.hpp | 4 +- .../prim/functor/algebra_solver_powell.hpp | 16 +- stan/math/rev/functor/algebra_solver_fp.hpp | 7 +- .../rev/functor/algebra_solver_newton.hpp | 10 +- .../rev/functor/algebra_solver_powell.hpp | 6 +- stan/math/rev/functor/algebra_system.hpp | 13 +- stan/math/rev/functor/kinsol_data.hpp | 1 + .../functor/algebra_solver_newton_test.cpp | 237 ++++++++++ .../math/rev/functor/algebra_solver_test.cpp | 421 ++---------------- .../math/rev/functor/util_algebra_solver.hpp | 130 +++++- 10 files changed, 414 insertions(+), 431 deletions(-) create mode 100644 test/unit/math/rev/functor/algebra_solver_newton_test.cpp diff --git a/stan/math/prim/functor/algebra_solver_fp.hpp b/stan/math/prim/functor/algebra_solver_fp.hpp index a0b2273d631..f721e523942 100644 --- a/stan/math/prim/functor/algebra_solver_fp.hpp +++ b/stan/math/prim/functor/algebra_solver_fp.hpp @@ -99,9 +99,7 @@ struct KinsolFixedPointEnv { }; /** - * Private interface for solving fixed point problems using KINSOL. Users should - * call the KINSOL fixed point solver through `algebra_solver_fp` or - * `algebra_solver_fp_impl`. + * Solve algebraic equations using KINSOL fixed point solver * * @tparam F type of the equation system functor f * @tparam T_u type of scaling vector for unknowns. We allow diff --git a/stan/math/prim/functor/algebra_solver_powell.hpp b/stan/math/prim/functor/algebra_solver_powell.hpp index 9fbcae4fe1e..5352c1bcd82 100644 --- a/stan/math/prim/functor/algebra_solver_powell.hpp +++ b/stan/math/prim/functor/algebra_solver_powell.hpp @@ -18,15 +18,13 @@ namespace stan { namespace math { /** - * Private interface for calling the Powell solver. Users should call the Powell - * solver through `algebra_solver_powell` or `algebra_solver_powell_impl`. + * Solve algebraic equations using Powell solver * - * @tparam F type of equation system function, curried with respect to inputs + * @tparam F type of equation system function * @tparam T type of elements in the x vector * @tparam Args types of additional parameters to the equation system functor * - * @param[in] f Functor that evaluates the system of equations, curried with - * respect to its input (i.e., "y") values. + * @param[in] f Functor that evaluates the system of equations * @param[in] x Vector of starting values (initial guess). * @param[in, out] msgs the print stream for warning messages. * @param[in] relative_tolerance determines the convergence criteria @@ -95,9 +93,7 @@ T& algebra_solver_powell_call_solver( * (xtol in Eigen's code), the function tolerance, * and the maximum number of steps (maxfev in Eigen's code). * - * This function is overloaded to handle both constant and var-type parameters. - * This overload handles non-var parameters, and checks the input and calls the - * algebraic solver only. + * This overload handles non-autodiff parameters. * * @tparam F type of equation system function * @tparam T type of elements in the x vector @@ -137,7 +133,6 @@ Eigen::VectorXd algebra_solver_powell_impl(const F& f, const T& x, const auto& x_ref = to_ref(x); auto x_val = to_ref(value_of(x_ref)); - // Curry the input function w.r.t. y auto f_wrt_x = [&f, msgs, &args...](const auto& x) { return f(x, msgs, args...); }; check_nonzero_size("algebra_solver_powell", "initial guess", x_val); @@ -166,9 +161,6 @@ Eigen::VectorXd algebra_solver_powell_impl(const F& f, const T& x, * (xtol in Eigen's code), the function tolerance, * and the maximum number of steps (maxfev in Eigen's code). * - * Signature to maintain backward compatibility, will be removed - * in the future. - * * @tparam F type of equation system function * @tparam T1 type of elements in the x vector * @tparam T2 type of elements in the y vector diff --git a/stan/math/rev/functor/algebra_solver_fp.hpp b/stan/math/rev/functor/algebra_solver_fp.hpp index c04adada888..702056c86f5 100644 --- a/stan/math/rev/functor/algebra_solver_fp.hpp +++ b/stan/math/rev/functor/algebra_solver_fp.hpp @@ -36,10 +36,7 @@ namespace math { * The user can also specify the scaling controls, the function * tolerance, and the maximum number of steps. * - * This function is overloaded to handle both constant and var-type parameters. - * This overload handles var parameters, and checks the input, calls the - * algebraic solver, and appropriately handles derivative propagation through - * the `reverse_pass_callback`. + * This overload handles var parameters. * * The Jacobian \(J_{xy}\) (i.e., Jacobian of unknown \(x\) w.r.t. the parameter * \(y\)) is calculated given the solution as follows. Since @@ -139,7 +136,7 @@ Eigen::Matrix algebra_solver_fp_impl( arena_t ret = theta_dbl; auto Jf_xT_lu_ptr - = make_unsafe_chainable_ptr((Eigen::MatrixXd::Identity(x.size(), x.size()) - Jf_x).transpose().eval().partialPivLu()); // Lu + = make_unsafe_chainable_ptr((Eigen::MatrixXd::Identity(x.size(), x.size()) - Jf_x).transpose().eval().partialPivLu()); reverse_pass_callback([f, ret, arena_args_tuple, Jf_xT_lu_ptr, msgs]() mutable { // Contract specificities with inverse Jacobian of f with respect to x. diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index 94d77bae89b..ef14268f8db 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -24,9 +24,7 @@ namespace math { * The user can also specify the scaled step size, the function * tolerance, and the maximum number of steps. * - * This function is overloaded to handle both constant and var-type parameters. - * This overload handles non-var parameters, and checks the input and calls the - * algebraic solver only. + * This overload handles non-autodiff parameters. * * @tparam F type of equation system function. * @tparam T type of initial guess vector. @@ -85,10 +83,7 @@ Eigen::VectorXd algebra_solver_newton_impl(const F& f, const T& x, * The user can also specify the scaled step size, the function * tolerance, and the maximum number of steps. * - * This function is overloaded to handle both constant and var-type parameters. - * This overload handles var parameters, and checks the input, calls the - * algebraic solver, and appropriately handles derivative propagation through - * the `reverse_pass_callback`. + * This overload handles var parameters. * * The Jacobian \(J_{xy}\) (i.e., Jacobian of unknown \(x\) w.r.t. the parameter * \(y\)) is calculated given the solution as follows. Since @@ -169,7 +164,6 @@ Eigen::Matrix algebra_solver_newton_impl( }, args_vals_tuple); - // Evaluate and store the Jacobian. auto f_wrt_x = [&](const auto& x) { return apply([&](const auto&... args) { return f(x, msgs, args...); }, args_vals_tuple); diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 883360bdafb..fc3c7bb45a2 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -28,10 +28,7 @@ namespace math { * (xtol in Eigen's code), the function tolerance, * and the maximum number of steps (maxfev in Eigen's code). * - * This function is overloaded to handle both constant and var-type parameters. - * This overload handles var parameters, and checks the input, calls the - * algebraic solver, and appropriately handles derivative propagation through - * the `reverse_pass_callback`. + * This overload handles var parameters. * * The Jacobian \(J_{xy}\) (i.e., Jacobian of unknown \(x\) w.r.t. the parameter * \(y\)) is calculated given the solution as follows. Since @@ -97,7 +94,6 @@ Eigen::Matrix algebra_solver_powell_impl( }, arena_args_tuple); - // Curry the input function w.r.t. y auto f_wrt_x = [&args_vals_tuple, &f, msgs](const auto& x) { return apply([&x, &f, msgs](const auto&... args) { return f(x, msgs, args...); }, args_vals_tuple); diff --git a/stan/math/rev/functor/algebra_system.hpp b/stan/math/rev/functor/algebra_system.hpp index c0d67f54f8e..e27e56b4228 100644 --- a/stan/math/rev/functor/algebra_system.hpp +++ b/stan/math/rev/functor/algebra_system.hpp @@ -33,10 +33,7 @@ struct nlo_functor { }; /** - * A functor with the required operators to call Eigen's - * algebraic solver. - * It is also used in the vari classes of the algebraic solvers - * to compute the requisite sensitivities. + * A functor with the required operators to call Eigen's algebraic solver. * * @tparam S wrapper around the algebraic system functor. Has the * signature required for jacobian (i.e takes only one argument). @@ -56,8 +53,8 @@ struct hybrj_functor_solver : nlo_functor { /** * Computes the value the algebraic function, f, when pluging in the - * independent variables, and the Jacobian w.r.t unknowns. Required - * by Eigen. + * independent variables, and the Jacobian w.r.t unknowns. + * * @param [in] iv independent variables * @param [in, out] fvec value of algebraic function when plugging in iv. */ @@ -67,8 +64,8 @@ struct hybrj_functor_solver : nlo_functor { } /** - * Assign the Jacobian to fjac (signature required by Eigen). Required - * by Eigen. + * Assign the Jacobian to fjac. + * * @param [in] iv independent variables. * @param [in, out] fjac matrix container for jacobian */ diff --git a/stan/math/rev/functor/kinsol_data.hpp b/stan/math/rev/functor/kinsol_data.hpp index 6cb0c57bc5a..642b6a3b5bd 100644 --- a/stan/math/rev/functor/kinsol_data.hpp +++ b/stan/math/rev/functor/kinsol_data.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include diff --git a/test/unit/math/rev/functor/algebra_solver_newton_test.cpp b/test/unit/math/rev/functor/algebra_solver_newton_test.cpp new file mode 100644 index 00000000000..7925bdb518e --- /dev/null +++ b/test/unit/math/rev/functor/algebra_solver_newton_test.cpp @@ -0,0 +1,237 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +////////////////////////////////////////////////////////////////////////// +// Tests for newton solver. + +TEST_F(algebra_solver_simple_eq_test, newton_dbl) { + bool is_newton = true; + Eigen::VectorXd theta = simple_eq_test(simple_eq_functor(), y_dbl, is_newton); +} + +TEST_F(algebra_solver_simple_eq_test, newton_tuned_dbl) { + bool is_newton = true; + Eigen::VectorXd theta = simple_eq_test(simple_eq_functor(), y_dbl, is_newton, + true, scale_step, xtol, ftol, maxfev); +} + +TEST_F(algebra_solver_simple_eq_nopara_test, newton_dbl) { + using stan::math::algebra_solver_newton; + Eigen::VectorXd theta = algebra_solver_newton(simple_eq_functor_nopara(), x, + y_dummy, dat, dummy_dat_int); + EXPECT_EQ(20, theta(0)); + EXPECT_EQ(2, theta(1)); +} + +TEST_F(algebra_solver_non_linear_eq_test, newton_dbl) { + bool is_newton = true; + Eigen::VectorXd theta + = non_linear_eq_test(non_linear_eq_functor(), y_dbl, is_newton); + EXPECT_FLOAT_EQ(-y_dbl(0), theta(0)); + EXPECT_FLOAT_EQ(-y_dbl(1), theta(1)); + EXPECT_FLOAT_EQ(y_dbl(2), theta(2)); +} + +TEST_F(error_message_test, newton_dbl) { + bool is_newton = true; + error_conditions_test(non_linear_eq_functor(), y_3, is_newton); +} + +TEST_F(max_steps_test, newton_dbl) { + bool is_newton = true; + max_num_steps_test(y, is_newton); +} + +TEST(MathMatrixRevMat, unsolvable_flag_newton_dbl) { + Eigen::VectorXd y(2); + y << 1, 1; + + unsolvable_flag_test(y); +} + +TEST_F(degenerate_eq_test, newton_guess1_dbl) { + using stan::math::algebra_solver_newton; + + // This first initial guess produces the + // solution x = {8, 8} + + Eigen::VectorXd theta = algebra_solver_newton(degenerate_eq_functor(), + x_guess_1, y_dbl, dat, dat_int); + EXPECT_FLOAT_EQ(8, theta(0)); + EXPECT_FLOAT_EQ(8, theta(1)); +} + +TEST_F(degenerate_eq_test, newton_guess2_dbl) { + using stan::math::algebra_solver_newton; + // This next initial guess produces the + // solution x = {5, 5} + + Eigen::VectorXd theta = algebra_solver_newton(degenerate_eq_functor(), + x_guess_2, y_dbl, dat, dat_int); + EXPECT_FLOAT_EQ(5, theta(0)); + EXPECT_FLOAT_EQ(5, theta(1)); +} + +// For the next two unit tests, see if the initial +// guess determines neighborhood of the +// solution, when solutions have different scales, +// using y_scale. + +TEST_F(degenerate_eq_test, newton_guess2_scale_dbl) { + using stan::math::algebra_solver_newton; + + Eigen::VectorXd theta = algebra_solver_newton( + degenerate_eq_functor(), x_guess_2, y_scale, dat, dat_int); + EXPECT_FLOAT_EQ(5, theta(0)); + EXPECT_FLOAT_EQ(5, theta(1)); +} + +TEST_F(degenerate_eq_test, newton_guess_saddle_point_dbl) { + // Newton solver fails this test because the initial point is + // a saddle point. + using stan::math::algebra_solver_newton; + std::stringstream err_msg; + err_msg << "algebra_solver failed with error flag -11."; + std::string msg = err_msg.str(); + + EXPECT_THROW_MSG(algebra_solver_newton(degenerate_eq_functor(), x_guess_3, + y_scale, dat, dat_int), + std::runtime_error, msg); +} + +TEST_F(algebra_solver_simple_eq_test, newton) { + using stan::math::var; + bool is_newton = true; + for (int k = 0; k < n_x; k++) { + Eigen::Matrix y = y_dbl; + + Eigen::Matrix theta + = simple_eq_test(simple_eq_functor(), y, is_newton); + + AVEC y_vec = createAVEC(y(0), y(1), y(2)); + VEC g; + theta(k).grad(y_vec, g); + + for (int i = 0; i < n_y; i++) + EXPECT_EQ(J(k, i), g[i]); + } +} + +TEST_F(algebra_solver_simple_eq_test, newton_tuned) { + using stan::math::var; + bool is_newton = true; + for (int k = 0; k < n_x; k++) { + Eigen::Matrix y = y_dbl; + + Eigen::Matrix theta + = simple_eq_test(simple_eq_functor(), y, is_newton, true, scale_step, + xtol, ftol, maxfev); + + AVEC y_vec = createAVEC(y(0), y(1), y(2)); + VEC g; + theta(k).grad(y_vec, g); + + for (int i = 0; i < n_y; i++) + EXPECT_EQ(J(k, i), g[i]); + } +} + +TEST_F(algebra_solver_simple_eq_test, newton_init_is_para) { + using stan::math::algebra_solver_newton; + Eigen::VectorXd theta + = algebra_solver_newton(simple_eq_functor(), x_var, y_dbl, dat, dat_int); + EXPECT_EQ(20, theta(0)); + EXPECT_EQ(2, theta(1)); +} + +TEST_F(algebra_solver_non_linear_eq_test, newton) { + using stan::math::var; + bool is_newton = true; + for (int k = 0; k < n_x; k++) { + Eigen::Matrix y = y_dbl; + Eigen::Matrix theta + = non_linear_eq_test(non_linear_eq_functor(), y, is_newton); + + EXPECT_FLOAT_EQ(-y(0).val(), theta(0).val()); + EXPECT_FLOAT_EQ(-y(1).val(), theta(1).val()); + EXPECT_FLOAT_EQ(y(2).val(), theta(2).val()); + + AVEC y_vec = createAVEC(y(0), y(1), y(2)); + VEC g; + theta(k).grad(y_vec, g); + + for (int i = 0; i < n_y; i++) + EXPECT_NEAR(J(k, i), g[i], err); + } +} + +TEST_F(error_message_test, newton) { + using stan::math::var; + bool is_newton = true; + Eigen::Matrix y = y_2; + error_conditions_test(non_linear_eq_functor(), y, is_newton); +} + +TEST_F(max_steps_test, newton) { + bool is_newton = true; + max_num_steps_test(y_var, is_newton); +} + +TEST(MathMatrixRevMat, unsolvable_flag_newton) { + Eigen::Matrix y(2); + y << 1, 1; + + unsolvable_flag_test(y); +} + +TEST_F(degenerate_eq_test, newton_guess1) { + using stan::math::algebra_solver_newton; + // using stan::math::sum; + using stan::math::var; + + // This first initial guess produces the + // solution x = {8, 8} + for (int k = 0; k < n_x; k++) { + Eigen::Matrix y = y_dbl; + Eigen::Matrix theta = algebra_solver_newton( + degenerate_eq_functor(), x_guess_1, y, dat, dat_int); + EXPECT_FLOAT_EQ(8, theta(0).val()); + EXPECT_FLOAT_EQ(8, theta(1).val()); + + AVEC y_vec = createAVEC(y(0), y(1)); + VEC g; + theta(k).grad(y_vec, g); + + for (int l = 0; l < n_y; l++) + EXPECT_NEAR(J1(k, l), g[l], tolerance); + } +} + +TEST_F(degenerate_eq_test, newton_guess2) { + using stan::math::algebra_solver_newton; + using stan::math::var; + // This next initial guess produces the + // solution x = {5, 5} + for (int k = 0; k < 1; k++) { + Eigen::Matrix y = y_dbl; + Eigen::Matrix theta = algebra_solver_newton( + degenerate_eq_functor(), x_guess_2, y, dat, dat_int); + EXPECT_FLOAT_EQ(5, theta(0).val()); + EXPECT_FLOAT_EQ(5, theta(0).val()); + + AVEC y_vec = createAVEC(y(0), y(1)); + VEC g; + theta(k).grad(y_vec, g); + + for (int l = 0; l < n_y; l++) + EXPECT_NEAR(J2(k, l), g[l], tolerance); + } +} diff --git a/test/unit/math/rev/functor/algebra_solver_test.cpp b/test/unit/math/rev/functor/algebra_solver_test.cpp index 40add575bd1..cfbb010d750 100644 --- a/test/unit/math/rev/functor/algebra_solver_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_test.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -10,175 +9,9 @@ #include #include -// Every test exists in four versions for the cases -// where y (the auxiliary parameters) are passed as -// data (double type) or parameters (var types), -// and the cases where the solver is based on Powell's -// or Newton's method. - -class algebra_solver_simple_eq_test : public ::testing::Test { - protected: - void SetUp() override { - n_x = 2; - n_y = 3; - - y_dbl = stan::math::to_vector({5, 4, 2}); - Eigen::MatrixXd J_(n_x, n_y); - J_ << 4, 5, 0, 0, 0, 1; - J = J_; - - x_var = stan::math::to_vector({1, 1}); - } - - void TearDown() override { stan::math::recover_memory(); } - - int n_x; - int n_y; - Eigen::VectorXd y_dbl; - Eigen::Matrix x_var; - std::vector dat; - std::vector dat_int; - double scale_step = 1e-3; - double xtol = 1e-6; - double ftol = 1e-3; - int maxfev = 1e+2; - - Eigen::MatrixXd J; -}; - -class algebra_solver_simple_eq_nopara_test : public ::testing::Test { - protected: - void SetUp() override { x = stan::math::to_vector({1, 1}); } - - void TearDown() override { stan::math::recover_memory(); } - - int n_x = 2; - Eigen::VectorXd x; - std::vector dat = {5, 4, 2}; - Eigen::VectorXd y_dummy; - std::vector dummy_dat_int; -}; - -class algebra_solver_non_linear_eq_test : public ::testing::Test { - protected: - void SetUp() override { - y_dbl = stan::math::to_vector({4, 6, 3}); - Eigen::MatrixXd J_(n_x, n_y); - J_ << -1, 0, 0, 0, -1, 0, 0, 0, 1; - J = J_; - } - - void TearDown() override { stan::math::recover_memory(); } - - int n_x = 3; - int n_y = 3; - double err = 1e-11; - - Eigen::VectorXd y_dbl; - Eigen::MatrixXd J; -}; - -class error_message_test : public ::testing::Test { - protected: - void SetUp() override { - y_2 = stan::math::to_vector({4, 6}); - y_3 = stan::math::to_vector({4, 6, 3}); - } - - void TearDown() override { stan::math::recover_memory(); } - - Eigen::VectorXd y_2; - Eigen::VectorXd y_3; -}; - -class max_steps_test : public ::testing::Test { - protected: - void SetUp() override { - y = stan::math::to_vector({1, 1, 1}); - y_var = stan::math::to_vector({1, 1, 1}); - } - - void TearDown() override { stan::math::recover_memory(); } - - Eigen::VectorXd y; - Eigen::Matrix y_var; -}; - -class degenerate_eq_test : public ::testing::Test { - protected: - void SetUp() override { - using stan::math::to_vector; - y_dbl = to_vector({5, 8}); - y_scale = to_vector({5, 100}); - x_guess_1 = to_vector({10, 1}); - x_guess_2 = to_vector({1, 1}); - x_guess_3 = to_vector({5, 100}); // 80, 80 - - Eigen::MatrixXd J_(n_x, n_y); - J_ << 0, 1, 0, 1; - J1 = J_; - J_ << 1, 0, 1, 0; - J2 = J_; - } - - void TearDown() override { stan::math::recover_memory(); } - - int n_x = 2; - int n_y = 2; - double tolerance = 1e-10; - Eigen::VectorXd y_dbl; - Eigen::VectorXd y_scale; - Eigen::VectorXd x_guess_1; - Eigen::VectorXd x_guess_2; - Eigen::VectorXd x_guess_3; - Eigen::MatrixXd J1; - Eigen::MatrixXd J2; - std::vector dat; - std::vector dat_int; -}; - ////////////////////////////////////////////////////////////////////////// // Tests for powell solver. -TEST_F(algebra_solver_simple_eq_test, powell) { - using stan::math::var; - bool is_newton = false; - for (int k = 0; k < n_x; k++) { - Eigen::Matrix y = y_dbl; - - Eigen::Matrix theta - = simple_eq_test(simple_eq_functor(), y, is_newton); - - stan::math::set_zero_all_adjoints(); - AVEC y_vec = createAVEC(y(0), y(1), y(2)); - VEC g; - theta(k).grad(y_vec, g); - - for (int i = 0; i < n_y; i++) { - EXPECT_EQ(J(k, i), g[i]); - } - } -} - -TEST_F(algebra_solver_simple_eq_test, powell_tuned) { - using stan::math::var; - bool is_newton = false; - for (int k = 0; k < n_x; k++) { - Eigen::Matrix y = y_dbl; - - Eigen::Matrix theta - = simple_eq_test(simple_eq_functor(), y, is_newton, true, scale_step, - xtol, ftol, maxfev); - - AVEC y_vec = createAVEC(y(0), y(1), y(2)); - VEC g; - theta(k).grad(y_vec, g); - - for (int i = 0; i < n_y; i++) - EXPECT_EQ(J(k, i), g[i]); - } -} - TEST_F(algebra_solver_simple_eq_test, powell_dbl) { bool is_newton = false; Eigen::VectorXd theta = simple_eq_test(simple_eq_functor(), y_dbl, is_newton); @@ -190,43 +23,6 @@ TEST_F(algebra_solver_simple_eq_test, powell_tuned_dbl) { true, scale_step, xtol, ftol, maxfev); } -TEST_F(algebra_solver_simple_eq_nopara_test, powell) { - using stan::math::algebra_solver_powell; - Eigen::VectorXd theta = algebra_solver_powell(simple_eq_functor_nopara(), x, - y_dummy, dat, dummy_dat_int); - EXPECT_EQ(20, theta(0)); - EXPECT_EQ(2, theta(1)); -} - -TEST_F(algebra_solver_simple_eq_test, powell_init_is_para) { - using stan::math::algebra_solver_powell; - Eigen::VectorXd theta - = algebra_solver_powell(simple_eq_functor(), x_var, y_dbl, dat, dat_int); - EXPECT_EQ(20, theta(0)); - EXPECT_EQ(2, theta(1)); -} - -TEST_F(algebra_solver_non_linear_eq_test, powell) { - using stan::math::var; - bool is_newton = false; - for (int k = 0; k < n_x; k++) { - Eigen::Matrix y = y_dbl; - Eigen::Matrix theta - = non_linear_eq_test(non_linear_eq_functor(), y, is_newton); - - EXPECT_FLOAT_EQ(-y(0).val(), theta(0).val()); - EXPECT_FLOAT_EQ(-y(1).val(), theta(1).val()); - EXPECT_FLOAT_EQ(y(2).val(), theta(2).val()); - - AVEC y_vec = createAVEC(y(0), y(1), y(2)); - VEC g; - theta(k).grad(y_vec, g); - - for (int i = 0; i < n_y; i++) - EXPECT_NEAR(J(k, i), g[i], err); - } -} - TEST_F(algebra_solver_non_linear_eq_test, powell_dbl) { bool is_newton = false; Eigen::VectorXd theta @@ -236,11 +32,12 @@ TEST_F(algebra_solver_non_linear_eq_test, powell_dbl) { EXPECT_FLOAT_EQ(y_dbl(2), theta(2)); } -TEST_F(error_message_test, powell) { - using stan::math::var; - bool is_newton = false; - Eigen::Matrix y = y_2; - error_conditions_test(non_linear_eq_functor(), y, is_newton); +TEST_F(algebra_solver_simple_eq_nopara_test, powell_double) { + using stan::math::algebra_solver_powell; + Eigen::VectorXd theta = algebra_solver_powell(simple_eq_functor_nopara(), x, + y_dummy, dat, dummy_dat_int); + EXPECT_EQ(20, theta(0)); + EXPECT_EQ(2, theta(1)); } TEST_F(error_message_test, powell_dbl) { @@ -248,72 +45,17 @@ TEST_F(error_message_test, powell_dbl) { error_conditions_test(non_linear_eq_functor(), y_3, is_newton); } -TEST(unsolvable_test, powell) { - using stan::math::var; - Eigen::Matrix y(2); - y << 1, 1; - unsolvable_test(y); -} - TEST(unsolvable_test, powell_dbl) { Eigen::VectorXd y(2); y << 1, 1; unsolvable_test(y); } -TEST_F(max_steps_test, powell) { - bool is_newton = false; - max_num_steps_test(y_var, is_newton); -} - TEST_F(max_steps_test, powell_dbl) { bool is_newton = false; max_num_steps_test(y, is_newton); } -TEST_F(degenerate_eq_test, powell_guess1) { - using stan::math::algebra_solver_powell; - using stan::math::var; - - // This first initial guess produces the - // solution x = {8, 8} - for (int k = 0; k < n_x; k++) { - Eigen::Matrix y = y_dbl; - Eigen::Matrix theta = algebra_solver_powell( - degenerate_eq_functor(), x_guess_1, y, dat, dat_int); - EXPECT_FLOAT_EQ(8, theta(0).val()); - EXPECT_FLOAT_EQ(8, theta(1).val()); - - AVEC y_vec = createAVEC(y(0), y(1)); - VEC g; - theta(k).grad(y_vec, g); - - for (int l = 0; l < n_y; l++) - EXPECT_NEAR(J1(k, l), g[l], tolerance); - } -} - -TEST_F(degenerate_eq_test, powell_guess2) { - using stan::math::algebra_solver_powell; - using stan::math::var; - // This next initial guess produces the - // solution x = {5, 5} - for (int k = 0; k < 1; k++) { - Eigen::Matrix y = y_dbl; - Eigen::Matrix theta = algebra_solver_powell( - degenerate_eq_functor(), x_guess_2, y, dat, dat_int); - EXPECT_FLOAT_EQ(5, theta(0).val()); - EXPECT_FLOAT_EQ(5, theta(0).val()); - - AVEC y_vec = createAVEC(y(0), y(1)); - VEC g; - theta(k).grad(y_vec, g); - - for (int l = 0; l < n_y; l++) - EXPECT_NEAR(J2(k, l), g[l], tolerance); - } -} - TEST_F(degenerate_eq_test, powell_guess1_dbl) { using stan::math::algebra_solver_powell; @@ -360,30 +102,29 @@ TEST_F(degenerate_eq_test, powell_guess_saddle_point_dbl) { EXPECT_FLOAT_EQ(100, theta(1)); } -////////////////////////////////////////////////////////////////////////// -// Tests for newton solver. - -TEST_F(algebra_solver_simple_eq_test, newton) { +TEST_F(algebra_solver_simple_eq_test, powell) { using stan::math::var; - bool is_newton = true; + bool is_newton = false; for (int k = 0; k < n_x; k++) { Eigen::Matrix y = y_dbl; Eigen::Matrix theta = simple_eq_test(simple_eq_functor(), y, is_newton); + stan::math::set_zero_all_adjoints(); AVEC y_vec = createAVEC(y(0), y(1), y(2)); VEC g; theta(k).grad(y_vec, g); - for (int i = 0; i < n_y; i++) + for (int i = 0; i < n_y; i++) { EXPECT_EQ(J(k, i), g[i]); + } } } -TEST_F(algebra_solver_simple_eq_test, newton_tuned) { +TEST_F(algebra_solver_simple_eq_test, powell_tuned) { using stan::math::var; - bool is_newton = true; + bool is_newton = false; for (int k = 0; k < n_x; k++) { Eigen::Matrix y = y_dbl; @@ -400,36 +141,17 @@ TEST_F(algebra_solver_simple_eq_test, newton_tuned) { } } -TEST_F(algebra_solver_simple_eq_test, newton_dbl) { - bool is_newton = true; - Eigen::VectorXd theta = simple_eq_test(simple_eq_functor(), y_dbl, is_newton); -} - -TEST_F(algebra_solver_simple_eq_test, newton_tuned_dbl) { - bool is_newton = true; - Eigen::VectorXd theta = simple_eq_test(simple_eq_functor(), y_dbl, is_newton, - true, scale_step, xtol, ftol, maxfev); -} - -TEST_F(algebra_solver_simple_eq_nopara_test, newton) { - using stan::math::algebra_solver_newton; - Eigen::VectorXd theta = algebra_solver_newton(simple_eq_functor_nopara(), x, - y_dummy, dat, dummy_dat_int); - EXPECT_EQ(20, theta(0)); - EXPECT_EQ(2, theta(1)); -} - -TEST_F(algebra_solver_simple_eq_test, newton_init_is_para) { - using stan::math::algebra_solver_newton; +TEST_F(algebra_solver_simple_eq_test, powell_init_is_para) { + using stan::math::algebra_solver_powell; Eigen::VectorXd theta - = algebra_solver_newton(simple_eq_functor(), x_var, y_dbl, dat, dat_int); + = algebra_solver_powell(simple_eq_functor(), x_var, y_dbl, dat, dat_int); EXPECT_EQ(20, theta(0)); EXPECT_EQ(2, theta(1)); } -TEST_F(algebra_solver_non_linear_eq_test, newton) { +TEST_F(algebra_solver_non_linear_eq_test, powell) { using stan::math::var; - bool is_newton = true; + bool is_newton = false; for (int k = 0; k < n_x; k++) { Eigen::Matrix y = y_dbl; Eigen::Matrix theta @@ -448,61 +170,34 @@ TEST_F(algebra_solver_non_linear_eq_test, newton) { } } -TEST_F(algebra_solver_non_linear_eq_test, newton_dbl) { - bool is_newton = true; - Eigen::VectorXd theta - = non_linear_eq_test(non_linear_eq_functor(), y_dbl, is_newton); - EXPECT_FLOAT_EQ(-y_dbl(0), theta(0)); - EXPECT_FLOAT_EQ(-y_dbl(1), theta(1)); - EXPECT_FLOAT_EQ(y_dbl(2), theta(2)); -} - -TEST_F(error_message_test, newton) { +TEST_F(error_message_test, powell) { using stan::math::var; - bool is_newton = true; + bool is_newton = false; Eigen::Matrix y = y_2; error_conditions_test(non_linear_eq_functor(), y, is_newton); } -TEST_F(error_message_test, newton_dbl) { - bool is_newton = true; - error_conditions_test(non_linear_eq_functor(), y_3, is_newton); -} - -TEST_F(max_steps_test, newton) { - bool is_newton = true; - max_num_steps_test(y_var, is_newton); -} - -TEST_F(max_steps_test, newton_dbl) { - bool is_newton = true; - max_num_steps_test(y, is_newton); -} - -TEST(MathMatrixRevMat, unsolvable_flag_newton) { - Eigen::Matrix y(2); +TEST(unsolvable_test, powell) { + using stan::math::var; + Eigen::Matrix y(2); y << 1, 1; - - unsolvable_flag_test(y); + unsolvable_test(y); } -TEST(MathMatrixRevMat, unsolvable_flag_newton_dbl) { - Eigen::VectorXd y(2); - y << 1, 1; - - unsolvable_flag_test(y); +TEST_F(max_steps_test, powell) { + bool is_newton = false; + max_num_steps_test(y_var, is_newton); } -TEST_F(degenerate_eq_test, newton_guess1) { - using stan::math::algebra_solver_newton; - // using stan::math::sum; +TEST_F(degenerate_eq_test, powell_guess1) { + using stan::math::algebra_solver_powell; using stan::math::var; // This first initial guess produces the // solution x = {8, 8} for (int k = 0; k < n_x; k++) { Eigen::Matrix y = y_dbl; - Eigen::Matrix theta = algebra_solver_newton( + Eigen::Matrix theta = algebra_solver_powell( degenerate_eq_functor(), x_guess_1, y, dat, dat_int); EXPECT_FLOAT_EQ(8, theta(0).val()); EXPECT_FLOAT_EQ(8, theta(1).val()); @@ -516,14 +211,14 @@ TEST_F(degenerate_eq_test, newton_guess1) { } } -TEST_F(degenerate_eq_test, newton_guess2) { - using stan::math::algebra_solver_newton; +TEST_F(degenerate_eq_test, powell_guess2) { + using stan::math::algebra_solver_powell; using stan::math::var; // This next initial guess produces the // solution x = {5, 5} for (int k = 0; k < 1; k++) { Eigen::Matrix y = y_dbl; - Eigen::Matrix theta = algebra_solver_newton( + Eigen::Matrix theta = algebra_solver_powell( degenerate_eq_functor(), x_guess_2, y, dat, dat_int); EXPECT_FLOAT_EQ(5, theta(0).val()); EXPECT_FLOAT_EQ(5, theta(0).val()); @@ -536,53 +231,3 @@ TEST_F(degenerate_eq_test, newton_guess2) { EXPECT_NEAR(J2(k, l), g[l], tolerance); } } - -TEST_F(degenerate_eq_test, newton_guess1_dbl) { - using stan::math::algebra_solver_newton; - - // This first initial guess produces the - // solution x = {8, 8} - - Eigen::VectorXd theta = algebra_solver_newton(degenerate_eq_functor(), - x_guess_1, y_dbl, dat, dat_int); - EXPECT_FLOAT_EQ(8, theta(0)); - EXPECT_FLOAT_EQ(8, theta(1)); -} - -TEST_F(degenerate_eq_test, newton_guess2_dbl) { - using stan::math::algebra_solver_newton; - // This next initial guess produces the - // solution x = {5, 5} - - Eigen::VectorXd theta = algebra_solver_newton(degenerate_eq_functor(), - x_guess_2, y_dbl, dat, dat_int); - EXPECT_FLOAT_EQ(5, theta(0)); - EXPECT_FLOAT_EQ(5, theta(1)); -} - -// For the next two unit tests, see if the initial -// guess determines neighborhood of the -// solution, when solutions have different scales, -// using y_scale. - -TEST_F(degenerate_eq_test, newton_guess2_scale_dbl) { - using stan::math::algebra_solver_newton; - - Eigen::VectorXd theta = algebra_solver_newton( - degenerate_eq_functor(), x_guess_2, y_scale, dat, dat_int); - EXPECT_FLOAT_EQ(5, theta(0)); - EXPECT_FLOAT_EQ(5, theta(1)); -} - -TEST_F(degenerate_eq_test, newton_guess_saddle_point_dbl) { - // Newton solver fails this test because the initial point is - // a saddle point. - using stan::math::algebra_solver_newton; - std::stringstream err_msg; - err_msg << "algebra_solver failed with error flag -11."; - std::string msg = err_msg.str(); - - EXPECT_THROW_MSG(algebra_solver_newton(degenerate_eq_functor(), x_guess_3, - y_scale, dat, dat_int), - std::runtime_error, msg); -} diff --git a/test/unit/math/rev/functor/util_algebra_solver.hpp b/test/unit/math/rev/functor/util_algebra_solver.hpp index bec90820196..16c5834133f 100644 --- a/test/unit/math/rev/functor/util_algebra_solver.hpp +++ b/test/unit/math/rev/functor/util_algebra_solver.hpp @@ -8,6 +8,133 @@ #include #include +// Every test exists in four versions for the cases +// where y (the auxiliary parameters) are passed as +// data (double type) or parameters (var types), +// and the cases where the solver is based on Powell's +// or Newton's method. + +class algebra_solver_simple_eq_test : public ::testing::Test { + protected: + void SetUp() override { + n_x = 2; + n_y = 3; + + y_dbl = stan::math::to_vector({5, 4, 2}); + Eigen::MatrixXd J_(n_x, n_y); + J_ << 4, 5, 0, 0, 0, 1; + J = J_; + + x_var = stan::math::to_vector({1, 1}); + } + + void TearDown() override { stan::math::recover_memory(); } + + int n_x; + int n_y; + Eigen::VectorXd y_dbl; + Eigen::Matrix x_var; + std::vector dat; + std::vector dat_int; + double scale_step = 1e-3; + double xtol = 1e-6; + double ftol = 1e-3; + int maxfev = 1e+2; + + Eigen::MatrixXd J; +}; + +class algebra_solver_simple_eq_nopara_test : public ::testing::Test { + protected: + void SetUp() override { x = stan::math::to_vector({1, 1}); } + + void TearDown() override { stan::math::recover_memory(); } + + int n_x = 2; + Eigen::VectorXd x; + std::vector dat = {5, 4, 2}; + Eigen::VectorXd y_dummy; + std::vector dummy_dat_int; +}; + +class algebra_solver_non_linear_eq_test : public ::testing::Test { + protected: + void SetUp() override { + y_dbl = stan::math::to_vector({4, 6, 3}); + Eigen::MatrixXd J_(n_x, n_y); + J_ << -1, 0, 0, 0, -1, 0, 0, 0, 1; + J = J_; + } + + void TearDown() override { stan::math::recover_memory(); } + + int n_x = 3; + int n_y = 3; + double err = 1e-11; + + Eigen::VectorXd y_dbl; + Eigen::MatrixXd J; +}; + +class error_message_test : public ::testing::Test { + protected: + void SetUp() override { + y_2 = stan::math::to_vector({4, 6}); + y_3 = stan::math::to_vector({4, 6, 3}); + } + + void TearDown() override { stan::math::recover_memory(); } + + Eigen::VectorXd y_2; + Eigen::VectorXd y_3; +}; + +class max_steps_test : public ::testing::Test { + protected: + void SetUp() override { + y = stan::math::to_vector({1, 1, 1}); + y_var = stan::math::to_vector({1, 1, 1}); + } + + void TearDown() override { stan::math::recover_memory(); } + + Eigen::VectorXd y; + Eigen::Matrix y_var; +}; + +class degenerate_eq_test : public ::testing::Test { + protected: + void SetUp() override { + using stan::math::to_vector; + y_dbl = to_vector({5, 8}); + y_scale = to_vector({5, 100}); + x_guess_1 = to_vector({10, 1}); + x_guess_2 = to_vector({1, 1}); + x_guess_3 = to_vector({5, 100}); // 80, 80 + + Eigen::MatrixXd J_(n_x, n_y); + J_ << 0, 1, 0, 1; + J1 = J_; + J_ << 1, 0, 1, 0; + J2 = J_; + } + + void TearDown() override { stan::math::recover_memory(); } + + int n_x = 2; + int n_y = 2; + double tolerance = 1e-10; + Eigen::VectorXd y_dbl; + Eigen::VectorXd y_scale; + Eigen::VectorXd x_guess_1; + Eigen::VectorXd x_guess_2; + Eigen::VectorXd x_guess_3; + Eigen::MatrixXd J1; + Eigen::MatrixXd J2; + std::vector dat; + std::vector dat_int; +}; + /* wrapper function that either calls the dogleg or the newton solver. */ template Eigen::Matrix general_algebra_solver( @@ -172,8 +299,7 @@ inline void error_conditions_test(const F& f, << "and size of the vector of unknowns, x, (3) must match in size"; std::string msg = err_msg.str(); EXPECT_THROW_MSG( - // CHECK: Should this test run on the Newton solver too? - algebra_solver_powell(non_square_eq_functor(), x, y, dat, dat_int), + general_algebra_solver(is_newton, non_square_eq_functor(), x, y, dat, dat_int), std::invalid_argument, msg); Eigen::VectorXd x_bad(static_cast(0)); From dbf55153ccf289da29e9925e0084997747ccebfb Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Sun, 11 Jul 2021 23:18:36 +0000 Subject: [PATCH 68/88] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- test/unit/math/rev/functor/util_algebra_solver.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/unit/math/rev/functor/util_algebra_solver.hpp b/test/unit/math/rev/functor/util_algebra_solver.hpp index 16c5834133f..3bc941cd453 100644 --- a/test/unit/math/rev/functor/util_algebra_solver.hpp +++ b/test/unit/math/rev/functor/util_algebra_solver.hpp @@ -298,9 +298,9 @@ inline void error_conditions_test(const F& f, err_msg << "size of the algebraic system's output (2) " << "and size of the vector of unknowns, x, (3) must match in size"; std::string msg = err_msg.str(); - EXPECT_THROW_MSG( - general_algebra_solver(is_newton, non_square_eq_functor(), x, y, dat, dat_int), - std::invalid_argument, msg); + EXPECT_THROW_MSG(general_algebra_solver(is_newton, non_square_eq_functor(), x, + y, dat, dat_int), + std::invalid_argument, msg); Eigen::VectorXd x_bad(static_cast(0)); std::stringstream err_msg2; From 241495904cdb7695ea0644af1dfa04bcfc1ac2f5 Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Thu, 15 Jul 2021 12:11:52 -0400 Subject: [PATCH 69/88] move newton back to rev and fix check test for algebra solver newton --- stan/math/prim/functor.hpp | 1 - .../prim/functor/algebra_solver_powell.hpp | 271 ------------------ .../rev/functor/algebra_solver_powell.hpp | 249 +++++++++++++++- stan/math/rev/functor/kinsol_data.hpp | 12 +- .../functor/algebra_solver_newton_test.cpp | 27 -- 5 files changed, 253 insertions(+), 307 deletions(-) delete mode 100644 stan/math/prim/functor/algebra_solver_powell.hpp diff --git a/stan/math/prim/functor.hpp b/stan/math/prim/functor.hpp index 1c2632dfae2..456750bac41 100644 --- a/stan/math/prim/functor.hpp +++ b/stan/math/prim/functor.hpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include diff --git a/stan/math/prim/functor/algebra_solver_powell.hpp b/stan/math/prim/functor/algebra_solver_powell.hpp deleted file mode 100644 index 8e2ab9e1b90..00000000000 --- a/stan/math/prim/functor/algebra_solver_powell.hpp +++ /dev/null @@ -1,271 +0,0 @@ -#ifndef STAN_MATH_PRIM_FUNCTOR_ALGEBRA_SOLVER_POWELL_HPP -#define STAN_MATH_PRIM_FUNCTOR_ALGEBRA_SOLVER_POWELL_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace stan { -namespace math { - -/** - * Solve algebraic equations using Powell solver - * - * @tparam F type of equation system function - * @tparam T type of elements in the x vector - * @tparam Args types of additional parameters to the equation system functor - * - * @param[in] f Functor that evaluates the system of equations - * @param[in] x Vector of starting values (initial guess). - * @param[in, out] msgs the print stream for warning messages. - * @param[in] relative_tolerance determines the convergence criteria - * for the solution. - * @param[in] function_tolerance determines whether roots are acceptable. - * @param[in] max_num_steps maximum number of function evaluations. - * @return theta_dbl Double vector of solutions to the system of equations. - * @pre x has size greater than zero. - * @pre x has only finite elements. - * @pre f returns finite values when passed any value of x and the given args. - * @pre relative_tolerance is non-negative. - * @pre function_tolerance is non-negative. - * @pre max_num_steps is positive. - * @throw std::domain_error solver exceeds max_num_steps. - * @throw std::domain_error if the norm of the solution exceeds - * the function tolerance. - */ -template * = nullptr> -T& algebra_solver_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) { - // Construct the solver - hybrj_functor_solver hfs(f); - Eigen::HybridNonLinearSolver solver(hfs); - - // Compute theta_dbl - solver.parameters.xtol = relative_tolerance; - solver.parameters.maxfev = max_num_steps; - solver.solve(x); - - // Check if the max number of steps has been exceeded - if (solver.nfev >= max_num_steps) { - [&]() STAN_COLD_PATH { - throw_domain_error("algebra_solver", "maximum number of iterations", - max_num_steps, "(", ") was exceeded in the solve."); - }(); - } - - // Check solution is a root - double system_norm = f(x).stableNorm(); - if (system_norm > function_tolerance) { - [&]() STAN_COLD_PATH { - std::ostringstream message; - message << "the norm of the algebraic function is " << system_norm - << " but should be lower than the function " - << "tolerance:"; - throw_domain_error("algebra_solver", message.str().c_str(), - function_tolerance, "", - ". Consider decreasing the relative tolerance and " - "increasing max_num_steps."); - }(); - } - - return x; -} - -/** - * Return the solution to the specified system of algebraic - * equations given an initial guess, and parameters and data, - * which get passed into the algebraic system. - * Use Powell's dogleg solver. - * - * The user can also specify the relative tolerance - * (xtol in Eigen's code), the function tolerance, - * and the maximum number of steps (maxfev in Eigen's code). - * - * This overload handles non-autodiff parameters. - * - * @tparam F type of equation system function - * @tparam T type of elements in the x vector - * @tparam Args types of additional parameters to the equation system functor - * - * @param[in] f Functor that evaluates the system of equations. - * @param[in] x Vector of starting values (initial guess). - * @param[in, out] msgs the print stream for warning messages. - * @param[in] relative_tolerance determines the convergence criteria - * for the solution. - * @param[in] function_tolerance determines whether roots are acceptable. - * @param[in] max_num_steps maximum number of function evaluations. - * @param[in] args additional parameters to the equation system functor. - * @return theta Vector of solutions to the system of equations. - * @pre f returns finite values when passed any value of x and the given args. - * @throw std::invalid_argument if x has size zero. - * @throw std::invalid_argument if x has non-finite elements. - * elements. - * @throw std::invalid_argument if relative_tolerance is strictly - * negative. - * @throw std::invalid_argument if function_tolerance is strictly - * negative. - * @throw std::invalid_argument if max_num_steps is not positive. - * @throw std::domain_error solver exceeds max_num_steps. - * @throw std::domain_error if the norm of the solution exceeds - * the function tolerance. - */ -template * = nullptr, - require_all_st_arithmetic* = nullptr> -Eigen::VectorXd algebra_solver_powell_impl(const F& f, const T& x, - std::ostream* const msgs, - const double relative_tolerance, - const double function_tolerance, - const int64_t max_num_steps, - const Args&... args) { - const auto& x_ref = to_ref(x); - auto x_val = to_ref(value_of(x_ref)); - - auto f_wrt_x - = [&f, msgs, &args...](const auto& x) { return f(x, msgs, args...); }; - - check_nonzero_size("algebra_solver_powell", "initial guess", x_val); - check_finite("algebra_solver_powell", "initial guess", x_val); - check_nonnegative("alegbra_solver_powell", "relative_tolerance", - relative_tolerance); - check_nonnegative("algebra_solver_powell", "function_tolerance", - function_tolerance); - check_positive("algebra_solver_powell", "max_num_steps", max_num_steps); - check_matching_sizes("algebra_solver", "the algebraic system's output", - f_wrt_x(x_ref), "the vector of unknowns, x,", x_ref); - - // Solve the system - return algebra_solver_powell_call_solver(f_wrt_x, x_val, msgs, - relative_tolerance, - function_tolerance, max_num_steps); -} - -/** - * Return the solution to the specified system of algebraic - * equations given an initial guess, and parameters and data, - * which get passed into the algebraic system. - * Use Powell's dogleg solver. - * - * The user can also specify the relative tolerance - * (xtol in Eigen's code), the function tolerance, - * and the maximum number of steps (maxfev in Eigen's code). - * - * @tparam F type of equation system function - * @tparam T1 type of elements in the x vector - * @tparam T2 type of elements in the y vector - * - * @param[in] f Functor that evaluates the system of equations. - * @param[in] x Vector of starting values (initial guess). - * @param[in] y parameter vector for the equation system. - * @param[in] dat continuous data vector for the equation system. - * @param[in] dat_int integer data vector for the equation system. - * @param[in, out] msgs the print stream for warning messages. - * @param[in] relative_tolerance determines the convergence criteria - * for the solution. - * @param[in] function_tolerance determines whether roots are acceptable. - * @param[in] max_num_steps maximum number of function evaluations. - * @return theta Vector of solutions to the system of equations. - * @throw std::invalid_argument if x has size zero. - * @throw std::invalid_argument if x has non-finite elements. - * @throw std::invalid_argument if y has non-finite elements. - * @throw std::invalid_argument if dat has non-finite elements. - * @throw std::invalid_argument if dat_int has non-finite - * elements. - * @throw std::invalid_argument if relative_tolerance is strictly - * negative. - * @throw std::invalid_argument if function_tolerance is strictly - * negative. - * @throw std::invalid_argument if max_num_steps is not positive. - * @throw std::domain_error solver exceeds max_num_steps. - * @throw std::domain_error if the norm of the solution exceeds - * the function tolerance. - */ -template * = nullptr> -Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_powell( - const F& f, const T1& x, const T2& y, const std::vector& dat, - const std::vector& dat_int, std::ostream* const msgs = nullptr, - const double relative_tolerance = 1e-10, - const double function_tolerance = 1e-6, - const int64_t max_num_steps = 1e+3) { - return algebra_solver_powell_impl(algebra_solver_adapter(f), x, msgs, - relative_tolerance, function_tolerance, - max_num_steps, y, dat, dat_int); -} - -/** - * Return the solution to the specified system of algebraic - * equations given an initial guess, and parameters and data, - * which get passed into the algebraic system. - * Use Powell's dogleg solver. - * - * The user can also specify the relative tolerance - * (xtol in Eigen's code), the function tolerance, - * and the maximum number of steps (maxfev in Eigen's code). - * - * Signature to maintain backward compatibility, will be removed - * in the future. - * - * @tparam F type of equation system function - * @tparam T1 type of elements in the x vector - * @tparam T2 type of elements in the y vector - * - * @param[in] f Functor that evaluates the system of equations. - * @param[in] x Vector of starting values (initial guess). - * @param[in] y parameter vector for the equation system. - * @param[in] dat continuous data vector for the equation system. - * @param[in] dat_int integer data vector for the equation system. - * @param[in, out] msgs the print stream for warning messages. - * @param[in] relative_tolerance determines the convergence criteria - * for the solution. - * @param[in] function_tolerance determines whether roots are acceptable. - * @param[in] max_num_steps maximum number of function evaluations. - * @return theta Vector of solutions to the system of equations. - * @pre f returns finite values when passed any value of x and the given y, dat, - * and dat_int. - * @throw std::invalid_argument if x has size zero. - * @throw std::invalid_argument if x has non-finite elements. - * @throw std::invalid_argument if y has non-finite elements. - * @throw std::invalid_argument if dat has non-finite elements. - * @throw std::invalid_argument if dat_int has non-finite - * elements. - * @throw std::invalid_argument if relative_tolerance is strictly - * negative. - * @throw std::invalid_argument if function_tolerance is strictly - * negative. - * @throw std::invalid_argument if max_num_steps is not positive. - * @throw boost::math::evaluation_error (which is a subclass of - * std::domain_error) if solver exceeds max_num_steps. - * @throw boost::math::evaluation_error (which is a subclass of - * std::domain_error) if the norm of the solution exceeds the - * function tolerance. - */ -template * = nullptr> -Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver( - const F& f, const T1& x, const T2& y, const std::vector& dat, - const std::vector& 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 algebra_solver_powell(f, x, y, dat, dat_int, msgs, relative_tolerance, - function_tolerance, max_num_steps); -} - -} // namespace math -} // namespace stan - -#endif diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 67aadac0411..22143f28133 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -18,6 +17,254 @@ namespace stan { namespace math { + /** + * Solve algebraic equations using Powell solver + * + * @tparam F type of equation system function + * @tparam T type of elements in the x vector + * @tparam Args types of additional parameters to the equation system functor + * + * @param[in] f Functor that evaluates the system of equations + * @param[in] x Vector of starting values (initial guess). + * @param[in, out] msgs the print stream for warning messages. + * @param[in] relative_tolerance determines the convergence criteria + * for the solution. + * @param[in] function_tolerance determines whether roots are acceptable. + * @param[in] max_num_steps maximum number of function evaluations. + * @return theta_dbl Double vector of solutions to the system of equations. + * @pre x has size greater than zero. + * @pre x has only finite elements. + * @pre f returns finite values when passed any value of x and the given args. + * @pre relative_tolerance is non-negative. + * @pre function_tolerance is non-negative. + * @pre max_num_steps is positive. + * @throw std::domain_error solver exceeds max_num_steps. + * @throw std::domain_error if the norm of the solution exceeds + * the function tolerance. + */ + template * = nullptr> + T& algebra_solver_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) { + // Construct the solver + hybrj_functor_solver hfs(f); + Eigen::HybridNonLinearSolver solver(hfs); + + // Compute theta_dbl + solver.parameters.xtol = relative_tolerance; + solver.parameters.maxfev = max_num_steps; + solver.solve(x); + + // Check if the max number of steps has been exceeded + if (solver.nfev >= max_num_steps) { + [&]() STAN_COLD_PATH { + throw_domain_error("algebra_solver", "maximum number of iterations", + max_num_steps, "(", ") was exceeded in the solve."); + }(); + } + + // Check solution is a root + double system_norm = f(x).stableNorm(); + if (system_norm > function_tolerance) { + [&]() STAN_COLD_PATH { + std::ostringstream message; + message << "the norm of the algebraic function is " << system_norm + << " but should be lower than the function " + << "tolerance:"; + throw_domain_error("algebra_solver", message.str().c_str(), + function_tolerance, "", + ". Consider decreasing the relative tolerance and " + "increasing max_num_steps."); + }(); + } + + return x; + } + + /** + * Return the solution to the specified system of algebraic + * equations given an initial guess, and parameters and data, + * which get passed into the algebraic system. + * Use Powell's dogleg solver. + * + * The user can also specify the relative tolerance + * (xtol in Eigen's code), the function tolerance, + * and the maximum number of steps (maxfev in Eigen's code). + * + * This overload handles non-autodiff parameters. + * + * @tparam F type of equation system function + * @tparam T type of elements in the x vector + * @tparam Args types of additional parameters to the equation system functor + * + * @param[in] f Functor that evaluates the system of equations. + * @param[in] x Vector of starting values (initial guess). + * @param[in, out] msgs the print stream for warning messages. + * @param[in] relative_tolerance determines the convergence criteria + * for the solution. + * @param[in] function_tolerance determines whether roots are acceptable. + * @param[in] max_num_steps maximum number of function evaluations. + * @param[in] args additional parameters to the equation system functor. + * @return theta Vector of solutions to the system of equations. + * @pre f returns finite values when passed any value of x and the given args. + * @throw std::invalid_argument if x has size zero. + * @throw std::invalid_argument if x has non-finite elements. + * elements. + * @throw std::invalid_argument if relative_tolerance is strictly + * negative. + * @throw std::invalid_argument if function_tolerance is strictly + * negative. + * @throw std::invalid_argument if max_num_steps is not positive. + * @throw std::domain_error solver exceeds max_num_steps. + * @throw std::domain_error if the norm of the solution exceeds + * the function tolerance. + */ + template * = nullptr, + require_all_st_arithmetic* = nullptr> + Eigen::VectorXd algebra_solver_powell_impl(const F& f, const T& x, + std::ostream* const msgs, + const double relative_tolerance, + const double function_tolerance, + const int64_t max_num_steps, + const Args&... args) { + const auto& x_ref = to_ref(x); + auto x_val = to_ref(value_of(x_ref)); + + auto f_wrt_x + = [&f, msgs, &args...](const auto& x) { return f(x, msgs, args...); }; + + check_nonzero_size("algebra_solver_powell", "initial guess", x_val); + check_finite("algebra_solver_powell", "initial guess", x_val); + check_nonnegative("alegbra_solver_powell", "relative_tolerance", + relative_tolerance); + check_nonnegative("algebra_solver_powell", "function_tolerance", + function_tolerance); + check_positive("algebra_solver_powell", "max_num_steps", max_num_steps); + check_matching_sizes("algebra_solver", "the algebraic system's output", + f_wrt_x(x_ref), "the vector of unknowns, x,", x_ref); + + // Solve the system + return algebra_solver_powell_call_solver(f_wrt_x, x_val, msgs, + relative_tolerance, + function_tolerance, max_num_steps); + } + + /** + * Return the solution to the specified system of algebraic + * equations given an initial guess, and parameters and data, + * which get passed into the algebraic system. + * Use Powell's dogleg solver. + * + * The user can also specify the relative tolerance + * (xtol in Eigen's code), the function tolerance, + * and the maximum number of steps (maxfev in Eigen's code). + * + * @tparam F type of equation system function + * @tparam T1 type of elements in the x vector + * @tparam T2 type of elements in the y vector + * + * @param[in] f Functor that evaluates the system of equations. + * @param[in] x Vector of starting values (initial guess). + * @param[in] y parameter vector for the equation system. + * @param[in] dat continuous data vector for the equation system. + * @param[in] dat_int integer data vector for the equation system. + * @param[in, out] msgs the print stream for warning messages. + * @param[in] relative_tolerance determines the convergence criteria + * for the solution. + * @param[in] function_tolerance determines whether roots are acceptable. + * @param[in] max_num_steps maximum number of function evaluations. + * @return theta Vector of solutions to the system of equations. + * @throw std::invalid_argument if x has size zero. + * @throw std::invalid_argument if x has non-finite elements. + * @throw std::invalid_argument if y has non-finite elements. + * @throw std::invalid_argument if dat has non-finite elements. + * @throw std::invalid_argument if dat_int has non-finite + * elements. + * @throw std::invalid_argument if relative_tolerance is strictly + * negative. + * @throw std::invalid_argument if function_tolerance is strictly + * negative. + * @throw std::invalid_argument if max_num_steps is not positive. + * @throw std::domain_error solver exceeds max_num_steps. + * @throw std::domain_error if the norm of the solution exceeds + * the function tolerance. + */ + template * = nullptr> + Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_powell( + const F& f, const T1& x, const T2& y, const std::vector& dat, + const std::vector& dat_int, std::ostream* const msgs = nullptr, + const double relative_tolerance = 1e-10, + const double function_tolerance = 1e-6, + const int64_t max_num_steps = 1e+3) { + return algebra_solver_powell_impl(algebra_solver_adapter(f), x, msgs, + relative_tolerance, function_tolerance, + max_num_steps, y, dat, dat_int); + } + + /** + * Return the solution to the specified system of algebraic + * equations given an initial guess, and parameters and data, + * which get passed into the algebraic system. + * Use Powell's dogleg solver. + * + * The user can also specify the relative tolerance + * (xtol in Eigen's code), the function tolerance, + * and the maximum number of steps (maxfev in Eigen's code). + * + * Signature to maintain backward compatibility, will be removed + * in the future. + * + * @tparam F type of equation system function + * @tparam T1 type of elements in the x vector + * @tparam T2 type of elements in the y vector + * + * @param[in] f Functor that evaluates the system of equations. + * @param[in] x Vector of starting values (initial guess). + * @param[in] y parameter vector for the equation system. + * @param[in] dat continuous data vector for the equation system. + * @param[in] dat_int integer data vector for the equation system. + * @param[in, out] msgs the print stream for warning messages. + * @param[in] relative_tolerance determines the convergence criteria + * for the solution. + * @param[in] function_tolerance determines whether roots are acceptable. + * @param[in] max_num_steps maximum number of function evaluations. + * @return theta Vector of solutions to the system of equations. + * @pre f returns finite values when passed any value of x and the given y, dat, + * and dat_int. + * @throw std::invalid_argument if x has size zero. + * @throw std::invalid_argument if x has non-finite elements. + * @throw std::invalid_argument if y has non-finite elements. + * @throw std::invalid_argument if dat has non-finite elements. + * @throw std::invalid_argument if dat_int has non-finite + * elements. + * @throw std::invalid_argument if relative_tolerance is strictly + * negative. + * @throw std::invalid_argument if function_tolerance is strictly + * negative. + * @throw std::invalid_argument if max_num_steps is not positive. + * @throw boost::math::evaluation_error (which is a subclass of + * std::domain_error) if solver exceeds max_num_steps. + * @throw boost::math::evaluation_error (which is a subclass of + * std::domain_error) if the norm of the solution exceeds the + * function tolerance. + */ + template * = nullptr> + Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver( + const F& f, const T1& x, const T2& y, const std::vector& dat, + const std::vector& 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 algebra_solver_powell(f, x, y, dat, dat_int, msgs, relative_tolerance, + function_tolerance, max_num_steps); + } + /** * Return the solution to the specified system of algebraic * equations given an initial guess, and parameters and data, diff --git a/stan/math/rev/functor/kinsol_data.hpp b/stan/math/rev/functor/kinsol_data.hpp index 642b6a3b5bd..a97cbc0ea6a 100644 --- a/stan/math/rev/functor/kinsol_data.hpp +++ b/stan/math/rev/functor/kinsol_data.hpp @@ -71,13 +71,11 @@ class kinsol_system_data { Eigen::Map f_eval_map(N_VGetArrayPointer(f_eval), explicit_system->N_); - - f_eval_map = apply( - [&](const auto&... args) { - return explicit_system->f_(x_eigen, explicit_system->msgs_, args...); - }, - explicit_system->args_tuple_); - + auto result = apply([&](const auto&... args) { + return explicit_system->f_(x_eigen, explicit_system->msgs_, args...); + }, explicit_system->args_tuple_); + check_matching_sizes("", "the algebraic system's output", result, "the vector of unknowns, x,", f_eval_map); + f_eval_map = result; return 0; } diff --git a/test/unit/math/rev/functor/algebra_solver_newton_test.cpp b/test/unit/math/rev/functor/algebra_solver_newton_test.cpp index 7925bdb518e..c15a7538c4c 100644 --- a/test/unit/math/rev/functor/algebra_solver_newton_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_newton_test.cpp @@ -12,33 +12,6 @@ ////////////////////////////////////////////////////////////////////////// // Tests for newton solver. -TEST_F(algebra_solver_simple_eq_test, newton_dbl) { - bool is_newton = true; - Eigen::VectorXd theta = simple_eq_test(simple_eq_functor(), y_dbl, is_newton); -} - -TEST_F(algebra_solver_simple_eq_test, newton_tuned_dbl) { - bool is_newton = true; - Eigen::VectorXd theta = simple_eq_test(simple_eq_functor(), y_dbl, is_newton, - true, scale_step, xtol, ftol, maxfev); -} - -TEST_F(algebra_solver_simple_eq_nopara_test, newton_dbl) { - using stan::math::algebra_solver_newton; - Eigen::VectorXd theta = algebra_solver_newton(simple_eq_functor_nopara(), x, - y_dummy, dat, dummy_dat_int); - EXPECT_EQ(20, theta(0)); - EXPECT_EQ(2, theta(1)); -} - -TEST_F(algebra_solver_non_linear_eq_test, newton_dbl) { - bool is_newton = true; - Eigen::VectorXd theta - = non_linear_eq_test(non_linear_eq_functor(), y_dbl, is_newton); - EXPECT_FLOAT_EQ(-y_dbl(0), theta(0)); - EXPECT_FLOAT_EQ(-y_dbl(1), theta(1)); - EXPECT_FLOAT_EQ(y_dbl(2), theta(2)); -} TEST_F(error_message_test, newton_dbl) { bool is_newton = true; From 48e44ed819a7de8fde0bc5837359fbc181e0e7db Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Thu, 15 Jul 2021 16:13:02 +0000 Subject: [PATCH 70/88] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- .../rev/functor/algebra_solver_powell.hpp | 474 +++++++++--------- stan/math/rev/functor/kinsol_data.hpp | 11 +- .../functor/algebra_solver_newton_test.cpp | 1 - 3 files changed, 244 insertions(+), 242 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 22143f28133..530b6c5c870 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -17,253 +17,253 @@ namespace stan { namespace math { - /** - * Solve algebraic equations using Powell solver - * - * @tparam F type of equation system function - * @tparam T type of elements in the x vector - * @tparam Args types of additional parameters to the equation system functor - * - * @param[in] f Functor that evaluates the system of equations - * @param[in] x Vector of starting values (initial guess). - * @param[in, out] msgs the print stream for warning messages. - * @param[in] relative_tolerance determines the convergence criteria - * for the solution. - * @param[in] function_tolerance determines whether roots are acceptable. - * @param[in] max_num_steps maximum number of function evaluations. - * @return theta_dbl Double vector of solutions to the system of equations. - * @pre x has size greater than zero. - * @pre x has only finite elements. - * @pre f returns finite values when passed any value of x and the given args. - * @pre relative_tolerance is non-negative. - * @pre function_tolerance is non-negative. - * @pre max_num_steps is positive. - * @throw std::domain_error solver exceeds max_num_steps. - * @throw std::domain_error if the norm of the solution exceeds - * the function tolerance. - */ - template * = nullptr> - T& algebra_solver_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) { - // Construct the solver - hybrj_functor_solver hfs(f); - Eigen::HybridNonLinearSolver solver(hfs); +/** + * Solve algebraic equations using Powell solver + * + * @tparam F type of equation system function + * @tparam T type of elements in the x vector + * @tparam Args types of additional parameters to the equation system functor + * + * @param[in] f Functor that evaluates the system of equations + * @param[in] x Vector of starting values (initial guess). + * @param[in, out] msgs the print stream for warning messages. + * @param[in] relative_tolerance determines the convergence criteria + * for the solution. + * @param[in] function_tolerance determines whether roots are acceptable. + * @param[in] max_num_steps maximum number of function evaluations. + * @return theta_dbl Double vector of solutions to the system of equations. + * @pre x has size greater than zero. + * @pre x has only finite elements. + * @pre f returns finite values when passed any value of x and the given args. + * @pre relative_tolerance is non-negative. + * @pre function_tolerance is non-negative. + * @pre max_num_steps is positive. + * @throw std::domain_error solver exceeds max_num_steps. + * @throw std::domain_error if the norm of the solution exceeds + * the function tolerance. + */ +template * = nullptr> +T& algebra_solver_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) { + // Construct the solver + hybrj_functor_solver hfs(f); + Eigen::HybridNonLinearSolver solver(hfs); - // Compute theta_dbl - solver.parameters.xtol = relative_tolerance; - solver.parameters.maxfev = max_num_steps; - solver.solve(x); + // Compute theta_dbl + solver.parameters.xtol = relative_tolerance; + solver.parameters.maxfev = max_num_steps; + solver.solve(x); - // Check if the max number of steps has been exceeded - if (solver.nfev >= max_num_steps) { - [&]() STAN_COLD_PATH { - throw_domain_error("algebra_solver", "maximum number of iterations", - max_num_steps, "(", ") was exceeded in the solve."); - }(); - } - - // Check solution is a root - double system_norm = f(x).stableNorm(); - if (system_norm > function_tolerance) { - [&]() STAN_COLD_PATH { - std::ostringstream message; - message << "the norm of the algebraic function is " << system_norm - << " but should be lower than the function " - << "tolerance:"; - throw_domain_error("algebra_solver", message.str().c_str(), - function_tolerance, "", - ". Consider decreasing the relative tolerance and " - "increasing max_num_steps."); - }(); - } + // Check if the max number of steps has been exceeded + if (solver.nfev >= max_num_steps) { + [&]() STAN_COLD_PATH { + throw_domain_error("algebra_solver", "maximum number of iterations", + max_num_steps, "(", ") was exceeded in the solve."); + }(); + } - return x; + // Check solution is a root + double system_norm = f(x).stableNorm(); + if (system_norm > function_tolerance) { + [&]() STAN_COLD_PATH { + std::ostringstream message; + message << "the norm of the algebraic function is " << system_norm + << " but should be lower than the function " + << "tolerance:"; + throw_domain_error("algebra_solver", message.str().c_str(), + function_tolerance, "", + ". Consider decreasing the relative tolerance and " + "increasing max_num_steps."); + }(); } - /** - * Return the solution to the specified system of algebraic - * equations given an initial guess, and parameters and data, - * which get passed into the algebraic system. - * Use Powell's dogleg solver. - * - * The user can also specify the relative tolerance - * (xtol in Eigen's code), the function tolerance, - * and the maximum number of steps (maxfev in Eigen's code). - * - * This overload handles non-autodiff parameters. - * - * @tparam F type of equation system function - * @tparam T type of elements in the x vector - * @tparam Args types of additional parameters to the equation system functor - * - * @param[in] f Functor that evaluates the system of equations. - * @param[in] x Vector of starting values (initial guess). - * @param[in, out] msgs the print stream for warning messages. - * @param[in] relative_tolerance determines the convergence criteria - * for the solution. - * @param[in] function_tolerance determines whether roots are acceptable. - * @param[in] max_num_steps maximum number of function evaluations. - * @param[in] args additional parameters to the equation system functor. - * @return theta Vector of solutions to the system of equations. - * @pre f returns finite values when passed any value of x and the given args. - * @throw std::invalid_argument if x has size zero. - * @throw std::invalid_argument if x has non-finite elements. - * elements. - * @throw std::invalid_argument if relative_tolerance is strictly - * negative. - * @throw std::invalid_argument if function_tolerance is strictly - * negative. - * @throw std::invalid_argument if max_num_steps is not positive. - * @throw std::domain_error solver exceeds max_num_steps. - * @throw std::domain_error if the norm of the solution exceeds - * the function tolerance. - */ - template * = nullptr, - require_all_st_arithmetic* = nullptr> - Eigen::VectorXd algebra_solver_powell_impl(const F& f, const T& x, - std::ostream* const msgs, - const double relative_tolerance, - const double function_tolerance, - const int64_t max_num_steps, - const Args&... args) { - const auto& x_ref = to_ref(x); - auto x_val = to_ref(value_of(x_ref)); + return x; +} - auto f_wrt_x - = [&f, msgs, &args...](const auto& x) { return f(x, msgs, args...); }; +/** + * Return the solution to the specified system of algebraic + * equations given an initial guess, and parameters and data, + * which get passed into the algebraic system. + * Use Powell's dogleg solver. + * + * The user can also specify the relative tolerance + * (xtol in Eigen's code), the function tolerance, + * and the maximum number of steps (maxfev in Eigen's code). + * + * This overload handles non-autodiff parameters. + * + * @tparam F type of equation system function + * @tparam T type of elements in the x vector + * @tparam Args types of additional parameters to the equation system functor + * + * @param[in] f Functor that evaluates the system of equations. + * @param[in] x Vector of starting values (initial guess). + * @param[in, out] msgs the print stream for warning messages. + * @param[in] relative_tolerance determines the convergence criteria + * for the solution. + * @param[in] function_tolerance determines whether roots are acceptable. + * @param[in] max_num_steps maximum number of function evaluations. + * @param[in] args additional parameters to the equation system functor. + * @return theta Vector of solutions to the system of equations. + * @pre f returns finite values when passed any value of x and the given args. + * @throw std::invalid_argument if x has size zero. + * @throw std::invalid_argument if x has non-finite elements. + * elements. + * @throw std::invalid_argument if relative_tolerance is strictly + * negative. + * @throw std::invalid_argument if function_tolerance is strictly + * negative. + * @throw std::invalid_argument if max_num_steps is not positive. + * @throw std::domain_error solver exceeds max_num_steps. + * @throw std::domain_error if the norm of the solution exceeds + * the function tolerance. + */ +template * = nullptr, + require_all_st_arithmetic* = nullptr> +Eigen::VectorXd algebra_solver_powell_impl(const F& f, const T& x, + std::ostream* const msgs, + const double relative_tolerance, + const double function_tolerance, + const int64_t max_num_steps, + const Args&... args) { + const auto& x_ref = to_ref(x); + auto x_val = to_ref(value_of(x_ref)); - check_nonzero_size("algebra_solver_powell", "initial guess", x_val); - check_finite("algebra_solver_powell", "initial guess", x_val); - check_nonnegative("alegbra_solver_powell", "relative_tolerance", - relative_tolerance); - check_nonnegative("algebra_solver_powell", "function_tolerance", - function_tolerance); - check_positive("algebra_solver_powell", "max_num_steps", max_num_steps); - check_matching_sizes("algebra_solver", "the algebraic system's output", - f_wrt_x(x_ref), "the vector of unknowns, x,", x_ref); + auto f_wrt_x + = [&f, msgs, &args...](const auto& x) { return f(x, msgs, args...); }; - // Solve the system - return algebra_solver_powell_call_solver(f_wrt_x, x_val, msgs, - relative_tolerance, - function_tolerance, max_num_steps); - } + check_nonzero_size("algebra_solver_powell", "initial guess", x_val); + check_finite("algebra_solver_powell", "initial guess", x_val); + check_nonnegative("alegbra_solver_powell", "relative_tolerance", + relative_tolerance); + check_nonnegative("algebra_solver_powell", "function_tolerance", + function_tolerance); + check_positive("algebra_solver_powell", "max_num_steps", max_num_steps); + check_matching_sizes("algebra_solver", "the algebraic system's output", + f_wrt_x(x_ref), "the vector of unknowns, x,", x_ref); - /** - * Return the solution to the specified system of algebraic - * equations given an initial guess, and parameters and data, - * which get passed into the algebraic system. - * Use Powell's dogleg solver. - * - * The user can also specify the relative tolerance - * (xtol in Eigen's code), the function tolerance, - * and the maximum number of steps (maxfev in Eigen's code). - * - * @tparam F type of equation system function - * @tparam T1 type of elements in the x vector - * @tparam T2 type of elements in the y vector - * - * @param[in] f Functor that evaluates the system of equations. - * @param[in] x Vector of starting values (initial guess). - * @param[in] y parameter vector for the equation system. - * @param[in] dat continuous data vector for the equation system. - * @param[in] dat_int integer data vector for the equation system. - * @param[in, out] msgs the print stream for warning messages. - * @param[in] relative_tolerance determines the convergence criteria - * for the solution. - * @param[in] function_tolerance determines whether roots are acceptable. - * @param[in] max_num_steps maximum number of function evaluations. - * @return theta Vector of solutions to the system of equations. - * @throw std::invalid_argument if x has size zero. - * @throw std::invalid_argument if x has non-finite elements. - * @throw std::invalid_argument if y has non-finite elements. - * @throw std::invalid_argument if dat has non-finite elements. - * @throw std::invalid_argument if dat_int has non-finite - * elements. - * @throw std::invalid_argument if relative_tolerance is strictly - * negative. - * @throw std::invalid_argument if function_tolerance is strictly - * negative. - * @throw std::invalid_argument if max_num_steps is not positive. - * @throw std::domain_error solver exceeds max_num_steps. - * @throw std::domain_error if the norm of the solution exceeds - * the function tolerance. - */ - template * = nullptr> - Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_powell( - const F& f, const T1& x, const T2& y, const std::vector& dat, - const std::vector& dat_int, std::ostream* const msgs = nullptr, - const double relative_tolerance = 1e-10, - const double function_tolerance = 1e-6, - const int64_t max_num_steps = 1e+3) { - return algebra_solver_powell_impl(algebra_solver_adapter(f), x, msgs, - relative_tolerance, function_tolerance, - max_num_steps, y, dat, dat_int); - } + // Solve the system + return algebra_solver_powell_call_solver(f_wrt_x, x_val, msgs, + relative_tolerance, + function_tolerance, max_num_steps); +} - /** - * Return the solution to the specified system of algebraic - * equations given an initial guess, and parameters and data, - * which get passed into the algebraic system. - * Use Powell's dogleg solver. - * - * The user can also specify the relative tolerance - * (xtol in Eigen's code), the function tolerance, - * and the maximum number of steps (maxfev in Eigen's code). - * - * Signature to maintain backward compatibility, will be removed - * in the future. - * - * @tparam F type of equation system function - * @tparam T1 type of elements in the x vector - * @tparam T2 type of elements in the y vector - * - * @param[in] f Functor that evaluates the system of equations. - * @param[in] x Vector of starting values (initial guess). - * @param[in] y parameter vector for the equation system. - * @param[in] dat continuous data vector for the equation system. - * @param[in] dat_int integer data vector for the equation system. - * @param[in, out] msgs the print stream for warning messages. - * @param[in] relative_tolerance determines the convergence criteria - * for the solution. - * @param[in] function_tolerance determines whether roots are acceptable. - * @param[in] max_num_steps maximum number of function evaluations. - * @return theta Vector of solutions to the system of equations. - * @pre f returns finite values when passed any value of x and the given y, dat, - * and dat_int. - * @throw std::invalid_argument if x has size zero. - * @throw std::invalid_argument if x has non-finite elements. - * @throw std::invalid_argument if y has non-finite elements. - * @throw std::invalid_argument if dat has non-finite elements. - * @throw std::invalid_argument if dat_int has non-finite - * elements. - * @throw std::invalid_argument if relative_tolerance is strictly - * negative. - * @throw std::invalid_argument if function_tolerance is strictly - * negative. - * @throw std::invalid_argument if max_num_steps is not positive. - * @throw boost::math::evaluation_error (which is a subclass of - * std::domain_error) if solver exceeds max_num_steps. - * @throw boost::math::evaluation_error (which is a subclass of - * std::domain_error) if the norm of the solution exceeds the - * function tolerance. - */ - template * = nullptr> - Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver( - const F& f, const T1& x, const T2& y, const std::vector& dat, - const std::vector& 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 algebra_solver_powell(f, x, y, dat, dat_int, msgs, relative_tolerance, - function_tolerance, max_num_steps); - } +/** + * Return the solution to the specified system of algebraic + * equations given an initial guess, and parameters and data, + * which get passed into the algebraic system. + * Use Powell's dogleg solver. + * + * The user can also specify the relative tolerance + * (xtol in Eigen's code), the function tolerance, + * and the maximum number of steps (maxfev in Eigen's code). + * + * @tparam F type of equation system function + * @tparam T1 type of elements in the x vector + * @tparam T2 type of elements in the y vector + * + * @param[in] f Functor that evaluates the system of equations. + * @param[in] x Vector of starting values (initial guess). + * @param[in] y parameter vector for the equation system. + * @param[in] dat continuous data vector for the equation system. + * @param[in] dat_int integer data vector for the equation system. + * @param[in, out] msgs the print stream for warning messages. + * @param[in] relative_tolerance determines the convergence criteria + * for the solution. + * @param[in] function_tolerance determines whether roots are acceptable. + * @param[in] max_num_steps maximum number of function evaluations. + * @return theta Vector of solutions to the system of equations. + * @throw std::invalid_argument if x has size zero. + * @throw std::invalid_argument if x has non-finite elements. + * @throw std::invalid_argument if y has non-finite elements. + * @throw std::invalid_argument if dat has non-finite elements. + * @throw std::invalid_argument if dat_int has non-finite + * elements. + * @throw std::invalid_argument if relative_tolerance is strictly + * negative. + * @throw std::invalid_argument if function_tolerance is strictly + * negative. + * @throw std::invalid_argument if max_num_steps is not positive. + * @throw std::domain_error solver exceeds max_num_steps. + * @throw std::domain_error if the norm of the solution exceeds + * the function tolerance. + */ +template * = nullptr> +Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_powell( + const F& f, const T1& x, const T2& y, const std::vector& dat, + const std::vector& dat_int, std::ostream* const msgs = nullptr, + const double relative_tolerance = 1e-10, + const double function_tolerance = 1e-6, + const int64_t max_num_steps = 1e+3) { + return algebra_solver_powell_impl(algebra_solver_adapter(f), x, msgs, + relative_tolerance, function_tolerance, + max_num_steps, y, dat, dat_int); +} + +/** + * Return the solution to the specified system of algebraic + * equations given an initial guess, and parameters and data, + * which get passed into the algebraic system. + * Use Powell's dogleg solver. + * + * The user can also specify the relative tolerance + * (xtol in Eigen's code), the function tolerance, + * and the maximum number of steps (maxfev in Eigen's code). + * + * Signature to maintain backward compatibility, will be removed + * in the future. + * + * @tparam F type of equation system function + * @tparam T1 type of elements in the x vector + * @tparam T2 type of elements in the y vector + * + * @param[in] f Functor that evaluates the system of equations. + * @param[in] x Vector of starting values (initial guess). + * @param[in] y parameter vector for the equation system. + * @param[in] dat continuous data vector for the equation system. + * @param[in] dat_int integer data vector for the equation system. + * @param[in, out] msgs the print stream for warning messages. + * @param[in] relative_tolerance determines the convergence criteria + * for the solution. + * @param[in] function_tolerance determines whether roots are acceptable. + * @param[in] max_num_steps maximum number of function evaluations. + * @return theta Vector of solutions to the system of equations. + * @pre f returns finite values when passed any value of x and the given y, dat, + * and dat_int. + * @throw std::invalid_argument if x has size zero. + * @throw std::invalid_argument if x has non-finite elements. + * @throw std::invalid_argument if y has non-finite elements. + * @throw std::invalid_argument if dat has non-finite elements. + * @throw std::invalid_argument if dat_int has non-finite + * elements. + * @throw std::invalid_argument if relative_tolerance is strictly + * negative. + * @throw std::invalid_argument if function_tolerance is strictly + * negative. + * @throw std::invalid_argument if max_num_steps is not positive. + * @throw boost::math::evaluation_error (which is a subclass of + * std::domain_error) if solver exceeds max_num_steps. + * @throw boost::math::evaluation_error (which is a subclass of + * std::domain_error) if the norm of the solution exceeds the + * function tolerance. + */ +template * = nullptr> +Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver( + const F& f, const T1& x, const T2& y, const std::vector& dat, + const std::vector& 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 algebra_solver_powell(f, x, y, dat, dat_int, msgs, relative_tolerance, + function_tolerance, max_num_steps); +} /** * Return the solution to the specified system of algebraic diff --git a/stan/math/rev/functor/kinsol_data.hpp b/stan/math/rev/functor/kinsol_data.hpp index a97cbc0ea6a..8557c29dace 100644 --- a/stan/math/rev/functor/kinsol_data.hpp +++ b/stan/math/rev/functor/kinsol_data.hpp @@ -71,10 +71,13 @@ class kinsol_system_data { Eigen::Map f_eval_map(N_VGetArrayPointer(f_eval), explicit_system->N_); - auto result = apply([&](const auto&... args) { - return explicit_system->f_(x_eigen, explicit_system->msgs_, args...); - }, explicit_system->args_tuple_); - check_matching_sizes("", "the algebraic system's output", result, "the vector of unknowns, x,", f_eval_map); + auto result = apply( + [&](const auto&... args) { + return explicit_system->f_(x_eigen, explicit_system->msgs_, args...); + }, + explicit_system->args_tuple_); + check_matching_sizes("", "the algebraic system's output", result, + "the vector of unknowns, x,", f_eval_map); f_eval_map = result; return 0; } diff --git a/test/unit/math/rev/functor/algebra_solver_newton_test.cpp b/test/unit/math/rev/functor/algebra_solver_newton_test.cpp index c15a7538c4c..586659012ef 100644 --- a/test/unit/math/rev/functor/algebra_solver_newton_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_newton_test.cpp @@ -12,7 +12,6 @@ ////////////////////////////////////////////////////////////////////////// // Tests for newton solver. - TEST_F(error_message_test, newton_dbl) { bool is_newton = true; error_conditions_test(non_linear_eq_functor(), y_3, is_newton); From 759161af2bb9e21d4441d9d9a48cb16904dc828d Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Thu, 15 Jul 2021 12:13:42 -0400 Subject: [PATCH 71/88] add back tests for newton solver --- .../functor/algebra_solver_newton_test.cpp | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/test/unit/math/rev/functor/algebra_solver_newton_test.cpp b/test/unit/math/rev/functor/algebra_solver_newton_test.cpp index c15a7538c4c..7925bdb518e 100644 --- a/test/unit/math/rev/functor/algebra_solver_newton_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_newton_test.cpp @@ -12,6 +12,33 @@ ////////////////////////////////////////////////////////////////////////// // Tests for newton solver. +TEST_F(algebra_solver_simple_eq_test, newton_dbl) { + bool is_newton = true; + Eigen::VectorXd theta = simple_eq_test(simple_eq_functor(), y_dbl, is_newton); +} + +TEST_F(algebra_solver_simple_eq_test, newton_tuned_dbl) { + bool is_newton = true; + Eigen::VectorXd theta = simple_eq_test(simple_eq_functor(), y_dbl, is_newton, + true, scale_step, xtol, ftol, maxfev); +} + +TEST_F(algebra_solver_simple_eq_nopara_test, newton_dbl) { + using stan::math::algebra_solver_newton; + Eigen::VectorXd theta = algebra_solver_newton(simple_eq_functor_nopara(), x, + y_dummy, dat, dummy_dat_int); + EXPECT_EQ(20, theta(0)); + EXPECT_EQ(2, theta(1)); +} + +TEST_F(algebra_solver_non_linear_eq_test, newton_dbl) { + bool is_newton = true; + Eigen::VectorXd theta + = non_linear_eq_test(non_linear_eq_functor(), y_dbl, is_newton); + EXPECT_FLOAT_EQ(-y_dbl(0), theta(0)); + EXPECT_FLOAT_EQ(-y_dbl(1), theta(1)); + EXPECT_FLOAT_EQ(y_dbl(2), theta(2)); +} TEST_F(error_message_test, newton_dbl) { bool is_newton = true; From bd72a6eb6cb49d3944f0b6d42aedb2b6236f3348 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Tue, 3 Aug 2021 13:29:23 -0500 Subject: [PATCH 72/88] Respond to comments and temporarily patch failing test. --- stan/math/prim/functor/algebra_solver_fp.hpp | 4 ---- stan/math/rev/functor/algebra_solver_fp.hpp | 7 +++---- stan/math/rev/functor/algebra_solver_newton.hpp | 7 +++---- stan/math/rev/functor/algebra_solver_powell.hpp | 7 +++---- test/unit/math/rev/functor/algebra_solver_fp_test.cpp | 3 +++ 5 files changed, 12 insertions(+), 16 deletions(-) diff --git a/stan/math/prim/functor/algebra_solver_fp.hpp b/stan/math/prim/functor/algebra_solver_fp.hpp index 8bc269f74a7..ec886d8cc4b 100644 --- a/stan/math/prim/functor/algebra_solver_fp.hpp +++ b/stan/math/prim/functor/algebra_solver_fp.hpp @@ -68,11 +68,7 @@ struct KinsolFixedPointEnv { nv_f_scal_(N_VNew_Serial(N_)) { for (int i = 0; i < N_; ++i) { NV_Ith_S(nv_x_, i) = stan::math::value_of(x(i)); - } - for (int i = 0; i < N_; ++i) { NV_Ith_S(nv_u_scal_, i) = stan::math::value_of(u_scale[i]); - } - for (int i = 0; i < N_; ++i) { NV_Ith_S(nv_f_scal_, i) = stan::math::value_of(f_scale[i]); } } diff --git a/stan/math/rev/functor/algebra_solver_fp.hpp b/stan/math/rev/functor/algebra_solver_fp.hpp index ca7c538a0bb..f5304fb9cfa 100644 --- a/stan/math/rev/functor/algebra_solver_fp.hpp +++ b/stan/math/rev/functor/algebra_solver_fp.hpp @@ -135,16 +135,15 @@ Eigen::Matrix algebra_solver_fp_impl( using ret_type = Eigen::Matrix; arena_t ret = theta_dbl; - auto Jf_xT_lu_ptr = make_unsafe_chainable_ptr( + auto Jf_x_T_lu_ptr = make_unsafe_chainable_ptr( (Eigen::MatrixXd::Identity(x.size(), x.size()) - Jf_x) .transpose() .eval() .partialPivLu()); reverse_pass_callback( - [f, ret, arena_args_tuple, Jf_xT_lu_ptr, msgs]() mutable { - // Contract specificities with inverse Jacobian of f with respect to x. - Eigen::VectorXd eta = Jf_xT_lu_ptr->solve(ret.adj().eval()); + [f, ret, arena_args_tuple, Jf_x_T_lu_ptr, msgs]() mutable { + Eigen::VectorXd eta = Jf_x_T_lu_ptr->solve(ret.adj().eval()); // Contract with Jacobian of f with respect to y using a nested reverse // autodiff pass. diff --git a/stan/math/rev/functor/algebra_solver_newton.hpp b/stan/math/rev/functor/algebra_solver_newton.hpp index abae90fb100..51809bed762 100644 --- a/stan/math/rev/functor/algebra_solver_newton.hpp +++ b/stan/math/rev/functor/algebra_solver_newton.hpp @@ -176,13 +176,12 @@ Eigen::Matrix algebra_solver_newton_impl( using ret_type = Eigen::Matrix; arena_t ret = theta_dbl; - auto Jf_xT_lu_ptr + auto Jf_x_T_lu_ptr = make_unsafe_chainable_ptr(Jf_x.transpose().partialPivLu()); // Lu - reverse_pass_callback([f, ret, arena_args_tuple, Jf_xT_lu_ptr, + reverse_pass_callback([f, ret, arena_args_tuple, Jf_x_T_lu_ptr, msgs]() mutable { - // Contract specificities with inverse Jacobian of f with respect to x. - Eigen::VectorXd eta = -Jf_xT_lu_ptr->solve(ret.adj().eval()); + Eigen::VectorXd eta = -Jf_x_T_lu_ptr->solve(ret.adj().eval()); // Contract with Jacobian of f with respect to y using a nested reverse // autodiff pass. diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 530b6c5c870..1be5e029533 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -367,15 +367,14 @@ Eigen::Matrix algebra_solver_powell_impl( jacobian(f_wrt_x, x_val, f_x, Jf_x); using ret_type = Eigen::Matrix; - auto Jf_xT_lu_ptr + auto Jf_x_T_lu_ptr = make_unsafe_chainable_ptr(Jf_x.transpose().partialPivLu()); // Lu arena_t ret = x_val; - reverse_pass_callback([f, ret, arena_args_tuple, Jf_xT_lu_ptr, + reverse_pass_callback([f, ret, arena_args_tuple, Jf_x_T_lu_ptr, msgs]() mutable { - // Contract specificities with inverse Jacobian of f with respect to x. - Eigen::VectorXd eta = -Jf_xT_lu_ptr->solve(ret.adj().eval()); + Eigen::VectorXd eta = -Jf_x_T_lu_ptr->solve(ret.adj().eval()); // Contract with Jacobian of f with respect to y using a nested reverse // autodiff pass. diff --git a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp index b4c9cd3c142..be43bd76dbc 100644 --- a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp @@ -484,6 +484,9 @@ TEST_F(FP_direct_prod_func_test, algebra_solver_fp) { EXPECT_NEAR(g_newton[j], g_fp[j], 1.e-8); } } + + // TODO(@jgaeb): Can we remove this? + stan::math::recover_memory(); } TEST_F(FP_2d_func_test, exception_handling) { From ecefac5a4eb3c70bbb230575b8e22510dc0acdec Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Tue, 3 Aug 2021 13:39:16 -0500 Subject: [PATCH 73/88] Add unsafe chainable object unit tests. --- .../math/rev/core/chainable_object_test.cpp | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/test/unit/math/rev/core/chainable_object_test.cpp b/test/unit/math/rev/core/chainable_object_test.cpp index 5523b05ed63..2610ee0bd51 100644 --- a/test/unit/math/rev/core/chainable_object_test.cpp +++ b/test/unit/math/rev/core/chainable_object_test.cpp @@ -66,3 +66,68 @@ TEST(AgradRev, make_chainable_ptr_nested_test) { EXPECT_EQ((ChainableObjectTest::counter), 1); } + +class UnsafeChainableObjectTest { + public: + static int counter; + + ~UnsafeChainableObjectTest() { counter++; } +}; + +int UnsafeChainableObjectTest::counter = 0; + +TEST(AgradRev, unsafe_chainable_object_test) { + { + auto ptr = new stan::math::unsafe_chainable_object( + UnsafeChainableObjectTest()); + UnsafeChainableObjectTest::counter = 0; + } + + EXPECT_EQ((UnsafeChainableObjectTest::counter), 0); + stan::math::recover_memory(); + EXPECT_EQ((UnsafeChainableObjectTest::counter), 1); +} + +TEST(AgradRev, unsafe_chainable_object_nested_test) { + stan::math::start_nested(); + + { + auto ptr = new stan::math::unsafe_chainable_object( + UnsafeChainableObjectTest()); + UnsafeChainableObjectTest::counter = 0; + } + + EXPECT_EQ((UnsafeChainableObjectTest::counter), 0); + + stan::math::recover_memory_nested(); + + EXPECT_EQ((UnsafeChainableObjectTest::counter), 1); +} + +TEST(AgradRev, make_unsafe_chainable_ptr_test) { + { + UnsafeChainableObjectTest* ptr + = stan::math::make_unsafe_chainable_ptr(UnsafeChainableObjectTest()); + UnsafeChainableObjectTest::counter = 0; + } + + EXPECT_EQ((UnsafeChainableObjectTest::counter), 0); + stan::math::recover_memory(); + EXPECT_EQ((UnsafeChainableObjectTest::counter), 1); +} + +TEST(AgradRev, make_unsafe_chainable_ptr_nested_test) { + stan::math::start_nested(); + + { + UnsafeChainableObjectTest* ptr + = stan::math::make_unsafe_chainable_ptr(UnsafeChainableObjectTest()); + UnsafeChainableObjectTest::counter = 0; + } + + EXPECT_EQ((UnsafeChainableObjectTest::counter), 0); + + stan::math::recover_memory_nested(); + + EXPECT_EQ((UnsafeChainableObjectTest::counter), 1); +} From 6a2b5b9867d5cb2d9d2880831918c2420f0bc3a6 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Mon, 9 Aug 2021 16:57:12 -0500 Subject: [PATCH 74/88] Add variadic tests for newton and powell solvers. --- .../functor/algebra_solver_newton_test.cpp | 22 +++++ .../math/rev/functor/algebra_solver_test.cpp | 22 +++++ .../math/rev/functor/util_algebra_solver.hpp | 88 +++++++++++++++++++ 3 files changed, 132 insertions(+) diff --git a/test/unit/math/rev/functor/algebra_solver_newton_test.cpp b/test/unit/math/rev/functor/algebra_solver_newton_test.cpp index 7925bdb518e..ffc47a63c80 100644 --- a/test/unit/math/rev/functor/algebra_solver_newton_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_newton_test.cpp @@ -235,3 +235,25 @@ TEST_F(degenerate_eq_test, newton_guess2) { EXPECT_NEAR(J2(k, l), g[l], tolerance); } } + +TEST_F(variadic_test, newton) { + using stan::math::var; + bool is_newton = true; + for (int k = 0; k < n_x; k++) { + var y_1 = y_1_dbl; + var y_2 = y_2_dbl; + var y_3 = y_3_dbl; + + Eigen::Matrix theta + = variadic_eq_test(variadic_eq_functor(), A, y_1, y_2, y_3, i, + is_newton, scaling_step_size, relative_tolerance, + function_tolerance, max_num_steps); + + AVEC y_vec = createAVEC(y_1, y_2, y_3); + VEC g; + theta(k).grad(y_vec, g); + + for (int i = 0; i < n_y; i++) + EXPECT_NEAR(J(k, i), g[i], 1e-6); + } +} diff --git a/test/unit/math/rev/functor/algebra_solver_test.cpp b/test/unit/math/rev/functor/algebra_solver_test.cpp index cfbb010d750..1a6af27173b 100644 --- a/test/unit/math/rev/functor/algebra_solver_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_test.cpp @@ -231,3 +231,25 @@ TEST_F(degenerate_eq_test, powell_guess2) { EXPECT_NEAR(J2(k, l), g[l], tolerance); } } + +TEST_F(variadic_test, powell) { + using stan::math::var; + bool is_newton = true; + for (int k = 0; k < n_x; k++) { + var y_1 = y_1_dbl; + var y_2 = y_2_dbl; + var y_3 = y_3_dbl; + + Eigen::Matrix theta + = variadic_eq_test(variadic_eq_functor(), A, y_1, y_2, y_3, i, + is_newton, scaling_step_size, relative_tolerance, + function_tolerance, max_num_steps); + + AVEC y_vec = createAVEC(y_1, y_2, y_3); + VEC g; + theta(k).grad(y_vec, g); + + for (int i = 0; i < n_y; i++) + EXPECT_NEAR(J(k, i), g[i], 1e-6); + } +} diff --git a/test/unit/math/rev/functor/util_algebra_solver.hpp b/test/unit/math/rev/functor/util_algebra_solver.hpp index 3bc941cd453..f01f120a34b 100644 --- a/test/unit/math/rev/functor/util_algebra_solver.hpp +++ b/test/unit/math/rev/functor/util_algebra_solver.hpp @@ -135,6 +135,49 @@ class degenerate_eq_test : public ::testing::Test { std::vector dat_int; }; +class variadic_test : public ::testing::Test { + protected: + void SetUp() override { + n_x = 2; + n_y = 3; + + y_1_dbl = 5; + y_2_dbl = 4; + y_3_dbl = 2; + + Eigen::MatrixXd A_(n_x, n_x); + A_ << 1, 2, 2, 1; + A = A_; + + x_var = stan::math::to_vector({1, 3}); + + Eigen::MatrixXd J_(n_x, n_y); + J_ << 4, 5, 0, 0, 0, 1; + J = J_; + + i = 3; + } + + void TearDown() override { stan::math::recover_memory(); } + + int n_x; + int n_y; + Eigen::MatrixXd A_; + Eigen::Matrix A; + double y_1_dbl; + double y_2_dbl; + double y_3_dbl; + int i; + Eigen::Matrix x_var; + + double scaling_step_size = 1e-3; + double relative_tolerance = 1e-6; + double function_tolerance = 1e-3; + int max_num_steps = 1e+2; + + Eigen::MatrixXd J; +}; + /* wrapper function that either calls the dogleg or the newton solver. */ template Eigen::Matrix general_algebra_solver( @@ -233,6 +276,20 @@ struct degenerate_eq_functor { } }; +struct variadic_eq_functor { + template + inline Eigen::Matrix, Eigen::Dynamic, 1> + operator()(const T1& x, std::ostream* pstream__, const T2& A, const T3& y_1, + const T3& y_2, const T3& y_3, const T4& i) const { + Eigen::Matrix, Eigen::Dynamic, 1> z(2); + z(0) = x(0) - y_1 * y_2; + z(1) = x(1) - y_3; + for (int j = 0; j < i; ++j) + z = A * z; + return z; + } +}; + /* template code for running tests in the prim and rev regime */ template @@ -398,3 +455,34 @@ inline void max_num_steps_test(Eigen::Matrix& y, function_tolerance, max_num_steps), std::domain_error, msg); } + +template +Eigen::Matrix variadic_eq_test( + const F& f, const Eigen::Matrix A, + const stan::math::var& y_1, const stan::math::var& y_2, const + stan::math::var& y_3, const int i, bool is_newton = false, double + scaling_step_size = 1e-3, double relative_tolerance = 1e-10, double + function_tolerance = 1e-6, int32_t max_num_steps = 1e+3) { + using stan::math::algebra_solver_newton; + using stan::math::algebra_solver_powell; + using stan::math::var; + + int n_x = 2; + Eigen::VectorXd x(2); + x << 1, 1; // initial guess + + Eigen::Matrix theta; + + theta = is_newton ? + algebra_solver_newton_impl(variadic_eq_functor(), x, &std::cout, + scaling_step_size, function_tolerance, + max_num_steps, A, y_1, y_2, y_3, i) : + algebra_solver_powell_impl(variadic_eq_functor(), x, &std::cout, + relative_tolerance, function_tolerance, + max_num_steps, A, y_1, y_2, y_3, i); + + EXPECT_NEAR(20, value_of(theta(0)), 1e-6); + EXPECT_NEAR(2, value_of(theta(1)), 1e-6); + + return theta; +} From 24e943a206bd738d54e6fc94d271e1cbf66b4b23 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Tue, 10 Aug 2021 15:07:20 -0500 Subject: [PATCH 75/88] Undo changes to fixed point solver. --- stan/math/prim/functor/algebra_solver_fp.hpp | 302 ------------ stan/math/rev/functor/algebra_solver_fp.hpp | 436 +++++++++++++----- .../rev/functor/algebra_solver_fp_test.cpp | 269 ++++++----- 3 files changed, 482 insertions(+), 525 deletions(-) delete mode 100644 stan/math/prim/functor/algebra_solver_fp.hpp diff --git a/stan/math/prim/functor/algebra_solver_fp.hpp b/stan/math/prim/functor/algebra_solver_fp.hpp deleted file mode 100644 index ec886d8cc4b..00000000000 --- a/stan/math/prim/functor/algebra_solver_fp.hpp +++ /dev/null @@ -1,302 +0,0 @@ -#ifndef STAN_MATH_PRIM_FUNCTOR_FP_SOLVER_HPP -#define STAN_MATH_PRIM_FUNCTOR_FP_SOLVER_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace stan { -namespace math { - -/** - * KINSOL algebraic system data holder that handles - * construction & destruction of SUNDIALS data, as well as - * auxiliary data that will be used for functor evaluation. - * - * @tparam F functor type for system function. - * @tparam T_u type of scaling vector for unknowns. We allow - * it to be @c var because scaling could be parameter - * dependent. Internally these params are converted to data - * because scaling is applied. - * @tparam T_f type of scaling vector for residual. We allow - * it to be @c var because scaling could be parameter - * dependent. Internally these params are converted to data - * because scaling is applied. - * @tparam Args types of additional parameters to the equation system functor. - */ -template -struct KinsolFixedPointEnv { - /** RHS functor. */ - const F& f_; - /** system size */ - const size_t N_; - /** message stream */ - std::ostream* msgs_; - /** arguments and parameters */ - std::tuple args_tuple_; - /** KINSOL memory block */ - void* mem_; - /** NVECTOR for unknowns */ - N_Vector nv_x_; - /** NVECTOR for scaling u */ - N_Vector nv_u_scal_; - /** NVECTOR for scaling f */ - N_Vector nv_f_scal_; - - KinsolFixedPointEnv(const F& f, const Eigen::MatrixXd x, - const std::vector& u_scale, - const std::vector& f_scale, std::ostream* const msgs, - const Args&... args) - : f_(f), - N_(x.size()), - msgs_(msgs), - args_tuple_(args...), - mem_(KINCreate()), - nv_x_(N_VNew_Serial(N_)), - nv_u_scal_(N_VNew_Serial(N_)), - nv_f_scal_(N_VNew_Serial(N_)) { - for (int i = 0; i < N_; ++i) { - NV_Ith_S(nv_x_, i) = stan::math::value_of(x(i)); - NV_Ith_S(nv_u_scal_, i) = stan::math::value_of(u_scale[i]); - NV_Ith_S(nv_f_scal_, i) = stan::math::value_of(f_scale[i]); - } - } - - ~KinsolFixedPointEnv() { - N_VDestroy_Serial(nv_x_); - N_VDestroy_Serial(nv_u_scal_); - N_VDestroy_Serial(nv_f_scal_); - KINFree(&mem_); - } - - /** Implements the user-defined function passed to KINSOL. */ - static int kinsol_f_system(const N_Vector x, const N_Vector f, - void* const user_data) { - auto g = static_cast*>( - user_data); - Eigen::Map(N_VGetArrayPointer(f), g->N_) = apply( - [&x, &g](const auto&... args) { - return g->f_(Eigen::Map(NV_DATA_S(x), g->N_), - g->msgs_, args...); - }, - g->args_tuple_); - return 0; - } -}; - -/** - * Solve algebraic equations using KINSOL fixed point solver - * - * @tparam F type of the equation system functor f - * @tparam T_u type of scaling vector for unknowns. We allow - * it to be @c var because scaling could be parameter - * dependent. Internally these params are converted to data - * because scaling is applied. - * @tparam T_f type of scaling vector for residual. We allow - * it to be @c var because scaling could be parameter - * dependent. Internally these params are converted to data - * because scaling is applied. - * @param x initial point and final solution. - * @param env KINSOL solution environment - * @param f_tol Function tolerance - * @param max_num_steps max nb. of iterations. - */ -template * = nullptr> -Eigen::VectorXd kinsol_solve_fp(const F& f, const T& x, - const double function_tolerance, - const double max_num_steps, - const std::vector& u_scale, - const std::vector& f_scale, - std::ostream* const msgs, const Args&... args) { - KinsolFixedPointEnv env(f, x, u_scale, f_scale, msgs, - args...); - const int N = x.size(); - constexpr int default_anderson_depth = 4; - const int anderson_depth = std::min(N, default_anderson_depth); - - check_flag_sundials(KINSetNumMaxIters(env.mem_, max_num_steps), - "KINSetNumMaxIters"); - check_flag_sundials(KINSetMAA(env.mem_, anderson_depth), "KINSetMAA"); - check_flag_sundials(KINInit(env.mem_, &env.kinsol_f_system, env.nv_x_), - "KINInit"); - check_flag_sundials(KINSetFuncNormTol(env.mem_, function_tolerance), - "KINSetFuncNormTol"); - check_flag_sundials(KINSetUserData(env.mem_, static_cast(&env)), - "KINSetUserData"); - - check_flag_kinsol( - KINSol(env.mem_, env.nv_x_, KIN_FP, env.nv_u_scal_, env.nv_f_scal_), - max_num_steps); - - return Eigen::Map(N_VGetArrayPointer(env.nv_x_), N); -} - -/** - * Return a fixed pointer to the specified system of algebraic - * equations of form - * \[ - * x = F(x; theta) - * \] - * given an initial guess \(x\), and parameters \(theta\) and data. Use the - * KINSOL solver from the SUNDIALS suite. - * - * The user can also specify the scaling controls, the function - * tolerance, and the maximum number of steps. - * - * This function is overloaded to handle both constant and var-type parameters. - * This overload handles var parameters, and checks the input, calls the - * algebraic solver, and appropriately handles derivative propagation through - * the `reverse_pass_callback`. - * - * @tparam F type of equation system function. - * @tparam T type of initial guess vector. - * @tparam T_u type of scaling vector for unknowns. We allow - * it to be @c var because scaling could be parameter - * dependent. Internally these params are converted to data - * because scaling is applied. - * @tparam T_f type of scaling vector for residual. We allow - * it to be @c var because scaling could be parameter - * dependent. Internally these params are converted to data - * because scaling is applied. - * - * @param[in] f functor that evaluated the system of equations. - * @param[in] x vector of starting values. - * @param[in, out] msgs the print stream for warning messages. - * @param[in] u_scale diagonal scaling matrix elements \(Du\) - * such that \(Du x\) has all components roughly the same - * magnitude when \(x\) is close to a solution. - * (ref. KINSOL user guide chap.2 sec. "Scaling") - * @param[in] f_scale diagonal scaling matrix elements such - * that \(Df (x - f(x))\) has all components roughly the same - * magnitude when \(x\) is not too close to a solution. - * (ref. KINSOL user guide chap.2 sec. "Scaling") - * @param[in] function_tolerance Function-norm stopping tolerance. - * @param[in] max_num_steps maximum number of function evaluations. - * @param[in] args additional parameters to the equation system functor. - * @pre f returns finite values when passed any value of x and the given args. - * @throw std::invalid_argument if x has size zero. - * @throw std::invalid_argument if x has non-finite elements. - * @throw std::invalid_argument if scaled_step_size is strictly - * negative. - * @throw std::invalid_argument if function_tolerance is strictly - * negative. - * @throw std::invalid_argument if max_num_steps is not positive. - * @throw boost::math::evaluation_error (which is a subclass of - * std::runtime_error) if solver exceeds max_num_steps. - */ -template * = nullptr, - require_all_st_arithmetic* = nullptr> -Eigen::VectorXd algebra_solver_fp_impl(const F& f, const T& x, - std::ostream* const msgs, - const std::vector& u_scale, - const std::vector& f_scale, - const double function_tolerance, - const int max_num_steps, - const Args&... args) { - const auto& x_ref = to_ref(value_of(x)); - - check_nonzero_size("algebra_solver_fp", "initial guess", x_ref); - check_finite("algebra_solver_fp", "initial guess", x_ref); - check_nonnegative("algebra_solver_fp", "u_scale", u_scale); - check_nonnegative("algebra_solver_fp", "f_scale", f_scale); - check_nonnegative("algebra_solver_fp", "function_tolerance", - function_tolerance); - check_positive("algebra_solver_fp", "max_num_steps", max_num_steps); - check_matching_sizes("algebra_solver_fp", "the algebraic system's output", - value_of(f(x_ref, msgs, args...)), - "the vector of unknowns, x,", x_ref); - - return kinsol_solve_fp(f, x_ref, function_tolerance, max_num_steps, u_scale, - f_scale, msgs, args...); -} - -/** - * Return a fixed pointer to the specified system of algebraic - * equations of form - * - * x = F(x; theta) - * - * given an initial guess x, and parameters theta and data. Use the - * KINSOL solver from the SUNDIALS suite. - * - * The user can also specify the scaling controls, the function - * tolerance, and the maximum number of steps. - * - * Signature to maintain backward compatibility, will be removed - * in the future. - * - * @tparam F type of equation system function. - * @tparam T type of initial guess vector. The final solution - * type doesn't depend on initial guess type, - * but we allow initial guess to be either data or param. - * @tparam T_u type of scaling vector for unknowns. We allow - * it to be @c var because scaling could be parameter - * dependent. Internally these params are converted to data - * because scaling is applied. - * @tparam T_f type of scaling vector for residual. We allow - * it to be @c var because scaling could be parameter - * dependent. Internally these params are converted to data - * because scaling is applied. - * - * @param[in] f Functor that evaluated the system of equations. - * @param[in] x Vector of starting values. - * @param[in] y Parameter vector for the equation system. The function - * is overloaded to treat y as a vector of doubles or of a - * a template type T. - * @param[in] dat Continuous data vector for the equation system. - * @param[in] dat_int Integer data vector for the equation system. - * @param[in, out] msgs The print stream for warning messages. - * @param[in] u_scale diagonal scaling matrix elements Du - * such that Du*x has all components roughly the same - * magnitude when x is close to a solution. - * (ref. KINSOL user guide chap.2 sec. "Scaling") - * @param[in] f_scale diagonal scaling matrix elements such - * that Df*(x-f(x)) has all components roughly the same - * magnitude when x is not too close to a solution. - * (ref. KINSOL user guide chap.2 sec. "Scaling") - * @param[in] f_tol Function-norm stopping tolerance. - * @param[in] max_num_steps maximum number of function evaluations. - * @pre f returns finite values when passed any value of x and the given y, dat, - * and dat_int. - * @throw std::invalid_argument if x has size zero. - * @throw std::invalid_argument if x has non-finite elements. - * @throw std::invalid_argument if dat has non-finite elements. - * @throw std::invalid_argument if dat_int has non-finite elements. - * @throw std::invalid_argument if scaled_step_size is strictly - * negative. - * @throw std::invalid_argument if function_tolerance is strictly - * negative. - * @throw std::invalid_argument if max_num_steps is not positive. - * @throw boost::math::evaluation_error (which is a subclass of - * std::runtime_error) if solver exceeds max_num_steps. - */ -template * = nullptr, - require_eigen_vector_t* = nullptr> -Eigen::Matrix, Eigen::Dynamic, 1> algebra_solver_fp( - const F& f, const T1& x, const T2& y, const std::vector& dat, - const std::vector& dat_int, const std::vector& u_scale, - const std::vector& f_scale, std::ostream* const msgs = nullptr, - double function_tolerance = 1e-8, int max_num_steps = 200) { - return algebra_solver_fp_impl(algebra_solver_adapter(f), to_ref(x), msgs, - u_scale, f_scale, function_tolerance, - max_num_steps, to_ref(y), dat, dat_int); -} - -} // namespace math -} // namespace stan - -#endif diff --git a/stan/math/rev/functor/algebra_solver_fp.hpp b/stan/math/rev/functor/algebra_solver_fp.hpp index f5304fb9cfa..3fe66b399f5 100644 --- a/stan/math/rev/functor/algebra_solver_fp.hpp +++ b/stan/math/rev/functor/algebra_solver_fp.hpp @@ -7,11 +7,10 @@ #include #include #include +#include +#include +#include #include -#include -#include -#include -#include #include #include #include @@ -24,46 +23,304 @@ namespace stan { namespace math { +/** + * KINSOL algebraic system data holder that handles + * construction & destruction of SUNDIALS data, as well as + * auxiliary data that will be used for functor evaluation. + * + * @tparam F functor type for system function. + */ +template +struct KinsolFixedPointEnv { + /** RHS functor. */ + const F& f_; + /** val of params for @c y_ to refer to when + params are @c var type */ + const Eigen::VectorXd y_dummy; + /** ref to val of params */ + const Eigen::VectorXd& y_; + /** system size */ + const size_t N_; + /** nb. of params */ + const size_t M_; + /** real data */ + const std::vector& x_r_; + /** integer data */ + const std::vector& x_i_; + /** message stream */ + std::ostream* msgs_; + /** KINSOL memory block */ + void* mem_; + /** NVECTOR for unknowns */ + N_Vector nv_x_; + /** NVECTOR for scaling u */ + N_Vector nv_u_scal_; + /** NVECTOR for scaling f */ + N_Vector nv_f_scal_; + + /** Constructor when y is data */ + template + KinsolFixedPointEnv(const F& f, const Eigen::Matrix& x, + const Eigen::VectorXd& y, const std::vector& x_r, + const std::vector& x_i, std::ostream* msgs, + const std::vector& u_scale, + const std::vector& f_scale) + : f_(f), + y_dummy(), + y_(y), + N_(x.size()), + M_(y.size()), + x_r_(x_r), + x_i_(x_i), + msgs_(msgs), + mem_(KINCreate()), + nv_x_(N_VNew_Serial(N_)), + nv_u_scal_(N_VNew_Serial(N_)), + nv_f_scal_(N_VNew_Serial(N_)) { + for (int i = 0; i < N_; ++i) { + NV_Ith_S(nv_x_, i) = stan::math::value_of(x(i)); + NV_Ith_S(nv_u_scal_, i) = stan::math::value_of(u_scale[i]); + NV_Ith_S(nv_f_scal_, i) = stan::math::value_of(f_scale[i]); + } + } + + /** Constructor when y is param */ + template + KinsolFixedPointEnv(const F& f, const Eigen::Matrix& x, + const Eigen::Matrix& y, + const std::vector& x_r, + const std::vector& x_i, std::ostream* msgs, + const std::vector& u_scale, + const std::vector& f_scale) + : f_(f), + y_dummy(stan::math::value_of(y)), + y_(y_dummy), + N_(x.size()), + M_(y.size()), + x_r_(x_r), + x_i_(x_i), + msgs_(msgs), + mem_(KINCreate()), + nv_x_(N_VNew_Serial(N_)), + nv_u_scal_(N_VNew_Serial(N_)), + nv_f_scal_(N_VNew_Serial(N_)) { + for (int i = 0; i < N_; ++i) { + NV_Ith_S(nv_x_, i) = stan::math::value_of(x(i)); + NV_Ith_S(nv_u_scal_, i) = stan::math::value_of(u_scale[i]); + NV_Ith_S(nv_f_scal_, i) = stan::math::value_of(f_scale[i]); + } + } + + ~KinsolFixedPointEnv() { + N_VDestroy_Serial(nv_x_); + N_VDestroy_Serial(nv_u_scal_); + N_VDestroy_Serial(nv_f_scal_); + KINFree(&mem_); + } + + /** Implements the user-defined function passed to KINSOL. */ + static int kinsol_f_system(N_Vector x, N_Vector f, void* user_data) { + auto g = static_cast*>(user_data); + Eigen::VectorXd x_eigen(Eigen::Map(NV_DATA_S(x), g->N_)); + Eigen::Map(N_VGetArrayPointer(f), g->N_) + = g->f_(x_eigen, g->y_, g->x_r_, g->x_i_, g->msgs_); + return 0; + } +}; + +/** + * Calculate Jacobian Jxy(Jacobian of unknown x w.r.t. the * param y) + * given the solution. Specifically, for + * + * x - f(x, y) = 0 + * + * we have (Jpq = Jacobian matrix dq/dq) + * + * Jxy - Jfx * Jxy = Jfy + * + * therefore Jxy can be solved from system + * + * (I - Jfx) * Jxy = Jfy + * + * Jfx and Jfy are obtained through one AD evaluation of f + * w.r.t combined vector [x, y]. + */ +struct FixedPointADJac { + /** + * Calculate Jacobian Jxy. + * + * @tparam F RHS functor type + * @param x fixed point solution + * @param y RHS parameters + * @param env KINSOL working environment, see doc for @c KinsolFixedPointEnv. + */ + template + inline Eigen::Matrix operator()( + const Eigen::VectorXd& x, const Eigen::Matrix& y, + KinsolFixedPointEnv& env) { + using stan::math::precomputed_gradients; + using stan::math::to_array_1d; + using stan::math::var; + + auto g = [&env](const Eigen::Matrix& par_) { + Eigen::Matrix x_(par_.head(env.N_)); + Eigen::Matrix y_(par_.tail(env.M_)); + return env.f_(x_, y_, env.x_r_, env.x_i_, env.msgs_); + }; + + Eigen::VectorXd theta(x.size() + y.size()); + for (int i = 0; i < env.N_; ++i) { + theta(i) = x(i); + } + for (int i = 0; i < env.M_; ++i) { + theta(i + env.N_) = env.y_(i); + } + Eigen::Matrix fx; + Eigen::Matrix J_theta; + stan::math::jacobian(g, theta, fx, J_theta); + Eigen::MatrixXd A(J_theta.block(0, 0, env.N_, env.N_)); + Eigen::MatrixXd b(J_theta.block(0, env.N_, env.N_, env.M_)); + A = Eigen::MatrixXd::Identity(env.N_, env.N_) - A; + Eigen::MatrixXd Jxy = A.colPivHouseholderQr().solve(b); + std::vector gradients(env.M_); + Eigen::Matrix x_sol(env.N_); + std::vector yv(to_array_1d(y)); + for (int i = 0; i < env.N_; ++i) { + gradients = to_array_1d(Eigen::VectorXd(Jxy.row(i))); + x_sol[i] = precomputed_gradients(x(i), yv, gradients); + } + return x_sol; + } +}; + +/** + * Fixed point solver for problem of form + * + * x = F(x; theta) + * + * with x as unknowns and theta parameters. + * + * The solution for FP iteration + * doesn't involve derivatives but only data types. + * + * @tparam fp_env_type solver environment setup that handles + * workspace & auxiliary data encapsulation & RAII, namely + * the work environment. Currently only support KINSOL's + * dense matrix. + * @tparam fp_jac_type functor type for calculating the + * jacobian. Currently only support @c + * FixedPointADJac that obtain dense Jacobian + * through QR decomposition. + */ +template +struct FixedPointSolver; + +/** + * Specialization for fixed point solver when using KINSOL. + * + * @tparam F RHS functor for fixed point iteration. + * @tparam fp_jac_type functor type for calculating the jacobian + */ +template +struct FixedPointSolver, fp_jac_type> { + /** + * Solve FP using KINSOL + * + * @param x initial point and final solution. + * @param env KINSOL solution environment + * @param f_tol Function tolerance + * @param max_num_steps max nb. of iterations. + */ + void kinsol_solve_fp(Eigen::VectorXd& x, KinsolFixedPointEnv& env, + double f_tol, int max_num_steps) { + int N = env.N_; + void* mem = env.mem_; + + const int default_anderson_depth = 4; + int anderson_depth = std::min(N, default_anderson_depth); + + check_flag_sundials(KINSetNumMaxIters(mem, max_num_steps), + "KINSetNumMaxIters"); + check_flag_sundials(KINSetMAA(mem, anderson_depth), "KINSetMAA"); + check_flag_sundials(KINInit(mem, &env.kinsol_f_system, env.nv_x_), + "KINInit"); + check_flag_sundials(KINSetFuncNormTol(mem, f_tol), "KINSetFuncNormTol"); + check_flag_sundials(KINSetUserData(mem, static_cast(&env)), + "KINSetUserData"); + + check_flag_kinsol( + KINSol(mem, env.nv_x_, KIN_FP, env.nv_u_scal_, env.nv_f_scal_), + max_num_steps); + + for (int i = 0; i < N; ++i) { + x(i) = NV_Ith_S(env.nv_x_, i); + } + } + + /** + * Solve data-only FP problem so no need to calculate jacobian. + * + * @tparam T1 type of init point of iterations + * + * @param x initial point and final solution. + * @param y RHS functor parameters + * @param env KINSOL solution environment + * @param f_tol Function tolerance + * @param max_num_steps max nb. of iterations. + */ + template + Eigen::Matrix solve(const Eigen::Matrix& x, + const Eigen::Matrix& y, + KinsolFixedPointEnv& env, double f_tol, + int max_num_steps) { + Eigen::VectorXd xd(stan::math::value_of(x)); + kinsol_solve_fp(xd, env, f_tol, max_num_steps); + return xd; + } + + /** + * Solve FP problem and calculate jacobian. + * + * @tparam T1 type of init point of iterations + * + * @param x initial point and final solution. + * @param y RHS functor parameters + * @param env KINSOL solution environment + * @param f_tol Function tolerance + * @param max_num_steps max nb. of iterations. + */ + template + Eigen::Matrix solve( + const Eigen::Matrix& x, + const Eigen::Matrix& y, + KinsolFixedPointEnv& env, double f_tol, int max_num_steps) { + using stan::math::value_of; + using stan::math::var; + + // FP solution + Eigen::VectorXd xd(solve(x, Eigen::VectorXd(), env, f_tol, max_num_steps)); + + fp_jac_type jac_sol; + return jac_sol(xd, y, env); + } +}; + /** * Return a fixed pointer to the specified system of algebraic * equations of form - * \[ - * x = F(x; theta) - * \] - * given an initial guess \(x\), and parameters \(theta\) and data. Use the + * + * x = F(x; theta) + * + * given an initial guess x, and parameters theta and data. Use the * KINSOL solver from the SUNDIALS suite. * * The user can also specify the scaling controls, the function * tolerance, and the maximum number of steps. * - * This overload handles var parameters. - * - * The Jacobian \(J_{xy}\) (i.e., Jacobian of unknown \(x\) w.r.t. the parameter - * \(y\)) is calculated given the solution as follows. Since - * \[ - * x - f(x, y) = 0, - * \] - * we have (\(J_{pq}\) being the Jacobian matrix \(\tfrac {dq} {dq}\)) - * \[ - * J_{xy} - J_{fx} J_{xy} = J_{fy}, - * \] - * and therefore \(J_{xy}\) can be solved from system - * \[ - * (I - J_{fx}) * J_{xy} = J_{fy}. - * \] - * Let \(eta\) be the adjoint with respect to \(x\); then to calculate - * \[ - * \eta J_{xy}, - * \] - * we solve - * \[ - * (\eta * (I - J_{fx})^{-1}) * J_{fy}. - * \] - * (This is virtually identical to the Powell and Newton solvers, except - * \(-J_{fx}\) has been replaced by \((I - J_{fx}\).) - * * @tparam F type of equation system function. - * @tparam T type of initial guess vector. + * @tparam T type of initial guess vector. The final solution + * type doesn't depend on initial guess type, + * but we allow initial guess to be either data or param. * @tparam T_u type of scaling vector for unknowns. We allow * it to be @c var because scaling could be parameter * dependent. Internally these params are converted to data @@ -73,23 +330,29 @@ namespace math { * dependent. Internally these params are converted to data * because scaling is applied. * - * @param[in] f functor that evaluated the system of equations. - * @param[in] x vector of starting values. - * @param[in, out] msgs the print stream for warning messages. - * @param[in] u_scale diagonal scaling matrix elements \(Du\) - * such that \(Du x\) has all components roughly the same - * magnitude when \(x\) is close to a solution. + * @param[in] f Functor that evaluated the system of equations. + * @param[in] x Vector of starting values. + * @param[in] y Parameter vector for the equation system. The function + * is overloaded to treat y as a vector of doubles or of a + * a template type T. + * @param[in] x_r Continuous data vector for the equation system. + * @param[in] x_i Integer data vector for the equation system. + * @param[in, out] msgs The print stream for warning messages. + * @param[in] u_scale diagonal scaling matrix elements Du + * such that Du*x has all components roughly the same + * magnitude when x is close to a solution. * (ref. KINSOL user guide chap.2 sec. "Scaling") * @param[in] f_scale diagonal scaling matrix elements such - * that \(Df (x - f(x))\) has all components roughly the same - * magnitude when \(x\) is not too close to a solution. + * that Df*(x-f(x)) has all components roughly the same + * magnitude when x is not too close to a solution. * (ref. KINSOL user guide chap.2 sec. "Scaling") - * @param[in] function_tolerance Function-norm stopping tolerance. + * @param[in] f_tol Function-norm stopping tolerance. * @param[in] max_num_steps maximum number of function evaluations. - * @param[in] args additional parameters to the equation system functor. - * @pre f returns finite values when passed any value of x and the given args. * @throw std::invalid_argument if x has size zero. * @throw std::invalid_argument if x has non-finite elements. + * @throw std::invalid_argument if y has non-finite elements. + * @throw std::invalid_argument if dat has non-finite elements. + * @throw std::invalid_argument if dat_int has non-finite elements. * @throw std::invalid_argument if scaled_step_size is strictly * negative. * @throw std::invalid_argument if function_tolerance is strictly @@ -98,68 +361,25 @@ namespace math { * @throw boost::math::evaluation_error (which is a subclass of * std::runtime_error) if solver exceeds max_num_steps. */ -template * = nullptr, - require_any_st_var* = nullptr> -Eigen::Matrix algebra_solver_fp_impl( - const F& f, const T& x, std::ostream* const msgs, - const std::vector& u_scale, const std::vector& f_scale, - const double function_tolerance, const int max_num_steps, - const Args&... args) { - auto arena_args_tuple = std::make_tuple(to_arena(args)...); - - auto args_vals_tuple = apply( - [](const auto&... args) { return std::make_tuple(value_of(args)...); }, - arena_args_tuple); - - auto f_wrt_x = [&msgs, &f, &args_vals_tuple](const auto& x) { - return apply( - [&x, &msgs, &f](const auto&... args) { return f(x, msgs, args...); }, - args_vals_tuple); - }; - - // FP solution - Eigen::VectorXd theta_dbl = apply( - [&f, function_tolerance, &u_scale, &f_scale, &msgs, max_num_steps, - x_val = value_of(x)](const auto&... vals) { - return kinsol_solve_fp(f, x_val, function_tolerance, max_num_steps, - u_scale, f_scale, msgs, vals...); - }, - args_vals_tuple); - - Eigen::MatrixXd Jf_x; - Eigen::VectorXd f_x; - - jacobian(f_wrt_x, theta_dbl, f_x, Jf_x); - - using ret_type = Eigen::Matrix; - arena_t ret = theta_dbl; - - auto Jf_x_T_lu_ptr = make_unsafe_chainable_ptr( - (Eigen::MatrixXd::Identity(x.size(), x.size()) - Jf_x) - .transpose() - .eval() - .partialPivLu()); - - reverse_pass_callback( - [f, ret, arena_args_tuple, Jf_x_T_lu_ptr, msgs]() mutable { - Eigen::VectorXd eta = Jf_x_T_lu_ptr->solve(ret.adj().eval()); - - // Contract with Jacobian of f with respect to y using a nested reverse - // autodiff pass. - { - nested_rev_autodiff rev; - auto x_nrad_ = apply( - [&](const auto&... args) { - return eval(f(ret.val(), msgs, args...)); - }, - arena_args_tuple); - x_nrad_.adj() = eta; - grad(); - } - }); - - return ret_type(ret); +template +Eigen::Matrix algebra_solver_fp( + const F& f, const Eigen::Matrix& x, + const Eigen::Matrix& y, const std::vector& x_r, + const std::vector& x_i, const std::vector& u_scale, + const std::vector& f_scale, std::ostream* msgs = nullptr, + double f_tol = 1e-8, + int max_num_steps = 200) { // NOLINT(runtime/int) + algebra_solver_check(x, y, x_r, x_i, f_tol, max_num_steps); + check_nonnegative("algebra_solver", "u_scale", u_scale); + check_nonnegative("algebra_solver", "f_scale", f_scale); + check_matching_sizes("algebra_solver", "the algebraic system's output", + value_of(f(x, y, x_r, x_i, msgs)), + "the vector of unknowns, x,", x); + + KinsolFixedPointEnv env(f, x, y, x_r, x_i, msgs, u_scale, + f_scale); // NOLINT + FixedPointSolver, FixedPointADJac> fp; + return fp.solve(x, y, env, f_tol, max_num_steps); } } // namespace math diff --git a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp index be43bd76dbc..4e30a9b92fe 100644 --- a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp @@ -1,7 +1,7 @@ #include +#include #include #include -#include #include #include #include @@ -12,6 +12,8 @@ using stan::math::algebra_solver_fp; using stan::math::finite_diff_gradient_auto; +using stan::math::FixedPointADJac; +using stan::math::FixedPointSolver; using stan::math::KinsolFixedPointEnv; using stan::math::to_array_1d; using stan::math::to_var; @@ -29,11 +31,13 @@ struct FP_exp_func_test : public ::testing::Test { * RHS functor */ struct FP_exp_func { - template - inline Eigen::Matrix, Eigen::Dynamic, 1> - operator()(const T1& x, const T2& y, const T3& x_r, const T4& x_i, - std::ostream* pstream__) const { - using scalar = stan::return_type_t; + template + inline Eigen::Matrix, -1, 1> operator()( + const Eigen::Matrix& x, + const Eigen::Matrix& y, + const std::vector& x_r, const std::vector& x_i, + std::ostream* pstream__) const { + using scalar = stan::return_type_t; Eigen::Matrix z(1); z(0) = stan::math::exp(-y(0) * x(0)); return z; @@ -43,8 +47,8 @@ struct FP_exp_func_test : public ::testing::Test { FP_exp_func f; Eigen::VectorXd x; Eigen::VectorXd y; - std::vector dat; - std::vector dat_int; + std::vector x_r; + std::vector x_i; std::ostream* msgs; std::vector u_scale; std::vector f_scale; @@ -55,18 +59,20 @@ struct FP_exp_func_test : public ::testing::Test { : f(), x(stan::math::to_vector(std::vector{0.5})), y(stan::math::to_vector(std::vector{1.0})), - dat(), - dat_int(), + x_r(), + x_i(), msgs(nullptr), u_scale{1.0}, f_scale{1.0} {} auto fd_functor(int i) { auto f_fd = [this, i](const Eigen::VectorXd& y_) { - double function_tolerance = 1.e-12; + KinsolFixedPointEnv env(f, x, y_, x_r, x_i, msgs, u_scale, + f_scale); + FixedPointSolver, FixedPointADJac> fp; + double f_tol = 1.e-12; int max_num_steps = 100; - return algebra_solver_fp(f, x, y_, dat, dat_int, u_scale, f_scale, msgs, - function_tolerance, max_num_steps)(0); + return fp.solve(x, y_, env, f_tol, max_num_steps)(0); }; return f_fd; } @@ -84,11 +90,13 @@ struct FP_2d_func_test : public ::testing::Test { * RHS functor */ struct FP_2d_func { - template - inline Eigen::Matrix, -1, 1> operator()( - const T1& x, const T2& y, const T3& x_r, const T4& x_i, + template + inline Eigen::Matrix, -1, 1> operator()( + const Eigen::Matrix& x, + const Eigen::Matrix& y, + const std::vector& x_r, const std::vector& x_i, std::ostream* pstream__) const { - using scalar = stan::return_type_t; + using scalar = stan::return_type_t; Eigen::Matrix z(2); z(0) = y(0) * sqrt(x(1)); z(1) = y(1) * sqrt(y(2) - x(0) * x(0)); @@ -99,8 +107,8 @@ struct FP_2d_func_test : public ::testing::Test { FP_2d_func f; Eigen::VectorXd x; Eigen::VectorXd y; - std::vector dat; - std::vector dat_int; + std::vector x_r; + std::vector x_i; std::ostream* msgs; std::vector u_scale; std::vector f_scale; @@ -111,18 +119,20 @@ struct FP_2d_func_test : public ::testing::Test { : f(), x(stan::math::to_vector(std::vector{0.1, 0.1})), y(stan::math::to_vector(std::vector{1.0, 1.0, 1.0})), - dat(), - dat_int(), + x_r(), + x_i(), msgs(nullptr), u_scale{1.0, 1.0}, f_scale{1.0, 1.0} {} auto fd_functor(int i) { auto f_fd = [this, i](const Eigen::VectorXd& y_) { - double function_tolerance = 1.e-12; + KinsolFixedPointEnv env(f, x, y_, x_r, x_i, msgs, u_scale, + f_scale); + FixedPointSolver, FixedPointADJac> fp; + double f_tol = 1.e-12; int max_num_steps = 100; - return algebra_solver_fp(f, x, y_, dat, dat_int, u_scale, f_scale, msgs, - function_tolerance, max_num_steps)(i); + return fp.solve(x, y_, env, f_tol, max_num_steps)(i); }; return f_fd; } @@ -144,11 +154,13 @@ struct FP_degenerated_func_test : public ::testing::Test { * RHS functor */ struct FP_degenerated_func { - template - inline Eigen::Matrix, Eigen::Dynamic, 1> - operator()(const T1& x, const T2& y, const T3& dat, const T4& dat_int, - std::ostream* pstream__) const { - using scalar = stan::return_type_t; + template + inline Eigen::Matrix, -1, 1> operator()( + const Eigen::Matrix& x, + const Eigen::Matrix& y, + const std::vector& x_r, const std::vector& x_i, + std::ostream* pstream__) const { + using scalar = stan::return_type_t; Eigen::Matrix z(2); z(0) = y(0) + (x(0) - y(0)) * y(1) / x(1); z(1) = y(0) + (x(1) - y(0)) * y(1) / x(0); @@ -159,8 +171,8 @@ struct FP_degenerated_func_test : public ::testing::Test { FP_degenerated_func f; Eigen::VectorXd x; Eigen::VectorXd y; - std::vector dat; - std::vector dat_int; + std::vector x_r; + std::vector x_i; std::ostream* msgs; std::vector u_scale; std::vector f_scale; @@ -171,18 +183,23 @@ struct FP_degenerated_func_test : public ::testing::Test { : f(), x(stan::math::to_vector(std::vector{5.0, 100.0})), y(stan::math::to_vector(std::vector{5.0, 100.0})), - dat(), - dat_int(), + x_r(), + x_i(), msgs(nullptr), u_scale{1.0, 1.0}, f_scale{1.0, 1.0} {} auto fd_functor(int i) { auto f_fd = [this, i](const Eigen::VectorXd& y_) { - double function_tolerance = 1.e-12; + KinsolFixedPointEnv env(f, x, y_, x_r, x_i, msgs, + u_scale, // NOLINT + f_scale); + FixedPointSolver, + FixedPointADJac> + fp; // NOLINT + double f_tol = 1.e-12; int max_num_steps = 100; - return algebra_solver_fp(f, x, y_, dat, dat_int, u_scale, f_scale, msgs, - function_tolerance, max_num_steps)(i); + return fp.solve(x, y_, env, f_tol, max_num_steps)(i); }; return f_fd; } @@ -200,11 +217,13 @@ struct FP_direct_prod_func_test : public ::testing::Test { * RHS functor */ struct FP_direct_prod_func { - template - inline Eigen::Matrix, Eigen::Dynamic, 1> - operator()(const T1& x, const T2& y, const T3& dat, const T4& dat_int, - std::ostream* pstream__) const { - using scalar = stan::return_type_t; + template + inline Eigen::Matrix, -1, 1> operator()( + const Eigen::Matrix& x, + const Eigen::Matrix& y, + const std::vector& x_r, const std::vector& x_i, + std::ostream* pstream__) const { + using scalar = stan::return_type_t; const size_t n = x.size(); Eigen::Matrix z(n); const size_t m = 10; @@ -229,14 +248,16 @@ struct FP_direct_prod_func_test : public ::testing::Test { * Newton root functor */ struct FP_direct_prod_newton_func { - template - inline Eigen::Matrix, Eigen::Dynamic, 1> - operator()(const T1& x, const T2& y, const T3& dat, const T4& dat_int, - std::ostream* pstream__) const { - using scalar = stan::return_type_t; + template + inline Eigen::Matrix, -1, 1> operator()( + const Eigen::Matrix& x, + const Eigen::Matrix& y, + const std::vector& x_r, const std::vector& x_i, + std::ostream* pstream__) const { + using scalar = stan::return_type_t; const size_t n = x.size(); Eigen::Matrix z(n); - z = FP_direct_prod_func()(x, y, dat, dat_int, pstream__); + z = FP_direct_prod_func()(x, y, x_r, x_i, pstream__); for (size_t i = 0; i < n; ++i) { z(i) = x(i) - z(i); } @@ -249,8 +270,8 @@ struct FP_direct_prod_func_test : public ::testing::Test { const int n; Eigen::VectorXd x; Eigen::VectorXd y; - std::vector dat; - std::vector dat_int; + std::vector x_r; + std::vector x_i; std::ostream* msgs; std::vector u_scale; std::vector f_scale; @@ -263,8 +284,8 @@ struct FP_direct_prod_func_test : public ::testing::Test { n(400), x(stan::math::to_vector(std::vector(n, 0.2))), y(stan::math::to_vector(std::vector(n, 1.0))), - dat(), - dat_int(), + x_r(), + x_i(), msgs(nullptr), u_scale(n, 1.0), f_scale(n, 1.0) { @@ -275,22 +296,29 @@ struct FP_direct_prod_func_test : public ::testing::Test { auto fd_functor(int i) { auto f_fd = [this, i](const Eigen::VectorXd& y_) { - double function_tolerance = 1.e-12; + KinsolFixedPointEnv env(f, x, y_, x_r, x_i, msgs, + u_scale, // NOLINT + f_scale); + FixedPointSolver, + FixedPointADJac> + fp; // NOLINT + double f_tol = 1.e-12; int max_num_steps = 100; - return algebra_solver_fp(f, x, y_, dat, dat_int, u_scale, f_scale, msgs, - function_tolerance, max_num_steps)(i); + return fp.solve(x, y_, env, f_tol, max_num_steps)(i); }; return f_fd; } }; TEST_F(FP_exp_func_test, solve) { - double function_tolerance = 1.e-12; + KinsolFixedPointEnv env(f, x, y, x_r, x_i, msgs, u_scale, + f_scale); + FixedPointSolver, FixedPointADJac> fp; + double f_tol = 1.e-12; int max_num_steps = 100; { Eigen::Matrix res - = algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, msgs, - function_tolerance, max_num_steps); + = fp.solve(x, y, env, f_tol, max_num_steps); // NOLINT EXPECT_FLOAT_EQ(res(0), 0.567143290409); } @@ -298,33 +326,35 @@ TEST_F(FP_exp_func_test, solve) { x(0) = 0.1; y(0) = 0.8; Eigen::Matrix res - = algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, msgs, - function_tolerance, max_num_steps); + = fp.solve(x, y, env, f_tol, max_num_steps); // NOLINT EXPECT_FLOAT_EQ(res(0), 0.612584823501); } } TEST_F(FP_2d_func_test, solve) { - double function_tolerance = 1.e-12; + KinsolFixedPointEnv env(f, x, y, x_r, x_i, msgs, u_scale, + f_scale); + FixedPointSolver, FixedPointADJac> fp; + double f_tol = 1.e-12; int max_num_steps = 100; Eigen::Matrix res - = algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, msgs, - function_tolerance, max_num_steps); + = fp.solve(x, y, env, f_tol, max_num_steps); // NOLINT EXPECT_FLOAT_EQ(res(0), 0.7861513777574); EXPECT_FLOAT_EQ(res(1), 0.6180339887499); } TEST_F(FP_exp_func_test, gradient) { - double function_tolerance = 1.e-12; + KinsolFixedPointEnv env(f, x, y, x_r, x_i, msgs, u_scale, + f_scale); + FixedPointSolver, FixedPointADJac> fp; + double f_tol = 1.e-12; int max_num_steps = 100; + Eigen::Matrix yp(to_var(y)); x(0) = 0.1; y(0) = 0.8; - Eigen::Matrix yp(to_var(y)); - Eigen::Matrix x_sol - = algebra_solver_fp(f, x, yp, dat, dat_int, u_scale, f_scale, msgs, - function_tolerance, max_num_steps); + = fp.solve(x, yp, env, f_tol, max_num_steps); // NOLINT EXPECT_FLOAT_EQ(value_of(x_sol(0)), 0.612584823501); stan::math::set_zero_all_adjoints(); x_sol(0).grad(); @@ -336,68 +366,70 @@ TEST_F(FP_exp_func_test, gradient) { } TEST_F(FP_2d_func_test, gradient) { - double function_tolerance = 1.e-12; + KinsolFixedPointEnv env(f, x, y, x_r, x_i, msgs, u_scale, + f_scale); + FixedPointSolver, FixedPointADJac> fp; + double f_tol = 1.e-12; int max_num_steps = 100; Eigen::Matrix yp(to_var(y)); Eigen::Matrix x_sol - = algebra_solver_fp(f, x, yp, dat, dat_int, u_scale, f_scale, msgs, - function_tolerance, max_num_steps); + = fp.solve(x, yp, env, f_tol, max_num_steps); // NOLINT EXPECT_FLOAT_EQ(value_of(x_sol(0)), 0.7861513777574); EXPECT_FLOAT_EQ(value_of(x_sol(1)), 0.6180339887499); double fx; Eigen::VectorXd grad_fx; - for (int i = 0; i < x.size(); ++i) { + for (int i = 0; i < env.N_; ++i) { stan::math::set_zero_all_adjoints(); x_sol(i).grad(); auto f_fd = fd_functor(i); finite_diff_gradient_auto(f_fd, y, fx, grad_fx); - for (int j = 0; j < y.size(); ++j) { + for (int j = 0; j < env.M_; ++j) { EXPECT_FLOAT_EQ(grad_fx(j), yp(j).adj()); } } } TEST_F(FP_2d_func_test, gradient_with_var_init_point) { - double function_tolerance = 1.e-12; + KinsolFixedPointEnv env(f, x, y, x_r, x_i, msgs, u_scale, + f_scale); + FixedPointSolver, FixedPointADJac> fp; + double f_tol = 1.e-12; int max_num_steps = 100; Eigen::Matrix yp(to_var(y)); Eigen::Matrix xp(to_var(x)); - Eigen::Matrix x_sol - = algebra_solver_fp(f, xp, yp, dat, dat_int, u_scale, f_scale, msgs, - function_tolerance, max_num_steps); + Eigen::Matrix x_sol = fp.solve(xp, yp, env, f_tol, max_num_steps); EXPECT_FLOAT_EQ(value_of(x_sol(0)), 0.7861513777574); EXPECT_FLOAT_EQ(value_of(x_sol(1)), 0.6180339887499); double fx; Eigen::VectorXd grad_fx; - for (int i = 0; i < x.size(); ++i) { + for (int i = 0; i < env.N_; ++i) { stan::math::set_zero_all_adjoints(); x_sol(i).grad(); auto f_fd = fd_functor(i); finite_diff_gradient_auto(f_fd, y, fx, grad_fx); - for (int j = 0; j < y.size(); ++j) { + for (int j = 0; j < env.M_; ++j) { EXPECT_FLOAT_EQ(grad_fx(j), yp(j).adj()); } } } TEST_F(FP_2d_func_test, algebra_solver_fp) { - double function_tolerance = 1.e-12; + double f_tol = 1.e-12; int max_num_steps = 100; - Eigen::Matrix xd - = algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, 0, - function_tolerance, max_num_steps); + Eigen::Matrix xd = algebra_solver_fp( + f, x, y, x_r, x_i, u_scale, f_scale, 0, f_tol, max_num_steps); // NOLINT EXPECT_FLOAT_EQ(xd(0), 0.7861513777574); EXPECT_FLOAT_EQ(xd(1), 0.6180339887499); Eigen::Matrix yp(to_var(y)); Eigen::Matrix xv - = algebra_solver_fp(f, x, yp, dat, dat_int, u_scale, f_scale, 0, - function_tolerance, max_num_steps); + = algebra_solver_fp(f, x, yp, x_r, x_i, u_scale, f_scale, 0, f_tol, + max_num_steps); // NOLINT EXPECT_FLOAT_EQ(value_of(xv(0)), 0.7861513777574); EXPECT_FLOAT_EQ(value_of(xv(1)), 0.6180339887499); @@ -417,19 +449,18 @@ TEST_F(FP_2d_func_test, algebra_solver_fp) { } TEST_F(FP_degenerated_func_test, algebra_solver_fp) { - double function_tolerance = 1.e-12; + double f_tol = 1.e-12; int max_num_steps = 100; - Eigen::Matrix xd - = algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, 0, - function_tolerance, max_num_steps); + Eigen::Matrix xd = algebra_solver_fp( + f, x, y, x_r, x_i, u_scale, f_scale, 0, f_tol, max_num_steps); // NOLINT EXPECT_FLOAT_EQ(xd(0), 5.0); EXPECT_FLOAT_EQ(xd(1), 5.0); Eigen::Matrix yp(to_var(y)); Eigen::Matrix xv - = algebra_solver_fp(f, x, yp, dat, dat_int, u_scale, f_scale, 0, - function_tolerance, max_num_steps); + = algebra_solver_fp(f, x, yp, x_r, x_i, u_scale, f_scale, 0, f_tol, + max_num_steps); // NOLINT EXPECT_FLOAT_EQ(value_of(xv(0)), 5.0); EXPECT_FLOAT_EQ(value_of(xv(1)), 5.0); @@ -452,10 +483,10 @@ TEST_F(FP_degenerated_func_test, scaling_vector_as_params) { double f_tol = 1.e-12; int max_num_steps = 100; - const std::vector u_scale_v(to_var(u_scale)); - const std::vector f_scale_v(to_var(f_scale)); + std::vector u_scale_v(to_var(u_scale)); + std::vector f_scale_v(to_var(f_scale)); Eigen::Matrix xd = algebra_solver_fp( - f, x, y, dat, dat_int, u_scale, f_scale, 0, f_tol, max_num_steps); + f, x, y, x_r, x_i, u_scale, f_scale, 0, f_tol, max_num_steps); // NOLINT EXPECT_FLOAT_EQ(xd(0), 5.0); EXPECT_FLOAT_EQ(xd(1), 5.0); } @@ -466,9 +497,9 @@ TEST_F(FP_direct_prod_func_test, algebra_solver_fp) { Eigen::Matrix yp(to_var(y)); Eigen::Matrix xv_fp = algebra_solver_fp( - f, x, yp, dat, dat_int, u_scale, f_scale, 0, f_tol, max_num_steps); + f, x, yp, x_r, x_i, u_scale, f_scale, 0, f_tol, max_num_steps); // NOLINT Eigen::Matrix xv_newton = algebra_solver_newton( - f_newton, x, yp, dat, dat_int, 0, 1.e-3, f_tol, max_num_steps); + f_newton, x, yp, x_r, x_i, 0, 1.e-3, f_tol, max_num_steps); // NOLINT for (int i = 0; i < n; ++i) { EXPECT_FLOAT_EQ(value_of(xv_fp(i)), value_of(xv_newton(i))); } @@ -484,9 +515,6 @@ TEST_F(FP_direct_prod_func_test, algebra_solver_fp) { EXPECT_NEAR(g_newton[j], g_fp[j], 1.e-8); } } - - // TODO(@jgaeb): Can we remove this? - stan::math::recover_memory(); } TEST_F(FP_2d_func_test, exception_handling) { @@ -498,18 +526,18 @@ TEST_F(FP_2d_func_test, exception_handling) { err_msg << "algebra_solver: maximum number of iterations (4) was exceeded " "in the solve."; std::string msg = err_msg.str(); - EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, - 0, f_tol, max_num_steps), + EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, x_r, x_i, u_scale, f_scale, 0, + f_tol, max_num_steps), // NOLINT std::domain_error, msg); } { std::stringstream err_msg; - err_msg << "algebra_solver_fp: initial guess has size 0"; + err_msg << "algebra_solver: initial guess has size 0"; std::string msg = err_msg.str(); x = Eigen::VectorXd(); - EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, - 0, f_tol, max_num_steps), + EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, x_r, x_i, u_scale, f_scale, 0, + f_tol, max_num_steps), // NOLINT std::invalid_argument, msg); x = Eigen::VectorXd(2); x << 0.1, 0.1; @@ -517,44 +545,55 @@ TEST_F(FP_2d_func_test, exception_handling) { { std::stringstream err_msg; - err_msg << "algebra_solver_fp: u_scale[1] is -1"; + err_msg << "algebra_solver: continuous data[1] is inf"; + std::string msg = err_msg.str(); + x_r.push_back(std::numeric_limits::infinity()); + EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, x_r, x_i, u_scale, f_scale, 0, + f_tol, max_num_steps), // NOLINT + std::domain_error, msg); + x_r.clear(); + } + + { + std::stringstream err_msg; + err_msg << "algebra_solver: u_scale[1] is -1"; std::string msg = err_msg.str(); u_scale[0] = -1.0; - EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, - 0, f_tol, max_num_steps), + EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, x_r, x_i, u_scale, f_scale, 0, + f_tol, max_num_steps), // NOLINT std::domain_error, msg); u_scale[0] = 1.0; } { std::stringstream err_msg; - err_msg << "algebra_solver_fp: f_scale[1] is -1"; + err_msg << "algebra_solver: f_scale[1] is -1"; std::string msg = err_msg.str(); f_scale[0] = -1.0; - EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, - 0, f_tol, max_num_steps), + EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, x_r, x_i, u_scale, f_scale, 0, + f_tol, max_num_steps), // NOLINT std::domain_error, msg); f_scale[0] = 1.0; } { std::stringstream err_msg; - err_msg << "algebra_solver_fp: function_tolerance is -0.1"; + err_msg << "algebra_solver: function_tolerance is -0.1"; std::string msg = err_msg.str(); f_tol = -0.1; - EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, - 0, f_tol, max_num_steps), + EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, x_r, x_i, u_scale, f_scale, 0, + f_tol, max_num_steps), // NOLINT std::domain_error, msg); f_tol = 1.e-8; } { std::stringstream err_msg; - err_msg << "algebra_solver_fp: size of the algebraic system's output"; + err_msg << "algebra_solver: size of the algebraic system's output"; std::string msg = err_msg.str(); x = Eigen::VectorXd::Zero(4); - EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, dat, dat_int, u_scale, f_scale, - 0, f_tol, max_num_steps), + EXPECT_THROW_MSG(algebra_solver_fp(f, x, y, x_r, x_i, u_scale, f_scale, 0, + f_tol, max_num_steps), // NOLINT std::invalid_argument, msg); x = Eigen::VectorXd(2); x << 0.1, 0.1; From 54dbf64e9ba53c7a968fdc9c95aeaae9297c0868 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Tue, 10 Aug 2021 15:09:21 -0500 Subject: [PATCH 76/88] Remove fixed point solve from prim functor header. --- stan/math/prim/functor.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/stan/math/prim/functor.hpp b/stan/math/prim/functor.hpp index 456750bac41..0ec5c343ff7 100644 --- a/stan/math/prim/functor.hpp +++ b/stan/math/prim/functor.hpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include From a9e511fcb60eb292c7d97beeb78ec4a3c6fa019d Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Tue, 10 Aug 2021 17:24:16 -0500 Subject: [PATCH 77/88] Fix FP solver test. --- stan/math/rev/functor/algebra_system.hpp | 19 +++++++++++++++++++ .../rev/functor/algebra_solver_fp_test.cpp | 12 ++++-------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/stan/math/rev/functor/algebra_system.hpp b/stan/math/rev/functor/algebra_system.hpp index e27e56b4228..76b32ffe77f 100644 --- a/stan/math/rev/functor/algebra_system.hpp +++ b/stan/math/rev/functor/algebra_system.hpp @@ -95,6 +95,25 @@ struct hybrj_functor_solver : nlo_functor { Eigen::VectorXd get_value(const Eigen::VectorXd& iv) const { return fs_(iv); } }; +// TODO(jgaeb): Remove this when the chain method of the fixed point solver is +// updated. +template +void algebra_solver_check(const Eigen::Matrix& x, + const Eigen::Matrix y, + const std::vector& dat, + const std::vector& dat_int, + double function_tolerance, + long int max_num_steps) { // NOLINT(runtime/int) + check_nonzero_size("algebra_solver", "initial guess", x); + check_finite("algebra_solver", "initial guess", x); + check_finite("algebra_solver", "parameter vector", y); + check_finite("algebra_solver", "continuous data", dat); + check_finite("algebra_solver", "integer data", dat_int); + + check_nonnegative("algebra_solver", "function_tolerance", function_tolerance); + check_positive("algebra_solver", "max_num_steps", max_num_steps); +} + } // namespace math } // namespace stan diff --git a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp index 4e30a9b92fe..895fc51ffb8 100644 --- a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp @@ -217,11 +217,9 @@ struct FP_direct_prod_func_test : public ::testing::Test { * RHS functor */ struct FP_direct_prod_func { - template + template inline Eigen::Matrix, -1, 1> operator()( - const Eigen::Matrix& x, - const Eigen::Matrix& y, - const std::vector& x_r, const std::vector& x_i, + const T0& x, const T1& y, const T2& x_r, const T3& x_i, std::ostream* pstream__) const { using scalar = stan::return_type_t; const size_t n = x.size(); @@ -248,11 +246,9 @@ struct FP_direct_prod_func_test : public ::testing::Test { * Newton root functor */ struct FP_direct_prod_newton_func { - template + template inline Eigen::Matrix, -1, 1> operator()( - const Eigen::Matrix& x, - const Eigen::Matrix& y, - const std::vector& x_r, const std::vector& x_i, + const T0& x, const T1& y, const T2& x_r, const T3& x_i, std::ostream* pstream__) const { using scalar = stan::return_type_t; const size_t n = x.size(); From a0dcf9cc617859141c055381d66da8abf6d8ebad Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Tue, 10 Aug 2021 17:45:50 -0500 Subject: [PATCH 78/88] Fix minor formatting issues. --- test/unit/math/rev/core/chainable_object_test.cpp | 6 ++++-- test/unit/math/rev/functor/util_algebra_solver.hpp | 13 +++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/test/unit/math/rev/core/chainable_object_test.cpp b/test/unit/math/rev/core/chainable_object_test.cpp index 2610ee0bd51..7263088e0ae 100644 --- a/test/unit/math/rev/core/chainable_object_test.cpp +++ b/test/unit/math/rev/core/chainable_object_test.cpp @@ -78,7 +78,8 @@ int UnsafeChainableObjectTest::counter = 0; TEST(AgradRev, unsafe_chainable_object_test) { { - auto ptr = new stan::math::unsafe_chainable_object( + auto ptr + = new stan::math::unsafe_chainable_object( UnsafeChainableObjectTest()); UnsafeChainableObjectTest::counter = 0; } @@ -92,7 +93,8 @@ TEST(AgradRev, unsafe_chainable_object_nested_test) { stan::math::start_nested(); { - auto ptr = new stan::math::unsafe_chainable_object( + auto ptr + = new stan::math::unsafe_chainable_object( UnsafeChainableObjectTest()); UnsafeChainableObjectTest::counter = 0; } diff --git a/test/unit/math/rev/functor/util_algebra_solver.hpp b/test/unit/math/rev/functor/util_algebra_solver.hpp index f01f120a34b..56bb8a4dab9 100644 --- a/test/unit/math/rev/functor/util_algebra_solver.hpp +++ b/test/unit/math/rev/functor/util_algebra_solver.hpp @@ -458,11 +458,12 @@ inline void max_num_steps_test(Eigen::Matrix& y, template Eigen::Matrix variadic_eq_test( - const F& f, const Eigen::Matrix A, - const stan::math::var& y_1, const stan::math::var& y_2, const - stan::math::var& y_3, const int i, bool is_newton = false, double - scaling_step_size = 1e-3, double relative_tolerance = 1e-10, double - function_tolerance = 1e-6, int32_t max_num_steps = 1e+3) { + const F& f, + const Eigen::Matrix A, + const stan::math::var& y_1, const stan::math::var& y_2, + const stan::math::var& y_3, const int i, bool is_newton = false, + double scaling_step_size = 1e-3, double relative_tolerance = 1e-10, + double function_tolerance = 1e-6, int32_t max_num_steps = 1e+3) { using stan::math::algebra_solver_newton; using stan::math::algebra_solver_powell; using stan::math::var; @@ -473,7 +474,7 @@ Eigen::Matrix variadic_eq_test( Eigen::Matrix theta; - theta = is_newton ? + theta = is_newton ? algebra_solver_newton_impl(variadic_eq_functor(), x, &std::cout, scaling_step_size, function_tolerance, max_num_steps, A, y_1, y_2, y_3, i) : From f6c5db66ecd0e7bbd1ad5fe04cd5dd73d54f714a Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Tue, 10 Aug 2021 22:54:13 +0000 Subject: [PATCH 79/88] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- test/unit/math/rev/core/chainable_object_test.cpp | 8 ++++---- .../rev/functor/algebra_solver_newton_test.cpp | 4 ++-- test/unit/math/rev/functor/algebra_solver_test.cpp | 5 ++--- test/unit/math/rev/functor/util_algebra_solver.hpp | 14 +++++++------- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/test/unit/math/rev/core/chainable_object_test.cpp b/test/unit/math/rev/core/chainable_object_test.cpp index 7263088e0ae..fddd02fd3cf 100644 --- a/test/unit/math/rev/core/chainable_object_test.cpp +++ b/test/unit/math/rev/core/chainable_object_test.cpp @@ -79,8 +79,8 @@ int UnsafeChainableObjectTest::counter = 0; TEST(AgradRev, unsafe_chainable_object_test) { { auto ptr - = new stan::math::unsafe_chainable_object( - UnsafeChainableObjectTest()); + = new stan::math::unsafe_chainable_object( + UnsafeChainableObjectTest()); UnsafeChainableObjectTest::counter = 0; } @@ -94,8 +94,8 @@ TEST(AgradRev, unsafe_chainable_object_nested_test) { { auto ptr - = new stan::math::unsafe_chainable_object( - UnsafeChainableObjectTest()); + = new stan::math::unsafe_chainable_object( + UnsafeChainableObjectTest()); UnsafeChainableObjectTest::counter = 0; } diff --git a/test/unit/math/rev/functor/algebra_solver_newton_test.cpp b/test/unit/math/rev/functor/algebra_solver_newton_test.cpp index ffc47a63c80..8f00d04c9c9 100644 --- a/test/unit/math/rev/functor/algebra_solver_newton_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_newton_test.cpp @@ -246,8 +246,8 @@ TEST_F(variadic_test, newton) { Eigen::Matrix theta = variadic_eq_test(variadic_eq_functor(), A, y_1, y_2, y_3, i, - is_newton, scaling_step_size, relative_tolerance, - function_tolerance, max_num_steps); + is_newton, scaling_step_size, relative_tolerance, + function_tolerance, max_num_steps); AVEC y_vec = createAVEC(y_1, y_2, y_3); VEC g; diff --git a/test/unit/math/rev/functor/algebra_solver_test.cpp b/test/unit/math/rev/functor/algebra_solver_test.cpp index 0456dec73f9..481aa94bd4d 100644 --- a/test/unit/math/rev/functor/algebra_solver_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_test.cpp @@ -12,7 +12,6 @@ ////////////////////////////////////////////////////////////////////////// // Tests for powell solver. - TEST_F(algebra_solver_simple_eq_test, powell_dbl) { bool is_newton = false; Eigen::VectorXd theta = simple_eq_test(simple_eq_functor(), y_dbl, is_newton); @@ -250,8 +249,8 @@ TEST_F(variadic_test, powell) { Eigen::Matrix theta = variadic_eq_test(variadic_eq_functor(), A, y_1, y_2, y_3, i, - is_newton, scaling_step_size, relative_tolerance, - function_tolerance, max_num_steps); + is_newton, scaling_step_size, relative_tolerance, + function_tolerance, max_num_steps); std::vector y_vec{y_1, y_2, y_3}; std::vector g; diff --git a/test/unit/math/rev/functor/util_algebra_solver.hpp b/test/unit/math/rev/functor/util_algebra_solver.hpp index 56bb8a4dab9..63833170bfb 100644 --- a/test/unit/math/rev/functor/util_algebra_solver.hpp +++ b/test/unit/math/rev/functor/util_algebra_solver.hpp @@ -474,13 +474,13 @@ Eigen::Matrix variadic_eq_test( Eigen::Matrix theta; - theta = is_newton ? - algebra_solver_newton_impl(variadic_eq_functor(), x, &std::cout, - scaling_step_size, function_tolerance, - max_num_steps, A, y_1, y_2, y_3, i) : - algebra_solver_powell_impl(variadic_eq_functor(), x, &std::cout, - relative_tolerance, function_tolerance, - max_num_steps, A, y_1, y_2, y_3, i); + theta = is_newton + ? algebra_solver_newton_impl( + variadic_eq_functor(), x, &std::cout, scaling_step_size, + function_tolerance, max_num_steps, A, y_1, y_2, y_3, i) + : algebra_solver_powell_impl( + variadic_eq_functor(), x, &std::cout, relative_tolerance, + function_tolerance, max_num_steps, A, y_1, y_2, y_3, i); EXPECT_NEAR(20, value_of(theta(0)), 1e-6); EXPECT_NEAR(2, value_of(theta(1)), 1e-6); From 5ce9d32ab44014c6303e7761461b67839b9c1de5 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Tue, 10 Aug 2021 20:33:50 -0500 Subject: [PATCH 80/88] Fix AVEC / VEC in newton solver tests. --- .../functor/algebra_solver_newton_test.cpp | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test/unit/math/rev/functor/algebra_solver_newton_test.cpp b/test/unit/math/rev/functor/algebra_solver_newton_test.cpp index 8f00d04c9c9..f1ce36158aa 100644 --- a/test/unit/math/rev/functor/algebra_solver_newton_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_newton_test.cpp @@ -116,8 +116,8 @@ TEST_F(algebra_solver_simple_eq_test, newton) { Eigen::Matrix theta = simple_eq_test(simple_eq_functor(), y, is_newton); - AVEC y_vec = createAVEC(y(0), y(1), y(2)); - VEC g; + std::vector y_vec {y(0), y(1), y(2)}; + std::vector g; theta(k).grad(y_vec, g); for (int i = 0; i < n_y; i++) @@ -135,8 +135,8 @@ TEST_F(algebra_solver_simple_eq_test, newton_tuned) { = simple_eq_test(simple_eq_functor(), y, is_newton, true, scale_step, xtol, ftol, maxfev); - AVEC y_vec = createAVEC(y(0), y(1), y(2)); - VEC g; + std::vector y_vec {y(0), y(1), y(2)}; + std::vector g; theta(k).grad(y_vec, g); for (int i = 0; i < n_y; i++) @@ -164,8 +164,8 @@ TEST_F(algebra_solver_non_linear_eq_test, newton) { EXPECT_FLOAT_EQ(-y(1).val(), theta(1).val()); EXPECT_FLOAT_EQ(y(2).val(), theta(2).val()); - AVEC y_vec = createAVEC(y(0), y(1), y(2)); - VEC g; + std::vector y_vec {y(0), y(1), y(2)}; + std::vector g; theta(k).grad(y_vec, g); for (int i = 0; i < n_y; i++) @@ -206,8 +206,8 @@ TEST_F(degenerate_eq_test, newton_guess1) { EXPECT_FLOAT_EQ(8, theta(0).val()); EXPECT_FLOAT_EQ(8, theta(1).val()); - AVEC y_vec = createAVEC(y(0), y(1)); - VEC g; + std::vector y_vec {y(0), y(1)}; + std::vector g; theta(k).grad(y_vec, g); for (int l = 0; l < n_y; l++) @@ -227,8 +227,8 @@ TEST_F(degenerate_eq_test, newton_guess2) { EXPECT_FLOAT_EQ(5, theta(0).val()); EXPECT_FLOAT_EQ(5, theta(0).val()); - AVEC y_vec = createAVEC(y(0), y(1)); - VEC g; + std::vector y_vec {y(0), y(1)}; + std::vector g; theta(k).grad(y_vec, g); for (int l = 0; l < n_y; l++) @@ -249,8 +249,8 @@ TEST_F(variadic_test, newton) { is_newton, scaling_step_size, relative_tolerance, function_tolerance, max_num_steps); - AVEC y_vec = createAVEC(y_1, y_2, y_3); - VEC g; + std::vector y_vec {y_1, y_2, y_3}; + std::vector g; theta(k).grad(y_vec, g); for (int i = 0; i < n_y; i++) From 70d581721246bcb3adb325773386df253c729d3d Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Wed, 11 Aug 2021 04:20:09 +0000 Subject: [PATCH 81/88] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- .../math/rev/functor/algebra_solver_newton_test.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/unit/math/rev/functor/algebra_solver_newton_test.cpp b/test/unit/math/rev/functor/algebra_solver_newton_test.cpp index f1ce36158aa..c88db8a8914 100644 --- a/test/unit/math/rev/functor/algebra_solver_newton_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_newton_test.cpp @@ -116,7 +116,7 @@ TEST_F(algebra_solver_simple_eq_test, newton) { Eigen::Matrix theta = simple_eq_test(simple_eq_functor(), y, is_newton); - std::vector y_vec {y(0), y(1), y(2)}; + std::vector y_vec{y(0), y(1), y(2)}; std::vector g; theta(k).grad(y_vec, g); @@ -135,7 +135,7 @@ TEST_F(algebra_solver_simple_eq_test, newton_tuned) { = simple_eq_test(simple_eq_functor(), y, is_newton, true, scale_step, xtol, ftol, maxfev); - std::vector y_vec {y(0), y(1), y(2)}; + std::vector y_vec{y(0), y(1), y(2)}; std::vector g; theta(k).grad(y_vec, g); @@ -164,7 +164,7 @@ TEST_F(algebra_solver_non_linear_eq_test, newton) { EXPECT_FLOAT_EQ(-y(1).val(), theta(1).val()); EXPECT_FLOAT_EQ(y(2).val(), theta(2).val()); - std::vector y_vec {y(0), y(1), y(2)}; + std::vector y_vec{y(0), y(1), y(2)}; std::vector g; theta(k).grad(y_vec, g); @@ -206,7 +206,7 @@ TEST_F(degenerate_eq_test, newton_guess1) { EXPECT_FLOAT_EQ(8, theta(0).val()); EXPECT_FLOAT_EQ(8, theta(1).val()); - std::vector y_vec {y(0), y(1)}; + std::vector y_vec{y(0), y(1)}; std::vector g; theta(k).grad(y_vec, g); @@ -227,7 +227,7 @@ TEST_F(degenerate_eq_test, newton_guess2) { EXPECT_FLOAT_EQ(5, theta(0).val()); EXPECT_FLOAT_EQ(5, theta(0).val()); - std::vector y_vec {y(0), y(1)}; + std::vector y_vec{y(0), y(1)}; std::vector g; theta(k).grad(y_vec, g); @@ -249,7 +249,7 @@ TEST_F(variadic_test, newton) { is_newton, scaling_step_size, relative_tolerance, function_tolerance, max_num_steps); - std::vector y_vec {y_1, y_2, y_3}; + std::vector y_vec{y_1, y_2, y_3}; std::vector g; theta(k).grad(y_vec, g); From 5f7709a67306e7e71b86c5a198deb2a9fb8ae0f1 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Wed, 11 Aug 2021 00:20:16 -0500 Subject: [PATCH 82/88] Restore memory leak fix for fp test. --- test/unit/math/rev/functor/algebra_solver_fp_test.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp index 895fc51ffb8..99ebeb2cfc9 100644 --- a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp @@ -511,6 +511,8 @@ TEST_F(FP_direct_prod_func_test, algebra_solver_fp) { EXPECT_NEAR(g_newton[j], g_fp[j], 1.e-8); } } + + stan::math::recover_memory(); } TEST_F(FP_2d_func_test, exception_handling) { From 044fa01d3a766bf549ff355b8498153b0b94b7a0 Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Wed, 11 Aug 2021 05:21:20 +0000 Subject: [PATCH 83/88] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- test/unit/math/rev/functor/algebra_solver_fp_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp index 99ebeb2cfc9..67b742968bf 100644 --- a/test/unit/math/rev/functor/algebra_solver_fp_test.cpp +++ b/test/unit/math/rev/functor/algebra_solver_fp_test.cpp @@ -511,7 +511,7 @@ TEST_F(FP_direct_prod_func_test, algebra_solver_fp) { EXPECT_NEAR(g_newton[j], g_fp[j], 1.e-8); } } - + stan::math::recover_memory(); } From fec4d1f76916dfde133d45c7fd2ee77212e4ae3f Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Wed, 11 Aug 2021 15:12:33 -0500 Subject: [PATCH 84/88] Fix pesky multiple evaluation bug. --- stan/math/rev/functor/algebra_solver_powell.hpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 1be5e029533..edd852a4fdb 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -131,11 +131,15 @@ Eigen::VectorXd algebra_solver_powell_impl(const F& f, const T& x, const double function_tolerance, const int64_t max_num_steps, const Args&... args) { - const auto& x_ref = to_ref(x); - auto x_val = to_ref(value_of(x_ref)); + auto x_val = to_ref(value_of(x)); + auto args_vals_tuple = std::make_tuple(to_ref(args)...); auto f_wrt_x - = [&f, msgs, &args...](const auto& x) { return f(x, msgs, args...); }; + = [&args_vals_tuple, &f, msgs](const auto& x) { + return apply( + [&x, &f, msgs](const auto&... args) { return f(x, msgs, args...); }, + args_vals_tuple); + }; check_nonzero_size("algebra_solver_powell", "initial guess", x_val); check_finite("algebra_solver_powell", "initial guess", x_val); @@ -145,7 +149,7 @@ Eigen::VectorXd algebra_solver_powell_impl(const F& f, const T& x, function_tolerance); check_positive("algebra_solver_powell", "max_num_steps", max_num_steps); check_matching_sizes("algebra_solver", "the algebraic system's output", - f_wrt_x(x_ref), "the vector of unknowns, x,", x_ref); + f_wrt_x(x_val), "the vector of unknowns, x,", x_val); // Solve the system return algebra_solver_powell_call_solver(f_wrt_x, x_val, msgs, From b852e9a0d928b837f7a566b4ccc92aa499ec79fa Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Wed, 11 Aug 2021 15:26:09 -0500 Subject: [PATCH 85/88] Fully document algebra_solver_powell_call_solver. --- stan/math/rev/functor/algebra_solver_powell.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index edd852a4fdb..5d97a08873c 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -31,6 +31,7 @@ namespace math { * for the solution. * @param[in] function_tolerance determines whether roots are acceptable. * @param[in] max_num_steps maximum number of function evaluations. + * @param[in] args additional parameters to the equation system functor. * @return theta_dbl Double vector of solutions to the system of equations. * @pre x has size greater than zero. * @pre x has only finite elements. From 09e15443e14871561a4fb3e23988fc8b5f732245 Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Wed, 11 Aug 2021 20:49:12 +0000 Subject: [PATCH 86/88] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- stan/math/rev/functor/algebra_solver_powell.hpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index 5d97a08873c..a3c2e304f4e 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -135,11 +135,10 @@ Eigen::VectorXd algebra_solver_powell_impl(const F& f, const T& x, auto x_val = to_ref(value_of(x)); auto args_vals_tuple = std::make_tuple(to_ref(args)...); - auto f_wrt_x - = [&args_vals_tuple, &f, msgs](const auto& x) { - return apply( - [&x, &f, msgs](const auto&... args) { return f(x, msgs, args...); }, - args_vals_tuple); + auto f_wrt_x = [&args_vals_tuple, &f, msgs](const auto& x) { + return apply( + [&x, &f, msgs](const auto&... args) { return f(x, msgs, args...); }, + args_vals_tuple); }; check_nonzero_size("algebra_solver_powell", "initial guess", x_val); From 0b93db84f030c4ca7f51c61915838fb7ba491021 Mon Sep 17 00:00:00 2001 From: Johann Gaebler Date: Wed, 11 Aug 2021 22:53:11 -0500 Subject: [PATCH 87/88] Attempt to resolve non-const lvalue reference issue. --- .../rev/functor/algebra_solver_powell.hpp | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index a3c2e304f4e..bf6ea3263b7 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -132,7 +132,7 @@ Eigen::VectorXd algebra_solver_powell_impl(const F& f, const T& x, const double function_tolerance, const int64_t max_num_steps, const Args&... args) { - auto x_val = to_ref(value_of(x)); + plain_type_t x_ref = to_ref(value_of(x)); auto args_vals_tuple = std::make_tuple(to_ref(args)...); auto f_wrt_x = [&args_vals_tuple, &f, msgs](const auto& x) { @@ -141,18 +141,18 @@ Eigen::VectorXd algebra_solver_powell_impl(const F& f, const T& x, args_vals_tuple); }; - check_nonzero_size("algebra_solver_powell", "initial guess", x_val); - check_finite("algebra_solver_powell", "initial guess", x_val); + check_nonzero_size("algebra_solver_powell", "initial guess", x_ref); + check_finite("algebra_solver_powell", "initial guess", x_ref); check_nonnegative("alegbra_solver_powell", "relative_tolerance", relative_tolerance); check_nonnegative("algebra_solver_powell", "function_tolerance", function_tolerance); check_positive("algebra_solver_powell", "max_num_steps", max_num_steps); check_matching_sizes("algebra_solver", "the algebraic system's output", - f_wrt_x(x_val), "the vector of unknowns, x,", x_val); + f_wrt_x(x_ref), "the vector of unknowns, x,", x_ref); // Solve the system - return algebra_solver_powell_call_solver(f_wrt_x, x_val, msgs, + return algebra_solver_powell_call_solver(f_wrt_x, x_ref, msgs, relative_tolerance, function_tolerance, max_num_steps); } @@ -336,8 +336,7 @@ Eigen::Matrix algebra_solver_powell_impl( const F& f, const T& x, std::ostream* const msgs, const double relative_tolerance, const double function_tolerance, const int64_t max_num_steps, const T_Args&... args) { - const auto& x_ref = to_ref(x); - auto x_val = to_ref(value_of(x_ref)); + plain_type_t x_ref = to_ref(value_of(x)); auto arena_args_tuple = std::make_tuple(to_arena(args)...); auto args_vals_tuple = apply( [&](const auto&... args) { @@ -351,8 +350,8 @@ Eigen::Matrix algebra_solver_powell_impl( args_vals_tuple); }; - check_nonzero_size("algebra_solver_powell", "initial guess", x_val); - check_finite("algebra_solver_powell", "initial guess", x_val); + check_nonzero_size("algebra_solver_powell", "initial guess", x_ref); + check_finite("algebra_solver_powell", "initial guess", x_ref); check_nonnegative("alegbra_solver_powell", "relative_tolerance", relative_tolerance); check_nonnegative("algebra_solver_powell", "function_tolerance", @@ -362,19 +361,19 @@ Eigen::Matrix algebra_solver_powell_impl( f_wrt_x(x_ref), "the vector of unknowns, x,", x_ref); // Solve the system - algebra_solver_powell_call_solver(f_wrt_x, x_val, msgs, relative_tolerance, + algebra_solver_powell_call_solver(f_wrt_x, x_ref, msgs, relative_tolerance, function_tolerance, max_num_steps); Eigen::MatrixXd Jf_x; Eigen::VectorXd f_x; - jacobian(f_wrt_x, x_val, f_x, Jf_x); + jacobian(f_wrt_x, x_ref, f_x, Jf_x); using ret_type = Eigen::Matrix; auto Jf_x_T_lu_ptr = make_unsafe_chainable_ptr(Jf_x.transpose().partialPivLu()); // Lu - arena_t ret = x_val; + arena_t ret = x_ref; reverse_pass_callback([f, ret, arena_args_tuple, Jf_x_T_lu_ptr, msgs]() mutable { From 09ca12da5ac0397de83b7a1b07d2cb626ad73295 Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Fri, 13 Aug 2021 00:05:42 -0400 Subject: [PATCH 88/88] hard copy x before passing it to the solver impl for powell. Use a chainable pointer for the arguments passed to the user defined function (UDF). --- .../prim/functor/algebra_solver_adapter.hpp | 7 +++---- .../math/rev/functor/algebra_solver_powell.hpp | 18 +++++++++--------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/stan/math/prim/functor/algebra_solver_adapter.hpp b/stan/math/prim/functor/algebra_solver_adapter.hpp index 8fc92a000c1..528912c660e 100644 --- a/stan/math/prim/functor/algebra_solver_adapter.hpp +++ b/stan/math/prim/functor/algebra_solver_adapter.hpp @@ -17,10 +17,9 @@ struct algebra_solver_adapter { explicit algebra_solver_adapter(const F& f) : f_(f) {} - template - auto operator()(const T1& x, std::ostream* msgs, const T2& y, const T3& dat, - const T4& dat_int) const { - return f_(x, y, dat, dat_int, msgs); + template + auto operator()(const T1& x, std::ostream* msgs, T2&&... args) const { + return f_(x, args..., msgs); } }; diff --git a/stan/math/rev/functor/algebra_solver_powell.hpp b/stan/math/rev/functor/algebra_solver_powell.hpp index bf6ea3263b7..5415f361cad 100644 --- a/stan/math/rev/functor/algebra_solver_powell.hpp +++ b/stan/math/rev/functor/algebra_solver_powell.hpp @@ -51,8 +51,8 @@ T& algebra_solver_powell_call_solver(const F& f, T& x, std::ostream* const msgs, const int64_t max_num_steps, const Args&... args) { // Construct the solver - hybrj_functor_solver hfs(f); - Eigen::HybridNonLinearSolver solver(hfs); + hybrj_functor_solver hfs(f); + Eigen::HybridNonLinearSolver> solver(hfs); // Compute theta_dbl solver.parameters.xtol = relative_tolerance; @@ -61,7 +61,7 @@ T& algebra_solver_powell_call_solver(const F& f, T& x, std::ostream* const msgs, // Check if the max number of steps has been exceeded if (solver.nfev >= max_num_steps) { - [&]() STAN_COLD_PATH { + [max_num_steps]() STAN_COLD_PATH { throw_domain_error("algebra_solver", "maximum number of iterations", max_num_steps, "(", ") was exceeded in the solve."); }(); @@ -70,7 +70,7 @@ T& algebra_solver_powell_call_solver(const F& f, T& x, std::ostream* const msgs, // Check solution is a root double system_norm = f(x).stableNorm(); if (system_norm > function_tolerance) { - [&]() STAN_COLD_PATH { + [function_tolerance, system_norm]() STAN_COLD_PATH { std::ostringstream message; message << "the norm of the algebraic function is " << system_norm << " but should be lower than the function " @@ -132,7 +132,7 @@ Eigen::VectorXd algebra_solver_powell_impl(const F& f, const T& x, const double function_tolerance, const int64_t max_num_steps, const Args&... args) { - plain_type_t x_ref = to_ref(value_of(x)); + auto x_ref = eval(value_of(x)); auto args_vals_tuple = std::make_tuple(to_ref(args)...); auto f_wrt_x = [&args_vals_tuple, &f, msgs](const auto& x) { @@ -336,13 +336,13 @@ Eigen::Matrix algebra_solver_powell_impl( const F& f, const T& x, std::ostream* const msgs, const double relative_tolerance, const double function_tolerance, const int64_t max_num_steps, const T_Args&... args) { - plain_type_t x_ref = to_ref(value_of(x)); - auto arena_args_tuple = std::make_tuple(to_arena(args)...); + auto x_ref = eval(value_of(x)); + auto arena_args_tuple = make_chainable_ptr(std::make_tuple(eval(args)...)); auto args_vals_tuple = apply( [&](const auto&... args) { return std::make_tuple(to_ref(value_of(args))...); }, - arena_args_tuple); + *arena_args_tuple); auto f_wrt_x = [&args_vals_tuple, &f, msgs](const auto& x) { return apply( @@ -386,7 +386,7 @@ Eigen::Matrix algebra_solver_powell_impl( Eigen::VectorXd ret_val = ret.val(); auto x_nrad_ = apply( [&](const auto&... args) { return eval(f(ret_val, msgs, args...)); }, - arena_args_tuple); + *arena_args_tuple); x_nrad_.adj() = eta; grad(); }