LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
qsys_mg1_setf.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_QSYS_QSYS_MG1_SETF_H
6#define LINE_API_QSYS_QSYS_MG1_SETF_H
7
8/**
9 * @file
10 * @ingroup api_qsys
11 * M/G/1 under SETF (shortest elapsed time first), the non-preemptive
12 * counterpart of FB/LAS.
13 *
14 * Templated port of matlab/src/api/qsys/qsys_mg1_setf.m, cross-checked against
15 * jar/src/main/java/jline/api/qsys/Qsys_mg1_setf.java.
16 *
17 * Each class is represented by the single job size x_k = 1/mu_k and evaluated
18 * at that size:
19 *
20 * rho_x = sum_i lambda_i int_0^x Fbar_i(t) dt
21 * num(x) = sum_i lambda_i int_0^x t Fbar_i(t) dt
22 * E[R] = sum_i (lambda_i/lambda) (1+cs_i^2)/(2 mu_i)
23 * W_k = num(x)/(1-rho_x)^2 + x/(1-rho_x) + E[R]/(1-rho_x)
24 *
25 * the last term being the non-preemptive penalty that separates SETF from FB.
26 * For an exponential class the two truncated integrals are closed forms; for
27 * any other class MATLAB substitutes the bounded surrogates min(x, 1/mu_i) and
28 * min(x^2/2, 1/mu_i^2), which is an approximation and is reproduced verbatim
29 * here rather than improved on.
30 *
31 * Note the branch test: MATLAB compares cs(i) == 1 exactly in this file, not
32 * within a tolerance as its FB and PSJF siblings do, so a cs of 1 - 1e-12
33 * takes the surrogate branch. That asymmetry is part of the reference
34 * behaviour and is preserved.
35 *
36 * ARITHMETIC. exp appears in the exponential branch, so the function is gated
37 * on transcendental arithmetic.
38 *
39 * The returned rhohat is Q/(1+Q) with Q = sum_k lambda_k W_k, the qsys family
40 * convention; the utilization sum_k rho_k is not part of the return value
41 * because MATLAB overwrites it.
42 */
43
44#include <cstddef>
45#include <vector>
46
48#include "line/num/number.h"
49#include "line/util/error.h"
50
51namespace line {
52namespace qsys {
53
54template <class T>
56 std::vector<T> W; ///< per-class mean response time
57 T rhohat; ///< Q/(1+Q) with Q = sum_k lambda_k W_k
58};
59
60namespace detail {
61
62/** Shared argument validation for the M/G/1 discipline family. */
63template <class T>
64T mg1_discipline_check(const char* fn, const std::vector<T>& lambda, const std::vector<T>& mu,
65 const std::vector<T>& cs) {
66 const std::size_t K = lambda.size();
67 if (mu.size() != K || cs.size() != K)
68 throw InputError(std::string(fn) + ": lambda, mu and cs must have the same length");
69 if (K == 0) throw InputError(std::string(fn) + ": at least one class is required");
70 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
71 T rho = zero;
72 for (std::size_t i = 0; i < K; ++i) {
73 if (lambda[i] <= zero || mu[i] <= zero)
74 throw InputError(std::string(fn) + ": lambda and mu must be positive");
75 if (cs[i] < zero) throw InputError(std::string(fn) + ": cs must be non-negative");
76 rho += lambda[i] / mu[i];
77 }
78 if (rho >= one) throw InputError(std::string(fn) + ": system is unstable, rho >= 1");
79 return rho;
80}
81
82/** rhohat = Q/(1+Q) with Q = sum_k lambda_k W_k. */
83template <class T>
84T mg1_discipline_rhohat(const std::vector<T>& lambda, const std::vector<T>& W) {
86 for (std::size_t k = 0; k < W.size(); ++k) Q += lambda[k] * W[k];
87 return Q / (num_traits<T>::from_int(1) + Q);
88}
89
90} // namespace detail
91
92/**
93 * @brief M/G/1 under SETF (shortest elapsed time first), the non-preemptive
94 * counterpart of FB/LAS.
95 *
96 * @param lambda per-class arrival rates
97 * @param mu per-class service rates
98 * @param cs per-class coefficients of variation of the service time
99 */
100template <class T>
101Mg1DisciplineResult<T> qsys_mg1_setf(const std::vector<T>& lambda, const std::vector<T>& mu,
102 const std::vector<T>& cs) {
104 "qsys_mg1_setf requires transcendental arithmetic");
105 detail::mg1_discipline_check("qsys_mg1_setf", lambda, mu, cs);
106 const std::size_t K = lambda.size();
107 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
108 const T two = num_traits<T>::from_int(2);
109
110 T lambda_total = zero;
111 for (const T& v : lambda) lambda_total += v;
112
113 // Mean residual service time of the mixture.
114 T E_R = zero;
115 for (std::size_t i = 0; i < K; ++i) {
116 const T p_i = lambda[i] / lambda_total;
117 const T E_S_i = one / mu[i];
118 const T E_S2_i = (one + cs[i] * cs[i]) / (mu[i] * mu[i]);
119 E_R += p_i * E_S2_i / (two * E_S_i);
120 }
121
123 r.W.assign(K, zero);
124 for (std::size_t k = 0; k < K; ++k) {
125 const T x = one / mu[k];
126 T rho_x = zero, numer = zero;
127 for (std::size_t i = 0; i < K; ++i) {
128 T int_Fbar, int_tFbar;
129 if (cs[i] == one) {
130 const T e = detail::num_exp(T(-mu[i] * x));
131 int_Fbar = (one - e) / mu[i];
132 int_tFbar = (one - e * (one + mu[i] * x)) / (mu[i] * mu[i]);
133 } else {
134 int_Fbar = detail::num_min(x, T(one / mu[i]));
135 int_tFbar = detail::num_min(T(x * x / two), T(one / (mu[i] * mu[i])));
136 }
137 rho_x += lambda[i] * int_Fbar;
138 numer += lambda[i] * int_tFbar;
139 }
140 if (rho_x >= one)
141 throw NumericError("qsys_mg1_setf: truncated load reaches one, the mean is infinite");
142 r.W[k] = numer / ((one - rho_x) * (one - rho_x)) + x / (one - rho_x) + E_R / (one - rho_x);
143 }
144 r.rhohat = detail::mg1_discipline_rhohat(lambda, r.W);
145 return r;
146}
147
148} // namespace qsys
149} // namespace line
150
151#endif // LINE_API_QSYS_QSYS_MG1_SETF_H
InputError(const std::string &what)
Definition error.h:39
NumericError(const std::string &what)
Definition error.h:45
The exception types the port throws.
Mg1DisciplineResult< T > qsys_mg1_setf(const std::vector< T > &lambda, const std::vector< T > &mu, const std::vector< T > &cs)
M/G/1 under SETF (shortest elapsed time first), the non-preemptive counterpart of FB/LAS.
Number-type abstraction for the templated API port.
Shared return type and arithmetic helpers for the templated qsys port.
std::vector< T > W
per-class mean response time
T rhohat
Q/(1+Q) with Q = sum_k lambda_k W_k.