Files
ortools-clone/ortools/sat/cp_model_presolve_test.cc
Corentin Le Molgat b05315de21 sat: backport from main
2025-09-22 17:24:20 +02:00

8637 lines
243 KiB
C++

// Copyright 2010-2025 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "ortools/sat/cp_model_presolve.h"
#include <algorithm>
#include <cstdint>
#include <numeric>
#include <random>
#include <string>
#include <utility>
#include <vector>
#include "absl/log/check.h"
#include "absl/random/bit_gen_ref.h"
#include "absl/random/random.h"
#include "gtest/gtest.h"
#include "ortools/base/gmock.h"
#include "ortools/base/parse_test_proto.h"
#include "ortools/linear_solver/linear_solver.pb.h"
#include "ortools/lp_data/lp_data.h"
#include "ortools/lp_data/lp_parser.h"
#include "ortools/lp_data/proto_utils.h"
#include "ortools/port/proto_utils.h"
#include "ortools/sat/cp_model.pb.h"
#include "ortools/sat/cp_model_checker.h"
#include "ortools/sat/cp_model_solver.h"
#include "ortools/sat/cp_model_utils.h"
#include "ortools/sat/lp_utils.h"
#include "ortools/sat/model.h"
#include "ortools/sat/presolve_context.h"
#include "ortools/sat/sat_parameters.pb.h"
#include "ortools/util/logging.h"
#include "ortools/util/sorted_interval_list.h"
namespace operations_research {
namespace sat {
namespace {
using ::google::protobuf::contrib::parse_proto::ParseTestProto;
using ::testing::EqualsProto;
MATCHER_P(ModelEqualsIgnoringConstraintsOrder, expected, "") {
CpModelProto arg_no_ct = arg;
arg_no_ct.clear_constraints();
CpModelProto expected_no_ct = expected;
expected_no_ct.clear_constraints();
if (!ExplainMatchResult(EqualsProto(expected_no_ct), arg_no_ct,
result_listener)) {
return false;
}
std::vector<testing::Matcher<ConstraintProto>> expected_constraints;
for (const ConstraintProto& constraint : expected.constraints()) {
expected_constraints.push_back(EqualsProto(constraint));
}
return ExplainMatchResult(
testing::UnorderedElementsAreArray(expected_constraints),
arg.constraints(), result_listener);
}
std::vector<int> RandomPermutation(int num_variables, absl::BitGenRef random) {
std::vector<int> permutation(num_variables);
std::iota(permutation.begin(), permutation.end(), 0);
std::shuffle(permutation.begin(), permutation.end(), random);
return permutation;
}
// Generate a triangular clause system with a known random solution, and fix the
// "singleton" variables so that the full solution can be found by pure
// propagation.
//
// TODO(user): do the same with a linear system.
CpModelProto RandomTrivialSatProblem(int num_variables,
absl::BitGenRef random) {
CpModelProto result;
result.set_name("Random trivial SAT");
std::vector<int> solution_literals;
for (int i = 0; i < num_variables; ++i) {
solution_literals.push_back(absl::Bernoulli(random, 1.0 / 2) ? i : -i - 1);
IntegerVariableProto* var = result.add_variables();
var->add_domain(0);
var->add_domain(1);
}
const std::vector<int> perm_a = RandomPermutation(num_variables, random);
const std::vector<int> perm_b = RandomPermutation(num_variables, random);
for (int i = 0; i < num_variables; ++i) {
ConstraintProto* ct = result.add_constraints();
for (int j = 0; j <= perm_a[i]; ++j) {
ct->mutable_bool_or()->add_literals(solution_literals[perm_b[j]]);
}
}
return result;
}
CpModelProto PresolveForTest(
CpModelProto initial_model, SatParameters extra_params = SatParameters(),
CpSolverStatus expected_status = CpSolverStatus::UNKNOWN) {
CpModelProto presolved_model = initial_model;
CpModelProto mapping_model;
std::vector<int> mapping;
Model model;
model.GetOrCreate<SolverLogger>()->EnableLogging(true);
model.GetOrCreate<SolverLogger>()->SetLogToStdOut(true);
auto* params = model.GetOrCreate<SatParameters>();
params->set_permute_variable_randomly(false);
params->set_cp_model_probing_level(0);
params->set_convert_intervals(false);
params->MergeFrom(extra_params);
PresolveContext context(&model, &presolved_model, &mapping_model);
CpModelPresolver presolver(&context, &mapping);
EXPECT_EQ(presolver.Presolve(), expected_status);
return presolved_model;
}
// This expects the presolve to remove everything and return the mapping model.
CpModelProto GetMappingModel(CpModelProto initial_model,
SatParameters extra_params = SatParameters()) {
CpModelProto presolved_model = initial_model;
CpModelProto mapping_model;
std::vector<int> mapping;
Model model;
auto* params = model.GetOrCreate<SatParameters>();
params->set_permute_variable_randomly(false);
params->set_cp_model_probing_level(0);
params->set_convert_intervals(false);
params->MergeFrom(extra_params);
PresolveContext context(&model, &presolved_model, &mapping_model);
CpModelPresolver presolver(&context, &mapping);
presolver.Presolve();
EXPECT_THAT(CpModelProto(), testing::EqualsProto(presolved_model));
return mapping_model;
}
// Return a proto with reduced domain after presolve.
CpModelProto GetReducedDomains(CpModelProto initial_model) {
const int num_vars = initial_model.variables().size();
CpModelProto presolved_model = initial_model;
CpModelProto mapping_model;
std::vector<int> mapping;
Model model;
auto* params = model.GetOrCreate<SatParameters>();
params->set_keep_all_feasible_solutions_in_presolve(true);
params->set_permute_variable_randomly(false);
params->set_cp_model_probing_level(0);
params->set_convert_intervals(false);
PresolveContext context(&model, &presolved_model, &mapping_model);
CpModelPresolver presolver(&context, &mapping);
presolver.Presolve();
// Only keep variable domain, and erase extra ones.
mapping_model.clear_constraints();
mapping_model.mutable_variables()->DeleteSubrange(
num_vars, mapping_model.mutable_variables()->size() - num_vars);
return mapping_model;
}
void ExpectInfeasibleDuringPresolve(CpModelProto initial_model) {
PresolveForTest(initial_model, SatParameters(), CpSolverStatus::INFEASIBLE);
}
CpModelProto PresolveOneConstraint(const CpModelProto& initial_model,
const int constraint_index) {
CpModelProto presolved_model = initial_model;
CpModelProto mapping_model;
std::vector<int> mapping;
Model model;
model.GetOrCreate<SatParameters>()
->set_keep_all_feasible_solutions_in_presolve(true);
PresolveContext context(&model, &presolved_model, &mapping_model);
CpModelPresolver presolver(&context, &mapping);
context.InitializeNewDomains();
context.UpdateNewConstraintsVariableUsage();
presolver.PresolveOneConstraint(constraint_index);
presolver.RemoveEmptyConstraints();
for (int i = 0; i < presolved_model.variables_size(); ++i) {
FillDomainInProto(context.DomainOf(i),
presolved_model.mutable_variables(i));
}
return presolved_model;
}
TEST(PresolveCpModelTest, BoolAndWithDuplicate) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
enforcement_literal: [ 0, 1, 2 ]
bool_and { literals: [ 2, 3, 4 ] }
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
enforcement_literal: [ 2, 1, 0 ]
bool_and { literals: [ 3, 4 ] }
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, BoolAndWithNegatedDuplicate) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
enforcement_literal: [ 0, 1, 2 ]
bool_and { literals: [ -3, 3, 4 ] }
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints { bool_or { literals: [ -3, -2, -1 ] } }
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, EmptyPresolvedProblem) {
std::mt19937 random(12345);
const CpModelProto initial_model = RandomTrivialSatProblem(100, random);
CpModelProto presolved_model = initial_model;
CpModelProto mapping_model;
Model model;
std::vector<int> mapping;
PresolveContext context(&model, &presolved_model, &mapping_model);
PresolveCpModel(&context, &mapping);
EXPECT_EQ(presolved_model.variables_size(), 0);
EXPECT_TRUE(mapping.empty());
{
Model tmp_model;
tmp_model.Add(NewSatParameters("cp_model_presolve:false"));
const CpSolverResponse r = SolveCpModel(presolved_model, &tmp_model);
EXPECT_EQ(r.status(), CpSolverStatus::OPTIMAL);
}
model.Add(NewSatParameters("cp_model_presolve:false"));
const CpSolverResponse response = SolveCpModel(mapping_model, &model);
std::vector<int64_t> solution(response.solution().begin(),
response.solution().end());
EXPECT_TRUE(SolutionIsFeasible(initial_model, solution));
}
TEST(PresolveCpModelTest, SimplifyRemovableConstraint) {
const CpModelProto initial_model = ParseTestProto(R"pb(
name: "celar"
variables { domain: [ 16, 792 ] }
variables { domain: [ 16, 792 ] }
variables { domain: [ 16, 792 ] }
variables { domain: [ -776, 776 ] }
variables { domain: [ 0, 776 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ -776, 776 ] }
variables { domain: [ 0, 776 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ -238, 238 ] }
variables { domain: [ 238, 238 ] }
constraints {
name: "int_lin_eq"
linear {
vars: 0
vars: 1
vars: 3
coeffs: 1
coeffs: -1
coeffs: -1
domain: 0
domain: 0
}
}
constraints {
name: "int_abs"
lin_max {
target: { vars: 4 coeffs: 1 }
exprs: { vars: 3 coeffs: 1 }
exprs: { vars: 3 coeffs: -1 }
}
}
constraints {
name: "int_le_reif"
enforcement_literal: 5
linear { vars: 4 coeffs: 1 domain: -9223372036854775808 domain: 59 }
}
constraints {
name: "int_le_reif (negated)"
enforcement_literal: -6
linear { vars: 4 coeffs: 1 domain: 60 domain: 9223372036854775807 }
}
constraints {
name: "int_lin_eq"
linear {
vars: 0
vars: 2
vars: 6
coeffs: 1
coeffs: -1
coeffs: -1
domain: 0
domain: 0
}
}
constraints {
name: "int_abs"
lin_max {
target: { vars: 7 coeffs: 1 }
exprs: { vars: 6 coeffs: 1 }
exprs: { vars: 6 coeffs: -1 }
}
}
constraints {
name: "int_le_reif"
enforcement_literal: 8
linear { vars: 7 coeffs: 1 domain: -9223372036854775808 domain: 186 }
}
constraints {
name: "int_le_reif (negated)"
enforcement_literal: -9
linear { vars: 7 coeffs: 1 domain: 187 domain: 9223372036854775807 }
}
constraints {
name: "int_lin_eq"
linear {
vars: 1
vars: 2
vars: 9
coeffs: 1
coeffs: -1
coeffs: -1
domain: 0
domain: 0
}
}
constraints {
name: "int_abs"
lin_max {
target: { vars: 10 coeffs: 1 }
exprs: { vars: 9 coeffs: 1 }
exprs: { vars: 9 coeffs: -1 }
}
}
)pb");
// This model is FEASIBLE, but before the CL, trying to solve it was crashing.
// because of the encoding of the variable was created after the var was
// marked as removable. It was then in both presolved and mapping models, and
// the postsolve phase was failing.
Model model;
const CpSolverResponse response = SolveCpModel(initial_model, &model);
EXPECT_EQ(response.status(), CpSolverStatus::OPTIMAL);
}
TEST(PresolveCpModelTest, BasicLinearConstraintPresolve) {
// Note(user): I tried a random small problem. Note that the conversion to LP
// put artificial large bounds to x and y which allow to start the round of
// propagations that reduces the domains of the variables.
//
// When removing z, this is: y = 3 + 2x, 0 <= x + y <= 2 and there is actually
// only one solution (x = -1). The presolve simplify everything.
const std::string text_lp =
"y - 2 x + z = 6;"
"x + y + z <= 5;"
"x + y >= 0;"
"z = 3 ;";
glop::LinearProgram lp;
CHECK(ParseLp(text_lp, &lp));
MPModelProto mp_model;
LinearProgramToMPModelProto(lp, &mp_model);
CpModelProto initial_model;
SolverLogger logger;
ConvertMPModelProtoToCpModelProto(SatParameters(), mp_model, &initial_model,
&logger);
const CpModelProto mapping_model = GetMappingModel(initial_model);
// By default we clear the names.
EXPECT_EQ(mapping_model.variables(1).name(), "");
EXPECT_EQ(mapping_model.variables(1).domain(0), -1);
EXPECT_EQ(mapping_model.variables(1).domain(1), -1);
}
// This test used to fail before CL 180337997.
TEST(PresolveCpModelTest, LinearConstraintCornerCasePresolve) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
constraints {
linear {
vars: [ 0, 0, 1, 2 ]
coeffs: [ 1, -1, 1, 1 ]
domain: [ 5, 10 ]
}
}
constraints {
linear {
vars: [ 1, 2 ]
coeffs: [ 1, 2 ]
domain: [ 3, 3 ]
}
}
)pb");
// This model is UNSAT, but before the CL, trying to solve it was crashing.
// because of the duplicate singleton var 0 in the first constraint.
Model model;
const CpSolverResponse response = SolveCpModel(initial_model, &model);
EXPECT_EQ(response.status(), CpSolverStatus::INFEASIBLE);
}
// This test show how we extract simple bool => bound encoding from a big-M
// encoding.
TEST(PresolveCpModelTest, LinearConstraintSplitting) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 10 ] }
constraints {
linear {
vars: [ 0, 1 ]
coeffs: [ 10, 1 ]
domain: [ 3, 15 ]
}
}
)pb");
// The model is equivalent to var0 => var1 <= 5
// not(var0) => var1 >= 3
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 10 ] }
constraints {
enforcement_literal: 0
linear {
vars: 1
coeffs: [ 1 ]
domain: [ 0, 5 ]
}
}
constraints {
enforcement_literal: -1
linear {
vars: 1
coeffs: [ 1 ]
domain: [ 3, 10 ]
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest,
ExtractEnforcementLiteralFromLinearConstraintPositiveMax) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 2 ] }
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 2, 7, 1 ]
domain: [ 0, 10 ]
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 2 ] }
constraints {
enforcement_literal: 0
enforcement_literal: 1
linear {
vars: 2
coeffs: 1
domain: [ 0, 1 ]
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest,
ExtractEnforcementLiteralFromLinearConstraintNegativeMax) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 2 ] }
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 2, -3, 1 ]
domain: [ -10, 1 ]
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 2 ] }
constraints {
enforcement_literal: -2
linear {
vars: 2
coeffs: 1
domain: [ 0, 1 ]
}
}
constraints {
enforcement_literal: -2
bool_and { literals: -1 }
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest,
ExtractEnforcementLiteralFromLinearConstraintPositiveMin) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 2 ] }
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 2, 3, 1 ]
domain: [ 3, 100 ]
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 2 ] }
constraints {
enforcement_literal: -2
linear {
vars: 2
coeffs: 1
domain: [ 1, 2 ]
}
}
constraints {
enforcement_literal: -2
bool_and { literals: 0 }
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest,
ExtractEnforcementLiteralFromLinearConstraintNegativeMin) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 2 ] }
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 2, -3, 1 ]
domain: [ 0, 100 ]
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 2 ] }
constraints {
enforcement_literal: 1
linear {
vars: 2
coeffs: 1
domain: [ 1, 2 ]
}
}
constraints {
enforcement_literal: -1
bool_and { literals: -2 }
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest,
ExtractEnforcementLiteralFromLinearConstraintMultiple) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 2 ] }
constraints {
name: "r0"
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 2, 3, 1 ]
domain: [ 2, 100 ]
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 2 ] }
constraints {
name: "r0"
enforcement_literal: -1
enforcement_literal: -2
linear {
vars: 2
coeffs: 1
domain: [ 2, 2 ]
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, BasicLinMaxPresolve) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 7, 12 ] }
variables { domain: [ -2, 4 ] }
variables { domain: [ 0, 20 ] }
constraints {
lin_max {
target: { vars: 3 coeffs: 1 }
exprs: { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
}
}
constraints { dummy_constraint { vars: [ 0, 1, 2, 3 ] } }
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 7, 12 ] }
variables { domain: [ -2, 4 ] }
variables { domain: [ 7, 12 ] }
constraints {
lin_max {
target: { vars: 3 coeffs: 1 }
exprs: { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
}
}
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, MoreAdvancedPresolve) {
// We can remove variable zero from the max since it do not change the
// outcome.
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 10, 12 ] }
variables { domain: [ 10, 13 ] }
variables { domain: [ 10, 20 ] }
constraints {
lin_max {
target: { vars: 3 coeffs: 1 }
exprs: { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
}
}
constraints { dummy_constraint { vars: [ 0, 1, 2, 3 ] } }
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 10, 12 ] }
variables { domain: [ 10, 13 ] }
variables { domain: [ 10, 13 ] }
constraints {
lin_max {
target: { vars: 3 coeffs: 1 }
exprs: { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
}
}
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, ConvertToEquality) {
// We can infer that the target is necessarily equal to the second variable.
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -4, 0 ] }
variables { domain: [ 0, 12 ] }
variables { domain: [ -2, 0 ] }
variables { domain: [ 0, 20 ] }
constraints {
lin_max {
target: { vars: 3 coeffs: 1 }
exprs: { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
}
}
constraints { dummy_constraint { vars: [ 0, 1, 2, 3 ] } }
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ -4, 0 ] }
variables { domain: [ 0, 12 ] }
variables { domain: [ -2, 0 ] }
variables { domain: [ 0, 12 ] }
constraints {
linear {
vars: [ 3, 1 ]
coeffs: [ 1, -1 ]
domain: [ 0, 0 ]
}
}
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, ConvertToEqualityDoNotWork) {
// Compared to ConvertToEquality, here we can't because the min of that
// variable is too low.
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -4, 0 ] }
variables { domain: [ -3, 12 ] }
variables { domain: [ -2, 0 ] }
variables { domain: [ 0, 20 ] }
constraints {
lin_max {
target: { vars: 3 coeffs: 1 }
exprs: { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
}
}
constraints { dummy_constraint { vars: [ 0, 1, 2, 3 ] } }
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ -4, 0 ] }
variables { domain: [ -3, 12 ] }
variables { domain: [ -2, 0 ] }
variables { domain: [ 0, 12 ] }
constraints {
lin_max {
target: { vars: 3 coeffs: 1 }
exprs: { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
}
}
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, LinMaxExprEqualTarget) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: -16777224 domain: 1 }
constraints {
lin_max {
target { vars: -1 coeffs: 1 }
exprs { vars: -1 coeffs: 1 }
exprs { vars: 0 coeffs: -10 offset: 1 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 1, 1 ] }
)pb");
const CpModelProto presolved_model = GetReducedDomains(initial_model);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, BasicLinAbsPresolveVarToAbs) {
// Note that we use duplicate constraints otherwise, the presolve will
// solve the problem for us because m appear in only one constraint.
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -10, 10 ] }
variables { domain: [ -2, 12 ] }
variables { domain: [ 1, 10 ] }
variables { domain: [ 0, 20 ] }
constraints {
lin_max {
target { vars: 1 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
exprs { vars: 0 coeffs: -1 }
}
}
constraints { dummy_constraint { vars: [ 1, 2, 3 ] } }
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ -10, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 1, 10 ] }
variables { domain: [ 0, 20 ] }
constraints {
lin_max {
target { vars: 1 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
exprs { vars: 0 coeffs: -1 }
}
}
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, BasicLinAbsPresolveAbsToVar) {
// Note that we use duplicate constraints otherwise, the presolve will
// solve the problem for us because m appear in only one constraint.
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -20, 20 ] }
variables { domain: [ 0, 12 ] }
variables { domain: [ 1, 10 ] }
variables { domain: [ 0, 20 ] }
constraints {
lin_max {
target { vars: 1 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
exprs { vars: 0 coeffs: -1 }
}
}
constraints { dummy_constraint { vars: [ 1, 2, 3 ] } }
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ -12, 12 ] }
variables { domain: [ 0, 12 ] }
variables { domain: [ 1, 10 ] }
variables { domain: [ 0, 20 ] }
constraints {
lin_max {
target { vars: 1 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
exprs { vars: 0 coeffs: -1 }
}
}
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, BasicLinAbsPresolveFixedTarget) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -20, 20 ] }
variables { domain: [ 0, 4 ] }
variables { domain: [ -10, 10 ] }
constraints {
lin_max {
target { offset: 5 }
exprs { vars: 0 coeffs: 1 }
exprs { vars: 0 coeffs: -1 }
}
}
constraints {
lin_max {
target { vars: 2 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 4 ] }
variables { domain: [ 0, 5 ] }
constraints {
lin_max {
target: { vars: 2 coeffs: 1 }
exprs: { vars: 0 coeffs: 10 offset: -5 }
exprs: { vars: 1 coeffs: 1 }
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, RemoveAbsFromUnaryLinear) {
// Make sure we can only remove the varibale 1 here.
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -20, 20 ] }
variables { domain: [ 0, 12 ] }
variables { domain: [ 0, 1 ] }
constraints { dummy_constraint { vars: [ 0, 2 ] } }
constraints {
lin_max {
target { vars: 1 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
exprs { vars: 0 coeffs: -1 }
}
}
constraints {
enforcement_literal: 2
linear {
vars: 1
coeffs: 1
domain: [ 3, 5 ]
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ -12, 12 ] }
variables { domain: [ 0, 1 ] }
constraints {
enforcement_literal: 1
linear { vars: 0 coeffs: 1 domain: -5 domain: -3 domain: 3 domain: 5 }
}
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, LinMaxBasicPresolveSingleVar) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 7, 12 ] }
variables { domain: [ -2, 4 ] }
variables { domain: [ 0, 20 ] }
constraints {
lin_max {
target { vars: 3 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
}
}
constraints {
lin_max {
target { vars: 3 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 7, 12 ] }
variables { domain: [ -2, 4 ] }
variables { domain: [ 7, 12 ] }
constraints {
lin_max {
target { vars: 3 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, LinMaxBasicPresolveExprs) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 1, 2 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ -2, -1 ] }
variables { domain: [ -3, 0 ] }
constraints {
lin_max {
target { vars: 3 coeffs: 1 }
exprs {
vars: [ 0, 1 ]
coeffs: [ 2, 3 ]
offset: -5
}
exprs {
vars: [ 1, 2 ]
coeffs: [ 2, -5 ]
offset: -6
}
exprs {
vars: [ 0, 2 ]
coeffs: [ -2, 3 ]
}
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 1, 2 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ -2, -1 ] }
variables { domain: [ -1, 0 ] }
constraints {
lin_max {
target { vars: 3 coeffs: 1 }
exprs {
vars: [ 0, 1 ]
coeffs: [ 2, 3 ]
offset: -5
}
exprs {
vars: [ 1, 2 ]
coeffs: [ 2, -5 ]
offset: -6
}
}
}
)pb");
const CpModelProto presolved_model = PresolveOneConstraint(initial_model, 0);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, NoOverlapRemovedRedundantIntervals) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 1, 3 ] }
variables { domain: [ 1, 3 ] }
variables { domain: [ 3, 5 ] }
variables { domain: [ 1, 7 ] }
variables { domain: [ 1, 12 ] }
variables { domain: [ 5, 10 ] }
variables { domain: [ 1, 7 ] }
variables { domain: [ 6, 12 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
size { vars: 1 coeffs: 1 }
end { vars: 2 coeffs: 1 }
}
}
constraints {
interval {
start { vars: 3 coeffs: 1 }
size { vars: 4 coeffs: 1 }
end { vars: 5 coeffs: 1 }
}
}
constraints {
interval {
start { vars: 6 coeffs: 1 }
size { vars: 7 coeffs: 1 }
end { vars: 8 coeffs: 1 }
}
}
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 1, 1, -1 ]
domain: [ 0, 0 ]
}
}
constraints {
linear {
vars: [ 3, 4, 5 ]
coeffs: [ 1, 1, -1 ]
domain: [ 0, 0 ]
}
}
constraints {
linear {
vars: [ 6, 7, 8 ]
coeffs: [ 1, 1, -1 ]
domain: [ 0, 0 ]
}
}
constraints { no_overlap { intervals: [ 0, 1, 2 ] } }
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 1, 3 ] }
variables { domain: [ 1, 3 ] }
variables { domain: [ 3, 5 ] }
variables { domain: [ 1, 7 ] }
variables { domain: [ 4, 10 ] }
variables { domain: [ 5, 10 ] }
variables { domain: [ 1, 7 ] }
variables { domain: [ 6, 12 ] }
constraints {
interval {
start { vars: 3 coeffs: 1 }
size { vars: 4 coeffs: 1 }
end { vars: 5 coeffs: 1 }
}
}
constraints {
interval {
start { vars: 6 coeffs: 1 }
size { vars: 7 coeffs: 1 }
end { vars: 8 coeffs: 1 }
}
}
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 1, 1, -1 ]
domain: [ 0, 0 ]
}
}
constraints {
linear {
vars: [ 3, 4, 5 ]
coeffs: [ 1, 1, -1 ]
domain: [ 0, 0 ]
}
}
constraints {
linear {
vars: [ 6, 7, 8 ]
coeffs: [ 1, 1, -1 ]
domain: [ 0, 0 ]
}
}
constraints { no_overlap { intervals: [ 0, 1 ] } }
)pb");
SatParameters params;
params.set_convert_intervals(true);
params.set_cp_model_probing_level(2);
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model,
ModelEqualsIgnoringConstraintsOrder(expected_presolved_model));
}
TEST(PresolveCpModelTest, NoOverlapMergeFixedIntervals) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 20 ] }
variables { domain: [ 3, 6 ] }
variables { domain: [ 3, 26 ] }
variables { domain: [ 0, 20 ] }
variables { domain: [ 3, 6 ] }
variables { domain: [ 3, 26 ] }
constraints {
interval {
start { offset: 0 }
size { offset: 5 }
end { offset: 5 }
}
}
constraints {
interval {
start { offset: 6 }
size { offset: 3 }
end { offset: 9 }
}
}
constraints {
interval {
start { vars: 0 coeffs: 1 }
size { vars: 1 coeffs: 1 }
end { vars: 2 coeffs: 1 }
}
}
constraints {
interval {
start { vars: 3 coeffs: 1 }
size { vars: 4 coeffs: 1 }
end { vars: 5 coeffs: 1 }
}
}
constraints { no_overlap { intervals: [ 0, 1, 2, 3 ] } }
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 20 ] }
variables { domain: [ 3, 6 ] }
variables { domain: [ 3, 26 ] }
variables { domain: [ 0, 20 ] }
variables { domain: [ 3, 6 ] }
variables { domain: [ 3, 26 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
size { vars: 1 coeffs: 1 }
end { vars: 2 coeffs: 1 }
}
}
constraints {
interval {
start { vars: 3 coeffs: 1 }
size { vars: 4 coeffs: 1 }
end { vars: 5 coeffs: 1 }
}
}
constraints {
interval {
start {}
end { offset: 9 }
size { offset: 9 }
}
}
constraints { no_overlap { intervals: [ 0, 1, 2 ] } }
)pb");
SatParameters params;
params.set_convert_intervals(true);
params.set_enumerate_all_solutions(true);
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, NoOverlapNoMergingOfFixedIntervals) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 20 ] }
variables { domain: [ 1, 6 ] }
variables { domain: [ 1, 26 ] }
variables { domain: [ 0, 20 ] }
variables { domain: [ 3, 6 ] }
variables { domain: [ 3, 26 ] }
constraints {
interval {
start { offset: 0 }
size { offset: 5 }
end { offset: 5 }
}
}
constraints {
interval {
start { offset: 6 }
size { offset: 3 }
end { offset: 9 }
}
}
constraints {
interval {
start { vars: 0 coeffs: 1 }
size { vars: 1 coeffs: 1 }
end { vars: 2 coeffs: 1 }
}
}
constraints {
interval {
start { vars: 3 coeffs: 1 }
size { vars: 4 coeffs: 1 }
end { vars: 5 coeffs: 1 }
}
}
constraints { no_overlap { intervals: [ 0, 1, 2, 3 ] } }
)pb");
SatParameters params;
params.set_convert_intervals(true);
params.set_enumerate_all_solutions(true);
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(initial_model));
}
TEST(PresolveCpModelTest, RemoveIsolatedFixedIntervalsBefore) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 5, 20 ] }
variables { domain: [ 3, 6 ] }
variables { domain: [ 8, 26 ] }
variables { domain: [ 5, 20 ] }
variables { domain: [ 3, 6 ] }
variables { domain: [ 8, 26 ] }
constraints {
interval {
start { offset: 0 }
size { offset: 5 }
end { offset: 5 }
}
}
constraints {
interval {
start { vars: 0 coeffs: 1 }
size { vars: 1 coeffs: 1 }
end { vars: 2 coeffs: 1 }
}
}
constraints {
interval {
start { vars: 3 coeffs: 1 }
size { vars: 4 coeffs: 1 }
end { vars: 5 coeffs: 1 }
}
}
constraints { no_overlap { intervals: [ 0, 1, 2 ] } }
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 5, 20 ] }
variables { domain: [ 3, 6 ] }
variables { domain: [ 8, 26 ] }
variables { domain: [ 5, 20 ] }
variables { domain: [ 3, 6 ] }
variables { domain: [ 8, 26 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
size { vars: 1 coeffs: 1 }
end { vars: 2 coeffs: 1 }
}
}
constraints {
interval {
start { vars: 3 coeffs: 1 }
size { vars: 4 coeffs: 1 }
end { vars: 5 coeffs: 1 }
}
}
constraints { no_overlap { intervals: 0 intervals: 1 } }
)pb");
SatParameters params;
params.set_convert_intervals(true);
params.set_enumerate_all_solutions(true);
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, RemoveIsolatedFixedIntervalsAfter) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 5, 20 ] }
variables { domain: [ 3, 6 ] }
variables { domain: [ 8, 26 ] }
variables { domain: [ 5, 20 ] }
variables { domain: [ 3, 6 ] }
variables { domain: [ 8, 26 ] }
constraints {
interval {
start { offset: 26 }
size { offset: 5 }
end { offset: 31 }
}
}
constraints {
interval {
start { vars: 0 coeffs: 1 }
size { vars: 1 coeffs: 1 }
end { vars: 2 coeffs: 1 }
}
}
constraints {
interval {
start { vars: 3 coeffs: 1 }
size { vars: 4 coeffs: 1 }
end { vars: 5 coeffs: 1 }
}
}
constraints { no_overlap { intervals: [ 0, 1, 2 ] } }
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 5, 20 ] }
variables { domain: [ 3, 6 ] }
variables { domain: [ 8, 26 ] }
variables { domain: [ 5, 20 ] }
variables { domain: [ 3, 6 ] }
variables { domain: [ 8, 26 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
size { vars: 1 coeffs: 1 }
end { vars: 2 coeffs: 1 }
}
}
constraints {
interval {
start { vars: 3 coeffs: 1 }
size { vars: 4 coeffs: 1 }
end { vars: 5 coeffs: 1 }
}
}
constraints { no_overlap { intervals: 0 intervals: 1 } }
)pb");
SatParameters params;
params.set_convert_intervals(true);
params.set_enumerate_all_solutions(true);
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, SplitNoOverlap) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 8 ] }
variables { domain: [ 5, 13 ] }
variables { domain: [ 1, 9 ] }
variables { domain: [ 6, 14 ] }
variables { domain: [ 14, 20 ] }
variables { domain: [ 19, 25 ] }
variables { domain: [ 18, 22 ] }
variables { domain: [ 23, 27 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
size { offset: 5 }
end { vars: 1 coeffs: 1 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
size { offset: 5 }
end { vars: 3 coeffs: 1 }
}
}
constraints {
interval {
start { vars: 4 coeffs: 1 }
size { offset: 5 }
end { vars: 5 coeffs: 1 }
}
}
constraints {
interval {
start { vars: 6 coeffs: 1 }
size { offset: 5 }
end { vars: 7 coeffs: 1 }
}
}
constraints { no_overlap { intervals: [ 0, 1, 2, 3 ] } }
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 8 ] }
variables { domain: [ 5, 13 ] }
variables { domain: [ 1, 9 ] }
variables { domain: [ 6, 14 ] }
variables { domain: [ 14, 20 ] }
variables { domain: [ 19, 25 ] }
variables { domain: [ 18, 22 ] }
variables { domain: [ 23, 27 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
size { offset: 5 }
end { vars: 1 coeffs: 1 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
size { offset: 5 }
end { vars: 3 coeffs: 1 }
}
}
constraints {
interval {
start { vars: 4 coeffs: 1 }
size { offset: 5 }
end { vars: 5 coeffs: 1 }
}
}
constraints {
interval {
start { vars: 6 coeffs: 1 }
size { offset: 5 }
end { vars: 7 coeffs: 1 }
}
}
constraints { no_overlap { intervals: 0 intervals: 1 } }
constraints { no_overlap { intervals: 2 intervals: 3 } }
)pb");
SatParameters params;
params.set_convert_intervals(true);
params.set_enumerate_all_solutions(true);
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model,
ModelEqualsIgnoringConstraintsOrder(expected_presolved_model));
}
TEST(PresolveCpModelTest, NoOverlapDuplicateNonZeroSizedInterval) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: 0 domain: 1 }
constraints {
interval {
start { offset: 1 }
end { offset: 1 }
size { vars: 0 coeffs: 5 offset: 3 }
}
}
constraints { no_overlap { intervals: 0 intervals: 0 } }
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model =
PresolveForTest(initial_model, params, CpSolverStatus::INFEASIBLE);
}
TEST(PresolveCpModelTest, NoOverlapDuplicatePossiblyZeroSizedInterval) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: 0 domain: 1 }
constraints {
interval {
start { offset: 1 }
end { offset: 1 }
size { vars: 0 coeffs: 5 }
}
}
constraints { no_overlap { intervals: 0 intervals: 0 } }
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: 0 domain: 0 }
)pb");
const CpModelProto presolved_model = GetReducedDomains(initial_model);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, NoOverlapDuplicateOptionalPossiblyZeroSizedInterval) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: 0 domain: 3 }
variables { domain: 0 domain: 1 }
constraints {
enforcement_literal: 1
interval {
start { offset: 1 }
end { offset: 1 }
size { vars: 0 coeffs: 5 }
}
}
constraints { no_overlap { intervals: 0 intervals: 0 } }
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: 0 domain: 3 }
variables { domain: 0 domain: 1 }
constraints {
enforcement_literal: 1
linear { vars: 0 coeffs: 1 domain: 0 domain: 0 }
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, CumulativeWithNoInterval) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 5, 5 ] }
constraints {
cumulative {
intervals: []
demands: []
capacity { offset: 0 }
}
}
)pb");
CpModelProto presolved_model = PresolveForTest(initial_model);
EXPECT_EQ(presolved_model.constraints_size(), 0);
}
TEST(PresolveCpModelTest, CumulativeWithUnperformedIntervals) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 1 ] }
constraints {
enforcement_literal: 1
interval {
start { vars: 0 coeffs: 1 }
size { offset: 2 }
end { vars: 0 coeffs: 1 offset: 2 }
}
}
constraints {
enforcement_literal: 3
interval {
start { vars: 2 coeffs: 1 }
size { offset: 2 }
end { vars: 2 coeffs: 1 offset: 2 }
}
}
constraints {
cumulative {
intervals: [ 0, 1 ]
demands { offset: 2 }
demands { offset: 3 }
capacity { offset: 4 }
}
}
constraints {
linear {
vars: 1
coeffs: 1
domain: [ 0, 0 ]
}
}
constraints {
linear {
vars: 2
coeffs: 1
domain: [ 0, 0 ]
}
}
)pb");
CpModelProto presolved_model = PresolveForTest(initial_model);
EXPECT_EQ(presolved_model.constraints_size(), 0);
}
TEST(PresolveCpModelTest, SplitCumulative) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 8 ] }
variables { domain: [ 1, 9 ] }
variables { domain: [ 2, 7 ] }
variables { domain: [ 13, 20 ] }
variables { domain: [ 18, 22 ] }
variables { domain: [ 16, 30 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 0 coeffs: 1 offset: 4 }
size { offset: 4 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
end { vars: 1 coeffs: 1 offset: 4 }
size { offset: 4 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
end { vars: 2 coeffs: 1 offset: 4 }
size { offset: 4 }
}
}
constraints {
interval {
start { vars: 3 coeffs: 1 }
end { vars: 3 coeffs: 1 offset: 4 }
size { offset: 4 }
}
}
constraints {
interval {
start { vars: 4 coeffs: 1 }
end { vars: 4 coeffs: 1 offset: 4 }
size { offset: 4 }
}
}
constraints {
interval {
start { vars: 5 coeffs: 1 }
end { vars: 5 coeffs: 1 offset: 4 }
size { offset: 4 }
}
}
constraints {
cumulative {
intervals: [ 0, 1, 2, 3, 4, 5 ],
demands { offset: 1 }
demands { offset: 1 }
demands { offset: 1 }
demands { offset: 1 }
demands { offset: 1 }
demands { offset: 1 }
capacity: { offset: 2 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 8 ] }
variables { domain: [ 1, 9 ] }
variables { domain: [ 2, 7 ] }
variables { domain: [ 13, 20 ] }
variables { domain: [ 18, 22 ] }
variables { domain: [ 16, 30 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 0 coeffs: 1 offset: 4 }
size { offset: 4 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
end { vars: 1 coeffs: 1 offset: 4 }
size { offset: 4 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
end { vars: 2 coeffs: 1 offset: 4 }
size { offset: 4 }
}
}
constraints {
interval {
start { vars: 3 coeffs: 1 }
end { vars: 3 coeffs: 1 offset: 4 }
size { offset: 4 }
}
}
constraints {
interval {
start { vars: 4 coeffs: 1 }
end { vars: 4 coeffs: 1 offset: 4 }
size { offset: 4 }
}
}
constraints {
interval {
start { vars: 5 coeffs: 1 }
end { vars: 5 coeffs: 1 offset: 4 }
size { offset: 4 }
}
}
constraints {
cumulative {
intervals: [ 0, 1, 2 ],
demands { offset: 1 }
demands { offset: 1 }
demands { offset: 1 }
capacity { offset: 2 }
}
}
constraints {
cumulative {
intervals: [ 3, 5, 4 ],
demands { offset: 1 }
demands { offset: 1 }
demands { offset: 1 }
capacity: { offset: 2 }
}
}
)pb");
SatParameters params;
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, CumulativeZeroDemands) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 8 ] }
variables { domain: [ 1, 9 ] }
variables { domain: [ 2, 7 ] }
variables { domain: [ 2, 4 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 0 coeffs: 1 offset: 4 }
size { offset: 4 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
end { vars: 1 coeffs: 1 offset: 4 }
size { offset: 4 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
end { vars: 2 coeffs: 1 offset: 4 }
size { offset: 4 }
}
}
constraints {
cumulative {
intervals: [ 0, 1, 2 ],
demands:
[ { offset: 0 }
, { offset: 2 }
, { vars: 3 coeffs: 1 }],
capacity: { offset: 4 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 8 ] }
variables { domain: [ 1, 9 ] }
variables { domain: [ 2, 7 ] }
variables { domain: [ 2, 4 ] }
constraints {
interval {
start { vars: 1 coeffs: 1 }
end { vars: 1 coeffs: 1 offset: 4 }
size { offset: 4 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
end { vars: 2 coeffs: 1 offset: 4 }
size { offset: 4 }
}
}
constraints {
cumulative {
intervals: [ 0, 1 ],
demands:
[ { offset: 2 }
, { vars: 3 coeffs: 1 }],
capacity: { offset: 4 }
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, CumulativeDemandsDoNotFit) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 8 ] }
variables { domain: [ 1, 9 ] }
variables { domain: [ 2, 7 ] }
variables { domain: [ 2, 8 ] }
variables { domain: [ 0, 1 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 0 coeffs: 1 offset: 4 }
size { offset: 4 }
}
}
constraints {
enforcement_literal: 4
interval {
start { vars: 1 coeffs: 1 }
end { vars: 1 coeffs: 1 offset: 3 }
size { offset: 3 }
}
}
constraints {
cumulative {
intervals: [ 0, 1 ],
demands:
[ { vars: 2 coeffs: 1 }
, { vars: 3 coeffs: 1 }],
capacity: { offset: 4 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 8 ] }
variables { domain: [ 1, 9 ] }
variables { domain: [ 2, 4 ] }
variables { domain: [ 2, 8 ] }
variables { domain: [ 0, 1 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 0 coeffs: 1 offset: 4 }
size { offset: 4 }
}
}
constraints {
enforcement_literal: 4
interval {
start { vars: 1 coeffs: 1 }
end { vars: 1 coeffs: 1 offset: 3 }
size { offset: 3 }
}
}
constraints {
cumulative {
intervals: [ 0, 1 ],
demands:
[ { vars: 2 coeffs: 1 }
, { vars: 3 coeffs: 1 }],
capacity: { offset: 4 }
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, CumulativeDemandsDoNotFitSizeMinZero) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 4 ] }
variables { domain: [ 0, 4 ] }
variables { domain: [ 0, 4 ] }
variables { domain: [ 0, 4 ] }
variables { domain: [ 0, 4 ] }
variables { domain: [ 0, 4 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
linear {
vars: [ 3, 6, 0 ]
coeffs: [ -1, 1, 1 ]
domain: [ 0, 0 ]
}
}
constraints {
interval {
start {
vars: [ 0 ]
coeffs: [ 1 ]
}
end {
vars: [ 3 ]
coeffs: [ 1 ]
}
size {
vars: [ 6 ]
coeffs: [ 1 ]
}
}
}
constraints {
linear {
vars: [ 4, 7, 1 ]
coeffs: [ -1, 1, 1 ]
domain: [ 0, 0 ]
}
}
constraints {
interval {
start {
vars: [ 1 ]
coeffs: [ 1 ]
}
end {
vars: [ 4 ]
coeffs: [ 1 ]
}
size {
vars: [ 7 ]
coeffs: [ 1 ]
}
}
}
constraints {
linear {
vars: [ 5, 8, 2 ]
coeffs: [ -1, 1, 1 ]
domain: [ 0, 0 ]
}
}
constraints {
interval {
start {
vars: [ 2 ]
coeffs: [ 1 ]
}
end {
vars: [ 5 ]
coeffs: [ 1 ]
}
size {
vars: [ 8 ]
coeffs: [ 1 ]
}
}
}
constraints {
cumulative {
capacity { offset: 1 }
intervals: [ 1, 3, 5 ]
demands { offset: 1 }
demands { offset: 5 }
demands { offset: 1 }
}
}
)pb");
EXPECT_EQ(Solve(initial_model).status(), CpSolverStatus::OPTIMAL);
}
TEST(PresolveCpModelTest, CumulativeRemoveIncompativeDemands) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 8 ] }
variables { domain: [ 0, 8 ] }
variables { domain: [ 1, 9 ] }
variables { domain: [ 2, 4 ] }
variables { domain: [ 5, 8 ] }
variables { domain: [ 0, 1 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 0 coeffs: 1 offset: 4 }
size { offset: 4 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
end { vars: 1 coeffs: 1 offset: 4 }
size { offset: 4 }
}
}
constraints {
enforcement_literal: 5
interval {
start { vars: 2 coeffs: 1 }
end { vars: 2 coeffs: 1 offset: 3 }
size { offset: 3 }
}
}
constraints {
cumulative {
intervals: [ 0, 1, 2 ],
demands: { vars: 3 coeffs: 1 }
demands: { vars: 3 coeffs: 1 }
demands: { vars: 4 coeffs: 1 }
capacity: { offset: 4 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 8 ] }
variables { domain: [ 0, 8 ] }
variables { domain: [ 1, 9 ] }
variables { domain: [ 2, 4 ] }
variables { domain: [ 5, 8 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 0 coeffs: 1 offset: 4 }
size { offset: 4 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
end { vars: 1 coeffs: 1 offset: 4 }
size { offset: 4 }
}
}
constraints {
cumulative {
intervals: [ 0, 1 ],
demands: { vars: 3 coeffs: 1 }
demands: { vars: 3 coeffs: 1 }
capacity: { offset: 4 }
}
}
)pb");
SatParameters params;
// This will force all variables to be kept, even if unused.
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, CumulativeGcdDemands) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 8 ] }
variables { domain: [ 1, 9 ] }
variables { domain: [ 2, 7 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 0 coeffs: 1 offset: 4 }
size { offset: 4 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
end { vars: 1 coeffs: 1 offset: 3 }
size { offset: 3 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
end { vars: 2 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
cumulative {
intervals: [ 0, 1, 2 ],
demands: { offset: 2 }
demands: { offset: 2 }
demands: { offset: 2 }
capacity: { offset: 4 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 8 ] }
variables { domain: [ 1, 9 ] }
variables { domain: [ 2, 7 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 0 coeffs: 1 offset: 4 }
size { offset: 4 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
end { vars: 1 coeffs: 1 offset: 3 }
size { offset: 3 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
end { vars: 2 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
cumulative {
intervals: [ 0, 1, 2 ],
demands: { offset: 1 }
demands: { offset: 1 }
demands: { offset: 1 }
capacity: { offset: 2 }
}
}
)pb");
SatParameters params;
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, NoOverlap2DOneBox) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 5, 15 ] }
variables { domain: [ 5, 5 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 5, 15 ] }
variables { domain: [ 5, 5 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 1 coeffs: 1 }
size { vars: 2 coeffs: 1 }
}
}
constraints {
interval {
start { vars: 3 coeffs: 1 }
end { vars: 4 coeffs: 1 }
size { vars: 5 coeffs: 1 }
}
}
constraints { no_overlap_2d { x_intervals: 0 y_intervals: 1 } }
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 5, 15 ] }
variables { domain: [ 5, 5 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 5, 15 ] }
variables { domain: [ 5, 5 ] }
)pb");
const CpModelProto presolved_model = GetReducedDomains(initial_model);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, NoOverlap2DRemoveInactiveBoxes) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 5, 15 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 5, 15 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 5, 15 ] }
variables { domain: [ 0, 0 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 1 coeffs: 1 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
end { vars: 3 coeffs: 1 }
size { offset: 5 }
}
}
constraints {
enforcement_literal: 6
interval {
start { vars: 4 coeffs: 1 }
end { vars: 5 coeffs: 1 }
size { offset: 5 }
}
}
constraints {
no_overlap_2d {
x_intervals: [ 0, 1, 2 ]
y_intervals: [ 0, 1, 2 ]
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 5, 15 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 5, 15 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 5, 15 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 1 coeffs: 1 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
end { vars: 3 coeffs: 1 }
size { offset: 5 }
}
}
constraints {
no_overlap_2d {
x_intervals: [ 0, 1 ]
y_intervals: [ 0, 1 ]
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, NoOverlap2DNoRemoveNullAreaBoxes) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 5, 15 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 5, 15 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 5, 15 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 1 coeffs: 1 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
end { vars: 3 coeffs: 1 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 4 coeffs: 1 }
end { vars: 5 coeffs: 1 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 6 coeffs: 1 }
end { vars: 7 coeffs: 1 }
size {}
}
}
constraints {
no_overlap_2d {
x_intervals: [ 0, 1, 2 ]
y_intervals: [ 0, 1, 3 ]
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(initial_model));
}
TEST(PresolveCpModelTest, NoOverlap2DSplitBoxes) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 5 ] } # 0: start 0
variables { domain: [ 2, 4 ] } # 3: start 1
variables { domain: [ 8, 12 ] } # 6: start 2
variables { domain: [ 9, 13 ] } # 9: start 3
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 0 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
end { vars: 1 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
end { vars: 2 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
interval {
start { vars: 3 coeffs: 1 }
end { vars: 3 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
no_overlap_2d {
x_intervals: [ 0, 1, 2, 3 ]
y_intervals: [ 0, 1, 2, 3 ]
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 5 ] }
variables { domain: [ 2, 4 ] }
variables { domain: [ 8, 12 ] }
variables { domain: [ 9, 13 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 0 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
end { vars: 1 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
end { vars: 2 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
interval {
start { vars: 3 coeffs: 1 }
end { vars: 3 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
no_overlap_2d {
x_intervals: 0
x_intervals: 1
y_intervals: 0
y_intervals: 1
}
}
constraints {
no_overlap_2d {
x_intervals: 2
x_intervals: 3
y_intervals: 2
y_intervals: 3
}
}
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, NoOverlap2DSplitSingletonBoxes) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 5 ] }
variables { domain: [ 2, 4 ] }
variables { domain: [ 8, 12 ] } # Disjoint from the other two
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 0 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
end { vars: 1 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
end { vars: 2 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
no_overlap_2d {
x_intervals: [ 0, 1, 2 ]
y_intervals: [ 0, 1, 2 ]
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 5 ] }
variables { domain: [ 2, 4 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 0 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
end { vars: 1 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
no_overlap_2d {
x_intervals: [ 0, 1 ]
y_intervals: [ 0, 1 ]
}
}
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, NoOverlap2DMerge) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 0 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
end { vars: 1 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
end { vars: 2 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 3 coeffs: 1 }
end { vars: 3 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 4 coeffs: 1 }
end { vars: 4 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 5 coeffs: 1 }
end { vars: 5 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
no_overlap_2d {
x_intervals: [ 0, 1, 2, 3 ]
y_intervals: [ 0, 1, 2, 3 ]
}
}
constraints {
no_overlap_2d {
x_intervals: [ 0, 1, 4, 5 ]
y_intervals: [ 0, 1, 4, 5 ]
}
}
constraints {
no_overlap_2d {
x_intervals: [ 2, 3, 4, 5 ]
y_intervals: [ 2, 3, 4, 5 ]
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 0 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
end { vars: 1 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
end { vars: 2 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 3 coeffs: 1 }
end { vars: 3 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 4 coeffs: 1 }
end { vars: 4 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 5 coeffs: 1 }
end { vars: 5 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
no_overlap_2d {
x_intervals: [ 0, 1, 2, 3, 5, 4 ]
y_intervals: [ 0, 1, 2, 3, 5, 4 ]
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, NoOverlap2DMergePartial) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 0 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
end { vars: 1 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
end { vars: 2 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 3 coeffs: 1 }
end { vars: 3 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 4 coeffs: 1 }
end { vars: 4 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 5 coeffs: 1 }
end { vars: 5 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
no_overlap_2d {
x_intervals: [ 0, 1, 2, 3, 4 ]
y_intervals: [ 0, 1, 2, 3, 4 ]
}
}
constraints {
no_overlap_2d {
x_intervals: [ 0, 1, 3, 4, 5 ]
y_intervals: [ 0, 1, 3, 4, 5 ]
}
}
constraints {
no_overlap_2d {
x_intervals: [ 1, 3, 4, 5 ]
y_intervals: [ 1, 3, 4, 5 ]
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 0 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
end { vars: 1 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
end { vars: 2 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 3 coeffs: 1 }
end { vars: 3 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 4 coeffs: 1 }
end { vars: 4 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 5 coeffs: 1 }
end { vars: 5 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
no_overlap_2d {
x_intervals: [ 0, 1, 2, 3, 4 ]
y_intervals: [ 0, 1, 2, 3, 4 ]
}
}
constraints {
no_overlap_2d {
x_intervals: [ 0, 1, 3, 4, 5 ]
y_intervals: [ 0, 1, 3, 4, 5 ]
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, NoOverlap2DMergeWithOverlaps) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 0 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
end { vars: 1 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
end { vars: 2 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 3 coeffs: 1 }
end { vars: 3 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 4 coeffs: 1 }
end { vars: 4 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 5 coeffs: 1 }
end { vars: 5 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
no_overlap_2d {
x_intervals: [ 0, 1, 2, 3, 4 ]
y_intervals: [ 0, 1, 2, 3, 4 ]
}
}
constraints {
no_overlap_2d {
x_intervals: [ 0, 1, 2, 4, 5 ]
y_intervals: [ 0, 1, 2, 4, 5 ]
}
}
constraints {
no_overlap_2d {
x_intervals: [ 1, 3, 4, 5 ]
y_intervals: [ 1, 3, 4, 5 ]
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 0 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
end { vars: 1 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
end { vars: 2 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 3 coeffs: 1 }
end { vars: 3 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 4 coeffs: 1 }
end { vars: 4 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
interval {
start { vars: 5 coeffs: 1 }
end { vars: 5 coeffs: 1 offset: 5 }
size { offset: 5 }
}
}
constraints {
no_overlap_2d {
x_intervals: [ 0, 1, 2, 3, 4, 5 ]
y_intervals: [ 0, 1, 2, 3, 4, 5 ]
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, IntProdWithLeftConstant) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables {
name: 'x'
domain: [ 10, 12 ]
}
variables {
name: 'y'
domain: [ 2, 2 ]
}
variables {
name: 'p'
domain: [ 0, 100 ]
}
constraints {
int_prod {
target { vars: 2 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables {
name: 'x'
domain: [ 10, 12 ]
}
variables {
name: 'y'
domain: [ 2, 2 ]
}
variables {
name: 'p'
domain: [ 20, 24 ]
}
constraints {
linear {
vars: 2
vars: 0
coeffs: 1
coeffs: -2
domain: [ 0, 0 ]
}
}
)pb");
const CpModelProto presolved_model =
PresolveOneConstraint(initial_model, /*constraint_index=*/0);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, EnforcedIntProdWithLeftConstant) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 10, 12 ] }
variables { domain: [ 2, 2 ] }
variables { domain: [ 0, 100 ] }
variables { domain: [ 0, 1 ] }
constraints {
enforcement_literal: 3
int_prod {
target { vars: 2 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 10, 12 ] }
variables { domain: [ 2, 2 ] }
variables { domain: [ 0, 100 ] }
variables { domain: [ 0, 1 ] }
constraints {
enforcement_literal: 3
linear {
vars: 2
vars: 0
coeffs: 1
coeffs: -2
domain: [ 0, 0 ]
}
}
)pb");
const CpModelProto presolved_model =
PresolveOneConstraint(initial_model, /*constraint_index=*/0);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, IntProdWithRightConstant) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables {
name: 'x'
domain: [ 10, 14 ]
}
variables {
name: 'y'
domain: [ 2, 2 ]
}
variables {
name: 'p'
domain: [ 0, 100 ]
}
constraints {
int_prod {
target { vars: 2 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables {
name: 'x'
domain: [ 10, 14 ]
}
variables {
name: 'y'
domain: [ 2, 2 ]
}
variables {
name: 'p'
domain: [ 20, 28 ]
}
constraints {
linear {
vars: 2
vars: 0
coeffs: 1
coeffs: -2
domain: [ 0, 0 ]
}
}
)pb");
const CpModelProto presolved_model =
PresolveOneConstraint(initial_model, /*constraint_index=*/0);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, IntProdWithXEqualTwoX) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -10, 20 ] }
variables { domain: [ 2, 2 ] }
constraints {
int_prod {
target { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
}
}
)pb");
const CpModelProto mapping_model = GetMappingModel(initial_model);
const CpModelProto expected_mapping_model = ParseTestProto(R"pb(
variables { domain: [ 0, 0 ] }
variables { domain: [ 2, 2 ] }
)pb");
EXPECT_THAT(expected_mapping_model, testing::EqualsProto(mapping_model));
}
TEST(PresolveCpModelTest, IntProdWithConstantProduct) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 2000 ] }
variables { domain: [ 2, 2 ] }
variables { domain: [ 5, 5 ] }
constraints {
int_prod {
target { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
}
}
)pb");
const CpModelProto mapping_model = GetMappingModel(initial_model);
const CpModelProto expected_mapping_model = ParseTestProto(R"pb(
variables { domain: [ 10, 10 ] }
variables { domain: [ 2, 2 ] }
variables { domain: [ 5, 5 ] }
constraints {
int_prod {
target { vars: 0 coeffs: 1 }
exprs { offset: 2 }
exprs { offset: 5 }
}
}
)pb");
EXPECT_THAT(expected_mapping_model, testing::EqualsProto(mapping_model));
}
TEST(PresolveCpModelTest, AlwaysFalseIntProd) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 20, 30 ] }
variables { domain: [ 2, 2 ] }
variables { domain: [ 5, 5 ] }
constraints {
int_prod {
target { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
}
}
)pb");
PresolveForTest(initial_model, SatParameters(), CpSolverStatus::INFEASIBLE);
}
TEST(PresolveCpModelTest, EnforcedAlwaysFalseIntProd) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 20, 30 ] }
variables { domain: [ 2, 2 ] }
variables { domain: [ 5, 5 ] }
variables { domain: [ 0, 1 ] }
constraints {
enforcement_literal: 3
int_prod {
target { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
}
}
)pb");
const CpModelProto expected_presolved_model;
const CpModelProto presolved_model = PresolveForTest(initial_model);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, IntProdWithOverflow) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -100000000000, 100000000000 ] }
variables { domain: [ 0, 0, 100000000000, 100000000000 ] }
variables { domain: [ 0, 0, 100000000000, 100000000000 ] }
constraints {
int_prod {
target { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
}
}
constraints { dummy_constraint { vars: [ 0, 1, 2 ] } }
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 0 ] }
variables { domain: [ 0, 0, 100000000000, 100000000000 ] }
variables { domain: [ 0, 0, 100000000000, 100000000000 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
enforcement_literal: 3
bool_and { literals: -5 }
}
constraints {
linear {
vars: 1
vars: 3
coeffs: 1
coeffs: -100000000000
domain: 0
domain: 0
}
}
constraints {
linear {
vars: 2
vars: 4
coeffs: 1
coeffs: -100000000000
domain: 0
domain: 0
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, IntProdWithOverflowLargeConstantFactor) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: 0 domain: 266 }
constraints {
int_prod {
target { offset: 1862270976 }
exprs { offset: 1862270975 }
exprs { vars: 0 coeffs: 250970374144 offset: 1 }
}
}
)pb");
EXPECT_EQ(Solve(initial_model).status(), CpSolverStatus::INFEASIBLE);
}
TEST(PresolveCpModelTest, IntProdWithOverflowLargeNegativeConstantFactor) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: 0 domain: 266 }
constraints {
int_prod {
target { offset: -1862270976 }
exprs { offset: -1862270975 }
exprs { vars: 0 coeffs: 250970374144 offset: 1 }
}
}
)pb");
EXPECT_EQ(Solve(initial_model).status(), CpSolverStatus::INFEASIBLE);
}
TEST(PresolveCpModelTest, IntProdWithIdentity) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -10, 20 ] }
variables { domain: [ 1, 1 ] }
constraints {
int_prod {
target { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
}
}
)pb");
const CpModelProto mapping_model = GetMappingModel(initial_model);
const CpModelProto expected_mapping_model = ParseTestProto(R"pb(
variables { domain: 0 domain: 0 }
variables { domain: 1 domain: 1 }
)pb");
EXPECT_THAT(mapping_model, testing::EqualsProto(expected_mapping_model));
}
TEST(PresolveCpModelTest, IntProdWithXEqualX2) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -10, 20 ] }
constraints {
int_prod {
target { vars: 0 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
)pb");
const CpModelProto presolved_model =
PresolveOneConstraint(initial_model, /*constraint_index=*/0);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, IntSquareDomainReduction) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -3, 5 ] }
variables { domain: [ -30, 30 ] }
constraints {
int_prod {
target { vars: 1 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ -3, 5 ] }
variables { domain: [ 0, 1, 4, 4, 9, 9, 16, 16, 25, 25 ] }
constraints {
int_prod {
target { vars: 1 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
}
}
)pb");
const CpModelProto presolved_model =
PresolveOneConstraint(initial_model, /*constraint_index=*/0);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, IntSquareLargeDomainReduction) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -20, 110 ] }
variables { domain: [ -200000, 200000 ] }
constraints {
int_prod {
target { vars: 1 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ -20, 110 ] }
variables { domain: [ 0, 12100 ] }
constraints {
int_prod {
target { vars: 1 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
}
}
)pb");
const CpModelProto presolved_model =
PresolveOneConstraint(initial_model, /*constraint_index=*/0);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, IntSquareExprDomainReduction) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -20, 110 ] }
variables { domain: [ -9000, 9000 ] }
constraints {
int_prod {
target { vars: 1 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ -20, 94 ] }
variables { domain: [ 0, 9000 ] }
constraints {
int_prod {
target { vars: 1 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
}
}
)pb");
const CpModelProto presolved_model =
PresolveOneConstraint(initial_model, /*constraint_index=*/0);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, IntProdWithAffineRelation) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -10, 20 ] }
variables { domain: [ 0, 5 ] }
variables { domain: [ 0, 0, 3, 3, 6, 6, 9, 9 ] }
constraints {
int_prod {
target { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
}
}
# Add this just to avoid triggering the rule of unused target variable.
objective {
vars: [ 0, 1 ]
coeffs: [ 1, 1 ]
}
)pb");
// The variable 2 is detected to be of the form 3 * new_var1. Subsequently,
// the product target is detected to be a multiple of 3, so its target is
// replaced by new_var2. The domain are computed accordingly.
CpModelProto presolved_model = PresolveForTest(initial_model);
presolved_model.clear_objective();
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 6 ] } # This is old_var_0 / 3.
variables { domain: [ 0, 5 ] }
variables { domain: [ 0, 3 ] } # This is old_var_2 / 3.
constraints {
int_prod {
target { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
}
}
)pb");
EXPECT_THAT(presolved_model, EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, EnforcedIntProdWithAffineRelation) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -10, 20 ] }
variables { domain: [ 0, 5 ] }
variables { domain: [ 0, 0, 3, 3, 6, 6, 9, 9 ] }
variables { domain: [ 0, 1 ] }
constraints {
enforcement_literal: 3
int_prod {
target { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
}
}
# Add this just to avoid triggering the rule of unused target variable.
objective {
vars: [ 0, 1, 3 ]
coeffs: [ 1, 1, -1 ]
}
)pb");
// The variable 2 is detected to be of the form 3 * new_var1. Subsequently,
// the product target is detected to be a multiple of 3, so its target is
// replaced by new_var2. The domain are computed accordingly.
CpModelProto presolved_model = PresolveForTest(initial_model);
presolved_model.clear_objective();
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 9 ] } # This is old_var_0 / 3.
variables { domain: [ 0, 5 ] }
variables { domain: [ 0, 3 ] } # This is old_var_2 / 3.
variables { domain: [ 0, 1 ] }
constraints {
enforcement_literal: 3
int_prod {
target { vars: 0 coeffs: 1 offset: -3 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
}
}
)pb");
EXPECT_THAT(presolved_model, EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, IntProdCoeffDividesTarget) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 3, 9 ] }
variables { domain: [ 1, 10 ] }
variables { domain: [ 0, 1000 ] }
constraints {
int_prod {
target { vars: 2 coeffs: 10 offset: 20 }
exprs { vars: 0 coeffs: 1 offset: 3 }
exprs { vars: 1 coeffs: 5 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 3, 9 ] }
variables { domain: [ 1, 10 ] }
variables { domain: [ 1, 58 ] }
constraints {
int_prod {
target { vars: 2 coeffs: 2 offset: 4 }
exprs { vars: 0 coeffs: 1 offset: 3 }
exprs { vars: 1 coeffs: 1 }
}
}
)pb");
const CpModelProto presolved_model =
PresolveOneConstraint(initial_model, /*constraint_index=*/0);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, EnforcedIntProdCoeffDividesTarget) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 3, 9 ] }
variables { domain: [ 1, 10 ] }
variables { domain: [ 0, 1000 ] }
variables { domain: [ 0, 1 ] }
constraints {
enforcement_literal: 3
int_prod {
target { vars: 2 coeffs: 10 offset: 20 }
exprs { vars: 0 coeffs: 1 offset: 3 }
exprs { vars: 1 coeffs: 5 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 3, 9 ] }
variables { domain: [ 1, 10 ] }
variables { domain: [ 0, 1000 ] }
variables { domain: [ 0, 1 ] }
constraints {
enforcement_literal: 3
int_prod {
target { vars: 2 coeffs: 2 offset: 4 }
exprs { vars: 0 coeffs: 1 offset: 3 }
exprs { vars: 1 coeffs: 1 }
}
}
)pb");
const CpModelProto presolved_model =
PresolveOneConstraint(initial_model, /*constraint_index=*/0);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, IntProdGlobalGcd) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 3, 9 ] }
variables { domain: [ 1, 10 ] }
variables { domain: [ 0, 200 ] }
constraints {
int_prod {
target { vars: 2 coeffs: 9 offset: 18 }
exprs { vars: 0 coeffs: 2 offset: 4 }
exprs { vars: 1 coeffs: 6 offset: -6 }
}
}
)pb");
// The gcd is 12 !
// So we have 9 * target + 18 is a multiple of 12, so target can be for
// instance written 4 * new_target + 2.
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 3, 9 ] }
variables { domain: [ 1, 10 ] }
variables {
domain: [ 0, 10, 12, 14, 16, 16, 18, 20, 22, 22, 25, 25, 28, 28, 31, 31 ]
} # We divide by 4
constraints {
int_prod {
target { vars: 2 coeffs: 3 offset: 6 }
exprs { vars: 0 coeffs: 1 offset: 2 }
exprs { vars: 1 coeffs: 1 offset: -1 }
}
}
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, NullProduct) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -10, 20 ] }
variables { domain: [ 0, 5 ] }
variables { domain: [ 0, 0 ] }
constraints {
int_prod {
target { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
}
}
constraints { dummy_constraint { vars: [ 0, 1, 2 ] } }
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 0 ] }
variables { domain: [ 0, 5 ] } # Many possible values here.
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, EnforcedNullProduct) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -10, 20 ] }
variables { domain: [ 0, 5 ] }
variables { domain: [ 0, 0 ] }
variables { domain: [ 0, 1 ] }
constraints {
enforcement_literal: 3
int_prod {
target { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
}
}
constraints { dummy_constraint { vars: [ 0, 1, 2 ] } }
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ -10, 20 ] }
variables { domain: [ 0, 5 ] } # Many possible values here.
variables { domain: [ 0, 0 ] }
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, BooleanProduct) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
enforcement_literal: 5
int_prod {
target { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: -1 offset: 1 }
exprs { vars: 3 coeffs: 1 }
exprs { vars: 4 coeffs: -1 offset: 1 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
enforcement_literal: 5
enforcement_literal: 0
bool_and { literals: [ 1, -3, 3, -5 ] }
}
constraints { bool_or { literals: [ -6, -4, -2, 0, 2, 4 ] } }
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
params.set_permute_variable_randomly(false);
params.set_cp_model_probing_level(0);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, AffineBooleanProduct) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 30 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 2 ] }
constraints {
int_prod {
target { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 2 offset: 3 }
exprs { vars: 2 coeffs: 3 offset: 2 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 6, 6, 10, 10, 15, 15, 24, 25 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 2 ] }
constraints {
enforcement_literal: -2
linear { vars: 0 vars: 2 coeffs: 1 coeffs: -9 domain: 6 domain: 6 }
}
constraints {
enforcement_literal: 1
linear { vars: 0 vars: 2 coeffs: 1 coeffs: -15 domain: 10 domain: 10 }
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
params.set_permute_variable_randomly(false);
params.set_cp_model_probing_level(0);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, EnforcedAffineBooleanProduct) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 30 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 2 ] }
variables { domain: [ 0, 1 ] }
constraints {
enforcement_literal: 3
int_prod {
target { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 2 offset: 3 }
exprs { vars: 2 coeffs: 3 offset: 2 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 30 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 2 ] }
variables { domain: [ 0, 1 ] }
constraints {
enforcement_literal: [ 3, -2 ]
linear { vars: 0 vars: 2 coeffs: 1 coeffs: -9 domain: 6 domain: 6 }
}
constraints {
enforcement_literal: [ 3, 1 ]
linear { vars: 0 vars: 2 coeffs: 1 coeffs: -15 domain: 10 domain: 10 }
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
params.set_permute_variable_randomly(false);
params.set_cp_model_probing_level(0);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, IntDivSimplification) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 3, 20 ] }
variables { domain: [ -5, 5 ] }
constraints {
int_div {
target { vars: 1 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 3, 20 ] }
variables { domain: [ 1, 1 ] }
)pb");
const CpModelProto presolved_model = GetReducedDomains(initial_model);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, IntDivSingleVariable) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 20 ] }
constraints {
int_div {
target { vars: 0 coeffs: -6 offset: 12 }
exprs { offset: 12 }
exprs { vars: 0 coeffs: 1 offset: 1 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, IntDivSimplificationOpp) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 3, 20 ] }
variables { domain: [ -5, 5 ] }
constraints {
int_div {
target { vars: 1 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
exprs { vars: 0 coeffs: -1 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 3, 20 ] }
variables { domain: [ -1, -1 ] }
)pb");
const CpModelProto presolved_model = GetReducedDomains(initial_model);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, PositiveFixedTargetAndPositiveDivisor) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -30, 30 ] }
constraints {
int_div {
target { offset: 3 }
exprs { vars: 0 coeffs: 1 }
exprs { offset: 5 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 15, 19 ] }
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, ZeroFixedTargetAndPositiveDivisor) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -30, 30 ] }
constraints {
int_div {
target { offset: 0 }
exprs { vars: 0 coeffs: 1 }
exprs { offset: 5 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ -4, 4 ] }
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, NegativeFixedTargetAndPositiveDivisor) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -30, 30 ] }
constraints {
int_div {
target { offset: -3 }
exprs { vars: 0 coeffs: 1 }
exprs { offset: 5 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ -19, -15 ] }
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, PositiveFixedTargetAndNegativeDivisor) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -30, 30 ] }
constraints {
int_div {
target { offset: 3 }
exprs { vars: 0 coeffs: 1 }
exprs { offset: -5 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ -19, -15 ] }
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, ZeroFixedTargetAndNegativeDivisor) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -30, 30 ] }
constraints {
int_div {
target { offset: 0 }
exprs { vars: 0 coeffs: 1 }
exprs { offset: -5 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ -4, 4 ] }
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, NegativeFixedTargetAndNegativeDivisor) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -30, 30 ] }
constraints {
int_div {
target { offset: -3 }
exprs { vars: 0 coeffs: 1 }
exprs { offset: -5 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 15, 19 ] }
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, TargetFixedToPositiveValue) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 210, 288 ] }
variables { domain: [ -30, 30 ] }
constraints {
int_div {
target { vars: 1 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
exprs { offset: 100 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 210, 288 ] }
variables { domain: [ 2, 2 ] }
)pb");
const CpModelProto presolved_model = GetReducedDomains(initial_model);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, TargetFixedToZeroValue) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -55, 75 ] }
variables { domain: [ -30, 30 ] }
constraints {
int_div {
target { vars: 1 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
exprs { offset: 100 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ -55, 75 ] }
variables { domain: [ 0, 0 ] }
)pb");
const CpModelProto presolved_model = GetReducedDomains(initial_model);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, TargetFixedToNegativeValue) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 210, 288 ] }
variables { domain: [ -30, 30 ] }
constraints {
int_div {
target { vars: 1 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
exprs { offset: -100 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 210, 288 ] }
variables { domain: [ -2, -2 ] }
)pb");
const CpModelProto presolved_model = GetReducedDomains(initial_model);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, TargetFixedThenExprPropagated) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 110, 288 ] }
variables { domain: [ 2, 30 ] }
constraints {
int_div {
target { vars: 1 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
exprs { offset: 100 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 200, 288 ] }
variables { domain: [ 2, 2 ] }
)pb");
const CpModelProto presolved_model = GetReducedDomains(initial_model);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, IntModFixesTargetToZero) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 1, 20 ] }
variables { domain: [ 2, 7 ] }
variables { domain: [ -3, 0 ] }
constraints {
int_mod {
target { vars: 2 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 1, 20 ] }
variables { domain: [ 2, 7 ] }
variables { domain: [ 0, 10 ] }
constraints {
int_div {
target { vars: 2 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
}
}
constraints {
int_prod {
target { vars: 0 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
}
}
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, IntModReduceTargetDomain) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 20 ] }
variables { domain: [ 2, 7 ] }
variables { domain: [ 0, 8 ] }
constraints {
int_mod {
target { vars: 2 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 20 ] }
variables { domain: [ 2, 7 ] }
variables { domain: [ 0, 6 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 20 ] }
constraints {
int_div {
target { vars: 3 coeffs: 1 }
exprs { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
}
}
constraints {
int_prod {
target { vars: 4 coeffs: 1 }
exprs { vars: 3 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
}
}
constraints {
linear {
vars: 0
vars: 2
vars: 4
coeffs: 1
coeffs: -1
coeffs: -1
domain: 0
domain: 0
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, IntModFixedTargetAndMod) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 1, 20 ] }
variables { domain: [ -5, 11 ] }
variables { domain: [ -17, 8 ] }
constraints {
int_mod {
target { offset: 2 }
exprs { vars: 0 coeffs: 1 }
exprs { offset: 5 }
}
}
constraints {
int_mod {
target { offset: 2 }
exprs { vars: 1 coeffs: 1 }
exprs { offset: 5 }
}
}
constraints {
int_mod {
target { offset: -2 }
exprs { vars: 2 coeffs: 1 }
exprs { offset: 5 }
}
})pb");
// We get a representative for each int_mod.
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: 0 domain: 3 }
variables { domain: 0 domain: 1 }
variables { domain: 0 domain: 3 }
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, LinearConstraintWithGcd) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
constraints {
linear {
vars: 0
coeffs: 100
vars: 1
coeffs: 200
domain: [ 320, 999 ]
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_EQ(presolved_model.variables_size(), 2);
ASSERT_EQ(1, presolved_model.constraints_size());
const LinearConstraintProto& lin = presolved_model.constraints(0).linear();
EXPECT_EQ(0, lin.vars(0));
EXPECT_EQ(1, lin.vars(1));
EXPECT_EQ(1, lin.coeffs(0));
EXPECT_EQ(2, lin.coeffs(1));
EXPECT_EQ(4, lin.domain(0));
EXPECT_EQ(9, lin.domain(1));
}
TEST(PresolveCpModelTest, RemoveNonUsefulTerms) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
linear {
vars: [ 0, 1, 2, 3 ]
coeffs: [ 10, 10, 4, 3 ]
domain: [ 0, 29 ]
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 2 ] }
variables { domain: [ 0, 2 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
linear {
vars: [ 0, 1 ]
coeffs: [ 1, 1 ]
domain: [ 0, 2 ]
}
}
)pb");
const CpModelProto presolved_model = PresolveOneConstraint(initial_model, 0);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, RemoveNonUsefulTerms2) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
linear {
vars: [ 0, 1, 2, 3 ]
coeffs: [ 9, 9, 4, 3 ]
domain: [ 0, 26 ]
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 2 ] }
variables { domain: [ 0, 2 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
linear {
vars: [ 0, 1 ]
coeffs: [ 1, 1 ]
domain: [ 0, 2 ]
}
}
)pb");
const CpModelProto presolved_model = PresolveOneConstraint(initial_model, 0);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, RemoveNonUsefulTerms3) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 1 ] }
constraints {
linear {
vars: [ 0, 1 ]
coeffs: [ 10, 7 ]
domain: [ 0, 17 ]
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
)pb");
const CpModelProto presolved_model = PresolveOneConstraint(initial_model, 0);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, DetectApproximateGCD) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 100 ] }
variables { domain: [ 0, 100 ] }
constraints {
linear {
vars: [ 0, 1 ]
coeffs: [ 1001, 999 ]
domain: [ 0, 28500 ]
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 28 ] }
variables { domain: [ 0, 28 ] }
constraints {
linear {
vars: [ 0, 1 ]
coeffs: [ 1, 1 ]
domain: [ 0, 28 ]
}
}
)pb");
const CpModelProto presolved_model = PresolveOneConstraint(initial_model, 0);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, LinearConstraintWithGcdInfeasible) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
constraints {
linear {
vars: [ 0, 1 ]
coeffs: [ 4, 4 ]
domain: [ 9, 9 ]
}
}
)pb");
EXPECT_EQ(Solve(initial_model).status(), CpSolverStatus::INFEASIBLE);
}
TEST(PresolveCpModelTest,
LinearConstraintWithGcdFalseConstraintWithEnforcement) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 1 ] }
constraints {
enforcement_literal: 2
linear {
vars: [ 0, 1 ]
coeffs: [ 4, 4 ]
domain: [ 9, 9 ]
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 0 ] }
)pb");
const CpModelProto presolved_model = PresolveOneConstraint(initial_model, 0);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, IntervalPresolveNegativeSize) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -7, -7, 0, 0 ] }
constraints {
interval {
start { offset: 0 }
end { vars: 0 coeffs: 1 }
size { vars: 0 coeffs: 1 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 0 ] }
)pb");
const CpModelProto presolved_model = PresolveOneConstraint(initial_model, 0);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
// TODO(user): really stop testing the full presolve, we always have to add
// irrelevant constraint so that stuff are not presolved away.
TEST(PresolveCpModelTest, BasicIntervalPresolve) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 20 ] }
variables { domain: [ 5, 15 ] }
variables { domain: [ 3, 10 ] }
variables { domain: [ 0, 20 ] }
variables { domain: [ 5, 15 ] }
variables { domain: [ 3, 10 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 1 coeffs: 1 }
size { vars: 2 coeffs: 1 }
}
}
constraints {
interval {
start { vars: 3 coeffs: 1 }
end { vars: 4 coeffs: 1 }
size { vars: 5 coeffs: 1 }
}
}
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 1, -1, 1 ]
domain: [ 0, 0 ]
}
}
constraints {
linear {
vars: [ 3, 4, 5 ]
coeffs: [ 1, -1, 1 ]
domain: [ 0, 0 ]
}
}
constraints {
no_overlap_2d {
x_intervals: [ 0, 1 ]
y_intervals: [ 0, 1 ]
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 12 ] }
variables { domain: [ 5, 15 ] }
variables { domain: [ 3, 10 ] }
variables { domain: [ 0, 12 ] }
variables { domain: [ 5, 15 ] }
variables { domain: [ 3, 10 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 1 coeffs: 1 }
size { vars: 2 coeffs: 1 }
}
}
constraints {
interval {
start { vars: 3 coeffs: 1 }
end { vars: 4 coeffs: 1 }
size { vars: 5 coeffs: 1 }
}
}
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 1, -1, 1 ]
domain: [ 0, 0 ]
}
}
constraints {
linear {
vars: [ 3, 4, 5 ]
coeffs: [ 1, -1, 1 ]
domain: [ 0, 0 ]
}
}
constraints {
no_overlap_2d {
x_intervals: [ 0, 1 ]
y_intervals: [ 0, 1 ]
}
}
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, ExpandMinimizeObjective) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -10, 10 ] }
variables { domain: [ -10, 10 ] }
variables { domain: [ -4611686018427387903, 4611686018427387903 ] }
constraints { dummy_constraint { vars: [ 0, 1 ] } }
constraints {
linear { vars: 0 vars: 1 coeffs: 1 coeffs: 2 domain: -10 domain: 10 }
}
constraints {
linear {
vars: 0
vars: 1
vars: 2
coeffs: 1
coeffs: 2
coeffs: -1
domain: 3
domain: 3
}
}
objective { vars: 2 coeffs: 2 offset: 1 }
)pb");
// We both expand the objective and merge it with other parallel constraint.
const CpModelProto expected_model = ParseTestProto(R"pb(
variables { domain: [ -10, 10 ] }
variables { domain: [ -10, 10 ] }
objective {
vars: [ 0, 1 ]
coeffs: [ 1, 2 ]
offset: -2.5
scaling_factor: 2
integer_before_offset: -3
integer_scaling_factor: 2
domain: [ -10, 10 ]
}
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_model));
}
TEST(PresolveCpModelTest, ExpandMinimizeObjectiveWithOppositeCoeff) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -10, 10 ] }
variables { domain: [ -10, 10 ] }
variables { domain: [ -4611686018427387903, 4611686018427387903 ] }
constraints { dummy_constraint { vars: [ 0, 1 ] } }
constraints {
linear { vars: 0 vars: 1 coeffs: 1 coeffs: 2 domain: -10 domain: 10 }
}
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 1, 2, 1 ]
domain: [ 3, 3 ]
}
}
objective { vars: 2 coeffs: 2 offset: 1 }
)pb");
const CpModelProto expected_model = ParseTestProto(R"pb(
variables { domain: [ -10, 10 ] }
variables { domain: [ -10, 10 ] }
constraints {
linear {
vars: [ 0, 1 ]
coeffs: [ 1, 2 ]
domain: [ -10, 10 ]
}
}
objective {
vars: [ 0, 1 ]
coeffs: [ -1, -2 ]
offset: 3.5
scaling_factor: 2
integer_before_offset: 3
integer_scaling_factor: 2
domain: [ -30, 30 ]
}
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_model));
}
TEST(PresolveCpModelTest, ExpandMaximizeObjective) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -10, 10 ] }
variables { domain: [ -10, 10 ] }
variables { domain: [ -4611686018427387903, 4611686018427387903 ] }
constraints {
all_diff {
exprs { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
}
}
constraints {
linear { vars: 0 vars: 1 coeffs: 1 coeffs: 1 domain: -10 domain: 10 }
}
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 1, 2, -1 ]
domain: [ 3, 3 ]
}
}
objective { vars: -3 coeffs: 2 scaling_factor: -1 offset: 1 }
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_EQ(2, presolved_model.objective().vars_size());
EXPECT_EQ(0, presolved_model.objective().vars(0));
EXPECT_EQ(-1, presolved_model.objective().coeffs(0));
EXPECT_EQ(1, presolved_model.objective().vars(1));
EXPECT_EQ(-2, presolved_model.objective().coeffs(1));
EXPECT_EQ(3.5, presolved_model.objective().offset());
EXPECT_EQ(-2, presolved_model.objective().scaling_factor());
}
TEST(PresolveCpModelTest, ExpandMaximizeObjectiveWithOppositeCoeff) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -10, 10 ] }
variables { domain: [ -10, 10 ] }
variables { domain: [ -4611686018427387903, 4611686018427387903 ] }
constraints {
all_diff {
exprs { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
}
}
constraints {
linear {
vars: [ 0, 1 ]
coeffs: [ 1, 1 ]
domain: [ -10, 10 ]
}
}
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 1, 2, 1 ]
domain: [ 3, 3 ]
}
}
objective { vars: -3 coeffs: 2 scaling_factor: -1 offset: 1 }
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_EQ(2, presolved_model.objective().vars_size());
EXPECT_EQ(0, presolved_model.objective().vars(0));
EXPECT_EQ(1, presolved_model.objective().coeffs(0));
EXPECT_EQ(1, presolved_model.objective().vars(1));
EXPECT_EQ(2, presolved_model.objective().coeffs(1));
EXPECT_EQ(-2.5, presolved_model.objective().offset());
EXPECT_EQ(-2, presolved_model.objective().scaling_factor());
}
TEST(PresolveCpModelTest, ExpandMinimizeObjectiveWithLimitingLinearEquation) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -10, 10 ] }
variables { domain: [ -10, 10 ] }
variables { domain: [ -8, 7 ] }
constraints {
linear {
vars: [ 0, 1 ]
coeffs: [ 1, 1 ]
domain: [ -10, 10 ]
}
}
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 1, 2, -1 ]
domain: [ 3, 3 ]
}
}
objective { vars: 2 coeffs: 2 offset: 1 }
)pb");
// The objective domain without offset above (and after moving the coeff to
// the scaling) is [-8, 7], and when doing the transformation new_expression =
// old_obj + 3, the domain of the new expression is [-5, 10].
const CpModelProto expected_model = ParseTestProto(R"pb(
variables { domain: [ -10, 9 ] }
variables { domain: [ -7, 3 ] }
constraints {
linear {
vars: [ 0, 1 ]
coeffs: [ 1, 1 ]
domain: [ -10, 10 ]
}
}
objective {
vars: [ 0, 1 ]
coeffs: [ 1, 2 ]
offset: -2.5
scaling_factor: 2
integer_before_offset: -3
integer_scaling_factor: 2
domain: [ -5, 10 ]
}
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_model));
}
TEST(PresolveCpModelTest, ExpandMinimizeObjectiveWithLimitingLinearEquation2) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -10, 10 ] }
variables { domain: [ -10, 10 ] }
variables { domain: [ -8, 7 ] }
constraints {
linear {
vars: [ 0, 1 ]
coeffs: [ 1, 1 ]
domain: [ -10, 10 ]
}
}
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 1, 2, 1 ]
domain: [ 3, 3 ]
}
}
objective { vars: 2 coeffs: 2 offset: 1 }
)pb");
// This time, we have new_obj = old_obj - 3.
// Note that the variable #2 is removed, but this do not remove any feasible
// solution since its value will be uniquely determined via the removed
// constraint x0 + 2x1 + x2 = 3. The objective domain constrains x0 + 2x1
// to take feasible value for x3.
const CpModelProto expected_model = ParseTestProto(R"pb(
variables { domain: [ -10, 10 ] }
variables { domain: [ -7, 10 ] }
constraints {
linear {
vars: [ 0, 1 ]
coeffs: [ 1, 1 ]
domain: [ -10, 10 ]
}
}
objective {
vars: [ 0, 1 ]
coeffs: [ -1, -2 ]
offset: 3.5
scaling_factor: 2
integer_before_offset: 3
integer_scaling_factor: 2
domain: [ -11, 4 ]
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_model));
}
TEST(PresolveCpModelTest, ExpandObjectiveInfeasible) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -10, 10 ] }
variables { domain: [ -10, 10 ] }
constraints {
linear {
vars: [ 0, 1 ]
coeffs: [ 1, 1 ]
domain: [ -10, 10 ]
}
}
objective {
vars: [ 0, 1 ]
coeffs: [ 1, 1 ]
domain: [ 30, 40 ]
}
)pb");
Model tmp_model;
EXPECT_EQ(SolveCpModel(initial_model, &tmp_model).status(),
CpSolverStatus::INFEASIBLE);
}
TEST(PresolveCpModelTest, ExpandObjectiveWithLimitedPresolve) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 2 ] }
variables { domain: [ 0, 2 ] }
constraints {
linear { vars: 0 vars: 1 coeffs: -1 coeffs: 1 domain: -1 domain: -1 }
}
objective { vars: 1 coeffs: 1 })pb");
SatParameters params;
params.set_max_presolve_iterations(0);
params.set_log_search_progress(true);
EXPECT_EQ(SolveWithParameters(initial_model, params).status(),
CpSolverStatus::OPTIMAL);
}
TEST(PresolveCpModelTest, CircuitConstraint) {
// A rho shape.
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
circuit {
tails: 0
heads: 0
literals: 0 # needed not to be unsat.
tails: 0
heads: 1
literals: 1
tails: 1
heads: 2
literals: 2
tails: 2
heads: 3
literals: 3
tails: 3
heads: 1
literals: 4
}
}
)pb");
// There is just one possible solution, detected by the presolve.
const CpModelProto mapping_model = GetMappingModel(initial_model);
const CpModelProto expected_mapping_model = ParseTestProto(R"pb(
variables { domain: [ 1, 1 ] }
variables { domain: [ 0, 0 ] }
variables { domain: [ 1, 1 ] }
variables { domain: [ 1, 1 ] }
variables { domain: [ 1, 1 ] }
)pb");
EXPECT_THAT(expected_mapping_model, testing::EqualsProto(mapping_model));
}
// Fully specified circuit. This used to remove the constraint instead of
// dedecting infeasibility since some mandatory node are not in the 0 <-> 1
// circuit.
TEST(PresolveCpModelTest, FixedButIncompleteCircuitConstraint) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 1, 1 ] }
variables { domain: [ 1, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
circuit {
tails: [ 0, 1, 1, 2, 1, 3, 2, 3 ]
heads: [ 1, 0, 2, 1, 3, 1, 3, 2 ]
literals: [ 0, 1, 2, 3, 4, 5, 6, 7 ]
}
}
)pb");
ExpectInfeasibleDuringPresolve(initial_model);
}
TEST(PresolveCpModelTest, CircuitConstraintWithDuplicateLiteral) {
// A rho shape.
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 1, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
circuit {
tails: 0
heads: 0
literals: 0 # set at true
tails: 0
heads: 1
literals: 1 # will be false
tails: 1
heads: 2
literals: 2
tails: 2
heads: 3
literals: 3
tails: 3
heads: 1
literals: 4
tails: 1
heads: 1
literals: 1
tails: 2
heads: 2
literals: 1
tails: 3
heads: 3
literals: 1
}
}
)pb");
// There is just one possible solution, detected by the presolve.
SatParameters params;
params.set_max_presolve_iterations(1);
const CpModelProto mapping_model = GetMappingModel(initial_model);
const CpModelProto expected_mapping_model = ParseTestProto(R"pb(
variables { domain: [ 1, 1 ] }
variables { domain: [ 0, 0 ] }
variables { domain: [ 1, 1 ] }
variables { domain: [ 1, 1 ] }
variables { domain: [ 1, 1 ] }
)pb");
EXPECT_THAT(expected_mapping_model, testing::EqualsProto(mapping_model));
}
TEST(PresolveCpModelTest, RouteConstraint) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 1, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 0 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 0 ] }
variables { domain: [ 0, 1 ] }
constraints {
routes {
tails: [ 0, 0, 1, 1, 2, 2 ]
heads: [ 1, 2, 0, 2, 1, 0 ]
literals: [ 0, 1, 2, 3, 4, 5 ]
}
}
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
const CpModelProto expected_model = ParseTestProto(R"pb(
variables { domain: [ 1, 1 ] }
constraints {
routes {
tails: [ 0, 1, 2 ]
heads: [ 1, 2, 0 ]
literals: [ 0, 0, 0 ]
}
}
)pb");
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_model));
}
// The presolve used to fail by removing all arcs incident to node 2 and thus
// node 2 was no longer considered as unreachable.
TEST(PresolveCpModelTest, RouteConstraintWithUnreachableNode) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 1, 1 ] }
variables { domain: [ 0, 0 ] }
constraints {
routes {
tails: [ 0, 0, 2, 1 ]
heads: [ 1, 2, 1, 0 ]
literals: [ 0, 1, 1, 0 ]
}
}
)pb");
ExpectInfeasibleDuringPresolve(initial_model);
}
TEST(PresolveCpModelTest, CircuitConstraintWithDegree2) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
circuit {
tails: 0
heads: 0
literals: 0
tails: 1
heads: 1
literals: 1
tails: 0
heads: 1
literals: 2
tails: 1
heads: 0
literals: 3
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
constraints {
circuit {
tails: 0
heads: 0
literals: 0
tails: 1
heads: 1
literals: 0
tails: 0
heads: 1
literals: -1
tails: 1
heads: 0
literals: -1
}
}
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, UsedToCrash) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 2, 2 ] }
variables { domain: [ 0, 2 ] }
constraints {
all_diff {
exprs { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
}
}
constraints {
linear {
vars: 1
vars: 0
coeffs: 1
coeffs: -1
domain: [ 1, 9223372036854775807 ]
}
}
constraints { linear { vars: 1 coeffs: 1 domain: 1 domain: 1 } }
)pb");
ExpectInfeasibleDuringPresolve(initial_model);
}
TEST(PresolveCpModelTest, FixedAllDifferent) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 1, 50 ] }
variables { domain: [ 3, 3 ] }
variables { domain: [ 1, 50 ] }
variables { domain: [ 1, 50 ] }
constraints {
all_diff {
exprs { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
exprs { vars: 3 coeffs: 1 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 1, 2, 4, 50 ] }
variables { domain: [ 1, 2, 4, 50 ] }
variables { domain: [ 1, 2, 4, 50 ] }
constraints {
all_diff {
exprs { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
}
}
)pb");
SatParameters extra_params;
extra_params.set_symmetry_level(0);
const CpModelProto presolved_model =
PresolveForTest(initial_model, extra_params);
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, AllDifferentWithExpressionsSharingVariable) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 50 ] }
variables { domain: [ 2, 20 ] }
variables { domain: [ 1, 50 ] }
constraints {
all_diff {
exprs { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
exprs { vars: 0 coeffs: -1 }
exprs { vars: 1 coeffs: 2 offset: -3 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 1, 50 ] }
variables { domain: [ 2, 2, 4, 20 ] }
variables { domain: [ 1, 50 ] }
constraints {
all_diff {
exprs { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
exprs { vars: 0 coeffs: -1 }
exprs { vars: 1 coeffs: 2 offset: -3 }
}
}
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, DetectDifferentVariablesAndAddNoOverlap) {
CpModelProto cp_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 91, 905 ] }
variables { domain: [ 638, 937 ] }
variables { domain: [ 0, 69 ] }
variables { domain: [ 575, 930 ] }
constraints {
linear {
vars: [ 3, 4 ]
coeffs: [ 1, -1 ]
domain: [ -863, -506 ]
}
}
constraints {
linear {
vars: [ 2, 3 ]
coeffs: [ 1, -1 ]
domain: [ 569, 909 ]
}
}
constraints {
linear {
vars: [ 1, 2 ]
coeffs: [ 1, -1 ]
domain: [ -846, -32 ]
}
}
constraints {
linear {
vars: [ 1, 3 ]
coeffs: [ -1, 1 ]
domain: [ -868, -22 ]
}
}
constraints {
enforcement_literal: 0
linear {
vars: [ 1, 4 ]
coeffs: [ 1, -1 ]
domain: [ -839, -300 ]
}
}
constraints {
enforcement_literal: -1
linear {
vars: [ 1, 4 ]
coeffs: [ 1, -1 ]
domain: [ 310, 330 ]
}
}
constraints {
enforcement_literal: -1
linear {
vars: [ 2, 4 ]
coeffs: [ -1, 1 ]
domain: [ 275, 292 ]
}
}
constraints {
enforcement_literal: 0
linear {
vars: [ 2, 4 ]
coeffs: [ -1, 1 ]
domain: [ -362, -123 ]
}
}
solution_hint {
vars: [ 0, 1, 2, 3, 4 ]
values: [ 1, 397, 836, 69, 713 ]
}
)pb");
ASSERT_TRUE(SolutionIsFeasible(cp_model, cp_model.solution_hint().values()));
Model model;
CpModelProto mapping_model;
PresolveContext context(&model, &cp_model, &mapping_model);
std::vector<int> mapping;
CpModelPresolver presolver(&context, &mapping);
context.InitializeNewDomains();
context.UpdateNewConstraintsVariableUsage();
presolver.DetectDifferentVariables();
context.WriteVariableDomainsToProto();
bool has_no_overlap_constraint = false;
for (const ConstraintProto& constraint : cp_model.constraints()) {
if (constraint.has_no_overlap()) {
has_no_overlap_constraint = true;
break;
}
}
EXPECT_TRUE(has_no_overlap_constraint);
EXPECT_TRUE(SolutionIsFeasible(cp_model, cp_model.solution_hint().values()));
EXPECT_EQ(Solve(cp_model).status(), CpSolverStatus::OPTIMAL);
}
TEST(PresolveCpModelTest, PermutationMandatoryValues) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 1, 3 ] }
variables { domain: [ 1, 3 ] }
variables { domain: [ 1, 3 ] }
variables { domain: [ 1, 4 ] }
constraints {
all_diff {
exprs { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
exprs { vars: 3 coeffs: 1 }
}
}
)pb");
Model model;
model.GetOrCreate<SatParameters>()->set_expand_alldiff_constraints(false);
CpModelProto presolved_model = initial_model;
CpModelProto mapping_model;
std::vector<int> mapping;
PresolveContext context(&model, &presolved_model, &mapping_model);
PresolveCpModel(&context, &mapping);
const IntegerVariableProto expected_var = ParseTestProto(R"pb(
domain: [ 4, 4 ])pb");
EXPECT_THAT(expected_var, testing::EqualsProto(mapping_model.variables(3)));
}
TEST(PresolveCpModelTest, CircuitCornerCase1) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
circuit {
tails: 1
tails: 2
tails: 0
heads: 2
heads: 0
heads: 1
literals: 0
literals: 1
literals: 2
}
}
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
EXPECT_EQ(0, presolved_model.constraints_size());
}
TEST(PresolveCpModelTest, CircuitCornerCase2) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
circuit {
tails: 0
heads: 1
literals: 0
tails: 1
heads: 1
literals: 1
tails: 0
heads: 2
literals: 2
tails: 2
heads: 2
literals: 3
}
}
)pb");
Model tmp_model;
EXPECT_EQ(SolveCpModel(initial_model, &tmp_model).status(),
CpSolverStatus::INFEASIBLE);
}
TEST(PresolveCpModelTest, ObjectiveWithLargeCoefficient) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
objective: {
vars: [ -1, -2, -3 ]
scaling_factor: -1.0
coeffs: [ 194833170077, 3656800, 19394221124 ]
}
)pb");
SatParameters params;
params.set_enumerate_all_solutions(true);
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
objective: {
vars: [ 0, 1, 2 ]
scaling_factor: -1.0
coeffs: [ -194833170077, -3656800, -19394221124 ]
# We simplify the domain.
domain: -214231048001
domain: 0
}
)pb");
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, MipSimplificationExample) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 1, 2, 4 ]
domain: [ 4, 4 ]
}
}
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 1, 2, 4 ]
domain: [ 4, 4 ]
}
}
)pb");
const CpModelProto mapping_model = GetMappingModel(initial_model);
const CpModelProto expected_mapping_model = ParseTestProto(R"pb(
variables { domain: [ 0, 0 ] }
variables { domain: [ 0, 0 ] }
variables { domain: [ 1, 1 ] }
)pb");
EXPECT_THAT(expected_mapping_model, testing::EqualsProto(mapping_model));
}
TEST(PresolveCpModelTest, TriviallyUnsatCumulative) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 2, 2 ] } # size
variables { domain: [ 0, 9 ] } # end
variables { domain: [ 2, 2 ] } # capacity
variables { domain: [ 5, 5 ] } # demand
constraints {
interval {
start { vars: 0 coeffs: 1 }
size { vars: 1 coeffs: 1 }
end { vars: 2 coeffs: 1 }
}
}
constraints {
cumulative {
capacity: { offset: 2 }
demands: { offset: 5 }
intervals: [ 0 ]
}
}
)pb");
ExpectInfeasibleDuringPresolve(initial_model);
}
TEST(PresolveCpModelTest, ZeroDemandCumulative) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 9 ] } # start
constraints {
interval {
start { vars: 0 coeffs: 1 }
size { offset: 2 }
end { vars: 0 coeffs: 1 offset: 2 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
size { offset: 2 }
end { vars: 1 coeffs: 1 offset: 2 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
size { offset: 2 }
end { vars: 2 coeffs: 1 offset: 2 }
}
}
constraints {
cumulative {
capacity { offset: 2 }
demands { offset: 1 }
demands { offset: 2 }
demands { offset: 0 }
intervals: [ 0, 1, 2 ]
}
}
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 9 ] } # start
constraints {
interval {
start { vars: 0 coeffs: 1 }
size { offset: 2 }
end { vars: 0 coeffs: 1 offset: 2 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
size { offset: 2 }
end { vars: 1 coeffs: 1 offset: 2 }
}
}
constraints {
cumulative {
capacity { offset: 2 }
demands { offset: 1 }
demands { offset: 2 }
intervals: [ 0, 1 ]
}
}
)pb");
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, CapacityExceedsDemands) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 1 ] } # optional literal
variables { domain: [ 7, 8 ] } # capacity
constraints {
interval {
start { vars: 0 coeffs: 1 }
size { offset: 2 }
end { vars: 0 coeffs: 1 offset: 2 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
size { offset: 2 }
end { vars: 1 coeffs: 1 offset: 2 }
}
}
constraints {
enforcement_literal: 3
interval {
start { vars: 2 coeffs: 1 }
size { offset: 2 }
end { vars: 2 coeffs: 1 offset: 2 }
}
}
constraints {
cumulative {
capacity { vars: 4 coeffs: 1 }
demands { offset: 1 }
demands { offset: 2 }
demands { offset: 4 }
intervals: [ 0, 1, 2 ]
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 1 ] } # optional literal
variables { domain: [ 0, 1 ] } # capacity representative
)pb");
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, CumulativeDivideByGcd) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 1 ] } # optional literal
constraints {
interval {
start { vars: 0 coeffs: 1 }
size { offset: 2 }
end { vars: 0 coeffs: 1 offset: 2 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
size { offset: 2 }
end { vars: 1 coeffs: 1 offset: 2 }
}
}
constraints {
enforcement_literal: 3
interval {
start { vars: 2 coeffs: 1 }
size { offset: 2 }
end { vars: 2 coeffs: 1 offset: 2 }
}
}
constraints {
cumulative {
capacity { offset: 13 }
demands { offset: 3 }
demands { offset: 6 }
demands { offset: 9 }
intervals: [ 0, 1, 2 ]
}
}
)pb");
SatParameters params;
params.set_enumerate_all_solutions(true);
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 1 ] } # optional literal
constraints {
interval {
start { vars: 0 coeffs: 1 }
size { offset: 2 }
end { vars: 0 coeffs: 1 offset: 2 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
size { offset: 2 }
end { vars: 1 coeffs: 1 offset: 2 }
}
}
constraints {
enforcement_literal: 3
interval {
start { vars: 2 coeffs: 1 }
size { offset: 2 }
end { vars: 2 coeffs: 1 offset: 2 }
}
}
constraints {
cumulative {
capacity { offset: 4 }
demands { offset: 1 }
demands { offset: 2 }
demands { offset: 3 }
intervals: [ 0, 1, 2 ]
}
}
)pb");
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, CumulativeDivideByGcdBug) {
const CpModelProto initial_model = ParseTestProto(R"pb(
name: "Multi-device peak-memory minimization."
variables { domain: [ 0, 0 ] }
variables { domain: [ 1, 1 ] }
variables { domain: [ 1, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 0 ] }
variables { domain: [ 1, 1 ] }
variables { domain: [ 1, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 1, 1 ] }
variables { domain: [ 0, 13 ] }
variables { domain: [ 13, 13 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 0 coeffs: 1 offset: 1 }
size { offset: 1 }
}
}
constraints {
enforcement_literal: 3
interval {
start { vars: 4 coeffs: 1 }
end { vars: 4 coeffs: 1 offset: 1 }
size { offset: 1 }
}
}
constraints {
enforcement_literal: 7
interval {
start { vars: 8 coeffs: 1 }
end { vars: 10 coeffs: 1 }
size { vars: 9 coeffs: 1 }
}
}
constraints {
enforcement_literal: 7
linear {
vars: [ 8, 9, 10 ]
coeffs: [ 1, 1, -1 ]
domain: [ 0, 0 ]
}
}
constraints { linear { vars: 3 coeffs: 1 domain: 1 domain: 1 } }
constraints {
enforcement_literal: 3
linear { vars: 0 vars: 4 coeffs: 1 coeffs: -1 domain: 0 domain: 0 }
}
constraints {
linear {
vars: 3
vars: 7
coeffs: 1
coeffs: -1
domain: -9223372036854775808
domain: 0
}
}
constraints {
enforcement_literal: 3
linear {
vars: 8
vars: 0
coeffs: 1
coeffs: -1
domain: -9223372036854775808
domain: 0
}
}
constraints {
enforcement_literal: 3
linear {
vars: 11
vars: 10
coeffs: 1
coeffs: -1
domain: -9223372036854775808
domain: 0
}
}
constraints {
enforcement_literal: 7
linear {
vars: 8
vars: 11
coeffs: 1
coeffs: -1
domain: -9223372036854775808
domain: -1
}
}
constraints {
enforcement_literal: 3
linear {
vars: 8
vars: 4
coeffs: 1
coeffs: -1
domain: -9223372036854775808
domain: 0
}
}
constraints {
enforcement_literal: 3
linear {
vars: 6
vars: 10
coeffs: 1
coeffs: -1
domain: -9223372036854775808
domain: 0
}
}
constraints {
cumulative {
capacity { vars: 12 coeffs: 1 }
intervals: 2
demands { vars: 13 coeffs: 1 }
}
}
objective { vars: 12 coeffs: 1 }
)pb");
const CpSolverResponse response = Solve(initial_model);
EXPECT_EQ(response.status(), CpSolverStatus::OPTIMAL);
EXPECT_EQ(13.0, response.objective_value());
}
TEST(PresolveCpModelTest, NonConflictingDemands) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 2 ] } # start 0
variables { domain: [ 2, 6 ] } # start 1
variables { domain: [ 4, 8 ] } # start 2
variables { domain: [ 8, 10 ] } # start 3
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 0 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
end { vars: 1 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
end { vars: 2 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
interval {
start { vars: 3 coeffs: 1 }
end { vars: 3 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
# Fixed interval that creates the potential overload.
interval {
start { offset: 5 }
end { offset: 7 }
size { offset: 2 }
}
}
constraints {
cumulative {
capacity { offset: 2 }
demands: { offset: 1 }
demands: { offset: 1 }
demands: { offset: 1 }
demands: { offset: 1 }
demands: { offset: 1 }
intervals: [ 0, 1, 2, 3, 4 ]
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 2 ] }
variables { domain: [ 2, 6 ] }
variables { domain: [ 4, 8 ] }
variables { domain: [ 8, 10 ] }
constraints {
interval {
start { vars: 1 coeffs: 1 }
end { vars: 1 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
end { vars: 2 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
interval {
start { offset: 5 }
end { offset: 7 }
size { offset: 2 }
}
}
constraints {
cumulative {
capacity { offset: 2 }
intervals: [ 0, 1, 2 ]
demands { offset: 1 }
demands { offset: 1 }
demands { offset: 1 }
}
}
)pb");
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, NonConflictingDemandsInTheMiddle) {
// Initially, all intervals are connected through overlap.
// Then interval 3 should be removed as it can never cause an overlap.
// Then the cumulative should be split in 2.
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 4 ] } # start 0
variables { domain: [ 0, 4 ] } # start 1
variables { domain: [ 0, 5 ] } # start 2
variables { domain: [ 6, 9 ] } # start 3
variables { domain: [ 10, 15 ] } # start 4
variables { domain: [ 11, 15 ] } # start 5
variables { domain: [ 11, 15 ] } # start 6
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 0 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
end { vars: 1 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
end { vars: 2 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
interval {
start { vars: 3 coeffs: 1 }
end { vars: 3 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
interval {
start { vars: 4 coeffs: 1 }
end { vars: 4 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
interval {
start { vars: 5 coeffs: 1 }
end { vars: 5 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
interval {
start { vars: 6 coeffs: 1 }
end { vars: 6 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
cumulative {
capacity { offset: 2 }
demands { offset: 1 }
demands { offset: 1 }
demands { offset: 1 }
demands { offset: 1 }
demands { offset: 1 }
demands { offset: 1 }
demands { offset: 1 }
intervals: [ 0, 1, 2, 3, 4, 5, 6 ]
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 4 ] }
variables { domain: [ 0, 4 ] }
variables { domain: [ 0, 5 ] }
variables { domain: [ 6, 9 ] }
variables { domain: [ 10, 15 ] }
variables { domain: [ 11, 15 ] }
variables { domain: [ 11, 15 ] }
constraints {
interval {
start { vars: 0 coeffs: 1 }
end { vars: 0 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
end { vars: 1 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
end { vars: 2 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
interval {
start { vars: 4 coeffs: 1 }
end { vars: 4 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
interval {
start { vars: 5 coeffs: 1 }
end { vars: 5 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
interval {
start { vars: 6 coeffs: 1 }
end { vars: 6 coeffs: 1 offset: 2 }
size { offset: 2 }
}
}
constraints {
cumulative {
capacity { offset: 2 }
intervals: [ 0, 1, 2 ]
demands { offset: 1 }
demands { offset: 1 }
demands { offset: 1 }
}
}
constraints {
cumulative {
capacity { offset: 2 }
intervals: [ 3, 4, 5 ]
demands { offset: 1 }
demands { offset: 1 }
demands { offset: 1 }
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, ConvertToNoOverlap) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 7 ] } # capacity
constraints {
interval {
start { vars: 0 coeffs: 1 }
size { offset: 2 }
end { vars: 0 coeffs: 1 offset: 2 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
size { offset: 2 }
end { vars: 1 coeffs: 1 offset: 2 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
size { offset: 2 }
end { vars: 2 coeffs: 1 offset: 2 }
}
}
constraints {
cumulative {
capacity { vars: 3 coeffs: 1 }
demands { offset: 4 }
demands { offset: 4 }
demands { offset: 4 }
intervals: [ 0, 1, 2 ]
}
}
)pb");
SatParameters params;
params.set_enumerate_all_solutions(true);
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 4, 7 ] } # capacity
constraints {
interval {
start { vars: 0 coeffs: 1 }
size { offset: 2 }
end { vars: 0 coeffs: 1 offset: 2 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
size { offset: 2 }
end { vars: 1 coeffs: 1 offset: 2 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
size { offset: 2 }
end { vars: 2 coeffs: 1 offset: 2 }
}
}
constraints { no_overlap { intervals: [ 0, 1, 2 ] } }
)pb");
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, ConvertToNoOverlapVariableDemand) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 4, 6 ] } # variable demand
variables { domain: [ 0, 7 ] } # capacity
constraints {
interval {
start { vars: 0 coeffs: 1 }
size { offset: 2 }
end { vars: 0 coeffs: 1 offset: 2 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
size { offset: 2 }
end { vars: 1 coeffs: 1 offset: 2 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
size { offset: 2 }
end { vars: 2 coeffs: 1 offset: 2 }
}
}
constraints {
cumulative {
capacity { vars: 4 coeffs: 1 }
demands { vars: 3 coeffs: 1 }
demands { vars: 3 coeffs: 1 }
demands { vars: 3 coeffs: 1 }
intervals: [ 0, 1, 2 ]
}
}
)pb");
SatParameters params;
params.set_enumerate_all_solutions(true);
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 4, 6 ] } # variable demand
variables { domain: [ 4, 7 ] } # capacity
constraints {
interval {
start { vars: 0 coeffs: 1 }
size { offset: 2 }
end { vars: 0 coeffs: 1 offset: 2 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
size { offset: 2 }
end { vars: 1 coeffs: 1 offset: 2 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
size { offset: 2 }
end { vars: 2 coeffs: 1 offset: 2 }
}
}
constraints {
linear { vars: 3 vars: 4 coeffs: 1 coeffs: -1 domain: -3 domain: 0 }
}
constraints { no_overlap { intervals: [ 0, 1, 2 ] } }
)pb");
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, NoConvertToNoOverlap) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 7, 8 ] } # capacity
constraints {
interval {
start { vars: 0 coeffs: 1 }
size { offset: 2 }
end { vars: 0 coeffs: 1 offset: 2 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
size { offset: 2 }
end { vars: 1 coeffs: 1 offset: 2 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
size { offset: 2 }
end { vars: 2 coeffs: 1 offset: 2 }
}
}
constraints {
cumulative {
capacity { vars: 3 coeffs: 1 }
demands { offset: 4 }
demands { offset: 4 }
demands { offset: 4 }
intervals: [ 0, 1, 2 ]
}
}
)pb");
SatParameters extra_params;
extra_params.set_symmetry_level(0);
const CpModelProto presolved_model =
PresolveForTest(initial_model, extra_params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 9 ] } # start
variables { domain: [ 0, 1 ] } # capacity
constraints {
interval {
start { vars: 0 coeffs: 1 }
size { offset: 2 }
end { vars: 0 coeffs: 1 offset: 2 }
}
}
constraints {
interval {
start { vars: 1 coeffs: 1 }
size { offset: 2 }
end { vars: 1 coeffs: 1 offset: 2 }
}
}
constraints {
interval {
start { vars: 2 coeffs: 1 }
size { offset: 2 }
end { vars: 2 coeffs: 1 offset: 2 }
}
}
constraints {
cumulative {
capacity { vars: 3 coeffs: 1 offset: 7 }
demands { offset: 4 }
demands { offset: 4 }
demands { offset: 4 }
intervals: [ 0, 1, 2 ]
}
}
)pb");
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, ConversionToBoolOr) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
objective {
vars: [ 0, 1, 2 ]
coeffs: [ 1, 1, 1 ]
}
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 1, 2, 3 ]
domain: [ 1, 100 ]
}
}
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 1, 1, 1 ]
domain: [ 0, 2 ]
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
objective {
vars: [ 0, 1, 2 ]
coeffs: [ 1, 1, 1 ]
domain: [ 0, 3 ]
scaling_factor: 1
}
constraints { bool_or { literals: [ 0, 1, 2 ] } }
constraints { bool_or { literals: [ -3, -2, -1 ] } }
)pb");
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, ConversionToAtMostOnePositive) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
objective {
vars: [ 0, 1, 2 ]
coeffs: [ 1, 1, 1 ]
}
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 2, 2, 3 ]
domain: [ 0, 3 ]
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
objective {
vars: [ 0, 1, 2 ]
coeffs: [ 1, 1, 1 ]
domain: [ 0, 3 ]
scaling_factor: 1
}
constraints { at_most_one { literals: [ 0, 1, 2 ] } }
)pb");
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, ConversionToAtMostOneNegative) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
objective {
vars: [ 0, 1, 2 ]
coeffs: [ 1, 1, 1 ]
}
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 1, 1, 1 ]
domain: [ 2, 3 ]
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
// Note that the order of the literal in the constraint do not change.
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
objective {
vars: [ 0, 1, 2 ]
coeffs: [ 1, 1, 1 ]
domain: [ 0, 3 ]
scaling_factor: 1
}
constraints { at_most_one { literals: [ -3, -2, -1 ] } }
)pb");
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, ExtractAtMostOne) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 10 ] }
objective {
vars: [ 0, 1, 2 ]
coeffs: [ 1, 1, 1 ]
}
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 3, 2, 1 ]
domain: [ 1, 3 ]
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 3 ] }
objective {
vars: [ 0, 1, 2 ]
coeffs: [ 1, 1, 1 ]
domain: [ 0, 5 ]
scaling_factor: 1
}
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 3, 2, 1 ]
domain: [ 1, 3 ]
}
}
constraints {
enforcement_literal: 0
bool_and { literals: -2 }
}
)pb");
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, DuplicateLiteralsBoolOr) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints { at_most_one { literals: [ 0, 1, 2 ] } }
constraints { bool_or { literals: [ 0, 1, 0, 2 ] } }
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints { exactly_one { literals: [ 0, 1, 2 ] } }
)pb");
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, FalseLiteralBoolXor) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 0 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints { bool_xor { literals: [ 0, 1, 2, 3 ] } }
)pb");
SatParameters params;
params.set_symmetry_level(0);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints { bool_xor { literals: [ 0, 1, 2 ] } }
)pb");
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, TrueLiteralBoolXor) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 1, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints { bool_xor { literals: [ 0, 1, 2, 3 ] } }
)pb");
SatParameters params;
params.set_symmetry_level(0);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 1, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints { bool_xor { literals: [ 1, 2, 3, 0 ] } }
)pb");
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, TwoTrueLiteralBoolXor) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 1, 1 ] }
variables { domain: [ 1, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints { bool_xor { literals: [ 0, 1, 2, 3, 4 ] } }
)pb");
SatParameters params;
params.set_symmetry_level(0);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints { bool_xor { literals: [ 0, 1, 2 ] } }
)pb");
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, OneActiveLiteralToFalseBoolXor) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 1, 1 ] }
variables { domain: [ 0, 1 ] }
constraints { bool_xor { literals: [ 0, 1 ] } }
)pb");
const CpModelProto presolved_model = GetReducedDomains(initial_model);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 1, 1 ] }
variables { domain: [ 0, 0 ] }
)pb");
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, BoolXorNotPresolvedIfEnforcementUnknown) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 1, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
enforcement_literal: 2
bool_xor { literals: [ 0, 1 ] }
}
)pb");
const CpModelProto presolved_model = PresolveOneConstraint(initial_model, 0);
EXPECT_THAT(presolved_model, testing::EqualsProto(initial_model));
}
TEST(PresolveCpModelTest, BoolXorChangedToBoolOrIfAlwaysFalseWhenEnforced) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
enforcement_literal: [ 0, 1, 2 ]
bool_xor {}
}
)pb");
const CpModelProto presolved_model = PresolveOneConstraint(initial_model, 0);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints { bool_or { literals: [ -1, -2, -3 ] } }
)pb");
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, BoolXorChangedToBoolOrIfAlwaysFalseWhenEnforced2) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
enforcement_literal: [ 0, 1, 2 ]
bool_xor { literals: [ 1, 1 ] }
}
)pb");
const CpModelProto presolved_model = PresolveOneConstraint(initial_model, 0);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints { bool_or { literals: [ -1, -2, -3 ] } }
)pb");
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, BoolXorChangedToBoolOrIfAlwaysFalseWhenEnforced3) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 1, 1 ] }
constraints {
enforcement_literal: [ 0, 1, 2 ]
bool_xor { literals: [ 1, -2, 3 ] }
}
)pb");
const CpModelProto presolved_model = PresolveOneConstraint(initial_model, 0);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 1, 1 ] }
constraints { bool_or { literals: [ -1, -2, -3 ] } }
)pb");
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, OneActiveLiteralToTrueBoolXor) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 1, 1 ] }
variables { domain: [ 1, 1 ] }
variables { domain: [ 0, 1 ] }
constraints { bool_xor { literals: [ 0, 1, 2 ] } }
)pb");
const CpModelProto presolved_model = GetReducedDomains(initial_model);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 1, 1 ] }
variables { domain: [ 1, 1 ] }
variables { domain: [ 1, 1 ] }
)pb");
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, TwoActiveLiteralsAndTrueBoolXor) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 1, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints { bool_xor { literals: [ 0, 1, 2 ] } }
)pb");
SatParameters params;
params.set_symmetry_level(0);
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
)pb");
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, TwoActiveLiteralsAndFalseBoolXor) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 0 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints { bool_xor { literals: [ 0, 1, 2 ] } }
)pb");
SatParameters params;
params.set_symmetry_level(0);
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
)pb");
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, SetPPCRedundentConstraints) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints { at_most_one { literals: [ 0, 1, 2 ] } }
constraints { at_most_one { literals: [ 0, 1, 2 ] } }
constraints { bool_or { literals: [ 0, 1, 2 ] } }
constraints { bool_or { literals: [ 0, 1, 2 ] } }
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints { exactly_one { literals: [ 0, 1, 2 ] } }
)pb");
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, SetPPCDominatedConstraint) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints { at_most_one { literals: [ 0, 1, 2 ] } }
constraints { at_most_one { literals: [ 0, 1, 2, 3 ] } }
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints { at_most_one { literals: [ 0, 1, 2, 3 ] } }
)pb");
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, SetPPCFixVariables) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints { bool_or { literals: [ 0, 1, 2 ] } }
constraints { at_most_one { literals: [ 0, 1, 2, 3 ] } }
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints { exactly_one { literals: [ 0, 1, 2 ] } }
)pb");
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, DuplicateInAtMostOne) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints { at_most_one { literals: [ 0, 1, 2, 3, 2 ] } }
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints { at_most_one { literals: [ 0, 1, 2 ] } }
)pb");
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, CanonicalBinaryVarAndTable) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 2 ] }
variables { domain: [ -1, -1, 1, 1 ] }
constraints {
table {
exprs { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
values: [ 0, -1, 1, -1, 2, -1, 2, 1 ]
}
}
)pb");
SatParameters params;
params.set_disable_constraint_expansion(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 2 ] }
variables { domain: [ 0, 1 ] }
constraints {
table {
exprs { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
values: [ 0, 0, 1, 0, 2, 0, 2, 1 ]
}
}
)pb");
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, DuplicateVariablesInTable) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 2 ] }
variables { domain: [ 0, 2 ] }
constraints {
table {
exprs { vars: 0 coeffs: 1 }
exprs { vars: 0 coeffs: -1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
values: [
0, 0, 0, 0, 1, -1, 0, 0, 1, 0, 0, 0, 1, -1, 1, 1, 2, -2, 2, 2
]
}
}
)pb");
SatParameters params;
params.set_disable_constraint_expansion(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 2 ] }
variables { domain: [ 0, 2 ] }
constraints {
table {
exprs { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
values: [ 0, 0, 1, 0, 1, 1, 2, 2 ]
}
}
)pb");
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, CanonicalAffineVar) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 0, 2, 2, 4, 4 ] }
variables { domain: [ 1, 10 ] }
constraints {
linear {
vars: [ 0, 1 ]
coeffs: [ 1, 2 ]
domain: [ 3, 1000 ]
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 2 ] }
variables { domain: [ 1, 10 ] }
constraints {
linear {
vars: [ 1, 0 ]
coeffs: [ 1, 1 ]
domain: [ 2, 12 ]
}
}
)pb");
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, IdempotentElement) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 1, 1, 3, 4 ] }
variables { domain: [ 0, 5 ] }
variables { domain: [ 0, 5 ] }
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 1, 1, 1 ]
domain: [ 5, 5 ]
}
}
constraints {
element {
linear_index { vars: 0 coeffs: 1 }
linear_target { vars: 0 coeffs: 1 }
exprs { offset: 1 }
exprs { offset: 1 }
exprs { offset: 3 }
exprs { offset: 3 }
exprs { offset: 4 }
exprs { offset: 12 }
}
}
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
)pb");
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, AffineElement) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 5 ] }
variables { domain: [ 0, 7 ] }
constraints {
element {
linear_index { vars: 0 coeffs: 1 }
linear_target { vars: 1 coeffs: 1 }
exprs { offset: 1 }
exprs { offset: 2 }
exprs { offset: 3 }
exprs { offset: 4 }
exprs { offset: 5 }
exprs { offset: 6 }
}
}
constraints { dummy_constraint { vars: [ 0, 1 ] } }
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 5 ] }
variables { domain: [ 1, 6 ] }
constraints {
linear {
vars: [ 1, 0 ]
coeffs: [ 1, -1 ]
domain: [ 1, 1 ]
}
}
)pb");
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, AffineElementWithScaledBooleanIndex) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 0, 3, 3 ] }
variables { domain: [ 0, 2 ] }
constraints {
element {
linear_index { vars: 0 coeffs: 1 }
linear_target { vars: 1 coeffs: 1 }
exprs { offset: 2 }
exprs { offset: 0 }
exprs { offset: 0 }
exprs { offset: 0 }
}
}
constraints { dummy_constraint { vars: [ 0, 1 ] } }
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 0, 3, 3 ] }
variables { domain: [ 0, 0, 2, 2 ] }
variables { domain: [ 0, 1 ] }
constraints {
linear { vars: 0 vars: 2 coeffs: 1 coeffs: -3 domain: 0 domain: 0 }
}
constraints {
linear { vars: 1 vars: 2 coeffs: 1 coeffs: 2 domain: 2 domain: 2 }
}
)pb");
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, AffineElementWithNonIntegerSlope) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 6 ] }
variables { domain: [ -2, 2 ] }
constraints {
element {
linear_index { vars: 0 coeffs: 1 }
linear_target { vars: 1 coeffs: 1 }
exprs { offset: 2 }
exprs { offset: 5 }
exprs { offset: 6 }
exprs { offset: 0 }
exprs { offset: 7 }
exprs { offset: 8 }
exprs { offset: -2 }
}
}
constraints { dummy_constraint { vars: [ 0, 1 ] } }
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 0, 3, 3, 6, 6 ] }
variables { domain: [ -2, -2, 0, 0, 2, 2 ] }
variables { domain: [ 0, 2 ] }
constraints {
linear { vars: 0 vars: 2 coeffs: 1 coeffs: 3 domain: 6 domain: 6 }
}
constraints {
linear { vars: 1 vars: 2 coeffs: 1 coeffs: -2 domain: -2 domain: -2 }
}
)pb");
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, ReduceDomainsInAutomaton) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 1, 1, 3, 3, 6, 10 ] }
variables { domain: [ 1, 1, 3, 3, 6, 6 ] }
variables { domain: [ 1, 3, 6, 6 ] }
constraints {
automaton {
exprs { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
transition_tail: [ 4, 1, 1, 1, 2, 3, 4 ]
transition_head: [ 4, 2, 3, 4, 2, 3, 4 ]
transition_label: [ 4, 1, 3, 6, 1, 3, 6 ]
starting_state: 1
final_states: [ 2, 3, 4 ]
}
}
)pb");
SatParameters params;
params.set_disable_constraint_expansion(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 1, 1, 3, 3, 6, 6 ] }
variables { domain: [ 1, 1, 3, 3, 6, 6 ] }
variables { domain: [ 1, 1, 3, 3, 6, 6 ] }
constraints {
automaton {
exprs { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
transition_tail: [ 4, 1, 1, 1, 2, 3, 4 ]
transition_head: [ 4, 2, 3, 4, 2, 3, 4 ]
transition_label: [ 4, 1, 3, 6, 1, 3, 6 ]
starting_state: 1
final_states: [ 2, 3, 4 ]
}
}
)pb");
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, UnsatIntegerLinearConstraint) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 7, 6, 5 ]
domain: [ 1, 4 ]
}
}
)pb");
ExpectInfeasibleDuringPresolve(initial_model);
}
TEST(PresolveCpModelTest, LinMaxCanBeRemoved) {
// The target variable is not constraining after simple propagation and not
// used anywhere else.
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -15, 8 ] }
variables { domain: [ -2, 7 ] }
variables { domain: [ -4, 11 ] }
constraints {
lin_max {
target: { vars: 0 coeffs: 1 }
exprs: { vars: 1 coeffs: 1 }
exprs: { vars: 2 coeffs: 1 }
}
}
constraints { dummy_constraint { vars: [ 1, 2 ] } }
)pb");
CpModelProto presolved_model = initial_model;
CpModelProto mapping_model;
std::vector<int> mapping;
Model model;
auto* params = model.GetOrCreate<SatParameters>();
params->set_permute_variable_randomly(false);
params->set_cp_model_probing_level(0);
PresolveContext context(&model, &presolved_model, &mapping_model);
CpModelPresolver presolver(&context, &mapping);
presolver.Presolve();
const CpModelProto expected_mapping_model = ParseTestProto(R"pb(
variables { domain: [ -2, 8 ] }
variables { domain: [ -2, 7 ] }
variables { domain: [ -4, 8 ] }
constraints {
lin_max {
target { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
}
}
)pb");
EXPECT_THAT(expected_mapping_model, testing::EqualsProto(mapping_model));
}
TEST(PresolveCpModelTest, LinMaxCannotBeRemoved) {
// Almost the same as above, but the target of the int_max might constraint
// the other variable via its lower bound, so we cannot remove it.
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 8 ] }
variables { domain: [ -2, 7 ] }
variables { domain: [ -4, 11 ] }
constraints {
lin_max {
target { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 8 ] }
variables { domain: [ -2, 7 ] }
variables { domain: [ -4, 8 ] }
constraints {
lin_max {
target: { vars: 0 coeffs: 1 }
exprs: { vars: 1 coeffs: 1 }
exprs: { vars: 2 coeffs: 1 }
}
}
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, LinMaxCannotBeRemovedWithHoles) {
// Almost the same as above, but the target does not contains the infered
// domain.
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -4, 2, 5, 11 ] }
variables { domain: [ 0, 2, 4, 8 ] }
variables { domain: [ -2, 1, 4, 7 ] }
constraints {
lin_max {
target { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 2, 5, 8 ] }
variables { domain: [ 0, 2, 4, 8 ] }
variables { domain: [ -2, 1, 4, 7 ] }
constraints {
lin_max {
target: { vars: 0 coeffs: 1 }
exprs: { vars: 1 coeffs: 1 }
exprs: { vars: 2 coeffs: 1 }
}
}
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, DetectVarValueEncoding) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 1, 3 ] }
constraints {
enforcement_literal: 0
linear {
vars: 1
coeffs: 1
domain: [ 2, 2 ]
}
}
constraints {
enforcement_literal: -1
linear {
vars: 1
coeffs: 1
domain: [ 1, 1, 3, 3 ]
}
}
)pb");
Model model;
model.GetOrCreate<SatParameters>()
->set_keep_all_feasible_solutions_in_presolve(true);
CpModelProto presolved_model = initial_model;
CpModelProto mapping_model;
std::vector<int> mapping;
PresolveContext context(&model, &presolved_model, &mapping_model);
PresolveCpModel(&context, &mapping);
int encoding_literal = -1;
EXPECT_TRUE(context.HasVarValueEncoding(1, int64_t{2}, &encoding_literal));
EXPECT_EQ(encoding_literal, 0);
}
TEST(FindDuplicateConstraintsTest, BasicTest) {
const CpModelProto model = ParseTestProto(R"pb(
variables { domain: [ 0, 8 ] }
variables { domain: [ -2, 7 ] }
variables { domain: [ -4, 11 ] }
constraints {
lin_max {
target { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 2 }
}
}
constraints {
name: "name are ignored"
lin_max {
target { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 2 }
}
}
constraints {
lin_max {
target { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 2 }
}
}
constraints {
lin_max {
target { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 2 }
}
}
)pb");
std::vector<std::pair<int, int>> duplicates = FindDuplicateConstraints(model);
EXPECT_THAT(duplicates,
::testing::ElementsAre(std::make_pair(1, 0), std::make_pair(2, 0),
std::make_pair(3, 0)));
}
TEST(FindDuplicateConstraintsTest, LinearConstraintParallelToObjective) {
const CpModelProto model = ParseTestProto(R"pb(
constraints {
name: "name are ignored"
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 3, 3, 7 ]
}
}
objective {
vars: [ 0, 1, 2 ]
coeffs: [ 3, 3, 7 ]
}
)pb");
std::vector<std::pair<int, int>> duplicates = FindDuplicateConstraints(model);
EXPECT_THAT(duplicates,
::testing::ElementsAre(std::make_pair(0, kObjectiveConstraint)));
}
TEST(DetectDuplicateConstraintsTest, DifferentRedundantEnforcement) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 5 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
enforcement_literal: [ 4 ]
linear {
vars: [ 0, 1, 2, 3 ]
coeffs: [ 2, 3, 4, 1 ]
domain: [ 0, 6 ]
}
}
constraints {
enforcement_literal: [ 5 ]
linear {
vars: [ 0, 1, 2, 3 ]
coeffs: [ 2, 3, 4, 1 ]
domain: [ 0, 6 ]
}
}
constraints {
enforcement_literal: [ 4, 5 ]
linear {
vars: [ 0, 1, 2, 3 ]
coeffs: [ 2, 3, 4, 1 ]
domain: [ 0, 6 ]
}
}
constraints { bool_or { literals: [ -5, 5 ] } }
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 5 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
enforcement_literal: [ 5 ]
linear {
vars: [ 0, 1, 2, 3 ]
coeffs: [ 2, 3, 4, 1 ]
domain: [ 0, 6 ]
}
}
constraints {
enforcement_literal: -6
bool_and { literals: -5 }
})pb");
SatParameters params;
params.set_cp_model_probing_level(2);
params.set_keep_all_feasible_solutions_in_presolve(true);
CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveTest, EncodingIssue) {
const CpModelProto model = ParseTestProto(R"pb(
variables { domain: [ 1, 3 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 0, 2, 2 ] }
variables { domain: [ 0, 0, 3, 3 ] }
constraints {
element {
linear_index { vars: 0 coeffs: 1 }
linear_target: { offset: 1 }
exprs { offset: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
exprs { vars: 3 coeffs: 1 }
}
}
constraints {
linear {
vars: [ 1, 2, 3 ]
coeffs: [ 1, 1, 1 ]
domain: [ 1, 1 ]
}
}
constraints {
element {
linear_index { vars: 0 coeffs: 1 }
linear_target { vars: 4 coeffs: 1 }
exprs { vars: 4 coeffs: 1 }
exprs { offset: 2 }
exprs { offset: 0 }
exprs { offset: 0 }
}
}
constraints {
element {
linear_index { vars: 0 coeffs: 1 }
linear_target { vars: 5 coeffs: 1 }
exprs { vars: 5 coeffs: 1 }
exprs { offset: 0 }
exprs { offset: 3 }
exprs { offset: 0 }
}
}
)pb");
SatParameters params;
params.set_log_search_progress(true);
const CpSolverResponse response = SolveWithParameters(model, params);
EXPECT_EQ(response.status(), CpSolverStatus::OPTIMAL);
}
// This test was failing with the wrong optimal.
TEST(PresolveCpModelTest, FailedRandomTest) {
const CpModelProto model = ParseTestProto(R"pb(
variables { domain: [ 7, 7 ] }
variables { domain: [ -5, 8 ] }
variables { domain: [ -6, -2 ] }
variables { domain: [ -8, -2 ] }
variables { domain: [ -7, -4 ] }
variables { domain: [ -4, 7 ] }
constraints {
linear {
vars: 0
vars: 2
vars: 4
vars: 5
coeffs: -8
coeffs: 7
coeffs: -35
coeffs: 80
domain: -42
domain: -21
}
}
constraints {
linear {
vars: 0
vars: 2
vars: 4
vars: 5
coeffs: 40
coeffs: -35
coeffs: 175
coeffs: -400
domain: 105
domain: 105
domain: 110
domain: 110
domain: 115
domain: 115
domain: 120
domain: 120
domain: 125
domain: 125
domain: 130
domain: 130
domain: 135
domain: 135
domain: 140
domain: 140
domain: 145
domain: 145
domain: 150
domain: 150
domain: 155
domain: 155
domain: 160
domain: 160
domain: 165
domain: 165
domain: 170
domain: 170
domain: 175
domain: 175
domain: 180
domain: 180
domain: 185
domain: 185
domain: 190
domain: 190
domain: 195
domain: 195
domain: 200
domain: 200
domain: 205
domain: 205
domain: 210
domain: 210
}
}
constraints {
linear {
vars: 0
vars: 2
vars: 4
vars: 5
coeffs: -8
coeffs: 7
coeffs: -35
coeffs: 80
domain: -42
domain: -21
}
}
constraints {
linear {
vars: 0
vars: 2
vars: 4
vars: 5
coeffs: 40
coeffs: -35
coeffs: 175
coeffs: -400
domain: 105
domain: 105
domain: 110
domain: 110
domain: 115
domain: 115
domain: 120
domain: 120
domain: 125
domain: 125
domain: 130
domain: 130
domain: 135
domain: 135
domain: 140
domain: 140
domain: 145
domain: 145
domain: 150
domain: 150
domain: 155
domain: 155
domain: 160
domain: 160
domain: 165
domain: 165
domain: 170
domain: 170
domain: 175
domain: 175
domain: 180
domain: 180
domain: 185
domain: 185
domain: 190
domain: 190
domain: 195
domain: 195
domain: 200
domain: 200
domain: 205
domain: 205
domain: 210
domain: 210
}
}
objective { vars: 3 vars: 1 vars: 2 coeffs: 37 coeffs: -18 coeffs: -7 }
)pb");
SatParameters params;
params.set_log_search_progress(true);
const CpSolverResponse response = SolveWithParameters(model, params);
EXPECT_EQ(response.status(), CpSolverStatus::OPTIMAL);
EXPECT_EQ(response.objective_value(), -419);
}
TEST(PresolveCpModelTest, DetectDuplicateVarEqValueEncoding) {
CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 9 ] }
constraints {
enforcement_literal: 0
linear {
vars: [ 2 ]
coeffs: [ 1 ]
domain: [ 6, 6 ]
}
}
constraints {
enforcement_literal: -1
linear {
vars: [ 2 ]
coeffs: [ 1 ]
domain: [ 0, 5, 7, 9 ]
}
}
constraints {
enforcement_literal: 1
linear {
vars: [ 2 ]
coeffs: [ 1 ]
domain: [ 6, 6 ]
}
}
constraints {
enforcement_literal: -2
linear {
vars: [ 2 ]
coeffs: [ 1 ]
domain: [ 0, 5, 7, 9 ]
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 9 ] }
constraints {
enforcement_literal: 0
linear {
vars: [ 1 ]
coeffs: [ 1 ]
domain: [ 6, 6 ]
}
}
constraints {
enforcement_literal: -1
linear {
vars: [ 1 ]
coeffs: [ 1 ]
domain: [ 0, 5, 7, 9 ]
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, EqualityWithOnlyTwoOddBooleans) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 99 ] }
variables { domain: [ 0, 99 ] }
constraints {
linear {
vars: [ 0, 1, 2, 3 ]
coeffs: [ 1, 3, 4, 4 ]
domain: [ 60, 60 ]
}
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 15 ] }
variables { domain: [ 0, 15 ] }
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 1, 1, 1 ]
domain: [ 15, 15 ]
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, DualEquality) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 99 ] }
variables { domain: [ 0, 99 ] }
constraints {
enforcement_literal: 0
bool_and { literals: 1 }
}
# Anything that we don't know how to presolve.
# TODO(user): could be nice to had a "unknown" constraint for this purpose.
constraints {
all_diff {
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
exprs { vars: 3 coeffs: 1 }
}
}
objective {
vars: [ 0, 1, 2, 3 ]
coeffs: [ -1, 1, 1, 1 ]
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 99 ] }
variables { domain: [ 0, 99 ] }
constraints {
all_diff {
exprs { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
}
}
objective {
vars: [ 1, 2 ]
coeffs: [ 1, 1 ]
scaling_factor: 1
domain: [ 0, 198 ]
}
)pb");
CpModelProto presolved_model = PresolveForTest(initial_model);
EXPECT_THAT(presolved_model, EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, EmptyProduct) {
// A rho shape.
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
constraints { int_prod { target: { vars: 0 coeffs: 1 } } }
constraints { dummy_constraint { vars: [ 0 ] } }
)pb");
CpModelProto presolved_model = PresolveForTest(initial_model);
const CpModelProto expected_model = ParseTestProto(R"pb(
variables { domain: [ 1, 1 ] }
)pb");
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_model));
}
TEST(PresolveCpModelTest, ElementWithTargetEqualIndex) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 1, 1 ] } # 0
variables { domain: [ 0, 4 ] } # 1 - ok
variables { domain: [ 3, 7 ] } # 2
variables { domain: [ 3, 3 ] } # 3 - ok
variables { domain: [ 4, 9 ] } # 4 - ok
constraints {
element {
linear_index { vars: 0 coeffs: 1 }
linear_target { vars: 0 coeffs: 1 }
exprs { vars: 1 coeffs: 1 }
exprs { vars: 2 coeffs: 1 }
exprs { vars: 3 coeffs: 1 }
exprs { vars: 4 coeffs: 1 }
exprs { vars: 5 coeffs: 1 }
}
}
)pb");
SatParameters params;
params.set_disable_constraint_expansion(true);
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: 1 domain: 1 domain: 3 domain: 4 }
variables { domain: 0 domain: 4 }
variables { domain: 3 domain: 7 }
variables { domain: 4 domain: 9 }
constraints {
element {
linear_index { vars: 0 coeffs: 1 }
linear_target { vars: 0 coeffs: 1 }
exprs {}
exprs { vars: 1 coeffs: 1 }
exprs {}
exprs { offset: 3 }
exprs { vars: 3 coeffs: 1 }
}
}
)pb");
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, ReduceDomainsInInverse) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 1, 3 ] }
variables { domain: [ 0, 4 ] }
variables { domain: [ 0, 2 ] }
variables { domain: [ 0, 2 ] }
variables { domain: [ 0, 2 ] }
constraints {
inverse {
f_direct: [ 0, 1, 2 ]
f_inverse: [ 3, 4, 5 ]
}
}
)pb");
const CpModelProto domains = GetReducedDomains(initial_model);
const CpModelProto expected_domains = ParseTestProto(R"pb(
variables { domain: [ 0, 2 ] }
variables { domain: [ 1, 2 ] }
variables { domain: [ 0, 2 ] }
variables { domain: [ 0, 0, 2, 2 ] }
variables { domain: [ 0, 2 ] }
variables { domain: [ 0, 2 ] }
)pb");
EXPECT_THAT(expected_domains, testing::EqualsProto(domains));
}
TEST(PresolveCpModelTest, RemoveZeroEventsFromReservoir) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 8 ] }
variables { domain: [ 0, 9 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 11 ] }
variables { domain: [ 1, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
reservoir {
min_level: 0
max_level: 10
time_exprs { vars: 0 coeffs: 1 }
time_exprs { vars: 1 coeffs: 1 }
time_exprs { vars: 2 coeffs: 1 }
time_exprs { vars: 3 coeffs: 1 }
active_literals: [ 4, 4, 5, 6 ]
level_changes: { offset: 3 }
level_changes: { offset: 0 }
level_changes: { offset: 3 }
level_changes: { offset: -2 }
}
}
)pb");
SatParameters params;
params.set_disable_constraint_expansion(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 8 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 11 ] }
variables { domain: [ 1, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
reservoir {
min_level: 0
max_level: 6
time_exprs { vars: 0 coeffs: 1 }
time_exprs { vars: 1 coeffs: 1 }
time_exprs { vars: 2 coeffs: 1 }
active_literals: [ 3, 4, 5 ]
level_changes: { offset: 3 }
level_changes: { offset: 3 }
level_changes: { offset: -2 }
}
}
)pb");
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, RemoveInactiveEventsFromReservoir) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 8 ] }
variables { domain: [ 0, 9 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 11 ] }
variables { domain: [ 0, 0 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
reservoir {
min_level: 0
max_level: 10
time_exprs { vars: 0 coeffs: 1 }
time_exprs { vars: 1 coeffs: 1 }
time_exprs { vars: 2 coeffs: 1 }
time_exprs { vars: 3 coeffs: 1 }
active_literals: [ 4, 4, 5, 6 ]
level_changes: { offset: 3 }
level_changes: { offset: -1 }
level_changes: { offset: 3 }
level_changes: { offset: -2 }
}
}
)pb");
SatParameters params;
params.set_disable_constraint_expansion(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 11 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
reservoir {
min_level: 0
max_level: 3
time_exprs { vars: 0 coeffs: 1 }
time_exprs { vars: 1 coeffs: 1 }
active_literals: [ 2, 3 ]
level_changes: { offset: 3 }
level_changes: { offset: -2 }
}
}
)pb");
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, RemoveUnusedEncoding) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 3 ] }
constraints { dummy_constraint { vars: [ 0, 1, 2 ] } }
constraints {
enforcement_literal: 0
linear {
vars: 3
coeffs: 1
domain: [ 0, 0 ]
}
}
constraints {
enforcement_literal: 1
linear {
vars: 3
coeffs: 1
domain: [ 1, 1 ]
}
}
constraints {
enforcement_literal: 2
linear {
vars: 3
coeffs: 1
domain: [ 2, 2 ]
}
}
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints { at_most_one { literals: [ 0, 1, 2 ] } }
)pb");
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, RemoveUnusedEncodingWithObjective) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 2 ] }
objective {
vars: [ 3 ]
coeffs: [ 1 ]
}
constraints { dummy_constraint { vars: [ 0, 1, 2 ] } }
constraints {
enforcement_literal: 0
linear {
vars: 3
coeffs: 1
domain: [ 0, 0 ]
}
}
constraints {
enforcement_literal: 1
linear {
vars: 3
coeffs: 1
domain: [ 1, 1 ]
}
}
constraints {
enforcement_literal: 2
linear {
vars: 3
coeffs: 1
domain: [ 2, 2 ]
}
}
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints { exactly_one { literals: [ 3, 4, 5 ] } }
constraints {
enforcement_literal: -4
bool_and { literals: -1 }
}
constraints {
enforcement_literal: -5
bool_and { literals: -2 }
}
constraints {
enforcement_literal: -6
bool_and { literals: -3 }
}
objective: {
vars: [ 3, 5 ]
coeffs: [ -1, 1 ]
scaling_factor: 1
offset: 1
integer_before_offset: 1
domain: [ -1, 1 ]
}
)pb");
EXPECT_THAT(presolved_model,
ModelEqualsIgnoringConstraintsOrder(expected_presolved_model));
}
TEST(PresolveCpModelTest, RemovableEnforcementLiteral) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
constraints { dummy_constraint { vars: [ 1, 2, 3 ] } }
constraints {
enforcement_literal: 0
linear {
vars: 1
coeffs: 1
domain: [ 0, 5 ]
}
}
constraints {
enforcement_literal: -1
linear {
vars: 1
coeffs: 1
domain: [ 4, 7 ]
}
}
)pb");
const CpModelProto presolved_model = PresolveForTest(initial_model);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 7 ] }
variables { domain: [ 0, 10 ] }
variables { domain: [ 0, 10 ] }
)pb");
EXPECT_THAT(expected_presolved_model, testing::EqualsProto(presolved_model));
}
TEST(PresolveCpModelTest, LinearAndExactlyOne) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 5 ] }
constraints { exactly_one { literals: [ 0, 1, 2 ] } }
constraints {
linear {
vars: [ 0, 1, 2, 3 ]
coeffs: [ 2, 3, 4, 1 ]
domain: [ 0, 6 ]
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 4 ] }
constraints { exactly_one { literals: [ 0, 1, 2 ] } }
constraints {
linear {
vars: [ 1, 2, 3 ]
coeffs: [ 1, 2, 1 ]
domain: [ 0, 4 ]
}
}
)pb");
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, LinearAndAtMostOnePropagation) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 50 ] }
constraints { at_most_one { literals: [ 0, 1, 2 ] } }
constraints {
linear {
vars: [ 0, 1, 2, 3 ]
coeffs: [ 2, 3, 4, -1 ]
domain: [ 0, 10 ]
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
// Not only we need to consider the pair of constraint to know that the
// last variable is <= 4, but once we know that, we can extract the variable
// with coefficient 4 as an enforcement literal.
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 4 ] }
constraints {
enforcement_literal: -3
linear {
vars: [ 0, 1, 3 ]
coeffs: [ 2, 3, -1 ]
domain: [ 0, 5 ]
}
}
constraints { at_most_one { literals: [ 0, 1, 2 ] } }
)pb");
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, LinMaxWithBoolean) {
const int num_vars = 5;
const int max_value = 4;
absl::BitGen random;
for (int runs = 0; runs < 1000; ++runs) {
// Create num_vars variable that are either fixed or have two values.
CpModelProto cp_model;
for (int i = 0; i < num_vars; ++i) {
FillDomainInProto(
Domain::FromValues({absl::Uniform<int>(random, 0, max_value + 1),
absl::Uniform<int>(random, 0, max_value + 1)}),
cp_model.add_variables());
}
// We randomize the variable to have duplicates.
LinearArgumentProto* lin_max =
cp_model.add_constraints()->mutable_lin_max();
lin_max->mutable_target()->add_vars(
absl::Uniform<int>(random, 0, num_vars));
lin_max->mutable_target()->add_coeffs(1);
for (int i = 0; i < absl::Uniform<int>(random, 0, num_vars); ++i) {
LinearExpressionProto* expr = lin_max->add_exprs();
expr->add_vars(absl::Uniform<int>(random, 0, num_vars));
expr->add_coeffs(1);
}
int num_solutions_without_presolve = 0;
{
Model model;
SatParameters parameters;
parameters.set_enumerate_all_solutions(true);
parameters.set_keep_all_feasible_solutions_in_presolve(true);
parameters.set_cp_model_presolve(false);
parameters.set_log_search_progress(true);
model.Add(NewSatParameters(parameters));
model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse& r) {
num_solutions_without_presolve++;
}));
SolveCpModel(cp_model, &model);
}
int num_solutions_with_presolve = 0;
{
Model model;
SatParameters parameters;
parameters.set_enumerate_all_solutions(true);
parameters.set_keep_all_feasible_solutions_in_presolve(true);
parameters.set_log_search_progress(true);
model.Add(NewSatParameters(parameters));
model.Add(NewFeasibleSolutionObserver(
[&](const CpSolverResponse& r) { num_solutions_with_presolve++; }));
SolveCpModel(cp_model, &model);
}
// Note that the solution are checked by the checker, so there is not really
// any need to compare that we get exactly the same ones.
ASSERT_EQ(num_solutions_with_presolve, num_solutions_without_presolve)
<< ProtobufDebugString(cp_model);
}
}
TEST(PresolveCpModelTest, Bug174584992) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ -288230376151711744, 262144 ] }
variables { domain: [ 0, 5 ] }
constraints {
name: "T"
linear { vars: 1 vars: 0 coeffs: 1 coeffs: 2 }
}
)pb");
Model tmp_model;
EXPECT_EQ(SolveCpModel(initial_model, &tmp_model).status(),
CpSolverStatus::INFEASIBLE);
}
TEST(PresolveTest, DetectInfeasibilityDuringMerging) {
ExpectInfeasibleDuringPresolve(ParseTestProto(R"pb(
variables { domain: [ -100, 100 ] }
variables { domain: [ -100, 100 ] }
variables { domain: [ -100, 100 ] }
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 1, 2, 3 ]
domain: [ 0, 10 ]
}
}
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 1, 2, 3 ]
domain: [ 11, 20 ]
}
}
)pb"));
}
TEST(PresolveTest, DetectEncodingFromLinear) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ -100, 100 ] }
constraints { exactly_one { literals: [ 0, 1, 2, 3, 4 ] } }
constraints {
linear {
vars: [ 0, 1, 3, 4, 5 ]
coeffs: [ 1, 7, -2, 4, 1 ]
domain: [ 10, 10 ]
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
// The values are 10, 10-1, 10-7, 10+2, and 10-4.
EXPECT_EQ(ReadDomainFromProto(presolved_model.variables(5)).ToString(),
"[3][6][9,10][12]");
}
TEST(PresolveTest, OrToolsIssue2924) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 2 ] }
variables { domain: [ 0, 2 ] }
variables { domain: [ 0, 2 ] }
variables { domain: [ 0, 1000 ] } # This lower bound caused issues.
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 1, 1, 1 ]
domain: [ 0, 1 ]
}
}
constraints {
linear {
vars: [ 0, 1, 2, 3 ]
coeffs: [ 80, 100, 120, -1 ]
domain: [ 95, 95 ]
}
}
objective {
vars: [ 3 ]
coeffs: [ 1 ]
}
)pb");
SatParameters params;
params.set_log_search_progress(true);
EXPECT_EQ(SolveWithParameters(initial_model, params).status(),
CpSolverStatus::OPTIMAL);
}
TEST(PresolveCpModelTest, AtMostOneAndLinear) {
// Using the at most one, the linear constraint will be always true.
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 4 ] } # variable 4
variables { domain: [ 0, 1 ] }
constraints {
linear {
vars: [ 0, 1, 2, 4 ]
coeffs: [ 1, 1, 1, 1 ]
domain: [ 0, 5 ]
}
}
constraints { at_most_one { literals: [ 0, 1, 2, 5 ] } }
)pb");
const CpModelProto expected_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 4 ] }
variables { domain: [ 0, 1 ] }
constraints { at_most_one { literals: [ 0, 1, 2, 5 ] } }
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_model));
}
TEST(PresolveCpModelTest, AtMostOneWithSingleton) {
// Using the at most one, the linear constraint will be always true.
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 100 ] }
variables { domain: [ 0, 100 ] }
variables { domain: [ 0, 100 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
linear {
vars: [ 0, 1, 2, 3, 4 ]
coeffs: [ 1534, 5646, 4564, 145, 178 ]
domain: [ 47888, 53888 ]
}
}
constraints { at_most_one { literals: [ 3, 4, 5 ] } }
objective {
vars: [ 0, 1, 2, 3, 4, 5 ]
coeffs: [ 1534, 5646, 4564, -878, -787, -874 ]
}
)pb");
// We transform the at most one to exactly one and then shift the cost to
// the other variable so we can remove a singleton.
const CpModelProto expected_model = ParseTestProto(R"pb(
variables { domain: [ 0, 32 ] }
variables { domain: [ 0, 9 ] }
variables { domain: [ 0, 11 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints {
linear {
vars: [ 0, 1, 2, 3, 4 ]
coeffs: [ 1534, 5646, 4564, 145, 178 ]
domain: [ 47888, 53888 ]
}
}
constraints {
enforcement_literal: 3
bool_and { literals: [ -5 ] }
}
objective {
scaling_factor: 1
offset: -874
integer_before_offset: -874
vars: [ 0, 1, 2, 3, 4 ]
coeffs: [ 1534, 5646, 4564, -4, 87 ]
domain: [ -4, 150193 ]
}
)pb");
SatParameters params;
CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_model));
}
TEST(PresolveCpModelTest, PresolveDiophantinePreservesSolutionHint) {
// Diophantine equation: https://miplib.zib.de/instance_details_ej.html.
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 1, 10000000 ] }
variables { domain: [ 0, 10000000 ] }
variables { domain: [ 0, 10000000 ] }
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 31013, -41014, -51015 ]
domain: [ 0, 0 ]
}
}
objective {
vars: [ 0 ]
coeffs: [ 1 ]
}
solution_hint {
vars: [ 0, 1, 2 ]
values: [ 25508, 1, 15506 ]
}
)pb");
SatParameters params;
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
ASSERT_EQ(presolved_model.solution_hint().vars_size(),
presolved_model.variables_size());
std::vector<int64_t> solution_hint(presolved_model.variables_size());
for (int i = 0; i < presolved_model.solution_hint().vars_size(); ++i) {
solution_hint[presolved_model.solution_hint().vars(i)] =
presolved_model.solution_hint().values(i);
}
EXPECT_TRUE(SolutionIsFeasible(presolved_model, solution_hint));
}
TEST(PresolveCpModelTest, SolveDiophantine) {
// Diophantine equation: https://miplib.zib.de/instance_details_ej.html.
const CpModelProto model_proto = ParseTestProto(R"pb(
variables { domain: [ 1, 10000000 ] }
variables { domain: [ 0, 10000000 ] }
variables { domain: [ 0, 10000000 ] }
constraints {
linear {
vars: [ 0, 1, 2 ]
coeffs: [ 31013, -41014, -51015 ]
domain: [ 0, 0 ]
}
}
objective {
vars: [ 0 ]
coeffs: [ 1 ]
}
)pb");
SatParameters params;
params.set_cp_model_presolve(true);
// Should solve in < .01 second. Note that deterministic time is not
// completely accurate.
params.set_max_deterministic_time(.001);
const CpSolverResponse response_with =
SolveWithParameters(model_proto, params);
EXPECT_EQ(response_with.status(), CpSolverStatus::OPTIMAL);
EXPECT_EQ(response_with.solution(0), 25508);
// Does not solve without presolving.
params.set_cp_model_presolve(false);
const CpSolverResponse response_without =
SolveWithParameters(model_proto, params);
EXPECT_NE(response_without.status(), CpSolverStatus::OPTIMAL);
}
TEST(PresolveCpModelTest, IncompatibleLinear) {
// a <=> x <= y
// b <=> x >= y
// a => not(b)
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 6 ] }
variables { domain: [ 0, 6 ] }
constraints {
enforcement_literal: [ 0 ]
linear {
vars: [ 2, 3 ]
coeffs: [ 1, -1 ]
domain: [ -6, 0 ]
}
}
constraints {
enforcement_literal: [ -1 ]
linear {
vars: [ 2, 3 ]
coeffs: [ 1, -1 ]
domain: [ 1, 6 ]
}
}
constraints {
enforcement_literal: [ 1 ]
linear {
vars: [ 2, 3 ]
coeffs: [ 1, -1 ]
domain: [ 0, 6 ]
}
}
constraints {
enforcement_literal: [ -2 ]
linear {
vars: [ 2, 3 ]
coeffs: [ 1, -1 ]
domain: [ -6, -1 ]
}
}
constraints {
enforcement_literal: 0
bool_and { literals: [ -2 ] }
}
)pb");
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 6 ] }
variables { domain: [ 0, 6 ] }
constraints {
enforcement_literal: [ 0 ]
linear {
vars: [ 1, 2 ]
coeffs: [ 1, -1 ]
domain: [ -6, -1 ]
}
}
constraints {
enforcement_literal: [ -1 ]
linear {
vars: [ 1, 2 ]
coeffs: [ 1, -1 ]
domain: [ 1, 6 ]
}
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
CpModelProto presolved_model = PresolveForTest(initial_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, SearchStrategySurvivePresolve) {
const CpModelProto proto = ParseTestProto(R"pb(
variables {
name: "x"
domain: [ 1, 10 ]
}
variables {
name: "y"
domain: [ 3, 8 ]
}
search_strategy {
exprs: { vars: 1 coeffs: 1 }
exprs: { vars: 0 coeffs: -1 }
domain_reduction_strategy: SELECT_MAX_VALUE
}
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
CpModelProto presolved_model = PresolveForTest(proto, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(proto));
}
TEST(PresolveCpModelTest, AmoRectangle) {
// We need a large rectangle, so we generate this by hand.
CpModelProto model;
for (int i = 0; i < 100; ++i) {
auto* var = model.add_variables();
var->add_domain(0);
var->add_domain(1);
}
for (int i = 0; i < 10; ++i) {
auto* var = model.add_variables();
var->add_domain(0);
var->add_domain(5);
}
{
auto* amo = model.add_constraints()->mutable_at_most_one();
for (int i = 0; i < 100; ++i) amo->add_literals(i);
}
{
auto* linear = model.add_constraints()->mutable_linear();
for (int i = 0; i < 100; ++i) {
linear->add_vars(i);
linear->add_coeffs(1);
}
linear->add_vars(100);
linear->add_coeffs(1);
linear->add_vars(101);
linear->add_coeffs(1);
linear->add_domain(0);
linear->add_domain(5);
}
{
auto* linear = model.add_constraints()->mutable_linear();
for (int i = 0; i < 100; ++i) {
linear->add_vars(i);
linear->add_coeffs(3);
}
linear->add_vars(102);
linear->add_coeffs(1);
linear->add_vars(103);
linear->add_coeffs(1);
linear->add_domain(0);
linear->add_domain(5);
}
{
auto* linear = model.add_constraints()->mutable_linear();
for (int i = 0; i < 100; ++i) {
linear->add_vars(i);
linear->add_coeffs(-2);
}
linear->add_vars(104);
linear->add_coeffs(1);
linear->add_vars(105);
linear->add_coeffs(1);
linear->add_domain(0);
linear->add_domain(5);
}
CpModelProto expected_presolved_model;
for (int i = 0; i < 100; ++i) {
auto* var = expected_presolved_model.add_variables();
var->add_domain(0);
var->add_domain(1);
}
for (int i = 0; i < 10; ++i) {
auto* var = expected_presolved_model.add_variables();
var->add_domain(0);
var->add_domain(5);
}
{
// New new variable.
auto* var = expected_presolved_model.add_variables();
var->add_domain(0);
var->add_domain(1);
}
{
auto* linear = expected_presolved_model.add_constraints()->mutable_linear();
linear->add_vars(100);
linear->add_coeffs(1);
linear->add_vars(101);
linear->add_coeffs(1);
linear->add_vars(110);
linear->add_coeffs(1);
linear->add_domain(0);
linear->add_domain(5);
}
{
auto* linear = expected_presolved_model.add_constraints()->mutable_linear();
linear->add_vars(102);
linear->add_coeffs(1);
linear->add_vars(103);
linear->add_coeffs(1);
linear->add_vars(110);
linear->add_coeffs(3);
linear->add_domain(0);
linear->add_domain(5);
}
{
auto* linear = expected_presolved_model.add_constraints()->mutable_linear();
linear->add_vars(104);
linear->add_coeffs(1);
linear->add_vars(105);
linear->add_coeffs(1);
linear->add_vars(110);
linear->add_coeffs(-2);
linear->add_domain(0);
linear->add_domain(5);
}
{
auto* exo =
expected_presolved_model.add_constraints()->mutable_exactly_one();
exo->add_literals(NegatedRef(110));
for (int i = 0; i < 100; ++i) exo->add_literals(i);
}
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
const CpModelProto presolved_model = PresolveForTest(model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_presolved_model));
}
TEST(PresolveCpModelTest, PreserveHints) {
const CpModelProto input_model = ParseTestProto(R"pb(
variables { domain: [ 1, 1, 4, 4 ] }
variables { domain: [ 0, 0, 3, 3, 9, 9 ] }
constraints {
linear {
vars: [ 0, 1 ]
coeffs: [ 1, 1 ]
domain: [ 1, 10 ]
}
}
solution_hint {
vars: [ 0, 1 ]
values: [ 1, 9 ]
}
)pb");
const CpModelProto expected_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1, 3, 3 ] }
constraints {
enforcement_literal: 0
linear { vars: 1 coeffs: 1 domain: 0 domain: 1 }
}
solution_hint { vars: 0 vars: 1 values: 0 values: 3 }
)pb");
SatParameters params;
params.set_keep_all_feasible_solutions_in_presolve(true);
CpModelProto presolved_model = PresolveForTest(input_model, params);
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_model));
}
TEST(PresolveCpModelTest, DuplicateColumns) {
CpModelProto presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints { at_most_one { literals: [ 1, 2, 3 ] } }
constraints {
linear {
vars: [ 0, 2, 3 ]
coeffs: [ 1, 2, 2 ]
domain: [ 1, 10 ]
}
}
)pb");
Model model;
CpModelProto mapping_model;
PresolveContext context(&model, &presolved_model, &mapping_model);
std::vector<int> mapping;
CpModelPresolver presolver(&context, &mapping);
context.InitializeNewDomains();
context.UpdateNewConstraintsVariableUsage();
presolver.DetectDuplicateColumns();
context.WriteVariableDomainsToProto();
const CpModelProto expected_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
constraints { at_most_one { literals: [ 1, 4 ] } }
constraints {
linear {
vars: [ 0, 4 ]
coeffs: [ 1, 2 ]
domain: [ 1, 10 ]
}
}
)pb");
EXPECT_THAT(presolved_model, testing::EqualsProto(expected_model));
}
TEST(PresolveCpModelTest, TrivialAfterPresolveWithVariousOffsets) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 0 ] }
variables { domain: [ 1, 1 ] }
floating_point_objective {
vars: [ 0, 2 ]
coeffs: [ 1, 1 ]
maximize: true
}
)pb");
const CpSolverResponse response = Solve(initial_model);
EXPECT_EQ(response.status(), CpSolverStatus::OPTIMAL);
EXPECT_EQ(response.objective_value(), 2);
}
TEST(PresolveCpModelTest, EmptyDomain) {
// The model checker doesn't allow empty domains, but we still might generate
// them in LNS.
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [] }
)pb");
PresolveForTest(initial_model, SatParameters(), CpSolverStatus::INFEASIBLE);
}
TEST(PresolveCpModelTest, CanonicalizeAndRemapRoutesConstraintNodeVariables) {
// A complete graph with 3 nodes and the following arcs:
// 0 --l0-> 1 --l2-> 2 --l4-> 0
// 0 <-l1-- 1 <-l3-- 2 <-l5-- 0
//
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
# unused, should be removed.
variables { domain: [ 0, 0 ] }
# fixed value, should be removed.
variables { domain: [ 5, 5 ] }
variables { domain: [ 0, 10 ] }
# should be replaced with an affine representative in [0, 4]
variables { domain: [ 0, 0, 2, 2, 4, 4, 6, 6, 8, 8 ] }
constraints {
routes {
tails: [ 0, 1, 1, 2, 2, 0 ]
heads: [ 1, 0, 2, 1, 0, 2 ]
literals: [ 0, 1, 2, 3, 4, 5 ]
dimensions: {
exprs {
vars: [ 7 ]
coeffs: [ 1 ]
}
exprs {
vars: [ 8 ]
coeffs: [ 1 ]
}
exprs {
vars: [ 9 ]
coeffs: [ 1 ]
}
}
}
}
constraints {
enforcement_literal: 0
linear {
vars: [ 7, 8 ]
coeffs: [ 1, -1 ]
domain: [ 0, 10 ]
}
}
constraints {
enforcement_literal: 1
linear {
vars: [ 8, 7 ]
coeffs: [ 1, -1 ]
domain: [ 0, 10 ]
}
}
constraints {
enforcement_literal: 2
linear {
vars: [ 8, 9 ]
coeffs: [ 1, -1 ]
domain: [ 0, 10 ]
}
}
constraints {
enforcement_literal: 3
linear {
vars: [ 9, 8 ]
coeffs: [ 1, -1 ]
domain: [ 0, 10 ]
}
}
constraints {
enforcement_literal: 4
linear {
vars: [ 9, 7 ]
coeffs: [ 1, -1 ]
domain: [ 0, 10 ]
}
}
constraints {
enforcement_literal: 5
linear {
vars: [ 7, 9 ]
coeffs: [ 1, -1 ]
domain: [ 0, 10 ]
}
}
)pb");
SatParameters params;
const CpModelProto presolved_model = PresolveForTest(initial_model, params);
const CpModelProto expected_presolved_model = ParseTestProto(R"pb(
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 1 ] }
variables { domain: [ 0, 8 ] }
variables { domain: [ 0, 4 ] }
constraints {
routes {
tails: [ 0, 1, 1, 2, 2, 0 ]
heads: [ 1, 0, 2, 1, 0, 2 ]
literals: [ 0, 1, 2, 3, 4, 5 ]
dimensions: {
exprs { offset: 5 }
exprs {
vars: [ 6 ]
coeffs: [ 1 ]
}
exprs {
vars: [ 7 ]
coeffs: [ 2 ]
}
}
}
}
# ... more constraints (omitted) ...
)pb");
EXPECT_THAT(presolved_model.variables(),
testing::Pointwise(testing::EqualsProto(),
expected_presolved_model.variables()));
EXPECT_THAT(presolved_model.constraints(0),
testing::EqualsProto(expected_presolved_model.constraints(0)));
}
TEST(PresolveCpModelTest, InnerObjectiveLowerBound) {
const CpModelProto initial_model = ParseTestProto(R"pb(
variables { domain: [ 1, 10 ] }
variables { domain: [ -1647, 504, 3054, 3054 ] }
constraints {
linear {
vars: 0
vars: 1
coeffs: 2
coeffs: 1
domain: [ 10, 10 ]
}
}
objective {
vars: 1
coeffs: 2
domain: [ 8, 10 ]
}
)pb");
const CpSolverResponse r = Solve(initial_model);
EXPECT_EQ(r.inner_objective_lower_bound(), 8);
}
TEST(PresolveCpModelTest, ModelWithoutVariables) {
const CpModelProto cp_model = ParseTestProto(
R"pb(
constraints {
all_diff {
exprs { offset: 1 }
exprs { offset: 2 }
}
}
)pb");
SatParameters params;
params.set_log_search_progress(true);
params.set_debug_crash_if_presolve_breaks_hint(true);
params.set_cp_model_presolve(false);
CpSolverResponse response = SolveWithParameters(cp_model, params);
EXPECT_EQ(response.status(), CpSolverStatus::OPTIMAL);
}
} // namespace
} // namespace sat
} // namespace operations_research