1function [enabled_next_states,enabled_rates,enabled_sync,gctr_start,depRatesSamples,arvRatesSamples,outprob_a,outprob_p,rate_a,eventCache] = solver_ssa_findenabled(sn,node_a,enabled_next_states,cur_state,outprob_a,event_a,class_a,isSimulation,node_p,local,outprob_p,event_p,class_p,sync,gsync,depRatesSamples,samples_collected,arvRatesSamples,last_node_a,last_node_p,eventCache)
2enabled_sync = []; % row
is action label, col1=rate, col2=
new state
8 isf_a = sn.nodeToStateful(node_a{act});
10 % isf_p = sn.nodeToStateful(node_p{act});
11 % %update_cond_a =
true;
12 % %enabled_next_states{act} = cur_state;
13 %
if isempty(enabled_next_states{act}) || ...
14 % isempty(enabled_next_states{act}{isf_a})|| ...
15 % isempty(enabled_next_states{act}{isf_p}) || ...
16 % length(cur_state) < max(isf_a,isf_p)
17 % % no data or a lot has changed
18 % enabled_next_states{act} = cur_state;
19 % update_cond_a =
true;
20 % elseif sn.nodetype(node_p{act}) == NodeType.Cache
21 % enabled_next_states{act} = cur_state;
22 % update_cond_a =
true;
23 % elseif (isf_a == isf_p) || (length(cur_state{isf_a}) == length(cur_state_1{isf_a}) && ...
24 % length(cur_state{isf_p}) == length(cur_state_1{isf_p}) && ...
25 % all(cur_state{isf_a}==cur_state_1{isf_a}) && ...
26 % all(cur_state{isf_p}==cur_state_1{isf_p}))
27 % % active
is unchanged
28 % enabled_next_states{act}{isf_p} = cur_state{isf_p};
29 % update_cond_a =
false;
31 % enabled_next_states{act} = cur_state;
32 % update_cond_a =
true;
35 % enabled_next_states{act} = cur_state;
36 % update_cond_a =
true;
38 enabled_next_states{act} = cur_state;
40 % Immediate-feedback self-loop: a departure that routes back to
the same
41 % FCFS-family station and arrives as a
class flagged in sn.immfeed must
42 % hold
the server instead of re-queueing behind
the waiting jobs. The
43 % departure half
is computed with noPromote (see State.afterEventStation).
44 immfeed_selfloop_act =
false;
45 if event_a{act}==EventType.DEP && node_p{act}==node_a{act} ...
46 && node_a{act}>=1 && node_a{act}<=sn.nnodes && sn.isstation(node_a{act}) ...
47 && isfield(sn,
'immfeed') && ~isempty(sn.immfeed)
48 istA_if = sn.nodeToStation(node_a{act});
49 if istA_if>=1 && istA_if<=size(sn.immfeed,1) ...
50 && class_p{act}>=1 && class_p{act}<=size(sn.immfeed,2) ...
51 && sn.immfeed(istA_if, class_p{act})
52 immfeed_selfloop_act =
true;
56 [enabled_next_states{act}{isf_a}, rate_a{act}, outprob_a{act}, eventCache] = State.afterEvent(sn, node_a{act}, cur_state{isf_a}, event_a{act}, class_a{act}, isSimulation, eventCache, [], immfeed_selfloop_act);
59 if isempty(enabled_next_states{act}{isf_a}) || isempty(rate_a{act})
63 for ia=1:size(enabled_next_states{act}{isf_a},1) %
for all possible
new states, check
if they are enabled
64 %
if the transition cannot occur
65 if isnan(rate_a{act}(ia)) || rate_a{act}(ia) == 0 % handles degenerate rate values
66 % set
the transition with a zero rate so that it
is
68 rate_a{act}(ia) = 1e-38; % ~ zero in 32-bit precision
71 if enabled_next_states{act}{isf_a}(ia,:) == -1 % hash not found
74 update_cond_p =
true; %samples_collected == 1 || ((node_p{act} == last_node_a || node_p{act} == last_node_p)) || isempty(outprob_a{act}) || isempty(outprob_p{act});
77 if node_p{act} ~= local
78 if node_p{act} == node_a{act} %self-loop, active and passive are
the same
81 [enabled_next_states{act}{isf_p}, ~, outprob_p{act}, eventCache] = State.afterEvent(sn, node_p{act}, enabled_next_states{act}{isf_p}, event_p{act}, class_p{act}, isSimulation, eventCache);
84 isf_p = sn.nodeToStateful(node_p{act});
86 [enabled_next_states{act}{isf_p}, ~, outprob_p{act}, eventCache] = State.afterEvent(sn, node_p{act}, enabled_next_states{act}{isf_p}, event_p{act}, class_p{act}, isSimulation, eventCache);
89 if ~isempty(enabled_next_states{act}{isf_p})
90 if sn.isstatedep(node_a{act},3)
91 prob_sync_p{act} = sync{act}.passive{1}.prob(cur_state, enabled_next_states{act}); %state-dependent
93 prob_sync_p{act} = sync{act}.passive{1}.prob;
99 if ~isempty(enabled_next_states{act}{isf_a})
100 if node_p{act} == local
101 prob_sync_p{act} = 1;
103 if ~isnan(rate_a{act})
104 if all(~cellfun(@isempty,enabled_next_states{act}))
105 if event_a{act} == EventType.DEP
106 node_a_sf{act} = isf_a;
107 node_p_sf{act} = isf_p;
108 depRatesSamples(samples_collected,node_a_sf{act},class_a{act}) = depRatesSamples(samples_collected,node_a_sf{act},class_a{act}) + outprob_a{act} * outprob_p{act} * rate_a{act}(ia) * prob_sync_p{act};
109 arvRatesSamples(samples_collected,node_p_sf{act},class_p{act}) = arvRatesSamples(samples_collected,node_p_sf{act},class_p{act}) + outprob_a{act} * outprob_p{act} * rate_a{act}(ia) * prob_sync_p{act};
111 % simulate also self-loops as we need to log them
112 %
if any(~cellfun(@isequal,new_state{act},cur_state))
113 if node_p{act} < local && ~sn.csmask(class_a{act}, class_p{act}) && sn.nodetype(node_p{act})~=NodeType.Source && (rate_a{act}(ia) * prob_sync_p{act} >0)
114 line_error(mfilename,sprintf(
'Error: state-dependent routing at node %d (%s) violates the class switching mask (node %d -> node %d, class %d -> class %d).', node_a{act}, sn.nodenames{node_a{act}}, node_a{act}, node_p{act}, class_a{act}, class_p{act}));
116 enabled_rates(ctr) = rate_a{act}(ia) * prob_sync_p{act};
117 enabled_sync(ctr) = act;
127for gact=1:G %
event at node ind with global side-effects
128 gind = gsync{gact}.active{1}.node; % get
the active node (transition) from
the gsync
event
129 [enabled_next_states{A+gact}, outrate, outprob] = State.afterGlobalEvent(sn, gind, cur_state, gsync{gact}, isSimulation);
130 for ia=find(outrate .* outprob)
131 enabled_rates(ctr) = outrate(ia) * outprob(ia);
132 enabled_sync(ctr) = A+gact;