LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
pfqn_mva_interval.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_MVA_INTERVAL_H
6#define LINE_API_PFQN_PFQN_MVA_INTERVAL_H
7
8/**
9 * @file
10 * @ingroup api_pfqn
11 * Exact interval-valued MVA for single-class closed product-form networks.
12 *
13 * Port of `matlab/src/api/pfqn/pfqn_mva_interval.m`, the algorithm of J. Luthi
14 * and G. Haring, "Mean value analysis for queueing network models with
15 * intervals as input parameters", Performance Evaluation 32(3):185-215, 1998.
16 *
17 * WHY CORNERS AND NOT INTERVAL ARITHMETIC. Single-class MVA is monotone in
18 * every input -- the throughput decreases in each demand and in the think time
19 * and increases in the population, a station's own queue length and residence
20 * time increase in its own demand and in the population and decrease in the
21 * other demands and in the think time, and the totals increase in every demand
22 * and in the population and decrease in the think time (their Theorems 2-5,
23 * Table 1). By their Theorem 1 the exact range of a function monotone in each
24 * argument is attained AT A CORNER of the input box, so every bound below is
25 * one ordinary MVA call at the corner the sign pattern selects: 2*(m+2) calls,
26 * m being the number of thick demand intervals. Running the recursion in
27 * interval arithmetic instead is valid but far wider, because every input
28 * recurs at each step -- the dependency problem, 14x too wide on the paper's
29 * own example.
30 *
31 * WHAT THE INTERVAL IS, AND WHAT IT IS NOT. It is the exact hull of MVA over
32 * the input box, conditional on the demands lying in that box; it says nothing
33 * about the accuracy of MVA itself. It must therefore NOT be composed with the
34 * brackets of SolverBA, which bracket the exact solution of a model whose
35 * demands are known. The two answer different questions and intersecting them
36 * would claim a guarantee neither provides.
37 *
38 * Delay stations are folded into Z exactly as `pfqn_mva` folds them: a delay
39 * demand interval enters as a term of the think-time interval, and the hull of
40 * the sum is the sum of the hulls when the delays vary independently.
41 * Load-independent single-server queueing stations only, one class only; the
42 * monotonicity theorems cover no other case.
43 */
44
45#include <algorithm>
46#include <cstddef>
47#include <vector>
48
51#include "line/num/number.h"
52#include "line/util/error.h"
53#include "line/util/matrix.h"
54
55namespace line {
56namespace pfqn {
57
58/** Every output of `pfqn_mva_interval`, each a [lower, upper] pair. */
59template <class T>
61 T Xlo, Xup; ///< throughput interval
62 Matrix<T> Q; ///< (M x 2) mean queue length per station
63 Matrix<T> U; ///< (M x 2) utilization enclosure per station
64 Matrix<T> R; ///< (M x 2) residence time per station
65 T Rtot_lo, Rtot_up; ///< total response time
66 T Qtot_lo, Qtot_up; ///< total number of jobs at the stations
67};
68
69/**
70 * @brief Exact interval-valued MVA for single-class closed product-form
71 * networks.
72 *
73 * @param L (M x 2) demand intervals, column 0 lower and column 1 upper.
74 * @param nlo lower population endpoint (integer, at least 1).
75 * @param nup upper population endpoint.
76 * @param zlo lower think-time endpoint.
77 * @param zup upper think-time endpoint.
78 */
79template <class T>
80MvaIntervalResult<T> pfqn_mva_interval(const Matrix<T>& L, int nlo, int nup, const T& zlo,
81 const T& zup) {
82 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
83 if (L.empty()) throw InputError("pfqn_mva_interval requires at least one queueing station");
84 if (L.cols() != 2)
85 throw InputError(
86 "pfqn_mva_interval is a single-class method: L must be M x 2, one [lower upper] demand "
87 "interval per station");
88 const std::size_t M = L.rows();
89 for (std::size_t i = 0; i < M; ++i) {
90 if (L(i, 0) < zero || L(i, 1) < zero)
91 throw InputError("pfqn_mva_interval: demands must be nonnegative");
92 if (L(i, 0) > L(i, 1))
93 throw InputError(
94 "pfqn_mva_interval: an interval lower endpoint exceeds its upper endpoint");
95 }
96 if (zlo < zero || zup < zero)
97 throw InputError("pfqn_mva_interval: think times must be nonnegative");
98 if (zlo > zup)
99 throw InputError("pfqn_mva_interval: the think-time interval is inverted");
100 if (nlo > nup) throw InputError("pfqn_mva_interval: the population interval is inverted");
101 if (nlo < 1)
102 throw InputError(
103 "pfqn_mva_interval requires a population interval with at least one job; the "
104 "monotonicity theorems assume n >= 1");
105
107 std::vector<bool> thick(M, false);
108 Matrix<T> Llo(M, 1, zero), Lup(M, 1, zero);
109 for (std::size_t i = 0; i < M; ++i) {
110 Llo(i, 0) = L(i, 0);
111 Lup(i, 0) = L(i, 1);
112 thick[i] = L(i, 1) > T(L(i, 0) + tiny);
113 }
114
115 auto call = [&](const Matrix<T>& d, int n, const T& z) {
116 return pfqn_mva(d, std::vector<int>(1, n), Matrix<T>(1, 1, z));
117 };
118
120 out.Q = Matrix<T>(M, 2, zero);
121 out.U = Matrix<T>(M, 2, zero);
122 out.R = Matrix<T>(M, 2, zero);
123
124 // S1: the throughput upper bound, and the upper bounds of the stations whose
125 // demand is thin -- their own demand is fixed, so lowering the others
126 // maximizes them.
127 const MvaResult<T> s1 = call(Llo, nup, zlo);
128 out.Xup = s1.XN[0];
129 // S2: the same quantities at the opposite corner, giving the lower bounds.
130 const MvaResult<T> s2 = call(Lup, nlo, zup);
131 out.Xlo = s2.XN[0];
132 for (std::size_t i = 0; i < M; ++i) {
133 if (thick[i]) continue;
134 out.Q(i, 1) = s1.QN(i, 0);
135 out.R(i, 1) = s1.CN(i, 0);
136 out.Q(i, 0) = s2.QN(i, 0);
137 out.R(i, 0) = s2.CN(i, 0);
138 }
139
140 // S3/S4: the totals increase in the demands and the population and decrease
141 // in the think time, so their corners differ from the throughput's.
142 const MvaResult<T> s3 = call(Llo, nlo, zup);
143 const MvaResult<T> s4 = call(Lup, nup, zlo);
144 out.Rtot_lo = zero;
145 out.Qtot_lo = zero;
146 out.Rtot_up = zero;
147 out.Qtot_up = zero;
148 for (std::size_t i = 0; i < M; ++i) {
149 out.Rtot_lo += s3.CN(i, 0);
150 out.Qtot_lo += s3.QN(i, 0);
151 out.Rtot_up += s4.CN(i, 0);
152 out.Qtot_up += s4.QN(i, 0);
153 }
154
155 // S5/S6: one pair of calls per thick station, its own demand at the endpoint
156 // that maximizes (minimizes) it and the others at the opposite endpoint.
157 for (std::size_t k = 0; k < M; ++k) {
158 if (!thick[k]) continue;
159 Matrix<T> d = Llo;
160 d(k, 0) = Lup(k, 0);
161 const MvaResult<T> s5 = call(d, nup, zlo);
162 out.Q(k, 1) = s5.QN(k, 0);
163 out.R(k, 1) = s5.CN(k, 0);
164 d = Lup;
165 d(k, 0) = Llo(k, 0);
166 const MvaResult<T> s6 = call(d, nlo, zup);
167 out.Q(k, 0) = s6.QN(k, 0);
168 out.R(k, 0) = s6.CN(k, 0);
169 }
170
171 // U = X D is not covered by the monotonicity table, so it is enclosed by the
172 // product of the two intervals, capped at the range of a single-server
173 // utilization. Where the demand is thin the product is already exact.
174 for (std::size_t i = 0; i < M; ++i) {
175 out.U(i, 0) = T(out.Xlo * Llo(i, 0));
176 const T up = T(out.Xup * Lup(i, 0));
177 out.U(i, 1) = up < one ? up : one;
178 }
179 return out;
180}
181
182} // namespace pfqn
183} // namespace line
184
185#endif // LINE_API_PFQN_PFQN_MVA_INTERVAL_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
bool empty() const
Definition matrix.h:92
The exception types the port throws.
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
Dense matrix and non-owning view.
MvaIntervalResult< T > pfqn_mva_interval(const Matrix< T > &L, int nlo, int nup, const T &zlo, const T &zup)
Exact interval-valued MVA for single-class closed product-form networks.
MvaResult< T > pfqn_mva(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const std::vector< int > &mi)
Exact Mean Value Analysis for closed product-form networks (Reiser and Lavenberg 1980).
Definition pfqn_mva.h:71
Number-type abstraction for the templated API port.
Exact Mean Value Analysis for closed product-form networks (Reiser and Lavenberg 1980).
static constexpr double Zero
Definition lang_types.h:670
Every output of pfqn_mva_interval, each a [lower, upper] pair.
Matrix< T > R
(M x 2) residence time per station
Matrix< T > Q
(M x 2) mean queue length per station
Matrix< T > U
(M x 2) utilization enclosure per station
T Qtot_up
total number of jobs at the stations
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
Matrix< T > CN
(M x R) residence time
Definition pfqn_mva.h:48