LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
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
47
#include "
line/api/qsys/qsys_types.h
"
48
#include "
line/num/number.h
"
49
#include "
line/util/error.h
"
50
51
namespace
line
{
52
namespace
qsys
{
53
54
template
<
class
T>
55
struct
Mg1DisciplineResult
{
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
60
namespace
detail {
61
62
/** Shared argument validation for the M/G/1 discipline family. */
63
template
<
class
T>
64
T 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. */
83
template
<
class
T>
84
T mg1_discipline_rhohat(
const
std::vector<T>& lambda,
const
std::vector<T>& W) {
85
T Q =
num_traits<T>::from_int
(0);
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
*/
100
template
<
class
T>
101
Mg1DisciplineResult<T>
qsys_mg1_setf
(
const
std::vector<T>& lambda,
const
std::vector<T>& mu,
102
const
std::vector<T>& cs) {
103
static_assert
(
num_traits<T>::has_transcendental
,
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
122
Mg1DisciplineResult<T>
r;
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
line::InputError::InputError
InputError(const std::string &what)
Definition
error.h:39
line::NumericError::NumericError
NumericError(const std::string &what)
Definition
error.h:45
error.h
The exception types the port throws.
line::qsys
Definition
qsys_bmapm1.h:58
line::qsys::qsys_mg1_setf
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.
Definition
qsys_mg1_setf.h:101
line
Definition
aoi_dist2ph.h:52
number.h
Number-type abstraction for the templated API port.
qsys_types.h
Shared return type and arithmetic helpers for the templated qsys port.
line::num_traits
Definition
number.h:111
line::qsys::Mg1DisciplineResult
Definition
qsys_mg1_setf.h:55
line::qsys::Mg1DisciplineResult::W
std::vector< T > W
per-class mean response time
Definition
qsys_mg1_setf.h:56
line::qsys::Mg1DisciplineResult::rhohat
T rhohat
Q/(1+Q) with Q = sum_k lambda_k W_k.
Definition
qsys_mg1_setf.h:57
include
line
api
qsys
qsys_mg1_setf.h
Generated by
1.18.0