LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
pfqn_mwrbb.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_PFQN_PFQN_MWRBB_H
6#define LINE_API_PFQN_PFQN_MWRBB_H
7
8/**
9 * @file
10 * @ingroup api_pfqn
11 * Majumdar-Woodside robust box bounds on the per-class throughput of a closed
12 * multiclass network with mixed scheduling disciplines (Perf. Eval. 32 (1998)
13 * 101-136).
14 *
15 * Templated port of matlab/src/api/pfqn/pfqn_mwrbb.m together with its three
16 * local functions mwrbb_denom, mwrbb_station_wrest and mwrbb_residence. The
17 * upper bound intersects the no-contention bound (eq. 2) with the
18 * utilization bound (eq. 3); the lower bound is the throughput guarantee of
19 * Theorem 2 (eq. 15), whose per-visit queueing delay depends on the discipline
20 * at the station: FIFO (Theorem 1 / Lemma 1), processor sharing (Lemma 2),
21 * preemptive priority (Lemma 3) and non-preemptive priority (Lemmas 4-5). The
22 * coupled inequalities are resolved by interval narrowing.
23 *
24 * The bounds are distribution-insensitive (NBUE service only) and routing
25 * insensitive: mean visits, mean demands, populations, think times,
26 * disciplines and priorities are the whole input.
27 *
28 * ARITHMETIC. Only sums, products, minima and divisions appear, so the bounds
29 * are EXACT in rational arithmetic and are deliberately left ungated. The
30 * fixed point is a monotone narrowing whose stopping rule (1e-13 absolute on
31 * both bound vectors) is a double constant converted into T, so a higher
32 * precision instantiation stops at the same place, not further -- the
33 * remaining slack there is the bound's, not the iteration's.
34 */
35
36#include <algorithm>
37#include <cstddef>
38#include <vector>
39
40#include "line/num/number.h"
41#include "line/util/error.h"
42#include "line/util/matrix.h"
43
44namespace line {
45namespace pfqn {
46
47/** Station discipline codes, matching the MATLAB `sched` argument. */
48enum class MwrbbSched { Fifo = 0, Ps = 1, PrioNonPreemptive = 2, PrioPreemptive = 3, Aba = 4 };
49
50/** Return value of pfqn_mwrbb, mirroring [Xlo, Xup, Wlo]. */
51template <class T>
53 std::vector<T> Xlo;
54 std::vector<T> Xup;
56};
57
58namespace detail {
59
60/**
61 * Per-visit residence at station k for class c EXCLUDING the isolated
62 * higher-priority 1/f_c term (MATLAB mwrbb_station_wrest).
63 */
64template <class T>
65T mwrbb_station_wrest(std::size_t k, std::size_t c, const Matrix<T>& V, const Matrix<T>& S,
66 const std::vector<T>& N, const std::vector<T>& fup, const T& fc,
67 const std::vector<MwrbbSched>& sched, const std::vector<int>& prio) {
68 const std::size_t C = V.cols();
69 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
70 const T Vkc = V(k, c), Skc = S(k, c);
71 const T scale = T(fc * Vkc);
72 const MwrbbSched d = sched[k];
73
74 if (d == MwrbbSched::Fifo) {
75 T s = zero;
76 for (std::size_t m = 0; m < C; ++m) {
77 T pcm = one;
78 if (scale != zero) {
79 pcm = T(T(fup[m] * V(k, m)) / scale);
80 if (pcm > one) pcm = one;
81 }
82 s += N[m] * S(k, m) * pcm;
83 }
84 return s; // the m = c term is the tagged job's own service
85 }
86 if (d == MwrbbSched::Ps) {
87 T dp = zero;
88 for (std::size_t m = 0; m < C; ++m) {
89 T Ncont = N[m];
90 if (m == c) Ncont = T(N[c] - one);
91 T term = Skc;
92 if (scale != zero) {
93 const T alt = T(T(fup[m] * V(k, m) * S(k, m)) / scale);
94 if (alt < term) term = alt;
95 }
96 dp += Ncont * term;
97 }
98 return T(Skc + dp);
99 }
100 if (d == MwrbbSched::Aba) {
101 T s = zero;
102 for (std::size_t m = 0; m < C; ++m) s += N[m] * S(k, m);
103 return s;
104 }
105
106 // Preemptive (3) or non-preemptive (2) priority.
107 T dp = zero;
108 for (std::size_t m = 0; m < C; ++m) {
109 if (prio[m] != prio[c]) continue; // higher priority goes through Bh
110 T Ncont = N[m];
111 if (m == c) Ncont = T(N[c] - one);
112 T pcm = one;
113 if (scale != zero) {
114 pcm = T(T(fup[m] * V(k, m)) / scale);
115 if (pcm > one) pcm = one;
116 }
117 dp += Ncont * S(k, m) * pcm;
118 }
120 // Water-filling over the lower-priority classes, longest demand first.
121 std::vector<std::size_t> lower;
122 for (std::size_t m = 0; m < C; ++m)
123 if (prio[m] > prio[c]) lower.push_back(m);
124 std::sort(lower.begin(), lower.end(),
125 [&](std::size_t a, std::size_t b) { return S(k, b) < S(k, a); });
126 T budget = one;
127 for (std::size_t idx = 0; idx < lower.size(); ++idx) {
128 const std::size_t l = lower[idx];
129 T al = zero;
130 if (N[l] > zero) {
131 al = T(budget / N[l]);
132 if (scale != zero) {
133 const T capr = T(T(fup[l] * V(k, l)) / scale);
134 if (capr < al) al = capr;
135 }
136 }
137 if (al < zero) al = zero;
138 dp += N[l] * al * S(k, l);
139 budget = T(budget - N[l] * al);
140 if (budget < zero) budget = zero;
141 }
142 }
143 return T(Skc + dp);
144}
145
146} // namespace detail
147
148/**
149 * @brief Majumdar-Woodside robust box bounds on the per-class throughput of a
150 * closed multiclass network with mixed scheduling disciplines (Perf.
151 * Eval. 32 (1998) 101-136).
152 *
153 * @param V (K x C) mean visits
154 * @param S (K x C) mean demand per visit
155 * @param N (C) population
156 * @param Z (C) think time, empty for zero
157 * @param sched (K) per-station discipline, empty for all FIFO
158 * @param prio (C) class priority, lower value = higher priority; empty for equal
159 */
160template <class T>
161MwrbbBounds<T> pfqn_mwrbb(const Matrix<T>& V, const Matrix<T>& S, const std::vector<T>& N,
162 const std::vector<T>& Z, const std::vector<MwrbbSched>& sched,
163 const std::vector<int>& prio) {
164 const std::size_t K = V.rows(), C = V.cols();
165 if (S.rows() != K || S.cols() != C) throw InputError("pfqn_mwrbb: V and S have different shapes");
166 if (N.size() != C) throw InputError("pfqn_mwrbb: N has the wrong length");
167 if (!Z.empty() && Z.size() != C) throw InputError("pfqn_mwrbb: Z has the wrong length");
168 if (!sched.empty() && sched.size() != K) throw InputError("pfqn_mwrbb: sched has the wrong length");
169 if (!prio.empty() && prio.size() != C) throw InputError("pfqn_mwrbb: prio has the wrong length");
170 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
171 const std::vector<T> Zv = Z.empty() ? std::vector<T>(C, zero) : Z;
172 const std::vector<MwrbbSched> sc =
173 sched.empty() ? std::vector<MwrbbSched>(K, MwrbbSched::Fifo) : sched;
174 const std::vector<int> pr = prio.empty() ? std::vector<int>(C, 0) : prio;
175
176 // No-contention upper bound on the cycle rate f_c = X_c/N_c (eqs. 1-2).
177 std::vector<T> fup(C, zero), flo(C, zero);
178 for (std::size_t c = 0; c < C; ++c) {
179 T s = Zv[c];
180 for (std::size_t k = 0; k < K; ++k) s += V(k, c) * S(k, c);
181 if (s == zero) throw InputError("pfqn_mwrbb: a class has no demand and no think time");
182 fup[c] = T(one / s);
183 }
184
185 const T tol = num_traits<T>::from_double(1e-13);
186 for (int it = 0; it < 20000; ++it) {
187 const std::vector<T> fup_old = fup, flo_old = flo;
188
189 // Utilization-based narrowing of the upper bounds (eq. 3).
190 for (std::size_t c = 0; c < C; ++c) {
191 T cap = fup[c];
192 for (std::size_t k = 0; k < K; ++k) {
193 T other = zero;
194 for (std::size_t m = 0; m < C; ++m)
195 if (m != c) other += N[m] * V(k, m) * S(k, m) * flo[m];
196 const T denomk = T(N[c] * V(k, c) * S(k, c));
197 if (denomk > zero) {
198 const T v = T(T(one - other) / denomk);
199 if (v < cap) cap = v;
200 }
201 }
202 if (cap < zero) cap = zero;
203 if (cap < fup[c]) fup[c] = cap;
204 }
205
206 // lower-bound narrowing rationale: see _kb/03-api-layer.md (cpp port notes: pfqn)
207 for (std::size_t c = 0; c < C; ++c) {
208 T DEN = Zv[c], Bh = zero;
209 const T fc = flo[c];
210 for (std::size_t k = 0; k < K; ++k) {
211 if (V(k, c) == zero) continue;
213 for (std::size_t m = 0; m < C; ++m)
214 if (pr[m] < pr[c]) Bh += N[m] * fup[m] * V(k, m) * S(k, m);
215 }
216 DEN += V(k, c) * detail::mwrbb_station_wrest(k, c, V, S, N, fup, fc, sc, pr);
217 }
218 if (DEN == zero) throw NumericError("pfqn_mwrbb: zero cycle time in the lower bound");
219 T val = T(T(one - Bh) / DEN);
220 if (val < zero) val = zero;
221 if (val > flo[c]) flo[c] = val;
222 }
223
224 T du = zero, dl = zero;
225 for (std::size_t c = 0; c < C; ++c) {
226 const T a = num_abs(T(fup[c] - fup_old[c]));
227 const T b = num_abs(T(flo[c] - flo_old[c]));
228 if (a > du) du = a;
229 if (b > dl) dl = b;
230 }
231 if (du < tol && dl < tol) break;
232 }
233
235 r.Xlo.resize(C);
236 r.Xup.resize(C);
237 for (std::size_t c = 0; c < C; ++c) {
238 r.Xlo[c] = T(N[c] * flo[c]);
239 r.Xup[c] = T(N[c] * fup[c]);
240 }
241 r.Wlo = Matrix<T>(K, C, zero);
242 for (std::size_t c = 0; c < C; ++c)
243 for (std::size_t k = 0; k < K; ++k) {
244 if (V(k, c) == zero) continue;
245 T W = detail::mwrbb_station_wrest(k, c, V, S, N, fup, flo[c], sc, pr);
246 const T scale = T(flo[c] * V(k, c));
248 scale > zero) {
249 for (std::size_t m = 0; m < C; ++m)
250 if (pr[m] < pr[c]) W += T(N[m] * fup[m] * V(k, m) * S(k, m) / scale);
251 }
252 r.Wlo(k, c) = W;
253 }
254 return r;
255}
256
257template <class T>
258MwrbbBounds<T> pfqn_mwrbb(const Matrix<T>& V, const Matrix<T>& S, const std::vector<T>& N,
259 const std::vector<T>& Z) {
260 return pfqn_mwrbb(V, S, N, Z, std::vector<MwrbbSched>(), std::vector<int>());
261}
262
263} // namespace pfqn
264} // namespace line
265
266#endif // LINE_API_PFQN_PFQN_MWRBB_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
NumericError(const std::string &what)
Definition error.h:45
The exception types the port throws.
Dense matrix and non-owning view.
MwrbbSched
Station discipline codes, matching the MATLAB sched argument.
Definition pfqn_mwrbb.h:48
MwrbbBounds< T > pfqn_mwrbb(const Matrix< T > &V, const Matrix< T > &S, const std::vector< T > &N, const std::vector< T > &Z, const std::vector< MwrbbSched > &sched, const std::vector< int > &prio)
Majumdar-Woodside robust box bounds on the per-class throughput of a closed multiclass network with m...
Definition pfqn_mwrbb.h:161
T num_abs(const T &v)
Definition number.h:172
Number-type abstraction for the templated API port.
Return value of pfqn_mwrbb, mirroring [Xlo, Xup, Wlo].
Definition pfqn_mwrbb.h:52
std::vector< T > Xlo
Definition pfqn_mwrbb.h:53
std::vector< T > Xup
Definition pfqn_mwrbb.h:54