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 not supported for networks with multiple queues in SolverMAM. The MAM solver uses QBD (quasi-birth-death) analysis, which is fundamentally a single-queue method. Use SolverCTMC or SolverSSA for state probabilities in networks with multiple queues.');
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 % Approximate joint (level,phase) from level marginal + uniform phase;
120 % see _kb/06-solver-catalog.md for rationale
121
122 [pdistr] = MMAPPH1FCFS(D_approx, {pie{:}}, {D0{:}}, 'ncDistr', maxLevel);
123 pdistr = abs(pdistr);
124 pdistr = pdistr / sum(pdistr);
125
126 % Build phase distribution - for simplicity, use the steady-state
127 % phase distribution from the service process
128 % This is an approximation
129 nPhases = size(D0{1}, 1);
130 for k=2:K
131 nPhases = max(nPhases, size(D0{k}, 1));
132 end
133
134 % Construct joint probability matrix: rows = levels, cols = phases
135 % For now, approximate by assuming phases are independent of level
136 % and use the service process steady-state distribution
137 avgPie = zeros(1, nPhases);
138 for k=1:K
139 if size(pie{k}, 2) == nPhases
140 avgPie = avgPie + pie{k} * T(ist,k) / lambda_total;
141 end
142 end
143
144 if sum(avgPie) == 0 || any(isnan(avgPie))
145 avgPie = ones(1, nPhases) / nPhases;
146 else
147 avgPie = avgPie / sum(avgPie);
148 end
149
150 % Joint distribution: P(level, phase) ≈ P(level) * P(phase)
151 Pstate = zeros(min(maxLevel, length(pdistr)), nPhases);
152 for level=1:min(maxLevel, length(pdistr))
153 Pstate(level, :) = pdistr(level) * avgPie;
154 end
155
156 % If specific state requested, extract it
157 if ~isempty(state)
158 if isstruct(state)
159 level = state.level;
160 phase = state.phase;
161 elseif length(state) >= 2
162 level = state(1);
163 phase = state(2);
164 else
165 line_error(mfilename,'State must be a 2-element vector [level, phase] or structure with .level and .phase fields.');
166 end
167
168 % Check bounds (MATLAB indexing: level+1, phase)
169 if level+1 > size(Pstate, 1) || phase > size(Pstate, 2) || level < 0 || phase < 1
170 Pstate = 0.0;
171 else
172 Pstate = Pstate(level+1, phase);
173 end
174 end
175
176 catch ME
177 line_error(mfilename,'Failed to compute state probabilities: %s', ME.message);
178 end
179
180else
181 % Open model - compute joint probability distribution using MAM (MMAPPH1FCFS)
182
183 % Get model parameters
184 PH = sn.proc;
185 pie = cell(1, K);
186 D0 = cell(1, K);
187
188 % Extract service process parameters for the queue station
189 for k = 1:K
190 PH{ist}{k} = map_scale(PH{ist}{k}, 1./sn.rates(ist,k)/sn.nservers(ist));
191 pie{k} = map_pie(PH{ist}{k});
192 D0{k} = PH{ist}{k}{1};
193 if any(isnan(D0{k}))
194 D0{k} = -GlobalConstants.Immediate;
195 pie{k} = 1;
196 end
197 end
198
199 % Build the arrival process from source
200 refstat = sn.refstat(1); % Source station
201 PH_src = sn.proc{refstat};
202
203 % Build arrival process cell array: {D0, D1, D2, ...} for K classes
204 D_arr = cell(1, K + 1);
205
206 % Check total arrival rate
207 totalLambda = 0;
208 for k = 1:K
209 if ~isnan(PH_src{k}{1})
210 totalLambda = totalLambda + map_lambda(PH_src{k});
211 end
212 end
213
214 % Compute queue length distribution using MMAPPH1FCFS
215 maxLevel = 100; % Maximum queue length to compute
216 if ~isempty(self.options.cutoff) && isfinite(self.options.cutoff) && self.options.cutoff > 0
217 maxLevel = self.options.cutoff;
218 end
219
220 if totalLambda < GlobalConstants.FineTol
221 % No arrivals at this station
222 if isempty(state)
223 Pstate = zeros(maxLevel, 1);
224 Pstate(1, 1) = 1.0;
225 else
226 if isstruct(state)
227 level = state.level;
228 phase = state.phase;
229 elseif length(state) >= 2
230 level = state(1);
231 phase = state(2);
232 else
233 line_error(mfilename, 'State must be a 2-element vector [level, phase] or structure.');
234 end
235 if level == 0 && phase == 1
236 Pstate = 1.0;
237 else
238 Pstate = 0.0;
239 end
240 end
241 else
242 % Build the aggregate arrival MMAP
243 arrMaps = cell(1, K);
244 for k = 1:K
245 if ~isnan(PH_src{k}{1})
246 arrMaps{k} = PH_src{k};
247 else
248 arrMaps{k} = map_exponential(Inf); % No arrivals
249 end
250 end
251
252 % Superpose all arrival processes
253 if K == 1
254 % Single class - use the arrival process directly
255 arrProcess = arrMaps{1};
256 D_arr{1} = arrProcess{1}; % D0
257 D_arr{2} = arrProcess{2}; % D1
258 else
259 % Multiple classes - superpose MAPs
260 superMAP = arrMaps{1};
261 for k = 2:K
262 superMAP = map_super({superMAP, arrMaps{k}});
263 end
264 % Build MMAP with class marking based on arrival rates
265 D_arr{1} = superMAP{1}; % D0
266 for k = 1:K
267 lambdaK = map_lambda(arrMaps{k});
268 D_arr{k + 1} = (lambdaK / totalLambda) * superMAP{2};
269 end
270 end
271
272 try
273 [pdistr] = MMAPPH1FCFS(D_arr, pie, D0, 'ncDistr', maxLevel);
274 pdistr = abs(pdistr);
275 pdistr = pdistr / sum(pdistr);
276
277 % Build phase distribution - use the steady-state phase distribution
278 nPhases = size(D0{1}, 1);
279 for k = 2:K
280 nPhases = max(nPhases, size(D0{k}, 1));
281 end
282
283 % Compute weighted average phase distribution
284 V = cellsum(sn.visits);
285 avgPie = zeros(1, nPhases);
286 for k = 1:K
287 if size(pie{k}, 2) <= nPhases
288 pieK = [pie{k}, zeros(1, nPhases - size(pie{k}, 2))];
289 lambdaK = map_lambda(arrMaps{k});
290 avgPie = avgPie + pieK * lambdaK / totalLambda;
291 end
292 end
293
294 if sum(avgPie) == 0 || any(isnan(avgPie))
295 avgPie = ones(1, nPhases) / nPhases;
296 else
297 avgPie = avgPie / sum(avgPie);
298 end
299
300 % Joint distribution: P(level, phase) ≈ P(level) * P(phase)
301 Pstate = zeros(min(maxLevel, length(pdistr)), nPhases);
302 for level = 1:min(maxLevel, length(pdistr))
303 Pstate(level, :) = pdistr(level) * avgPie;
304 end
305
306 % If specific state requested, extract it
307 if ~isempty(state)
308 if isstruct(state)
309 level = state.level;
310 phase = state.phase;
311 elseif length(state) >= 2
312 level = state(1);
313 phase = state(2);
314 else
315 line_error(mfilename, 'State must be a 2-element vector [level, phase] or structure.');
316 end
317
318 if level + 1 > size(Pstate, 1) || phase > size(Pstate, 2) || level < 0 || phase < 1
319 Pstate = 0.0;
320 else
321 Pstate = Pstate(level + 1, phase);
322 end
323 end
324 catch ME
325 line_error(mfilename, sprintf('Failed to compute state probabilities: %s', ME.message));
326 end
327 end
328end
329
330end