1classdef LineEvaluator < handle
2 % LineEvaluator Interface between
the optimizer and SolverAuto. Applies
3 % decision variable values to a per-evaluation model copy, solves, and
4 % extracts per-(station,
class) and system metrics. Mirrors native-Python
5 % line_solver.opt.evaluator.LineEvaluator.
7 % A copied model carries a cached NetworkStruct that scalar setters
do not
8 % invalidate, so evaluateValues forces refreshStruct after applying
13 variables % cell of opt.DecisionVariable
14 fixedVariables % cell of {var, value}
21 function obj = LineEvaluator(model, variables, fixedVariables)
22 obj.baseModel = model;
23 obj.variables = variables;
24 if nargin >= 3 && ~isempty(fixedVariables)
25 obj.fixedVariables = fixedVariables;
27 obj.fixedVariables = {};
30 obj.varOffsets = zeros(1, numel(variables));
31 for i = 1:numel(variables)
32 obj.varOffsets(i) = dim;
33 dim = dim + variables{i}.getDimension();
35 obj.totalDimension = dim;
38 function n = getEvaluationCount(obj), n = obj.evaluationCount; end
40 function b = getBounds(obj)
42 for i = 1:numel(obj.variables)
43 b = [b; obj.variables{i}.getBounds()]; %#ok<AGROW>
47 function values = decodeVariables(obj, x)
48 values = containers.Map(
'KeyType',
'char',
'ValueType',
'any');
49 for i = 1:numel(obj.variables)
50 var = obj.variables{i};
51 offset = obj.varOffsets(i);
52 dim = var.getDimension();
53 slice = x(offset+1 : offset+dim);
54 values(var.getName()) = var.decode(slice);
58 function applyVariables(obj, model, values)
59 for i = 1:numel(obj.variables)
60 var = obj.variables{i};
61 if isKey(values, var.getName())
62 var.apply(model, values(var.getName()));
67 function m = copyModel(obj)
68 m = obj.baseModel.copy();
71 function result = evaluateValues(obj, values)
72 obj.evaluationCount = obj.evaluationCount + 1;
73 result = opt.EvaluationResult();
75 model = obj.copyModel();
76 for k = 1:numel(obj.fixedVariables)
77 fv = obj.fixedVariables{k};
78 fv{1}.apply(model, fv{2});
80 obj.applyVariables(model, values);
81 model.refreshStruct();
83 solver = SolverAuto(model);
84 [QN, UN, RN, ~, ~, TN] = solver.getAvg();
85 result.feasible =
true;
87 result.solverUsed = solver.getSelectedSolverName();
89 result.solverUsed =
'';
91 obj.extractMetrics(model, QN, UN, RN, TN, result);
92 obj.extractSystemMetrics(solver, model, result);
93 result.sensitivities = opt.sens.computeModelSensitivities(model);
95 result.feasible =
false;
99 function extractMetrics(~, model, QN, UN, RN, TN, result)
100 sn = model.getStruct();
102 for ist = 1:sn.nstations
103 nodeIdx = sn.stationToNode(ist);
104 st = sn.nodenames{nodeIdx};
106 cl = sn.classnames{r};
107 if ~isempty(RN), result.setResponseTime(st, cl, RN(ist, r)); end
108 if ~isempty(TN), result.setThroughput(st, cl, TN(ist, r)); end
109 if ~isempty(QN), result.setQueueLength(st, cl, QN(ist, r)); end
111 if isKey(result.utilizations, st)
112 result.utilizations(st) = result.utilizations(st) + UN(ist, r);
114 result.utilizations(st) = UN(ist, r);
121 function extractSystemMetrics(~, solver, model, result)
123 [SysRespT, SysTput] = solver.getAvgSys();
127 SysRespT = SysRespT(:); SysTput = SysTput(:);
128 sn = model.getStruct();
129 chains = sn.chains; classnames = sn.classnames;
131 nchains = size(chains, 1); nclasses = size(chains, 2);
137 members{end+1} = classnames{k}; %#ok<AGROW>
138 if isinf(njobs(k)), isOpen =
true; end
141 if isempty(members),
continue; end
142 tput = 0.0;
if ci <= numel(SysTput), tput = SysTput(ci); end
143 respt = NaN;
if ci <= numel(SysRespT), respt = SysRespT(ci); end
144 if isOpen && tput > 0 && result.queueLengths.Count > 0
146 ks = keys(result.queueLengths);
148 parts = strsplit(ks{kk},
'||');
149 if any(strcmp(parts{2}, members))
150 jobsInSystem = jobsInSystem + result.queueLengths(ks{kk});
153 respt = jobsInSystem / tput;
155 for mi = 1:numel(members)
156 if ~isnan(respt), result.systemResponseTimes(members{mi}) = respt; end
157 result.systemThroughputs(members{mi}) = tput;
162 function result = evaluateValuesWithCache(obj, values, cache)
163 key = opt.LineEvaluator.valuesKey(values);
167 result = obj.evaluateValues(values);
168 cache(key) = result; %#ok<NASGU>
174 function key = valuesKey(values)
175 ks = sort(keys(values));
176 parts = cell(1, numel(ks));
180 parts{i} = [ks{i}
'=' mat2str(round(v(:).
' * 1e9) / 1e9)];
182 parts{i} = [ks{i} '=
' num2str(v)];
185 key = strjoin(parts, ';
');