LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
dqsys_geoxgeo1.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_GEOXGEO1_H
6#define LINE_API_DQSYS_DQSYS_GEOXGEO1_H
7
8/**
9 * @file
10 * @ingroup api_dqsys
11 * Geo^X/Geo/1: the discrete-time single-server queue with batch arrivals.
12 *
13 * Templated port of matlab/src/api/qsys/dqsys_geoxgeo1.m, cross-checked against
14 * jar/src/main/java/jline/api/qsys/Qsys_geoxgeo1.java.
15 *
16 * A batch arrives in a slot with probability a and carries X >= 1 jobs; the
17 * server completes a job in a slot with probability s. With A(z) the pgf of
18 * the number of jobs arriving in one slot the slot-boundary content obeys
19 *
20 * P(z) = p_0 s (z-1) A(z) / ( z - A(z)(s + (1-s) z) ), p_0 = 1 - lambda/s
21 *
22 * and differentiating at z = 1 gives
23 *
24 * E[N] = lambda + ( a E[X(X-1)]/2 + lambda (1-s) ) / (s - lambda)
25 *
26 * so the batch law enters only through its first two factorial moments. For a
27 * geometric batch with parameter beta, E[X] = 1/beta and
28 * E[X(X-1)] = 2(1-beta)/beta^2, and at beta = 1 the result collapses onto
29 * dqsys_geogeo1.
30 *
31 * Everything is a rational function of (a, beta, s), so the exact
32 * instantiation carries no rounding. That matters in the same regime as for
33 * Geo/Geo/1: the interesting case is s - lambda small, and that is exactly
34 * where the double evaluation of the E[N] quotient loses digits.
35 *
36 * The MATLAB entry point returns the pgf as a function handle taking (z, A(z));
37 * here that is the free function dqsys_geoxgeo1_pgf, which takes the result
38 * struct and the same pair.
39 */
40
41#include <string>
42
44#include "line/num/number.h"
45#include "line/util/error.h"
46
47namespace line {
48namespace dqsys {
49
50template <class T>
54 T batchMean; ///< E[X]
56 T serviceProb; ///< s
57 T arrivalRate; ///< lambda = a E[X]
58 T throughput; ///< lambda
59 T utilization; ///< lambda/s
60 T boundaryEmptyProb; ///< 1 - lambda/s, at the slot boundary
66};
67
68/**
69 * Geo^X/Geo/1 for an arbitrary batch law given by its first two factorial
70 * moments. This is MATLAB's local dqsys_geoxgeo1_moments, exposed here because
71 * a C++ caller has no other way to reach it.
72 */
73template <class T>
74GeoXGeo1Result<T> dqsys_geoxgeo1_moments(const T& a, const T& batchMean,
75 const T& batchSecondFactorial, const T& s,
77 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
78 const T two = num_traits<T>::from_int(2);
79 if (a <= zero || a > one) throw InputError("dqsys_geoxgeo1: a must lie in (0,1]");
80 if (s <= zero || s > one) throw InputError("dqsys_geoxgeo1: s must lie in (0,1]");
81 if (batchMean < one)
82 throw InputError(
83 "dqsys_geoxgeo1: mean batch size must be at least 1: a batch that arrives carries at "
84 "least one job");
85 if (batchSecondFactorial < zero)
86 throw InputError("dqsys_geoxgeo1: E[X(X-1)] must be non-negative");
87 // factorial-moment inequality rationale: see _kb/03-api-layer.md (cpp port notes: qsys)
88 const T minSecondFactorial = batchMean * batchMean - batchMean;
90 if (batchSecondFactorial < minSecondFactorial)
91 throw InputError(
92 "dqsys_geoxgeo1: E[X(X-1)] is below E[X]^2-E[X], so the batch moments describe no "
93 "random variable");
94 } else {
95 const T slack = T(num_traits<T>::from_double(1e-9)) *
96 (minSecondFactorial > one ? minSecondFactorial : one);
97 if (batchSecondFactorial < minSecondFactorial - slack)
98 throw InputError(
99 "dqsys_geoxgeo1: E[X(X-1)] is below E[X]^2-E[X], so the batch moments describe no "
100 "random variable");
101 }
102
103 const T lambda = a * batchMean;
104 if (lambda >= s) throw InputError("dqsys_geoxgeo1: load lambda/s must be strictly less than 1");
105
107 r.convention = convention;
108 r.batchArrivalProb = a;
109 r.batchMean = batchMean;
110 r.batchSecondFactorialMoment = batchSecondFactorial;
111 r.serviceProb = s;
112 r.arrivalRate = lambda;
113 r.throughput = lambda;
114 r.utilization = lambda / s;
115 r.boundaryEmptyProb = one - r.utilization;
116
117 // P'(1) from the generating function.
118 const T meanAtBoundary =
119 lambda + (a * batchSecondFactorial / two + lambda * (one - s)) / (s - lambda);
120 const T meanSojournAtBoundary = meanAtBoundary / lambda;
121 r.meanWaitingTime = meanSojournAtBoundary - one / s;
122 r.meanWaitingQueue = lambda * r.meanWaitingTime;
123
124 if (convention == GeoConvention::LAS_DA) {
125 r.meanQueueLength = meanAtBoundary;
126 r.meanSojournTime = meanSojournAtBoundary;
127 r.meanServiceTime = one / s;
128 } else {
129 // One departure earlier: the epoch drops exactly the departures of the
130 // slot, whose rate is lambda, hence one slot of sojourn.
131 r.meanQueueLength = meanAtBoundary - lambda;
132 r.meanSojournTime = meanSojournAtBoundary - one;
133 r.meanServiceTime = (one - s) / s;
134 }
135 return r;
136}
137
138/**
139 * @brief Geo^X/Geo/1: the discrete-time single-server queue with batch
140 * arrivals.
141 *
142 * @param a per-slot probability that a batch arrives, in (0,1]
143 * @param beta geometric batch-size parameter, in (0,1]; E[X] = 1/beta
144 * @param s per-slot service completion probability, in (0,1]
145 * @param convention slot-boundary convention (late arrival, early arrival)
146 */
147template <class T>
148GeoXGeo1Result<T> dqsys_geoxgeo1(const T& a, const T& beta, const T& s,
150 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
151 const T two = num_traits<T>::from_int(2);
152 if (beta <= zero || beta > one)
153 throw InputError("dqsys_geoxgeo1: beta must lie in (0,1]");
154 const T batchMean = one / beta;
155 const T batchSecondFactorial = two * (one - beta) / (beta * beta);
156 return dqsys_geoxgeo1_moments(a, batchMean, batchSecondFactorial, s, convention);
157}
158
159/**
160 * Probability generating function of the stationary queue length.
161 *
162 * @param r a result of dqsys_geoxgeo1
163 * @param z argument in (0,1]
164 * @param Az the value A(z) of the slot-arrival pgf at the same z
165 *
166 * No pmf is offered: for a general batch law the stationary distribution has
167 * no elementary closed form, so only the generating function is exact.
168 */
169template <class T>
170T dqsys_geoxgeo1_pgf(const GeoXGeo1Result<T>& r, const T& z, const T& Az) {
171 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
172 if (z <= zero || z > one) throw InputError("dqsys_geoxgeo1: pgf argument z must lie in (0,1]");
173 if (z == one) return one;
174 const T s = r.serviceProb;
175 const T denom = z - Az * (s + (one - s) * z);
176 if (denom == zero) throw NumericError("dqsys_geoxgeo1: pgf denominator vanishes");
177 const T boundary = r.boundaryEmptyProb * s * (z - one) * Az / denom;
178 if (r.convention == GeoConvention::LAS_DA) return boundary;
179 return boundary * (s / z + one - s) + r.boundaryEmptyProb * s * (one - one / z);
180}
181
182} // namespace dqsys
183} // namespace line
184
185#endif // LINE_API_DQSYS_DQSYS_GEOXGEO1_H
InputError(const std::string &what)
Definition error.h:39
NumericError(const std::string &what)
Definition error.h:45
Geo/Geo/1: the discrete-time single-server queue with geometric interarrival and service times.
The exception types the port throws.
GeoXGeo1Result< T > dqsys_geoxgeo1(const T &a, const T &beta, const T &s, GeoConvention convention=GeoConvention::LAS_DA)
Geo^X/Geo/1: the discrete-time single-server queue with batch arrivals.
T dqsys_geoxgeo1_pgf(const GeoXGeo1Result< T > &r, const T &z, const T &Az)
Probability generating function of the stationary queue length.
GeoXGeo1Result< T > dqsys_geoxgeo1_moments(const T &a, const T &batchMean, const T &batchSecondFactorial, const T &s, GeoConvention convention=GeoConvention::LAS_DA)
Geo^X/Geo/1 for an arbitrary batch law given by its first two factorial moments.
Number-type abstraction for the templated API port.
T boundaryEmptyProb
1 - lambda/s, at the slot boundary