LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
smp_stage_quad.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2012-2026, QORE Lab, Imperial College London
3 * All rights reserved.
4 */
5#ifndef LINE_API_MC_SMP_STAGE_QUAD_H
6#define LINE_API_MC_SMP_STAGE_QUAD_H
7
8/**
9 * @file
10 * @ingroup api_mc
11 * Embedded jump probabilities and mean holding time of a semi-Markov stage.
12 *
13 * Port of `matlab/src/api/mc/smp_stage_quad.m`. For KERNEL arcs both are in
14 * closed form: P = probs and m = sum_h p_h E[F_h]. For COMPETING arcs they are
15 * P_h = CIF_h(Inf) and m = int_0^Inf (1 - Fhold(t)) dt (see smp_stage_cif.h),
16 * evaluated on [0, U], where U starts at the largest arc mean and doubles
17 * until the joint survival is below 1e-12 (or U reaches 1e8 times that mean).
18 * The grid is 20001 uniform points on [0, U] merged with 4001 points on
19 * [0, min(U, 40 E[F_h])] and the point min(E[F_h], U) per arc, so a fast clock
20 * beside a slow one is still resolved. P is renormalized to sum to one, and
21 * the mean is Simpson's rule with the midpoint survival from a second
22 * smp_stage_cif call.
23 *
24 * `U` is returned as well: the horizon the quadrature covered.
25 */
26
27#include <algorithm>
28#include <cmath>
29#include <cstddef>
30#include <cstdio>
31#include <string>
32#include <vector>
33
37#include "line/util/error.h"
38
39namespace line {
40namespace mc {
41
42/** What `smp_stage_quad` returns. */
44 std::vector<double> P; ///< the embedded jump probabilities, one per arc
45 double m = 0.0; ///< the mean holding time
46 double U = 0.0; ///< the quadrature horizon
47};
48
49template <class T>
51 const std::vector<double>& probs) {
52 const std::size_t E = arcs.size();
53 SmpStageQuad out;
54 out.P.assign(E, 0.0);
55 std::vector<std::size_t> live;
56 for (std::size_t h = 0; h < E; ++h)
57 if (!arcs[h].disabled) live.push_back(h);
58 if (live.empty()) return out;
59 std::vector<double> mu(live.size(), 0.0);
60 for (std::size_t k = 0; k < live.size(); ++k) {
61 mu[k] = num_traits<T>::to_double(arcs[live[k]].mean);
62 if (!(std::isfinite(mu[k]) && mu[k] > 0.0)) {
63 char buf[64];
64 std::snprintf(buf, sizeof buf, "%g", mu[k]);
65 throw InputError(std::string("smp_stage_quad: the ") +
66 lang::process_to_text(arcs[live[k]].type) +
67 " law of the transition to stage " + std::to_string(live[k] + 1) +
68 " has mean " + buf +
69 ": a semi-Markov stage needs every transition law to have a finite "
70 "positive mean.");
71 }
72 }
73 if (!probs.empty()) {
74 for (std::size_t k = 0; k < live.size(); ++k) {
75 out.P[live[k]] = probs[live[k]];
76 out.m += probs[live[k]] * mu[k];
77 }
78 out.U = *std::max_element(mu.begin(), mu.end());
79 return out;
80 }
81 const double mumax = *std::max_element(mu.begin(), mu.end());
82 const auto survival = [&](double t) {
83 double s = 1.0;
84 for (std::size_t h : live)
85 s *= 1.0 - num_traits<T>::to_double(
87 return s;
88 };
89 double U = mumax;
90 while (survival(U) > 1e-12 && U < 1e8 * mumax) U = 2.0 * U;
91 out.U = U;
92 std::vector<double> g = smp_detail::linspace(0.0, U, 20001);
93 for (std::size_t k = 0; k < live.size(); ++k) {
94 const std::vector<double> gk = smp_detail::linspace(0.0, std::min(U, 40.0 * mu[k]), 4001);
95 g.insert(g.end(), gk.begin(), gk.end());
96 g.push_back(std::min(mu[k], U));
97 }
98 std::sort(g.begin(), g.end());
99 g.erase(std::unique(g.begin(), g.end()), g.end());
100 const SmpStageCif c = smp_stage_cif(arcs, std::vector<double>(), g);
101 const std::size_t ng = g.size();
102 double tot = 0.0;
103 for (std::size_t h = 0; h < E; ++h) {
104 out.P[h] = c.C(h, ng - 1);
105 tot += out.P[h];
106 }
107 if (tot > 0.0)
108 for (std::size_t h = 0; h < E; ++h) out.P[h] /= tot;
109 std::vector<double> gm(ng - 1);
110 for (std::size_t j = 0; j + 1 < ng; ++j) gm[j] = (g[j] + g[j + 1]) / 2.0;
111 const SmpStageCif cm = smp_stage_cif(arcs, std::vector<double>(), gm);
112 double acc = 0.0;
113 for (std::size_t j = 0; j + 1 < ng; ++j)
114 acc += ((1.0 - c.Fhold[j]) + 4.0 * (1.0 - cm.Fhold[j]) + (1.0 - c.Fhold[j + 1])) *
115 (g[j + 1] - g[j]);
116 out.m = acc / 6.0;
117 return out;
118}
119
120} // namespace mc
121} // namespace line
122
123#endif // LINE_API_MC_SMP_STAGE_QUAD_H
InputError(const std::string &what)
Definition error.h:105
What refreshProcessRepresentations and refreshLST compute FROM a distribution: the (D0,...
The exception types the port throws.
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
T dist_cdf(const Distrib< T > &d, const T &x)
F(x) = P{X <= x}, MATLAB's Distribution.evalCDF.
const char * process_to_text(ProcessType p)
The MATLAB ProcessType name, as sn.procid prints it.
Definition lang_types.h:649
std::vector< double > linspace(double a, double b, std::size_t n)
linspace(a, b, n), with the last point exactly b.
SmpStageQuad smp_stage_quad(const std::vector< lang::Distrib< T > > &arcs, const std::vector< double > &probs)
SmpStageCif smp_stage_cif(const std::vector< lang::Distrib< T > > &arcs, const std::vector< double > &probs, const std::vector< double > &t)
CIF and holding-time CDF of a stage at the ascending instants t.
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
Cumulative incidence functions of one semi-Markov stage.
What smp_stage_cif returns: C(h, j) = CIF_h(t_j) and Fhold[j].
Matrix< double > C
std::vector< double > Fhold
What smp_stage_quad returns.
double U
the quadrature horizon
std::vector< double > P
the embedded jump probabilities, one per arc
double m
the mean holding time