1function [outspace, outrate, outprob, eventCache] = afterEventStationPAS(sn, ind, ist, inspace, event,
class, isSimulation, eventCache, R, V, key) %#ok<INUSL>
2% [OUTSPACE, OUTRATE, OUTPROB, EVENTCACHE] = AFTEREVENTSTATIONPAS(...)
4% Event handler
for pass-and-swap (PAS) / order-independent (OI) stations.
6% State layout (see State.fromMarginal): the local state
is the full ordered
7% list of class indices c=(c1,...,cn), c1 the oldest job, stored left-aligned
8% in the first W = sn.cap(ist) columns and right zero-padded; the trailing V
9% columns hold routing local variables (carried through unchanged). There
is
10% no server/buffer split: service
is governed by the total rate function mu(c)
11% (sn.nodeparam{ind}.svcRateFun) and the swapping graph G
12% (sn.nodeparam{ind}.swapGraph), per Dorsman & Gardner (2024), Sect. 2.
14% - ARV (passive): a class-`class` job joins at the back, c -> (c, class),
15% subject to capacity (lost when full).
16% - DEP (active): for each position p, the service token of position p fires at
17% rate Delta_mu(c1..cp) = mu(c1..cp) - mu(c1..c_{p-1}); the pass-and-swap
18% mechanism (State.passAndSwap) then determines the departing class. A DEP of
19% `class` collects all positions whose pass-and-swap ejects a class-`class`
21% - PHASE: none (PAS service
is exponential).
23% Copyright (c) 2012-2026, Imperial College London
30muFun = sn.nodeparam{ind}.svcRateFun;
31G = sn.nodeparam{ind}.swapGraph;
33 line_error(mfilename,
'PAS station has no service rate function mu(c); set it via setService(@(c) ...).');
36W = size(inspace,2) - V; % width of the ordered-list region
38varcols = inspace(:,(W+1):end); % routing local variables
39nrows = size(inspace,1);
43 case EventType.ARV % passive: append the arriving job at the back of the list
45 c = list(row, list(row,:)>0); % current ordered list (contiguous, left-aligned)
48 continue; % buffer full: arrival
is lost
51 outspace = [outspace; newc, zeros(1, W-numel(newc)), varcols(row,:)]; %#ok<AGROW>
52 outrate = [outrate; -1]; % passive action, rate unspecified %#ok<AGROW>
53 outprob = [outprob; 1]; %#ok<AGROW>
55 case EventType.DEP % active: a
class-`
class` job departs via pass-and-swap
57 c = list(row, list(row,:)>0);
62 muPrev = 0; % mu of the empty prefix = 0
64 muCur = muFun(c(1:p));
65 ratep = muCur - muPrev; % Delta_mu(c1..cp): rate of position p
68 continue; % position p receives no service
70 [cnew, depClass] = State.passAndSwap(c, p, G);
72 continue; % a job of a different
class departs
74 outspace = [outspace; cnew, zeros(1, W-numel(cnew)), varcols(row,:)]; %#ok<AGROW>
75 outrate = [outrate; ratep]; %#ok<AGROW>
76 outprob = [outprob; 1]; %#ok<AGROW>
80 % PAS service
is exponential: no intra-service phase transitions.
84 if ~isnan(key) && isobject(eventCache)
85 eventCache(key) = {outprob, outspace, outrate};
87 if size(outspace,1) > 1
88 if event == EventType.DEP
89 tot_rate = sum(outrate);
90 cum_rate = cumsum(outrate) / tot_rate;
91 firing_ctr = 1 + max([0, find(rand > cum_rate
')]);
92 outspace = outspace(firing_ctr,:);
93 outrate = sum(outrate);
94 outprob = outprob(firing_ctr,:);
96 cum_prob = cumsum(outprob) / sum(outprob);
97 firing_ctr = 1 + max([0, find(rand > cum_prob')]);
98 outspace = outspace(firing_ctr,:);