LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
qsys_mapmap1.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_MAPMAP1_H
6#define LINE_API_QSYS_QSYS_MAPMAP1_H
7
8/**
9 * @file
10 * @ingroup api_qsys
11 * The MAP/MAP/1 FCFS queue: mean number in system, waiting time, sojourn time,
12 * utilization and the queue-length distribution.
13 *
14 * ALGORITHM, AND HOW IT DIFFERS FROM THE MATLAB REFERENCE.
15 * matlab/src/api/qsys/qsys_mapmap1.m obtains these quantities by calling
16 * BUTools' MMAPPH1FCFS, which is not transcribed here; this port computes the
17 * SAME quantities from the port's own
18 * quasi-birth-death machinery (line/api/mam/qbd_mapmap1.h), which solves the
19 * level-independent QBD whose level is the number in system and whose phase is
20 * the pair (arrival phase, service phase). The algorithm is therefore
21 * different, the quantity is the same.
22 *
23 * The reference does one further thing this port deliberately does not. Before
24 * calling BUTools it replaces the service MAP (D0,D1) by the phase-type
25 * distribution
26 *
27 * sigma = theta D1 / (theta D1 e), S = D0, theta (D0+D1) = 0,
28 *
29 * i.e. by the stationary marginal of a service time. That embedded PH keeps the
30 * service-time MARGINAL exactly but DISCARDS the serial correlation between
31 * consecutive service times, so the reference solves a MAP/PH/1 queue in place
32 * of the MAP/MAP/1 queue it names. The two coincide exactly when the service
33 * MAP is a renewal process (D1 = (-D0 e) sigma, which includes every PH-renewal
34 * service), and diverge otherwise. This port solves the genuine MAP/MAP/1 QBD,
35 * so on a correlated service MAP it does not agree with the reference and is
36 * not meant to: see the measured numbers below.
37 *
38 * MEASURED AGREEMENT (MATLAB R2025a, T = double). Metric order is
39 * meanQueueLength / meanWaitingTime / meanSojournTime / utilization. Arrival
40 * MAPs used below:
41 * P(2) = Poisson, D0 = [-2], D1 = [2]
42 * C = correlated MMPP2, D0 = [-2.5 0.2; 0.1 -0.7], D1 = diag(2.3, 0.6),
43 * lambda = 7/6
44 *
45 * 1. M/M/1 collapse, qsys_mapmap1(P(2), service D0 = [-3], D1 = [3]). MATLAB
46 * 1.999999999999999 / 0.6666666666666656 / 0.9999999999999989 /
47 * 0.6666666666666666; port 2 / 0.666666666666668 / 1 / 0.666666666666667,
48 * the textbook values. Relative differences 5e-16, 1.8e-15, 1.1e-15.
49 *
50 * 2. Renewal service, where the reference's PH reduction is exact. Arrival C,
51 * service the Erlang-2 renewal MAP D0 = [-6 6; 0 -6], D1 = [0 0; 6 0].
52 * MATLAB 0.8364637529649164 / 0.3836355977794533 / 0.7169689311127866 /
53 * 0.3888888888888888; port 0.836463752964917 / 0.383635597779453 /
54 * 0.716968931112786 / 0.388888888888889. Relative differences below 1e-15
55 * on every metric: with a renewal service the two model the same system and
56 * the port reproduces the reference to machine precision.
57 *
58 * 3. Correlated service, where the reference's PH reduction is NOT exact.
59 * Arrival C, service MAP D0 = [-5 0.4; 0.2 -1.4], D1 = diag(4.6, 1.2)
60 * (lambda_s = 7/3, so rho = 1/2). MATLAB qsys_mapmap1 reports
61 * 2.0305355320573 / 1.311887598906254 / 1.740459027477683 / 0.5; this port
62 * returns 2.54755753480255 / 1.75504931554505 / 2.18362074411647 / 0.5,
63 * relative differences of 2.03e-1, 2.53e-1 and 2.03e-1. The port's value is
64 * the correct MAP/MAP/1 answer: LINE's own MATLAB qbd_mapmap1 on the
65 * identical pair of MAPs returns QN = 2.547554867631855, UN =
66 * 0.5000000000000002 and RN = 2.183618457970162, i.e. the port to within
67 * 1.05e-6. That residual 1.05e-6 is MATLAB qbd_mapmap1's truncated level
68 * sum, which stops once the accumulated mass reaches 1 - 1e-10 and so
69 * discards the tail times its level index; this port sums the geometric
70 * tail in closed form (see qbd_mapmap1.h). The 20% gap against
71 * qsys_mapmap1.m is a different thing entirely: it is the service
72 * correlation the reference's PH reduction throws away.
73 *
74 * meanWaitingTime is meanSojournTime minus the mean service time 1/lambda_s,
75 * and utilization is lambda_a/lambda_s, both matching the reference's own
76 * definitions.
77 *
78 * ARITHMETIC. The function is gated on num_traits<T>::has_transcendental
79 * because qbd_mapmap1 reaches R by cyclic reduction, a fixed-point iteration
80 * driven to a tolerance that does not terminate in a finite number of field
81 * operations; running it at exact rational arithmetic would produce a rational
82 * with a denominator doubling every step and still not the exact R. See
83 * qbd_r.h. Everything downstream of R here -- the mean, the level
84 * probabilities, the Little's-law conversions -- is finite exact matrix
85 * algebra and carries no additional error.
86 */
87
88#include <cstddef>
89#include <vector>
90
93#include "line/api/mam/qbd_r.h"
94#include "line/num/number.h"
95#include "line/util/error.h"
96#include "line/util/matrix.h"
97
98namespace line {
99namespace qsys {
100
101/**
102 * Return value of the MAP/MAP/1 family (qsys_mapmap1, qsys_mapph1, qsys_phph1),
103 * carrying the same quantities as the MATLAB result struct.
104 */
105template <class T>
107 T meanQueueLength; ///< E[N], number in system
108 T meanWaitingTime; ///< E[Wq], time in queue
109 T meanSojournTime; ///< E[W] = E[Wq] + E[S]
110 T utilization; ///< rho = lambda_a / lambda_s
111 std::vector<T> queueLengthDist; ///< P(N = n), n = 0, 1, ...
112};
113
114/**
115 * MAP/MAP/1 by the exact QBD solution.
116 *
117 * @param arrival arrival MAP (D0, D1)
118 * @param service service MAP (D0, D1); its correlation IS honoured
119 * @param dist_size how many entries of queueLengthDist to materialize
120 */
121template <class T>
123 std::size_t dist_size) {
125 "qsys_mapmap1 requires transcendental arithmetic");
126 if (dist_size == 0) throw InputError("qsys_mapmap1: dist_size must be positive");
127 const T zero = num_traits<T>::from_int(0);
128 const mam::QbdMapMap1Result<T> q = mam::qbd_mapmap1(arrival, service, T(zero), dist_size);
129
130 const T lambda_a = mam::map_lambda(arrival);
131 const T lambda_s = mam::map_lambda(service);
132 const T mean_service = num_traits<T>::from_int(1) / lambda_s;
133
135 r.meanQueueLength = q.QN;
136 r.meanSojournTime = q.QN / lambda_a;
137 r.meanWaitingTime = r.meanSojournTime - mean_service;
138 r.utilization = lambda_a / lambda_s;
139 r.queueLengthDist.assign(q.pqueue.rows(), zero);
140 for (std::size_t k = 0; k < q.pqueue.rows(); ++k) {
141 T s = zero;
142 for (std::size_t j = 0; j < q.pqueue.cols(); ++j) s += q.pqueue(k, j);
143 r.queueLengthDist[k] = s;
144 }
145 return r;
146}
147
148/** qsys_mapmap1 with 100 materialized levels, the reference's numQLProbs. */
149template <class T>
150MapMap1Result<T> qsys_mapmap1(const mam::Map<T>& arrival, const mam::Map<T>& service) {
151 return qsys_mapmap1(arrival, service, static_cast<std::size_t>(100));
152}
153
154} // namespace qsys
155} // namespace line
156
157#endif // LINE_API_QSYS_QSYS_MAPMAP1_H
InputError(const std::string &what)
Definition error.h:39
The exception types the port throws.
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
Dense matrix and non-owning view.
QbdMapMap1Result< T > qbd_mapmap1(const Map< T > &arrival, const Map< T > &service_in, const T &util, std::size_t max_levels)
MAP/MAP/1 queue (qbd_mapmap1.m).
T map_lambda(const Map< T > &m)
Stationary arrival rate, lambda = pi D1 e.
Definition map_moment.h:79
MapMap1Result< T > qsys_mapmap1(const mam::Map< T > &arrival, const mam::Map< T > &service, std::size_t dist_size)
MAP/MAP/1 by the exact QBD solution.
Number-type abstraction for the templated API port.
The MAP/MAP/1 queue solved as a quasi-birth-death process.
Quasi-birth-death processes: the rate matrix R, the fundamental matrix G, the caudal characteristic,...
A MAP as the pair of matrices (D0, D1).
Definition map_moment.h:53
Result of qbd_mapmap1, mirroring the MATLAB return list.
T QN
mean number in system, closed form
Matrix< T > pqueue
level distribution, row k = pi_k
Return value of the MAP/MAP/1 family (qsys_mapmap1, qsys_mapph1, qsys_phph1), carrying the same quant...
T utilization
rho = lambda_a / lambda_s
T meanQueueLength
E[N], number in system.
T meanSojournTime
E[W] = E[Wq] + E[S].
std::vector< T > queueLengthDist
P(N = n), n = 0, 1, ...
T meanWaitingTime
E[Wq], time in queue.