LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
fes_compute_metrics.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_FES_FES_COMPUTE_METRICS_H
6#define LINE_API_FES_FES_COMPUTE_METRICS_H
7
8/**
9 * @file
10 * @ingroup api_fes
11 * Per-station metrics of the ISOLATED subnetwork at every population state, the
12 * companion of `fes_compute_throughputs`.
13 *
14 * `fes_compute_throughputs` returns the aggregate throughput X(n) that becomes
15 * the flow-equivalent server's rate. That is all the REDUCED model needs, but it
16 * is not enough to report the COLLAPSED stations' own metrics: those are
17 * recovered by conditioning on the FES population,
18 *
19 * E[Q_i] = sum_n P(N_fes = n) * Q_i(n),
20 *
21 * the Chandy-Herzog-Woo hierarchical decomposition, which is EXACT when the
22 * subnetwork is product-form. This header supplies the Q_i(n) and U_i(n) that
23 * sum is taken over, on the same lattice and in the same linearized order as the
24 * throughput table.
25 *
26 * Port of `matlab/src/api/fes/fes_compute_metrics.m`.
27 */
28
29#include <cstddef>
30#include <vector>
31
35#include "line/util/error.h"
36#include "line/util/matrix.h"
37
38namespace line {
39namespace fes {
40
41/** The per-population tables the conditional sum is taken over. */
42template <class T>
44 /** Indexed by the 0-based linearized population state; each (M_sub x K). */
45 std::vector<Matrix<T>> QN;
46 /** Indexed by the 0-based linearized population state; each (M_sub x K). */
47 std::vector<Matrix<T>> UN;
48};
49
50/**
51 * @brief Per-station metrics of the ISOLATED subnetwork at every population
52 * state, the companion of `fes_compute_throughputs`.
53 *
54 * @param L (M_sub x K) service demands of the isolated subnetwork
55 * @param mi (M_sub) servers per station; a delay station's entry is ignored
56 * @param isDelay (M_sub) true where the station is a pure delay
57 * @param cutoffs (K) per-class population cutoffs
58 */
59template <class T>
60FesConditionalMetrics<T> fes_compute_metrics(const Matrix<T>& L, const std::vector<int>& mi,
61 const std::vector<bool>& isDelay,
62 const std::vector<int>& cutoffs) {
63 const std::size_t M_sub = L.rows();
64 const std::size_t K = L.cols();
65 if (isDelay.size() != M_sub)
66 throw InputError("fes_compute_metrics: isDelay has the wrong length");
67 if (cutoffs.size() != K)
68 throw InputError("fes_compute_metrics: cutoffs and demands disagree on the class count");
69
70 const T zero = num_traits<T>::from_int(0);
71
72 std::vector<std::size_t> queueIdx, delayIdx;
73 for (std::size_t i = 0; i < M_sub; ++i) (isDelay[i] ? delayIdx : queueIdx).push_back(i);
74 const std::size_t M_queue = queueIdx.size();
75
76 Matrix<T> L_queue(M_queue, K, zero);
77 std::vector<int> mi_queue;
78 for (std::size_t a = 0; a < M_queue; ++a) {
79 for (std::size_t k = 0; k < K; ++k) L_queue(a, k) = L(queueIdx[a], k);
80 mi_queue.push_back(mi.empty() ? 1 : mi[queueIdx[a]]);
81 }
82 Matrix<T> Z(1, K, zero);
83 for (std::size_t d : delayIdx)
84 for (std::size_t k = 0; k < K; ++k) Z(0, k) += L(d, k);
85
86 std::size_t tableSize = 1;
87 for (int c : cutoffs) {
88 if (c < 0) throw InputError("fes_compute_metrics: negative cutoff");
89 tableSize *= static_cast<std::size_t>(c + 1);
90 }
91
93 out.QN.assign(tableSize, Matrix<T>(M_sub, K, zero));
94 out.UN.assign(tableSize, Matrix<T>(M_sub, K, zero));
95
96 for (std::size_t idx = 0; idx < tableSize; ++idx) {
97 const std::vector<int> nvec = ljd_delinearize(idx + 1, cutoffs);
98 int totalPop = 0;
99 for (int v : nvec) totalPop += v;
100 if (totalPop == 0) continue; // an empty subnetwork holds nothing
101
102 Matrix<T> Q(M_sub, K, zero), U(M_sub, K, zero);
103 std::vector<T> XN(K, zero);
104 if (M_queue > 0) {
105 // `mi` is the additive C=L*(mi+Qarv) term of pfqn_mva, not a server
106 // count: multiservers go through pfqn_mvams. Its UN is per STATION on
107 // the closed multiserver branch and per station-class elsewhere, so it
108 // is not read here -- utilization is recomputed analytically as
109 // U=X*L/S, the [0,1] convention LINE uses at every queueing station
110 // whatever its multiplicity.
111 // See _kb/03-api-layer.md (pfqn_mva: mi is not S).
112 const pfqn::MvaResult<T> r =
113 pfqn::pfqn_mvams(std::vector<T>(K, zero), L_queue, nvec, Z,
114 std::vector<int>(), mi_queue);
115 XN = r.XN;
116 for (std::size_t a = 0; a < M_queue; ++a) {
117 const int srv = mi_queue.empty() || mi_queue[a] < 1 ? 1 : mi_queue[a];
118 for (std::size_t k = 0; k < K; ++k) {
119 if (a < r.QN.rows() && k < r.QN.cols()) Q(queueIdx[a], k) = r.QN(a, k);
120 U(queueIdx[a], k) = T(XN[k] * L_queue(a, k) / num_traits<T>::from_int(srv));
121 }
122 }
123 } else {
124 for (std::size_t k = 0; k < K; ++k)
125 if (nvec[k] > 0 && Z(0, k) > zero)
126 XN[k] = num_traits<T>::from_int(nvec[k]) / Z(0, k);
127 }
128 // A delay holds X_k * Z_i(k) jobs by Little's law on a station with no
129 // queueing, and its INF "utilization" is that same population.
130 for (std::size_t d : delayIdx)
131 for (std::size_t k = 0; k < K; ++k) {
132 Q(d, k) = T(XN[k] * L(d, k));
133 U(d, k) = Q(d, k);
134 }
135 out.QN[idx] = Q;
136 out.UN[idx] = U;
137 }
138 return out;
139}
140
141} // namespace fes
142} // namespace line
143
144#endif // LINE_API_FES_FES_COMPUTE_METRICS_H
InputError(const std::string &what)
Definition error.h:39
std::size_t cols() const
Definition matrix.h:90
std::size_t rows() const
Definition matrix.h:89
The exception types the port throws.
Linearized index of a per-class population vector, for Limited Joint Dependence (LJD) tables.
Dense matrix and non-owning view.
std::vector< int > ljd_delinearize(std::size_t idx, const std::vector< int > &cutoffs)
Inverse of ljd_linearize: the population vector behind an index.
FesConditionalMetrics< T > fes_compute_metrics(const Matrix< T > &L, const std::vector< int > &mi, const std::vector< bool > &isDelay, const std::vector< int > &cutoffs)
Per-station metrics of the ISOLATED subnetwork at every population state, the companion of fes_comput...
MvaResult< T > pfqn_mvams(const std::vector< T > &lambda, const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const std::vector< int > &mi, const std::vector< int > &S)
General-purpose exact MVA for mixed networks with multiserver stations.
Definition pfqn_mvams.h:840
Exact Mean Value Analysis for closed product-form networks (Reiser and Lavenberg 1980).
Exact Mean Value Analysis for mixed open/closed networks with multiserver stations.
The per-population tables the conditional sum is taken over.
std::vector< Matrix< T > > QN
Indexed by the 0-based linearized population state; each (M_sub x K).
std::vector< Matrix< T > > UN
Indexed by the 0-based linearized population state; each (M_sub x K).
std::vector< T > XN
(R) per-class throughput
Definition pfqn_mva.h:45
Matrix< T > QN
(M x R) mean queue length
Definition pfqn_mva.h:46