5#ifndef LINE_API_MC_CTMC_TRANSIENT_H
6#define LINE_API_MC_CTMC_TRANSIENT_H
68template <
class T,
class F>
69void ode23(F f,
const T& t0,
const T& t1,
const std::vector<T>& y0,
double rtol,
double atol,
70 std::vector<T>& tout, std::vector<std::vector<T>>& yout) {
71 const std::size_t n = y0.size();
77 const T eps = std::numeric_limits<T>::epsilon();
78 const T span = t1 - t0;
79 if (!(span > zero))
throw InputError(
"ode23: the time interval must have positive length");
82 std::vector<T> y = y0, f1(n), f2(n), f3(n), f4(n), ytmp(n), ynew(n);
90 for (std::size_t i = 0; i < n; ++i) {
92 if (d < threshold) d = threshold;
93 const T v =
num_abs(T(f1[i] / d));
97 if (h * rh > one) h = one / rh;
106 if (h > hmax) h = hmax;
107 if (h < hmin) h = hmin;
113 bool nofailed =
true;
118 for (std::size_t i = 0; i < n; ++i)
121 for (std::size_t i = 0; i < n; ++i)
129 for (std::size_t i = 0; i < n; ++i) {
131 const T dn =
num_abs(T(ynew[i]));
133 if (d < threshold) d = threshold;
138 if (v > err) err = v;
142 if (!(err > rtolT))
break;
144 throw NumericError(
"ode23: step size underflow, the system is too stiff for ode23");
148 if (h < hmin) h = hmin;
191 double rtol = 1e-3,
double atol = 1e-6) {
193 "ctmc_transient requires transcendental arithmetic: the ode23 step controller "
194 "raises the error ratio to the power 1/3, and the result is an approximation of "
195 "pi0 exp(Qt) governed by a tolerance rather than an exact quantity");
196 const std::size_t n = Q.
rows();
197 if (Q.
cols() != n)
throw InputError(
"ctmc_transient: generator is not square");
198 if (pi0.size() != n)
throw InputError(
"ctmc_transient: pi0 has the wrong length");
201 std::vector<std::vector<T>> yv;
203 [&Q, n](
const std::vector<T>& y, std::vector<T>& dy) {
204 for (std::size_t j = 0; j < n; ++j) {
206 for (std::size_t i = 0; i < n; ++i) s += y[i] * Q(i, j);
210 t0, t1, pi0, rtol, atol, tv, yv);
215 for (std::size_t k = 0; k < yv.size(); ++k)
216 for (std::size_t j = 0; j < n; ++j) r.pi(k, j) = yv[k][j];
228 double atol = 1e-6) {
229 const std::size_t n = Q.
rows();
230 if (n == 0)
throw InputError(
"ctmc_transient: empty generator");
256 const std::vector<T>& grid);
260 const T& t0,
const T& t1,
const T& dt) {
262 throw InputError(
"ctmc_transient_on_grid: the timestep must be positive");
263 if (r.
t.empty())
return r;
268 for (
double x = d0; x <= d1 + 1e-12 * (d1 - d0); x += dd)
302 const std::vector<T>& grid) {
303 if (r.
t.empty() || grid.empty())
return r;
304 const std::size_t n = Q.
rows();
311 for (std::size_t g = 0; g < grid.size(); ++g) {
315 const std::size_t k1 = (k + 1 < r.
t.size()) ? k + 1 : k;
317 if (k1 == k || b <= a) {
318 for (std::size_t j = 0; j < n; ++j) out.
pi(g, j) = r.
pi(k, j);
325 double u = (x - a) / (b - a);
326 if (u < 0.0) u = 0.0;
327 if (u > 1.0) u = 1.0;
328 for (std::size_t j = 0; j < n; ++j)
NumericError(const std::string &what)
The exception types the port throws.
Dense matrix and non-owning view.
TransientResult< T > ctmc_transient_on_grid(const Matrix< T > &Q, const TransientResult< T > &r, const std::vector< T > &grid)
Resample an adaptive transient onto the uniform grid t0 : dt : t1.
TransientResult< T > ctmc_transient(const Matrix< T > &Q, const std::vector< T > &pi0, const T &t0, const T &t1, double rtol=1e-3, double atol=1e-6)
Transient distribution of a CTMC over a time interval, by integrating the forward equations d pi/dt =...
Number-type abstraction for the templated API port.
std::vector< T > t
accepted time points, the first being t0
Matrix< T > pi
one row per time point