LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
solver_mam_ldqbd.m
1function [QN, UN, RN, TN, CN, XN, totiter] = solver_mam_ldqbd(sn, options)
2% SOLVER_MAM_LDQBD Solve single-class closed queueing networks using LD-QBD
3%
4% Uses Level-Dependent Quasi-Birth-Death (LD-QBD) process to compute
5% exact performance metrics for single-class closed queueing networks
6% consisting of a Delay (infinite server) and a Queue (FCFS).
7%
8% The LD-QBD approach models the system where:
9% - Level n = number of jobs at the queue (0 <= n <= N)
10% - Jobs at delay = N - n
11% - Transition rates depend on the current level
12%
13% Supports PH-type service distributions (Exp, Erlang, HyperExp, etc.)
14%
15% Copyright (c) 2012-2026, Imperial College London
16% All rights reserved.
17
18%% Validate model structure
19M = sn.nstations;
20K = sn.nclasses;
21N = sn.njobs';
22
23% Check: single-class closed network
24if K ~= 1
25 line_error(mfilename, 'LDQBD method requires a single-class model.');
26end
27
28if ~isfinite(N)
29 line_error(mfilename, 'LDQBD method requires a closed model.');
30end
31
32% Check: must have exactly one delay and one queue
33nDelay = sum(sn.sched == SchedStrategy.INF);
34nQueue = sum(sn.sched == SchedStrategy.FCFS);
35
36if nDelay ~= 1 || nQueue ~= 1 || M ~= 2
37 line_error(mfilename, 'LDQBD method requires exactly one Delay and one Queue station.');
38end
39
40%% Identify stations
41delayIdx = find(sn.sched == SchedStrategy.INF);
42queueIdx = find(sn.sched == SchedStrategy.FCFS);
43
44%% Get service parameters
45PH = sn.proc;
46rates = sn.rates;
47nservers = sn.nservers;
48
49% Delay station rate (infinite server)
50lambda_d = rates(delayIdx, 1);
51
52% Get routing probability from Delay to Queue
53% sn.rt is (M*K) x (M*K) routing table
54rt = sn.rt;
55p_delay_to_queue = rt(delayIdx, queueIdx); % Since K=1, simple indexing works
56
57% Effective arrival rate to queue = delay rate * routing probability
58lambda_eff = lambda_d * p_delay_to_queue;
59
60% Queue service process
61PH_queue = PH{queueIdx}{1};
62nServers = nservers(queueIdx);
63
64% Check if queue service is exponential (1x1 matrix) or PH
65if numel(PH_queue{1}) == 1
66 % Exponential service
67 mu = -PH_queue{1}; % Rate from D0 matrix
68 nPhases = 1;
69 isPH = false;
70else
71 % PH-type service (Erlang, HyperExp, etc.)
72 nPhases = size(PH_queue{1}, 1);
73 isPH = true;
74 D0 = PH_queue{1}; % Sub-generator (negative diag = rates out)
75 D1 = PH_queue{2}; % Absorption/completion transitions
76 alpha = map_pie(PH_queue); % Initial phase distribution
77end
78
79%% Construct LD-QBD matrices
80% For a closed network with N jobs:
81% Level n = number of jobs at queue (0 <= n <= N)
82% Q0^(n): upward transitions (arrival from delay)
83% Q1^(n): local transitions (within level)
84% Q2^(n): downward transitions (departure from queue)
85
86Q0 = cell(N, 1); % {Q0^(0), Q0^(1), ..., Q0^(N-1)}
87Q1 = cell(N+1, 1); % {Q1^(0), Q1^(1), ..., Q1^(N)}
88Q2 = cell(N, 1); % {Q2^(1), Q2^(2), ..., Q2^(N)}
89
90if ~isPH
91 % Exponential service case (scalar matrices)
92 for n = 0:N-1
93 % Arrival rate: (N-n) * lambda_eff (effective rate to queue)
94 Q0{n+1} = (N - n) * lambda_eff;
95 end
96
97 for n = 0:N
98 arrival_rate = (N - n) * lambda_eff;
99 if n > 0
100 % Service rate: min(n, nServers) * mu
101 departure_rate = min(n, nServers) * mu;
102 else
103 departure_rate = 0;
104 end
105 Q1{n+1} = -(arrival_rate + departure_rate);
106 end
107
108 for n = 1:N
109 % Service rate: min(n, nServers) * mu
110 Q2{n} = min(n, nServers) * mu;
111 end
112else
113 % PH-type service case (matrix-valued)
114 % State space at level n (n>0): nPhases phases
115 % State space at level 0: 1 state (empty queue)
116
117 % Level 0 -> 1: arrival starts service in some phase
118 % 1 x nPhases matrix: arrival rate * initial phase distribution
119 Q0{1} = N * lambda_eff * alpha;
120
121 % Level n -> n+1 (n >= 1): arrival, preserve phase
122 for n = 1:N-1
123 % nPhases x nPhases matrix: diagonal (preserve phase)
124 Q0{n+1} = (N - n) * lambda_eff * eye(nPhases);
125 end
126
127 % Level 0: 1x1 (no service, only arrivals)
128 Q1{1} = -(N * lambda_eff);
129
130 % Level n >= 1: phase transitions within level
131 for n = 1:N
132 arrival_rate = (N - n) * lambda_eff;
133 % Number of active servers at level n
134 c_n = min(n, nServers);
135
136 % Local transition matrix: D0 (phase transitions) - arrivals
137 % Scale D0 by number of active servers for multi-server
138 Q1{n+1} = c_n * D0 - arrival_rate * eye(nPhases);
139 end
140
141 % Level 1 -> 0: service completion, go to empty state
142 % nPhases x 1 column vector (sum over destination phases)
143 c_1 = min(1, nServers);
144 Q2{1} = c_1 * D1 * ones(nPhases, 1);
145
146 % Level n -> n-1 (n >= 2): service completion, next job starts
147 % D1(i,j) is rate of completing from phase i and next starting in phase j
148 for n = 2:N
149 c_n = min(n, nServers);
150 % nPhases x nPhases: D1 encodes completion and restart phase
151 Q2{n} = c_n * D1;
152 end
153end
154
155%% Solve LD-QBD using the ldqbd function
156ldqbd_options = struct('epsilon', options.tol, 'maxIter', options.iter_max, 'verbose', false);
157
158[R, pi_ldqbd] = ldqbd(Q0, Q1, Q2, ldqbd_options);
159
160%% Compute performance metrics from steady-state distribution
161% Mean queue length at the queue station
162mean_queue = (0:N) * pi_ldqbd';
163
164% Mean queue length at delay station (complementary)
165mean_delay = N - mean_queue;
166
167%% Compute throughput and utilization
168% For delay (infinite server): each job at delay generates arrivals to queue
169% at the effective rate lambda_eff = lambda_d * P(Delay->Queue)
170% System throughput X = mean_delay * lambda_eff (flow balance)
171X = mean_delay * lambda_eff;
172
173% Mean service time at queue
174if ~isPH
175 mean_service = 1/mu;
176else
177 mean_service = map_mean(PH_queue);
178end
179
180% Queue utilization: probability queue is busy (at least 1 job)
181% For multi-server: average fraction of server capacity in use
182if nServers == 1
183 util_queue = 1 - pi_ldqbd(1);
184else
185 % For c-server queue: U = sum_{n=1}^{N} min(n,c)/c * pi(n)
186 util_queue = 0;
187 for n = 1:N
188 util_queue = util_queue + (min(n, nServers) / nServers) * pi_ldqbd(n+1);
189 end
190end
191
192% Response time at queue (using Little's law: R = Q/X)
193if X > 0
194 R_queue = mean_queue / X;
195else
196 R_queue = 0;
197end
198
199% Response time at delay (constant for infinite server)
200R_delay = 1 / lambda_d;
201
202%% Populate output matrices
203QN = zeros(M, K);
204UN = zeros(M, K);
205RN = zeros(M, K);
206TN = zeros(M, K);
207CN = zeros(1, K);
208XN = zeros(1, K);
209
210% Delay station metrics
211QN(delayIdx, 1) = mean_delay;
212UN(delayIdx, 1) = mean_delay; % For infinite server, U = Q
213RN(delayIdx, 1) = R_delay;
214TN(delayIdx, 1) = X;
215
216% Queue station metrics
217QN(queueIdx, 1) = mean_queue;
218UN(queueIdx, 1) = util_queue / nServers; % Per-server utilization
219RN(queueIdx, 1) = R_queue;
220TN(queueIdx, 1) = X;
221
222% System-level metrics
223XN(1) = X;
224CN(1) = R_delay + R_queue; % Cycle time
225
226totiter = 1; % LDQBD is a direct method
227
228end