LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
solver_mam_sync.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_SYNC_H
6#define LINE_SOLVERS_MAM_SOLVER_MAM_SYNC_H
7
8/**
9 * @file
10 * @ingroup line_solvers
11 * The 'sync.faster' and 'sync.slower' methods of SolverMAM: the synchronous
12 * approximation of an M/G/c/K station with dual bounds, qsys_mgck_sync.
13 *
14 * Port of matlab/src/solvers/MAM/mam_sync_applicable.m and solver_mam_sync.m.
15 * 'sync.faster' returns lower bounds on the mean queue length, response time
16 * and loss probability, 'sync.slower' upper ones; options.config.sync_phases
17 * (MamOptions::sync_phases) selects the phase expansion.
18 *
19 * THE RULE IS A SHAPE: the synchronous approximation is a model of one M/G/c/K
20 * station, so the network must be a single open class with one Source (Poisson)
21 * and one FCFS Queue with a finite capacity and the drop rule, every completion
22 * leaving the network. The service law must be one the embedded chain can take
23 * exactly: deterministic, or phase type. The phase expansion needs it
24 * deterministic.
25 *
26 * ONE PREDICATE, THREE CALLERS: `runner_detail::check_model_method`, the
27 * `mam_dispatch` arm and `solver_mam_sync` all ask `mam_sync_refusal`.
28 */
29
30#include <cmath>
31#include <cstddef>
32#include <string>
33#include <vector>
34
40#include "line/util/error.h"
41
42namespace line {
43namespace mam {
44
45/** What qsys_mgck_sync needs, read off the model by `mam_sync_refusal`. */
46template <class T>
49 std::size_t c = 1;
50 std::size_t K = 1;
52 std::size_t nphases = 0;
53 std::size_t srcIdx = 0; ///< 1-based Source station
54 std::size_t qIdx = 0; ///< 1-based Queue station
55};
56
57namespace sync_detail {
58
59inline const char* drop_to_text(lang::DropStrategy d) {
60 switch (d) {
61 case lang::DropStrategy::WAITQ: return "WAITQ";
62 case lang::DropStrategy::DROP: return "DROP";
63 case lang::DropStrategy::BAS: return "BAS";
64 case lang::DropStrategy::BBS: return "BBS";
65 case lang::DropStrategy::RSRD: return "RSRD";
66 case lang::DropStrategy::RETRIAL: return "RETRIAL";
67 case lang::DropStrategy::RETRIAL_WITH_LIMIT: return "RETRIAL_WITH_LIMIT";
68 }
69 return "unknown";
70}
71
72} // namespace sync_detail
73
74/**
75 * `mam_sync_applicable.m`: why the sync methods cannot answer this model, or ""
76 * when they can, in which case `par` (when given) is filled.
77 */
78template <class T>
79std::string mam_sync_refusal(const qn::NetworkStruct<T>& L, std::size_t nph,
80 MamSyncParams<T>* par = nullptr) {
83 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
84 if (L.nclasses != 1 || !std::isinf(L.classes[0].population))
85 return "The sync methods require a single open class.";
86 std::size_t nsrc = 0, nq = 0, src = 0, q = 0;
87 for (std::size_t i = 0; i < L.nstations; ++i) {
88 if (L.stations[i].sched == SchedStrategy::EXT) {
89 ++nsrc;
90 src = i + 1;
91 } else {
92 ++nq;
93 q = i + 1;
94 }
95 }
96 if (L.nstations != 2 || nsrc != 1 || nq != 1)
97 return "The sync methods require exactly one Source and one Queue station.";
98 if (L.stations[q - 1].sched != SchedStrategy::FCFS)
99 return std::string("The sync methods require an FCFS queue; the station uses ") +
100 lang::sched_to_text(L.stations[q - 1].sched) +
101 ". The synchronous approximation is a model of the M/G/c/K FIFO queue.";
102 if (L.procid(src, 1) != ProcessType::EXP || L.phases_of(src, 1) > 1)
103 return "The sync methods require Poisson (exponential) arrivals.";
104 // every completion at the queue leaves the network: no feedback to the queue
105 const std::size_t qrow = (q - 1) * L.nclasses;
106 if (L.rt.rows() > qrow && L.rt(qrow, qrow) > zero)
107 return "The sync methods do not support feedback: every completion must leave the network.";
108 bool ld = false;
109 for (const T& v : L.stations[q - 1].lldscaling)
110 if (v != one) ld = true;
111 if (L.setupparam.find(q) != L.setupparam.end() || ld)
112 return "The sync methods do not support setup times or load dependence.";
113 const double c = L.stations[q - 1].nservers;
114 const double K = L.stations[q - 1].cap;
115 if (!std::isfinite(c) || c < 1.0) return "The sync methods require a finite number of servers.";
116 if (!std::isfinite(K))
117 return "The sync methods require a finite capacity: the synchronous approximation is an "
118 "M/G/c/K model. Set Queue.setCapacity(K).";
119 if (K < c)
120 return "The sync methods require a capacity no smaller than the number of servers.";
121 if (!L.droprule.empty() && L.droprule[q - 1][0] != lang::DropStrategy::DROP)
122 return std::string("The sync methods require the drop rule at the finite buffer; the "
123 "station uses ") +
124 sync_detail::drop_to_text(L.droprule[q - 1][0]) + ".";
125 // the declared law: sn_nonmarkov_toph retags a non-Markovian fit, only DET survives
126 const lang::Distrib<T>& dq = L.service[q - 1][0];
127 const lang::Distrib<T>& decl = dq.declared ? *dq.declared : dq;
128 const ProcessType pid = decl.type;
130 if (pid == ProcessType::DET) {
131 svc = qsys::ServiceLaw<T>::deterministic(T(one / L.rates(q - 1, 0)));
132 } else if (pid == ProcessType::EXP || pid == ProcessType::ERLANG ||
133 pid == ProcessType::HYPEREXP || pid == ProcessType::PH ||
134 pid == ProcessType::APH || pid == ProcessType::COXIAN ||
135 pid == ProcessType::COX2) {
136 if (nph > 0)
137 return "The synchronous phase expansion (options.config.sync_phases >= 1) requires a "
138 "deterministic service time.";
139 const Map<T> m = lang::dist_to_map(dq);
141 } else {
142 return std::string("The sync methods take a deterministic or phase-type service time; "
143 "the station declares ") +
144 lang::process_to_text(pid) + ".";
145 }
146 if (par) {
147 par->lambda = L.rates(src - 1, 0);
148 par->c = static_cast<std::size_t>(c);
149 par->K = static_cast<std::size_t>(K);
150 par->svc = svc;
151 par->nphases = nph;
152 par->srcIdx = src;
153 par->qIdx = q;
154 }
155 return std::string();
156}
157
158/** Port of `solver_mam_sync.m`: fills Q, U, R, T, C, X from qsys_mgck_sync. */
159template <class T>
162 const std::string why = mam_sync_refusal(L, opt.sync_phases, &par);
163 if (!why.empty()) throw UnsupportedError(ErrorKind{"Structure"}, "SolverMAM: " + why);
164 const std::string prefix = "sync.";
165 if (opt.method.compare(0, prefix.size(), prefix) != 0)
166 throw UnsupportedError(ErrorKind{"Method"},
167 "SolverMAM: solver_mam_sync answers 'sync.faster' and 'sync.slower' "
168 "only, not '" + opt.method + "'");
169 const std::string variant = opt.method.substr(prefix.size());
171 qsys::qsys_mgck_sync(par.lambda, par.c, par.K, par.svc, variant, par.nphases);
172
173 const T zero = num_traits<T>::from_int(0);
174 const std::size_t M = L.nstations, K = L.nclasses;
176 s.Q = Matrix<T>(M, K, zero);
177 s.U = Matrix<T>(M, K, zero);
178 s.R = Matrix<T>(M, K, zero);
179 s.Tp = Matrix<T>(M, K, zero);
180 s.C.assign(K, zero);
181 s.X.assign(K, zero);
182 const std::size_t si = par.srcIdx - 1, qi = par.qIdx - 1;
183 s.Tp(si, 0) = r.throughput;
184 s.Q(qi, 0) = r.meanQueueLength;
185 s.U(qi, 0) = r.utilization;
186 s.R(qi, 0) = r.meanSojournTime;
187 s.Tp(qi, 0) = r.throughput;
188 s.X[0] = r.throughput;
189 s.C[0] = r.meanSojournTime;
190 s.iter = 1;
191 s.method = opt.method;
192 return s;
193}
194
195} // namespace mam
196} // namespace line
197
198#endif // LINE_SOLVERS_MAM_SOLVER_MAM_SYNC_H
UnsupportedError(const std::string &what)
Definition error.h:119
A network plus its refreshed NetworkStruct.
std::size_t phases_of(std::size_t ist, std::size_t r) const
sn.phases(i,r): the order of the process representation.
std::vector< std::vector< Distrib< T > > > service
service[i][r], 0-based station and class; a disabled entry marks a pair never visited.
std::map< std::size_t, SetupDelayOffParam< T > > setupparam
Setup / delay-off, keyed by 1-based STATION index.
Matrix< T > rt
sn.rt and sn.rtnodes: the class-expanded routing.
Matrix< double > procid() const
sn.procid (M x K): the ProcessType id of each (station, class) pair, as refreshProcessTypes....
std::vector< JobClass > classes
std::vector< Station< T > > stations
stations[k-1] is the k-th station
Matrix< T > rates
(nstations x nclasses) service rates and SCVs, with a PARALLEL disabled flag instead of MATLAB's NaN ...
std::vector< std::vector< DropStrategy > > droprule
What refreshProcessRepresentations and refreshLST compute FROM a distribution: the (D0,...
The exception types the port throws.
The option and result types SolverMAM shares with its analyzers.
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
mam::Map< T > dist_to_map(const Distrib< T > &d)
The (D0,D1) pair that reaches sn.proc.
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
Definition lang_types.h:185
DropStrategy
Blocking and loss rules, with the values of MATLAB DropStrategy.
Definition lang_types.h:431
ProcessType
Distribution kinds, with the values of MATLAB ProcessType.
Definition lang_types.h:490
const char * sched_to_text(SchedStrategy s)
Definition lang_types.h:235
const char * process_to_text(ProcessType p)
The MATLAB ProcessType name, as sn.procid prints it.
Definition lang_types.h:601
const char * drop_to_text(lang::DropStrategy d)
mva::MvaSolution< T > solver_mam_sync(const qn::NetworkStruct< T > &L, const MamOptions &opt)
Port of solver_mam_sync.m: fills Q, U, R, T, C, X from qsys_mgck_sync.
std::vector< T > map_pie(const Map< T > &m)
Phase distribution seen by an arriving job, pie = pi D1 / (pi D1 e).
Definition map_moment.h:89
std::string mam_sync_refusal(const qn::NetworkStruct< T > &L, std::size_t nph, MamSyncParams< T > *par=nullptr)
mam_sync_applicable.m: why the sync methods cannot answer this model, or "" when they can,...
MgckSyncResult< T > qsys_mgck_sync(const T &lambda, std::size_t c, std::size_t K, const ServiceLaw< T > &svc, const std::string &variant, std::size_t nphases, const T &tol, std::size_t nmaxCap)
Synchronous approximation of the M/G/c/K queue with dual bounds.
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
A queueing network and its refreshed NetworkStruct.
Synchronous approximation of the M/G/c/K queue with dual bounds (German, EPEW 2025).
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
Definition error.h:37
std::shared_ptr< Distrib< T > > declared
The law as DECLARED, when this one is a surrogate fitted over it.
Definition lang_types.h:831
The options SolverMAM reads.
Definition mam_types.h:30
What qsys_mgck_sync needs, read off the model by mam_sync_refusal.
std::size_t qIdx
1-based Queue station
qsys::ServiceLaw< T > svc
std::size_t srcIdx
1-based Source station
A MAP as the pair of matrices (D0, D1).
Definition map_moment.h:53
Matrix< T > D0
Definition map_moment.h:54
Class-level results, the [Q,U,R,T,C,X] of the MATLAB analyzers.
Definition mva_types.h:97
std::vector< T > X
Definition mva_types.h:99
std::vector< T > C
Definition mva_types.h:99
Return value of qsys_mgck_sync, mirroring the MATLAB result struct.
T meanSojournTime
meanQueueLength / throughput
T utilization
mean busy servers / c in the synchronized model
T meanQueueLength
mean number in system
T throughput
lambda (1 - pK)
Service-time descriptor, the C++ form of the MATLAB svc struct.
static ServiceLaw deterministic(const T &d)
Constant service time d.
static ServiceLaw phase_type(const std::vector< T > &alpha, const Matrix< T > &Tmat)
Phase type (alpha, Tmat).