LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
qsys_ldps_workload.m
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.
5%
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
14% a PS station;
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
19% through beta.
20%
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
23%
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,
28%
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
31%
32% p_h propto rho^h / prod_{k=1}^{h} alpha(k),
33%
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.
37%
38% Inputs:
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.
48% Outputs:
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
53%
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.
57%
58% Reference: J.W. Cohen, "The multiple phase service network with generalized
59% processor sharing", Acta Informatica 12, 245-284 (1979), Sect. 9.
60
61% Copyright (c) 2012-2026, Imperial College London
62% All rights reserved.
63
64if nargin < 4
65 line_error(mfilename,'Requires at least four inputs: lambda, B, alpha, N.');
66end
67
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.');
71end
72if ~isa(B,'Distribution')
73 line_error(mfilename,'B must be a Distribution object giving the required service time.');
74end
75if B.isDisabled() || B.isImmediate()
76 line_error(mfilename,'B must be an active service time distribution, not Disabled or Immediate.');
77end
78if ~B.isContinuous()
79 line_error(mfilename,'B must be a continuous distribution: Cohen (1979) Sect. 9 assumes an absolutely continuous required service time.');
80end
81beta = B.getMean();
82if ~isfinite(beta) || beta <= 0
83 line_error(mfilename,'B must have a finite positive mean.');
84end
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.');
87end
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).');
90end
91alpha = alpha(:).';
92if numel(alpha) < 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)));
94end
95alpha = alpha(1:N);
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.');
98end
99
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.
103rho = lambda * beta;
104logw = [0, cumsum(log(rho) - log(alpha))];
105logw = logw - max(logw);
106w = exp(logw);
107p = w / sum(w);
108
109%% grid
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.');
116end
117userGrid = nargin >= 5 && ~isempty(t);
118if userGrid
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.');
121 end
122 t = t(:).';
123 tmax = max(t);
124 if tmax <= 0
125 tmax = m1e;
126 end
127else
128 hmax = find(p > 1e-12, 1, 'last') - 1;
129 if isempty(hmax) || hmax < 1
130 hmax = 1;
131 end
132 tmax = m1e * (hmax + 8*sqrt(hmax));
133 tmax = max(tmax, 8*m1e);
134end
135
136% Internal uniform grid: the convolutions below need a constant step. The
137% result is interpolated back onto t when the caller supplied one.
138% Accuracy is second order in the step for an absolutely continuous B, the
139% case Cohen assumes. It falls back to first order when B has an atom, so
140% that 1-B is discontinuous and the equilibrium density is a step: Det is the
141% extreme case. Raise ngrid for those. Measured against closed forms, the
142% error of F decays by 4.00x per grid doubling for Exp service (Erlang
143% mixture oracle) and by about 2x for Det service (Irwin-Hall mixture
144% oracle), confirming the two regimes.
145if nargin < 6 || isempty(ngrid)
146 ngrid = 2001;
147end
148if ~isnumeric(ngrid) || ~isscalar(ngrid) || ~isfinite(ngrid) || ngrid < 2 || ngrid ~= round(ngrid)
149 line_error(mfilename,'ngrid must be a finite integer of at least 2.');
150end
151tg = linspace(0, tmax, ngrid);
152dt = tg(2) - tg(1);
153
154%% equilibrium residual service distribution, eq. (9.3)
155% Density of Psi is (1-B(v))/beta. evalCDF is not vectorized by every
156% Distribution subclass, so it is evaluated pointwise.
157e = arrayfun(@(v) (1 - B.evalCDF(v)) / beta, tg);
158
159%% workload distribution, eq. (9.2)
160% F = sum_h p_h Psi^{h*}. The h=0 term is degenerate at zero, contributing the
161% atom p_0 over the whole non-negative grid.
162Fg = p(1) * ones(1, ngrid);
163dens = e;
164for h = 1:N
165 if h > 1
166 dens = local_convtrap(dens, e, dt, ngrid);
167 end
168 Fg = Fg + p(h+1) * cumtrapz(tg, dens);
169end
170
171if userGrid
172 F = interp1(tg, Fg, t, 'linear');
173else
174 F = Fg;
175 t = tg;
176end
177end
178
179function c = local_convtrap(f, g, dt, n)
180% Convolution of two densities sampled on a uniform grid, using the
181% trapezoidal rule rather than the rectangle rule implied by a bare conv().
182% The correction matters here because the equilibrium density does not vanish
183% at the origin: e(0)=1/beta. Writing t_i=i*dt,
184%
185% (f*g)(t_i) = int_0^{t_i} f(v) g(t_i-v) dv
186% ~ dt*[ sum_{j=0}^{i} f_j g_{i-j} - (f_0 g_i + f_i g_0)/2 ],
187%
188% i.e. conv() less half of each endpoint. Without the correction each
189% convolution over-counts by dt*f_0*g_i, which accumulates over h and drives
190% the mixture CDF above one.
191c = conv(f, g);
192c = c(1:n) * dt;
193c = c - dt * (f(1)*g(1:n) + f(1:n)*g(1)) / 2;
194end
Definition Station.m:245