1%{ @file fes_compute_throughputs.m
2 % @brief Computes throughputs
for all population states
for FES scaling table
4 % @author LINE Development Team
8 % @brief Computes throughputs
for all population states
11 % Enumerates all population states (n1, n2, ..., nK) up to
the specified
12 % cutoffs and solves
the isolated subnetwork with MVA to get per-
class throughputs.
13 % These throughputs are stored in a linearized scaling table
for use as
14 % Limited Joint Dependence (LJD) service rates in
the Flow-Equivalent Server.
18 %
scalingTable = fes_compute_throughputs(L, mi, isDelay, cutoffs, options)
23 % <tr><th>Name<th>Description
24 % <tr><td>L<td>Service demands matrix (M_sub x K) from fes_build_isolated
25 % <tr><td>mi<td>Number of servers per station (1 x M_sub), Inf
for Delay
26 % <tr><td>isDelay<td>Boolean array (1 x M_sub),
true if station
is a Delay
27 % <tr><td>cutoffs<td>Per-class population cutoffs [N1, N2, ..., NK]
28 % <tr><td>options<td>Options struct with field .verbose (default: false)
33 % <tr><th>Name<th>Description
34 % <tr><td>scalingTable<td>Cell array {
class} of linearized throughput vectors
37function
scalingTable = fes_compute_throughputs(L, mi, isDelay, cutoffs, options)
42if ~isfield(options,
'verbose')
43 options.verbose = false;
49% Separate Queue and Delay stations for MVA
50% Queue stations go into L_queue, Delay stations contribute to Z (think time)
51queueIdx = find(~isDelay);
52delayIdx = find(isDelay);
54M_queue = length(queueIdx);
56% Extract Queue station demands and servers
58 L_queue = L(queueIdx, :);
59 mi_queue = mi(queueIdx);
61 % No Queue stations - all Delay
62 L_queue = zeros(0, K);
66% Compute think time Z from Delay stations
67% Z(k) = sum of service demands at Delay stations for class k
69 Z = sum(L(delayIdx, :), 1);
75tableSize = prod(cutoffs + 1);
77% Initialize scaling tables (one per class)
84 fprintf(
'Computing FES throughputs for %d states (%d Queue, %d Delay stations)...\n', ...
85 tableSize, M_queue, length(delayIdx));
88% Enumerate all states
using BFS
89stateQueue = {zeros(1, K)}; % Start with zero population
90% Each population state maps to a unique linearized index in 1..tableSize,
91% so a logical array suffices to track which states have been visited.
92visited =
false(1, tableSize);
95while ~isempty(stateQueue)
99 % Linearized index uniquely identifies
this state
100 idx = ljd_linearize(nvec, cutoffs);
105 nStates = nStates + 1;
107 % Compute throughput
for this state
108 totalPop = sum(nvec);
111 % Zero population: throughput
is 0
for all classes
116 % Solve with pfqn_mva
119 % Call MVA with Queue stations and think times
120 [XN, ~, ~, ~] = pfqn_mva(L_queue, nvec, Z, mi_queue);
122 % Only Delay stations - throughput
is N(k) / Z(k) for each class
125 if nvec(k) > 0 && Z(k) > 0
126 XN(k) = nvec(k) / Z(k);
140 % If solver fails, set throughput to 0
142 fprintf(
'Warning: MVA failed for state %s: %s\n', mat2str(nvec), ME.message);
150 % Add neighboring states to queue
152 if nvec(k) < cutoffs(k)
154 newState(k) = newState(k) + 1;
155 newIdx = ljd_linearize(newState, cutoffs);
157 stateQueue{end+1} = newState;
164 fprintf(
'Computed throughputs for %d states.\n', nStates);