LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
ParamEstimator.m
1classdef ParamEstimator < handle
2 % Abstract class for service demand estimators
3 %
4 % Copyright (c) 2012-2026, Imperial College London
5 % All rights reserved.
6
7 properties (Access = public)
8 options; % Data structure with engine options
9 model; % Model to be analyzed
10 samples; % Input dataset
11 samplesAggr; % Input dataset
12 end
13
14 methods (Hidden)
15 %Constructor
16 function self = ParamEstimator(model, options)
17 % SELF = SOLVER(MODEL, NAME, OPTIONS)
18 if ~exist('options','var')
19 options = self.defaultOptions;
20 end
21 self.model = model;
22 self.options = options;
23 self.samples = cell(model.getNumberOfNodes, model.getNumberOfClasses);
24 self.samplesAggr = cell(model.getNumberOfNodes, 1);
25 end
26 end
27
28 methods
29 function self = addSamples(self, sampleData)
30 i = self.model.getNodeIndex(sampleData.node);
31 r = 0;
32 if sampleData.isAggregate
33 %jobclass = NaN;
34 self.samplesAggr{i}{end+1} = sampleData;
35 else
36 r = self.model.getClassIndex(sampleData.class);
37 % store
38 self.samples{i,r}{end+1} = sampleData;
39 end
40 end
41
42 function data = getData(self)
43 data = self.samples;
44 end
45
46 function data = getDataAggr(self)
47 data = self.samplesAggr;
48 end
49
50 % look into the data available for that node and class for the
51 % first ArvR dataset
52 function data = getArvR(self, node, jobclass)
53 data = [];
54 i = self.model.getNodeIndex(node);
55 r = self.model.getClassIndex(jobclass);
56 nodeData = self.samples{i,r};
57 for d=1:length(nodeData)
58 if nodeData{d}.type == MetricType.ArvR
59 data = nodeData{d};
60 return
61 end
62 end
63 end
64
65 % look into the data available for that node and class for the
66 % first ArvR dataset
67 function data = getUtil(self, node, jobclass)
68 data = [];
69 i = self.model.getNodeIndex(node);
70 r = self.model.getClassIndex(jobclass);
71 nodeData = self.samples{i,r};
72 for d=1:length(nodeData)
73 if nodeData{d}.type == MetricType.Util
74 data = nodeData{d};
75 return
76 end
77 end
78 end
79
80 % look into the data available for that node and class for the
81 % first ArvR dataset
82 function data = getRespT(self, node, jobclass)
83 data = [];
84 i = self.model.getNodeIndex(node);
85 r = self.model.getClassIndex(jobclass);
86 nodeData = self.samples{i,r};
87 for d=1:length(nodeData)
88 if nodeData{d}.type == MetricType.RespT
89 data = nodeData{d};
90 return
91 end
92 end
93 end
94
95 % look into the data available for that node and class for the
96 % first ArvR dataset
97 function data = getAggrUtil(self, node)
98 data = [];
99 i = self.model.getNodeIndex(node);
100 nodeAggrData = self.samplesAggr{i};
101 for d=1:length(nodeAggrData)
102 if nodeAggrData{d}.type == MetricType.Util
103 data = nodeAggrData{d};
104 return
105 end
106 end
107 end
108
109 % look into the data available for that node and class for the
110 % first QLen dataset
111 function data = getQLen(self, node, jobclass, ev)
112 data = [];
113 i = self.model.getNodeIndex(node);
114 r = self.model.getClassIndex(jobclass);
115 nodeData = self.samples{i,r};
116 if ~exist('ev', 'var')
117 for d=1:length(nodeData)
118 if nodeData{d}.type == MetricType.QLen
119 data{end+1} = nodeData{d};
120 end
121 end
122 if length(data) == 1
123 data = data{1};
124 end
125 else
126 for d=1:length(nodeData)
127 % e.g., arrival queue-length
128 if nodeData{d}.type == MetricType.QLen && (nodeData{d}.cond.node == ev.node) && (nodeData{d}.cond.class == ev.class) && (nodeData{d}.cond.event == ev.event)
129 data = nodeData{d};
130 return
131 end
132 end
133 end
134 end
135
136 % look into the data available for that node and class for the
137 % first QLen dataset
138 function data = getAggrQLen(self, node, ev)
139 data = [];
140 i = self.model.getNodeIndex(node);
141 nodeData = self.samplesAggr{i};
142 if ~exist('ev', 'var')
143 for d=1:length(nodeData)
144 if nodeData{d}.type == MetricType.QLen
145 data = nodeData{d};
146 return
147 end
148 end
149 else
150 for d=1:length(nodeData)
151 % e.g., arrival queue-length
152 if nodeData{d}.type == MetricType.QLen && (nodeData{d}.cond.node == ev.node) && (nodeData{d}.cond.class == ev.class) && (nodeData{d}.cond.event == ev.event)
153 data = nodeData{d};
154 return
155 end
156 end
157 end
158 end
159
160 % look into the data available for that node and class for the
161 % first Tput dataset
162 function data = getTput(self, node, jobclass)
163 data = [];
164 i = self.model.getNodeIndex(node);
165 r = self.model.getClassIndex(jobclass);
166 nodeData = self.samples{i,r};
167 for d=1:length(nodeData)
168 if nodeData{d}.type == MetricType.Tput
169 data = nodeData{d};
170 return
171 end
172 end
173 end
174
175 function method = autoMethod(self)
176 % AUTOMETHOD Automatically select the best estimation method
177 % based on the available sampled metrics.
178 sn = self.model.getStruct;
179 hasArvR = false; hasRespT = false; hasUtil = false;
180 hasQLen = false; hasTput = false; hasTrace = false;
181 hasAggrUtil = false; hasAggrQLen = false;
182
183 % scan per-class metrics
184 for i = 1:size(self.samples, 1)
185 for r = 1:size(self.samples, 2)
186 nodeData = self.samples{i,r};
187 for d = 1:length(nodeData)
188 sm = nodeData{d};
189 if sm.type == MetricType.ArvR, hasArvR = true; end
190 if sm.type == MetricType.RespT, hasRespT = true; end
191 if sm.type == MetricType.Util, hasUtil = true; end
192 if sm.type == MetricType.QLen, hasQLen = true; end
193 if sm.type == MetricType.Tput, hasTput = true; end
194 if sm.isTrace(), hasTrace = true; end
195 end
196 end
197 end
198
199 % scan aggregate metrics
200 for i = 1:size(self.samplesAggr, 1)
201 nodeAggrData = self.samplesAggr{i};
202 for d = 1:length(nodeAggrData)
203 sm = nodeAggrData{d};
204 if sm.type == MetricType.Util, hasAggrUtil = true; end
205 if sm.type == MetricType.QLen, hasAggrQLen = true; end
206 end
207 end
208
209 % select method based on available data
210 if hasTrace && hasRespT && hasAggrQLen
211 method = 'erps';
212 elseif hasTrace && hasRespT && hasArvR
213 method = 'mlps';
214 elseif hasArvR && hasRespT && (hasUtil || hasAggrUtil)
215 method = 'ubo';
216 elseif hasArvR && (hasUtil || hasAggrUtil)
217 method = 'ubr';
218 elseif hasQLen
219 method = 'qmle';
220 else
221 error('Insufficient data to automatically select an estimation method. Please set options.method manually.');
222 end
223
224 self.options.method = method;
225 end
226
227 interpolate(self);
228 estVal = estimateAt(self, nodes);
229 estVal = estimator_ubo(self, nodes);
230 estVal = estimator_ubr(self, nodes);
231 estVal = estimator_erps(self, nodes);
232 estVal = estimator_ekf(self, nodes);
233 estVal = estimator_mcmc(self, nodes);
234 estVal = estimator_mle(self, nodes);
235 estVal = estimator_rnn(self, nodes);
236 estVal = estimator_mlps(self, nodes);
237 estVal = estimator_fmlps(self, nodes);
238 estVal = estimator_qmle(self, nodes);
239 [eqModel, eqNode] = buildClosedEquivalentForPS(self, node);
240 estVal = estimator_gibbs(self, nodes);
241 end
242
243 methods (Static)
244 function options = defaultOptions()
245 % OPTIONS = DEFAULTOPTIONS()
246 % Return default options
247 options = struct();
248 options.verbose = 1;
249 options.method = 'ubr';
250 options.variant = 'default';
251 options.iter_max = 1000;
252 options.tol = 1e-3;
253 options.solver = @SolverAuto;
254 options.openPopulation = 100;
255 end
256
257 function desc = getRequiredMetrics(method)
258 % GETREQUIREDMETRICS Return description of metrics required
259 % by each estimation method.
260 switch method
261 case 'ubr'
262 desc = 'ArvR (per-class) + Util (per-class or aggregate)';
263 case 'ubo'
264 desc = 'ArvR (per-class) + RespT (per-class) + Util (aggregate)';
265 case 'erps'
266 desc = 'RespT (per-class) + QLen (aggregate, conditional on class arrivals). PS stations only.';
267 case 'ekf'
268 desc = 'RespT (per-class) + Util (aggregate). Sequential/recursive estimation.';
269 case 'mcmc'
270 desc = 'QLen (aggregate). Gibbs sampling with MCMC. Open/mixed via closed equivalence.';
271 case 'mle'
272 desc = 'ArvR (per-class) + RespT (per-class) + Util (aggregate)';
273 case 'rnn'
274 desc = 'QLen (per-class, trace format). Transient queue-length traces.';
275 case 'mlps'
276 desc = 'ArvR (per-class, trace) + RespT (per-class, trace). PS stations only. Open/mixed via closed equivalence.';
277 case 'fmlps'
278 desc = 'ArvR (per-class, trace) + RespT (per-class, trace). PS stations only. Open/mixed via closed equivalence.';
279 case 'qmle'
280 desc = 'QLen (per-class). Open/mixed via closed equivalence (Z_r = N_r / lambda_r).';
281 case 'gibbs'
282 desc = 'ArvR (per-class, trace) + RespT (per-class, trace) + Tput (per-class). Gibbs sampling.';
283 otherwise
284 desc = sprintf('Unknown method: %s', method);
285 end
286 end
287 end
288end
Definition mmt.m:124