2 % @brief Checks
if network
is a valid Fork-Join topology
for FJ_codes analysis
4 % @author LINE Development Team
8 % @brief Checks
if network
is a valid Fork-Join topology
for FJ_codes analysis
11 % Validates that the network structure matches the requirements
for FJ_codes:
12 % - Single Fork-Join pair
13 % - K parallel queues between Fork and Join
14 % - Homogeneous service distributions across parallel queues
15 % - Supported distributions (Exp, HyperExp(2), Erlang(2), MAP(2))
20 % [isFJ, fjInfo] = fj_isfj(sn)
25 % <tr><th>Name<th>Description
26 % <tr><td>sn<td>Network structure
31 % <tr><th>Name<th>Description
32 % <tr><td>isFJ<td>True
if network
is valid FJ topology
for FJ_codes
33 % <tr><td>fjInfo<td>Struct with fields: forkIdx, joinIdx, queueIdx, K, errorMsg
37 % Z. Qiu, J.F. Pérez, and
P. Harrison,
"Beyond the Mean in Fork-Join Queues:
38 % Efficient Approximation for Response-Time Tails", IFIP Performance 2015.
39 % Copyright 2015 Imperial College London
41function [isFJ, fjInfo] = fj_isfj(sn)
52% Check
if model has open
classes only
53if ~sn_is_open_model(sn)
54 fjInfo.errorMsg =
'FJ_codes only supports open queueing models.';
58% Check
if network has fork-join
59if ~sn_has_fork_join(sn)
60 fjInfo.errorMsg = 'Network does not contain Fork-Join structure.';
64% Find Fork and Join
nodes
65forkIndices = find(sn.nodetype == NodeType.Fork);
66joinIndices = find(sn.nodetype == NodeType.Join);
68if isempty(forkIndices) || isempty(joinIndices)
69 fjInfo.errorMsg =
'Network must contain both Fork and Join nodes.';
73% FJ_codes supports single Fork-Join pair
74if length(forkIndices) > 1
75 fjInfo.errorMsg = 'FJ_codes only supports a single Fork-Join pair. Found multiple Fork
nodes.';
79if length(joinIndices) > 1
80 fjInfo.errorMsg =
'FJ_codes only supports a single Fork-Join pair. Found multiple Join nodes.';
84forkIdx = forkIndices(1);
85joinIdx = joinIndices(1);
87% Check
if Fork and Join are paired
using sn.fj matrix
88if sn.fj(forkIdx, joinIdx) == 0
89 fjInfo.errorMsg = sprintf(
'Fork node %d and Join node %d are not paired.', forkIdx, joinIdx);
93fjInfo.forkIdx = forkIdx;
94fjInfo.joinIdx = joinIdx;
96% Find queues between Fork and Join
97% These are
nodes that receive routing from Fork and route to Join
100 if sn.nodetype(i) == NodeType.Queue
101 % Check
if this queue
is in a path from Fork to Join
102 % Use rtnodes which
is indexed by node indices (not station indices)
103 hasForkInput =
false;
104 hasJoinOutput =
false;
106 % Check
if Fork routes to
this queue (
using rtnodes
for node-based routing)
107 if sn.rtnodes(forkIdx, i) > 0
110 % Check
if this queue routes to Join
111 if sn.rtnodes(i, joinIdx) > 0
112 hasJoinOutput =
true;
115 if hasForkInput && hasJoinOutput
116 queueIdx = [queueIdx, i];
122 fjInfo.errorMsg =
'No Queue nodes found between Fork and Join.';
127fjInfo.queueIdx = queueIdx;
130% Validate homogeneous service distributions across parallel queues
131% For each
class, all K queues must have the same service distribution
133 % Get PH representation of first queue
's service distribution
134 firstQueueIdx = queueIdx(1);
135 firstPH = sn.proc{sn.nodeToStation(firstQueueIdx)}{r};
137 if isempty(firstPH) || isnan(firstPH{1}(1))
138 fjInfo.errorMsg = sprintf('Queue %d has no valid service distribution
for class %d.
', ...
143 % Check all other queues have the same distribution
145 queueIdx_k = queueIdx(k);
146 ph_k = sn.proc{sn.nodeToStation(queueIdx_k)}{r};
148 if isempty(ph_k) || isnan(ph_k{1}(1))
149 fjInfo.errorMsg = sprintf('Queue %d has no valid service distribution
for class %d.
', ...
154 % Compare PH representations (must be identical)
155 % Compare number of phases
156 if length(ph_k{1}) ~= length(firstPH{1})
157 fjInfo.errorMsg = sprintf('Queues have heterogeneous service distributions
for class %d. FJ_codes
requires homogeneous servers.
', r);
161 % Compare initial probability vector and rate matrix
162 if ~isequal(size(ph_k{1}), size(firstPH{1})) || ...
163 ~isequal(size(ph_k{2}), size(firstPH{2})) || ...
164 max(abs(ph_k{1} - firstPH{1})) > GlobalConstants.FineTol || ...
165 max(max(abs(ph_k{2} - firstPH{2}))) > GlobalConstants.FineTol
166 fjInfo.errorMsg = sprintf('Queues have heterogeneous service distributions
for class %d. FJ_codes
requires homogeneous servers.
', r);
172% Validate supported scheduling strategies (FCFS or PS)
174 queueSt = sn.nodeToStation(queueIdx(k));
175 if sn.sched(queueSt) ~= SchedStrategy.FCFS && sn.sched(queueSt) ~= SchedStrategy.PS
176 fjInfo.errorMsg = sprintf('Queue %d has unsupported scheduling strategy. FJ_codes supports FCFS or PS only.
', queueIdx(k));
181% All validations passed