4 % @brief Perfect/approximate stationary state sampler
for closed
5 % single-
class multiserver product-form networks.
11 % @brief Draws states exactly distributed according to
the product-form
12 % stationary distribution of a closed single-
class Jackson network
13 % with multiple servers,
using monotone Coupling From The Past
14 % (Propp-Wilson) as proposed by Kijima and Matsui (WSC 2005,
15 %
"Approximate/Perfect Samplers for Closed Jackson Networks").
16 % @fn pfqn_cftp(L, N, S, nsamples, method)
17 % @param L Service demand (visit ratio / service rate) vector, one entry
18 % per station, L(i) = theta_i/mu_i.
19 % @param N Total closed population K (scalar, single
class).
20 % @param S Number of servers per station (
default: 1; use Inf
for a
21 % delay/infinite-server station).
22 % @param nsamples Number of independent samples to draw (
default: 1).
23 % @param method
'cftp' for exact/perfect sampling (default) or
'approx'
24 % for
the rapidly-mixing approximate sampler M_A.
25 % @return Q Empirical mean queue length per station (1 x M).
26 % @return X Sampled states, one per row (nsamples x M), each row sums to N.
27 % @return T Per-sample coalescence horizon (
'cftp') or mixing steps used
28 % (
'approx'), returned as (nsamples x 1) for diagnostics.
31function [Q,X,T] = pfqn_cftp(L,N,S,nsamples,method)
32% [Q,X,T] = PFQN_CFTP(L,N,S,NSAMPLES,METHOD)
34% Exact (perfect) stationary state sampling for closed single-class
35% multiserver product-form networks via monotone Coupling From The Past.
37% Reference: S. Kijima and T. Matsui,
"Approximate/Perfect Samplers for
38% Closed Jackson Networks", Proc. Winter Simulation Conference, 2005.
41% L - demands (stations x 1), L(i) = theta_i/mu_i
42% N - total population K (scalar)
43% S - servers per station (stations x 1), Inf for infinite server
44% NSAMPLES - number of independent samples (default 1)
45% METHOD -
'cftp' (exact, default) or
'approx' (rapidly mixing M_A)
48% Q - empirical mean queue length (1 x stations)
49% X - sampled states (nsamples x stations), each row sums to N
50% T - coalescence horizon / mixing steps per sample (nsamples x 1)
52L = L(:).
'; % row vector of demands
54if nargin < 3 || isempty(S)
58if nargin < 4 || isempty(nsamples)
61if nargin < 5 || isempty(method)
67 error(
'pfqn_cftp: at least two stations are required.');
70 error('pfqn_cftp: all demands L must be strictly positive.');
73% Precompute cumulative log service factors:
74% logfac(i,m+1) = sum_{t=1}^m log(min(t,S(i))), m = 0..K
75% so that log alpha_i(m) = m*log(L(i)) - logfac(i,m+1).
80 acc = acc + log(min(m,S(i)));
91 [X(smp,:),T(smp)] = draw_cftp(logL,logfac,K,M);
93 [X(smp,:),T(smp)] = draw_approx(logL,logfac,K,M);
95 error(
'pfqn_cftp: unknown method ''%s''.',method);
101% ---- one exact draw via monotone CFTP ------------------------------------
102function [x,horizon] = draw_cftp(logL,logfac,K,M)
103% top state x_U = (K,0,...,0), bottom state x_L = (0,...,0,K)
104u = []; % u(t): uniform for step t before present
108 % prepend randomness
for the newly exposed (older) steps T .. 2T-1;
109 % older steps sit at higher indices and are applied first
110 u(old+1:Tback) = rand(1,Tback-old); %#ok<AGROW>
111 xU = [K, zeros(1,M-1)];
112 xL = [zeros(1,M-1), K];
113 for t = Tback:-1:1 % oldest step first
114 xU = monotone_update(xU,u(t),logL,logfac,M);
115 xL = monotone_update(xL,u(t),logL,logfac,M);
126% ---- monotone update on a consecutive pair -------------------------------
127function x = monotone_update(x,u,logL,logfac,M)
128% single uniform u in [0,1) encodes: pair index (integer part) and
the
129% split Lambda (fractional part), per Kijima-Matsui section 4.1
130lam = 1 + u*(M-1); % in [1,M)
131j = floor(lam); % pair (j, j+1), 1 <= j <= M-1
132if j > M-1, j = M-1; end
133Lambda = lam - j; % uniform in [0,1)
135l = split_index(logL,logfac,j,j+1,k,Lambda);
140% ---- inverse-CDF split: smallest l with Lambda <= g^k_{ij}(l) ------------
141function l = split_index(logL,logfac,i,j,k,Lambda)
142% w(s) propto alpha_i(s) * alpha_j(k-s), s = 0..k
144lw = (s.*logL(i) - logfac(i,s+1)) + ((k-s).*logL(j) - logfac(j,k-s+1));
149l = find(Lambda <= cdf,1,
'first') - 1; % s index (0-based) -> l
150if isempty(l), l = k; end
153% ---- approximate rapidly-mixing sampler M_A ------------------------------
154function [x,steps] = draw_approx(logL,logfac,K,M,eps)
155if nargin < 5, eps = 1e-2; end
156steps = ceil(M*(M-1)/2 * log(K/eps)); % mixing-time bound (Theorem 1)
159 p = randperm(M,2); % distinct pair, not necessarily adjacent
162 l = split_index(logL,logfac,i,j,k,rand);
168function x = mnrnd_start(K,M)
169% arbitrary feasible start: all customers at station 1
170x = [K, zeros(1,M-1)];