Automatic Differentiation
 
Loading...
Searching...
No Matches
matrix_exp_action_handler.hpp
Go to the documentation of this file.
1#ifndef STAN_MATH_PRIM_FUN_MATRIX_EXP_ACTION_HANDLER_HPP
2#define STAN_MATH_PRIM_FUN_MATRIX_EXP_ACTION_HANDLER_HPP
3
8#include <unsupported/Eigen/MatrixFunctions>
9#include <vector>
10#include <cmath>
11
12namespace stan {
13namespace math {
14
27 static constexpr int _p_max = 8;
28 static constexpr int _m_max = 55;
29 static constexpr double _tol
30 = 1.1e-16; // from the paper, double precision: 2^-53
31 static constexpr std::array<double, 100> _theta_m{
32 2.22044605e-16, 2.58095680e-08, 1.38634787e-05, 3.39716884e-04,
33 2.40087636e-03, 9.06565641e-03, 2.38445553e-02, 4.99122887e-02,
34 8.95776020e-02, 1.44182976e-01, 2.14235807e-01, 2.99615891e-01,
35 3.99777534e-01, 5.13914694e-01, 6.41083523e-01, 7.80287426e-01,
36 9.30532846e-01, 1.09086372e+00, 1.26038106e+00, 1.43825260e+00,
37 1.62371595e+00, 1.81607782e+00, 2.01471078e+00, 2.21904887e+00,
38 2.42858252e+00, 2.64285346e+00, 2.86144963e+00, 3.08400054e+00,
39 3.31017284e+00, 3.53966635e+00, 3.77221050e+00, 4.00756109e+00,
40 4.24549744e+00, 4.48581986e+00, 4.72834735e+00, 4.97291563e+00,
41 5.21937537e+00, 5.46759063e+00, 5.71743745e+00, 5.96880263e+00,
42 6.22158266e+00, 6.47568274e+00, 6.73101590e+00, 6.98750228e+00,
43 7.24506843e+00, 7.50364669e+00, 7.76317466e+00, 8.02359473e+00,
44 8.28485363e+00, 8.54690205e+00, 8.80969427e+00, 9.07318789e+00,
45 9.33734351e+00, 9.60212447e+00, 9.86749668e+00, 1.01334283e+01,
46 1.03998897e+01, 1.06668532e+01, 1.09342929e+01, 1.12021845e+01,
47 1.14705053e+01, 1.17392341e+01, 1.20083509e+01, 1.22778370e+01,
48 1.25476748e+01, 1.28178476e+01, 1.30883399e+01, 1.33591369e+01,
49 1.36302250e+01, 1.39015909e+01, 1.41732223e+01, 1.44451076e+01,
50 1.47172357e+01, 1.49895963e+01, 1.52621795e+01, 1.55349758e+01,
51 1.58079765e+01, 1.60811732e+01, 1.63545578e+01, 1.66281227e+01,
52 1.69018609e+01, 1.71757655e+01, 1.74498298e+01, 1.77240478e+01,
53 1.79984136e+01, 1.82729215e+01, 1.85475662e+01, 1.88223426e+01,
54 1.90972458e+01, 1.93722711e+01, 1.96474142e+01, 1.99226707e+01,
55 2.01980367e+01, 2.04735082e+01, 2.07490816e+01, 2.10247533e+01,
56 2.13005199e+01, 2.15763782e+01, 2.18523250e+01, 2.21283574e+01};
57
58 public:
66 template <typename EigMat1, typename EigMat2,
69 inline Eigen::MatrixXd action(EigMat1&& mat, EigMat2&& b,
70 const double t = 1.0) {
71 Eigen::MatrixXd A = std::forward<EigMat1>(mat);
72 double mu = A.trace() / A.rows();
73 A.diagonal().array() -= mu;
74
75 int m, s;
76 decltype(auto) b_eval = to_ref(std::forward<EigMat2>(b));
77 set_approx_order(A, b_eval, t, m, s);
78
79 double eta = exp(t * mu / s);
80
81 Eigen::MatrixXd f = b_eval;
82 Eigen::MatrixXd bi = b_eval;
83
84 for (int i = 0; i < s; ++i) {
85 // maximum absolute row sum, aka inf-norm of matrix operator
86 double c1 = matrix_operator_inf_norm(bi);
87 for (int j = 0; j < m; ++j) {
88 bi = (t / (s * (j + 1))) * (A * bi);
89 f += bi;
90 double c2 = matrix_operator_inf_norm(bi);
91 if (c1 + c2 < _tol * matrix_operator_inf_norm(f))
92 break;
93 c1 = c2;
94 }
95 f *= eta;
96 bi = f;
97 }
98
99 return f;
100 }
101
107 template <typename EigenMat>
108 double matrix_operator_inf_norm(EigenMat&& x) {
109 return make_holder(
110 [](auto&& x_) { return x_.cwiseAbs().rowwise().sum().maxCoeff(); },
111 std::forward<EigenMat>(x));
112 }
113
131 template <typename EigMat1, require_all_eigen_t<EigMat1>* = nullptr,
132 require_all_st_same<double, EigMat1>* = nullptr>
133 double mat_power_1_norm(EigMat1&& mat, const int m) {
134 auto&& mat_ref = to_ref(std::forward<EigMat1>(mat));
135 if ((mat_ref.array() > 0.0).all()) {
136 Eigen::VectorXd e = Eigen::VectorXd::Constant(mat_ref.rows(), 1.0);
137 for (int j = 0; j < m; ++j) {
138 e = mat_ref.transpose() * e;
139 }
140 return e.lpNorm<Eigen::Infinity>();
141 } else {
142 return mat_ref.pow(m).cwiseAbs().colwise().sum().maxCoeff();
143 }
144 }
145
162 template <typename EigMat1, typename EigMat2,
165 inline void set_approx_order(EigMat1&& mat, EigMat2&& b, const double t,
166 int& m, int& s) {
167 if (t < _tol) {
168 m = 0;
169 s = 1;
170 return;
171 }
172
173 // L1 norm
174 double normA = mat.colwise().template lpNorm<1>().maxCoeff();
175 if (normA < _tol) {
176 m = 0;
177 s = 1;
178 return;
179 }
180
181 Eigen::VectorXd alpha(_p_max - 1);
182 if (normA * (_m_max * b.cols())
183 < 4.0 * _theta_m[_m_max] * _p_max * (_p_max + 3)) {
184 alpha = Eigen::VectorXd::Constant(_p_max - 1, normA);
185 } else {
186 Eigen::VectorXd eta(_p_max);
187 for (int p = 0; p < _p_max; ++p) {
188 eta[p] = std::pow(mat_power_1_norm(mat, p + 2), 1.0 / (p + 2));
189 }
190 for (int p = 0; p < _p_max - 1; ++p) {
191 alpha[p] = std::max(eta[p], eta[p + 1]);
192 }
193 }
194
195 Eigen::MatrixXd mt = Eigen::MatrixXd::Zero(_p_max - 1, _m_max);
196 for (int p = 1; p < _p_max; ++p) {
197 for (int i = p * (p + 1) - 2; i < _m_max; ++i) {
198 mt(p - 1, i) = alpha[p - 1] / _theta_m[i];
199 }
200 }
201
202 Eigen::VectorXd u = Eigen::VectorXd::LinSpaced(_m_max, 1, _m_max);
203 Eigen::MatrixXd c = stan::math::ceil(mt) * u.asDiagonal();
204 for (Eigen::MatrixXd::Index i = 0; i < c.size(); ++i) {
205 if (c(i) <= 1.e-16) {
206 c(i) = std::numeric_limits<double>::infinity();
207 }
208 }
209 int cost = c.colwise().minCoeff().minCoeff(&m);
210 if (std::isinf(cost)) {
211 s = 1;
212 return;
213 }
214 s = std::max(cost / m, 1);
215 }
216};
217
218} // namespace math
219} // namespace stan
220
221#endif
double mat_power_1_norm(EigMat1 &&mat, const int m)
Estimate the 1-norm of mat^m.
Eigen::MatrixXd action(EigMat1 &&mat, EigMat2 &&b, const double t=1.0)
Perform the matrix exponential action exp(A*t)*B.
double matrix_operator_inf_norm(EigenMat &&x)
Eigen expression for matrix operator infinity norm.
static constexpr std::array< double, 100 > _theta_m
void set_approx_order(EigMat1 &&mat, EigMat2 &&b, const double t, int &m, int &s)
Approximation is based on parameter "m" and "s", proposed in CODE FRAGMENT 3.1 of the reference.
The implementation of the work by Awad H.
require_all_t< is_eigen< std::decay_t< Types > >... > require_all_eigen_t
Require all of the types satisfy is_eigen.
Definition is_eigen.hpp:123
require_all_t< std::is_same< scalar_type_t< std::decay_t< T > >, scalar_type_t< std::decay_t< Types > > >... > require_all_st_same
All scalar types of T and all of the Types satisfy std::is_same.
static constexpr double e()
Return the base of the natural logarithm.
Definition constants.hpp:20
auto make_holder(F &&func, Args &&... args)
Calls given function with given arguments.
Definition holder.hpp:481
auto sum(const std::vector< T > &m)
Return the sum of the entries of the specified standard vector.
Definition sum.hpp:23
ref_type_t< T && > to_ref(T &&a)
This evaluates expensive Eigen expressions.
Definition to_ref.hpp:18
fvar< T > ceil(const fvar< T > &x)
Definition ceil.hpp:13
fvar< T > exp(const fvar< T > &x)
Definition exp.hpp:15
The lgamma implementation in stan-math is based on either the reentrant safe lgamma_r implementation ...
bool isinf(const stan::math::var &a)
Return 1 if the specified argument is positive infinity or negative infinity and 0 otherwise.
Definition std_isinf.hpp:16