LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
Loading...
Searching...
No Matches
qsys_mdc_crommelin.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_MDC_CROMMELIN_H
6
#define LINE_API_QSYS_QSYS_MDC_CROMMELIN_H
7
8
/**
9
* @file
10
* @ingroup api_qsys
11
* M/D/c by Crommelin's embedded chain.
12
*
13
* Templated port of jar/src/main/java/jline/api/qsys/Qsys_mdc_crommelin.java and
14
* the native Python `qsys_mdc_crommelin`; MATLAB carries the method under
15
* `qsys_dmc.m`'s "see also" rather than as its own file.
16
*
17
* The chain is embedded at multiples of the deterministic service time s:
18
* X_{n+1} = max(0, X_n - c) + A_n, A_n ~ Poisson(lambda s),
19
* because in one service period exactly min(X_n, c) jobs complete and the
20
* arrivals in that period are Poisson. The embedded epochs are Poisson arrival
21
* epochs, so PASTA makes the embedded law the time-average law, and the result
22
* is EXACT for M/D/c under FCFS, not an approximation.
23
*
24
* THE TRUNCATION IS THE ONLY ERROR. The default level is
25
* max(200, min(2500, 10/(1-rho) + 200)), which the references settled on
26
* empirically at six digits for moderate c; the cap keeps the dense LU
27
* affordable, since the transition matrix is triangular-banded but not sparse.
28
* A caller comparing against another codebase must pass the SAME truncation to
29
* both, as the defaults are the only free parameter.
30
*
31
* The Poisson weights are formed in logs and exponentiated once, which is what
32
* keeps lambda s in the hundreds from overflowing the factorial.
33
*
34
* ARITHMETIC: transcendental, for the Poisson weights.
35
*/
36
37
#include <algorithm>
38
#include <cmath>
39
#include <cstddef>
40
#include <vector>
41
42
#include "
line/api/qsys/qsys_types.h
"
43
#include "
line/num/number.h
"
44
#include "
line/util/error.h
"
45
#include "
line/util/lu.h
"
46
#include "
line/util/matrix.h
"
47
48
namespace
line
{
49
namespace
qsys
{
50
51
template
<
class
T>
52
struct
MDcCrommelinResult
{
53
T
meanQueueLength
;
///< E[N], jobs in system
54
T
meanWaitingQueue
;
///< Lq = E[(N-c)+]
55
T
meanWaitingTime
;
///< Wq = Lq/lambda
56
T
meanSojournTime
;
///< W = Wq + s
57
T
utilization
;
///< rho = lambda s / c
58
};
59
60
/**
61
* @brief M/D/c by Crommelin's embedded chain.
62
*
63
* @param lambda_arr Poisson arrival rate
64
* @param s deterministic service time
65
* @param c number of servers
66
* @param truncation state-space cap; <= 0 selects the reference's automatic level
67
*/
68
template
<
class
T>
69
MDcCrommelinResult<T>
qsys_mdc_crommelin
(
const
T& lambda_arr,
const
T& s,
unsigned
c,
70
long
truncation = -1) {
71
static_assert
(
num_traits<T>::has_transcendental
,
72
"qsys_mdc_crommelin forms Poisson weights in logs"
);
73
const
double
lam =
num_traits<T>::to_double
(lambda_arr);
74
const
double
sv =
num_traits<T>::to_double
(s);
75
if
(!(lam > 0.0))
throw
InputError
(
"qsys_mdc_crommelin: the arrival rate must be positive"
);
76
if
(!(sv > 0.0))
throw
InputError
(
"qsys_mdc_crommelin: the service time must be positive"
);
77
if
(c < 1)
throw
InputError
(
"qsys_mdc_crommelin: the number of servers must be at least one"
);
78
79
const
double
a = lam * sv;
80
const
double
rho = a /
static_cast<
double
>
(c);
81
if
(!(rho < 1.0 - 1e-12))
82
throw
InputError
(
"qsys_mdc_crommelin: the load must be strictly below one"
);
83
84
const
long
autoN = std::max(
85
200L, std::min(2500L,
static_cast<
long
>
(10.0 / (1.0 - rho)) + 200L));
86
const
std::size_t nMax =
static_cast<
std::size_t
>
(truncation > 0 ? truncation : autoN);
87
const
std::size_t n = nMax + 1;
88
89
// Poisson(a) weights in logs, so a in the hundreds does not overflow.
90
std::vector<double> pmf(n);
91
const
double
logA = std::log(a);
92
double
logFact = 0.0;
93
for
(std::size_t k = 0; k < n; ++k) {
94
pmf[k] = std::exp(-a +
static_cast<
double
>
(k) * logA - logFact);
95
logFact += std::log(
static_cast<
double
>
(k + 1));
96
}
97
98
// (P' - I) with the last row replaced by the normalization sum(pi) = 1.
99
// P(i,j) = pmf[j - max(0, i - c)] for j >= max(0, i - c), zero otherwise.
100
const
T zero =
num_traits<T>::from_int
(0), one =
num_traits<T>::from_int
(1);
101
Matrix<T>
A(n, n, zero);
102
for
(std::size_t i = 0; i < n; ++i) {
103
const
std::size_t base = (i <= c) ? 0 : i - c;
104
for
(std::size_t j = base; j < n; ++j)
105
A(j, i) =
num_traits<T>::from_double
(pmf[j - base]);
106
}
107
for
(std::size_t i = 0; i < n; ++i) A(i, i) = T(A(i, i) - one);
108
for
(std::size_t j = 0; j < n; ++j) A(n - 1, j) = one;
109
110
std::vector<T> rhs(n, zero);
111
rhs[n - 1] = one;
112
const
std::vector<T> pi =
solve
(A, rhs);
113
114
T meanN = zero, Lq = zero;
115
for
(std::size_t i = 0; i < n; ++i) {
116
meanN +=
num_traits<T>::from_int
(
static_cast<
long
>
(i)) * pi[i];
117
if
(i > c) Lq +=
num_traits<T>::from_int
(
static_cast<
long
>
(i - c)) * pi[i];
118
}
119
120
MDcCrommelinResult<T>
r;
121
r.
meanQueueLength
= meanN;
122
r.
meanWaitingQueue
= Lq;
123
r.
meanWaitingTime
= T(Lq / lambda_arr);
124
r.
meanSojournTime
= T(r.
meanWaitingTime
+ s);
125
r.
utilization
=
num_traits<T>::from_double
(rho);
126
return
r;
127
}
128
129
}
// namespace qsys
130
}
// namespace line
131
132
#endif
// LINE_API_QSYS_QSYS_MDC_CROMMELIN_H
line::InputError::InputError
InputError(const std::string &what)
Definition
error.h:39
line::Matrix
Definition
matrix.h:56
error.h
The exception types the port throws.
lu.h
LU factorization with partial pivoting, templated on the number type.
matrix.h
Dense matrix and non-owning view.
line::qsys
Definition
qsys_bmapm1.h:58
line::qsys::qsys_mdc_crommelin
MDcCrommelinResult< T > qsys_mdc_crommelin(const T &lambda_arr, const T &s, unsigned c, long truncation=-1)
M/D/c by Crommelin's embedded chain.
Definition
qsys_mdc_crommelin.h:69
line
Definition
aoi_dist2ph.h:52
line::solve
std::vector< T > solve(const Matrix< T > &A, const std::vector< T > &b)
Convenience: solve Ax = b, leaving A and b untouched.
Definition
lu.h:158
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::MDcCrommelinResult
Definition
qsys_mdc_crommelin.h:52
line::qsys::MDcCrommelinResult::meanWaitingQueue
T meanWaitingQueue
Lq = E[(N-c)+].
Definition
qsys_mdc_crommelin.h:54
line::qsys::MDcCrommelinResult::utilization
T utilization
rho = lambda s / c
Definition
qsys_mdc_crommelin.h:57
line::qsys::MDcCrommelinResult::meanSojournTime
T meanSojournTime
W = Wq + s.
Definition
qsys_mdc_crommelin.h:56
line::qsys::MDcCrommelinResult::meanWaitingTime
T meanWaitingTime
Wq = Lq/lambda.
Definition
qsys_mdc_crommelin.h:55
line::qsys::MDcCrommelinResult::meanQueueLength
T meanQueueLength
E[N], jobs in system.
Definition
qsys_mdc_crommelin.h:53
include
line
api
qsys
qsys_mdc_crommelin.h
Generated by
1.18.0