1%% LDES warm start from an auxiliary solver
2% An auxiliary solver
is passed to SolverLDES as an argument, its steady-state
3% distribution
is computed, and that distribution decides
the initial state of
4%
the simulation. Since
the simulation then starts (approximately) in steady
5% state,
the initialization bias vanishes and no warmup samples are discarded,
6% so a target accuracy
is reached with fewer simulated events (and less time)
7% than a cold-started run.
9% Auxiliary-solver dispatch inside SolverLDES.initFromSolver:
10% - SolverCTMC:
the exact stationary distribution over
the aggregate state
11% space
is computed and
the initial state
is its mode;
12% - any other solver (e.g. SolverMVA):
the steady-state mean queue lengths
13% are rounded to a placement conserving
the closed populations.
15% The benchmark model
is a closed NEAR-BALANCED tandem, whose job split mixes
16% slowly:
the bias of
the default cold start (all jobs at
the reference
17% station) persists beyond what
the MSER-5 transient filter can remove.
19% Copyright (c) 2012-2026, Imperial College London
23seeds = [23000 23001 23002];
24budgets = [10000 50000 200000];
26% Exact reference solution (exact MVA;
the model
is product-form)
27exact = SolverMVA(buildModel(N),
'method',
'exact').getAvgQLen();
28fprintf(
'Exact mean queue lengths: %s\n', mat2str(round(exact
', 2)));
30% Warm-start placement, computed ONCE by passing the auxiliary solver to
31% SolverLDES; the derived options.init_sol is then reused across runs.
34proto = SolverLDES(m, SolverMVA(m, 'method
', 'exact
'));
35initSol = proto.options.init_sol;
37fprintf('Warm placement from SolverMVA (%.3fs): %s\n\n
', initTime, mat2str(initSol));
39% On a small instance, SolverCTMC yields the mode of the exact stationary
40% distribution instead (feasible when the state space is small):
42protoCtmc = SolverLDES(mS, SolverCTMC(mS));
43fprintf('CTMC distribution-mode placement (N=20): %s\n\n
', mat2str(protoCtmc.options.init_sol));
45% Compare accuracy and wall-clock time of cold vs warm starts
46fprintf('%10s %14s %14s\n
', 'samples
', 'COLD err/time
', 'WARM err/time
');
47coldTime = 0; warmTime = initTime;
48coldAt = NaN; warmAt = NaN;
50 [ce, ct] = runSet(@() buildModel(N), exact, b, seeds, []);
51 [we, wt] = runSet(@() buildModel(N), exact, b, seeds, initSol);
52 coldTime = coldTime + ct; warmTime = warmTime + wt;
53 if isnan(coldAt) && ce < 0.10, coldAt = b; end
54 if isnan(warmAt) && we < 0.10, warmAt = b; end
55 fprintf('%10d %6.2f%% %5.2fs %6.2f%% %5.2fs\n
', b, 100*ce, ct, 100*we, wt);
56 if ~isnan(coldAt) && ~isnan(warmAt)
60fprintf('\nSamples to reach 10%% error: COLD=%d, WARM=%d\n
', coldAt, warmAt);
62function [err, rtime] = runSet(factory, exact, samples, seeds, initSol)
63% Average L1 relative error and total wall-clock over the seed set.
69 solver = SolverLDES(model, 'samples
', samples, 'seed
', s, 'verbose
', false);
71 solver = SolverLDES(model, 'samples
', samples, 'seed
', s, 'verbose
', false);
72 solver.options.init_sol = initSol;
73 solver.options.config.tranfilter = 'fixed
';
74 solver.options.config.warmupfrac = 0;
76 QN = solver.getAvgQLen();
77 rtime = rtime + toc(T0);
78 err = err + sum(abs(QN - exact)) / sum(exact);
80err = err / length(seeds);
83function model = buildModel(n)
84% Closed near-balanced tandem: Think(Exp,1) -> Queue1(Exp,1.0) -> Queue2(Exp,0.98)
85model = Network('ldesWarmStart
');
86think = Delay(model, 'Think
');
87queue1 = Queue(model, 'Queue1
', SchedStrategy.FCFS);
88queue2 = Queue(model, 'Queue2
', SchedStrategy.FCFS);
89jobs = ClosedClass(model, 'Jobs
', n, think);
90think.setService(jobs, Exp(1.0));
91queue1.setService(jobs, Exp(1.0));
92queue2.setService(jobs, Exp(0.98)); % near-balanced bottleneck
93P = model.initRoutingMatrix();
94P{1}(think, queue1) = 1.0;
95P{1}(queue1, queue2) = 1.0;
96P{1}(queue2, think) = 1.0;