LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
linemodel_writer.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_IO_LINEMODEL_WRITER_H
6#define LINE_IO_LINEMODEL_WRITER_H
7
8/**
9 * @file
10 * @ingroup line_io
11 * model.json writers for a LayeredNetwork, a Workflow and an Environment: the
12 * `layered2json`, `workflow2json` and `environment2json` branches of
13 * `linemodel_save.m`, and the inverses of `lqn_json_reader.h`,
14 * `workflow_reader.h` and `environment_reader.h`. A Network is written by
15 * `network_writer.h`, which the Environment writer reuses for every stage.
16 *
17 * WHICH SPELLING. The four codebases' writers disagree on a few optional keys,
18 * and every reader accepts the union. Where they disagree this writer takes the
19 * form every reader decodes to the same value:
20 *
21 * host multiplicity/scheduling/quantum/speedFactor always written (Python, JAR);
22 * MATLAB omits the defaults, and the JAR and native readers default them differently
23 * task multiplicity/scheduling, call `mean` always written (Python, JAR)
24 * task think time `thinkTime` plus `thinkTimeMean`/`thinkTimeSCV` (Python, JAR)
25 * activity `hostDemand` always written, an Immediate as `{"type":"Immediate"}` (Python, JAR)
26 * ItemEntry `accessProb` a DiscreteSampler object over x = 1..n; the JAR reads only
27 * the object form, and Python's bare array is unreadable there
28 * entry `forwarding`, activity `thinkTime` JAR keys, read by the JAR and C++ readers
29 * precedences the TYPED form (`type` + `activities`), as MATLAB and Python write it
30 * Loop `loopCount` written whenever the count is declared, as MATLAB does
31 * AND-join quorum `preParams: [q]`, only when a quorum is set, as MATLAB does
32 * task `priority`, activity `callOrder` written when not the default (0, STOCHASTIC), as the JAR does
33 *
34 * WHAT IS DROPPED. An entry's `type` (PH1PH2/NONE) and a host declared as `Host` rather than
35 * `Processor` have no key in any codebase or in the schema; no solver reads either, so the
36 * document reloads as the same model and they are left out rather than refused.
37 *
38 * WHAT IS REFUSED. An `LqnModel` field with no wire key in ANY codebase's
39 * reader is refused by name rather than dropped, because a document that
40 * reloads as a different model is worse than no document: a phase-2 activity,
41 * a call group (RROBIN/JSQ), delayed-hit retrieval, the
42 * load/class/joint dependence tables and server pools. An Environment stage
43 * holding a LayeredNetwork is refused too, since its `LqnStruct` cannot be
44 * turned back into the declared model and no reference writer serializes it.
45 *
46 * KEY ORDER is alphabetical, since `nlohmann::json` keeps objects sorted, where
47 * the MATLAB and Python writers keep insertion order. JSON objects are unordered
48 * and every reader looks keys up by name, so the order carries no meaning.
49 */
50
51#include <cmath>
52#include <fstream>
53#include <iostream>
54#include <map>
55#include <string>
56#include <vector>
57
63#include "line/util/error.h"
64
65namespace line {
66namespace io {
67
68namespace detail {
69
70template <class T>
71double to_d(const T& x) {
72 return num_traits<T>::to_double(x);
73}
74
75/** A count as a JSON integer when it is one, as the reference writers emit it. */
76inline json count_to_json(double v) {
77 if (std::isfinite(v) && v == std::floor(v) && std::fabs(v) < 9.0e15)
78 return json(static_cast<long long>(v));
79 return json(v);
80}
81
82/** `inf_multiplicity()`: an infinite multiplicity is Java's Integer.MAX_VALUE on the wire. */
83inline json lqn_mult_to_json(double m) {
84 if (!std::isfinite(m)) return json(2147483647LL);
85 return count_to_json(m);
86}
87
88/** A distribution that is really there: neither unset, immediate nor of zero mean. */
89template <class T>
90bool lqn_dist_declared(const lang::Distrib<T>& d) {
91 return d.type != lang::ProcessType::DISABLED && d.type != lang::ProcessType::IMMEDIATE &&
92 to_d(d.mean) > lang::GlobalConstants::FineTol;
93}
94
95/** `prectype_to_str`: the JAR-compatible precedence spelling of the Workflow wire. */
96inline const char* prectype_to_str(lang::PrecedenceType t) {
97 switch (t) {
98 case lang::PrecedenceType::PRE_SEQ: return "pre";
99 case lang::PrecedenceType::PRE_AND: return "pre-AND";
100 case lang::PrecedenceType::PRE_OR: return "pre-OR";
101 case lang::PrecedenceType::POST_SEQ: return "post";
102 case lang::PrecedenceType::POST_AND: return "post-AND";
103 case lang::PrecedenceType::POST_OR: return "post-OR";
104 case lang::PrecedenceType::POST_LOOP: return "post-LOOP";
105 case lang::PrecedenceType::POST_CACHE: return "post-CACHE";
106 default: break;
107 }
108 throw UnsupportedError("linemodel_writer: a precedence carries a type with no wire spelling");
109}
110
111/**
112 * `lincon2json`: admission-constraint rows in the NAMED form. A positional (A, b)
113 * is resolved against `cols`, the element's operands in declaration order.
114 */
115template <class T>
116json lincon_to_json(const std::vector<lqn::detail::RawLinConRow<T>>& named, const Matrix<T>* A,
117 const std::vector<T>* b, const std::vector<std::string>& cols,
118 const std::string& elem) {
119 json rows = json::array();
120 if (A != NULL && !A->empty()) {
121 for (std::size_t r = 0; r < A->rows(); ++r) {
122 json ops = json::array(), cf = json::array();
123 for (std::size_t c = 0; c < A->cols(); ++c) {
124 const double a = to_d((*A)(r, c));
125 if (a == 0.0) continue;
126 if (c >= cols.size())
127 throw InputError("linemodel_save: admission constraint on " + elem +
128 " references column " + std::to_string(c + 1) +
129 " but the element has only " + std::to_string(cols.size()) +
130 " operands");
131 ops.push_back(cols[c]);
132 cf.push_back(a);
133 }
134 if (ops.empty()) continue;
135 json row;
136 row["operands"] = ops;
137 row["coeffs"] = cf;
138 row["cap"] = to_d(b->at(r));
139 rows.push_back(row);
140 }
141 }
142 for (const lqn::detail::RawLinConRow<T>& nr : named) {
143 json row;
144 row["operands"] = nr.names;
145 json cf = json::array();
146 for (const T& c : nr.coeffs) cf.push_back(to_d(c));
147 row["coeffs"] = cf;
148 row["cap"] = to_d(nr.cap);
149 rows.push_back(row);
150 }
151 return rows;
152}
153
154/** Refuse, by name, a declared LqnModel feature no reader on the wire can rebuild. */
155inline void lqn_refuse(const std::string& elem, const std::string& what) {
156 throw UnsupportedError("linemodel_save: " + elem + " declares " + what +
157 ", which no model.json reader (MATLAB, JAR, Python or C++) rebuilds; "
158 "refusing rather than writing a document that reloads as a "
159 "different model");
160}
161
162template <class T>
163void lqn_refuse_dependence(const std::string& elem, const std::vector<T>& lld,
164 const lang::CdScaling<T>& cd, const lang::CdScaling<T>& jd) {
165 if (!lld.empty()) lqn_refuse(elem, "a load-dependent service scaling");
166 if (cd) lqn_refuse(elem, "a class-dependent service scaling");
167 if (jd) lqn_refuse(elem, "a joint-dependent service scaling");
168}
169
170/** One typed-form precedence, following `layered2json`. */
171template <class T>
172json lqn_precedence_to_json(const lqn::detail::RawPrecedence<T>& p, const std::string& task) {
173 typedef lang::PrecedenceType P;
174 json pj;
175 pj["task"] = task;
176 std::vector<std::string> both(p.preacts);
177 both.insert(both.end(), p.postacts.begin(), p.postacts.end());
178 if (p.pretype == P::PRE_SEQ && p.posttype == P::POST_SEQ) {
179 pj["type"] = "Serial";
180 pj["activities"] = both;
181 } else if (p.pretype == P::PRE_SEQ && p.posttype == P::POST_AND) {
182 pj["type"] = "AndFork";
183 pj["activities"] = both;
184 } else if (p.pretype == P::PRE_AND && p.posttype == P::POST_SEQ) {
185 pj["type"] = "AndJoin";
186 pj["activities"] = both;
187 // the quorum travels as a one-element preParams, as MATLAB linemodel_save writes it
188 if (p.has_quorum) pj["preParams"] = json::array({p.quorum});
189 } else if (p.pretype == P::PRE_SEQ && p.posttype == P::POST_OR) {
190 pj["type"] = "OrFork";
191 pj["activities"] = both;
192 if (!p.postparams.empty()) {
193 json pr = json::array();
194 for (const T& x : p.postparams) pr.push_back(to_d(x));
195 pj["probabilities"] = pr;
196 }
197 } else if (p.pretype == P::PRE_OR && p.posttype == P::POST_SEQ) {
198 if (!p.preparams.empty()) lqn_refuse("task '" + task + "'", "OR-join branch probabilities");
199 pj["type"] = "OrJoin";
200 pj["activities"] = both;
201 } else if (p.posttype == P::POST_LOOP) {
202 // postacts is the body followed by the exit; the trigger travels as preActivity
203 pj["type"] = "Loop";
204 pj["activities"] = p.postacts;
205 if (!p.preacts.empty()) pj["preActivity"] = p.preacts[0];
206 if (!p.postparams.empty()) {
207 for (const T& c : p.postparams)
208 if (to_d(c) != to_d(p.postparams[0]))
209 lqn_refuse("task '" + task + "'", "a loop whose body activities repeat "
210 "different numbers of times");
211 pj["loopCount"] = to_d(p.postparams[0]);
212 }
213 } else if (p.pretype == P::PRE_SEQ && p.posttype == P::POST_CACHE) {
214 pj["type"] = "CacheAccess";
215 pj["activities"] = both;
216 } else {
217 lqn_refuse("task '" + task + "'", "a precedence of a pre/post type pair with no typed form");
218 }
219 return pj;
220}
221
222} // namespace detail
223
224/** `layered2json`: the `model` object of a LayeredNetwork document. */
225template <class T>
226detail::json lqn_model_to_json(const lqn::LqnModel<T>& m) {
227 using detail::json;
228 using detail::to_d;
229 json out;
230 out["type"] = "LayeredNetwork";
231 out["name"] = m.name.empty() ? std::string("LQN") : m.name;
232
233 // Operands of a host constraint are its tasks, of a task constraint its entries.
234 std::vector<std::vector<std::string>> tasks_of(m.procs.size()), entries_of(m.tasks.size());
235 for (const auto& t : m.tasks) tasks_of.at(t.proc_slot).push_back(t.name);
236 for (const auto& e : m.entries) entries_of.at(e.task_slot).push_back(e.name);
237
238 json hosts = json::array();
239 for (std::size_t i = 0; i < m.procs.size(); ++i) {
240 const lqn::detail::RawProc& p = m.procs[i];
241 const std::string who = "host '" + p.name + "'";
242 if (m.proc_lldscaling.count(i) || m.proc_cdscaling.count(i) || m.proc_jdscaling.count(i))
243 detail::lqn_refuse(who, "a queue-dependent service scaling");
244 if (m.proc_pools.count(i) && !m.proc_pools.at(i).empty())
245 detail::lqn_refuse(who, "server pools");
246 json h;
247 h["name"] = p.name;
248 h["multiplicity"] = detail::lqn_mult_to_json(p.mult);
249 h["scheduling"] = detail::sched_to_json(p.sched);
250 h["quantum"] = p.quantum;
251 h["speedFactor"] = p.speed_factor;
252 if (p.repl > 1) h["replication"] = detail::count_to_json(p.repl);
253 static const std::vector<lqn::detail::RawLinConRow<T>> kNone;
254 const auto named = m.proc_linconrows.find(i);
255 const auto pos = m.proc_lincon.find(i);
256 const json rows = detail::lincon_to_json<T>(
257 named == m.proc_linconrows.end() ? kNone : named->second,
258 pos == m.proc_lincon.end() ? NULL : &pos->second.first,
259 pos == m.proc_lincon.end() ? NULL : &pos->second.second, tasks_of[i], p.name);
260 if (!rows.empty()) h["admissionConstraints"] = rows;
261 hosts.push_back(h);
262 }
263 out["hosts"] = hosts;
264
265 json tasks = json::array();
266 for (std::size_t i = 0; i < m.tasks.size(); ++i) {
267 const lqn::detail::RawTask<T>& t = m.tasks[i];
268 const std::string who = "task '" + t.name + "'";
269 detail::lqn_refuse_dependence(who, t.lldscaling, t.cdscaling, t.jdscaling);
270 if (!t.pools.empty()) detail::lqn_refuse(who, "server pools");
271 if (t.retrieval) detail::lqn_refuse(who, "delayed-hit retrieval (setRetrieval)");
272 json tj;
273 tj["name"] = t.name;
274 tj["host"] = m.procs.at(t.proc_slot).name;
275 tj["multiplicity"] = detail::lqn_mult_to_json(t.mult);
276 tj["scheduling"] = detail::sched_to_json(t.sched);
277 if (t.repl > 1) tj["replication"] = detail::count_to_json(t.repl);
278 if (t.priority != 0) tj["priority"] = t.priority;
279 if (detail::lqn_dist_declared(t.thinktime)) {
280 tj["thinkTime"] = detail::dist_to_json(t.thinktime);
281 tj["thinkTimeMean"] = to_d(t.thinktime.mean);
282 tj["thinkTimeSCV"] = to_d(t.thinktime.scv);
283 }
284 if (!t.fanin.empty()) {
285 json fi = json::object();
286 for (const auto& kv : t.fanin) fi[kv.first] = detail::count_to_json(kv.second);
287 tj["fanIn"] = fi;
288 }
289 if (!t.fanout.empty()) {
290 json fo = json::object();
291 for (const auto& kv : t.fanout) fo[kv.first] = detail::count_to_json(kv.second);
292 tj["fanOut"] = fo;
293 }
294 const json rows = detail::lincon_to_json<T>(t.linconrows, &t.lincon_A, &t.lincon_b,
295 entries_of[i], t.name);
296 if (!rows.empty()) tj["admissionConstraints"] = rows;
297 const bool has_setup = detail::lqn_dist_declared(t.setuptime);
298 if (has_setup) tj["setupTime"] = detail::dist_to_json(t.setuptime);
299 if (detail::lqn_dist_declared(t.delayofftime))
300 tj["delayOffTime"] = detail::dist_to_json(t.delayofftime);
301 if (t.nitems > 0) {
302 tj["taskType"] = "CacheTask";
303 tj["totalItems"] = static_cast<long long>(t.nitems);
304 tj["cacheCapacity"] = t.itemcap;
305 tj["replacementStrategy"] = detail::replacement_to_json(t.replacestrat);
306 } else if (has_setup) {
307 tj["taskType"] = "SetupTask";
308 }
309 tasks.push_back(tj);
310 }
311 out["tasks"] = tasks;
312
313 json entries = json::array();
314 std::map<std::string, std::string> reply_of; // activity -> entry it replies to
315 for (const lqn::detail::RawEntry<T>& e : m.entries) {
316 json ej;
317 ej["name"] = e.name;
318 ej["task"] = m.tasks.at(e.task_slot).name;
319 if (e.has_arrival) ej["arrival"] = detail::dist_to_json(e.arrival);
320 if (!e.fwd_dest.empty()) {
321 json fw = json::array();
322 for (std::size_t k = 0; k < e.fwd_dest.size(); ++k) {
323 json f;
324 f["dest"] = e.fwd_dest[k];
325 f["prob"] = k < e.fwd_prob.size() ? to_d(e.fwd_prob[k]) : 1.0;
326 fw.push_back(f);
327 }
328 ej["forwarding"] = fw;
329 }
330 if (e.cardinality > 0) {
331 ej["entryType"] = "ItemEntry";
332 ej["totalItems"] = static_cast<long long>(e.cardinality);
333 if (!e.popularity.empty()) {
334 std::vector<T> x;
335 for (std::size_t k = 1; k <= e.popularity.size(); ++k)
336 x.push_back(num_traits<T>::from_int(static_cast<long>(k)));
337 ej["accessProb"] =
338 detail::dist_to_json(lang::Distrib<T>::discrete_sampler(e.popularity, x));
339 }
340 }
341 for (const std::string& a : e.reply_activities) reply_of[a] = e.name;
342 entries.push_back(ej);
343 }
344 out["entries"] = entries;
345
346 json acts = json::array();
347 for (const lqn::detail::RawActivity<T>& a : m.acts) {
348 const std::string who = "activity '" + a.name + "'";
349 if (a.phase != 1) detail::lqn_refuse(who, "phase " + std::to_string(a.phase));
350 if (!a.call_groups.empty()) detail::lqn_refuse(who, "a routed call group (RROBIN/JSQ)");
351 json aj;
352 aj["name"] = a.name;
353 aj["task"] = m.tasks.at(a.task_slot).name;
354 aj["hostDemand"] = detail::dist_to_json(a.hostdem);
355 if (!a.bound_to_entry.empty()) aj["boundToEntry"] = a.bound_to_entry;
356 const auto r = reply_of.find(a.name);
357 if (r != reply_of.end()) aj["repliesTo"] = r->second;
358 if (detail::lqn_dist_declared(a.thinktime)) aj["thinkTime"] = detail::dist_to_json(a.thinktime);
359 if (a.call_order != "STOCHASTIC") aj["callOrder"] = a.call_order;
360 const char* kKey[2] = {"synchCalls", "asynchCalls"};
361 const std::vector<lqn::detail::RawCall<T>>* calls[2] = {&a.sync_calls, &a.async_calls};
362 for (int w = 0; w < 2; ++w) {
363 if (calls[w]->empty()) continue;
364 json arr = json::array();
365 for (const lqn::detail::RawCall<T>& c : *calls[w]) {
366 json cj;
367 cj["dest"] = c.dest;
368 cj["mean"] = to_d(c.mean);
369 arr.push_back(cj);
370 }
371 aj[kKey[w]] = arr;
372 }
373 acts.push_back(aj);
374 }
375 out["activities"] = acts;
376
377 json precs = json::array();
378 for (const lqn::detail::RawTask<T>& t : m.tasks)
379 for (const lqn::detail::RawPrecedence<T>& p : t.precedences)
380 precs.push_back(detail::lqn_precedence_to_json(p, t.name));
381 if (!precs.empty()) out["precedences"] = precs;
382 return out;
383}
384
385/** `workflow2json`: the `model` object of a Workflow document. */
386template <class T>
388 using detail::json;
389 json out;
390 out["type"] = "Workflow";
391 out["name"] = wf.name();
392 json acts = json::array();
393 for (const workflow::WorkflowActivity<T>& a : wf.activities()) {
394 json aj;
395 aj["name"] = a.name();
396 aj["hostDemand"] = detail::dist_to_json(a.host_demand());
397 acts.push_back(aj);
398 }
399 out["activities"] = acts;
400 json precs = json::array();
401 for (const workflow::Precedence<T>& p : wf.precedences()) {
402 json pj;
403 pj["preActs"] = p.pre_acts;
404 pj["postActs"] = p.post_acts;
405 pj["preType"] = detail::prectype_to_str(p.pre_type);
406 pj["postType"] = detail::prectype_to_str(p.post_type);
407 if (!p.pre_params.empty()) pj["preParams"] = detail::vec_to_json(p.pre_params);
408 if (!p.post_params.empty()) pj["postParams"] = detail::vec_to_json(p.post_params);
409 precs.push_back(pj);
410 }
411 out["precedences"] = precs;
412 return out;
413}
414
415/**
416 * `environment2json`: the `model` object of an Environment document.
417 *
418 * The stages go out EXPANDED, each with its own network, plus the `nodeFailures`
419 * descriptors that alone carry the reset policies, as both reference writers do.
420 * A reset FUNCTION on an ordinary arc has no wire form in any codebase; it is
421 * dropped with a warning, as MATLAB warns for a custom node-failure policy.
422 */
423template <class T>
425 using detail::json;
426 json out;
427 out["type"] = "Environment";
428 out["name"] = e.name();
429 const std::size_t E = e.nstages();
430 out["numStages"] = static_cast<long long>(E);
431
432 json stages = json::array();
433 for (std::size_t s = 0; s < E; ++s) {
434 const env::EnvStage<T>& st = e.stage(s);
435 if (st.has_lqn)
436 throw UnsupportedError(
437 "linemodel_save: stage '" + st.name +
438 "' holds a LayeredNetwork, whose finalized LqnStruct cannot be written back as "
439 "the declared model; no reference writer serializes a layered stage either");
440 json sj;
441 sj["name"] = st.name;
442 if (!st.type.empty()) sj["type"] = st.type;
443 if (st.has_model) sj["model"] = network_to_json(st.model);
444 stages.push_back(sj);
445 }
446 out["stages"] = stages;
447
448 // Arcs into or out of a DOWN_<node> stage are a node failure's, whose reset travels in nodeFailures.
449 std::vector<bool> failure_stage(E, false);
450 for (const env::NodeFailure<T>& nf : e.node_failures()) {
451 const std::size_t d = e.find_stage(env::Environment<T>::down_stage_name(nf.node));
452 if (d < E) failure_stage[d] = true;
453 }
454 json trans = json::array();
455 for (std::size_t a = 0; a < E; ++a)
456 for (std::size_t b = 0; b < E; ++b) {
457 const env::EnvArc<T>& arc = e.arc(a, b);
458 if (!arc.enabled || arc.dist.type == lang::ProcessType::DISABLED) continue;
459 if ((arc.reset || arc.reset_rates) && !failure_stage[a] && !failure_stage[b])
460 std::cerr << "[LINE] Warning: linemodel_save: the transition " << e.stage(a).name
461 << " -> " << e.stage(b).name
462 << " carries a reset function, which cannot be serialized to JSON; the "
463 "saved model reloads without it.\n";
464 json tj;
465 tj["from"] = static_cast<long long>(a);
466 tj["to"] = static_cast<long long>(b);
467 tj["distribution"] = detail::dist_to_json(arc.dist);
468 trans.push_back(tj);
469 }
470 out["transitions"] = trans;
471
472 json fails = json::array();
473 for (const env::NodeFailure<T>& nf : e.node_failures()) {
474 json nj;
475 nj["node"] = nf.node;
476 nj["breakdownRate"] = detail::dist_to_json(nf.breakdown);
477 if (nf.has_repair) nj["repairRate"] = detail::dist_to_json(nf.repair);
478 nj["downService"] = detail::dist_to_json(nf.down_service);
479 if (nf.breakdown_reset == "custom")
480 std::cerr << "[LINE] Warning: linemodel_save: node failure on \"" << nf.node
481 << "\" uses a custom breakdown reset function, which cannot be serialized "
482 "to JSON; the saved model falls back to the 'keep' policy on reload.\n";
483 else
484 nj["breakdownResetPolicy"] = nf.breakdown_reset;
485 if (!nf.repair_reset.empty()) {
486 if (nf.repair_reset == "custom")
487 std::cerr << "[LINE] Warning: linemodel_save: node failure on \"" << nf.node
488 << "\" uses a custom repair reset function, which cannot be serialized "
489 "to JSON; the saved model falls back to the 'keep' policy on reload.\n";
490 else
491 nj["repairResetPolicy"] = nf.repair_reset;
492 }
493 fails.push_back(nj);
494 }
495 if (!fails.empty()) out["nodeFailures"] = fails;
496 return out;
497}
498
499/** The `{format, version, model}` envelope around a `model` object, with the wire's non-finites. */
500inline detail::json linemodel_envelope(const detail::json& model) {
501 detail::json root;
502 root["format"] = "line-model";
503 root["version"] = "1.0";
504 root["model"] = model;
505 detail::wire_nonfinite(root);
506 return root;
507}
508
509/** Write an envelope, indented as `write_network_json` indents it. */
510inline void write_linemodel_json(const detail::json& root, const std::string& path) {
511 std::ofstream out(path.c_str());
512 if (!out) throw InputError("linemodel_save: cannot open " + path + " for writing");
513 out << root.dump(2) << "\n";
514}
515
516} // namespace io
517} // namespace line
518
519#endif // LINE_IO_LINEMODEL_WRITER_H
InputError(const std::string &what)
Definition error.h:39
UnsupportedError(const std::string &what)
Definition error.h:51
const std::string & name() const
const std::vector< NodeFailure< T > > & node_failures() const
nodeFailures, the declarative record of the breakdowns declared here.
const EnvStage< T > & stage(std::size_t e) const
std::size_t find_stage(const std::string &nm) const
The index of the stage called nm, or nstages() when there is none.
std::size_t nstages() const
static std::string down_stage_name(const std::string &nm)
The name addNodeBreakdown gives the stage in which nm is down.
const EnvArc< T > & arc(std::size_t e, std::size_t h) const
A computational activity.
Definition workflow.h:101
A random environment: a port of matlab/src/lang/Environment.m, restricted to what SolverENV reads out...
The exception types the port throws.
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
.lqnx -> LqnStruct, a port of matlab/src/lang/layered/@LayeredNetwork/parseXML.m followed by ....
detail::json environment_to_json(const env::Environment< T > &e)
environment2json: the model object of an Environment document.
detail::json network_to_json(const qn::NetworkStruct< T > &sn)
qn::NetworkStruct -> the model.json model object.
detail::json lqn_model_to_json(const lqn::LqnModel< T > &m)
layered2json: the model object of a LayeredNetwork document.
detail::json linemodel_envelope(const detail::json &model)
The {format, version, model} envelope around a model object, with the wire's non-finites.
detail::json workflow_to_json(const workflow::Workflow< T > &wf)
workflow2json: the model object of a Workflow document.
void write_linemodel_json(const detail::json &root, const std::string &path)
Write an envelope, indented as write_network_json indents it.
PrecedenceType
Activity precedence kinds, with the values of MATLAB ActivityPrecedenceType.
Definition lang_types.h:472
std::function< std::vector< T >(const std::vector< T > &)> CdScaling
A class-dependent scaling map, sn.cdscaling.
Definition lang_types.h:731
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
qn::NetworkStruct -> model.json, the inverse of network_reader.h.
One arc of the environment process.
lang::Distrib< T > dist
the e -> h transition time
ResetMarginal reset
empty means the identity
ResetEnvRates< T > reset_rates
empty means the rate does not depend on the state
One stage: a name, a category, and the model in force while it lasts.
std::string type
the stage category, informational only
qn::NetworkStruct< T > model
Environment.nodeFailures{k}: the declarative record of one node breakdown.
static Distrib discrete_sampler(const std::vector< T > &p, const std::vector< T > &x)
DiscreteSampler(p, x): the pmf p over the points x.
static constexpr double FineTol
Definition lang_types.h:760
The intermediate model, and the second stage that flattens it.
Definition lqn_reader.h:409
std::vector< detail::RawTask< T > > tasks
Definition lqn_reader.h:413
std::vector< detail::RawActivity< T > > acts
Definition lqn_reader.h:415
std::map< std::size_t, std::vector< detail::RawServerPool< T > > > proc_pools
Definition lqn_reader.h:439
std::vector< detail::RawProc > procs
Definition lqn_reader.h:412
std::map< std::size_t, CdScaling< T > > proc_jdscaling
Definition lqn_reader.h:437
std::map< std::size_t, std::pair< Matrix< T >, std::vector< T > > > proc_lincon
Definition lqn_reader.h:425
std::map< std::size_t, std::vector< detail::RawLinConRow< T > > > proc_linconrows
Admission constraints declared on a HOST, by 0-based processor slot.
Definition lqn_reader.h:424
std::map< std::size_t, CdScaling< T > > proc_cdscaling
Definition lqn_reader.h:435
std::string name
LayeredNetwork.getName(); empty when unnamed.
Definition lqn_reader.h:411
std::map< std::size_t, std::vector< T > > proc_lldscaling
Queue-dependent service rates and compatibility pools declared on a HOST, by 0-based processor slot.
Definition lqn_reader.h:434
std::vector< detail::RawEntry< T > > entries
Definition lqn_reader.h:414
One precedence of the activity graph.
Definition workflow.h:78
An activity workflow reduced to one phase-type law.