LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
fj_branch_members.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_FJ_FJ_BRANCH_MEMBERS_H
6#define LINE_API_FJ_FJ_BRANCH_MEMBERS_H
7
8/**
9 * @file
10 * @ingroup api_fj
11 * Activities belonging to each branch of an AND-join.
12 *
13 * Templated port of matlab/src/api/fj/fj_branch_members.m. Each immediate
14 * predecessor of the join activity is the tail of one branch; the branch is
15 * recovered by walking backwards along the activity graph until an activity
16 * marked POST_AND is reached, that one being the head the AND-fork spawned.
17 * Branches between a fork and its join are disjoint paths, so the walk is
18 * unambiguous, and the guard on the number of steps bounds it by the activity
19 * count.
20 *
21 * SCOPE. The reference takes a LayeredNetworkStruct, but it reads only four
22 * plain numeric fields of it -- graph, ashift, nacts, actposttype -- and no
23 * LayeredNetwork object, no cell array of processes and no derived index map.
24 * Those four are taken here as explicit arguments, in a small view struct, so
25 * the algorithm is ported in full without pulling the LQN object layer into
26 * this tree. Nothing is stubbed: the walk, the activity-range test, the
27 * merge/start stop condition and the guard are all as written.
28 *
29 * INDEXING. The reference is 1-based and its indices are absolute LQN element
30 * indices, activities occupying (ashift, ashift + nacts]. The port keeps that
31 * convention exactly, so an index that appears in the result can be compared
32 * against a MATLAB one without a shift: joinIdx, ashift and every returned
33 * index are 1-based absolute indices.
34 *
35 * ARITHMETIC. Only the test graph(i, j) > 0 touches the number type, so this
36 * is a structural algorithm and is instantiated at T = Rational as well.
37 */
38
39#include <cstddef>
40#include <vector>
41
42#include "line/num/number.h"
43#include "line/util/error.h"
44#include "line/util/matrix.h"
45
46namespace line {
47namespace fj {
48
49/** MATLAB's ActivityPrecedenceType codes, as stored in lqn.actposttype. */
60
61/**
62 * The four LayeredNetworkStruct fields fj_branch_members reads.
63 *
64 * graph is the full (nidx x nidx) call/precedence graph in absolute 1-based
65 * indices, actposttype is indexed the same way, and activities are the indices
66 * in (ashift, ashift + nacts].
67 */
68template <class T>
71 std::size_t ashift = 0;
72 std::size_t nacts = 0;
73 std::vector<int> actposttype; ///< absolute-index vector, 1-based reading
74};
75
76/**
77 * Branch membership of an AND-join.
78 *
79 * @param lqn the four fields listed above
80 * @param joinaidx absolute 1-based index of the AND-join activity
81 * @return one vector per branch, head last: entry 0 is the tail (the immediate
82 * predecessor of the join) and the last entry is the branch head, which
83 * is the order the reference builds the chain in
84 */
85template <class T>
86std::vector<std::vector<std::size_t>> fj_branch_members(const LqnBranchView<T>& lqn,
87 std::size_t joinaidx) {
88 const std::size_t nidx = lqn.graph.rows();
89 if (lqn.graph.cols() != nidx) throw InputError("fj_branch_members: graph is not square");
90 if (joinaidx < 1 || joinaidx > nidx)
91 throw InputError("fj_branch_members: the join index is out of the graph");
92 if (lqn.actposttype.size() < lqn.ashift + lqn.nacts)
93 throw InputError("fj_branch_members: actposttype is shorter than the activity range");
94 const T zero = num_traits<T>::from_int(0);
95
96 std::vector<std::vector<std::size_t>> members;
97 for (std::size_t tail = 1; tail <= nidx; ++tail) {
98 if (!(lqn.graph(tail - 1, joinaidx - 1) > zero)) continue;
99 if (tail <= lqn.ashift || tail > lqn.ashift + lqn.nacts) continue; // not an activity
100
101 std::vector<std::size_t> chain;
102 chain.push_back(tail);
103 std::size_t cur = tail;
104 std::size_t guard = 0;
105 while (guard < lqn.nacts) {
106 ++guard;
107 if (lqn.actposttype[cur - 1] == APC_POST_AND) break; // the branch head
108 std::size_t prev = 0;
109 std::size_t nprev = 0;
110 for (std::size_t p = 1; p <= nidx; ++p) {
111 if (!(lqn.graph(p - 1, cur - 1) > zero)) continue;
112 if (p <= lqn.ashift || p > lqn.ashift + lqn.nacts) continue;
113 ++nprev;
114 prev = p;
115 }
116 if (nprev != 1) break; // a merge, or the start of the graph
117 cur = prev;
118 chain.push_back(cur);
119 }
120 members.push_back(chain);
121 }
122 return members;
123}
124
125} // namespace fj
126} // namespace line
127
128#endif // LINE_API_FJ_FJ_BRANCH_MEMBERS_H
InputError(const std::string &what)
Definition error.h:39
The exception types the port throws.
Dense matrix and non-owning view.
std::vector< std::vector< std::size_t > > fj_branch_members(const LqnBranchView< T > &lqn, std::size_t joinaidx)
Branch membership of an AND-join.
ActivityPrecedenceCode
MATLAB's ActivityPrecedenceType codes, as stored in lqn.actposttype.
Number-type abstraction for the templated API port.
The four LayeredNetworkStruct fields fj_branch_members reads.
std::vector< int > actposttype
absolute-index vector, 1-based reading