5#ifndef LINE_SOLVERS_MVA_FJ_HT_H
6#define LINE_SOLVERS_MVA_FJ_HT_H
80void fj_copy_block(qn::NetworkStruct<T>& V, std::size_t r, std::size_t s, std::size_t a,
82 const typename std::map<std::pair<std::size_t, std::size_t>, Matrix<T>>::const_iterator it =
83 V.P.find(std::make_pair(r, s));
84 if (it == V.P.end())
return;
85 const Matrix<T> src = it->second;
86 const T zero = num_traits<T>::from_int(0);
87 for (std::size_t i = 0; i < src.rows(); ++i)
88 for (std::size_t j = 0; j < src.cols(); ++j)
89 if (src(i, j) > zero || src(i, j) < zero) V.set_route(a, b, i + 1, j + 1, src(i, j));
94void fj_copy_row_all_blocks(qn::NetworkStruct<T>& V, std::size_t src, std::size_t dst) {
95 std::vector<std::pair<std::size_t, std::size_t>> keys;
96 for (
typename std::map<std::pair<std::size_t, std::size_t>, Matrix<T>>::const_iterator it =
98 it != V.P.end(); ++it)
99 keys.push_back(it->first);
100 for (std::size_t k = 0; k < keys.size(); ++k) {
101 typename std::map<std::pair<std::size_t, std::size_t>, Matrix<T>>::iterator it =
103 if (it == V.P.end() || it->second.rows() < src || it->second.rows() < dst)
continue;
104 for (std::size_t j = 0; j < it->second.cols(); ++j)
105 it->second(dst - 1, j) = it->second(src - 1, j);
120 tr.heidelberger_trivedi =
true;
123 std::vector<std::size_t> forkNodes;
124 for (std::size_t i = 0; i < L.
nodes.size(); ++i)
125 if (L.
nodes[i].nodetype == NodeType::Fork) forkNodes.push_back(i + 1);
126 if (forkNodes.empty())
return tr;
136 std::vector<std::vector<std::vector<std::size_t>>> heads(forkNodes.size());
137 for (std::size_t a = 0; a < forkNodes.size(); ++a) {
139 F.
node = forkNodes[a];
143 "fj_ht: the fork node '" + L.
nodes[F.
node - 1].name +
"' of model '" + L.
name +
144 "' sends more than one task per link (tasksPerLink=" +
145 std::to_string(
static_cast<long>(F.
fanOut + 0.5)) +
146 "); multiple tasks per link are not supported in H-T, use fork_join='mmt'");
147 for (std::size_t p = 0; p < L.
fj.size(); ++p) {
148 if (L.
fj[p].first != F.
node)
continue;
151 std::to_string(F.
node) +
152 " with more than one join station; the reference supports "
153 "one join per fork");
158 "fj_ht: the fork node '" + L.
nodes[F.
node - 1].name +
"' of model '" + L.
name +
159 "' has no join; the H-T method charges the whole fork-join span at the "
160 "synchronisation point, so it needs one. Use fork_join='mmt'");
162 heads[a].assign(L.
nclasses + 1, std::vector<std::size_t>());
163 for (std::size_t r = 1; r <= L.
nclasses; ++r) {
164 for (std::size_t j = 1; j <= L.
nodes.size(); ++j) {
166 for (std::size_t s = 1; s <= L.
nclasses && !linked; ++s)
168 if (linked) heads[a][r].push_back(j);
172 tr.forks.push_back(F);
188 for (std::size_t a = 0; a <
tr.forks.size(); ++a) {
189 V.
nodes[
tr.forks[a].node - 1].nodetype = NodeType::Router;
198 for (std::size_t j = 1; j <= L.
nodes.size(); ++j) {
199 if (L.
nodes[j - 1].nodetype != NodeType::Join)
continue;
200 const std::size_t st = V.
nodes[j - 1].station;
202 throw InputError(
"fj_ht: the join node '" + V.
nodes[j - 1].name +
"' of model '" +
203 L.
name +
"' is not a station");
204 V.
stations[st - 1].nodetype = NodeType::Delay;
205 V.
stations[st - 1].sched = SchedStrategy::INF;
206 V.
stations[st - 1].nservers = std::numeric_limits<double>::infinity();
207 V.
nodes[j - 1].nodetype = NodeType::Delay;
208 for (std::size_t k = 1; k <= V.
classes.size(); ++k)
210 tr.joinStations.push_back(st);
213 ad.
name =
"Auxiliary Delay - " + V.
nodes[j - 1].name;
215 ad.
sched = SchedStrategy::INF;
216 ad.
nservers = std::numeric_limits<double>::infinity();
219 tr.auxDelayStation[j] = adstat;
220 tr.auxDelayNode[j] = adnode;
221 for (std::size_t k = 1; k <= V.
classes.size(); ++k)
229 detail::fj_copy_row_all_blocks(V, j, adnode);
230 for (std::size_t r = 1; r <= V.
classes.size(); ++r) {
231 detail::fj_clear_row(V, r, r, j);
235 for (std::size_t a = 0; a <
tr.forks.size(); ++a)
236 tr.forks[a].joinStation = V.
nodes[
tr.forks[a].joinNode - 1].station;
239 tr.fjclassmap.assign(V.
classes.size() + 1, 0);
240 tr.fjforkmap.assign(V.
classes.size() + 1, 0);
241 tr.fanout.assign(V.
classes.size() + 1, 0.0);
242 tr.auxdisabled.assign(V.
classes.size() + 1,
false);
243 tr.auxbranch.assign(V.
classes.size() + 1, 0);
245 for (std::size_t fa = 0; fa <
tr.forks.size(); ++fa) {
247 const std::size_t adstat =
tr.auxDelayStation[F.
joinNode];
248 const std::size_t adnode =
tr.auxDelayNode[F.
joinNode];
251 std::vector<bool> forked(L.
nclasses + 1,
false);
252 for (std::size_t c = 0; c < L.
nchains; ++c)
253 for (std::size_t r = 1; r <= L.
nclasses; ++r)
256 for (std::size_t c = 0; c < L.
nchains; ++c) {
257 const std::vector<std::size_t>& ic = L.
inchain[c];
259 for (std::size_t x = 0; x < ic.size(); ++x)
260 if (forked[ic[x]]) any =
true;
265 std::vector<std::vector<std::size_t>> aux(L.
nclasses + 1);
266 for (std::size_t x = 0; x < ic.size(); ++x) {
267 const std::size_t r = ic[x];
269 if (std::isinf(L.
classes[r - 1].population))
271 "fj_ht: class '" + L.
classes[r - 1].name +
"' of model '" + L.
name +
272 "' is open and reaches a fork; the H-T method can be used only on closed "
273 "models, use fork_join='mmt'");
274 for (std::size_t par = 1; par <= F.
origfanout[r]; ++par) {
278 xc.
type = JobClassType::CLOSED;
296 for (std::size_t i = 1; i <= L.
stations.size(); ++i) {
298 if (nt == NodeType::Join) {
300 }
else if (nt == NodeType::Source || nt == NodeType::Fork) {
308 tr.fjclassmap.resize(V.
classes.size() + 1, 0);
309 tr.fjforkmap.resize(V.
classes.size() + 1, 0);
310 tr.fanout.resize(V.
classes.size() + 1, 0.0);
311 tr.auxdisabled.resize(V.
classes.size() + 1,
false);
312 tr.auxbranch.resize(V.
classes.size() + 1, 0);
313 tr.fjclassmap[s] = r;
314 tr.fjforkmap[s] = fa;
316 tr.auxbranch[s] = par;
317 tr.auxclasses.push_back(s);
323 for (std::size_t x = 0; x < ic.size(); ++x) {
324 const std::size_t r = ic[x];
325 if (aux[r].empty())
continue;
326 for (std::size_t y = 0; y < ic.size(); ++y) {
327 const std::size_t s = ic[y];
328 if (aux[s].empty())
continue;
329 for (std::size_t par = 1; par <= aux[r].size() && par <= aux[s].size(); ++par) {
330 const std::size_t a = aux[r][par - 1], b = aux[s][par - 1];
331 detail::fj_copy_block(V, r, s, a, b);
335 detail::fj_clear_row(V, a, b, F.
node);
338 detail::fj_clear_row(V, a, b, adnode);
345 detail::fj_clear_row(V, r, s, F.
node);
352 tr.fjclassmap.resize(V.
classes.size() + 1, 0);
353 tr.fjforkmap.resize(V.
classes.size() + 1, 0);
354 tr.fanout.resize(V.
classes.size() + 1, 0.0);
355 tr.auxdisabled.resize(V.
classes.size() + 1,
false);
356 tr.auxbranch.resize(V.
classes.size() + 1, 0);
390 if (method.empty() || method ==
"default" || method ==
"mmt" || method ==
"fjt")
392 if (method ==
"ht" || method ==
"heidelberger-trivedi")
return fj_ht(L);
394 "' is not a fork-join method; options.config.fork_join is one of "
395 "'default', 'mmt', 'fjt' (the MMT transform) or 'ht', "
396 "'heidelberger-trivedi'");
UnsupportedError(const std::string &what)
A network plus its refreshed NetworkStruct.
void set_route(std::size_t r, std::size_t s, std::size_t i, std::size_t j, const T &p)
P{r,s}(i,j) = p, with 1-based NODE and class indices.
std::size_t add_class(const JobClass &cl)
Add a class and grow the service table.
T get_route(std::size_t r, std::size_t s, std::size_t i, std::size_t j) const
P{r,s}(i,j), AS THE USER SET IT.
std::vector< Matrix< T > > nodevisits
(nchains) each (nnodes x nclasses)
std::vector< std::vector< Distrib< T > > > service
service[i][r], 0-based station and class; a disabled entry marks a pair never visited.
void refresh_chains()
Port of MNetwork.refreshChains followed by sn_refresh_visits.
void promote_stateful(std::size_t ind)
Port of MNetwork.refreshLocalVars: the per-node local-variable widths.
std::vector< JobClass > classes
std::vector< Station< T > > stations
stations[k-1] is the k-th station
void set_service(std::size_t station, std::size_t cls, const Distrib< T > &d)
std::vector< std::vector< std::size_t > > inchain
1-based class indices per chain
std::vector< NodeDef > nodes
every node, in creation order
std::vector< std::pair< std::size_t, std::size_t > > fj
fj(f,j): the Join node j that closes the Fork node f, 1-based.
std::size_t add_station(const Station< T > &st)
Add a station, which is also a node, and grow the service table.
std::vector< std::size_t > station_to_node
(nstations) 1-based node index
void refresh_routing()
Port of the part of MNetwork.refreshRoutingMatrix this port reaches: the expansion of a routing STRAT...
The exception types the port throws.
The fork-join transform SolverMVA applies before solving a layer that contains a Fork.
NodeType
Node kinds, with the values of MATLAB NodeType.
void fj_widen_csmatrix(FjMmt< T > &tr)
Widen every explicit ClassSwitch matrix to the auxiliary-expanded class set.
FjMmt< T > fj_fork_join_transform(const qn::NetworkStruct< T > &L, const std::string &method)
options.config.fork_join -> the transform it names.
FjMmt< T > fj_mmt(const qn::NetworkStruct< T > &L)
Build the transformed layer.
FjMmt< T > fj_ht(const qn::NetworkStruct< T > &L)
Build the H-T transform of L.
void fj_sort_forks(FjMmt< T > &tr)
Port of ModelAdapter.sortForks: fill in outer and parent on every fork record.
Conservation laws of a layered queueing network, enumerated from its structure.
A queueing network and its refreshed NetworkStruct.
static Distrib immediate()
The Immediate singleton.
One record per Fork of the base layer, ascending in node index.
std::size_t node
1-based node index, shared with the base layer
std::size_t joinNode
0 when the fork has no join
double fanOut
sn.nodeparam{f}.fanOut, which MATLAB sets to the fork's tasksPerLink and NOT to its number of output ...
std::vector< std::size_t > origfanout
(nclasses+1) the fork's number of output links for each class.
The transformed layer and the bookkeeping the fixed point needs to drive it and to merge its results ...
One job class of the network.
std::size_t refstat
1-based reference station
bool completes
Whether passage through the reference station is a COMPLETION.
int attr_kind
LayeredNetworkElement of the element it stands for.
std::size_t attr_idx
index of that element
double population
infinite for an open class
bool is_ref_class
marks the chain's reference class
One station of the network.
double nservers
may be infinite (a Delay, or an inf-scheduled task)