100 std::map<std::string, std::vector<VariablePtr>> by_type;
101 std::vector<std::string> discovered;
102 for (
const VariablePtr& variable : problem_.variables()) {
103 const std::string type = variable->type();
104 if (by_type.find(type) == by_type.end()) discovered.push_back(type);
105 by_type[type].push_back(variable);
107 subproblems_.clear();
108 std::set<std::string> used;
110 const auto found = by_type.find(type);
111 if (found == by_type.end())
continue;
112 subproblems_.push_back({type, type, found->second, {}});
115 for (
const std::string& type : discovered)
116 if (used.find(type) == used.end())
117 subproblems_.push_back({type, type, by_type.at(type), {}});
141 if (dependencies_.empty())
return subproblems_;
142 std::map<std::string, std::size_t> indegree;
143 std::map<std::string, std::vector<std::string>> adjacent;
144 std::map<std::string, SubProblem> by_name;
145 for (
const SubProblem& subproblem : subproblems_) {
146 indegree[subproblem.name] = 0;
147 adjacent[subproblem.name] = {};
148 by_name[subproblem.name] = subproblem;
150 for (
const auto& target : dependencies_) {
151 if (indegree.find(target.first) == indegree.end())
continue;
152 for (
const std::string& source : target.second) {
153 if (adjacent.find(source) == adjacent.end())
continue;
154 adjacent[source].push_back(target.first);
155 ++indegree[target.first];
158 std::vector<std::string> queue;
159 for (
const SubProblem& subproblem : subproblems_)
160 if (indegree[subproblem.name] == 0) queue.push_back(subproblem.name);
161 std::vector<SubProblem> ordered;
162 for (std::size_t head = 0; head < queue.size(); ++head) {
163 const std::string current = queue[head];
164 ordered.push_back(by_name.at(current));
165 for (
const std::string& successor : adjacent[current])
166 if (--indegree[successor] == 0) queue.push_back(successor);
168 return ordered.size() == subproblems_.size() ? ordered : subproblems_;
172 const auto start = std::chrono::steady_clock::now();
174 if (subproblems_.empty()) {
180 double previous_objective = std::numeric_limits<double>::infinity();
181 for (std::size_t cycle = 1; cycle <= max_cycles; ++cycle) {
182 for (
const SubProblem& subproblem : ordered) {
189 fixed_values[value.first] = value.second;
191 const double current_objective = evaluate_full_objective(fixed_values);
194 if (std::abs(current_objective - previous_objective) < tolerance) {
198 previous_objective = current_objective;
204 std::chrono::steady_clock::now() - start).count();
211 bool auto_freeze =
true,
double freeze_tolerance = 1e-3,
212 std::vector<std::string> frozen_layers = {})
const {
214 const auto start = std::chrono::steady_clock::now();
217 std::map<std::string, std::vector<VariablePtr>> groups;
218 std::vector<std::string> group_order;
220 const std::vector<std::string> owned = variable->layers(model);
221 const std::string layer = owned.empty() ?
"_nolayer" : owned.front();
222 if (groups.find(layer) == groups.end()) group_order.push_back(layer);
223 groups[layer].push_back(variable);
227 std::map<std::string, std::vector<double>> previous_signatures;
228 bool have_previous_signatures =
false;
229 double previous_objective = std::numeric_limits<double>::infinity();
232 for (std::size_t cycle = 1; cycle <= max_cycles; ++cycle) {
233 for (
const std::string& layer : group_order) {
234 if (std::find(frozen_layers.begin(), frozen_layers.end(), layer) !=
237 const SubProblem subproblem{layer, groups[layer].front()->type(),
239 OptimizationProblem partial = create_partial_problem(subproblem, fixed_values);
240 OptimizationResult solved =
LineOptSolver(partial, solver_options_).solve();
243 for (
const auto& value : solved.variable_values)
244 fixed_values[value.first] = value.second;
247 const EvaluationResult evaluation = full_evaluator.evaluate(fixed_values);
249 double current_objective = std::numeric_limits<double>::infinity();
250 std::map<std::string, std::vector<double>> signatures;
251 if (evaluation.feasible) {
253 for (
const auto& fixed : problem_.fixed_variables())
254 all_values[fixed.first->name()] = fixed.second;
255 for (
const auto& value : fixed_values) all_values[value.first] = value.second;
256 current_objective = problem_.objective()->evaluate_with_penalty(
257 evaluation, all_values, solver_options_.penalty_weight);
258 for (
const ConstraintPtr& constraint : problem_.constraints())
259 current_objective += constraint->evaluate(evaluation, all_values) *
260 solver_options_.penalty_weight;
261 signatures = layer_signatures(evaluation, group_order);
264 if (auto_freeze && have_previous_signatures) {
265 std::map<std::string, double> moved;
266 bool active_moved =
false;
267 for (
const std::string& layer : group_order) {
268 const auto before = previous_signatures.find(layer);
269 const auto after = signatures.find(layer);
270 moved[layer] = signature_delta(
271 before == previous_signatures.end() ? std::vector<double>()
273 after == signatures.end() ? std::vector<double>() : after->second);
274 if (std::find(frozen_layers.begin(), frozen_layers.end(), layer) ==
275 frozen_layers.end() &&
276 moved[layer] > freeze_tolerance)
279 for (
const std::string& layer : group_order) {
280 const auto frozen = std::find(frozen_layers.begin(), frozen_layers.end(), layer);
281 if (frozen != frozen_layers.end()) {
282 if (active_moved) frozen_layers.erase(frozen);
283 }
else if (moved[layer] < freeze_tolerance) {
284 frozen_layers.push_back(layer);
289 previous_signatures = std::move(signatures);
290 have_previous_signatures =
true;
292 if (std::abs(current_objective - previous_objective) < tolerance) {
296 previous_objective = current_objective;
298 if (frozen_layers.size() >= group_order.size()) {
305 std::sort(frozen_layers.begin(), frozen_layers.end());
306 frozen_layers.erase(std::unique(frozen_layers.begin(), frozen_layers.end()),
307 frozen_layers.end());
310 std::chrono::steady_clock::now() - start).count();