LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
infer_mlps.m
1function demandEst = infer_mlps(model, node, rt, class, ql)
2% INFER_MLPS MLPS demand estimation using sn struct-level operations.
3%
4% Estimates service demands at a PS queue using the Maximum Likelihood
5% for Processor Sharing method. Pre-builds augmented CTMC models for
6% each unique (tagClass, aQueue) combination once, then uses
7% sn_set_service + solver_ctmc directly inside the optimization loop.
8%
9% Inputs:
10% model - LINE Network model with delay rates set and queue rates to estimate
11% node - PS queue node (Station object)
12% rt - response time samples (column vector, n x 1)
13% class - class of each sample (column vector, n x 1)
14% ql - queue lengths at arrival (n x R matrix, per-class)
15%
16% Returns:
17% demandEst - 1 x R vector of estimated mean service demands
18%
19% Copyright (c) 2012-2026, Imperial College London
20% All rights reserved.
21% This code is released under the 3-Clause BSD License.
22
23sn = model.getStruct();
24R = sn.nclasses;
25nCores = node.getNumberOfServers();
26
27% Get delay rates from model
28delayIdx = 0;
29for i = 1:sn.nstations
30 if sn.sched(i) == SchedStrategy.INF
31 delayIdx = i;
32 break;
33 end
34end
35muZ = zeros(1, R);
36for k = 1:R
37 muZ(k) = sn.mu{delayIdx}{k}(1);
38end
39
40% Initial point estimate
41meanQL = mean(sum(ql, 2));
42Vtilde = min(meanQL, nCores);
43x0 = zeros(1, R);
44for j = 1:R
45 if ~isempty(rt(class == j))
46 x0(j) = Vtilde * mean(rt(class == j)) / meanQL;
47 else
48 x0(j) = 1e-3;
49 end
50end
51
52xLB = zeros(1, R);
53xUB = max(rt) * ones(1, R);
54
55newR = R + 1;
56augMuZ = [muZ, 0]; % placeholder for tagged class rate
57
58%% Pre-build augmented CTMC models for all unique (tagClass, aQueue)
59% This eliminates Network/ClosedClass/SolverCTMC construction from the
60% optimization loop. Only rates change between iterations.
61uniqueTC = unique(class);
62uniqueQL = unique(ql, 'rows');
63ctmcOpts = []; % will be set from first solver instance
64
65% Cached augmented models stored as parallel arrays keyed by state string:
66% prebuiltKeys{i} is the key, prebuiltVals{i} the corresponding struct.
67prebuiltKeys = {};
68prebuiltVals = {};
69for u = 1:length(uniqueTC)
70 tc = uniqueTC(u);
71 augMuZ(newR) = muZ(tc);
72 for v = 1:size(uniqueQL, 1)
73 aq = uniqueQL(v, :);
74
75 % Population: move 1 job from tagClass to tagged class
76 N = aq; N(tc) = N(tc) - 1; N(newR) = 1;
77
78 % Build augmented Network model (once per combo)
79 augModel = Network('mlps_aug');
80 augNode = cell(1, 2);
81 augNode{1} = Delay(augModel, 'Think');
82 augNode{2} = Queue(augModel, 'Queue1', SchedStrategy.PS);
83 augNode{2}.setNumberOfServers(nCores);
84 for r = 1:newR
85 augClass = ClosedClass(augModel, sprintf('Class%d', r), N(r), augNode{1}, 0);
86 augNode{1}.setService(augClass, Exp(augMuZ(r)));
87 augNode{2}.setService(augClass, Exp(1)); % placeholder
88 end
89 P = augModel.initRoutingMatrix;
90 for r = 1:newR
91 P{r} = Network.serialRouting(augNode);
92 end
93 augModel.link(P);
94
95 % Use SolverCTMC to get proper options and run initial solve
96 solver = SolverCTMC(augModel);
97 if isempty(ctmcOpts)
98 ctmcOpts = solver.getOptions();
99 end
100 [infGen, eventFilt, ev] = solver.getGenerator();
101 stateSpaceAggr = solver.getStateSpaceAggr();
102 augSn = augModel.getStruct();
103
104 % Find tagged departure event indices (invariant across rate changes)
105 queueNodeIdx = augNode{2}.index;
106 taggedDepIdx = [];
107 for e = 1:length(ev)
108 if ev{e}.active{1}.node == queueNodeIdx && ...
109 ev{e}.active{1}.class == newR && ...
110 ev{e}.active{1}.event == EventType.DEP
111 taggedDepIdx(end+1) = e;
112 end
113 end
114
115 % Find subset where tagged job is at Queue (invariant)
116 queueStIdx = augNode{2}.stationIndex;
117 taggedColAtQueue = (queueStIdx - 1) * newR + newR;
118 subset = find(stateSpaceAggr(:, taggedColAtQueue) == 1);
119
120 % Extract queue state space for subset (invariant)
121 queueCols = (queueStIdx - 1) * newR + (1:newR);
122 SSqueue = stateSpaceAggr(subset, queueCols);
123
124 cacheKey = mat2str([tc, aq]);
125 prebuiltKeys{end+1} = cacheKey; %#ok<AGROW>
126 prebuiltVals{end+1} = struct(...
127 'sn', augSn, ...
128 'queueStIdx', queueStIdx, ...
129 'taggedDepIdx', taggedDepIdx, ...
130 'subset', subset, ...
131 'SSqueue', SSqueue, ...
132 'N', N, ...
133 'tagClass', tc); %#ok<AGROW>
134 end
135end
136
137%% Optimization options
138options = optimset();
139options.Display = 'iter';
140options.LargeScale = 'on';
141options.MaxIter = 1e10;
142options.MaxFunEvals = 1e10;
143options.MaxSQPIter = 5000;
144options.TolCon = 1e-6;
145options.Algorithm = 'interior-point';
146
147[demandEst, ~] = fmincon(@objfun, x0, [], [], [], [], xLB, xUB, [], options);
148
149 function f = objfun(x)
150 TOL = 1e-6;
151 rates = 1 ./ x; % x = mean demands
152
153 % Build cache: update rates via sn_set_service, call solver_ctmc.
154 % Stored as parallel arrays keyed by state string.
155 cacheKeys = {};
156 cacheVals = {};
157
158 for kk = 1:length(prebuiltKeys)
159 key = prebuiltKeys{kk};
160 pb = prebuiltVals{kk};
161
162 % Set augmented rates: base classes + tagged class
163 augRates = [rates, rates(pb.tagClass)];
164
165 % Update service rates in cached sn struct (cell-of-cells format)
166 sn_upd = pb.sn;
167 for cc = 1:newR
168 sn_upd = sn_set_service_coc(sn_upd, pb.queueStIdx, cc, augRates(cc));
169 end
170
171 % Run solver_ctmc with updated rates (reuses sn topology/state structure)
172 [infGen, ~, ~, Dfilt] = solver_ctmc(sn_upd, ctmcOpts);
173
174 % Build D1 from cached departure event indices
175 D1 = sparse(size(infGen, 1), size(infGen, 2));
176 for di = 1:length(pb.taggedDepIdx)
177 D1 = D1 + Dfilt{pb.taggedDepIdx(di)};
178 end
179
180 % Extract sub-generator using cached subset
181 MAPQ1 = infGen - D1;
182 A = full(MAPQ1(pb.subset, pb.subset));
183
184 cacheKeys{end+1} = key; %#ok<AGROW>
185 cacheVals{end+1} = struct('A', A, 'SSqueue', pb.SSqueue, 'N', pb.N); %#ok<AGROW>
186 end
187
188 % Compute likelihoods using cached CTMCs
189 ftemp = zeros(size(rt));
190 for r = 1:length(rt)
191 cacheKey = mat2str([class(r), ql(r, :)]);
192 cached = cacheVals{find(strcmp(cacheKeys, cacheKey), 1)};
193 ftemp(r) = log(TOL + eval_mlps_likelihood(cached.A, cached.SSqueue, cached.N, rt(r)));
194 end
195 f = -sum(ftemp);
196 end
197
198end
199
200
201function LIKE = eval_mlps_likelihood(A, SSqueue, N, Rsam)
202% Compute MLPS likelihood from pre-built CTMC components.
203
204pie = zeros(1, length(A));
205idx = matchrow(SSqueue, N);
206if ~isempty(idx) && idx > 0
207 pie(idx) = 1;
208end
209
210MAP = {A, -A * ones(size(pie))' * pie};
211LIKE = map_pdf(MAP, Rsam);
212
213end