1function [muf, Psi, mu] = pfqn_oi_fnc(Phi, N, f, options)
2% [MUF, PSI, MU] = PFQN_OI_FNC(PHI, N, F, OPTIONS)
4% Order-independent (OI) generalization of
the load-dependent functional
5% server (FNC) of Casale,
"On Single-Class Load-Dependent Normalizing
6% Constant Equations", QEST 2006 (Theorem 3, Corollary 1). It
is the OI
7% counterpart of PFQN_FNC.
9% Given
the balance function Phi of an existing OI station in
the model and a
10% queue-dependent target f(n) (
default f(n) = sum(n),
the total occupancy),
11%
the routine builds a *functional server*: an auxiliary OI station whose
12% balance function Psi satisfies
the convolution identity
14% (Psi * Phi)(n) = (1 + f(n)) Phi(n), (*)
16% where *
is the OI (population-lattice) convolution
17% (Psi * Phi)(n) = sum_{0<=k<=n} Psi(k) Phi(n-k). By construction Psi(0)=1
18% (since f(0)=0). Inserting this server (with
the same per-class demand as
19%
the existing station) into
the network and letting G, G^{+} be
the OI
20% normalizing constants without and with it, (*) yields
the FNC identity
22% E[f(n)] = G^{+}/G - 1,
24% i.e.
the mean of
the queue-dependent function
is read off a ratio of
25% normalizing constants, with no probabilities and no Little
's law. For
26% f(n)=sum(n) this returns the exact total mean queue length of the station.
28% Construction (two steps):
29% 1. Deconvolution of (*) for the FNC balance function, solved triangularly
30% in column-major order (k<n precedes n):
31% Psi(n) = (1+f(n)) Phi(n) - sum_{0<=k<n} Psi(k) Phi(n-k).
32% 2. Balanced-fairness inversion of Psi to the FNC rate function:
33% mu_f(n) = ( sum_{r: n_r>0} Psi(n-e_r) ) / Psi(n).
34% Step 2 alone is the scalar PFQN_FNC when R=1 and Phi is the trivial LI
35% balance (Phi==1): there Psi==1 and mu_f==1, i.e. the FNC degenerates to
36% an identical LI copy of the station, as in the QEST 2006 example.
38% As noted in that reference (Sec. 6), the FNC balance/rate may be signed or
39% non-physical; this is immaterial because only the final normalizing-constant
40% ratio is used, and it stays positive with its usual interpretation. Hence
41% the functional server is best convolved through its balance function Psi
42% rather than through the positivity-guarded rate peeling of PFQN_OI_NC.
45% Phi - balance function of the existing OI station over the lattice, an
46% R-dimensional array of size (N_1+1) x ... x (N_R+1) (Phi(n) at
47% subscript n+1), or a flat column-major vector. Obtain it by the
48% forward balanced-fairness recursion from the station rate function.
49% N - (1 x R) closed population vector, finite. Optional when Phi is a
50% full R-dimensional array (then N = size(Phi) - 1).
51% f - target queue-dependent function handle f(n), n a (1 x R) count
52% vector, with f(0)=0 (default f = @(n) sum(n)).
53% options - solver options (optional, accepted for signature parity).
56% muf - function handle muf(n) giving the FNC rate mu_f(n) (out-of-lattice
57% states return Inf). mu_f(0)=0; states with Psi(n)=0 return Inf.
58% Psi - R-dimensional array (lattice shape) with the FNC balance function.
59% mu - R-dimensional array with the tabulated FNC rate mu_f.
61% See also PFQN_FNC, PFQN_OI_NC.
63% Copyright (c) 2012-2026, Imperial College London
67 options = struct(); %#ok<NASGU>
69if nargin < 3 || isempty(f)
72if nargin < 2 || isempty(N)
81if numel(Phiv) ~= total
82 line_error(mfilename,
'numel(Phi) must equal prod(N+1).');
85% Column-major strides and decoded subscripts.
87for d = 2:R, stride(d) = stride(d-1) * shp(d-1); end
88subs = zeros(total, R);
91 for d = 1:R, subs(i, d) = mod(li, shp(d)); li = floor(li / shp(d)); end
94% ---- Step 1: deconvolve (Psi * Phi)(n) = (1 + f(n)) Phi(n)
for Psi --------
95Psiv = zeros(total, 1);
98 acc = (1 + f(n)) * Phiv(i);
99 % subtract sum over proper sub-lattice k < n of Psi(k) Phi(n-k)
103 idx = 1 + sum((n - k) .* stride); % linear index of n-k
104 acc = acc - Psiv(j) * Phiv(idx);
110% ---- Step 2: balanced-fairness inversion of Psi to
the FNC rate -----------
115 muv(i) = 0; % empty state
120 muv(i) = Inf; % non-physical / undefined rate
125 if n(r) > 0, num = num + Psiv(i - stride(r)); end
127 muv(i) = num / denom;
131 Psi = Psiv; mu = muv;
133 Psi = reshape(Psiv, shp); mu = reshape(muv, shp);
135muf = @(nq) oi_fnc_eval(nq, muv, shp, stride);
138function val = oi_fnc_eval(n, muv, shp, stride)
140if numel(n) ~= numel(shp) || any(n < 0) || any(n > shp - 1)
141 val = Inf; % outside
the tabulated lattice
144val = muv(1 + sum(n .* stride));