1function [outglspace, outrate, outprob] = afterGlobalEvent(sn, ind, glspace, glevent, isSimulation)
2% [OUTGLSPACE, OUTRATE, OUTPROB] = AFTERGLOBALEVENT(QN, IND, GLSPACE, GLEVENT, ISSIMULATION)
4% Copyright (c) 2012-2026, Imperial College London
7outspace = []; % state space of ind node
8outrate = []; % rate of synchronization
9outprob = []; % probability of synchronization
10outglspace = glspace; %
new global state after synchronization
13%phasessz = sn.phasessz;
14%phaseshift = sn.phaseshift;
15if sn.nodetype(ind) == NodeType.Transition % same isa(glevent,
'ModeEvent')
17 isf = sn.nodeToStateful(ind);
18 inspace = glspace{isf};
19 V = sum(sn.nvars(ind,:));
20 % in
this case service
is always immediate so sum(K)=1
21 space_var = inspace(:,(end-V+1):end); % local state variables
22 if sn.nodetype(ind) == NodeType.Transition
23 fK = sn.nodeparam{ind}.firingphases;
24 fKs = [0,cumsum(fK,2)];
25 nmodes = sn.nodeparam{ind}.nmodes;
26 mode = glevent.active{1}.mode;
27 space_buf = inspace(:,1:nmodes); % idle servers count put in buf
28 space_srv = inspace(:,(nmodes+1):(nmodes+sum(fK))); % enabled servers
' phases
30 switch glevent.active{1}.event
32 enabling_m = sn.nodeparam{ind}.enabling{mode}; % enabling requirement for mode m
33 ep_space = zeros(sn.nnodes,R);
34 for j=1:length(glevent.passive)
35 ep_ind = glevent.passive{j}.node; % index of enabling place
36 ep_isf = sn.nodeToStateful(ep_ind);
39 ep_space_buf = glspace{ep_isf};
40 ep_space_srv = zeros(1,R);
42 [~,ep_space(ep_ind,1:R)] = State.toMarginalAggr(sn,ep_ind, glspace{ep_isf},K,Ks,ep_space_buf,ep_space_srv,ep_space_var);
45 if any(ep_space < enabling_m)
46 % disable all servers in this mode
47 space_buf(m) = sn.nodeparam{ind}.nmodeservers(m);
48 space_srv((fKs(m)+1):(fKs(m)+fK(m))) = 0;
49 outspace = [space_buf, space_srv, space_var];
50 outrate = GlobalConstants.Immediate;
53 % find enabling degree, i.e., running servers
55 while all(ep_space >= en_degree_m * enabling_m)
56 en_degree_m = en_degree_m + 1;
58 en_degree_m = min(en_degree_m - 1, sn.nodeparam{ind}.nmodeservers(m));
59 running_m = sum(space_srv((fKs(m)+1):(fKs(m)+fK(m))));
60 if running_m == en_degree_m
61 % all running as expected, do nothing
65 elseif running_m < en_degree_m
66 % fewer expected, start en_degree_m - running_m
67 space_buf(m) = space_buf(m) - (en_degree_m - running_m);
68 pentry = sn.nodeparam{ind}.firingpie{mode};
69 nadd = en_degree_m - running_m;
70 combs = sortrows(multichoose(fK(m), nadd),'descend
');
72 for i = 1:size(combs,1)
74 space_srv_k = space_srv;
76 space_srv_k(fKs(m)+k) = space_srv_k(fKs(m)+k) + comb(k);
78 outspace(end+1,:) = [space_buf, space_srv_k, space_var];
79 outrate(end+1,:) = GlobalConstants.Immediate;
81 % compute multinomial coefficient
82 % multinomial probability: n! / (k1! * k2! * ...) * p1^k1 * p2^k2 * ...
83 logprob = factln(nadd);
86 logprob = logprob + comb(k)*log(pentry(k)) - factln(comb(k));
87 elseif (pentry(k)==0 && comb(k)==0)
89 else % (pentry(k)==0 && comb(k)>0)
90 logprob = -Inf; % set zero outprob
93 outprob(end+1,:) = exp(logprob);
95 %outprob = outprob / sum(outprob);
96 else %running_m > en_degree_m
97 % more than expected, stop running_m - en_degree_m
99 diff = en_degree_m - running_m;
100 srv_vec = space_srv((fKs(m)+1):(fKs(m)+fK(m)));
103 % Generate all combinations of indices to remove
104 idx_combinations = nchoosek(1:n, diff);
106 % Generate all derived vectors removing diff
107 for i = 1:size(idx_combinations, 1)
109 idx(idx_combinations(i, :)) = false;
110 outspace(i,:) = [space_buf, srv_vec(idx), space_var];
111 outrate(i,1) = GlobalConstants.Immediate;
112 outprob(i,1) = 1 / size(idx_combinations, 1);
116 % update new state space
117 outglspace{isf} = outspace;
119 %% Update transition servers
120 fK = sn.nodeparam{ind}.firingphases;
121 fKs = [0,cumsum(fK,2)];
122 mode = glevent.active{1}.mode;
123 % find transition server counts
124 [~,nim,~,kim] = State.toMarginal(sn,ind,inspace,fK,fKs,space_buf,space_srv,space_var);
125 % state of transition mode
126 enabling_m = sn.nodeparam{ind}.enabling{mode}; % enabling requirement for mode m
127 firing_m = sn.nodeparam{ind}.firing{mode}; % firing requirement for mode m
128 % find enabling degree, i.e., running servers
129 ep_space = zeros(sn.nnodes,R);
130 for j=1:length(glevent.passive)
131 ep_ind = glevent.passive{j}.node; % index of enabling place
132 ep_isf = sn.nodeToStateful(ep_ind);
134 Ks = [0,cumsum(K,2)];
135 ep_space_buf = outglspace{ep_isf}; % this equals glspace at this point
136 ep_space_srv = zeros(1,R);
138 [~,ep_space(ep_ind,1:R)] = State.toMarginalAggr(sn,ep_ind, glspace{ep_isf},K,Ks,ep_space_buf,ep_space_srv,ep_space_var);
141 while all(ep_space >= en_degree_m * enabling_m)
142 en_degree_m = en_degree_m + 1;
144 %en_degree_m = min(en_degree_m - 1, sn.nodeparam{ind}.nmodeservers(m));
145 en_degree_m = min(en_degree_m - 1, nim(mode)); % the actual enabling degree depends on servers actually in execution, though this should coincide
147 % Create a new state for all possible phase transitions
148 % in the exponential case, this will be a single state
149 % We will later do the Cartesian product with the new place
151 for k=1:K(mode) % new phase
152 % we do not support MAP firing yet, so we use the total
153 % departure rate from the current phase k in firingproc
154 rate_kd = sum(sn.nodeparam{ind}.firingproc{mode}{2}(k,:)).*kim(:,mode,k);
155 space_buf_kd = space_buf;
156 space_srv_kd = space_srv;
157 % decrease by one the firing server
158 space_srv_kd(fKs(mode)+k) = space_srv_kd(fKs(mode)+k) - 1;
159 % move the server back to the disabled pool
160 space_buf_kd(mode) = space_buf_kd(mode) + 1;
162 outspace = [outspace; space_buf_kd, space_srv_kd, space_var_kd]; %#ok<AGROW>
163 outrate = [outrate; rate_kd]; %#ok<AGROW>
164 outprob = [outprob; 1.0]; %#ok<AGROW>
166 outglspace{isf} = outspace;
168 %% Process ID_PRE events, i.e., consume from all input places
169 for j=1:length(glevent.passive)
170 if glevent.passive{j}.event == EventType.PRE
171 ep_ind = glevent.passive{j}.node; % index of enabling place
172 ep_isf = sn.nodeToStateful(ep_ind);
173 ep_space_buf = outglspace{ep_isf}; % this equals glspace at this point
174 ep_ist = sn.nodeToStation(ep_ind);
175 % Places support only for FCFS, LCFS, SIRO
176 consume_set = en_degree_m * enabling_m; % jobs to be consumed
177 switch sn.sched(ep_ist)
178 case SchedStrategy.FCFS
179 for k = find(consume_set)
180 idx = find(ep_space_buf == k);
181 ep_space_buf(idx(end - consume_set(k) + 1:end)) = []; % Remove rightmost matches
183 case SchedStrategy.LCFS
184 for k = find(consume_set)
185 idx = find(ep_space_buf == k);
186 ep_space_buf(idx(1:consume_set(k))) = []; % Remove leftmost matches
188 case SchedStrategy.SIRO
189 ep_space_buf = ep_space_buf - consume_set; % Decrease counts directly
191 line_error(mfilename,sprintf('Scheduling strategy %s
is unsupported at places.
', SchedStrategy.toText(sn.sched(ist))));
193 % update state of enabling place
194 outglspace{ep_isf} = ep_space_buf;
198 %% Process ID_POST events, i.e., produce to all output places
199 for j=1:length(glevent.passive)
200 if glevent.passive{j}.event == EventType.POST
201 fp_ind = glevent.passive{j}.node; % index of place firing to
202 fp_isf = sn.nodeToStateful(fp_ind);
203 fp_space_buf = outglspace{fp_isf}; % this might have been already modified by the ID_PRE events
204 % Places support only for FCFS, LCFS, SIRO
205 produce_set = firing_m; % jobs to be fired
206 switch sn.sched(ep_ist)
207 case {SchedStrategy.FCFS, SchedStrategy.LCFS}
208 for k = find(produce_set)
209 fp_space_buf = [repmat(k, 1, find(produce_set == k)), fp_space_buf];
211 case SchedStrategy.SIRO
212 fp_space_buf = fp_space_buf + produce_set; % Decrease counts directly
214 line_error(mfilename,sprintf('Scheduling strategy %s
is unsupported at places.
', SchedStrategy.toText(sn.sched(ist))));
216 % update state of enabling place
217 outglspace{fp_isf} = fp_space_buf;
224% TODO: if there are FIRE events with Immediate rate it
225% is unclear if the ENABLE event should get priority.
226% The order of choice of what fires could change the system behavior.
227% On the other hand, this could cause perpetual loops if the enabling is
228% applied with priority on the same state with a FIRE?
230 if size(outspace,1) > 1
231 tot_rate = sum(outrate);
232 cum_rate = cumsum(outrate) / tot_rate;
233 firing_ctr = 1 + max([0,find( rand > cum_rate' )]); % select action
234 outspace = outspace(firing_ctr,:);
235 outrate = sum(outrate);
236 outprob = outprob(firing_ctr,:);