1function Pstate = getProb(self, node, state)
2% PSTATE = GETPROB(NODE, STATE)
4% Returns state probability
for the specified node and state
5% For QBD models, state
is a 2-element vector [level, phase]
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
13% Pstate - probability of the state, or full probability matrix
if state not specified
16 line_error(mfilename,
'getProb requires a node parameter.');
24% Convert node to station index
if needed
26 line_error(mfilename,
'Node number exceeds the number of nodes in the model.');
28ist = sn.nodeToStation(node);
30 line_error(mfilename,
'Specified node is not a station.');
33% Check
if this is a network (more than one queue station)
36 if sn.nodetype(sn.stationToNode(i)) == NodeType.Queue
37 queueStations = 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.');
45% Check
if the model has only one queue
47 line_error(mfilename,
'Model does not contain any queue stations.');
50% Ensure results are available
51if isempty(self.result)
55% Get model parameters needed for QBD solution
59% Check if this
is a closed model
61 % Closed model - compute probability distribution using QBD
62 maxLevel = sum(N(isfinite(N))) + 1;
64 % Build the arrival and service processes
69 % Extract service process parameters
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};
75 D0{k} = -GlobalConstants.Immediate;
80 % Build aggregate arrival process (approximation
using throughput)
81 T = self.result.Avg.T;
82 lambda_total = sum(T(ist,:));
84 if lambda_total < GlobalConstants.FineTol
85 % No traffic at
this station
87 % Return full probability matrix - all probability at state (0,1)
88 Pstate = zeros(maxLevel, 1);
95 elseif length(state) >= 2
99 line_error(mfilename,'State must be a 2-element vector [level, phase] or structure with .level and .phase fields.');
102 if level == 0 && phase == 1
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
115 D_approx{k+1} = T(ist,k) * ones(1,1); % Dk - arrivals of
class k
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
124 % For now, we approximate
using the marginal and assuming
125 % uniform distribution over phases (or
using initial distribution)
127 [pdistr] = MMAPPH1FCFS(D_approx, {pie{:}}, {D0{:}},
'ncDistr', maxLevel);
128 pdistr = abs(pdistr);
129 pdistr = pdistr / sum(pdistr);
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);
136 nPhases = max(nPhases, size(D0{k}, 1));
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);
144 if size(pie{k}, 2) == nPhases
145 avgPie = avgPie + pie{k} * T(ist,k) / lambda_total;
149 if sum(avgPie) == 0 || any(isnan(avgPie))
150 avgPie = ones(1, nPhases) / nPhases;
152 avgPie = avgPie / sum(avgPie);
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;
161 % If specific state requested, extract it
166 elseif length(state) >= 2
170 line_error(mfilename,
'State must be a 2-element vector [level, phase] or structure with .level and .phase fields.');
173 % Check bounds (MATLAB indexing: level+1, phase)
174 if level+1 > size(Pstate, 1) || phase > size(Pstate, 2) || level < 0 || phase < 1
177 Pstate = Pstate(level+1, phase);
182 line_error(mfilename,
'Failed to compute state probabilities: %s', ME.message);
186 % Open model - compute joint probability distribution
using MAM (MMAPPH1FCFS)
188 % Get model parameters
193 % Extract service process parameters
for the queue station
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};
199 D0{k} = -GlobalConstants.Immediate;
204 % Build the arrival process from source
205 refstat = sn.refstat(1); % Source station
206 PH_src = sn.proc{refstat};
208 % Build arrival process cell array: {D0, D1, D2, ...}
for K
classes
209 D_arr = cell(1, K + 1);
211 % Check total arrival rate
214 if ~isnan(PH_src{k}{1})
215 totalLambda = totalLambda + map_lambda(PH_src{k});
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;
225 if totalLambda < GlobalConstants.FineTol
226 % No arrivals at
this station
228 Pstate = zeros(maxLevel, 1);
234 elseif length(state) >= 2
238 line_error(mfilename,
'State must be a 2-element vector [level, phase] or structure.');
240 if level == 0 && phase == 1
247 % Build the aggregate arrival
MMAP
248 arrMaps = cell(1, K);
250 if ~isnan(PH_src{k}{1})
251 arrMaps{k} = PH_src{k};
253 arrMaps{k} = map_exponential(Inf); % No arrivals
257 % Superpose all arrival processes
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
264 % Multiple
classes - superpose MAPs
265 superMAP = arrMaps{1};
267 superMAP = map_super({superMAP, arrMaps{k}});
269 % Build
MMAP with
class marking based on arrival rates
270 D_arr{1} = superMAP{1}; % D0
272 lambdaK = map_lambda(arrMaps{k});
273 D_arr{k + 1} = (lambdaK / totalLambda) * superMAP{2};
278 [pdistr] = MMAPPH1FCFS(
D_arr, pie, D0,
'ncDistr', maxLevel);
279 pdistr = abs(pdistr);
280 pdistr = pdistr / sum(pdistr);
282 % Build phase distribution - use the steady-state phase distribution
283 nPhases = size(D0{1}, 1);
285 nPhases = max(nPhases, size(D0{k}, 1));
288 % Compute weighted average phase distribution
289 V = cellsum(sn.visits);
290 avgPie = zeros(1, nPhases);
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;
299 if sum(avgPie) == 0 || any(isnan(avgPie))
300 avgPie = ones(1, nPhases) / nPhases;
302 avgPie = avgPie / sum(avgPie);
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;
311 % If specific state requested, extract it
316 elseif length(state) >= 2
320 line_error(mfilename,
'State must be a 2-element vector [level, phase] or structure.');
323 if level + 1 > size(Pstate, 1) || phase > size(Pstate, 2) || level < 0 || phase < 1
326 Pstate = Pstate(level + 1, phase);
330 line_error(mfilename, sprintf(
'Failed to compute state probabilities: %s', ME.message));