Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
cp_model_postsolve.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 <limits>
19#include <vector>
20
21#include "absl/base/log_severity.h"
22#include "absl/log/check.h"
23#include "absl/types/span.h"
30
31namespace operations_research {
32namespace sat {
33
34// This postsolve is "special". If the clause is not satisfied, we fix the
35// first literal in the clause to true (even if it was fixed to false). This
36// allows to handle more complex presolve operations used by the SAT presolver.
37//
38// Also, any "free" Boolean should be fixed to some value for the subsequent
39// postsolve steps.
40void PostsolveClause(const ConstraintProto& ct, std::vector<Domain>* domains) {
41 const int size = ct.bool_or().literals_size();
42 CHECK_NE(size, 0);
43 bool satisfied = false;
44 for (int i = 0; i < size; ++i) {
45 const int ref = ct.bool_or().literals(i);
46 const int var = PositiveRef(ref);
47 if ((*domains)[var].IsFixed()) {
48 if ((*domains)[var].FixedValue() == (RefIsPositive(ref) ? 1 : 0)) {
49 satisfied = true;
50 }
51 } else {
52 // We still need to assign free variable.
53 //
54 // It is important to set its value so that the literal in the clause is
55 // false, so that we support the "filter_sat_postsolve_clauses" option and
56 // we use a bit less memory for postsolve clauses.
57 (*domains)[PositiveRef(ref)] = Domain(RefIsPositive(ref) ? 0 : 1);
58 }
59 }
60 if (satisfied) return;
61
62 // Change the value of the first variable (which was chosen at presolve).
63 const int first_ref = ct.bool_or().literals(0);
64 (*domains)[PositiveRef(first_ref)] = Domain(RefIsPositive(first_ref) ? 1 : 0);
65}
66
68 std::vector<Domain>* domains) {
69 bool satisfied = false;
70 std::vector<int> free_variables;
71 for (const int ref : ct.exactly_one().literals()) {
72 const int var = PositiveRef(ref);
73 if ((*domains)[var].IsFixed()) {
74 if ((*domains)[var].FixedValue() == (RefIsPositive(ref) ? 1 : 0)) {
75 CHECK(!satisfied) << "Two variables at one in exactly one.";
76 satisfied = true;
77 }
78 } else {
79 free_variables.push_back(ref);
80 }
81 }
82 if (!satisfied) {
83 // Fix one at true.
84 CHECK(!free_variables.empty()) << "All zero in exactly one";
85 const int ref = free_variables.back();
86 (*domains)[PositiveRef(ref)] = Domain(RefIsPositive(ref) ? 1 : 0);
87 free_variables.pop_back();
88 }
89
90 // Fix any free variable left at false.
91 for (const int ref : free_variables) {
92 (*domains)[PositiveRef(ref)] = Domain(RefIsPositive(ref) ? 0 : 1);
93 }
94}
95
96// For now we set the first unset enforcement literal to false.
97// There must be one.
99 std::vector<Domain>* domains) {
100 CHECK(!ct.enforcement_literal().empty()) << ProtobufShortDebugString(ct);
101 bool has_free_enforcement_literal = false;
102 for (const int enf : ct.enforcement_literal()) {
103 if ((*domains)[PositiveRef(enf)].IsFixed()) continue;
104 has_free_enforcement_literal = true;
105 if (RefIsPositive(enf)) {
106 (*domains)[enf] = Domain(0);
107 } else {
108 (*domains)[PositiveRef(enf)] = Domain(1);
109 }
110 break;
111 }
112 if (!has_free_enforcement_literal) {
113 LOG(FATAL)
114 << "Unsatisfied linear constraint with no free enforcement literal: "
116 }
117}
118
119// Here we simply assign all non-fixed variable to a feasible value. Which
120// should always exists by construction.
121void PostsolveLinear(const ConstraintProto& ct, std::vector<Domain>* domains) {
122 int64_t fixed_activity = 0;
123 const int size = ct.linear().vars().size();
124 std::vector<int> free_vars;
125 std::vector<int64_t> free_coeffs;
126 for (int i = 0; i < size; ++i) {
127 const int var = ct.linear().vars(i);
128 const int64_t coeff = ct.linear().coeffs(i);
129 CHECK_LT(var, domains->size());
130 if (coeff == 0) continue;
131 if ((*domains)[var].IsFixed()) {
132 fixed_activity += (*domains)[var].FixedValue() * coeff;
133 } else {
134 free_vars.push_back(var);
135 free_coeffs.push_back(coeff);
136 }
137 }
138
139 // Tricky: We sometime push two constraints for postsolve:
140 // 1/ l => A
141 // 2/ not(l) => B
142 // if B is true, it is better to fix `l` so that the constraint 2/ is
143 // enforced. This way we should have no problem when processing 1/
144 //
145 // TODO(user): This is a bit hacky, if we need to postsolve both
146 // constraints at once, it might be cleaner to do that in a single
147 // postsolve operation. However this allows us to reuse normal constraints
148 // for the postsolve specification, which is nice.
149 auto fix_unassigned_enforcement_to_true = [&](const ConstraintProto& ct) {
150 for (const int enf : ct.enforcement_literal()) {
151 Domain& d = (*domains)[PositiveRef(enf)];
152 if (!d.IsFixed()) {
153 d = Domain(RefIsPositive(enf) ? 1 : 0);
154 }
155 }
156 };
157
158 if (free_vars.empty()) {
159 const Domain rhs = ReadDomainFromProto(ct.linear());
160 if (!rhs.Contains(fixed_activity)) {
161 SetEnforcementLiteralToFalse(ct, domains);
162 } else {
163 fix_unassigned_enforcement_to_true(ct);
164 }
165 return;
166 }
167
168 // Fast track for the most common case.
169 const Domain initial_rhs = ReadDomainFromProto(ct.linear());
170 if (free_vars.size() == 1) {
171 const int var = free_vars[0];
172 const Domain domain = initial_rhs.AdditionWith(Domain(-fixed_activity))
173 .InverseMultiplicationBy(free_coeffs[0])
174 .IntersectionWith((*domains)[var]);
175 if (domain.IsEmpty()) {
176 SetEnforcementLiteralToFalse(ct, domains);
177 return;
178 }
179 fix_unassigned_enforcement_to_true(ct);
180 (*domains)[var] = Domain(domain.SmallestValue());
181 return;
182 }
183
184 // The postsolve code is a bit involved if there is more than one free
185 // variable, we have to postsolve them one by one.
186 //
187 // Here we recompute the same domains as during the presolve. Everything is
188 // like if we where substituting the variable one by one:
189 // terms[i] + fixed_activity \in rhs_domains[i]
190 // In the reverse order.
191 std::vector<Domain> rhs_domains;
192 rhs_domains.push_back(initial_rhs);
193 for (int i = 0; i + 1 < free_vars.size(); ++i) {
194 // Note that these should be exactly the same computation as the one done
195 // during presolve and should be exact. However, we have some tests that do
196 // not comply, so we don't check exactness here. Also, as long as we don't
197 // get empty domain below, and the complexity of the domain do not explode
198 // here, we should be fine.
199 Domain term = (*domains)[free_vars[i]].MultiplicationBy(-free_coeffs[i]);
200 rhs_domains.push_back(term.AdditionWith(rhs_domains.back()));
201 }
202 std::vector<int64_t> values(free_vars.size());
203 for (int i = free_vars.size() - 1; i >= 0; --i) {
204 // Choose a value for free_vars[i] that fall into rhs_domains[i] -
205 // fixed_activity. This will crash if the intersection is empty, but it
206 // shouldn't be.
207 const int var = free_vars[i];
208 const int64_t coeff = free_coeffs[i];
209 const Domain domain = rhs_domains[i]
210 .AdditionWith(Domain(-fixed_activity))
211 .InverseMultiplicationBy(coeff)
212 .IntersectionWith((*domains)[var]);
213
214 // TODO(user): I am not 100% that the algo here might cover all the presolve
215 // case, so if this fail, it might indicate an issue here and not in the
216 // presolve/solver code.
217 if (domain.IsEmpty()) {
218 LOG(INFO) << "Empty domain while trying to assign " << var;
219 for (int i = 0; i < size; ++i) {
220 const int var = ct.linear().vars(i);
221 LOG(INFO) << var << " " << (*domains)[var];
222 }
223 LOG(FATAL) << "Couldn't postsolve the constraint: "
225 }
226
227 CHECK(!domain.IsEmpty()) << ProtobufShortDebugString(ct);
228 const int64_t value = domain.SmallestValue();
229 values[i] = value;
230 fixed_activity += coeff * value;
231 }
232
233 // We assign that afterwards for better debugging if we run into the domains
234 // empty above.
235 for (int i = 0; i < free_vars.size(); ++i) {
236 (*domains)[free_vars[i]] = Domain(values[i]);
237 }
238
239 DCHECK(initial_rhs.Contains(fixed_activity));
240 fix_unassigned_enforcement_to_true(ct);
241}
242
243namespace {
244
245// Note that if a domain is not fixed, we just take its Min() value.
246int64_t EvaluateLinearExpression(const LinearExpressionProto& expr,
247 absl::Span<const Domain> domains) {
248 int64_t value = expr.offset();
249 for (int i = 0; i < expr.vars_size(); ++i) {
250 const int ref = expr.vars(i);
251 const int64_t increment =
252 domains[PositiveRef(expr.vars(i))].Min() * expr.coeffs(i);
253 value += RefIsPositive(ref) ? increment : -increment;
254 }
255 return value;
256}
257
258bool LinearExpressionIsFixed(const LinearExpressionProto& expr,
259 absl::Span<const Domain> domains) {
260 for (const int var : expr.vars()) {
261 if (!domains[var].IsFixed()) return false;
262 }
263 return true;
264}
265
266} // namespace
267
268// Compute the max of each expression, and assign it to the target expr. We only
269// support post-solving the case where whatever the value of all expression,
270// there will be a valid target.
271void PostsolveLinMax(const ConstraintProto& ct, std::vector<Domain>* domains) {
272 int64_t max_value = std::numeric_limits<int64_t>::min();
273 for (const LinearExpressionProto& expr : ct.lin_max().exprs()) {
274 // In most case all expression are fixed, except in the corner case where
275 // one of the expression refer to the target itself !
276 max_value = std::max(max_value, EvaluateLinearExpression(expr, *domains));
277 }
278
279 const LinearExpressionProto& target = ct.lin_max().target();
280 CHECK_EQ(target.vars().size(), 1);
281 CHECK(RefIsPositive(target.vars(0)));
282
283 max_value -= target.offset();
284 CHECK_EQ(max_value % target.coeffs(0), 0);
285 max_value /= target.coeffs(0);
286 (*domains)[target.vars(0)] = Domain(max_value);
287}
288
289// We only support 2 cases: either the index was removed, of the target, not
290// both.
291void PostsolveElement(const ConstraintProto& ct, std::vector<Domain>* domains) {
292 const LinearExpressionProto& index = ct.element().linear_index();
293 const LinearExpressionProto& target = ct.element().linear_target();
294
295 DCHECK(LinearExpressionIsFixed(index, *domains) ||
296 LinearExpressionIsFixed(target, *domains));
297
298 // Deal with fixed index.
299 if (LinearExpressionIsFixed(index, *domains)) {
300 const int64_t index_value = EvaluateLinearExpression(index, *domains);
301 const LinearExpressionProto& expr = ct.element().exprs(index_value);
302 DCHECK(LinearExpressionIsFixed(expr, *domains));
303 const int64_t expr_value = EvaluateLinearExpression(expr, *domains);
304 if (target.vars().empty()) {
305 DCHECK_EQ(expr_value, target.offset());
306 } else {
307 (*domains)[target.vars(0)] = Domain(GetInnerVarValue(target, expr_value));
308 }
309 return;
310 }
311
312 // Deal with fixed target (and constant vars).
313 const int64_t target_value = EvaluateLinearExpression(target, *domains);
314 int selected_index_value = -1;
315 for (const int64_t v : (*domains)[index.vars(0)].Values()) {
316 const int64_t index_value = index.offset() + v * index.coeffs(0);
317 DCHECK_GE(index_value, 0);
318 DCHECK_LT(index_value, ct.element().exprs_size());
319
320 const LinearExpressionProto& expr = ct.element().exprs(index_value);
321 const int64_t value = EvaluateLinearExpression(expr, *domains);
322 if (value == target_value) {
323 selected_index_value = index_value;
324 break;
325 }
326 }
327
328 CHECK_NE(selected_index_value, -1);
329 (*domains)[index.vars(0)] =
330 Domain(GetInnerVarValue(index, selected_index_value));
331}
332
333// We only support assigning to an affine target.
334void PostsolveIntMod(const ConstraintProto& ct, std::vector<Domain>* domains) {
335 const int64_t exp = EvaluateLinearExpression(ct.int_mod().exprs(0), *domains);
336 const int64_t mod = EvaluateLinearExpression(ct.int_mod().exprs(1), *domains);
337 CHECK_NE(mod, 0);
338 const int64_t target_value = exp % mod;
339
340 const LinearExpressionProto& target = ct.int_mod().target();
341 CHECK_EQ(target.vars().size(), 1);
342 const int64_t term_value = target_value - target.offset();
343 CHECK_EQ(term_value % target.coeffs(0), 0);
344 const int64_t value = term_value / target.coeffs(0);
345 CHECK((*domains)[target.vars(0)].Contains(value));
346 (*domains)[target.vars(0)] = Domain(value);
347}
348
349// We only support assigning to an affine target.
350void PostsolveIntProd(const ConstraintProto& ct, std::vector<Domain>* domains) {
351 int64_t target_value = 1;
352 for (const LinearExpressionProto& expr : ct.int_prod().exprs()) {
353 target_value *= EvaluateLinearExpression(expr, *domains);
354 }
355
356 const LinearExpressionProto& target = ct.int_prod().target();
357 CHECK_EQ(target.vars().size(), 1);
358 CHECK(RefIsPositive(target.vars(0)));
359
360 target_value -= target.offset();
361 CHECK_EQ(target_value % target.coeffs(0), 0);
362 target_value /= target.coeffs(0);
363 (*domains)[target.vars(0)] = Domain(target_value);
364}
365
366namespace {
367void CheckPostsolveFixedVariables(const ConstraintProto& ct,
368 absl::Span<const Domain> domains) {
369 if (DEBUG_MODE) {
370 for (const int var : UsedVariables(ct)) {
371 DCHECK(domains[PositiveRef(var)].IsFixed())
372 << "Variable " << PositiveRef(var)
373 << " is not fixed after postsolve for constraint: \""
375 << "\" with domain: " << domains[PositiveRef(var)] << ".";
376 }
377 }
378}
379
380void ArbitrarilyFixVariables(const ConstraintProto& ct,
381 absl::Span<Domain> domains) {
382 for (const int var : UsedVariables(ct)) {
383 if (!domains[PositiveRef(var)].IsFixed()) {
384 domains[PositiveRef(var)] = Domain(domains[PositiveRef(var)].Min());
385 }
386 }
387}
388} // namespace
389
390void PostsolveResponse(const int64_t num_variables_in_original_model,
391 const CpModelProto& mapping_proto,
392 absl::Span<const int> postsolve_mapping,
393 std::vector<int64_t>* solution) {
394 CHECK_EQ(solution->size(), postsolve_mapping.size());
395
396 // Read the initial variable domains, either from the fixed solution of the
397 // presolved problems or from the mapping model.
398 std::vector<Domain> domains(mapping_proto.variables_size());
399 for (int i = 0; i < postsolve_mapping.size(); ++i) {
400 CHECK_LE(postsolve_mapping[i], domains.size());
401 domains[postsolve_mapping[i]] = Domain((*solution)[i]);
402 }
403 for (int i = 0; i < domains.size(); ++i) {
404 if (domains[i].IsEmpty()) {
405 domains[i] = ReadDomainFromProto(mapping_proto.variables(i));
406 }
407 CHECK(!domains[i].IsEmpty());
408 }
409
410 // Process the constraints in reverse order.
411 const int num_constraints = mapping_proto.constraints_size();
412 for (int i = num_constraints - 1; i >= 0; i--) {
413 const ConstraintProto& ct = mapping_proto.constraints(i);
414
415 // We ignore constraint with an enforcement literal set to false. If the
416 // enforcement is still unclear, we still process this constraint.
417 bool constraint_can_be_ignored = false;
418 for (const int enf : ct.enforcement_literal()) {
419 const int var = PositiveRef(enf);
420 const bool is_false =
421 domains[var].IsFixed() &&
422 RefIsPositive(enf) == (domains[var].FixedValue() == 0);
423 if (is_false) {
424 constraint_can_be_ignored = true;
425 break;
426 }
427 }
428 if (constraint_can_be_ignored) {
429 ArbitrarilyFixVariables(ct, absl::MakeSpan(domains));
430 CheckPostsolveFixedVariables(ct, domains);
431 continue;
432 }
433
434 switch (ct.constraint_case()) {
436 PostsolveClause(ct, &domains);
437 break;
439 PostsolveExactlyOne(ct, &domains);
440 break;
442 PostsolveLinear(ct, &domains);
443 break;
445 PostsolveLinMax(ct, &domains);
446 break;
448 PostsolveElement(ct, &domains);
449 break;
451 PostsolveIntMod(ct, &domains);
452 break;
454 PostsolveIntProd(ct, &domains);
455 break;
456 default:
457 // This should never happen as we control what kind of constraint we
458 // add to the mapping_proto;
459 LOG(FATAL) << "Unsupported constraint: "
461 }
462 CheckPostsolveFixedVariables(ct, domains);
463 }
464
465 // Fill the response.
466 // Maybe fix some still unfixed variable.
467 solution->clear();
468 CHECK_LE(num_variables_in_original_model, domains.size());
469 for (int i = 0; i < num_variables_in_original_model; ++i) {
470 solution->push_back(domains[i].SmallestValue());
471 }
472}
473
475 const CpModelProto& mapping_proto,
476 absl::Span<const int> postsolve_mapping,
477 absl::Span<const Domain> search_domains,
478 CpSolverResponse* response,
479 SolverLogger* logger) {
480 // The [0, num_vars) part will contain the tightened domains.
481 const int num_original_vars = original_model.variables().size();
482 const int num_expanded_vars = mapping_proto.variables().size();
483 CHECK_LE(num_original_vars, num_expanded_vars);
484 std::vector<Domain> domains(num_expanded_vars);
485
486 // Start with the domain from the mapping proto. Note that by construction
487 // this should be tighter than the original variable domains.
488 for (int i = 0; i < num_expanded_vars; ++i) {
489 domains[i] = ReadDomainFromProto(mapping_proto.variables(i));
490 if (i < num_original_vars) {
491 CHECK(domains[i].IsIncludedIn(
492 ReadDomainFromProto(original_model.variables(i))));
493 }
494 }
495
496 // The first test is for the corner case of presolve closing the problem,
497 // in which case there is no more info to process.
498 int num_common_vars = 0;
499 int num_affine_reductions = 0;
500 if (!search_domains.empty()) {
501 if (postsolve_mapping.empty()) {
502 // Currently no mapping should mean all variables are in common. This
503 // happen when presolve is disabled, but we might still have more
504 // variables due to expansion for instance.
505 //
506 // There is also the corner case of presolve closing the problem,
507 CHECK_GE(search_domains.size(), num_original_vars);
508 num_common_vars = num_original_vars;
509 for (int i = 0; i < num_original_vars; ++i) {
510 domains[i] = domains[i].IntersectionWith(search_domains[i]);
511 }
512 } else {
513 // This is the normal presolve case.
514 // Intersect the domain of the variables in common.
515 CHECK_EQ(postsolve_mapping.size(), search_domains.size());
516 for (int search_i = 0; search_i < postsolve_mapping.size(); ++search_i) {
517 const int i_in_mapping_model = postsolve_mapping[search_i];
518 if (i_in_mapping_model < num_original_vars) {
519 ++num_common_vars;
520 }
521 domains[i_in_mapping_model] =
522 domains[i_in_mapping_model].IntersectionWith(
523 search_domains[search_i]);
524 }
525
526 // Look for affine relation, and do more intersection.
527 for (const ConstraintProto& ct : mapping_proto.constraints()) {
528 if (ct.constraint_case() != ConstraintProto::kLinear) continue;
529 const LinearConstraintProto& lin = ct.linear();
530 if (lin.vars().size() != 2) continue;
531 if (lin.domain().size() != 2) continue;
532 if (lin.domain(0) != lin.domain(1)) continue;
533 int v1 = lin.vars(0);
534 int v2 = lin.vars(1);
535 int c1 = lin.coeffs(0);
536 int c2 = lin.coeffs(1);
537 if (v2 < num_original_vars && v1 >= num_original_vars) {
538 std::swap(v1, v2);
539 std::swap(c1, c2);
540 }
541 if (v1 < num_original_vars && v2 >= num_original_vars) {
542 // We can reduce the domain of v1 by using the affine relation
543 // and the domain of v2.
544 // We have c1 * v2 + c2 * v2 = offset;
545 const int64_t offset = lin.domain(0);
546 const Domain restriction =
547 Domain(offset)
548 .AdditionWith(domains[v2].ContinuousMultiplicationBy(-c2))
550 if (!domains[v1].IsIncludedIn(restriction)) {
551 ++num_affine_reductions;
552 domains[v1] = domains[v1].IntersectionWith(restriction);
553 }
554 }
555 }
556 }
557 }
558
559 // Copy the names and replace domains.
560 *response->mutable_tightened_variables() = original_model.variables();
561 int num_tigher_domains = 0;
562 int num_empty = 0;
563 int num_fixed = 0;
564 for (int i = 0; i < num_original_vars; ++i) {
565 FillDomainInProto(domains[i], response->mutable_tightened_variables(i));
566 if (domains[i].IsEmpty()) {
567 ++num_empty;
568 continue;
569 }
570
571 if (domains[i].IsFixed()) num_fixed++;
572 const Domain original = ReadDomainFromProto(original_model.variables(i));
573 if (domains[i] != original) {
574 DCHECK(domains[i].IsIncludedIn(original));
575 ++num_tigher_domains;
576 }
577 }
578
579 // Some stats.
580 if (num_empty > 0) {
581 SOLVER_LOG(logger, num_empty,
582 " tightened domains are empty. This should not happen except if "
583 "we proven infeasibility or optimality.");
584 }
585 SOLVER_LOG(logger, "Filled tightened domains in the response.");
586 SOLVER_LOG(logger, "[TighteningInfo] num_tighter:", num_tigher_domains,
587 " num_fixed:", num_fixed,
588 " num_affine_reductions:", num_affine_reductions);
589 SOLVER_LOG(logger,
590 "[TighteningInfo] original_num_variables:", num_original_vars,
591 " during_presolve:", num_expanded_vars,
592 " after:", search_domains.size(), " in_common:", num_common_vars);
593 SOLVER_LOG(logger, "");
594}
595
596} // namespace sat
597} // namespace operations_research
Domain MultiplicationBy(int64_t coeff, bool *exact=nullptr) const
Domain IntersectionWith(const Domain &domain) const
bool Contains(int64_t value) const
Domain AdditionWith(const Domain &domain) const
Domain InverseMultiplicationBy(int64_t coeff) const
::int32_t enforcement_literal(int index) const
const ::operations_research::sat::BoolArgumentProto & exactly_one() const
const ::operations_research::sat::LinearConstraintProto & linear() const
const ::operations_research::sat::LinearArgumentProto & lin_max() const
const ::operations_research::sat::ElementConstraintProto & element() const
const ::operations_research::sat::BoolArgumentProto & bool_or() const
const ::operations_research::sat::LinearArgumentProto & int_mod() const
const ::operations_research::sat::LinearArgumentProto & int_prod() const
const ::operations_research::sat::IntegerVariableProto & variables(int index) const
const ::operations_research::sat::ConstraintProto & constraints(int index) const
::operations_research::sat::IntegerVariableProto *PROTOBUF_NONNULL mutable_tightened_variables(int index)
const ::operations_research::sat::LinearExpressionProto & exprs(int index) const
const ::operations_research::sat::LinearExpressionProto & linear_target() const
const ::operations_research::sat::LinearExpressionProto & linear_index() const
const ::operations_research::sat::LinearExpressionProto & exprs(int index) const
const ::operations_research::sat::LinearExpressionProto & target() const
void PostsolveElement(const ConstraintProto &ct, std::vector< Domain > *domains)
void PostsolveLinear(const ConstraintProto &ct, std::vector< Domain > *domains)
std::function< bool(const Model &)> IsFixed(IntegerVariable v)
Definition integer.h:1831
int64_t GetInnerVarValue(const LinearExpressionProto &expr, int64_t value)
void PostsolveExactlyOne(const ConstraintProto &ct, std::vector< Domain > *domains)
void SetEnforcementLiteralToFalse(const ConstraintProto &ct, std::vector< Domain > *domains)
void PostsolveIntProd(const ConstraintProto &ct, std::vector< Domain > *domains)
std::vector< int > UsedVariables(const ConstraintProto &ct)
void PostsolveLinMax(const ConstraintProto &ct, std::vector< Domain > *domains)
void FillDomainInProto(const Domain &domain, ProtoWithDomain *proto)
void PostsolveIntMod(const ConstraintProto &ct, std::vector< Domain > *domains)
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
void PostsolveClause(const ConstraintProto &ct, std::vector< Domain > *domains)
void PostsolveResponse(const int64_t num_variables_in_original_model, const CpModelProto &mapping_proto, absl::Span< const int > postsolve_mapping, std::vector< int64_t > *solution)
void FillTightenedDomainInResponse(const CpModelProto &original_model, const CpModelProto &mapping_proto, absl::Span< const int > postsolve_mapping, absl::Span< const Domain > search_domains, CpSolverResponse *response, SolverLogger *logger)
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
const bool DEBUG_MODE
Definition radix_sort.h:266
std::string ProtobufShortDebugString(const P &message)
Definition proto_utils.h:46
#define SOLVER_LOG(logger,...)
Definition logging.h:114