Google OR-Tools v9.14
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());
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 if (free_vars.empty()) {
139 const Domain rhs = ReadDomainFromProto(ct.linear());
140 if (!rhs.Contains(fixed_activity)) {
141 SetEnforcementLiteralToFalse(ct, domains);
142 } else {
143 // The constraint is satisfied, if there is any enforcement that are
144 // not fixed yet, we need to fix them.
145 //
146 // Tricky: We sometime push two constraints for postsolve:
147 // 1/ l => A
148 // 2/ not(l) => B
149 // if B is true, it is better to fix `l` so that the constraint 2/ is
150 // enforced. This way we should have no problem when processing 1/
151 //
152 // TODO(user): This is a bit hacky, if we need to postsolve both
153 // constraints at once, it might be cleaner to do that in a single
154 // postsolve operation. However this allows us to reuse normal constraints
155 // for the postsolve specification, which is nice.
156 for (const int enf : ct.enforcement_literal()) {
157 Domain& d = (*domains)[PositiveRef(enf)];
158 if (!d.IsFixed()) {
159 d = Domain(RefIsPositive(enf) ? 1 : 0);
160 }
161 }
162 }
163 return;
164 }
165
166 // Fast track for the most common case.
167 const Domain initial_rhs = ReadDomainFromProto(ct.linear());
168 if (free_vars.size() == 1) {
169 const int var = free_vars[0];
170 const Domain domain = initial_rhs.AdditionWith(Domain(-fixed_activity))
171 .InverseMultiplicationBy(free_coeffs[0])
172 .IntersectionWith((*domains)[var]);
173 if (domain.IsEmpty()) {
174 SetEnforcementLiteralToFalse(ct, domains);
175 return;
176 }
177 (*domains)[var] = Domain(domain.SmallestValue());
178 return;
179 }
180
181 // The postsolve code is a bit involved if there is more than one free
182 // variable, we have to postsolve them one by one.
183 //
184 // Here we recompute the same domains as during the presolve. Everything is
185 // like if we where substiting the variable one by one:
186 // terms[i] + fixed_activity \in rhs_domains[i]
187 // In the reverse order.
188 std::vector<Domain> rhs_domains;
189 rhs_domains.push_back(initial_rhs);
190 for (int i = 0; i + 1 < free_vars.size(); ++i) {
191 // Note that these should be exactly the same computation as the one done
192 // during presolve and should be exact. However, we have some tests that do
193 // not comply, so we don't check exactness here. Also, as long as we don't
194 // get empty domain below, and the complexity of the domain do not explode
195 // here, we should be fine.
196 Domain term = (*domains)[free_vars[i]].MultiplicationBy(-free_coeffs[i]);
197 rhs_domains.push_back(term.AdditionWith(rhs_domains.back()));
198 }
199 std::vector<int64_t> values(free_vars.size());
200 for (int i = free_vars.size() - 1; i >= 0; --i) {
201 // Choose a value for free_vars[i] that fall into rhs_domains[i] -
202 // fixed_activity. This will crash if the intersection is empty, but it
203 // shouldn't be.
204 const int var = free_vars[i];
205 const int64_t coeff = free_coeffs[i];
206 const Domain domain = rhs_domains[i]
207 .AdditionWith(Domain(-fixed_activity))
208 .InverseMultiplicationBy(coeff)
209 .IntersectionWith((*domains)[var]);
210
211 // TODO(user): I am not 100% that the algo here might cover all the presolve
212 // case, so if this fail, it might indicate an issue here and not in the
213 // presolve/solver code.
214 if (domain.IsEmpty()) {
215 LOG(INFO) << "Empty domain while trying to assign " << var;
216 for (int i = 0; i < size; ++i) {
217 const int var = ct.linear().vars(i);
218 LOG(INFO) << var << " " << (*domains)[var];
219 }
220 LOG(FATAL) << "Couldn't postsolve the constraint: "
222 }
223
224 CHECK(!domain.IsEmpty()) << ProtobufShortDebugString(ct);
225 const int64_t value = domain.SmallestValue();
226 values[i] = value;
227 fixed_activity += coeff * value;
228 }
229
230 // We assign that afterwards for better debugging if we run into the domains
231 // empty above.
232 for (int i = 0; i < free_vars.size(); ++i) {
233 (*domains)[free_vars[i]] = Domain(values[i]);
234 }
235
236 DCHECK(initial_rhs.Contains(fixed_activity));
237}
238
239namespace {
240
241// Note that if a domain is not fixed, we just take its Min() value.
242int64_t EvaluateLinearExpression(const LinearExpressionProto& expr,
243 absl::Span<const Domain> domains) {
244 int64_t value = expr.offset();
245 for (int i = 0; i < expr.vars_size(); ++i) {
246 const int ref = expr.vars(i);
247 const int64_t increment =
248 domains[PositiveRef(expr.vars(i))].Min() * expr.coeffs(i);
249 value += RefIsPositive(ref) ? increment : -increment;
250 }
251 return value;
252}
253
254bool LinearExpressionIsFixed(const LinearExpressionProto& expr,
255 absl::Span<const Domain> domains) {
256 for (const int var : expr.vars()) {
257 if (!domains[var].IsFixed()) return false;
258 }
259 return true;
260}
261
262} // namespace
263
264// Compute the max of each expression, and assign it to the target expr. We only
265// support post-solving the case where whatever the value of all expression,
266// there will be a valid target.
267void PostsolveLinMax(const ConstraintProto& ct, std::vector<Domain>* domains) {
268 int64_t max_value = std::numeric_limits<int64_t>::min();
269 for (const LinearExpressionProto& expr : ct.lin_max().exprs()) {
270 // In most case all expression are fixed, except in the corner case where
271 // one of the expression refer to the target itself !
272 max_value = std::max(max_value, EvaluateLinearExpression(expr, *domains));
273 }
274
275 const LinearExpressionProto& target = ct.lin_max().target();
276 CHECK_EQ(target.vars().size(), 1);
277 CHECK(RefIsPositive(target.vars(0)));
278
279 max_value -= target.offset();
280 CHECK_EQ(max_value % target.coeffs(0), 0);
281 max_value /= target.coeffs(0);
282 (*domains)[target.vars(0)] = Domain(max_value);
283}
284
285// We only support 2 cases: either the index was removed, of the target, not
286// both.
287void PostsolveElement(const ConstraintProto& ct, std::vector<Domain>* domains) {
288 const LinearExpressionProto& index = ct.element().linear_index();
289 const LinearExpressionProto& target = ct.element().linear_target();
290
291 DCHECK(LinearExpressionIsFixed(index, *domains) ||
292 LinearExpressionIsFixed(target, *domains));
293
294 // Deal with fixed index.
295 if (LinearExpressionIsFixed(index, *domains)) {
296 const int64_t index_value = EvaluateLinearExpression(index, *domains);
297 const LinearExpressionProto& expr = ct.element().exprs(index_value);
298 DCHECK(LinearExpressionIsFixed(expr, *domains));
299 const int64_t expr_value = EvaluateLinearExpression(expr, *domains);
300 if (target.vars().empty()) {
301 DCHECK_EQ(expr_value, target.offset());
302 } else {
303 (*domains)[target.vars(0)] = Domain(GetInnerVarValue(target, expr_value));
304 }
305 return;
306 }
307
308 // Deal with fixed target (and constant vars).
309 const int64_t target_value = EvaluateLinearExpression(target, *domains);
310 int selected_index_value = -1;
311 for (const int64_t v : (*domains)[index.vars(0)].Values()) {
312 const int64_t index_value = index.offset() + v * index.coeffs(0);
313 DCHECK_GE(index_value, 0);
314 DCHECK_LT(index_value, ct.element().exprs_size());
315
316 const LinearExpressionProto& expr = ct.element().exprs(index_value);
317 const int64_t value = EvaluateLinearExpression(expr, *domains);
318 if (value == target_value) {
319 selected_index_value = index_value;
320 break;
321 }
322 }
323
324 CHECK_NE(selected_index_value, -1);
325 (*domains)[index.vars(0)] =
326 Domain(GetInnerVarValue(index, selected_index_value));
327}
328
329// We only support assigning to an affine target.
330void PostsolveIntMod(const ConstraintProto& ct, std::vector<Domain>* domains) {
331 const int64_t exp = EvaluateLinearExpression(ct.int_mod().exprs(0), *domains);
332 const int64_t mod = EvaluateLinearExpression(ct.int_mod().exprs(1), *domains);
333 CHECK_NE(mod, 0);
334 const int64_t target_value = exp % mod;
335
336 const LinearExpressionProto& target = ct.int_mod().target();
337 CHECK_EQ(target.vars().size(), 1);
338 const int64_t term_value = target_value - target.offset();
339 CHECK_EQ(term_value % target.coeffs(0), 0);
340 const int64_t value = term_value / target.coeffs(0);
341 CHECK((*domains)[target.vars(0)].Contains(value));
342 (*domains)[target.vars(0)] = Domain(value);
343}
344
345// We only support assigning to an affine target.
346void PostsolveIntProd(const ConstraintProto& ct, std::vector<Domain>* domains) {
347 int64_t target_value = 1;
348 for (const LinearExpressionProto& expr : ct.int_prod().exprs()) {
349 target_value *= EvaluateLinearExpression(expr, *domains);
350 }
351
352 const LinearExpressionProto& target = ct.int_prod().target();
353 CHECK_EQ(target.vars().size(), 1);
354 CHECK(RefIsPositive(target.vars(0)));
355
356 target_value -= target.offset();
357 CHECK_EQ(target_value % target.coeffs(0), 0);
358 target_value /= target.coeffs(0);
359 (*domains)[target.vars(0)] = Domain(target_value);
360}
361
362namespace {
363void CheckPostsolveFixedVariables(const ConstraintProto& ct,
364 absl::Span<const Domain> domains) {
365 if (DEBUG_MODE) {
366 for (const int var : UsedVariables(ct)) {
367 DCHECK(domains[PositiveRef(var)].IsFixed())
368 << "Variable " << PositiveRef(var)
369 << " is not fixed after postsolve for constraint: \""
371 << "\" with domain: " << domains[PositiveRef(var)] << ".";
372 }
373 }
374}
375
376void ArbitrarilyFixVariables(const ConstraintProto& ct,
377 absl::Span<Domain> domains) {
378 for (const int var : UsedVariables(ct)) {
379 if (!domains[PositiveRef(var)].IsFixed()) {
380 domains[PositiveRef(var)] = Domain(domains[PositiveRef(var)].Min());
381 }
382 }
383}
384} // namespace
385
386void PostsolveResponse(const int64_t num_variables_in_original_model,
387 const CpModelProto& mapping_proto,
388 absl::Span<const int> postsolve_mapping,
389 std::vector<int64_t>* solution) {
390 CHECK_EQ(solution->size(), postsolve_mapping.size());
391
392 // Read the initial variable domains, either from the fixed solution of the
393 // presolved problems or from the mapping model.
394 std::vector<Domain> domains(mapping_proto.variables_size());
395 for (int i = 0; i < postsolve_mapping.size(); ++i) {
396 CHECK_LE(postsolve_mapping[i], domains.size());
397 domains[postsolve_mapping[i]] = Domain((*solution)[i]);
398 }
399 for (int i = 0; i < domains.size(); ++i) {
400 if (domains[i].IsEmpty()) {
401 domains[i] = ReadDomainFromProto(mapping_proto.variables(i));
402 }
403 CHECK(!domains[i].IsEmpty());
404 }
405
406 // Process the constraints in reverse order.
407 const int num_constraints = mapping_proto.constraints_size();
408 for (int i = num_constraints - 1; i >= 0; i--) {
409 const ConstraintProto& ct = mapping_proto.constraints(i);
410
411 // We ignore constraint with an enforcement literal set to false. If the
412 // enforcement is still unclear, we still process this constraint.
413 bool constraint_can_be_ignored = false;
414 for (const int enf : ct.enforcement_literal()) {
415 const int var = PositiveRef(enf);
416 const bool is_false =
417 domains[var].IsFixed() &&
418 RefIsPositive(enf) == (domains[var].FixedValue() == 0);
419 if (is_false) {
420 constraint_can_be_ignored = true;
421 break;
422 }
423 }
424 if (constraint_can_be_ignored) {
425 ArbitrarilyFixVariables(ct, absl::MakeSpan(domains));
426 CheckPostsolveFixedVariables(ct, domains);
427 continue;
428 }
429
430 switch (ct.constraint_case()) {
432 PostsolveClause(ct, &domains);
433 break;
435 PostsolveExactlyOne(ct, &domains);
436 break;
438 PostsolveLinear(ct, &domains);
439 break;
441 PostsolveLinMax(ct, &domains);
442 break;
444 PostsolveElement(ct, &domains);
445 break;
447 PostsolveIntMod(ct, &domains);
448 break;
450 PostsolveIntProd(ct, &domains);
451 break;
452 default:
453 // This should never happen as we control what kind of constraint we
454 // add to the mapping_proto;
455 LOG(FATAL) << "Unsupported constraint: "
457 }
458 CheckPostsolveFixedVariables(ct, domains);
459 }
460
461 // Fill the response.
462 // Maybe fix some still unfixed variable.
463 solution->clear();
464 CHECK_LE(num_variables_in_original_model, domains.size());
465 for (int i = 0; i < num_variables_in_original_model; ++i) {
466 solution->push_back(domains[i].SmallestValue());
467 }
468}
469
471 const CpModelProto& mapping_proto,
472 absl::Span<const int> postsolve_mapping,
473 absl::Span<const Domain> search_domains,
474 CpSolverResponse* response,
475 SolverLogger* logger) {
476 // The [0, num_vars) part will contain the tightened domains.
477 const int num_original_vars = original_model.variables().size();
478 const int num_expanded_vars = mapping_proto.variables().size();
479 CHECK_LE(num_original_vars, num_expanded_vars);
480 std::vector<Domain> domains(num_expanded_vars);
481
482 // Start with the domain from the mapping proto. Note that by construction
483 // this should be tighter than the original variable domains.
484 for (int i = 0; i < num_expanded_vars; ++i) {
485 domains[i] = ReadDomainFromProto(mapping_proto.variables(i));
486 if (i < num_original_vars) {
487 CHECK(domains[i].IsIncludedIn(
488 ReadDomainFromProto(original_model.variables(i))));
489 }
490 }
491
492 // The first test is for the corner case of presolve closing the problem,
493 // in which case there is no more info to process.
494 int num_common_vars = 0;
495 int num_affine_reductions = 0;
496 if (!search_domains.empty()) {
497 if (postsolve_mapping.empty()) {
498 // Currently no mapping should mean all variables are in common. This
499 // happen when presolve is disabled, but we might still have more
500 // variables due to expansion for instance.
501 //
502 // There is also the corner case of presolve closing the problem,
503 CHECK_GE(search_domains.size(), num_original_vars);
504 num_common_vars = num_original_vars;
505 for (int i = 0; i < num_original_vars; ++i) {
506 domains[i] = domains[i].IntersectionWith(search_domains[i]);
507 }
508 } else {
509 // This is the normal presolve case.
510 // Intersect the domain of the variables in common.
511 CHECK_EQ(postsolve_mapping.size(), search_domains.size());
512 for (int search_i = 0; search_i < postsolve_mapping.size(); ++search_i) {
513 const int i_in_mapping_model = postsolve_mapping[search_i];
514 if (i_in_mapping_model < num_original_vars) {
515 ++num_common_vars;
516 }
517 domains[i_in_mapping_model] =
518 domains[i_in_mapping_model].IntersectionWith(
519 search_domains[search_i]);
520 }
521
522 // Look for affine relation, and do more intersection.
523 for (const ConstraintProto& ct : mapping_proto.constraints()) {
524 if (ct.constraint_case() != ConstraintProto::kLinear) continue;
525 const LinearConstraintProto& lin = ct.linear();
526 if (lin.vars().size() != 2) continue;
527 if (lin.domain().size() != 2) continue;
528 if (lin.domain(0) != lin.domain(1)) continue;
529 int v1 = lin.vars(0);
530 int v2 = lin.vars(1);
531 int c1 = lin.coeffs(0);
532 int c2 = lin.coeffs(1);
533 if (v2 < num_original_vars && v1 >= num_original_vars) {
534 std::swap(v1, v2);
535 std::swap(c1, c2);
536 }
537 if (v1 < num_original_vars && v2 >= num_original_vars) {
538 // We can reduce the domain of v1 by using the affine relation
539 // and the domain of v2.
540 // We have c1 * v2 + c2 * v2 = offset;
541 const int64_t offset = lin.domain(0);
542 const Domain restriction =
543 Domain(offset)
544 .AdditionWith(domains[v2].ContinuousMultiplicationBy(-c2))
546 if (!domains[v1].IsIncludedIn(restriction)) {
547 ++num_affine_reductions;
548 domains[v1] = domains[v1].IntersectionWith(restriction);
549 }
550 }
551 }
552 }
553 }
554
555 // Copy the names and replace domains.
556 *response->mutable_tightened_variables() = original_model.variables();
557 int num_tigher_domains = 0;
558 int num_empty = 0;
559 int num_fixed = 0;
560 for (int i = 0; i < num_original_vars; ++i) {
561 FillDomainInProto(domains[i], response->mutable_tightened_variables(i));
562 if (domains[i].IsEmpty()) {
563 ++num_empty;
564 continue;
565 }
566
567 if (domains[i].IsFixed()) num_fixed++;
568 const Domain original = ReadDomainFromProto(original_model.variables(i));
569 if (domains[i] != original) {
570 DCHECK(domains[i].IsIncludedIn(original));
571 ++num_tigher_domains;
572 }
573 }
574
575 // Some stats.
576 if (num_empty > 0) {
577 SOLVER_LOG(logger, num_empty,
578 " tightened domains are empty. This should not happen except if "
579 "we proven infeasibility or optimality.");
580 }
581 SOLVER_LOG(logger, "Filled tightened domains in the response.");
582 SOLVER_LOG(logger, "[TighteningInfo] num_tighter:", num_tigher_domains,
583 " num_fixed:", num_fixed,
584 " num_affine_reductions:", num_affine_reductions);
585 SOLVER_LOG(logger,
586 "[TighteningInfo] original_num_variables:", num_original_vars,
587 " during_presolve:", num_expanded_vars,
588 " after:", search_domains.size(), " in_common:", num_common_vars);
589 SOLVER_LOG(logger, "");
590}
591
592} // namespace sat
593} // 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
int literals_size() const
repeated int32 literals = 1;
::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
int constraints_size() const
repeated .operations_research.sat.ConstraintProto constraints = 3;
int variables_size() const
repeated .operations_research.sat.IntegerVariableProto variables = 2;
::operations_research::sat::IntegerVariableProto *PROTOBUF_NONNULL mutable_tightened_variables(int index)
int exprs_size() const
repeated .operations_research.sat.LinearExpressionProto exprs = 6;
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:1593
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)
We only support assigning to an affine target.
std::vector< int > UsedVariables(const ConstraintProto &ct)
void PostsolveLinMax(const ConstraintProto &ct, std::vector< Domain > *domains)
void FillDomainInProto(const Domain &domain, ProtoWithDomain *proto)
Serializes a Domain into the domain field of a proto.
void PostsolveIntMod(const ConstraintProto &ct, std::vector< Domain > *domains)
We only support assigning to an affine target.
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
Reads a Domain from the domain field of a 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)
In SWIG mode, we don't want anything besides these top-level includes.
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:110