LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
pfqn_pas_nc.m
1function [G, lG] = pfqn_pas_nc(Z, N, mu, prec, options)
2% [G, LG] = PFQN_PAS_NC(Z, N, MU, PREC, OPTIONS)
3%
4% Normalizing constant of a closed pass-and-swap (P&S) queueing network that
5% comprises a single aggregated infinite-server (delay) node and an arbitrary
6% number of order-independent (OI) / P&S stations, restricted to the recurrent
7% communicating class selected by the placement order PREC.
8%
9% With a non-empty swap graph the ordered-state chain is reducible (Comte &
10% Dorsman, 2021, arXiv:2009.12299): the recurrent communicating classes are the
11% placement-order-adhering sets and the product form pi(c) = prod_m Phi_m(c_m)/G_C
12% holds per class. This routine returns that per-class constant G_C. It is a
13% MICROSTATE routine: it walks the ordered chains position by position, because
14% with a placement order the reachable set is a set of ORDERINGS and does not
15% collapse onto the count lattice. For the plain OI case (empty PREC, every
16% ordering feasible) the count lattice does suffice and PFQN_NCOI computes the
17% same G at far lower cost; use this routine only when a placement order is
18% present, or as a microstate reference.
19%
20% Method. Build station M's chain head-first: appending class r at chain
21% position k = sum(occ)+1 is admissible iff the placement order allows it (no
22% class already placed at that station must come after r), and contributes the
23% reciprocal OI prefix rate 1/mu_M(occ+e_r); the chain may be finalized only
24% when occ is a placement-order ideal at full multiplicity, whereupon the
25% recursion moves to station M-1. When every P&S station has been peeled the
26% residual population sits at the delay node with the multinomial weight
27% prod_r Z_r^{N_r}/N_r!. With PREC empty this is exactly the balanced-fairness
28% convolution of Bonald & Proutiere (2003) evaluated ordering by ordering,
29% Phi(0) = 1, Phi(n) = (1/mu(n)) * sum_{r: n_r>0} Phi(n - e_r).
30%
31% COST. The recursion visits one node per feasible ordered prefix, so with an
32% empty PREC the node count is sum_{b<=N} C(|b|+M-1,M-1) * |b|!/prod_r b_r!,
33% i.e. factorial in the total population sum(N). A placement order prunes the
34% orderings (a total order leaves a single one per count split), which is what
35% makes the microstate walk affordable in the P&S case.
36%
37% Parameters:
38% Z - (1 x R) think-time demand vector of the aggregated delay node.
39% Z(r) = 1/sigma_r for a delay with per-class rate sigma_r.
40% N - (1 x R) closed population vector, finite.
41% mu - cell array {1 x M} of function handles, one per P&S station. Each
42% mu{m}(n) returns the total service rate of station m given the
43% per-class occupancy (count) vector n (1 x R). For an OI station the
44% rate depends only on the support of n (which classes are present),
45% i.e. the sum of the capacities of the compatible servers. May be
46% empty to model a pure delay network.
47% prec - placement order. Either a cell {1 x M} of (R x R) precedence
48% matrices, one per station, or a single (R x R) matrix broadcast to
49% every station, with prec{m}(i,j) ~= 0 iff class i must be placed
50% before class j at station m (the closure returned by PAS_PLACEMENT,
51% fed by the global DAG of PAS_SWAP2ORDER). Empty or all-zero means no
52% order: every ordering is feasible and G is the plain OI constant.
53% NOTE the orientation: around a cycle 1->2->...->M->1 the chain of
54% each downstream station is traversed in the opposite direction, so
55% the downstream stations take the TRANSPOSE of the upstream order
56% (prec = {P, P'} for a two-station cycle). Passing the same P to both
57% stations of a cycle silently returns a smaller, wrong G.
58% options - solver options (optional, currently unused; accepted for
59% signature parity with the other pfqn_* routines).
60%
61% Returns:
62% G - Normalizing constant G_C of the communicating class.
63% lG - log(G_C).
64%
65% Example (two P&S stations in a cycle with swap graph SWAP, no delay):
66% H = pas_swap2order(swap, {@(c) mu1, @(c) mu2});
67% P = pas_placement(H);
68% G = pfqn_pas_nc([], N, {@(n) mu1, @(n) mu2}, {P, P'});
69%
70% See also PFQN_NCOI, PFQN_PAS_IS, PAS_PLACEMENT, PAS_SWAP2ORDER.
71%
72% Copyright (c) 2012-2026, Imperial College London
73% All rights reserved.
74
75if nargin < 5
76 options = struct(); %#ok<NASGU>
77end
78if nargin < 4
79 prec = {};
80end
81if nargin < 3 || isempty(mu)
82 mu = {};
83end
84if ~iscell(mu)
85 mu = {mu};
86end
87
88R = numel(N);
89if isempty(Z)
90 Z = zeros(1, R);
91end
92if numel(Z) ~= R
93 line_error(mfilename, 'Z and N must have the same number of classes.');
94end
95if any(~isfinite(N))
96 line_error(mfilename, 'pfqn_pas_nc requires finite (closed) populations.');
97end
98
99N = round(N(:)');
100Z = Z(:)';
101M = numel(mu);
102
103% Normalize the placement order to one logical (R x R) matrix per station.
104if ~iscell(prec)
105 if isempty(prec)
106 prec = {};
107 else
108 prec = repmat({prec}, 1, M);
109 end
110end
111if isempty(prec)
112 prec = repmat({false(R)}, 1, M);
113elseif numel(prec) == 1 && M > 1
114 prec = repmat(prec, 1, M);
115end
116if numel(prec) ~= M
117 line_error(mfilename, 'prec must supply one precedence matrix per station.');
118end
119for m = 1:M
120 if isempty(prec{m})
121 prec{m} = false(R);
122 else
123 if any(size(prec{m}) ~= [R, R])
124 line_error(mfilename, 'each precedence matrix must be R x R.');
125 end
126 prec{m} = logical(prec{m} ~= 0);
127 end
128end
129
130G = pas_nc_rec(Z, N, N, mu, prec, M, zeros(1, R));
131lG = log(G);
132end
133
134function G = pas_nc_rec(Z, N, Norig, mu, prec, m, occ)
135% Head-peeling recursion. N is the population still available to stations
136% m,m-1,...,1 and to the delay node; Norig is the total population (for the
137% order-ideal test); occ is the per-class chain already placed at station m.
138R = numel(N);
139
140if m == 0
141 % Base case: the residual population N sits at the delay node with
142 % unnormalized weight prod_r Z_r^{N_r} / N_r!.
143 logf = 0;
144 for r = 1:R
145 if N(r) > 0
146 if Z(r) <= 0
147 G = 0; % class r has population but no delay demand: infeasible
148 return
149 end
150 logf = logf + N(r) * log(Z(r)) - gammaln(N(r) + 1);
151 end
152 end
153 G = exp(logf);
154 return
155end
156
157% Step A: finalize station m's chain here and peel to station m-1, but only if
158% the accumulated occupancy is a placement-order ideal of station m (a class
159% present here requires all its order-predecessors present at full multiplicity;
160% otherwise the split is unreachable within the communicating class).
161if pas_nc_isideal(occ, prec{m}, Norig)
162 G = pas_nc_rec(Z, N, Norig, mu, prec, m - 1, zeros(1, R));
163else
164 G = 0;
165end
166
167% Step B: append one more class r at the next chain position of station m,
168% contributing the reciprocal OI prefix rate.
169active = mu{m};
170precm = prec{m};
171placed = occ > 0;
172for r = 1:R
173 if N(r) > 0 && ~any(precm(r, placed))
174 e_r = zeros(1, R);
175 e_r(r) = 1;
176 mu_r = active(occ + e_r);
177 if mu_r <= 0
178 continue
179 end
180 Nm = N;
181 Nm(r) = Nm(r) - 1;
182 G = G + (1 / mu_r) * pas_nc_rec(Z, Nm, Norig, mu, prec, m, occ + e_r);
183 end
184end
185end
186
187function tf = pas_nc_isideal(occ, precm, Norig)
188% True iff occ is a placement-order ideal at full multiplicity: for every
189% i prec j, occ(j) > 0 requires occ(i) = Norig(i). Reduces to support
190% downward-closure when Norig == 1, and to "always true" for an empty order.
191tf = true;
192R = numel(occ);
193for i = 1:R
194 for j = 1:R
195 if precm(i, j) && occ(j) > 0 && occ(i) < Norig(i)
196 tf = false;
197 return
198 end
199 end
200end
201end