5#ifndef LINE_API_SN_SN_FJ_VISITS_SPN_H
6#define LINE_API_SN_SN_FJ_VISITS_SPN_H
68void resolve_fork_dests(
const qn::NetworkStruct<T>& sn,
const Matrix<T>& P_r,
69 const std::vector<bool>& visited, std::size_t fork_nd, std::size_t cls,
70 double weight, std::vector<std::size_t>* out, std::vector<double>* wout,
71 std::vector<bool>* seen) {
72 const std::size_t I = sn.nodes.size();
73 if ((*seen)[fork_nd])
return;
74 (*seen)[fork_nd] =
true;
75 const qn::ForkParam<T>* fk = sn.fork_param_of(fork_nd + 1);
76 const bool variable = fk != 0;
77 for (std::size_t j = 0; j < I; ++j) {
78 if (!(num_traits<T>::to_double(P_r(fork_nd, j)) > 0.0) || !visited[j])
continue;
82 if (variable && j < fk->fan_out_link.rows() && cls < fk->fan_out_link.cols())
83 w *= num_traits<T>::to_double(fk->fan_out_prob(j, cls)) *
84 num_traits<T>::to_double(fk->fan_out_link(j, cls));
85 if (sn.nodes[j].nodetype == qn::NodeType::Fork) {
86 resolve_fork_dests(sn, P_r, visited, j, cls, w, out, wout, seen);
87 }
else if (sn.nodes[j].station != 0) {
96std::vector<std::size_t> fork_leaves(
const qn::NetworkStruct<T>& sn,
const Matrix<T>& P_r,
97 const std::vector<bool>& visited, std::size_t fork_nd,
98 std::size_t cls = 0, std::vector<double>* weights = 0) {
99 std::vector<std::size_t> out;
100 std::vector<double> w;
101 std::vector<bool> seen(sn.nodes.size(),
false);
102 resolve_fork_dests(sn, P_r, visited, fork_nd, cls, 1.0, &out, &w, &seen);
103 if (weights) *weights = w;
144 const std::vector<bool>& visited, std::size_t cls = 0) {
145 const std::size_t I =
sn.nodes.size();
149 for (std::size_t nd = 0; nd < I; ++nd) {
150 if (!visited[nd])
continue;
152 if (t == qn::NodeType::Fork) out.
forkNodes.push_back(nd);
153 else if (t == qn::NodeType::Join) out.
joinNodes.push_back(nd);
154 else if (
sn.nodes[nd].station != 0 && t != qn::NodeType::Source &&
155 t != qn::NodeType::Sink)
162 for (std::size_t pass = 0; pass < out.
joinNodes.size(); ++pass)
163 for (std::size_t ji = 0; ji < out.
joinNodes.size(); ++ji) {
164 const std::size_t jnd = out.
joinNodes[ji];
166 for (std::size_t src = 0; src < I; ++src) {
168 if (
sn.nodes[src].nodetype == qn::NodeType::Join && out.
joinLeaves[src] > 0)
179 for (std::size_t fi = 0; fi < out.
forkNodes.size(); ++fi) {
180 const std::size_t fnd = out.
forkNodes[fi];
181 bool outermost =
true;
182 for (std::size_t src = 0; src < I; ++src)
184 sn.nodes[src].nodetype == qn::NodeType::Join)
186 if (!outermost)
continue;
188 std::vector<std::size_t> frontier(1, fnd);
189 for (std::size_t h = 0; h < frontier.size(); ++h) {
190 const std::size_t nd = frontier[h];
191 for (std::size_t j = 0; j < I; ++j) {
193 if (
sn.nodes[j].nodetype == qn::NodeType::Join || out.
inRegion[j])
continue;
195 frontier.push_back(j);
198 std::vector<double> leafw;
199 const std::vector<std::size_t> leaves =
200 fjvdetail::fork_leaves(
sn, P_r, visited, fnd, cls, &leafw);
203 "sn_fj_visits_spn: a Fork reaches no station on any branch, so the auxiliary net "
204 "has nothing to synchronize; the routing of this class is malformed");
207 double expected = 0.0;
208 for (std::size_t li = 0; li < leaves.size(); ++li) expected += leafw[li];
211 "sn_fj_visits_spn: a Fork emits no task in expectation, so its Join can never "
212 "fire; at least one branch must be certain to emit at least one task");
213 if (expected > out.
B) out.
B = expected;
215 for (std::size_t ji = 0; ji < out.
joinNodes.size(); ++ji)
218 "sn_fj_visits_spn: a Join has no branch feeding it, so the auxiliary net cannot "
219 "be balanced and its throughputs would not be uniform");
232 const std::size_t I =
sn.nodes.size(), K =
sn.nclasses, C =
sn.nchains;
233 std::vector<Matrix<T>> nodevisits(C,
Matrix<T>(I, K, zero));
234 if (
sn.fj.empty())
return nodevisits;
236 const double fineTol = 1e-8;
237 for (std::size_t c = 0; c < C; ++c) {
238 if (c >=
sn.inchain.size() ||
sn.inchain[c].empty())
continue;
239 const std::vector<std::size_t>& classes =
sn.inchain[c];
241 for (std::size_t ci = 0; ci < classes.size(); ++ci) {
242 const std::size_t r = classes[ci] - 1;
246 if (
sn.rtnodes.rows() == I * K &&
sn.rtnodes.cols() == I * K)
247 for (std::size_t i = 0; i < I; ++i)
248 for (std::size_t j = 0; j < I; ++j) P_r(i, j) =
sn.rtnodes(i * K + r, j * K + r);
251 const std::size_t refstat =
sn.classes[r].refstat;
252 if (refstat == 0 || refstat >
sn.station_to_node.size())
continue;
253 const std::size_t refnode =
sn.station_to_node[refstat - 1] - 1;
254 std::vector<bool> visited(I,
false);
255 visited[refnode] =
true;
259 for (std::size_t i = 0; i < I; ++i) {
260 if (!visited[i])
continue;
261 for (std::size_t j = 0; j < I; ++j)
269 bool hasFork =
false;
270 for (std::size_t i = 0; i < I; ++i)
271 if (visited[i] &&
sn.nodes[i].nodetype == qn::NodeType::Fork) hasFork =
true;
276 for (std::size_t i = 0; i < I; ++i)
277 if (visited[i]) nodevisits[c](i, r) = one;
288 for (std::size_t si = 0; si < st.
stationNodes.size(); ++si) {
290 nodevisits[c](nd, r) = st.
inRegion[nd] ? invB : one;
295 const std::size_t r0 = classes[0] - 1;
296 const std::size_t refstat0 =
sn.classes[r0].refstat;
297 if (refstat0 == 0 || refstat0 >
sn.station_to_node.size())
continue;
298 const std::size_t refnode_c =
sn.station_to_node[refstat0 - 1] - 1;
299 for (std::size_t ci = 0; ci < classes.size(); ++ci) {
300 const std::size_t r = classes[ci] - 1;
301 const T nv = nodevisits[c](refnode_c, r);
303 for (std::size_t i = 0; i < I; ++i) nodevisits[c](i, r) = nodevisits[c](i, r) / nv;
A network plus its refreshed NetworkStruct.
The exception types the port throws.
Dense matrix and non-owning view.
FjSpnStructure fj_spn_structure(const qn::NetworkStruct< T > &sn, const Matrix< T > &P_r, const std::vector< bool > &visited, std::size_t cls=0)
Classify one class's visited subgraph and size the auxiliary net.
std::vector< Matrix< T > > sn_fj_visits_spn(const qn::NetworkStruct< T > &sn)
Per-chain fork-join node visit ratios.
NodeType
Node kinds, with the values of MATLAB NodeType.
A queueing network and its refreshed NetworkStruct.
Number-type abstraction for the templated API port.
What the auxiliary construction found, for one class.
std::vector< std::size_t > stationNodes
std::vector< std::size_t > joinNodes
0-based node indices
double B
Circulating population: the largest EXPECTED outermost leaf count.
std::vector< std::size_t > joinLeaves
per node, the leaves a Join synchronizes
std::vector< bool > inRegion
Per node, whether it sits INSIDE a fork-join region: reachable from an outermost Fork without crossin...
std::vector< std::size_t > forkNodes