LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
LineEvaluator.m
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.
6 %
7 % A copied model carries a cached NetworkStruct that scalar setters do not
8 % invalidate, so evaluateValues forces refreshStruct after applying
9 % variables.
10
11 properties
12 baseModel
13 variables % cell of opt.DecisionVariable
14 fixedVariables % cell of {var, value}
15 evaluationCount = 0;
16 totalDimension
17 varOffsets
18 end
19
20 methods
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;
26 else
27 obj.fixedVariables = {};
28 end
29 dim = 0;
30 obj.varOffsets = zeros(1, numel(variables));
31 for i = 1:numel(variables)
32 obj.varOffsets(i) = dim;
33 dim = dim + variables{i}.getDimension();
34 end
35 obj.totalDimension = dim;
36 end
37
38 function n = getEvaluationCount(obj), n = obj.evaluationCount; end
39
40 function b = getBounds(obj)
41 b = [];
42 for i = 1:numel(obj.variables)
43 b = [b; obj.variables{i}.getBounds()]; %#ok<AGROW>
44 end
45 end
46
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);
55 end
56 end
57
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()));
63 end
64 end
65 end
66
67 function m = copyModel(obj)
68 m = obj.baseModel.copy();
69 end
70
71 function result = evaluateValues(obj, values)
72 obj.evaluationCount = obj.evaluationCount + 1;
73 result = opt.EvaluationResult();
74 try
75 model = obj.copyModel();
76 for k = 1:numel(obj.fixedVariables)
77 fv = obj.fixedVariables{k};
78 fv{1}.apply(model, fv{2});
79 end
80 obj.applyVariables(model, values);
81 model.refreshStruct();
82
83 solver = SolverAuto(model);
84 [QN, UN, RN, ~, ~, TN] = solver.getAvg();
85 result.feasible = true;
86 try
87 result.solverUsed = solver.getSelectedSolverName();
88 catch
89 result.solverUsed = '';
90 end
91 obj.extractMetrics(model, QN, UN, RN, TN, result);
92 obj.extractSystemMetrics(solver, model, result);
93 result.sensitivities = opt.sens.computeModelSensitivities(model);
94 catch ME %#ok<NASGU>
95 result.feasible = false;
96 end
97 end
98
99 function extractMetrics(~, model, QN, UN, RN, TN, result)
100 sn = model.getStruct();
101 R = sn.nclasses;
102 for ist = 1:sn.nstations
103 nodeIdx = sn.stationToNode(ist);
104 st = sn.nodenames{nodeIdx};
105 for r = 1:R
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
110 if ~isempty(UN)
111 if isKey(result.utilizations, st)
112 result.utilizations(st) = result.utilizations(st) + UN(ist, r);
113 else
114 result.utilizations(st) = UN(ist, r);
115 end
116 end
117 end
118 end
119 end
120
121 function extractSystemMetrics(~, solver, model, result)
122 try
123 [SysRespT, SysTput] = solver.getAvgSys();
124 catch
125 return;
126 end
127 SysRespT = SysRespT(:); SysTput = SysTput(:);
128 sn = model.getStruct();
129 chains = sn.chains; classnames = sn.classnames;
130 njobs = sn.njobs(:);
131 nchains = size(chains, 1); nclasses = size(chains, 2);
132 for ci = 1:nchains
133 members = {};
134 isOpen = false;
135 for k = 1:nclasses
136 if chains(ci, k) > 0
137 members{end+1} = classnames{k}; %#ok<AGROW>
138 if isinf(njobs(k)), isOpen = true; end
139 end
140 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
145 jobsInSystem = 0.0;
146 ks = keys(result.queueLengths);
147 for kk = 1:numel(ks)
148 parts = strsplit(ks{kk}, '||');
149 if any(strcmp(parts{2}, members))
150 jobsInSystem = jobsInSystem + result.queueLengths(ks{kk});
151 end
152 end
153 respt = jobsInSystem / tput;
154 end
155 for mi = 1:numel(members)
156 if ~isnan(respt), result.systemResponseTimes(members{mi}) = respt; end
157 result.systemThroughputs(members{mi}) = tput;
158 end
159 end
160 end
161
162 function result = evaluateValuesWithCache(obj, values, cache)
163 key = opt.LineEvaluator.valuesKey(values);
164 if isKey(cache, key)
165 result = cache(key);
166 else
167 result = obj.evaluateValues(values);
168 cache(key) = result; %#ok<NASGU>
169 end
170 end
171 end
172
173 methods (Static)
174 function key = valuesKey(values)
175 ks = sort(keys(values));
176 parts = cell(1, numel(ks));
177 for i = 1:numel(ks)
178 v = values(ks{i});
179 if isnumeric(v)
180 parts{i} = [ks{i} '=' mat2str(round(v(:).' * 1e9) / 1e9)];
181 else
182 parts{i} = [ks{i} '=' num2str(v)];
183 end
184 end
185 key = strjoin(parts, ';');
186 end
187 end
188end
Definition Station.m:245