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 % see _kb/06-solver-catalog.md
for rationale (SSA immfeed self-loop)
41 immfeed_selfloop_act =
false;
42 if event_a{act}==EventType.DEP && node_p{act}==node_a{act} ...
43 && node_a{act}>=1 && node_a{act}<=sn.nnodes && sn.isstation(node_a{act}) ...
44 && isfield(sn,
'immfeed') && ~isempty(sn.immfeed)
45 istA_if = sn.nodeToStation(node_a{act});
46 if istA_if>=1 && istA_if<=size(sn.immfeed,1) ...
47 && class_p{act}>=1 && class_p{act}<=size(sn.immfeed,2) ...
48 && sn.immfeed(istA_if, class_p{act})
49 immfeed_selfloop_act =
true;
53 [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);
56 if isempty(enabled_next_states{act}{isf_a}) || isempty(rate_a{act})
60 for ia=1:size(enabled_next_states{act}{isf_a},1) %
for all possible
new states, check
if they are enabled
61 %
if the transition cannot occur
62 if isnan(rate_a{act}(ia)) || rate_a{act}(ia) == 0 % handles degenerate rate values
63 % set the transition with a zero rate so that it
is
65 rate_a{act}(ia) = 1e-38; % ~ zero in 32-bit precision
68 if enabled_next_states{act}{isf_a}(ia,:) == -1 % hash not found
71 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});
74 if node_p{act} ~= local
75 if node_p{act} == node_a{act} %self-loop, active and passive are the same
78 [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);
81 isf_p = sn.nodeToStateful(node_p{act});
83 [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);
86 if ~isempty(enabled_next_states{act}{isf_p})
87 if sn.isstatedep(node_a{act},3)
88 prob_sync_p{act} = sync{act}.passive{1}.prob(cur_state, enabled_next_states{act}); %state-dependent
90 prob_sync_p{act} = sync{act}.passive{1}.prob;
96 if ~isempty(enabled_next_states{act}{isf_a})
97 if node_p{act} == local
100 if ~isnan(rate_a{act})
101 if all(~cellfun(@isempty,enabled_next_states{act}))
102 if event_a{act} == EventType.DEP
103 node_a_sf{act} = isf_a;
104 node_p_sf{act} = isf_p;
105 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};
106 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};
108 % simulate also self-loops as we need to log them
109 %
if any(~cellfun(@isequal,new_state{act},cur_state))
110 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)
111 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}));
113 enabled_rates(ctr) = rate_a{act}(ia) * prob_sync_p{act};
114 enabled_sync(ctr) = act;
124for gact=1:G %
event at node ind with global side-effects
125 gind = gsync{gact}.active{1}.node; % get the active node (transition) from the gsync
event
126 [enabled_next_states{A+gact}, outrate, outprob] = State.afterGlobalEvent(sn, gind, cur_state, gsync{gact}, isSimulation);
127 for ia=find(outrate .* outprob)
128 enabled_rates(ctr) = outrate(ia) * outprob(ia);
129 enabled_sync(ctr) = A+gact;