1%{ @file fj_is_homogeneous.m
2 % @brief Checks
for a single fork-join pair with homogeneous parallel branches
4 % @author LINE Development Team
8 % @brief Checks
for a single fork-join pair with homogeneous parallel branches
11 % This
is NOT a test
for the presence of Fork/Join
nodes (use sn_has_fork_join
12 %
for that): most fork-join models fail this predicate. It tests membership in
13 % the homogeneous class on which the FJ_codes tail approximation of Qiu, Perez
14 % and Harrison (IFIP Performance 2015)
is defined, i.e. the requirements:
15 % - Single Fork-Join pair
16 % - K parallel queues between Fork and Join
17 % - Homogeneous service distributions across parallel queues
18 % - Supported distributions (Exp, HyperExp(2), Erlang(2), MAP(2))
23 % [isHomogeneous, fjInfo] = fj_is_homogeneous(sn)
28 % <tr><th>Name<th>Description
29 % <tr><td>sn<td>Network structure
34 % <tr><th>Name<th>Description
35 % <tr><td>isHomogeneous<td>True if network
is valid FJ topology for FJ_codes
36 % <tr><td>fjInfo<td>Struct with fields: forkIdx, joinIdx, queueIdx, K, errorMsg
40 % Z. Qiu, J.F. Pérez, and
P. Harrison,
"Beyond the Mean in Fork-Join Queues:
41 % Efficient Approximation for Response-Time Tails", IFIP Performance 2015.
42 % Copyright 2015 Imperial College London
44function [isHomogeneous, fjInfo] = fj_is_homogeneous(sn)
55% Check
if model has open classes only
56if ~sn_is_open_model(sn)
57 fjInfo.errorMsg =
'FJ_codes only supports open queueing models.';
61% Check
if network has fork-join
62if ~sn_has_fork_join(sn)
63 fjInfo.errorMsg =
'Network does not contain Fork-Join structure.';
67% Find Fork and Join
nodes
68forkIndices = find(sn.nodetype == NodeType.Fork);
69joinIndices = find(sn.nodetype == NodeType.Join);
71if isempty(forkIndices) || isempty(joinIndices)
72 fjInfo.errorMsg =
'Network must contain both Fork and Join nodes.';
76% FJ_codes supports single Fork-Join pair
77if length(forkIndices) > 1
78 fjInfo.errorMsg =
'FJ_codes only supports a single Fork-Join pair. Found multiple Fork nodes.';
82if length(joinIndices) > 1
83 fjInfo.errorMsg =
'FJ_codes only supports a single Fork-Join pair. Found multiple Join nodes.';
87forkIdx = forkIndices(1);
88joinIdx = joinIndices(1);
90% Check
if Fork and Join are paired
using sn.fj matrix
91if sn.fj(forkIdx, joinIdx) == 0
92 fjInfo.errorMsg = sprintf(
'Fork node %d and Join node %d are not paired.', forkIdx, joinIdx);
96fjInfo.forkIdx = forkIdx;
97fjInfo.joinIdx = joinIdx;
99% Find queues between Fork and Join
100% These are
nodes that receive routing from Fork and route to Join
103 if sn.nodetype(i) == NodeType.Queue
104 % Check
if this queue
is in a path from Fork to Join
105 % Use rtnodes which
is indexed by node indices (not station indices)
106 hasForkInput =
false;
107 hasJoinOutput =
false;
109 % Check
if Fork routes to
this queue (
using rtnodes
for node-based routing)
110 if sn.rtnodes(forkIdx, i) > 0
113 % Check
if this queue routes to Join
114 if sn.rtnodes(i, joinIdx) > 0
115 hasJoinOutput =
true;
118 if hasForkInput && hasJoinOutput
119 queueIdx = [queueIdx, i];
125 fjInfo.errorMsg =
'No Queue nodes found between Fork and Join.';
130fjInfo.queueIdx = queueIdx;
133% Validate homogeneous service distributions across parallel queues
134% For each
class, all K queues must have the same service distribution
136 % Get PH representation of first queue
's service distribution
137 firstQueueIdx = queueIdx(1);
138 firstPH = sn.proc{sn.nodeToStation(firstQueueIdx)}{r};
140 if isempty(firstPH) || isnan(firstPH{1}(1))
141 fjInfo.errorMsg = sprintf('Queue %d has no valid service distribution
for class %d.
', ...
146 % Check all other queues have the same distribution
148 queueIdx_k = queueIdx(k);
149 ph_k = sn.proc{sn.nodeToStation(queueIdx_k)}{r};
151 if isempty(ph_k) || isnan(ph_k{1}(1))
152 fjInfo.errorMsg = sprintf('Queue %d has no valid service distribution
for class %d.
', ...
157 % Compare PH representations (must be identical)
158 % Compare number of phases
159 if length(ph_k{1}) ~= length(firstPH{1})
160 fjInfo.errorMsg = sprintf('Queues have heterogeneous service distributions
for class %d. FJ_codes
requires homogeneous servers.
', r);
164 % Compare initial probability vector and rate matrix
165 if ~isequal(size(ph_k{1}), size(firstPH{1})) || ...
166 ~isequal(size(ph_k{2}), size(firstPH{2})) || ...
167 max(abs(ph_k{1} - firstPH{1})) > GlobalConstants.FineTol || ...
168 max(max(abs(ph_k{2} - firstPH{2}))) > GlobalConstants.FineTol
169 fjInfo.errorMsg = sprintf('Queues have heterogeneous service distributions
for class %d. FJ_codes
requires homogeneous servers.
', r);
175% Validate supported scheduling strategies (FCFS or PS)
177 queueSt = sn.nodeToStation(queueIdx(k));
178 if sn.sched(queueSt) ~= SchedStrategy.FCFS && sn.sched(queueSt) ~= SchedStrategy.PS
179 fjInfo.errorMsg = sprintf('Queue %d has unsupported scheduling strategy. FJ_codes supports FCFS or PS only.
', queueIdx(k));
184% All validations passed