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);
181 for (std::size_t a = 0; a <
tr.forks.size(); ++a)
182 V.
nodes[
tr.forks[a].node - 1].nodetype = NodeType::Router;
189 for (std::size_t j = 1; j <= L.
nodes.size(); ++j) {
190 if (L.
nodes[j - 1].nodetype != NodeType::Join)
continue;
191 const std::size_t st = V.
nodes[j - 1].station;
193 throw InputError(
"fj_ht: the join node '" + V.
nodes[j - 1].name +
"' of model '" +
194 L.
name +
"' is not a station");
195 V.
stations[st - 1].nodetype = NodeType::Delay;
196 V.
stations[st - 1].sched = SchedStrategy::INF;
197 V.
stations[st - 1].nservers = std::numeric_limits<double>::infinity();
198 V.
nodes[j - 1].nodetype = NodeType::Delay;
199 for (std::size_t k = 1; k <= V.
classes.size(); ++k)
201 tr.joinStations.push_back(st);
204 ad.
name =
"Auxiliary Delay - " + V.
nodes[j - 1].name;
206 ad.
sched = SchedStrategy::INF;
207 ad.
nservers = std::numeric_limits<double>::infinity();
210 tr.auxDelayStation[j] = adstat;
211 tr.auxDelayNode[j] = adnode;
212 for (std::size_t k = 1; k <= V.
classes.size(); ++k)
220 detail::fj_copy_row_all_blocks(V, j, adnode);
221 for (std::size_t r = 1; r <= V.
classes.size(); ++r) {
222 detail::fj_clear_row(V, r, r, j);
226 for (std::size_t a = 0; a <
tr.forks.size(); ++a)
227 tr.forks[a].joinStation = V.
nodes[
tr.forks[a].joinNode - 1].station;
230 tr.fjclassmap.assign(V.
classes.size() + 1, 0);
231 tr.fjforkmap.assign(V.
classes.size() + 1, 0);
232 tr.fanout.assign(V.
classes.size() + 1, 0.0);
233 tr.auxdisabled.assign(V.
classes.size() + 1,
false);
234 tr.auxbranch.assign(V.
classes.size() + 1, 0);
236 for (std::size_t fa = 0; fa <
tr.forks.size(); ++fa) {
238 const std::size_t adstat =
tr.auxDelayStation[F.
joinNode];
239 const std::size_t adnode =
tr.auxDelayNode[F.
joinNode];
242 std::vector<bool> forked(L.
nclasses + 1,
false);
243 for (std::size_t c = 0; c < L.
nchains; ++c)
244 for (std::size_t r = 1; r <= L.
nclasses; ++r)
247 for (std::size_t c = 0; c < L.
nchains; ++c) {
248 const std::vector<std::size_t>& ic = L.
inchain[c];
250 for (std::size_t x = 0; x < ic.size(); ++x)
251 if (forked[ic[x]]) any =
true;
256 std::vector<std::vector<std::size_t>> aux(L.
nclasses + 1);
257 for (std::size_t x = 0; x < ic.size(); ++x) {
258 const std::size_t r = ic[x];
260 if (std::isinf(L.
classes[r - 1].population))
262 "fj_ht: class '" + L.
classes[r - 1].name +
"' of model '" + L.
name +
263 "' is open and reaches a fork; the H-T method can be used only on closed "
264 "models, use fork_join='mmt'");
265 for (std::size_t par = 1; par <= F.
origfanout[r]; ++par) {
269 xc.
type = JobClassType::CLOSED;
287 for (std::size_t i = 1; i <= L.
stations.size(); ++i) {
289 if (nt == NodeType::Join) {
291 }
else if (nt == NodeType::Source || nt == NodeType::Fork) {
299 tr.fjclassmap.resize(V.
classes.size() + 1, 0);
300 tr.fjforkmap.resize(V.
classes.size() + 1, 0);
301 tr.fanout.resize(V.
classes.size() + 1, 0.0);
302 tr.auxdisabled.resize(V.
classes.size() + 1,
false);
303 tr.auxbranch.resize(V.
classes.size() + 1, 0);
304 tr.fjclassmap[s] = r;
305 tr.fjforkmap[s] = fa;
307 tr.auxbranch[s] = par;
308 tr.auxclasses.push_back(s);
314 for (std::size_t x = 0; x < ic.size(); ++x) {
315 const std::size_t r = ic[x];
316 if (aux[r].empty())
continue;
317 for (std::size_t y = 0; y < ic.size(); ++y) {
318 const std::size_t s = ic[y];
319 if (aux[s].empty())
continue;
320 for (std::size_t par = 1; par <= aux[r].size() && par <= aux[s].size(); ++par) {
321 const std::size_t a = aux[r][par - 1], b = aux[s][par - 1];
322 detail::fj_copy_block(V, r, s, a, b);
326 detail::fj_clear_row(V, a, b, F.
node);
329 detail::fj_clear_row(V, a, b, adnode);
336 detail::fj_clear_row(V, r, s, F.
node);
343 tr.fjclassmap.resize(V.
classes.size() + 1, 0);
344 tr.fjforkmap.resize(V.
classes.size() + 1, 0);
345 tr.fanout.resize(V.
classes.size() + 1, 0.0);
346 tr.auxdisabled.resize(V.
classes.size() + 1,
false);
347 tr.auxbranch.resize(V.
classes.size() + 1, 0);
381 if (method.empty() || method ==
"default" || method ==
"mmt" || method ==
"fjt")
383 if (method ==
"ht" || method ==
"heidelberger-trivedi")
return fj_ht(L);
385 "' is not a fork-join method; options.config.fork_join is one of "
386 "'default', 'mmt', 'fjt' (the MMT transform) or 'ht', "
387 "'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.
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.
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)