LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
pas_saturation.m
1% Pass-and-swap (PAS) saturation handling for large / unbounded buffers.
2%
3% An order-independent service rate mu(c) usually SATURATES: beyond some
4% per-class count threshold tau_r, adding more class-r jobs no longer changes
5% mu (e.g. a compatibility / rank function saturates once each present class
6% has one job; an M/M/K rate saturates at K jobs). A large finite buffer can
7% therefore approximate an unbounded queue cheaply.
8%
9% This uses the Comte (thesis Sect. 4.1) compatibility queue: I=2 classes,
10% S=3 servers, S_1={1,2}, S_2={2,3}, rank function
11% mu(A) = sum_{ s : exists present class in A compatible with s } cap_s,
12% so mu({1})=mu({2})=cap_1+cap_2 = 3, mu({1,2})=cap_1+cap_2+cap_3 = 5, and mu
13% is constant once both classes are present (cutoffs tau=[1,1]).
14%
15% It checks that (i) SolverLDES matches SolverCTMC on a finite buffer and
16% (ii) an unset/infinite buffer raises a clean, actionable error.
17clear node jobclass
18
19% compatibility comp(s,i) = 1 iff server s serves class i; capacities cap_s
20comp = [1 0; % server 1 -> class 1
21 1 1; % server 2 -> classes 1, 2 (shared)
22 0 1]; % server 3 -> class 2
23cap_s = [2; 1; 2];
24lambda = [1.5 1.0];
25I = size(comp, 2);
26S = size(comp, 1);
27
28% Rank function: total capacity of servers compatible with a present class.
29muRank = @(c) sum(cap_s(any(comp(:, unique(c(c>0))), 2)));
30
31% (1) a large buffer approximates the unbounded queue; LDES matches CTMC ------
32CAP = 8;
33Qc = qlen(CTMC(build(muRank, CAP, I, S, lambda), 'cutoff', CAP), I);
34Ql = qlen(LDES(build(muRank, CAP, I, S, lambda), 'samples', 3e5, 'seed', 23000), I);
35fprintf('open compatibility PAS, buffer=%d:\n', CAP);
36fprintf(' CTMC QLen = %s\n', mat2str(round(Qc, 5)));
37fprintf(' LDES QLen = %s\n', mat2str(round(Ql, 5)));
38rel = max(abs(Qc - Ql) ./ max(Qc, 1e-12));
39fprintf(' LDES vs CTMC max rel|dQ| = %.2f%%\n', 100 * rel);
40assert(rel <= 0.03, 'LDES does not match CTMC within simulation noise');
41
42% (2) clean error for an unset / infinite buffer -----------------------------
43fprintf('\ninfinite-buffer PAS raises a clean, actionable error:\n');
44try
45 LDES(build(muRank, Inf, I, S, lambda), 'samples', 1000, 'seed', 1).getAvgTable;
46 error('expected a finite-buffer error');
47catch e
48 fprintf(' %s\n', e.message);
49end
50
51fprintf(['\nPASS: PAS saturation handling matches CTMC on a large buffer and ' ...
52 'errors cleanly on infinite buffers.\n']);
53
54function model = build(mu, cap, I, S, lambda)
55model = Network('PASsaturation');
56source = Source(model, 'Source');
57queue = Queue(model, 'PASQueue', SchedStrategy.PAS);
58sink = Sink(model, 'Sink');
59jobclass = cell(1, I);
60for r = 1:I
61 jobclass{r} = OpenClass(model, sprintf('Class%d', r));
62 source.setArrival(jobclass{r}, Exp(lambda(r)));
63end
64queue.setService(mu);
65queue.setSwapGraph(zeros(I)); % empty graph: plain OI queue
66queue.setNumberOfServers(S);
67if ~isinf(cap)
68 queue.setCap(cap);
69end
70P = model.initRoutingMatrix;
71for r = 1:I
72 P{jobclass{r}} = Network.serialRouting(source, queue, sink);
73end
74model.link(P);
75end
76
77function q = qlen(solver, I)
78QN = solver.getAvgQLen; % nstations x nclasses mean queue length
79sn = solver.model.getStruct;
80sidx = sn.nodeToStation(find(strcmp(sn.nodenames, 'PASQueue'), 1));
81q = QN(sidx, 1:I);
82end