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);
149 auto fix_unassigned_enforcement_to_true = [&](
const ConstraintProto& ct) {
158 if (free_vars.empty()) {
160 if (!rhs.
Contains(fixed_activity)) {
163 fix_unassigned_enforcement_to_true(ct);
170 if (free_vars.size() == 1) {
171 const int var = free_vars[0];
179 fix_unassigned_enforcement_to_true(ct);
191 std::vector<Domain> rhs_domains;
192 rhs_domains.push_back(initial_rhs);
193 for (
int i = 0;
i + 1 < free_vars.size(); ++
i) {
200 rhs_domains.push_back(term.
AdditionWith(rhs_domains.back()));
202 std::vector<int64_t> values(free_vars.size());
203 for (
int i = free_vars.size() - 1;
i >= 0; --
i) {
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]);
218 LOG(INFO) <<
"Empty domain while trying to assign " << var;
219 for (
int i = 0;
i < size; ++
i) {
221 LOG(INFO) << var <<
" " << (*domains)[var];
223 LOG(FATAL) <<
"Couldn't postsolve the constraint: "
230 fixed_activity += coeff * value;
235 for (
int i = 0;
i < free_vars.size(); ++
i) {
236 (*domains)[free_vars[
i]] =
Domain(values[
i]);
239 DCHECK(initial_rhs.
Contains(fixed_activity));
240 fix_unassigned_enforcement_to_true(ct);
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 =
259 absl::Span<const Domain> domains) {
260 for (
const int var : expr.vars()) {
261 if (!domains[var].
IsFixed())
return false;
272 int64_t max_value = std::numeric_limits<int64_t>::min();
276 max_value = std::max(max_value, EvaluateLinearExpression(expr, *domains));
280 CHECK_EQ(target.
vars().size(), 1);
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);
295 DCHECK(LinearExpressionIsFixed(index, *domains) ||
296 LinearExpressionIsFixed(target, *domains));
299 if (LinearExpressionIsFixed(index, *domains)) {
300 const int64_t index_value = EvaluateLinearExpression(index, *domains);
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());
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);
321 const int64_t value = EvaluateLinearExpression(expr, *domains);
322 if (value == target_value) {
323 selected_index_value = index_value;
328 CHECK_NE(selected_index_value, -1);
329 (*domains)[index.
vars(0)] =
335 const int64_t exp = EvaluateLinearExpression(ct.
int_mod().
exprs(0), *domains);
336 const int64_t mod = EvaluateLinearExpression(ct.
int_mod().
exprs(1), *domains);
338 const int64_t target_value = exp % mod;
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));
351 int64_t target_value = 1;
353 target_value *= EvaluateLinearExpression(expr, *domains);
357 CHECK_EQ(target.
vars().size(), 1);
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);
367void CheckPostsolveFixedVariables(
const ConstraintProto& ct,
368 absl::Span<const Domain> domains) {
373 <<
" is not fixed after postsolve for constraint: \""
375 <<
"\" with domain: " << domains[
PositiveRef(var)] <<
".";
381 absl::Span<Domain> domains) {
392 absl::Span<const int> postsolve_mapping,
394 CHECK_EQ(
solution->size(), postsolve_mapping.size());
399 for (
int i = 0;
i < postsolve_mapping.size(); ++
i) {
400 CHECK_LE(postsolve_mapping[
i], domains.size());
403 for (
int i = 0;
i < domains.size(); ++
i) {
404 if (domains[
i].IsEmpty()) {
407 CHECK(!domains[
i].IsEmpty());
412 for (
int i = num_constraints - 1;
i >= 0;
i--) {
417 bool constraint_can_be_ignored =
false;
420 const bool is_false =
421 domains[var].IsFixed() &&
424 constraint_can_be_ignored =
true;
428 if (constraint_can_be_ignored) {
429 ArbitrarilyFixVariables(ct, absl::MakeSpan(domains));
430 CheckPostsolveFixedVariables(ct, domains);
459 LOG(FATAL) <<
"Unsupported constraint: "
462 CheckPostsolveFixedVariables(ct, domains);
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());
476 absl::Span<const int> postsolve_mapping,
477 absl::Span<const Domain> search_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);
488 for (
int i = 0;
i < num_expanded_vars; ++
i) {
490 if (
i < num_original_vars) {
491 CHECK(domains[
i].IsIncludedIn(
498 int num_common_vars = 0;
499 int num_affine_reductions = 0;
500 if (!search_domains.empty()) {
501 if (postsolve_mapping.empty()) {
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]);
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) {
521 domains[i_in_mapping_model] =
522 domains[i_in_mapping_model].IntersectionWith(
523 search_domains[search_i]);
530 if (lin.
vars().size() != 2)
continue;
531 if (lin.
domain().size() != 2)
continue;
534 int v2 = lin.
vars(1);
537 if (v2 < num_original_vars && v1 >= num_original_vars) {
541 if (v1 < num_original_vars && v2 >= num_original_vars) {
545 const int64_t offset = lin.
domain(0);
546 const Domain restriction =
548 .
AdditionWith(domains[v2].ContinuousMultiplicationBy(-c2))
550 if (!domains[
v1].IsIncludedIn(restriction)) {
551 ++num_affine_reductions;
552 domains[
v1] = domains[
v1].IntersectionWith(restriction);
561 int num_tigher_domains = 0;
564 for (
int i = 0;
i < num_original_vars; ++
i) {
566 if (domains[
i].IsEmpty()) {
571 if (domains[
i].
IsFixed()) num_fixed++;
573 if (domains[
i] != original) {
574 DCHECK(domains[
i].IsIncludedIn(original));
575 ++num_tigher_domains;
582 " tightened domains are empty. This should not happen except if "
583 "we proven infeasibility or optimality.");
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);
590 "[TighteningInfo] original_num_variables:", num_original_vars,
591 " during_presolve:", num_expanded_vars,
592 " 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
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
int variables_size() 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
::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)
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)
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,...)