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;
790 void save_buffer_capacity(xml::Element& section, std::size_t ind) {
791 const std::size_t ist = sn_.nodes[ind - 1].station;
792 std::string v =
"-1";
793 if (ist != 0 && std::isfinite(sn_.cap[ist - 1])) {
794 const double total = reachable_population(ist);
795 const double ns = sn_.stations[ist - 1].nservers;
796 if (sn_.cap[ist - 1] < total && std::isfinite(ns)) {
797 assert_station_cap_exportable(ist);
824 double reachable_population(std::size_t ist)
const {
826 for (std::size_t c = 0; c < sn_.nchains; ++c) {
828 double chain_jobs = 0.0;
829 for (std::size_t r : sn_.inchain[c]) {
830 if (!sn_.disabled[ist - 1][r - 1]) served =
true;
831 chain_jobs += sn_.classes[r - 1].population;
833 if (served) n += chain_jobs;
851 bool is_bas_destination(std::size_t ist, std::size_t r)
const {
852 if (ist == 0 || sn_.isbasdestination.size() < ist)
return false;
853 if (sn_.isbasdestination[ist - 1].size() < r)
return false;
854 return sn_.isbasdestination[ist - 1][r - 1];
874 const char* drop_strategy_text(std::size_t ist, std::size_t r)
const {
875 if (ist == 0)
return "drop";
877 if (d == DropStrategy::WAITQ && is_bas_destination(ist, r))
924 void assert_station_cap_exportable(std::size_t ist)
const {
925 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
926 if (sn_.disabled[ist - 1][r - 1])
continue;
931 "SolverJMT: station '" + nname(sn_.station_to_node[ist - 1]) +
932 "' applies drop strategy '" +
jmt_drop_text(dr) +
"' to class '" + cname(r) +
933 "' and carries a finite capacity " +
jmt_int(sn_.cap[ist - 1]) +
934 " it can reach. JMT's queue section reads only 'drop', 'waiting queue', "
935 "'BAS blocking' and 'retrial'; it does not approximate anything else, it "
936 "ignores it, so the capacity would stop being enforced and the run would "
937 "return the unconstrained answer. Use SolverCTMC, SolverSSA or SolverLDES");
938 if (!std::isfinite(sn_.classes[r - 1].population))
continue;
939 if (dr != DropStrategy::WAITQ)
941 if (is_bas_destination(ist, r))
945 "SolverJMT: station '" + nname(sn_.station_to_node[ist - 1]) +
946 "' carries a finite capacity " +
jmt_int(sn_.cap[ist - 1]) +
947 " that binds for the closed class '" + cname(r) +
948 "'. LINE blocks a closed job that finds no room -- the upstream departure is "
949 "disabled and the job stays where it is -- and no JMT drop strategy reproduces "
950 "that: 'waiting queue' does not enforce the size at all, and 'BAS blocking' "
951 "completes the service before blocking, which is a different queueing model. "
952 "Use SolverCTMC, SolverSSA or SolverLDES, or declare DropStrategy.BAS if "
953 "blocking after service is the model you want, which SolverJMT does export");
958 void save_drop_strategy(xml::Element& section, std::size_t ind) {
959 const std::size_t ist = sn_.nodes[ind - 1].station;
960 xml::Element& p =
jmt_param(section,
"java.lang.String",
"dropStrategies",
true);
961 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
962 if (!keep_[r - 1])
continue;
964 const char* txt = drop_strategy_text(ist, r);
965 xml::Element& sp = p.add_child(
"subParameter");
966 sp.set_attr(
"classPath",
"java.lang.String");
967 sp.set_attr(
"name",
"dropStrategy");
968 sp.add_text_child(
"value", txt);
976 void save_get_strategy(xml::Element& section, std::size_t ind) {
977 const std::size_t ist = sn_.nodes[ind - 1].station;
978 const bool polling = sn_.nodes[ind - 1].nodetype == NodeType::Queue && ist != 0 &&
979 sn_.stations[ist - 1].sched == SchedStrategy::POLLING;
981 xml::Element& p = section.add_child(
"parameter");
982 p.set_attr(
"classPath",
"jmt.engine.NetStrategies.QueueGetStrategies.FCFSstrategy");
983 p.set_attr(
"name",
"FCFSstrategy");
986 const typename qn::NetworkStruct<T>::PollingParam pp = sn_.effective_polling(ist);
987 xml::Element& p = section.add_child(
"parameter");
989 case PollingType::GATED:
990 p.set_attr(
"classPath",
991 "jmt.engine.NetStrategies.QueueGetStrategies.GatedPollingGetStrategy");
993 case PollingType::EXHAUSTIVE:
996 "jmt.engine.NetStrategies.QueueGetStrategies.ExhaustivePollingGetStrategy");
998 case PollingType::KLIMITED: {
999 p.set_attr(
"classPath",
1000 "jmt.engine.NetStrategies.QueueGetStrategies.LimitedPollingGetStrategy");
1001 xml::Element& k = p.add_child(
"subParameter");
1002 k.set_attr(
"classPath",
"java.lang.Integer");
1003 k.set_attr(
"name",
"pollingKValue");
1004 k.add_text_child(
"value",
jmt_int(
static_cast<double>(pp.pk)));
1007 case PollingType::DECREMENTING:
1009 "SolverJMT: JMT does not support the decrementing (semiexhaustive) polling "
1010 "discipline; use the LDES solver");
1012 p.set_attr(
"name",
"FCFSstrategy");
1024 void save_put_strategy(xml::Element& section, std::size_t ind) {
1025 const std::size_t ist = sn_.nodes[ind - 1].station;
1027 jmt_param(section,
"jmt.engine.NetStrategies.QueuePutStrategy",
"QueuePutStrategy",
true);
1028 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1029 if (!keep_[r - 1])
continue;
1031 const char* nm =
"TailStrategy";
1033 switch (sn_.stations[ist - 1].sched) {
1034 case SchedStrategy::SIRO: nm =
"RandStrategy";
break;
1035 case SchedStrategy::LJF: nm =
"LJFStrategy";
break;
1036 case SchedStrategy::SJF: nm =
"SJFStrategy";
break;
1037 case SchedStrategy::LEPT: nm =
"LEPTStrategy";
break;
1038 case SchedStrategy::SEPT: nm =
"SEPTStrategy";
break;
1039 case SchedStrategy::LCFS: nm =
"HeadStrategy";
break;
1040 case SchedStrategy::LCFSPRIO: nm =
"HeadStrategyPriority";
break;
1041 case SchedStrategy::LCFSPR: nm =
"LCFSPRStrategy";
break;
1042 case SchedStrategy::LCFSPI: nm =
"LCFSPIStrategy";
break;
1043 case SchedStrategy::LCFSPRPRIO: nm =
"LCFSPRStrategyPriority";
break;
1044 case SchedStrategy::LCFSPIPRIO: nm =
"LCFSPIStrategyPriority";
break;
1045 case SchedStrategy::FCFSPR: nm =
"FCFSPRStrategy";
break;
1046 case SchedStrategy::FCFSPI: nm =
"FCFSPIStrategy";
break;
1047 case SchedStrategy::FCFSPRPRIO: nm =
"FCFSPRStrategyPriority";
break;
1048 case SchedStrategy::FCFSPIPRIO: nm =
"FCFSPIStrategyPriority";
break;
1049 case SchedStrategy::HOL: nm =
"TailStrategyPriority";
break;
1050 case SchedStrategy::EDD: nm =
"EDDStrategy";
break;
1051 case SchedStrategy::EDF: nm =
"EDFStrategy";
break;
1052 case SchedStrategy::SRPT: nm =
"SRPTStrategy";
break;
1053 case SchedStrategy::SRPTPRIO: nm =
"SRPTStrategyPriority";
break;
1054 default: nm =
"TailStrategy";
break;
1057 xml::Element& sp = p.add_child(
"subParameter");
1058 sp.set_attr(
"classPath",
1059 std::string(
"jmt.engine.NetStrategies.QueuePutStrategies.") + nm);
1060 sp.set_attr(
"name", nm);
1076 void save_number_of_servers(xml::Element& section, std::size_t ind) {
1077 const std::size_t ist = sn_.nodes[ind - 1].station;
1078 double maxjobs = 1.0;
1080 if (sn_.stations[ist - 1].sched == SchedStrategy::LPS) {
1083 maxjobs = sn_.stations[ist - 1].nservers;
1084 for (
const T& s : sn_.stations[ist - 1].lldscaling)
1085 maxjobs = std::max(maxjobs, d(s));
1092 void save_server_visits(xml::Element& section) {
1093 xml::Element& p =
jmt_param(section,
"java.lang.Integer",
"numberOfVisits",
true);
1094 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1095 if (!keep_[r - 1])
continue;
1097 xml::Element& sp = p.add_child(
"subParameter");
1098 sp.set_attr(
"classPath",
"java.lang.Integer");
1099 sp.set_attr(
"name",
"numberOfVisits");
1100 sp.add_text_child(
"value",
"1");
1115 struct ServerPools {
1116 bool present =
false;
1117 std::vector<std::string> names;
1118 std::vector<double> counts;
1119 std::vector<std::vector<bool>> compat;
1121 std::vector<std::size_t> parallelism;
1124 ServerPools server_pools(std::size_t ind)
const {
1126 const std::size_t ist = sn_.nodes[ind - 1].station;
1127 if (ist == 0)
return pools;
1128 const auto& st = sn_.stations[ist - 1];
1129 const bool has_types = !st.server_types.empty();
1130 bool has_parallelism =
false;
1131 for (std::size_t n : st.server_parallelism) {
1132 if (n > 1) { has_parallelism =
true;
break; }
1134 if (!has_types && !has_parallelism)
return pools;
1136 pools.present =
true;
1137 pools.parallelism.assign(sn_.nclasses, 1);
1138 for (std::size_t r = 0; r < sn_.nclasses && r < st.server_parallelism.size(); ++r) {
1139 pools.parallelism[r] = st.server_parallelism[r] < 1 ? 1 : st.server_parallelism[r];
1142 pools.policy = st.hetero_policy;
1143 for (
const auto& pool : st.server_types) {
1144 pools.names.push_back(pool.name);
1145 pools.counts.push_back(pool.count);
1146 std::vector<bool> row(sn_.nclasses,
true);
1147 for (std::size_t r = 0; r < sn_.nclasses; ++r) {
1150 row[r] = pool.compatible.empty()
1152 : (r < pool.compatible.size() ? pool.compatible[r] :
false);
1154 pools.compat.push_back(row);
1157 pools.names.push_back(sn_.nodes[ind - 1].name +
" - Server Type 1");
1158 pools.counts.push_back(st.nservers);
1159 pools.compat.push_back(std::vector<bool>(sn_.nclasses,
true));
1165 void save_class_parallelism(xml::Element& section, std::size_t ind) {
1166 const ServerPools pools = server_pools(ind);
1167 if (!pools.present)
return;
1168 xml::Element& p =
jmt_param(section,
"java.lang.Integer",
"classParallelism",
true);
1169 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1170 if (!keep_[r - 1])
continue;
1172 xml::Element& sp = p.add_child(
"subParameter");
1173 sp.set_attr(
"classPath",
"java.lang.Integer");
1174 sp.set_attr(
"name",
"serverParallelism");
1175 sp.add_text_child(
"value",
jmt_int(
static_cast<double>(pools.parallelism[r - 1])));
1180 void save_server_type_names(xml::Element& section, std::size_t ind) {
1181 const ServerPools pools = server_pools(ind);
1182 if (!pools.present)
return;
1183 xml::Element& p =
jmt_param(section,
"java.lang.String",
"serverNames",
true);
1184 for (
const std::string& name : pools.names) {
1185 xml::Element& sp = p.add_child(
"subParameter");
1186 sp.set_attr(
"classPath",
"java.lang.String");
1187 sp.set_attr(
"name",
"serverTypesNames");
1188 sp.add_text_child(
"value", name);
1193 void save_servers_per_type(xml::Element& section, std::size_t ind) {
1194 const ServerPools pools = server_pools(ind);
1195 if (!pools.present)
return;
1196 xml::Element& p =
jmt_param(section,
"java.lang.Integer",
"serversPerServerType",
true);
1197 for (
double count : pools.counts) {
1198 xml::Element& sp = p.add_child(
"subParameter");
1199 sp.set_attr(
"classPath",
"java.lang.Integer");
1200 sp.set_attr(
"name",
"serverTypesNumOfServers");
1201 sp.add_text_child(
"value",
jmt_int(count));
1206 void save_server_compatibilities(xml::Element& section, std::size_t ind) {
1207 const ServerPools pools = server_pools(ind);
1208 if (!pools.present)
return;
1209 xml::Element& p =
jmt_param(section,
"java.lang.Object",
"serverCompatibilities",
true);
1210 for (
const std::vector<bool>& row : pools.compat) {
1211 xml::Element& tn = p.add_child(
"subParameter");
1212 tn.set_attr(
"array",
"true");
1213 tn.set_attr(
"classPath",
"java.lang.Boolean");
1214 tn.set_attr(
"name",
"serverTypesCompatibilities");
1215 for (std::size_t r = 0; r < sn_.nclasses; ++r) {
1216 xml::Element& cn = tn.add_child(
"subParameter");
1217 cn.set_attr(
"classPath",
"java.lang.Boolean");
1218 cn.set_attr(
"name",
"compatibilities");
1219 cn.add_text_child(
"value", row[r] ?
"true" :
"false");
1225 void save_hetero_sched_policy(xml::Element& section, std::size_t ind) {
1226 const ServerPools pools = server_pools(ind);
1227 if (!pools.present)
return;
1241 void warn_hetero_rates(std::size_t ind)
const {
1242 const std::size_t ist = sn_.nodes[ind - 1].station;
1243 if (ist == 0)
return;
1244 const auto& pools = sn_.stations[ist - 1].server_types;
1245 if (pools.size() < 2)
return;
1246 bool first_set =
false, distinct =
false;
1248 for (
const auto& pool : pools) {
1249 for (
const lang::Distrib<T>& sd : pool.service) {
1250 if (sd.disabled)
continue;
1251 const double mean = num_traits<T>::to_double(sd.mean);
1252 if (!(mean > 0))
continue;
1253 if (!first_set) { first = mean; first_set =
true; }
1254 else if (std::abs(mean - first) > 1e-12) { distinct =
true; }
1257 if (!distinct)
return;
1258 std::cerr <<
"[LINE] Warning: JMT keys service strategies by job class, so the "
1259 <<
"per-server-type service rates of station '" << sn_.nodes[ind - 1].name
1260 <<
"' cannot be exported; every pool will serve at the class service rate. "
1261 <<
"Use the LDES or CTMC solver for per-type rates." << std::endl;
1271 void save_service_strategy(xml::Element& section, std::size_t ind) {
1272 const std::size_t ist = sn_.nodes[ind - 1].station;
1273 xml::Element& p =
jmt_param(section,
"jmt.engine.NetStrategies.ServiceStrategy",
1274 "ServiceStrategy",
true);
1275 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1276 if (!keep_[r - 1])
continue;
1278 xml::Element& sts = p.add_child(
"subParameter");
1279 const JmtDistView<T> v =
1280 ist == 0 ? JmtDistView<T>() :
jmt_dist_view(sn_.service[ist - 1][r - 1]);
1282 sts.set_attr(
"classPath",
1283 "jmt.engine.NetStrategies.ServiceStrategies."
1284 "DisabledServiceTimeStrategy");
1285 sts.set_attr(
"name",
"DisabledServiceTimeStrategy");
1289 sts.set_attr(
"classPath",
1290 "jmt.engine.NetStrategies.ServiceStrategies.ZeroServiceTimeStrategy");
1291 sts.set_attr(
"name",
"ZeroServiceTimeStrategy");
1294 sts.set_attr(
"classPath",
1295 "jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy");
1296 sts.set_attr(
"name",
"ServiceTimeStrategy");
1309 void save_arrival_strategy(xml::Element& section, std::size_t ind) {
1310 const std::size_t ist = sn_.nodes[ind - 1].station;
1311 xml::Element& p =
jmt_param(section,
"jmt.engine.NetStrategies.ServiceStrategy",
1312 "ServiceStrategy",
true);
1313 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1314 if (!keep_[r - 1])
continue;
1316 xml::Element& sts = p.add_child(
"subParameter");
1317 sts.set_attr(
"classPath",
1318 "jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy");
1319 sts.set_attr(
"name",
"ServiceTimeStrategy");
1320 const bool closed = std::isfinite(sn_.classes[r - 1].population);
1321 const JmtDistView<T> v =
1322 ist == 0 ? JmtDistView<T>() :
jmt_dist_view(sn_.service[ist - 1][r - 1]);
1324 sts.add_text_child(
"value",
"null");
1328 sts.set_attr(
"classPath",
1329 "jmt.engine.NetStrategies.ServiceStrategies.ZeroServiceTimeStrategy");
1330 sts.set_attr(
"name",
"ZeroServiceTimeStrategy");
1338 void save_preemptive_strategy(xml::Element& section, std::size_t ind) {
1339 const std::size_t ist = sn_.nodes[ind - 1].station;
1340 xml::Element& p =
jmt_param(section,
"jmt.engine.NetStrategies.PSStrategy",
"PSStrategy",
1342 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1343 if (!keep_[r - 1])
continue;
1345 const char* nm =
nullptr;
1346 if (ist != 0)
switch (sn_.stations[ist - 1].sched) {
1349 case SchedStrategy::PS:
1350 case SchedStrategy::LPS: nm =
"EPSStrategy";
break;
1351 case SchedStrategy::DPS: nm =
"DPSStrategy";
break;
1352 case SchedStrategy::GPS: nm =
"GPSStrategy";
break;
1353 case SchedStrategy::PSPRIO: nm =
"EPSStrategyPriority";
break;
1354 case SchedStrategy::DPSPRIO: nm =
"DPSStrategyPriority";
break;
1355 case SchedStrategy::GPSPRIO: nm =
"GPSStrategyPriority";
break;
1356 default: nm =
nullptr;
break;
1358 xml::Element& sp = p.add_child(
"subParameter");
1359 if (nm !=
nullptr) {
1360 sp.set_attr(
"classPath", std::string(
"jmt.engine.NetStrategies.PSStrategies.") + nm);
1361 sp.set_attr(
"name", nm);
1367 void save_preemptive_weights(xml::Element& section, std::size_t ind) {
1368 const std::size_t ist = sn_.nodes[ind - 1].station;
1369 xml::Element& p =
jmt_param(section,
"java.lang.Double",
"serviceWeights",
true);
1370 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1371 if (!keep_[r - 1])
continue;
1374 if (ist != 0 && sn_.stations[ist - 1].schedparam.size() >= r)
1375 w = d(sn_.stations[ist - 1].schedparam[r - 1]);
1376 xml::Element& sp = p.add_child(
"subParameter");
1377 sp.set_attr(
"classPath",
"java.lang.Double");
1378 sp.set_attr(
"name",
"serviceWeight");
1379 sp.add_text_child(
"value",
jmt_num(w));
1393 void save_delay_off_strategy(xml::Element& section, std::size_t ind) {
1394 const std::size_t ist = sn_.nodes[ind - 1].station;
1395 if (ist == 0)
return;
1396 const auto it = sn_.setupparam.find(ist);
1397 if (it == sn_.setupparam.end())
return;
1398 const qn::SetupDelayOffParam<T>& sp = it->second;
1400 for (
const lang::Distrib<T>& s : sp.setup)
1401 if (!s.disabled) any =
true;
1404 const char* names[2] = {
"delayOffTime",
"setUpTime"};
1405 for (
int which = 0; which < 2; ++which) {
1406 const std::vector<lang::Distrib<T>>& tab = which == 0 ? sp.delayoff : sp.setup;
1407 xml::Element& p =
jmt_param(section,
"java.lang.Object", names[which],
true);
1408 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1410 xml::Element& row = p.add_child(
"subParameter");
1411 row.set_attr(
"array",
"true");
1412 row.set_attr(
"classPath",
"jmt.engine.NetStrategies.ServiceStrategy");
1413 row.set_attr(
"name", names[which]);
1414 xml::Element& sts = row.add_child(
"subParameter");
1415 sts.set_attr(
"classPath",
1416 "jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy");
1417 sts.set_attr(
"name",
"ServiceTimeStrategy");
1418 if (r <= tab.size() && !tab[r - 1].disabled)
1419 append_simple_distribution(sts, tab[r - 1]);
1421 append_zero_time(sts);
1427 void append_zero_time(xml::Element& parent) {
1428 xml::Element& dn = parent.add_child(
"subParameter");
1429 dn.set_attr(
"classPath",
"jmt.engine.random.DeterministicDistr");
1430 dn.set_attr(
"name",
"Deterministic");
1431 xml::Element& par = parent.add_child(
"subParameter");
1432 par.set_attr(
"classPath",
"jmt.engine.random.DeterministicDistrPar");
1433 par.set_attr(
"name",
"distrPar");
1434 jmt_scalar(par,
"java.lang.Double",
"t",
"0.0");
1447 void append_simple_distribution(xml::Element& parent,
const lang::Distrib<T>& dist) {
1449 append_zero_time(parent);
1452 const double mean = d(dist.mean);
1454 xml::Element& dn = parent.add_child(
"subParameter");
1455 dn.set_attr(
"classPath",
"jmt.engine.random.Exponential");
1456 dn.set_attr(
"name",
"Exponential");
1457 xml::Element& par = parent.add_child(
"subParameter");
1458 par.set_attr(
"classPath",
"jmt.engine.random.ExponentialPar");
1459 par.set_attr(
"name",
"distrPar");
1460 jmt_double(par,
"lambda", mean > 0.0 ? 1.0 / mean : 0.0);
1464 const std::size_t ph = dist.phases();
1465 xml::Element& dn = parent.add_child(
"subParameter");
1466 dn.set_attr(
"classPath",
"jmt.engine.random.Erlang");
1467 dn.set_attr(
"name",
"Erlang");
1468 xml::Element& par = parent.add_child(
"subParameter");
1469 par.set_attr(
"classPath",
"jmt.engine.random.ErlangPar");
1470 par.set_attr(
"name",
"distrPar");
1471 jmt_double(par,
"alpha", mean > 0.0 ?
static_cast<double>(ph) / mean : 0.0);
1475 xml::Element& dn = parent.add_child(
"subParameter");
1476 dn.set_attr(
"classPath",
"jmt.engine.random.DeterministicDistr");
1477 dn.set_attr(
"name",
"Deterministic");
1478 xml::Element& par = parent.add_child(
"subParameter");
1479 par.set_attr(
"classPath",
"jmt.engine.random.DeterministicDistrPar");
1480 par.set_attr(
"name",
"distrPar");
1494 void save_routing_strategy(xml::Element& section, std::size_t ind) {
1495 const std::size_t K = sn_.nclasses;
1496 const bool is_cs = sn_.nodes[ind - 1].nodetype == NodeType::ClassSwitch;
1498 jmt_param(section,
"jmt.engine.NetStrategies.RoutingStrategy",
"RoutingStrategy",
true);
1499 for (std::size_t r = 1; r <= K; ++r) {
1500 if (!keep_[r - 1])
continue;
1503 is_cs ? RoutingStrategy::RAND
1504 : (sn_.nodes[ind - 1].routing.size() >= r ? sn_.nodes[ind - 1].routing[r - 1]
1505 : RoutingStrategy::PROB);
1506 xml::Element& sp = p.add_child(
"subParameter");
1508 case RoutingStrategy::RAND:
1509 sp.set_attr(
"classPath",
1510 "jmt.engine.NetStrategies.RoutingStrategies.RandomStrategy");
1511 sp.set_attr(
"name",
"Random");
1513 case RoutingStrategy::RROBIN:
1514 sp.set_attr(
"classPath",
1515 "jmt.engine.NetStrategies.RoutingStrategies.RoundRobinStrategy");
1516 sp.set_attr(
"name",
"Round Robin");
1518 case RoutingStrategy::JSQ:
1519 sp.set_attr(
"classPath",
1520 "jmt.engine.NetStrategies.RoutingStrategies."
1521 "ShortestQueueLengthRoutingStrategy");
1522 sp.set_attr(
"name",
"Join the Shortest Queue (JSQ)");
1524 case RoutingStrategy::SQ: {
1525 sp.set_attr(
"classPath",
1526 "jmt.engine.NetStrategies.RoutingStrategies.PowerOfKRoutingStrategy");
1527 sp.set_attr(
"name",
"Power of k");
1528 const int dpar = sn_.nodes[ind - 1].routing_param.size() >= r
1529 ? sn_.nodes[ind - 1].routing_param[r - 1]
1535 jmt_scalar(sp,
"java.lang.Boolean",
"withMemory",
"false");
1538 case RoutingStrategy::WRROBIN: {
1539 sp.set_attr(
"classPath",
1540 "jmt.engine.NetStrategies.RoutingStrategies."
1541 "WeightedRoundRobinStrategy");
1542 sp.set_attr(
"name",
"Weighted Round Robin");
1544 jmt_param_sub(sp,
"jmt.engine.NetStrategies.RoutingStrategies.WeightEntry",
1545 "WeightEntryArray");
1546 const std::map<std::size_t, double>& w =
1547 sn_.nodes[ind - 1].routing_weights.size() >= r
1548 ? sn_.nodes[ind - 1].routing_weights[r - 1]
1550 for (std::size_t j : outputs_of(ind)) {
1551 const auto wi = w.find(j);
1552 xml::Element& e = arr.add_child(
"subParameter");
1553 e.set_attr(
"classPath",
1554 "jmt.engine.NetStrategies.RoutingStrategies.WeightEntry");
1555 e.set_attr(
"name",
"WeightEntry");
1556 jmt_scalar(e,
"java.lang.String",
"stationName", nname(j));
1558 jmt_int(wi == w.end() ? 0.0 : wi->second));
1562 case RoutingStrategy::PROB: {
1563 sp.set_attr(
"classPath",
1564 "jmt.engine.NetStrategies.RoutingStrategies.EmpiricalStrategy");
1565 sp.set_attr(
"name",
"Probabilities");
1566 xml::Element& arr = jmt_param_sub(sp,
"jmt.engine.random.EmpiricalEntry",
1567 "EmpiricalEntryArray");
1568 for (std::size_t j : outputs_of(ind)) {
1569 const double pr = route_node(ind, r, j, r);
1570 if (!(pr > 0.0))
continue;
1571 xml::Element& e = arr.add_child(
"subParameter");
1572 e.set_attr(
"classPath",
"jmt.engine.random.EmpiricalEntry");
1573 e.set_attr(
"name",
"EmpiricalEntry");
1574 jmt_scalar(e,
"java.lang.String",
"stationName", nname(j));
1582 "jmt.engine.NetStrategies.RoutingStrategies.DisabledRoutingStrategy");
1583 sp.set_attr(
"name",
"Random");
1590 static xml::Element& jmt_param_sub(xml::Element& parent,
const char* cp,
const char* name) {
1591 xml::Element& e = parent.add_child(
"subParameter");
1592 e.set_attr(
"array",
"true");
1593 e.set_attr(
"classPath", cp);
1594 e.set_attr(
"name", name);
1599 double route_node(std::size_t i, std::size_t r, std::size_t j, std::size_t s)
const {
1600 const std::size_t K = sn_.nclasses;
1601 const std::size_t a = (i - 1) * K + (r - 1), b = (j - 1) * K + (s - 1);
1602 if (sn_.rtnodes.rows() <= a || sn_.rtnodes.cols() <= b)
return 0.0;
1603 return d(sn_.rtnodes(a, b));
1615 void save_class_switch_strategy(xml::Element& section, std::size_t ind) {
1616 const std::size_t K = sn_.nclasses;
1617 const std::vector<std::size_t> jset = outputs_of(ind);
1618 xml::Element& p =
jmt_param(section,
"java.lang.Object",
"matrix",
true);
1619 for (std::size_t r = 1; r <= K; ++r) {
1620 if (!keep_[r - 1])
continue;
1622 xml::Element& row = p.add_child(
"subParameter");
1623 row.set_attr(
"array",
"true");
1624 row.set_attr(
"classPath",
"java.lang.Float");
1625 row.set_attr(
"name",
"row");
1626 for (std::size_t s = 1; s <= K; ++s) {
1627 if (!keep_[s - 1])
continue;
1630 for (std::size_t j : jset) acc += route_node(ind, r, j, s);
1631 xml::Element& cell = row.add_child(
"subParameter");
1632 cell.set_attr(
"classPath",
"java.lang.Float");
1633 cell.set_attr(
"name",
"cell");
1634 cell.add_text_child(
"value",
jmt_fmt(acc));
1650 void save_fork_strategy(xml::Element& section, std::size_t ind) {
1651 const qn::ForkParam<T>* fk = sn_.fork_param_of(ind);
1652 const double fan_out = sn_.nodes[ind - 1].tasks_per_link;
1660 bool simplified =
true;
1662 for (std::size_t k = 0; k < fk->fan_out_link.rows() && simplified; ++k)
1663 for (std::size_t r = 0; r < fk->fan_out_link.cols() && simplified; ++r) {
1664 const double p = num_traits<T>::to_double(fk->fan_out_prob(k, r));
1665 if (p == 0.0)
continue;
1666 if (p != 1.0) simplified =
false;
1667 if (num_traits<T>::to_double(fk->fan_out_link(k, r)) != fan_out)
1669 if (!fk->fan_out_dist[k][r].disabled) simplified =
false;
1673 simplified ?
"true" :
"false");
1676 jmt_param(section,
"jmt.engine.NetStrategies.ForkStrategy",
"ForkStrategy",
true);
1677 const std::vector<std::size_t> outs = outputs_of(ind);
1678 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1679 if (!keep_[r - 1])
continue;
1681 xml::Element& cs = p.add_child(
"subParameter");
1682 cs.set_attr(
"classPath",
"jmt.engine.NetStrategies.ForkStrategies.ProbabilitiesFork");
1683 cs.set_attr(
"name",
"Branch Probabilities");
1684 xml::Element& arr = jmt_param_sub(
1685 cs,
"jmt.engine.NetStrategies.ForkStrategies.OutPath",
"EmpiricalEntryArray");
1687 ? sn_.nodes[ind - 1].routing[r - 1]
1688 : RoutingStrategy::PROB;
1689 if (rs != RoutingStrategy::PROB || outs.empty())
continue;
1690 for (std::size_t oi = 0; oi < outs.size(); ++oi) {
1691 xml::Element& entry = arr.add_child(
"subParameter");
1692 entry.set_attr(
"classPath",
"jmt.engine.NetStrategies.ForkStrategies.OutPath");
1693 entry.set_attr(
"name",
"OutPathEntry");
1694 xml::Element& unit = entry.add_child(
"subParameter");
1695 unit.set_attr(
"classPath",
"jmt.engine.random.EmpiricalEntry");
1696 unit.set_attr(
"name",
"outUnitProbability");
1697 jmt_scalar(unit,
"java.lang.String",
"stationName", nname(outs[oi]));
1700 const std::size_t k0 = outs[oi] - 1, r0 = r - 1;
1701 const bool has_fan = fk != 0;
1702 const double branch_p =
1703 has_fan ? num_traits<T>::to_double(fk->fan_out_prob(k0, r0)) : 1.0;
1709 std::vector<double> pts, prs;
1710 if (has_fan && !fk->fan_out_dist[k0][r0].disabled) {
1711 const lang::Distrib<T>& d = fk->fan_out_dist[k0][r0];
1713 for (std::size_t e = 0; e < d.params.size(); ++e)
1714 tot += num_traits<T>::to_double(d.params[e]);
1715 for (std::size_t e = 0; e < d.params.size(); ++e) {
1716 pts.push_back(d.trace.empty()
1717 ?
static_cast<double>(e + 1)
1718 : num_traits<T>::to_double(d.trace[e]));
1719 prs.push_back(num_traits<T>::to_double(d.params[e]) / tot);
1721 }
else if (has_fan) {
1722 pts.push_back(num_traits<T>::to_double(fk->fan_out_link(k0, r0)));
1725 pts.push_back(fan_out);
1730 jmt_param_sub(entry,
"jmt.engine.random.EmpiricalEntry",
"JobsPerLinkDis");
1731 for (std::size_t e = 0; e < pts.size(); ++e) {
1732 xml::Element& jple = jpl.add_child(
"subParameter");
1733 jple.set_attr(
"classPath",
"jmt.engine.random.EmpiricalEntry");
1734 jple.set_attr(
"name",
"EmpiricalEntry");
1750 void save_join_strategy(xml::Element& section, std::size_t ind) {
1751 const auto it = sn_.joindecl.find(ind);
1752 const bool partial =
1753 it != sn_.joindecl.end() && it->second.strategy == JoinStrategy::PARTIAL;
1754 const double quorum = it != sn_.joindecl.end() ? it->second.quorum : 0.0;
1756 jmt_param(section,
"jmt.engine.NetStrategies.JoinStrategy",
"JoinStrategy",
true);
1757 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1759 xml::Element& sp = p.add_child(
"subParameter");
1761 sp.set_attr(
"classPath",
"jmt.engine.NetStrategies.JoinStrategies.PartialJoin");
1762 sp.set_attr(
"name",
"Quorum");
1764 sp.set_attr(
"classPath",
"jmt.engine.NetStrategies.JoinStrategies.NormalJoin");
1765 sp.set_attr(
"name",
"Standard Join");
1767 const double req = partial ? quorum : (quorum > 0.0 ? quorum : -1.0);
1773 void save_log_tunnel(xml::Element& section, std::size_t ind) {
1774 const qn::NodeDef::LoggerParam& lg = sn_.nodes[ind - 1].logger;
1775 std::string path = lg.file_path.empty() ? sn_.log_path : lg.file_path;
1776 if (!path.empty() && path[path.size() - 1] !=
'/') path +=
'/';
1777 const char* bools[7] = {
"logExecTimestamp",
"logLoggerName",
"logTimeStamp",
1778 "logJobID",
"logJobClass",
"logTimeSameClass",
1780 const bool vals[7] = {lg.start_time, lg.logger_name, lg.timestamp, lg.job_id,
1781 lg.job_class, lg.time_same_class, lg.time_any_class};
1782 jmt_param_value(section,
"java.lang.String",
"logfileName", lg.file_name);
1784 for (
int j = 0; j < 7; ++j)
1785 jmt_param_value(section,
"java.lang.Boolean", bools[j], vals[j] ?
"true" :
"false");
1787 jmt_int(
static_cast<double>(sn_.nclasses)));
1800 void save_metrics(xml::Element& sim) {
1804 for (
int k = 0; k < 5; ++k) save_metric(sim, kinds[k]);
1807 save_fcr_metrics(sim);
1808 save_cache_hit_rate_metrics(sim);
1812 for (std::size_t ist = 1; ist <= sn_.nstations; ++ist)
1813 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1814 if (!keep_[r - 1])
continue;
1816 xml::Element& m = sim.add_child(
"measure");
1817 m.set_attr(
"alpha",
jmt_sig2(1.0 - opt_.sim_conf_int));
1818 m.set_attr(
"name", std::string(
"Performance_") +
1819 jmt_int(
static_cast<double>(ist)));
1820 m.set_attr(
"nodeType",
"station");
1821 m.set_attr(
"precision",
jmt_sig2(opt_.sim_max_rel_err));
1822 m.set_attr(
"referenceNode", nname(sn_.station_to_node[ist - 1]));
1823 m.set_attr(
"referenceUserClass", cname(r));
1825 m.set_attr(
"verbose",
"false");
1830 void save_cache_hit_rate_metrics(xml::Element& sim) {
1831 for (
const auto& kv : sn_.nodeparam) {
1832 const std::size_t ind = kv.first;
1833 if (sn_.nodes[ind - 1].nodetype != NodeType::Cache)
continue;
1834 const qn::CacheParam<T>& cp = kv.second;
1835 for (std::size_t r = 0; r < cp.hitclass.size(); ++r) {
1836 if (cp.hitclass[r] == 0)
continue;
1837 xml::Element& m = sim.add_child(
"measure");
1838 m.set_attr(
"alpha",
jmt_sig2(1.0 - opt_.sim_conf_int));
1839 m.set_attr(
"name",
"CacheHitRate_" + nname(ind) +
"_" + cname(cp.hitclass[r]));
1840 m.set_attr(
"nodeType",
"station");
1841 m.set_attr(
"precision",
jmt_sig2(opt_.sim_max_rel_err));
1842 m.set_attr(
"referenceNode", nname(ind));
1843 m.set_attr(
"referenceUserClass", cname(cp.hitclass[r]));
1844 m.set_attr(
"type",
"Cache Hit Rate");
1845 m.set_attr(
"verbose",
"false");
1851 void save_fcr_metrics(xml::Element& sim) {
1852 static const char* kinds[6] = {
"Number of Customers",
"Response Time",
"Residence Time",
1853 "Throughput",
"FCR Capacity",
"FCR Memory"};
1855 for (std::size_t f = 1; f <= sn_.regions.size(); ++f) {
1856 const std::string fcr =
"FCRegion" +
jmt_int(
static_cast<double>(f));
1857 for (
int k = 0; k < 6; ++k) {
1858 std::string flat(kinds[k]);
1859 flat.erase(std::remove(flat.begin(), flat.end(),
' '), flat.end());
1860 xml::Element& m = sim.add_child(
"measure");
1861 m.set_attr(
"alpha",
jmt_sig2(1.0 - opt_.sim_conf_int));
1862 m.set_attr(
"name",
"FCR_" + fcr +
"_" + flat +
"_" +
1863 jmt_int(
static_cast<double>(counter)));
1864 m.set_attr(
"nodeType",
"region");
1865 m.set_attr(
"precision",
jmt_sig2(opt_.sim_max_rel_err));
1866 m.set_attr(
"referenceNode", fcr);
1867 m.set_attr(
"referenceUserClass",
"");
1868 m.set_attr(
"type", kinds[k]);
1869 m.set_attr(
"verbose",
"false");
1876 void save_links(xml::Element& sim) {
1878 for (std::size_t j = 1; j <= sn_.nodes.size(); ++j)
1879 for (std::size_t i = 1; i <= sn_.nodes.size(); ++i) {
1880 if (!conn_[i - 1][j - 1])
continue;
1881 xml::Element& c = sim.add_child(
"connection");
1882 c.set_attr(
"source", nname(i));
1883 c.set_attr(
"target", nname(j));
1896 void save_preload(xml::Element& sim) {
1902 std::vector<std::pair<std::size_t, double>> pops;
1904 std::vector<Row> rows;
1905 for (std::size_t ist = 1; ist <= sn_.nstations; ++ist) {
1906 const std::size_t ind = sn_.station_to_node[ist - 1];
1907 const NodeType ty = sn_.nodes[ind - 1].nodetype;
1908 if (ty == NodeType::Source || ty == NodeType::Join)
continue;
1909 const std::vector<double> nir = initial_marginal(ist);
1912 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1913 const double n = sn_.classes[r - 1].population;
1914 if (std::isfinite(n) && n == 0.0 && nir[r - 1] == 0.0)
continue;
1915 row.pops.emplace_back(r, nir[r - 1]);
1917 if (!row.pops.empty()) rows.push_back(row);
1919 if (rows.empty())
return;
1920 xml::Element& preload = sim.add_child(
"preload");
1921 for (
const Row& row : rows) {
1922 xml::Element& st = preload.add_child(
"stationPopulations");
1923 st.set_attr(
"stationName", nname(row.node));
1924 for (
const auto& pc : row.pops) {
1925 xml::Element& cp = st.add_child(
"classPopulation");
1926 cp.set_attr(
"population",
jmt_int(pc.second));
1927 cp.set_attr(
"refClass", cname(pc.first));
1940 std::vector<double> initial_marginal(std::size_t ist)
const {
1941 std::vector<double> nir(sn_.nclasses, 0.0);
1942 const std::size_t ind = sn_.station_to_node[ist - 1];
1943 const auto im = sn_.initmarking.find(ind);
1944 if (im != sn_.initmarking.end()) {
1945 for (std::size_t r = 0; r < sn_.nclasses && r < im->second.size(); ++r)
1946 nir[r] = d(im->second[r]);
1949 for (std::size_t r = 0; r < sn_.nclasses; ++r) {
1950 const double n = sn_.classes[r].population;
1951 if (std::isfinite(n) && sn_.classes[r].refstat == ist) nir[r] = n;
1959 void save_total_capacity(xml::Element& section, std::size_t ind) {
1960 const std::size_t ist = sn_.nodes[ind - 1].station;
1961 const std::string v =
1962 (ist == 0 || !std::isfinite(sn_.cap[ist - 1])) ?
"-1" :
jmt_int(sn_.cap[ist - 1]);
1967 void save_place_capacities(xml::Element& section, std::size_t ind) {
1968 const std::size_t ist = sn_.nodes[ind - 1].station;
1969 xml::Element& p =
jmt_param(section,
"java.lang.Integer",
"capacities",
true);
1970 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1971 if (!keep_[r - 1])
continue;
1973 const double c = ist == 0 ? std::numeric_limits<double>::infinity()
1974 : sn_.classcap[ist - 1][r - 1];
1975 xml::Element& sp = p.add_child(
"subParameter");
1976 sp.set_attr(
"classPath",
"java.lang.Integer");
1977 sp.set_attr(
"name",
"capacity");
1978 sp.add_text_child(
"value", std::isfinite(c) ?
jmt_int(c) :
"-1");
1986 void save_drop_rule(xml::Element& section, std::size_t ind) {
1987 const std::size_t ist = sn_.nodes[ind - 1].station;
1988 xml::Element& p =
jmt_param(section,
"java.lang.String",
"dropRules",
true);
1989 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
1990 if (!keep_[r - 1])
continue;
1992 xml::Element& sp = p.add_child(
"subParameter");
1993 sp.set_attr(
"classPath",
"java.lang.String");
1994 sp.set_attr(
"name",
"dropRule");
1995 sp.add_text_child(
"value", drop_strategy_text(ist, r));
2004 void save_put_strategies(xml::Element& section, std::size_t ind) {
2005 const std::size_t ist = sn_.nodes[ind - 1].station;
2006 xml::Element& p =
jmt_param(section,
"jmt.engine.NetStrategies.QueuePutStrategy",
2007 "QueuePutStrategy",
true);
2008 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2009 if (!keep_[r - 1])
continue;
2011 const char* nm =
"TailStrategy";
2013 if (sn_.stations[ist - 1].sched == SchedStrategy::SIRO) nm =
"RandStrategy";
2014 else if (sn_.stations[ist - 1].sched == SchedStrategy::LCFS) nm =
"HeadStrategy";
2016 xml::Element& sp = p.add_child(
"subParameter");
2017 sp.set_attr(
"classPath",
2018 std::string(
"jmt.engine.NetStrategies.QueuePutStrategies.") + nm);
2019 sp.set_attr(
"name", nm);
2026 const qn::TransitionParam<T>& transparam(std::size_t ind)
const {
2027 const auto it = sn_.transparam.find(ind);
2028 if (it == sn_.transparam.end())
2029 throw InputError(
"SolverJMT: transition '" + nname(ind) +
"' carries no mode table");
2046 void save_enabling_conditions(xml::Element& section, std::size_t ind) {
2047 const qn::TransitionParam<T>& tp = transparam(ind);
2049 jmt_param(section,
"jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix",
2050 "enablingConditions",
true);
2051 for (std::size_t m = 0; m < tp.nmodes; ++m) {
2052 xml::Element& cond = p.add_child(
"subParameter");
2053 cond.set_attr(
"classPath",
2054 "jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix");
2055 cond.set_attr(
"name",
"enablingCondition");
2056 xml::Element& vecs = jmt_param_sub(
2057 cond,
"jmt.engine.NetStrategies.TransitionUtilities.TransitionVector",
2059 for (std::size_t k = 1; k <= sn_.nodes.size(); ++k) {
2060 bool relevant =
false;
2061 for (std::size_t r = 1; r <= sn_.nclasses && !relevant; ++r) {
2062 const double en = arc(tp.enabling, m, k, r);
2063 const double in = arc(tp.inhibiting, m, k, r);
2064 relevant = (std::isfinite(en) && en > 0.0) ||
2065 (std::isfinite(in) && in > 0.0);
2067 if (!relevant)
continue;
2068 xml::Element& vec = vecs.add_child(
"subParameter");
2069 vec.set_attr(
"classPath",
2070 "jmt.engine.NetStrategies.TransitionUtilities.TransitionVector");
2071 vec.set_attr(
"name",
"enablingVector");
2072 jmt_scalar(vec,
"java.lang.String",
"stationName", nname(k));
2073 xml::Element& entries = jmt_param_sub(vec,
"java.lang.Integer",
"enablingEntries");
2074 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2075 const double en = arc(tp.enabling, m, k, r);
2077 xml::Element& e = entries.add_child(
"subParameter");
2078 e.set_attr(
"classPath",
"java.lang.Integer");
2079 e.set_attr(
"name",
"enablingEntry");
2080 e.add_text_child(
"value", std::isfinite(en) ?
jmt_int(en) :
"-1");
2094 void save_inhibiting_conditions(xml::Element& section, std::size_t ind) {
2095 const qn::TransitionParam<T>& tp = transparam(ind);
2096 const std::vector<std::size_t> inputs = inputs_of(ind);
2098 jmt_param(section,
"jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix",
2099 "inhibitingConditions",
true);
2100 for (std::size_t m = 0; m < tp.nmodes; ++m) {
2101 xml::Element& cond = p.add_child(
"subParameter");
2102 cond.set_attr(
"classPath",
2103 "jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix");
2104 cond.set_attr(
"name",
"inhibitingCondition");
2105 xml::Element& vecs = jmt_param_sub(
2106 cond,
"jmt.engine.NetStrategies.TransitionUtilities.TransitionVector",
2107 "inhibitingVectors");
2108 for (std::size_t k : inputs) {
2109 xml::Element& vec = vecs.add_child(
"subParameter");
2110 vec.set_attr(
"classPath",
2111 "jmt.engine.NetStrategies.TransitionUtilities.TransitionVector");
2112 vec.set_attr(
"name",
"inhibitingVector");
2113 jmt_scalar(vec,
"java.lang.String",
"stationName", nname(k));
2114 xml::Element& entries = jmt_param_sub(vec,
"java.lang.Integer",
"inhibitingEntries");
2115 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2116 const double in = arc(tp.inhibiting, m, k, r);
2118 xml::Element& e = entries.add_child(
"subParameter");
2119 e.set_attr(
"classPath",
"java.lang.Integer");
2120 e.set_attr(
"name",
"inhibitingEntry");
2121 e.add_text_child(
"value", std::isfinite(in) ?
jmt_int(in) :
"0");
2128 void save_firing_outcomes(xml::Element& section, std::size_t ind) {
2129 const qn::TransitionParam<T>& tp = transparam(ind);
2130 const std::vector<std::size_t> outs = outputs_of(ind);
2132 jmt_param(section,
"jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix",
2133 "firingOutcomes",
true);
2134 for (std::size_t m = 0; m < tp.nmodes; ++m) {
2135 xml::Element& out = p.add_child(
"subParameter");
2136 out.set_attr(
"classPath",
2137 "jmt.engine.NetStrategies.TransitionUtilities.TransitionMatrix");
2138 out.set_attr(
"name",
"firingOutcome");
2139 xml::Element& vecs = jmt_param_sub(
2140 out,
"jmt.engine.NetStrategies.TransitionUtilities.TransitionVector",
2142 for (std::size_t k : outs) {
2143 xml::Element& vec = vecs.add_child(
"subParameter");
2144 vec.set_attr(
"classPath",
2145 "jmt.engine.NetStrategies.TransitionUtilities.TransitionVector");
2146 vec.set_attr(
"name",
"firingVector");
2147 jmt_scalar(vec,
"java.lang.String",
"stationName", nname(k));
2148 xml::Element& entries = jmt_param_sub(vec,
"java.lang.Integer",
"firingEntries");
2149 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2150 const double f = arc(tp.firing, m, k, r);
2152 xml::Element& e = entries.add_child(
"subParameter");
2153 e.set_attr(
"classPath",
"java.lang.Integer");
2154 e.set_attr(
"name",
"firingEntry");
2155 e.add_text_child(
"value",
jmt_int(f));
2162 double arc(
const std::vector<Matrix<T>>& tab, std::size_t m, std::size_t k,
2163 std::size_t r)
const {
2164 if (m >= tab.size() || k == 0 || k > tab[m].rows() || r == 0 || r > tab[m].cols())
2166 return d(tab[m](k - 1, r - 1));
2170 void save_mode_names(xml::Element& section, std::size_t ind) {
2171 const qn::TransitionParam<T>& tp = transparam(ind);
2172 xml::Element& p =
jmt_param(section,
"java.lang.String",
"modeNames",
true);
2173 for (std::size_t m = 0; m < tp.nmodes; ++m) {
2174 xml::Element& sp = p.add_child(
"subParameter");
2175 sp.set_attr(
"classPath",
"java.lang.String");
2176 sp.set_attr(
"name",
"modeName");
2177 sp.add_text_child(
"value", m < tp.modenames.size() ? tp.modenames[m] : std::string());
2182 void save_numbers_of_servers(xml::Element& section, std::size_t ind) {
2183 const qn::TransitionParam<T>& tp = transparam(ind);
2184 xml::Element& p =
jmt_param(section,
"java.lang.Integer",
"numbersOfServers",
true);
2185 for (std::size_t m = 0; m < tp.nmodes; ++m) {
2186 const double ns = m < tp.nmodeservers.size() ? tp.nmodeservers[m] : 1.0;
2187 xml::Element& sp = p.add_child(
"subParameter");
2188 sp.set_attr(
"classPath",
"java.lang.Integer");
2189 sp.set_attr(
"name",
"numberOfServers");
2190 sp.add_text_child(
"value", std::isfinite(ns) ?
jmt_int(ns) :
"-1");
2202 void save_timing_strategies(xml::Element& section, std::size_t ind) {
2203 const qn::TransitionParam<T>& tp = transparam(ind);
2204 xml::Element& p =
jmt_param(section,
"jmt.engine.NetStrategies.ServiceStrategy",
2205 "timingStrategies",
true);
2206 for (std::size_t m = 0; m < tp.nmodes; ++m) {
2207 xml::Element& sp = p.add_child(
"subParameter");
2208 const bool immediate = m < tp.timing.size() &&
2211 sp.set_attr(
"classPath",
2212 "jmt.engine.NetStrategies.ServiceStrategies.ZeroServiceTimeStrategy");
2213 sp.set_attr(
"name",
"ZeroServiceTimeStrategy");
2216 sp.set_attr(
"classPath",
2217 "jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy");
2218 sp.set_attr(
"name",
"timingStrategy");
2219 if (m >= tp.firingproc.size())
2221 (m < tp.modenames.size() ? tp.modenames[m] : std::string()) +
2222 "' of transition '" + nname(ind) +
"' has no firing process");
2224 "SolverJMT (transition firing)");
2229 void save_firing_priorities(xml::Element& section, std::size_t ind) {
2230 const qn::TransitionParam<T>& tp = transparam(ind);
2231 xml::Element& p =
jmt_param(section,
"java.lang.Integer",
"firingPriorities",
true);
2232 for (std::size_t m = 0; m < tp.nmodes; ++m) {
2233 const double v = m < tp.firingprio.size() ? tp.firingprio[m] : 1.0;
2234 xml::Element& sp = p.add_child(
"subParameter");
2235 sp.set_attr(
"classPath",
"java.lang.Integer");
2236 sp.set_attr(
"name",
"firingPriority");
2237 sp.add_text_child(
"value", std::isfinite(v) ?
jmt_int(v) :
"-1");
2250 void save_firing_weights(xml::Element& section, std::size_t ind) {
2251 const qn::TransitionParam<T>& tp = transparam(ind);
2252 xml::Element& p =
jmt_param(section,
"java.lang.Double",
"firingWeights",
true);
2253 for (std::size_t m = 0; m < tp.nmodes; ++m) {
2254 const double v = m < tp.fireweight.size() ? d(tp.fireweight[m]) : 1.0;
2255 xml::Element& sp = p.add_child(
"subParameter");
2256 sp.set_attr(
"classPath",
"java.lang.Double");
2257 sp.set_attr(
"name",
"firingWeight");
2258 sp.add_text_child(
"value", std::isfinite(v) ?
jmt_fmt(v) :
"-1");
2274 std::vector<std::vector<double>> class_cap_constraints()
const {
2275 const double inf = std::numeric_limits<double>::infinity();
2276 std::vector<std::vector<double>> con(sn_.nstations, std::vector<double>(sn_.nclasses, inf));
2277 if (sn_.classcap.empty())
return con;
2278 std::vector<double> chainpop(sn_.nclasses, inf);
2279 for (std::size_t c = 0; c < sn_.inchain.size(); ++c) {
2282 for (std::size_t r : sn_.inchain[c]) {
2283 const double n = sn_.classes[r - 1].population;
2284 if (std::isfinite(n)) tot += n;
else open =
true;
2286 for (std::size_t r : sn_.inchain[c]) chainpop[r - 1] = open ? inf : tot;
2288 for (std::size_t ist = 1; ist <= sn_.nstations; ++ist) {
2289 const NodeType ty = sn_.nodes[sn_.station_to_node[ist - 1] - 1].nodetype;
2290 if (ty == NodeType::Source || ty == NodeType::Place)
continue;
2291 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2292 if (sn_.disabled[ist - 1][r - 1])
continue;
2293 const double cc = sn_.classcap[ist - 1][r - 1];
2294 if (std::isfinite(cc) && cc < 2147483647.0 &&
2295 cc < std::min(sn_.cap[ist - 1], chainpop[r - 1]))
2296 con[ist - 1][r - 1] = cc;
2312 void assert_class_cap_exportable(std::size_t ist, std::size_t r)
const {
2313 const std::string sname = nname(sn_.station_to_node[ist - 1]);
2314 if (std::isfinite(sn_.classes[r - 1].population))
2316 "SolverJMT: station '" + sname +
"' carries a finite capacity " +
2317 jmt_int(sn_.classcap[ist - 1][r - 1]) +
" for the closed class '" + cname(r) +
2318 "'. LINE holds a blocked closed job at its upstream station, whereas JMT can "
2319 "only express a per-class capacity as a blocking region, which parks the job in "
2320 "the region input station instead, freeing the upstream server and losing it "
2321 "from the population count. Use SolverCTMC, SolverSSA or SolverLDES, or express "
2322 "the limit as the station capacity");
2324 if (dr == DropStrategy::BAS || dr == DropStrategy::BBS || dr == DropStrategy::RSRD ||
2325 dr == DropStrategy::RETRIAL || dr == DropStrategy::RETRIAL_WITH_LIMIT)
2327 "SolverJMT: station '" + sname +
"' applies drop strategy '" +
jmt_drop_text(dr) +
2328 "' to class '" + cname(r) +
2329 "' and also carries a finite capacity for it. JMT exports a per-class capacity "
2330 "as a blocking region, which can only drop or defer an arrival. Remove the "
2331 "per-class capacity, or use the station capacity, which is exported with its "
2347 void save_regions(xml::Element& sim) {
2348 const std::vector<std::vector<double>> con = class_cap_constraints();
2349 std::vector<bool> covered(sn_.nstations,
false);
2350 std::size_t lps_idx = sn_.regions.size();
2352 for (std::size_t ist = 1; ist <= sn_.nstations; ++ist) {
2353 if (sn_.stations[ist - 1].sched != SchedStrategy::LPS)
continue;
2355 const std::size_t ind = sn_.station_to_node[ist - 1];
2356 const double limit = sn_.stations[ist - 1].schedparam.empty()
2358 : d(sn_.stations[ist - 1].schedparam[0]);
2359 xml::Element& br = sim.add_child(
"blockingRegion");
2360 br.set_attr(
"name",
"LPSRegion" +
jmt_int(
static_cast<double>(lps_idx)));
2361 br.set_attr(
"type",
"default");
2362 br.add_child(
"regionNode").set_attr(
"nodeName", nname(ind));
2363 br.add_child(
"globalConstraint").set_attr(
"maxJobs",
jmt_num(limit));
2364 br.add_child(
"globalMemoryConstraint").set_attr(
"maxMemory",
"-1");
2365 covered[ist - 1] =
true;
2366 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2367 if (!std::isfinite(con[ist - 1][r - 1]))
continue;
2368 assert_class_cap_exportable(ist, r);
2370 "SolverJMT: station '" + nname(ind) +
2371 "' has both LPS scheduling and a finite capacity for the open class '" +
2373 "'. JMT expresses both through a single blocking region, which admits only "
2374 "one drop rule per class, but LPS requires blocking while the open-class "
2375 "capacity requires dropping. Remove the per-class capacity or use a non-LPS "
2376 "scheduling strategy");
2378 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2379 xml::Element& dr = br.add_child(
"dropRules");
2380 dr.set_attr(
"jobClass", cname(r));
2381 dr.set_attr(
"dropThisClass",
"false");
2385 for (std::size_t f = 1; f <= sn_.regions.size(); ++f) {
2386 const typename qn::NetworkStruct<T>::Region& rg = sn_.regions[f - 1];
2387 std::vector<std::size_t> members;
2388 for (std::size_t ist = 1; ist <= sn_.nstations; ++ist)
2389 if (ist <= rg.members.size() && rg.members[ist - 1]) {
2390 members.push_back(ist);
2391 covered[ist - 1] =
true;
2393 std::vector<double> region_class_cap(sn_.nclasses,
2394 std::numeric_limits<double>::infinity());
2395 for (std::size_t ist : members)
2396 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2397 if (!std::isfinite(con[ist - 1][r - 1]))
continue;
2398 if (members.size() > 1)
2400 "SolverJMT: station '" + nname(sn_.station_to_node[ist - 1]) +
2401 "' carries a finite capacity for class '" + cname(r) +
2402 "' and also belongs to the multi-station region FCRegion" +
2403 jmt_int(
static_cast<double>(f)) +
2404 ". JMT constrains a blocking region as a whole and allows a node to "
2405 "belong to only one region, so a per-station class capacity cannot "
2406 "be expressed alongside it");
2407 assert_class_cap_exportable(ist, r);
2408 if (!(r <= rg.rule.size() && rg.rule[r - 1] == DropStrategy::DROP))
2410 "SolverJMT: station '" + nname(sn_.station_to_node[ist - 1]) +
2411 "' carries a finite capacity for class '" + cname(r) +
2412 "' and also belongs to region FCRegion" +
2413 jmt_int(
static_cast<double>(f)) +
2414 ", whose drop rule for that class is not DROP. A JMT blocking "
2415 "region admits a single drop rule per class, shared by all of its "
2416 "constraints, and the per-class capacity of an open class is a loss "
2418 region_class_cap[r - 1] = con[ist - 1][r - 1];
2421 xml::Element& br = sim.add_child(
"blockingRegion");
2422 br.set_attr(
"name",
"FCRegion" +
jmt_int(
static_cast<double>(f)));
2423 br.set_attr(
"type",
"default");
2424 for (std::size_t ist : members)
2425 br.add_child(
"regionNode")
2426 .set_attr(
"nodeName", nname(sn_.station_to_node[ist - 1]));
2430 members.empty() ? -1.0 : rg.cap[members[0] - 1][sn_.nclasses];
2431 const double gmem = members.empty() ? -1.0 : rg.maxmem[members[0] - 1];
2432 br.add_child(
"globalConstraint").set_attr(
"maxJobs",
jmt_num(gmax));
2433 br.add_child(
"globalMemoryConstraint").set_attr(
"maxMemory",
jmt_num(gmem));
2434 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2435 double cmax = members.empty() ? -1.0 : rg.cap[members[0] - 1][r - 1];
2439 cmax = std::isfinite(region_class_cap[r - 1]) ? region_class_cap[r - 1] : -1.0;
2440 else if (std::isfinite(region_class_cap[r - 1]))
2441 cmax = std::min(cmax, region_class_cap[r - 1]);
2442 if (cmax == -1.0 || !std::isfinite(cmax))
continue;
2443 xml::Element& cc = br.add_child(
"classConstraint");
2444 cc.set_attr(
"jobClass", cname(r));
2445 cc.set_attr(
"maxJobsPerClass",
jmt_num(cmax));
2451 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2452 xml::Element& dr = br.add_child(
"dropRules");
2453 dr.set_attr(
"jobClass", cname(r));
2454 dr.set_attr(
"dropThisClass",
2455 (r <= rg.rule.size() && rg.rule[r - 1] == DropStrategy::DROP)
2459 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2460 if (r > rg.weight.size())
continue;
2461 const double w = d(rg.weight[r - 1]);
2462 if (w == 1.0)
continue;
2463 xml::Element& cw = br.add_child(
"classWeight");
2464 cw.set_attr(
"jobClass", cname(r));
2465 cw.set_attr(
"weight",
jmt_num(w));
2467 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2468 if (r > rg.size.size())
continue;
2469 const double s = d(rg.size[r - 1]);
2470 if (s == 1.0)
continue;
2471 xml::Element& cs = br.add_child(
"classSize");
2472 cs.set_attr(
"jobClass", cname(r));
2473 cs.set_attr(
"size",
jmt_num(s));
2477 std::size_t cap_idx = 0;
2478 for (std::size_t ist = 1; ist <= sn_.nstations; ++ist) {
2479 if (covered[ist - 1])
continue;
2481 for (std::size_t r = 1; r <= sn_.nclasses; ++r)
2482 if (std::isfinite(con[ist - 1][r - 1])) any =
true;
2485 for (std::size_t r = 1; r <= sn_.nclasses; ++r)
2486 if (std::isfinite(con[ist - 1][r - 1])) assert_class_cap_exportable(ist, r);
2487 xml::Element& br = sim.add_child(
"blockingRegion");
2488 br.set_attr(
"name",
"ClassCapRegion" +
jmt_int(
static_cast<double>(cap_idx)));
2489 br.set_attr(
"type",
"default");
2490 br.add_child(
"regionNode")
2491 .set_attr(
"nodeName", nname(sn_.station_to_node[ist - 1]));
2492 br.add_child(
"globalConstraint").set_attr(
"maxJobs",
"-1");
2493 br.add_child(
"globalMemoryConstraint").set_attr(
"maxMemory",
"-1");
2494 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2495 if (!std::isfinite(con[ist - 1][r - 1]))
continue;
2496 xml::Element& cc = br.add_child(
"classConstraint");
2497 cc.set_attr(
"jobClass", cname(r));
2498 cc.set_attr(
"maxJobsPerClass",
jmt_num(con[ist - 1][r - 1]));
2500 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2501 if (!std::isfinite(con[ist - 1][r - 1]))
continue;
2502 xml::Element& dr = br.add_child(
"dropRules");
2503 dr.set_attr(
"jobClass", cname(r));
2504 dr.set_attr(
"dropThisClass",
"true");
2525 void save_cache_strategy(xml::Element& section, std::size_t ind) {
2526 const auto it = sn_.nodeparam.find(ind);
2527 if (it == sn_.nodeparam.end())
2528 throw InputError(
"SolverJMT: cache node '" + nname(ind) +
"' carries no parameters");
2529 const qn::CacheParam<T>& cp = it->second;
2530 const std::size_t K = sn_.nclasses;
2533 jmt_int(
static_cast<double>(cp.nitems)));
2534 xml::Element& cap =
jmt_param(section,
"java.lang.Integer",
"cacheCapacity",
true);
2535 for (std::size_t l = 0; l < cp.itemcap.size(); ++l) {
2536 xml::Element& sp = cap.add_child(
"subParameter");
2537 sp.set_attr(
"classPath",
"java.lang.Integer");
2538 sp.set_attr(
"name",
"capacity");
2539 sp.add_text_child(
"value",
jmt_int(
static_cast<double>(cp.itemcap[l])));
2542 const std::size_t nlev = cp.itemcap.size();
2544 xml::Element& mat =
jmt_param(section,
"java.lang.Object",
"matrix",
true);
2545 for (std::size_t a = 1; a <= nlev; ++a) {
2546 xml::Element& row = mat.add_child(
"subParameter");
2547 row.set_attr(
"array",
"true");
2548 row.set_attr(
"classPath",
"java.lang.Float");
2549 row.set_attr(
"name",
"row");
2550 for (std::size_t b = 1; b <= nlev; ++b) {
2551 const bool one = lru ? (a < nlev ? b == a + 1 : b == nlev) : a == b;
2552 xml::Element& cell = row.add_child(
"subParameter");
2553 cell.set_attr(
"classPath",
"java.lang.Float");
2554 cell.set_attr(
"name",
"cell");
2555 cell.add_text_child(
"value", one ?
"1.0" :
"0.0");
2559 xml::Element& jc =
jmt_param(section,
"jmt.engine.QueueNet.JobClass",
"jobClasses",
true);
2560 for (std::size_t r = 1; r <= K; ++r) {
2561 const bool used = (r <= cp.hitclass.size() && cp.hitclass[r - 1] > 0) ||
2562 (r <= cp.missclass.size() && cp.missclass[r - 1] > 0);
2563 if (!used)
continue;
2564 xml::Element& sp = jc.add_child(
"subParameter");
2565 sp.set_attr(
"classPath",
"jmt.engine.QueueNet.JobClass");
2566 sp.set_attr(
"name",
"jobClass");
2567 sp.add_text_child(
"value", cname(r));
2569 const char* switch_names[2] = {
"hitClasses",
"missClasses"};
2570 const char* entry_names[2] = {
"hitClass",
"missClass"};
2571 for (
int w = 0; w < 2; ++w) {
2572 const std::vector<std::size_t>& tab = w == 0 ? cp.hitclass : cp.missclass;
2574 jmt_param(section,
"jmt.engine.QueueNet.JobClass", switch_names[w],
true);
2575 for (std::size_t r = 1; r <= K && r <= tab.size(); ++r) {
2576 if (tab[r - 1] == 0)
continue;
2577 xml::Element& sp = p.add_child(
"subParameter");
2578 sp.set_attr(
"classPath",
"jmt.engine.QueueNet.JobClass");
2579 sp.set_attr(
"name", entry_names[w]);
2580 sp.add_text_child(
"value", cname(tab[r - 1]));
2584 const char* policy =
nullptr;
2585 switch (cp.replacestrat) {
2587 policy =
"jmt.engine.NetStrategies.CacheStrategies.LRUCache";
2591 policy =
"jmt.engine.NetStrategies.CacheStrategies.FIFOCache";
2594 policy =
"jmt.engine.NetStrategies.CacheStrategies.RandomCache";
2598 "SolverJMT: cache '" + nname(ind) +
2599 "' uses a replacement policy JMT has no cache object for (HLRU, CLIMB and "
2600 "QLRU); use SolverLDES, which simulates them directly");
2602 xml::Element& rp = section.add_child(
"parameter");
2603 rp.set_attr(
"classPath", policy);
2604 rp.set_attr(
"name",
"replacePolicy");
2614 section,
"jmt.engine.random.discrete.DiscreteDistribution",
"popularity",
true);
2615 for (std::size_t r = 1; r <= K; ++r) {
2617 const bool reads = r <= cp.pread.size() && !cp.pread[r - 1].empty();
2618 const typename qn::CacheParam<T>::Popularity kind =
2619 r <= cp.preadkind.size() ? cp.preadkind[r - 1]
2620 :
typename qn::CacheParam<T>::Popularity();
2621 xml::Element& sp = pop.add_child(
"subParameter");
2623 sp.set_attr(
"classPath",
"jmt.engine.random.discrete.Zipf");
2624 sp.set_attr(
"name",
"popularity");
2626 jmt_scalar(sp,
"java.lang.Integer",
"numberOfElements",
2627 jmt_int(
static_cast<double>(kind.n)));
2629 sp.set_attr(
"classPath",
"jmt.engine.random.discrete.Uniform");
2630 sp.set_attr(
"name",
"popularity");
2631 jmt_scalar(sp,
"java.lang.Integer",
"min",
"1");
2633 jmt_int(
static_cast<double>(kind.n == 0 ? cp.nitems : kind.n)));
2635 sp.set_attr(
"classPath",
"jmt.engine.random.discrete.DiscreteDistribution");
2636 sp.set_attr(
"name",
"null");
2637 sp.add_text_child(
"value",
"null");
2652 void save_impatience(xml::Element& section, std::size_t ind) {
2653 const std::size_t ist = sn_.nodes[ind - 1].station;
2655 section,
"jmt.engine.NetStrategies.ImpatienceStrategies.Impatience",
"Impatience",
true);
2656 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2657 if (!keep_[r - 1])
continue;
2659 xml::Element& sp = p.add_child(
"subParameter");
2660 const qn::Station<T>* st = ist == 0 ? nullptr : &sn_.stations[ist - 1];
2661 const bool balks = st !=
nullptr && r <= st->balking.size() &&
2662 st->balking[r - 1].strategy ==
2665 sp.set_attr(
"classPath",
"jmt.engine.NetStrategies.ImpatienceStrategies.Balking");
2666 sp.set_attr(
"name",
"Balking");
2667 save_balking_strategy(sp, st->balking[r - 1].thresholds, st->nservers);
2670 sp.set_attr(
"classPath",
"jmt.engine.NetStrategies.ImpatienceStrategies.Reneging");
2671 sp.set_attr(
"name",
"Reneging");
2672 const bool renegs = st !=
nullptr && r <= st->impatience.size() &&
2674 r <= st->patience.size() && !st->patience[r - 1].disabled;
2676 sp.add_text_child(
"value",
"null");
2679 const JmtDistView<T> v =
jmt_dist_view(st->patience[r - 1]);
2685 xml::Element& dn = sp.add_child(
"subParameter");
2686 dn.set_attr(
"classPath",
"jmt.engine.random.Uniform");
2687 dn.set_attr(
"name",
"Uniform");
2688 xml::Element& par = sp.add_child(
"subParameter");
2689 par.set_attr(
"classPath",
"jmt.engine.random.UniformPar");
2690 par.set_attr(
"name",
"distrPar");
2691 jmt_scalar(par,
"java.lang.Double",
"min",
"0.0");
2692 jmt_double(par,
"max", v.rate > 0.0 ? 2.0 / v.rate : 0.0);
2711 void save_balking_strategy(xml::Element& balking,
2712 const std::vector<
typename qn::Station<T>::BalkingThreshold>& th,
2717 std::vector<typename qn::Station<T>::BalkingThreshold> sorted = th;
2718 std::sort(sorted.begin(), sorted.end(),
2719 [](
const typename qn::Station<T>::BalkingThreshold& a,
2720 const typename qn::Station<T>::BalkingThreshold& b) {
2721 return a.min_jobs < b.min_jobs;
2724 (!std::isfinite(nservers) || nservers < 1.0) ? 1.0 : nservers;
2725 std::vector<Bp> bps;
2726 for (std::size_t i = 0; i < sorted.size(); ++i) {
2727 const double lo = sorted[i].min_jobs;
2730 const double hi = sorted[i].max_jobs < 0.0
2731 ? std::numeric_limits<double>::infinity()
2732 : sorted[i].max_jobs;
2733 const double pr = d(sorted[i].probability);
2734 bps.push_back(Bp{std::max(0.0, lo - S), pr});
2735 if (!std::isfinite(hi))
continue;
2736 const double next_lo = i + 1 < sorted.size()
2737 ? sorted[i + 1].min_jobs
2738 : std::numeric_limits<double>::infinity();
2739 if (hi + 1.0 < next_lo) bps.push_back(Bp{std::max(0.0, hi + 1.0 - S), 0.0});
2741 xml::Element& ld = balking.add_child(
"subParameter");
2742 ld.set_attr(
"classPath",
"jmt.engine.NetStrategies.ServiceStrategies.LoadDependentStrategy");
2743 ld.set_attr(
"name",
"LoadDependentStrategy");
2744 xml::Element& arr = jmt_param_sub(
2745 ld,
"jmt.engine.NetStrategies.ServiceStrategies.LDParameter",
"LDParameter");
2746 for (
const Bp& b : bps) {
2747 xml::Element& rn = arr.add_child(
"subParameter");
2748 rn.set_attr(
"classPath",
"jmt.engine.NetStrategies.ServiceStrategies.LDParameter");
2749 rn.set_attr(
"name",
"LDParameter");
2753 xml::Element& dn = rn.add_child(
"subParameter");
2754 dn.set_attr(
"classPath",
"jmt.engine.random.Exponential");
2755 dn.set_attr(
"name",
"Exponential");
2756 xml::Element& par = rn.add_child(
"subParameter");
2757 par.set_attr(
"classPath",
"jmt.engine.random.ExponentialPar");
2758 par.set_attr(
"name",
"distrPar");
2759 jmt_scalar(par,
"java.lang.Double",
"lambda",
"1.0");
2762 jmt_scalar(balking,
"java.lang.Boolean",
"priorityActivated",
"false");
2772 void save_retrial_distributions(xml::Element& section, std::size_t ind) {
2773 const std::size_t ist = sn_.nodes[ind - 1].station;
2774 const auto it = ist == 0 ? sn_.retrialparam.end() : sn_.retrialparam.find(ist);
2775 xml::Element& p =
jmt_param(section,
"jmt.engine.NetStrategies.ServiceStrategy",
2776 "retrialDistributions",
true);
2777 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2778 if (!keep_[r - 1])
continue;
2780 xml::Element& sts = p.add_child(
"subParameter");
2781 sts.set_attr(
"classPath",
2782 "jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy");
2783 sts.set_attr(
"name",
"ServiceTimeStrategy");
2784 const bool has = it != sn_.retrialparam.end() &&
2785 r <= it->second.retrial_proc.size() &&
2786 !it->second.retrial_proc[r - 1].disabled;
2788 xml::Element& dn = sts.add_child(
"subParameter");
2789 dn.set_attr(
"classPath",
"jmt.engine.random.Exponential");
2790 dn.set_attr(
"name",
"Exponential");
2791 xml::Element& par = sts.add_child(
"subParameter");
2792 par.set_attr(
"classPath",
"jmt.engine.random.ExponentialPar");
2793 par.set_attr(
"name",
"distrPar");
2794 jmt_scalar(par,
"java.lang.Double",
"lambda",
"1.000000000000");
2798 "SolverJMT (retrial delay)");
2812 void save_switchover_strategy(xml::Element& section, std::size_t ind) {
2813 const std::size_t ist = sn_.nodes[ind - 1].station;
2814 const typename qn::NetworkStruct<T>::PollingParam pp = sn_.effective_polling(ist);
2815 xml::Element& p =
jmt_param(section,
"jmt.engine.NetStrategies.ServiceStrategy",
2816 "SwitchoverStrategy",
true);
2817 for (std::size_t r = 1; r <= sn_.nclasses; ++r) {
2819 xml::Element& sts = p.add_child(
"subParameter");
2820 sts.set_attr(
"classPath",
2821 "jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy");
2822 sts.set_attr(
"name",
"ServiceTimeStrategy");
2823 const lang::Distrib<T>& so =
2824 r <= pp.switchover.size() ? pp.switchover[r - 1] : empty_dist_;
2833 sts.set_attr(
"classPath",
2834 "jmt.engine.NetStrategies.ServiceStrategies.ZeroServiceTimeStrategy");
2835 sts.set_attr(
"name",
"ZeroServiceTimeStrategy");
2842 std::map<std::size_t, double> empty_weights_;
2843 lang::Distrib<T> empty_dist_;