5#ifndef LINE_IO_CODE_GEN_H
6#define LINE_IO_CODE_GEN_H
73inline std::string
mfmt(
const char* spec,
double x) {
74 if (std::isnan(x))
return "NaN";
75 if (std::isinf(x))
return x > 0 ?
"Inf" :
"-Inf";
77 std::snprintf(buf,
sizeof(buf), spec, x);
78 return std::string(buf);
81inline std::string
fmt_f(
double x) {
return mfmt(
"%f", x); }
82inline std::string
fmt_g(
double x) {
return mfmt(
"%g", x); }
85inline std::string
fmt_d(
double x) {
86 if (std::isnan(x) || std::isinf(x))
return mfmt(
"%f", x);
87 if (x == std::floor(x) && std::fabs(x) < 9.007199254740992e15) {
89 std::snprintf(buf,
sizeof(buf),
"%.0f", x == 0.0 ? 0.0 : x);
90 return std::string(buf);
95inline std::string
fmt_d(std::size_t x) {
return std::to_string(x); }
96inline std::string
fmt_d(
int x) {
return std::to_string(x); }
102 throw UnsupportedError(
"code_gen: a station carries no scheduling strategy to write");
104 for (std::size_t i = 0; i < t.size(); ++i)
105 out.push_back(
static_cast<char>(std::toupper(
static_cast<unsigned char>(t[i]))));
132 const std::size_t nd =
sn.station_to_node[i];
140 throw UnsupportedError(
"code_gen: the Replayer of class '" +
sn.classes[k].name +
"' at '" +
141 sn.nodes[nd - 1].name +
142 "' was built from in-memory samples, so there is no trace file to name");
161 ps.
nphases = std::max(1.0, std::round(1.0 / ps.
scv));
169 for (std::size_t f = 0; f <
sn.fj.size(); ++f)
170 if (
sn.fj[f].second == j)
return sn.fj[f].first;
171 throw InputError(
"code_gen: Join '" +
sn.nodes[j - 1].name +
172 "' closes no Fork, so the model cannot be written as source");
185 for (std::size_t c = 0; c <
sn.chains.size(); ++c) {
186 if (k >=
sn.chains[c].size() || !
sn.chains[c][k])
continue;
187 std::size_t cand = 0;
188 for (std::size_t r = 0; r <
sn.classes.size() && r <
sn.chains[c].size(); ++r) {
189 const double n =
sn.classes[r].population;
190 if (
sn.chains[c][r] && n > 0 && std::isfinite(n)) {
191 cand =
sn.classes[r].refstat;
195 if (cand == 0) cand =
sn.classes[k].refstat;
196 if (cand >= 1 && cand <=
sn.stations.size()) ist = cand;
199 for (std::size_t i = 0; ist == 0 && i <
sn.stations.size(); ++i) {
206 for (std::size_t a = 0; a < m.
D0.rows() && ist == 0; ++a)
207 for (std::size_t b = 0; b < m.
D0.cols(); ++b)
213 return ist > 0 ?
sn.station_to_node[ist - 1] : 0;
219 for (std::size_t i = 0; i <
sn.nodes.size(); ++i) {
220 switch (
sn.nodes[i].nodetype) {
233 ", which the generator has no statement for");
247 std::vector<Route> out;
248 const std::size_t K =
sn.classes.size(), I =
sn.nodes.size();
249 if (
sn.rtnodes.rows() != I * K)
return out;
250 for (std::size_t k = 0; k < K; ++k)
251 for (std::size_t c = 0; c < K; ++c)
252 for (std::size_t i = 0; i < I; ++i)
253 for (std::size_t m = 0; m < I; ++m) {
258 out.push_back(
Route{k, c, i, m, p});
264inline std::ofstream
open_out(
const std::string& path) {
265 std::ofstream f(path.c_str());
266 if (!f)
throw InputError(
"code_gen: cannot open " + path +
" for writing");
284 check_node_types(
sn,
"QN2MATLAB");
285 const std::size_t K =
sn.classes.size();
286 os <<
"model = Network('" << model_name <<
"');\n";
287 os <<
"\n%% Block 1: nodes";
289 for (std::size_t n = 0; n <
sn.nodes.size(); ++n) {
290 const std::size_t i = n + 1;
293 case NodeType::Source:
294 os <<
"node{" << i <<
"} = Source(model, '" << nd.
name <<
"');\n";
296 case NodeType::Delay:
297 os <<
"node{" << i <<
"} = DelayStation(model, '" << nd.
name <<
"');\n";
299 case NodeType::Queue: {
301 os <<
"node{" << i <<
"} = Queue(model, '" << nd.
name <<
"', SchedStrategy."
302 << sched_property(st.
sched) <<
");\n";
303 if (st.
nservers > 1) os <<
"node{" << i <<
"}.setNumServers(" << fmt_d(st.
nservers) <<
");\n";
306 case NodeType::Router:
307 os <<
"node{" << i <<
"} = Router(model, '" << nd.
name <<
"');\n";
310 os <<
"node{" << i <<
"} = Fork(model, '" << nd.
name <<
"');\n";
313 os <<
"node{" << i <<
"} = Join(model, '" << nd.
name <<
"', node{" << fork_of(
sn, i) <<
"});\n";
316 os <<
"node{" << i <<
"} = Sink(model, '" << nd.
name <<
"');\n";
318 case NodeType::ClassSwitch:
319 os <<
"node{" << i <<
"} = Router(model, '" << nd.
name
320 <<
"'); % Class switching is embedded in the routing matrix \n";
326 os <<
"\n%% Block 2: classes\n";
327 for (std::size_t k = 0; k < K; ++k) {
329 const std::string kk = fmt_d(k + 1);
331 os <<
"jobclass{" << kk <<
"} = OpenClass(model, '" << jc.
name <<
"', " << fmt_d(jc.
prio) <<
");\n";
333 const std::size_t ref = jc.
population > 0 ?
sn.station_to_node[jc.
refstat - 1] : empty_class_ref(
sn, k);
334 os <<
"jobclass{" << kk <<
"} = ClosedClass(model, '" << jc.
name <<
"', " << fmt_d(jc.
population)
335 <<
", node{" << ref <<
"}, " << fmt_d(jc.
prio) <<
");\n";
339 for (std::size_t i = 0; i <
sn.stations.size(); ++i)
340 for (std::size_t k = 0; k < K; ++k) {
341 const ProcSpec ps = proc_spec(
sn, i, k);
342 if (ps.kind == ProcSpec::SKIP)
continue;
343 const std::size_t nd =
sn.station_to_node[i];
344 const std::string head =
345 "node{" + fmt_d(nd) +
"}." + (ps.arrival ?
"setArrival" :
"setService") +
"(jobclass{" + fmt_d(k + 1) +
"}, ";
346 const std::string tail =
"); % (" +
sn.nodes[nd - 1].name +
"," +
sn.classes[k].name +
")\n";
348 case ProcSpec::REPLAYER: os << head <<
"Replayer('" << ps.file <<
"')" << tail;
break;
349 case ProcSpec::IMMEDIATE: os << head <<
"Immediate()" << tail;
break;
350 case ProcSpec::EXP: os << head <<
"Exp.fitMean(" << fmt_f(ps.mean) <<
")" << tail;
break;
352 os << head <<
"APH.fitMeanAndSCV(" << fmt_f(ps.mean) <<
"," << fmt_f(ps.scv) <<
")" << tail;
354 case ProcSpec::DISABLED: os << head <<
"Disabled.getInstance()" << tail;
break;
355 case ProcSpec::ERLANG:
356 os << head <<
"Erlang(" << fmt_f(ps.nphases / ps.mean) <<
"," << fmt_f(ps.nphases) <<
")" << tail;
361 os <<
"\n%% Block 3: topology";
363 os <<
"P = model.initRoutingMatrix(); % initialize routing matrix \n";
364 for (
const Route& r : routes(
sn)) {
365 const bool fork =
sn.nodes[r.i].nodetype == NodeType::Fork;
366 os <<
"P{" << r.k + 1 <<
"," << r.c + 1 <<
"}(" << r.i + 1 <<
"," << r.m + 1
367 <<
") = " << (fork ? std::string(
"1.0") : fmt_d(r.p)) <<
"; % (" <<
sn.nodes[r.i].name <<
","
368 <<
sn.classes[r.k].name <<
") -> (" <<
sn.nodes[r.m].name <<
"," <<
sn.classes[r.c].name <<
")\n";
370 os <<
"model.link(P);\n";
395 std::ostringstream os;
411 bool headers =
true) {
414 check_node_types(
sn,
"QN2JAVA");
415 const std::size_t K =
sn.classes.size();
416 if (headers) os <<
"\tpublic static Network ex() {\n";
417 os <<
"\t\tNetwork model = new Network(\"" << model_name <<
"\");\n";
418 os <<
"\n\t\t// Block 1: nodes";
420 for (std::size_t n = 0; n <
sn.nodes.size(); ++n) {
421 const std::size_t i = n + 1;
424 case NodeType::Source:
425 os <<
"\t\tSource node" << i <<
" = new Source(model, \"" << nd.
name <<
"\");\n";
427 case NodeType::Delay:
428 os <<
"\t\tDelay node" << i <<
" = new Delay(model, \"" << nd.
name <<
"\");\n";
430 case NodeType::Queue: {
432 os <<
"\t\tQueue node" << i <<
" = new Queue(model, \"" << nd.
name <<
"\", SchedStrategy."
433 << sched_property(st.
sched) <<
");\n";
436 os <<
"\t\tnode" << i <<
".setNumberOfServers(Integer.MAX_VALUE);\n";
438 os <<
"\t\tnode" << i <<
".setNumberOfServers(" << fmt_d(st.
nservers) <<
");\n";
442 case NodeType::Router:
443 os <<
"\t\tRouter node" << i <<
" = new Router(model, \"" << nd.
name <<
"\");\n";
446 os <<
"\t\tFork node" << i <<
" = new Fork(model, \"" << nd.
name <<
"\");\n";
449 os <<
"\t\tJoin node" << i <<
" = new Join(model, \"" << nd.
name <<
"\", node" << fork_of(
sn, i)
453 os <<
"\t\tSink node" << i <<
" = new Sink(model, \"" << nd.
name <<
"\");\n";
455 case NodeType::ClassSwitch:
456 os <<
"\t\tRouter node" << i <<
" = new Router(model, \"" << nd.
name
457 <<
"\"); // Dummy node, class switching is embedded in the routing matrix P \n";
463 os <<
"\n\t\t// Block 2: classes\n";
464 for (std::size_t k = 0; k < K; ++k) {
467 os <<
"\t\tOpenClass jobclass" << k + 1 <<
" = new OpenClass(model, \"" << jc.
name <<
"\", "
468 << fmt_d(jc.
prio) <<
");\n";
470 const std::size_t ref = jc.
population > 0 ?
sn.station_to_node[jc.
refstat - 1] : empty_class_ref(
sn, k);
471 os <<
"\t\tClosedClass jobclass" << k + 1 <<
" = new ClosedClass(model, \"" << jc.
name <<
"\", "
472 << fmt_d(jc.
population) <<
", node" << ref <<
", " << fmt_d(jc.
prio) <<
");\n";
476 for (std::size_t i = 0; i <
sn.stations.size(); ++i)
477 for (std::size_t k = 0; k < K; ++k) {
478 const ProcSpec ps = proc_spec(
sn, i, k);
479 if (ps.kind == ProcSpec::SKIP)
continue;
480 const std::size_t nd =
sn.station_to_node[i];
481 const std::string head =
"\t\tnode" + fmt_d(nd) +
"." + (ps.arrival ?
"setArrival" :
"setService") +
482 "(jobclass" + fmt_d(k + 1) +
", ";
483 const std::string tail =
"); // (" +
sn.nodes[nd - 1].name +
"," +
sn.classes[k].name +
")\n";
486 const std::vector<T>& sp =
sn.stations[i].schedparam;
488 const std::string weight = (!ps.arrival && w != 1.0) ?
", " + fmt_f(w) : std::string();
490 case ProcSpec::REPLAYER:
491 os << head <<
"new Replayer(\"" << ps.file <<
"\")" << weight << tail;
493 case ProcSpec::IMMEDIATE: os << head <<
"Immediate.getInstance()" << tail;
break;
494 case ProcSpec::EXP: os << head <<
"Exp.fitMean(" << fmt_f(ps.mean) <<
")" << weight << tail;
break;
496 os << head <<
"APH.fitMeanAndSCV(" << fmt_f(ps.mean) <<
"," << fmt_f(ps.scv) <<
")" << weight
499 case ProcSpec::DISABLED: os << head <<
"Disabled.getInstance()" << tail;
break;
500 case ProcSpec::ERLANG:
501 os << head <<
"new Erlang(" << fmt_f(ps.nphases / ps.mean) <<
"," << fmt_d(ps.nphases) <<
")"
507 os <<
"\n\t\t// Block 3: topology";
509 os <<
"\t\tRoutingMatrix routingMatrix = model.initRoutingMatrix(); \n";
511 for (
const Route& r : routes(
sn)) {
512 const bool fork =
sn.nodes[r.i].nodetype == NodeType::Fork;
513 os <<
"\t\troutingMatrix.set(jobclass" << r.k + 1 <<
", jobclass" << r.c + 1 <<
", node" << r.i + 1
514 <<
", node" << r.m + 1 <<
", " << fmt_f(fork ?
sn.nodes[r.i].tasks_per_link : r.p) <<
"); // ("
515 <<
sn.nodes[r.i].name <<
"," <<
sn.classes[r.k].name <<
") -> (" <<
sn.nodes[r.m].name <<
","
516 <<
sn.classes[r.c].name <<
")\n";
518 os <<
"\n\t\tmodel.link(routingMatrix);\n\n";
520 os <<
"\t\treturn model;\n";
540 bool headers =
true) {
548 bool headers =
true) {
549 std::ostringstream os;
558namespace code_gen_detail {
562 if (std::isnan(v))
return "Double.NaN";
563 if (std::isinf(v))
return v > 0 ?
"Double.POSITIVE_INFINITY" :
"Double.NEGATIVE_INFINITY";
565 if (v == std::round(v) && std::fabs(v) < 9007199254740992.0) {
566 std::snprintf(buf,
sizeof(buf),
"%.0f.0", v == 0.0 ? 0.0 : v);
569 for (
int p = 15; p <= 17; ++p) {
570 std::snprintf(buf,
sizeof(buf),
"%.*g", p, v);
571 if (std::strtod(buf,
nullptr) == v)
break;
583std::string
j_arr(
const std::vector<T>& v) {
584 std::string s =
"new double[]{";
585 for (std::size_t i = 0; i < v.size(); ++i) s += (i ?
", " :
"") +
j_num_t(v[i]);
592 std::string s =
"new Matrix(new double[][]{{";
593 for (std::size_t i = 0; i < v.size(); ++i) s += (i ?
", " :
"") +
j_num_t(v[i]);
600 std::string s =
"new Matrix(new double[][]{";
601 for (std::size_t i = 0; i < m.
rows(); ++i) {
602 s += i ?
", {" :
"{";
603 for (std::size_t j = 0; j < m.
cols(); ++j) s += (j ?
", " :
"") +
j_num_t(m(i, j));
611 std::snprintf(buf,
sizeof(buf),
"%.0f", std::round(v) == 0.0 ? 0.0 : std::round(v));
624 const std::vector<T>& p = d.
params;
625 auto par = [&](std::size_t k) ->
const T& {
628 " carries fewer parameters than its constructor takes");
631 auto jn = [&](std::size_t k) {
return j_num_t(par(k)); };
633 auto half = [&](std::size_t from, std::size_t n) {
return std::vector<T>(p.begin() + from, p.begin() + from + n); };
634 auto cls2 = [&](
const char* c) {
return std::string(
"new ") + c +
"(" + jn(0) +
", " + jn(1) +
")"; };
635 if (d.
disabled || d.
type == ProcessType::DISABLED)
return "new Disabled()";
637 case ProcessType::IMMEDIATE:
return "new Immediate()";
638 case ProcessType::EXP:
640 case ProcessType::DET:
return "new Det(" + jn(0) +
")";
641 case ProcessType::GEOMETRIC:
return "new Geometric(" + jn(0) +
")";
642 case ProcessType::POISSON:
return "new Poisson(" + jn(0) +
")";
643 case ProcessType::BERNOULLI:
return "new Bernoulli(" + jn(0) +
")";
644 case ProcessType::ERLANG:
return "new Erlang(" + jn(0) +
", " + jr(1) +
")";
645 case ProcessType::HYPEREXP:
646 if (p.size() == 3)
return "new HyperExp(" + jn(0) +
", " + jn(1) +
", " + jn(2) +
")";
647 return "new HyperExp(" +
j_arr(half(0, p.size() / 2)) +
", " +
j_arr(half(p.size() / 2, p.size() / 2)) +
")";
648 case ProcessType::GAMMA:
return cls2(
"Gamma");
649 case ProcessType::LOGNORMAL:
return cls2(
"Lognormal");
650 case ProcessType::UNIFORM:
return cls2(
"Uniform");
651 case ProcessType::PARETO:
return cls2(
"Pareto");
652 case ProcessType::NORMAL:
return cls2(
"Normal");
653 case ProcessType::DUNIFORM:
return cls2(
"DiscreteUniform");
654 case ProcessType::BINOMIAL:
return "new Binomial(" + jr(0) +
", " + jn(1) +
")";
655 case ProcessType::WEIBULL:
return "new Weibull(" + jn(1) +
", " + jn(0) +
")";
656 case ProcessType::COXIAN:
657 case ProcessType::COX2:
659 return "new Coxian(" +
j_mat_row(half(0, p.size() / 2)) +
", " +
j_mat_row(half(p.size() / 2, p.size() / 2)) +
")";
660 case ProcessType::PH:
return "new PH(" +
j_mat_row(p) +
", " +
j_mat(d.
D0) +
")";
661 case ProcessType::APH:
return "new APH(" +
j_mat_row(p) +
", " +
j_mat(d.
D0) +
")";
662 case ProcessType::ME:
return "new ME(" +
j_mat_row(p) +
", " +
j_mat(d.
D0) +
")";
663 case ProcessType::MAP:
return "new MAP(" +
j_mat(d.
D0) +
", " +
j_mat(d.
D1) +
")";
664 case ProcessType::RAP:
return "new RAP(" +
j_mat(d.
D0) +
", " +
j_mat(d.
D1) +
")";
665 case ProcessType::MMPP2:
return "new MMPP2(" + jn(0) +
", " + jn(1) +
", " + jn(2) +
", " + jn(3) +
")";
666 case ProcessType::ZIPF:
return "new Zipf(" + jn(0) +
", " + jr(1) +
")";
667 case ProcessType::DISCRETESAMPLER: {
668 std::vector<T> x = d.
trace;
673 case ProcessType::REPLAYER: {
675 throw UnsupportedError(
"LQN2JAVA: a Replayer built from in-memory samples has no trace file to name");
678 if (c ==
'\\' || c ==
'"') f.push_back(
'\\');
681 return "new Replayer(\"" + f +
"\")";
695 std::vector<std::vector<bool>> g(
sn.nacts + 1, std::vector<bool>(
sn.nentries + 1,
false));
696 std::map<std::string, std::size_t> ent, act;
697 for (std::size_t e = 1; e <=
sn.nentries; ++e) ent[
sn.names[
sn.eshift + e]] = e;
698 for (std::size_t a = 1; a <=
sn.nacts; ++a) act[
sn.names[
sn.ashift + a]] = a;
699 const std::map<std::string, std::vector<std::string>> rep = lqn::detail::reply_activities(m,
sn);
700 for (
const auto& kv : rep) {
701 const auto ei = ent.find(kv.first);
702 if (ei == ent.end())
continue;
703 for (
const std::string& an : kv.second) {
704 const auto ai = act.find(an);
705 if (ai != act.end()) g[ai->second][ei->second] =
true;
714 if (tslot >= m.
tasks.size())
return 0;
715 for (
const lqn::detail::RawPrecedence<T>& p : m.
tasks[tslot].precedences) {
717 if (std::find(p.postacts.begin(), p.postacts.end(), post_name) == p.postacts.end())
continue;
718 if (p.has_quorum)
return p.quorum;
727 for (std::size_t i = 0; i < s.size(); ++i)
728 if (s[i] !=
' ') out.push_back(
static_cast<char>(std::toupper(
static_cast<unsigned char>(s[i]))));
747 const std::vector<std::vector<bool>> replygraph = reply_graph(model,
sn);
751 os <<
"package jline.examples;\n\n";
752 os <<
"import java.util.ArrayList;\n";
753 os <<
"import jline.lang.*;\n";
754 os <<
"import jline.lang.layered.*;\n";
755 os <<
"import jline.lang.constant.*;\n";
756 os <<
"import jline.lang.processes.*;\n";
757 os <<
"import jline.util.matrix.Matrix;\n";
758 os <<
"import jline.solvers.ln.SolverLN;\n\n";
759 os <<
"public class TestSolver" << upper_nospace(model_name) <<
" {\n\n";
760 os <<
"\tpublic static void main(String[] args) throws Exception{\n\n";
761 os <<
"\tLayeredNetwork model = new LayeredNetwork(\"" << model_name <<
"\");\n";
763 for (std::size_t h = 1; h <=
sn.nhosts; ++h) {
764 const std::string mult = std::isinf(
sn.mult[h]) ? std::string(
"Integer.MAX_VALUE") : fmt_d(
sn.mult[h]);
765 os <<
"\tProcessor P" << h <<
" = new Processor(model, \"" <<
sn.names[h] <<
"\", " << mult <<
", "
766 << sched_feature(
sn.sched[h]) <<
");\n";
767 if (
sn.repl[h] != 1) os <<
"P" << h <<
".setReplication(" << fmt_d(
sn.repl[h]) <<
");\n";
770 for (std::size_t t = 1; t <=
sn.ntasks; ++t) {
771 const std::size_t tidx =
sn.tshift + t;
772 const std::string mult =
773 std::isinf(
sn.mult[tidx]) ? std::string(
"Integer.MAX_VALUE") : fmt_d(
sn.mult[tidx]);
774 os <<
"\tTask T" << t <<
" = new Task(model, \"" <<
sn.names[tidx] <<
"\", " << mult <<
", "
775 << sched_feature(
sn.sched[tidx]) <<
").on(P" <<
sn.parent[tidx] <<
");\n";
776 if (
sn.repl[tidx] != 1) os <<
"\tT" << t <<
".setReplication(" << fmt_d(
sn.repl[tidx]) <<
");\n";
779 os <<
"\tT" << t <<
".setThinkTime(" << java_dist(th) <<
");\n";
782 for (std::size_t e = 1; e <=
sn.nentries; ++e) {
783 const std::size_t eidx =
sn.eshift + e;
784 os <<
"\tEntry E" << e <<
" = new Entry(model, \"" <<
sn.names[eidx] <<
"\").on(T"
785 <<
sn.parent[eidx] -
sn.tshift <<
");\n";
788 for (std::size_t a = 1; a <=
sn.nacts; ++a) {
789 const std::size_t aidx =
sn.ashift + a;
790 const std::size_t tidx =
sn.parent[aidx];
792 for (std::size_t e = 1; e <=
sn.nentries; ++e)
793 if (
sn.graph.get(
sn.eshift + e, aidx) != zero) bound +=
".boundTo(E" + std::to_string(e) +
")";
796 std::vector<std::size_t> rt;
797 bool all_nonref =
true;
798 for (std::size_t e = 1; e <=
sn.nentries; ++e)
799 if (replygraph[a][e]) {
801 if (
sn.isref[
sn.parent[
sn.eshift + e]]) all_nonref =
false;
803 if (!rt.empty() && all_nonref)
804 for (std::size_t e : rt) replies +=
".repliesTo(E" + std::to_string(e) +
")";
807 for (std::size_t c = 1; c <=
sn.ncalls; ++c) {
808 if (
sn.callpair_src[c] != aidx)
continue;
809 const std::string target = std::to_string(
sn.callpair_dst[c] -
sn.eshift);
814 os <<
"\tActivity A" << a <<
" = new Activity(model, \"" <<
sn.names[aidx] <<
"\", "
815 << java_dist(
sn.hostdem[aidx]) <<
").on(T" << tidx -
sn.tshift <<
");";
816 if (!bound.empty()) os <<
" A" << a << bound <<
";";
817 if (!calls.empty()) os <<
" A" << a << calls <<
";";
818 if (!replies.empty()) os <<
" A" << a << replies <<
";";
823 os <<
"\tA" << a <<
".setThinkTime(" << java_dist(ath) <<
");\n";
828 for (std::size_t ai = 1; ai <=
sn.nacts; ++ai) {
829 const std::size_t aidx =
sn.ashift + ai;
830 const std::size_t tidx =
sn.parent[aidx];
831 for (std::size_t bidx :
sn.graph.succ(aidx))
832 if (bidx >
sn.ashift &&
sn.actpretype[aidx] == PrecedenceType::PRE_SEQ &&
833 sn.actposttype[bidx] == PrecedenceType::POST_SEQ)
834 os <<
"\tT" << tidx -
sn.tshift <<
".addPrecedence(ActivityPrecedence.Serial(\"" <<
sn.names[aidx]
835 <<
"\", \"" <<
sn.names[bidx] <<
"\"));\n";
839 bool has_pre_acts =
false, has_post_acts =
false;
840 std::vector<bool> processed(
sn.nacts + 1,
false);
841 for (std::size_t ai = 1; ai <=
sn.nacts; ++ai) {
842 const std::size_t aidx =
sn.ashift + ai;
843 const std::size_t tidx =
sn.parent[aidx];
844 if (processed[ai])
continue;
845 for (std::size_t bidx :
sn.graph.succ(aidx)) {
846 if (!(bidx >
sn.ashift &&
sn.actposttype[bidx] == PrecedenceType::POST_LOOP))
continue;
847 if (processed[bidx -
sn.ashift])
continue;
848 const std::size_t loop_start = bidx;
849 std::vector<std::string> names;
850 std::size_t cur = loop_start;
852 names.push_back(
sn.names[cur]);
853 processed[cur -
sn.ashift] =
true;
854 std::size_t end_idx = 0, next_idx = 0;
855 for (std::size_t s :
sn.graph.succ(cur)) {
856 if (s <=
sn.ashift ||
sn.actposttype[s] != PrecedenceType::POST_LOOP)
continue;
857 if (s == loop_start)
continue;
858 const double wt = w(cur, s);
859 if (wt > 0 && wt < 1) end_idx = s;
863 const double wt = w(cur, end_idx);
864 const double counts = wt > 0 ? 1.0 / wt : 1.0;
865 names.push_back(
sn.names[end_idx]);
866 processed[end_idx -
sn.ashift] =
true;
867 os <<
"\n\t// Loop Activity Precedence \n";
869 os <<
"\tArrayList<String> precActs = new ArrayList<String>();\n";
872 os <<
"\tprecActs = new ArrayList<String>();\n";
874 for (
const std::string& n : names) os <<
"\tprecActs.add(\"" << n <<
"\");\n";
875 os <<
"\tT" << tidx -
sn.tshift <<
".addPrecedence(ActivityPrecedence.Loop(\"" <<
sn.names[aidx]
876 <<
"\", precActs, Matrix.singleton(" << fmt_g(counts) <<
")));\n";
878 }
else if (next_idx > 0) {
889 bool has_probs =
false;
891 std::size_t prec_marker = 0;
892 std::string prec_acts, prob_string;
893 for (std::size_t ai = 1; ai <=
sn.nacts; ++ai) {
894 const std::size_t aidx =
sn.ashift + ai;
895 const std::size_t tidx =
sn.parent[aidx];
896 std::size_t prob_ctr = 0;
897 for (std::size_t bidx :
sn.graph.succ(aidx)) {
898 if (!(bidx >
sn.ashift &&
sn.actposttype[bidx] == PrecedenceType::POST_OR))
continue;
899 const std::string add =
"\tprecActs.add(\"" +
sn.names[bidx] +
"\");\n";
901 const std::string prob =
902 "\tprobs.set(0," + std::to_string(prob_ctr - 1) +
"," + fmt_g(w(aidx, bidx)) +
");\n";
903 if (prec_marker == 0) {
904 prec_marker = aidx -
sn.ashift;
912 if (prec_marker > 0) {
913 os <<
"\n\t// OrFork Activity Precedence \n";
915 os <<
"\tArrayList<String> precActs = new ArrayList<String>();\n";
918 os <<
"\tprecActs = new ArrayList<String>();\n";
921 os <<
"\tMatrix probs = new Matrix(1," << prob_ctr <<
");\n";
924 os <<
"\tprobs = new Matrix(1," << prob_ctr <<
");\n";
926 os << prec_acts << prob_string;
927 os <<
"\tT" << tidx -
sn.tshift <<
".addPrecedence(ActivityPrecedence.OrFork(\""
928 <<
sn.names[prec_marker +
sn.ashift] <<
"\", precActs, probs));\n";
935 for (std::size_t ai = 1; ai <=
sn.nacts; ++ai) {
936 const std::size_t aidx =
sn.ashift + ai;
937 const std::size_t tidx =
sn.parent[aidx];
938 std::string post_acts;
939 for (std::size_t bidx :
sn.graph.succ(aidx))
940 if (bidx >
sn.ashift &&
sn.actposttype[bidx] == PrecedenceType::POST_AND)
941 post_acts +=
"\tpostActs.add(\"" +
sn.names[bidx] +
"\");\n";
942 if (post_acts.empty())
continue;
943 os <<
"\n\t// AndFork Activity Precedence \n";
944 if (!has_post_acts) {
945 os <<
"\tArrayList<String> postActs = new ArrayList<String>();\n";
946 has_post_acts =
true;
948 os <<
"\t postActs = new ArrayList<String>();\n";
951 os <<
"\tT" << tidx -
sn.tshift <<
".addPrecedence(ActivityPrecedence.AndFork(\"" <<
sn.names[aidx]
952 <<
"\", postActs));\n";
956 for (
int pass = 0; pass < 2; ++pass) {
957 const PrecedenceType want = pass == 0 ? PrecedenceType::PRE_OR : PrecedenceType::PRE_AND;
958 for (std::size_t bi =
sn.nacts; bi >= 1; --bi) {
959 const std::size_t bidx =
sn.ashift + bi;
960 const std::size_t tidx =
sn.parent[bidx];
961 std::string prec_acts;
962 for (std::size_t aidx :
sn.graph.pred(bidx))
963 if (aidx >
sn.ashift &&
sn.actpretype[aidx] == want)
964 prec_acts +=
"\tprecActs.add(\"" +
sn.names[aidx] +
"\");\n";
965 if (prec_acts.empty())
continue;
966 os << (pass == 0 ?
"\n\t// OrJoin Activity Precedence \n" :
"\n\t// AndJoin Activity Precedence \n");
968 os <<
"\tArrayList<String> precActs = new ArrayList<String>();\n";
971 os <<
"\tprecActs = new ArrayList<String>();\n";
974 const std::size_t local = tidx -
sn.tshift;
976 os <<
"\tT" << local <<
".addPrecedence(ActivityPrecedence.OrJoin(precActs, \"" <<
sn.names[bidx]
979 const std::size_t q = and_join_quorum(model, local - 1,
sn.names[bidx]);
981 os <<
"\tT" << local <<
".addPrecedence(ActivityPrecedence.AndJoin(precActs, \""
982 <<
sn.names[bidx] <<
"\"));\n";
984 os <<
"\tT" << local <<
".addPrecedence(ActivityPrecedence.AndJoin(precActs, \""
985 <<
sn.names[bidx] <<
"\", Matrix.singleton(" << j_round(
double(q)) <<
")));\n";
990 os <<
"\n\t// Model solution \n";
991 os <<
"\tSolverLN solver = new SolverLN(model);\n";
992 os <<
"\tsolver.getEnsembleAvg();\n";
1005 lqn2java(model,
"myLayeredModel", os);
1018 std::ostringstream os;
1027namespace code_gen_detail {
1031 if (std::isnan(v))
return "NaN";
1032 if (std::isinf(v))
return v > 0 ?
"Inf" :
"-Inf";
1033 if (v == std::round(v) && std::fabs(v) < 1e15) {
1035 std::snprintf(buf,
sizeof(buf),
"%.0f", v == 0.0 ? 0.0 : v);
1039 for (
int p = 15; p <= 17; ++p) {
1040 std::snprintf(buf,
sizeof(buf),
"%.*g", p, v);
1041 if (std::strtod(buf,
nullptr) == v)
break;
1053std::string
m_row(
const std::vector<T>& v) {
1054 if (v.empty())
return "[]";
1056 std::string s =
"[";
1057 for (std::size_t i = 0; i < v.size(); ++i) s += (i ?
", " :
"") +
m_scalar_t(v[i]);
1064 if (M.
rows() == 0 || M.
cols() == 0)
return "[]";
1066 std::string s =
"[";
1067 for (std::size_t i = 0; i < M.
rows(); ++i) {
1069 for (std::size_t j = 0; j < M.
cols(); ++j) s += (j ?
", " :
"") +
m_scalar_t(M(i, j));
1076 std::string out =
"'";
1078 if (c ==
'\'') out +=
"''";
1079 else out.push_back(c);
1085inline std::string
m_cellstr(
const std::vector<std::string>& c) {
1086 std::string s =
"{";
1087 for (std::size_t i = 0; i < c.size(); ++i) s += (i ?
", " :
"") +
m_quote(c[i]);
1101 return "ReplacementStrategy.RR";
1113 const std::vector<T>& p = d.
params;
1114 auto par = [&](std::size_t k) -> std::string {
1117 " carries fewer parameters than its constructor takes");
1120 auto half = [&](std::size_t from, std::size_t n) {
1121 return m_row(std::vector<T>(p.begin() + from, p.begin() + from + n));
1123 if (d.
disabled || d.
type == ProcessType::DISABLED)
return "Disabled()";
1125 case ProcessType::IMMEDIATE:
return "Immediate()";
1126 case ProcessType::EXP:
1128 case ProcessType::DET:
return "Det(" + par(0) +
")";
1129 case ProcessType::ERLANG:
return "Erlang(" + par(0) +
", " + par(1) +
")";
1130 case ProcessType::HYPEREXP:
1131 if (p.size() == 3)
return "HyperExp(" + par(0) +
", " + par(1) +
", " + par(2) +
")";
1132 return "HyperExp(" + half(0, p.size() / 2) +
", " + half(p.size() / 2, p.size() / 2) +
")";
1133 case ProcessType::GAMMA:
return "Gamma(" + par(0) +
", " + par(1) +
")";
1134 case ProcessType::LOGNORMAL:
return "Lognormal(" + par(0) +
", " + par(1) +
")";
1135 case ProcessType::UNIFORM:
return "Uniform(" + par(0) +
", " + par(1) +
")";
1136 case ProcessType::PARETO:
return "Pareto(" + par(0) +
", " + par(1) +
")";
1137 case ProcessType::NORMAL:
return "Normal(" + par(0) +
", " + par(1) +
")";
1138 case ProcessType::BINOMIAL:
return "Binomial(" + par(0) +
", " + par(1) +
")";
1139 case ProcessType::DUNIFORM:
return "DiscreteUniform(" + par(0) +
", " + par(1) +
")";
1140 case ProcessType::WEIBULL:
return "Weibull(" + par(1) +
", " + par(0) +
")";
1141 case ProcessType::GEOMETRIC:
return "Geometric(" + par(0) +
")";
1142 case ProcessType::POISSON:
return "Poisson(" + par(0) +
")";
1143 case ProcessType::BERNOULLI:
return "Bernoulli(" + par(0) +
")";
1144 case ProcessType::COXIAN:
1145 case ProcessType::COX2:
1147 if (d.
cox_scalar_form && p.size() == 4)
return "Coxian(" + par(0) +
", " + par(1) +
", " + par(2) +
")";
1148 return "Coxian(" + half(0, p.size() / 2) +
", " + half(p.size() / 2, p.size() / 2) +
")";
1149 case ProcessType::PH:
return "PH(" +
m_row(p) +
", " +
m_matrix(d.
D0) +
")";
1150 case ProcessType::APH:
return "APH(" +
m_row(p) +
", " +
m_matrix(d.
D0) +
")";
1151 case ProcessType::ME:
return "ME(" +
m_row(p) +
", " +
m_matrix(d.
D0) +
")";
1154 case ProcessType::MMPP2:
1155 return "MMPP2(" + par(0) +
", " + par(1) +
", " + par(2) +
", " + par(3) +
")";
1156 case ProcessType::ZIPF:
return "Zipf(" + par(0) +
", " + par(1) +
")";
1157 case ProcessType::DISCRETESAMPLER: {
1158 std::vector<T> x = d.
trace;
1161 return "DiscreteSampler(" +
m_row(p) +
", " +
m_row(x) +
")";
1163 case ProcessType::REPLAYER:
1165 throw UnsupportedError(
"LQN2MATLAB: a Replayer built from in-memory samples has no trace file to name");
1180std::string
m_prec(
const lqn::detail::RawPrecedence<T>& ap) {
1182 const std::vector<std::string>& pre = ap.preacts;
1183 const std::vector<std::string>& post = ap.postacts;
1184 std::vector<T> preparams = ap.preparams;
1185 if (preparams.empty() && ap.has_quorum) preparams.push_back(
num_traits<T>::from_int(
static_cast<long>(ap.quorum)));
1186 const PrecedenceType a = ap.pretype, b = ap.posttype;
1187 if (a == PrecedenceType::PRE_SEQ && b == PrecedenceType::POST_SEQ && pre.size() == 1 && post.size() == 1 &&
1188 preparams.empty() && ap.postparams.empty())
1189 return "ActivityPrecedence.Serial(" +
m_quote(pre[0]) +
", " +
m_quote(post[0]) +
")";
1190 if (a == PrecedenceType::PRE_AND && b == PrecedenceType::POST_SEQ && post.size() == 1 && ap.postparams.empty()) {
1191 if (preparams.empty())
return "ActivityPrecedence.AndJoin(" +
m_cellstr(pre) +
", " +
m_quote(post[0]) +
")";
1192 return "ActivityPrecedence.AndJoin(" +
m_cellstr(pre) +
", " +
m_quote(post[0]) +
", " +
m_row(preparams) +
")";
1194 if (a == PrecedenceType::PRE_OR && b == PrecedenceType::POST_SEQ && post.size() == 1 && preparams.empty() &&
1195 ap.postparams.empty())
1196 return "ActivityPrecedence.OrJoin(" +
m_cellstr(pre) +
", " +
m_quote(post[0]) +
")";
1197 if (a == PrecedenceType::PRE_SEQ && b == PrecedenceType::POST_AND && pre.size() == 1 && preparams.empty() &&
1198 ap.postparams.empty())
1199 return "ActivityPrecedence.AndFork(" +
m_quote(pre[0]) +
", " +
m_cellstr(post) +
")";
1200 if (a == PrecedenceType::PRE_SEQ && b == PrecedenceType::POST_OR && pre.size() == 1 && preparams.empty())
1201 return "ActivityPrecedence.OrFork(" +
m_quote(pre[0]) +
", " +
m_cellstr(post) +
", " +
m_row(ap.postparams) +
")";
1202 if (a == PrecedenceType::PRE_SEQ && b == PrecedenceType::POST_LOOP && pre.size() == 1 && preparams.empty()) {
1204 std::vector<T> count;
1205 if (!ap.postparams.empty()) count.push_back(ap.postparams[0]);
1206 return "ActivityPrecedence.Loop(" +
m_quote(pre[0]) +
", " +
m_cellstr(post) +
", " +
m_row(count) +
")";
1208 if (a == PrecedenceType::PRE_SEQ && b == PrecedenceType::POST_CACHE && pre.size() == 1 && preparams.empty() &&
1209 ap.postparams.empty())
1210 return "ActivityPrecedence.CacheAccess(" +
m_quote(pre[0]) +
", " +
m_cellstr(post) +
")";
1211 auto type_name = [](PrecedenceType t) -> std::string {
1213 case PrecedenceType::PRE_SEQ:
return "ActivityPrecedenceType.PRE_SEQ";
1214 case PrecedenceType::PRE_AND:
return "ActivityPrecedenceType.PRE_AND";
1215 case PrecedenceType::PRE_OR:
return "ActivityPrecedenceType.PRE_OR";
1216 case PrecedenceType::POST_SEQ:
return "ActivityPrecedenceType.POST_SEQ";
1217 case PrecedenceType::POST_AND:
return "ActivityPrecedenceType.POST_AND";
1218 case PrecedenceType::POST_OR:
return "ActivityPrecedenceType.POST_OR";
1219 case PrecedenceType::POST_LOOP:
return "ActivityPrecedenceType.POST_LOOP";
1220 case PrecedenceType::POST_CACHE:
return "ActivityPrecedenceType.POST_CACHE";
1222 return m_scalar(
double(
static_cast<int>(t)));
1224 return "ActivityPrecedence(" +
m_cellstr(pre) +
", " +
m_cellstr(post) +
", " + type_name(a) +
", " +
1225 type_name(b) +
", " +
m_row(preparams) +
", " +
m_row(ap.postparams) +
")";
1231 const std::vector<T>* b,
const std::vector<lqn::detail::RawLinConRow<T>>* rows,
1232 const std::vector<T>* lld,
bool has_cd,
bool has_jd,
1233 const std::vector<lqn::detail::RawServerPool<T>>* pools) {
1234 if (A && A->
rows() > 0 && A->
cols() > 0) os << v <<
".setConstraint(" <<
m_matrix(*A) <<
", " <<
m_row(*b) <<
");\n";
1236 for (
const lqn::detail::RawLinConRow<T>& r : *rows)
1239 if (lld && !lld->empty()) os << v <<
".setLoadDependence(" <<
m_row(*lld) <<
");\n";
1240 if (has_cd || has_jd)
1241 throw UnsupportedError(
"LQN2MATLAB: " + owner +
" declares a " + (has_cd ?
"class" :
"joint") +
1242 "-dependent rate as a compiled function, which has no MATLAB source to write");
1244 for (
const lqn::detail::RawServerPool<T>& sp : *pools)
1245 os << v <<
".addServerType(ServerType(" <<
m_quote(sp.name) <<
", " <<
m_scalar(sp.count) <<
", "
1270 std::map<std::string, std::size_t> entry_idx, act_idx;
1271 for (std::size_t e = 0; e < m.
entries.size(); ++e) entry_idx.emplace(m.
entries[e].name, e + 1);
1272 for (std::size_t a = 0; a < m.
acts.size(); ++a) act_idx.emplace(m.
acts[a].name, a + 1);
1273 auto entry_ref = [&](
const std::string& n) {
1274 const auto it = entry_idx.find(n);
1275 return it == entry_idx.end() ? m_quote(n) :
"E{" + std::to_string(it->second) +
"}";
1279 os <<
"% LayeredNetwork generated by LQN2MATLAB\n";
1280 os <<
"model = LayeredNetwork(" << m_quote(model_name) <<
");\n";
1281 os <<
"P = {}; T = {}; E = {}; A = {};\n";
1283 os <<
"\n%% Block 1: processors\n";
1284 for (std::size_t p = 0; p < m.
procs.size(); ++p) {
1285 const lqn::detail::RawProc& h = m.
procs[p];
1286 const std::string hv =
"P{" + std::to_string(p + 1) +
"}";
1288 const double quantum = h.quantum == 0.0 ? 0.001 : h.quantum;
1289 const char* ctor = h.is_host_class ?
" = Host(model, " :
" = Processor(model, ";
1290 if (quantum != 0.001 || h.speed_factor != 1.0)
1291 os << hv << ctor << m_quote(h.name) <<
", " << m_scalar(h.mult) <<
", " << sched(h.sched)
1292 <<
", " << m_scalar(quantum) <<
", " << m_scalar(h.speed_factor) <<
");\n";
1294 os << hv << ctor << m_quote(h.name) <<
", " << m_scalar(h.mult) <<
", " << sched(h.sched)
1296 if (h.repl != 1.0) os << hv <<
".setReplication(" << m_scalar(h.repl) <<
");\n";
1303 m_server_extras<T>(os, hv, h.name, lc == m.
proc_lincon.end() ?
nullptr : &lc->second.first,
1304 lc == m.
proc_lincon.end() ?
nullptr : &lc->second.second,
1309 pools == m.
proc_pools.end() ?
nullptr : &pools->second);
1312 os <<
"\n%% Block 2: tasks\n";
1313 for (std::size_t t = 0; t < m.
tasks.size(); ++t) {
1314 const lqn::detail::RawTask<T>& tk = m.
tasks[t];
1315 const std::string tv =
"T{" + std::to_string(t + 1) +
"}";
1316 const std::string on =
".on(P{" + std::to_string(tk.proc_slot + 1) +
"});\n";
1317 const bool setup = !m_default_time(tk.setuptime) || !m_default_time(tk.delayofftime);
1318 if (tk.nitems > 0) {
1319 std::vector<double> cap(tk.itemcap.begin(), tk.itemcap.end());
1320 os << tv <<
" = CacheTask(model, " << m_quote(tk.name) <<
", " << m_scalar(
double(tk.nitems)) <<
", "
1321 << m_row(cap) <<
", " << m_repl(tk.replacestrat) <<
", " << m_scalar(tk.mult) <<
", " << sched(tk.sched)
1323 if (tk.retrieval) os << tv <<
".setRetrieval(true);\n";
1325 os << tv <<
" = " << (setup ?
"SetupTask" :
"Task") <<
"(model, " << m_quote(tk.name) <<
", "
1326 << m_scalar(tk.mult) <<
", " << sched(tk.sched) <<
")" << on;
1328 if (!m_default_time(tk.thinktime)) os << tv <<
".setThinkTime(" << m_dist(tk.thinktime) <<
");\n";
1329 if (!m_default_time(tk.setuptime)) os << tv <<
".setSetupTime(" << m_dist(tk.setuptime) <<
");\n";
1330 if (!m_default_time(tk.delayofftime)) os << tv <<
".setDelayOffTime(" << m_dist(tk.delayofftime) <<
");\n";
1331 if (tk.repl != 1.0) os << tv <<
".setReplication(" << m_scalar(tk.repl) <<
");\n";
1332 if (tk.priority != 0) os << tv <<
".setPriority(" << tk.priority <<
");\n";
1333 for (
const auto& fi : tk.fanin) os << tv <<
".setFanIn(" << m_quote(fi.first) <<
", " << m_scalar(fi.second) <<
");\n";
1334 for (
const auto& fo : tk.fanout)
1335 os << tv <<
".setFanOut(" << m_quote(fo.first) <<
", " << m_scalar(fo.second) <<
");\n";
1336 m_server_extras<T>(os, tv, tk.name, &tk.lincon_A, &tk.lincon_b, &tk.linconrows, &tk.lldscaling,
1337 static_cast<bool>(tk.cdscaling),
static_cast<bool>(tk.jdscaling), &tk.pools);
1340 os <<
"\n%% Block 3: entries\n";
1341 for (std::size_t e = 0; e < m.
entries.size(); ++e) {
1342 const lqn::detail::RawEntry<T>& en = m.
entries[e];
1343 const std::string ev =
"E{" + std::to_string(e + 1) +
"}";
1344 const std::string on =
".on(T{" + std::to_string(en.task_slot + 1) +
"});\n";
1345 if (en.cardinality > 0) {
1347 for (std::size_t k = 0; k < en.popularity.size(); ++k) x.push_back(
num_traits<T>::from_int(
static_cast<long>(k + 1)));
1348 os << ev <<
" = ItemEntry(model, " << m_quote(en.name) <<
", " << m_scalar(
double(en.cardinality))
1349 <<
", DiscreteSampler(" << m_row(en.popularity) <<
", " << m_row(x) <<
"))" << on;
1351 os << ev <<
" = Entry(model, " << m_quote(en.name) <<
")" << on;
1353 if (en.type !=
"PH1PH2") os << ev <<
".setType(" << m_quote(en.type) <<
");\n";
1354 if (en.has_arrival) os << ev <<
".setArrival(" << m_dist(en.arrival) <<
");\n";
1356 for (std::size_t e = 0; e < m.
entries.size(); ++e)
1357 for (std::size_t f = 0; f < m.
entries[e].fwd_dest.size(); ++f)
1358 os <<
"E{" << e + 1 <<
"}.forward(" << entry_ref(m.
entries[e].fwd_dest[f]) <<
", "
1359 << m_scalar_t(m.
entries[e].fwd_prob[f]) <<
");\n";
1361 os <<
"\n%% Block 4: activities\n";
1362 for (std::size_t a = 0; a < m.
acts.size(); ++a) {
1363 const lqn::detail::RawActivity<T>& ac = m.
acts[a];
1364 const std::string av =
"A{" + std::to_string(a + 1) +
"}";
1365 os << av <<
" = Activity(model, " << m_quote(ac.name) <<
", " << m_dist(ac.hostdem) <<
").on(T{"
1366 << ac.task_slot + 1 <<
"})";
1367 if (!ac.bound_to_entry.empty()) os <<
".boundTo(" << entry_ref(ac.bound_to_entry) <<
")";
1369 if (ac.call_order !=
"STOCHASTIC") os << av <<
".setCallOrder(" << m_quote(ac.call_order) <<
");\n";
1370 if (!m_default_time(ac.thinktime)) os << av <<
".setThinkTime(" << m_dist(ac.thinktime) <<
");\n";
1371 if (ac.phase != 1) os << av <<
".setPhase(" << ac.phase <<
");\n";
1372 for (
const lqn::detail::RawCall<T>& c : ac.sync_calls)
1373 os << av <<
".synchCall(" << entry_ref(c.dest) <<
", " << m_scalar_t(c.mean) <<
");\n";
1374 for (
const auto& g : ac.call_groups) {
1379 throw UnsupportedError(std::string(
"LQN2MATLAB: call groups carry RROBIN or JSQ; routing strategy ") +
1381 os << av <<
".recordCallGroup(" << rs <<
", " << m_cellstr(g.second) <<
");\n";
1383 for (
const lqn::detail::RawCall<T>& c : ac.async_calls)
1384 os << av <<
".asynchCall(" << entry_ref(c.dest) <<
", " << m_scalar_t(c.mean) <<
");\n";
1387 os <<
"\n%% Block 5: replies\n";
1388 for (std::size_t e = 0; e < m.
entries.size(); ++e)
1389 for (
const std::string& rn : m.
entries[e].reply_activities) {
1390 const auto it = act_idx.find(rn);
1391 const bool is_ref = it != act_idx.end() &&
1393 if (it == act_idx.end() || is_ref)
1394 os <<
"E{" << e + 1 <<
"}.replyActivity{end+1} = " << m_quote(rn) <<
";\n";
1396 os <<
"A{" << it->second <<
"}.repliesTo(E{" << e + 1 <<
"});\n";
1399 os <<
"\n%% Block 6: precedences\n";
1400 for (std::size_t t = 0; t < m.
tasks.size(); ++t)
1401 for (
const lqn::detail::RawPrecedence<T>& ap : m.
tasks[t].precedences)
1402 os <<
"T{" << t + 1 <<
"}.addPrecedence(" << m_prec(ap) <<
");\n";
1408 lqn2matlab(model, model.
name.empty() ? std::string(
"myLayeredModel") : model.
name, os);
1427 std::ostringstream os;
1495 lqn2java(model, model.
name.empty() ? std::string(
"myLayeredModel") : model.
name, os);
1498namespace code_gen_detail {
1502 std::ifstream in(path.c_str());
1503 if (!in)
throw InputError(
"code_gen: cannot open " + path);
1507 }
catch (
const detail::json::parse_error& e) {
1508 throw InputError(
"code_gen: malformed JSON in " + path +
": " + e.what());
1510 const detail::json& model = root.contains(
"model") ? root.at(
"model") : root;
1511 return model.contains(
"name") && model.at(
"name").is_string() ? model.at(
"name").get<std::string>()
1522template <
class T =
double>
1534template <
class T =
double>
UnsupportedError(const std::string &what)
const LqnModel< T > & model() const
A network plus its refreshed NetworkStruct.
A queueing network under construction.
const NetworkStruct< T > & get_struct()
The refreshed struct, MATLAB's model.getStruct().
What refreshProcessRepresentations and refreshLST compute FROM a distribution: the (D0,...
The exception types the port throws.
Build a layered queueing network in code, as the MATLAB constructors do.
model.json with type: "LayeredNetwork" -> LqnStruct, via LqnBuilder.
.lqnx -> LqnStruct, a port of matlab/src/lang/layered/@LayeredNetwork/parseXML.m followed by ....
LqnModel -> .lqnx, a port of matlab/src/lang/layered/@LayeredNetwork/writeXML.m.
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
std::string j_round(double v)
std::string json_model_name(const std::string &path, const std::string &fallback)
The name of the model in a model.json envelope, fallback when it has none.
std::string j_mat_row(const std::vector< T > &v)
jmat of a row vector: new Matrix(new double[][]{{a, b}}).
std::size_t fork_of(const qn::NetworkStruct< T > &sn, std::size_t j)
find(sn.fj(:,j)): the 1-based Fork node the 1-based Join node j closes.
std::string m_scalar_t(const T &v)
bool m_default_time(const lang::Distrib< T > &d)
True for a think / setup / delay-off time left at its constructor default.
std::ofstream open_out(const std::string &path)
Opens path for writing or throws, naming it.
std::string m_row(const std::vector< T > &v)
num2code of a row vector: [a, b], a scalar when it has one element, [] when empty.
std::string fmt_f(double x)
std::string m_dist(const lang::Distrib< T > &d)
dist2code: the constructor call that rebuilds d from its parameters, which this port keeps in MATLAB ...
std::string fmt_d(double x)
MATLAB d of a double: the integer when it is one, else MATLAB's e override.
std::string m_prec(const lqn::detail::RawPrecedence< T > &ap)
The ActivityPrecedence expression of one declared precedence (precCode).
void m_server_extras(std::ostream &os, const std::string &v, const std::string &owner, const Matrix< T > *A, const std::vector< T > *b, const std::vector< lqn::detail::RawLinConRow< T > > *rows, const std::vector< T > *lld, bool has_cd, bool has_jd, const std::vector< lqn::detail::RawServerPool< T > > *pools)
emitServerExtras: admission constraints, load dependence and server pools of a host or task.
std::size_t empty_class_ref(const qn::NetworkStruct< T > &sn, std::size_t k)
zeroPopRefNode(sn, k) of QN2MATLAB / QN2JAVA: the NODE index (1-based) of the reference station of ze...
std::vector< Route > routes(const qn::NetworkStruct< T > &sn)
std::string m_scalar(double v)
scalar2code: a double at the shortest of 15..17 significant digits that reads back as itself.
std::string m_matrix(const Matrix< T > &M)
num2code of a matrix: [a, b; c, d].
std::size_t and_join_quorum(const lqn::LqnModel< T > &m, std::size_t tslot, const std::string &post_name)
The quorum of the AND-join into post_name on task slot tslot, 0 when it declares none.
std::string j_num(double v)
jnum: a Java double literal at the shortest spelling that reads back as v.
std::string j_mat(const Matrix< T > &m)
jmat of a matrix, row by row.
std::string sched_property(lang::SchedStrategy s)
SchedStrategy.toProperty(SchedStrategy.toText(s)): the enum constant name.
std::string java_dist(const lang::Distrib< T > &d)
javaDist: the Java constructor call rebuilding d with the same parameters.
std::vector< std::vector< bool > > reply_graph(const lqn::LqnModel< T > &m, const lqn::LqnStruct< T > &sn)
sn.replygraph: (nacts+1) x (nentries+1), 1-based, true where the activity replies to the entry.
ProcSpec proc_spec(const qn::NetworkStruct< T > &sn, std::size_t i, std::size_t k)
The branch of QN2MATLAB's process block for station i, class k (0-based).
std::string mfmt(const char *spec, double x)
f, e or g as MATLAB's fprintf prints it: C's text, with Inf / NaN spelt MATLAB's way.
std::string upper_nospace(const std::string &s)
std::string m_quote(const std::string &s)
q(str): a MATLAB single-quoted literal.
std::string m_cellstr(const std::vector< std::string > &c)
cellstr2code: {'a', 'b'}.
std::string fmt_g(double x)
std::string j_arr(const std::vector< T > &v)
jarr: a new double[]{...} literal.
std::string sched_feature(lang::SchedStrategy s)
strrep(SchedStrategy.toFeature(s),'_','.
std::string m_repl(lang::ReplacementStrategy r)
void check_node_types(const qn::NetworkStruct< T > &sn, const char *who)
Refuses a node type neither MATLAB generator has a statement for.
std::string j_num_t(const T &v)
bool is_layered_json(const std::string &path)
True when a file is a LayeredNetwork model.json rather than an .lqnx.
void line2matlab(qn::Network< T > &model, std::ostream &os)
Port of LINE2MATLAB(model) for a Network: QN2MATLAB under the model's own name.
std::string lqn2java_string(const lqn::LqnModel< T > &model, const std::string &model_name="myLayeredModel")
LQN2JAVA returned as a string.
lqn::LqnModel< T > read_lqn_json_model(const std::string &path)
Parse a LayeredNetwork model.json file into its intermediate LqnModel<T>.
qn::Network< T > read_network_json(const std::string &path)
Parse a model.json file into a qn::Network<T>.
void line2java_json(const std::string &json_path, std::ostream &os)
LINE2JAVA on a model.json file, dispatching on the model type as MATLAB dispatches on the class of th...
void lqn2java(const lqn::LqnModel< T > &model, const std::string &model_name, std::ostream &os)
Port of LQN2JAVA(model, modelName, fid): a JLINE program that rebuilds the layered network and solves...
void qn2matlab(const qn::NetworkStruct< T > &sn, const std::string &model_name, std::ostream &os)
Port of QN2MATLAB(model, modelName, fid): a MATLAB script that rebuilds the network from its refreshe...
void line2java(qn::Network< T > &model, std::ostream &os)
Port of LINE2JAVA(model) for a Network: QN2JAVA under the model's own name.
std::string qn2matlab_string(qn::Network< T > &model, const std::string &model_name="myModel")
QN2MATLAB returned as a string.
std::string lqn2matlab_string(const lqn::LqnModel< T > &model, const std::string &model_name)
LQN2MATLAB returned as a string.
void line2matlab_json(const std::string &json_path, std::ostream &os)
LINE2MATLAB on a model.json file: a LayeredNetwork goes to LQN2MATLAB, any other network to QN2MATLAB...
void lqn2matlab(const lqn::LqnModel< T > &m, const std::string &model_name, std::ostream &os)
Port of LQN2MATLAB(model, modelName, fid), the generator form: a MATLAB script that rebuilds the laye...
void qn2java(const qn::NetworkStruct< T > &sn, const std::string &model_name, std::ostream &os, bool headers=true)
Port of QN2JAVA(model, modelName, fid, headers): the body of a JLINE method public static Network ex(...
std::string qn2java_string(qn::Network< T > &model, const std::string &model_name="myModel", bool headers=true)
QN2JAVA returned as a string.
mam::Map< T > dist_to_map(const Distrib< T > &d)
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
PrecedenceType
Activity precedence kinds, with the values of MATLAB ActivityPrecedenceType.
const char * node_type_to_text(NodeType t)
Name of a node kind, for diagnostics.
ProcessType
Distribution kinds, with the values of MATLAB ProcessType.
const char * sched_to_text(SchedStrategy s)
const char * process_to_text(ProcessType p)
The MATLAB ProcessType name, as sn.procid prints it.
const char * routing_to_text(RoutingStrategy r)
NodeType
Node kinds, with the values of MATLAB NodeType.
ReplacementStrategy
Cache replacement policies, with the values of MATLAB ReplacementStrategy.
@ HLRU
h-LRU / LRU(m): h lists, promote i -> i+1 on a hit
@ CLIMB
move up one position on a hit (transposition rule)
@ QLRU
q-LRU: LRU with probabilistic admission on a miss
@ FIFO
first in, first out
LqnStruct< T > lqn_finalize(const LqnModel< T > &m)
Port of @LayeredNetwork/getStruct.m: flatten the model into its struct.
T map_mean(const Map< T > &m)
Mean inter-arrival time, 1/lambda.
T map_scv(const Map< T > &m)
Squared coefficient of variation.
Conservation laws of a layered queueing network, enumerated from its structure.
The Network constructor API: Queue, Delay, Source, Sink, Router, ClassSwitch, Cache,...
Reader for the LINE model.json interchange (a Network model) into a qn::Network<T> built through the ...
A queueing network and its refreshed NetworkStruct.
One (station, class) process, reduced to what QN2MATLAB / QN2JAVA decide on.
enum line::io::code_gen_detail::ProcSpec::Kind kind
bool arrival
the station is EXT scheduled: setArrival, not setService
The (k, c, i, m, p) of every positive sn.rtnodes entry, in QN2MATLAB's loop order,...
Matrix< T > D0
The (D0,D1) pair when the type carries one directly.
std::vector< T > params
Constructor arguments, in MATLAB getParam order.
std::vector< T > trace
Replayer / Trace samples; empty for every other type.
bool cox_scalar_form
Built by cox2(mu1, mu2, phi1), MATLAB's 3-argument Coxian(mu1, mu2, phi1); read only by the code gene...
std::string trace_file
The trace FILE a Replayer was read from, when there was one.
static constexpr double CoarseTol
The intermediate model, and the second stage that flattens it.
std::vector< detail::RawTask< T > > tasks
std::vector< detail::RawActivity< T > > acts
std::map< std::size_t, std::vector< detail::RawServerPool< T > > > proc_pools
std::vector< detail::RawProc > procs
std::map< std::size_t, CdScaling< T > > proc_jdscaling
std::map< std::size_t, std::pair< Matrix< T >, std::vector< T > > > proc_lincon
std::map< std::size_t, std::vector< detail::RawLinConRow< T > > > proc_linconrows
Admission constraints declared on a HOST, by 0-based processor slot.
std::map< std::size_t, CdScaling< T > > proc_cdscaling
std::string name
LayeredNetwork.getName(); empty when unnamed.
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.
std::vector< detail::RawEntry< T > > entries
A MAP as the pair of matrices (D0, D1).
One job class of the network.
std::size_t refstat
1-based reference station
double population
infinite for an open class
One station of the network.
double nservers
may be infinite (a Delay, or an inf-scheduled task)