5#ifndef LINE_LANG_LANG_TYPES_H
6#define LINE_LANG_LANG_TYPES_H
102 default:
return "Unknown Metric";
160 default:
return "INIT";
277 default:
return "none";
325 std::string(
"the LQN XML schema has no spelling for scheduling discipline '") +
326 sched_to_text(s) +
"'; it carries inf, fcfs, ps, ref, hol, pri, rand, sjf, ljf and lcfs");
419 default:
return "disabled";
471enum class LqnElement { HOST = 0, TASK = 1, ENTRY = 2, ACTIVITY = 3, CALL = 4 };
643 default:
return "none";
675 default:
return false;
736using CdScaling = std::function<std::vector<T>(
const std::vector<T>&)>;
749using GdScaling = std::function<std::vector<T>(
const std::vector<T>&)>;
767 static constexpr double Zero = 1e-14;
776 static constexpr double MaxInt = 2147483647.0;
887 std::shared_ptr<PriorSpec<T>>
prior;
950 d.
params.push_back(lambda);
1015 if (r == 0)
throw InputError(
"Erlang: the number of phases must be positive");
1016 if (!(phase_rate > zero))
throw InputError(
"Erlang: the phase rate must be positive");
1020 d.
params.push_back(phase_rate);
1026 for (std::size_t i = 0; i < r; ++i) {
1027 d.
D0(i, i) = T(-phase_rate);
1028 if (i + 1 < r) d.
D0(i, i + 1) = phase_rate;
1030 d.
D1(r - 1, 0) = phase_rate;
1046 if (!(c2 > zero))
throw InputError(
"Erlang.fitMeanAndSCV: the SCV must be positive");
1049 "Erlang.fitMeanAndSCV: the Erlang distribution requires a squared coefficient "
1050 "of variation <= 1; use HyperExp, Coxian or APH above 1");
1053 const std::size_t r =
static_cast<std::size_t
>(r_d < 1.0 ? 1.0 : r_d);
1066 const std::size_t n = p.size();
1067 if (n == 0 || lambda.size() != n)
1068 throw InputError(
"HyperExp: p and lambda must be non-empty and of equal length");
1072 for (
const T& v : p) d.
params.push_back(v);
1073 for (
const T& v : lambda) d.
params.push_back(v);
1076 T m1 = zero, m2 = zero;
1077 for (std::size_t i = 0; i < n; ++i) {
1078 if (!(lambda[i] > zero))
throw InputError(
"HyperExp: the phase rates must be positive");
1079 d.
D0(i, i) = T(-lambda[i]);
1080 for (std::size_t j = 0; j < n; ++j) d.
D1(i, j) = T(lambda[i] * p[j]);
1081 m1 += T(p[i] / lambda[i]);
1082 m2 += T(two * p[i] / (lambda[i] * lambda[i]));
1085 d.
scv = T((m2 - m1 * m1) / (m1 * m1));
1098 if (!(lambda1 > zero) || !(lambda2 > zero))
1099 throw InputError(
"HyperExp: the phase rates must be positive");
1100 if (p < zero || p > one)
throw InputError(
"HyperExp: p is not a probability");
1105 d.
params.push_back(lambda1);
1106 d.
params.push_back(lambda2);
1107 const T q = T(one - p);
1108 d.
mean = T(p / lambda1 + q / lambda2);
1110 (p / (lambda1 * lambda1) + q / (lambda2 * lambda2)));
1114 d.
D0(0, 0) = T(-lambda1);
1115 d.
D0(1, 1) = T(-lambda2);
1116 d.
D1(0, 0) = T(lambda1 * p);
1117 d.
D1(0, 1) = T(lambda1 * q);
1118 d.
D1(1, 0) = T(lambda2 * p);
1119 d.
D1(1, 1) = T(lambda2 * q);
1129 const std::size_t n = mu.size();
1130 if (n == 0 || phi.size() != n)
1131 throw InputError(
"Coxian: mu and phi must be non-empty and of equal length");
1135 for (
const T& v : mu) d.
params.push_back(v);
1136 for (
const T& v : phi) d.
params.push_back(v);
1139 for (std::size_t i = 0; i < n; ++i) {
1140 if (!(mu[i] > zero))
throw InputError(
"Coxian: the phase rates must be positive");
1141 d.
D0(i, i) = T(-mu[i]);
1142 if (i + 1 < n) d.
D0(i, i + 1) = T(mu[i] * (one - phi[i]));
1143 d.
D1(i, 0) = T(mu[i] * phi[i]);
1154 std::vector<T> mu, phi;
1157 phi.push_back(phi1);
1172 const std::size_t n = alpha.size();
1173 if (n == 0 || A.
rows() != n || A.
cols() != n)
1174 throw InputError(
"PH: alpha and the subgenerator have inconsistent sizes");
1178 for (
const T& v : alpha) d.
params.push_back(v);
1181 for (std::size_t i = 0; i < n; ++i) {
1183 for (std::size_t j = 0; j < n; ++j) out += A(i, j);
1184 for (std::size_t j = 0; j < n; ++j) d.
D1(i, j) = T(-out * alpha[j]);
1195 throw InputError(
"MAP: D0 and D1 must be square and of the same order");
1199 const std::size_t n =
D0.
rows();
1202 for (std::size_t i = 0; i < n; ++i) {
1203 for (std::size_t j = 0; j < n; ++j) s[i] -=
D0(i, j);
1207 throw InputError(
"PH: the subgenerator D0 has no exit rate");
1208 for (std::size_t j = 0; j < n; ++j) {
1209 for (std::size_t i = 0; i < n; ++i) alpha[j] +=
D1(i, j);
1210 alpha[j] = T(alpha[j] / stot);
1213 for (std::size_t i = 0; i < n; ++i)
1214 for (std::size_t j = 0; j < n; ++j)
1216 for (std::size_t i = 0; i < n; ++i)
1217 for (std::size_t j = 0; j < n; ++j)
1219 1e-10 * std::max(1.0, scale))
1220 throw InputError(
"PH: D1 must equal the exit vector -D0 e times the entry "
1221 "vector; this (D0, D1) is not a renewal phase-type");
1263 const std::vector<std::vector<
Matrix<T>>>& blocks) {
1265 for (std::size_t k = 0; k < D0segs.size(); ++k) {
1267 for (std::size_t i = 0; i < A.
rows(); ++i) {
1269 for (std::size_t j = 0; j < A.
cols(); ++j) {
1271 throw InputError(who +
": off-diagonal D0 entries must be non-negative; "
1272 "segment " + std::to_string(k + 1) +
" entry (" +
1273 std::to_string(i) +
"," + std::to_string(j) +
") is not");
1276 for (
const std::vector<
Matrix<T>>& blk : blocks) {
1277 for (std::size_t j = 0; j < blk[k].
cols(); ++j) {
1279 throw InputError(who +
": every arrival block must be non-negative; "
1280 "segment " + std::to_string(k + 1) +
" entry (" +
1281 std::to_string(i) +
"," + std::to_string(j) +
1283 rowsum += blk[k](i, j);
1287 throw InputError(who +
": D0 plus every arrival block must have zero row sums "
1288 "(generator); segment " + std::to_string(k + 1) +
" row " +
1289 std::to_string(i) +
" sums to " +
1303 const std::vector<
Matrix<T>>& mats,
bool ignore_diagonal) {
1304 if (mats.empty())
return;
1305 for (std::size_t k = 1; k < mats.size(); ++k) {
1306 for (std::size_t i = 0; i < mats[0].rows(); ++i)
1307 for (std::size_t j = 0; j < mats[0].cols(); ++j) {
1308 if (ignore_diagonal && i == j)
continue;
1312 throw InputError(who +
": the " + label +
" sparsity pattern must be "
1313 "identical across segments; segment " +
1314 std::to_string(k + 1) +
" differs from segment 1 at (" +
1315 std::to_string(i) +
"," + std::to_string(j) +
"). A "
1316 "schedule that switches a transition on or off cannot be "
1317 "expressed as a per-entry multiplier on the time-averaged "
1318 "process. Keep the entry present with a small positive "
1330 const std::size_t n = D0segs.size();
1331 if (n == 0 || D1segs.size() != n)
1332 throw InputError(who +
": the two segment lists must be non-empty and equally long");
1333 if (breakpoints.size() != n + 1)
1335 ": breakpoints is the BOUNDARY vector and must hold one more entry "
1336 "than there are segments");
1337 for (std::size_t k = 0; k + 1 < breakpoints.size(); ++k)
1338 if (!(breakpoints[k + 1] > breakpoints[k]))
1339 throw InputError(who +
": breakpoints must be strictly increasing");
1340 const std::size_t order = D0segs[0].rows();
1341 for (std::size_t k = 0; k < n; ++k)
1342 if (D0segs[k].rows() != order || D0segs[k].cols() != order ||
1343 D1segs[k].rows() != order || D1segs[k].cols() != order)
1345 ": every segment must have the same order; the schedule "
1346 "modulates one phase structure and does not switch between them");
1351 std::vector<std::vector<Matrix<T>>> one;
1352 one.push_back(D1segs);
1366 for (std::size_t k = 0; k < n; ++k) total += T(breakpoints[k + 1] - breakpoints[k]);
1369 for (std::size_t k = 0; k < n; ++k) {
1370 const T w = T(T(breakpoints[k + 1] - breakpoints[k]) / total);
1371 for (std::size_t a = 0; a < order; ++a)
1372 for (std::size_t b = 0; b < order; ++b) {
1373 d.
D0(a, b) += w * D0segs[k](a, b);
1374 d.
D1(a, b) += w * D1segs[k](a, b);
1402 const std::vector<
Matrix<T>>& D1segs,
bool cyclic) {
1413 static Distrib pht(
const std::vector<T>& breakpoints,
const std::vector<std::vector<T>>& alphas,
1414 const std::vector<
Matrix<T>>& Ssegs,
bool cyclic) {
1416 if (alphas.size() != Ssegs.size() || alphas.empty())
1417 throw InputError(
"PHt: alpha and S must have the same, non-zero number of segments");
1418 std::vector<Matrix<T>> D0segs, D1segs;
1419 for (std::size_t k = 0; k < Ssegs.size(); ++k) {
1421 const std::vector<T>& a = alphas[k];
1423 throw InputError(
"PHt: alpha and the sub-generator disagree in order");
1425 for (std::size_t i = 0; i < S.
rows(); ++i) {
1427 for (std::size_t j = 0; j < S.
cols(); ++j) s += S(i, j);
1428 for (std::size_t j = 0; j < S.
cols(); ++j)
D1(i, j) = T(-s * a[j]);
1430 D0segs.push_back(S);
1431 D1segs.push_back(
D1);
1446 const std::vector<std::vector<
Matrix<T>>>& Dmarksegs,
bool cyclic) {
1448 if (Dmarksegs.empty())
throw InputError(
"MMAPt: no marked arrival block was given");
1449 const std::size_t n = D0segs.size();
1450 for (
const std::vector<
Matrix<T>>& blk : Dmarksegs)
1451 if (blk.
size() != n)
1452 throw InputError(
"MMAPt: every mark must be defined on the whole schedule; one "
1453 "carries " + std::to_string(blk.
size()) +
" segments against " +
1454 std::to_string(n) +
" in D0");
1455 std::vector<Matrix<T>> D1segs;
1456 for (std::size_t k = 0; k < n; ++k) {
1457 Matrix<T> agg(D0segs[k].rows(), D0segs[k].cols(), zero);
1458 for (
const std::vector<
Matrix<T>>& blk : Dmarksegs) {
1459 if (blk[k].rows() != D0segs[k].rows() || blk[k].cols() != D0segs[k].cols())
1460 throw InputError(
"MMAPt: a mark block and D0 disagree in order in segment " +
1461 std::to_string(k + 1));
1462 for (std::size_t a = 0; a < agg.
rows(); ++a)
1463 for (std::size_t b = 0; b < agg.
cols(); ++b) agg(a, b) += blk[k](a, b);
1465 D1segs.push_back(agg);
1468 for (std::size_t c = 0; c < Dmarksegs.size(); ++c)
1476 for (std::size_t k = 0; k < n; ++k) total += T(breakpoints[k + 1] - breakpoints[k]);
1477 const std::size_t order = D0segs[0].rows();
1478 for (
const std::vector<
Matrix<T>>& blk : Dmarksegs) {
1480 for (std::size_t k = 0; k < n; ++k) {
1481 const T w = T(T(breakpoints[k + 1] - breakpoints[k]) / total);
1482 for (std::size_t a = 0; a < order; ++a)
1483 for (std::size_t b = 0; b < order; ++b) nom(a, b) += w * blk[k](a, b);
1485 d.
Dmark.push_back(nom);
1510 const std::vector<std::vector<std::vector<
Matrix<T>>>>& Dbatchsegs,
1513 if (Dbatchsegs.empty())
throw InputError(
"BMMAPt: no marked batch block was given");
1514 const std::size_t n = D0segs.size();
1515 if (n == 0)
throw InputError(
"BMMAPt: D0 must carry at least one segment");
1516 const std::size_t B = Dbatchsegs[0].size();
1517 if (B == 0)
throw InputError(
"BMMAPt: mark 1 declares no batch size");
1518 for (std::size_t c = 0; c < Dbatchsegs.size(); ++c) {
1519 if (Dbatchsegs[c].size() != B)
1520 throw InputError(
"BMMAPt: mark " + std::to_string(c + 1) +
" declares " +
1521 std::to_string(Dbatchsegs[c].size()) +
" batch sizes against " +
1522 std::to_string(B) +
" in mark 1; the batch axis is dense and "
1523 "every mark must span it (use a zero block for an unused size)");
1524 for (std::size_t b = 0; b < B; ++b)
1525 if (Dbatchsegs[c][b].size() != n)
1526 throw InputError(
"BMMAPt: block (mark " + std::to_string(c + 1) +
", batch " +
1527 std::to_string(b + 1) +
") carries " +
1528 std::to_string(Dbatchsegs[c][b].size()) +
1529 " segments against " + std::to_string(n) +
" in D0; every "
1530 "block must be defined on the whole schedule");
1533 std::vector<std::vector<Matrix<T>>> Dmarksegs;
1534 for (std::size_t c = 0; c < Dbatchsegs.size(); ++c) {
1535 std::vector<Matrix<T>> permark;
1536 for (std::size_t k = 0; k < n; ++k) {
1537 Matrix<T> acc(D0segs[k].rows(), D0segs[k].cols(), zero);
1538 for (std::size_t b = 0; b < B; ++b) {
1539 const Matrix<T>& M = Dbatchsegs[c][b][k];
1540 if (M.
rows() != D0segs[k].rows() || M.
cols() != D0segs[k].cols())
1541 throw InputError(
"BMMAPt: block (mark " + std::to_string(c + 1) +
1542 ", batch " + std::to_string(b + 1) +
") and D0 disagree "
1543 "in order in segment " + std::to_string(k + 1));
1544 for (std::size_t a = 0; a < acc.
rows(); ++a)
1545 for (std::size_t d = 0; d < acc.
cols(); ++d) acc(a, d) += M(a, d);
1547 permark.push_back(acc);
1549 Dmarksegs.push_back(permark);
1554 Distrib d =
mmapt(breakpoints, D0segs, Dmarksegs, cyclic);
1569 const std::vector<std::vector<T>>& alphas,
1571 const std::vector<std::vector<std::vector<T>>>& exits,
bool cyclic) {
1573 if (alphas.size() != Ssegs.size() || alphas.empty())
1574 throw InputError(
"MPHt: alpha and S must have the same, non-zero number of segments");
1575 if (exits.empty())
throw InputError(
"MPHt: no marked exit vector was given");
1576 const std::size_t n = Ssegs.size();
1577 for (
const std::vector<std::vector<T>>& ex : exits)
1579 throw InputError(
"MPHt: every mark must be defined on the whole schedule");
1580 std::vector<std::vector<Matrix<T>>> Dmarksegs;
1581 for (
const std::vector<std::vector<T>>& ex : exits) {
1582 std::vector<Matrix<T>> blocks;
1583 for (std::size_t k = 0; k < n; ++k) {
1585 const std::vector<T>& a = alphas[k];
1586 if (S.
rows() != S.
cols() || S.
rows() != a.size() || ex[k].size() != S.
rows())
1587 throw InputError(
"MPHt: alpha, S and the exit vector disagree in order in "
1588 "segment " + std::to_string(k + 1));
1590 for (std::size_t i = 0; i < S.
rows(); ++i)
1591 for (std::size_t j = 0; j < S.
cols(); ++j) block(i, j) = T(ex[k][i] * a[j]);
1592 blocks.push_back(block);
1594 Dmarksegs.push_back(blocks);
1597 for (std::size_t k = 0; k < n; ++k) {
1599 for (std::size_t i = 0; i < S.
rows(); ++i) {
1600 T rowsum = zero, marked = zero;
1601 for (std::size_t j = 0; j < S.
cols(); ++j) rowsum += S(i, j);
1602 for (
const std::vector<std::vector<T>>& ex : exits) marked += ex[k][i];
1604 if (std::fabs(slack) > 1e-10)
1605 throw InputError(
"MPHt: the exit vectors must partition the absorption rate "
1606 "of S; in segment " + std::to_string(k + 1) +
" phase " +
1607 std::to_string(i) +
" they miss it by " +
1608 std::to_string(slack));
1611 Distrib d =
mmapt(breakpoints, Ssegs, Dmarksegs, cyclic);
1621 static Distrib nhpp(
const std::vector<T>& breakpoints,
const std::vector<T>& rates,
1623 std::vector<Matrix<T>> D0segs, D1segs;
1624 for (std::size_t k = 0; k < rates.size(); ++k) {
1625 Matrix<T> a(1, 1, T(-rates[k])), b(1, 1, rates[k]);
1626 D0segs.push_back(a);
1627 D1segs.push_back(b);
1638 if (!(b > a))
throw InputError(
"Uniform: the upper bound must exceed the lower bound");
1645 const T w = T(b - a);
1654 throw InputError(
"Pareto: the shape must exceed 2 for a finite variance");
1658 d.
params.push_back(shape);
1659 d.
params.push_back(scale);
1660 d.
mean = T(shape * scale / (shape - one));
1661 const T var = T(scale * scale * shape / ((shape - one) * (shape - one)) / (shape - two));
1680 d.
params.push_back(shape);
1681 d.
params.push_back(scale);
1682 d.
mean = T(shape * scale);
1683 d.
scv = T(one / shape);
1690 "Weibull: its moments are values of the gamma function, which exact arithmetic "
1691 "has no representation for; use the double or real backend");
1695 if (!(a > 0.0) || !(r > 0.0))
1696 throw InputError(
"Weibull: the scale and the shape must be positive");
1697 const double g1 = std::tgamma(1.0 + 1.0 / r);
1698 const double g2 = std::tgamma(1.0 + 2.0 / r);
1702 d.
params.push_back(scale);
1703 d.
params.push_back(shape);
1713 "Lognormal: its moments are values of exp, which exact arithmetic has no "
1714 "representation for; use the double or real backend");
1718 if (!(sg > 0.0))
throw InputError(
"Lognormal: sigma must be positive");
1722 d.
params.push_back(logmean);
1723 d.
params.push_back(logsigma);
1740 throw InputError(
"Normal: sigma must be positive");
1745 d.
params.push_back(sigma);
1770 if (samples.empty())
throw InputError(
"Replayer: the trace is empty");
1777 for (
const T& x : samples) {
1799 if (!(b >= a))
throw InputError(
"DiscreteUniform: the upper bound must not be below the lower");
1805 d.
mean = T((a + b) / two);
1806 const T w = T(b - a + one);
1820 d.
scv = T((one - p) / p);
1833 d.
scv = T((one - p) / (n * p));
1846 d.
params.push_back(lambda);
1848 d.
scv = T(one / lambda);
1862 d.
mean = T(one / p);
1879 "Zipf: its moments are generalized harmonic sums of a real exponent, which exact "
1880 "arithmetic has no representation for; use the double or real backend");
1882 if (n == 0)
throw InputError(
"Zipf: the item count must be positive");
1884 auto harmonic = [n](
double e) {
1886 for (std::size_t k = 1; k <= n; ++k) acc += std::pow(
double(k), -e);
1889 const double h0 = harmonic(sv), h1 = harmonic(sv - 1.0), h2 = harmonic(sv - 2.0);
1895 const double m1 = h1 / h0;
1913 if (p.empty())
throw InputError(
"DiscreteSampler: the probability vector is empty");
1914 if (!x.empty() && x.size() != p.size())
1915 throw InputError(
"DiscreteSampler: p and x must have the same length");
1923 for (std::size_t k = 0; k < p.size(); ++k) {
1927 m2 += T(p[k] * pt * pt);
1933 d.
scv = T((m2 - m1 * m1) / (m1 * m1));
1944 if (x.size() != F.size() || x.size() < 2)
1945 throw InputError(
"EmpiricalCDF: x and F must be equally long and hold at least two points");
1953 T m1 = T(F[0] * x[0]), m2 = T(F[0] * x[0] * x[0]);
1954 for (std::size_t i = 0; i + 1 < x.size(); ++i) {
1955 const T mid = T((x[i + 1] - x[i]) / two + x[i]);
1956 const T w = T(F[i + 1] - F[i]);
1958 m2 += T(mid * mid * w);
2003 if (D1k.empty())
throw InputError(
"MMAP: no marked arrival block was given");
2007 throw InputError(
"MMAP: every marked block must have the order of D0");
2008 for (std::size_t i = 0; i < agg.
rows(); ++i)
2009 for (std::size_t j = 0; j < agg.
cols(); ++j) agg(i, j) += Dk(i, j);
2025 const std::vector<std::vector<T>>& exits) {
2026 const double tol = 1e-10;
2028 const std::size_t h = alpha.size();
2029 if (h == 0 || exits.empty())
2030 throw InputError(
"MPH: alpha, S and a non-empty list of exit vectors are all required");
2031 if (S.
rows() != h || S.
cols() != h)
2032 throw InputError(
"MPH: S must be square of order " + std::to_string(h) +
" to match alpha");
2034 for (
const T& a : alpha) {
2038 if (std::fabs(asum - 1.0) > tol)
2039 throw InputError(
"MPH: alpha must sum to one; it sums to " + std::to_string(asum) +
2040 ". A defective entry law would put mass on an instantaneous "
2041 "completion that carries no mark.");
2042 for (std::size_t i = 0; i < h; ++i)
2043 for (std::size_t j = 0; j < h; ++j) {
2045 if (i == j && v >= 0.0)
throw InputError(
"MPH: the diagonal of S must be negative");
2046 if (i != j && v < 0.0)
2047 throw InputError(
"MPH: off-diagonal entries of S must be non-negative");
2049 std::vector<double> marked(h, 0.0);
2050 for (std::size_t k = 0; k < exits.size(); ++k) {
2051 if (exits[k].size() != h)
2052 throw InputError(
"MPH: exit vector " + std::to_string(k + 1) +
" must have " +
2053 std::to_string(h) +
" entries");
2054 for (std::size_t i = 0; i < h; ++i) {
2057 throw InputError(
"MPH: exit vector " + std::to_string(k + 1) +
" must be non-negative");
2061 bool any_exit =
false;
2062 for (std::size_t i = 0; i < h; ++i) {
2063 double absorb = 0.0;
2065 if (std::fabs(marked[i] - absorb) > tol)
2066 throw InputError(
"MPH: the exit vectors must partition the absorption rate of S. "
2067 "Every way of leaving the transient phases must carry exactly one mark.");
2068 if (marked[i] != 0.0) any_exit =
true;
2070 if (!any_exit)
throw InputError(
"MPH: every exit vector is zero, so absorption can never occur");
2071 std::vector<Matrix<T>> blocks;
2072 for (
const std::vector<T>& sk : exits) {
2074 for (std::size_t i = 0; i < h; ++i)
2075 for (std::size_t j = 0; j < h; ++j) block(i, j) = T(sk[i] * alpha[j]);
2076 blocks.push_back(block);
2089 if (D.size() < 2)
throw InputError(
"BMAP: the block list must carry D0 and at least one batch block");
2090 const std::vector<Matrix<T>> batches(D.begin() + 1, D.end());
2111 v.push_back(
rate());
2114 for (std::size_t i = 0; i <
D0.
rows(); ++i) v.push_back(T(-
D0(i, i)));
2127 for (std::size_t i = 0; i <
D1.
rows(); ++i) {
2129 for (std::size_t j = 0; j <
D1.
cols(); ++j) s +=
D1(i, j);
2130 const T out = T(-
D0(i, i));
2150 const std::size_t n = alpha.size();
2152 for (
unsigned i = 0; i < k; ++i) x = solve_neg(A, x);
2154 for (std::size_t i = 0; i < n; ++i) acc += alpha[i] * x[i];
2156 for (
unsigned i = 2; i <= k; ++i) fact *= num_traits<T>::from_int(
static_cast<long>(i));
2157 return T(fact * acc);
2169 static std::vector<T> solve_neg(
const Matrix<T>& A,
const std::vector<T>& b) {
2171 const std::size_t n = A.
rows();
2172 if (A.
cols() != n || b.size() != n)
2173 throw InputError(
"phase-type moment: the subgenerator is not square");
2175 std::vector<T> x = b;
2176 for (std::size_t i = 0; i < n; ++i)
2177 for (std::size_t j = 0; j < n; ++j) M(i, j) = T(-A(i, j));
2178 for (std::size_t col = 0; col < n; ++col) {
2179 std::size_t best = col;
2181 for (std::size_t r = col + 1; r < n; ++r) {
2189 for (std::size_t j = 0; j < n; ++j) std::swap(M(col, j), M(best, j));
2190 std::swap(x[col], x[best]);
2192 if (M(col, col) == zero)
2193 throw NumericError(ErrorKind{
"Singular"},
"phase-type moment: the subgenerator is singular");
2194 for (std::size_t r = 0; r < n; ++r) {
2195 if (r == col)
continue;
2196 const T f = T(M(r, col) / M(col, col));
2197 if (f == zero)
continue;
2198 for (std::size_t j = 0; j < n; ++j) M(r, j) = T(M(r, j) - f * M(col, j));
2199 x[r] = T(x[r] - f * x[col]);
2202 for (std::size_t i = 0; i < n; ++i) x[i] = T(x[i] / M(i, i));
2283 const std::size_t n = d.
D0.rows();
2286 for (std::size_t i = 0; i < n; ++i)
2287 for (std::size_t j = 0; j < n; ++j) ImD0(i, j) = T((i == j ? one : T(0)) - d.
D0(i, j));
2291 const std::vector<std::size_t> piv =
lu_factor(LU);
2293 for (std::size_t j = 0; j < n; ++j) {
2294 std::vector<T> col(n);
2295 for (std::size_t i = 0; i < n; ++i) col[i] = d.
D1(i, j);
2297 for (std::size_t i = 0; i < n; ++i) P(i, j) = col[i];
2300 std::vector<T> y(n, one);
2302 std::vector<T> z = y;
2305 for (std::size_t i = 0; i < n; ++i) {
2306 m1 += T(alpha[i] * y[i]);
2308 for (std::size_t j = 0; j < n; ++j) dz += T(d.
D0(i, j) * z[j]);
2309 fac2 += T(alpha[i] * dz);
2311 fac2 = T(two * fac2);
2312 const T m2 = T(fac2 + m1);
2314 d.
scv = T((m2 - m1 * m1) / (m1 * m1));
Cache(model, name, params).
ClassSwitch(model, name, C).
Delay(model, name): the infinite-server station.
NumericError(const std::string &what)
Place(model, name): a Petri-net place.
Queue(model, name, strategy).
Router(model, name): a stateless routing node.
Sink(model, name): the external departure node, which holds no jobs.
Source(model, name): the external arrival station.
Transition(model, name): a Petri-net transition, with its modes declared after it as MATLAB,...
UnsupportedError(const std::string &what)
Equilibrium distribution of a discrete-time Markov chain, and stochastic complementation.
The exception types the port throws.
LU factorization with partial pivoting, templated on the number type.
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
Dense matrix and non-owning view.
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
DropStrategy
Blocking and loss rules, with the values of MATLAB DropStrategy.
TimingStrategy
SPN transition timing, with the values of MATLAB TimingStrategy.
@ IMMEDIATE
fires with zero delay, resolved by weight and priority
@ TIMED
fires after its firing distribution elapses
BalkingStrategy
Balking rules, with the values of MATLAB BalkingStrategy.
SignalType
G-network signal classes, with the values of MATLAB SignalType.
@ REPLY
completes a synchronous call, releasing a held server
@ NEGATIVE
removes a batch of jobs (Gelenbe's negative customer)
@ CATASTROPHE
removes EVERY job at the station
bool process_is_batch(ProcessType p)
True when an EVENT of this process releases (or, as a service process, completes) a BATCH of jobs who...
JoinStrategy
Join rules, with the values of MATLAB JoinStrategy.
PrecedenceType
Activity precedence kinds, with the values of MATLAB ActivityPrecedenceType.
LqnElement
LQN element kinds, with the values of MATLAB LayeredNetworkElement.
RoutingStrategy
Routing strategies, with the values of MATLAB RoutingStrategy.
@ SDR
Krzesinski (1987) product-form state-dependent routing.
SchedStrategy sched_from_lqnx(const std::string &s)
Parse the scheduling attribute of an .lqnx processor or task.
DepartureDiscipline
When a Place releases a served token, MATLAB DepartureDiscipline.
bool process_is_marked(ProcessType p)
True for every marked family.
CallType
Call kinds, with the values of MATLAB CallType.
const char * metric_to_text(MetricType metric)
Port of MetricType.toText.
RemovalPolicy
Which job a negative signal removes, with the values of MATLAB RemovalPolicy.
@ LCFS
the newest waiting job; servers only once nobody waits
@ RANDOM
uniform over waiting AND in-service jobs
EventType
The events a state can undergo, with the values of MATLAB EventType.
@ PHASE
service advances a phase WITHOUT departing
@ READ
a cache item is read
@ FAILURE
the server breaks down, going from up to down
@ STAGE
a random environment changes stage
@ SWITCH
a polling server advances its switchover timer
@ LOCAL
dummy event, no state change outside the node
@ RENEGE
a waiting job abandons the queue (impatience)
@ POST
produce to a place or queue buffer
@ START
a job begins or resumes holding a server
@ ENABLE
an SPN mode becomes enabled
@ RETRY
an orbiting job retries entry at a retrial station
@ REPAIR
the server is repaired, going from down to up, resuming the held job, which is why it emits no START
@ PRE
consume from a place or queue buffer, no server effect
@ INIT
the model is initialized, t = 0
@ PREEMPT
a job holding a server is pushed back into the buffer
MetricType
Solver output metrics, with the numeric values of MATLAB MetricType.
HeteroSchedPolicy
How a heterogeneous station picks among its server types, MATLAB HeteroSchedPolicy.
PollingType
Polling service disciplines, with the values of MATLAB PollingType.
@ KLIMITED
serve at most K per visit (K in pollingPar)
@ EXHAUSTIVE
serve until the queue empties
@ GATED
serve exactly the jobs present at the polling instant
@ DECREMENTING
serve until the queue is one shorter than at arrival
JobClassType
Job class kinds, with the values of MATLAB JobClassType.
std::string sched_to_lqnx(SchedStrategy s)
The scheduling attribute an .lqnx processor or task carries for a strategy.
bool process_is_marked_schedule(ProcessType p)
True when sn.proc holds the MARKED SCHEDULE slot.
const char * node_type_to_text(NodeType t)
Name of a node kind, for diagnostics.
ProcessType
Distribution kinds, with the values of MATLAB ProcessType.
@ NORMAL
A Gaussian, and the ONE family whose value is not MATLAB's, because MATLAB has none to copy: ProcessT...
@ BMMAPT
BMMAPT crosses the BATCH axis with the two above: a block is indexed by segment, mark and batch size,...
@ MPH
The MARKED families, MATLAB's ProcessType.m:36-38.
@ NHPP
The time-INHOMOGENEOUS families of Ko and Pender (ORL 45, 2017): an NHPP is a rate schedule lambda(t)...
@ PRIOR
A Prior: a weighted set of ALTERNATIVE distributions, or a density over a scalar parameter plus a fac...
void dmap_refresh_moments(Distrib< T > &d)
The first two moments of a DISCRETE-time MAP, from its own law.
std::function< std::vector< T >(const std::vector< T > &)> GdScaling
A globally state-dependent scaling, sn.gdscaling.
const char * sched_to_text(SchedStrategy s)
const char * process_to_text(ProcessType p)
The MATLAB ProcessType name, as sn.procid prints it.
bool process_is_markovian(ProcessType p)
ProcessType.isMarkovian: true when sn.proc carries an exact matrix representation of the law,...
bool process_is_marked_stationary(ProcessType p)
True when the type carries PER-MARK arrival blocks, i.e.
std::function< std::vector< T >(const std::vector< T > &)> CdScaling
A class-dependent scaling map, sn.cdscaling.
const char * routing_to_text(RoutingStrategy r)
NodeType
Node kinds, with the values of MATLAB NodeType.
const char * event_to_text(EventType e)
ReplacementStrategy
Cache replacement policies, with the values of MATLAB ReplacementStrategy.
@ HLRU
h-LRU / LRU(m): h lists, promote i -> i+1 on a hit
@ CLIMB
move up one position on a hit (transposition rule)
@ QLRU
q-LRU: LRU with probabilistic admission on a miss
@ FIFO
first in, first out
ImpatienceType
Impatience kinds, with the values of MATLAB ImpatienceType.
T map_mean(const Map< T > &m)
Mean inter-arrival time, 1/lambda.
T map_scv(const Map< T > &m)
Squared coefficient of variation.
T map_lambda(const Map< T > &m)
Stationary arrival rate, lambda = pi D1 e.
std::vector< T > dtmc_solve(const Matrix< T > &P)
Stationary distribution of a stochastic matrix P.
Conservation laws of a layered queueing network, enumerated from its structure.
void lu_solve(const Matrix< T > &LU, const std::vector< std::size_t > &piv, std::vector< T > &b)
Solve LUx = Pb in place on b, using the factors from lu_factor.
std::vector< std::size_t > lu_factor(Matrix< T > &A)
In-place LU of A (n x n).
Number-type abstraction for the templated API port.
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
bool is_immediate() const
static Distrib sched_dist(const std::vector< T > &breakpoints, const std::vector< Matrix< T > > &D0segs, const std::vector< Matrix< T > > &D1segs, bool cyclic, ProcessType tag)
The shared constructor of the three schedule families.
static Distrib empirical_cdf(const std::vector< T > &x, const std::vector< T > &F)
EmpiricalCDF(x, F): the moments of the MIDPOINT rule over the CDF bins, which is what MATLAB Empirica...
std::vector< std::vector< std::vector< Matrix< double > > > > sched_Dbatch
static Distrib nhpp(const std::vector< T > &breakpoints, const std::vector< T > &rates, bool cyclic)
NHPP(breakpoints, rates, cyclic): a MAPt of ORDER ONE, which is what an inhomogeneous Poisson process...
std::vector< T > mu_vec() const
Phase rates, MATLAB's getMu: the total outgoing rate of each phase.
static void check_common_support(const std::string &who, const std::string &label, const std::vector< Matrix< T > > &mats, bool ignore_diagonal)
Reject a schedule whose matrices do not share one sparsity pattern, the twin of MATLAB's MAPt....
static Distrib mph(const std::vector< T > &alpha, const Matrix< T > &S, const std::vector< std::vector< T > > &exits)
MPH(alpha, S, {s_k}): the marked phase-type law, a PH with K labelled exits.
bool has_map() const
True when the type carries a (D0,D1) pair of its own.
static Distrib mmapt(const std::vector< T > &breakpoints, const std::vector< Matrix< T > > &D0segs, const std::vector< std::vector< Matrix< T > > > &Dmarksegs, bool cyclic)
MMAPt(breakpoints, {D0_k}, {{D1^(c)_k}}, cyclic): the marked schedule.
std::vector< std::vector< Matrix< double > > > sched_Dmark
static Distrib replayer(const std::vector< double > &samples)
std::vector< double > sched_bp
static Distrib normal(const T &mu, const T &sigma)
Normal(mu, sigma): the Gaussian, for use as a continuous Prior's parameter density.
static Distrib dmap(const Matrix< T > &D0, const Matrix< T > &D1)
DMAP(D0, D1): a DISCRETE-time MAP, where D0 + D1 is stochastic rather than a generator.
static Distrib exp_rate(const T &r)
static Distrib phase_type(const std::vector< T > &alpha, const Matrix< T > &A, bool acyclic)
PH / APH given by (alpha, A): D0 = A and D1 = (-A e) alpha.
std::vector< Matrix< double > > Dmark
static Distrib mapt(const std::vector< T > &breakpoints, const std::vector< Matrix< T > > &D0segs, const std::vector< Matrix< T > > &D1segs, bool cyclic)
MAPt(breakpoints, {D0_k}, {D1_k}, cyclic): a piecewise-constant (D0(t), D1(t)).
std::shared_ptr< Distrib< double > > declared
static Distrib weibull(const T &scale, const T &shape)
static Distrib replayer_from(const std::vector< T > &samples, const std::string &path)
Replayer / Trace read FROM A FILE, which keeps the path beside the samples.
bool has_schedule() const
static Distrib bmap(const std::vector< Matrix< T > > &D)
BMAP: the batch-size blocks D0, D1, ..., Dk, where Dj carries an arrival of batch size j.
static Distrib mmap(const Matrix< T > &D0, const std::vector< Matrix< T > > &D1k)
MMAP: D0 plus one D1 block per mark.
static Distrib disabled_dist()
static Distrib pht(const std::vector< T > &breakpoints, const std::vector< std::vector< T > > &alphas, const std::vector< Matrix< T > > &Ssegs, bool cyclic)
PHt(breakpoints, {alpha_k}, {S_k}, cyclic), stored as its equivalent MAP schedule: D0 = S and D1 = (-...
static double ph_moment(const std::vector< double > &alpha, const Matrix< double > &A, unsigned k)
static Distrib erlang_fit(const T &m, const T &c2)
Erlang fitted to a mean and an SCV, as MATLAB's Erlang.fitMeanAndSCV.
static Distrib pareto(const T &shape, const T &scale)
Pareto(shape, scale), with the MATLAB parameter order (alpha, k).
std::vector< double > params
static Distrib gamma_dist(const T &shape, const T &scale)
Gamma(shape, scale), Weibull(scale, shape) and Lognormal(mu, sigma).
static Distrib cox2(const T &mu1, const T &mu2, const T &phi1)
Cox2(mu1, mu2, phi1), MATLAB's two-phase Coxian constructor.
static Distrib map_dist(const Matrix< T > &D0, const Matrix< T > &D1, ProcessType tag)
A MAP given by its two matrices; the moments are those of its stationary phase.
static Distrib poisson(const T &lambda)
Poisson(lambda), whose SCV is 1/lambda – the count's variance is lambda and its mean is lambda,...
std::vector< double > trace
static Distrib bernoulli(const T &p)
Bernoulli(p): one trial, mean p and variance p(1-p).
static double ph_moment_from(const Matrix< double > &A, std::size_t start, unsigned k)
static Distrib hyperexp_n(const std::vector< T > &p, const std::vector< T > &lambda)
The same for any number of branches, which is what MATLAB HyperExp accepts and what the writers emit ...
static Distrib discrete_uniform(const T &a, const T &b)
DiscreteUniform(a, b) over the integers a..b inclusive.
std::shared_ptr< PriorSpec< double > > prior
static Distrib geometric(const T &p)
Geometric(p) on the MATLAB convention: the NUMBER OF TRIALS to the first success, support {1,...
static Distrib det(const T &m)
static Distrib bmmapt(const std::vector< T > &breakpoints, const std::vector< Matrix< T > > &D0segs, const std::vector< std::vector< std::vector< Matrix< T > > > > &Dbatchsegs, bool cyclic)
BMMAPt(breakpoints, {D0_k}, {{{D^(c,b)_k}}}, cyclic): the BATCH marked schedule.
static Distrib rap(const Matrix< T > &H0, const Matrix< T > &H1)
RAP(H0, H1): a rational arrival process, whose moments are the MAP ones.
bool has_batch_schedule() const
static Distrib uniform(const T &a, const T &b)
Uniform(a, b).
std::vector< Matrix< double > > sched_D1
static Distrib lognormal(const T &logmean, const T &logsigma)
static Distrib hyperexp(const T &p, const T &lambda1, const T &lambda2)
HyperExp(p, lambda1, lambda2): phase i chosen with probability p_i.
std::vector< Matrix< double > > sched_D0
static Distrib me(const std::vector< T > &alpha, const Matrix< T > &A)
ME(alpha, A): the matrix-exponential distribution, whose moments are the phase-type ones – k!
static Distrib immediate()
The point mass at zero.
std::vector< T > phi_vec() const
Completion probabilities, MATLAB's getPhi: (D1 e) .
static Distrib erlang(const T &phase_rate, std::size_t r)
Erlang(alpha, r): r phases of rate alpha, as MATLAB's Erlang(phaseRate, nphases).
bool has_marked_schedule() const
static Distrib exp_mean(const T &m)
std::size_t max_batch_size() const
Largest batch size the schedule declares, or 1 when it carries no batch axis.
static Distrib mpht(const std::vector< T > &breakpoints, const std::vector< std::vector< T > > &alphas, const std::vector< Matrix< T > > &Ssegs, const std::vector< std::vector< std::vector< T > > > &exits, bool cyclic)
MPHt(breakpoints, {alpha_k}, {S_k}, {{s^(c)_k}}, cyclic), stored LOWERED to MMAPt form segment by seg...
std::size_t phases() const
The order of the representation, MATLAB's sn.phases.
static void check_sched_generator(const std::string &who, const std::vector< Matrix< T > > &D0segs, const std::vector< std::vector< Matrix< T > > > &blocks)
Reject a schedule segment that is not a generator, the check MATLAB, the JAR and Python all apply and...
static Distrib coxian(const std::vector< T > &mu, const std::vector< T > &phi)
Coxian(mu, phi): phase i completes with probability phi(i) and otherwise moves to phase i+1.
static Distrib discrete_sampler(const std::vector< T > &p, const std::vector< T > &x)
DiscreteSampler(p, x): the pmf p over the points x.
static Distrib binomial(const T &n, const T &p)
Binomial(n, p).
static Distrib zipf(const T &s, std::size_t n)
Zipf(s, n) over the ranks 1..n, with the generalized harmonic moments H(s-1,n)/H(s,...
The MATLAB GlobalConstants, as reported by lineStart at its defaults.
static constexpr double Immediate
Rate of an Immediate distribution; its mean is 1/Immediate = 1e-8.
static constexpr double FineTol
static constexpr double ArcTol
Below this an off-diagonal entry is NO ARC of the phase / state graph.
static constexpr double Zero
static constexpr double CoarseTol
static constexpr double MaxInt
Stand-in for an unbounded COUNT, MATLAB GlobalConstants.MaxInt.
A LINE Distribution, as the model layer and sn carry it.
std::function< Distrib< T >(const T &)> factory
theta -> Distribution; the continuous form only.
bool continuous
True for the parameter-density form, false for the alternative-set form.
Distrib< T > param_dist
The law of the scalar parameter; the continuous form only.
std::vector< T > probabilities
std::vector< Distrib< T > > alternatives
The alternatives and their weights; the discrete form only.
A MAP as the pair of matrices (D0, D1).