54 std::vector<std::pair<VariablePtr, Value>> fixed = {},
60 std::vector<std::pair<VariablePtr, Value>> fixed = {})
61 :
LineEvaluator(
Model(std::move(model)), std::move(variables), std::move(fixed), {}) {}
64 std::vector<std::pair<VariablePtr, Value>> fixed = {},
66 : base_(std::move(model)), variables_(std::move(variables)),
67 fixed_(std::move(fixed)), solve_(std::move(
solve)) {
72 std::size_t dimension = 0;
74 offsets_.push_back(dimension);
75 dimension += variable->dimension();
77 dimension_ = dimension;
81 return std::holds_alternative<lqn::LqnModel<double>>(base_);
84 std::vector<std::pair<double, double>>
bounds()
const {
85 return std::vector<std::pair<double, double>>(dimension_, {0.0, 1.0});
89 for (std::size_t i = 0; i < variables_.size(); ++i) {
90 const auto begin = x.begin() + offsets_[i];
91 out[variables_[i]->name()] = variables_[i]->decode(
92 std::vector<double>(begin, begin + variables_[i]->dimension()));
99 const auto start = std::chrono::steady_clock::now();
103 evaluate_layered(values, out);
105 evaluate_flat(values, out);
107 }
catch (
const std::exception&) {
110 out.
solve_time = std::chrono::duration<double>(
111 std::chrono::steady_clock::now() - start).count();
120 apply_layered(model, values);
126 for (
const auto& row : table.
rows)
127 out[
metric_key(layered_name(row.station), layered_name(row.jobclass))] = {
128 row.dTput, row.dRespT, row.dQLen, row.dUtil};
130 }
catch (
const std::exception&) {
136 static std::string layered_name(
const std::string& value) {
137 if (value.size() > 2 && value[1] ==
':' &&
138 std::string(
"PTERA").find(value[0]) != std::string::npos)
139 return value.substr(2);
143 void evaluate_flat(
const VariableValues& values, EvaluationResult& out)
const {
145 for (
const auto& fixed : fixed_) fixed.first->apply(model, fixed.second);
147 const auto found = values.find(variable->name());
148 if (found != values.end()) variable->apply(model, found->second);
150 const qn::NetworkStruct<double>& structure = model.
get_struct();
151 const mva::AvgResult<double> average = solve_(structure);
152 out.solver_used = average.actualmethod;
153 for (std::size_t i = 0; i < structure.nstations; ++i) {
154 double utilization = 0.0;
155 for (std::size_t r = 0; r < structure.nclasses; ++r) {
156 const std::string& station = structure.stations[i].name;
157 const std::string& jobclass = structure.classes[r].name;
158 if (!average.RN.empty()) out.set_response_time(station, jobclass, average.RN(i, r));
159 if (!average.TN.empty()) out.set_throughput(station, jobclass, average.TN(i, r));
160 if (!average.QN.empty()) out.set_queue_length(station, jobclass, average.QN(i, r));
161 if (!average.UN.empty()) utilization += average.UN(i, r);
163 out.utilizations[structure.stations[i].name] = utilization;
166 for (std::size_t c = 0; c < structure.nchains; ++c) {
167 double response = c < system.CN.size()
169 : std::numeric_limits<double>::quiet_NaN();
170 const double throughput = c < system.XN.size() ? system.XN[c] : 0.0;
173 for (
const std::size_t member : structure.inchain[c]) {
174 open = open || std::isinf(structure.classes[member - 1].population);
175 for (std::size_t i = 0; i < structure.nstations; ++i)
176 jobs += out.queue_length(structure.stations[i].name,
177 structure.classes[member - 1].name);
179 if (open && throughput > 0.0) response = jobs / throughput;
180 for (
const std::size_t member : structure.inchain[c]) {
181 out.system_response_times[structure.classes[member - 1].name] = response;
182 out.system_throughputs[structure.classes[member - 1].name] = throughput;
185 const std::optional<SensitivityData> sensitivity =
188 out.sensitivities = std::make_shared<SensitivityData>(*sensitivity);
191 void apply_layered(lqn::LqnModel<double>& model,
const VariableValues& values)
const {
192 for (
const auto& fixed : fixed_) fixed.first->apply(model, fixed.second);
194 const auto found = values.find(variable->name());
195 if (found != values.end()) variable->apply(model, found->second);
199 void evaluate_layered(
const VariableValues& values, EvaluationResult& out)
const {
200 lqn::LqnModel<double> model = std::get<lqn::LqnModel<double>>(base_);
201 apply_layered(model, values);
203 ln::SolverLN<double> solver(structure, ln::LnOptions());
204 const ln::LnSolution<double> average = solver.get_ensemble_avg();
205 out.solver_used =
"SolverLN";
206 for (std::size_t i = 1; i <= structure.nidx; ++i) {
207 const std::string& name = structure.names[i];
208 if (i < average.RN.size() && i < average.defined_R.size() &&
209 average.defined_R[i] && std::isfinite(average.RN[i]))
210 out.set_response_time(name, name, average.RN[i]);
211 if (i < average.TN.size() && i < average.defined_T.size() &&
212 average.defined_T[i] && std::isfinite(average.TN[i]))
213 out.set_throughput(name, name, average.TN[i]);
214 if (i < average.QN.size() && i < average.defined_Q.size() &&
215 average.defined_Q[i] && std::isfinite(average.QN[i]))
216 out.set_queue_length(name, name, average.QN[i]);
217 if (i < average.UN.size() && i < average.defined_U.size() &&
218 average.defined_U[i] && std::isfinite(average.UN[i]))
219 out.utilizations[name] = average.UN[i];
221 for (std::size_t task = 1; task <= structure.ntasks; ++task) {
222 const std::size_t index = structure.tshift + task;
223 if (index >= structure.isref.size() || !structure.isref[index])
continue;
224 const std::string& name = structure.names[index];
225 const double throughput = out.throughput(name, name);
226 if (throughput > 0.0) out.system_throughputs[name] = throughput;
227 double response = 0.0;
228 bool have_response =
false;
229 for (
const std::size_t entry : structure.entriesof[index]) {
230 const double value = out.response_time(structure.names[entry],
231 structure.names[entry]);
232 if (std::isfinite(value)) {
234 have_response =
true;
237 if (have_response) out.system_response_times[name] = response;
242 std::vector<VariablePtr> variables_;
243 std::vector<std::pair<VariablePtr, Value>> fixed_;
245 std::vector<std::size_t> offsets_;
246 std::size_t dimension_ = 0;
247 std::size_t count_ = 0;