1function [Qt, Ut, Tt] = solver_mam_transient_qbd(sn, options)
2% SOLVER_MAM_TRANSIENT_QBD Transient analysis of a single-
class open queue via
3%
the Laplace-domain transient QBD method plus numerical inverse Laplace.
5% Supports single-server MAP/MAP/1 (infinite buffer) and MAP/MAP/1/N (finite
6% buffer), where arrival and service are read uniformly as (D0,D1) MAPs from
7% sn.proc;
this subsumes M/M/1, M/PH/1 and correlated-MAP arrival/service that
8%
the libQBD/expm fast path (solver_mam_ldqbd_transient) cannot represent.
10% The transient level-to-level transform V(s,0,m)
is computed by
11% mam_transient2_open (infinite) or mam_transient2 (finite) and inverted with
12%
the CME-based matrix inverse Laplace transform. Metrics returned in
the
13% [metric, time] layout used by getTranAvg.
15% Copyright (c) 2012-2026, Imperial College London
21 line_error(mfilename,
'Transient QBD method requires a single-class model.');
25 line_error(mfilename, 'Transient QBD method
requires an open model.
');
28sourceIdx = find(sn.sched == SchedStrategy.EXT);
29queueIdx = find(sn.sched == SchedStrategy.FCFS);
30if numel(sourceIdx) ~= 1 || numel(queueIdx) ~= 1
31 line_error(mfilename, 'Transient QBD method
requires exactly one Source and one FCFS Queue.
');
33if sn.nservers(queueIdx) ~= 1
34 line_error(mfilename, 'Transient QBD (Laplace) method supports single-server queues only.
');
37%% Arrival and service MAPs (uniform (D0,D1) representation)
38arrProc = sn.proc{sourceIdx}{1};
39svcProc = sn.proc{queueIdx}{1};
40Da0 = arrProc{1}; Da1 = arrProc{2};
41Ds0 = svcProc{1}; Ds1 = svcProc{2};
44Ina = eye(na); Ins = eye(ns);
46% Repeating-level blocks (levels >= 1): matches qbd_rg convention.
47Lrep = krons(Da0, Ds0); % local, both phases evolve
48Frep = kron(Da1, Ins); % arrival, level up
49Brep = kron(Ina, Ds1); % service completion, level down
51% Level-0 (empty) block: service phase frozen, only arrival evolves.
56% Initial distribution: empty system, stationary arrival and service phases.
57pi_arr = map_prob({Da0, Da1});
58pi_svc = map_prob({Ds0, Ds1});
59pi0 = kron(pi_arr, pi_svc); % 1 x (na*ns), level-0 phase distribution
61% Service-completion rate vector over (arrival,service) phases at a busy level.
62sExit = Ds1 * ones(ns, 1);
63wDep = kron(ones(na, 1), sExit); % departure-rate weight, (na*ns) x 1
64wOne = ones(na * ns, 1);
66bufCap = sn.cap(queueIdx);
67isFinite = ~isinf(bufCap);
69%% Time grid and ILT budget
70T_start = options.timespan(1);
71T_end = options.timespan(2);
73nTimePoints = min(101, max(11, round(dur * 10)));
74times = linspace(T_start, T_end, nTimePoints)';
75% The inverse Laplace transform
is singular at t=0 (s = beta/t -> Inf). At t=0
76%
the system starts empty, so E[N]=U=Tput=0 exactly; evaluate
the ILT only on
77% strictly positive times and fill
the initial condition directly.
80if isfield(options,
'iter_max') && ~isempty(options.iter_max) && options.iter_max > 1
81 maxFnEvals = min(1000, max(11, round(options.iter_max)));
88 % Closed piecewise QBD: T=[0,Ncap], K=1 regime.
89 % Lv{1}=level 0, L{1}=interior repeating, Lv{2}=top level Ncap.
90 LvTop = kron(Da0 + Da1, Ins) + kron(Ina, Ds0); % arrivals blocked (lost) at full buffer
96 % Level dimensions: level 0 and levels 1..Ncap all na*ns here.
99 % EN(s), U(s), Dep(s) as scalar Laplace functions inverted per metric.
100 EN = zeros(size(times)); DEP = zeros(size(times)); Uval = zeros(size(times));
101 EN(posMask) = matlab_ilt(@(s) laplace_EN_finite(s, Bc, Lc, Fc, Lvc, Tc, Ncap, pi0), tpos, maxFnEvals);
102 P0 = matlab_ilt(@(s) laplace_P0(s, Bc, Lc, Fc, Lvc, Tc, pi0, wOne,
false), tpos, maxFnEvals);
103 DEP(posMask) = matlab_ilt(@(s) laplace_DEP_finite(s, Bc, Lc, Fc, Lvc, Tc, Ncap, pi0, wDep), tpos, maxFnEvals);
104 Uval(posMask) = 1 - P0;
106 % Open piecewise QBD: T=[0,1], K=2 (regime 1 = level 0 boundary; regime 2 repeats).
113 % Homogeneous open
case: V(s,0,m) = V(s,0,1) * R^(m-1)
for m>=1, so
the
114 % level sums have exact closed forms (no truncation):
115 % E[N](s) = pi0 * V(s,0,1) * (I-R)^-2 * 1
116 % Tput(s) = pi0 * V(s,0,1) * (I-R)^-1 * wDep
117 % P0(s) = pi0 * V(s,0,0) * 1
118 EN = zeros(size(times)); DEP = zeros(size(times)); Uval = zeros(size(times));
119 EN(posMask) = matlab_ilt(@(s) laplace_EN_open(s, Bo, Lo, Fo, Lvo, To, pi0), tpos, maxFnEvals);
120 P0 = matlab_ilt(@(s) laplace_P0(s, Bo, Lo, Fo, Lvo, To, pi0, wOne,
true), tpos, maxFnEvals);
121 DEP(posMask) = matlab_ilt(@(s) laplace_DEP_open(s, Bo, Lo, Fo, Lvo, To, pi0, wDep), tpos, maxFnEvals);
122 Uval(posMask) = 1 - P0;
126Qt = cell(M, K); Ut = cell(M, K); Tt = cell(M, K);
127Qt{queueIdx, 1} = [EN, times];
128Ut{queueIdx, 1} = [Uval, times];
129Tt{queueIdx, 1} = [DEP, times];
132% ------------------------------------------------------------------------
133function val = laplace_P0(s, B, L, F, Lv, T, pi0, wOne, isOpen)
134% Laplace transform of
P(N(t)=0): pi0 * V(s,0,0) * 1.
136 V = mam_transient2_open(B, L, F, Lv, T, 0, 0, s);
138 V = mam_transient2(B, L, F, Lv, T, 0, 0, s);
143% ------------------------------------------------------------------------
144function val = laplace_EN_finite(s, B, L, F, Lv, T, Ncap, pi0)
145% sum_{m=0}^{Ncap} m * pi0 * V(s,0,m) * 1.
148 V = mam_transient2(B, L, F, Lv, T, 0, m, s);
149 acc = acc + m * (pi0 * V * ones(size(V, 2), 1));
154% ------------------------------------------------------------------------
155function val = laplace_DEP_finite(s, B, L, F, Lv, T, Ncap, pi0, wDep)
156% sum_{m=1}^{Ncap} pi0 * V(s,0,m) * wDep (departure rate).
159 V = mam_transient2(B, L, F, Lv, T, 0, m, s);
160 acc = acc + pi0 * V * wDep;
165% ------------------------------------------------------------------------
166function val = laplace_EN_open(s, B, L, F, Lv, T, pi0)
167% E[N](s) = pi0 * V(s,0,1) * (I-R)^-2 * 1 (exact geometric first moment).
169IK = eye(size(L{K}, 1));
170[~, R] = qbd_fundmat(B{K}, L{K} - s*IK, F{K},
'GR');
171V1 = mam_transient2_open(B, L, F, Lv, T, 0, 1, s);
173val = pi0 * V1 * (ImR \ (ImR \ ones(size(IK, 1), 1)));
176% ------------------------------------------------------------------------
177function val = laplace_DEP_open(s, B, L, F, Lv, T, pi0, wDep)
178% Tput(s) = pi0 * V(s,0,1) * (I-R)^-1 * wDep (exact geometric zeroth moment).
180IK = eye(size(L{K}, 1));
181[~, R] = qbd_fundmat(B{K}, L{K} - s*IK, F{K},
'GR');
182V1 = mam_transient2_open(B, L, F, Lv, T, 0, 1, s);
183val = pi0 * V1 * ((IK - R) \ wDep);