Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
solution_crush.cc
Go to the documentation of this file.
1// Copyright 2010-2025 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <algorithm>
17#include <cstdint>
18#include <memory>
19#include <optional>
20#ifdef CHECK_CRUSH
21#include <sstream>
22#include <string>
23#endif
24#include <utility>
25#include <vector>
26
27#include "absl/algorithm/container.h"
28#include "absl/container/btree_map.h"
29#include "absl/container/flat_hash_map.h"
30#include "absl/container/flat_hash_set.h"
31#include "absl/log/check.h"
32#include "absl/log/log.h"
33#include "absl/types/span.h"
40#include "ortools/sat/util.h"
42
43namespace operations_research {
44namespace sat {
45
47 int num_vars, const absl::flat_hash_map<int, int64_t>& solution) {
48 CHECK(!solution_is_loaded_);
49 CHECK(var_has_value_.empty());
50 CHECK(var_values_.empty());
51 solution_is_loaded_ = true;
52 var_has_value_.resize(num_vars, false);
53 var_values_.resize(num_vars, 0);
54 for (const auto [var, value] : solution) {
55 var_has_value_[var] = true;
56 var_values_[var] = value;
57 }
58}
59
60void SolutionCrush::Resize(int new_size) {
61 if (!solution_is_loaded_) return;
62 var_has_value_.resize(new_size, false);
63 var_values_.resize(new_size, 0);
64}
65
67 int64_t value) {
68 DCHECK(RefIsPositive(var));
69 if (!solution_is_loaded_) return;
70 if (!HasValue(PositiveRef(literal)) && HasValue(var)) {
71 SetLiteralValue(literal, GetVarValue(var) == value);
72 }
73}
74
76 int64_t value, bool is_le) {
77 DCHECK(RefIsPositive(var));
78 if (!solution_is_loaded_) return;
79 if (!HasValue(PositiveRef(literal)) && HasValue(var)) {
80 SetLiteralValue(
81 literal, is_le ? GetVarValue(var) <= value : GetVarValue(var) >= value);
82 }
83}
84
86 int new_var, absl::Span<const std::pair<int, int64_t>> linear,
87 int64_t offset) {
88 if (!solution_is_loaded_) return;
89 int64_t new_value = offset;
90 for (const auto [var, coeff] : linear) {
91 if (!HasValue(var)) return;
92 new_value += coeff * GetVarValue(var);
93 }
94 SetVarValue(new_var, new_value);
95}
96
98 absl::Span<const int> vars,
99 absl::Span<const int64_t> coeffs,
100 int64_t offset) {
101 DCHECK_EQ(vars.size(), coeffs.size());
102 if (!solution_is_loaded_) return;
103 int64_t new_value = offset;
104 for (int i = 0; i < vars.size(); ++i) {
105 const int var = vars[i];
106 const int64_t coeff = coeffs[i];
107 if (!HasValue(var)) return;
108 new_value += coeff * GetVarValue(var);
109 }
110 SetVarValue(new_var, new_value);
111}
112
113void SolutionCrush::SetVarToClause(int new_var, absl::Span<const int> clause) {
114 if (!solution_is_loaded_) return;
115 int new_value = 0;
116 bool all_have_value = true;
117 for (const int literal : clause) {
118 const int var = PositiveRef(literal);
119 if (!HasValue(var)) {
120 all_have_value = false;
121 break;
122 }
123 if (GetVarValue(var) == (RefIsPositive(literal) ? 1 : 0)) {
124 new_value = 1;
125 break;
126 }
127 }
128 // Leave the `new_var` unassigned if any literal is unassigned.
129 if (all_have_value) {
130 SetVarValue(new_var, new_value);
131 }
132}
133
135 absl::Span<const int> conjunction) {
136 if (!solution_is_loaded_) return;
137 int new_value = 1;
138 bool all_have_value = true;
139 for (const int literal : conjunction) {
140 const int var = PositiveRef(literal);
141 if (!HasValue(var)) {
142 all_have_value = false;
143 break;
144 }
145 if (GetVarValue(var) == (RefIsPositive(literal) ? 0 : 1)) {
146 new_value = 0;
147 break;
148 }
149 }
150 // Leave the `new_var` unassigned if any literal is unassigned.
151 if (all_have_value) {
152 SetVarValue(new_var, new_value);
153 }
154}
155
157 int new_var, int64_t value,
158 absl::Span<const std::pair<int, int64_t>> linear, const Domain& domain) {
159 if (!solution_is_loaded_) return;
160 int64_t linear_value = 0;
161 bool all_have_value = true;
162 for (const auto [var, coeff] : linear) {
163 if (!HasValue(var)) {
164 all_have_value = false;
165 break;
166 }
167 linear_value += GetVarValue(var) * coeff;
168 }
169 if (all_have_value && !domain.Contains(linear_value)) {
170 SetVarValue(new_var, value);
171 }
172}
173
175 int literal, bool value, absl::Span<const std::pair<int, int64_t>> linear,
176 const Domain& domain) {
178 PositiveRef(literal), RefIsPositive(literal) ? value : !value, linear,
179 domain);
180}
181
182void SolutionCrush::SetVarToValueIf(int var, int64_t value, int condition_lit) {
184 var, value, {{PositiveRef(condition_lit), 1}},
185 Domain(RefIsPositive(condition_lit) ? 0 : 1));
186}
187
189 int var, const LinearExpressionProto& expr, int condition_lit) {
190 if (!solution_is_loaded_) return;
191 if (!HasValue(PositiveRef(condition_lit))) return;
192 if (!GetLiteralValue(condition_lit)) return;
193 const std::optional<int64_t> expr_value = GetExpressionValue(expr);
194 if (expr_value.has_value()) {
195 SetVarValue(var, expr_value.value());
196 }
197}
198
199void SolutionCrush::SetLiteralToValueIf(int literal, bool value,
200 int condition_lit) {
202 literal, value, {{PositiveRef(condition_lit), 1}},
203 Domain(RefIsPositive(condition_lit) ? 0 : 1));
204}
205
207 int var, absl::Span<const int> condition_lits, int64_t value_if_true,
208 int64_t value_if_false) {
209 if (!solution_is_loaded_) return;
210 bool condition_value = true;
211 for (const int condition_lit : condition_lits) {
212 if (!HasValue(PositiveRef(condition_lit))) return;
213 if (!GetLiteralValue(condition_lit)) {
214 condition_value = false;
215 break;
216 }
217 }
218 SetVarValue(var, condition_value ? value_if_true : value_if_false);
219}
220
221void SolutionCrush::MakeLiteralsEqual(int lit1, int lit2) {
222 if (!solution_is_loaded_) return;
223 if (HasValue(PositiveRef(lit2))) {
224 SetLiteralValue(lit1, GetLiteralValue(lit2));
225 } else if (HasValue(PositiveRef(lit1))) {
226 SetLiteralValue(lit2, GetLiteralValue(lit1));
227 }
228}
229
230void SolutionCrush::SetOrUpdateVarToDomain(int var, const Domain& domain) {
231 if (!solution_is_loaded_) return;
232 if (HasValue(var)) {
233 SetVarValue(var, domain.ClosestValue(GetVarValue(var)));
234 } else if (domain.IsFixed()) {
235 SetVarValue(var, domain.FixedValue());
236 }
237}
238
240 int var, const Domain& reduced_var_domain,
241 std::optional<int64_t> unique_escape_value,
242 bool push_down_when_not_in_domain,
243 const absl::btree_map<int64_t, int>& encoding) {
244 if (!solution_is_loaded_) return;
245 if (HasValue(var)) {
246 const int64_t old_value = GetVarValue(var);
247 int64_t new_value = old_value;
248 if (reduced_var_domain.Contains(old_value)) return;
249 if (unique_escape_value.has_value()) {
250 new_value = unique_escape_value.value();
251 } else if (push_down_when_not_in_domain) {
252 DCHECK_GT(old_value, reduced_var_domain.Min());
253 new_value = reduced_var_domain.ValueAtOrBefore(old_value);
254 } else {
255 DCHECK_LT(old_value, reduced_var_domain.Max());
256 new_value = reduced_var_domain.ValueAtOrAfter(old_value);
257 }
258
259 SetLiteralValue(encoding.at(new_value), true);
260 CHECK(!encoding.contains(old_value));
261 SetVarValue(var, new_value);
262 }
263}
264
266 // Set lit1 and lit2 to false if "lit1 - lit2 == 0" is violated.
267 const int sign1 = RefIsPositive(lit1) ? 1 : -1;
268 const int sign2 = RefIsPositive(lit2) ? 1 : -1;
269 const std::vector<std::pair<int, int64_t>> linear = {
270 {PositiveRef(lit1), sign1}, {PositiveRef(lit2), -sign2}};
271 const Domain domain = Domain((sign1 == 1 ? 0 : -1) - (sign2 == 1 ? 0 : -1));
272 SetLiteralToValueIfLinearConstraintViolated(lit1, false, linear, domain);
273 SetLiteralToValueIfLinearConstraintViolated(lit2, false, linear, domain);
274}
275
276void SolutionCrush::UpdateLiteralsWithDominance(int lit, int dominating_lit) {
277 if (!solution_is_loaded_) return;
278 if (!HasValue(PositiveRef(lit)) || !HasValue(PositiveRef(dominating_lit))) {
279 return;
280 }
281 if (GetLiteralValue(lit) && !GetLiteralValue(dominating_lit)) {
282 SetLiteralValue(lit, false);
283 SetLiteralValue(dominating_lit, true);
284 }
285}
286
288 int var, bool value,
289 absl::Span<const std::unique_ptr<SparsePermutation>> generators) {
290 if (!solution_is_loaded_) return;
291 if (!HasValue(var)) return;
292 if (GetVarValue(var) == static_cast<int64_t>(value)) return;
293
294 std::vector<int> schrier_vector;
295 std::vector<int> orbit;
296 GetSchreierVectorAndOrbit(var, generators, &schrier_vector, &orbit);
297
298 bool found_target = false;
299 int target_var;
300 for (int v : orbit) {
301 if (HasValue(v) && GetVarValue(v) == static_cast<int64_t>(value)) {
302 found_target = true;
303 target_var = v;
304 break;
305 }
306 }
307 if (!found_target) {
308 VLOG(1) << "Couldn't transform solution properly";
309 return;
310 }
311
312 const std::vector<int> generator_idx =
313 TracePoint(target_var, schrier_vector, generators);
314 for (const int i : generator_idx) {
315 PermuteVariables(*generators[i]);
316 }
317
318 DCHECK(HasValue(var));
319 DCHECK_EQ(GetVarValue(var), value);
320}
321
323 absl::Span<const std::vector<int>> orbitope, int row, int pivot_col,
324 bool value) {
325 if (!solution_is_loaded_) return;
326 int col = -1;
327 for (int c = 0; c < orbitope[row].size(); ++c) {
328 if (GetLiteralValue(orbitope[row][c]) == value) {
329 if (col != -1) {
330 VLOG(2) << "Multiple literals in row with given value";
331 return;
332 }
333 col = c;
334 }
335 }
336 if (col < pivot_col) {
337 // Nothing to do.
338 return;
339 }
340 // Swap the value of the literals in column `col` with the value of the ones
341 // in column `pivot_col`, if they all have a value.
342 for (int i = 0; i < orbitope.size(); ++i) {
343 if (!HasValue(PositiveRef(orbitope[i][col]))) return;
344 if (!HasValue(PositiveRef(orbitope[i][pivot_col]))) return;
345 }
346 for (int i = 0; i < orbitope.size(); ++i) {
347 const int src_lit = orbitope[i][col];
348 const int dst_lit = orbitope[i][pivot_col];
349 const bool src_value = GetLiteralValue(src_lit);
350 const bool dst_value = GetLiteralValue(dst_lit);
351 SetLiteralValue(src_lit, dst_value);
352 SetLiteralValue(dst_lit, src_value);
353 }
354}
355
357 int ref, int64_t min_value, int64_t max_value,
358 absl::Span<const std::pair<int, Domain>> dominating_refs) {
359 if (!solution_is_loaded_) return;
360 const std::optional<int64_t> ref_value = GetRefValue(ref);
361 if (!ref_value.has_value()) return;
362 // This can happen if the solution is not initially feasible (in which
363 // case we can't fix it).
364 if (*ref_value < min_value) return;
365 // If the value is already in the new domain there is nothing to do.
366 if (*ref_value <= max_value) return;
367 // The quantity to subtract from the value of `ref`.
368 const int64_t ref_value_delta = *ref_value - max_value;
369
370 SetRefValue(ref, *ref_value - ref_value_delta);
371 int64_t remaining_delta = ref_value_delta;
372 for (const auto& [dominating_ref, dominating_ref_domain] : dominating_refs) {
373 const std::optional<int64_t> dominating_ref_value =
374 GetRefValue(dominating_ref);
375 if (!dominating_ref_value.has_value()) continue;
376 const int64_t new_dominating_ref_value =
377 dominating_ref_domain.ValueAtOrBefore(*dominating_ref_value +
378 remaining_delta);
379 // This might happen if the solution is not initially feasible.
380 if (!dominating_ref_domain.Contains(new_dominating_ref_value)) continue;
381 SetRefValue(dominating_ref, new_dominating_ref_value);
382 remaining_delta -= (new_dominating_ref_value - *dominating_ref_value);
383 if (remaining_delta == 0) break;
384 }
385}
386
388 absl::Span<const int> enforcement_lits, std::optional<int> var_index,
389 absl::Span<const int> vars, absl::Span<const int64_t> default_values,
390 absl::Span<const int64_t> coeffs, int64_t rhs) {
391 DCHECK_EQ(vars.size(), coeffs.size());
392 DCHECK(!var_index.has_value() || var_index.value() < vars.size());
393 if (!solution_is_loaded_) return;
394 bool constraint_is_enforced = true;
395 for (const int lit : enforcement_lits) {
396 if (!HasValue(PositiveRef(lit))) return;
397 constraint_is_enforced = constraint_is_enforced && GetLiteralValue(lit);
398 }
399 if (!constraint_is_enforced) {
400 for (int i = 0; i < vars.size(); ++i) {
401 if (!HasValue(vars[i])) {
402 SetVarValue(vars[i], default_values[i]);
403 }
404 }
405 return;
406 }
407 int64_t term_value = rhs;
408 for (int i = 0; i < vars.size(); ++i) {
409 if (HasValue(vars[i])) {
410 if (i != var_index) {
411 term_value -= GetVarValue(vars[i]) * coeffs[i];
412 }
413 } else if (!var_index.has_value()) {
414 var_index = i;
415 } else if (var_index.value() != i) {
416 return;
417 }
418 }
419 if (!var_index.has_value()) return;
420 SetVarValue(vars[var_index.value()], term_value / coeffs[var_index.value()]);
421
422#ifdef CHECK_CRUSH
423 if (term_value % coeffs[var_index.value()] != 0) {
424 std::stringstream lhs;
425 for (int i = 0; i < vars.size(); ++i) {
426 lhs << (i == var_index ? "x" : std::to_string(GetVarValue(vars[i])));
427 lhs << " * " << coeffs[i];
428 if (i < vars.size() - 1) lhs << " + ";
429 }
430 LOG(FATAL) << "Linear constraint incompatible with solution: " << lhs
431 << " != " << rhs;
432 }
433#endif
434}
435
437 const ReservoirConstraintProto& reservoir, int64_t min_level,
438 int64_t max_level, absl::Span<const int> level_vars,
439 const CircuitConstraintProto& circuit) {
440 if (!solution_is_loaded_) return;
441 // The values of the active events, in the order they should appear in the
442 // circuit. The values are collected first, and sorted later.
443 struct ReservoirEventValues {
444 int index; // In the reservoir constraint.
445 int64_t time;
446 int64_t level_change;
447 };
448 const int num_events = reservoir.time_exprs_size();
449 std::vector<ReservoirEventValues> active_event_values;
450 for (int i = 0; i < num_events; ++i) {
451 if (!HasValue(PositiveRef(reservoir.active_literals(i)))) return;
452 if (GetLiteralValue(reservoir.active_literals(i))) {
453 const std::optional<int64_t> time_value =
454 GetExpressionValue(reservoir.time_exprs(i));
455 const std::optional<int64_t> change_value =
456 GetExpressionValue(reservoir.level_changes(i));
457 if (!time_value.has_value() || !change_value.has_value()) return;
458 active_event_values.push_back(
459 {i, time_value.value(), change_value.value()});
460 }
461 }
462
463 // Update the `level_vars` values by computing the level at each active event.
464 std::sort(active_event_values.begin(), active_event_values.end(),
465 [](const ReservoirEventValues& a, const ReservoirEventValues& b) {
466 return a.time < b.time;
467 });
468 int64_t current_level = 0;
469 for (int i = 0; i < active_event_values.size(); ++i) {
470 int j = i;
471 // Adjust the order of the events occurring at the same time, in the
472 // circuit, so that, at each node, the level is between `var_min` and
473 // `var_max`. For instance, if e1 = {t, +1} and e2 = {t, -1}, and if
474 // `current_level` = 0, `var_min` = -1 and `var_max` = 0, then e2 must occur
475 // before e1.
476 while (j < active_event_values.size() &&
477 active_event_values[j].time == active_event_values[i].time &&
478 (current_level + active_event_values[j].level_change < min_level ||
479 current_level + active_event_values[j].level_change > max_level)) {
480 ++j;
481 }
482 if (j < active_event_values.size() &&
483 active_event_values[j].time == active_event_values[i].time) {
484 if (i != j) std::swap(active_event_values[i], active_event_values[j]);
485 current_level += active_event_values[i].level_change;
486 SetVarValue(level_vars[active_event_values[i].index], current_level);
487 } else {
488 return;
489 }
490 }
491
492 // The index of each event in `active_event_values`, or -1 if the event's
493 // "active" value is false.
494 std::vector<int> active_event_value_index(num_events, -1);
495 for (int i = 0; i < active_event_values.size(); ++i) {
496 active_event_value_index[active_event_values[i].index] = i;
497 }
498 // Set the level vars of inactive events to an arbitrary value.
499 for (int i = 0; i < num_events; ++i) {
500 if (active_event_value_index[i] == -1) {
501 SetVarValue(level_vars[i], min_level);
502 }
503 }
504
505 for (int i = 0; i < circuit.literals_size(); ++i) {
506 const int head = circuit.heads(i);
507 const int tail = circuit.tails(i);
508 const int literal = circuit.literals(i);
509 if (tail == num_events) {
510 if (head == num_events) {
511 // Self-arc on the start and end node.
512 SetLiteralValue(literal, active_event_values.empty());
513 } else {
514 // Arc from the start node to an event node.
515 SetLiteralValue(literal, !active_event_values.empty() &&
516 active_event_values.front().index == head);
517 }
518 } else if (head == num_events) {
519 // Arc from an event node to the end node.
520 SetLiteralValue(literal, !active_event_values.empty() &&
521 active_event_values.back().index == tail);
522 } else if (tail != head) {
523 // Arc between two different event nodes.
524 const int tail_index = active_event_value_index[tail];
525 const int head_index = active_event_value_index[head];
526 SetLiteralValue(literal, tail_index != -1 && tail_index != -1 &&
527 head_index == tail_index + 1);
528 }
529 }
530}
531
533 int var, const LinearExpressionProto& time_i,
534 const LinearExpressionProto& time_j, int active_i, int active_j) {
535 if (!solution_is_loaded_) return;
536 std::optional<int64_t> time_i_value = GetExpressionValue(time_i);
537 std::optional<int64_t> time_j_value = GetExpressionValue(time_j);
538 std::optional<int64_t> active_i_value = GetRefValue(active_i);
539 std::optional<int64_t> active_j_value = GetRefValue(active_j);
540 if (time_i_value.has_value() && time_j_value.has_value() &&
541 active_i_value.has_value() && active_j_value.has_value()) {
542 const bool reified_value = (active_i_value.value() != 0) &&
543 (active_j_value.value() != 0) &&
544 (time_i_value.value() <= time_j_value.value());
545 SetVarValue(var, reified_value);
546 }
547}
548
550 int div_var, int prod_var,
551 int64_t default_div_value,
552 int64_t default_prod_value) {
553 if (!solution_is_loaded_) return;
554 bool constraint_is_enforced = true;
555 for (const int lit : ct.enforcement_literal()) {
556 if (!HasValue(PositiveRef(lit))) return;
557 constraint_is_enforced = constraint_is_enforced && GetLiteralValue(lit);
558 }
559 if (!constraint_is_enforced) {
560 SetVarValue(div_var, default_div_value);
561 SetVarValue(prod_var, default_prod_value);
562 return;
563 }
564 const LinearArgumentProto& int_mod = ct.int_mod();
565 std::optional<int64_t> v = GetExpressionValue(int_mod.exprs(0));
566 if (!v.has_value()) return;
567 const int64_t expr_value = v.value();
568
569 v = GetExpressionValue(int_mod.exprs(1));
570 if (!v.has_value()) return;
571 const int64_t mod_expr_value = v.value();
572
573 v = GetExpressionValue(int_mod.target());
574 if (!v.has_value()) return;
575 const int64_t target_expr_value = v.value();
576
577 // target_expr_value should be equal to "expr_value % mod_expr_value".
578 SetVarValue(div_var, expr_value / mod_expr_value);
579 SetVarValue(prod_var, expr_value - target_expr_value);
580}
581
583 absl::Span<const int> prod_vars) {
584 DCHECK_EQ(int_prod.exprs_size(), prod_vars.size() + 2);
585 if (!solution_is_loaded_) return;
586 std::optional<int64_t> v = GetExpressionValue(int_prod.exprs(0));
587 if (!v.has_value()) return;
588 int64_t last_prod_value = v.value();
589 for (int i = 1; i < int_prod.exprs_size() - 1; ++i) {
590 v = GetExpressionValue(int_prod.exprs(i));
591 if (!v.has_value()) return;
592 last_prod_value *= v.value();
593 SetVarValue(prod_vars[i - 1], last_prod_value);
594 }
595}
596
598 const LinearArgumentProto& lin_max,
599 absl::Span<const int> enforcement_lits) {
600 if (!solution_is_loaded_) return;
601 DCHECK_EQ(enforcement_lits.size(), lin_max.exprs_size());
602 const std::optional<int64_t> target_value =
603 GetExpressionValue(lin_max.target());
604 if (!target_value.has_value()) return;
605 int enforcement_value_sum = 0;
606 for (int i = 0; i < enforcement_lits.size(); ++i) {
607 const std::optional<int64_t> expr_value =
608 GetExpressionValue(lin_max.exprs(i));
609 if (!expr_value.has_value()) return;
610 if (enforcement_value_sum == 0) {
611 const bool enforcement_value = target_value.value() <= expr_value.value();
612 SetLiteralValue(enforcement_lits[i], enforcement_value);
613 enforcement_value_sum += enforcement_value;
614 } else {
615 SetLiteralValue(enforcement_lits[i], false);
616 }
617 }
618}
619
621 const AutomatonConstraintProto& automaton,
622 absl::Span<const StateVar> state_vars,
623 absl::Span<const TransitionVar> transition_vars) {
624 if (!solution_is_loaded_) return;
625 absl::flat_hash_map<std::pair<int64_t, int64_t>, int64_t> transitions;
626 for (int i = 0; i < automaton.transition_tail_size(); ++i) {
627 transitions[{automaton.transition_tail(i), automaton.transition_label(i)}] =
628 automaton.transition_head(i);
629 }
630
631 std::vector<int64_t> label_values;
632 std::vector<int64_t> state_values;
633 int64_t current_state = automaton.starting_state();
634 state_values.push_back(current_state);
635 for (int i = 0; i < automaton.exprs_size(); ++i) {
636 const std::optional<int64_t> label_value =
637 GetExpressionValue(automaton.exprs(i));
638 if (!label_value.has_value()) return;
639 label_values.push_back(label_value.value());
640
641 const auto it = transitions.find({current_state, label_value.value()});
642 if (it == transitions.end()) return;
643 current_state = it->second;
644 state_values.push_back(current_state);
645 }
646
647 for (const auto& [var, time, state] : state_vars) {
648 SetVarValue(var, state_values[time] == state);
649 }
650 for (const auto& [var, time, transition_tail, transition_label] :
651 transition_vars) {
652 SetVarValue(var, state_values[time] == transition_tail &&
653 label_values[time] == transition_label);
654 }
655}
656
658 absl::Span<const int> column_vars, absl::Span<const int> existing_row_lits,
659 absl::Span<const TableRowLiteral> new_row_lits) {
660 if (!solution_is_loaded_) return;
661 int row_lit_values_sum = 0;
662 for (const int lit : existing_row_lits) {
663 if (!HasValue(PositiveRef(lit))) return;
664 row_lit_values_sum += GetLiteralValue(lit);
665 }
666 const int num_vars = column_vars.size();
667 for (const auto& [lit, var_values] : new_row_lits) {
668 if (row_lit_values_sum >= 1) {
669 SetLiteralValue(lit, false);
670 continue;
671 }
672 bool row_lit_value = true;
673 for (int var_index = 0; var_index < num_vars; ++var_index) {
674 const auto& values = var_values[var_index];
675 if (!values.empty() &&
676 absl::c_find(values, GetVarValue(column_vars[var_index])) ==
677 values.end()) {
678 row_lit_value = false;
679 break;
680 }
681 }
682 SetLiteralValue(lit, row_lit_value);
683 row_lit_values_sum += row_lit_value;
684 }
685}
686
688 const LinearConstraintProto& linear, absl::Span<const int> bucket_lits) {
689 if (!solution_is_loaded_) return;
690 int64_t expr_value = 0;
691 for (int i = 0; i < linear.vars_size(); ++i) {
692 const int var = linear.vars(i);
693 if (!HasValue(var)) return;
694 expr_value += linear.coeffs(i) * GetVarValue(var);
695 }
696 DCHECK_LE(bucket_lits.size(), linear.domain_size() / 2);
697 for (int i = 0; i < bucket_lits.size(); ++i) {
698 const int64_t lb = linear.domain(2 * i);
699 const int64_t ub = linear.domain(2 * i + 1);
700 SetLiteralValue(bucket_lits[i], expr_value >= lb && expr_value <= ub);
701 }
702}
703
705 if (!solution_is_loaded_) return;
706 model.clear_solution_hint();
707 for (int i = 0; i < var_values_.size(); ++i) {
708 if (var_has_value_[i]) {
710 model.mutable_solution_hint()->add_values(var_values_[i]);
711 }
712 }
713}
714
715void SolutionCrush::PermuteVariables(const SparsePermutation& permutation) {
716 CHECK(solution_is_loaded_);
717 permutation.ApplyToDenseCollection(var_has_value_);
718 permutation.ApplyToDenseCollection(var_values_);
719}
720
722 const CompactVectorVector<int, Rectangle>& areas, const CpModelProto& model,
723 absl::Span<const int> x_intervals, absl::Span<const int> y_intervals,
724 absl::Span<const BoxInAreaLiteral> box_in_area_lits) {
725 if (!solution_is_loaded_) return;
726 struct RectangleTypeAndIndex {
727 enum class Type {
728 kHintedBox,
729 kArea,
730 };
731
732 int index;
733 Type type;
734 };
735 std::vector<Rectangle> rectangles_for_intersections;
736 std::vector<RectangleTypeAndIndex> rectangles_index;
737
738 for (int i = 0; i < x_intervals.size(); ++i) {
739 const ConstraintProto& x_ct = model.constraints(x_intervals[i]);
740 const ConstraintProto& y_ct = model.constraints(y_intervals[i]);
741
742 const std::optional<int64_t> x_min =
743 GetExpressionValue(x_ct.interval().start());
744 const std::optional<int64_t> x_max =
745 GetExpressionValue(x_ct.interval().end());
746 const std::optional<int64_t> y_min =
747 GetExpressionValue(y_ct.interval().start());
748 const std::optional<int64_t> y_max =
749 GetExpressionValue(y_ct.interval().end());
750
751 if (!x_min.has_value() || !x_max.has_value() || !y_min.has_value() ||
752 !y_max.has_value()) {
753 return;
754 }
755 if (*x_min > *x_max || *y_min > *y_max) {
756 VLOG(2) << "Hinted no_overlap_2d coordinate has max lower than min";
757 return;
758 }
759 const Rectangle box = {.x_min = x_min.value(),
760 .x_max = x_max.value(),
761 .y_min = y_min.value(),
762 .y_max = y_max.value()};
763 rectangles_for_intersections.push_back(box);
764 rectangles_index.push_back(
765 {.index = i, .type = RectangleTypeAndIndex::Type::kHintedBox});
766 }
767
768 for (int i = 0; i < areas.size(); ++i) {
769 for (const Rectangle& area : areas[i]) {
770 rectangles_for_intersections.push_back(area);
771 rectangles_index.push_back(
772 {.index = i, .type = RectangleTypeAndIndex::Type::kArea});
773 }
774 }
775
776 const std::vector<std::pair<int, int>> intersections =
777 FindPartialRectangleIntersections(rectangles_for_intersections);
778
779 absl::flat_hash_set<std::pair<int, int>> box_to_area_pairs;
780
781 for (const auto& [rec1_index, rec2_index] : intersections) {
782 RectangleTypeAndIndex rec1 = rectangles_index[rec1_index];
783 RectangleTypeAndIndex rec2 = rectangles_index[rec2_index];
784 if (rec1.type == rec2.type) {
785 DCHECK(rec1.type == RectangleTypeAndIndex::Type::kHintedBox);
786 VLOG(2) << "Hinted position of boxes in no_overlap_2d are overlapping";
787 return;
788 }
789 if (rec1.type != RectangleTypeAndIndex::Type::kHintedBox) {
790 std::swap(rec1, rec2);
791 }
792
793 box_to_area_pairs.insert({rec1.index, rec2.index});
794 }
795
796 for (const auto& [box_index, area_index, literal] : box_in_area_lits) {
797 SetLiteralValue(literal,
798 box_to_area_pairs.contains({box_index, area_index}));
799 }
800}
801
802} // namespace sat
803} // namespace operations_research
int64_t ValueAtOrAfter(int64_t input) const
bool Contains(int64_t value) const
int64_t ValueAtOrBefore(int64_t input) const
int64_t ClosestValue(int64_t wanted) const
void ApplyToDenseCollection(Collection &span) const
const ::operations_research::sat::LinearExpressionProto & exprs(int index) const
::int32_t enforcement_literal(int index) const
const ::operations_research::sat::IntervalConstraintProto & interval() const
const ::operations_research::sat::LinearArgumentProto & int_mod() const
const ::operations_research::sat::ConstraintProto & constraints(int index) const
::operations_research::sat::PartialVariableAssignment *PROTOBUF_NONNULL mutable_solution_hint()
const ::operations_research::sat::LinearExpressionProto & end() const
const ::operations_research::sat::LinearExpressionProto & start() const
const ::operations_research::sat::LinearExpressionProto & exprs(int index) const
const ::operations_research::sat::LinearExpressionProto & target() const
const ::operations_research::sat::LinearExpressionProto & time_exprs(int index) const
const ::operations_research::sat::LinearExpressionProto & level_changes(int index) const
void LoadSolution(int num_vars, const absl::flat_hash_map< int, int64_t > &solution)
void SetIntModExpandedVars(const ConstraintProto &ct, int div_var, int prod_var, int64_t default_div_value, int64_t default_prod_value)
void MaybeSwapOrbitopeColumns(absl::Span< const std::vector< int > > orbitope, int row, int pivot_col, bool value)
void SetLinearWithComplexDomainExpandedVars(const LinearConstraintProto &linear, absl::Span< const int > bucket_lits)
void StoreSolutionAsHint(CpModelProto &model) const
void SetLinMaxExpandedVars(const LinearArgumentProto &lin_max, absl::Span< const int > enforcement_lits)
void SetVarToConjunction(int var, absl::Span< const int > conjunction)
void SetReservoirCircuitVars(const ReservoirConstraintProto &reservoir, int64_t min_level, int64_t max_level, absl::Span< const int > level_vars, const CircuitConstraintProto &circuit)
void SetVarToValueIf(int var, int64_t value, int condition_lit)
void AssignVariableToPackingArea(const CompactVectorVector< int, Rectangle > &areas, const CpModelProto &model, absl::Span< const int > x_intervals, absl::Span< const int > y_intervals, absl::Span< const BoxInAreaLiteral > box_in_area_lits)
void SetAutomatonExpandedVars(const AutomatonConstraintProto &automaton, absl::Span< const StateVar > state_vars, absl::Span< const TransitionVar > transition_vars)
void SetVarToLinearConstraintSolution(absl::Span< const int > enforcement_lits, std::optional< int > var_index, absl::Span< const int > vars, absl::Span< const int64_t > default_values, absl::Span< const int64_t > coeffs, int64_t rhs)
void SetVarToValueIfLinearConstraintViolated(int var, int64_t value, absl::Span< const std::pair< int, int64_t > > linear, const Domain &domain)
void SetVarToReifiedPrecedenceLiteral(int var, const LinearExpressionProto &time_i, const LinearExpressionProto &time_j, int active_i, int active_j)
void MakeLiteralsEqual(int lit1, int lit2)
void UpdateLiteralsWithDominance(int lit, int dominating_lit)
void SetLiteralToValueIfLinearConstraintViolated(int literal, bool value, absl::Span< const std::pair< int, int64_t > > linear, const Domain &domain)
void UpdateRefsWithDominance(int ref, int64_t min_value, int64_t max_value, absl::Span< const std::pair< int, Domain > > dominating_refs)
void SetIntProdExpandedVars(const LinearArgumentProto &int_prod, absl::Span< const int > prod_vars)
void SetVarToLinearExpressionIf(int var, const LinearExpressionProto &expr, int condition_lit)
void MaybeSetLiteralToValueEncoding(int literal, int var, int64_t value)
void MaybeSetLiteralToOrderEncoding(int literal, int var, int64_t value, bool is_le)
void SetOrUpdateVarToDomain(int var, const Domain &domain)
void MaybeUpdateVarWithSymmetriesToValue(int var, bool value, absl::Span< const std::unique_ptr< SparsePermutation > > generators)
void SetVarToLinearExpression(int var, absl::Span< const std::pair< int, int64_t > > linear, int64_t offset=0)
void UpdateLiteralsToFalseIfDifferent(int lit1, int lit2)
void SetLiteralToValueIf(int literal, bool value, int condition_lit)
void SetOrUpdateVarToDomainWithOptionalEscapeValue(int var, const Domain &reduced_var_domain, std::optional< int64_t > unique_escape_value, bool push_down_when_not_in_domain, const absl::btree_map< int64_t, int > &encoding)
void SetTableExpandedVars(absl::Span< const int > column_vars, absl::Span< const int > existing_row_lits, absl::Span< const TableRowLiteral > new_row_lits)
void SetVarToConditionalValue(int var, absl::Span< const int > condition_lits, int64_t value_if_true, int64_t value_if_false)
void SetVarToClause(int var, absl::Span< const int > clause)
void GetSchreierVectorAndOrbit(int point, absl::Span< const std::unique_ptr< SparsePermutation > > generators, std::vector< int > *schrier_vector, std::vector< int > *orbit)
std::vector< int > TracePoint(int point, absl::Span< const int > schrier_vector, absl::Span< const std::unique_ptr< SparsePermutation > > generators)
std::vector< std::pair< int, int > > FindPartialRectangleIntersections(absl::Span< const Rectangle > rectangles)
OR-Tools root namespace.
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid