LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
solver_nc_pas_is_analyzer.m
1function [Q,U,R,T,C,X,lG,runtime,iter,method] = solver_nc_pas_is_analyzer(sn, options)
2% [Q,U,R,T,C,X,LG,RUNTIME,ITER,METHOD] = SOLVER_NC_PAS_IS_ANALYZER(SN, OPTIONS)
3%
4% Importance-sampling (IS) normalizing-constant analysis of a closed two-station
5% pass-and-swap (P&S) tandem with a non-empty swap graph (Casale, Comte &
6% Dorsman, 2026). With a genuine swap graph the ordered-state chain is reducible
7% (Comte & Dorsman, 2021, arXiv:2009.12299); the recurrent communicating class
8% carries a per-class product form pi(c) = Phi_1(c_1) Phi_2(c_2)/G_C, and its
9% constant G_C is estimated by the auto-normalized IS routine PFQN_PAS_IS. This
10% is the Monte-Carlo counterpart of SOLVER_NC_OI_ANALYZER for the case that the
11% exact OI convolution does not apply (non-empty swap graph).
12%
13% Station 1 is the upstream P&S queue (prefix of the ordering), station 2 the
14% downstream queue (reversed suffix); the swap graph is a class-level property
15% read from the upstream station. Both stations must be OI/P&S with unit per-
16% class visits (the balanced-fairness framework). Mean per-class queue lengths
17% come directly from PFQN_PAS_IS (auto-normalized IS, same samples for numerator
18% and denominator). Per-class throughput uses the balanced-fairness ratio
19% X_r = G(N - e_r)/G(N) with common random numbers across the N and N-e_r runs
20% for variance reduction; utilization, response time, and system time follow the
21% same conventions as SOLVER_NC_OI_ANALYZER.
22%
23% Copyright (c) 2012-2026, Imperial College London
24% All rights reserved.
25Tstart = tic;
26iter = 1;
27method = 'is'; % importance sampling, specialized to OI/P&S stations
28
29M = sn.nstations;
30K = sn.nclasses;
31
32% ---- reject class switching (P&S rank rates are per raw class) -------------
33for c = 1:sn.nchains
34 if numel(sn.inchain{c}) > 1
35 line_error(mfilename, 'solver_nc_pas_is requires one class per chain (no class switching).');
36 end
37end
38if any(isinf(sn.njobs))
39 line_error(mfilename, 'solver_nc_pas_is requires a closed queueing network.');
40end
41if M ~= 2
42 line_error(mfilename, 'solver_nc_pas_is models a two-station pass-and-swap tandem (got %d stations).', M);
43end
44N = round(sn.njobs(:)');
45
46% ---- classify stations and read swap graph ---------------------------------
47svc = cell(M, 1);
48swapG = cell(M, 1);
49for ist = 1:M
50 if sn.sched(ist) ~= SchedStrategy.PAS && sn.sched(ist) ~= SchedStrategy.OI
51 line_error(mfilename, 'solver_nc_pas_is requires both stations to be OI/PAS (station %d is not).', ist);
52 end
53 ind = sn.stationToNode(ist);
54 if ind < 1 || ind > numel(sn.nodeparam) || ~isstruct(sn.nodeparam{ind})
55 line_error(mfilename, 'station %d has no OI/PAS node parameters.', ist);
56 end
57 svc{ist} = sn.nodeparam{ind}.svcRateFun;
58 if isempty(svc{ist})
59 line_error(mfilename, 'OI/PAS station %d has no service rate function; set it via setService(@(c) ...).', ist);
60 end
61 if isfield(sn.nodeparam{ind}, 'swapGraph')
62 swapG{ist} = sn.nodeparam{ind}.swapGraph;
63 else
64 swapG{ist} = [];
65 end
66end
67
68% The stored swap graph is the raw (undirected) class-compatibility graph. The
69% IS estimator needs the GLOBAL placement-order DAG H that defines the recurrent
70% communicating class D; derive it from the P&S dynamics on the single-job-per-
71% class instance (PAS_SWAP2ORDER). Station 2 is the reversed suffix inside
72% PFQN_PAS_IS, so a single global order suffices.
73G1 = swapG{1}; if isempty(G1), G1 = zeros(K, K); end
74G2 = swapG{2}; if isempty(G2), G2 = zeros(K, K); end
75H = pas_swap2order({G1, G2}, {svc{1}, svc{2}}, ones(1, K));
76
77% ---- per-class visits (chain == class); require unit visits ----------------
78V = zeros(M, K);
79for r = 1:K
80 c = find(sn.chains(:, r));
81 vis = sn.visits{c};
82 for ist = 1:M
83 isf = sn.stationToStateful(ist);
84 V(ist, r) = vis(isf, r);
85 end
86 vref = V(sn.refstat(r), r);
87 if vref > 0
88 V(:, r) = V(:, r) / vref;
89 end
90end
91for ist = 1:M
92 for r = 1:K
93 if N(r) > 0 && abs(V(ist, r) - 1) > 1e-9
94 line_error(mfilename, 'solver_nc_pas_is requires unit per-class visits (station %d, class %d, V=%g).', ist, r, V(ist, r));
95 end
96 end
97end
98
99% ---- OI rank-rate handles on a per-class count vector ----------------------
100% svcRateFun(c) takes an ordered microstate list; for an OI station it is
101% permutation-invariant, so evaluate it on a canonical microstate for count n.
102rate1 = @(n) svc{1}(pas_is_microstate(n));
103rate2 = @(n) svc{2}(pas_is_microstate(n));
104mu = {rate1, rate2};
105
106% ---- IS options; fix a base seed so the N and N-e_r runs share randoms -----
107isopt = options;
108if ~isfield(isopt, 'samples') || isempty(isopt.samples)
109 if isfield(options, 'iter_max') && ~isempty(options.iter_max) && options.iter_max > 1
110 isopt.samples = options.iter_max;
111 else
112 isopt.samples = 1e4;
113 end
114end
115if ~isfield(isopt, 'seed') || isempty(isopt.seed)
116 if isfield(options, 'seed') && ~isempty(options.seed)
117 isopt.seed = options.seed;
118 else
119 isopt.seed = 23456;
120 end
121end
122
123% ---- normalizing constant and mean queue lengths at population N -----------
124[G, lG, Qpas] = pfqn_pas_is(N, mu, H, isopt);
125
126Q = zeros(M, K);
127Q(1, :) = Qpas(1, :);
128Q(2, :) = Qpas(2, :);
129
130% ---- per-class throughput X_r = G(N - e_r)/G(N) (common random numbers) ----
131X = zeros(1, K);
132for r = 1:K
133 if N(r) > 0
134 er = zeros(1, K); er(r) = 1;
135 Gr = pfqn_pas_is(N - er, mu, H, isopt);
136 if G > 0
137 X(r) = Gr / G;
138 end
139 end
140end
141
142% ---- throughput, utilization, response time --------------------------------
143T = zeros(M, K);
144U = zeros(M, K);
145R = zeros(M, K);
146for ist = 1:M
147 for r = 1:K
148 T(ist, r) = X(r) * V(ist, r);
149 end
150end
151for ist = 1:M
152 S = sn.nservers(ist);
153 if ~isfinite(S) || S <= 0, S = 1; end
154 for r = 1:K
155 if N(r) > 0
156 er = zeros(1, K); er(r) = 1;
157 muR = mu{ist}(er); % rank rate with only class r present
158 if muR > 0
159 U(ist, r) = T(ist, r) / muR / S;
160 end
161 end
162 end
163end
164for ist = 1:M
165 for r = 1:K
166 if T(ist, r) > 0
167 R(ist, r) = Q(ist, r) / T(ist, r);
168 end
169 end
170end
171
172C = zeros(1, K);
173for r = 1:K
174 if X(r) > 0
175 C(r) = N(r) / X(r);
176 end
177end
178
179runtime = toc(Tstart);
180end
181
182% ==========================================================================
183function c = pas_is_microstate(n)
184% Canonical ordered microstate holding n_r copies of class r (n a count
185% vector). For an order-independent station the rank rate is invariant to the
186% ordering, so this representative suffices to evaluate svcRateFun(c).
187c = repelem(1:numel(n), round(n));
188end
Definition Station.m:245