LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
solver_mam_mapmap1_exact.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_SOLVERS_MAM_SOLVER_MAM_MAPMAP1_EXACT_H
6#define LINE_SOLVERS_MAM_SOLVER_MAM_MAPMAP1_EXACT_H
7
8/**
9 * @file
10 * @ingroup line_solvers
11 * Port of `solver_mam_mapmap1_exact.m`: the exact fast path SolverMAM tries
12 * BEFORE anything else, for a single-class open Source -> FCFS Queue -> Sink
13 * model whose arrival or service is a genuinely CORRELATED MAP.
14 *
15 * Why it comes first. The decomposition methods approximate exactly this case:
16 * `dec.source` hands the service to `MMAPPH1FCFS` as a renewal phase type,
17 * which keeps the service-time marginal and discards the correlation between
18 * consecutive services. When the process is renewal the two agree and the fast
19 * path stands down (the `is_renewal_map` test below); when it is not, the fast
20 * path returns the exact matrix-geometric answer instead.
21 *
22 * The reference reaches `Q_CT_MAP_MAP_1`; this port reaches `qbd_mapmap1`,
23 * which solves the same level-independent QBD from the port's own machinery
24 * (see `api/mam/qbd_mapmap1.h` for the measured agreement).
25 *
26 * `ok = false` means "not an exactly-solvable single MAP/MAP/1 queue", and the
27 * caller falls through to the decomposition. Every early return in the
28 * reference is reproduced, including the stability test lambda < mu: an
29 * unstable or degenerate model is left to the fallback rather than answered
30 * with a divergent geometric series.
31 */
32
33#include <cmath>
34#include <vector>
35
42
43namespace line {
44namespace mam {
45
46/** Result of the fast path; `ok` false means the model is not in its regime. */
47template <class T>
49 bool ok = false;
51};
52
53template <class T>
56 if constexpr (!num_traits<T>::has_transcendental) {
57 // The QBD's cyclic reduction is tolerance-terminated; under exact
58 // arithmetic the fast path simply does not claim the model, and the
59 // dispatch below refuses by name.
60 return out;
61 } else {
63 const T zero = num_traits<T>::from_int(0);
64 const std::size_t M = L.nstations, K = L.nclasses;
65 if (K != 1) return out;
66 if (!std::isinf(L.classes[0].population)) return out;
67
68 std::size_t src = 0, q = 0;
69 std::size_t nsrc = 0, nq = 0;
70 for (std::size_t i = 1; i <= M; ++i) {
71 if (L.stations[i - 1].sched == SchedStrategy::EXT) {
72 src = i;
73 ++nsrc;
74 } else if (L.stations[i - 1].sched == SchedStrategy::FCFS) {
75 q = i;
76 ++nq;
77 }
78 }
79 if (nsrc != 1 || nq != 1) return out;
80 if (L.stations[q - 1].nservers != 1.0) return out;
81 // The arrival the queue sees equals the source MAP only when nothing sits
82 // between them, so the two must be the model's only stations.
83 if (M != 2) return out;
84 if (L.disabled[src - 1][0] || L.disabled[q - 1][0]) return out;
85
86 const Map<T> arv = lang::dist_to_map(L.service[src - 1][0]);
87 const Map<T> svc = lang::dist_to_map(L.service[q - 1][0]);
88 // A RAP or ME pair is a valid point process but not a MAP, and the QBD
89 // takes Markovian blocks; leave those to the fallback.
90 if (!basic_detail::is_markovian_map(arv) || !basic_detail::is_markovian_map(svc)) return out;
91 // Renewal on both sides means the decomposition is already exact.
92 if (basic_detail::is_renewal_map(arv) && basic_detail::is_renewal_map(svc)) return out;
93
94 const T lambda = map_lambda(arv);
95 const T mu = map_lambda(svc);
96 if (!(lambda < mu)) return out;
97
98 const QbdMapMap1Result<T> r = qbd_mapmap1(arv, svc);
99
100 mva::MvaSolution<T>& s = out.sol;
101 s.Q = Matrix<T>(M, K, zero);
102 s.U = Matrix<T>(M, K, zero);
103 s.R = Matrix<T>(M, K, zero);
104 s.Tp = Matrix<T>(M, K, zero);
105 s.C.assign(K, zero);
106 s.X.assign(K, zero);
107 s.Tp(src - 1, 0) = lambda;
108 s.Tp(q - 1, 0) = lambda;
109 s.Q(q - 1, 0) = r.QN;
110 s.U(q - 1, 0) = T(lambda / mu);
111 s.R(q - 1, 0) = T(r.QN / lambda);
112 s.C[0] = s.R(q - 1, 0);
113 s.X[0] = lambda;
114 s.iter = 1;
115 out.ok = true;
116 return out;
117 } // if constexpr has_transcendental
118}
119
120} // namespace mam
121} // namespace line
122
123#endif // LINE_SOLVERS_MAM_SOLVER_MAM_MAPMAP1_EXACT_H
A network plus its refreshed NetworkStruct.
std::vector< std::vector< Distrib< T > > > service
service[i][r], 0-based station and class; a disabled entry marks a pair never visited.
std::vector< std::vector< bool > > disabled
std::vector< JobClass > classes
std::vector< Station< T > > stations
stations[k-1] is the k-th station
What refreshProcessRepresentations and refreshLST compute FROM a distribution: the (D0,...
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
The option and result types every MVA analyzer shares.
mam::Map< T > dist_to_map(const Distrib< T > &d)
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
Definition lang_types.h:181
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).
MapMap1Exact< T > solver_mam_mapmap1_exact(const qn::NetworkStruct< T > &L)
T map_lambda(const Map< T > &m)
Stationary arrival rate, lambda = pi D1 e.
Definition map_moment.h:79
A queueing network and its refreshed NetworkStruct.
The MAP/MAP/1 queue solved as a quasi-birth-death process.
Port of solver_mam_basic.m, the dec.source analyzer and the default algorithm of SolverMAM.
Result of the fast path; ok false means the model is not in its regime.
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
Class-level results, the [Q,U,R,T,C,X] of the MATLAB analyzers.
Definition mva_types.h:96
std::vector< T > X
Definition mva_types.h:98
std::vector< T > C
Definition mva_types.h:98