1function [F, t, p] = qsys_ldps_workload(lambda, B, alpha, N, t, ngrid)
2% [F,t,p] = QSYS_LDPS_WORKLOAD(lambda,B,alpha,N,t) - Stationary distribution of
3% the quantity of work in a single-stage load-dependent processor sharing
4% station with Poisson arrivals and blocking.
6% Assumed model (Cohen, 1979, Sect. 9; the model of Sect. 7 with one stage):
7% - a single service stage fed by a Poisson arrival stream of rate lambda;
8% - blocking capacity N: a request arriving when N requests are already
9% present
is lost and leaves no trace on the state;
10% - generalized processor sharing: when x requests are present each of them
11% accrues service at rate f(x), so the stage completes work at total rate
12% x*f(x). This function
is parametrized by the LINE load-dependent total
13% rate scaling alpha(x)=x*f(x), i.e. the argument of setLoadDependence at
15% - required service times are i.i.d. with absolutely continuous
16% distribution B and finite mean beta.
17% The result
is insensitive to B beyond its shape only through the equilibrium
18% residual distribution Psi below; the state probabilities p depend on B only
21% Let psi_t denote the total amount of service still to be given to the
22% requests present at time t. Cohen (1979) eqs. (9.1)-(9.3) give
24% Pr{psi_t < psi} =
sum_{h=0}^{N} p_h Psi^{h*}(psi),
25% p_h = (rho^h/h!) phi(h) /
sum_{k=0}^{N} (rho^k/k!) phi(k), rho=lambda*beta
26% phi(h) = 1/prod_{k=1}^{h} f(k), phi(0)=1,
27% Psi(psi) = int_0^psi (1-B(v))/beta dv,
29% with Psi^{h*} the h-fold convolution of Psi and Psi^{0*} degenerate at zero.
30% Substituting f(k)=alpha(k)/k the factorial cancels, leaving
32% p_h propto rho^h / prod_{k=1}^{h} alpha(k),
34% which
is the familiar load-dependent birth-death
form. Psi
is the
35% equilibrium (residual life) distribution of B, so psi_t
is a mixture of
36% h-fold convolutions of residual service times, with an atom p_0 at zero.
39% lambda : Poisson arrival rate (finite, positive)
40% B : Distribution
object for the required service time (continuous,
41% finite positive mean)
42% alpha : rate scaling alpha(n)=n*f(n)
for n=1..N (finite, positive)
43% N : blocking capacity (finite positive integer)
44% t : optional grid at which the CDF
is returned. Default: an
45% automatically sized grid covering the bulk of the distribution.
46% ngrid : optional number of points of the internal uniform quadrature
47% grid on which the convolutions are formed. Default 2001.
49% F : F(j) = Pr{psi_t <= t(j)}. Note F(1)=p(1)=Pr{psi_t=0} when t(1)=0,
50% since the workload has an atom at zero of size p_0.
51% t : grid at which F
is reported
52% p : (1,N+1) vector, p(h+1)=Pr{x_t=h}, the stationary number in system
54% This
is the model of Cohen (1979) Sect. 9 only. It
is not the weighted
55% GPS/DPS discipline of SchedStrategy.GPS, whose per-class weights this
56% formula does not represent.
58% Reference: J.W. Cohen, "The multiple phase service network with generalized
59% processor sharing", Acta Informatica 12, 245-284 (1979), Sect. 9.
61% Copyright (c) 2012-2026, Imperial College London
65 line_error(mfilename,'Requires at least four inputs: lambda, B, alpha, N.');
68%% gating: reject anything outside the assumed model
69if ~isnumeric(lambda) || ~isscalar(lambda) || ~isfinite(lambda) || lambda <= 0
70 line_error(mfilename,'lambda must be a finite positive scalar, the rate of the Poisson arrival stream.');
72if ~isa(B,'Distribution')
73 line_error(mfilename,'B must be a Distribution
object giving the required service time.');
75if B.isDisabled() || B.isImmediate()
76 line_error(mfilename,'B must be an active service time distribution, not Disabled or Immediate.');
79 line_error(mfilename,'B must be a continuous distribution: Cohen (1979) Sect. 9 assumes an absolutely continuous required service time.');
82if ~isfinite(beta) || beta <= 0
83 line_error(mfilename,'B must have a finite positive mean.');
85if ~isnumeric(N) || ~isscalar(N) || ~isfinite(N) || N < 1 || N ~= round(N)
86 line_error(mfilename,'N must be a finite positive integer, the blocking capacity of the service stage.');
88if ~isnumeric(alpha) || ~isvector(alpha) || isempty(alpha)
89 line_error(mfilename,'alpha must be a numeric vector holding the rate scaling alpha(n)=n*f(n).');
93 line_error(mfilename,sprintf('alpha must supply the rate scaling for n=1..%d, but only %d entries were given.',N,numel(alpha)));
96if any(~isfinite(alpha)) || any(alpha <= 0)
97 line_error(mfilename,'alpha(n) must be finite and strictly positive for n=1..N, since every request in a busy stage
is served at a positive rate.');
100%% stationary number in system, eq. (9.1)
101% p_h propto rho^h/prod_{k<=h} alpha(k), accumulated in logs so that large
102% rho or large N
do not overflow before normalization.
104logw = [0, cumsum(log(rho) - log(alpha))];
105logw = logw - max(logw);
110% Psi has mean m1e, the mean of the equilibrium residual life of B. The
111% workload
is a mixture of h-fold convolutions of Psi, so sizing the grid on
112% the largest h carrying non-negligible mass covers the bulk of the support.
113m1e = beta * (1 + B.getSCV()) / 2;
114if ~isfinite(m1e) || m1e <= 0
115 line_error(mfilename,
'B must have a finite second moment: the equilibrium residual service time is otherwise undefined.');
117userGrid = nargin >= 5 && ~isempty(t);
119 if ~isnumeric(t) || ~isvector(t) || any(~isfinite(t)) || any(t < 0)
120 line_error(mfilename,'t must be a vector of finite non-negative times.');
128 hmax = find(p > 1e-12, 1, 'last') - 1;
129 if isempty(hmax) || hmax < 1
132 tmax = m1e * (hmax + 8*sqrt(hmax));
133 tmax = max(tmax, 8*m1e);
136% see _kb/03-api-layer.md (qsys/ family) for rationale
137if nargin < 6 || isempty(ngrid)
140if ~isnumeric(ngrid) || ~isscalar(ngrid) || ~isfinite(ngrid) || ngrid < 2 || ngrid ~= round(ngrid)
141 line_error(mfilename,'ngrid must be a finite integer of at least 2.');
143tg = linspace(0, tmax, ngrid);
146%% equilibrium residual service distribution, eq. (9.3)
147% Density of Psi
is (1-B(v))/beta. evalCDF
is not vectorized by every
148% Distribution subclass, so it
is evaluated pointwise.
149e = arrayfun(@(v) (1 - B.evalCDF(v)) / beta, tg);
151%% workload distribution, eq. (9.2)
152% F = sum_h p_h Psi^{h*}. The h=0 term
is degenerate at zero, contributing the
153% atom p_0 over the whole non-negative grid.
154Fg = p(1) * ones(1, ngrid);
158 dens = local_convtrap(dens, e, dt, ngrid);
160 Fg = Fg + p(h+1) * cumtrapz(tg, dens);
164 F = interp1(tg, Fg, t,
'linear');
171function c = local_convtrap(f, g, dt, n)
172% Convolution of two densities sampled on a uniform grid,
using the
173% trapezoidal rule rather than the rectangle rule implied by a bare conv().
174% The correction matters here because the equilibrium density does not vanish
175% at the origin: e(0)=1/beta. Writing t_i=i*dt,
177% (f*g)(t_i) = int_0^{t_i} f(v) g(t_i-v) dv
178% ~ dt*[
sum_{j=0}^{i} f_j g_{i-j} - (f_0 g_i + f_i g_0)/2 ],
180% i.e. conv() less half of each endpoint. Without the correction each
181% convolution over-counts by dt*f_0*g_i, which accumulates over h and drives
182% the mixture CDF above one.
185c = c - dt * (f(1)*g(1:n) + f(1:n)*g(1)) / 2;