LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
getProb.m
1function Pstate = getProb(self, node, state)
2% PSTATE = GETPROB(NODE, STATE)
3%
4% Returns state probability for the specified node and state
5% For QBD models, state is a 2-element vector [level, phase]
6%
7% Parameters:
8% node - node/station index
9% state - state vector [level, phase] or structure with .level and .phase fields
10% If state is omitted or empty, returns full probability matrix
11%
12% Returns:
13% Pstate - probability of the state, or full probability matrix if state not specified
14
15if nargin < 2
16 line_error(mfilename,'getProb requires a node parameter.');
17end
18if nargin < 3
19 state = [];
20end
21
22sn = self.getStruct;
23
24% Convert node to station index if needed
25if node > sn.nnodes
26 line_error(mfilename,'Node number exceeds the number of nodes in the model.');
27end
28ist = sn.nodeToStation(node);
29if ist == 0
30 line_error(mfilename,'Specified node is not a station.');
31end
32
33% Check if this is a network (more than one queue station)
34queueStations = 0;
35for i=1:sn.nstations
36 if sn.nodetype(sn.stationToNode(i)) == NodeType.Queue
37 queueStations = queueStations + 1;
38 end
39end
40
41if queueStations > 1
42 line_error(mfilename,'getProb is currently only supported for single queue models in SolverMAM.\nFor networks, this method is not yet implemented.');
43end
44
45% Check if the model has only one queue
46if queueStations == 0
47 line_error(mfilename,'Model does not contain any queue stations.');
48end
49
50% Ensure results are available
51if isempty(self.result)
52 self.run;
53end
54
55% Get model parameters needed for QBD solution
56K = sn.nclasses;
57N = sn.njobs';
58
59% Check if this is a closed model
60if all(isfinite(N))
61 % Closed model - compute probability distribution using QBD
62 maxLevel = sum(N(isfinite(N))) + 1;
63
64 % Build the arrival and service processes
65 PH = sn.proc;
66 pie = cell(1,K);
67 D0 = cell(1,K);
68
69 % Extract service process parameters
70 for k=1:K
71 PH{ist}{k} = map_scale(PH{ist}{k}, 1./sn.rates(ist,k)/sn.nservers(ist));
72 pie{k} = map_pie(PH{ist}{k});
73 D0{k} = PH{ist}{k}{1};
74 if any(isnan(D0{k}))
75 D0{k} = -GlobalConstants.Immediate;
76 pie{k} = 1;
77 end
78 end
79
80 % Build aggregate arrival process (approximation using throughput)
81 T = self.result.Avg.T;
82 lambda_total = sum(T(ist,:));
83
84 if lambda_total < GlobalConstants.FineTol
85 % No traffic at this station
86 if isempty(state)
87 % Return full probability matrix - all probability at state (0,1)
88 Pstate = zeros(maxLevel, 1);
89 Pstate(1, 1) = 1.0;
90 else
91 % Parse state
92 if isstruct(state)
93 level = state.level;
94 phase = state.phase;
95 elseif length(state) >= 2
96 level = state(1);
97 phase = state(2);
98 else
99 line_error(mfilename,'State must be a 2-element vector [level, phase] or structure with .level and .phase fields.');
100 end
101
102 if level == 0 && phase == 1
103 Pstate = 1.0;
104 else
105 Pstate = 0.0;
106 end
107 end
108 return;
109 end
110
111 % Build a simple MMAP approximation based on class throughputs
112 D_approx = cell(1, K+1);
113 D_approx{1} = -lambda_total * eye(1); % D0
114 for k=1:K
115 D_approx{k+1} = T(ist,k) * ones(1,1); % Dk - arrivals of class k
116 end
117
118 try
119 % For getProb, we need the joint distribution over (level, phase)
120 % The MMAPPH1FCFS ncDistr option gives us marginal over levels
121 % To get the full joint distribution, we would need to access
122 % the internal QBD solution
123 %
124 % For now, we approximate using the marginal and assuming
125 % uniform distribution over phases (or using initial distribution)
126
127 [pdistr] = MMAPPH1FCFS(D_approx, {pie{:}}, {D0{:}}, 'ncDistr', maxLevel);
128 pdistr = abs(pdistr);
129 pdistr = pdistr / sum(pdistr);
130
131 % Build phase distribution - for simplicity, use the steady-state
132 % phase distribution from the service process
133 % This is an approximation
134 nPhases = size(D0{1}, 1);
135 for k=2:K
136 nPhases = max(nPhases, size(D0{k}, 1));
137 end
138
139 % Construct joint probability matrix: rows = levels, cols = phases
140 % For now, approximate by assuming phases are independent of level
141 % and use the service process steady-state distribution
142 avgPie = zeros(1, nPhases);
143 for k=1:K
144 if size(pie{k}, 2) == nPhases
145 avgPie = avgPie + pie{k} * T(ist,k) / lambda_total;
146 end
147 end
148
149 if sum(avgPie) == 0 || any(isnan(avgPie))
150 avgPie = ones(1, nPhases) / nPhases;
151 else
152 avgPie = avgPie / sum(avgPie);
153 end
154
155 % Joint distribution: P(level, phase) ≈ P(level) * P(phase)
156 Pstate = zeros(min(maxLevel, length(pdistr)), nPhases);
157 for level=1:min(maxLevel, length(pdistr))
158 Pstate(level, :) = pdistr(level) * avgPie;
159 end
160
161 % If specific state requested, extract it
162 if ~isempty(state)
163 if isstruct(state)
164 level = state.level;
165 phase = state.phase;
166 elseif length(state) >= 2
167 level = state(1);
168 phase = state(2);
169 else
170 line_error(mfilename,'State must be a 2-element vector [level, phase] or structure with .level and .phase fields.');
171 end
172
173 % Check bounds (MATLAB indexing: level+1, phase)
174 if level+1 > size(Pstate, 1) || phase > size(Pstate, 2) || level < 0 || phase < 1
175 Pstate = 0.0;
176 else
177 Pstate = Pstate(level+1, phase);
178 end
179 end
180
181 catch ME
182 line_error(mfilename,'Failed to compute state probabilities: %s', ME.message);
183 end
184
185else
186 % Open model - compute joint probability distribution using MAM (MMAPPH1FCFS)
187
188 % Get model parameters
189 PH = sn.proc;
190 pie = cell(1, K);
191 D0 = cell(1, K);
192
193 % Extract service process parameters for the queue station
194 for k = 1:K
195 PH{ist}{k} = map_scale(PH{ist}{k}, 1./sn.rates(ist,k)/sn.nservers(ist));
196 pie{k} = map_pie(PH{ist}{k});
197 D0{k} = PH{ist}{k}{1};
198 if any(isnan(D0{k}))
199 D0{k} = -GlobalConstants.Immediate;
200 pie{k} = 1;
201 end
202 end
203
204 % Build the arrival process from source
205 refstat = sn.refstat(1); % Source station
206 PH_src = sn.proc{refstat};
207
208 % Build arrival process cell array: {D0, D1, D2, ...} for K classes
209 D_arr = cell(1, K + 1);
210
211 % Check total arrival rate
212 totalLambda = 0;
213 for k = 1:K
214 if ~isnan(PH_src{k}{1})
215 totalLambda = totalLambda + map_lambda(PH_src{k});
216 end
217 end
218
219 % Compute queue length distribution using MMAPPH1FCFS
220 maxLevel = 100; % Maximum queue length to compute
221 if ~isempty(self.options.cutoff) && isfinite(self.options.cutoff) && self.options.cutoff > 0
222 maxLevel = self.options.cutoff;
223 end
224
225 if totalLambda < GlobalConstants.FineTol
226 % No arrivals at this station
227 if isempty(state)
228 Pstate = zeros(maxLevel, 1);
229 Pstate(1, 1) = 1.0;
230 else
231 if isstruct(state)
232 level = state.level;
233 phase = state.phase;
234 elseif length(state) >= 2
235 level = state(1);
236 phase = state(2);
237 else
238 line_error(mfilename, 'State must be a 2-element vector [level, phase] or structure.');
239 end
240 if level == 0 && phase == 1
241 Pstate = 1.0;
242 else
243 Pstate = 0.0;
244 end
245 end
246 else
247 % Build the aggregate arrival MMAP
248 arrMaps = cell(1, K);
249 for k = 1:K
250 if ~isnan(PH_src{k}{1})
251 arrMaps{k} = PH_src{k};
252 else
253 arrMaps{k} = map_exponential(Inf); % No arrivals
254 end
255 end
256
257 % Superpose all arrival processes
258 if K == 1
259 % Single class - use the arrival process directly
260 arrProcess = arrMaps{1};
261 D_arr{1} = arrProcess{1}; % D0
262 D_arr{2} = arrProcess{2}; % D1
263 else
264 % Multiple classes - superpose MAPs
265 superMAP = arrMaps{1};
266 for k = 2:K
267 superMAP = map_super({superMAP, arrMaps{k}});
268 end
269 % Build MMAP with class marking based on arrival rates
270 D_arr{1} = superMAP{1}; % D0
271 for k = 1:K
272 lambdaK = map_lambda(arrMaps{k});
273 D_arr{k + 1} = (lambdaK / totalLambda) * superMAP{2};
274 end
275 end
276
277 try
278 [pdistr] = MMAPPH1FCFS(D_arr, pie, D0, 'ncDistr', maxLevel);
279 pdistr = abs(pdistr);
280 pdistr = pdistr / sum(pdistr);
281
282 % Build phase distribution - use the steady-state phase distribution
283 nPhases = size(D0{1}, 1);
284 for k = 2:K
285 nPhases = max(nPhases, size(D0{k}, 1));
286 end
287
288 % Compute weighted average phase distribution
289 V = cellsum(sn.visits);
290 avgPie = zeros(1, nPhases);
291 for k = 1:K
292 if size(pie{k}, 2) <= nPhases
293 pieK = [pie{k}, zeros(1, nPhases - size(pie{k}, 2))];
294 lambdaK = map_lambda(arrMaps{k});
295 avgPie = avgPie + pieK * lambdaK / totalLambda;
296 end
297 end
298
299 if sum(avgPie) == 0 || any(isnan(avgPie))
300 avgPie = ones(1, nPhases) / nPhases;
301 else
302 avgPie = avgPie / sum(avgPie);
303 end
304
305 % Joint distribution: P(level, phase) ≈ P(level) * P(phase)
306 Pstate = zeros(min(maxLevel, length(pdistr)), nPhases);
307 for level = 1:min(maxLevel, length(pdistr))
308 Pstate(level, :) = pdistr(level) * avgPie;
309 end
310
311 % If specific state requested, extract it
312 if ~isempty(state)
313 if isstruct(state)
314 level = state.level;
315 phase = state.phase;
316 elseif length(state) >= 2
317 level = state(1);
318 phase = state(2);
319 else
320 line_error(mfilename, 'State must be a 2-element vector [level, phase] or structure.');
321 end
322
323 if level + 1 > size(Pstate, 1) || phase > size(Pstate, 2) || level < 0 || phase < 1
324 Pstate = 0.0;
325 else
326 Pstate = Pstate(level + 1, phase);
327 end
328 end
329 catch ME
330 line_error(mfilename, sprintf('Failed to compute state probabilities: %s', ME.message));
331 end
332 end
333end
334
335end