68 const T one = nt::from_int(1);
69 const std::size_t I =
sn.nodes.size();
70 const std::size_t K =
sn.nclasses;
71 const std::size_t C =
sn.nchains;
73 if (I == 0 || K == 0 || C == 0)
74 throw InputError(
"QN2LQN: the queueing network has no closed chain to convert");
75 if (
sn.nodevisits.size() != C ||
sn.inchain.size() != C)
76 throw InputError(
"QN2LQN: the NetworkStruct visits or chains are not refreshed");
77 for (std::size_t r = 0; r < K; ++r)
78 if (!std::isfinite(
sn.classes[r].population))
80 "QN2LQN: open classes are not supported; SolverQNS sends open models to "
83 std::vector<std::size_t> chain_of(K, C);
84 for (std::size_t c = 0; c < C; ++c)
85 for (std::size_t x = 0; x <
sn.inchain[c].size(); ++x) {
86 const std::size_t r1 =
sn.inchain[c][x];
87 if (r1 == 0 || r1 > K)
88 throw InputError(
"QN2LQN: chain " + std::to_string(c + 1) +
89 " contains an out-of-range class index");
90 if (chain_of[r1 - 1] != C)
91 throw InputError(
"QN2LQN: class " + std::to_string(r1) +
92 " belongs to more than one chain");
95 for (std::size_t r = 0; r < K; ++r)
97 throw InputError(
"QN2LQN: class " + std::to_string(r + 1) +
98 " belongs to no chain");
101 const std::string pseudo_host =
sn.name.empty() ? std::string(
"QN") :
sn.name;
104 std::vector<std::string> ref_task(C), ref_entry(C);
105 for (std::size_t c = 0; c < C; ++c) {
107 for (std::size_t x = 0; x <
sn.inchain[c].size(); ++x)
108 jobs +=
sn.classes[
sn.inchain[c][x] - 1].population;
109 ref_task[c] =
"RefTask_" + std::to_string(c + 1);
110 ref_entry[c] =
"Chain_" + std::to_string(c + 1);
112 b.
entry(ref_entry[c], ref_task[c]);
118 std::vector<std::string> entries(I * K), service_acts(I * K);
119 const auto ir = [K](std::size_t i, std::size_t r) {
return i * K + r; };
120 const auto cir = [I, K](std::size_t c, std::size_t i, std::size_t r) {
121 return (c * I + i) * K + r;
124 for (std::size_t i = 0; i < I; ++i) {
126 if (nd.
nodetype != qn::NodeType::Queue && nd.
nodetype != qn::NodeType::Delay)
129 throw InputError(
"QN2LQN: service node '" + nd.
name +
"' has no station row");
130 const std::size_t ist = nd.
station - 1;
132 const std::string task =
"T_" + nd.
name;
134 for (std::size_t r = 0; r < K; ++r) {
135 const std::size_t c = chain_of[r];
136 if (
sn.nodevisits[c].rows() != I ||
sn.nodevisits[c].cols() != K)
137 throw InputError(
"QN2LQN: nodevisits has the wrong dimensions for chain " +
138 std::to_string(c + 1));
139 if (!(nt::to_double(
sn.nodevisits[c](i, r)) > 0.0))
continue;
140 if (ist >=
sn.service.size() || r >=
sn.service[ist].size() ||
141 sn.service[ist][r].disabled)
142 throw InputError(
"QN2LQN: visited station '" + nd.
name +
"', class '" +
143 sn.classes[r].name +
"' has no service distribution");
144 entries[ir(i, r)] = detail::qn2lqn_indexed(
"E", i, r);
145 service_acts[ir(i, r)] = detail::qn2lqn_indexed(
"Q", i, r);
146 b.
entry(entries[ir(i, r)], task);
147 b.
activity(service_acts[ir(i, r)],
sn.service[ist][r], task);
148 b.
bound_to(service_acts[ir(i, r)], entries[ir(i, r)]);
149 b.
replies_to(service_acts[ir(i, r)], entries[ir(i, r)]);
153 std::vector<std::string> pseudo(C * I * K);
154 std::vector<bool> bound(C,
false);
155 std::vector<std::size_t> bound_i(C, 0), bound_r(C, 0);
156 const auto incoming = [&](std::size_t i, std::size_t r) {
157 const std::size_t col = i * K + r;
158 if (
sn.rtnodes.cols() <= col)
return false;
159 for (std::size_t row = 0; row <
sn.rtnodes.rows(); ++row)
160 if (nt::to_double(
sn.rtnodes(row, col)) > 0.0)
return true;
164 for (std::size_t i = 0; i < I; ++i) {
166 if (type == qn::NodeType::ClassSwitch || type == qn::NodeType::Router ||
167 type == qn::NodeType::Logger || type == qn::NodeType::Fork ||
168 type == qn::NodeType::Join) {
169 const char* prefix = (type == qn::NodeType::Fork || type == qn::NodeType::Join)
172 for (std::size_t r = 0; r < K; ++r) {
173 if (!incoming(i, r))
continue;
174 const std::size_t c = chain_of[r];
175 pseudo[cir(c, i, r)] = detail::qn2lqn_pseudo(prefix, c, i, r);
178 }
else if (type == qn::NodeType::Queue || type == qn::NodeType::Delay) {
179 for (std::size_t r = 0; r < K; ++r) {
180 const std::size_t c = chain_of[r];
181 if (service_acts[ir(i, r)].empty())
continue;
182 pseudo[cir(c, i, r)] = detail::qn2lqn_indexed(
"A", i, r);
184 const std::size_t first_r =
sn.inchain[c].front() - 1;
185 const std::size_t refstat =
sn.classes[first_r].refstat;
186 if (refstat == 0 || refstat >
sn.station_to_node.size())
187 throw InputError(
"QN2LQN: chain " + std::to_string(c + 1) +
188 " has an invalid reference station");
189 const std::size_t refnode =
sn.station_to_node[refstat - 1] - 1;
190 if (i == refnode && r == first_r) {
191 b.
bound_to(pseudo[cir(c, i, r)], ref_entry[c]);
196 b.
sync_call(pseudo[cir(c, i, r)], entries[ir(i, r)], one);
198 }
else if (type != qn::NodeType::Source && type != qn::NodeType::Sink) {
201 ", which the MATLAB conversion does not map");
204 for (std::size_t c = 0; c < C; ++c)
206 throw InputError(
"QN2LQN: chain " + std::to_string(c + 1) +
207 " has no visited reference station to bind to its entry");
209 for (std::size_t c = 0; c < C; ++c) {
210 for (std::size_t i = 0; i < I; ++i) {
211 if (!detail::qn2lqn_routing_node(
sn.nodes[i].nodetype))
continue;
212 for (std::size_t rx = 0; rx <
sn.inchain[c].size(); ++rx) {
213 const std::size_t r =
sn.inchain[c][rx] - 1;
214 const std::string& pre = pseudo[cir(c, i, r)];
215 if (pre.empty() || !incoming(i, r))
continue;
216 std::vector<std::string> posts;
217 std::vector<T> probs;
218 for (std::size_t j = 0; j < I; ++j) {
219 if (!detail::qn2lqn_routing_node(
sn.nodes[j].nodetype) ||
220 sn.nodes[j].nodetype == qn::NodeType::Join)
222 for (std::size_t sx = 0; sx <
sn.inchain[c].size(); ++sx) {
223 const std::size_t s =
sn.inchain[c][sx] - 1;
224 const T pr =
sn.rtnodes(i * K + r, j * K + s);
225 if (!(nt::to_double(pr) > 0.0))
continue;
226 if (j == bound_i[c] && s == bound_r[c]) {
227 const std::string end = detail::qn2lqn_pseudo(
"End", c, i, r);
229 posts.push_back(end);
231 const std::string& post = pseudo[cir(c, j, s)];
233 throw InputError(
"QN2LQN: route from node '" +
sn.nodes[i].name +
234 "' reaches a node/class with no pseudo-activity");
235 posts.push_back(post);
240 if (posts.empty())
continue;
241 if (
sn.nodes[i].nodetype == qn::NodeType::Fork)
251 for (std::size_t c = 0; c < C; ++c)
252 for (std::size_t j = 0; j < I; ++j) {
253 if (
sn.nodes[j].nodetype != qn::NodeType::Join)
continue;
254 for (std::size_t sx = 0; sx <
sn.inchain[c].size(); ++sx) {
255 const std::size_t s =
sn.inchain[c][sx] - 1;
256 const std::string& post = pseudo[cir(c, j, s)];
257 if (post.empty())
continue;
258 std::vector<std::string> pres;
259 for (std::size_t i = 0; i < I; ++i) {
260 if (!detail::qn2lqn_routing_node(
sn.nodes[i].nodetype))
continue;
261 for (std::size_t rx = 0; rx <
sn.inchain[c].size(); ++rx) {
262 const std::size_t r =
sn.inchain[c][rx] - 1;
263 if (nt::to_double(
sn.rtnodes(i * K + r, j * K + s)) <= 0.0)
continue;
264 const std::string& pre = pseudo[cir(c, i, r)];
265 if (!pre.empty()) pres.push_back(pre);
270 else if (pres.size() == 1)
271 b.
serial(pres.front(), post);