5#ifndef LINE_API_PFQN_EGFLINEARIZER_H
6#define LINE_API_PFQN_EGFLINEARIZER_H
108T num_pow_real(
const T& base,
const T& e) {
111 const T r = pow(base, e);
115 const double er = std::floor(ed + 0.5);
117 return num_pow_int(base,
static_cast<unsigned>(er));
119 "pfqn_egflinearizer: exact arithmetic cannot evaluate a non-integer real power; the "
121 ", which is not an integer, so this population needs the double or real backend");
130std::vector<Matrix<T>> egflin_estimate(std::size_t M, std::size_t R,
const std::vector<int>& N_1,
132 const std::vector<T>& alpha) {
134 std::vector<Matrix<T>> Q1(R + 1,
Matrix<T>(M, R, zero));
135 for (std::size_t i = 0; i < M; ++i)
136 for (std::size_t r = 0; r < R; ++r)
137 for (std::size_t s = 1; s <= R; ++s) {
138 const std::vector<int> Ns =
oner(N_1, s);
140 if (N_1[r] <= 0 || Ns[r] <= 0) {
143 const T na = num_pow_real(num_traits<T>::from_int(Ns[r]), alpha[r]);
144 const T n1a = num_pow_real(num_traits<T>::from_int(N_1[r]), alpha[r]);
145 Q1[s](i, r) = na * (Q(i, r) / n1a + Delta[r](i, s - 1));
153void egflin_forward_mva(
const Matrix<T>& L, std::size_t M, std::size_t R,
154 const std::vector<int>& N_1,
const std::vector<T>& Z,
155 const std::vector<Matrix<T>>& Q1, Matrix<T>& Q, Matrix<T>& W,
157 const T zero = num_traits<T>::from_int(0);
158 const T one = num_traits<T>::from_int(1);
159 for (std::size_t i = 0; i < M; ++i)
160 for (std::size_t r = 0; r < R; ++r) {
162 for (std::size_t s = 0; s < R; ++s) acc += Q1[r + 1](i, s);
163 W(i, r) = L(i, r) * acc;
165 for (std::size_t r = 0; r < R; ++r) {
167 for (std::size_t i = 0; i < M; ++i) den += W(i, r);
171 if (den == zero)
throw NumericError(
"pfqn_egflinearizer: zero total residence time");
172 X[r] = num_traits<T>::from_int(N_1[r]) / den;
174 for (std::size_t i = 0; i < M; ++i) Q(i, r) = X[r] * W(i, r);
180int egflin_core(
const Matrix<T>& L, std::size_t M, std::size_t R,
const std::vector<int>& N_1,
181 const std::vector<T>& Z, Matrix<T>& Q,
const std::vector<Matrix<T>>& Delta,
182 const std::vector<T>& alpha,
double tol,
int maxiter, Matrix<T>& W,
183 std::vector<T>& X,
bool cntest =
false) {
192 const Matrix<T> Qlast = Q;
193 const std::vector<Matrix<T>> Q1 = egflin_estimate(M, R, N_1, Q, Delta, alpha);
194 egflin_forward_mva(L, M, R, N_1, Z, Q1, Q, W, X);
200 for (std::size_t i = 0; i < M; ++i)
201 for (std::size_t r = 0; r < R; ++r) {
202 if (N_1[r] <= 0)
continue;
203 const double d = std::fabs(num_traits<T>::to_double(T(Q(i, r) - Qlast(i, r)))) /
204 static_cast<double>(N_1[r]);
210 const bool done = e < tol || iter > maxiter;
243 const std::vector<SchedStrategy>& type,
double tol,
244 int maxiter,
const std::vector<T>& alpha,
248 const std::size_t M = L.
rows();
249 const std::size_t R = N.size();
252 "pfqn_egflinearizer: demand matrix and population vector disagree on the class count");
253 if (alpha.size() != R)
254 throw InputError(
"pfqn_egflinearizer: alpha has the wrong class count");
255 if (!type.empty() && type.size() != M)
256 throw InputError(
"pfqn_egflinearizer: scheduling vector has the wrong station count");
262 if (!cntest && tol <= 0)
263 throw InputError(
"pfqn_egflinearizer: tolerance must be positive");
265 if (v < 0)
throw InputError(
"pfqn_egflinearizer: negative population");
268 const std::vector<T> Zs =
sum_rows(Z, R);
274 res.
C.assign(R, zero);
275 res.
X.assign(R, zero);
279 bool anyDemand =
false;
280 for (std::size_t i = 0; i < M; ++i)
281 for (std::size_t r = 0; r < R; ++r)
282 if (L(i, r) != zero) anyDemand =
true;
283 if (M == 0 || !anyDemand) {
284 for (std::size_t r = 0; r < R; ++r) {
285 if (N[r] == 0)
continue;
288 "pfqn_egflinearizer: a class has neither demand nor think time");
290 for (std::size_t i = 0; i < M; ++i) res.
U(i, r) = res.
X[r] * L(i, r);
296 std::vector<Matrix<T>> Q(R + 1,
Matrix<T>(M, R, zero));
297 for (std::size_t s = 0; s <= R; ++s) {
298 const std::vector<int> N_1 =
oner(N, s);
299 bool feasible =
true;
301 if (v < 0) feasible =
false;
302 if (!feasible)
continue;
303 std::vector<T> Nt(R, zero);
304 for (std::size_t r = 0; r < R; ++r) Nt[r] = num_traits<T>::from_int(N_1[r]);
311 std::vector<Matrix<T>> Delta(R,
Matrix<T>(M, R, zero));
314 std::vector<T> X(R, zero);
315 for (
int I = 0; I < npasses; ++I) {
316 for (std::size_t s = 0; s <= R; ++s) {
317 const std::vector<int> N_1 =
oner(N, s);
318 bool feasible =
true;
320 if (v < 0) feasible =
false;
321 if (!feasible)
continue;
322 res.
totiter += detail::egflin_core(L, M, R, N_1, Zs, Q[s], Delta, alpha, tol,
323 maxiter - res.
totiter, W, X, cntest);
325 for (std::size_t i = 0; i < M; ++i)
326 for (std::size_t r = 0; r < R; ++r) {
329 for (std::size_t s = 0; s < R; ++s) Delta[r](i, s) = zero;
334 Q[r + 1](i, r) = zero;
337 for (std::size_t s = 1; s <= R; ++s) {
338 const std::vector<int> Ns =
oner(N, s);
342 Delta[r](i, s - 1) = Q[s](i, r) / nsa - Q[0](i, r) / nra;
345 Delta[r](i, s - 1) = -Q[0](i, r) / nra;
351 res.
totiter += detail::egflin_core(L, M, R, N, Zs, Q[0], Delta, alpha, tol,
352 maxiter - res.
totiter, W, X, cntest);
356 for (std::size_t i = 0; i < M; ++i)
357 for (std::size_t r = 0; r < R; ++r) res.
U(i, r) = X[r] * L(i, r);
358 for (std::size_t r = 0; r < R; ++r)
366 const Matrix<T>& Z,
const std::vector<T>& alpha) {
NumericError(const std::string &what)
UnsupportedError(const std::string &what)
The exception types the port throws.
Dense matrix and non-owning view.
std::vector< T > sum_rows(const Matrix< T > &Z, std::size_t R)
Sum the rows of a think-time matrix into a length-R vector, the sum(Z,1) that every AMVA entry point ...
std::vector< int > oner(const std::vector< int > &N, std::size_t r)
matlab/src/util/oner.m: decrement position r of N, with r given 1-based and r == 0 meaning "leave N a...
AmvaResult< T > pfqn_bs(const Matrix< T > &L, const std::vector< T > &N, const std::vector< T > &Z, const std::vector< AmvaSched > &type, double tol=1e-6, std::size_t maxiter=1000, const Matrix< T > &QN0=Matrix< T >())
Bard-Schweitzer approximate MVA.
LinearizerResult< T > pfqn_egflinearizer(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const std::vector< SchedStrategy > &type, double tol, int maxiter, const std::vector< T > &alpha, const Matrix< T > &QN0, int npasses=3)
Extended generalized fixed-point Linearizer (De Souza e Silva and Muntz's generalization of Chandy an...
bool is_cntol(double tol)
True when tol is the sentinel requesting the Chandy-Neuse test.
double pfqn_cntol(const std::vector< T > &N)
Termination cutoff at the given population vector.
double enorm_diff(const Matrix< T > &A, const Matrix< T > &B)
Frobenius norm of the difference of two equally shaped matrices, AS A DOUBLE.
T num_pow_int(const T &base, unsigned e)
Integer power, valid in any field (no transcendental requirement).
Number-type abstraction for the templated API port.
Scaffolding shared by the approximate-MVA family.
Bard-Schweitzer approximate MVA.
Chandy-Neuse population-scaled termination cutoff for approximate MVA.
Matrix< T > QN
(M x R) queue length
Return value of the Linearizer family, mirroring [Q,U,W,C,X,totiter].
Matrix< T > U
(M x R) utilization
std::vector< T > C
(R) cycle time, N_r/X_r - Z_r
std::vector< T > X
(R) per-class throughput
Matrix< T > W
(M x R) per-station residence time
Matrix< T > Q
(M x R) mean queue length
int totiter
total inner iterations across all Core calls