1function [outspace, outrate, outprob, eventCache] = afterEvent(sn, ind, inspace, event,
class, isSimulation, eventCache)
2% [OUTSPACE, OUTRATE, OUTPROB] = AFTEREVENT(QN, IND, INSPACE, EVENT, CLASS, ISSIMULATION, EVENTCACHE)
4% Copyright (c) 2012-2026, Imperial College London
7% Event cache
for faster simulation
8if isSimulation && nargin >= 7 && isobject(eventCache)
9 vector = [ind, event,
class, inspace];
10 key = mat2str(vector);
11 if eventCache.isKey(key)
12 cachedResult = eventCache(key);
13 outprob = cachedResult{1};
14 outspace = cachedResult{2};
15 outrate = cachedResult{3};
16 if size(outspace,1) > 1
17 tot_rate = sum(outrate);
18 cum_rate = cumsum(outrate) / tot_rate;
19 firing_ctr = 1 + max([0,find( rand > cum_rate
' )]); % select action
20 outspace = outspace(firing_ctr,:);
21 outrate = sum(outrate);
22 outprob = outprob(firing_ctr,:);
30% Else continue to main body below
34phasessz = sn.phasessz;
35phaseshift = sn.phaseshift;
39isf = sn.nodeToStateful(ind);
42 ismkvmod = any(sn.procid(sn.nodeToStation(ind),:)==ProcessType.MAP | sn.procid(sn.nodeToStation(ind),:)==ProcessType.MMPP2);
43 ismkvmodclass = zeros(R,1);
45 ismkvmodclass(r) = any(sn.procid(sn.nodeToStation(ind),r)==ProcessType.MAP | sn.procid(sn.nodeToStation(ind),r)==ProcessType.MMPP2);
49lldscaling = sn.lldscaling;
51 lldlimit = max(sum(sn.nclosedjobs),1);
52 lldscaling = ones(M,lldlimit);
54 lldlimit = size(lldscaling,2);
57cdscaling = sn.cdscaling;
59 cdscaling = cell(M,1);
60 cdscaling{1} = @(ni) 1;
61 for i=2:M % faster this way
62 cdscaling{i} = cdscaling{1};
66hasOnlyExp = false; % true if all service processes are exponential
68 ist = sn.nodeToStation(ind);
70 Ks = phaseshift(ist,:);
78 classcap = sn.classcap;
79 if K(class) == 0 % if this class is not accepted at the resource
80 eventCache(key) = {outprob, outspace,outrate};
83 V = sum(sn.nvars(ind,:));
84 % Place nodes: state format is [buffer(R), server(sum(K))] after ARV
85 if sn.nodetype(ind) == NodeType.Place
86 space_var = zeros(size(inspace,1), 0); % proper dimensions for concatenation
87 state_len = size(inspace, 2);
88 expected_len = R + sum(K);
89 if state_len == expected_len
90 % State already has [buffer, server] format
91 space_buf = inspace(:, 1:R);
92 space_srv = inspace(:, (R+1):end);
94 % Initial state: just buffer counts
96 space_srv = zeros(size(inspace,1), sum(K));
98 % Fallback for unexpected formats
100 space_srv = zeros(size(inspace,1), sum(K));
103 space_var = inspace(:,(end-V+1):end); % local state variables
104 space_srv = inspace(:,(end-sum(K)-V+1):(end-V)); % server state
105 space_buf = inspace(:,1:(end-sum(K)-V)); % buffer state
107elseif sn.isstateful(ind)
108 V = sum(sn.nvars(ind,:));
109 % in this case service is always immediate so sum(K)=1
110 space_var = inspace(:,(end-V+1):end); % local state variables
111 if sn.nodetype(ind) == NodeType.Transition
112 K = sn.nodeparam{ind}.firingphases;
113 nmodes = sn.nodeparam{ind}.nmodes;
114 % Handle NaN firingphases (non-phase-type distributions like Pareto)
115 % Infer phase count from D0 matrix size
117 K = zeros(1, nmodes);
119 if iscell(sn.nodeparam{ind}.firingproc) && ~isempty(sn.nodeparam{ind}.firingproc{m})
120 K(m) = size(sn.nodeparam{ind}.firingproc{m}{1}, 1);
126 Ks = [0,cumsum(K,2)];
127 space_buf = inspace(:,1:nmodes); % idle servers count put in buf
128 space_srv = inspace(:,(nmodes+1):(nmodes+sum(K))); % enabled servers' phases
129 % Handle both state formats: with and without fired component
130 expected_len_with_fired = 2*nmodes + sum(K);
131 expected_len_without_fired = nmodes + sum(K);
132 if size(inspace, 2) >= expected_len_with_fired
133 space_fired = inspace(:,(nmodes+sum(K)+1):(2*nmodes+sum(K))); % servers that just fired
134 elseif size(inspace, 2) == expected_len_without_fired
135 % Legacy format without fired component - initialize to zeros
136 space_fired = zeros(size(inspace,1), nmodes);
138 line_error(mfilename,
'Unexpected state vector length for Transition node');
141 space_buf = []; % buffer state
142 space_srv = inspace(:,(end-R-V+1):(end-V)); % server state
143 space_fired = []; % only
for Transition
nodes
152 [outspace, outrate, outprob, eventCache] = State.afterEventStation(sn, ind, inspace, event,
class, isSimulation, eventCache, ...
153 M, R, S, phasessz, phaseshift, pie, isf, ismkvmod, ismkvmodclass, lldscaling, lldlimit, cdscaling, ...
154 hasOnlyExp, ist, K, Ks, mu, phi, proc, capacity, classcap, V, space_buf, space_srv, space_var, key);
155elseif sn.isstateful(ind)
156 switch sn.nodetype(ind)
158 [outspace, outrate, outprob, eventCache] = State.afterEventRouter(sn, ind, event,
class, isSimulation, eventCache, space_buf, space_srv, space_var, key);
160 [outspace, outrate, outprob, eventCache] = State.afterEventCache(sn, ind, event,
class, isSimulation, eventCache, R, space_buf, space_srv, space_var, key);
161 case NodeType.Transition
162 [outspace, outrate, outprob, eventCache] = State.afterEventTransition(sn, ind, inspace, K, Ks, event,
class, isSimulation, eventCache, R, space_buf, space_srv, space_fired, space_var, key);
163 end %
switch nodeType