LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
solver_mam_qsys.m
1function [QN, UN, RN, TN, piAgg, GM] = solver_mam_qsys(arrival, service, options)
2% SOLVER_MAM_QSYS Unified solver for MAP/BMAP queueing systems with single server
3%
4% [QN, UN, RN, TN, PI, GM] = SOLVER_MAM_QSYS(ARRIVAL, SERVICE) solves a queueing
5% system with Markovian arrival and service processes. Automatically detects
6% whether arrivals or service are batched and applies the appropriate
7% matrix-analytic method:
8% - BMAP/MAP/1: M/G/1 type analysis (batch arrivals, single service)
9% - MAP/BMAP/1: GI/M/1 type analysis (single arrivals, batch service)
10%
11% Input:
12% arrival - Arrival process: MAP object, BMAP object, or cell array
13% MAP: {D0, D1} or MAP object
14% BMAP: {D0, D1, D2, ..., DK} or BMAP object
15% service - Service process: MAP object, BMAP object, or cell array
16% MAP: {S0, S1} or MAP object
17% BMAP: {S0, S1, S2, ..., SK} or BMAP object
18% options - (optional) Options structure with fields:
19% .nMoments: number of queue length moments to compute (default: 3)
20%
21% Output:
22% QN - Mean queue length E[N]
23% UN - Server utilization rho
24% RN - Mean response time E[R]
25% TN - Throughput (arrival rate)
26% piAgg - Aggregated stationary probabilities
27% GM - G matrix (M/G/1 type) or R matrix (GI/M/1 type)
28%
29% Queue Types:
30% BMAP/MAP/1 - Batch arrivals, single service (M/G/1 type)
31% Level increases by batch size (1, 2, 3, ...), decreases by 1
32% MAP/BMAP/1 - Single arrivals, batch service (GI/M/1 type)
33% Level increases by 1, decreases by batch size (1, 2, 3, ...)
34%
35% References:
36% Stathopoulos, Riska, Hua, Smirni: ETAQA algorithm
37% MAMSolver: www.cs.wm.edu/MAMSolver
38%
39% See also: MAP, BMAP
40%
41% Copyright (c) 2012-2026, Imperial College London
42% All rights reserved.
43
44%% Input validation and setup
45if nargin < 3
46 options = struct();
47end
48if ~isfield(options, 'nMoments')
49 options.nMoments = 3;
50end
51
52%% Determine arrival type (MAP or BMAP)
53[arrivalMatrices, arrivalIsBatch, ma] = parseProcess(arrival, 'arrival');
54
55%% Determine service type (MAP or BMAP)
56[serviceMatrices, serviceIsBatch, ms] = parseProcess(service, 'service');
57
58%% Route to appropriate solver based on queue type
59if arrivalIsBatch && ~serviceIsBatch
60 % BMAP/MAP/1 queue - M/G/1 type analysis
61 [QN, UN, RN, TN, piAgg, GM] = solveBMAPMAP1(arrivalMatrices, serviceMatrices, ma, ms, options);
62elseif ~arrivalIsBatch && serviceIsBatch
63 % MAP/BMAP/1 queue - GI/M/1 type analysis
64 [QN, UN, RN, TN, piAgg, GM] = solveMAPBMAP1(arrivalMatrices, serviceMatrices, ma, ms, options);
65elseif ~arrivalIsBatch && ~serviceIsBatch
66 % MAP/MAP/1 queue - treat as BMAP/MAP/1 with batch size 1
67 [QN, UN, RN, TN, piAgg, GM] = solveBMAPMAP1(arrivalMatrices, serviceMatrices, ma, ms, options);
68else
69 % BMAP/BMAP/1 - both batched, use finite CTMC truncation
70 [QN, UN, RN, TN, piAgg, GM] = solveBMAPBMAP1(arrivalMatrices, serviceMatrices, ma, ms, options);
71end
72
73end
74
75%% Helper function to parse arrival/service process
76function [matrices, isBatch, nPhases] = parseProcess(proc, procType)
77 if isa(proc, 'BMAP')
78 K = proc.getMaxBatchSize();
79 matrices = cell(1, K + 1);
80 matrices{1} = proc.D(0); % D0
81 for k = 1:K
82 matrices{k+1} = proc.D(1, k); % Dk (batch size k)
83 end
84 isBatch = K > 1;
85 nPhases = size(matrices{1}, 1);
86 elseif isa(proc, 'MAP')
87 matrices = {proc.D(0), proc.D(1)};
88 isBatch = false;
89 nPhases = size(matrices{1}, 1);
90 elseif iscell(proc)
91 matrices = proc;
92 isBatch = length(proc) > 2; % More than {D0, D1} means batch process
93 nPhases = size(proc{1}, 1);
94 else
95 error('%s must be a MAP object, BMAP object, or cell array', procType);
96 end
97
98 % Validate dimensions
99 for k = 1:length(matrices)
100 if size(matrices{k}, 1) ~= nPhases || size(matrices{k}, 2) ~= nPhases
101 error('All %s matrices must be %dx%d', procType, nPhases, nPhases);
102 end
103 end
104end
105
106%% BMAP/MAP/1 solver using M/G/1 type analysis
107function [QN, UN, RN, TN, pi, G] = solveBMAPMAP1(D, S, ma, ms, options)
108% Solves BMAP/MAP/1 using M/G/1 type matrix-analytic methods
109%
110% M/G/1 type structure (level can jump UP by any amount, DOWN by exactly 1):
111% A0 = I_ma \otimes S1 (service completion, level -1)
112% A1 = D0 \otimes I_ms + I_ma \otimes S0 (phase changes, level 0)
113% A_{k+1} = D_k \otimes I_ms (batch arrival size k, level +k)
114
115K = length(D) - 1; % max batch size for arrivals
116m = ma * ms; % Combined phases per level
117
118% Extract MAP service matrices
119S0 = S{1};
120S1 = S{2};
121
122%% Construct M/G/1 type matrices using Kronecker products
123I_ma = eye(ma);
124I_ms = eye(ms);
125
126A = zeros(m, m * (K + 2));
127
128% A0 = I_ma \otimes S1 (service completion)
129A0 = kron(I_ma, S1);
130A(:, 1:m) = A0;
131
132% A1 = D0 \otimes I_ms + I_ma \otimes S0 (phase changes only)
133A1 = kron(D{1}, I_ms) + kron(I_ma, S0);
134A(:, m+1:2*m) = A1;
135
136% A_{k+1} = D_k \otimes I_ms for k >= 1 (batch arrivals)
137for k = 1:K
138 Ak = kron(D{k+1}, I_ms);
139 A(:, (k+1)*m + 1 : (k+2)*m) = Ak;
140end
141
142% B = [B0, B1, B2, ..., BK] for level 0 (empty queue)
143B = zeros(m, m * (K + 1));
144
145% B0: At level 0, no service (add S1 back as self-loop)
146B0 = kron(D{1}, I_ms) + kron(I_ma, S0 + S1);
147B(:, 1:m) = B0;
148
149% B_k = D_k \otimes I_ms for k >= 1 (batch arrivals from empty queue)
150for k = 1:K
151 Bk = kron(D{k+1}, I_ms);
152 B(:, k*m + 1 : (k+1)*m) = Bk;
153end
154
155%% Verify stability condition
156D0 = D{1};
157D1_total = zeros(ma, ma);
158for k = 1:K
159 D1_total = D1_total + D{k+1};
160end
161
162% Stationary distribution of BMAP
163pi_bmap = map_prob({D0, D1_total});
164e_ma = ones(ma, 1);
165
166% Total customer arrival rate: sum_k (k * π * Dk * e)
167lambda_total = 0;
168for k = 1:K
169 rate_k = pi_bmap * D{k+1} * e_ma;
170 lambda_total = lambda_total + k * rate_k;
171end
172
173% Compute service rate from MAP
174pi_map = map_prob({S0, S1});
175e_ms = ones(ms, 1);
176mu = pi_map * S1 * e_ms; % Service rate
177
178% Utilization
179rho = lambda_total / mu;
180
181if rho >= 1
182 warning('solver_mam_qsys:Unstable', ...
183 'System is unstable (rho = %.4f >= 1). Results may be invalid.', rho);
184end
185
186%% Compute G matrix using MG1_G_ETAQA
187G = MG1_G_ETAQA(A);
188
189%% Compute stationary probabilities using MG1_pi_ETAQA
190pi = MG1_pi_ETAQA(B, A, G);
191
192%% Compute queue length moments using MG1_qlen_ETAQA
193qlen_moments = zeros(1, options.nMoments);
194for n = 1:options.nMoments
195 qlen_moments(n) = MG1_qlen_ETAQA(B, A, pi, n);
196end
197
198%% Compute performance metrics
199QN = qlen_moments(1); % Mean queue length (E[N])
200UN = rho; % Utilization
201TN = lambda_total; % Throughput (total arrival rate)
202RN = QN / TN; % Mean response time (Little's law: E[R] = E[N] / λ)
203
204end
205
206%% MAP/BMAP/1 solver using GI/M/1 type analysis
207function [QN, UN, RN, TN, piAgg, R] = solveMAPBMAP1(C, D, ma, ms, options)
208% Solves MAP/BMAP/1 using GI/M/1 type matrix-analytic methods
209%
210% GI/M/1 type structure (level jumps UP by 1, DOWN by any amount):
211% A0 = C1 \otimes I_ms (MAP arrival, level +1)
212% A1 = C0 \otimes I_ms + I_ma \otimes D0 (phase changes, level 0)
213% A_{k+1} = I_ma \otimes D_k (batch size k service, level -k)
214
215C0 = C{1};
216C1 = C{2};
217K = length(D) - 1; % max batch size for service
218m = ma * ms; % Combined phases per level
219
220%% Validate inputs
221% Check MAP is valid generator
222rowSumsC = sum(C0 + C1, 2);
223if max(abs(rowSumsC)) > 1e-10
224 error('MAP matrices C0 + C1 must have zero row sums');
225end
226
227% Check BMAP is valid generator
228DTotal = D{1};
229for k = 1:K
230 DTotal = DTotal + D{k+1};
231end
232rowSumsD = sum(DTotal, 2);
233if max(abs(rowSumsD)) > 1e-10
234 error('BMAP matrices D0 + D1 + ... + DK must have zero row sums');
235end
236
237%% Compute arrival and service rates
238% Arrival rate from MAP
239piC = map_prob({C0, C1});
240lambdaArr = piC * C1 * ones(ma, 1);
241
242% Service rate from BMAP (total customers served per unit time)
243piD = map_prob({D{1}, DTotal - D{1}}); % D0, sum of D1...DK
244muTotal = 0;
245for k = 1:K
246 muTotal = muTotal + k * (piD * D{k+1} * ones(ms, 1));
247end
248
249% Utilization
250rho = lambdaArr / muTotal;
251
252if rho >= 1.0
253 warning('solver_mam_qsys:Unstable', ...
254 'System is unstable (rho = %.4f >= 1). Results may be invalid.', rho);
255end
256
257%% Construct A matrix for GI/M/1-type (repeating part)
258A = zeros(m * (K + 2), m);
259
260% A0: MAP arrival (level +1)
261A0 = kron(C1, eye(ms));
262A(1:m, :) = A0;
263
264% A1: Phase changes only (level 0)
265A1 = kron(C0, eye(ms)) + kron(eye(ma), D{1});
266A(m+1:2*m, :) = A1;
267
268% A_{k+1}: Batch service of size k (level -k)
269for k = 1:K
270 Ak = kron(eye(ma), D{k+1});
271 A((k+1)*m+1:(k+2)*m, :) = Ak;
272end
273
274%% Construct B matrix for boundary levels
275B = zeros(m * (K + 1) + m, m);
276
277% B1 (level 0 -> level 0): Phase changes only, no service from empty
278B1 = kron(C0, eye(ms)) + kron(eye(ma), D{1});
279% Add service transitions that would go to negative levels back to level 0
280for k = 1:K
281 B1 = B1 + kron(eye(ma), D{k+1});
282end
283B(1:m, :) = B1;
284
285% B2, B3, ..., B_{K+1}: Transitions to level 0 from levels 1, 2, ..., K
286for j = 1:K
287 Bj = zeros(m, m);
288 for k = j:K
289 Bj = Bj + kron(eye(ma), D{k+1});
290 end
291 B(m + (j-1)*m + 1 : m + j*m, :) = Bj;
292end
293
294%% Compute R matrix using GI/M/1 ETAQA
295R = GIM1_R_ETAQA(A);
296
297%% Compute stationary probabilities using GI/M/1 ETAQA
298piAgg = GIM1_pi_ETAQA(B, A, R, 'Boundary', A0);
299
300%% Compute performance metrics
301% Mean queue length using ETAQA moments
302QN = GIM1_qlen_ETAQA(B, A, R, piAgg, 1, 'Boundary', A0);
303
304% Throughput equals arrival rate (in steady state)
305TN = lambdaArr;
306
307% Utilization
308UN = rho;
309
310% Mean response time via Little's Law
311RN = QN / TN;
312
313end
314
315%% BMAP/BMAP/1 solver using finite CTMC truncation
316function [QN, UN, RN, TN, piAgg, G] = solveBMAPBMAP1(D, S, ma, ms, options)
317% Solves BMAP/BMAP/1 using finite CTMC truncation
318%
319% When both arrivals and service are batched, the process is neither
320% M/G/1 type nor GI/M/1 type. We construct a finite truncated CTMC
321% over the Kronecker product of arrival and service phases per level.
322%
323% State: (level, arrival_phase, service_phase)
324% level = number of customers in system (0, 1, ..., L)
325% arrival_phase = 1, ..., ma
326% service_phase = 1, ..., ms
327
328Ka = length(D) - 1; % max batch size for arrivals
329Ks = length(S) - 1; % max batch size for service
330m = ma * ms; % combined phases per level
331
332%% Compute arrival and service rates for stability check
333D1_total = zeros(ma, ma);
334for k = 1:Ka
335 D1_total = D1_total + D{k+1};
336end
337pi_bmap = map_prob({D{1}, D1_total});
338e_ma = ones(ma, 1);
339lambda_total = 0;
340for k = 1:Ka
341 lambda_total = lambda_total + k * (pi_bmap * D{k+1} * e_ma);
342end
343
344S1_total = zeros(ms, ms);
345for k = 1:Ks
346 S1_total = S1_total + S{k+1};
347end
348pi_smap = map_prob({S{1}, S1_total});
349e_ms = ones(ms, 1);
350mu_total = 0;
351for k = 1:Ks
352 mu_total = mu_total + k * (pi_smap * S{k+1} * e_ms);
353end
354
355rho = lambda_total / mu_total;
356
357if rho >= 1
358 warning('solver_mam_qsys:Unstable', ...
359 'System is unstable (rho = %.4f >= 1). Results may be invalid.', rho);
360end
361
362%% Choose truncation level based on utilization
363L = max(100, ceil(20 / (1 - min(rho, 0.99))));
364L = min(L, 500); % cap for memory
365
366%% Build generator matrix Q as sparse (L+1)*m x (L+1)*m
367N_states = (L + 1) * m;
368I_ma = speye(ma);
369I_ms = speye(ms);
370
371% Pre-compute Kronecker products
372arrKron = cell(1, Ka);
373for k = 1:Ka
374 arrKron{k} = kron(sparse(D{k+1}), I_ms);
375end
376svcKron = cell(1, Ks);
377for k = 1:Ks
378 svcKron{k} = kron(I_ma, sparse(S{k+1}));
379end
380phaseKron = kron(sparse(D{1}), I_ms) + kron(I_ma, sparse(S{1}));
381
382% Estimate non-zeros and pre-allocate
383nnz_est = (L+1) * m * m * (1 + Ka + Ks);
384ii = zeros(nnz_est, 1);
385jj = zeros(nnz_est, 1);
386vv = zeros(nnz_est, 1);
387cnt = 0;
388
389for lev = 0:L
390 base = lev * m;
391
392 % Internal phase transitions: D_0 x I_ms + I_ma x S_0
393 [ri, ci, vi] = find(phaseKron);
394 n_entries = length(ri);
395 ii(cnt+1:cnt+n_entries) = base + ri;
396 jj(cnt+1:cnt+n_entries) = base + ci;
397 vv(cnt+1:cnt+n_entries) = vi;
398 cnt = cnt + n_entries;
399
400 % Arrivals: batch size k, level -> min(level+k, L)
401 for k = 1:Ka
402 new_lev = min(lev + k, L);
403 dest = new_lev * m;
404 [ri, ci, vi] = find(arrKron{k});
405 n_entries = length(ri);
406 ii(cnt+1:cnt+n_entries) = base + ri;
407 jj(cnt+1:cnt+n_entries) = dest + ci;
408 vv(cnt+1:cnt+n_entries) = vi;
409 cnt = cnt + n_entries;
410 end
411
412 if lev > 0
413 % Service: batch size j, level -> max(level-j, 0)
414 for j = 1:Ks
415 new_lev = max(lev - j, 0);
416 dest = new_lev * m;
417 [ri, ci, vi] = find(svcKron{j});
418 n_entries = length(ri);
419 ii(cnt+1:cnt+n_entries) = base + ri;
420 jj(cnt+1:cnt+n_entries) = dest + ci;
421 vv(cnt+1:cnt+n_entries) = vi;
422 cnt = cnt + n_entries;
423 end
424 else
425 % Level 0: server idle, service completions stay at level 0
426 for j = 1:Ks
427 [ri, ci, vi] = find(svcKron{j});
428 n_entries = length(ri);
429 ii(cnt+1:cnt+n_entries) = base + ri;
430 jj(cnt+1:cnt+n_entries) = base + ci;
431 vv(cnt+1:cnt+n_entries) = vi;
432 cnt = cnt + n_entries;
433 end
434 end
435end
436
437% Trim and assemble sparse Q
438ii = ii(1:cnt);
439jj = jj(1:cnt);
440vv = vv(1:cnt);
441Q = sparse(ii, jj, vv, N_states, N_states);
442
443% Fix diagonal: Q(i,i) = -sum of off-diagonal entries in row i
444d = full(sum(Q, 2));
445Q = Q - spdiags(d, 0, N_states, N_states);
446
447%% Solve for steady-state distribution
448% Solve pi * Q = 0 with pi * e = 1
449% Equivalent to Q' * pi' = 0
450QT = Q';
451% Replace last equation with normalization constraint
452QT(N_states, :) = 1;
453b = sparse(N_states, 1);
454b(N_states) = 1;
455pi = full(QT \ b)';
456
457% Ensure non-negative
458pi = max(pi, 0);
459pi = pi / sum(pi);
460
461%% Compute performance metrics
462% Mean queue length
463levels = 0:L;
464QN = levels * reshape(sum(reshape(pi, m, L+1), 1), L+1, 1);
465
466% Utilization and throughput
467UN = rho;
468TN = lambda_total;
469
470% Mean response time via Little's law
471RN = QN / TN;
472
473% Aggregate probabilities by level
474piAgg = sum(reshape(pi, m, L+1), 1);
475
476G = []; % No G matrix for truncation approach
477
478end