Files
ortools-clone/ortools/sat/integer_search_test.cc
2025-04-14 16:35:59 +02:00

149 lines
5.1 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/integer_search.h"
#include <random>
#include <vector>
#include "absl/container/flat_hash_set.h"
#include "gtest/gtest.h"
#include "ortools/base/logging.h"
#include "ortools/sat/cp_model.pb.h"
#include "ortools/sat/cp_model_mapping.h"
#include "ortools/sat/cp_model_solver.h"
#include "ortools/sat/integer.h"
#include "ortools/sat/integer_base.h"
#include "ortools/sat/model.h"
#include "ortools/sat/sat_parameters.pb.h"
#include "ortools/util/random_engine.h"
namespace operations_research {
namespace sat {
namespace {
// We generate a simple assignment problem for which the LP should give us the
// optimal, thus we expect no conflict.
TEST(ExploitLpSolution, LpIsOptimal) {
CpModelProto problem;
const int n = 10;
// n^2 Boolean arc variables.
std::vector<std::vector<int>> graph(n, std::vector<int>(n));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
graph[i][j] = problem.variables_size();
sat::IntegerVariableProto* var = problem.add_variables();
var->add_domain(0);
var->add_domain(1);
}
}
// Exactly one incoming and one outgoing.
for (int i = 0; i < n; ++i) {
auto* ct = problem.add_constraints()->mutable_linear();
ct->add_domain(1);
ct->add_domain(1);
for (int j = 0; j < n; ++j) {
ct->add_coeffs(1);
ct->add_vars(graph[i][j]);
}
}
for (int i = 0; i < n; ++i) {
auto* ct = problem.add_constraints()->mutable_linear();
ct->add_domain(1);
ct->add_domain(1);
for (int j = 0; j < n; ++j) {
ct->add_coeffs(1);
ct->add_vars(graph[j][i]);
}
}
// Some cost.
random_engine_t random;
std::uniform_int_distribution<int> dist(10, 100);
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
problem.mutable_objective()->add_coeffs(dist(random));
problem.mutable_objective()->add_vars(graph[i][j]);
}
}
for (const bool exploit_integer_lp_solution : {true, false}) {
Model model;
SatParameters parameters;
parameters.set_cp_model_presolve(false);
parameters.set_exploit_integer_lp_solution(exploit_integer_lp_solution);
parameters.set_exploit_all_lp_solution(false);
parameters.set_linearization_level(2);
parameters.set_num_workers(1);
model.Add(NewSatParameters(parameters));
const CpSolverResponse response = SolveCpModel(problem, &model);
LOG(INFO) << CpSolverResponseStats(response);
EXPECT_EQ(response.status(), CpSolverStatus::OPTIMAL);
if (exploit_integer_lp_solution) {
EXPECT_EQ(response.num_conflicts(), 0);
} else {
EXPECT_GT(response.num_conflicts(), 0);
}
}
}
TEST(ValueSelectionTest, AtMinValue) {
Model model;
const IntegerVariable x = model.Add(NewIntegerVariable(5, 10));
EXPECT_EQ(IntegerLiteral::LowerOrEqual(x, IntegerValue(5)),
AtMinValue(x, model.GetOrCreate<IntegerTrail>()));
}
TEST(ValueSelectionTest, GreaterOrEqualToMiddleValue) {
Model model;
auto* integer_trail = model.GetOrCreate<IntegerTrail>();
const IntegerVariable x = model.Add(NewIntegerVariable(6, 10));
EXPECT_EQ(IntegerLiteral::GreaterOrEqual(x, IntegerValue(8)),
GreaterOrEqualToMiddleValue(x, integer_trail));
EXPECT_EQ(IntegerLiteral::LowerOrEqual(x, IntegerValue(8)),
GreaterOrEqualToMiddleValue(NegationOf(x), integer_trail));
}
TEST(ValueSelectionTest, SplitAroundGivenValue) {
Model model;
const IntegerVariable x = model.Add(NewIntegerVariable(6, 10));
EXPECT_EQ(IntegerLiteral::GreaterOrEqual(x, IntegerValue(10)),
SplitAroundGivenValue(x, IntegerValue(10), &model));
EXPECT_EQ(IntegerLiteral::LowerOrEqual(x, IntegerValue(6)),
SplitAroundGivenValue(x, IntegerValue(6), &model));
EXPECT_EQ(IntegerLiteral(),
SplitAroundGivenValue(x, IntegerValue(5), &model));
}
TEST(ValueSelectionTest, SplitAroundGivenValueObjectiveDirection) {
Model model;
const IntegerVariable x = model.Add(NewIntegerVariable(6, 10));
const IntegerVariable y = model.Add(NewIntegerVariable(6, 10));
model.GetOrCreate<ObjectiveDefinition>()
->objective_impacting_variables.insert(x);
model.GetOrCreate<ObjectiveDefinition>()
->objective_impacting_variables.insert(NegationOf(y));
EXPECT_EQ(IntegerLiteral::LowerOrEqual(x, IntegerValue(8)),
SplitAroundGivenValue(x, IntegerValue(8), &model));
EXPECT_EQ(IntegerLiteral::GreaterOrEqual(y, IntegerValue(8)),
SplitAroundGivenValue(y, IntegerValue(8), &model));
EXPECT_EQ(IntegerLiteral(),
SplitAroundGivenValue(x, IntegerValue(5), &model));
}
} // namespace
} // namespace sat
} // namespace operations_research