1function [Pmarg, logPmarg] = getProbMarg(self, ist,
jobclass, state_m)
2% [PMARG, LOGPMARG] = GETPROBMARG(IST, JOBCLASS, STATE_M)
4% Probability distribution
for queue length of a SINGLE
class at a station.
5% Returns
P(n jobs of
class r)
for n=0,1,...,N(r).
7% Compare with getProbAggr: returns probability of a specific per-
class
8% distribution, e.g.,
P(2
class-1, 1
class-2) as a scalar.
13% state_m - (optional) Specific states to query, or [] for all
16% Pmarg - Vector where Pmarg(n+1) =
P(n jobs of this class)
17% logPmarg - Log probabilities for numerical stability
20 line_error(mfilename,'getProbMarg requires station and job class parameters.');
28% Check if station index
is valid
30 line_error(mfilename,'Station number exceeds the number of stations in the model.');
33 line_error(mfilename,'Job class index exceeds the number of
classes in the model.');
36% Check if this
is a network (more than one queue station)
39 if sn.nodetype(sn.stationToNode(i)) == NodeType.Queue
40 queueStations = queueStations + 1;
45 line_error(mfilename,'getProbMarg
is currently only supported for single queue models in SolverMAM.\nFor networks, this method
is not yet implemented.');
48% Check if the model has only one queue
50 line_error(mfilename,'Model does not contain any queue stations.');
53% Ensure results are available
54if isempty(self.result)
58% Get model parameters needed for QBD solution
62% Check if this
is a closed model
64 % Closed model - compute probability distribution using QBD
65 maxLevel = sum(N(isfinite(N))) + 1;
67 % Build the arrival and service processes
72 % Extract service process parameters
74 PH{ist}{k} = map_scale(PH{ist}{k}, 1./sn.rates(ist,k)/sn.nservers(ist));
75 pie{k} = map_pie(PH{ist}{k});
76 D0{k} = PH{ist}{k}{1};
78 D0{k} = -GlobalConstants.Immediate;
83 % Build aggregate arrival process (approximation
using throughput)
84 % This
is a simplified approach -
for closed networks we use the throughput
85 % to approximate the arrival process
86 T = self.result.Avg.T;
87 lambda_total = sum(T(ist,:));
89 if lambda_total < GlobalConstants.FineTol
90 % No traffic at
this station
91 Pmarg = zeros(1, maxLevel);
92 Pmarg(1) = 1.0; % All probability at state 0
93 logPmarg = log(Pmarg);
94 logPmarg(Pmarg == 0) = -Inf;
97 if max(state_m) > length(Pmarg) - 1
98 line_error(mfilename,
'Requested state exceeds maximum population.');
100 Pmarg = Pmarg(state_m + 1); % +1
for MATLAB indexing
101 logPmarg = logPmarg(state_m + 1);
106 % Build a simple
MMAP approximation based on
class throughputs
107 D_approx = cell(1, K+1);
108 D_approx{1} = -lambda_total * eye(1); % D0
110 D_approx{k+1} = T(ist,k) * ones(1,1); % Dk - arrivals of
class k
114 % Compute the queue length distribution
using MMAPPH1FCFS
115 [pdistr] = MMAPPH1FCFS(D_approx, {pie{:}}, {D0{:}},
'ncDistr', maxLevel);
117 % Extract distribution
for the specific
class
118 % This
is an approximation: we extract the portion corresponding to
this class
120 % Normalize and extract portion
for this class
121 pdistr = abs(pdistr);
122 pdistr = pdistr / sum(pdistr);
124 % For the specific
class, we approximate the marginal
125 % by assuming independence and
using the
class's share
126 class_share = N(jobclass) / sum(N);
127 max_class_level = N(jobclass) + 1;
129 Pmarg = zeros(1, max_class_level);
131 if n+1 <= length(pdistr)
132 Pmarg(n+1) = pdistr(n+1);
137 Pmarg = Pmarg / sum(Pmarg);
138 logPmarg = log(Pmarg);
139 logPmarg(Pmarg == 0) = -Inf;
141 line_error(mfilename,'Invalid
class index or zero population for class.');
144 % Filter based on state_m
if provided
146 if max(state_m) > length(Pmarg) - 1
147 line_error(mfilename,
'Requested state exceeds maximum population for this class.');
149 Pmarg = Pmarg(state_m + 1); % +1
for MATLAB indexing
150 logPmarg = logPmarg(state_m + 1);
154 line_error(mfilename,
'Failed to compute marginalized probabilities: %s', ME.message);
158 % Open model - compute probability distribution
using MAM (MMAPPH1FCFS)
160 % Get model parameters
165 % Extract service process parameters
for the queue station
167 PH{ist}{k} = map_scale(PH{ist}{k}, 1./sn.rates(ist,k)/sn.nservers(ist));
168 pie{k} = map_pie(PH{ist}{k});
169 D0{k} = PH{ist}{k}{1};
171 D0{k} = -GlobalConstants.Immediate;
176 % Build the arrival process from source
177 % Find the source station (reference station
for open
classes)
178 nChains = sn.nchains;
179 V = cellsum(sn.visits);
181 % Get arrival process from source and build aggregate
MMAP
182 refstat = sn.refstat(1); % Source station
183 PH_src = sn.proc{refstat};
185 % Build arrival process cell array: {D0, D1, D2, ...}
for K
classes
186 D_arr = cell(1, K + 1);
188 % Check
if all arrivals are from a single source with simple MAPs
191 if ~isnan(PH_src{k}{1})
192 totalLambda = totalLambda + map_lambda(PH_src{k});
196 if totalLambda < GlobalConstants.FineTol
197 % No arrivals at
this station
198 Pmarg = zeros(1, 100);
200 logPmarg = log(Pmarg);
201 logPmarg(Pmarg == 0) = -Inf;
203 % Build the aggregate arrival
MMAP
204 % For simple exponential arrivals, construct directly
205 arrMaps = cell(1, K);
207 if ~isnan(PH_src{k}{1})
208 arrMaps{k} = PH_src{k};
210 arrMaps{k} = map_exponential(Inf); % No arrivals
214 % Superpose all arrival processes
216 % Single
class - use the arrival process directly
217 arrProcess = arrMaps{1};
218 D_arr{1} = arrProcess{1}; % D0
219 D_arr{2} = arrProcess{2}; % D1
221 % Multiple
classes - superpose MAPs
222 superMAP = arrMaps{1};
224 superMAP = map_super({superMAP, arrMaps{k}});
226 % Build
MMAP with
class marking based on arrival rates
227 D_arr{1} = superMAP{1}; % D0
229 lambdaK = map_lambda(arrMaps{k});
230 D_arr{k + 1} = (lambdaK / totalLambda) * superMAP{2};
234 % Compute queue length distribution
using MMAPPH1FCFS
235 maxLevel = 100; % Maximum queue length to compute
236 if ~isempty(self.options.cutoff) && isfinite(self.options.cutoff) && self.options.cutoff > 0
237 maxLevel = self.options.cutoff;
243 lambdaK = map_lambda(arrMaps{k});
244 if ~isnan(sn.rates(ist, k)) && sn.rates(ist, k) > 0
245 aggrUtil = aggrUtil + lambdaK / (sn.rates(ist, k) * sn.nservers(ist));
249 if aggrUtil >= 1 - GlobalConstants.FineTol
250 line_warning(mfilename,
'System utilization %.2f%% is too high, distribution may be inaccurate.', aggrUtil * 100);
254 [pdistr] = MMAPPH1FCFS(
D_arr, {pie{:}}, {D0{:}},
'ncDistr', maxLevel);
255 pdistr = abs(pdistr);
256 pdistr = pdistr / sum(pdistr);
259 logPmarg = log(Pmarg);
260 logPmarg(Pmarg == 0) = -Inf;
262 line_error(mfilename, sprintf('Failed to compute queue length distribution: %s
', ME.message));
266 % Filter based on state_m if provided
268 if max(state_m) > length(Pmarg) - 1
269 line_error(mfilename, 'Requested state exceeds maximum queue length.
');
271 Pmarg = Pmarg(state_m + 1);
272 logPmarg = logPmarg(state_m + 1);