5#ifndef LINE_API_NPFQN_NPFQN_BND_BGT_H
6#define LINE_API_NPFQN_NPFQN_BND_BGT_H
80 std::vector<std::vector<T> >
Qub;
84 std::vector<std::vector<T> >
L;
108 const std::vector<std::vector<std::size_t> >& sigma, std::size_t J) {
111 const std::size_t I = lambda.size();
112 if (mu.size() != I || sigma.size() != I)
113 throw InputError(
"npfqn_bnd_bgt: mu and sigma must have one entry per type");
118 std::vector<std::size_t> firstOf(I, 0), nextOf;
119 for (std::size_t i = 0; i < I; ++i) {
120 if (mu[i].size() != sigma[i].size())
121 throw InputError(
"npfqn_bnd_bgt: mu and sigma disagree on the stage count of a type");
122 if (mu[i].empty())
throw InputError(
"npfqn_bnd_bgt: a type has no stage");
123 if (!(lambda[i] > zero))
124 throw InputError(
"npfqn_bnd_bgt: every type needs a strictly positive arrival rate");
125 firstOf[i] = muc.size();
126 for (std::size_t k = 0; k < mu[i].size(); ++k) {
130 muc.push_back(mu[i][k]);
133 const std::size_t N = muc.size();
134 for (std::size_t c = 0; c < N; ++c)
135 if (!(muc[c] > zero))
136 throw InputError(
"npfqn_bnd_bgt: every stage needs a strictly positive service rate");
138 for (std::size_t c = 0; c + 1 < N; ++c)
142 out.
rho.assign(N, zero);
144 for (std::size_t c = 0; c < N; ++c) {
148 for (std::size_t j = 0; j < J; ++j)
151 " is saturated: the load condition of the reference fails");
155 for (std::size_t i = 0; i < I; ++i) scale = T(scale + lambda[i]);
156 for (std::size_t c = 0; c < N; ++c) scale = T(scale + muc[c]);
158 std::vector<T> lam(I, zero), mus(N, zero);
159 for (std::size_t i = 0; i < I; ++i) lam[i] = T(lambda[i] / scale);
160 for (std::size_t c = 0; c < N; ++c) mus[c] = T(muc[c] / scale);
163 const std::size_t oV = J * N, ig = J * N + J, nv = J * N + J + 1;
166 for (std::size_t v = 0; v < J * N; ++v) m.
set_bounds(v, zero, one);
168 for (std::size_t j = 0; j < J; ++j) {
169 for (std::size_t c = 0; c < N; ++c) {
173 m.
row_add(j * N + c, T(-mus[c]));
174 if (nextOf[c] < N) m.
row_add(j * N + nextOf[c], mus[c]);
179 m.
row_add(j * N + c, T(-mus[c]));
180 if (nextOf[c] < N) m.
row_add(j * N + nextOf[c], mus[c]);
187 for (std::size_t jp = 0; jp < J; ++jp)
188 if (jp != j) m.
row_add(jp * N + c, w);
198 throw UnsupportedError(std::string(
"npfqn_bnd_bgt: GLP[dm] did not solve to optimality (") +
203 "npfqn_bnd_bgt: GLP[dm] has no solution with gamma > 0: this network is not certified "
204 "globally stable, so no finite piecewise-linear Lyapunov bound exists");
214 for (std::size_t v = 0; v < J * N; ++v) m2.
set_cost(v, one);
223 out.
L.assign(J, std::vector<T>(N, zero));
225 for (std::size_t j = 0; j < J; ++j)
226 for (std::size_t c = 0; c < N; ++c) {
227 out.
L[j][c] = s.
x[j * N + c];
228 if (out.
L[j][c] > out.
Lmax) out.
Lmax = out.
L[j][c];
230 out.
V.assign(J, zero);
231 for (std::size_t j = 0; j < J; ++j) out.
V[j] = s.
x[oV + j];
233 const T Lg = T(out.
Lmax + gamma);
234 const T Lh = T(out.
Lmax + gamma / two);
242 out.
Qub.assign(I, std::vector<T>());
243 out.
finite.assign(N,
true);
244 for (std::size_t i = 0; i < I; ++i) out.
Qub[i].assign(mu[i].size(), zero);
245 for (std::size_t c = 0; c < N; ++c) {
247 for (std::size_t j = 0; j < J; ++j)
248 if (out.
L[j][c] > best) best = out.
L[j][c];
UnsupportedError(const std::string &what)
Sparse LP in the natural form, with per-variable bounds.
void set_maximize(bool m)
true to maximize c'x (the default), false to minimize.
void emit(LpSense sense, const T &rhs)
Emit the accumulated row with the given relation and right-hand side.
void set_cost(std::size_t j, const T &v)
void row_clear()
Discard whatever the row accumulator holds.
void set_bounds(std::size_t j, const T &lo, const T &hi)
void row_add(std::size_t j, const T &v)
row(j) += v, the accumulation the MATLAB reference performs.
The exception types the port throws.
A sparse LP backend for line::lp::LpModel, on HiGHS (MIT).
LpSolution< T > lp_solve(const LpModel< T > &model, std::size_t dense_max_cols=512)
Solve, choosing the backend by arithmetic and size.
const char * lp_status_name(LpStatus s)
BndBgt< T > npfqn_bnd_bgt(const std::vector< T > &lambda, const std::vector< std::vector< T > > &mu, const std::vector< std::vector< std::size_t > > &sigma, std::size_t J)
Piecewise-linear Lyapunov UPPER bound on the steady-state queue lengths of a multitype (deterministic...
Number-type abstraction for the templated API port.
Templated primal simplex with Bland's rule.
T objective
c'x, in the sense requested (max or min)
std::vector< T > x
primal solution in the ORIGINAL variable space
std::vector< T > rho
per-class nominal load
std::vector< T > rhoStation
per-station nominal load
std::vector< std::vector< T > > L
the Lyapunov coefficients, [J][N]
std::vector< bool > finite
false where max_j L is 0 and Qub is meaningless
std::vector< std::size_t > classType
std::vector< std::size_t > classStage
T tailRatio
geometric decay ratio of the tail bound
T tailStep
step of the tail bound, 2(Lmax+gamma/2)
T U
the Theorem 4 bound on E[L^j'Q]
T gamma
the drift certificate, strictly positive on success
T Lmax
max over j and (i,k) of L
T B
exception parameter of the smoothed function
std::vector< std::size_t > classStation
std::vector< std::vector< T > > Qub
per type and stage, the bound on E[Q(i,k)]
std::vector< T > V
the per-station slack
T scale
the uniformization divisor