1% Test ETAQA departure process
for FCFS queues in SolverMAM dec.mmap
3% Creates a 2-queue tandem with non-Poisson (Erlang) arrivals and Erlang
4% service, solved with dec.mmap (ETAQA-based departures). Validates that:
5% 1. dec.mmap returns finite results and converges
6% 2. Results are consistent with dec.source and SolverMVA baselines
7% 3. The ETAQA truncation level can be configured via options
9%% Model: Erl(5)/Erl(2)/1 -> Erl(3)/1 tandem
10model = Network(
'ETAQA-FCFS-Tandem');
12source = Source(model,
'Source');
13queue1 = Queue(model,
'Queue1', SchedStrategy.FCFS);
14queue2 = Queue(model,
'Queue2', SchedStrategy.FCFS);
15sink = Sink(model,
'Sink');
17oclass = OpenClass(model,
'Class1');
18source.setArrival(oclass, Erlang.fitMeanAndOrder(2, 5)); % mean=2, SCV=0.2
19queue1.setService(oclass, Erlang.fitMeanAndOrder(0.8, 2)); % rho1=0.4
20queue2.setService(oclass, Erlang.fitMeanAndOrder(1.2, 3)); % rho2=0.6
22model.link(Network.serialRouting(source, queue1, queue2, sink));
24%% Solve with dec.mmap (uses ETAQA departure process)
25solverMMAP = SolverMAM(model,
'method',
'dec.mmap');
26T_mmap = solverMMAP.getAvgTable();
28%% Solve with dec.source (baseline)
29solverSRC = SolverMAM(model,
'method',
'dec.source');
30T_src = solverSRC.getAvgTable();
32%% Solve with MVA (exact
for mean values)
33solverMVA = SolverMVA(model);
34T_mva = solverMVA.getAvgTable();
37fprintf(
'\n=== ETAQA FCFS Departure Process Test ===\n');
38fprintf(
'%-10s %10s %10s %10s\n',
'Station',
'MVA',
'dec.source',
'dec.mmap');
39fprintf(
'--- Queue Lengths ---\n');
40for i = 1:height(T_mva)
41 stn = string(T_mva.Station(i));
42 if contains(stn,
'Queue')
43 fprintf('%-10s %10.4f %10.4f %10.4f\n', stn, ...
44 T_mva.QLen(i), T_src.QLen(i), T_mmap.QLen(i));
47fprintf('--- Response Times ---\n');
48for i = 1:height(T_mva)
49 stn =
string(T_mva.Station(i));
50 if contains(stn, 'Queue')
51 fprintf('%-10s %10.4f %10.4f %10.4f\n', stn, ...
52 T_mva.RespT(i), T_src.RespT(i), T_mmap.RespT(i));
56%% Validate: dec.mmap results should be finite and within 20% of MVA
57Q_mmap = T_mmap.QLen(contains(
string(T_mmap.Station), 'Queue'));
58Q_mva = T_mva.QLen(contains(
string(T_mva.Station), 'Queue'));
60assert(all(isfinite(Q_mmap)), 'ETAQA FCFS: dec.mmap returned non-finite queue lengths');
61assert(all(Q_mmap >= 0), 'ETAQA FCFS: dec.mmap returned negative queue lengths');
63relErr = abs(Q_mmap - Q_mva) ./ max(Q_mva, 1e-6);
64fprintf('\nRelative error vs MVA: %.2f%%, %.2f%%\n', relErr*100);
65assert(all(relErr < 0.20), ...
66 sprintf('ETAQA FCFS: dec.mmap queue lengths deviate >20%% from MVA (err=%.2f%%)', max(relErr)*100));
68%% Test configurable truncation level
69solverMMAP4 = SolverMAM(model, 'method', 'dec.mmap');
70solverMMAP4.options.config.etaqa_trunc = 4;
71T_mmap4 = solverMMAP4.getAvgTable();
72Q_mmap4 = T_mmap4.QLen(contains(
string(T_mmap4.Station), 'Queue'));
73assert(all(isfinite(Q_mmap4)), 'ETAQA FCFS: truncation level 4 returned non-finite results');
75fprintf('\nETAQA FCFS departure process test PASSED.\n');