5#ifndef LINE_SOLVERS_MAM_SOLVER_MAM_DT_H
6#define LINE_SOLVERS_MAM_SOLVER_MAM_DT_H
55void assert_scope(
const qn::NetworkStruct<T>& sn) {
59 "SolverMAM: the discrete-time path supports open models only. A closed slotted model "
60 "needs a level-dependent discrete chain, which the Q-MAM discrete-time catalogue does "
64 "SolverMAM: the discrete-time path supports one class only. Independent per-class "
65 "lattice sources fire in the same slot with positive probability, and a batch of "
66 "simultaneous arrivals of different classes is not an MMAP[K], which is what "
67 "Q_DT_MMAPK_PHK_1 consumes.");
68 for (std::size_t ist = 0; ist < sn.nstations; ++ist) {
70 if (sched == SchedStrategy::EXT)
continue;
71 if (sched != SchedStrategy::FCFS)
73 "SolverMAM: the discrete-time path supports FCFS single-server stations and the "
75 if (sn.stations[ist].nservers > 1)
77 "SolverMAM: the discrete-time path models one server per station; a slotted "
78 "multiserver queue needs the level-dependent boundary of Geo/Geo/c.");
84Dmap<T> station_law(
const qn::NetworkStruct<T>& sn, std::size_t ist, std::size_t r,
88 if (pt == ProcessType::DMAP) {
89 if (slot_length != 1.0)
91 "SolverMAM: a DMAP is defined on its own slot, so it cannot be combined with a "
92 "slotlength other than one.");
94 d.D0 = sn.service[ist][r].D0;
95 d.D1 = sn.service[ist][r].D1;
99 num_traits<T>::from_double(1.0 / (num_traits<T>::to_double(sn.rates(ist, r)) * slot_length));
105std::vector<T> solve_single_station(
const Dmap<T>& arv,
const Dmap<T>& svc,
106 std::size_t max_num_comp) {
120Matrix<T> routing(
const qn::NetworkStruct<T>& sn, std::size_t source_idx,
121 const std::vector<std::size_t>& queue_idx) {
123 const T zero = num_traits<T>::from_int(0);
124 const std::size_t I = sn.nof_nodes(), K = sn.nclasses;
125 Matrix<T> Pn(I, I, zero);
126 for (std::size_t a = 0; a < I; ++a)
127 for (std::size_t b = 0; b < I; ++b) Pn(a, b) = sn.rtnodes(a * K, b * K);
129 std::vector<std::size_t> sink_nodes;
130 for (std::size_t ind = 0; ind < I; ++ind)
131 if (sn.nodes[ind].nodetype == NodeType::Sink) {
132 sink_nodes.push_back(ind);
133 for (std::size_t b = 0; b < I; ++b) Pn(ind, b) = zero;
136 std::vector<std::size_t> station_nodes;
137 station_nodes.push_back(sn.station_to_node[source_idx] - 1);
138 for (std::size_t idx = 0; idx < queue_idx.size(); ++idx)
139 station_nodes.push_back(sn.station_to_node[queue_idx[idx]] - 1);
141 std::vector<std::size_t> inter_nodes;
142 for (std::size_t ind = 0; ind < I; ++ind) {
143 const bool is_station =
144 std::find(station_nodes.begin(), station_nodes.end(), ind) != station_nodes.end();
145 const bool is_sink = std::find(sink_nodes.begin(), sink_nodes.end(), ind) != sink_nodes.end();
146 if (!is_station && !is_sink) inter_nodes.push_back(ind);
149 auto sub = [&](
const std::vector<std::size_t>& rows,
const std::vector<std::size_t>& cols) {
150 Matrix<T> out(rows.size(), cols.size(), zero);
151 for (std::size_t a = 0; a < rows.size(); ++a)
152 for (std::size_t b = 0; b < cols.size(); ++b) out(a, b) = Pn(rows[a], cols[b]);
156 const std::size_t ns = station_nodes.size();
157 Matrix<T> full = sub(station_nodes, station_nodes);
158 if (!inter_nodes.empty()) {
159 Matrix<T> Psn = sub(station_nodes, inter_nodes);
160 Matrix<T> Pnn = sub(inter_nodes, inter_nodes);
161 Matrix<T> Pns = sub(inter_nodes, station_nodes);
162 Matrix<T> Inn = detail::dt_eye<T>(inter_nodes.size());
163 Matrix<T> inv = detail::dt_left_solve(detail::dt_sub(Inn, Pnn), Inn);
164 full = detail::dt_add(full, detail::dt_mul(detail::dt_mul(Psn, inv), Pns));
167 Matrix<T> P(ns, ns - 1, zero);
168 for (std::size_t a = 0; a < ns; ++a)
169 for (std::size_t b = 1; b < ns; ++b) P(a, b - 1) = full(a, b);
176 std::size_t idx, std::size_t space_max) {
179 if (num_traits<T>::to_double(P(0, idx)) > 0) {
183 for (std::size_t j = 0; j < dep.size(); ++j) {
184 const T p = P(1 + j, idx);
185 if (num_traits<T>::to_double(p) <= 0)
continue;
197 "SolverMAM: a queue receives no arrivals in the discrete-time routing matrix.");
203T visit_ratio(
const qn::NetworkStruct<T>& sn, std::size_t ist) {
204 T v = num_traits<T>::from_int(0);
205 for (std::size_t c = 0; c < sn.visits.size(); ++c) {
206 const Matrix<T>& vm = sn.visits[c];
207 if (ist >= vm.rows())
continue;
208 for (std::size_t r = 0; r < vm.cols(); ++r) v = v + vm(ist, r);
221 double slot_length) {
224 dt_detail::assert_scope(
sn);
226 const std::size_t M =
sn.nstations, K =
sn.nclasses;
227 std::vector<Dmap<T>> law(M);
228 for (std::size_t ist = 0; ist < M; ++ist) law[ist] = dt_detail::station_law(
sn, ist, 0, slot_length);
230 std::size_t source_idx = M;
231 std::vector<std::size_t> queue_idx;
232 for (std::size_t ist = 0; ist < M; ++ist) {
233 if (
sn.stations[ist].sched == SchedStrategy::EXT)
236 queue_idx.push_back(ist);
239 throw UnsupportedError(
"SolverMAM: the discrete-time path requires an open model with a Source.");
241 const std::size_t nq = queue_idx.size();
242 std::vector<T> QN(nq, zero), UN(nq, zero), TN(nq, zero);
247 src_batch.push_back(law[source_idx].D0);
248 src_batch.push_back(law[source_idx].D1);
252 const std::vector<T> ql =
253 dt_detail::solve_single_station(law[source_idx], law[queue_idx[0]], 1000);
254 for (std::size_t i = 0; i < ql.size(); ++i)
262 Matrix<T> P = dt_detail::routing(
sn, source_idx, queue_idx);
263 std::vector<DBatch<T>> dep(nq);
264 for (std::size_t idx = 0; idx < nq; ++idx) {
265 const T p = lambda_slot * dt_detail::visit_ratio(
sn, queue_idx[idx]);
272 std::vector<T> QNprev(nq, zero), QNprev2(nq, zero), UNprev(nq, zero), TNprev(nq, zero);
273 const int iter_max =
opt.iter_max > 0 ?
opt.iter_max : 100;
274 const double iter_tol =
opt.tol > 0 ?
opt.tol : 1e-3;
275 auto max_rel = [&](
const std::vector<T>& a,
const std::vector<T>& b) {
277 for (std::size_t i = 0; i < a.size(); ++i) {
284 for (
int it = 1; it <= iter_max; ++it) {
286 for (std::size_t idx = 0; idx < nq; ++idx) {
287 DBatch<T> arv = dt_detail::arrivals(src_batch, dep, P, idx,
opt.space_max);
293 d.push_back(r.
dep.D0);
294 d.push_back(r.
dep.D1);
297 if (it > 1 && max_rel(QN, QNprev) < iter_tol)
break;
298 if (it > 2 && max_rel(QN, QNprev2) < iter_tol) {
305 for (std::size_t idx = 0; idx < nq; ++idx) {
306 QN[idx] = (QN[idx] + QNprev[idx]) * half;
307 UN[idx] = (UN[idx] + UNprev[idx]) * half;
308 TN[idx] = (TN[idx] + TNprev[idx]) * half;
330 for (std::size_t idx = 0; idx < nq; ++idx) {
331 const std::size_t ist = queue_idx[idx];
332 s.
Q(ist, 0) = QN[idx];
333 s.
U(ist, 0) = UN[idx];
334 s.
Tp(ist, 0) = TN[idx] / slot;
337 const T lambda = lambda_slot / slot;
338 s.
Tp(source_idx, 0) = lambda;
341 for (std::size_t idx = 0; idx < nq; ++idx) qtot = qtot + QN[idx];
342 s.
C[0] = qtot / lambda;
UnsupportedError(const std::string &what)
A network plus its refreshed NetworkStruct.
Discrete-time (slotted) matrix-analytic primitives and queues.
The exception types the port throws.
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
The option and result types SolverMAM shares with its analyzers.
bool sn_is_open_model(const qn::NetworkStruct< T > &sn)
all(isinf(sn.njobs)): EVERY class is open, which a mixed model fails.
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
ProcessType
Distribution kinds, with the values of MATLAB ProcessType.
NodeType
Node kinds, with the values of MATLAB NodeType.
DBatch< T > dmap_compress_batch(const DBatch< T > &A, std::size_t max_order)
Order reduction of a BATCH stream.
std::vector< T > q_dt_map_map_1(const Dmap< T > &arv, const Dmap< T > &svc, std::size_t max_num_comp=1000)
Queue length distribution of a discrete-time D-MAP/D-MAP/1/FCFS queue, the queue-length half of Q_DT_...
std::vector< Matrix< T > > DBatch
A discrete batch arrival stream, entry k carrying the slots with k events.
T dmap_lambda_batch(const DBatch< T > &A)
Mean number of EVENTS per slot, pi sum_k k A_k e.
std::vector< T > q_dt_ph_ph_1(const Dph< T > &arv, const Dph< T > &svc, std::size_t max_num_comp=1000)
Queue length of a discrete-time DPH/DPH/1/FCFS queue, via the D-MAP route.
DtQueueResult< T > mg1_dt_queue(const DBatch< T > &arv, const Dmap< T > &svc, std::size_t max_num_comp=1000, bool want_departure=false)
Discrete-time single-server queue with batch D-MAP arrivals, DBMAP/D-MAP/1.
Dph< T > dph_from_dist(lang::ProcessType type, const T &mean_slots, const T &scv)
Exact discrete phase-type representation of a lattice-valued law.
Dmap< T > dph_to_dmap(const Dph< T > &d)
Renewal D-MAP (A, a alpha) of a discrete phase-type law.
MamSolution< T > solver_mam_dt(const qn::NetworkStruct< T > &sn, const MamOptions &opt, double slot_length)
Discrete-time analysis of sn, returning the same metric tuple as every other MAM analyzer.
Dph< T > dmap_to_dph(const Dmap< T > &d)
Discrete phase-type law underlying a renewal D-MAP.
DBatch< T > dmap_super(const DBatch< T > &A, const DBatch< T > &B)
Superposition, E_k = sum_{i+j=k} kron(A_i, B_j).
bool dmap_is_renewal(const Dmap< T > &d)
True when D1 has rank one, i.e.
DBatch< T > dmap_thin(const DBatch< T > &A, const T &p)
Bernoulli thinning, B_k = sum_{n>=k} C(n,k) p^k (1-p)^(n-k) A_n.
A queueing network and its refreshed NetworkStruct.
Port of matlab/src/api/sn/sn_is_discrete_time.m.
Ports of the sn_has_* / sn_is_* predicate family of matlab/src/api/sn.
Outcome of a slotted station solve.
The options SolverMAM reads.
What the MAM dispatch returns: the metrics plus the algorithm that ran.
std::string actualmethod
The concrete algorithm, as the reference's actualmethod.
mva::MvaSolution< T > sol
Class-level results, the [Q,U,R,T,C,X] of the MATLAB analyzers.