LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
Transition.m
1classdef Transition < StatefulNode
2 % A class for a stochastic Petri net transition
3 %
4 % Copyright (c) 2012-2026, Imperial College London
5 % All rights reserved.
6
7 properties
8 enablingConditions;
9 inhibitingConditions;
10 modes;
11 modeNames;
12 numberOfServers;
13 timingStrategies;
14 distributions;
15 firingPriorities;
16 firingWeights;
17 firingOutcomes;
18 firingRateDependence;
19 cap;
20 end
21
22 methods
23 function self = Transition(model,name)
24 % TRANSITION(MODEL, NAME)
25
26 self@StatefulNode(name);
27 if model.isMatlabNative()
28 classes = model.getClasses();
29 self.input = Enabling(classes);
30 self.output = Firing(classes);
31 self.cap = Inf; % Compatible with other nodes
32
33 self.setModel(model);
34 self.model.addNode(self);
35
36 self.server = Timing();
37
38 self.enablingConditions = [];
39 self.inhibitingConditions = [];
40 self.modeNames = {};
41 self.numberOfServers = [];
42 self.timingStrategies = [];
43 self.distributions = {};
44 self.firingPriorities = [];
45 self.firingWeights = [];
46 self.firingOutcomes = [];
47 self.firingRateDependence = {};
48 elseif model.isJavaNative()
49 self.setModel(model);
50 self.obj = jline.lang.nodes.Transition(model.obj, name);
51 self.index = model.obj.getNodeIndex(self.obj);
52 end
53 end
54
55 function self = init(self)
56 % SELF = INIT()
57
58 nclasses = length(self.model.getClasses());
59 nnodes = length(self.model.getNodes());
60
61 self.enablingConditions = cell(self.getNumberOfModes);
62 self.inhibitingConditions = cell(self.getNumberOfModes);
63 self.firingOutcomes = cell(self.getNumberOfModes);
64 for m=1:self.getNumberOfModes
65 self.enablingConditions{m} = zeros(nnodes,nclasses);
66 self.inhibitingConditions{m} = Inf*ones(nnodes,nclasses); % Inf = no inhibitor arc (never inhibits); zeros would inhibit at >=0 tokens, i.e. always
67 self.firingOutcomes{m} = zeros(nnodes,nclasses);
68 end
69 self.numberOfServers = ones(1,self.getNumberOfModes);
70 self.timingStrategies = repmat(TimingStrategy.TIMED,1,self.getNumberOfModes);
71 self.firingWeights = ones(1,self.getNumberOfModes);
72 self.firingPriorities = ones(1,self.getNumberOfModes);
73 self.distributions = cell(1, self.getNumberOfModes);
74 self.distributions(:) = {Exp(1)};
75 % Empty == unit multiplier (marking-independent firing rate).
76 self.firingRateDependence = cell(1, self.getNumberOfModes);
77 end
78
79 function mode = addMode(self, modeName)
80 nclasses = length(self.model.getClasses());
81 nnodes = length(self.model.getNodes());
82 self.modeNames{end+1} = modeName;
83 self.enablingConditions{end+1} = zeros(nnodes,nclasses);
84 self.inhibitingConditions{end+1} = Inf*ones(nnodes,nclasses);
85 self.numberOfServers(end+1) = 1;
86 self.timingStrategies(end+1) = TimingStrategy.TIMED;
87 self.firingWeights(end+1) = 1.0;
88 self.firingPriorities(end+1) = 1.0;
89 self.distributions{end+1} = Exp(1);
90 self.firingOutcomes{end+1} = zeros(nnodes,nclasses);
91 self.firingRateDependence{end+1} = [];
92 mode = Mode(self,modeName);
93 self.modes{end+1} = mode;
94 end
95
96 function self = setEnablingConditions(self, mode, class, inputNode, enablingCondition)
97 % SELF = SETENABLINGCONDITIONS(MODE, CLASS, NODE, ENABLINGCONDITIONS)
98
99 if isa(inputNode, 'Place')
100 inputNode = self.model.getNodeIndex(inputNode.name);
101 self.enablingConditions{mode}(inputNode,class) = enablingCondition;
102 else
103 error('Node must be a Place node.');
104 end
105 end
106
107 function self = setInhibitingConditions(self, mode, class, inputNode, inhibitingCondition)
108 % SELF = SETINHIBITINGCONDITIONS(MODE, CLASS, NODE, INHIBITINGCONDITIONS)
109
110 if isa(inputNode, 'Place')
111 inputNode = self.model.getNodeIndex(inputNode.name);
112 self.inhibitingConditions{mode}(inputNode,class) = inhibitingCondition;
113 else
114 error('Node must be a Place node.');
115 end
116 end
117
118 function self = setModeNames(self, mode, modeName)
119 % SELF = SETMODENAMES(MODE, MODENAMES)
120
121 self.modeNames{mode} = modeName;
122 end
123
124 function self = setNumberOfServers(self, mode, numberOfServers)
125 % SELF = SETNUMBEROFSERVERS(MODE, NUMOFSERVERS)
126
127 self.numberOfServers(mode) = numberOfServers;
128 end
129
130 function self = setTimingStrategy(self, mode, timingStrategy)
131 % SELF = SETTIMINGSTRATEGY(MODE, TIMINGSTRATEGY)
132
133 self.timingStrategies(mode) = timingStrategy;
134 end
135
136 function self = setFiringPriorities(self, mode, firingPriority)
137 % SELF = SETFIRINGPRIORITIES(MODE, FIRINGPRIORITIES)
138
139 self.firingPriorities(mode) = firingPriority;
140 end
141
142 function self = setFiringWeights(self, mode, firingWeight)
143 % SELF = SETFIRINGWEIGHTS(MODE, FIRINGWEIGHTS)
144
145 self.firingWeights(mode) = firingWeight;
146 end
147
148 function self = setFiringOutcome(self, mode, class, node, firingOutcome)
149 % SELF = SETFIRINGOUTCOMES(MODE, NODE, CLASS,FIRINGOUTCOME)
150 ind = self.model.getNodeIndex(node);
151 self.firingOutcomes{mode}(ind,class) = firingOutcome;
152 end
153
154 function self = setDistribution(self, mode, distribution)
155 self.distributions{mode} = distribution;
156 end
157
158 function self = setFiringRateDependence(self, mode, g)
159 % SELF = SETFIRINGRATEDEPENDENCE(MODE, G)
160 % Marking-dependent firing-rate multiplier for a timed mode.
161 % G is a function handle g(m) of the input-place marking matrix m
162 % (shaped like enablingConditions{mode}, nnodes x nclasses),
163 % returning a positive scalar. The effective firing rate of an
164 % enabled binding becomes rate_base(mode)*g(m). Empty G restores
165 % the unit (marking-independent) multiplier.
166 %
167 % Exact only for memoryless firing: the mode must be TIMED (not
168 % IMMEDIATE, which uses firingWeights) and exponentially
169 % distributed. This mirrors the PS/FCFS-only restriction on
170 % station load/class dependence.
171 if ~isempty(g) && ~isa(g,'function_handle')
172 line_error(mfilename,'Firing-rate dependence must be a function handle g(m) or empty.');
173 end
174 if ~isempty(g)
175 if self.timingStrategies(mode) == TimingStrategy.IMMEDIATE
176 line_error(mfilename,'Firing-rate dependence is not supported for IMMEDIATE modes; use setFiringWeights for immediate transitions.');
177 end
178 if ~isa(self.distributions{mode},'Exp')
179 line_error(mfilename,'Firing-rate dependence is supported only for exponentially distributed (memoryless) firing; set an Exp distribution on the mode first.');
180 end
181 end
182 self.firingRateDependence{mode} = g;
183 end
184
185 function nmodes = getNumberOfModes(self)
186 nmodes = length(self.modeNames);
187 end
188
189 function modes = getModes(self)
190 modes = self.modes;
191 end
192
193 function [map,mu,phi] = getServiceRates(self)
194 % [PH,MU,PHI] = GETPHSERVICERATES()
195
196 map = cell(1,self.getNumberOfModes);
197 mu = cell(1,self.getNumberOfModes);
198 phi = cell(1,self.getNumberOfModes);
199
200 for r=1:self.getNumberOfModes
201 switch class(self.distributions{r})
202 case {'Replayer','Trace'}
203 aph = self.distributions{r}.fitAPH;
204 map{r} = aph.getProcess();
205 mu{r} = aph.getMu;
206 phi{r} = aph.getPhi;
207 case {'Exp','Coxian','Erlang','HyperExp','Markovian','APH','MAP'}
208 map{r} = self.distributions{r}.getProcess();
209 mu{r} = self.distributions{r}.getMu;
210 phi{r} = self.distributions{r}.getPhi;
211 case 'MMPP2'
212 map{r} = self.distributions{r}.getProcess();
213 mu{r} = self.distributions{r}.getMu;
214 phi{r} = self.distributions{r}.getPhi;
215 case {'Det','Uniform','Pareto','Gamma','Weibull','Lognormal','Expolynomial'}
216 map{r} = self.distributions{r}.getProcess();
217 mu{r} = [self.distributions{r}.getRate];
218 phi{r} = [1];
219 otherwise
220 map{r} = {[NaN],[NaN]};
221 mu{r} = NaN;
222 phi{r} = NaN;
223 end
224 end
225 end
226 end
227end
Definition fjtag.m:161