1function [T,rho] = qsys_mm1_dps(lambda, mu, w, tol, maxCutoff)
2% [T,RHO] = QSYS_MM1_DPS(LAMBDA, MU, W, TOL, MAXCUTOFF)
4% Numerically exact M/M/1 Discriminatory Processor Sharing (DPS) queue.
6% Solves
the multiclass DPS continuous-time Markov chain on
the per-
class
7% population vector (n_1..n_K): arrivals lambda(k), class-k completion rate
8% mu(k)*n_k*w(k)/sum_j n_j*w(j). The state space
is truncated at a total
9% population level chosen from
the geometric tail bound and doubled until
the
10% per-class mean counts are stable, so
the result
is exact to solver precision
11% and conserves
the M/M/1 total for equal service rates by construction.
14% lambda - per-class Poisson arrival rates (1,K)
15% mu - per-class exponential service rates (1,K)
16% w - per-class DPS weights (1,K), positive
17% tol - convergence tolerance on
the mean counts (default 1e-10)
18% maxCutoff - hard bound on
the truncation level (default 2048)
21% T - per-class mean response times (1,K) via Little
's law
22% rho - total utilization sum_k lambda(k)/mu(k)
24% Copyright (c) 2012-2026, Imperial College London
27if nargin < 4 || isempty(tol), tol = 1e-10; end
28if nargin < 5 || isempty(maxCutoff), maxCutoff = 2048; end
30lambda = lambda(:)'; mu = mu(:)
'; w = w(:)';
32if any(lambda <= 0) || any(mu <= 0) || any(w <= 0)
33 line_error(mfilename,'lambda, mu, w must all be positive');
35rho = sum(lambda ./ mu);
37 line_error(mfilename,'System
is unstable: utilization rho >= 1');
40N = max(16, ceil(log(tol)/log(rho)));
42ENprev = solveTrunc(N);
44 N2 = min(2*N, maxCutoff);
46 if max(abs(EN - ENprev)) < tol
51 if N2 == maxCutoff, break; end
55 function EN = solveTrunc(Ncut)
56 % enumerate states with total population <= Ncut
58 [grids{:}] = ndgrid(0:Ncut);
59 S = zeros(numel(grids{1}), K);
60 for kk=1:K, S(:,kk) = grids{kk}(:); end
61 S = S(sum(S,2) <= Ncut, :);
63 keys = S * ((Ncut+1).^(0:K-1))
';
64 idx = sparse(keys+1, 1, 1:n);
65 rows = []; cols = []; vals = [];
69 % arrivals in
class kk (blocked at
the truncation level)
72 dstKeys = keys(src) + (Ncut+1)^(kk-1);
73 dst = full(idx(dstKeys+1));
74 rows = [rows; src]; cols = [cols; dst]; vals = [vals; repmat(lambda(kk), numel(src), 1)]; %#ok<AGROW>
75 % departures in
class kk (DPS capacity split)
78 r = mu(kk) .* S(src,kk) .* w(kk) ./ den(src);
79 dstKeys = keys(src) - (Ncut+1)^(kk-1);
80 dst = full(idx(dstKeys+1));
81 rows = [rows; src]; cols = [cols; dst]; vals = [vals; r]; %#ok<AGROW>
83 Q = sparse(rows, cols, vals, n, n);
84 Q = Q - diag(sum(Q,2));
87 b = zeros(n,1); b(1) = 1;