LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
sn_print.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_SN_SN_PRINT_H
6#define LINE_API_SN_SN_PRINT_H
7
8/**
9 * @file
10 * @ingroup api_sn
11 * Port of matlab/src/api/sn/sn_print.m, jline.api.sn.SnPrint and the python
12 * `sn_print` in api/sn/utils.py: the full debug dump of a NetworkStruct, one
13 * `field: value` line per field, matrices in the compact `[a b; c d]` form,
14 * integer-valued entries printed as integers.
15 *
16 * The field list follows the reference's, in its order and naming: the C++
17 * NetworkStruct is object-shaped (per-station and per-class records instead of
18 * MATLAB's flat matrices), so every MATLAB field is DERIVED from the records
19 * here (the index maps, isstation / isstateful / isstatedep, phasessz and
20 * phaseshift, csmask, mu / phi / pie from the (D0, D1) pairs, connmatrix from
21 * rtnodes). The few fields this struct does not carry at all -- rtorig, space,
22 * sync, gsync, which the refresh folds away or the CTMC analyzer builds on the
23 * fly -- are the only ones not printed; `network_struct.h` documents why each
24 * is absent. One port-only extra, csmatrix, follows the reference's fields.
25 *
26 * Returned as a string rather than printed, so a caller can route it to a
27 * log, a CLI or a test, following `sn_print_routing_matrix`.
28 *
29 * ARITHMETIC: field, but the rendering is in double.
30 */
31
32#include <cmath>
33#include <cstddef>
34#include <cstdio>
35#include <exception>
36#include <limits>
37#include <map>
38#include <string>
39#include <vector>
40
44#include "line/num/number.h"
45#include "line/util/matrix.h"
46
47namespace line {
48namespace api {
49
50namespace sn_print_detail {
51
52/** One number, integer-rendered when it is one, as the reference prints. */
53inline std::string num(double v) {
54 if (std::isnan(v)) return "NaN";
55 if (std::isinf(v)) return v > 0 ? "Inf" : "-Inf";
56 if (v == std::floor(v) && std::fabs(v) < 1e15) {
57 char buf[32];
58 std::snprintf(buf, sizeof(buf), "%lld", static_cast<long long>(v));
59 return buf;
60 }
61 char buf[32];
62 std::snprintf(buf, sizeof(buf), "%g", v);
63 return buf;
64}
65
66/** The compact `[a b; c d]` form of the reference's printMatrixCompact. */
67inline std::string mat(const std::vector<std::vector<double>>& m) {
68 if (m.empty() || m[0].empty()) return "[]";
69 std::string out = "[";
70 for (std::size_t i = 0; i < m.size(); ++i) {
71 if (i > 0) out += "; ";
72 for (std::size_t j = 0; j < m[i].size(); ++j) {
73 if (j > 0) out += " ";
74 out += num(m[i][j]);
75 }
76 }
77 return out + "]";
78}
79
80inline std::string row(const std::vector<double>& v) {
81 return mat(std::vector<std::vector<double>>(1, v));
82}
83
84template <class T>
85std::string tmat(const Matrix<T>& m) {
86 std::vector<std::vector<double>> d(m.rows(), std::vector<double>(m.cols()));
87 for (std::size_t i = 0; i < m.rows(); ++i)
88 for (std::size_t j = 0; j < m.cols(); ++j) d[i][j] = num_traits<T>::to_double(m(i, j));
89 return mat(d);
90}
91
92inline std::string names(const std::vector<std::string>& v) {
93 if (v.empty()) return "[]";
94 std::string out = "[";
95 for (std::size_t i = 0; i < v.size(); ++i) {
96 if (i > 0) out += ", ";
97 out += "\"" + v[i] + "\"";
98 }
99 return out + "]";
100}
101
102} // namespace sn_print_detail
103
104/** The full `field: value` dump of a NetworkStruct, ending with a newline. */
105template <class T>
106std::string sn_print(const qn::NetworkStruct<T>& sn) {
107 using sn_print_detail::mat;
108 using sn_print_detail::names;
109 using sn_print_detail::num;
110 using sn_print_detail::row;
111 using sn_print_detail::tmat;
112 typedef std::vector<double> Row;
113 typedef std::vector<std::vector<double>> Tab;
114 const std::size_t M = sn.nof_stations(), K = sn.nclasses, I = sn.nof_nodes();
115 const std::size_t S = sn.nof_stateful();
116 const double nan = std::numeric_limits<double>::quiet_NaN();
117 std::string out;
118
119 out += "nstations: " + num(static_cast<double>(M)) + "\n";
120 out += "nstateful: " + num(static_cast<double>(S)) + "\n";
121 out += "nnodes: " + num(static_cast<double>(I)) + "\n";
122 out += "nclasses: " + num(static_cast<double>(K)) + "\n";
123 out += "nclosedjobs: " + num(sn.nclosedjobs()) + "\n";
124 out += "nchains: " + num(static_cast<double>(sn.nchains)) + "\n";
125
126 // ---- the index maps the reference carries as fields; derived here
127 Row isstation(I), isstateful(I), nodeToStateful(I), nodeToStation(I);
128 Tab isstatedep(I, Row(3, 0.0));
129 for (std::size_t i = 0; i < I; ++i) {
130 isstation[i] = sn.nodes[i].station != 0 ? 1.0 : 0.0;
131 isstateful[i] = sn.nodes[i].stateful ? 1.0 : 0.0;
132 nodeToStateful[i] = static_cast<double>(sn.stateful_index(i + 1));
133 nodeToStation[i] = static_cast<double>(sn.nodes[i].station);
134 // refreshStruct.m: col 1 buffer, col 2 service (a Cache), col 3 routing
135 if (sn.nodes[i].nodetype == qn::NodeType::Cache) isstatedep[i][1] = 1.0;
136 for (std::size_t r = 0; r < sn.nodes[i].routing.size(); ++r) {
137 const lang::RoutingStrategy rs = sn.nodes[i].routing[r];
141 isstatedep[i][2] = 1.0;
142 }
143 }
144 Row stationToNode(M), stationToStateful(M), statefulToStation(S), statefulToNode(S);
145 for (std::size_t i = 0; i < M; ++i) {
146 stationToNode[i] = static_cast<double>(sn.node_of_station(i + 1));
147 stationToStateful[i] = static_cast<double>(sn.stateful_of_station(i + 1));
148 }
149 for (std::size_t f = 0; f < S; ++f) {
150 const std::size_t ind = sn.stateful_nodes[f];
151 statefulToNode[f] = static_cast<double>(ind);
152 statefulToStation[f] = static_cast<double>(ind > 0 ? sn.nodes[ind - 1].station : 0);
153 }
154
155 Row refstat(K), njobsv = sn.njobs(), classprio(K);
156 for (std::size_t r = 0; r < K; ++r) {
157 refstat[r] = static_cast<double>(sn.classes[r].refstat);
158 classprio[r] = static_cast<double>(sn.classes[r].prio);
159 }
160 out += "refstat: " + row(refstat) + "\n";
161 out += "njobs: " + row(njobsv) + "\n";
162 Row nservers(M), cap(M);
163 Tab classcap(M, Row(K)), phases(M, Row(K)), phasessz(M, Row(K)), phaseshift(M, Row(K + 1, 0.0));
164 Tab schedparam(M, Row(K, 0.0));
165 Tab droprule(M, Row(K)), procid(M, Row(K));
166 for (std::size_t i = 0; i < M; ++i) {
167 nservers[i] = sn.stations[i].nservers;
168 cap[i] = i < sn.cap.size() ? sn.cap[i] : std::numeric_limits<double>::infinity();
169 double cum = 0.0;
170 for (std::size_t r = 0; r < K; ++r) {
171 classcap[i][r] = i < sn.classcap.size() && r < sn.classcap[i].size()
172 ? sn.classcap[i][r]
173 : std::numeric_limits<double>::infinity();
174 phases[i][r] = static_cast<double>(sn.phases_of(i + 1, r + 1));
175 phasessz[i][r] = static_cast<double>(sn.phasessz_of(i + 1, r + 1));
176 cum += phasessz[i][r];
177 phaseshift[i][r + 1] = cum; // [0, cumsum(phasessz(i,:))]
178 if (r < sn.stations[i].schedparam.size())
179 schedparam[i][r] = num_traits<T>::to_double(sn.stations[i].schedparam[r]);
180 droprule[i][r] = i < sn.droprule.size() && r < sn.droprule[i].size()
181 ? static_cast<double>(static_cast<int>(sn.droprule[i][r]))
182 : static_cast<double>(static_cast<int>(qn::DropStrategy::WAITQ));
183 procid[i][r] = static_cast<double>(static_cast<int>(sn.procid(i + 1, r + 1)));
184 }
185 }
186 out += "nservers: " + row(nservers) + "\n";
187
188 // `sn.connmatrix`: this port carries no link field (see
189 // NetworkStruct::downstream_stations); the node graph is read off rtnodes,
190 // which differs only on a declared link that routes zero mass.
191 {
192 Tab conn(I, Row(I, 0.0));
193 if (sn.rtnodes.rows() == I * K && K > 0)
194 for (std::size_t a = 0; a < I; ++a)
195 for (std::size_t b = 0; b < I; ++b) {
196 bool any = false;
197 for (std::size_t r = 0; r < K && !any; ++r)
198 for (std::size_t s2 = 0; s2 < K && !any; ++s2)
199 any = num_traits<T>::to_double(sn.rtnodes(a * K + r, b * K + s2)) > 0.0;
200 conn[a][b] = any ? 1.0 : 0.0;
201 }
202 out += "connmatrix: " + (I > 0 ? mat(conn) : std::string("[]")) + "\n";
203 }
204 out += "scv: " + tmat(sn.scv) + "\n";
205 out += "isstation: " + row(isstation) + "\n";
206 out += "isstateful: " + row(isstateful) + "\n";
207 out += "isstatedep: " + (I > 0 ? mat(isstatedep) : std::string("[]")) + "\n";
208 out += "nodeToStateful: " + row(nodeToStateful) + "\n";
209 out += "nodeToStation: " + row(nodeToStation) + "\n";
210 out += "stationToNode: " + row(stationToNode) + "\n";
211 out += "stationToStateful: " + row(stationToStateful) + "\n";
212 out += "statefulToStation: " + row(statefulToStation) + "\n";
213 out += "statefulToNode: " + row(statefulToNode) + "\n";
214 out += "rates: " + tmat(sn.rates) + "\n";
215 out += "classprio: " + row(classprio) + "\n";
216 out += "phases: " + mat(phases) + "\n";
217 out += "phasessz: " + mat(phasessz) + "\n";
218 out += "phaseshift: " + mat(phaseshift) + "\n";
219 out += "schedparam: " + mat(schedparam) + "\n";
220
221 Tab chains(sn.chains.size());
222 for (std::size_t c = 0; c < sn.chains.size(); ++c)
223 for (std::size_t r = 0; r < sn.chains[c].size(); ++r)
224 chains[c].push_back(sn.chains[c][r] ? 1.0 : 0.0);
225 out += "chains: " + mat(chains) + "\n";
226 out += "rt: " + tmat(sn.rt) + "\n";
227
228 Tab nvars(sn.nvars.size());
229 for (std::size_t i = 0; i < sn.nvars.size(); ++i)
230 for (std::size_t v = 0; v < sn.nvars[i].size(); ++v)
231 nvars[i].push_back(static_cast<double>(sn.nvars[i][v]));
232 out += "nvars: " + mat(nvars) + "\n";
233 out += "rtnodes: " + tmat(sn.rtnodes) + "\n";
234
235 // `sn.csmask(r,s)`: some node switches class r into class s
236 {
237 Tab csmask(K, Row(K, 0.0));
238 for (typename std::map<std::size_t, Matrix<T>>::const_iterator it = sn.csmatrix.begin();
239 it != sn.csmatrix.end(); ++it)
240 for (std::size_t r = 0; r < K && r < it->second.rows(); ++r)
241 for (std::size_t s2 = 0; s2 < K && s2 < it->second.cols(); ++s2)
242 if (num_traits<T>::to_double(it->second(r, s2)) > 0.0) csmask[r][s2] = 1.0;
243 out += "csmask: " + (K > 0 ? mat(csmask) : std::string("[]")) + "\n";
244 }
245 {
246 Row isslc(K, 0.0);
247 for (std::size_t r = 0; r < K; ++r) isslc[r] = sn.classes[r].self_looping ? 1.0 : 0.0;
248 out += "isslc: " + row(isslc) + "\n";
249 }
250 out += "cap: " + row(cap) + "\n";
251 out += "classcap: " + mat(classcap) + "\n";
252
253 Row refclass(sn.refclass.size());
254 for (std::size_t c = 0; c < sn.refclass.size(); ++c)
255 refclass[c] = static_cast<double>(sn.refclass[c]);
256 out += "refclass: " + row(refclass) + "\n";
257
258 Tab lld;
259 for (std::size_t i = 0; i < M; ++i)
260 if (!sn.stations[i].lldscaling.empty()) {
261 Row rw;
262 for (std::size_t n = 0; n < sn.stations[i].lldscaling.size(); ++n)
263 rw.push_back(num_traits<T>::to_double(sn.stations[i].lldscaling[n]));
264 lld.push_back(rw);
265 }
266 out += "lldscaling: " + (lld.empty() ? std::string("[]") : mat(lld)) + "\n";
267
268 Tab fj(sn.fj.size());
269 for (std::size_t e = 0; e < sn.fj.size(); ++e) {
270 fj[e].push_back(static_cast<double>(sn.fj[e].first));
271 fj[e].push_back(static_cast<double>(sn.fj[e].second));
272 }
273 out += "fj: " + mat(fj) + "\n";
274
275 out += "nodetype: ";
276 if (sn.nodes.empty()) {
277 out += "[]\n";
278 } else {
279 out += "[";
280 for (std::size_t i = 0; i < I; ++i) {
281 if (i > 0) out += ", ";
282 out += lang::node_type_to_text(sn.nodes[i].nodetype);
283 }
284 out += "]\n";
285 }
286 std::vector<std::string> classnames(K), nodenames(I);
287 for (std::size_t r = 0; r < K; ++r) classnames[r] = sn.classes[r].name;
288 for (std::size_t i = 0; i < I; ++i) nodenames[i] = sn.nodes[i].name;
289 out += "classnames: " + names(classnames) + "\n";
290 out += "nodenames: " + names(nodenames) + "\n";
291
292 // `rtorig` is not carried by this port (the refresh folds it into rt), and
293 // `space` is built by the CTMC analyzer rather than stored; neither is
294 // printed. `state` and `stateprior` are the declared pair (statespace,
295 // stateprior), keyed by node name as the reference prints them.
296 out += "state: {";
297 {
298 bool first = true;
299 for (std::size_t f = 0; f < S; ++f) {
300 const std::size_t ind = sn.stateful_nodes[f];
301 if (!first) out += ", ";
302 first = false;
303 out += "\"" + sn.nodes[ind - 1].name + "\": ";
304 const typename std::map<std::size_t, Matrix<T>>::const_iterator it =
305 sn.statespace.find(ind);
306 out += (it == sn.statespace.end() || it->second.rows() == 0) ? std::string("null")
307 : tmat(it->second);
308 }
309 }
310 out += "}\n";
311 out += "stateprior: {";
312 {
313 bool first = true;
314 for (std::size_t f = 0; f < S; ++f) {
315 const std::size_t ind = sn.stateful_nodes[f];
316 if (!first) out += ", ";
317 first = false;
318 out += "\"" + sn.nodes[ind - 1].name + "\": ";
319 const typename std::map<std::size_t, std::vector<T>>::const_iterator it =
320 sn.stateprior.find(ind);
321 if (it == sn.stateprior.end() || it->second.empty()) {
322 out += "null";
323 } else {
324 Row rw;
325 for (std::size_t a = 0; a < it->second.size(); ++a)
326 rw.push_back(num_traits<T>::to_double(it->second[a]));
327 out += row(rw);
328 }
329 }
330 }
331 out += "}\n";
332
333 // `sn.routing(ind, r)`: the RoutingStrategy id per node and class
334 {
335 Tab routing(I, Row(K, static_cast<double>(static_cast<int>(lang::RoutingStrategy::DISABLED))));
336 for (std::size_t i = 0; i < I; ++i)
337 for (std::size_t r = 0; r < K && r < sn.nodes[i].routing.size(); ++r)
338 routing[i][r] = static_cast<double>(static_cast<int>(sn.nodes[i].routing[r]));
339 out += "routing: " + (I > 0 && K > 0 ? mat(routing) : std::string("[]")) + "\n";
340 }
341 out += "procid: " + mat(procid) + "\n";
342
343 // mu, phi, pie: per station and class, from the (D0, D1) representation,
344 // as refreshProcessRepresentations derives them
345 {
346 std::string smu = "mu: {", sphi = "phi: {", spie = "pie: {";
347 for (std::size_t i = 0; i < M; ++i) {
348 if (i > 0) {
349 smu += ", ";
350 sphi += ", ";
351 spie += ", ";
352 }
353 const std::string head = "\"" + sn.stations[i].name + "\": {";
354 smu += head;
355 sphi += head;
356 spie += head;
357 for (std::size_t r = 0; r < K; ++r) {
358 if (r > 0) {
359 smu += ", ";
360 sphi += ", ";
361 spie += ", ";
362 }
363 const std::string ch = "\"" + sn.classes[r].name + "\": ";
364 smu += ch;
365 sphi += ch;
366 spie += ch;
367 const lang::Distrib<T>& d = sn.service[i][r];
368 if (d.disabled || d.D0.rows() == 0 || d.D1.rows() != d.D0.rows()) {
369 smu += "null";
370 sphi += "null";
371 spie += "null";
372 continue;
373 }
374 const std::size_t n = d.D0.rows();
375 Row muv(n), phiv(n), piev(n, nan);
376 for (std::size_t a = 0; a < n; ++a) {
377 const double d0 = -num_traits<T>::to_double(d.D0(a, a));
378 double d1 = 0.0;
379 for (std::size_t b = 0; b < n; ++b) d1 += num_traits<T>::to_double(d.D1(a, b));
380 muv[a] = d0;
381 phiv[a] = d0 != 0.0 ? d1 / d0 : 0.0;
382 }
383 try {
384 mam::Map<T> m;
385 m.D0 = d.D0;
386 m.D1 = d.D1;
387 const std::vector<T> pie = mam::map_pie(m);
388 for (std::size_t a = 0; a < n && a < pie.size(); ++a)
389 piev[a] = num_traits<T>::to_double(pie[a]);
390 } catch (const std::exception&) {
391 // a degenerate (D0, D1) has no embedded law to report
392 }
393 smu += row(muv);
394 sphi += row(phiv);
395 spie += row(piev);
396 }
397 smu += "}";
398 sphi += "}";
399 spie += "}";
400 }
401 out += smu + "}\n" + sphi + "}\n";
402 out += "proc: {";
403 for (std::size_t i = 0; i < M; ++i) {
404 if (i > 0) out += ", ";
405 out += "\"" + sn.stations[i].name + "\": {";
406 for (std::size_t r = 0; r < K; ++r) {
407 if (r > 0) out += ", ";
408 const lang::Distrib<T>& d = sn.service[i][r];
409 out += "\"" + sn.classes[r].name + "\": ";
410 if (d.disabled) {
411 out += "null";
412 } else {
413 out += std::string("{\"type\": \"") + lang::process_to_text(d.type) +
414 "\", \"mean\": " + num(num_traits<T>::to_double(d.mean)) +
415 ", \"scv\": " + num(num_traits<T>::to_double(d.scv)) + "}";
416 }
417 }
418 out += "}";
419 }
420 out += "}\n";
421 out += spie + "}\n";
422 }
423
424 out += "sched: {";
425 for (std::size_t i = 0; i < M; ++i) {
426 if (i > 0) out += ", ";
427 out += "\"" + sn.stations[i].name + "\": \"" +
428 lang::sched_to_text(sn.stations[i].sched) + "\"";
429 }
430 out += "}\n";
431
432 out += "inchain: {";
433 for (std::size_t c = 0; c < sn.inchain.size(); ++c) {
434 if (c > 0) out += ", ";
435 Row rw;
436 for (std::size_t k = 0; k < sn.inchain[c].size(); ++k)
437 rw.push_back(static_cast<double>(sn.inchain[c][k]));
438 out += num(static_cast<double>(c)) + ": " + row(rw);
439 }
440 out += "}\n";
441
442 out += "visits: {";
443 for (std::size_t c = 0; c < sn.visits.size(); ++c) {
444 if (c > 0) out += ", ";
445 out += num(static_cast<double>(c)) + ": " + tmat(sn.visits[c]);
446 }
447 out += "}\n";
448 out += "nodevisits: {";
449 for (std::size_t c = 0; c < sn.nodevisits.size(); ++c) {
450 if (c > 0) out += ", ";
451 out += num(static_cast<double>(c)) + ": " + tmat(sn.nodevisits[c]);
452 }
453 out += "}\n";
454 out += "droprule: " + mat(droprule) + "\n";
455
456 // `sn.nodeparam`: the per-node parameter blocks this struct carries, keyed
457 // by node name; a node with none prints null
458 out += "nodeparam: {";
459 for (std::size_t i = 0; i < I; ++i) {
460 if (i > 0) out += ", ";
461 out += "\"" + sn.nodes[i].name + "\": ";
462 const std::size_t ind = i + 1;
463 std::string blk;
464 {
465 const typename std::map<std::size_t, qn::CacheParam<T>>::const_iterator it =
466 sn.nodeparam.find(ind);
467 if (it != sn.nodeparam.end()) {
468 Row cap_r, hit, miss;
469 for (std::size_t a = 0; a < it->second.itemcap.size(); ++a)
470 cap_r.push_back(it->second.itemcap[a]);
471 for (std::size_t a = 0; a < it->second.hitclass.size(); ++a)
472 hit.push_back(static_cast<double>(it->second.hitclass[a]));
473 for (std::size_t a = 0; a < it->second.missclass.size(); ++a)
474 miss.push_back(static_cast<double>(it->second.missclass[a]));
475 blk = "{\"nitems\": " + num(static_cast<double>(it->second.nitems)) +
476 ", \"itemcap\": " + row(cap_r) + ", \"hitclass\": " + row(hit) +
477 ", \"missclass\": " + row(miss) + "}";
478 }
479 }
480 if (blk.empty()) {
481 const typename std::map<std::size_t, typename qn::NetworkStruct<T>::JoinDecl>::const_iterator
482 it = sn.joindecl.find(ind);
483 if (it != sn.joindecl.end())
484 blk = "{\"joinStrategy\": " +
485 num(static_cast<double>(static_cast<int>(it->second.strategy))) +
486 ", \"joinRequired\": " + num(it->second.quorum) + "}";
487 }
488 if (blk.empty()) {
489 const typename std::map<std::size_t, qn::TransitionParam<T>>::const_iterator it =
490 sn.transparam.find(ind);
491 if (it != sn.transparam.end())
492 blk = "{\"nmodes\": " + num(static_cast<double>(it->second.nmodes)) +
493 ", \"modenames\": " + names(it->second.modenames) + "}";
494 }
495 if (blk.empty()) {
496 const typename std::map<std::size_t, qn::ForkParam<T>>::const_iterator it =
497 sn.forkparam.find(ind);
498 if (it != sn.forkparam.end())
499 blk = "{\"fanOut\": " + tmat(it->second.fan_out_link) + "}";
500 else if (sn.nodes[i].nodetype == qn::NodeType::Fork)
501 blk = "{\"fanOut\": " + num(sn.nodes[i].tasks_per_link) + "}";
502 }
503 out += blk.empty() ? std::string("null") : blk;
504 }
505 out += "}\n";
506
507 // `sync` and `gsync` are built by refresh_sync / refresh_global_sync when a
508 // state-space solver runs and are not stored on this struct, so they are
509 // not printed. cdscaling / jdscaling are function handles in the reference
510 // and print as presence here.
511 {
512 std::string scd = "cdscaling: {", sjd = "jdscaling: {";
513 for (std::size_t i = 0; i < M; ++i) {
514 if (i > 0) {
515 scd += ", ";
516 sjd += ", ";
517 }
518 scd += "\"" + sn.stations[i].name + "\": " +
519 (static_cast<bool>(sn.stations[i].cdscaling) ? "\"<function>\"" : "null");
520 sjd += "\"" + sn.stations[i].name + "\": " +
521 (static_cast<bool>(sn.stations[i].jdscaling) ? "\"<function>\"" : "null");
522 }
523 out += scd + "}\n" + sjd + "}\n";
524 }
525
526 // port-only extra, after the reference's fields
527 out += "csmatrix: {";
528 {
529 bool first = true;
530 for (typename std::map<std::size_t, Matrix<T>>::const_iterator it = sn.csmatrix.begin();
531 it != sn.csmatrix.end(); ++it) {
532 if (!first) out += ", ";
533 first = false;
534 out += num(static_cast<double>(it->first)) + ": " + tmat(it->second);
535 }
536 }
537 out += "}\n";
538 return out;
539}
540
541} // namespace api
542} // namespace line
543
544#endif // LINE_API_SN_SN_PRINT_H
bool empty() const
Definition matrix.h:92
A network plus its refreshed NetworkStruct.
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
Dense matrix and non-owning view.
std::string sn_print(const qn::NetworkStruct< T > &sn)
The full field: value dump of a NetworkStruct, ending with a newline.
Definition sn_print.h:106
RoutingStrategy
Routing strategies, with the values of MATLAB RoutingStrategy.
Definition lang_types.h:396
@ SDR
Krzesinski (1987) product-form state-dependent routing.
Definition lang_types.h:405
const char * node_type_to_text(NodeType t)
Name of a node kind, for diagnostics.
Definition lang_types.h:348
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
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
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
A queueing network and its refreshed NetworkStruct.
Number-type abstraction for the templated API port.
Matrix< T > D0
The (D0,D1) pair when the type carries one directly.
Definition lang_types.h:863
A MAP as the pair of matrices (D0, D1).
Definition map_moment.h:53
Matrix< T > D1
Definition map_moment.h:55
Matrix< T > D0
Definition map_moment.h:54