1function [ni, nir] = toMarginalAggr(sn, ind, state_i, K, Ks, space_buf, space_srv, space_var) %#ok<INUSD>
2% TOMARGINALAGGR Compute aggregate marginal distributions
for a specific node
4% [NI, NIR] = TOMARGINALAGGR(SN, IND, STATE_I, K, KS, SPACE_BUF, SPACE_SRV, SPACE_VAR)
6% @brief Computes aggregate marginal job counts from state information without phase details
8% This function provides a simplified version of toMarginal that computes
9% aggregate job counts per node and per
class, without considering individual
10% service phases. It
is more efficient when phase-level detail
is not required.
12% @param sn Network structure or Network
object
13% @param ind Node index to extract marginal information
for
14% @param state_i Global state matrix or vector
15% @param K Vector of population
for each class
16% @param Ks Matrix of populations per chain and
class
17% @param space_buf Buffer space configuration
18% @param space_srv Service space configuration
19% @param space_var Variable space configuration
21% @
return ni Total jobs in node IND (aggregate across all
classes)
22% @
return nir Jobs per
class in node IND [vector:
classes]
24% Copyright (c) 2012-2026, Imperial College London
27if ~isstruct(sn) % the input can be a Network object too
31ist = sn.nodeToStation(ind);
32%isf = sn.nodeToStateful(ind);
34if ~sn.isstation(ind) && sn.isstateful(ind) % if stateful node
35 % Strip trailing nvars columns (e.g. RROBIN pointer on a Router) and
36 % keep the buffer portion. If the buffer
is empty (the node has no
37 % per-class job buffer at all, e.g. a Router only carrying nvars
38 % bookkeeping), return zeros of the expected (1, R) shape rather than
39 % an empty slice, so callers can index nir(r) for r = 1..R.
40 nvarsSum = sum(sn.nvars(ind,:));
41 bufferLen = size(state_i, 2) - nvarsSum;
46 ni = sum(state_i(1:bufferLen));
47 nir = state_i(1:bufferLen);
55% PAS/OI stations: the state is the ordered list of class indices (no server split).
56if sn.isstation(ind) && sn.sched(ist) == SchedStrategy.PAS
57 Vp = sum(sn.nvars(ind,:));
58 listcols = state_i(:,1:(end-Vp));
59 nir = zeros(size(state_i,1),R);
61 nir(:,r) = sum(listcols==r,2);
67% Place nodes: compute total jobs per class from state
68if sn.nodetype(ind) == NodeType.Place
69 state_len = size(state_i, 2);
70 % Check if this is queue-based state (FCFS/LCFS/HOL) vs count-based
71 % Queue-based: state contains class indices [1,1,2,1] meaning job arrivals
72 % Count-based: state contains counts per class [n1, n2, ...] or [buf(R), srv]
73 % Detect queue-based: length != R and length != 2*R and all values are valid class indices
74 is_queue_based = state_len ~= R && state_len ~= 2*R;
75 if is_queue_based && state_len > 0
76 % Check all values are valid class indices (1 to R)
77 all_vals = state_i(:);
78 is_queue_based = all(all_vals >= 1 & all_vals <= R);
82 % Queue-based format: count occurrences of each class
83 nir = zeros(size(state_i,1), R);
85 nir(:,r) = sum(state_i == r, 2);
87 elseif nargin >= 4 && ~isempty(K)
88 expected_len = R + sum(K);
89 if state_len == expected_len
90 % State has [buffer, server] format
91 buf_part = state_i(:, 1:R);
92 srv_part = state_i(:, (R+1):end);
96 nir(:,r) = nir(:,r) + srv_part(:, Ks(r)+k);
100 % State
is just counts per class (simple SIRO format)
103 % Unknown format - just take first R columns
104 nir = state_i(:, 1:min(R, state_len));
106 nir = [nir, zeros(size(nir,1), R - size(nir,2))];
110 % No K provided - assume state is counts per class
111 nir = state_i(:, 1:min(R, size(state_i, 2)));
113 nir = [nir, zeros(size(nir,1), R - size(nir,2))];
120% Source nodes have infinite population but report queue length 0 (jobs are external)
121if sn.nodetype(ind) == NodeType.Source
122 nir = zeros(size(state_i,1), R);
123 ni = zeros(size(state_i,1), 1);
128 K = sn.phasessz(ist,:);
131 Ks = sn.phaseshift(ist,:);
135 space_var = state_i(:,(end-sum(sn.nvars(ind,:))+1):end); % server state
136 space_srv = state_i(:,(end-sum(K)+1):(end-sum(sn.nvars(ind,:))));
137 space_buf = state_i(:,1:(end-sum(K)));
140nir = zeros(size(state_i,1),R); % class-r jobs in service
143 nir(:,r) = nir(:,r) + space_srv(:,Ks(r)+k);
147 case SchedStrategy.EXT
151 case SchedStrategy.FCFS
153 nir(:,r) = nir(:,r) + sum(space_buf==r,2); % class-r jobs in station
155 case SchedStrategy.HOL
157 nir(:,r) = nir(:,r) + sum(space_buf==r,2); % class-r jobs in station
159 case SchedStrategy.LCFS
161 nir(:,r) = nir(:,r) + sum(space_buf==r,2); % class-r jobs in station
163 case SchedStrategy.SIRO
165 nir(:,r) = nir(:,r) + space_buf(:,r); % class-r jobs in station
167 case SchedStrategy.SEPT
169 nir(:,r) = nir(:,r) + space_buf(:,r); % class-r jobs in station
171 case SchedStrategy.LEPT
173 nir(:,r) = nir(:,r) + space_buf(:,r); % class-r jobs in station
175 %otherwise % possibly other stateful nodes
180 if isnan(sn.rates(ist,r)) && sn.nodetype(ind) ~= NodeType.Place % if disabled
185ni = sum(nir,2); % total jobs in station