* bump abseil to 20250814 * bump protobuf to v32.0 * cmake: add ccache auto support * backport flatzinc, math_opt and sat update
392 lines
11 KiB
C++
392 lines
11 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_copy.h"
|
|
|
|
#include "gtest/gtest.h"
|
|
#include "ortools/base/gmock.h"
|
|
#include "ortools/base/parse_test_proto.h"
|
|
#include "ortools/base/protobuf_util.h"
|
|
#include "ortools/linear_solver/linear_solver.pb.h"
|
|
#include "ortools/sat/cp_model.pb.h"
|
|
#include "ortools/sat/model.h"
|
|
#include "ortools/sat/presolve_context.h"
|
|
#include "ortools/sat/sat_parameters.pb.h"
|
|
|
|
namespace operations_research {
|
|
namespace sat {
|
|
namespace {
|
|
|
|
using ::google::protobuf::contrib::parse_proto::ParseTestProto;
|
|
using ::testing::EqualsProto;
|
|
using ::testing::IsEmpty;
|
|
|
|
TEST(ModelCopyTest, IntervalsAddLinearConstraints) {
|
|
const CpModelProto initial_model = ParseTestProto(R"pb(
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ -10, 10 ] }
|
|
variables { domain: [ -10, 10 ] }
|
|
variables { domain: [ -10, 10 ] }
|
|
constraints {
|
|
enforcement_literal: 0
|
|
interval {
|
|
start: { vars: 1 coeffs: 1 }
|
|
size: { vars: 2 coeffs: 1 }
|
|
end: { vars: 3 coeffs: 1 }
|
|
}
|
|
}
|
|
)pb");
|
|
|
|
Model model;
|
|
CpModelProto new_cp_model;
|
|
PresolveContext context(&model, &new_cp_model, nullptr);
|
|
|
|
ImportModelWithBasicPresolveIntoContext(initial_model, &context);
|
|
const CpModelProto expected_model = ParseTestProto(R"pb(
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ -10, 10 ] }
|
|
variables { domain: [ -10, 10 ] }
|
|
variables { domain: [ -10, 10 ] }
|
|
constraints {
|
|
enforcement_literal: 0
|
|
interval {
|
|
start: { vars: 1 coeffs: 1 }
|
|
size: { vars: 2 coeffs: 1 }
|
|
end: { vars: 3 coeffs: 1 }
|
|
}
|
|
}
|
|
constraints {
|
|
enforcement_literal: 0
|
|
linear {
|
|
vars: [ 1, 2, 3 ]
|
|
coeffs: [ 1, 1, -1 ]
|
|
domain: [ 0, 0 ]
|
|
}
|
|
}
|
|
|
|
constraints {
|
|
enforcement_literal: 0
|
|
linear {
|
|
vars: [ 2 ]
|
|
coeffs: [ 1 ]
|
|
domain: [ 0, 10 ]
|
|
}
|
|
}
|
|
)pb");
|
|
EXPECT_THAT(new_cp_model, EqualsProto(expected_model));
|
|
}
|
|
|
|
TEST(ModelCopyTest, IntervalsWithFixedStartAndEnd) {
|
|
const CpModelProto initial_model = ParseTestProto(R"pb(
|
|
variables { domain: [ 10, 10 ] }
|
|
variables { domain: [ 10, 10 ] }
|
|
variables { domain: [ 10, 10 ] }
|
|
constraints {
|
|
interval {
|
|
start: { vars: 0 coeffs: 1 }
|
|
size: { vars: 1 coeffs: 1 }
|
|
end: { vars: 2 coeffs: 2 }
|
|
}
|
|
}
|
|
)pb");
|
|
|
|
Model model;
|
|
CpModelProto new_cp_model;
|
|
PresolveContext context(&model, &new_cp_model, nullptr);
|
|
|
|
ImportModelWithBasicPresolveIntoContext(initial_model, &context);
|
|
const CpModelProto expected_model = ParseTestProto(R"pb(
|
|
variables { domain: [ 10, 10 ] }
|
|
variables { domain: [ 10, 10 ] }
|
|
variables { domain: [ 10, 10 ] }
|
|
constraints {
|
|
interval {
|
|
start { offset: 10 }
|
|
end { offset: 20 }
|
|
size { offset: 10 }
|
|
}
|
|
}
|
|
)pb");
|
|
EXPECT_THAT(new_cp_model, EqualsProto(expected_model));
|
|
}
|
|
|
|
TEST(ModelCopyTest, RemoveDuplicateFromClauses) {
|
|
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 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
constraints {
|
|
enforcement_literal: [ 1, 2, 3, 2 ]
|
|
bool_or { literals: [ -4, 9, 8 ] }
|
|
}
|
|
)pb");
|
|
const CpModelProto expected_moded = 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, 1 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
constraints { bool_or { literals: [ -2, -3, -4, 9, 8 ] } }
|
|
)pb");
|
|
CpModelProto new_cp_model;
|
|
Model model;
|
|
PresolveContext context(&model, &new_cp_model, nullptr);
|
|
ImportModelWithBasicPresolveIntoContext(initial_model, &context);
|
|
EXPECT_THAT(new_cp_model, EqualsProto(expected_moded));
|
|
}
|
|
|
|
TEST(ModelCopyTest, RemoveDuplicateFromEnforcementLiterals) {
|
|
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, 100 ] }
|
|
variables { domain: [ 0, 100 ] }
|
|
variables { domain: [ 0, 100 ] }
|
|
constraints {
|
|
enforcement_literal: [ 1, 2, 3, 2 ] # duplicate
|
|
linear {
|
|
vars: [ 4, 5, 6 ]
|
|
coeffs: [ 1, 1, 1 ]
|
|
domain: [ 0, 100 ]
|
|
}
|
|
}
|
|
constraints {
|
|
enforcement_literal: [ 2, -3, 2 ] # can never be enforced.
|
|
linear {
|
|
vars: [ 4, 5, 6 ]
|
|
coeffs: [ 2, 1, 3 ]
|
|
domain: [ 0, 100 ]
|
|
}
|
|
}
|
|
)pb");
|
|
const CpModelProto expected_moded = ParseTestProto(R"pb(
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ 0, 100 ] }
|
|
variables { domain: [ 0, 100 ] }
|
|
variables { domain: [ 0, 100 ] }
|
|
constraints {
|
|
enforcement_literal: [ 1, 2, 3 ]
|
|
linear {
|
|
vars: [ 4, 5, 6 ]
|
|
coeffs: [ 1, 1, 1 ]
|
|
domain: [ 0, 100 ]
|
|
}
|
|
}
|
|
)pb");
|
|
CpModelProto new_cp_model;
|
|
Model model;
|
|
model.GetOrCreate<SatParameters>()
|
|
->set_keep_all_feasible_solutions_in_presolve(true);
|
|
PresolveContext context(&model, &new_cp_model, nullptr);
|
|
ImportModelWithBasicPresolveIntoContext(initial_model, &context);
|
|
EXPECT_THAT(new_cp_model, EqualsProto(expected_moded));
|
|
}
|
|
|
|
TEST(ModelCopyTest, ChangeEnforcedAtMostOrExactlyOneToLinear) {
|
|
const CpModelProto initial_model = ParseTestProto(R"pb(
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
constraints {
|
|
enforcement_literal: [ 0, 1 ]
|
|
at_most_one { literals: [ 2, -4 ] }
|
|
}
|
|
constraints {
|
|
enforcement_literal: [ 0, 1 ]
|
|
exactly_one { literals: [ 2, 3 ] }
|
|
}
|
|
)pb");
|
|
const CpModelProto expected_moded = ParseTestProto(R"pb(
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
constraints {
|
|
enforcement_literal: [ 0, 1 ]
|
|
linear {
|
|
vars: [ 2, 3 ]
|
|
coeffs: [ 1, -1 ]
|
|
domain: [ -1, 0 ]
|
|
}
|
|
}
|
|
constraints {
|
|
enforcement_literal: [ 0, 1 ]
|
|
linear {
|
|
vars: [ 2, 3 ]
|
|
coeffs: [ 1, 1 ]
|
|
domain: [ 1, 1 ]
|
|
}
|
|
}
|
|
)pb");
|
|
CpModelProto new_cp_model;
|
|
Model model;
|
|
model.GetOrCreate<SatParameters>()
|
|
->set_keep_all_feasible_solutions_in_presolve(true);
|
|
PresolveContext context(&model, &new_cp_model, nullptr);
|
|
ImportModelWithBasicPresolveIntoContext(initial_model, &context);
|
|
EXPECT_THAT(new_cp_model, EqualsProto(expected_moded));
|
|
}
|
|
|
|
TEST(ModelCopyTest, LegacyElementConstraint) {
|
|
const CpModelProto initial_model = ParseTestProto(R"pb(
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
constraints {
|
|
enforcement_literal: [ 0, 1, 1 ]
|
|
element {
|
|
index: 0
|
|
target: 1
|
|
vars: [ 2, 3 ]
|
|
}
|
|
}
|
|
)pb");
|
|
const CpModelProto expected_moded = ParseTestProto(R"pb(
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
constraints {
|
|
enforcement_literal: [ 0, 1 ]
|
|
element {
|
|
linear_index { vars: 0 coeffs: 1 }
|
|
linear_target { vars: 1 coeffs: 1 }
|
|
exprs { vars: 2 coeffs: 1 }
|
|
exprs { vars: 3 coeffs: 1 }
|
|
}
|
|
}
|
|
)pb");
|
|
CpModelProto new_cp_model;
|
|
Model model;
|
|
model.GetOrCreate<SatParameters>()
|
|
->set_keep_all_feasible_solutions_in_presolve(true);
|
|
PresolveContext context(&model, &new_cp_model, nullptr);
|
|
ImportModelWithBasicPresolveIntoContext(initial_model, &context);
|
|
EXPECT_THAT(new_cp_model, EqualsProto(expected_moded));
|
|
}
|
|
|
|
TEST(ModelCopyTest, ElementConstraint) {
|
|
const CpModelProto initial_model = ParseTestProto(R"pb(
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
constraints {
|
|
enforcement_literal: [ 0, 1, 1 ]
|
|
element {
|
|
linear_index { vars: 0 coeffs: 1 }
|
|
linear_target { vars: 1 coeffs: 1 }
|
|
exprs { vars: 2 coeffs: 1 }
|
|
exprs { vars: 3 coeffs: 1 }
|
|
}
|
|
}
|
|
)pb");
|
|
const CpModelProto expected_moded = ParseTestProto(R"pb(
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
variables { domain: [ 0, 1 ] }
|
|
constraints {
|
|
enforcement_literal: [ 0, 1 ]
|
|
element {
|
|
linear_index { vars: 0 coeffs: 1 }
|
|
linear_target { vars: 1 coeffs: 1 }
|
|
exprs { vars: 2 coeffs: 1 }
|
|
exprs { vars: 3 coeffs: 1 }
|
|
}
|
|
}
|
|
)pb");
|
|
CpModelProto new_cp_model;
|
|
Model model;
|
|
model.GetOrCreate<SatParameters>()
|
|
->set_keep_all_feasible_solutions_in_presolve(true);
|
|
PresolveContext context(&model, &new_cp_model, nullptr);
|
|
ImportModelWithBasicPresolveIntoContext(initial_model, &context);
|
|
EXPECT_THAT(new_cp_model, EqualsProto(expected_moded));
|
|
}
|
|
|
|
TEST(ModelCopyTest, ExpandedNonAffineExpressionsShareVariableWhenPossible) {
|
|
const CpModelProto initial_model = ParseTestProto(R"pb(
|
|
variables { domain: [ 0, 10 ] }
|
|
variables { domain: [ 0, 10 ] }
|
|
constraints {
|
|
all_diff {
|
|
exprs {
|
|
vars: [ 0, 1 ]
|
|
coeffs: [ 1, 2 ]
|
|
offset: 1
|
|
}
|
|
exprs {
|
|
vars: [ 0, 1 ]
|
|
coeffs: [ 2, 4 ]
|
|
offset: 3
|
|
}
|
|
exprs {
|
|
vars: [ 1, 0 ]
|
|
coeffs: [ -2, -1 ]
|
|
offset: 6
|
|
}
|
|
}
|
|
}
|
|
)pb");
|
|
const CpModelProto expected_moded = ParseTestProto(R"pb(
|
|
variables { domain: [ 0, 10 ] }
|
|
variables { domain: [ 0, 10 ] }
|
|
variables { domain: [ 0, 30 ] }
|
|
constraints {
|
|
all_diff {
|
|
exprs { vars: 2 coeffs: 1 offset: 1 }
|
|
exprs { vars: 2 coeffs: 2 offset: 3 }
|
|
exprs { vars: 2 coeffs: -1 offset: 6 }
|
|
}
|
|
}
|
|
constraints {
|
|
linear {
|
|
vars: [ 2, 0, 1 ]
|
|
coeffs: [ -1, 1, 2 ]
|
|
domain: [ 0, 0 ]
|
|
}
|
|
}
|
|
)pb");
|
|
CpModelProto new_cp_model;
|
|
Model model;
|
|
model.GetOrCreate<SatParameters>()
|
|
->set_keep_all_feasible_solutions_in_presolve(true);
|
|
PresolveContext context(&model, &new_cp_model, nullptr);
|
|
ImportModelWithBasicPresolveIntoContext(initial_model, &context);
|
|
EXPECT_THAT(new_cp_model, EqualsProto(expected_moded));
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace sat
|
|
} // namespace operations_research
|