LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
pfqn_oi_insvc.m
1function [g, Xi, Phi] = pfqn_oi_insvc(oirate, N, options)
2% [G, XI, PHI] = PFQN_OI_INSVC(OIRATE, N, OPTIONS)
3%
4% Conditional mean number of IN-SERVICE jobs per class at an order-independent
5% (OI) station, as a function of the per-class count vector n. This is the
6% quantity underlying the LINE utilization convention at OI stations,
7% U_r = E[sir_r] / c,
8% with c the number of servers and sir_r the number of class-r jobs receiving a
9% strictly positive service rate.
10%
11% In an OI station the state is the ordered list c = (c_1,...,c_n) of job
12% classes (position 1 = head) and the job in position p is served at the rank
13% rate increment
14% Delta_p(c) = mu(c_1..c_p) - mu(c_1..c_{p-1}),
15% so that the total rate telescopes to mu(c). Position p is IN SERVICE when
16% Delta_p(c) > 0, and
17% sir_r(c) = #{p : c_p = r, Delta_p(c) > 0}.
18% Note that sir_r counts JOBS, not servers: a single job served concurrently by
19% several compatible servers counts once. This matches the definition used by
20% the exact CTMC solver (State.toMarginal, PAS branch) and by LDES.
21%
22% Because mu is permutation-invariant (a function of the count vector), the
23% unnormalized weight of an ordering c of the multiset n factorizes over its
24% prefixes as w(c) = prod_{p=1}^{|n|} 1/mu(n(c_1..c_p)), and the OI balance
25% function is Phi(n) = sum_{orderings c of n} w(c), which obeys the standard
26% balanced-fairness recursion (condition on the tail element c_{|n|}):
27%
28% Phi(0) = 1, Phi(n) = (1/mu(n)) sum_{r: n_r>0} Phi(n - e_r).
29%
30% Conditioning the same way on the tail element and using
31% sir_r(c) = sir_r(c_1..c_{|n|-1}) + [c_{|n|} = r] * 1{mu(n) > mu(n - e_r)}
32% gives the companion recursion for the sir-weighted balance
33% Xi_r(n) = sum_{orderings c of n} w(c) sir_r(c):
34%
35% Xi_r(0) = 0,
36% Xi_r(n) = (1/mu(n)) [ sum_{s: n_s>0} Xi_r(n - e_s)
37% + 1{n_r > 0} 1{mu(n) > mu(n - e_r)} Phi(n - e_r) ].
38%
39% Given n, every ordering carries the same class-weight factor prod_r x_r^{n_r},
40% so the conditional law of the ordering is w(c)/Phi(n) and
41%
42% E[sir_r | n] = Xi_r(n) / Phi(n) =: g_r(n),
43%
44% a function of the count vector alone. The station's in-service mean then
45% follows from the count marginal pM by E[sir_r] = sum_n pM(n) g_r(n), or, in
46% normalizing-constant form, from the functional-server identity of
47% PFQN_OI_FNC applied to f(n) = g_r(n) (note g_r(0) = 0, as required).
48%
49% Parameters:
50% oirate - function handle mu(n) returning the OI total service rate for the
51% per-class count vector n (1 x R). mu(0) is taken as 0.
52% N - (1 x R) closed population vector, finite.
53% options - solver options (optional, currently unused).
54%
55% Returns:
56% g - (prod(N+1) x R) table, column-major over the lattice 0 <= n <= N, with
57% g(1 + sum(n .* stride), r) = E[sir_r | n].
58% Xi - (prod(N+1) x R) table with the sir-weighted balance Xi_r(n).
59% Phi - (prod(N+1) x 1) table with the OI balance function Phi(n).
60%
61% See also PFQN_OI_FNC, PFQN_OI_NC, PFQN_MVAOI, PFQN_MVAOI_MARG.
62%
63% Copyright (c) 2012-2026, Imperial College London
64% All rights reserved.
65
66if nargin < 3
67 options = struct(); %#ok<NASGU>
68end
69if ~isa(oirate, 'function_handle')
70 line_error(mfilename, 'oirate must be a function handle mu(n).');
71end
72N = round(N(:)');
73if any(~isfinite(N)) || any(N < 0)
74 line_error(mfilename, 'pfqn_oi_insvc requires finite nonnegative populations.');
75end
76R = numel(N);
77shp = N + 1;
78stride = ones(1, R);
79for d = 2:R
80 stride(d) = stride(d-1) * shp(d-1);
81end
82total = prod(shp);
83
84% Decode the lattice once and tabulate the rank rate mu(n).
85subs = zeros(total, R);
86for i = 1:total
87 li = i - 1;
88 for d = 1:R
89 subs(i, d) = mod(li, shp(d));
90 li = floor(li / shp(d));
91 end
92end
93muv = zeros(total, 1);
94for i = 1:total
95 if sum(subs(i, :)) > 0
96 muv(i) = oirate(subs(i, :));
97 end
98end
99
100Phi = zeros(total, 1);
101Xi = zeros(total, R);
102for i = 1:total
103 n = subs(i, :);
104 if sum(n) == 0
105 Phi(i) = 1;
106 continue
107 end
108 mun = muv(i);
109 if mun <= 0
110 % Unreachable state (no server can serve this composition): zero weight.
111 continue
112 end
113 sPhi = 0;
114 sXi = zeros(1, R);
115 for s = 1:R
116 if n(s) > 0
117 j = i - stride(s);
118 sPhi = sPhi + Phi(j);
119 sXi = sXi + Xi(j, :);
120 end
121 end
122 Phi(i) = sPhi / mun;
123 for r = 1:R
124 acc = sXi(r);
125 if n(r) > 0
126 j = i - stride(r);
127 if mun > muv(j)
128 acc = acc + Phi(j); % the tail class-r job is in service
129 end
130 end
131 Xi(i, r) = acc / mun;
132 end
133end
134
135g = zeros(total, R);
136for i = 1:total
137 if Phi(i) > 0
138 g(i, :) = Xi(i, :) / Phi(i);
139 end
140end
141end
Definition Station.m:245