234 const std::size_t E = envObj.nstages();
241 std::vector<std::vector<double>> qfirst_prev(E), qfirst_curr(E);
243 for (it = 1; it <= opt.iter_max; ++it) {
244 for (std::size_t e = 0; e < E; ++e) analyze(e);
247 qfirst_prev = qfirst_curr;
248 qfirst_curr.assign(E, std::vector<double>());
249 for (std::size_t e = 0; e < E; ++e) {
250 qfirst_curr[e].reserve(M * K);
251 for (std::size_t i = 0; i < M; ++i)
252 for (std::size_t r = 0; r < K; ++r) qfirst_curr[e].push_back(tranQ[e][i][r][0]);
256 for (std::size_t e = 0; e < E && conv; ++e) {
257 const double d = detail::env_maxpe(qfirst_curr[e], qfirst_prev[e]);
258 if (d < 0.0)
continue;
259 if (!std::isfinite(d) || d >= opt.iter_tol) conv =
false;
274 if (
opt.stage_solver !=
"fluid" &&
opt.stage_solver !=
"ctmc")
276 "SolverENV: stage solver '" +
opt.stage_solver +
277 "' is not available; the environment coupling needs a TRANSIENT stage solve and "
278 "only the fluid analyzer and the enumerated CTMC provide one in this port");
279 ctmc_stages = (
opt.stage_solver ==
"ctmc");
280 if (
opt.method ==
"statevec")
282 "SolverENV: the state-vector analyzer (solver_env_statevec_analyzer.m) carries the "
283 "full joint distribution across a switch, which THIS class does not: it is the "
284 "mean-field coupling and carries the marginal means. The state-vector coupling is "
285 "ported as env::SolverEnvStatevec, with its own options and its CTMC stage solver; "
286 "reach it through env::solver_env (env_dispatch.h), which is the analyzer "
287 "selection of SolverENV.init");
288 if (
opt.method ==
"avg" ||
opt.method ==
"dec")
290 "SolverENV: the closed-form fast/slow environment limits (SolverENV.solveEnvLimit) "
291 "replace this fixed point rather than configure it -- neither carries anything "
292 "across a switch. They are ported as env::SolverEnvLimit, with their own result "
293 "type; reach them through env::solver_env (env_dispatch.h), which is the method "
294 "dispatch of SolverENV.runAnalyzer");
298 statedep = (
opt.method ==
"statedep");
308 if (
opt.method !=
"meanfield" &&
opt.method !=
"default" &&
opt.method !=
"mean"
309 &&
opt.method !=
"meancov" &&
opt.method !=
"blend" &&
opt.method !=
"blending"
310 &&
opt.method !=
"smp"
316 meancov = (
opt.method ==
"meancov");
327 std::string sm =
opt.stage.method;
328 if (sm.size() > 4 && sm.compare(0, 4,
"fld.") == 0) sm = sm.substr(4);
329 kp_stages = (!ctmc_stages && sm ==
"kp");
339 dae_stages = (!ctmc_stages && !kp_stages && sm ==
"dae");
346 if (!(
opt.timespan_end > 0.0) || !std::isfinite(
opt.timespan_end))
348 "SolverENV: the stage transient needs a finite positive horizon, "
349 "options.timespan(2); the mean-field coupling integrates each stage's "
350 "TRAJECTORY against the holding-time CDF and has nothing to integrate over an "
352 if (
opt.tran_points < 2)
353 throw InputError(
"SolverENV: the transient grid needs at least two points");
354 if (!std::is_same<T, double>::value)
356 "SolverENV: a fluid stage integrates its drift with LSODA, which is double only; "
357 "rerun with --arith double");
360 if (
opt.method ==
"smp") smp_stage_probabilities();
361 const std::size_t E = envObj.nstages();
365 for (std::size_t e = 1; e < E; ++e)
366 if (stage_M(e) != M || stage_K(e) != K)
368 "SolverENV: every stage must have the same stations and classes; the metrics "
369 "are blended entrywise across them. A LAYERED stage counts the block-diagonal "
370 "union of the layers SolverLN builds for it, which is what layerBlocks "
371 "reports, so two layered stages agree exactly when they have the same layer "
372 "shape -- not merely the same LQN element count");
376 tranT.assign(E, std::vector<double>());
377 tranQ.assign(E, std::vector<std::vector<std::vector<double>>>());
378 tranU.assign(E, std::vector<std::vector<std::vector<double>>>());
379 tranTp.assign(E, std::vector<std::vector<std::vector<double>>>());
380 steady.assign(E, StageSteady());
381 steady_asked.assign(E, 0);
382 det_sojourn = (
opt.sojourn ==
"deterministic");
383 dvals.assign(E, 0.0);
384 refresh_sojourn_means();
387 for (std::size_t e = 0; e < E; ++e)
388 for (std::size_t h = 0; h < E; ++h)
389 if (envObj.arc(e, h).enabled && envObj.arc(e, h).reset_rates) any =
true;
392 "SolverENV: method 'statedep' updates each environment transition from the "
393 "state its stage is left in, and no arc carries a rate reset "
394 "(Environment::set_env_rate_reset, resetEnvRatesFun in the reference); the "
395 "run would be the mean-field fixed point under a different name");
413 void build_lqn_stages() {
414 const std::size_t E = envObj.nstages();
415 lnsolv.assign(E, std::shared_ptr<ln::SolverLN<T>>());
416 lnblk.assign(E, ln::LnLayerBlocks());
417 if (!envObj.has_lqn_stages())
return;
420 "SolverENV: a LayeredNetwork stage is solved by SolverLN over its layers, not by "
421 "an enumerated CTMC over one stage generator -- an LQN has no single generator to "
422 "enumerate. Leave stage_solver at 'fluid', which names the engine each LAYER is "
423 "integrated with (EnvOptions::lqn.layer_solver)");
424 if (opt.lqn.layer_solver !=
"fluid")
426 "SolverENV: a LayeredNetwork stage is carried across an environment switch by its "
427 "queue lengths, so the coupling needs the stage's TRANSIENT; among the layer "
428 "engines only the fluid one produces one, and this ensemble asks for '" +
429 opt.lqn.layer_solver +
"' layers (EnvOptions::lqn.layer_solver)");
430 for (std::size_t e = 0; e < E; ++e) {
431 if (!envObj.is_lqn(e))
continue;
432 ln::LnOptions lo = opt.lqn;
440 lo.timespan_end = opt.timespan_end;
441 lo.tran_points = opt.tran_points;
442 lo.tran_grid.clear();
443 lnsolv[e] = std::make_shared<ln::SolverLN<T>>(envObj.stage(e).lqn_model, lo);
444 lnblk[e] = lnsolv[e]->layer_blocks();
449 std::size_t stage_M(std::size_t e)
const {
450 return envObj.is_lqn(e) ? lnblk[e].M : envObj.stage(e).model.nstations;
454 std::size_t stage_K(std::size_t e)
const {
455 return envObj.is_lqn(e) ? lnblk[e].K : envObj.stage(e).model.nclasses;
476 void lqn_stage_transient(std::size_t e,
bool seed) {
477 ln::SolverLN<T>& s = *lnsolv[e];
478 s.init_from_marginal(seed ? entry[e] : Matrix<double>());
479 s.set_tran_grid(stage_grid(e));
480 const ln::LnTranSolution tr = s.get_tran_avg();
481 const ln::LnLayerBlocks& b = lnblk[e];
486 if (!tr.layers.empty()) tranT[e] = tr.layers[0].t;
487 const std::size_t P = std::max<std::size_t>(tranT[e].size(), 1);
488 tranQ[e].assign(M, std::vector<std::vector<double>>(K, std::vector<double>(P, 0.0)));
489 tranU[e].assign(M, std::vector<std::vector<double>>(K, std::vector<double>(P, 0.0)));
490 tranTp[e].assign(M, std::vector<std::vector<double>>(K, std::vector<double>(P, 0.0)));
491 if (tranT[e].empty())
return;
497 for (std::size_t l = 0; l < tr.layers.size(); ++l) {
498 const ln::LnTranLayer& L = tr.layers[l];
499 for (std::size_t i = 0; i < b.msz[l]; ++i)
500 for (std::size_t r = 0; r < b.ksz[l]; ++r) {
501 const std::size_t row = b.roff[l] + i, col = b.coff[l] + r;
502 copy_series(L.QN[i][r], tranQ[e][row][col]);
503 copy_series(L.UN[i][r], tranU[e][row][col]);
504 copy_series(L.TN[i][r], tranTp[e][row][col]);
517 static void copy_series(
const std::vector<double>& src, std::vector<double>& dst) {
518 for (std::size_t j = 0; j < dst.size() && j < src.size(); ++j) dst[j] = src[j];
522 double arc_cdf(std::size_t e, std::size_t h,
double t)
const {
523 const EnvArc<T>& a = envObj.arc(e, h);
524 if (!a.enabled)
return 0.0;
525 return num_traits<T>::to_double(
lang::dist_cdf(a.dist, num_traits<T>::from_double(t)));
537 void smp_stage_probabilities() {
538 const std::size_t E = envObj.nstages();
539 const double eps = 1e-8;
540 Matrix<double> E0(E, E, 0.0);
541 for (std::size_t k = 0; k < E; ++k)
542 for (std::size_t h = 0; h < E; ++h) {
543 const EnvArc<T>& a = envObj.arc(k, h);
544 if (!a.enabled)
continue;
546 E0(k, h) = (m > 0.0) ? 1.0 / m : 0.0;
548 Matrix<double> P(E, E, 0.0);
549 for (std::size_t k = 0; k < E; ++k)
550 for (std::size_t e = 0; e < E; ++e) {
551 if (k == e || !envObj.arc(k, e).enabled)
continue;
553 while (arc_cdf(k, e, Tup) < 1.0 - eps) {
555 if (Tup > 1e6)
break;
557 const std::size_t N = std::max<std::size_t>(1000,
static_cast<std::size_t
>(std::lround(Tup * 100.0)));
558 const double dt = Tup /
static_cast<double>(N);
559 double sum = 0.0, Fprev = arc_cdf(k, e, 0.0);
560 for (std::size_t i = 0; i < N; ++i) {
561 const double t1 =
static_cast<double>(i + 1) * dt;
562 const double Fnext = arc_cdf(k, e, t1);
563 const double tmid = t1 - 0.5 * dt;
565 for (std::size_t h = 0; h < E; ++h)
566 if (h != k && h != e && envObj.arc(k, h).enabled) surv *= 1.0 - arc_cdf(k, h, tmid);
567 sum += (Fnext - Fprev) * surv;
574 std::vector<double> hold(E, 0.0);
575 const std::size_t Nh = 10000;
576 for (std::size_t k = 0; k < E; ++k) {
577 auto surv = [&](
double t) {
579 for (std::size_t h = 0; h < E; ++h)
580 if (h != k) s *= 1.0 - arc_cdf(k, h, t);
584 while (surv(U) > eps) {
588 const double dt = U /
static_cast<double>(Nh);
589 double integral = 0.0;
590 for (std::size_t i = 0; i < Nh; ++i) {
591 const double t0 =
static_cast<double>(i) * dt, t1 = t0 + dt;
592 integral += (surv(t0) + 4.0 * surv(0.5 * (t0 + t1)) + surv(t1)) * dt / 6.0;
598 for (std::size_t e = 0; e < E; ++e) denom += pie[e] * hold[e];
599 std::vector<double> pi(E, 0.0);
600 for (std::size_t k = 0; k < E; ++k) pi[k] = pie[k] * hold[k] / denom;
601 envObj.prob_env = pi;
602 Matrix<double> emb(E, E, 0.0);
603 for (std::size_t e = 0; e < E; ++e) {
605 for (std::size_t h = 0; h < E; ++h)
606 if (h != e) s += pi[h] * E0(h, e);
608 for (std::size_t k = 0; k < E; ++k)
609 if (k != e) emb(k, e) = pi[k] * E0(k, e) / s;
611 envObj.prob_orig = emb;
615 void refresh_sojourn_means() {
616 if (!det_sojourn)
return;
617 for (std::size_t e = 0; e < envObj.nstages(); ++e)
636 const std::size_t E = envObj.nstages();
637 for (std::size_t e = 0; e < E; ++e) {
638 if (envObj.is_lqn(e)) {
642 lqn_stage_transient(e,
false);
643 if (tranT[e].empty())
continue;
644 const std::size_t last = tranT[e].size() - 1;
645 for (std::size_t i = 0; i < M; ++i)
646 for (std::size_t r = 0; r < K; ++r) entry[e](i, r) = tranQ[e][i][r][last];
654 const ctmc::CtmcTransient<T> tr = ctmc_stage_transient(e,
false, std::vector<double>());
655 if (tr.t.empty())
continue;
656 for (std::size_t i = 0; i < M; ++i)
657 for (std::size_t r = 0; r < K; ++r)
658 entry[e](i, r) = num_traits<T>::to_double(tr.QNt[i][r].back());
662 kp_stage_transient(e,
false);
663 if (tranT[e].empty())
continue;
664 const std::size_t last = tranT[e].size() - 1;
665 for (std::size_t i = 0; i < M; ++i)
666 for (std::size_t r = 0; r < K; ++r) entry[e](i, r) = tranQ[e][i][r][last];
669 fluid::FluidOptions fo = opt.stage;
675 const std::vector<fluid::FluidTranPoint> tr =
677 opt.timespan_end, opt.tran_points)
679 opt.timespan_end, opt.tran_points);
680 if (tr.empty())
continue;
681 for (std::size_t i = 0; i < M; ++i)
682 for (std::size_t r = 0; r < K; ++r) entry[e](i, r) = tr.back().
QN(i, r);
687 void analyze(std::size_t e) {
688 if (envObj.is_lqn(e)) {
689 lqn_stage_transient(e,
true);
694 tranQ[e].assign(M, std::vector<std::vector<double>>(K));
695 tranU[e].assign(M, std::vector<std::vector<double>>(K));
696 tranTp[e].assign(M, std::vector<std::vector<double>>(K));
707 const ctmc::CtmcTransient<T> tr = ctmc_stage_transient(e,
true, stage_grid(e));
708 for (std::size_t j = 0; j < tr.t.size(); ++j) {
709 tranT[e].push_back(num_traits<T>::to_double(tr.t[j]));
710 for (std::size_t i = 0; i < M; ++i)
711 for (std::size_t r = 0; r < K; ++r) {
712 tranQ[e][i][r].push_back(num_traits<T>::to_double(tr.QNt[i][r][j]));
713 tranU[e][i][r].push_back(num_traits<T>::to_double(tr.UNt[i][r][j]));
714 tranTp[e][i][r].push_back(num_traits<T>::to_double(tr.TNt[i][r][j]));
719 const qn::NetworkStruct<T>& sn = envObj.stage(e).model;
721 kp_stage_transient(e,
true);
724 fluid::FluidOptions fo = opt.stage;
725 fo.init_sol = initsol_from_marginal(sn, entry[e]);
730 if (meancov && dae_stages) fo.init_qcov = centry[e];
731 const std::vector<fluid::FluidTranPoint> tr =
733 opt.tran_points, stage_grid(e))
735 opt.tran_points, stage_grid(e));
736 for (
const fluid::FluidTranPoint& p : tr) {
737 tranT[e].push_back(p.t);
738 for (std::size_t i = 0; i < M; ++i)
739 for (std::size_t r = 0; r < K; ++r) {
740 tranQ[e][i][r].push_back(p.QN(i, r));
741 tranU[e][i][r].push_back(p.UN(i, r));
742 tranTp[e][i][r].push_back(p.TN(i, r));
744 if (meancov && dae_stages && p.QCov.rows() == M * K) tranC[e].push_back(p.QCov);
749 if (tranC[e].size() != tranT[e].size()) tranC[e].clear();
768 void kp_stage_transient(std::size_t e,
bool seed) {
771 const qn::NetworkStruct<T>& sn = envObj.stage(e).model;
772 fluid::FluidOptions fo = opt.stage;
775 fo.timespan_end = opt.timespan_end;
777 fo.init_qlen = entry[e];
778 if (meancov) fo.init_qcov = centry[e];
780 fluid::FluidKpTransient tr;
783 const std::size_t P = tr.t.size();
784 tranQ[e].assign(M, std::vector<std::vector<double>>(K, std::vector<double>(P, 0.0)));
785 tranU[e].assign(M, std::vector<std::vector<double>>(K, std::vector<double>(P, 0.0)));
786 tranTp[e].assign(M, std::vector<std::vector<double>>(K, std::vector<double>(P, 0.0)));
790 for (std::size_t n = 0; n < P; ++n)
791 for (std::size_t i = 0; i < M; ++i)
792 for (std::size_t r = 0; r < K; ++r) {
793 tranQ[e][i][r][n] = tr.QN[n](i, r);
794 tranU[e][i][r][n] = tr.UN[n](i, r);
795 tranTp[e][i][r][n] = tr.TN[n](i, r);
797 if (meancov) tranC[e] = tr.QCov;
816 ctmc::CtmcTransient<T> ctmc_stage_transient(std::size_t e,
bool seed,
817 const std::vector<double>& grid)
const {
818 qn::NetworkStruct<T> sn = envObj.stage(e).model;
822 if (seed) seed_ctmc_state(sn, entry[e]);
824 g.reserve(grid.size());
825 for (std::size_t j = 0; j < grid.size(); ++j) g.push_back(num_traits<T>::from_double(grid[j]));
827 sn, ctmc_stage_options(), num_traits<T>::from_int(0),
828 num_traits<T>::from_double(opt.timespan_end), g);
832 ctmc::CtmcOptions ctmc_stage_options()
const {
833 ctmc::CtmcOptions co;
834 co.cutoff = opt.stage_cutoff;
846 void seed_ctmc_state(qn::NetworkStruct<T>& sn,
const Matrix<double>& Q)
const {
847 if (Q.empty())
return;
848 const std::vector<std::vector<std::size_t>> nir = round_marginal(sn, Q);
849 for (std::size_t i = 0; i < sn.nstations && i < nir.size(); ++i) {
850 const std::size_t ind = sn.station_to_node[i];
851 std::vector<std::size_t> ph(sn.nclasses, 1);
852 for (std::size_t r = 0; r < sn.nclasses; ++r) ph[r] = sn.phasessz_of(i + 1, r + 1);
856 Matrix<T> space(1, row.size());
857 for (std::size_t c = 0; c < row.size(); ++c) space(0, c) = row[c];
858 sn.statespace[ind] = space;
859 sn.stateprior[ind] = std::vector<T>(1, num_traits<T>::from_int(1));
872 std::vector<std::vector<std::size_t>> round_marginal(
const qn::NetworkStruct<T>& sn,
873 const Matrix<double>& Q)
const {
874 std::vector<std::vector<std::size_t>> n(sn.nstations, std::vector<std::size_t>(sn.nclasses, 0));
875 for (std::size_t r = 0; r < sn.nclasses; ++r) {
876 const double pop = sn.classes[r].population;
877 std::vector<double> frac(sn.nstations, 0.0);
879 for (std::size_t i = 0; i < sn.nstations; ++i) {
880 const double q = (i < Q.rows() && r < Q.cols()) ? std::max(0.0, Q(i, r)) : 0.0;
881 const double fl = std::floor(q);
882 n[i][r] =
static_cast<std::size_t
>(fl);
884 placed +=
static_cast<long>(fl);
886 if (!std::isfinite(pop))
continue;
887 long want =
static_cast<long>(std::llround(pop));
888 while (placed < want) {
889 std::size_t best = 0;
891 for (std::size_t i = 0; i < sn.nstations; ++i)
892 if (frac[i] > bv) { bv = frac[i]; best = i; }
898 while (placed > want) {
899 std::size_t best = 0;
902 for (std::size_t i = 0; i < sn.nstations; ++i)
903 if (n[i][r] > 0 && frac[i] < bv) { bv = frac[i]; best = i; any =
true; }
915 Matrix<double> QN, UN, TN;
961 const StageSteady& stage_steady(std::size_t e) {
962 if (steady_asked[e])
return steady[e];
964 StageSteady& out = steady[e];
965 out.QN = Matrix<double>(M, K, 0.0);
966 out.UN = Matrix<double>(M, K, 0.0);
967 out.TN = Matrix<double>(M, K, 0.0);
968 if (envObj.is_lqn(e))
return out;
969 const qn::NetworkStruct<T>& sn = envObj.stage(e).model;
972 copy_finite(a.QN, out.QN);
973 copy_finite(a.UN, out.UN);
974 copy_finite(a.TN, out.TN);
977 fluid::FluidOptions fo = opt.stage;
981 fo.init_qlen = Matrix<double>();
982 fo.init_qcov = Matrix<double>();
983 fo.timespan_end = std::numeric_limits<double>::infinity();
984 fluid::FluidSolution s;
988 }
else if (dae_stages) {
992 fo.method =
"closing";
995 copy_finite(s.QN, out.QN);
996 copy_finite(s.UN, out.UN);
997 copy_finite(s.TN, out.TN);
1010 static void copy_finite(
const Matrix<S>& src, Matrix<double>& dst) {
1011 for (std::size_t i = 0; i < dst.rows(); ++i)
1012 for (std::size_t r = 0; r < dst.cols(); ++r) {
1013 const double v = num_traits<S>::to_double(src(i, r));
1014 if (std::isfinite(v)) dst(i, r) = v;
1043 void stage_exit(std::size_t e,
bool want_all, Matrix<double>& Qe, Matrix<double>& Ue,
1044 Matrix<double>& Te) {
1045 std::vector<double> w;
1047 if (!tranT[e].empty() && !det_sojourn) {
1048 w = cdf_weights(envObj.hold_time[e].map(), tranT[e]);
1049 for (
double v : w) wsum += v;
1051 if (tranT[e].empty() || (!det_sojourn && !(wsum > 0.0))) {
1052 const StageSteady& st = stage_steady(e);
1060 for (std::size_t i = 0; i < M; ++i)
1061 for (std::size_t r = 0; r < K; ++r) {
1065 Qe(i, r) = detail::env_det_eval(tranT[e], tranQ[e][i][r], dvals[e]);
1066 if (!want_all)
continue;
1067 Ue(i, r) = detail::env_det_eval(tranT[e], tranU[e][i][r], dvals[e]);
1068 Te(i, r) = detail::env_det_eval(tranT[e], tranTp[e][i][r], dvals[e]);
1071 Qe(i, r) = weighted(tranQ[e][i][r], w, wsum);
1072 if (!want_all)
continue;
1073 Ue(i, r) = weighted(tranU[e][i][r], w, wsum);
1074 Te(i, r) = weighted(tranTp[e][i][r], w, wsum);
1083 const std::size_t E = envObj.nstages();
1084 std::vector<std::vector<Matrix<double>>> Qexit(
1085 E, std::vector<Matrix<double>>(E, Matrix<double>(M, K, 0.0)));
1089 std::vector<std::vector<Matrix<double>>> Uexit, Texit;
1091 Uexit.assign(E, std::vector<Matrix<double>>(E, Matrix<double>(M, K, 0.0)));
1092 Texit.assign(E, std::vector<Matrix<double>>(E, Matrix<double>(M, K, 0.0)));
1094 for (std::size_t e = 0; e < E; ++e) {
1099 Matrix<double> Qd(M, K, 0.0), Ud(M, K, 0.0), Td(M, K, 0.0);
1100 stage_exit(e, statedep, Qd, Ud, Td);
1101 for (std::size_t h = 0; h < E; ++h) {
1103 if (!statedep)
continue;
1113 const std::size_t n2 = M * K;
1114 std::vector<Matrix<double>> Cexit;
1116 Cexit.assign(E, Matrix<double>(n2, n2, 0.0));
1117 for (std::size_t e = 0; e < E; ++e)
1118 if (!tranT[e].empty()) Cexit[e] = stage_exit_cov(e);
1121 for (std::size_t e = 0; e < E; ++e) {
1122 if (tranT[e].empty())
continue;
1123 Matrix<double> Qe(M, K, 0.0);
1124 std::vector<double> ment(meancov ? n2 : 0, 0.0);
1125 Matrix<double> sent(meancov ? n2 : 0, meancov ? n2 : 0, 0.0);
1126 for (std::size_t h = 0; h < E; ++h) {
1127 const double p = envObj.prob_orig(h, e);
1128 if (!(p > 0.0))
continue;
1130 const Matrix<double> reset = f ? f(Qexit[h][e]) : Qexit[h][e];
1131 if (reset.rows() != M || reset.cols() != K)
1133 "SolverENV: a reset policy returned a matrix of the wrong shape");
1134 for (std::size_t i = 0; i < M; ++i)
1135 for (std::size_t r = 0; r < K; ++r) Qe(i, r) += p * reset(i, r);
1136 if (!meancov)
continue;
1142 const Matrix<double> R = reset_jacobian(f, Qexit[h][e]);
1143 const Matrix<double> Ch = congruence(R, Cexit[h]);
1144 for (std::size_t a = 0; a < n2; ++a) {
1145 const double ma = reset(a % M, a / M);
1147 for (std::size_t b = 0; b < n2; ++b)
1148 sent(a, b) += p * (Ch(a, b) + ma * reset(b % M, b / M));
1152 if (!meancov)
continue;
1153 Matrix<double> Ce(n2, n2, 0.0);
1154 for (std::size_t a = 0; a < n2; ++a)
1155 for (std::size_t b = 0; b < n2; ++b)
1156 Ce(a, b) = 0.5 * ((sent(a, b) - ment[a] * ment[b]) +
1157 (sent(b, a) - ment[b] * ment[a]));
1166 if (!statedep)
return;
1167 bool touched =
false;
1168 for (std::size_t e = 0; e < E; ++e)
1169 for (std::size_t h = 0; h < E; ++h) {
1170 const EnvArc<T>& a = envObj.arc(e, h);
1171 if (!a.enabled || !a.reset_rates)
continue;
1172 envObj.set_transition_dist(e, h,
1173 a.reset_rates(a.dist, Qexit[e][h], Uexit[e][h],
1177 if (!touched)
return;
1183 refresh_sojourn_means();
1190 void finish(EnvSolution& out) {
1191 const std::size_t E = envObj.nstages();
1192 out.QExit.assign(E, Matrix<double>(M, K, 0.0));
1193 out.UExit.assign(E, Matrix<double>(M, K, 0.0));
1194 out.TExit.assign(E, Matrix<double>(M, K, 0.0));
1195 for (std::size_t e = 0; e < E; ++e)
1200 stage_exit(e,
true, out.QExit[e], out.UExit[e], out.TExit[e]);
1201 for (std::size_t e = 0; e < E; ++e) {
1202 const double p = envObj.prob_env[e];
1203 for (std::size_t i = 0; i < M; ++i)
1204 for (std::size_t r = 0; r < K; ++r) {
1205 out.QN(i, r) += p * out.QExit[e](i, r);
1206 out.UN(i, r) += p * out.UExit[e](i, r);
1207 out.TN(i, r) += p * out.TExit[e](i, r);
1211 if (!meancov)
return;
1218 const std::size_t n = M * K;
1219 std::vector<double> mval(n, 0.0);
1220 Matrix<double> sval(n, n, 0.0);
1221 for (std::size_t e = 0; e < E; ++e) {
1222 const double p = envObj.prob_env[e];
1223 if (!(p > 0.0) || tranT[e].empty())
continue;
1224 const Matrix<double> Ce = stage_exit_cov(e);
1225 for (std::size_t a = 0; a < n; ++a) {
1226 const double ma = out.QExit[e](a % M, a / M);
1228 for (std::size_t b = 0; b < n; ++b)
1229 sval(a, b) += p * (Ce(a, b) + ma * out.QExit[e](b % M, b / M));
1232 out.QCov = Matrix<double>(n, n, 0.0);
1233 out.QVar = Matrix<double>(M, K, 0.0);
1234 for (std::size_t a = 0; a < n; ++a) {
1235 for (std::size_t b = 0; b < n; ++b)
1236 out.QCov(a, b) = 0.5 * ((sval(a, b) - mval[a] * mval[b]) +
1237 (sval(b, a) - mval[b] * mval[a]));
1238 out.QVar(a % M, a / M) = std::max(0.0, out.QCov(a, a));
1262 static constexpr std::size_t kCdfInterp = 5000;
1264 static std::vector<double> refine_grid(
const mam::Map<double>& m,
1265 const std::vector<double>& t) {
1266 if (t.size() < 2)
return t;
1271 bool all_zero =
true;
1272 for (std::size_t a = 0; a < m.D1.rows() && all_zero; ++a)
1273 for (std::size_t b = 0; b < m.D1.cols() && all_zero; ++b)
1274 if (m.D1(a, b) != 0.0) all_zero =
false;
1275 if (all_zero)
return t;
1276 const double t0 = t.front();
1277 const double tend = t.back();
1279 if (!(mean_sojourn > 0.0) || !std::isfinite(mean_sojourn))
1280 mean_sojourn = (tend - t0) / 10.0;
1281 double tcdf = std::min(tend, 5.0 * mean_sojourn);
1282 if (tcdf <= t0) tcdf = tend;
1283 const std::size_t ndense =
static_cast<std::size_t
>(0.9 * kCdfInterp);
1284 const std::size_t ntail = kCdfInterp - ndense;
1285 const bool with_tail = tcdf < tend && ntail > 1;
1286 std::vector<double> fine;
1287 fine.reserve(with_tail ? ndense + ntail : ndense);
1288 for (std::size_t k = 0; k < ndense; ++k)
1289 fine.push_back(t0 + (tcdf - t0) *
static_cast<double>(k) /
1290 static_cast<double>(ndense - 1));
1292 for (std::size_t k = 1; k <= ntail; ++k)
1293 fine.push_back(tcdf + (tend - tcdf) *
static_cast<double>(k) /
1294 static_cast<double>(ntail));
1313 std::vector<double> stage_grid(std::size_t e)
const {
1314 std::vector<double> ends(2);
1316 ends[1] = opt.timespan_end;
1317 const std::vector<double> g = refine_grid(envObj.hold_time[e].map(), ends);
1320 if (g.size() < 3)
return std::vector<double>();
1337 Matrix<double> stage_exit_cov(std::size_t e)
const {
1338 const std::size_t n = M * K;
1339 Matrix<double> C(n, n, 0.0);
1340 const std::vector<double>& t = tranT[e];
1341 if (t.size() < 2)
return C;
1342 const bool have_c = tranC[e].size() == t.size();
1344 if (!have_c)
return C;
1348 double d = std::max(t.front(), std::min(dvals[e], t.back()));
1349 std::size_t j = t.size() - 1;
1350 for (std::size_t k = 1; k < t.size(); ++k)
1355 const double dt = t[j] - t[j - 1];
1356 const double a = (dt > 0.0) ? (d - t[j - 1]) / dt : 0.0;
1357 for (std::size_t p = 0; p < n; ++p)
1358 for (std::size_t q = 0; q < n; ++q)
1359 C(p, q) = (1.0 - a) * tranC[e][j - 1](p, q) + a * tranC[e][j](p, q);
1362 const std::vector<double> w = cdf_weights(envObj.hold_time[e].map(), t);
1364 for (
double v : w) wsum += v;
1365 if (!(wsum > 0.0))
return C;
1366 std::vector<double> mexit(n, 0.0);
1367 for (std::size_t a = 0; a < n; ++a)
1368 mexit[a] = weighted(tranQ[e][a % M][a / M], w, wsum);
1369 for (std::size_t a = 0; a < n; ++a) {
1370 const std::vector<double>& qa = tranQ[e][a % M][a / M];
1371 for (std::size_t b = 0; b < n; ++b) {
1372 const std::vector<double>& qb = tranQ[e][b % M][b / M];
1374 for (std::size_t k = 0; k < w.size() && k < qa.size() && k < qb.size(); ++k)
1375 acc += w[k] * qa[k] * qb[k];
1377 for (std::size_t k = 0; k < w.size(); ++k) acc += w[k] * tranC[e][k](a, b);
1378 C(a, b) = acc / wsum - mexit[a] * mexit[b];
1381 for (std::size_t a = 0; a < n; ++a)
1382 for (std::size_t b = a + 1; b < n; ++b) {
1383 const double v = 0.5 * (C(a, b) + C(b, a));
1401 Matrix<double> reset_jacobian(
const ResetMarginal& f,
const Matrix<double>& qexit)
const {
1402 const std::size_t n = M * K;
1403 Matrix<double> R(n, n, 0.0);
1405 for (std::size_t a = 0; a < n; ++a) R(a, a) = 1.0;
1408 const Matrix<double> base = f(qexit);
1409 if (base.rows() != M || base.cols() != K)
return R;
1411 for (std::size_t i = 0; i < M; ++i)
1412 for (std::size_t r = 0; r < K; ++r) scale = std::max(scale, std::fabs(qexit(i, r)));
1413 const double step = 1e-6 * scale;
1414 for (std::size_t j = 0; j < n; ++j) {
1415 Matrix<double> qp = qexit;
1416 qp(j % M, j / M) += step;
1417 const Matrix<double> pert = f(qp);
1418 if (pert.rows() != M || pert.cols() != K)
continue;
1419 for (std::size_t a = 0; a < n; ++a)
1420 R(a, j) = (pert(a % M, a / M) - base(a % M, a / M)) / step;
1426 static Matrix<double> congruence(
const Matrix<double>& R,
const Matrix<double>& C) {
1427 const std::size_t n = R.rows();
1428 Matrix<double> RC(n, n, 0.0), out(n, n, 0.0);
1429 for (std::size_t a = 0; a < n; ++a)
1430 for (std::size_t b = 0; b < n; ++b) {
1432 for (std::size_t c = 0; c < n; ++c) acc += R(a, c) * C(c, b);
1435 for (std::size_t a = 0; a < n; ++a)
1436 for (std::size_t b = 0; b < n; ++b) {
1438 for (std::size_t c = 0; c < n; ++c) acc += RC(a, c) * R(b, c);
1445 std::vector<double> cdf_weights(
const mam::Map<double>& m,
const std::vector<double>& t)
const {
1446 std::vector<double> w(t.size(), 0.0);
1447 if (t.size() < 2)
return w;
1449 bool all_zero =
true;
1450 for (std::size_t a = 0; a < m.D1.rows() && all_zero; ++a)
1451 for (std::size_t b = 0; b < m.D1.cols() && all_zero; ++b)
1452 if (m.D1(a, b) != 0.0) all_zero =
false;
1453 if (all_zero)
return w;
1455 for (std::size_t j = 1; j < t.size(); ++j) w[j] = F[j] - F[j - 1];
1459 static double weighted(
const std::vector<double>& v,
const std::vector<double>& w,
1462 for (std::size_t j = 0; j < v.size() && j < w.size(); ++j) s += v[j] * w[j];
1475 std::vector<double> initsol_from_marginal(
const qn::NetworkStruct<T>& sn,
1476 const Matrix<double>& Q)
const {
1478 std::vector<double> y(L.nstates, 0.0);
1479 for (std::size_t i = 0; i < sn.nstations && i < Q.rows(); ++i)
1480 for (std::size_t r = 0; r < sn.nclasses && r < Q.cols(); ++r) {
1481 if (!L.enabled[i][r])
continue;
1482 y[L.qidx[i][r]] = std::max(0.0, Q(i, r));
1487 Environment<T>& envObj;
1489 std::size_t M = 0, K = 0;
1490 bool det_sojourn =
false;
1491 bool statedep =
false;
1492 bool ctmc_stages =
false;
1493 bool meancov =
false;
1494 bool kp_stages =
false;
1495 bool dae_stages =
false;
1496 std::vector<double> dvals;
1497 std::vector<Matrix<double>> entry;
1505 std::vector<Matrix<double>> centry;
1506 std::vector<std::vector<Matrix<double>>> tranC;
1507 std::vector<std::vector<double>> tranT;
1508 std::vector<std::vector<std::vector<std::vector<double>>>> tranQ, tranU, tranTp;
1514 std::vector<StageSteady> steady;
1515 std::vector<char> steady_asked;
1517 std::vector<std::shared_ptr<ln::SolverLN<T>>> lnsolv;
1519 std::vector<ln::LnLayerBlocks> lnblk;