LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
pas_swap2order.m
1function H = pas_swap2order(swap, listRate, N0)
2% H = PAS_SWAP2ORDER(SWAP, LISTRATE, N0)
3%
4% Derive the GLOBAL placement-order DAG H of a closed two-station pass-and-swap
5% (P&S) tandem 1->2->1 directly from its swap graph, for use with PFQN_PAS_IS.
6%
7% With a non-empty swap graph the ordered-state chain is reducible (Comte &
8% Dorsman, 2021, arXiv:2009.12299): the recurrent communicating class is the set
9% of splits (c_{1..k}; c_{ell..k+1}) of the orderings c that are the linear
10% extensions of a single placement partial order on the classes (their Prop.).
11% PFQN_PAS_IS samples those orderings from H, so it needs exactly this global
12% order -- NOT the per-station placement orders consumed by the exact
13% convolution PFQN_PAS_NC (which carry the same information one station at a
14% time, transposed around the cycle, and are individually over-constrained for
15% the single-order IS formulation). Feed H to PAS_PLACEMENT to obtain the
16% precedence closure that PFQN_PAS_NC expects.
17%
18% The placement order is a class-level property, independent of the per-class
19% multiplicity, so it is extracted from the minimal single-job-per-class
20% instance (N0 = ones(1,R)): enumerate the reachable communicating class from
21% the all-in-queue-1 initial state; each reachable state (l1;l2) exposes the
22% full ordering c = [l1, reverse(l2)] in D; then set H(i,j)=1 iff class i
23% precedes class j in EVERY c in D (forced precedence). Incomparable pairs are
24% left 0 (antichain). The result is valid for any population N.
25%
26% Parameters:
27% swap - (R x R) swap graph, or cell {G1,G2} of the two per-queue graphs;
28% G(a,b)~=0 means class a chases class b. Empty/all-zero => plain
29% OI: H = 0 (every ordering feasible).
30% listRate - cell {1 x 2} of OI microstate rate functions; listRate{m}(c) is
31% the total service rate of queue m on the ordered prefix c. Used
32% to prune zero-rate (non-head) completions.
33% N0 - (1 x R) minimal probing population; defaults to ones(1,R).
34%
35% Returns:
36% H - (R x R) global placement-order DAG; H(i,j)=1 iff i must precede j.
37%
38% See also PFQN_PAS_IS, PAS_PLACEMENT, PFQN_PAS_NC.
39%
40% Copyright (c) 2012-2026, Imperial College London
41% All rights reserved.
42
43M = 2;
44if ~iscell(swap), swap = repmat({swap}, 1, M); end
45if nargin < 3 || isempty(N0)
46 R = size(swap{1}, 1);
47 N0 = ones(1, R);
48end
49R = numel(N0);
50
51if (isempty(swap{1}) || ~any(swap{1}(:) ~= 0)) && ...
52 (isempty(swap{2}) || ~any(swap{2}(:) ~= 0))
53 H = zeros(R, R); % pure OI: no placement constraint
54 return
55end
56
57% minimal single-job-per-class initial placement, all jobs at queue 1
58initState = [];
59for r = 1:R, initState = [initState, repmat(r, 1, N0(r))]; end %#ok<AGROW>
60init = {initState, []};
61
62% breadth-first enumeration of the reachable (communicating) class
63key = containers.Map('KeyType', 'char', 'ValueType', 'logical');
64classStates = {}; frontier = {init}; key(pas_enc(init)) = true;
65while ~isempty(frontier)
66 st = frontier{end}; frontier(end) = [];
67 classStates{end+1} = st; %#ok<AGROW>
68 for m = 1:M
69 c = st{m}; next = mod(m, M) + 1;
70 for p = 1:numel(c)
71 % Marginal OI completion rate of the p-th customer. The empty-prefix
72 % rate is 0 by definition (no jobs -> no service); some svcRateFun
73 % handles return a nonzero constant on [], so guard p==1 explicitly.
74 if p == 1
75 prevRate = 0;
76 else
77 prevRate = listRate{m}(c(1:p-1));
78 end
79 rate = listRate{m}(c(1:p)) - prevRate;
80 if rate <= 1e-12, continue; end
81 [cnew, dep] = pas_swap_local(c, p, swap{m});
82 stn = st; stn{m} = cnew; stn{next} = [st{next}, dep];
83 k = pas_enc(stn);
84 if ~isKey(key, k), key(k) = true; frontier{end+1} = stn; end %#ok<AGROW>
85 end
86 end
87end
88
89% full orderings D: every reachable state (l1;l2) exposes c = [l1, reverse(l2)]
90Dmap = containers.Map('KeyType', 'char', 'ValueType', 'logical');
91D = {};
92for s = 1:numel(classStates)
93 c = [classStates{s}{1}, fliplr(classStates{s}{2})];
94 kc = sprintf('%d,', c);
95 if ~isKey(Dmap, kc), Dmap(kc) = true; D{end+1} = c; end %#ok<AGROW>
96end
97
98% forced precedence: i precedes j iff in every c in D, every copy of i is older
99% than every copy of j (position-wise), whenever both are present.
100H = false(R);
101for a = 1:R
102 for b = 1:R
103 if a == b, continue; end
104 both = false; forced = true;
105 for s = 1:numel(D)
106 c = D{s};
107 pa = find(c == a); pb = find(c == b);
108 if isempty(pa) || isempty(pb), continue; end
109 both = true;
110 if ~(max(pa) < min(pb)), forced = false; break; end
111 end
112 H(a, b) = both && forced;
113 end
114end
115H = double(H);
116end
117
118% ------------------------------------------------------------------------
119function s = pas_enc(st)
120parts = cell(1, numel(st));
121for m = 1:numel(st), parts{m} = sprintf('%d,', st{m}); end
122s = strjoin(parts, '|');
123end
124
125function [cnew, dep] = pas_swap_local(c, p, G)
126n = numel(c); chain = p; moving = c(p); cur = p;
127while true
128 q = -1;
129 for j = cur+1:n, if ~isempty(G) && G(moving, c(j)) ~= 0, q = j; break; end; end
130 if q == -1, break; end
131 chain(end+1) = q; moving = c(q); cur = q; %#ok<AGROW>
132end
133dep = c(chain(end)); tmp = c;
134for i = 1:numel(chain)-1, tmp(chain(i+1)) = c(chain(i)); end
135tmp(chain(1)) = []; cnew = tmp;
136end