Update bop folder + add test

This commit is contained in:
Guillaume Chatelet
2025-09-23 12:01:56 +00:00
committed by Mizux Seiha
parent cb2fae32ed
commit c807a53c0f
2 changed files with 287 additions and 80 deletions

View File

@@ -14,6 +14,7 @@
load("@protobuf//bazel:cc_proto_library.bzl", "cc_proto_library")
load("@protobuf//bazel:proto_library.bzl", "proto_library")
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_test.bzl", "cc_test")
package(default_visibility = ["//visibility:public"])
@@ -24,16 +25,15 @@ proto_library(
cc_proto_library(
name = "bop_parameters_cc_proto",
deps = ["bop_parameters_proto"],
deps = [":bop_parameters_proto"],
)
cc_library(
name = "bop_types",
hdrs = ["bop_types.h"],
deps = [
"//ortools/base",
"//ortools/base:intops",
"//ortools/base:strong_vector",
"//ortools/util:strong_integers",
],
)
@@ -45,21 +45,19 @@ cc_library(
":bop_parameters_cc_proto",
":bop_solution",
":bop_types",
"//ortools/base",
"//ortools/base:intops",
"//ortools/base:strong_vector",
"@abseil-cpp//absl/synchronization",
"//ortools/glop:lp_solver",
# "//ortools/glop",
"//ortools/lp_data:base",
"//ortools/sat:boolean_problem",
"//ortools/sat:boolean_problem_cc_proto",
"//ortools/sat:clause",
"//ortools/sat:sat_base",
"//ortools/sat:sat_solver",
"//ortools/base:threadpool",
"//ortools/util:stats",
"//ortools/util:strong_integers",
"//ortools/util:time_limit",
"@abseil-cpp//absl/base:core_headers",
"@abseil-cpp//absl/log",
"@abseil-cpp//absl/log:check",
"@abseil-cpp//absl/strings",
"@abseil-cpp//absl/synchronization",
],
)
@@ -70,9 +68,17 @@ cc_library(
deps = [
":bop_base",
":bop_solution",
"//ortools/base",
":bop_types",
"//ortools/base:strong_vector",
"//ortools/sat:boolean_problem",
"//ortools/sat:boolean_problem_cc_proto",
"//ortools/sat:pb_constraint",
"//ortools/sat:restart",
"//ortools/sat:sat_base",
"//ortools/sat:sat_solver",
"//ortools/util:bitset",
"//ortools/util:strong_integers",
"@abseil-cpp//absl/log:check",
],
)
@@ -82,11 +88,27 @@ cc_library(
hdrs = ["bop_solution.h"],
deps = [
":bop_types",
"//ortools/base",
"//ortools/base:intops",
"//ortools/base:strong_vector",
"//ortools/sat:boolean_problem",
"//ortools/sat:boolean_problem_cc_proto",
"//ortools/sat:pb_constraint",
"@abseil-cpp//absl/log:check",
"@abseil-cpp//absl/strings",
],
)
cc_test(
name = "bop_solution_test",
size = "small",
srcs = ["bop_solution_test.cc"],
deps = [
":bop_solution",
":bop_types",
"//ortools/base:gmock_main",
"//ortools/sat:boolean_problem_cc_proto",
"//ortools/util:strong_integers",
"@abseil-cpp//absl/log:check",
"@protobuf",
],
)
@@ -101,24 +123,29 @@ cc_library(
":bop_types",
":bop_util",
"//ortools/algorithms:sparse_permutation",
"//ortools/base",
"//ortools/base:intops",
"//ortools/base:strong_vector",
"//ortools/base:stl_util",
"//ortools/glop:lp_solver",
#"//ortools/glop",
"//ortools/lp_data:lp_print_utils",
"//ortools/glop:parameters_cc_proto",
"//ortools/lp_data",
"//ortools/lp_data:base",
"//ortools/sat:boolean_problem",
"//ortools/sat:boolean_problem_cc_proto",
"//ortools/sat:clause",
"//ortools/sat:lp_utils",
"//ortools/sat:pb_constraint",
"//ortools/sat:sat_base",
"//ortools/sat:sat_parameters_cc_proto",
"//ortools/sat:sat_solver",
"//ortools/sat:symmetry",
"//ortools/sat:util",
"//ortools/util:bitset",
"//ortools/util:stats",
"//ortools/util:strong_integers",
"//ortools/util:time_limit",
"@abseil-cpp//absl/random",
"@protobuf",
"@abseil-cpp//absl/log",
"@abseil-cpp//absl/log:check",
"@abseil-cpp//absl/random:bit_gen_ref",
"@abseil-cpp//absl/random:distributions",
"@abseil-cpp//absl/strings",
"@abseil-cpp//absl/strings:str_format",
],
)
@@ -132,23 +159,26 @@ cc_library(
":bop_solution",
":bop_types",
":bop_util",
"//ortools/base",
"//ortools/base:intops",
"//ortools/base:strong_vector",
"//ortools/base:stl_util",
"//ortools/glop:lp_solver",
#"//ortools/glop",
"//ortools/lp_data:lp_print_utils",
"//ortools/lp_data",
"//ortools/lp_data:base",
"//ortools/sat:boolean_problem",
"//ortools/sat:boolean_problem_cc_proto",
"//ortools/sat:lp_utils",
"//ortools/sat:pb_constraint",
"//ortools/sat:sat_base",
"//ortools/sat:sat_parameters_cc_proto",
"//ortools/sat:sat_solver",
"//ortools/util:bitset",
"//ortools/util:stats",
"//ortools/util:strong_integers",
"//ortools/util:time_limit",
"@abseil-cpp//absl/random",
"@abseil-cpp//absl/cleanup",
"@protobuf",
"@abseil-cpp//absl/log",
"@abseil-cpp//absl/log:check",
"@abseil-cpp//absl/random:bit_gen_ref",
"@abseil-cpp//absl/random:distributions",
"@abseil-cpp//absl/strings",
],
)
@@ -158,19 +188,22 @@ cc_library(
hdrs = ["complete_optimizer.h"],
deps = [
":bop_base",
":bop_parameters_cc_proto",
":bop_solution",
":bop_types",
":bop_util",
"//ortools/base",
"//ortools/base:intops",
"//ortools/base:stl_util",
"//ortools/base:strong_vector",
"//ortools/sat:boolean_problem",
"//ortools/sat:boolean_problem_cc_proto",
"//ortools/sat:encoding",
"//ortools/sat:optimization",
"//ortools/sat:model",
"//ortools/sat:pb_constraint",
"//ortools/sat:sat_base",
"//ortools/sat:sat_parameters_cc_proto",
"//ortools/sat:sat_solver",
"//ortools/util:stats",
"//ortools/util:strong_integers",
"//ortools/util:time_limit",
"@abseil-cpp//absl/log:check",
"@abseil-cpp//absl/strings",
],
)
@@ -180,18 +213,26 @@ cc_library(
hdrs = ["bop_ls.h"],
deps = [
":bop_base",
":bop_parameters_cc_proto",
":bop_solution",
":bop_types",
":bop_util",
"//ortools/base",
"//ortools/base:hash",
"//ortools/base:intops",
"//ortools/base:logging",
"//ortools/base:strong_vector",
"//ortools/sat:boolean_problem",
"//ortools/sat:boolean_problem_cc_proto",
"//ortools/sat:sat_base",
"//ortools/sat:sat_solver",
"//ortools/util:strong_integers",
"//ortools/util:time_limit",
"@abseil-cpp//absl/container:flat_hash_map",
"@abseil-cpp//absl/container:flat_hash_set",
"@abseil-cpp//absl/log:check",
"@abseil-cpp//absl/meta:type_traits",
"@abseil-cpp//absl/random",
"@abseil-cpp//absl/status:statusor",
"@abseil-cpp//absl/random:bit_gen_ref",
"@abseil-cpp//absl/strings",
"@abseil-cpp//absl/strings:str_format",
"@abseil-cpp//absl/types:span",
],
)
@@ -209,22 +250,24 @@ cc_library(
":bop_types",
":bop_util",
":complete_optimizer",
"//ortools/base",
"//ortools/base:hash",
"//ortools/base:intops",
"//ortools/base:strong_vector",
"@abseil-cpp//absl/log:vlog_is_on",
"@abseil-cpp//absl/status:statusor",
"//ortools/algorithms:sparse_permutation",
"//ortools/base:stl_util",
"//ortools/base:strong_vector",
"//ortools/glop:lp_solver",
#"//ortools/glop",
"//ortools/lp_data:base",
"//ortools/sat:boolean_problem",
"//ortools/sat:boolean_problem_cc_proto",
"//ortools/sat:sat_solver",
"//ortools/sat:symmetry",
"//ortools/util:random_engine",
"//ortools/util:stats",
"//ortools/util:strong_integers",
"//ortools/util:time_limit",
"@abseil-cpp//absl/log",
"@abseil-cpp//absl/log:check",
"@abseil-cpp//absl/log:vlog_is_on",
"@abseil-cpp//absl/strings",
"@abseil-cpp//absl/strings:str_format",
],
)
@@ -234,31 +277,22 @@ cc_library(
hdrs = ["bop_solver.h"],
deps = [
":bop_base",
":bop_fs",
":bop_lns",
":bop_ls",
":bop_parameters_cc_proto",
":bop_portfolio",
":bop_solution",
":bop_types",
":bop_util",
":complete_optimizer",
"//ortools/base",
"//ortools/base:intops",
"//ortools/base:strong_vector",
"//ortools/base:stl_util",
"//ortools/glop:lp_solver",
#"//ortools/glop",
"//ortools/lp_data:lp_print_utils",
"//ortools/base:threadpool",
"//ortools/lp_data:base",
"//ortools/sat:boolean_problem",
"//ortools/sat:boolean_problem_cc_proto",
"//ortools/sat:lp_utils",
"//ortools/sat:sat_solver",
"//ortools/base:threadpool",
"//ortools/util:bitset",
"//ortools/util:time_limit",
"@protobuf",
"//ortools/sat:pb_constraint",
"//ortools/util:stats",
"//ortools/util:time_limit",
"@abseil-cpp//absl/log",
"@abseil-cpp//absl/log:check",
"@abseil-cpp//absl/status",
"@abseil-cpp//absl/strings:str_format",
"@protobuf",
],
)
@@ -267,29 +301,28 @@ cc_library(
srcs = ["integral_solver.cc"],
hdrs = ["integral_solver.h"],
deps = [
":bop_base",
":bop_fs",
":bop_lns",
":bop_ls",
":bop_parameters_cc_proto",
":bop_solution",
":bop_solver",
":bop_types",
":bop_util",
"//ortools/base",
"//ortools/base:intops",
"//ortools/base:stl_util",
"//ortools/base:strong_vector",
"//ortools/glop:lp_solver",
"//ortools/base:threadpool",
"//ortools/lp_data",
"//ortools/lp_data:base",
"//ortools/lp_data:lp_decomposer",
"//ortools/lp_data:lp_utils",
"//ortools/lp_data:sparse",
"//ortools/lp_data:sparse_column",
"//ortools/lp_data:sparse_vector",
"//ortools/sat:boolean_problem",
"//ortools/sat:lp_utils",
"//ortools/sat:sat_solver",
"//ortools/sat:boolean_problem_cc_proto",
"//ortools/util:bitset",
"//ortools/util:stats",
"//ortools/util:fp_utils",
"//ortools/util:strong_integers",
"//ortools/util:time_limit",
"@protobuf",
"@abseil-cpp//absl/base:core_headers",
"@abseil-cpp//absl/log",
"@abseil-cpp//absl/log:check",
"@abseil-cpp//absl/strings:str_format",
],
)

View File

@@ -0,0 +1,174 @@
// 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/bop/bop_solution.h"
#include <string>
#include <vector>
#include "absl/log/check.h"
#include "google/protobuf/text_format.h"
#include "gtest/gtest.h"
#include "ortools/bop/bop_types.h"
#include "ortools/sat/boolean_problem.pb.h"
#include "ortools/util/strong_integers.h"
namespace operations_research {
namespace bop {
namespace {
using ::operations_research::sat::LinearBooleanProblem;
// Tests Bop solutions using a problem with no constraints.
// The solution is always feasible, but the cost can vary.
TEST(BopSolution, NoConstraints) {
const std::string kProblem =
"name: \"Test\" "
"num_variables: 3 "
"var_names: \"x\" "
"var_names: \"y\" "
"var_names: \"z\" "
"objective { " // 4 * (x + 2 * y - z + 3)
" literals: 1 coefficients: 1 " // x
" literals: 2 coefficients: 2 " // 2 * y
" literals: 3 coefficients: -1 " // - z
" offset: 3 scaling_factor: 4 "
"} ";
LinearBooleanProblem problem;
CHECK(google::protobuf::TextFormat::ParseFromString(kProblem, &problem));
// Empty solution: all variables are set depending on the coefficient sign.
BopSolution solution_001(problem, "NoConstraints");
EXPECT_TRUE(solution_001.IsFeasible());
EXPECT_EQ(-1, solution_001.GetCost());
EXPECT_EQ(4 * (-1 + 3), solution_001.GetScaledCost());
// Check accessors.
EXPECT_EQ(3, solution_001.Size());
EXPECT_EQ("NoConstraints", solution_001.name());
VariableIndex var(0);
const std::vector<bool> kValues = {false, false, true};
for (const bool value : solution_001) {
EXPECT_EQ(value, solution_001.Value(var));
EXPECT_EQ(kValues[var.value()], solution_001.Value(var));
++var;
}
// All-true solution.
BopSolution solution_111 = solution_001;
for (VariableIndex var(0); var < solution_111.Size(); ++var) {
solution_111.SetValue(var, true);
}
// Solution_000 should not have changed.
EXPECT_TRUE(solution_001.IsFeasible());
EXPECT_EQ(-1, solution_001.GetCost());
EXPECT_EQ(4 * (-1 + 3), solution_001.GetScaledCost());
EXPECT_EQ(solution_001.Size(), solution_111.Size());
for (VariableIndex var(0); var < solution_111.Size(); ++var) {
EXPECT_EQ(kValues[var.value()], solution_001.Value(var));
EXPECT_TRUE(solution_111.Value(var));
}
EXPECT_TRUE(solution_111.IsFeasible());
EXPECT_EQ(2, solution_111.GetCost());
EXPECT_EQ((2 + 3) * 4, solution_111.GetScaledCost());
// All false.
BopSolution solution_000 = solution_001;
solution_000.SetValue(VariableIndex(2), false);
EXPECT_TRUE(solution_000.IsFeasible());
EXPECT_EQ(0, solution_000.GetCost());
EXPECT_EQ(3 * 4, solution_000.GetScaledCost());
}
// Tests using a Two-constraints problem. Constraints can be broken
// independently. Note that any feasible solution has a cost of 1 (because of
// the
// first constraint).
TEST(BopSolution, TwoConstraints) {
const std::string kProblem =
"name: \"Test\" "
"num_variables: 3 "
"var_names: \"x\" "
"var_names: \"y\" "
"var_names: \"z\" "
"constraints { " // x + y == 1
" literals: 1 coefficients: 1 "
" literals: 2 coefficients: 1 "
" lower_bound: 1 "
" upper_bound: 1 "
" name: \"Ct_1\" "
"} "
"constraints { " // y + z <= 1
" literals: 2 coefficients: 1 "
" literals: 3 coefficients: 1 "
" upper_bound: 1 "
" name: \"Ct_2\" "
"} "
"objective { " // x + y
" literals: 1 coefficients: 1 "
" literals: 2 coefficients: 1 "
"} ";
LinearBooleanProblem problem;
CHECK(google::protobuf::TextFormat::ParseFromString(kProblem, &problem));
// Empty solution: all variables are set to false.
BopSolution solution_000(problem, "TwoConstraints");
EXPECT_FALSE(solution_000.IsFeasible());
EXPECT_EQ(0, solution_000.GetCost());
EXPECT_EQ(0, solution_000.GetScaledCost());
// All-true solution.
BopSolution solution_111 = solution_000;
for (VariableIndex var(0); var < solution_111.Size(); ++var) {
solution_111.SetValue(var, true);
}
EXPECT_FALSE(solution_111.IsFeasible());
EXPECT_EQ(2, solution_111.GetCost());
EXPECT_EQ(2, solution_111.GetScaledCost());
// Feasible solution with x true.
BopSolution solution_100 = solution_000;
solution_100.SetValue(VariableIndex(0), true);
EXPECT_TRUE(solution_100.IsFeasible());
EXPECT_EQ(1, solution_100.GetCost());
EXPECT_EQ(1, solution_100.GetScaledCost());
// Feasible solution with x and z true.
BopSolution solution_101 = solution_100;
solution_101.SetValue(VariableIndex(2), true);
EXPECT_TRUE(solution_101.IsFeasible());
EXPECT_EQ(1, solution_101.GetCost());
EXPECT_EQ(1, solution_101.GetScaledCost());
// Infeasible solution with y and z true.
BopSolution solution_two_true = solution_111;
solution_two_true.SetValue(VariableIndex(0), false);
EXPECT_FALSE(solution_two_true.IsFeasible());
EXPECT_EQ(1, solution_two_true.GetCost());
EXPECT_EQ(1, solution_two_true.GetScaledCost());
// Make solution_two_true feasible by swapping x and y values.
solution_two_true.SetValue(VariableIndex(0), true);
solution_two_true.SetValue(VariableIndex(1), false);
EXPECT_TRUE(solution_two_true.IsFeasible());
EXPECT_EQ(1, solution_two_true.GetCost());
EXPECT_EQ(1, solution_two_true.GetScaledCost());
}
} // anonymous namespace
} // namespace bop
} // namespace operations_research