LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
solver_ba_bpt.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_SOLVERS_BA_SOLVER_BA_BPT_H
6#define LINE_SOLVERS_BA_SOLVER_BA_BPT_H
7
8/**
9 * @file
10 * @ingroup line_solvers
11 * Achievable-region LOWER bound on the mean response times of a multiclass
12 * open Markovian network, valid for EVERY non-idling scheduling policy at
13 * every station.
14 *
15 * Templated port of matlab/src/solvers/BA/solver_ba_bpt_analyzer.m,
16 * cross-checked against
17 * jar/src/main/java/jline/solvers/ba/analyzers/Solver_ba_bpt_analyzer.java.
18 * The polyhedron is `npfqn_bnd_bpt`; this analyzer maps the LINE model onto it
19 * and reads the bound back per station and class.
20 *
21 * CLASS SPACE. The reference's "class" is a buffer: one exponential service
22 * rate, one Markovian routing law. LINE's (station, job class) pair is exactly
23 * that, so a pair carrying traffic becomes one LP class, the Source is absorbed
24 * into the external arrival vector, and class switching needs no special
25 * treatment because sn.rt already carries it.
26 *
27 * BOUND CONVENTION. R(i,r) minimizes x over the polyhedron with the objective
28 * set to that pair's unit vector, so each entry is a valid lower bound on its
29 * own. Q follows by Little's law from the bounded R and the EXACT throughput T
30 * (an open network's per-class rates are fixed by the traffic equations, not by
31 * the policy), and so does C. U is exact for the same reason.
32 *
33 * TIGHTNESS. Exact on M/M/1 and tight on the externally fed classes, but weak
34 * on a class whose arrivals are all internal: the only term coupling x_r to the
35 * second-moment block carries the factor lambda0_r, so an internally fed class
36 * can fall back to its own mean service time.
37 *
38 * ARITHMETIC. Rational-clean, like the API core it calls.
39 *
40 * Reference: D. Bertsimas, I. Paschalidis, J. Tsitsiklis (1994). Optimization
41 * of multiclass queueing networks: polyhedral and nonlinear characterizations
42 * of achievable performance. Annals of Applied Probability 4(1), 43-75.
43 */
44
45#include <cmath>
46#include <cstddef>
47#include <limits>
48#include <string>
49#include <vector>
50
54#include "line/util/error.h"
55#include "line/util/matrix.h"
56
57namespace line {
58namespace ba {
59
60/**
61 * @param L the model
62 * @param out the (Q,U,R,Tp,C,X) block to fill; shapes are set here
63 */
64template <class T, class Solution>
65void solver_ba_bpt(const qn::NetworkStruct<T>& L, Solution& out) {
66 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
67 const std::size_t M = L.nstations, K = L.nclasses;
68
69 out.Q = Matrix<T>(M, K, zero);
70 out.U = Matrix<T>(M, K, zero);
71 out.R = Matrix<T>(M, K, zero);
72 out.Tp = Matrix<T>(M, K, zero);
73 out.C.assign(K, zero);
74 out.X.assign(K, zero);
75 out.lG = std::numeric_limits<double>::quiet_NaN();
76 out.iter = 1;
77
78 // ---- model gates ----
79 for (std::size_t r = 0; r < K; ++r)
80 if (std::isfinite(L.classes[r].population))
81 throw UnsupportedError(
82 "solver_ba_bpt: method 'bpt.lower' supports fully open networks only "
83 "(no closed classes)");
84 std::vector<std::size_t> srcList, qstat;
85 for (std::size_t i = 0; i < M; ++i) {
86 if (L.stations[i].nodetype == qn::NodeType::Source)
87 srcList.push_back(i);
88 else
89 qstat.push_back(i);
90 }
91 if (srcList.empty())
92 throw UnsupportedError(
93 "solver_ba_bpt: method 'bpt.lower' requires an open network with a Source station");
94 for (std::size_t a = 0; a < qstat.size(); ++a) {
95 const std::size_t i = qstat[a];
96 if (L.stations[i].sched == lang::SchedStrategy::INF)
97 throw UnsupportedError(
98 "solver_ba_bpt: method 'bpt.lower' does not support delay (infinite-server) "
99 "stations: the achievable region is derived for one server per station");
100 const double ns = num_traits<T>::to_double(L.stations[i].nservers);
101 if (std::isfinite(ns) && ns > 1)
102 throw UnsupportedError(
103 "solver_ba_bpt: method 'bpt.lower' does not support multi-server stations");
104 }
105
106 // ---- station-space routing, with the Source absorbed into lambda0 ----
107 const Matrix<T> rtst = api::sn_rt_stations(L).rtst;
108
109 std::vector<std::size_t> pairStation, pairClass, pairFlat;
110 for (std::size_t a = 0; a < qstat.size(); ++a) {
111 const std::size_t i = qstat[a];
112 for (std::size_t r = 0; r < K; ++r) {
113 pairStation.push_back(i);
114 pairClass.push_back(r);
115 pairFlat.push_back(i * K + r);
116 }
117 }
118 const std::size_t np = pairFlat.size();
119
120 std::vector<T> lambda0(np, zero);
121 for (std::size_t si = 0; si < srcList.size(); ++si) {
122 const std::size_t s = srcList[si];
123 for (std::size_t r0 = 0; r0 < K; ++r0) {
124 const T arr = L.rates(s, r0);
125 const double ad = num_traits<T>::to_double(arr);
126 if (!std::isfinite(ad) || !(arr > zero)) continue;
127 for (std::size_t p = 0; p < np; ++p)
128 lambda0[p] = T(lambda0[p] + arr * rtst(s * K + r0, pairFlat[p]));
129 }
130 }
131
132 // Flow to the Sink or back to a Source is the exit probability, i.e. the
133 // row deficit, and needs no column.
134 Matrix<T> P(np, np, zero);
135 for (std::size_t p = 0; p < np; ++p)
136 for (std::size_t q = 0; q < np; ++q) P(p, q) = rtst(pairFlat[p], pairFlat[q]);
137
138 // ---- restrict to the pairs that actually carry traffic ----
139 Matrix<T> ImPt(np, np, zero);
140 for (std::size_t i = 0; i < np; ++i)
141 for (std::size_t j = 0; j < np; ++j) ImPt(i, j) = T((i == j ? one : zero) - P(j, i));
142 Matrix<T> rhs0(np, 1, zero);
143 for (std::size_t p = 0; p < np; ++p) rhs0(p, 0) = lambda0[p];
144 const Matrix<T> lamAll = matmul(inverse(ImPt), rhs0);
145 double lamMax = 0.0;
146 for (std::size_t p = 0; p < np; ++p)
147 lamMax = std::max(lamMax, num_traits<T>::to_double(lamAll(p, 0)));
148 const T lamTol = num_traits<T>::from_double(1e-12 * std::max(1.0, lamMax));
149 std::vector<std::size_t> keep;
150 for (std::size_t p = 0; p < np; ++p)
151 if (lamAll(p, 0) > lamTol) keep.push_back(p);
152 if (keep.empty()) throw UnsupportedError("solver_ba_bpt: the model carries no open traffic");
153
154 const std::size_t nk = keep.size();
155 std::vector<T> lam0k(nk, zero), muk(nk, zero);
156 std::vector<std::size_t> statk(nk), clsk(nk), ustat;
157 Matrix<T> Pk(nk, nk, zero);
158 for (std::size_t a = 0; a < nk; ++a) {
159 const std::size_t p = keep[a];
160 lam0k[a] = lambda0[p];
161 statk[a] = pairStation[p];
162 clsk[a] = pairClass[p];
163 for (std::size_t b = 0; b < nk; ++b) Pk(a, b) = P(p, keep[b]);
164 muk[a] = L.rates(statk[a], clsk[a]);
165 const double md = num_traits<T>::to_double(muk[a]);
166 if (!std::isfinite(md) || !(muk[a] > zero))
167 throw UnsupportedError("solver_ba_bpt: station " + std::to_string(statk[a] + 1) +
168 " has no service rate for class " + std::to_string(clsk[a] + 1) +
169 " but carries its traffic");
170 if (L.procid(statk[a] + 1, clsk[a] + 1) != lang::ProcessType::EXP)
171 throw UnsupportedError(
172 "solver_ba_bpt: method 'bpt.lower' requires exponential service: station " +
173 std::to_string(statk[a] + 1) + " class " + std::to_string(clsk[a] + 1) +
174 " is not exponential");
175 bool seen = false;
176 for (std::size_t u = 0; u < ustat.size(); ++u)
177 if (ustat[u] == statk[a]) seen = true;
178 if (!seen) ustat.push_back(statk[a]);
179 }
180 // Dense station index space for the LP.
181 std::vector<std::size_t> stationOf(nk, 0);
182 for (std::size_t a = 0; a < nk; ++a)
183 for (std::size_t u = 0; u < ustat.size(); ++u)
184 if (ustat[u] == statk[a]) stationOf[a] = u;
185
186 // ---- one LP per pair, objective = that pair's unit vector ----
187 for (std::size_t a = 0; a < nk; ++a) {
188 std::vector<T> e(nk, zero);
189 e[a] = one;
190 const npfqn::BndBpt<T> info = npfqn::npfqn_bnd_bpt(lam0k, muk, Pk, stationOf, e);
191 out.R(statk[a], clsk[a]) = info.zlb;
192 out.Tp(statk[a], clsk[a]) = info.lambda[a];
193 out.U(statk[a], clsk[a]) = info.rho[a];
194 }
195
196 // ---- exact open-network quantities ----
197 for (std::size_t si = 0; si < srcList.size(); ++si) {
198 const std::size_t s = srcList[si];
199 for (std::size_t r = 0; r < K; ++r) {
200 const T arr = L.rates(s, r);
201 const double ad = num_traits<T>::to_double(arr);
202 if (std::isfinite(ad) && arr > zero) {
203 out.Tp(s, r) = T(out.Tp(s, r) + arr);
204 out.X[r] = T(out.X[r] + arr);
205 }
206 }
207 }
208 for (std::size_t i = 0; i < M; ++i)
209 for (std::size_t r = 0; r < K; ++r) out.Q(i, r) = T(out.Tp(i, r) * out.R(i, r));
210 for (std::size_t r = 0; r < K; ++r) {
211 if (out.X[r] > zero) {
212 T sum = zero;
213 for (std::size_t i = 0; i < M; ++i) sum = T(sum + out.Q(i, r));
214 out.C[r] = T(sum / out.X[r]);
215 }
216 }
217}
218
219} // namespace ba
220} // namespace line
221
222#endif // LINE_SOLVERS_BA_SOLVER_BA_BPT_H
UnsupportedError(const std::string &what)
Definition error.h:51
A network plus its refreshed NetworkStruct.
std::vector< JobClass > classes
ProcessType procid(std::size_t ist, std::size_t r) const
sn.procid(i,r): the process type of a (station, class) pair.
std::vector< Station< T > > stations
stations[k-1] is the k-th station
Matrix< T > rates
(nstations x nclasses) service rates and SCVs, with a PARALLEL disabled flag instead of MATLAB's NaN ...
The exception types the port throws.
Dense matrix and non-owning view.
SnRtStations< T > sn_rt_stations(const qn::NetworkStruct< T > &sn)
void solver_ba_bpt(const qn::NetworkStruct< T > &L, Solution &out)
BndBpt< T > npfqn_bnd_bpt(const std::vector< T > &lambda0, const std::vector< T > &mu, const Matrix< T > &P, const std::vector< std::size_t > &stationOf, const std::vector< T > &c)
First-order linear-programming relaxation of the achievable region of a multiclass open Markovian que...
Matrix< T > inverse(const Matrix< T > &A)
Inverse by LU with one factorization and n back substitutions.
Definition linalg.h:72
Matrix< T > matmul(const Matrix< T > &A, const Matrix< T > &B)
Matrix product A B.
Definition linalg.h:36
A queueing network and its refreshed NetworkStruct.
First-order linear-programming relaxation of the achievable region of a multiclass open Markovian que...
Port of matlab/src/api/sn/sn_rt_stations.m.
std::vector< T > lambda
effective arrival rate of each class
std::vector< T > rho
per-class utilization lambda_r/mu_r
T zlb
lower bound on sum_r c_r x_r