445 save_xml_header(*
sim);
447 for (std::size_t ind = 1; ind <= sn_.nodes.size(); ++ind) save_node(*
sim, ind);
481 for (std::size_t ist = 1; ist <= sn_.nstations; ++ist) {
487 const qn::NodeType ty = sn_.nodes[sn_.station_to_node[ist - 1] - 1].nodetype;
488 if (ty == qn::NodeType::Source || ty == qn::NodeType::Sink)
continue;
494 if (!std::isfinite(sn_.cap[ist - 1]))
continue;
495 if (sn_.cap[ist - 1] >= reachable_population(ist))
continue;
496 if (!std::isfinite(sn_.stations[ist - 1].nservers))
continue;
498 return "SolverJMT: station '" + nname(sn_.station_to_node[ist - 1]) +
499 "' carries a finite capacity " +
jmt_int(sn_.cap[ist - 1]) +
500 " that binds. The JMVA document has no capacity element at all, so the "
501 "analytical engine would solve the model as if the buffer were unbounded "
502 "and report that as the answer. Use the 'jsim' method, which exports the "
503 "buffer with its drop rule when JMT can express it, or SolverCTMC, "
504 "SolverSSA or SolverLDES";
506 assert_station_cap_exportable(ist);
508 return std::string(e.what());
511 return std::string();
517 std::vector<std::vector<bool>> conn_;
518 std::vector<bool> keep_, cacheclass_;
521 const std::string& cname(std::size_t r)
const {
return sn_.
classes[r - 1].name; }
522 const std::string& nname(std::size_t i)
const {
return sn_.
nodes[i - 1].name; }
525 std::vector<std::size_t> outputs_of(std::size_t ind)
const {
526 std::vector<std::size_t> v;
527 for (std::size_t j = 1; j <= sn_.nodes.size(); ++j)
528 if (conn_[ind - 1][j - 1]) v.push_back(j);
533 std::vector<std::size_t> inputs_of(std::size_t ind)
const {
534 std::vector<std::size_t> v;
535 for (std::size_t j = 1; j <= sn_.nodes.size(); ++j)
536 if (conn_[j - 1][ind - 1]) v.push_back(j);
543 void save_xml_header(xml::Element& sim) {
544 sim.set_attr(
"xmlns:xsi",
"http://www.w3.org/2001/XMLSchema-instance");
545 sim.set_attr(
"name", opt_.file_name +
".jsimg");
546 sim.set_attr(
"xsi:noNamespaceSchemaLocation",
"SIMmodeldefinition.xsd");
547 sim.set_attr(
"disableStatisticStop",
"true");
548 sim.set_attr(
"logDecimalSeparator",
".");
549 sim.set_attr(
"logDelimiter",
";");
550 sim.set_attr(
"logPath", opt_.log_path);
551 sim.set_attr(
"logReplaceMode",
"0");
552 sim.set_attr(
"maxSamples",
553 std::isnan(opt_.max_samples) ?
"10000" :
jmt_int(opt_.max_samples));
554 sim.set_attr(
"maxEvents",
jmt_int(opt_.max_events));
555 if (std::isfinite(opt_.max_simulated_time)) {
557 std::snprintf(buf,
sizeof(buf),
"%.3f", opt_.max_simulated_time);
558 sim.set_attr(
"maxSimulated", buf);
560 sim.set_attr(
"polling",
"1.0");
561 sim.set_attr(
"seed",
jmt_int(
static_cast<double>(opt_.seed)));
572 void save_classes(xml::Element& sim) {
574 for (std::size_t r = 0; r < sn_.nclasses; ++r)
575 maxprio = std::max(maxprio, sn_.classes[r].prio);
576 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
577 if (!keep_[r - 1])
continue;
578 const qn::JobClass& cl = sn_.classes[r - 1];
579 xml::Element& uc = sim.add_child(
"userClass");
580 uc.set_attr(
"name", cl.name);
581 const bool open = !std::isfinite(cl.population);
582 uc.set_attr(
"type", open ?
"open" :
"closed");
587 uc.set_attr(
"softDeadline",
"0.0");
588 uc.set_attr(
"priority",
jmt_int(
static_cast<double>(maxprio - cl.prio)));
589 const std::size_t refst = cl.refstat;
590 const std::string refname = nname(sn_.station_to_node[refst - 1]);
592 uc.set_attr(
"customers",
jmt_int(cl.population));
593 uc.set_attr(
"referenceSource", refname);
594 }
else if (sn_.disabled[refst - 1][r - 1]) {
598 uc.set_attr(
"referenceSource",
"ClassSwitch");
600 uc.set_attr(
"referenceSource", refname);
608 void save_node(xml::Element& sim, std::size_t ind) {
610 xml::Element& node = sim.add_child(
"node");
611 node.set_attr(
"name", nname(ind));
612 const std::string parts[3] = {sec.input, sec.server, sec.output};
613 for (
int k = 0; k < 3; ++k) {
614 if (parts[k].empty())
continue;
615 std::string cls = parts[k];
620 xml::Element& xs = node.add_child(
"section");
621 xs.set_attr(
"className", cls);
622 if (cls ==
"Buffer") {
623 xs.set_attr(
"className",
"Queue");
624 save_buffer_capacity(xs, ind);
625 save_drop_strategy(xs, ind);
626 if (has_retrial(ind)) {
630 save_retrial_distributions(xs, ind);
631 save_get_strategy(xs, ind);
632 save_put_strategy(xs, ind);
634 save_get_strategy(xs, ind);
635 save_put_strategy(xs, ind);
636 save_impatience(xs, ind);
638 }
else if (cls ==
"Server") {
639 save_number_of_servers(xs, ind);
640 save_server_visits(xs);
641 save_service_strategy(xs, ind);
642 save_delay_off_strategy(xs, ind);
646 save_class_parallelism(xs, ind);
647 save_server_type_names(xs, ind);
648 save_servers_per_type(xs, ind);
649 save_server_compatibilities(xs, ind);
650 save_hetero_sched_policy(xs, ind);
651 warn_hetero_rates(ind);
652 warn_switchover_on_non_polling(ind);
653 }
else if (cls ==
"PreemptiveServer") {
654 save_number_of_servers(xs, ind);
655 save_server_visits(xs);
656 save_service_strategy(xs, ind);
657 save_delay_off_strategy(xs, ind);
658 }
else if (cls ==
"SharedServer") {
659 xs.set_attr(
"className",
"PSServer");
660 save_number_of_servers(xs, ind);
661 save_server_visits(xs);
662 save_service_strategy(xs, ind);
663 save_delay_off_strategy(xs, ind);
664 save_preemptive_strategy(xs, ind);
665 save_preemptive_weights(xs, ind);
666 }
else if (cls ==
"InfiniteServer") {
667 xs.set_attr(
"className",
"Delay");
668 save_service_strategy(xs, ind);
669 }
else if (cls ==
"PollingServer") {
670 xs.set_attr(
"className", polling_server_class(ind));
671 save_number_of_servers(xs, ind);
672 save_server_visits(xs);
673 save_service_strategy(xs, ind);
674 save_switchover_strategy(xs, ind);
675 }
else if (cls ==
"RandomSource") {
676 save_arrival_strategy(xs, ind);
677 }
else if (cls ==
"Dispatcher") {
678 xs.set_attr(
"className",
"Router");
679 save_routing_strategy(xs, ind);
680 }
else if (cls ==
"StatelessClassSwitcher") {
681 xs.set_attr(
"className",
"ClassSwitch");
682 save_class_switch_strategy(xs, ind);
683 }
else if (cls ==
"Cache") {
684 save_cache_strategy(xs, ind);
685 }
else if (cls ==
"LogTunnel") {
686 save_log_tunnel(xs, ind);
687 }
else if (cls ==
"Joiner") {
688 xs.set_attr(
"className",
"Join");
689 save_join_strategy(xs, ind);
690 }
else if (cls ==
"Forker") {
691 xs.set_attr(
"className",
"Fork");
692 save_fork_strategy(xs, ind);
693 }
else if (cls ==
"Storage") {
694 save_total_capacity(xs, ind);
695 save_place_capacities(xs, ind);
696 save_drop_rule(xs, ind);
697 save_get_strategy(xs, ind);
698 save_put_strategies(xs, ind);
699 }
else if (cls ==
"Enabling") {
700 save_enabling_conditions(xs, ind);
701 save_inhibiting_conditions(xs, ind);
702 }
else if (cls ==
"Firing") {
703 save_firing_outcomes(xs, ind);
704 }
else if (cls ==
"Timing") {
705 save_mode_names(xs, ind);
706 save_numbers_of_servers(xs, ind);
707 save_timing_strategies(xs, ind);
708 save_firing_priorities(xs, ind);
709 save_firing_weights(xs, ind);
716 const char* polling_server_class(std::size_t ind)
const {
717 const std::size_t ist = sn_.nodes[ind - 1].station;
718 const typename qn::NetworkStruct<T>::PollingParam pp = sn_.effective_polling(ist);
720 case PollingType::GATED:
return "GatedPollingServer";
721 case PollingType::EXHAUSTIVE:
return "ExhaustivePollingServer";
722 case PollingType::KLIMITED:
return "LimitedPollingServer";
723 case PollingType::DECREMENTING:
725 "SolverJMT: JMT does not support the decrementing (semiexhaustive) polling "
726 "discipline; use the LDES solver");
732 bool has_retrial(std::size_t ind)
const {
733 const std::size_t ist = sn_.nodes[ind - 1].station;
734 if (ist == 0)
return false;
735 const auto it = sn_.retrialparam.find(ist);
736 if (it == sn_.retrialparam.end())
return false;
737 for (
const lang::Distrib<T>& p : it->second.retrial_proc)
738 if (!p.disabled)
return true;
757 void warn_switchover_on_non_polling(std::size_t ind)
const {
758 const std::size_t ist = sn_.nodes[ind - 1].station;
759 if (ist == 0)
return;
760 for (
const lang::Distrib<T>& s : sn_.stations[ist - 1].switchover)
762 std::cerr <<
"[LINE] Warning: JMT does not support switchover times for "
763 <<
"non-polling queues. Switchover times will be ignored for node '"
764 << nname(ind) <<
"'." << std::endl;
771 if (!sn_.stations[ist - 1].switchover_pair.empty())
772 std::cerr <<
"[LINE] Warning: JMT does not support switchover times for "
773 <<
"non-polling queues. Switchover times will be ignored for node '"
774 << nname(ind) <<
"'." << std::endl;
798 void save_buffer_capacity(xml::Element& section, std::size_t ind) {
799 const std::size_t ist = sn_.nodes[ind - 1].station;
800 std::string v =
"-1";
801 if (ist != 0 && std::isfinite(sn_.cap[ist - 1])) {
802 const double total = reachable_population(ist);
803 const double ns = sn_.stations[ist - 1].nservers;
804 if (sn_.cap[ist - 1] < total && std::isfinite(ns)) {
805 assert_station_cap_exportable(ist);
832 double reachable_population(std::size_t ist)
const {
834 for (std::size_t c = 0; c < sn_.nchains; ++c) {
836 double chain_jobs = 0.0;
837 for (std::size_t r : sn_.inchain[c]) {
838 if (!sn_.disabled[ist - 1][r - 1]) served =
true;
839 chain_jobs += sn_.classes[r - 1].population;
841 if (served) n += chain_jobs;
859 bool is_bas_destination(std::size_t ist, std::size_t r)
const {
860 if (ist == 0 || sn_.isbasdestination.size() < ist)
return false;
861 if (sn_.isbasdestination[ist - 1].size() < r)
return false;
862 return sn_.isbasdestination[ist - 1][r - 1];
882 const char* drop_strategy_text(std::size_t ist, std::size_t r)
const {
883 if (ist == 0)
return "drop";
885 if (d == DropStrategy::WAITQ && is_bas_destination(ist, r))
932 void assert_station_cap_exportable(std::size_t ist)
const {
933 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
934 if (sn_.disabled[ist - 1][r - 1])
continue;
939 "SolverJMT: station '" + nname(sn_.station_to_node[ist - 1]) +
940 "' applies drop strategy '" +
jmt_drop_text(dr) +
"' to class '" + cname(r) +
941 "' and carries a finite capacity " +
jmt_int(sn_.cap[ist - 1]) +
942 " it can reach. JMT's queue section reads only 'drop', 'waiting queue', "
943 "'BAS blocking' and 'retrial'; it does not approximate anything else, it "
944 "ignores it, so the capacity would stop being enforced and the run would "
945 "return the unconstrained answer. Use SolverCTMC, SolverSSA or SolverLDES");
946 if (!std::isfinite(sn_.classes[r - 1].population))
continue;
947 if (dr != DropStrategy::WAITQ)
949 if (is_bas_destination(ist, r))
953 "SolverJMT: station '" + nname(sn_.station_to_node[ist - 1]) +
954 "' carries a finite capacity " +
jmt_int(sn_.cap[ist - 1]) +
955 " that binds for the closed class '" + cname(r) +
956 "'. LINE blocks a closed job that finds no room -- the upstream departure is "
957 "disabled and the job stays where it is -- and no JMT drop strategy reproduces "
958 "that: 'waiting queue' does not enforce the size at all, and 'BAS blocking' "
959 "completes the service before blocking, which is a different queueing model. "
960 "Use SolverCTMC, SolverSSA or SolverLDES, or declare DropStrategy.BAS if "
961 "blocking after service is the model you want, which SolverJMT does export");
966 void save_drop_strategy(xml::Element& section, std::size_t ind) {
967 const std::size_t ist = sn_.nodes[ind - 1].station;
968 xml::Element& p =
jmt_param(section,
"java.lang.String",
"dropStrategies",
true);
969 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
970 if (!keep_[r - 1])
continue;
972 const char* txt = drop_strategy_text(ist, r);
973 xml::Element& sp = p.add_child(
"subParameter");
974 sp.set_attr(
"classPath",
"java.lang.String");
975 sp.set_attr(
"name",
"dropStrategy");
976 sp.add_text_child(
"value", txt);
984 void save_get_strategy(xml::Element& section, std::size_t ind) {
985 const std::size_t ist = sn_.nodes[ind - 1].station;
986 const bool polling = sn_.nodes[ind - 1].nodetype == NodeType::Queue && ist != 0 &&
987 sn_.stations[ist - 1].sched == SchedStrategy::POLLING;
989 xml::Element& p = section.add_child(
"parameter");
990 p.set_attr(
"classPath",
"jmt.engine.NetStrategies.QueueGetStrategies.FCFSstrategy");
991 p.set_attr(
"name",
"FCFSstrategy");
994 const typename qn::NetworkStruct<T>::PollingParam pp = sn_.effective_polling(ist);
995 xml::Element& p = section.add_child(
"parameter");
997 case PollingType::GATED:
998 p.set_attr(
"classPath",
999 "jmt.engine.NetStrategies.QueueGetStrategies.GatedPollingGetStrategy");
1001 case PollingType::EXHAUSTIVE:
1004 "jmt.engine.NetStrategies.QueueGetStrategies.ExhaustivePollingGetStrategy");
1006 case PollingType::KLIMITED: {
1007 p.set_attr(
"classPath",
1008 "jmt.engine.NetStrategies.QueueGetStrategies.LimitedPollingGetStrategy");
1009 xml::Element& k = p.add_child(
"subParameter");
1010 k.set_attr(
"classPath",
"java.lang.Integer");
1011 k.set_attr(
"name",
"pollingKValue");
1012 k.add_text_child(
"value",
jmt_int(
static_cast<double>(pp.pk)));
1015 case PollingType::DECREMENTING:
1017 "SolverJMT: JMT does not support the decrementing (semiexhaustive) polling "
1018 "discipline; use the LDES solver");
1020 p.set_attr(
"name",
"FCFSstrategy");
1032 void save_put_strategy(xml::Element& section, std::size_t ind) {
1033 const std::size_t ist = sn_.nodes[ind - 1].station;
1035 jmt_param(section,
"jmt.engine.NetStrategies.QueuePutStrategy",
"QueuePutStrategy",
true);
1036 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1037 if (!keep_[r - 1])
continue;
1039 const char* nm =
"TailStrategy";
1041 switch (sn_.stations[ist - 1].sched) {
1042 case SchedStrategy::SIRO: nm =
"RandStrategy";
break;
1043 case SchedStrategy::LJF: nm =
"LJFStrategy";
break;
1044 case SchedStrategy::SJF: nm =
"SJFStrategy";
break;
1045 case SchedStrategy::LEPT: nm =
"LEPTStrategy";
break;
1046 case SchedStrategy::SEPT: nm =
"SEPTStrategy";
break;
1047 case SchedStrategy::LCFS: nm =
"HeadStrategy";
break;
1048 case SchedStrategy::LCFSPRIO: nm =
"HeadStrategyPriority";
break;
1049 case SchedStrategy::LCFSPR: nm =
"LCFSPRStrategy";
break;
1050 case SchedStrategy::LCFSPI: nm =
"LCFSPIStrategy";
break;
1051 case SchedStrategy::LCFSPRPRIO: nm =
"LCFSPRStrategyPriority";
break;
1052 case SchedStrategy::LCFSPIPRIO: nm =
"LCFSPIStrategyPriority";
break;
1053 case SchedStrategy::FCFSPR: nm =
"FCFSPRStrategy";
break;
1054 case SchedStrategy::FCFSPI: nm =
"FCFSPIStrategy";
break;
1055 case SchedStrategy::FCFSPRPRIO: nm =
"FCFSPRStrategyPriority";
break;
1056 case SchedStrategy::FCFSPIPRIO: nm =
"FCFSPIStrategyPriority";
break;
1057 case SchedStrategy::HOL: nm =
"TailStrategyPriority";
break;
1058 case SchedStrategy::EDD: nm =
"EDDStrategy";
break;
1059 case SchedStrategy::EDF: nm =
"EDFStrategy";
break;
1060 case SchedStrategy::SRPT: nm =
"SRPTStrategy";
break;
1061 case SchedStrategy::SRPTPRIO: nm =
"SRPTStrategyPriority";
break;
1062 default: nm =
"TailStrategy";
break;
1065 xml::Element& sp = p.add_child(
"subParameter");
1066 sp.set_attr(
"classPath",
1067 std::string(
"jmt.engine.NetStrategies.QueuePutStrategies.") + nm);
1068 sp.set_attr(
"name", nm);
1084 void save_number_of_servers(xml::Element& section, std::size_t ind) {
1085 const std::size_t ist = sn_.nodes[ind - 1].station;
1086 double maxjobs = 1.0;
1088 if (sn_.stations[ist - 1].sched == SchedStrategy::LPS) {
1091 maxjobs = sn_.stations[ist - 1].nservers;
1092 for (
const T& s : sn_.stations[ist - 1].lldscaling)
1093 maxjobs = std::max(maxjobs, d(s));
1100 void save_server_visits(xml::Element& section) {
1101 xml::Element& p =
jmt_param(section,
"java.lang.Integer",
"numberOfVisits",
true);
1102 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1103 if (!keep_[r - 1])
continue;
1105 xml::Element& sp = p.add_child(
"subParameter");
1106 sp.set_attr(
"classPath",
"java.lang.Integer");
1107 sp.set_attr(
"name",
"numberOfVisits");
1108 sp.add_text_child(
"value",
"1");
1123 struct ServerPools {
1124 bool present =
false;
1125 std::vector<std::string> names;
1126 std::vector<double> counts;
1127 std::vector<std::vector<bool>> compat;
1129 std::vector<std::size_t> parallelism;
1132 ServerPools server_pools(std::size_t ind)
const {
1134 const std::size_t ist = sn_.nodes[ind - 1].station;
1135 if (ist == 0)
return pools;
1136 const auto& st = sn_.stations[ist - 1];
1137 const bool has_types = !st.server_types.empty();
1138 bool has_parallelism =
false;
1139 for (std::size_t n : st.server_parallelism) {
1140 if (n > 1) { has_parallelism =
true;
break; }
1142 if (!has_types && !has_parallelism)
return pools;
1144 pools.present =
true;
1145 pools.parallelism.assign(sn_.nclasses, 1);
1146 for (std::size_t r = 0; r < sn_.nclasses && r < st.server_parallelism.size(); ++r) {
1147 pools.parallelism[r] = st.server_parallelism[r] < 1 ? 1 : st.server_parallelism[r];
1150 pools.policy = st.hetero_policy;
1151 for (
const auto& pool : st.server_types) {
1152 pools.names.push_back(pool.name);
1153 pools.counts.push_back(pool.count);
1154 std::vector<bool> row(sn_.nclasses,
true);
1155 for (std::size_t r = 0; r < sn_.nclasses; ++r) {
1158 row[r] = pool.compatible.empty()
1160 : (r < pool.compatible.size() ? pool.compatible[r] :
false);
1162 pools.compat.push_back(row);
1165 pools.names.push_back(sn_.nodes[ind - 1].name +
" - Server Type 1");
1166 pools.counts.push_back(st.nservers);
1167 pools.compat.push_back(std::vector<bool>(sn_.nclasses,
true));
1173 void save_class_parallelism(xml::Element& section, std::size_t ind) {
1174 const ServerPools pools = server_pools(ind);
1175 if (!pools.present)
return;
1176 xml::Element& p =
jmt_param(section,
"java.lang.Integer",
"classParallelism",
true);
1177 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1178 if (!keep_[r - 1])
continue;
1180 xml::Element& sp = p.add_child(
"subParameter");
1181 sp.set_attr(
"classPath",
"java.lang.Integer");
1182 sp.set_attr(
"name",
"serverParallelism");
1183 sp.add_text_child(
"value",
jmt_int(
static_cast<double>(pools.parallelism[r - 1])));
1188 void save_server_type_names(xml::Element& section, std::size_t ind) {
1189 const ServerPools pools = server_pools(ind);
1190 if (!pools.present)
return;
1191 xml::Element& p =
jmt_param(section,
"java.lang.String",
"serverNames",
true);
1192 for (
const std::string& name : pools.names) {
1193 xml::Element& sp = p.add_child(
"subParameter");
1194 sp.set_attr(
"classPath",
"java.lang.String");
1195 sp.set_attr(
"name",
"serverTypesNames");
1196 sp.add_text_child(
"value", name);
1201 void save_servers_per_type(xml::Element& section, std::size_t ind) {
1202 const ServerPools pools = server_pools(ind);
1203 if (!pools.present)
return;
1204 xml::Element& p =
jmt_param(section,
"java.lang.Integer",
"serversPerServerType",
true);
1205 for (
double count : pools.counts) {
1206 xml::Element& sp = p.add_child(
"subParameter");
1207 sp.set_attr(
"classPath",
"java.lang.Integer");
1208 sp.set_attr(
"name",
"serverTypesNumOfServers");
1209 sp.add_text_child(
"value",
jmt_int(count));
1214 void save_server_compatibilities(xml::Element& section, std::size_t ind) {
1215 const ServerPools pools = server_pools(ind);
1216 if (!pools.present)
return;
1217 xml::Element& p =
jmt_param(section,
"java.lang.Object",
"serverCompatibilities",
true);
1218 for (
const std::vector<bool>& row : pools.compat) {
1219 xml::Element& tn = p.add_child(
"subParameter");
1220 tn.set_attr(
"array",
"true");
1221 tn.set_attr(
"classPath",
"java.lang.Boolean");
1222 tn.set_attr(
"name",
"serverTypesCompatibilities");
1223 for (std::size_t r = 0; r < sn_.nclasses; ++r) {
1224 xml::Element& cn = tn.add_child(
"subParameter");
1225 cn.set_attr(
"classPath",
"java.lang.Boolean");
1226 cn.set_attr(
"name",
"compatibilities");
1227 cn.add_text_child(
"value", row[r] ?
"true" :
"false");
1233 void save_hetero_sched_policy(xml::Element& section, std::size_t ind) {
1234 const ServerPools pools = server_pools(ind);
1235 if (!pools.present)
return;
1249 void warn_hetero_rates(std::size_t ind)
const {
1250 const std::size_t ist = sn_.nodes[ind - 1].station;
1251 if (ist == 0)
return;
1252 const auto& pools = sn_.stations[ist - 1].server_types;
1253 if (pools.size() < 2)
return;
1254 bool first_set =
false, distinct =
false;
1256 for (
const auto& pool : pools) {
1257 for (
const lang::Distrib<T>& sd : pool.service) {
1258 if (sd.disabled)
continue;
1259 const double mean = num_traits<T>::to_double(sd.mean);
1260 if (!(mean > 0))
continue;
1261 if (!first_set) { first = mean; first_set =
true; }
1262 else if (std::abs(mean - first) > 1e-12) { distinct =
true; }
1265 if (!distinct)
return;
1266 std::cerr <<
"[LINE] Warning: JMT keys service strategies by job class, so the "
1267 <<
"per-server-type service rates of station '" << sn_.nodes[ind - 1].name
1268 <<
"' cannot be exported; every pool will serve at the class service rate. "
1269 <<
"Use the LDES or CTMC solver for per-type rates." << std::endl;
1279 void save_service_strategy(xml::Element& section, std::size_t ind) {
1280 const std::size_t ist = sn_.nodes[ind - 1].station;
1281 xml::Element& p =
jmt_param(section,
"jmt.engine.NetStrategies.ServiceStrategy",
1282 "ServiceStrategy",
true);
1283 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1284 if (!keep_[r - 1])
continue;
1286 xml::Element& sts = p.add_child(
"subParameter");
1287 const JmtDistView<T> v =
1288 ist == 0 ? JmtDistView<T>() :
jmt_dist_view(sn_.service[ist - 1][r - 1]);
1290 sts.set_attr(
"classPath",
1291 "jmt.engine.NetStrategies.ServiceStrategies."
1292 "DisabledServiceTimeStrategy");
1293 sts.set_attr(
"name",
"DisabledServiceTimeStrategy");
1297 sts.set_attr(
"classPath",
1298 "jmt.engine.NetStrategies.ServiceStrategies.ZeroServiceTimeStrategy");
1299 sts.set_attr(
"name",
"ZeroServiceTimeStrategy");
1302 sts.set_attr(
"classPath",
1303 "jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy");
1304 sts.set_attr(
"name",
"ServiceTimeStrategy");
1317 void save_arrival_strategy(xml::Element& section, std::size_t ind) {
1318 const std::size_t ist = sn_.nodes[ind - 1].station;
1319 xml::Element& p =
jmt_param(section,
"jmt.engine.NetStrategies.ServiceStrategy",
1320 "ServiceStrategy",
true);
1321 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1322 if (!keep_[r - 1])
continue;
1324 xml::Element& sts = p.add_child(
"subParameter");
1325 sts.set_attr(
"classPath",
1326 "jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy");
1327 sts.set_attr(
"name",
"ServiceTimeStrategy");
1328 const bool closed = std::isfinite(sn_.classes[r - 1].population);
1329 const JmtDistView<T> v =
1330 ist == 0 ? JmtDistView<T>() :
jmt_dist_view(sn_.service[ist - 1][r - 1]);
1332 sts.add_text_child(
"value",
"null");
1336 sts.set_attr(
"classPath",
1337 "jmt.engine.NetStrategies.ServiceStrategies.ZeroServiceTimeStrategy");
1338 sts.set_attr(
"name",
"ZeroServiceTimeStrategy");
1346 void save_preemptive_strategy(xml::Element& section, std::size_t ind) {
1347 const std::size_t ist = sn_.nodes[ind - 1].station;
1348 xml::Element& p =
jmt_param(section,
"jmt.engine.NetStrategies.PSStrategy",
"PSStrategy",
1350 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1351 if (!keep_[r - 1])
continue;
1353 const char* nm =
nullptr;
1354 if (ist != 0)
switch (sn_.stations[ist - 1].sched) {
1357 case SchedStrategy::PS:
1358 case SchedStrategy::LPS: nm =
"EPSStrategy";
break;
1359 case SchedStrategy::DPS: nm =
"DPSStrategy";
break;
1360 case SchedStrategy::GPS: nm =
"GPSStrategy";
break;
1361 case SchedStrategy::PSPRIO: nm =
"EPSStrategyPriority";
break;
1362 case SchedStrategy::DPSPRIO: nm =
"DPSStrategyPriority";
break;
1363 case SchedStrategy::GPSPRIO: nm =
"GPSStrategyPriority";
break;
1364 default: nm =
nullptr;
break;
1366 xml::Element& sp = p.add_child(
"subParameter");
1367 if (nm !=
nullptr) {
1368 sp.set_attr(
"classPath", std::string(
"jmt.engine.NetStrategies.PSStrategies.") + nm);
1369 sp.set_attr(
"name", nm);
1375 void save_preemptive_weights(xml::Element& section, std::size_t ind) {
1376 const std::size_t ist = sn_.nodes[ind - 1].station;
1377 xml::Element& p =
jmt_param(section,
"java.lang.Double",
"serviceWeights",
true);
1378 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1379 if (!keep_[r - 1])
continue;
1382 if (ist != 0 && sn_.stations[ist - 1].schedparam.size() >= r)
1383 w = d(sn_.stations[ist - 1].schedparam[r - 1]);
1384 xml::Element& sp = p.add_child(
"subParameter");
1385 sp.set_attr(
"classPath",
"java.lang.Double");
1386 sp.set_attr(
"name",
"serviceWeight");
1387 sp.add_text_child(
"value",
jmt_num(w));
1401 void save_delay_off_strategy(xml::Element& section, std::size_t ind) {
1402 const std::size_t ist = sn_.nodes[ind - 1].station;
1403 if (ist == 0)
return;
1404 const auto it = sn_.setupparam.find(ist);
1405 if (it == sn_.setupparam.end())
return;
1406 const qn::SetupDelayOffParam<T>& sp = it->second;
1408 for (
const lang::Distrib<T>& s : sp.setup)
1409 if (!s.disabled) any =
true;
1412 const char* names[2] = {
"delayOffTime",
"setUpTime"};
1413 for (
int which = 0; which < 2; ++which) {
1414 const std::vector<lang::Distrib<T>>& tab = which == 0 ? sp.delayoff : sp.setup;
1415 xml::Element& p =
jmt_param(section,
"java.lang.Object", names[which],
true);
1416 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1418 xml::Element& row = p.add_child(
"subParameter");
1419 row.set_attr(
"array",
"true");
1420 row.set_attr(
"classPath",
"jmt.engine.NetStrategies.ServiceStrategy");
1421 row.set_attr(
"name", names[which]);
1422 xml::Element& sts = row.add_child(
"subParameter");
1423 sts.set_attr(
"classPath",
1424 "jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy");
1425 sts.set_attr(
"name",
"ServiceTimeStrategy");
1426 if (r <= tab.size() && !tab[r - 1].disabled)
1427 append_simple_distribution(sts, tab[r - 1]);
1429 append_zero_time(sts);
1435 void append_zero_time(xml::Element& parent) {
1436 xml::Element& dn = parent.add_child(
"subParameter");
1437 dn.set_attr(
"classPath",
"jmt.engine.random.DeterministicDistr");
1438 dn.set_attr(
"name",
"Deterministic");
1439 xml::Element& par = parent.add_child(
"subParameter");
1440 par.set_attr(
"classPath",
"jmt.engine.random.DeterministicDistrPar");
1441 par.set_attr(
"name",
"distrPar");
1442 jmt_scalar(par,
"java.lang.Double",
"t",
"0.0");
1455 void append_simple_distribution(xml::Element& parent,
const lang::Distrib<T>& dist) {
1457 append_zero_time(parent);
1460 const double mean = d(dist.mean);
1462 xml::Element& dn = parent.add_child(
"subParameter");
1463 dn.set_attr(
"classPath",
"jmt.engine.random.Exponential");
1464 dn.set_attr(
"name",
"Exponential");
1465 xml::Element& par = parent.add_child(
"subParameter");
1466 par.set_attr(
"classPath",
"jmt.engine.random.ExponentialPar");
1467 par.set_attr(
"name",
"distrPar");
1468 jmt_double(par,
"lambda", mean > 0.0 ? 1.0 / mean : 0.0);
1472 const std::size_t ph = dist.phases();
1473 xml::Element& dn = parent.add_child(
"subParameter");
1474 dn.set_attr(
"classPath",
"jmt.engine.random.Erlang");
1475 dn.set_attr(
"name",
"Erlang");
1476 xml::Element& par = parent.add_child(
"subParameter");
1477 par.set_attr(
"classPath",
"jmt.engine.random.ErlangPar");
1478 par.set_attr(
"name",
"distrPar");
1479 jmt_double(par,
"alpha", mean > 0.0 ?
static_cast<double>(ph) / mean : 0.0);
1483 xml::Element& dn = parent.add_child(
"subParameter");
1484 dn.set_attr(
"classPath",
"jmt.engine.random.DeterministicDistr");
1485 dn.set_attr(
"name",
"Deterministic");
1486 xml::Element& par = parent.add_child(
"subParameter");
1487 par.set_attr(
"classPath",
"jmt.engine.random.DeterministicDistrPar");
1488 par.set_attr(
"name",
"distrPar");
1502 void save_routing_strategy(xml::Element& section, std::size_t ind) {
1503 const std::size_t K = sn_.nclasses;
1504 const bool is_cs = sn_.nodes[ind - 1].nodetype == NodeType::ClassSwitch;
1506 jmt_param(section,
"jmt.engine.NetStrategies.RoutingStrategy",
"RoutingStrategy",
true);
1507 for (std::size_t r = 1; r <= K; ++r) {
1508 if (!keep_[r - 1])
continue;
1511 is_cs ? RoutingStrategy::RAND
1512 : (sn_.nodes[ind - 1].routing.size() >= r ? sn_.nodes[ind - 1].routing[r - 1]
1513 : RoutingStrategy::PROB);
1514 xml::Element& sp = p.add_child(
"subParameter");
1516 case RoutingStrategy::RAND:
1517 sp.set_attr(
"classPath",
1518 "jmt.engine.NetStrategies.RoutingStrategies.RandomStrategy");
1519 sp.set_attr(
"name",
"Random");
1521 case RoutingStrategy::RROBIN:
1522 sp.set_attr(
"classPath",
1523 "jmt.engine.NetStrategies.RoutingStrategies.RoundRobinStrategy");
1524 sp.set_attr(
"name",
"Round Robin");
1526 case RoutingStrategy::JSQ:
1527 sp.set_attr(
"classPath",
1528 "jmt.engine.NetStrategies.RoutingStrategies."
1529 "ShortestQueueLengthRoutingStrategy");
1530 sp.set_attr(
"name",
"Join the Shortest Queue (JSQ)");
1532 case RoutingStrategy::SQ: {
1533 sp.set_attr(
"classPath",
1534 "jmt.engine.NetStrategies.RoutingStrategies.PowerOfKRoutingStrategy");
1535 sp.set_attr(
"name",
"Power of k");
1536 const int dpar = sn_.nodes[ind - 1].routing_param.size() >= r
1537 ? sn_.nodes[ind - 1].routing_param[r - 1]
1543 jmt_scalar(sp,
"java.lang.Boolean",
"withMemory",
"false");
1546 case RoutingStrategy::WRROBIN: {
1547 sp.set_attr(
"classPath",
1548 "jmt.engine.NetStrategies.RoutingStrategies."
1549 "WeightedRoundRobinStrategy");
1550 sp.set_attr(
"name",
"Weighted Round Robin");
1552 jmt_param_sub(sp,
"jmt.engine.NetStrategies.RoutingStrategies.WeightEntry",
1553 "WeightEntryArray");
1554 const std::map<std::size_t, double>& w =
1555 sn_.nodes[ind - 1].routing_weights.size() >= r
1556 ? sn_.nodes[ind - 1].routing_weights[r - 1]
1558 for (std::size_t j : outputs_of(ind)) {
1559 const auto wi = w.find(j);
1560 xml::Element& e = arr.add_child(
"subParameter");
1561 e.set_attr(
"classPath",
1562 "jmt.engine.NetStrategies.RoutingStrategies.WeightEntry");
1563 e.set_attr(
"name",
"WeightEntry");
1564 jmt_scalar(e,
"java.lang.String",
"stationName", nname(j));
1566 jmt_int(wi == w.end() ? 0.0 : wi->second));
1570 case RoutingStrategy::PROB: {
1571 sp.set_attr(
"classPath",
1572 "jmt.engine.NetStrategies.RoutingStrategies.EmpiricalStrategy");
1573 sp.set_attr(
"name",
"Probabilities");
1574 xml::Element& arr = jmt_param_sub(sp,
"jmt.engine.random.EmpiricalEntry",
1575 "EmpiricalEntryArray");
1576 for (std::size_t j : outputs_of(ind)) {
1577 const double pr = route_node(ind, r, j, r);
1578 if (!(pr > 0.0))
continue;
1579 xml::Element& e = arr.add_child(
"subParameter");
1580 e.set_attr(
"classPath",
"jmt.engine.random.EmpiricalEntry");
1581 e.set_attr(
"name",
"EmpiricalEntry");
1582 jmt_scalar(e,
"java.lang.String",
"stationName", nname(j));
1590 "jmt.engine.NetStrategies.RoutingStrategies.DisabledRoutingStrategy");
1591 sp.set_attr(
"name",
"Random");
1598 static xml::Element& jmt_param_sub(xml::Element& parent,
const char* cp,
const char* name) {
1599 xml::Element& e = parent.add_child(
"subParameter");
1600 e.set_attr(
"array",
"true");
1601 e.set_attr(
"classPath", cp);
1602 e.set_attr(
"name", name);
1607 double route_node(std::size_t i, std::size_t r, std::size_t j, std::size_t s)
const {
1608 const std::size_t K = sn_.nclasses;
1609 const std::size_t a = (i - 1) * K + (r - 1), b = (j - 1) * K + (s - 1);
1610 if (sn_.rtnodes.rows() <= a || sn_.rtnodes.cols() <= b)
return 0.0;
1611 return d(sn_.rtnodes(a, b));
1623 void save_class_switch_strategy(xml::Element& section, std::size_t ind) {
1624 const std::size_t K = sn_.nclasses;
1625 const std::vector<std::size_t> jset = outputs_of(ind);
1626 xml::Element& p =
jmt_param(section,
"java.lang.Object",
"matrix",
true);
1627 for (std::size_t r = 1; r <= K; ++r) {
1628 if (!keep_[r - 1])
continue;
1630 xml::Element& row = p.add_child(
"subParameter");
1631 row.set_attr(
"array",
"true");
1632 row.set_attr(
"classPath",
"java.lang.Float");
1633 row.set_attr(
"name",
"row");
1634 for (std::size_t s = 1; s <= K; ++s) {
1635 if (!keep_[s - 1])
continue;
1638 for (std::size_t j : jset) acc += route_node(ind, r, j, s);
1639 xml::Element& cell = row.add_child(
"subParameter");
1640 cell.set_attr(
"classPath",
"java.lang.Float");
1641 cell.set_attr(
"name",
"cell");
1642 cell.add_text_child(
"value",
jmt_fmt(acc));
1658 void save_fork_strategy(xml::Element& section, std::size_t ind) {
1659 const qn::ForkParam<T>* fk = sn_.fork_param_of(ind);
1660 const double fan_out = sn_.nodes[ind - 1].tasks_per_link;
1668 bool simplified =
true;
1670 for (std::size_t k = 0; k < fk->fan_out_link.rows() && simplified; ++k)
1671 for (std::size_t r = 0; r < fk->fan_out_link.cols() && simplified; ++r) {
1672 const double p = num_traits<T>::to_double(fk->fan_out_prob(k, r));
1673 if (p == 0.0)
continue;
1674 if (p != 1.0) simplified =
false;
1675 if (num_traits<T>::to_double(fk->fan_out_link(k, r)) != fan_out)
1677 if (!fk->fan_out_dist[k][r].disabled) simplified =
false;
1681 simplified ?
"true" :
"false");
1684 jmt_param(section,
"jmt.engine.NetStrategies.ForkStrategy",
"ForkStrategy",
true);
1685 const std::vector<std::size_t> outs = outputs_of(ind);
1686 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1687 if (!keep_[r - 1])
continue;
1689 xml::Element& cs = p.add_child(
"subParameter");
1690 cs.set_attr(
"classPath",
"jmt.engine.NetStrategies.ForkStrategies.ProbabilitiesFork");
1691 cs.set_attr(
"name",
"Branch Probabilities");
1692 xml::Element& arr = jmt_param_sub(
1693 cs,
"jmt.engine.NetStrategies.ForkStrategies.OutPath",
"EmpiricalEntryArray");
1695 ? sn_.nodes[ind - 1].routing[r - 1]
1696 : RoutingStrategy::PROB;
1697 if (rs != RoutingStrategy::PROB || outs.empty())
continue;
1698 for (std::size_t oi = 0; oi < outs.size(); ++oi) {
1699 xml::Element& entry = arr.add_child(
"subParameter");
1700 entry.set_attr(
"classPath",
"jmt.engine.NetStrategies.ForkStrategies.OutPath");
1701 entry.set_attr(
"name",
"OutPathEntry");
1702 xml::Element& unit = entry.add_child(
"subParameter");
1703 unit.set_attr(
"classPath",
"jmt.engine.random.EmpiricalEntry");
1704 unit.set_attr(
"name",
"outUnitProbability");
1705 jmt_scalar(unit,
"java.lang.String",
"stationName", nname(outs[oi]));
1708 const std::size_t k0 = outs[oi] - 1, r0 = r - 1;
1709 const bool has_fan = fk != 0;
1710 const double branch_p =
1711 has_fan ? num_traits<T>::to_double(fk->fan_out_prob(k0, r0)) : 1.0;
1717 std::vector<double> pts, prs;
1718 if (has_fan && !fk->fan_out_dist[k0][r0].disabled) {
1719 const lang::Distrib<T>& d = fk->fan_out_dist[k0][r0];
1721 for (std::size_t e = 0; e < d.params.size(); ++e)
1722 tot += num_traits<T>::to_double(d.params[e]);
1723 for (std::size_t e = 0; e < d.params.size(); ++e) {
1724 pts.push_back(d.trace.empty()
1725 ?
static_cast<double>(e + 1)
1726 : num_traits<T>::to_double(d.trace[e]));
1727 prs.push_back(num_traits<T>::to_double(d.params[e]) / tot);
1729 }
else if (has_fan) {
1730 pts.push_back(num_traits<T>::to_double(fk->fan_out_link(k0, r0)));
1733 pts.push_back(fan_out);
1738 jmt_param_sub(entry,
"jmt.engine.random.EmpiricalEntry",
"JobsPerLinkDis");
1739 for (std::size_t e = 0; e < pts.size(); ++e) {
1740 xml::Element& jple = jpl.add_child(
"subParameter");
1741 jple.set_attr(
"classPath",
"jmt.engine.random.EmpiricalEntry");
1742 jple.set_attr(
"name",
"EmpiricalEntry");
1758 void save_join_strategy(xml::Element& section, std::size_t ind) {
1759 const auto it = sn_.joindecl.find(ind);
1760 const bool partial =
1761 it != sn_.joindecl.end() && it->second.strategy == JoinStrategy::PARTIAL;
1762 const double quorum = it != sn_.joindecl.end() ? it->second.quorum : 0.0;
1764 jmt_param(section,
"jmt.engine.NetStrategies.JoinStrategy",
"JoinStrategy",
true);
1765 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1767 xml::Element& sp = p.add_child(
"subParameter");
1769 sp.set_attr(
"classPath",
"jmt.engine.NetStrategies.JoinStrategies.PartialJoin");
1770 sp.set_attr(
"name",
"Quorum");
1772 sp.set_attr(
"classPath",
"jmt.engine.NetStrategies.JoinStrategies.NormalJoin");
1773 sp.set_attr(
"name",
"Standard Join");
1775 const double req = partial ? quorum : (quorum > 0.0 ? quorum : -1.0);
1781 void save_log_tunnel(xml::Element& section, std::size_t ind) {
1782 const qn::NodeDef::LoggerParam& lg = sn_.nodes[ind - 1].logger;
1783 std::string path = lg.file_path.empty() ? sn_.log_path : lg.file_path;
1784 if (!path.empty() && path[path.size() - 1] !=
'/') path +=
'/';
1785 const char* bools[7] = {
"logExecTimestamp",
"logLoggerName",
"logTimeStamp",
1786 "logJobID",
"logJobClass",
"logTimeSameClass",
1788 const bool vals[7] = {lg.start_time, lg.logger_name, lg.timestamp, lg.job_id,
1789 lg.job_class, lg.time_same_class, lg.time_any_class};
1790 jmt_param_value(section,
"java.lang.String",
"logfileName", lg.file_name);
1792 for (
int j = 0; j < 7; ++j)
1793 jmt_param_value(section,
"java.lang.Boolean", bools[j], vals[j] ?
"true" :
"false");
1795 jmt_int(
static_cast<double>(sn_.nclasses)));
1808 void save_metrics(xml::Element& sim) {
1812 for (
int k = 0; k < 5; ++k) save_metric(sim, kinds[k]);
1815 save_fcr_metrics(sim);
1816 save_cache_hit_rate_metrics(sim);
1820 for (std::size_t ist = 1; ist <= sn_.nstations; ++ist)
1821 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1822 if (!keep_[r - 1])
continue;
1824 xml::Element& m = sim.add_child(
"measure");
1825 m.set_attr(
"alpha",
jmt_sig2(1.0 - opt_.sim_conf_int));
1826 m.set_attr(
"name", std::string(
"Performance_") +
1827 jmt_int(
static_cast<double>(ist)));
1828 m.set_attr(
"nodeType",
"station");
1829 m.set_attr(
"precision",
jmt_sig2(opt_.sim_max_rel_err));
1830 m.set_attr(
"referenceNode", nname(sn_.station_to_node[ist - 1]));
1831 m.set_attr(
"referenceUserClass", cname(r));
1833 m.set_attr(
"verbose",
"false");
1838 void save_cache_hit_rate_metrics(xml::Element& sim) {
1839 for (
const auto& kv : sn_.nodeparam) {
1840 const std::size_t ind = kv.first;
1841 if (sn_.nodes[ind - 1].nodetype != NodeType::Cache)
continue;
1842 const qn::CacheParam<T>& cp = kv.second;
1843 for (std::size_t r = 0; r < cp.hitclass.size(); ++r) {
1844 if (cp.hitclass[r] == 0)
continue;
1845 xml::Element& m = sim.add_child(
"measure");
1846 m.set_attr(
"alpha",
jmt_sig2(1.0 - opt_.sim_conf_int));
1847 m.set_attr(
"name",
"CacheHitRate_" + nname(ind) +
"_" + cname(cp.hitclass[r]));
1848 m.set_attr(
"nodeType",
"station");
1849 m.set_attr(
"precision",
jmt_sig2(opt_.sim_max_rel_err));
1850 m.set_attr(
"referenceNode", nname(ind));
1851 m.set_attr(
"referenceUserClass", cname(cp.hitclass[r]));
1852 m.set_attr(
"type",
"Cache Hit Rate");
1853 m.set_attr(
"verbose",
"false");
1859 void save_fcr_metrics(xml::Element& sim) {
1860 static const char* kinds[6] = {
"Number of Customers",
"Response Time",
"Residence Time",
1861 "Throughput",
"FCR Capacity",
"FCR Memory"};
1863 for (std::size_t f = 1; f <= sn_.regions.size(); ++f) {
1864 const std::string fcr =
"FCRegion" +
jmt_int(
static_cast<double>(f));
1865 for (
int k = 0; k < 6; ++k) {
1866 std::string flat(kinds[k]);
1867 flat.erase(std::remove(flat.begin(), flat.end(),
' '), flat.end());
1868 xml::Element& m = sim.add_child(
"measure");
1869 m.set_attr(
"alpha",
jmt_sig2(1.0 - opt_.sim_conf_int));
1870 m.set_attr(
"name",
"FCR_" + fcr +
"_" + flat +
"_" +
1871 jmt_int(
static_cast<double>(counter)));
1872 m.set_attr(
"nodeType",
"region");
1873 m.set_attr(
"precision",
jmt_sig2(opt_.sim_max_rel_err));
1874 m.set_attr(
"referenceNode", fcr);
1875 m.set_attr(
"referenceUserClass",
"");
1876 m.set_attr(
"type", kinds[k]);
1877 m.set_attr(
"verbose",
"false");
1884 void save_links(xml::Element& sim) {
1886 for (std::size_t j = 1; j <= sn_.nodes.size(); ++j)
1887 for (std::size_t i = 1; i <= sn_.nodes.size(); ++i) {
1888 if (!conn_[i - 1][j - 1])
continue;
1889 xml::Element& c = sim.add_child(
"connection");
1890 c.set_attr(
"source", nname(i));
1891 c.set_attr(
"target", nname(j));
1904 void save_preload(xml::Element& sim) {
1910 std::vector<std::pair<std::size_t, double>> pops;
1912 std::vector<Row> rows;
1913 for (std::size_t ist = 1; ist <= sn_.nstations; ++ist) {
1914 const std::size_t ind = sn_.station_to_node[ist - 1];
1915 const NodeType ty = sn_.nodes[ind - 1].nodetype;
1916 if (ty == NodeType::Source || ty == NodeType::Join)
continue;
1917 const std::vector<double> nir = initial_marginal(ist);
1920 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1921 const double n = sn_.classes[r - 1].population;
1922 if (std::isfinite(n) && n == 0.0 && nir[r - 1] == 0.0)
continue;
1923 row.pops.emplace_back(r, nir[r - 1]);
1925 if (!row.pops.empty()) rows.push_back(row);
1927 if (rows.empty())
return;
1928 xml::Element& preload = sim.add_child(
"preload");
1929 for (
const Row& row : rows) {
1930 xml::Element& st = preload.add_child(
"stationPopulations");
1931 st.set_attr(
"stationName", nname(row.node));
1932 for (
const auto& pc : row.pops) {
1933 xml::Element& cp = st.add_child(
"classPopulation");
1934 cp.set_attr(
"population",
jmt_int(pc.second));
1935 cp.set_attr(
"refClass", cname(pc.first));
1948 std::vector<double> initial_marginal(std::size_t ist)
const {
1949 std::vector<double> nir(sn_.nclasses, 0.0);
1950 const std::size_t ind = sn_.station_to_node[ist - 1];
1951 const auto im = sn_.initmarking.find(ind);
1952 if (im != sn_.initmarking.end()) {
1953 for (std::size_t r = 0; r < sn_.nclasses && r < im->second.size(); ++r)
1954 nir[r] = d(im->second[r]);
1957 for (std::size_t r = 0; r < sn_.nclasses; ++r) {
1958 const double n = sn_.classes[r].population;
1959 if (std::isfinite(n) && sn_.classes[r].refstat == ist) nir[r] = n;
1967 void save_total_capacity(xml::Element& section, std::size_t ind) {
1968 const std::size_t ist = sn_.nodes[ind - 1].station;
1969 const std::string v =
1970 (ist == 0 || !std::isfinite(sn_.cap[ist - 1])) ?
"-1" :
jmt_int(sn_.cap[ist - 1]);
1975 void save_place_capacities(xml::Element& section, std::size_t ind) {
1976 const std::size_t ist = sn_.nodes[ind - 1].station;
1977 xml::Element& p =
jmt_param(section,
"java.lang.Integer",
"capacities",
true);
1978 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1979 if (!keep_[r - 1])
continue;
1981 const double c = ist == 0 ? std::numeric_limits<double>::infinity()
1982 : sn_.classcap[ist - 1][r - 1];
1983 xml::Element& sp = p.add_child(
"subParameter");
1984 sp.set_attr(
"classPath",
"java.lang.Integer");
1985 sp.set_attr(
"name",
"capacity");
1986 sp.add_text_child(
"value", std::isfinite(c) ?
jmt_int(c) :
"-1");
1994 void save_drop_rule(xml::Element& section, std::size_t ind) {
1995 const std::size_t ist = sn_.nodes[ind - 1].station;
1996 xml::Element& p =
jmt_param(section,
"java.lang.String",
"dropRules",
true);
1997 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1998 if (!keep_[r - 1])
continue;
2000 xml::Element& sp = p.add_child(
"subParameter");
2001 sp.set_attr(
"classPath",
"java.lang.String");
2002 sp.set_attr(
"name",
"dropRule");
2003 sp.add_text_child(
"value", drop_strategy_text(ist, r));
2012 void save_put_strategies(xml::Element& section, std::size_t ind) {
2013 const std::size_t ist = sn_.nodes[ind - 1].station;
2014 xml::Element& p =
jmt_param(section,
"jmt.engine.NetStrategies.QueuePutStrategy",
2015 "QueuePutStrategy",
true);
2016 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2017 if (!keep_[r - 1])
continue;
2019 const char* nm =
"TailStrategy";
2021 if (sn_.stations[ist - 1].sched == SchedStrategy::SIRO) nm =
"RandStrategy";
2022 else if (sn_.stations[ist - 1].sched == SchedStrategy::LCFS) nm =
"HeadStrategy";
2024 xml::Element& sp = p.add_child(
"subParameter");
2025 sp.set_attr(
"classPath",
2026 std::string(
"jmt.engine.NetStrategies.QueuePutStrategies.") + nm);
2027 sp.set_attr(
"name", nm);
2034 const qn::TransitionParam<T>& transparam(std::size_t ind)
const {
2035 const auto it = sn_.transparam.find(ind);
2036 if (it == sn_.transparam.end())
2037 throw InputError(
"SolverJMT: transition '" + nname(ind) +
"' carries no mode table");
2054 void save_enabling_conditions(xml::Element& section, std::size_t ind) {
2055 const qn::TransitionParam<T>& tp = transparam(ind);
2057 jmt_param(section,
"jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix",
2058 "enablingConditions",
true);
2059 for (std::size_t m = 0; m < tp.nmodes; ++m) {
2060 xml::Element& cond = p.add_child(
"subParameter");
2061 cond.set_attr(
"classPath",
2062 "jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix");
2063 cond.set_attr(
"name",
"enablingCondition");
2064 xml::Element& vecs = jmt_param_sub(
2065 cond,
"jmt.engine.NetStrategies.TransitionUtilities.TransitionVector",
2067 for (std::size_t k = 1; k <= sn_.nodes.size(); ++k) {
2068 bool relevant =
false;
2069 for (std::size_t r = 1; r <= sn_.nclasses && !relevant; ++r) {
2070 const double en = arc(tp.enabling, m, k, r);
2071 const double in = arc(tp.inhibiting, m, k, r);
2072 relevant = (std::isfinite(en) && en > 0.0) ||
2073 (std::isfinite(in) && in > 0.0);
2075 if (!relevant)
continue;
2076 xml::Element& vec = vecs.add_child(
"subParameter");
2077 vec.set_attr(
"classPath",
2078 "jmt.engine.NetStrategies.TransitionUtilities.TransitionVector");
2079 vec.set_attr(
"name",
"enablingVector");
2080 jmt_scalar(vec,
"java.lang.String",
"stationName", nname(k));
2081 xml::Element& entries = jmt_param_sub(vec,
"java.lang.Integer",
"enablingEntries");
2082 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2083 const double en = arc(tp.enabling, m, k, r);
2085 xml::Element& e = entries.add_child(
"subParameter");
2086 e.set_attr(
"classPath",
"java.lang.Integer");
2087 e.set_attr(
"name",
"enablingEntry");
2088 e.add_text_child(
"value", std::isfinite(en) ?
jmt_int(en) :
"-1");
2102 void save_inhibiting_conditions(xml::Element& section, std::size_t ind) {
2103 const qn::TransitionParam<T>& tp = transparam(ind);
2104 const std::vector<std::size_t> inputs = inputs_of(ind);
2106 jmt_param(section,
"jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix",
2107 "inhibitingConditions",
true);
2108 for (std::size_t m = 0; m < tp.nmodes; ++m) {
2109 xml::Element& cond = p.add_child(
"subParameter");
2110 cond.set_attr(
"classPath",
2111 "jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix");
2112 cond.set_attr(
"name",
"inhibitingCondition");
2113 xml::Element& vecs = jmt_param_sub(
2114 cond,
"jmt.engine.NetStrategies.TransitionUtilities.TransitionVector",
2115 "inhibitingVectors");
2116 for (std::size_t k : inputs) {
2117 xml::Element& vec = vecs.add_child(
"subParameter");
2118 vec.set_attr(
"classPath",
2119 "jmt.engine.NetStrategies.TransitionUtilities.TransitionVector");
2120 vec.set_attr(
"name",
"inhibitingVector");
2121 jmt_scalar(vec,
"java.lang.String",
"stationName", nname(k));
2122 xml::Element& entries = jmt_param_sub(vec,
"java.lang.Integer",
"inhibitingEntries");
2123 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2124 const double in = arc(tp.inhibiting, m, k, r);
2126 xml::Element& e = entries.add_child(
"subParameter");
2127 e.set_attr(
"classPath",
"java.lang.Integer");
2128 e.set_attr(
"name",
"inhibitingEntry");
2129 e.add_text_child(
"value", std::isfinite(in) ?
jmt_int(in) :
"0");
2136 void save_firing_outcomes(xml::Element& section, std::size_t ind) {
2137 const qn::TransitionParam<T>& tp = transparam(ind);
2138 const std::vector<std::size_t> outs = outputs_of(ind);
2140 jmt_param(section,
"jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix",
2141 "firingOutcomes",
true);
2142 for (std::size_t m = 0; m < tp.nmodes; ++m) {
2143 xml::Element& out = p.add_child(
"subParameter");
2144 out.set_attr(
"classPath",
2145 "jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix");
2146 out.set_attr(
"name",
"firingOutcome");
2147 xml::Element& vecs = jmt_param_sub(
2148 out,
"jmt.engine.NetStrategies.TransitionUtilities.TransitionVector",
2150 for (std::size_t k : outs) {
2151 xml::Element& vec = vecs.add_child(
"subParameter");
2152 vec.set_attr(
"classPath",
2153 "jmt.engine.NetStrategies.TransitionUtilities.TransitionVector");
2154 vec.set_attr(
"name",
"firingVector");
2155 jmt_scalar(vec,
"java.lang.String",
"stationName", nname(k));
2156 xml::Element& entries = jmt_param_sub(vec,
"java.lang.Integer",
"firingEntries");
2157 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2158 const double f = arc(tp.firing, m, k, r);
2160 xml::Element& e = entries.add_child(
"subParameter");
2161 e.set_attr(
"classPath",
"java.lang.Integer");
2162 e.set_attr(
"name",
"firingEntry");
2163 e.add_text_child(
"value",
jmt_int(f));
2170 double arc(
const std::vector<Matrix<T>>& tab, std::size_t m, std::size_t k,
2171 std::size_t r)
const {
2172 if (m >= tab.size() || k == 0 || k > tab[m].rows() || r == 0 || r > tab[m].cols())
2174 return d(tab[m](k - 1, r - 1));
2178 void save_mode_names(xml::Element& section, std::size_t ind) {
2179 const qn::TransitionParam<T>& tp = transparam(ind);
2180 xml::Element& p =
jmt_param(section,
"java.lang.String",
"modeNames",
true);
2181 for (std::size_t m = 0; m < tp.nmodes; ++m) {
2182 xml::Element& sp = p.add_child(
"subParameter");
2183 sp.set_attr(
"classPath",
"java.lang.String");
2184 sp.set_attr(
"name",
"modeName");
2185 sp.add_text_child(
"value", m < tp.modenames.size() ? tp.modenames[m] : std::string());
2190 void save_numbers_of_servers(xml::Element& section, std::size_t ind) {
2191 const qn::TransitionParam<T>& tp = transparam(ind);
2192 xml::Element& p =
jmt_param(section,
"java.lang.Integer",
"numbersOfServers",
true);
2193 for (std::size_t m = 0; m < tp.nmodes; ++m) {
2194 const double ns = m < tp.nmodeservers.size() ? tp.nmodeservers[m] : 1.0;
2195 xml::Element& sp = p.add_child(
"subParameter");
2196 sp.set_attr(
"classPath",
"java.lang.Integer");
2197 sp.set_attr(
"name",
"numberOfServers");
2198 sp.add_text_child(
"value", std::isfinite(ns) ?
jmt_int(ns) :
"-1");
2210 void save_timing_strategies(xml::Element& section, std::size_t ind) {
2211 const qn::TransitionParam<T>& tp = transparam(ind);
2212 xml::Element& p =
jmt_param(section,
"jmt.engine.NetStrategies.ServiceStrategy",
2213 "timingStrategies",
true);
2214 for (std::size_t m = 0; m < tp.nmodes; ++m) {
2215 xml::Element& sp = p.add_child(
"subParameter");
2216 const bool immediate = m < tp.timing.size() &&
2219 sp.set_attr(
"classPath",
2220 "jmt.engine.NetStrategies.ServiceStrategies.ZeroServiceTimeStrategy");
2221 sp.set_attr(
"name",
"ZeroServiceTimeStrategy");
2224 sp.set_attr(
"classPath",
2225 "jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy");
2226 sp.set_attr(
"name",
"timingStrategy");
2227 if (m >= tp.firingproc.size())
2229 (m < tp.modenames.size() ? tp.modenames[m] : std::string()) +
2230 "' of transition '" + nname(ind) +
"' has no firing process");
2232 "SolverJMT (transition firing)");
2237 void save_firing_priorities(xml::Element& section, std::size_t ind) {
2238 const qn::TransitionParam<T>& tp = transparam(ind);
2239 xml::Element& p =
jmt_param(section,
"java.lang.Integer",
"firingPriorities",
true);
2240 for (std::size_t m = 0; m < tp.nmodes; ++m) {
2241 const double v = m < tp.firingprio.size() ? tp.firingprio[m] : 1.0;
2242 xml::Element& sp = p.add_child(
"subParameter");
2243 sp.set_attr(
"classPath",
"java.lang.Integer");
2244 sp.set_attr(
"name",
"firingPriority");
2245 sp.add_text_child(
"value", std::isfinite(v) ?
jmt_int(v) :
"-1");
2258 void save_firing_weights(xml::Element& section, std::size_t ind) {
2259 const qn::TransitionParam<T>& tp = transparam(ind);
2260 xml::Element& p =
jmt_param(section,
"java.lang.Double",
"firingWeights",
true);
2261 for (std::size_t m = 0; m < tp.nmodes; ++m) {
2262 const double v = m < tp.fireweight.size() ? d(tp.fireweight[m]) : 1.0;
2263 xml::Element& sp = p.add_child(
"subParameter");
2264 sp.set_attr(
"classPath",
"java.lang.Double");
2265 sp.set_attr(
"name",
"firingWeight");
2266 sp.add_text_child(
"value", std::isfinite(v) ?
jmt_fmt(v) :
"-1");
2282 std::vector<std::vector<double>> class_cap_constraints()
const {
2283 const double inf = std::numeric_limits<double>::infinity();
2284 std::vector<std::vector<double>> con(sn_.nstations, std::vector<double>(sn_.nclasses, inf));
2285 if (sn_.classcap.empty())
return con;
2286 std::vector<double> chainpop(sn_.nclasses, inf);
2287 for (std::size_t c = 0; c < sn_.inchain.size(); ++c) {
2290 for (std::size_t r : sn_.inchain[c]) {
2291 const double n = sn_.classes[r - 1].population;
2292 if (std::isfinite(n)) tot += n;
else open =
true;
2294 for (std::size_t r : sn_.inchain[c]) chainpop[r - 1] = open ? inf : tot;
2296 for (std::size_t ist = 1; ist <= sn_.nstations; ++ist) {
2297 const NodeType ty = sn_.nodes[sn_.station_to_node[ist - 1] - 1].nodetype;
2298 if (ty == NodeType::Source || ty == NodeType::Place)
continue;
2299 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2300 if (sn_.disabled[ist - 1][r - 1])
continue;
2301 const double cc = sn_.classcap[ist - 1][r - 1];
2302 if (std::isfinite(cc) && cc < 2147483647.0 &&
2303 cc < std::min(sn_.cap[ist - 1], chainpop[r - 1]))
2304 con[ist - 1][r - 1] = cc;
2320 void assert_class_cap_exportable(std::size_t ist, std::size_t r)
const {
2321 const std::string sname = nname(sn_.station_to_node[ist - 1]);
2322 if (std::isfinite(sn_.classes[r - 1].population))
2324 "SolverJMT: station '" + sname +
"' carries a finite capacity " +
2325 jmt_int(sn_.classcap[ist - 1][r - 1]) +
" for the closed class '" + cname(r) +
2326 "'. LINE holds a blocked closed job at its upstream station, whereas JMT can "
2327 "only express a per-class capacity as a blocking region, which parks the job in "
2328 "the region input station instead, freeing the upstream server and losing it "
2329 "from the population count. Use SolverCTMC, SolverSSA or SolverLDES, or express "
2330 "the limit as the station capacity");
2332 if (dr == DropStrategy::BAS || dr == DropStrategy::BBS || dr == DropStrategy::RSRD ||
2333 dr == DropStrategy::RETRIAL || dr == DropStrategy::RETRIAL_WITH_LIMIT)
2335 "SolverJMT: station '" + sname +
"' applies drop strategy '" +
jmt_drop_text(dr) +
2336 "' to class '" + cname(r) +
2337 "' and also carries a finite capacity for it. JMT exports a per-class capacity "
2338 "as a blocking region, which can only drop or defer an arrival. Remove the "
2339 "per-class capacity, or use the station capacity, which is exported with its "
2355 void save_regions(xml::Element& sim) {
2356 const std::vector<std::vector<double>> con = class_cap_constraints();
2357 std::vector<bool> covered(sn_.nstations,
false);
2358 std::size_t lps_idx = sn_.regions.size();
2360 for (std::size_t ist = 1; ist <= sn_.nstations; ++ist) {
2361 if (sn_.stations[ist - 1].sched != SchedStrategy::LPS)
continue;
2363 const std::size_t ind = sn_.station_to_node[ist - 1];
2364 const double limit = sn_.stations[ist - 1].schedparam.empty()
2366 : d(sn_.stations[ist - 1].schedparam[0]);
2367 xml::Element& br = sim.add_child(
"blockingRegion");
2368 br.set_attr(
"name",
"LPSRegion" +
jmt_int(
static_cast<double>(lps_idx)));
2369 br.set_attr(
"type",
"default");
2370 br.add_child(
"regionNode").set_attr(
"nodeName", nname(ind));
2371 br.add_child(
"globalConstraint").set_attr(
"maxJobs",
jmt_num(limit));
2372 br.add_child(
"globalMemoryConstraint").set_attr(
"maxMemory",
"-1");
2373 covered[ist - 1] =
true;
2374 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2375 if (!std::isfinite(con[ist - 1][r - 1]))
continue;
2376 assert_class_cap_exportable(ist, r);
2378 "SolverJMT: station '" + nname(ind) +
2379 "' has both LPS scheduling and a finite capacity for the open class '" +
2381 "'. JMT expresses both through a single blocking region, which admits only "
2382 "one drop rule per class, but LPS requires blocking while the open-class "
2383 "capacity requires dropping. Remove the per-class capacity or use a non-LPS "
2384 "scheduling strategy");
2386 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2387 xml::Element& dr = br.add_child(
"dropRules");
2388 dr.set_attr(
"jobClass", cname(r));
2389 dr.set_attr(
"dropThisClass",
"false");
2393 for (std::size_t f = 1; f <= sn_.regions.size(); ++f) {
2394 const typename qn::NetworkStruct<T>::Region& rg = sn_.regions[f - 1];
2395 std::vector<std::size_t> members;
2396 for (std::size_t ist = 1; ist <= sn_.nstations; ++ist)
2397 if (ist <= rg.members.size() && rg.members[ist - 1]) {
2398 members.push_back(ist);
2399 covered[ist - 1] =
true;
2401 std::vector<double> region_class_cap(sn_.nclasses,
2402 std::numeric_limits<double>::infinity());
2403 for (std::size_t ist : members)
2404 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2405 if (!std::isfinite(con[ist - 1][r - 1]))
continue;
2406 if (members.size() > 1)
2408 "SolverJMT: station '" + nname(sn_.station_to_node[ist - 1]) +
2409 "' carries a finite capacity for class '" + cname(r) +
2410 "' and also belongs to the multi-station region FCRegion" +
2411 jmt_int(
static_cast<double>(f)) +
2412 ". JMT constrains a blocking region as a whole and allows a node to "
2413 "belong to only one region, so a per-station class capacity cannot "
2414 "be expressed alongside it");
2415 assert_class_cap_exportable(ist, r);
2416 if (!(r <= rg.rule.size() && rg.rule[r - 1] == DropStrategy::DROP))
2418 "SolverJMT: station '" + nname(sn_.station_to_node[ist - 1]) +
2419 "' carries a finite capacity for class '" + cname(r) +
2420 "' and also belongs to region FCRegion" +
2421 jmt_int(
static_cast<double>(f)) +
2422 ", whose drop rule for that class is not DROP. A JMT blocking "
2423 "region admits a single drop rule per class, shared by all of its "
2424 "constraints, and the per-class capacity of an open class is a loss "
2426 region_class_cap[r - 1] = con[ist - 1][r - 1];
2429 xml::Element& br = sim.add_child(
"blockingRegion");
2430 br.set_attr(
"name",
"FCRegion" +
jmt_int(
static_cast<double>(f)));
2431 br.set_attr(
"type",
"default");
2432 for (std::size_t ist : members)
2433 br.add_child(
"regionNode")
2434 .set_attr(
"nodeName", nname(sn_.station_to_node[ist - 1]));
2438 members.empty() ? -1.0 : rg.cap[members[0] - 1][sn_.nclasses];
2439 const double gmem = members.empty() ? -1.0 : rg.maxmem[members[0] - 1];
2440 br.add_child(
"globalConstraint").set_attr(
"maxJobs",
jmt_num(gmax));
2441 br.add_child(
"globalMemoryConstraint").set_attr(
"maxMemory",
jmt_num(gmem));
2442 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2443 double cmax = members.empty() ? -1.0 : rg.cap[members[0] - 1][r - 1];
2447 cmax = std::isfinite(region_class_cap[r - 1]) ? region_class_cap[r - 1] : -1.0;
2448 else if (std::isfinite(region_class_cap[r - 1]))
2449 cmax = std::min(cmax, region_class_cap[r - 1]);
2450 if (cmax == -1.0 || !std::isfinite(cmax))
continue;
2451 xml::Element& cc = br.add_child(
"classConstraint");
2452 cc.set_attr(
"jobClass", cname(r));
2453 cc.set_attr(
"maxJobsPerClass",
jmt_num(cmax));
2459 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2460 xml::Element& dr = br.add_child(
"dropRules");
2461 dr.set_attr(
"jobClass", cname(r));
2462 dr.set_attr(
"dropThisClass",
2463 (r <= rg.rule.size() && rg.rule[r - 1] == DropStrategy::DROP)
2467 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2468 if (r > rg.weight.size())
continue;
2469 const double w = d(rg.weight[r - 1]);
2470 if (w == 1.0)
continue;
2471 xml::Element& cw = br.add_child(
"classWeight");
2472 cw.set_attr(
"jobClass", cname(r));
2473 cw.set_attr(
"weight",
jmt_num(w));
2475 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2476 if (r > rg.size.size())
continue;
2477 const double s = d(rg.size[r - 1]);
2478 if (s == 1.0)
continue;
2479 xml::Element& cs = br.add_child(
"classSize");
2480 cs.set_attr(
"jobClass", cname(r));
2481 cs.set_attr(
"size",
jmt_num(s));
2485 std::size_t cap_idx = 0;
2486 for (std::size_t ist = 1; ist <= sn_.nstations; ++ist) {
2487 if (covered[ist - 1])
continue;
2489 for (std::size_t r = 1; r <= sn_.nclasses; ++r)
2490 if (std::isfinite(con[ist - 1][r - 1])) any =
true;
2493 for (std::size_t r = 1; r <= sn_.nclasses; ++r)
2494 if (std::isfinite(con[ist - 1][r - 1])) assert_class_cap_exportable(ist, r);
2495 xml::Element& br = sim.add_child(
"blockingRegion");
2496 br.set_attr(
"name",
"ClassCapRegion" +
jmt_int(
static_cast<double>(cap_idx)));
2497 br.set_attr(
"type",
"default");
2498 br.add_child(
"regionNode")
2499 .set_attr(
"nodeName", nname(sn_.station_to_node[ist - 1]));
2500 br.add_child(
"globalConstraint").set_attr(
"maxJobs",
"-1");
2501 br.add_child(
"globalMemoryConstraint").set_attr(
"maxMemory",
"-1");
2502 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2503 if (!std::isfinite(con[ist - 1][r - 1]))
continue;
2504 xml::Element& cc = br.add_child(
"classConstraint");
2505 cc.set_attr(
"jobClass", cname(r));
2506 cc.set_attr(
"maxJobsPerClass",
jmt_num(con[ist - 1][r - 1]));
2508 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2509 if (!std::isfinite(con[ist - 1][r - 1]))
continue;
2510 xml::Element& dr = br.add_child(
"dropRules");
2511 dr.set_attr(
"jobClass", cname(r));
2512 dr.set_attr(
"dropThisClass",
"true");
2533 void save_cache_strategy(xml::Element& section, std::size_t ind) {
2534 const auto it = sn_.nodeparam.find(ind);
2535 if (it == sn_.nodeparam.end())
2536 throw InputError(
"SolverJMT: cache node '" + nname(ind) +
"' carries no parameters");
2537 const qn::CacheParam<T>& cp = it->second;
2538 const std::size_t K = sn_.nclasses;
2541 jmt_int(
static_cast<double>(cp.nitems)));
2542 xml::Element& cap =
jmt_param(section,
"java.lang.Integer",
"cacheCapacity",
true);
2543 for (std::size_t l = 0; l < cp.itemcap.size(); ++l) {
2544 xml::Element& sp = cap.add_child(
"subParameter");
2545 sp.set_attr(
"classPath",
"java.lang.Integer");
2546 sp.set_attr(
"name",
"capacity");
2547 sp.add_text_child(
"value",
jmt_int(
static_cast<double>(cp.itemcap[l])));
2550 const std::size_t nlev = cp.itemcap.size();
2552 xml::Element& mat =
jmt_param(section,
"java.lang.Object",
"matrix",
true);
2553 for (std::size_t a = 1; a <= nlev; ++a) {
2554 xml::Element& row = mat.add_child(
"subParameter");
2555 row.set_attr(
"array",
"true");
2556 row.set_attr(
"classPath",
"java.lang.Float");
2557 row.set_attr(
"name",
"row");
2558 for (std::size_t b = 1; b <= nlev; ++b) {
2559 const bool one = lru ? (a < nlev ? b == a + 1 : b == nlev) : a == b;
2560 xml::Element& cell = row.add_child(
"subParameter");
2561 cell.set_attr(
"classPath",
"java.lang.Float");
2562 cell.set_attr(
"name",
"cell");
2563 cell.add_text_child(
"value", one ?
"1.0" :
"0.0");
2567 xml::Element& jc =
jmt_param(section,
"jmt.engine.QueueNet.JobClass",
"jobClasses",
true);
2568 for (std::size_t r = 1; r <= K; ++r) {
2569 const bool used = (r <= cp.hitclass.size() && cp.hitclass[r - 1] > 0) ||
2570 (r <= cp.missclass.size() && cp.missclass[r - 1] > 0);
2571 if (!used)
continue;
2572 xml::Element& sp = jc.add_child(
"subParameter");
2573 sp.set_attr(
"classPath",
"jmt.engine.QueueNet.JobClass");
2574 sp.set_attr(
"name",
"jobClass");
2575 sp.add_text_child(
"value", cname(r));
2577 const char* switch_names[2] = {
"hitClasses",
"missClasses"};
2578 const char* entry_names[2] = {
"hitClass",
"missClass"};
2579 for (
int w = 0; w < 2; ++w) {
2580 const std::vector<std::size_t>& tab = w == 0 ? cp.hitclass : cp.missclass;
2582 jmt_param(section,
"jmt.engine.QueueNet.JobClass", switch_names[w],
true);
2583 for (std::size_t r = 1; r <= K && r <= tab.size(); ++r) {
2584 if (tab[r - 1] == 0)
continue;
2585 xml::Element& sp = p.add_child(
"subParameter");
2586 sp.set_attr(
"classPath",
"jmt.engine.QueueNet.JobClass");
2587 sp.set_attr(
"name", entry_names[w]);
2588 sp.add_text_child(
"value", cname(tab[r - 1]));
2592 const char* policy =
nullptr;
2593 switch (cp.replacestrat) {
2595 policy =
"jmt.engine.NetStrategies.CacheStrategies.LRUCache";
2599 policy =
"jmt.engine.NetStrategies.CacheStrategies.FIFOCache";
2602 policy =
"jmt.engine.NetStrategies.CacheStrategies.RandomCache";
2606 "SolverJMT: cache '" + nname(ind) +
2607 "' uses a replacement policy JMT has no cache object for (HLRU, CLIMB and "
2608 "QLRU); use SolverLDES, which simulates them directly");
2610 xml::Element& rp = section.add_child(
"parameter");
2611 rp.set_attr(
"classPath", policy);
2612 rp.set_attr(
"name",
"replacePolicy");
2622 section,
"jmt.engine.random.discrete.DiscreteDistribution",
"popularity",
true);
2623 for (std::size_t r = 1; r <= K; ++r) {
2625 const bool reads = r <= cp.pread.size() && !cp.pread[r - 1].empty();
2626 const typename qn::CacheParam<T>::Popularity kind =
2627 r <= cp.preadkind.size() ? cp.preadkind[r - 1]
2628 :
typename qn::CacheParam<T>::Popularity();
2629 xml::Element& sp = pop.add_child(
"subParameter");
2631 sp.set_attr(
"classPath",
"jmt.engine.random.discrete.Zipf");
2632 sp.set_attr(
"name",
"popularity");
2634 jmt_scalar(sp,
"java.lang.Integer",
"numberOfElements",
2635 jmt_int(
static_cast<double>(kind.n)));
2637 sp.set_attr(
"classPath",
"jmt.engine.random.discrete.Uniform");
2638 sp.set_attr(
"name",
"popularity");
2639 jmt_scalar(sp,
"java.lang.Integer",
"min",
"1");
2641 jmt_int(
static_cast<double>(kind.n == 0 ? cp.nitems : kind.n)));
2643 sp.set_attr(
"classPath",
"jmt.engine.random.discrete.DiscreteDistribution");
2644 sp.set_attr(
"name",
"null");
2645 sp.add_text_child(
"value",
"null");
2660 void save_impatience(xml::Element& section, std::size_t ind) {
2661 const std::size_t ist = sn_.nodes[ind - 1].station;
2663 section,
"jmt.engine.NetStrategies.ImpatienceStrategies.Impatience",
"Impatience",
true);
2664 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2665 if (!keep_[r - 1])
continue;
2667 xml::Element& sp = p.add_child(
"subParameter");
2668 const qn::Station<T>* st = ist == 0 ? nullptr : &sn_.stations[ist - 1];
2669 const bool balks = st !=
nullptr && r <= st->balking.size() &&
2670 st->balking[r - 1].strategy ==
2673 sp.set_attr(
"classPath",
"jmt.engine.NetStrategies.ImpatienceStrategies.Balking");
2674 sp.set_attr(
"name",
"Balking");
2675 save_balking_strategy(sp, st->balking[r - 1].thresholds, st->nservers);
2678 sp.set_attr(
"classPath",
"jmt.engine.NetStrategies.ImpatienceStrategies.Reneging");
2679 sp.set_attr(
"name",
"Reneging");
2680 const bool renegs = st !=
nullptr && r <= st->impatience.size() &&
2682 r <= st->patience.size() && !st->patience[r - 1].disabled;
2684 sp.add_text_child(
"value",
"null");
2687 const JmtDistView<T> v =
jmt_dist_view(st->patience[r - 1]);
2693 xml::Element& dn = sp.add_child(
"subParameter");
2694 dn.set_attr(
"classPath",
"jmt.engine.random.Uniform");
2695 dn.set_attr(
"name",
"Uniform");
2696 xml::Element& par = sp.add_child(
"subParameter");
2697 par.set_attr(
"classPath",
"jmt.engine.random.UniformPar");
2698 par.set_attr(
"name",
"distrPar");
2699 jmt_scalar(par,
"java.lang.Double",
"min",
"0.0");
2700 jmt_double(par,
"max", v.rate > 0.0 ? 2.0 / v.rate : 0.0);
2719 void save_balking_strategy(xml::Element& balking,
2720 const std::vector<
typename qn::Station<T>::BalkingThreshold>& th,
2725 std::vector<typename qn::Station<T>::BalkingThreshold> sorted = th;
2726 std::sort(sorted.begin(), sorted.end(),
2727 [](
const typename qn::Station<T>::BalkingThreshold& a,
2728 const typename qn::Station<T>::BalkingThreshold& b) {
2729 return a.min_jobs < b.min_jobs;
2732 (!std::isfinite(nservers) || nservers < 1.0) ? 1.0 : nservers;
2733 std::vector<Bp> bps;
2734 for (std::size_t i = 0; i < sorted.size(); ++i) {
2735 const double lo = sorted[i].min_jobs;
2738 const double hi = sorted[i].max_jobs < 0.0
2739 ? std::numeric_limits<double>::infinity()
2740 : sorted[i].max_jobs;
2741 const double pr = d(sorted[i].probability);
2742 bps.push_back(Bp{std::max(0.0, lo - S), pr});
2743 if (!std::isfinite(hi))
continue;
2744 const double next_lo = i + 1 < sorted.size()
2745 ? sorted[i + 1].min_jobs
2746 : std::numeric_limits<double>::infinity();
2747 if (hi + 1.0 < next_lo) bps.push_back(Bp{std::max(0.0, hi + 1.0 - S), 0.0});
2749 xml::Element& ld = balking.add_child(
"subParameter");
2750 ld.set_attr(
"classPath",
"jmt.engine.NetStrategies.ServiceStrategies.LoadDependentStrategy");
2751 ld.set_attr(
"name",
"LoadDependentStrategy");
2752 xml::Element& arr = jmt_param_sub(
2753 ld,
"jmt.engine.NetStrategies.ServiceStrategies.LDParameter",
"LDParameter");
2754 for (
const Bp& b : bps) {
2755 xml::Element& rn = arr.add_child(
"subParameter");
2756 rn.set_attr(
"classPath",
"jmt.engine.NetStrategies.ServiceStrategies.LDParameter");
2757 rn.set_attr(
"name",
"LDParameter");
2761 xml::Element& dn = rn.add_child(
"subParameter");
2762 dn.set_attr(
"classPath",
"jmt.engine.random.Exponential");
2763 dn.set_attr(
"name",
"Exponential");
2764 xml::Element& par = rn.add_child(
"subParameter");
2765 par.set_attr(
"classPath",
"jmt.engine.random.ExponentialPar");
2766 par.set_attr(
"name",
"distrPar");
2767 jmt_scalar(par,
"java.lang.Double",
"lambda",
"1.0");
2770 jmt_scalar(balking,
"java.lang.Boolean",
"priorityActivated",
"false");
2780 void save_retrial_distributions(xml::Element& section, std::size_t ind) {
2781 const std::size_t ist = sn_.nodes[ind - 1].station;
2782 const auto it = ist == 0 ? sn_.retrialparam.end() : sn_.retrialparam.find(ist);
2783 xml::Element& p =
jmt_param(section,
"jmt.engine.NetStrategies.ServiceStrategy",
2784 "retrialDistributions",
true);
2785 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2786 if (!keep_[r - 1])
continue;
2788 xml::Element& sts = p.add_child(
"subParameter");
2789 sts.set_attr(
"classPath",
2790 "jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy");
2791 sts.set_attr(
"name",
"ServiceTimeStrategy");
2792 const bool has = it != sn_.retrialparam.end() &&
2793 r <= it->second.retrial_proc.size() &&
2794 !it->second.retrial_proc[r - 1].disabled;
2796 xml::Element& dn = sts.add_child(
"subParameter");
2797 dn.set_attr(
"classPath",
"jmt.engine.random.Exponential");
2798 dn.set_attr(
"name",
"Exponential");
2799 xml::Element& par = sts.add_child(
"subParameter");
2800 par.set_attr(
"classPath",
"jmt.engine.random.ExponentialPar");
2801 par.set_attr(
"name",
"distrPar");
2802 jmt_scalar(par,
"java.lang.Double",
"lambda",
"1.000000000000");
2806 "SolverJMT (retrial delay)");
2820 void save_switchover_strategy(xml::Element& section, std::size_t ind) {
2821 const std::size_t ist = sn_.nodes[ind - 1].station;
2822 const typename qn::NetworkStruct<T>::PollingParam pp = sn_.effective_polling(ist);
2823 xml::Element& p =
jmt_param(section,
"jmt.engine.NetStrategies.ServiceStrategy",
2824 "SwitchoverStrategy",
true);
2825 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2827 xml::Element& sts = p.add_child(
"subParameter");
2828 sts.set_attr(
"classPath",
2829 "jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy");
2830 sts.set_attr(
"name",
"ServiceTimeStrategy");
2831 const lang::Distrib<T>& so =
2832 r <= pp.switchover.size() ? pp.switchover[r - 1] : empty_dist_;
2841 sts.set_attr(
"classPath",
2842 "jmt.engine.NetStrategies.ServiceStrategies.ZeroServiceTimeStrategy");
2843 sts.set_attr(
"name",
"ZeroServiceTimeStrategy");
2850 std::map<std::size_t, double> empty_weights_;
2851 lang::Distrib<T> empty_dist_;