LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
pas_closed_tandem_fig6.m
1function pas_closed_tandem_fig6()
2% PAS_CLOSED_TANDEM_FIG6 Closed tandem of two pass-and-swap (PAS) queues,
3% reproducing Figures 5 and 6 of Comte & Dorsman, "Pass-and-Swap Queues"
4% (2021, arXiv:2009.12299; Queueing Systems).
5%
6% Topology (Fig. 6): --> PASQueue1 --> PASQueue2 --> (closed tandem)
7%
8% There are I=6 customer classes with a SINGLE customer of each class. Both
9% queues share the swapping graph of Figure 5 (undirected; classes as nodes):
10%
11% 6 edges: 1-3, 1-4, 2-4, 2-5, 3-6, 4-6, 5-6
12% / | \ (class 6 at the top, classes 1,2 at the bottom).
13% 3 4 5
14% \/ \/
15% 1 2
16%
17% As in the paper's illustration, only the customer at the HEAD of each queue
18% receives a positive service rate (single server): mu(c)=mu1 at queue 1 and
19% nu(d)=mu2 at queue 2, independent of class so that the total rate depends
20% only on the customer multiset (order-independence). Upon a service
21% completion the departing customer is selected by the pass-and-swap scan over
22% the swapping graph and is routed to the back of the OTHER queue as a
23% customer of the same class.
24%
25% The model is solved exactly by SolverCTMC and by the SolverLDES simulator,
26% and both are validated against an independent product-form reference that
27% builds the Markov chain directly from the pass-and-swap kernel (Theorem 5).
28%
29% NOTE: a closed PAS network with a non-empty swapping graph has a reducible
30% generator (the pass-and-swap mechanism conserves the placement order, paper
31% Prop. 2/4), so the stationary distribution is that of the recurrent
32% component containing the initial state c=(1,2,3,4,5,6) of Figure 6a.
33
34mu1 = 1.0; mu2 = 1.3; % head-only single-server rates at queue 1, 2
35edges = [1 3; 1 4; 2 4; 2 5; 3 6; 4 6; 5 6];
36G = zeros(6);
37for k = 1:size(edges,1)
38 G(edges(k,1),edges(k,2)) = 1;
39 G(edges(k,2),edges(k,1)) = 1;
40end
41
42% ---- LINE model -------------------------------------------------------
43model = Network('PASclosedTandem');
44q1 = Queue(model, 'PASQueue1', SchedStrategy.PAS);
45q2 = Queue(model, 'PASQueue2', SchedStrategy.PAS);
46jobclass = cell(1,6);
47for r = 1:6
48 jobclass{r} = ClosedClass(model, sprintf('Class%d', r), 1, q1);
49end
50q1.setService(@(c) mu1); % head-only: mu(prefix)=mu1 => Delta mu>0 only at head
51q2.setService(@(c) mu2);
52q1.setSwapGraph(G); q1.setNumberOfServers(1); q1.setCap(6);
53q2.setSwapGraph(G); q2.setNumberOfServers(1); q2.setCap(6);
54P = model.initRoutingMatrix;
55for r = 1:6
56 P{jobclass{r}}(q1, q2) = 1.0;
57 P{jobclass{r}}(q2, q1) = 1.0;
58end
59model.link(P);
60
61% A closed PAS network with a non-empty swapping graph is reducible (one
62% recurrent component per placement order), so the initial job placement is a
63% REQUIRED model input -- there is no valid default. We use the initial state
64% of Figure 6a: queue 1 = (1,2,3,4,5,6) (class 1 oldest, at the head), queue 2
65% empty. Solving with a reversed placement would select the mirror component.
66q1.setState([1 2 3 4 5 6]);
67
68fprintf('=== CTMC (exact) ===\n');
69Tc = CTMC(model, 'cutoff', 6).getAvgTable;
70disp(Tc);
71
72fprintf('=== LDES (simulation, 6e5 samples) ===\n');
73Tl = LDES(model, 'samples', 6e5, 'seed', 23000, 'verbose', false).getAvgTable;
74
75% ---- independent product-form reference -------------------------------
76[Qref1, Qref2] = reference_pas_tandem(G, [mu1 mu2]);
77Qref = [Qref1, Qref2]'; % column order: q1 classes 1..6 then q2 classes 1..6
78
79qc = Tc.QLen; ql = Tl.QLen;
80fprintf('\n station class reference CTMC LDES\n');
81for i = 1:height(Tc)
82 fprintf(' %-9s %-6s %9.5f %9.5f %9.5f\n', string(Tc.Station(i)), ...
83 string(Tc.JobClass(i)), Qref(i), qc(i), ql(i));
84end
85errCTMC = max(abs(qc - Qref));
86errLDES = max(abs(ql - Qref));
87fprintf('\nCTMC vs reference: max|dQ| = %.3e\n', errCTMC);
88fprintf('LDES vs reference: max|dQ| = %.3e (simulation noise)\n', errLDES);
89assert(errCTMC <= 1e-9, 'CTMC does not match the pass-and-swap reference');
90assert(errLDES <= 1e-2, 'LDES does not match the pass-and-swap reference');
91fprintf('PASS: CTMC matches the pass-and-swap reference; LDES agrees within noise.\n');
92end
93
94% =======================================================================
95function [Q1, Q2] = reference_pas_tandem(G, mu)
96% Independent reference: build the CTMC of the closed PAS tandem directly from
97% the pass-and-swap kernel, by reachability from the initial state of Fig. 6a
98% (queue 1 = (1,...,6) ascending, queue 2 empty), head-only service.
99n = size(G,1);
100init1 = 1:n;
101states = {}; idx = containers.Map('KeyType','char','ValueType','double');
102 function id = getid(l1, l2)
103 key = [sprintf('%d,', l1), '|', sprintf('%d,', l2)];
104 if isKey(idx, key)
105 id = idx(key);
106 else
107 id = numel(states) + 1; idx(key) = id; states{id} = {l1, l2};
108 end
109 end
110getid(init1, []);
111fr = 1; E = zeros(0,3);
112while fr <= numel(states)
113 l1 = states{fr}{1}; l2 = states{fr}{2};
114 if ~isempty(l1) % head of queue 1 completes
115 [nl, dep] = pskernel(l1, 1, G);
116 j = getid(nl, [l2, dep]); E(end+1,:) = [fr, j, mu(1)]; %#ok<AGROW>
117 end
118 if ~isempty(l2) % head of queue 2 completes
119 [nl, dep] = pskernel(l2, 1, G);
120 j = getid([l1, dep], nl); E(end+1,:) = [fr, j, mu(2)]; %#ok<AGROW>
121 end
122 fr = fr + 1;
123end
124ns = numel(states); Qm = zeros(ns);
125for e = 1:size(E,1)
126 Qm(E(e,1),E(e,2)) = Qm(E(e,1),E(e,2)) + E(e,3);
127end
128for i = 1:ns, Qm(i,i) = -sum(Qm(i,:)); end
129pivec = ([Qm'; ones(1,ns)] \ [zeros(ns,1); 1])';
130Q1 = zeros(1,n); Q2 = zeros(1,n);
131for i = 1:ns
132 l1 = states{i}{1}; l2 = states{i}{2};
133 for r = 1:n
134 Q1(r) = Q1(r) + pivec(i)*sum(l1==r);
135 Q2(r) = Q2(r) + pivec(i)*sum(l2==r);
136 end
137end
138end
139
140% =======================================================================
141function [newlist, dep] = pskernel(list, p, G)
142% Pass-and-swap kernel: the customer at position p completes; it scans toward
143% the back for the first swappable class, takes its place, the ejected customer
144% repeats, and the last ejected customer departs. Classes shift one step along
145% the chain and the served slot is removed.
146nn = numel(list); chain = p; moving = list(p); cur = p;
147while true
148 q = 0;
149 for j = cur+1:nn
150 if G(moving, list(j)), q = j; break; end
151 end
152 if q == 0, break; end
153 chain(end+1) = q; moving = list(q); cur = q; %#ok<AGROW>
154end
155dep = list(chain(end));
156newlist = list;
157for i = 1:numel(chain)-1
158 newlist(chain(i+1)) = list(chain(i));
159end
160newlist(chain(1)) = [];
161end
Definition mmt.m:124