LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
Loading...
Searching...
No Matches
sn_fj_nodevisits_mmt.h
Go to the documentation of this file.
1
/*
2
* Copyright (c) 2012-2026, QORE Lab, Imperial College London
3
* All rights reserved.
4
*/
5
#ifndef LINE_API_SN_SN_FJ_NODEVISITS_MMT_H
6
#define LINE_API_SN_SN_FJ_NODEVISITS_MMT_H
7
8
/**
9
* @file
10
* @ingroup api_sn
11
* Post-MMT node visits of a fork-join model.
12
*
13
* Port of the fork block of `matlab/src/lang/@MNetwork/refreshStruct.m`
14
* (twins: the `sn.fj.any() && !isFJAugmented` tail of `Network.java:refreshStruct`,
15
* and `network.py:_refresh_fork_join_nodevisits`).
16
*
17
* WHY THE ROUTING ANSWER IS NOT THE ANSWER. `sn_refresh_visits` solves one
18
* traffic equation per chain, so a job that a Fork splits into siblings is
19
* counted once: its blunt fork correction leaves every visited node at 1 and a
20
* Join at its in-degree. That is the visit vector of ONE token, not of the
21
* work the fork actually releases. The reference recovers the rest from the MMT
22
* transformation, whose auxiliary open classes ARE the siblings: for every
23
* auxiliary chain the transformed layer grows, the original class's node visits
24
* become
25
*
26
* V_orig(:,r) <- tasksPerLink * ( V_orig(:,r) + V_aux(:,r) )
27
*
28
* with `V_aux` read off the auxiliary chain and the transformed layer's own
29
* Source, Sink and Fork rows zeroed (they carry the auxiliary arrival, not a
30
* visit of the original class). On the two-branch closed fork-join this turns
31
* (Think,F,Q1,Q2,J) = (1,1,1,1,2) into (1,2,1.5,1.5,3), which is what MATLAB,
32
* the JAR and native Python all report.
33
*
34
* `V_orig` ON THE RIGHT IS THE PRE-CORRECTION SNAPSHOT, and the reference is a
35
* value-semantics MATLAB struct where that happens for free: it reads `sn` and
36
* writes `self.sn`. Two forks feeding the same class therefore do not compound
37
* -- the last auxiliary chain wins, over the same untouched snapshot -- so this
38
* port keeps the snapshot explicitly rather than accumulating in place.
39
*
40
* NOT APPLIED to a struct that came out of `fj_tag` (`isfjaugmented`): there the
41
* Fork nodes are already resolved into tagged classes, and the reference skips
42
* the correction for exactly that reason.
43
*
44
* ARITHMETIC: field. One multiplication and one addition per entry.
45
*/
46
47
#include <cstddef>
48
#include <string>
49
#include <vector>
50
51
#include "
line/lang/qn/network_struct.h
"
52
#include "
line/num/number.h
"
53
#include "
line/solvers/mva/fj_mmt.h
"
54
#include "
line/util/matrix.h
"
55
56
namespace
line
{
57
namespace
api
{
58
59
/**
60
* Rewrite `sn.nodevisits` with the MMT correction. A no-op on a model with no
61
* fork-join pair, on a tag-augmented struct, and whenever the transformation
62
* grows no auxiliary class.
63
*/
64
template
<
class
T>
65
void
sn_fj_nodevisits_mmt
(
qn::NetworkStruct<T>
&
sn
) {
66
if
(
sn
.fj.empty() ||
sn
.isfjaugmented)
return
;
67
const
mva::FjMmt<T>
tr
=
mva::fj_mmt
(
sn
);
68
if
(!
tr
.active())
return
;
69
const
qn::NetworkStruct<T>
& V =
tr
.V;
70
if
(V.
nchains
<=
sn
.nchains)
return
;
71
72
// Every write reads the PRE-correction visits; see the header.
73
const
std::vector<Matrix<T>> X =
sn
.nodevisits;
74
const
std::size_t I =
sn
.nodes.size();
75
76
// The transformed layer keeps the base nodes and appends its own Source and
77
// Sink, so a row of V maps to a base row by NAME, and a row with no base
78
// counterpart contributes nothing.
79
std::vector<std::size_t> vrow_to_base(V.
nodes
.size(), 0);
// 1-based, 0 = absent
80
for
(std::size_t a = 0; a < V.
nodes
.size(); ++a) {
81
const
qn::NodeType
nt = V.
nodes
[a].nodetype;
82
if
(nt == qn::NodeType::Source || nt == qn::NodeType::Sink ||
83
nt == qn::NodeType::Fork)
84
continue
;
// the reference zeroes these rows before reading V_aux
85
for
(std::size_t i = 0; i < I; ++i)
86
if
(
sn
.nodes[i].name == V.
nodes
[a].name) {
87
vrow_to_base[a] = i + 1;
88
break
;
89
}
90
}
91
92
for
(std::size_t
nc
=
sn
.nchains;
nc
< V.
nchains
; ++
nc
) {
93
const
std::vector<std::size_t>& aux = V.
inchain
[
nc
];
94
if
(aux.empty())
continue
;
95
const
std::size_t a0 = aux[0];
96
if
(a0 >=
tr
.fjclassmap.size() ||
tr
.fjclassmap[a0] == 0)
continue
;
97
const
std::size_t forkIdx =
tr
.fjforkmap[a0];
98
if
(forkIdx >=
tr
.forks.size())
continue
;
99
const
T fanOut =
num_traits<T>::from_double
(
tr
.forks[forkIdx].fanOut);
100
101
// the base chain of the original class this auxiliary chain mirrors
102
std::size_t origChain = V.
nchains
;
103
for
(std::size_t c = 0; c <
sn
.nchains; ++c)
104
for
(std::size_t r :
sn
.inchain[c])
105
if
(r ==
tr
.fjclassmap[a0]) origChain = c;
106
if
(origChain >=
sn
.nchains)
continue
;
107
108
for
(std::size_t jaux = 0; jaux < aux.size(); ++jaux) {
109
const
std::size_t a = aux[jaux];
110
if
(a >=
tr
.fjclassmap.size() ||
tr
.fjclassmap[a] == 0)
continue
;
111
const
std::size_t r =
tr
.fjclassmap[a];
// original class mirrored by a
112
113
std::vector<T> vaux(I,
num_traits<T>::from_int
(0));
114
for
(std::size_t b = 0; b < V.
nodes
.size(); ++b)
115
if
(vrow_to_base[b] != 0) vaux[vrow_to_base[b] - 1] = V.
nodevisits
[
nc
](b, a - 1);
116
117
for
(std::size_t i = 0; i < I; ++i)
118
sn
.nodevisits[origChain](i, r - 1) = T(fanOut * (X[origChain](i, r - 1) + vaux[i]));
119
}
120
}
121
}
122
123
}
// namespace api
124
}
// namespace line
125
126
#endif
// LINE_API_SN_SN_FJ_NODEVISITS_MMT_H
line::qn::NetworkStruct
A network plus its refreshed NetworkStruct.
Definition
network_struct.h:838
line::qn::NetworkStruct::nodevisits
std::vector< Matrix< T > > nodevisits
(nchains) each (nnodes x nclasses)
Definition
network_struct.h:1130
line::qn::NetworkStruct::nchains
std::size_t nchains
Definition
network_struct.h:1074
line::qn::NetworkStruct::inchain
std::vector< std::vector< std::size_t > > inchain
1-based class indices per chain
Definition
network_struct.h:1127
line::qn::NetworkStruct::nodes
std::vector< NodeDef > nodes
every node, in creation order
Definition
network_struct.h:850
fj_mmt.h
The fork-join transform SolverMVA applies before solving a layer that contains a Fork.
matrix.h
Dense matrix and non-owning view.
line::api
Definition
infer_fmlps.h:69
line::api::sn_fj_nodevisits_mmt
void sn_fj_nodevisits_mmt(qn::NetworkStruct< T > &sn)
Rewrite sn.nodevisits with the MMT correction.
Definition
sn_fj_nodevisits_mmt.h:65
line::lang::NodeType
NodeType
Node kinds, with the values of MATLAB NodeType.
Definition
lang_types.h:324
line::mva::fj_mmt
FjMmt< T > fj_mmt(const qn::NetworkStruct< T > &L)
Build the transformed layer.
Definition
fj_mmt.h:444
line::nc
Definition
nc_dispatch.h:57
line::sn
Definition
sn_gd_balance.h:42
line::tr
Definition
fj_tag_transform.h:45
line
Definition
aoi_dist2ph.h:52
network_struct.h
A queueing network and its refreshed NetworkStruct.
number.h
Number-type abstraction for the templated API port.
line::mva::FjMmt
The transformed layer and the bookkeeping the fixed point needs to drive it and to merge its results ...
Definition
fj_mmt.h:113
line::num_traits
Definition
number.h:111
include
line
api
sn
sn_fj_nodevisits_mmt.h
Generated by
1.18.0