LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
dqsys_bernoulli1.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_DQSYS_DQSYS_BERNOULLI1_H
6#define LINE_API_DQSYS_DQSYS_BERNOULLI1_H
7
8/**
9 * @file
10 * @ingroup api_dqsys
11 * State dependent Bernoulli server on a discrete time scale.
12 *
13 * Templated port of matlab/src/api/dqsys/dqsys_bernoulli1.m. Time advances in
14 * slots. In the slot starting at t with n jobs present the job in service
15 * departs with probability p(n) and an arrival occurs with probability b(n),
16 * independently; both are recorded at the end of the slot with the departure
17 * resolved first (Daduna's LA rule and D/A rule). The queue length at slot
18 * boundaries is a discrete birth-death chain with
19 *
20 * pi(n) = [prod_{m=0}^{n-1} b(m) / prod_{m=0}^{n} c(m)]
21 * * [prod_{m=1}^{n-1} q(m) / prod_{m=1}^{n} p(m)] / H,
22 *
23 * c = 1-b and q = 1-p, which is theorem 2.3 of Daduna (2001), and corollary 2.8
24 * once b(n) = 0 above the capacity. For constant b and p it collapses to the
25 * Geo/Geo/1 law of `dqsys_geogeo1` under the LAS_DA convention.
26 *
27 * The law seen by an arriving customer, with himself not counted, is theorem
28 * 2.11 and is returned in `arrivalPmf`. It is not the time-stationary law:
29 * discrete time has no PASTA analogue, and the two differ even when the arrival
30 * stream is a state independent Bernoulli process. In that state independent
31 * case pi_1 is exactly the EAS-convention queue length law of `dqsys_geogeo1`,
32 * geometric with ratio r = b(1-p)/(p(1-b)).
33 *
34 * Both laws are built by their exact product recurrences rather than in log
35 * space, so the exact instantiation returns them with no rounding.
36 */
37
38#include <cmath>
39#include <cstddef>
40#include <vector>
41
42#include "line/num/number.h"
43#include "line/util/error.h"
44
45namespace line {
46namespace dqsys {
47
48/** Steady-state quantities of a finite-buffer Bernoulli server. */
49template <class T>
51 std::size_t capacity = 0; ///< buffer capacity in jobs
52 std::vector<T> arrivalProb; ///< offered b(n), n = 0..L
53 std::vector<T> serviceProb; ///< p(n), indexed by n-1
54 std::vector<T> pmf; ///< time-stationary law, theorem 2.3
55 std::vector<T> arrivalPmf; ///< arrival law, theorem 2.11
57 T utilization; ///< 1 - pi(0)
58 T throughput; ///< carried departures per slot
59 T lossProb; ///< fraction of offered arrivals lost
62 T meanSojournTime; ///< in slots, by Little's law
63 T meanWaitingTime; ///< in slots
64 T normConst; ///< H of theorem 2.3
65};
66
67/**
68 * Finite buffer of L jobs. An arrival in a slot that finds L jobs present is
69 * lost, which is the loss system of corollary 2.8.
70 *
71 * @param b offered arrival probabilities b(n) for n = 0..L, or one entry for a
72 * state independent stream
73 * @param p service probabilities p(n) for n = 1..L, or one entry for a state
74 * independent server
75 * @param L buffer capacity in jobs
76 */
77template <class T>
78Bernoulli1Result<T> dqsys_bernoulli1(const std::vector<T>& b, const std::vector<T>& p,
79 std::size_t L) {
80 if (L < 1) {
81 throw InputError("dqsys_bernoulli1: L must be a positive integer");
82 }
83 const T zero = num_traits<T>::from_int(0);
84 const T one = num_traits<T>::from_int(1);
85
86 std::vector<T> boff(L + 1);
87 if (b.size() == 1) {
88 for (std::size_t n = 0; n <= L; ++n) boff[n] = b[0];
89 } else if (b.size() == L + 1) {
90 boff = b;
91 } else {
92 throw InputError("dqsys_bernoulli1: the arrival probability vector must have one entry "
93 "per state 0..L");
94 }
95 std::vector<T> pv(L);
96 if (p.size() == 1) {
97 for (std::size_t n = 0; n < L; ++n) pv[n] = p[0];
98 } else if (p.size() == L) {
99 pv = p;
100 } else if (p.size() == L + 1) {
101 // A vector of length L+1 is accepted with its first entry, which would
102 // be p(0), ignored.
103 pv.assign(p.begin() + 1, p.end());
104 } else {
105 throw InputError("dqsys_bernoulli1: the service probability vector must have L or L+1 "
106 "entries");
107 }
108 for (std::size_t n = 0; n <= L; ++n) {
109 if (boff[n] < zero || boff[n] > one) {
110 throw InputError("dqsys_bernoulli1: arrival probabilities must lie in [0,1]");
111 }
112 }
113 for (std::size_t n = 0; n < L; ++n) {
114 if (!(pv[n] > zero) || pv[n] > one) {
115 throw InputError("dqsys_bernoulli1: service probabilities must lie in (0,1]");
116 }
117 }
118 std::vector<T> badm = boff;
119 badm[L] = zero; // an arrival finding L jobs is lost
120 for (std::size_t n = 0; n < L; ++n) {
121 if (!(badm[n] < one)) {
122 // c(n)=0 makes the weight of theorem 2.3 diverge at n; p(n)=1 is
123 // fine and truncates the chain instead, which is example 2.9.
124 throw InputError("dqsys_bernoulli1: arrival probabilities below the capacity must be "
125 "strictly less than one");
126 }
127 }
128
129 // Theorem 2.3 by its exact recurrence, u(0) = 1/c(0) and
130 // u(n) = u(n-1) b(n-1) q(n-1) / (c(n) p(n)) with q(0) := 1.
131 std::vector<T> u(L + 1, zero);
132 u[0] = one / (one - badm[0]);
133 for (std::size_t n = 1; n <= L; ++n) {
134 const T q = (n >= 2) ? (one - pv[n - 2]) : one;
135 u[n] = u[n - 1] * badm[n - 1] * q / ((one - badm[n]) * pv[n - 1]);
136 }
137 T H = zero;
138 for (std::size_t n = 0; n <= L; ++n) H = H + u[n];
139
141 r.capacity = L;
142 r.arrivalProb = boff;
143 r.serviceProb = pv;
144 r.pmf.resize(L + 1);
145 for (std::size_t n = 0; n <= L; ++n) r.pmf[n] = u[n] / H;
146
147 // Theorem 2.11 by its exact recurrence, v(0) = b(0)/(c(0) c(1)) and
148 // v(n) = v(n-1) b(n) q(n) / (c(n+1) p(n)).
149 std::vector<T> v(L, zero);
150 v[0] = badm[0] / ((one - badm[0]) * (one - badm[1]));
151 for (std::size_t n = 1; n < L; ++n) {
152 v[n] = v[n - 1] * badm[n] * (one - pv[n - 1]) / ((one - badm[n + 1]) * pv[n - 1]);
153 }
154 T Ha = zero;
155 for (std::size_t n = 0; n < L; ++n) Ha = Ha + v[n];
156 r.arrivalPmf.resize(L);
157 if (Ha > zero) {
158 for (std::size_t n = 0; n < L; ++n) r.arrivalPmf[n] = v[n] / Ha;
159 }
160
161 r.emptyProb = r.pmf[0];
162 r.utilization = one - r.pmf[0];
163 T q = zero, t = zero, offered = zero;
164 for (std::size_t n = 0; n <= L; ++n) {
165 q = q + r.pmf[n] * num_traits<T>::from_int(static_cast<long>(n));
166 offered = offered + r.pmf[n] * boff[n];
167 if (n >= 1) t = t + r.pmf[n] * pv[n - 1];
168 }
169 r.meanQueueLength = q;
170 r.throughput = t;
171 r.lossProb = (offered > zero) ? (r.pmf[L] * boff[L] / offered) : zero;
173 r.meanSojournTime = (t > zero) ? (q / t) : zero;
174 r.meanWaitingTime = (t > zero) ? (r.meanWaitingQueue / t) : zero;
175 r.normConst = H;
176 return r;
177}
178
179} // namespace dqsys
180} // namespace line
181
182#endif // LINE_API_DQSYS_DQSYS_BERNOULLI1_H
InputError(const std::string &what)
Definition error.h:39
The exception types the port throws.
Bernoulli1Result< T > dqsys_bernoulli1(const std::vector< T > &b, const std::vector< T > &p, std::size_t L)
Finite buffer of L jobs.
Number-type abstraction for the templated API port.
Steady-state quantities of a finite-buffer Bernoulli server.
T throughput
carried departures per slot
std::vector< T > serviceProb
p(n), indexed by n-1
std::vector< T > pmf
time-stationary law, theorem 2.3
T meanSojournTime
in slots, by Little's law
std::vector< T > arrivalPmf
arrival law, theorem 2.11
std::size_t capacity
buffer capacity in jobs
std::vector< T > arrivalProb
offered b(n), n = 0..L
T lossProb
fraction of offered arrivals lost