5#ifndef LINE_API_QSYS_QSYS_MMC_PRIO_TARDINESS_H
6#define LINE_API_QSYS_QSYS_MMC_PRIO_TARDINESS_H
46using Cplx = std::complex<double>;
50 std::transform(t.begin(), t.end(), t.begin(), [](
unsigned char ch) { return static_cast<char>(std::tolower(ch)); });
51 if (t ==
"np" || t ==
"hol" || t ==
"nonpreemptive" || t ==
"non-preemptive")
return false;
52 if (t ==
"pr" || t ==
"prpr" || t ==
"preemptive" || t ==
"fcfsprprio")
return true;
53 throw InputError(
ErrorKind(
"Argument"), std::string(fn) +
": unknown priority type '" + type +
"', use 'np' or 'pr'");
58 for (
unsigned k = 1; k <= c; ++k) B = A * B / (k + A * B);
59 return c * B / (c - A * (1 - B));
64 const double a = lb + cmu;
65 const double b = 2.0 * std::sqrt(lb * cmu);
66 return 2.0 * cmu / (s + a + std::sqrt(s + (a - b)) * std::sqrt(s + (a + b)));
81 std::vector<Cplx> diag(c), cp(c), dp(c), f(c);
82 std::vector<double> sup(c, 0.0), sub(c, 0.0);
83 for (
unsigned v = 0; v < c; ++v) {
84 diag[v] = s + (lb + (v + 1) * mu);
85 if (v + 1 < c) sup[v] = -lb;
86 if (v > 0) sub[v] = -
static_cast<double>(v) * mu;
88 diag[c - 1] = s + (lb + c * mu) - lb *
busy_lst(s, lb, c * mu);
89 cp[0] = sup[0] / diag[0];
91 for (
unsigned v = 1; v < c; ++v) {
92 const Cplx den = diag[v] - cp[v - 1] * sub[v];
94 dp[v] = (mu - dp[v - 1] * sub[v]) / den;
97 for (
int v =
static_cast<int>(c) - 2; v >= 0; --v) f[v] = dp[v] - cp[v] * f[v + 1];
102inline std::vector<double>
pr_means(
double lb,
double mu,
unsigned c) {
103 std::vector<double> diag(c), sup(c, 0.0), sub(c, 0.0), rhs(c, 1.0), cp(c), dp(c), x(c);
104 for (
unsigned v = 0; v < c; ++v) {
105 diag[v] = lb + (v + 1) * mu;
106 if (v + 1 < c) sup[v] = -lb;
107 if (v > 0) sub[v] = -
static_cast<double>(v) * mu;
109 diag[c - 1] = c * mu;
110 rhs[c - 1] = 1 + lb / (c * mu - lb);
111 cp[0] = sup[0] / diag[0];
112 dp[0] = rhs[0] / diag[0];
113 for (
unsigned v = 1; v < c; ++v) {
114 const double den = diag[v] - cp[v - 1] * sub[v];
115 cp[v] = sup[v] / den;
116 dp[v] = (rhs[v] - dp[v - 1] * sub[v]) / den;
118 x[c - 1] = dp[c - 1];
119 for (
int v =
static_cast<int>(c) - 2; v >= 0; --v) x[v] = dp[v] - cp[v] * x[v + 1];
124inline void exp_mix_tard(
double mu,
double theta,
double Pc,
double d,
double& S,
double& T,
double& P) {
125 S = 1.0 / mu + Pc / theta;
126 if (d <= 0) { T = S - d; P = 1.0;
return; }
127 if (std::isinf(d)) { T = 0.0; P = 0.0;
return; }
128 const double delta = theta - mu;
129 const double E1 = delta == 0 ? d : -std::expm1(-delta * d) / delta;
130 const double emd = std::exp(-mu * d);
131 T = (1 - Pc) * emd / mu + Pc * (emd / mu + emd * (1 + mu * E1) / theta);
132 P = (1 - Pc) * emd + Pc * emd * (1 + mu * E1);
137 if (d <= 0) { T = S - d; P = 1.0;
return; }
138 if (std::isinf(d)) { T = 0.0; P = 0.0;
return; }
141 T = std::min(std::max(T, 0.0), S);
142 P = std::min(std::max(P, 0.0), 1.0);
146inline void hypo_tard(
unsigned K,
double mu,
double theta,
double d,
double S,
double& T,
double& P) {
147 if (d <= 0) { T = S - d; P = 1.0;
return; }
148 if (std::isinf(d)) { T = 0.0; P = 0.0;
return; }
149 const double w = 1.0 - mu / theta;
150 const std::size_t M = w <= 0 ? 0 : static_cast<std::size_t>(std::ceil(std::log(1e-18) / std::log(w)));
151 const double y = theta * d;
152 const std::size_t J = K + M + 2;
153 std::vector<double> p(J), Qc(J + 1, 0.0);
154 for (std::size_t j = 0; j < J; ++j) {
155 p[j] = std::exp(-y +
static_cast<double>(j) * std::log(y) - std::lgamma(
static_cast<double>(j) + 1.0));
156 Qc[j + 1] = Qc[j] + p[j];
160 for (
unsigned j = 0; j < K; ++j) T += (K - j) * p[j];
162 for (std::size_t m = 0; m <= M; ++m) {
164 T += wm * Qc[K + m + 1];
168 T = std::min(std::max(T, 0.0), S);
169 P = std::min(std::max(P, 0.0), 1.0);
192 const std::vector<double>& d,
const std::string& type =
"np",
193 const std::string& method =
"euler") {
195 const char* fn =
"qsys_mmc_prio_tardiness";
196 const std::size_t R = lambda.size();
197 bool bad = R == 0 || !(mu > 0);
198 for (
double l : lambda)
199 if (!(l > 0)) bad =
true;
200 if (bad)
throw InputError(
ErrorKind(
"Argument"), std::string(fn) +
": lambda must be positive and mu a positive scalar");
201 if (c < 1)
throw InputError(
ErrorKind(
"Argument"), std::string(fn) +
": c must be a positive integer");
202 if (d.size() != 1 && d.size() != R)
203 throw InputError(
ErrorKind(
"Argument"), std::string(fn) +
": d must be a scalar or one due date per class");
204 std::vector<double> dv(R);
205 for (std::size_t i = 0; i < R; ++i) {
206 dv[i] = d.size() == 1 ? d[0] : d[i];
207 if (std::isnan(dv[i]))
208 throw InputError(
ErrorKind(
"Argument"), std::string(fn) +
": d must be a scalar or one due date per class");
210 const bool preemptive = is_preemptive(type, fn);
212 std::vector<double> Lam(R);
214 for (std::size_t i = 0; i < R; ++i) Lam[i] = (acc += lambda[i]);
215 const double A = Lam[R - 1] / mu;
217 const double PcAll = erlang_c(c, A);
219 out.
Tard.assign(R, 0.0);
220 out.
Ptard.assign(R, 0.0);
221 std::vector<double> S(R, 0.0);
222 const double cmu = c * mu;
223 for (std::size_t i = 0; i < R; ++i) {
224 const double lb = i == 0 ? 0.0 : Lam[i] - lambda[i];
225 const double Li = Lam[i];
228 const double a = Li / mu, rho = Li / cmu;
229 std::vector<double> pv(c + 1);
231 for (
unsigned v = 1; v <= c; ++v) pv[v] = pv[v - 1] * a / v;
232 double Z = pv[c] / (1 - rho);
233 for (
unsigned v = 0; v < c; ++v) Z += pv[v];
234 for (
double& x : pv) x /= Z;
236 exp_mix_tard(mu, cmu - Li, pv[c] / (1 - rho), dv[i], S[i], out.
Tard[i], out.
Ptard[i]);
239 const std::vector<double> Sv = pr_means(lb, mu, c);
241 for (
unsigned v = 0; v < c; ++v) s0 += pv[v] * Sv[v];
242 S[i] = s0 + pv[c] / (1 - rho) * Sv[c - 1] + pv[c] / ((cmu - lb) * (1 - rho) * (1 - rho));
243 F = [lb, mu, c, cmu, rho, pv](
Cplx s) {
244 const std::vector<Cplx> f = pr_state_lst(s, lb, mu, c);
245 const Cplx g = busy_lst(s, lb, cmu);
247 for (
unsigned v = 0; v < c; ++v) o += pv[v] * f[v];
248 return o + pv[c] * g * f[c - 1] / (1.0 - rho * g);
251 const double sig = Li / cmu;
253 exp_mix_tard(mu, cmu - Li, PcAll, dv[i], S[i], out.
Tard[i], out.
Ptard[i]);
256 S[i] = 1 / mu + PcAll / ((1 - sig) * (cmu - lb));
257 F = [lb, mu, cmu, sig, PcAll](
Cplx s) {
258 const Cplx g = busy_lst(s, lb, cmu);
259 return mu / (s + mu) * ((1 - PcAll) + PcAll * (1 - sig) * g / (1.0 - sig * g));
262 invert_tard(F, S[i], dv[i], m, out.
Tard[i], out.
Ptard[i]);
264 const std::string tstr = preemptive ?
"pr" :
"np";
265 out.
res.
set(
"meanSojournTime", S);
268 out.
res.
set(
"dueDate", dv);
269 out.
res.
analyzer =
"exact:M/M/" + std::to_string(c) +
"-prio-" + tstr +
"-tardiness";
The exception types the port throws.
Numerical inversion of a Laplace transform: Euler, Talbot, Gaver-Stehfest.
double laplace_invert(const LaplaceFn &F, double t, LaplaceMethod method=LaplaceMethod::Euler, std::size_t n=0)
Invert F at t by the named method.
LaplaceMethod
The methods laplace_invert accepts.
LaplaceMethod laplace_method(const std::string &s)
Parse the reference's method names, including its two Gaver spellings.
std::function< Cplx(Cplx)> LaplaceFn
The transform, evaluated at complex argument.
void throw_unstable(const char *fn, const char *what, double v)
A LINE:input:Unstable refusal "fn: unstable system: <what> = %g >= 1".
std::vector< Cplx > pr_state_lst(Cplx s, double lb, double mu, unsigned c)
Theorem 4 transforms f_0..f_{c-1}, by the tridiagonal (Thomas) solve.
void hypo_tard(unsigned K, double mu, double theta, double d, double S, double &T, double &P)
Tard and P of T = Exp(mu) + Erlang(K, theta) by nonnegative Poisson series.
Cplx ipow(Cplx z, unsigned k)
bool is_preemptive(const std::string &type, const char *fn)
double erlang_c(unsigned c, double A)
Cplx busy_lst(Cplx s, double lb, double cmu)
LST of the M/M/1 busy period with rates lb and cmu, conjugate form.
void exp_mix_tard(double mu, double theta, double Pc, double d, double &S, double &T, double &P)
{S, Tard, P} of T = X + B Y, X ~ Exp(mu), Y ~ Exp(theta), B ~ Bernoulli(Pc).
std::complex< double > Cplx
void invert_tard(const lti::LaplaceFn &F, double S, double d, lti::LaplaceMethod m, double &T, double &P)
Tard and P by inverting (s S - 1 + F)/s^2 and (1 - F)/s at d.
std::vector< double > pr_means(double lb, double mu, unsigned c)
Mean response times S_0..S_{c-1} of the Theorem 4 states.
MmcPrioTardinessResult qsys_mmc_prio_tardiness(const std::vector< double > &lambda, double mu, unsigned c, const std::vector< double > &d, const std::string &type="np", const std::string &method="euler")
Stationary per-class expected tardiness of a priority M/M/c queue.
QsysResT< double > QsysRes
std::complex< double > Cplx
Conservation laws of a layered queueing network, enumerated from its structure.
The res struct the exact queueing-system functions return, and the numeric helpers those functions sh...
MATLAB's [Tard, Ptard, res] outputs.
QsysRes res
meanSojournTime, tardiness, probTardy, dueDate, analyzer
std::vector< double > Ptard
per-class P(T_i > d_i)
std::vector< double > Tard
per-class E[(T_i - d_i)^+], class 1 first
void set(const std::string &name, const T &v)
Sets (or overwrites) a scalar field.