5#ifndef LINE_API_MAPQN_MAPQN_BND_LR_MVA_H
6#define LINE_API_MAPQN_MAPQN_BND_LR_MVA_H
78 if (
M <= 1)
throw InputError(
"mapqn_bnd_lr_mva: M must be at least 2");
79 if (
N < 1)
throw InputError(
"mapqn_bnd_lr_mva: N must be at least 1");
80 if (
K < 1)
throw InputError(
"mapqn_bnd_lr_mva: K must be at least 1");
81 if (
static_cast<int>(
muM.size()) !=
M - 1)
82 throw InputError(
"mapqn_bnd_lr_mva: muM must have M-1 entries");
83 const std::size_t k =
static_cast<std::size_t
>(
K);
85 throw InputError(
"mapqn_bnd_lr_mva: muMAP must be K x K");
87 throw InputError(
"mapqn_bnd_lr_mva: v must be K x K");
88 if (
r.
rows() !=
static_cast<std::size_t
>(
M) ||
r.
cols() !=
static_cast<std::size_t
>(
M))
89 throw InputError(
"mapqn_bnd_lr_mva: r must be M x M");
125 const std::size_t mk =
static_cast<std::size_t
>(
M) *
static_cast<std::size_t
>(
K);
131 std::size_t
UN(
int i,
int k)
const {
132 return off_UN +
static_cast<std::size_t
>(i) *
static_cast<std::size_t
>(
K) +
133 static_cast<std::size_t
>(k);
135 std::size_t
QN(
int i,
int k)
const {
136 return off_QN +
static_cast<std::size_t
>(i) *
static_cast<std::size_t
>(
K) +
137 static_cast<std::size_t
>(k);
139 std::size_t
B(
int j,
int k,
int i)
const {
141 (
static_cast<std::size_t
>(j) *
static_cast<std::size_t
>(
K) +
142 static_cast<std::size_t
>(k)) *
143 static_cast<std::size_t
>(
M) +
144 static_cast<std::size_t
>(i);
164T lr_mva_rate(
const LrMvaParams<T>& p,
int i,
int j,
int k,
int h) {
165 const std::size_t ki =
static_cast<std::size_t
>(k), hi =
static_cast<std::size_t
>(h);
166 const int last = p.M - 1;
168 if (k != h)
return T();
169 return T(p.r(
static_cast<std::size_t
>(i),
static_cast<std::size_t
>(j)) *
170 p.muM[
static_cast<std::size_t
>(i)]);
173 return T(p.r(
static_cast<std::size_t
>(last),
static_cast<std::size_t
>(j)) *
175 if (k == h)
return T();
176 return T(p.v(ki, hi) + p.r(
static_cast<std::size_t
>(last),
static_cast<std::size_t
>(last)) *
182void lr_mva_bounds(
const LrMvaParams<T>& p,
const LrMvaIndex& x, lp::LpModel<T>& m) {
183 const T one = num_traits<T>::from_int(1);
184 const T nn = num_traits<T>::from_int(p.N);
185 for (
int i = 0; i < p.M; ++i) {
186 for (
int k = 0; k < p.K; ++k) {
187 m.set_bounds(x.UN(i, k), T(), one);
188 m.set_bounds(x.QN(i, k), T(), nn);
189 for (
int j = 0; j < p.M; ++j) m.set_bounds(x.B(i, k, j), T(), nn);
196void lr_mva_qnb(
const LrMvaParams<T>& p,
const LrMvaIndex& x, lp::LpModel<T>& m) {
197 for (
int i = 0; i < p.M; ++i) {
198 for (
int k = 0; k < p.K; ++k) {
199 for (
int j = 0; j < p.M; ++j) {
200 m.row_add_int(x.QN(i, k), -1);
201 m.row_add_int(x.B(j, k, i), 1);
210void lr_mva_umax(
const LrMvaParams<T>& p,
const LrMvaIndex& x, lp::LpModel<T>& m) {
211 for (
int i = 0; i < p.M; ++i) {
212 for (
int k = 0; k < p.K; ++k) m.row_add_int(x.UN(i, k), 1);
219void lr_mva_popconstr(
const LrMvaParams<T>& p,
const LrMvaIndex& x, lp::LpModel<T>& m) {
220 for (
int i = 0; i < p.M; ++i)
221 for (
int k = 0; k < p.K; ++k) m.row_add_int(x.QN(i, k), 1);
227void lr_mva_flow(
const LrMvaParams<T>& p,
const LrMvaIndex& x, lp::LpModel<T>& m) {
228 for (
int i = 0; i < p.M; ++i) {
229 for (
int k = 0; k < p.K; ++k) {
230 for (
int mm = 0; mm < p.K; ++mm) {
231 for (
int w = 0; w < p.M; ++w) {
232 m.row_add(x.UN(w, k), lr_mva_rate(p, w, i, k, mm));
233 m.row_add(x.UN(i, mm), T(-lr_mva_rate(p, i, w, mm, k)));
243void lr_mva_ubal(
const LrMvaParams<T>& p,
const LrMvaIndex& x, lp::LpModel<T>& m) {
244 const int last = p.M - 1;
245 for (
int k = 0; k < p.K; ++k) {
246 for (
int h = 0; h < p.K; ++h) {
247 if (h == k)
continue;
248 for (
int w = 0; w < p.M; ++w) {
249 m.row_add(x.UN(last, k), lr_mva_rate(p, last, w, k, h));
250 m.row_add(x.UN(last, h), T(-lr_mva_rate(p, last, w, h, k)));
259void lr_mva_qbal(
const LrMvaParams<T>& p,
const LrMvaIndex& x, lp::LpModel<T>& m) {
260 const int last = p.M - 1;
261 for (
int k = 0; k < p.K; ++k) {
262 for (
int h = 0; h < p.K; ++h) {
263 if (h == k)
continue;
264 for (
int w = 0; w < p.M; ++w) m.row_add(x.QN(last, k), lr_mva_rate(p, last, w, k, h));
266 for (
int mm = 0; mm < p.K; ++mm)
267 for (
int j = 0; j < last; ++j)
268 m.row_add(x.UN(last, mm), lr_mva_rate(p, last, j, mm, k));
269 for (
int j = 0; j < last; ++j)
270 m.row_add(x.UN(j, k), T(-lr_mva_rate(p, j, last, k, k)));
271 for (
int h = 0; h < p.K; ++h) {
272 if (h == k)
continue;
273 for (
int w = 0; w < p.M; ++w)
274 m.row_add(x.QN(last, h), T(-lr_mva_rate(p, last, w, h, k)));
282void lr_mva_mcc(
const LrMvaParams<T>& p,
const LrMvaIndex& x, lp::LpModel<T>& m) {
283 const T np1 = num_traits<T>::from_int(p.N + 1);
284 for (
int i = 0; i < p.M; ++i) {
285 for (
int k = 0; k < p.K; ++k) {
286 for (
int mm = 0; mm < p.K; ++mm) {
287 for (
int w = 0; w < p.M; ++w) {
288 if (w == i)
continue;
289 m.row_add(x.QN(i, k), lr_mva_rate(p, i, w, k, mm));
291 for (
int j = 0; j < p.M; ++j) {
292 if (j == i)
continue;
293 const T q = lr_mva_rate(p, j, i, k, mm);
294 m.row_add(x.QN(j, k), q);
295 for (
int wp = 0; wp < p.M; ++wp) {
296 if (wp == i || wp == j)
continue;
297 m.row_add(x.B(j, k, wp), q);
299 m.row_add(x.UN(j, k), T(-(np1 * q)));
309void lr_mva_mcc2(
const LrMvaParams<T>& p,
const LrMvaIndex& x, lp::LpModel<T>& m) {
310 for (
int i = 0; i < p.M; ++i) {
311 for (
int k = 0; k < p.K; ++k) {
312 for (
int mm = 0; mm < p.K; ++mm) {
313 for (
int w = 0; w < p.M; ++w) {
314 if (w == i)
continue;
315 m.row_add(x.QN(i, k), lr_mva_rate(p, i, w, k, mm));
317 for (
int j = 0; j < p.M; ++j) {
318 if (j == i)
continue;
319 const T q = lr_mva_rate(p, j, i, k, mm);
320 m.row_add(x.B(j, k, i), T(-q));
321 m.row_add(x.UN(j, k), T(-q));
331void lr_mva_qmax(
const LrMvaParams<T>& p,
const LrMvaIndex& x, lp::LpModel<T>& m) {
332 for (
int w = 0; w < p.M; ++w) {
333 for (
int k = 0; k < p.K; ++k) {
334 m.row_add_int(x.QN(w, k), 1);
335 m.row_add_int(x.UN(w, k), -p.N);
343void lr_mva_qmin(
const LrMvaParams<T>& p,
const LrMvaIndex& x, lp::LpModel<T>& m) {
344 for (
int k = 0; k < p.K; ++k) {
345 for (
int j = 0; j < p.M; ++j) {
346 for (
int w = 0; w < p.M; ++w) m.row_add_int(x.QN(w, k), -1);
347 m.row_add_int(x.UN(j, k), p.N);
372 if (objective_queue < 0 || objective_queue >= p.
M)
373 throw InputError(
"mapqn_bnd_lr_mva: objective_queue out of range");
374 if (objective_level < -1 || objective_level >= p.
K)
375 throw InputError(
"mapqn_bnd_lr_mva: objective_level out of range");
379 detail::lr_mva_bounds(p, x, m);
383 detail::lr_mva_qnb(p, x, m);
384 detail::lr_mva_umax(p, x, m);
385 detail::lr_mva_popconstr(p, x, m);
386 detail::lr_mva_flow(p, x, m);
387 detail::lr_mva_ubal(p, x, m);
388 detail::lr_mva_qbal(p, x, m);
389 detail::lr_mva_mcc(p, x, m);
390 detail::lr_mva_mcc2(p, x, m);
391 detail::lr_mva_qmax(p, x, m);
392 detail::lr_mva_qmin(p, x, m);
395 const int first_level = (objective_level < 0) ? 0 : objective_level;
396 const int last_level = (objective_level < 0) ? p.
K - 1 : objective_level;
397 for (
int k = first_level; k <= last_level; ++k) {
399 : x.
QN(objective_queue, k),
414 if (!out.
ok)
return out;
416 out.
UN =
Matrix<T>(
static_cast<std::size_t
>(p.
M),
static_cast<std::size_t
>(p.
K));
417 out.
QN =
Matrix<T>(
static_cast<std::size_t
>(p.
M),
static_cast<std::size_t
>(p.
K));
418 for (
int i = 0; i < p.
M; ++i) {
419 for (
int k = 0; k < p.
K; ++k) {
420 const std::size_t ii =
static_cast<std::size_t
>(i), kk =
static_cast<std::size_t
>(k);
421 out.
UN(ii, kk) = sol.
x[x.
UN(i, k)];
422 out.
QN(ii, kk) = sol.
x[x.
QN(i, k)];
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.
std::size_t num_rows() const
void set_cost(std::size_t j, const T &v)
std::size_t num_vars() const
The exception types the port throws.
Model parameters and variable indexing shared by the mapqn QR bounds.
Dense matrix and non-owning view.
const char * lp_status_name(LpStatus s)
LpSolution< T > simplex_solve(const LpModel< T > &model, std::size_t max_iterations=0)
Solve the model.
MapqnBndLrMvaResult< T > mapqn_bnd_lr_mva(const LrMvaParams< T > &p, int objective_queue, int objective_level, MapqnSense sense=MapqnSense::Max, MapqnObjectiveVar objective_var=MapqnObjectiveVar::UN)
Bound UN or QN at (objective_queue, objective_level) over the MVA-shaped LR polytope.
MapqnSense
Which direction the bound is taken in.
MapqnObjectiveVar
The variable family the objective is taken over.
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
Variable layout: UN, then QN, then B, in the reference's declaration order.
std::size_t B(int j, int k, int i) const
std::size_t QN(int i, int k) const
std::size_t num_vars() const
std::size_t UN(int i, int k) const
Parameters of the MVA-shaped LR bound, mirroring the reference's params.
Matrix< T > r
(M x M) routing probabilities
Matrix< T > muMAP
(K x K) completion rates of the MAP queue
int K
number of levels of the MAP queue
int M
number of queues, the last of which is the MAP
Matrix< T > v
(K x K) level-change rates of the MAP queue
std::vector< T > muM
(M-1) service rates of the exponential queues
Result of an MVA-shaped LR bound solve.
Matrix< T > QN
M x K queue lengths.
Matrix< T > UN
M x K utilizations.
std::vector< T > x
full solution vector, indexed by LrMvaIndex
T objective
the bound on UN(objective_queue, objective_level)
std::string status
textual LP status
bool ok
the LP reached an optimal vertex