21#include "absl/base/log_severity.h"
22#include "absl/log/check.h"
23#include "absl/types/span.h"
43 bool satisfied =
false;
44 for (
int i = 0;
i < size; ++
i) {
47 if ((*domains)[var].
IsFixed()) {
48 if ((*domains)[var].FixedValue() == (
RefIsPositive(ref) ? 1 : 0)) {
60 if (satisfied)
return;
68 std::vector<Domain>* domains) {
69 bool satisfied =
false;
70 std::vector<int> free_variables;
73 if ((*domains)[var].
IsFixed()) {
74 if ((*domains)[var].FixedValue() == (
RefIsPositive(ref) ? 1 : 0)) {
75 CHECK(!satisfied) <<
"Two variables at one in exactly one.";
79 free_variables.push_back(ref);
84 CHECK(!free_variables.empty()) <<
"All zero in exactly one";
85 const int ref = free_variables.back();
87 free_variables.pop_back();
91 for (
const int ref : free_variables) {
99 std::vector<Domain>* domains) {
101 bool has_free_enforcement_literal =
false;
104 has_free_enforcement_literal =
true;
106 (*domains)[enf] =
Domain(0);
112 if (!has_free_enforcement_literal) {
114 <<
"Unsatisfied linear constraint with no free enforcement literal: "
122 int64_t fixed_activity = 0;
124 std::vector<int> free_vars;
125 std::vector<int64_t> free_coeffs;
126 for (
int i = 0;
i < size; ++
i) {
129 CHECK_LT(var, domains->size());
130 if (coeff == 0)
continue;
131 if ((*domains)[var].
IsFixed()) {
132 fixed_activity += (*domains)[var].FixedValue() * coeff;
134 free_vars.push_back(var);
135 free_coeffs.push_back(coeff);
138 if (free_vars.empty()) {
140 if (!rhs.
Contains(fixed_activity)) {
168 if (free_vars.size() == 1) {
169 const int var = free_vars[0];
188 std::vector<Domain> rhs_domains;
189 rhs_domains.push_back(initial_rhs);
190 for (
int i = 0;
i + 1 < free_vars.size(); ++
i) {
197 rhs_domains.push_back(term.
AdditionWith(rhs_domains.back()));
199 std::vector<int64_t> values(free_vars.size());
200 for (
int i = free_vars.size() - 1;
i >= 0; --
i) {
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]);
215 LOG(INFO) <<
"Empty domain while trying to assign " << var;
216 for (
int i = 0;
i < size; ++
i) {
218 LOG(INFO) << var <<
" " << (*domains)[var];
220 LOG(FATAL) <<
"Couldn't postsolve the constraint: "
227 fixed_activity += coeff * value;
232 for (
int i = 0;
i < free_vars.size(); ++
i) {
233 (*domains)[free_vars[
i]] =
Domain(values[
i]);
236 DCHECK(initial_rhs.
Contains(fixed_activity));
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 =
255 absl::Span<const Domain> domains) {
256 for (
const int var : expr.vars()) {
257 if (!domains[var].
IsFixed())
return false;
268 int64_t max_value = std::numeric_limits<int64_t>::min();
272 max_value = std::max(max_value, EvaluateLinearExpression(expr, *domains));
276 CHECK_EQ(target.
vars().size(), 1);
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);
291 DCHECK(LinearExpressionIsFixed(index, *domains) ||
292 LinearExpressionIsFixed(target, *domains));
295 if (LinearExpressionIsFixed(index, *domains)) {
296 const int64_t index_value = EvaluateLinearExpression(index, *domains);
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());
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);
317 const int64_t value = EvaluateLinearExpression(expr, *domains);
318 if (value == target_value) {
319 selected_index_value = index_value;
324 CHECK_NE(selected_index_value, -1);
325 (*domains)[index.
vars(0)] =
331 const int64_t exp = EvaluateLinearExpression(ct.
int_mod().
exprs(0), *domains);
332 const int64_t mod = EvaluateLinearExpression(ct.
int_mod().
exprs(1), *domains);
334 const int64_t target_value = exp % mod;
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));
347 int64_t target_value = 1;
349 target_value *= EvaluateLinearExpression(expr, *domains);
353 CHECK_EQ(target.
vars().size(), 1);
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);
363void CheckPostsolveFixedVariables(
const ConstraintProto& ct,
364 absl::Span<const Domain> domains) {
369 <<
" is not fixed after postsolve for constraint: \""
371 <<
"\" with domain: " << domains[
PositiveRef(var)] <<
".";
377 absl::Span<Domain> domains) {
388 absl::Span<const int> postsolve_mapping,
390 CHECK_EQ(
solution->size(), postsolve_mapping.size());
395 for (
int i = 0;
i < postsolve_mapping.size(); ++
i) {
396 CHECK_LE(postsolve_mapping[
i], domains.size());
399 for (
int i = 0;
i < domains.size(); ++
i) {
400 if (domains[
i].IsEmpty()) {
403 CHECK(!domains[
i].IsEmpty());
408 for (
int i = num_constraints - 1;
i >= 0;
i--) {
413 bool constraint_can_be_ignored =
false;
416 const bool is_false =
417 domains[var].IsFixed() &&
420 constraint_can_be_ignored =
true;
424 if (constraint_can_be_ignored) {
425 ArbitrarilyFixVariables(ct, absl::MakeSpan(domains));
426 CheckPostsolveFixedVariables(ct, domains);
455 LOG(FATAL) <<
"Unsupported constraint: "
458 CheckPostsolveFixedVariables(ct, domains);
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());
472 absl::Span<const int> postsolve_mapping,
473 absl::Span<const Domain> search_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);
484 for (
int i = 0;
i < num_expanded_vars; ++
i) {
486 if (
i < num_original_vars) {
487 CHECK(domains[
i].IsIncludedIn(
494 int num_common_vars = 0;
495 int num_affine_reductions = 0;
496 if (!search_domains.empty()) {
497 if (postsolve_mapping.empty()) {
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]);
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) {
517 domains[i_in_mapping_model] =
518 domains[i_in_mapping_model].IntersectionWith(
519 search_domains[search_i]);
526 if (lin.
vars().size() != 2)
continue;
527 if (lin.
domain().size() != 2)
continue;
530 int v2 = lin.
vars(1);
533 if (v2 < num_original_vars && v1 >= num_original_vars) {
537 if (v1 < num_original_vars && v2 >= num_original_vars) {
541 const int64_t offset = lin.
domain(0);
542 const Domain restriction =
544 .
AdditionWith(domains[v2].ContinuousMultiplicationBy(-c2))
546 if (!domains[
v1].IsIncludedIn(restriction)) {
547 ++num_affine_reductions;
548 domains[
v1] = domains[
v1].IntersectionWith(restriction);
557 int num_tigher_domains = 0;
560 for (
int i = 0;
i < num_original_vars; ++
i) {
562 if (domains[
i].IsEmpty()) {
567 if (domains[
i].
IsFixed()) num_fixed++;
569 if (domains[
i] != original) {
570 DCHECK(domains[
i].IsIncludedIn(original));
571 ++num_tigher_domains;
578 " tightened domains are empty. This should not happen except if "
579 "we proven infeasibility or optimality.");
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);
586 "[TighteningInfo] original_num_variables:", num_original_vars,
587 " during_presolve:", num_expanded_vars,
588 " after:", search_domains.size(),
" in_common:", num_common_vars);
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
int64_t SmallestValue() const
Domain InverseMultiplicationBy(int64_t coeff) const
::int32_t literals(int index) const
int literals_size() const
repeated int32 literals = 1;
ConstraintCase constraint_case() 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
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
::int32_t vars(int index) const
::int64_t coeffs(int index) const
::int64_t domain(int index) const
::int64_t coeffs(int index) const
::int32_t vars(int index) const
void PostsolveElement(const ConstraintProto &ct, std::vector< Domain > *domains)
void PostsolveLinear(const ConstraintProto &ct, std::vector< Domain > *domains)
bool RefIsPositive(int ref)
std::function< bool(const Model &)> IsFixed(IntegerVariable v)
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
std::string ProtobufShortDebugString(const P &message)
#define SOLVER_LOG(logger,...)