22#define STAN_MATH_FWD_FUNCTOR_HESSIAN_HPP
33
44#include < stan/math/fwd/core.hpp>
5+ #include < stan/math/fwd/fun/value_of.hpp>
56#include < stan/math/prim/fun/Eigen.hpp>
67
78namespace stan {
@@ -14,6 +15,9 @@ namespace math {
1415 * mixed definition, which is faster for Hessians, is that this
1516 * version is itself differentiable.
1617 *
18+ * Instead of returning the full symmetric Hessian, we return the
19+ * lower-triangular only as a column-major compressed sparse matrix.
20+ *
1721 * <p>The functor must implement
1822 *
1923 * <code>
@@ -35,23 +39,27 @@ namespace math {
3539 * @param[in] x Argument to function
3640 * @param[out] fx Function applied to argument
3741 * @param[out] grad gradient of function at argument
38- * @param[out] H Hessian of function at argument
42+ * @param[out] H Hessian of function at argument, as a lower-triangular
43+ * compressed sparse matrix
3944 */
4045template <typename T, typename F>
4146void hessian (const F& f, const Eigen::Matrix<T, Eigen::Dynamic, 1 >& x, T& fx,
4247 Eigen::Matrix<T, Eigen::Dynamic, 1 >& grad,
43- Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>& H) {
44- H.resize (x.size (), x.size ());
45- grad.resize (x.size ());
46- // size 0 separate because nothing to loop over in main body
47- if (x.size () == 0 ) {
48- fx = f (x);
48+ Eigen::SparseMatrix<T>& H) {
49+ int d = x.size ();
50+ if (d == 0 ) {
51+ fx = value_of (f (x));
4952 return ;
5053 }
51- Eigen::Matrix<fvar<fvar<T> >, Eigen::Dynamic, 1 > x_fvar (x.size ());
52- for (int i = 0 ; i < x.size (); ++i) {
53- for (int j = i; j < x.size (); ++j) {
54- for (int k = 0 ; k < x.size (); ++k) {
54+
55+ H.resize (d, d);
56+ H.reserve (Eigen::VectorXi::LinSpaced (d, 1 , d).reverse ());
57+ grad.resize (d);
58+
59+ Eigen::Matrix<fvar<fvar<T> >, Eigen::Dynamic, 1 > x_fvar (d);
60+ for (int i = 0 ; i < d; ++i) {
61+ for (int j = i; j < d; ++j) {
62+ for (int k = 0 ; k < d; ++k) {
5563 x_fvar (k) = fvar<fvar<T> >(fvar<T>(x (k), j == k), fvar<T>(i == k, 0 ));
5664 }
5765 fvar<fvar<T> > fx_fvar = f (x_fvar);
@@ -61,10 +69,38 @@ void hessian(const F& f, const Eigen::Matrix<T, Eigen::Dynamic, 1>& x, T& fx,
6169 if (i == j) {
6270 grad (i) = fx_fvar.d_ .val_ ;
6371 }
64- H (i, j) = fx_fvar.d_ .d_ ;
65- H (j, i) = H (i, j);
72+ H.insert (j, i) = fx_fvar.d_ .d_ ;
6673 }
6774 }
75+ H.makeCompressed ();
76+ }
77+
78+ /* *
79+ * Calculate the value, the gradient, and the Hessian,
80+ * of the specified function at the specified argument in
81+ * time O(N^3) time and O(N^2) space. The advantage over the
82+ * mixed definition, which is faster for Hessians, is that this
83+ * version is itself differentiable.
84+ *
85+ * Overload for returning the Hessian as a symmetric dense matrix.
86+ *
87+ * @tparam T type of elements in the vector and matrix
88+ * @tparam F type of function
89+ * @param[in] f Function
90+ * @param[in] x Argument to function
91+ * @param[out] fx Function applied to argument
92+ * @param[out] grad gradient of function at argument
93+ * @param[out] H Hessian of function at argument, as a symmetric matrix
94+ */
95+ template <typename T, typename F>
96+ void hessian (const F& f, const Eigen::Matrix<T, Eigen::Dynamic, 1 >& x, T& fx,
97+ Eigen::Matrix<T, Eigen::Dynamic, 1 >& grad,
98+ Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>& H) {
99+ Eigen::SparseMatrix<T> hess_sparse;
100+ hessian (f, x, fx, grad, hess_sparse);
101+
102+ H = Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>(hess_sparse)
103+ .template selfadjointView <Eigen::Lower>();
68104}
69105
70106} // namespace math
0 commit comments