265 const std::size_t E = envObj.nstages();
272 std::vector<std::vector<double>> qfirst_prev(E), qfirst_curr(E);
274 for (it = 1; it <= opt.iter_max; ++it) {
275 for (std::size_t e = 0; e < E; ++e) analyze(e);
278 qfirst_prev = qfirst_curr;
279 qfirst_curr.assign(E, std::vector<double>());
280 for (std::size_t e = 0; e < E; ++e) {
281 qfirst_curr[e].reserve(M * K);
282 for (std::size_t i = 0; i < M; ++i)
283 for (std::size_t r = 0; r < K; ++r) qfirst_curr[e].push_back(tranQ[e][i][r][0]);
287 for (std::size_t e = 0; e < E && conv; ++e) {
288 const double d = detail::env_maxpe(qfirst_curr[e], qfirst_prev[e]);
289 if (d < 0.0)
continue;
290 if (!std::isfinite(d) || d >= opt.iter_tol) conv =
false;
308 if (
opt.stage_solver ==
"mva" ||
opt.stage_solver ==
"nc")
310 "SolverENV: the mean-field environment coupling integrates each stage over its "
311 "sojourn, so it needs transient averages from the stage solver, which " +
312 std::string(
opt.stage_solver ==
"mva" ?
"SolverMVA" :
"SolverNC") +
313 " (stage 1) does not produce. Use a fluid or CTMC stage solver, or method='dec' "
315 if (
opt.stage_solver !=
"fluid" &&
opt.stage_solver !=
"ctmc")
318 "' is not available; the environment coupling needs a TRANSIENT stage solve and "
319 "only the fluid analyzer and the enumerated CTMC provide one in this port");
321 if (opt.
method ==
"statevec")
323 "SolverENV: the state-vector analyzer (solver_env_statevec_analyzer.m) carries the "
324 "full joint distribution across a switch, which THIS class does not: it is the "
325 "mean-field coupling and carries the marginal means. The state-vector coupling is "
326 "ported as env::SolverEnvStatevec, with its own options and its CTMC stage solver; "
327 "reach it through env::solver_env (env_dispatch.h), which is the analyzer "
328 "selection of SolverENV.init");
329 if (opt.method ==
"avg" || opt.method ==
"dec" || opt.method ==
"occupation")
331 "SolverENV: the closed-form fast/slow environment limits (SolverENV.solveEnvLimit) "
332 "and the occupation weighting (SolverENV.solveEnvOccupation) "
333 "replace this fixed point rather than configure it -- neither carries anything "
334 "across a switch. They are ported as env::SolverEnvLimit, with their own result "
335 "type; reach them through env::solver_env (env_dispatch.h), which is the method "
336 "dispatch of SolverENV.runAnalyzer");
340 smp = (opt.method ==
"smp");
341 statedep = (opt.method ==
"statedep");
351 if (opt.method !=
"meanfield" && opt.method !=
"default" && opt.method !=
"mean"
352 && opt.method !=
"meancov" && opt.method !=
"blend" && opt.method !=
"blending"
353 && opt.method !=
"smp"
355 throw UnsupportedError(ErrorKind{
"Method"},
"SolverENV: unknown method '" + opt.method +
"'");
359 meancov = (opt.method ==
"meancov");
370 std::string sm = opt.stage.method;
371 if (sm.size() > 4 && sm.compare(0, 4,
"fld.") == 0) sm = sm.substr(4);
372 kp_stages = (!ctmc_stages && sm ==
"kp");
382 dae_stages = (!ctmc_stages && !kp_stages && sm ==
"dae");
389 if (!(opt.timespan_end > 0.0) || !std::isfinite(opt.timespan_end))
391 "SolverENV: the stage transient needs a finite positive horizon, "
392 "options.timespan(2); the mean-field coupling integrates each stage's "
393 "TRAJECTORY against the holding-time CDF and has nothing to integrate over an "
395 if (opt.tran_points < 2)
396 throw InputError(
"SolverENV: the transient grid needs at least two points");
397 if (!std::is_same<T, double>::value)
399 "SolverENV: a fluid stage integrates its drift with LSODA, which is double only; "
400 "rerun with --arith double");
404 envObj.require_declared();
405 envObj.refuse_semi_markov(opt.method);
406 if (smp && envObj.is_semi_markov() && opt.stage_solver ==
"fluid" &&
407 !envObj.has_lqn_stages()) {
408 const qn::NetworkStruct<T>& sn1 = envObj.stage(0).model;
409 for (std::size_t ind = 0; ind < sn1.nodes.size(); ++ind)
412 "SolverENV: The mean-field cache fixed point integrates each stage's "
413 "holding time as a phase process inside the cache drift, which a "
414 "semi-Markov stage (a non-Markovian or kernel transition) does not have. "
415 "Fit the transitions to phase-type laws, or use a non-fluid stage solver.");
417 envObj.refuse_absorbing(opt.method);
419 const std::size_t E = envObj.nstages();
421 for (std::size_t e = 1; e < E; ++e)
422 if (envObj.is_lqn(0) && envObj.is_lqn(e) && lnsolv[e]->nlayers() != lnsolv[0]->nlayers())
424 "Stages 1 and " + std::to_string(e + 1) +
" are layered networks whose layers "
425 "differ (a zero-capacity element removes the layers only a trapped reference task "
426 "reaches), and method '" + opt.method +
"' couples the stages layer by layer. Use "
427 "method 'occupation'.");
430 for (std::size_t e = 1; e < E; ++e)
431 if (stage_M(e) != M || stage_K(e) != K)
433 "SolverENV: every stage must have the same stations and classes; the metrics "
434 "are blended entrywise across them. A LAYERED stage counts the block-diagonal "
435 "union of the layers SolverLN builds for it, which is what layerBlocks "
436 "reports, so two layered stages agree exactly when they have the same layer "
437 "shape -- not merely the same LQN element count");
438 entry.assign(E, Matrix<double>(M, K, 0.0));
439 centry.assign(E, Matrix<double>(M * K, M * K, 0.0));
440 tranC.assign(E, std::vector<Matrix<double>>());
441 tranT.assign(E, std::vector<double>());
442 tranQ.assign(E, std::vector<std::vector<std::vector<double>>>());
443 tranU.assign(E, std::vector<std::vector<std::vector<double>>>());
444 tranTp.assign(E, std::vector<std::vector<std::vector<double>>>());
445 steady.assign(E, StageSteady());
446 steady_asked.assign(E, 0);
447 det_sojourn = (opt.sojourn ==
"deterministic");
448 dvals.assign(E, 0.0);
449 refresh_sojourn_means();
452 for (std::size_t e = 0; e < E; ++e)
453 for (std::size_t h = 0; h < E; ++h)
454 if (envObj.arc(e, h).enabled && envObj.arc(e, h).reset_rates) any =
true;
457 "SolverENV: method 'statedep' updates each environment transition from the "
458 "state its stage is left in, and no arc carries a rate reset "
459 "(Environment::set_env_rate_reset, resetEnvRatesFun in the reference); the "
460 "run would be the mean-field fixed point under a different name");
478 void build_lqn_stages() {
479 const std::size_t E = envObj.nstages();
480 lnsolv.assign(E, std::shared_ptr<ln::SolverLN<T>>());
481 lnblk.assign(E, ln::LnLayerBlocks());
482 if (!envObj.has_lqn_stages())
return;
485 "SolverENV: a LayeredNetwork stage is solved by SolverLN over its layers, not by "
486 "an enumerated CTMC over one stage generator -- an LQN has no single generator to "
487 "enumerate. Leave stage_solver at 'fluid', which names the engine each LAYER is "
488 "integrated with (EnvOptions::lqn.layer_solver)");
489 if (opt.lqn.layer_solver !=
"fluid")
491 "SolverENV: a LayeredNetwork stage is carried across an environment switch by its "
492 "queue lengths, so the coupling needs the stage's TRANSIENT; among the layer "
493 "engines only the fluid one produces one, and this ensemble asks for '" +
494 opt.lqn.layer_solver +
"' layers (EnvOptions::lqn.layer_solver)");
495 for (std::size_t e = 0; e < E; ++e) {
496 if (!envObj.is_lqn(e))
continue;
497 ln::LnOptions lo = opt.lqn;
505 lo.timespan_end = opt.timespan_end;
506 lo.tran_points = opt.tran_points;
507 lo.tran_grid.clear();
508 lnsolv[e] = std::make_shared<ln::SolverLN<T>>(envObj.stage(e).lqn_model, lo);
509 lnblk[e] = lnsolv[e]->layer_blocks();
514 std::size_t stage_M(std::size_t e)
const {
515 return envObj.is_lqn(e) ? lnblk[e].M : envObj.stage(e).model.nstations;
519 std::size_t stage_K(std::size_t e)
const {
520 return envObj.is_lqn(e) ? lnblk[e].K : envObj.stage(e).model.nclasses;
541 void lqn_stage_transient(std::size_t e,
bool seed) {
542 ln::SolverLN<T>& s = *lnsolv[e];
543 s.init_from_marginal(seed ? entry[e] : Matrix<double>());
544 s.set_tran_grid(stage_grid(e));
545 const ln::LnTranSolution tr = s.get_tran_avg();
546 const ln::LnLayerBlocks& b = lnblk[e];
551 if (!tr.layers.empty()) tranT[e] = tr.layers[0].t;
552 const std::size_t
P = std::max<std::size_t>(tranT[e].size(), 1);
553 tranQ[e].assign(M, std::vector<std::vector<double>>(K, std::vector<double>(P, 0.0)));
554 tranU[e].assign(M, std::vector<std::vector<double>>(K, std::vector<double>(P, 0.0)));
555 tranTp[e].assign(M, std::vector<std::vector<double>>(K, std::vector<double>(P, 0.0)));
556 if (tranT[e].empty())
return;
562 for (std::size_t l = 0; l < tr.layers.size(); ++l) {
563 const ln::LnTranLayer& L = tr.layers[l];
564 for (std::size_t i = 0; i < b.msz[l]; ++i)
565 for (std::size_t r = 0; r < b.ksz[l]; ++r) {
566 const std::size_t row = b.roff[l] + i, col = b.coff[l] + r;
567 copy_series(L.QN[i][r], tranQ[e][row][col]);
568 copy_series(L.UN[i][r], tranU[e][row][col]);
569 copy_series(L.TN[i][r], tranTp[e][row][col]);
582 static void copy_series(
const std::vector<double>& src, std::vector<double>& dst) {
583 for (std::size_t j = 0; j < dst.size() && j < src.size(); ++j) dst[j] = src[j];
587 void refresh_sojourn_means() {
588 if (!det_sojourn)
return;
589 for (std::size_t e = 0; e < envObj.nstages(); ++e)
590 dvals[e] = envObj.hold_time_mean(e);
608 const std::size_t E = envObj.nstages();
609 for (std::size_t e = 0; e < E; ++e) {
610 if (envObj.is_lqn(e)) {
614 lqn_stage_transient(e,
false);
615 if (tranT[e].empty())
continue;
616 const std::size_t last = tranT[e].size() - 1;
617 for (std::size_t i = 0; i < M; ++i)
618 for (std::size_t r = 0; r < K; ++r) entry[e](i, r) = tranQ[e][i][r][last];
626 const ctmc::CtmcTransient<T> tr = ctmc_stage_transient(e,
false, std::vector<double>());
627 if (tr.t.empty())
continue;
628 for (std::size_t i = 0; i < M; ++i)
629 for (std::size_t r = 0; r < K; ++r)
630 entry[e](i, r) = num_traits<T>::to_double(tr.QNt[i][r].back());
634 kp_stage_transient(e,
false);
635 if (tranT[e].empty())
continue;
636 const std::size_t last = tranT[e].size() - 1;
637 for (std::size_t i = 0; i < M; ++i)
638 for (std::size_t r = 0; r < K; ++r) entry[e](i, r) = tranQ[e][i][r][last];
641 fluid::FluidOptions fo = opt.stage;
647 const std::vector<fluid::FluidTranPoint> tr =
649 opt.timespan_end, opt.tran_points)
651 opt.timespan_end, opt.tran_points);
652 if (tr.empty())
continue;
653 for (std::size_t i = 0; i < M; ++i)
654 for (std::size_t r = 0; r < K; ++r) entry[e](i, r) = tr.back().
QN(i, r);
659 void analyze(std::size_t e) {
660 if (envObj.is_lqn(e)) {
661 lqn_stage_transient(e,
true);
666 tranQ[e].assign(M, std::vector<std::vector<double>>(K));
667 tranU[e].assign(M, std::vector<std::vector<double>>(K));
668 tranTp[e].assign(M, std::vector<std::vector<double>>(K));
679 const ctmc::CtmcTransient<T> tr = ctmc_stage_transient(e,
true, stage_grid(e));
680 for (std::size_t j = 0; j < tr.t.size(); ++j) {
681 tranT[e].push_back(num_traits<T>::to_double(tr.t[j]));
682 for (std::size_t i = 0; i < M; ++i)
683 for (std::size_t r = 0; r < K; ++r) {
684 tranQ[e][i][r].push_back(num_traits<T>::to_double(tr.QNt[i][r][j]));
685 tranU[e][i][r].push_back(num_traits<T>::to_double(tr.UNt[i][r][j]));
686 tranTp[e][i][r].push_back(num_traits<T>::to_double(tr.TNt[i][r][j]));
691 const qn::NetworkStruct<T>& sn = envObj.stage(e).model;
693 kp_stage_transient(e,
true);
696 fluid::FluidOptions fo = opt.stage;
697 fo.init_sol = initsol_from_marginal(sn, entry[e]);
702 if (meancov && dae_stages) fo.init_qcov = centry[e];
703 const std::vector<fluid::FluidTranPoint> tr =
705 opt.tran_points, stage_grid(e))
707 opt.tran_points, stage_grid(e));
708 for (
const fluid::FluidTranPoint& p : tr) {
709 tranT[e].push_back(p.t);
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(p.QN(i, r));
713 tranU[e][i][r].push_back(p.UN(i, r));
714 tranTp[e][i][r].push_back(p.TN(i, r));
716 if (meancov && dae_stages && p.QCov.rows() == M * K) tranC[e].push_back(p.QCov);
721 if (tranC[e].size() != tranT[e].size()) tranC[e].clear();
740 void kp_stage_transient(std::size_t e,
bool seed) {
743 const qn::NetworkStruct<T>& sn = envObj.stage(e).model;
744 fluid::FluidOptions fo = opt.stage;
747 fo.timespan_end = opt.timespan_end;
749 fo.init_qlen = entry[e];
750 if (meancov) fo.init_qcov = centry[e];
752 fluid::FluidKpTransient tr;
755 const std::size_t
P = tr.t.size();
756 tranQ[e].assign(M, std::vector<std::vector<double>>(K, std::vector<double>(P, 0.0)));
757 tranU[e].assign(M, std::vector<std::vector<double>>(K, std::vector<double>(P, 0.0)));
758 tranTp[e].assign(M, std::vector<std::vector<double>>(K, std::vector<double>(P, 0.0)));
762 for (std::size_t n = 0; n <
P; ++n)
763 for (std::size_t i = 0; i < M; ++i)
764 for (std::size_t r = 0; r < K; ++r) {
765 tranQ[e][i][r][n] = tr.QN[n](i, r);
766 tranU[e][i][r][n] = tr.UN[n](i, r);
767 tranTp[e][i][r][n] = tr.TN[n](i, r);
769 if (meancov) tranC[e] = tr.QCov;
788 ctmc::CtmcTransient<T> ctmc_stage_transient(std::size_t e,
bool seed,
789 const std::vector<double>& grid)
const {
790 qn::NetworkStruct<T> sn = envObj.stage(e).model;
794 if (seed) seed_ctmc_state(sn, entry[e]);
796 g.reserve(grid.size());
797 for (std::size_t j = 0; j < grid.size(); ++j) g.push_back(num_traits<T>::from_double(grid[j]));
799 sn, ctmc_stage_options(), num_traits<T>::from_int(0),
800 num_traits<T>::from_double(opt.timespan_end), g);
804 ctmc::CtmcOptions ctmc_stage_options()
const {
805 ctmc::CtmcOptions co;
806 co.cutoff = opt.stage_cutoff;
818 void seed_ctmc_state(qn::NetworkStruct<T>& sn,
const Matrix<double>& Q)
const {
819 if (Q.empty())
return;
820 const std::vector<std::vector<std::size_t>> nir = round_marginal(sn, Q);
821 for (std::size_t i = 0; i < sn.nstations && i < nir.size(); ++i) {
822 const std::size_t ind = sn.station_to_node[i];
827 std::vector<std::size_t> ph(sn.nclasses, 1);
828 for (std::size_t r = 0; r < sn.nclasses; ++r) ph[r] = sn.phasessz_of(i + 1, r + 1);
832 Matrix<T> space(1, row.size());
833 for (std::size_t c = 0; c < row.size(); ++c) space(0, c) = row[c];
834 sn.statespace[ind] = space;
835 sn.stateprior[ind] = std::vector<T>(1, num_traits<T>::from_int(1));
848 std::vector<std::vector<std::size_t>> round_marginal(
const qn::NetworkStruct<T>& sn,
849 const Matrix<double>& Q)
const {
850 std::vector<std::vector<std::size_t>> n(sn.nstations, std::vector<std::size_t>(sn.nclasses, 0));
851 for (std::size_t r = 0; r < sn.nclasses; ++r) {
852 const double pop = sn.classes[r].population;
853 std::vector<double> frac(sn.nstations, 0.0);
855 for (std::size_t i = 0; i < sn.nstations; ++i) {
856 const double q = (i < Q.rows() && r < Q.cols()) ? std::max(0.0, Q(i, r)) : 0.0;
857 const double fl = std::floor(q);
858 n[i][r] =
static_cast<std::size_t
>(fl);
860 placed +=
static_cast<long>(fl);
862 if (!std::isfinite(pop))
continue;
863 long want =
static_cast<long>(std::llround(pop));
864 while (placed < want) {
865 std::size_t best = 0;
867 for (std::size_t i = 0; i < sn.nstations; ++i)
868 if (frac[i] > bv) { bv = frac[i]; best = i; }
874 while (placed > want) {
875 std::size_t best = 0;
878 for (std::size_t i = 0; i < sn.nstations; ++i)
879 if (n[i][r] > 0 && frac[i] < bv) { bv = frac[i]; best = i; any =
true; }
891 Matrix<double> QN, UN, TN;
937 const StageSteady& stage_steady(std::size_t e) {
938 if (steady_asked[e])
return steady[e];
940 StageSteady& out = steady[e];
941 out.QN = Matrix<double>(M, K, 0.0);
942 out.UN = Matrix<double>(M, K, 0.0);
943 out.TN = Matrix<double>(M, K, 0.0);
944 if (envObj.is_lqn(e))
return out;
945 const qn::NetworkStruct<T>& sn = envObj.stage(e).model;
948 copy_finite(a.QN, out.QN);
949 copy_finite(a.UN, out.UN);
950 copy_finite(a.TN, out.TN);
953 fluid::FluidOptions fo = opt.stage;
957 fo.init_qlen = Matrix<double>();
958 fo.init_qcov = Matrix<double>();
959 fo.timespan_end = std::numeric_limits<double>::infinity();
960 fluid::FluidSolution s;
964 }
else if (dae_stages) {
968 fo.method =
"closing";
971 copy_finite(s.QN, out.QN);
972 copy_finite(s.UN, out.UN);
973 copy_finite(s.TN, out.TN);
986 static void copy_finite(
const Matrix<S>& src, Matrix<double>& dst) {
987 for (std::size_t i = 0; i < dst.rows(); ++i)
988 for (std::size_t r = 0; r < dst.cols(); ++r) {
989 const double v = num_traits<S>::to_double(src(i, r));
990 if (std::isfinite(v)) dst(i, r) = v;
1019 void stage_exit(std::size_t e,
bool want_all, Matrix<double>& Qe, Matrix<double>& Ue,
1020 Matrix<double>& Te) {
1021 std::vector<double> w;
1023 if (!tranT[e].empty() && !det_sojourn) {
1024 w = cdf_weights(e, tranT[e]);
1025 for (
double v : w) wsum += v;
1027 if (tranT[e].empty() || (!det_sojourn && !(wsum > 0.0))) {
1028 const StageSteady& st = stage_steady(e);
1036 for (std::size_t i = 0; i < M; ++i)
1037 for (std::size_t r = 0; r < K; ++r) {
1041 Qe(i, r) = detail::env_det_eval(tranT[e], tranQ[e][i][r], dvals[e]);
1042 if (!want_all)
continue;
1043 Ue(i, r) = detail::env_det_eval(tranT[e], tranU[e][i][r], dvals[e]);
1044 Te(i, r) = detail::env_det_eval(tranT[e], tranTp[e][i][r], dvals[e]);
1047 Qe(i, r) = weighted(tranQ[e][i][r], w, wsum);
1048 if (!want_all)
continue;
1049 Ue(i, r) = weighted(tranU[e][i][r], w, wsum);
1050 Te(i, r) = weighted(tranTp[e][i][r], w, wsum);
1059 const std::size_t E = envObj.nstages();
1060 std::vector<std::vector<Matrix<double>>> Qexit(
1061 E, std::vector<Matrix<double>>(E, Matrix<double>(M, K, 0.0)));
1065 std::vector<std::vector<Matrix<double>>> Uexit, Texit;
1067 Uexit.assign(E, std::vector<Matrix<double>>(E, Matrix<double>(M, K, 0.0)));
1068 Texit.assign(E, std::vector<Matrix<double>>(E, Matrix<double>(M, K, 0.0)));
1070 for (std::size_t e = 0; e < E; ++e) {
1075 Matrix<double> Qd(M, K, 0.0), Ud(M, K, 0.0), Td(M, K, 0.0);
1076 stage_exit(e, statedep, Qd, Ud, Td);
1077 for (std::size_t h = 0; h < E; ++h) {
1079 if (!statedep)
continue;
1083 if (dest_resolved(e)) exit_by_destination(e, Qexit, Uexit, Texit);
1090 const std::size_t n2 = M * K;
1091 std::vector<Matrix<double>> Cexit;
1093 Cexit.assign(E, Matrix<double>(n2, n2, 0.0));
1094 for (std::size_t e = 0; e < E; ++e)
1095 if (!tranT[e].empty()) Cexit[e] = stage_exit_cov(e);
1098 for (std::size_t e = 0; e < E; ++e) {
1099 if (tranT[e].empty())
continue;
1100 Matrix<double> Qe(M, K, 0.0);
1101 std::vector<double> ment(meancov ? n2 : 0, 0.0);
1102 Matrix<double> sent(meancov ? n2 : 0, meancov ? n2 : 0, 0.0);
1103 for (std::size_t h = 0; h < E; ++h) {
1104 const double p = envObj.prob_orig(h, e);
1105 if (!(p > 0.0))
continue;
1107 const Matrix<double> reset = f ? f(Qexit[h][e]) : Qexit[h][e];
1108 if (reset.rows() != M || reset.cols() != K)
1110 "SolverENV: a reset policy returned a matrix of the wrong shape");
1111 for (std::size_t i = 0; i < M; ++i)
1112 for (std::size_t r = 0; r < K; ++r) Qe(i, r) += p * reset(i, r);
1113 if (!meancov)
continue;
1119 const Matrix<double> R = reset_jacobian(f, Qexit[h][e]);
1120 const Matrix<double> Ch = congruence(R, Cexit[h]);
1121 for (std::size_t a = 0; a < n2; ++a) {
1122 const double ma = reset(a % M, a / M);
1124 for (std::size_t b = 0; b < n2; ++b)
1125 sent(a, b) += p * (Ch(a, b) + ma * reset(b % M, b / M));
1129 if (!meancov)
continue;
1130 Matrix<double> Ce(n2, n2, 0.0);
1131 for (std::size_t a = 0; a < n2; ++a)
1132 for (std::size_t b = 0; b < n2; ++b)
1133 Ce(a, b) = 0.5 * ((sent(a, b) - ment[a] * ment[b]) +
1134 (sent(b, a) - ment[b] * ment[a]));
1143 if (!statedep)
return;
1144 bool touched =
false;
1145 for (std::size_t e = 0; e < E; ++e)
1146 for (std::size_t h = 0; h < E; ++h) {
1147 const EnvArc<T>& a = envObj.arc(e, h);
1148 if (!a.enabled || !a.reset_rates)
continue;
1149 envObj.set_transition_dist(e, h,
1150 a.reset_rates(a.dist, Qexit[e][h], Uexit[e][h],
1154 if (!touched)
return;
1160 refresh_sojourn_means();
1167 void finish(EnvSolution& out) {
1168 const std::size_t E = envObj.nstages();
1169 out.QExit.assign(E, Matrix<double>(M, K, 0.0));
1170 out.UExit.assign(E, Matrix<double>(M, K, 0.0));
1171 out.TExit.assign(E, Matrix<double>(M, K, 0.0));
1172 for (std::size_t e = 0; e < E; ++e)
1177 stage_exit(e,
true, out.QExit[e], out.UExit[e], out.TExit[e]);
1178 for (std::size_t e = 0; e < E; ++e) {
1179 const double p = envObj.prob_env[e];
1180 for (std::size_t i = 0; i < M; ++i)
1181 for (std::size_t r = 0; r < K; ++r) {
1182 out.QN(i, r) += p * out.QExit[e](i, r);
1183 out.UN(i, r) += p * out.UExit[e](i, r);
1184 out.TN(i, r) += p * out.TExit[e](i, r);
1188 if (!meancov)
return;
1195 const std::size_t n = M * K;
1196 std::vector<double> mval(n, 0.0);
1197 Matrix<double> sval(n, n, 0.0);
1198 for (std::size_t e = 0; e < E; ++e) {
1199 const double p = envObj.prob_env[e];
1200 if (!(p > 0.0) || tranT[e].empty())
continue;
1201 const Matrix<double> Ce = stage_exit_cov(e);
1202 for (std::size_t a = 0; a < n; ++a) {
1203 const double ma = out.QExit[e](a % M, a / M);
1205 for (std::size_t b = 0; b < n; ++b)
1206 sval(a, b) += p * (Ce(a, b) + ma * out.QExit[e](b % M, b / M));
1209 out.QCov = Matrix<double>(n, n, 0.0);
1210 out.QVar = Matrix<double>(M, K, 0.0);
1211 for (std::size_t a = 0; a < n; ++a) {
1212 for (std::size_t b = 0; b < n; ++b)
1213 out.QCov(a, b) = 0.5 * ((sval(a, b) - mval[a] * mval[b]) +
1214 (sval(b, a) - mval[b] * mval[a]));
1215 out.QVar(a % M, a / M) = std::max(0.0, out.QCov(a, a));
1239 static constexpr std::size_t kCdfInterp = 5000;
1241 static std::vector<double> refine_grid(
double mean_hold,
bool disabled,
1242 const std::vector<double>& t) {
1243 if (t.size() < 2)
return t;
1248 if (disabled)
return t;
1249 const double t0 = t.front();
1250 const double tend = t.back();
1251 double mean_sojourn = mean_hold;
1252 if (!(mean_sojourn > 0.0) || !std::isfinite(mean_sojourn))
1253 mean_sojourn = (tend - t0) / 10.0;
1254 double tcdf = std::min(tend, 5.0 * mean_sojourn);
1255 if (tcdf <= t0) tcdf = tend;
1256 const std::size_t ndense =
static_cast<std::size_t
>(0.9 * kCdfInterp);
1257 const std::size_t ntail = kCdfInterp - ndense;
1258 const bool with_tail = tcdf < tend && ntail > 1;
1259 std::vector<double> fine;
1260 fine.reserve(with_tail ? ndense + ntail : ndense);
1261 for (std::size_t k = 0; k < ndense; ++k)
1262 fine.push_back(t0 + (tcdf - t0) *
static_cast<double>(k) /
1263 static_cast<double>(ndense - 1));
1265 for (std::size_t k = 1; k <= ntail; ++k)
1266 fine.push_back(tcdf + (tend - tcdf) *
static_cast<double>(k) /
1267 static_cast<double>(ntail));
1286 std::vector<double> stage_grid(std::size_t e)
const {
1287 std::vector<double> ends(2);
1289 ends[1] = opt.timespan_end;
1290 const std::vector<double> g =
1291 refine_grid(hold_disabled(e) ? 0.0 : envObj.hold_time_mean(e), hold_disabled(e), ends);
1294 if (g.size() < 3)
return std::vector<double>();
1311 Matrix<double> stage_exit_cov(std::size_t e)
const {
1312 const std::size_t n = M * K;
1313 Matrix<double> C(n, n, 0.0);
1314 const std::vector<double>& t = tranT[e];
1315 if (t.size() < 2)
return C;
1316 const bool have_c = tranC[e].size() == t.size();
1318 if (!have_c)
return C;
1322 double d = std::max(t.front(), std::min(dvals[e], t.back()));
1323 std::size_t j = t.size() - 1;
1324 for (std::size_t k = 1; k < t.size(); ++k)
1329 const double dt = t[j] - t[j - 1];
1330 const double a = (dt > 0.0) ? (d - t[j - 1]) / dt : 0.0;
1331 for (std::size_t p = 0; p < n; ++p)
1332 for (std::size_t q = 0; q < n; ++q)
1333 C(p, q) = (1.0 - a) * tranC[e][j - 1](p, q) + a * tranC[e][j](p, q);
1336 const std::vector<double> w = cdf_weights(e, t);
1338 for (
double v : w) wsum += v;
1339 if (!(wsum > 0.0))
return C;
1340 std::vector<double> mexit(n, 0.0);
1341 for (std::size_t a = 0; a < n; ++a)
1342 mexit[a] = weighted(tranQ[e][a % M][a / M], w, wsum);
1343 for (std::size_t a = 0; a < n; ++a) {
1344 const std::vector<double>& qa = tranQ[e][a % M][a / M];
1345 for (std::size_t b = 0; b < n; ++b) {
1346 const std::vector<double>& qb = tranQ[e][b % M][b / M];
1348 for (std::size_t k = 0; k < w.size() && k < qa.size() && k < qb.size(); ++k)
1349 acc += w[k] * qa[k] * qb[k];
1351 for (std::size_t k = 0; k < w.size(); ++k) acc += w[k] * tranC[e][k](a, b);
1352 C(a, b) = acc / wsum - mexit[a] * mexit[b];
1355 for (std::size_t a = 0; a < n; ++a)
1356 for (std::size_t b = a + 1; b < n; ++b) {
1357 const double v = 0.5 * (C(a, b) + C(b, a));
1375 Matrix<double> reset_jacobian(
const ResetMarginal& f,
const Matrix<double>& qexit)
const {
1376 const std::size_t n = M * K;
1377 Matrix<double> R(n, n, 0.0);
1379 for (std::size_t a = 0; a < n; ++a) R(a, a) = 1.0;
1382 const Matrix<double> base = f(qexit);
1383 if (base.rows() != M || base.cols() != K)
return R;
1385 for (std::size_t i = 0; i < M; ++i)
1386 for (std::size_t r = 0; r < K; ++r) scale = std::max(scale, std::fabs(qexit(i, r)));
1387 const double step = 1e-6 * scale;
1388 for (std::size_t j = 0; j < n; ++j) {
1389 Matrix<double> qp = qexit;
1390 qp(j % M, j / M) += step;
1391 const Matrix<double> pert = f(qp);
1392 if (pert.rows() != M || pert.cols() != K)
continue;
1393 for (std::size_t a = 0; a < n; ++a)
1394 R(a, j) = (pert(a % M, a / M) - base(a % M, a / M)) / step;
1400 static Matrix<double> congruence(
const Matrix<double>& R,
const Matrix<double>& C) {
1401 const std::size_t n = R.rows();
1402 Matrix<double> RC(n, n, 0.0), out(n, n, 0.0);
1403 for (std::size_t a = 0; a < n; ++a)
1404 for (std::size_t b = 0; b < n; ++b) {
1406 for (std::size_t c = 0; c < n; ++c) acc += R(a, c) * C(c, b);
1409 for (std::size_t a = 0; a < n; ++a)
1410 for (std::size_t b = 0; b < n; ++b) {
1412 for (std::size_t c = 0; c < n; ++c) acc += RC(a, c) * R(b, c);
1422 bool hold_disabled(std::size_t e)
const {
1423 if (envObj.hold_time_is_semi_markov(e))
return false;
1424 const mam::Map<double> m = envObj.hold_time[e].map();
1425 for (std::size_t a = 0; a < m.D1.rows(); ++a)
1426 for (std::size_t b = 0; b < m.D1.cols(); ++b)
1427 if (m.D1(a, b) != 0.0)
return false;
1436 std::vector<double> cdf_weights(std::size_t e,
const std::vector<double>& t)
const {
1437 std::vector<double> w(t.size(), 0.0);
1438 if (t.size() < 2)
return w;
1439 if (hold_disabled(e))
return w;
1440 const std::vector<double> F = envObj.hold_time_cdf(e, t);
1441 for (std::size_t j = 1; j < t.size(); ++j) w[j] = F[j] - F[j - 1];
1455 bool dest_resolved(std::size_t e)
const {
1456 if (!smp || det_sojourn)
return false;
1457 const std::size_t E = envObj.nstages();
1458 std::size_t nsucc = 0;
1459 for (std::size_t h = 0; h < E; ++h)
1460 if (envObj.pemb(e, h) > 0.0) ++nsucc;
1461 if (nsucc < 2)
return false;
1462 if (envObj.is_kernel_stage(e))
return true;
1463 std::vector<lang::Distrib<T>> arcs;
1464 std::vector<double> probs;
1465 envObj.stage_arcs(e, arcs, probs);
1466 for (
const lang::Distrib<T>& a : arcs)
1482 void exit_by_destination(std::size_t e, std::vector<std::vector<Matrix<double>>>& Qexit,
1483 std::vector<std::vector<Matrix<double>>>& Uexit,
1484 std::vector<std::vector<Matrix<double>>>& Texit) {
1485 const std::size_t E = envObj.nstages();
1486 const StageSteady& st = stage_steady(e);
1487 for (std::size_t h = 0; h < E; ++h) {
1488 Qexit[e][h] = st.QN;
1489 if (!statedep)
continue;
1490 Uexit[e][h] = st.UN;
1491 Texit[e][h] = st.TN;
1493 if (tranT[e].empty())
return;
1494 std::vector<lang::Distrib<T>> arcs;
1495 std::vector<double> probs;
1496 envObj.stage_arcs(e, arcs, probs);
1498 const std::size_t nt = tranT[e].size();
1499 for (std::size_t h = 0; h < E; ++h) {
1500 if (!(envObj.pemb(e, h) > 0.0))
continue;
1501 std::vector<double> w(nt, 0.0);
1504 for (std::size_t j = 1; j < nt; ++j) {
1505 w[j] = c.C(h, j) - c.C(h, j - 1);
1506 if (std::isnan(w[j])) nan =
true;
1509 if (nan || !(wsum > 0.0))
continue;
1510 for (std::size_t i = 0; i < M; ++i)
1511 for (std::size_t r = 0; r < K; ++r) {
1512 Qexit[e][h](i, r) = weighted(tranQ[e][i][r], w, wsum);
1513 if (!statedep)
continue;
1514 Uexit[e][h](i, r) = weighted(tranU[e][i][r], w, wsum);
1515 Texit[e][h](i, r) = weighted(tranTp[e][i][r], w, wsum);
1520 static double weighted(
const std::vector<double>& v,
const std::vector<double>& w,
1523 for (std::size_t j = 0; j < v.size() && j < w.size(); ++j) s += v[j] * w[j];
1536 std::vector<double> initsol_from_marginal(
const qn::NetworkStruct<T>& sn,
1537 const Matrix<double>& Q)
const {
1539 std::vector<double> y(L.nstates, 0.0);
1540 for (std::size_t i = 0; i < sn.nstations && i < Q.rows(); ++i)
1541 for (std::size_t r = 0; r < sn.nclasses && r < Q.cols(); ++r) {
1542 if (!L.enabled[i][r])
continue;
1543 y[L.qidx[i][r]] = std::max(0.0, Q(i, r));
1548 Environment<T>& envObj;
1550 std::size_t M = 0, K = 0;
1551 bool det_sojourn =
false;
1552 bool statedep =
false;
1554 bool ctmc_stages =
false;
1555 bool meancov =
false;
1556 bool kp_stages =
false;
1557 bool dae_stages =
false;
1558 std::vector<double> dvals;
1559 std::vector<Matrix<double>> entry;
1567 std::vector<Matrix<double>> centry;
1568 std::vector<std::vector<Matrix<double>>> tranC;
1569 std::vector<std::vector<double>> tranT;
1570 std::vector<std::vector<std::vector<std::vector<double>>>> tranQ, tranU, tranTp;
1576 std::vector<StageSteady> steady;
1577 std::vector<char> steady_asked;
1579 std::vector<std::shared_ptr<ln::SolverLN<T>>> lnsolv;
1581 std::vector<ln::LnLayerBlocks> lnblk;