508 save_xml_header(*
sim);
510 for (std::size_t ind = 1; ind <= sn_.nodes.size(); ++ind) save_node(*
sim, ind);
544 for (std::size_t ist = 1; ist <= sn_.nstations; ++ist) {
550 const qn::NodeType ty = sn_.nodes[sn_.station_to_node[ist - 1] - 1].nodetype;
551 if (ty == qn::NodeType::Source || ty == qn::NodeType::Sink)
continue;
557 if (!std::isfinite(sn_.cap[ist - 1]))
continue;
558 if (sn_.cap[ist - 1] >= reachable_population(ist))
continue;
559 if (!std::isfinite(sn_.stations[ist - 1].nservers))
continue;
561 return "SolverJMT: station '" + nname(sn_.station_to_node[ist - 1]) +
562 "' carries a finite capacity " +
jmt_int(sn_.cap[ist - 1]) +
563 " that binds. The JMVA document has no capacity element at all, so the "
564 "analytical engine would solve the model as if the buffer were unbounded "
565 "and report that as the answer. Use the 'jsim' method, which exports the "
566 "buffer with its drop rule when JMT can express it, or SolverCTMC, "
567 "SolverSSA or SolverLDES";
569 assert_station_cap_exportable(ist);
571 return std::string(e.what());
574 return std::string();
580 std::vector<std::vector<bool>> conn_;
581 std::vector<bool> keep_, cacheclass_;
584 const std::string& cname(std::size_t r)
const {
return sn_.
classes[r - 1].name; }
585 const std::string& nname(std::size_t i)
const {
return sn_.
nodes[i - 1].name; }
588 std::vector<std::size_t> outputs_of(std::size_t ind)
const {
589 std::vector<std::size_t> v;
590 for (std::size_t j = 1; j <= sn_.nodes.size(); ++j)
591 if (conn_[ind - 1][j - 1]) v.push_back(j);
596 std::vector<std::size_t> inputs_of(std::size_t ind)
const {
597 std::vector<std::size_t> v;
598 for (std::size_t j = 1; j <= sn_.nodes.size(); ++j)
599 if (conn_[j - 1][ind - 1]) v.push_back(j);
608 std::vector<std::pair<std::size_t, std::size_t>> inhibitor_only_arcs()
const {
609 std::vector<std::pair<std::size_t, std::size_t>> arcs;
610 for (
const auto& kv : sn_.transparam) {
611 const std::size_t t = kv.first;
612 for (std::size_t p = 1; p <= sn_.nodes.size(); ++p) {
615 for (
const Matrix<T>& M : kv.second.inhibiting) {
616 if (p > M.rows())
continue;
617 for (std::size_t r = 0; r < M.cols() && !finite; ++r)
618 finite = std::isfinite(num_traits<T>::to_double(M(p - 1, r)));
621 if (finite) arcs.emplace_back(p, t);
630 void save_xml_header(xml::Element& sim) {
631 sim.set_attr(
"xmlns:xsi",
"http://www.w3.org/2001/XMLSchema-instance");
632 sim.set_attr(
"name", opt_.file_name +
".jsimg");
633 sim.set_attr(
"xsi:noNamespaceSchemaLocation",
"SIMmodeldefinition.xsd");
634 sim.set_attr(
"disableStatisticStop",
"true");
635 sim.set_attr(
"logDecimalSeparator",
".");
636 sim.set_attr(
"logDelimiter",
";");
637 sim.set_attr(
"logPath", opt_.log_path);
638 sim.set_attr(
"logReplaceMode",
"0");
639 sim.set_attr(
"maxSamples",
640 std::isnan(opt_.max_samples) ?
"10000" :
jmt_int(opt_.max_samples));
641 sim.set_attr(
"maxEvents",
jmt_int(opt_.max_events));
642 if (std::isfinite(opt_.max_simulated_time)) {
644 std::snprintf(buf,
sizeof(buf),
"%.3f", opt_.max_simulated_time);
645 sim.set_attr(
"maxSimulated", buf);
647 sim.set_attr(
"polling",
"1.0");
648 sim.set_attr(
"seed",
jmt_int(
static_cast<double>(opt_.seed)));
659 void save_classes(xml::Element& sim) {
661 for (std::size_t r = 0; r < sn_.nclasses; ++r)
662 maxprio = std::max(maxprio, sn_.classes[r].prio);
663 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
664 if (!keep_[r - 1])
continue;
665 const qn::JobClass& cl = sn_.classes[r - 1];
666 xml::Element& uc = sim.add_child(
"userClass");
667 uc.set_attr(
"name", cl.name);
668 const bool open = !std::isfinite(cl.population);
669 uc.set_attr(
"type", open ?
"open" :
"closed");
674 uc.set_attr(
"priority",
jmt_int(
static_cast<double>(maxprio - cl.prio)));
675 const std::size_t refst = cl.refstat;
676 const std::string refname = nname(sn_.station_to_node[refst - 1]);
678 uc.set_attr(
"customers",
jmt_int(cl.population));
679 uc.set_attr(
"referenceSource", refname);
680 }
else if (sn_.disabled[refst - 1][r - 1]) {
684 uc.set_attr(
"referenceSource",
"ClassSwitch");
686 uc.set_attr(
"referenceSource", refname);
694 void save_node(xml::Element& sim, std::size_t ind) {
696 xml::Element& node = sim.add_child(
"node");
697 node.set_attr(
"name", nname(ind));
699 save_class_soft_deadlines(node, ind);
700 const std::string parts[3] = {sec.input, sec.server, sec.output};
701 for (
int k = 0; k < 3; ++k) {
702 if (parts[k].empty())
continue;
703 std::string cls = parts[k];
708 xml::Element& xs = node.add_child(
"section");
709 xs.set_attr(
"className", cls);
710 if (cls ==
"Buffer") {
711 xs.set_attr(
"className",
"Queue");
712 save_buffer_capacity(xs, ind);
713 save_drop_strategy(xs, ind);
714 if (has_retrial(ind)) {
718 save_retrial_distributions(xs, ind);
719 save_get_strategy(xs, ind);
720 save_put_strategy(xs, ind);
722 save_get_strategy(xs, ind);
723 save_put_strategy(xs, ind);
724 save_impatience(xs, ind);
726 }
else if (cls ==
"Server") {
727 save_number_of_servers(xs, ind);
728 save_server_visits(xs);
729 save_service_strategy(xs, ind);
730 save_delay_off_strategy(xs, ind);
734 save_class_parallelism(xs, ind);
735 save_server_type_names(xs, ind);
736 save_servers_per_type(xs, ind);
737 save_server_compatibilities(xs, ind);
738 save_hetero_sched_policy(xs, ind);
739 warn_hetero_rates(ind);
740 warn_switchover_on_non_polling(ind);
741 }
else if (cls ==
"PreemptiveServer") {
742 save_number_of_servers(xs, ind);
743 save_server_visits(xs);
744 save_service_strategy(xs, ind);
745 save_delay_off_strategy(xs, ind);
746 }
else if (cls ==
"SharedServer") {
747 xs.set_attr(
"className",
"PSServer");
748 save_number_of_servers(xs, ind);
749 save_server_visits(xs);
750 save_service_strategy(xs, ind);
751 save_delay_off_strategy(xs, ind);
752 save_preemptive_strategy(xs, ind);
753 save_preemptive_weights(xs, ind);
754 }
else if (cls ==
"InfiniteServer") {
755 xs.set_attr(
"className",
"Delay");
756 save_service_strategy(xs, ind);
757 }
else if (cls ==
"PollingServer") {
758 xs.set_attr(
"className", polling_server_class(ind));
759 save_number_of_servers(xs, ind);
760 save_server_visits(xs);
761 save_service_strategy(xs, ind);
762 save_switchover_strategy(xs, ind);
763 }
else if (cls ==
"RandomSource") {
764 save_arrival_strategy(xs, ind);
765 }
else if (cls ==
"Dispatcher") {
766 xs.set_attr(
"className",
"Router");
767 save_routing_strategy(xs, ind);
768 }
else if (cls ==
"StatelessClassSwitcher") {
769 xs.set_attr(
"className",
"ClassSwitch");
770 save_class_switch_strategy(xs, ind);
771 }
else if (cls ==
"Cache") {
772 save_cache_strategy(xs, ind);
773 }
else if (cls ==
"LogTunnel") {
774 save_log_tunnel(xs, ind);
775 }
else if (cls ==
"Joiner") {
776 xs.set_attr(
"className",
"Join");
777 save_join_strategy(xs, ind);
778 }
else if (cls ==
"Forker") {
779 xs.set_attr(
"className",
"Fork");
780 save_fork_strategy(xs, ind);
781 }
else if (cls ==
"Storage") {
782 save_total_capacity(xs, ind);
783 save_place_capacities(xs, ind);
784 save_drop_rule(xs, ind);
785 save_get_strategy(xs, ind);
786 save_put_strategies(xs, ind);
787 }
else if (cls ==
"Enabling") {
788 save_enabling_conditions(xs, ind);
789 save_inhibiting_conditions(xs, ind);
790 }
else if (cls ==
"Firing") {
791 save_firing_outcomes(xs, ind);
792 }
else if (cls ==
"Timing") {
793 save_mode_names(xs, ind);
794 save_numbers_of_servers(xs, ind);
795 save_timing_strategies(xs, ind);
796 save_firing_priorities(xs, ind);
797 save_firing_weights(xs, ind);
804 const char* polling_server_class(std::size_t ind)
const {
805 const std::size_t ist = sn_.nodes[ind - 1].station;
806 const typename qn::NetworkStruct<T>::PollingParam pp = sn_.effective_polling(ist);
808 case PollingType::GATED:
return "GatedPollingServer";
809 case PollingType::EXHAUSTIVE:
return "ExhaustivePollingServer";
810 case PollingType::KLIMITED:
return "LimitedPollingServer";
811 case PollingType::DECREMENTING:
813 "SolverJMT: JMT does not support the decrementing (semiexhaustive) polling "
814 "discipline; use the LDES solver");
816 throw UnsupportedError(ErrorKind{
"SchedStrategy_POLLING"},
"SolverJMT: unknown polling discipline");
820 bool has_retrial(std::size_t ind)
const {
821 const std::size_t ist = sn_.nodes[ind - 1].station;
822 if (ist == 0)
return false;
823 const auto it = sn_.retrialparam.find(ist);
824 if (it == sn_.retrialparam.end())
return false;
825 for (
const lang::Distrib<T>& p : it->second.retrial_proc)
826 if (!p.disabled)
return true;
839 void warn_switchover_on_non_polling(std::size_t ind)
const {
840 const std::size_t ist = sn_.nodes[ind - 1].station;
841 if (ist == 0)
return;
842 for (
const lang::Distrib<T>& s : sn_.stations[ist - 1].switchover)
844 std::cerr <<
"[LINE] Warning: JMT does not support switchover times for "
845 <<
"non-polling queues. Switchover times will be ignored for node '"
846 << nname(ind) <<
"'." << std::endl;
853 if (!sn_.stations[ist - 1].switchover_pair.empty())
854 std::cerr <<
"[LINE] Warning: JMT does not support switchover times for "
855 <<
"non-polling queues. Switchover times will be ignored for node '"
856 << nname(ind) <<
"'." << std::endl;
880 void save_buffer_capacity(xml::Element& section, std::size_t ind) {
881 const std::size_t ist = sn_.nodes[ind - 1].station;
882 std::string v =
"-1";
883 if (ist != 0 && std::isfinite(sn_.cap[ist - 1])) {
884 const double total = reachable_population(ist);
885 const double ns = sn_.stations[ist - 1].nservers;
886 if (sn_.cap[ist - 1] < total && std::isfinite(ns)) {
887 assert_station_cap_exportable(ist);
914 double reachable_population(std::size_t ist)
const {
916 for (std::size_t c = 0; c < sn_.nchains; ++c) {
918 double chain_jobs = 0.0;
919 for (std::size_t r : sn_.inchain[c]) {
920 if (!sn_.disabled[ist - 1][r - 1]) served =
true;
921 chain_jobs += sn_.classes[r - 1].population;
923 if (served) n += chain_jobs;
941 bool is_bas_destination(std::size_t ist, std::size_t r)
const {
942 if (ist == 0 || sn_.isbasdestination.size() < ist)
return false;
943 if (sn_.isbasdestination[ist - 1].size() < r)
return false;
944 return sn_.isbasdestination[ist - 1][r - 1];
964 const char* drop_strategy_text(std::size_t ist, std::size_t r)
const {
965 if (ist == 0)
return "drop";
967 if (d == DropStrategy::WAITQ && is_bas_destination(ist, r))
1014 void assert_station_cap_exportable(std::size_t ist)
const {
1015 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1016 if (sn_.disabled[ist - 1][r - 1])
continue;
1021 "SolverJMT: station '" + nname(sn_.station_to_node[ist - 1]) +
1022 "' applies drop strategy '" +
jmt_drop_text(dr) +
"' to class '" + cname(r) +
1023 "' and carries a finite capacity " +
jmt_int(sn_.cap[ist - 1]) +
1024 " it can reach. JMT's queue section reads only 'drop', 'waiting queue', "
1025 "'BAS blocking' and 'retrial'; it does not approximate anything else, it "
1026 "ignores it, so the capacity would stop being enforced and the run would "
1027 "return the unconstrained answer. Use SolverCTMC, SolverSSA or SolverLDES");
1028 if (!std::isfinite(sn_.classes[r - 1].population))
continue;
1029 if (dr != DropStrategy::WAITQ)
1031 if (is_bas_destination(ist, r))
1035 "SolverJMT: station '" + nname(sn_.station_to_node[ist - 1]) +
1036 "' carries a finite capacity " +
jmt_int(sn_.cap[ist - 1]) +
1037 " that binds for the closed class '" + cname(r) +
1038 "'. LINE blocks a closed job that finds no room -- the upstream departure is "
1039 "disabled and the job stays where it is -- and no JMT drop strategy reproduces "
1040 "that: 'waiting queue' does not enforce the size at all, and 'BAS blocking' "
1041 "completes the service before blocking, which is a different queueing model. "
1042 "Use SolverCTMC, SolverSSA or SolverLDES, or declare DropStrategy.BAS if "
1043 "blocking after service is the model you want, which SolverJMT does export");
1048 void save_drop_strategy(xml::Element& section, std::size_t ind) {
1049 const std::size_t ist = sn_.nodes[ind - 1].station;
1050 xml::Element& p =
jmt_param(section,
"java.lang.String",
"dropStrategies",
true);
1051 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1052 if (!keep_[r - 1])
continue;
1054 const char* txt = drop_strategy_text(ist, r);
1055 xml::Element& sp = p.add_child(
"subParameter");
1056 sp.set_attr(
"classPath",
"java.lang.String");
1057 sp.set_attr(
"name",
"dropStrategy");
1058 sp.add_text_child(
"value", txt);
1066 void save_get_strategy(xml::Element& section, std::size_t ind) {
1067 const std::size_t ist = sn_.nodes[ind - 1].station;
1068 const bool polling = sn_.nodes[ind - 1].nodetype == NodeType::Queue && ist != 0 &&
1069 sn_.stations[ist - 1].sched == SchedStrategy::POLLING;
1071 xml::Element& p = section.add_child(
"parameter");
1072 p.set_attr(
"classPath",
"jmt.engine.NetStrategies.QueueGetStrategies.FCFSstrategy");
1073 p.set_attr(
"name",
"FCFSstrategy");
1076 const typename qn::NetworkStruct<T>::PollingParam pp = sn_.effective_polling(ist);
1077 xml::Element& p = section.add_child(
"parameter");
1079 case PollingType::GATED:
1080 p.set_attr(
"classPath",
1081 "jmt.engine.NetStrategies.QueueGetStrategies.GatedPollingGetStrategy");
1083 case PollingType::EXHAUSTIVE:
1086 "jmt.engine.NetStrategies.QueueGetStrategies.ExhaustivePollingGetStrategy");
1088 case PollingType::KLIMITED: {
1089 p.set_attr(
"classPath",
1090 "jmt.engine.NetStrategies.QueueGetStrategies.LimitedPollingGetStrategy");
1091 xml::Element& k = p.add_child(
"subParameter");
1092 k.set_attr(
"classPath",
"java.lang.Integer");
1093 k.set_attr(
"name",
"pollingKValue");
1094 k.add_text_child(
"value",
jmt_int(
static_cast<double>(pp.pk)));
1097 case PollingType::DECREMENTING:
1099 "SolverJMT: JMT does not support the decrementing (semiexhaustive) polling "
1100 "discipline; use the LDES solver");
1102 p.set_attr(
"name",
"FCFSstrategy");
1114 void save_put_strategy(xml::Element& section, std::size_t ind) {
1115 const std::size_t ist = sn_.nodes[ind - 1].station;
1117 jmt_param(section,
"jmt.engine.NetStrategies.QueuePutStrategy",
"QueuePutStrategy",
true);
1118 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1119 if (!keep_[r - 1])
continue;
1121 const char* nm =
"TailStrategy";
1123 switch (sn_.stations[ist - 1].sched) {
1124 case SchedStrategy::SIRO: nm =
"RandStrategy";
break;
1125 case SchedStrategy::LJF: nm =
"LJFStrategy";
break;
1126 case SchedStrategy::SJF: nm =
"SJFStrategy";
break;
1127 case SchedStrategy::LEPT: nm =
"LEPTStrategy";
break;
1128 case SchedStrategy::SEPT: nm =
"SEPTStrategy";
break;
1129 case SchedStrategy::LCFS: nm =
"HeadStrategy";
break;
1130 case SchedStrategy::LCFSPRIO: nm =
"HeadStrategyPriority";
break;
1131 case SchedStrategy::LCFSPR: nm =
"LCFSPRStrategy";
break;
1132 case SchedStrategy::LCFSPI: nm =
"LCFSPIStrategy";
break;
1133 case SchedStrategy::LCFSPRPRIO: nm =
"LCFSPRStrategyPriority";
break;
1134 case SchedStrategy::LCFSPIPRIO: nm =
"LCFSPIStrategyPriority";
break;
1135 case SchedStrategy::FCFSPR: nm =
"FCFSPRStrategy";
break;
1136 case SchedStrategy::FCFSPI: nm =
"FCFSPIStrategy";
break;
1137 case SchedStrategy::FCFSPRPRIO: nm =
"FCFSPRStrategyPriority";
break;
1138 case SchedStrategy::FCFSPIPRIO: nm =
"FCFSPIStrategyPriority";
break;
1139 case SchedStrategy::HOL: nm =
"TailStrategyPriority";
break;
1140 case SchedStrategy::EDD: nm =
"EDDStrategy";
break;
1141 case SchedStrategy::EDF: nm =
"EDFStrategy";
break;
1142 case SchedStrategy::SRPT: nm =
"SRPTStrategy";
break;
1143 case SchedStrategy::SRPTPRIO: nm =
"SRPTStrategyPriority";
break;
1144 default: nm =
"TailStrategy";
break;
1147 xml::Element& sp = p.add_child(
"subParameter");
1148 sp.set_attr(
"classPath",
1149 std::string(
"jmt.engine.NetStrategies.QueuePutStrategies.") + nm);
1150 sp.set_attr(
"name", nm);
1166 void save_number_of_servers(xml::Element& section, std::size_t ind) {
1167 const std::size_t ist = sn_.nodes[ind - 1].station;
1168 double maxjobs = 1.0;
1170 if (sn_.stations[ist - 1].sched == SchedStrategy::LPS) {
1173 maxjobs = sn_.stations[ist - 1].nservers;
1174 for (
const T& s : sn_.stations[ist - 1].lldscaling)
1175 maxjobs = std::max(maxjobs, d(s));
1182 void save_server_visits(xml::Element& section) {
1183 xml::Element& p =
jmt_param(section,
"java.lang.Integer",
"numberOfVisits",
true);
1184 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1185 if (!keep_[r - 1])
continue;
1187 xml::Element& sp = p.add_child(
"subParameter");
1188 sp.set_attr(
"classPath",
"java.lang.Integer");
1189 sp.set_attr(
"name",
"numberOfVisits");
1190 sp.add_text_child(
"value",
"1");
1205 struct ServerPools {
1206 bool present =
false;
1207 std::vector<std::string> names;
1208 std::vector<double> counts;
1209 std::vector<std::vector<bool>> compat;
1211 std::vector<std::size_t> parallelism;
1214 ServerPools server_pools(std::size_t ind)
const {
1216 const std::size_t ist = sn_.nodes[ind - 1].station;
1217 if (ist == 0)
return pools;
1218 const auto& st = sn_.stations[ist - 1];
1219 const bool has_types = !st.server_types.empty();
1220 bool has_parallelism =
false;
1221 for (std::size_t n : st.server_parallelism) {
1222 if (n > 1) { has_parallelism =
true;
break; }
1224 if (!has_types && !has_parallelism)
return pools;
1226 pools.present =
true;
1227 pools.parallelism.assign(sn_.nclasses, 1);
1228 for (std::size_t r = 0; r < sn_.nclasses && r < st.server_parallelism.size(); ++r) {
1229 pools.parallelism[r] = st.server_parallelism[r] < 1 ? 1 : st.server_parallelism[r];
1232 pools.policy = st.hetero_policy;
1233 for (
const auto& pool : st.server_types) {
1234 pools.names.push_back(pool.name);
1235 pools.counts.push_back(pool.count);
1236 std::vector<bool> row(sn_.nclasses,
true);
1237 for (std::size_t r = 0; r < sn_.nclasses; ++r) {
1240 row[r] = pool.compatible.empty()
1242 : (r < pool.compatible.size() ? pool.compatible[r] :
false);
1244 pools.compat.push_back(row);
1247 pools.names.push_back(sn_.nodes[ind - 1].name +
" - Server Type 1");
1248 pools.counts.push_back(st.nservers);
1249 pools.compat.push_back(std::vector<bool>(sn_.nclasses,
true));
1255 void save_class_parallelism(xml::Element& section, std::size_t ind) {
1256 const ServerPools pools = server_pools(ind);
1257 if (!pools.present)
return;
1258 xml::Element& p =
jmt_param(section,
"java.lang.Integer",
"classParallelism",
true);
1259 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1260 if (!keep_[r - 1])
continue;
1262 xml::Element& sp = p.add_child(
"subParameter");
1263 sp.set_attr(
"classPath",
"java.lang.Integer");
1264 sp.set_attr(
"name",
"serverParallelism");
1265 sp.add_text_child(
"value",
jmt_int(
static_cast<double>(pools.parallelism[r - 1])));
1270 void save_server_type_names(xml::Element& section, std::size_t ind) {
1271 const ServerPools pools = server_pools(ind);
1272 if (!pools.present)
return;
1273 xml::Element& p =
jmt_param(section,
"java.lang.String",
"serverNames",
true);
1274 for (
const std::string& name : pools.names) {
1275 xml::Element& sp = p.add_child(
"subParameter");
1276 sp.set_attr(
"classPath",
"java.lang.String");
1277 sp.set_attr(
"name",
"serverTypesNames");
1278 sp.add_text_child(
"value", name);
1283 void save_servers_per_type(xml::Element& section, std::size_t ind) {
1284 const ServerPools pools = server_pools(ind);
1285 if (!pools.present)
return;
1286 xml::Element& p =
jmt_param(section,
"java.lang.Integer",
"serversPerServerType",
true);
1287 for (
double count : pools.counts) {
1288 xml::Element& sp = p.add_child(
"subParameter");
1289 sp.set_attr(
"classPath",
"java.lang.Integer");
1290 sp.set_attr(
"name",
"serverTypesNumOfServers");
1291 sp.add_text_child(
"value",
jmt_int(count));
1296 void save_server_compatibilities(xml::Element& section, std::size_t ind) {
1297 const ServerPools pools = server_pools(ind);
1298 if (!pools.present)
return;
1299 xml::Element& p =
jmt_param(section,
"java.lang.Object",
"serverCompatibilities",
true);
1300 for (
const std::vector<bool>& row : pools.compat) {
1301 xml::Element& tn = p.add_child(
"subParameter");
1302 tn.set_attr(
"array",
"true");
1303 tn.set_attr(
"classPath",
"java.lang.Boolean");
1304 tn.set_attr(
"name",
"serverTypesCompatibilities");
1305 for (std::size_t r = 0; r < sn_.nclasses; ++r) {
1306 xml::Element& cn = tn.add_child(
"subParameter");
1307 cn.set_attr(
"classPath",
"java.lang.Boolean");
1308 cn.set_attr(
"name",
"compatibilities");
1309 cn.add_text_child(
"value", row[r] ?
"true" :
"false");
1315 void save_hetero_sched_policy(xml::Element& section, std::size_t ind) {
1316 const ServerPools pools = server_pools(ind);
1317 if (!pools.present)
return;
1331 void warn_hetero_rates(std::size_t ind)
const {
1332 const std::size_t ist = sn_.nodes[ind - 1].station;
1333 if (ist == 0)
return;
1334 const auto& pools = sn_.stations[ist - 1].server_types;
1335 if (pools.size() < 2)
return;
1336 bool first_set =
false, distinct =
false;
1338 for (
const auto& pool : pools) {
1339 for (
const lang::Distrib<T>& sd : pool.service) {
1340 if (sd.disabled)
continue;
1341 const double mean = num_traits<T>::to_double(sd.mean);
1342 if (!(mean > 0))
continue;
1343 if (!first_set) { first = mean; first_set =
true; }
1344 else if (std::abs(mean - first) > 1e-12) { distinct =
true; }
1347 if (!distinct)
return;
1348 std::cerr <<
"[LINE] Warning: JMT keys service strategies by job class, so the "
1349 <<
"per-server-type service rates of station '" << sn_.nodes[ind - 1].name
1350 <<
"' cannot be exported; every pool will serve at the class service rate. "
1351 <<
"Use the LDES or CTMC solver for per-type rates." << std::endl;
1361 void save_service_strategy(xml::Element& section, std::size_t ind) {
1362 const std::size_t ist = sn_.nodes[ind - 1].station;
1363 xml::Element& p =
jmt_param(section,
"jmt.engine.NetStrategies.ServiceStrategy",
1364 "ServiceStrategy",
true);
1365 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1366 if (!keep_[r - 1])
continue;
1368 xml::Element& sts = p.add_child(
"subParameter");
1369 const JmtDistView<T> v =
1370 ist == 0 ? JmtDistView<T>() :
jmt_dist_view(sn_.service[ist - 1][r - 1]);
1372 sts.set_attr(
"classPath",
1373 "jmt.engine.NetStrategies.ServiceStrategies."
1374 "DisabledServiceTimeStrategy");
1375 sts.set_attr(
"name",
"DisabledServiceTimeStrategy");
1379 sts.set_attr(
"classPath",
1380 "jmt.engine.NetStrategies.ServiceStrategies.ZeroServiceTimeStrategy");
1381 sts.set_attr(
"name",
"ZeroServiceTimeStrategy");
1384 sts.set_attr(
"classPath",
1385 "jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy");
1386 sts.set_attr(
"name",
"ServiceTimeStrategy");
1399 void save_arrival_strategy(xml::Element& section, std::size_t ind) {
1400 const std::size_t ist = sn_.nodes[ind - 1].station;
1401 xml::Element& p =
jmt_param(section,
"jmt.engine.NetStrategies.ServiceStrategy",
1402 "ServiceStrategy",
true);
1403 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1404 if (!keep_[r - 1])
continue;
1406 xml::Element& sts = p.add_child(
"subParameter");
1407 sts.set_attr(
"classPath",
1408 "jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy");
1409 sts.set_attr(
"name",
"ServiceTimeStrategy");
1410 const bool closed = std::isfinite(sn_.classes[r - 1].population);
1411 const JmtDistView<T> v =
1412 ist == 0 ? JmtDistView<T>() :
jmt_dist_view(sn_.service[ist - 1][r - 1]);
1414 sts.add_text_child(
"value",
"null");
1418 sts.set_attr(
"classPath",
1419 "jmt.engine.NetStrategies.ServiceStrategies.ZeroServiceTimeStrategy");
1420 sts.set_attr(
"name",
"ZeroServiceTimeStrategy");
1428 void save_preemptive_strategy(xml::Element& section, std::size_t ind) {
1429 const std::size_t ist = sn_.nodes[ind - 1].station;
1430 xml::Element& p =
jmt_param(section,
"jmt.engine.NetStrategies.PSStrategy",
"PSStrategy",
1432 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1433 if (!keep_[r - 1])
continue;
1435 const char* nm =
nullptr;
1436 if (ist != 0)
switch (sn_.stations[ist - 1].sched) {
1439 case SchedStrategy::PS:
1440 case SchedStrategy::LPS: nm =
"EPSStrategy";
break;
1441 case SchedStrategy::DPS: nm =
"DPSStrategy";
break;
1442 case SchedStrategy::GPS: nm =
"GPSStrategy";
break;
1443 case SchedStrategy::PSPRIO: nm =
"EPSStrategyPriority";
break;
1444 case SchedStrategy::DPSPRIO: nm =
"DPSStrategyPriority";
break;
1445 case SchedStrategy::GPSPRIO: nm =
"GPSStrategyPriority";
break;
1446 default: nm =
nullptr;
break;
1448 xml::Element& sp = p.add_child(
"subParameter");
1449 if (nm !=
nullptr) {
1450 sp.set_attr(
"classPath", std::string(
"jmt.engine.NetStrategies.PSStrategies.") + nm);
1451 sp.set_attr(
"name", nm);
1457 void save_preemptive_weights(xml::Element& section, std::size_t ind) {
1458 const std::size_t ist = sn_.nodes[ind - 1].station;
1459 xml::Element& p =
jmt_param(section,
"java.lang.Double",
"serviceWeights",
true);
1460 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1461 if (!keep_[r - 1])
continue;
1464 if (ist != 0 && sn_.stations[ist - 1].schedparam.size() >= r)
1465 w = d(sn_.stations[ist - 1].schedparam[r - 1]);
1466 xml::Element& sp = p.add_child(
"subParameter");
1467 sp.set_attr(
"classPath",
"java.lang.Double");
1468 sp.set_attr(
"name",
"serviceWeight");
1469 sp.add_text_child(
"value",
jmt_num(w));
1483 void save_delay_off_strategy(xml::Element& section, std::size_t ind) {
1484 const std::size_t ist = sn_.nodes[ind - 1].station;
1485 if (ist == 0)
return;
1486 const auto it = sn_.setupparam.find(ist);
1487 if (it == sn_.setupparam.end())
return;
1488 const qn::SetupDelayOffParam<T>& sp = it->second;
1490 for (
const lang::Distrib<T>& s : sp.setup)
1491 if (!s.disabled) any =
true;
1494 const char* names[2] = {
"delayOffTime",
"setUpTime"};
1495 for (
int which = 0; which < 2; ++which) {
1496 const std::vector<lang::Distrib<T>>& tab = which == 0 ? sp.delayoff : sp.setup;
1497 xml::Element& p =
jmt_param(section,
"java.lang.Object", names[which],
true);
1498 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1500 xml::Element& row = p.add_child(
"subParameter");
1501 row.set_attr(
"array",
"true");
1502 row.set_attr(
"classPath",
"jmt.engine.NetStrategies.ServiceStrategy");
1503 row.set_attr(
"name", names[which]);
1504 xml::Element& sts = row.add_child(
"subParameter");
1505 sts.set_attr(
"classPath",
1506 "jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy");
1507 sts.set_attr(
"name",
"ServiceTimeStrategy");
1508 if (r <= tab.size() && !tab[r - 1].disabled)
1509 append_simple_distribution(sts, tab[r - 1]);
1511 append_zero_time(sts);
1517 void append_zero_time(xml::Element& parent) {
1518 xml::Element& dn = parent.add_child(
"subParameter");
1519 dn.set_attr(
"classPath",
"jmt.engine.random.DeterministicDistr");
1520 dn.set_attr(
"name",
"Deterministic");
1521 xml::Element& par = parent.add_child(
"subParameter");
1522 par.set_attr(
"classPath",
"jmt.engine.random.DeterministicDistrPar");
1523 par.set_attr(
"name",
"distrPar");
1524 jmt_scalar(par,
"java.lang.Double",
"t",
"0.0");
1537 void append_simple_distribution(xml::Element& parent,
const lang::Distrib<T>& dist) {
1539 append_zero_time(parent);
1542 const double mean = d(dist.mean);
1544 xml::Element& dn = parent.add_child(
"subParameter");
1545 dn.set_attr(
"classPath",
"jmt.engine.random.Exponential");
1546 dn.set_attr(
"name",
"Exponential");
1547 xml::Element& par = parent.add_child(
"subParameter");
1548 par.set_attr(
"classPath",
"jmt.engine.random.ExponentialPar");
1549 par.set_attr(
"name",
"distrPar");
1550 jmt_double(par,
"lambda", mean > 0.0 ? 1.0 / mean : 0.0);
1554 const std::size_t ph = dist.phases();
1555 xml::Element& dn = parent.add_child(
"subParameter");
1556 dn.set_attr(
"classPath",
"jmt.engine.random.Erlang");
1557 dn.set_attr(
"name",
"Erlang");
1558 xml::Element& par = parent.add_child(
"subParameter");
1559 par.set_attr(
"classPath",
"jmt.engine.random.ErlangPar");
1560 par.set_attr(
"name",
"distrPar");
1561 jmt_double(par,
"alpha", mean > 0.0 ?
static_cast<double>(ph) / mean : 0.0);
1565 xml::Element& dn = parent.add_child(
"subParameter");
1566 dn.set_attr(
"classPath",
"jmt.engine.random.DeterministicDistr");
1567 dn.set_attr(
"name",
"Deterministic");
1568 xml::Element& par = parent.add_child(
"subParameter");
1569 par.set_attr(
"classPath",
"jmt.engine.random.DeterministicDistrPar");
1570 par.set_attr(
"name",
"distrPar");
1584 void save_routing_strategy(xml::Element& section, std::size_t ind) {
1585 const std::size_t K = sn_.nclasses;
1586 const bool is_cs = sn_.nodes[ind - 1].nodetype == NodeType::ClassSwitch;
1588 jmt_param(section,
"jmt.engine.NetStrategies.RoutingStrategy",
"RoutingStrategy",
true);
1589 for (std::size_t r = 1; r <= K; ++r) {
1590 if (!keep_[r - 1])
continue;
1593 is_cs ? RoutingStrategy::RAND
1594 : (sn_.nodes[ind - 1].routing.size() >= r ? sn_.nodes[ind - 1].routing[r - 1]
1595 : RoutingStrategy::PROB);
1596 xml::Element& sp = p.add_child(
"subParameter");
1598 case RoutingStrategy::RAND:
1599 sp.set_attr(
"classPath",
1600 "jmt.engine.NetStrategies.RoutingStrategies.RandomStrategy");
1601 sp.set_attr(
"name",
"Random");
1603 case RoutingStrategy::RROBIN:
1604 sp.set_attr(
"classPath",
1605 "jmt.engine.NetStrategies.RoutingStrategies.RoundRobinStrategy");
1606 sp.set_attr(
"name",
"Round Robin");
1608 case RoutingStrategy::JSQ:
1609 sp.set_attr(
"classPath",
1610 "jmt.engine.NetStrategies.RoutingStrategies."
1611 "ShortestQueueLengthRoutingStrategy");
1612 sp.set_attr(
"name",
"Join the Shortest Queue (JSQ)");
1614 case RoutingStrategy::SQ: {
1615 sp.set_attr(
"classPath",
1616 "jmt.engine.NetStrategies.RoutingStrategies.PowerOfKRoutingStrategy");
1617 sp.set_attr(
"name",
"Power of k");
1618 const int dpar = sn_.nodes[ind - 1].routing_param.size() >= r
1619 ? sn_.nodes[ind - 1].routing_param[r - 1]
1625 jmt_scalar(sp,
"java.lang.Boolean",
"withMemory",
"false");
1628 case RoutingStrategy::WRROBIN: {
1629 sp.set_attr(
"classPath",
1630 "jmt.engine.NetStrategies.RoutingStrategies."
1631 "WeightedRoundRobinStrategy");
1632 sp.set_attr(
"name",
"Weighted Round Robin");
1634 jmt_param_sub(sp,
"jmt.engine.NetStrategies.RoutingStrategies.WeightEntry",
1635 "WeightEntryArray");
1636 const std::map<std::size_t, double>& w =
1637 sn_.nodes[ind - 1].routing_weights.size() >= r
1638 ? sn_.nodes[ind - 1].routing_weights[r - 1]
1640 for (std::size_t j : outputs_of(ind)) {
1641 const auto wi = w.find(j);
1642 xml::Element& e = arr.add_child(
"subParameter");
1643 e.set_attr(
"classPath",
1644 "jmt.engine.NetStrategies.RoutingStrategies.WeightEntry");
1645 e.set_attr(
"name",
"WeightEntry");
1646 jmt_scalar(e,
"java.lang.String",
"stationName", nname(j));
1648 jmt_int(wi == w.end() ? 0.0 : wi->second));
1652 case RoutingStrategy::PROB: {
1653 sp.set_attr(
"classPath",
1654 "jmt.engine.NetStrategies.RoutingStrategies.EmpiricalStrategy");
1655 sp.set_attr(
"name",
"Probabilities");
1656 xml::Element& arr = jmt_param_sub(sp,
"jmt.engine.random.EmpiricalEntry",
1657 "EmpiricalEntryArray");
1658 for (std::size_t j : outputs_of(ind)) {
1659 const double pr = route_node(ind, r, j, r);
1660 if (!(pr > 0.0))
continue;
1661 xml::Element& e = arr.add_child(
"subParameter");
1662 e.set_attr(
"classPath",
"jmt.engine.random.EmpiricalEntry");
1663 e.set_attr(
"name",
"EmpiricalEntry");
1664 jmt_scalar(e,
"java.lang.String",
"stationName", nname(j));
1672 "jmt.engine.NetStrategies.RoutingStrategies.DisabledRoutingStrategy");
1673 sp.set_attr(
"name",
"Random");
1680 static xml::Element& jmt_param_sub(xml::Element& parent,
const char* cp,
const char* name) {
1681 xml::Element& e = parent.add_child(
"subParameter");
1682 e.set_attr(
"array",
"true");
1683 e.set_attr(
"classPath", cp);
1684 e.set_attr(
"name", name);
1689 double route_node(std::size_t i, std::size_t r, std::size_t j, std::size_t s)
const {
1690 const std::size_t K = sn_.nclasses;
1691 const std::size_t a = (i - 1) * K + (r - 1), b = (j - 1) * K + (s - 1);
1692 if (sn_.rtnodes.rows() <= a || sn_.rtnodes.cols() <= b)
return 0.0;
1693 return d(sn_.rtnodes(a, b));
1705 void save_class_switch_strategy(xml::Element& section, std::size_t ind) {
1706 const std::size_t K = sn_.nclasses;
1707 const std::vector<std::size_t> jset = outputs_of(ind);
1708 xml::Element& p =
jmt_param(section,
"java.lang.Object",
"matrix",
true);
1709 for (std::size_t r = 1; r <= K; ++r) {
1710 if (!keep_[r - 1])
continue;
1712 xml::Element& row = p.add_child(
"subParameter");
1713 row.set_attr(
"array",
"true");
1714 row.set_attr(
"classPath",
"java.lang.Float");
1715 row.set_attr(
"name",
"row");
1716 for (std::size_t s = 1; s <= K; ++s) {
1717 if (!keep_[s - 1])
continue;
1720 for (std::size_t j : jset) acc += route_node(ind, r, j, s);
1721 xml::Element& cell = row.add_child(
"subParameter");
1722 cell.set_attr(
"classPath",
"java.lang.Float");
1723 cell.set_attr(
"name",
"cell");
1724 cell.add_text_child(
"value",
jmt_fmt(acc));
1740 void save_fork_strategy(xml::Element& section, std::size_t ind) {
1741 const qn::ForkParam<T>* fk = sn_.fork_param_of(ind);
1742 const double fan_out = sn_.nodes[ind - 1].tasks_per_link;
1750 bool simplified =
true;
1752 for (std::size_t k = 0; k < fk->fan_out_link.rows() && simplified; ++k)
1753 for (std::size_t r = 0; r < fk->fan_out_link.cols() && simplified; ++r) {
1754 const double p = num_traits<T>::to_double(fk->fan_out_prob(k, r));
1755 if (p == 0.0)
continue;
1756 if (p != 1.0) simplified =
false;
1757 if (num_traits<T>::to_double(fk->fan_out_link(k, r)) != fan_out)
1759 if (!fk->fan_out_dist[k][r].disabled) simplified =
false;
1763 simplified ?
"true" :
"false");
1766 jmt_param(section,
"jmt.engine.NetStrategies.ForkStrategy",
"ForkStrategy",
true);
1767 const std::vector<std::size_t> outs = outputs_of(ind);
1768 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1769 if (!keep_[r - 1])
continue;
1771 xml::Element& cs = p.add_child(
"subParameter");
1772 cs.set_attr(
"classPath",
"jmt.engine.NetStrategies.ForkStrategies.ProbabilitiesFork");
1773 cs.set_attr(
"name",
"Branch Probabilities");
1774 xml::Element& arr = jmt_param_sub(
1775 cs,
"jmt.engine.NetStrategies.ForkStrategies.OutPath",
"EmpiricalEntryArray");
1777 ? sn_.nodes[ind - 1].routing[r - 1]
1778 : RoutingStrategy::PROB;
1779 if (rs != RoutingStrategy::PROB || outs.empty())
continue;
1780 for (std::size_t oi = 0; oi < outs.size(); ++oi) {
1781 xml::Element& entry = arr.add_child(
"subParameter");
1782 entry.set_attr(
"classPath",
"jmt.engine.NetStrategies.ForkStrategies.OutPath");
1783 entry.set_attr(
"name",
"OutPathEntry");
1784 xml::Element& unit = entry.add_child(
"subParameter");
1785 unit.set_attr(
"classPath",
"jmt.engine.random.EmpiricalEntry");
1786 unit.set_attr(
"name",
"outUnitProbability");
1787 jmt_scalar(unit,
"java.lang.String",
"stationName", nname(outs[oi]));
1790 const std::size_t k0 = outs[oi] - 1, r0 = r - 1;
1791 const bool has_fan = fk != 0;
1792 const double branch_p =
1793 has_fan ? num_traits<T>::to_double(fk->fan_out_prob(k0, r0)) : 1.0;
1799 std::vector<double> pts, prs;
1800 if (has_fan && !fk->fan_out_dist[k0][r0].disabled) {
1801 const lang::Distrib<T>& d = fk->fan_out_dist[k0][r0];
1803 for (std::size_t e = 0; e < d.params.size(); ++e)
1804 tot += num_traits<T>::to_double(d.params[e]);
1805 for (std::size_t e = 0; e < d.params.size(); ++e) {
1806 pts.push_back(d.trace.empty()
1807 ?
static_cast<double>(e + 1)
1808 : num_traits<T>::to_double(d.trace[e]));
1809 prs.push_back(num_traits<T>::to_double(d.params[e]) / tot);
1811 }
else if (has_fan) {
1812 pts.push_back(num_traits<T>::to_double(fk->fan_out_link(k0, r0)));
1815 pts.push_back(fan_out);
1820 jmt_param_sub(entry,
"jmt.engine.random.EmpiricalEntry",
"JobsPerLinkDis");
1821 for (std::size_t e = 0; e < pts.size(); ++e) {
1822 xml::Element& jple = jpl.add_child(
"subParameter");
1823 jple.set_attr(
"classPath",
"jmt.engine.random.EmpiricalEntry");
1824 jple.set_attr(
"name",
"EmpiricalEntry");
1840 void save_join_strategy(xml::Element& section, std::size_t ind) {
1841 const auto it = sn_.joindecl.find(ind);
1842 const bool partial =
1843 it != sn_.joindecl.end() && it->second.strategy == JoinStrategy::PARTIAL;
1844 const double quorum = it != sn_.joindecl.end() ? it->second.quorum : 0.0;
1846 jmt_param(section,
"jmt.engine.NetStrategies.JoinStrategy",
"JoinStrategy",
true);
1847 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1849 xml::Element& sp = p.add_child(
"subParameter");
1851 sp.set_attr(
"classPath",
"jmt.engine.NetStrategies.JoinStrategies.PartialJoin");
1852 sp.set_attr(
"name",
"Quorum");
1854 sp.set_attr(
"classPath",
"jmt.engine.NetStrategies.JoinStrategies.NormalJoin");
1855 sp.set_attr(
"name",
"Standard Join");
1857 const double req = partial ? quorum : (quorum > 0.0 ? quorum : -1.0);
1863 void save_log_tunnel(xml::Element& section, std::size_t ind) {
1864 const qn::NodeDef::LoggerParam& lg = sn_.nodes[ind - 1].logger;
1865 std::string path = lg.file_path.empty() ? sn_.log_path : lg.file_path;
1866 if (!path.empty() && path[path.size() - 1] !=
'/') path +=
'/';
1867 const char* bools[7] = {
"logExecTimestamp",
"logLoggerName",
"logTimeStamp",
1868 "logJobID",
"logJobClass",
"logTimeSameClass",
1870 const bool vals[7] = {lg.start_time, lg.logger_name, lg.timestamp, lg.job_id,
1871 lg.job_class, lg.time_same_class, lg.time_any_class};
1872 jmt_param_value(section,
"java.lang.String",
"logfileName", lg.file_name);
1874 for (
int j = 0; j < 7; ++j)
1875 jmt_param_value(section,
"java.lang.Boolean", bools[j], vals[j] ?
"true" :
"false");
1877 jmt_int(
static_cast<double>(sn_.nclasses)));
1890 void save_metrics(xml::Element& sim) {
1894 for (
int k = 0; k < 5; ++k) save_metric(sim, kinds[k]);
1899 save_sys_tard_metrics(sim);
1900 save_fcr_metrics(sim);
1901 save_fj_metrics(sim);
1902 save_cache_hit_rate_metrics(sim);
1910 void save_fj_metrics(xml::Element& sim) {
1911 std::vector<std::size_t> forks;
1912 for (
const auto& e : sn_.fj)
1913 if (e.first != 0 && e.second != 0) forks.push_back(e.first);
1914 std::sort(forks.begin(), forks.end());
1915 forks.erase(std::unique(forks.begin(), forks.end()), forks.end());
1916 static const char* types[2] = {
"Fork Join Response Time",
"Fork Join Number of Customers"};
1917 static const char* tags[2] = {
"FJRespT",
"FJQLen"};
1918 for (std::size_t f : forks)
1919 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1920 if (!keep_[r - 1])
continue;
1921 bool visits =
false;
1922 for (std::size_t c = 0; c < sn_.inchain.size() && c < sn_.nodevisits.size(); ++c) {
1923 const std::vector<std::size_t>& ic = sn_.inchain[c];
1924 if (std::find(ic.begin(), ic.end(), r) == ic.end())
continue;
1925 visits = num_traits<T>::to_double(sn_.nodevisits[c](f - 1, r - 1)) > 0.0;
1928 if (!visits)
continue;
1929 for (
int k = 0; k < 2; ++k) {
1930 xml::Element& m = sim.add_child(
"measure");
1931 m.set_attr(
"alpha",
jmt_sig2(1.0 - opt_.sim_conf_int));
1932 m.set_attr(
"name", std::string(tags[k]) +
"_" + nname(f) +
"_" + cname(r));
1933 m.set_attr(
"nodeType",
"station");
1934 m.set_attr(
"precision",
jmt_sig2(opt_.sim_max_rel_err));
1935 m.set_attr(
"referenceNode", nname(f));
1936 m.set_attr(
"referenceUserClass", cname(r));
1937 m.set_attr(
"type", types[k]);
1938 m.set_attr(
"verbose",
"false");
1944 for (std::size_t ist = 1; ist <= sn_.nstations; ++ist)
1945 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1946 if (!keep_[r - 1])
continue;
1948 xml::Element& m = sim.add_child(
"measure");
1949 m.set_attr(
"alpha",
jmt_sig2(1.0 - opt_.sim_conf_int));
1950 m.set_attr(
"name", std::string(
"Performance_") +
1951 jmt_int(
static_cast<double>(ist)));
1952 m.set_attr(
"nodeType",
"station");
1953 m.set_attr(
"precision",
jmt_sig2(opt_.sim_max_rel_err));
1954 m.set_attr(
"referenceNode", nname(sn_.station_to_node[ist - 1]));
1955 m.set_attr(
"referenceUserClass", cname(r));
1957 m.set_attr(
"verbose",
"false");
1962 void save_sys_tard_metrics(xml::Element& sim) {
1963 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1964 if (!keep_[r - 1])
continue;
1965 xml::Element& m = sim.add_child(
"measure");
1966 m.set_attr(
"alpha",
jmt_sig2(1.0 - opt_.sim_conf_int));
1967 m.set_attr(
"name",
"Performance_1");
1968 m.set_attr(
"nodeType",
"");
1969 m.set_attr(
"precision",
jmt_sig2(opt_.sim_max_rel_err));
1970 m.set_attr(
"referenceNode",
"");
1971 m.set_attr(
"referenceUserClass", cname(r));
1973 m.set_attr(
"verbose",
"false");
1985 void save_class_soft_deadlines(xml::Element& node, std::size_t ind) {
1986 const std::size_t ist = sn_.nodes[ind - 1].station;
1987 if (ist == 0)
return;
1989 if (sc != SchedStrategy::EDD && sc != SchedStrategy::EDF)
return;
1991 if (!reason.empty())
throw UnsupportedError(ErrorKind{
"Structure"}, reason);
1992 xml::Element& dl = node.add_child(
"classSoftDeadlines");
1993 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1994 if (!keep_[r - 1])
continue;
1997 const double d = sn_.classes[r - 1].deadline;
1999 std::snprintf(buf,
sizeof(buf),
"%.12f", std::isfinite(d) ? d : 0.0);
2000 dl.add_text_child(
"softDeadline", buf);
2005 void save_cache_hit_rate_metrics(xml::Element& sim) {
2006 for (
const auto& kv : sn_.nodeparam) {
2007 const std::size_t ind = kv.first;
2008 if (sn_.nodes[ind - 1].nodetype != NodeType::Cache)
continue;
2009 const qn::CacheParam<T>& cp = kv.second;
2010 for (std::size_t r = 0; r < cp.hitclass.size(); ++r) {
2011 if (cp.hitclass[r] == 0)
continue;
2012 xml::Element& m = sim.add_child(
"measure");
2013 m.set_attr(
"alpha",
jmt_sig2(1.0 - opt_.sim_conf_int));
2014 m.set_attr(
"name",
"CacheHitRate_" + nname(ind) +
"_" + cname(cp.hitclass[r]));
2015 m.set_attr(
"nodeType",
"station");
2016 m.set_attr(
"precision",
jmt_sig2(opt_.sim_max_rel_err));
2017 m.set_attr(
"referenceNode", nname(ind));
2018 m.set_attr(
"referenceUserClass", cname(cp.hitclass[r]));
2019 m.set_attr(
"type",
"Cache Hit Rate");
2020 m.set_attr(
"verbose",
"false");
2026 void save_fcr_metrics(xml::Element& sim) {
2027 static const char* kinds[6] = {
"Number of Customers",
"Response Time",
"Residence Time",
2028 "Throughput",
"FCR Capacity",
"FCR Memory"};
2030 for (std::size_t f = 1; f <= sn_.regions.size(); ++f) {
2031 const std::string fcr =
"FCRegion" +
jmt_int(
static_cast<double>(f));
2032 for (
int k = 0; k < 6; ++k) {
2033 std::string flat(kinds[k]);
2034 flat.erase(std::remove(flat.begin(), flat.end(),
' '), flat.end());
2035 xml::Element& m = sim.add_child(
"measure");
2036 m.set_attr(
"alpha",
jmt_sig2(1.0 - opt_.sim_conf_int));
2037 m.set_attr(
"name",
"FCR_" + fcr +
"_" + flat +
"_" +
2038 jmt_int(
static_cast<double>(counter)));
2039 m.set_attr(
"nodeType",
"region");
2040 m.set_attr(
"precision",
jmt_sig2(opt_.sim_max_rel_err));
2041 m.set_attr(
"referenceNode", fcr);
2042 m.set_attr(
"referenceUserClass",
"");
2043 m.set_attr(
"type", kinds[k]);
2044 m.set_attr(
"verbose",
"false");
2051 void save_links(xml::Element& sim) {
2053 for (std::size_t j = 1; j <= sn_.nodes.size(); ++j)
2054 for (std::size_t i = 1; i <= sn_.nodes.size(); ++i) {
2055 if (!conn_[i - 1][j - 1])
continue;
2056 xml::Element& c = sim.add_child(
"connection");
2057 c.set_attr(
"source", nname(i));
2058 c.set_attr(
"target", nname(j));
2060 for (
const auto& pt : inhibitor_only_arcs()) {
2061 xml::Element& c = sim.add_child(
"connection");
2062 c.set_attr(
"source", nname(pt.first));
2063 c.set_attr(
"target", nname(pt.second));
2076 void save_preload(xml::Element& sim) {
2082 std::vector<std::pair<std::size_t, double>> pops;
2084 std::vector<Row> rows;
2085 for (std::size_t ist = 1; ist <= sn_.nstations; ++ist) {
2086 const std::size_t ind = sn_.station_to_node[ist - 1];
2087 const NodeType ty = sn_.nodes[ind - 1].nodetype;
2088 if (ty == NodeType::Source || ty == NodeType::Join)
continue;
2089 const std::vector<double> nir = initial_marginal(ist);
2092 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2093 const double n = sn_.classes[r - 1].population;
2094 if (std::isfinite(n) && n == 0.0 && nir[r - 1] == 0.0)
continue;
2095 row.pops.emplace_back(r, nir[r - 1]);
2097 if (!row.pops.empty()) rows.push_back(row);
2099 if (rows.empty())
return;
2100 xml::Element& preload = sim.add_child(
"preload");
2101 for (
const Row& row : rows) {
2102 xml::Element& st = preload.add_child(
"stationPopulations");
2103 st.set_attr(
"stationName", nname(row.node));
2104 for (
const auto& pc : row.pops) {
2105 xml::Element& cp = st.add_child(
"classPopulation");
2106 cp.set_attr(
"population",
jmt_int(pc.second));
2107 cp.set_attr(
"refClass", cname(pc.first));
2120 std::vector<double> initial_marginal(std::size_t ist)
const {
2121 std::vector<double> nir(sn_.nclasses, 0.0);
2122 const std::size_t ind = sn_.station_to_node[ist - 1];
2123 const auto im = sn_.initmarking.find(ind);
2124 if (im != sn_.initmarking.end()) {
2125 for (std::size_t r = 0; r < sn_.nclasses && r < im->second.size(); ++r)
2126 nir[r] = d(im->second[r]);
2129 for (std::size_t r = 0; r < sn_.nclasses; ++r) {
2130 const double n = sn_.classes[r].population;
2131 if (std::isfinite(n) && sn_.classes[r].refstat == ist) nir[r] = n;
2139 void save_total_capacity(xml::Element& section, std::size_t ind) {
2140 const std::size_t ist = sn_.nodes[ind - 1].station;
2141 const std::string v =
2142 (ist == 0 || !std::isfinite(sn_.cap[ist - 1])) ?
"-1" :
jmt_int(sn_.cap[ist - 1]);
2147 void save_place_capacities(xml::Element& section, std::size_t ind) {
2148 const std::size_t ist = sn_.nodes[ind - 1].station;
2149 xml::Element& p =
jmt_param(section,
"java.lang.Integer",
"capacities",
true);
2150 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2151 if (!keep_[r - 1])
continue;
2153 const double c = ist == 0 ? std::numeric_limits<double>::infinity()
2154 : sn_.classcap[ist - 1][r - 1];
2155 xml::Element& sp = p.add_child(
"subParameter");
2156 sp.set_attr(
"classPath",
"java.lang.Integer");
2157 sp.set_attr(
"name",
"capacity");
2158 sp.add_text_child(
"value", std::isfinite(c) ?
jmt_int(c) :
"-1");
2166 void save_drop_rule(xml::Element& section, std::size_t ind) {
2167 const std::size_t ist = sn_.nodes[ind - 1].station;
2168 xml::Element& p =
jmt_param(section,
"java.lang.String",
"dropRules",
true);
2169 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2170 if (!keep_[r - 1])
continue;
2172 xml::Element& sp = p.add_child(
"subParameter");
2173 sp.set_attr(
"classPath",
"java.lang.String");
2174 sp.set_attr(
"name",
"dropRule");
2175 sp.add_text_child(
"value", drop_strategy_text(ist, r));
2184 void save_put_strategies(xml::Element& section, std::size_t ind) {
2185 const std::size_t ist = sn_.nodes[ind - 1].station;
2186 xml::Element& p =
jmt_param(section,
"jmt.engine.NetStrategies.QueuePutStrategy",
2187 "QueuePutStrategy",
true);
2188 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2189 if (!keep_[r - 1])
continue;
2191 const char* nm =
"TailStrategy";
2193 if (sn_.stations[ist - 1].sched == SchedStrategy::SIRO) nm =
"RandStrategy";
2194 else if (sn_.stations[ist - 1].sched == SchedStrategy::LCFS) nm =
"HeadStrategy";
2196 xml::Element& sp = p.add_child(
"subParameter");
2197 sp.set_attr(
"classPath",
2198 std::string(
"jmt.engine.NetStrategies.QueuePutStrategies.") + nm);
2199 sp.set_attr(
"name", nm);
2206 const qn::TransitionParam<T>& transparam(std::size_t ind)
const {
2207 const auto it = sn_.transparam.find(ind);
2208 if (it == sn_.transparam.end())
2209 throw InputError(
"SolverJMT: transition '" + nname(ind) +
"' carries no mode table");
2226 void save_enabling_conditions(xml::Element& section, std::size_t ind) {
2227 const qn::TransitionParam<T>& tp = transparam(ind);
2229 jmt_param(section,
"jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix",
2230 "enablingConditions",
true);
2231 for (std::size_t m = 0; m < tp.nmodes; ++m) {
2232 xml::Element& cond = p.add_child(
"subParameter");
2233 cond.set_attr(
"classPath",
2234 "jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix");
2235 cond.set_attr(
"name",
"enablingCondition");
2236 xml::Element& vecs = jmt_param_sub(
2237 cond,
"jmt.engine.NetStrategies.TransitionUtilities.TransitionVector",
2239 for (std::size_t k = 1; k <= sn_.nodes.size(); ++k) {
2240 bool relevant =
false;
2241 for (std::size_t r = 1; r <= sn_.nclasses && !relevant; ++r) {
2242 const double en = arc(tp.enabling, m, k, r);
2243 const double in = arc(tp.inhibiting, m, k, r);
2244 relevant = (std::isfinite(en) && en > 0.0) ||
2245 (std::isfinite(in) && in > 0.0);
2247 if (!relevant)
continue;
2248 xml::Element& vec = vecs.add_child(
"subParameter");
2249 vec.set_attr(
"classPath",
2250 "jmt.engine.NetStrategies.TransitionUtilities.TransitionVector");
2251 vec.set_attr(
"name",
"enablingVector");
2252 jmt_scalar(vec,
"java.lang.String",
"stationName", nname(k));
2253 xml::Element& entries = jmt_param_sub(vec,
"java.lang.Integer",
"enablingEntries");
2254 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2255 const double en = arc(tp.enabling, m, k, r);
2257 xml::Element& e = entries.add_child(
"subParameter");
2258 e.set_attr(
"classPath",
"java.lang.Integer");
2259 e.set_attr(
"name",
"enablingEntry");
2260 e.add_text_child(
"value", std::isfinite(en) ?
jmt_int(en) :
"-1");
2274 void save_inhibiting_conditions(xml::Element& section, std::size_t ind) {
2275 const qn::TransitionParam<T>& tp = transparam(ind);
2276 std::vector<std::size_t> inputs = inputs_of(ind);
2277 for (
const auto& pt : inhibitor_only_arcs())
2278 if (pt.second == ind && std::find(inputs.begin(), inputs.end(), pt.first) == inputs.end())
2279 inputs.push_back(pt.first);
2281 jmt_param(section,
"jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix",
2282 "inhibitingConditions",
true);
2283 for (std::size_t m = 0; m < tp.nmodes; ++m) {
2284 xml::Element& cond = p.add_child(
"subParameter");
2285 cond.set_attr(
"classPath",
2286 "jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix");
2287 cond.set_attr(
"name",
"inhibitingCondition");
2288 xml::Element& vecs = jmt_param_sub(
2289 cond,
"jmt.engine.NetStrategies.TransitionUtilities.TransitionVector",
2290 "inhibitingVectors");
2291 for (std::size_t k : inputs) {
2292 xml::Element& vec = vecs.add_child(
"subParameter");
2293 vec.set_attr(
"classPath",
2294 "jmt.engine.NetStrategies.TransitionUtilities.TransitionVector");
2295 vec.set_attr(
"name",
"inhibitingVector");
2296 jmt_scalar(vec,
"java.lang.String",
"stationName", nname(k));
2297 xml::Element& entries = jmt_param_sub(vec,
"java.lang.Integer",
"inhibitingEntries");
2298 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2299 const double in = arc(tp.inhibiting, m, k, r);
2301 xml::Element& e = entries.add_child(
"subParameter");
2302 e.set_attr(
"classPath",
"java.lang.Integer");
2303 e.set_attr(
"name",
"inhibitingEntry");
2304 e.add_text_child(
"value", std::isfinite(in) ?
jmt_int(in) :
"0");
2311 void save_firing_outcomes(xml::Element& section, std::size_t ind) {
2312 const qn::TransitionParam<T>& tp = transparam(ind);
2313 const std::vector<std::size_t> outs = outputs_of(ind);
2315 jmt_param(section,
"jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix",
2316 "firingOutcomes",
true);
2317 for (std::size_t m = 0; m < tp.nmodes; ++m) {
2318 xml::Element& out = p.add_child(
"subParameter");
2319 out.set_attr(
"classPath",
2320 "jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix");
2321 out.set_attr(
"name",
"firingOutcome");
2322 xml::Element& vecs = jmt_param_sub(
2323 out,
"jmt.engine.NetStrategies.TransitionUtilities.TransitionVector",
2325 for (std::size_t k : outs) {
2326 xml::Element& vec = vecs.add_child(
"subParameter");
2327 vec.set_attr(
"classPath",
2328 "jmt.engine.NetStrategies.TransitionUtilities.TransitionVector");
2329 vec.set_attr(
"name",
"firingVector");
2330 jmt_scalar(vec,
"java.lang.String",
"stationName", nname(k));
2331 xml::Element& entries = jmt_param_sub(vec,
"java.lang.Integer",
"firingEntries");
2332 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2333 const double f = arc(tp.firing, m, k, r);
2335 xml::Element& e = entries.add_child(
"subParameter");
2336 e.set_attr(
"classPath",
"java.lang.Integer");
2337 e.set_attr(
"name",
"firingEntry");
2338 e.add_text_child(
"value",
jmt_int(f));
2345 double arc(
const std::vector<Matrix<T>>& tab, std::size_t m, std::size_t k,
2346 std::size_t r)
const {
2347 if (m >= tab.size() || k == 0 || k > tab[m].rows() || r == 0 || r > tab[m].cols())
2349 return d(tab[m](k - 1, r - 1));
2353 void save_mode_names(xml::Element& section, std::size_t ind) {
2354 const qn::TransitionParam<T>& tp = transparam(ind);
2355 xml::Element& p =
jmt_param(section,
"java.lang.String",
"modeNames",
true);
2356 for (std::size_t m = 0; m < tp.nmodes; ++m) {
2357 xml::Element& sp = p.add_child(
"subParameter");
2358 sp.set_attr(
"classPath",
"java.lang.String");
2359 sp.set_attr(
"name",
"modeName");
2360 sp.add_text_child(
"value", m < tp.modenames.size() ? tp.modenames[m] : std::string());
2365 void save_numbers_of_servers(xml::Element& section, std::size_t ind) {
2366 const qn::TransitionParam<T>& tp = transparam(ind);
2367 xml::Element& p =
jmt_param(section,
"java.lang.Integer",
"numbersOfServers",
true);
2368 for (std::size_t m = 0; m < tp.nmodes; ++m) {
2369 const double ns = m < tp.nmodeservers.size() ? tp.nmodeservers[m] : 1.0;
2370 xml::Element& sp = p.add_child(
"subParameter");
2371 sp.set_attr(
"classPath",
"java.lang.Integer");
2372 sp.set_attr(
"name",
"numberOfServers");
2373 sp.add_text_child(
"value", std::isfinite(ns) ?
jmt_int(ns) :
"-1");
2385 void save_timing_strategies(xml::Element& section, std::size_t ind) {
2386 const qn::TransitionParam<T>& tp = transparam(ind);
2387 xml::Element& p =
jmt_param(section,
"jmt.engine.NetStrategies.ServiceStrategy",
2388 "timingStrategies",
true);
2389 for (std::size_t m = 0; m < tp.nmodes; ++m) {
2390 xml::Element& sp = p.add_child(
"subParameter");
2391 const bool immediate = m < tp.timing.size() &&
2394 sp.set_attr(
"classPath",
2395 "jmt.engine.NetStrategies.ServiceStrategies.ZeroServiceTimeStrategy");
2396 sp.set_attr(
"name",
"ZeroServiceTimeStrategy");
2399 sp.set_attr(
"classPath",
2400 "jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy");
2401 sp.set_attr(
"name",
"timingStrategy");
2402 if (m >= tp.firingproc.size())
2404 (m < tp.modenames.size() ? tp.modenames[m] : std::string()) +
2405 "' of transition '" + nname(ind) +
"' has no firing process");
2407 "SolverJMT (transition firing)");
2412 void save_firing_priorities(xml::Element& section, std::size_t ind) {
2413 const qn::TransitionParam<T>& tp = transparam(ind);
2414 xml::Element& p =
jmt_param(section,
"java.lang.Integer",
"firingPriorities",
true);
2415 for (std::size_t m = 0; m < tp.nmodes; ++m) {
2416 const double v = m < tp.firingprio.size() ? tp.firingprio[m] : 1.0;
2417 xml::Element& sp = p.add_child(
"subParameter");
2418 sp.set_attr(
"classPath",
"java.lang.Integer");
2419 sp.set_attr(
"name",
"firingPriority");
2420 sp.add_text_child(
"value", std::isfinite(v) ?
jmt_int(v) :
"-1");
2433 void save_firing_weights(xml::Element& section, std::size_t ind) {
2434 const qn::TransitionParam<T>& tp = transparam(ind);
2435 xml::Element& p =
jmt_param(section,
"java.lang.Double",
"firingWeights",
true);
2436 for (std::size_t m = 0; m < tp.nmodes; ++m) {
2437 const double v = m < tp.fireweight.size() ? d(tp.fireweight[m]) : 1.0;
2438 xml::Element& sp = p.add_child(
"subParameter");
2439 sp.set_attr(
"classPath",
"java.lang.Double");
2440 sp.set_attr(
"name",
"firingWeight");
2441 sp.add_text_child(
"value", std::isfinite(v) ?
jmt_fmt(v) :
"-1");
2457 std::vector<std::vector<double>> class_cap_constraints()
const {
2458 const double inf = std::numeric_limits<double>::infinity();
2459 std::vector<std::vector<double>> con(sn_.nstations, std::vector<double>(sn_.nclasses, inf));
2460 if (sn_.classcap.empty())
return con;
2461 std::vector<double> chainpop(sn_.nclasses, inf);
2462 for (std::size_t c = 0; c < sn_.inchain.size(); ++c) {
2465 for (std::size_t r : sn_.inchain[c]) {
2466 const double n = sn_.classes[r - 1].population;
2467 if (std::isfinite(n))
tot += n;
else open =
true;
2469 for (std::size_t r : sn_.inchain[c]) chainpop[r - 1] = open ? inf :
tot;
2471 for (std::size_t ist = 1; ist <= sn_.nstations; ++ist) {
2472 const NodeType ty = sn_.nodes[sn_.station_to_node[ist - 1] - 1].nodetype;
2473 if (ty == NodeType::Source || ty == NodeType::Place)
continue;
2474 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2475 if (sn_.disabled[ist - 1][r - 1])
continue;
2476 const double cc = sn_.classcap[ist - 1][r - 1];
2477 if (std::isfinite(cc) && cc < 2147483647.0 &&
2478 cc < std::min(sn_.cap[ist - 1], chainpop[r - 1]))
2479 con[ist - 1][r - 1] = cc;
2495 void assert_class_cap_exportable(std::size_t ist, std::size_t r)
const {
2496 const std::string sname = nname(sn_.station_to_node[ist - 1]);
2497 if (std::isfinite(sn_.classes[r - 1].population))
2499 "SolverJMT: station '" + sname +
"' carries a finite capacity " +
2500 jmt_int(sn_.classcap[ist - 1][r - 1]) +
" for the closed class '" + cname(r) +
2501 "'. LINE holds a blocked closed job at its upstream station, whereas JMT can "
2502 "only express a per-class capacity as a blocking region, which parks the job in "
2503 "the region input station instead, freeing the upstream server and losing it "
2504 "from the population count. Use SolverCTMC, SolverSSA or SolverLDES, or express "
2505 "the limit as the station capacity");
2507 if (dr == DropStrategy::BAS || dr == DropStrategy::BBS || dr == DropStrategy::RSRD ||
2508 dr == DropStrategy::RETRIAL || dr == DropStrategy::RETRIAL_WITH_LIMIT)
2510 "SolverJMT: station '" + sname +
"' applies drop strategy '" +
jmt_drop_text(dr) +
2511 "' to class '" + cname(r) +
2512 "' and also carries a finite capacity for it. JMT exports a per-class capacity "
2513 "as a blocking region, which can only drop or defer an arrival. Remove the "
2514 "per-class capacity, or use the station capacity, which is exported with its "
2530 void save_regions(xml::Element& sim) {
2531 const std::vector<std::vector<double>> con = class_cap_constraints();
2532 std::vector<bool> covered(sn_.nstations,
false);
2533 std::size_t lps_idx = sn_.regions.size();
2535 for (std::size_t ist = 1; ist <= sn_.nstations; ++ist) {
2536 if (sn_.stations[ist - 1].sched != SchedStrategy::LPS)
continue;
2538 const std::size_t ind = sn_.station_to_node[ist - 1];
2539 const double limit = sn_.stations[ist - 1].schedparam.empty()
2541 : d(sn_.stations[ist - 1].schedparam[0]);
2542 xml::Element& br = sim.add_child(
"blockingRegion");
2543 br.set_attr(
"name",
"LPSRegion" +
jmt_int(
static_cast<double>(lps_idx)));
2544 br.set_attr(
"type",
"default");
2545 br.add_child(
"regionNode").set_attr(
"nodeName", nname(ind));
2546 br.add_child(
"globalConstraint").set_attr(
"maxJobs",
jmt_num(limit));
2547 br.add_child(
"globalMemoryConstraint").set_attr(
"maxMemory",
"-1");
2548 covered[ist - 1] =
true;
2549 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2550 if (!std::isfinite(con[ist - 1][r - 1]))
continue;
2551 assert_class_cap_exportable(ist, r);
2553 "SolverJMT: station '" + nname(ind) +
2554 "' has both LPS scheduling and a finite capacity for the open class '" +
2556 "'. JMT expresses both through a single blocking region, which admits only "
2557 "one drop rule per class, but LPS requires blocking while the open-class "
2558 "capacity requires dropping. Remove the per-class capacity or use a non-LPS "
2559 "scheduling strategy");
2561 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2562 xml::Element& dr = br.add_child(
"dropRules");
2563 dr.set_attr(
"jobClass", cname(r));
2564 dr.set_attr(
"dropThisClass",
"false");
2568 for (std::size_t f = 1; f <= sn_.regions.size(); ++f) {
2569 const typename qn::NetworkStruct<T>::Region& rg = sn_.regions[f - 1];
2570 std::vector<std::size_t> members;
2571 for (std::size_t ist = 1; ist <= sn_.nstations; ++ist)
2572 if (ist <= rg.members.size() && rg.members[ist - 1]) {
2573 members.push_back(ist);
2574 covered[ist - 1] =
true;
2576 std::vector<double> region_class_cap(sn_.nclasses,
2577 std::numeric_limits<double>::infinity());
2578 for (std::size_t ist : members)
2579 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2580 if (!std::isfinite(con[ist - 1][r - 1]))
continue;
2581 if (members.size() > 1)
2583 "SolverJMT: station '" + nname(sn_.station_to_node[ist - 1]) +
2584 "' carries a finite capacity for class '" + cname(r) +
2585 "' and also belongs to the multi-station region FCRegion" +
2586 jmt_int(
static_cast<double>(f)) +
2587 ". JMT constrains a blocking region as a whole and allows a node to "
2588 "belong to only one region, so a per-station class capacity cannot "
2589 "be expressed alongside it");
2590 assert_class_cap_exportable(ist, r);
2591 if (!(r <= rg.rule.size() && rg.rule[r - 1] == DropStrategy::DROP))
2593 "SolverJMT: station '" + nname(sn_.station_to_node[ist - 1]) +
2594 "' carries a finite capacity for class '" + cname(r) +
2595 "' and also belongs to region FCRegion" +
2596 jmt_int(
static_cast<double>(f)) +
2597 ", whose drop rule for that class is not DROP. A JMT blocking "
2598 "region admits a single drop rule per class, shared by all of its "
2599 "constraints, and the per-class capacity of an open class is a loss "
2601 region_class_cap[r - 1] = con[ist - 1][r - 1];
2604 xml::Element& br = sim.add_child(
"blockingRegion");
2605 br.set_attr(
"name",
"FCRegion" +
jmt_int(
static_cast<double>(f)));
2606 br.set_attr(
"type",
"default");
2607 for (std::size_t ist : members)
2608 br.add_child(
"regionNode")
2609 .set_attr(
"nodeName", nname(sn_.station_to_node[ist - 1]));
2613 members.empty() ? -1.0 : rg.cap[members[0] - 1][sn_.nclasses];
2614 const double gmem = members.empty() ? -1.0 : rg.maxmem[members[0] - 1];
2615 br.add_child(
"globalConstraint").set_attr(
"maxJobs",
jmt_num(gmax));
2616 br.add_child(
"globalMemoryConstraint").set_attr(
"maxMemory",
jmt_num(gmem));
2617 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2618 double cmax = members.empty() ? -1.0 : rg.cap[members[0] - 1][r - 1];
2622 cmax = std::isfinite(region_class_cap[r - 1]) ? region_class_cap[r - 1] : -1.0;
2623 else if (std::isfinite(region_class_cap[r - 1]))
2624 cmax = std::min(cmax, region_class_cap[r - 1]);
2625 if (cmax == -1.0 || !std::isfinite(cmax))
continue;
2626 xml::Element& cc = br.add_child(
"classConstraint");
2627 cc.set_attr(
"jobClass", cname(r));
2628 cc.set_attr(
"maxJobsPerClass",
jmt_num(cmax));
2634 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2635 xml::Element& dr = br.add_child(
"dropRules");
2636 dr.set_attr(
"jobClass", cname(r));
2637 dr.set_attr(
"dropThisClass",
2638 (r <= rg.rule.size() && rg.rule[r - 1] == DropStrategy::DROP)
2642 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2643 if (r > rg.weight.size())
continue;
2644 const double w = d(rg.weight[r - 1]);
2645 if (w == 1.0)
continue;
2646 xml::Element& cw = br.add_child(
"classWeight");
2647 cw.set_attr(
"jobClass", cname(r));
2648 cw.set_attr(
"weight",
jmt_num(w));
2650 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2651 if (r > rg.size.size())
continue;
2652 const double s = d(rg.size[r - 1]);
2653 if (s == 1.0)
continue;
2654 xml::Element& cs = br.add_child(
"classSize");
2655 cs.set_attr(
"jobClass", cname(r));
2656 cs.set_attr(
"size",
jmt_num(s));
2660 std::size_t cap_idx = 0;
2661 for (std::size_t ist = 1; ist <= sn_.nstations; ++ist) {
2662 if (covered[ist - 1])
continue;
2664 for (std::size_t r = 1; r <= sn_.nclasses; ++r)
2665 if (std::isfinite(con[ist - 1][r - 1])) any =
true;
2668 for (std::size_t r = 1; r <= sn_.nclasses; ++r)
2669 if (std::isfinite(con[ist - 1][r - 1])) assert_class_cap_exportable(ist, r);
2670 xml::Element& br = sim.add_child(
"blockingRegion");
2671 br.set_attr(
"name",
"ClassCapRegion" +
jmt_int(
static_cast<double>(cap_idx)));
2672 br.set_attr(
"type",
"default");
2673 br.add_child(
"regionNode")
2674 .set_attr(
"nodeName", nname(sn_.station_to_node[ist - 1]));
2675 br.add_child(
"globalConstraint").set_attr(
"maxJobs",
"-1");
2676 br.add_child(
"globalMemoryConstraint").set_attr(
"maxMemory",
"-1");
2677 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2678 if (!std::isfinite(con[ist - 1][r - 1]))
continue;
2679 xml::Element& cc = br.add_child(
"classConstraint");
2680 cc.set_attr(
"jobClass", cname(r));
2681 cc.set_attr(
"maxJobsPerClass",
jmt_num(con[ist - 1][r - 1]));
2683 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2684 if (!std::isfinite(con[ist - 1][r - 1]))
continue;
2685 xml::Element& dr = br.add_child(
"dropRules");
2686 dr.set_attr(
"jobClass", cname(r));
2687 dr.set_attr(
"dropThisClass",
"true");
2715 void save_cache_strategy(xml::Element& section, std::size_t ind) {
2716 const auto it = sn_.nodeparam.find(ind);
2717 if (it == sn_.nodeparam.end())
2718 throw InputError(
"SolverJMT: cache node '" + nname(ind) +
"' carries no parameters");
2719 const qn::CacheParam<T>& cp = it->second;
2720 const std::size_t K = sn_.nclasses;
2727 jmt_int(
static_cast<double>(cp.nitems)));
2728 xml::Element& cap =
jmt_param(section,
"java.lang.Integer",
"cacheCapacity",
true);
2729 for (std::size_t l = 0; l < cp.itemcap.size(); ++l) {
2730 xml::Element& sp = cap.add_child(
"subParameter");
2731 sp.set_attr(
"classPath",
"java.lang.Integer");
2732 sp.set_attr(
"name",
"capacity");
2733 sp.add_text_child(
"value",
jmt_int(
static_cast<double>(cp.itemcap[l])));
2736 const std::size_t nlev = cp.itemcap.size();
2737 std::vector<std::size_t> readers;
2738 for (std::size_t r = 0; r < K; ++r)
2739 if ((r < cp.hitclass.size() && cp.hitclass[r] > 0) ||
2740 (r < cp.missclass.size() && cp.missclass[r] > 0))
2741 readers.push_back(r);
2742 const std::vector<std::vector<double>> promo = cache_jmt_promotion(cp, nlev, readers, ind);
2743 xml::Element& mat =
jmt_param(section,
"java.lang.Object",
"matrix",
true);
2744 for (std::size_t a = 0; a < nlev; ++a) {
2745 xml::Element& row = mat.add_child(
"subParameter");
2746 row.set_attr(
"array",
"true");
2747 row.set_attr(
"classPath",
"java.lang.Float");
2748 row.set_attr(
"name",
"row");
2750 for (std::size_t b = 0; b < nlev; ++b) {
2751 cum = std::min(cum + promo[a][b], 1.0);
2752 xml::Element& cell = row.add_child(
"subParameter");
2753 cell.set_attr(
"classPath",
"java.lang.Float");
2754 cell.set_attr(
"name",
"cell");
2756 std::snprintf(buf,
sizeof(buf),
"%.12g", cum);
2757 cell.add_text_child(
"value", buf);
2761 xml::Element& jc =
jmt_param(section,
"jmt.engine.QueueNet.JobClass",
"jobClasses",
true);
2762 for (std::size_t r = 1; r <= K; ++r) {
2763 const bool used = (r <= cp.hitclass.size() && cp.hitclass[r - 1] > 0) ||
2764 (r <= cp.missclass.size() && cp.missclass[r - 1] > 0);
2765 if (!used)
continue;
2766 xml::Element& sp = jc.add_child(
"subParameter");
2767 sp.set_attr(
"classPath",
"jmt.engine.QueueNet.JobClass");
2768 sp.set_attr(
"name",
"jobClass");
2769 sp.add_text_child(
"value", cname(r));
2771 const char* switch_names[2] = {
"hitClasses",
"missClasses"};
2772 const char* entry_names[2] = {
"hitClass",
"missClass"};
2773 for (
int w = 0; w < 2; ++w) {
2774 const std::vector<std::size_t>& tab = w == 0 ? cp.hitclass : cp.missclass;
2776 jmt_param(section,
"jmt.engine.QueueNet.JobClass", switch_names[w],
true);
2777 for (std::size_t r = 1; r <= K && r <= tab.size(); ++r) {
2778 if (tab[r - 1] == 0)
continue;
2779 xml::Element& sp = p.add_child(
"subParameter");
2780 sp.set_attr(
"classPath",
"jmt.engine.QueueNet.JobClass");
2781 sp.set_attr(
"name", entry_names[w]);
2782 sp.add_text_child(
"value", cname(tab[r - 1]));
2786 const char* policy =
nullptr;
2787 switch (cp.replacestrat) {
2789 policy =
"jmt.engine.NetStrategies.CacheStrategies.LRUCache";
2793 policy =
"jmt.engine.NetStrategies.CacheStrategies.FIFOCache";
2796 policy =
"jmt.engine.NetStrategies.CacheStrategies.RandomCache";
2801 xml::Element& rp = section.add_child(
"parameter");
2802 rp.set_attr(
"classPath", policy);
2803 rp.set_attr(
"name",
"replacePolicy");
2813 section,
"jmt.engine.random.discrete.DiscreteDistribution",
"popularity",
true);
2814 for (std::size_t r = 1; r <= K; ++r) {
2816 const bool reads = r <= cp.pread.size() && !cp.pread[r - 1].empty();
2817 const typename qn::CacheParam<T>::Popularity kind =
2818 r <= cp.preadkind.size() ? cp.preadkind[r - 1]
2819 :
typename qn::CacheParam<T>::Popularity();
2820 xml::Element& sp = pop.add_child(
"subParameter");
2822 sp.set_attr(
"classPath",
"jmt.engine.random.discrete.Zipf");
2823 sp.set_attr(
"name",
"popularity");
2825 jmt_scalar(sp,
"java.lang.Integer",
"numberOfElements",
2826 jmt_int(
static_cast<double>(kind.n)));
2829 sp.set_attr(
"classPath",
"jmt.engine.random.discrete.Uniform");
2830 sp.set_attr(
"name",
"popularity");
2831 jmt_scalar(sp,
"java.lang.Integer",
"min",
"1");
2833 jmt_int(
static_cast<double>(kind.n == 0 ? cp.nitems : kind.n)));
2837 sp.set_attr(
"classPath",
"jmt.engine.random.discrete.DiscreteDistribution");
2838 sp.set_attr(
"name",
"null");
2839 sp.add_text_child(
"value",
"null");
2855 std::vector<std::vector<double>> cache_jmt_promotion(
const qn::CacheParam<T>& cp,
2857 const std::vector<std::size_t>& readers,
2858 std::size_t ind)
const {
2859 std::vector<std::vector<double>>
P;
2860 const std::string why = cache_jmt_promotion_reason(cp, h, readers, ind, P);
2878 const auto it = sn_.nodeparam.find(ind);
2879 if (it == sn_.nodeparam.end())
return "SolverJMT: cache node '" + nname(ind) +
"' carries no parameters";
2881 const std::size_t K = sn_.nclasses;
2884 return "SolverJMT: cache '" + nname(ind) +
2885 "' uses a replacement policy JMT has no cache object for (HLRU, CLIMB and "
2886 "QLRU); use SolverLDES, which simulates them directly";
2887 const std::size_t nlev = cp.
itemcap.size();
2888 std::vector<std::size_t> readers;
2889 for (std::size_t r = 0; r < K; ++r)
2892 readers.push_back(r);
2893 std::vector<std::vector<double>> promo;
2895 const std::string why = cache_jmt_promotion_reason(cp, nlev, readers, ind, promo);
2896 if (!why.empty())
return why;
2900 for (std::size_t l = 0; l < nlev; ++l) {
2901 if (promo[l][l] > 0.0 && cp.
itemcap[l] > 1)
2902 return "SolverJMT: cache '" + nname(ind) +
"' uses " +
2904 " replacement, but JMT moves an item hit in list " + std::to_string(l + 1) +
2905 " (capacity " + std::to_string(cp.
itemcap[l]) +
2906 ") to the tail of that list, which is LRU order: the simulation would solve a "
2907 "different policy. JMT is exact for FIFO only when every list an item can stay "
2908 "in has capacity 1. Use SSA, LDES or CTMC.";
2912 for (std::size_t r = 1; r <= K; ++r) {
2913 const bool reads = r <= cp.
pread.size() && !cp.
pread[r - 1].empty();
2914 if (!reads)
continue;
2919 const auto& pm = cp.
pread[r - 1];
2921 for (
const auto& v : pm) psum +=
static_cast<double>(v);
2922 for (
const auto& v : pm)
2923 if (std::abs(
static_cast<double>(v) / psum - 1.0 /
static_cast<double>(pm.size())) > 1e-3)
2924 return "SolverJMT: cache '" + nname(ind) +
"' gives class '" + cname(r) +
2925 "' a non-uniform DiscreteSampler popularity, which JMT cannot express: its Cache "
2926 "section takes a parametric popularity (Zipf or uniform) and has no empirical "
2927 "discrete form, so this would be exported as a uniform draw over all " +
2928 std::to_string(cp.
nitems) +
" items and solved as a different model. Use Zipf, "
2929 "a uniform popularity, or a solver other than JMT (SSA, LDES, CTMC, MVA, NC all take it).";
2934 return "SolverJMT: cache '" + nname(ind) +
"' gives class '" + cname(r) +
2935 "' a popularity that is neither Zipf nor uniform; the JMT Cache section takes Zipf "
2936 "or a uniform popularity only. Use SSA, LDES, CTMC, MVA or NC.";
2938 return std::string();
2943 std::string cache_jmt_promotion_reason(
const qn::CacheParam<T>& cp, std::size_t h,
2944 const std::vector<std::size_t>& readers, std::size_t ind,
2945 std::vector<std::vector<double>>& P)
const {
2946 const double tol = 1e-8;
2947 std::vector<std::vector<double>> ref;
2948 for (
const std::size_t r : readers) {
2949 if (r >= cp.
accost.size())
continue;
2950 for (std::size_t k = 0; k < cp.
nitems && k < cp.
accost[r].size(); ++k) {
2952 if (M.
empty())
continue;
2953 std::vector<std::vector<double>> dm(M.
rows(), std::vector<double>(M.
cols(), 0.0));
2954 for (std::size_t a = 0; a < M.
rows(); ++a)
2955 for (std::size_t b = 0; b < M.
cols(); ++b) dm[a][b] =
static_cast<double>(M(a, b));
2960 bool same = dm.size() == ref.size() && dm[0].size() == ref[0].size();
2961 for (std::size_t a = 0; same && a < dm.size(); ++a)
2962 for (std::size_t b = 0; same && b < dm[a].size(); ++b)
2963 if (std::abs(dm[a][b] - ref[a][b]) > tol) same =
false;
2965 return "SolverJMT: cache '" + nname(ind) +
"' has an access graph that differs across "
2966 "classes or items; the JMT Cache section takes one list-promotion matrix for all "
2967 "of them. Use SSA, LDES or CTMC.";
2970 P.assign(h, std::vector<double>(h, 0.0));
2972 for (std::size_t l = 0; l < h; ++l) P[l][std::min(l + 1, h - 1)] = 1.0;
2973 return std::string();
2975 if (ref.size() != h + 1 || ref[0].size() != h + 1)
2976 return "SolverJMT: cache '" + nname(ind) +
"' has a " + std::to_string(ref.size()) +
"x" +
2977 std::to_string(ref[0].size()) +
" access graph for " + std::to_string(h) +
2978 " lists; expected " + std::to_string(h + 1) +
"x" + std::to_string(h + 1) +
".";
2979 double ins_sum = 0.0;
2980 for (std::size_t b = 1; b <= h; ++b) ins_sum += ref[0][b];
2981 if (ref[0][0] > 0.0 || std::abs(ins_sum - 1.0) > tol || std::abs(ref[0][1] - 1.0) > tol)
2982 return "SolverJMT: cache '" + nname(ind) +
"' inserts a missed item somewhere other than "
2983 "list 1; JMT always inserts a miss into the first list. Use SSA, LDES or CTMC.";
2984 for (std::size_t l = 1; l <= h; ++l) {
2985 if (ref[l][0] > 0.0)
2986 return "SolverJMT: cache '" + nname(ind) +
"' demotes a hit in list " + std::to_string(l) +
2987 " out of the cache; the JMT Cache section has no such move. Use SSA, LDES or CTMC.";
2989 for (std::size_t b = 1; b <= h; ++b) tot += ref[l][b];
2991 P[l - 1][l - 1] = 1.0;
2993 for (std::size_t b = 1; b <= h; ++b) P[l - 1][b - 1] = ref[l][b] / tot;
2996 return std::string();
3009 void save_impatience(xml::Element& section, std::size_t ind) {
3010 const std::size_t ist = sn_.nodes[ind - 1].station;
3012 section,
"jmt.engine.NetStrategies.ImpatienceStrategies.Impatience",
"Impatience",
true);
3013 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
3014 if (!keep_[r - 1])
continue;
3016 xml::Element& sp = p.add_child(
"subParameter");
3017 const qn::Station<T>* st = ist == 0 ? nullptr : &sn_.stations[ist - 1];
3018 const bool balks = st !=
nullptr && r <= st->balking.size() &&
3019 st->balking[r - 1].strategy ==
3022 sp.set_attr(
"classPath",
"jmt.engine.NetStrategies.ImpatienceStrategies.Balking");
3023 sp.set_attr(
"name",
"Balking");
3024 save_balking_strategy(sp, st->balking[r - 1].thresholds, st->nservers);
3027 sp.set_attr(
"classPath",
"jmt.engine.NetStrategies.ImpatienceStrategies.Reneging");
3028 sp.set_attr(
"name",
"Reneging");
3029 const bool renegs = st !=
nullptr && r <= st->impatience.size() &&
3031 r <= st->patience.size() && !st->patience[r - 1].disabled;
3033 sp.add_text_child(
"value",
"null");
3036 const JmtDistView<T> v =
jmt_dist_view(st->patience[r - 1]);
3042 xml::Element& dn = sp.add_child(
"subParameter");
3043 dn.set_attr(
"classPath",
"jmt.engine.random.Uniform");
3044 dn.set_attr(
"name",
"Uniform");
3045 xml::Element& par = sp.add_child(
"subParameter");
3046 par.set_attr(
"classPath",
"jmt.engine.random.UniformPar");
3047 par.set_attr(
"name",
"distrPar");
3048 jmt_scalar(par,
"java.lang.Double",
"min",
"0.0");
3049 jmt_double(par,
"max", v.rate > 0.0 ? 2.0 / v.rate : 0.0);
3068 void save_balking_strategy(xml::Element& balking,
3069 const std::vector<
typename qn::Station<T>::BalkingThreshold>& th,
3074 std::vector<typename qn::Station<T>::BalkingThreshold> sorted = th;
3075 std::sort(sorted.begin(), sorted.end(),
3076 [](
const typename qn::Station<T>::BalkingThreshold& a,
3077 const typename qn::Station<T>::BalkingThreshold& b) {
3078 return a.min_jobs < b.min_jobs;
3081 (!std::isfinite(nservers) || nservers < 1.0) ? 1.0 : nservers;
3082 std::vector<Bp> bps;
3083 for (std::size_t i = 0; i < sorted.size(); ++i) {
3084 const double lo = sorted[i].min_jobs;
3087 const double hi = sorted[i].max_jobs < 0.0
3088 ? std::numeric_limits<double>::infinity()
3089 : sorted[i].max_jobs;
3090 const double pr = d(sorted[i].probability);
3091 bps.push_back(Bp{std::max(0.0, lo - S), pr});
3092 if (!std::isfinite(hi))
continue;
3093 const double next_lo = i + 1 < sorted.size()
3094 ? sorted[i + 1].min_jobs
3095 : std::numeric_limits<double>::infinity();
3096 if (hi + 1.0 < next_lo) bps.push_back(Bp{std::max(0.0, hi + 1.0 - S), 0.0});
3098 xml::Element& ld = balking.add_child(
"subParameter");
3099 ld.set_attr(
"classPath",
"jmt.engine.NetStrategies.ServiceStrategies.LoadDependentStrategy");
3100 ld.set_attr(
"name",
"LoadDependentStrategy");
3101 xml::Element& arr = jmt_param_sub(
3102 ld,
"jmt.engine.NetStrategies.ServiceStrategies.LDParameter",
"LDParameter");
3103 for (
const Bp& b : bps) {
3104 xml::Element& rn = arr.add_child(
"subParameter");
3105 rn.set_attr(
"classPath",
"jmt.engine.NetStrategies.ServiceStrategies.LDParameter");
3106 rn.set_attr(
"name",
"LDParameter");
3110 xml::Element& dn = rn.add_child(
"subParameter");
3111 dn.set_attr(
"classPath",
"jmt.engine.random.Exponential");
3112 dn.set_attr(
"name",
"Exponential");
3113 xml::Element& par = rn.add_child(
"subParameter");
3114 par.set_attr(
"classPath",
"jmt.engine.random.ExponentialPar");
3115 par.set_attr(
"name",
"distrPar");
3116 jmt_scalar(par,
"java.lang.Double",
"lambda",
"1.0");
3119 jmt_scalar(balking,
"java.lang.Boolean",
"priorityActivated",
"false");
3129 void save_retrial_distributions(xml::Element& section, std::size_t ind) {
3130 const std::size_t ist = sn_.nodes[ind - 1].station;
3131 const auto it = ist == 0 ? sn_.retrialparam.end() : sn_.retrialparam.find(ist);
3132 xml::Element& p =
jmt_param(section,
"jmt.engine.NetStrategies.ServiceStrategy",
3133 "retrialDistributions",
true);
3134 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
3135 if (!keep_[r - 1])
continue;
3137 xml::Element& sts = p.add_child(
"subParameter");
3138 sts.set_attr(
"classPath",
3139 "jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy");
3140 sts.set_attr(
"name",
"ServiceTimeStrategy");
3141 const bool has = it != sn_.retrialparam.end() &&
3142 r <= it->second.retrial_proc.size() &&
3143 !it->second.retrial_proc[r - 1].disabled;
3145 xml::Element& dn = sts.add_child(
"subParameter");
3146 dn.set_attr(
"classPath",
"jmt.engine.random.Exponential");
3147 dn.set_attr(
"name",
"Exponential");
3148 xml::Element& par = sts.add_child(
"subParameter");
3149 par.set_attr(
"classPath",
"jmt.engine.random.ExponentialPar");
3150 par.set_attr(
"name",
"distrPar");
3151 jmt_scalar(par,
"java.lang.Double",
"lambda",
"1.000000000000");
3155 "SolverJMT (retrial delay)");
3169 void save_switchover_strategy(xml::Element& section, std::size_t ind) {
3170 const std::size_t ist = sn_.nodes[ind - 1].station;
3171 const typename qn::NetworkStruct<T>::PollingParam pp = sn_.effective_polling(ist);
3172 xml::Element& p =
jmt_param(section,
"jmt.engine.NetStrategies.ServiceStrategy",
3173 "SwitchoverStrategy",
true);
3174 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
3176 xml::Element& sts = p.add_child(
"subParameter");
3177 sts.set_attr(
"classPath",
3178 "jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy");
3179 sts.set_attr(
"name",
"ServiceTimeStrategy");
3182 const std::size_t rp = (r + sn_.nclasses - 2) % sn_.nclasses + 1;
3183 const lang::Distrib<T>& so =
3184 rp <= pp.switchover.size() ? pp.switchover[rp - 1] : empty_dist_;
3193 sts.set_attr(
"classPath",
3194 "jmt.engine.NetStrategies.ServiceStrategies.ZeroServiceTimeStrategy");
3195 sts.set_attr(
"name",
"ZeroServiceTimeStrategy");
3202 std::map<std::size_t, double> empty_weights_;
3203 lang::Distrib<T> empty_dist_;