62 std::vector<Domain>* domains) {
63 bool satisfied =
false;
64 std::vector<int> free_variables;
65 for (
const int ref :
ct.exactly_one().literals()) {
69 CHECK(!satisfied) <<
"Two variables at one in exactly one.";
73 free_variables.push_back(ref);
78 CHECK(!free_variables.empty()) <<
"All zero in exactly one";
79 const int ref = free_variables.back();
81 free_variables.pop_back();
85 for (
const int ref : free_variables) {
116 int64_t fixed_activity = 0;
117 const int size =
ct.linear().vars().size();
118 std::vector<int> free_vars;
119 std::vector<int64_t> free_coeffs;
120 for (
int i = 0;
i <
size; ++
i) {
121 const int var =
ct.linear().vars(
i);
122 const int64_t coeff =
ct.linear().coeffs(
i);
123 CHECK_LT(
var, domains->size());
124 if (coeff == 0)
continue;
126 fixed_activity += (*domains)[
var].FixedValue() * coeff;
128 free_vars.push_back(
var);
129 free_coeffs.push_back(coeff);
132 if (free_vars.empty()) {
134 if (!rhs.
Contains(fixed_activity)) {
142 if (free_vars.size() == 1) {
143 const int var = free_vars[0];
162 std::vector<Domain> rhs_domains;
163 rhs_domains.push_back(initial_rhs);
164 for (
int i = 0;
i + 1 < free_vars.size(); ++
i) {
171 rhs_domains.push_back(term.
AdditionWith(rhs_domains.back()));
173 for (
int i = free_vars.size() - 1;
i >= 0; --
i) {
177 const int var = free_vars[
i];
178 const int64_t coeff = free_coeffs[
i];
179 const Domain domain = rhs_domains[
i]
180 .AdditionWith(
Domain(-fixed_activity))
181 .InverseMultiplicationBy(coeff)
182 .IntersectionWith((*domains)[
var]);
191 fixed_activity += coeff *
value;
193 DCHECK(initial_rhs.
Contains(fixed_activity));
217 int64_t max_value = std::numeric_limits<int64_t>::min();
218 for (
const LinearExpressionProto& expr :
ct.lin_max().exprs()) {
221 max_value = std::max(max_value, EvaluateLinearExpression(expr, *domains));
224 const LinearExpressionProto& target =
ct.lin_max().target();
225 CHECK_EQ(target.vars().size(), 1);
228 max_value -= target.offset();
229 CHECK_EQ(max_value % target.coeffs(0), 0);
230 max_value /= target.coeffs(0);
231 (*domains)[target.vars(0)] =
Domain(max_value);
236 const int index_ref =
ct.element().index();
238 const int target_ref =
ct.element().target();
244 if (!(*domains)[target_var].
IsFixed() && !(*domains)[index_var].
IsFixed()) {
245 const int64_t index_var_value = (*domains)[index_var].Min();
246 (*domains)[index_var] =
Domain(index_var_value);
249 const int selected_ref =
ct.element().vars(
250 RefIsPositive(index_ref) ? index_var_value : -index_var_value);
251 const int selected_var =
PositiveRef(selected_ref);
252 if (!(*domains)[selected_var].
IsFixed()) {
253 (*domains)[selected_var] =
Domain((*domains)[selected_var].Min());
258 if ((*domains)[index_var].
IsFixed()) {
259 const int64_t index_var_value = (*domains)[index_var].FixedValue();
260 const int selected_ref =
ct.element().vars(
261 RefIsPositive(index_ref) ? index_var_value : -index_var_value);
262 const int selected_var =
PositiveRef(selected_ref);
263 if ((*domains)[selected_var].
IsFixed()) {
264 const int64_t selected_value = (*domains)[selected_var].FixedValue();
265 (*domains)[target_var] = (*domains)[target_var].IntersectionWith(
269 DCHECK(!(*domains)[target_var].IsEmpty());
271 const bool same_sign =
272 (selected_var == selected_ref) == (target_var == target_ref);
273 const Domain target_domain = (*domains)[target_var];
274 const Domain selected_domain = same_sign
275 ? (*domains)[selected_var]
276 : (*domains)[selected_var].
Negation();
279 (*domains)[target_var] =
280 (*domains)[target_var].IntersectionWith(
Domain(
value));
281 (*domains)[selected_var] = (*domains)[selected_var].IntersectionWith(
283 DCHECK(!(*domains)[target_var].IsEmpty());
284 DCHECK(!(*domains)[selected_var].IsEmpty());
290 const int64_t target_value = (*domains)[target_var].FixedValue();
291 int selected_index_value = -1;
292 for (
const int64_t v : (*domains)[index_var].Values()) {
293 const int64_t
i = index_var == index_ref ? v : -v;
296 const int ref =
ct.element().vars(
i);
298 const int64_t
value = (*domains)[
var].FixedValue();
300 if (
value == target_value) {
301 selected_index_value =
i;
305 if (
value == -target_value) {
306 selected_index_value =
i;
312 CHECK_NE(selected_index_value, -1);
313 (*domains)[index_var] = (*domains)[index_var].IntersectionWith(
Domain(
314 RefIsPositive(index_ref) ? selected_index_value : -selected_index_value));
315 DCHECK(!(*domains)[index_var].IsEmpty());
320 const int64_t exp = EvaluateLinearExpression(
ct.int_mod().exprs(0), *domains);
321 const int64_t mod = EvaluateLinearExpression(
ct.int_mod().exprs(1), *domains);
323 const int64_t target_value = exp % mod;
325 const LinearExpressionProto& target =
ct.int_mod().target();
326 CHECK_EQ(target.vars().size(), 1);
327 const int64_t term_value = target_value - target.offset();
328 CHECK_EQ(term_value % target.coeffs(0), 0);
329 const int64_t
value = term_value / target.coeffs(0);
330 CHECK((*domains)[target.vars(0)].Contains(
value));
335 const CpModelProto& mapping_proto,
336 const std::vector<int>& postsolve_mapping,
338 CHECK_EQ(
solution->size(), postsolve_mapping.size());
342 std::vector<Domain> domains(mapping_proto.variables_size());
343 for (
int i = 0;
i < postsolve_mapping.size(); ++
i) {
344 CHECK_LE(postsolve_mapping[
i], domains.size());
347 for (
int i = 0;
i < domains.size(); ++
i) {
348 if (domains[
i].IsEmpty()) {
351 CHECK(!domains[
i].IsEmpty());
355 const int num_constraints = mapping_proto.constraints_size();
356 for (
int i = num_constraints - 1;
i >= 0;
i--) {
357 const ConstraintProto&
ct = mapping_proto.constraints(
i);
361 bool constraint_can_be_ignored =
false;
362 for (
const int enf :
ct.enforcement_literal()) {
364 const bool is_false =
365 domains[
var].IsFixed() &&
368 constraint_can_be_ignored =
true;
372 if (constraint_can_be_ignored)
continue;
374 switch (
ct.constraint_case()) {
375 case ConstraintProto::kBoolOr:
378 case ConstraintProto::kExactlyOne:
381 case ConstraintProto::kLinear:
384 case ConstraintProto::kLinMax:
387 case ConstraintProto::kElement:
390 case ConstraintProto::kIntMod:
396 LOG(FATAL) <<
"Unsupported constraint: "
404 CHECK_LE(num_variables_in_original_model, domains.size());
405 for (
int i = 0;
i < num_variables_in_original_model; ++
i) {
406 solution->push_back(domains[
i].SmallestValue());
411 const CpModelProto& mapping_proto,
412 const std::vector<int>& postsolve_mapping,
413 const std::vector<Domain>& search_domains,
414 CpSolverResponse* response,
417 const int num_original_vars = original_model.variables().size();
418 const int num_expanded_vars = mapping_proto.variables().size();
419 CHECK_LE(num_original_vars, num_expanded_vars);
420 std::vector<Domain> domains(num_expanded_vars);
424 for (
int i = 0;
i < num_expanded_vars; ++
i) {
426 if (
i < num_original_vars) {
427 CHECK(domains[
i].IsIncludedIn(
434 int num_common_vars = 0;
435 int num_affine_reductions = 0;
436 if (!search_domains.empty()) {
437 if (postsolve_mapping.empty()) {
443 CHECK_GE(search_domains.size(), num_original_vars);
444 num_common_vars = num_original_vars;
445 for (
int i = 0;
i < num_original_vars; ++
i) {
446 domains[
i] = domains[
i].IntersectionWith(search_domains[
i]);
451 CHECK_EQ(postsolve_mapping.size(), search_domains.size());
452 for (
int search_i = 0; search_i < postsolve_mapping.size(); ++search_i) {
453 const int i_in_mapping_model = postsolve_mapping[search_i];
454 if (i_in_mapping_model < num_original_vars) {
457 domains[i_in_mapping_model] =
458 domains[i_in_mapping_model].IntersectionWith(
459 search_domains[search_i]);
463 for (
const ConstraintProto&
ct : mapping_proto.constraints()) {
464 if (
ct.constraint_case() != ConstraintProto::kLinear)
continue;
465 const LinearConstraintProto& lin =
ct.linear();
466 if (lin.vars().size() != 2)
continue;
467 if (lin.domain().size() != 2)
continue;
468 if (lin.domain(0) != lin.domain(1))
continue;
469 int v1 = lin.vars(0);
470 int v2 = lin.vars(1);
471 int c1 = lin.coeffs(0);
472 int c2 = lin.coeffs(1);
473 if (v2 < num_original_vars && v1 >= num_original_vars) {
477 if (v1 < num_original_vars && v2 >= num_original_vars) {
481 const int64_t offset = lin.domain(0);
482 const Domain restriction =
484 .
AdditionWith(domains[v2].ContinuousMultiplicationBy(-c2))
486 if (!domains[v1].IsIncludedIn(restriction)) {
487 ++num_affine_reductions;
488 domains[v1] = domains[v1].IntersectionWith(restriction);
496 *response->mutable_tightened_variables() = original_model.variables();
497 int num_tigher_domains = 0;
500 for (
int i = 0;
i < num_original_vars; ++
i) {
502 if (domains[
i].IsEmpty()) {
507 if (domains[
i].
IsFixed()) num_fixed++;
509 if (domains[
i] != original) {
510 DCHECK(domains[
i].IsIncludedIn(original));
511 ++num_tigher_domains;
518 " tightened domains are empty. This should not happen except if "
519 "we proven infeasibility or optimality.");
521 SOLVER_LOG(logger,
"Filled tightened domains in the response.");
522 SOLVER_LOG(logger,
"[TighteningInfo] num_tighter:", num_tigher_domains,
523 " num_fixed:", num_fixed,
524 " num_affine_reductions:", num_affine_reductions);
526 "[TighteningInfo] original_num_variables:", num_original_vars,
527 " during_presolve:", num_expanded_vars,
528 " 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
void FillTightenedDomainInResponse(const CpModelProto &original_model, const CpModelProto &mapping_proto, const std::vector< int > &postsolve_mapping, const std::vector< Domain > &search_domains, CpSolverResponse *response, SolverLogger *logger)