move set_cover code in its own directory

This commit is contained in:
Laurent Perron
2025-02-17 12:52:36 +01:00
parent cc9181db9d
commit 4efd54f5f0
43 changed files with 1898 additions and 312 deletions

View File

@@ -412,6 +412,7 @@ file(GLOB_RECURSE OR_TOOLS_PROTO_FILES RELATIVE ${PROJECT_SOURCE_DIR}
"ortools/packing/*.proto"
"ortools/sat/*.proto"
"ortools/scheduling/*.proto"
"ortools/set_cover/*.proto"
"ortools/util/*.proto"
)
if(USE_PDLP OR BUILD_MATH_OPT)
@@ -539,6 +540,7 @@ foreach(SUBPROJECT IN ITEMS
packing
routing
scheduling
set_cover
port
util)
add_subdirectory(ortools/${SUBPROJECT})

View File

@@ -151,6 +151,7 @@ file(GLOB_RECURSE OR_TOOLS_PROTO_PY_FILES RELATIVE ${PROJECT_SOURCE_DIR}
"ortools/routing/*.proto"
"ortools/sat/*.proto"
"ortools/scheduling/*.proto"
"ortools/set_cover/*.proto"
"ortools/util/*.proto"
)
list(REMOVE_ITEM OR_TOOLS_PROTO_PY_FILES "ortools/constraint_solver/demon_profiler.proto")
@@ -290,6 +291,7 @@ foreach(SUBPROJECT IN ITEMS
routing
sat
scheduling
set_cover
util)
add_subdirectory(ortools/${SUBPROJECT}/python)
endforeach()
@@ -351,6 +353,8 @@ file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/sat/python/__init__.py CONTENT "")
file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/sat/colab/__init__.py CONTENT "")
file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/scheduling/__init__.py CONTENT "")
file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/scheduling/python/__init__.py CONTENT "")
file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/set_cover/__init__.py CONTENT "")
file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/set_cover/python/__init__.py CONTENT "")
file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/util/__init__.py CONTENT "")
file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/util/python/__init__.py CONTENT "")
@@ -624,8 +628,6 @@ add_custom_command(
$<TARGET_FILE:init_pybind11> ${PYTHON_PROJECT}/init/python
COMMAND ${CMAKE_COMMAND} -E copy
$<TARGET_FILE:knapsack_solver_pybind11> ${PYTHON_PROJECT}/algorithms/python
COMMAND ${CMAKE_COMMAND} -E copy
$<TARGET_FILE:set_cover_pybind11> ${PYTHON_PROJECT}/algorithms/python
COMMAND ${CMAKE_COMMAND} -E copy
$<TARGET_FILE:linear_sum_assignment_pybind11> ${PYTHON_PROJECT}/graph/python
COMMAND ${CMAKE_COMMAND} -E copy
@@ -657,6 +659,8 @@ add_custom_command(
$<TARGET_FILE:cp_model_helper_pybind11> ${PYTHON_PROJECT}/sat/python
COMMAND ${CMAKE_COMMAND} -E copy
$<TARGET_FILE:rcpsp_pybind11> ${PYTHON_PROJECT}/scheduling/python
COMMAND ${CMAKE_COMMAND} -E copy
$<TARGET_FILE:set_cover_pybind11> ${PYTHON_PROJECT}/set_cover/python
COMMAND ${CMAKE_COMMAND} -E copy
$<TARGET_FILE:sorted_interval_list_pybind11> ${PYTHON_PROJECT}/util/python
COMMAND ${CMAKE_COMMAND} -E touch ${PROJECT_BINARY_DIR}/python/pybind11_timestamp
@@ -665,7 +669,6 @@ add_custom_command(
DEPENDS
init_pybind11
knapsack_solver_pybind11
set_cover_pybind11
linear_sum_assignment_pybind11
max_flow_pybind11
min_cost_flow_pybind11
@@ -679,6 +682,7 @@ add_custom_command(
$<TARGET_NAME_IF_EXISTS:pdlp_pybind11>
cp_model_helper_pybind11
rcpsp_pybind11
set_cover_pybind11
sorted_interval_list_pybind11
WORKING_DIRECTORY python
COMMAND_EXPAND_LISTS)
@@ -704,7 +708,6 @@ add_custom_command(
COMMAND ${CMAKE_COMMAND} -E remove -f stub_timestamp
COMMAND ${stubgen_EXECUTABLE} -p ortools.init.python.init --output .
COMMAND ${stubgen_EXECUTABLE} -p ortools.algorithms.python.knapsack_solver --output .
COMMAND ${stubgen_EXECUTABLE} -p ortools.algorithms.python.set_cover --output .
COMMAND ${stubgen_EXECUTABLE} -p ortools.graph.python.linear_sum_assignment --output .
COMMAND ${stubgen_EXECUTABLE} -p ortools.graph.python.max_flow --output .
COMMAND ${stubgen_EXECUTABLE} -p ortools.graph.python.min_cost_flow --output .
@@ -719,6 +722,7 @@ add_custom_command(
COMMAND ${stubgen_EXECUTABLE} -p ortools.routing.python.model --output .
COMMAND ${stubgen_EXECUTABLE} -p ortools.sat.python.cp_model_helper --output .
COMMAND ${stubgen_EXECUTABLE} -p ortools.scheduling.python.rcpsp --output .
COMMAND ${stubgen_EXECUTABLE} -p ortools.set_cover.python.set_cover --output .
COMMAND ${stubgen_EXECUTABLE} -p ortools.util.python.sorted_interval_list --output .
COMMAND ${CMAKE_COMMAND} -E touch ${PROJECT_BINARY_DIR}/python/stub_timestamp
MAIN_DEPENDENCY

View File

@@ -250,163 +250,6 @@ cc_test(
# query matching library.
# Weighted set covering library.
proto_library(
name = "set_cover_proto",
srcs = ["set_cover.proto"],
deps = [
"//ortools/util:int128_proto",
],
)
cc_proto_library(
name = "set_cover_cc_proto",
deps = [":set_cover_proto"],
)
py_proto_library(
name = "set_cover_py_pb2",
deps = [":set_cover_proto"],
)
cc_library(
name = "set_cover_lagrangian",
srcs = ["set_cover_lagrangian.cc"],
hdrs = ["set_cover_lagrangian.h"],
deps = [
":adjustable_k_ary_heap",
":set_cover_invariant",
":set_cover_model",
"//ortools/base:threadpool",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/synchronization",
],
)
cc_library(
name = "set_cover_model",
srcs = ["set_cover_model.cc"],
hdrs = ["set_cover_model.h"],
deps = [
":radix_sort",
":set_cover_cc_proto",
"//ortools/base:intops",
"//ortools/base:strong_vector",
"@com_google_absl//absl/log",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/numeric:bits",
"@com_google_absl//absl/random",
"@com_google_absl//absl/random:distributions",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/types:span",
],
)
cc_library(
name = "set_cover_invariant",
srcs = ["set_cover_invariant.cc"],
hdrs = ["set_cover_invariant.h"],
deps = [
":set_cover_cc_proto",
":set_cover_model",
"//ortools/base",
"//ortools/base:mathutil",
"@com_google_absl//absl/log",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/types:span",
],
)
cc_library(
name = "set_cover_heuristics",
srcs = ["set_cover_heuristics.cc"],
hdrs = ["set_cover_heuristics.h"],
deps = [
":adjustable_k_ary_heap",
":set_cover_invariant",
":set_cover_model",
"//ortools/base",
"@com_google_absl//absl/base",
"@com_google_absl//absl/log",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/numeric:bits",
"@com_google_absl//absl/random:distributions",
"@com_google_absl//absl/types:span",
],
)
cc_library(
name = "set_cover_mip",
srcs = ["set_cover_mip.cc"],
hdrs = ["set_cover_mip.h"],
deps = [
":set_cover_invariant",
":set_cover_model",
"//ortools/linear_solver",
"//ortools/lp_data:base",
"@com_google_absl//absl/log",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/types:span",
],
)
cc_library(
name = "set_cover_reader",
srcs = ["set_cover_reader.cc"],
hdrs = ["set_cover_reader.h"],
deps = [
":set_cover_cc_proto",
":set_cover_model",
"//ortools/base:file",
"//ortools/util:filelineiter",
"@com_google_absl//absl/log",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/strings:string_view",
],
)
cc_binary(
name = "set_cover_solve",
srcs = ["set_cover_solve.cc"],
deps = [
":set_cover_heuristics",
":set_cover_invariant",
":set_cover_model",
":set_cover_reader",
"//ortools/base",
"//ortools/base:timer",
"@com_google_absl//absl/flags:flag",
"@com_google_absl//absl/log",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
],
)
cc_test(
name = "set_cover_test",
size = "medium",
timeout = "eternal",
srcs = ["set_cover_test.cc"],
deps = [
":set_cover_cc_proto",
":set_cover_heuristics",
":set_cover_invariant",
":set_cover_mip",
":set_cover_model",
"//ortools/base:gmock_main",
"//ortools/base:parse_text_proto",
"@com_google_absl//absl/log",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/strings",
"@com_google_benchmark//:benchmark",
],
)
# Graph automorphism libraries.
cc_library(
name = "dense_doubly_linked_list",

View File

@@ -36,7 +36,6 @@ target_link_libraries(${NAME} PRIVATE
if(BUILD_TESTING)
file(GLOB _TEST_SRCS "*_test.cc")
list(FILTER _TEST_SRCS EXCLUDE REGEX ".*_stress_test.cc")
list(FILTER _TEST_SRCS EXCLUDE REGEX "set_cover_test.cc")
foreach(_FULL_FILE_NAME IN LISTS _TEST_SRCS)
get_filename_component(_NAME ${_FULL_FILE_NAME} NAME_WE)
get_filename_component(_FILE_NAME ${_FULL_FILE_NAME} NAME)

View File

@@ -78,30 +78,3 @@ py_test(
requirement("absl-py"),
],
)
# set_cover
pybind_extension(
name = "set_cover",
srcs = ["set_cover.cc"],
visibility = ["//visibility:public"],
deps = [
"//ortools/algorithms:set_cover_cc_proto",
"//ortools/algorithms:set_cover_heuristics",
"//ortools/algorithms:set_cover_invariant",
"//ortools/algorithms:set_cover_model",
"//ortools/algorithms:set_cover_reader",
"@com_google_absl//absl/strings",
"@pybind11_protobuf//pybind11_protobuf:native_proto_caster",
],
)
py_test(
name = "set_cover_test",
srcs = ["set_cover_test.py"],
python_version = "PY3",
deps = [
":set_cover",
"//ortools/algorithms:set_cover_py_pb2",
requirement("absl-py"),
],
)

View File

@@ -30,33 +30,3 @@ endif()
target_link_libraries(knapsack_solver_pybind11 PRIVATE ${PROJECT_NAMESPACE}::ortools)
add_library(${PROJECT_NAMESPACE}::knapsack_solver_pybind11 ALIAS knapsack_solver_pybind11)
# set_cover
pybind11_add_module(set_cover_pybind11 MODULE set_cover.cc)
set_target_properties(set_cover_pybind11 PROPERTIES
LIBRARY_OUTPUT_NAME "set_cover")
# note: macOS is APPLE and also UNIX !
if(APPLE)
set_target_properties(set_cover_pybind11 PROPERTIES
SUFFIX ".so"
INSTALL_RPATH "@loader_path;@loader_path/../../../${PYTHON_PROJECT}/.libs"
)
elseif(UNIX)
set_target_properties(set_cover_pybind11 PROPERTIES
INSTALL_RPATH "$ORIGIN:$ORIGIN/../../../${PYTHON_PROJECT}/.libs"
)
endif()
target_link_libraries(set_cover_pybind11 PRIVATE
${PROJECT_NAMESPACE}::ortools
pybind11_native_proto_caster
)
add_library(${PROJECT_NAMESPACE}::set_cover_pybind11 ALIAS set_cover_pybind11)
if(BUILD_TESTING)
file(GLOB PYTHON_SRCS "*_test.py")
foreach(FILE_NAME IN LISTS PYTHON_SRCS)
add_python_test(FILE_NAME ${FILE_NAME})
endforeach()
endif()

View File

@@ -59,7 +59,6 @@ setup(
],
'@PYTHON_PROJECT@.algorithms.python':[
'$<TARGET_FILE_NAME:knapsack_solver_pybind11>',
'$<TARGET_FILE_NAME:set_cover_pybind11>',
'*.pyi'
],
'@PYTHON_PROJECT@.bop':['*.pyi'],
@@ -125,6 +124,10 @@ setup(
'$<TARGET_FILE_NAME:rcpsp_pybind11>',
'*.pyi'
],
'@PYTHON_PROJECT@.set_cover.python':[
'$<TARGET_FILE_NAME:set_cover_pybind11>',
'*.pyi'
],
'@PYTHON_PROJECT@.util.python':[
'$<TARGET_FILE_NAME:sorted_interval_list_pybind11>',
'*.pyi'

View File

@@ -0,0 +1,268 @@
# 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.
load("@com_google_protobuf//bazel:cc_proto_library.bzl", "cc_proto_library")
load("@com_google_protobuf//bazel:proto_library.bzl", "proto_library")
load("@rules_cc//cc:defs.bzl", "cc_library")
load("@rules_python//python:proto.bzl", "py_proto_library")
package(default_visibility = ["//visibility:public"])
# Description:
# A solver for weighted set covering with side constraints
proto_library(
name = "set_cover_proto",
srcs = ["set_cover.proto"],
deps = [
"//ortools/util:int128_proto",
],
)
cc_proto_library(
name = "set_cover_cc_proto",
deps = [":set_cover_proto"],
)
py_proto_library(
name = "set_cover_py_pb2",
deps = [":set_cover_proto"],
)
cc_library(
name = "set_cover_lagrangian",
srcs = ["set_cover_lagrangian.cc"],
hdrs = ["set_cover_lagrangian.h"],
deps = [
":set_cover_invariant",
":set_cover_model",
"//ortools/algorithms:adjustable_k_ary_heap",
"//ortools/base:threadpool",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/synchronization",
],
)
cc_library(
name = "set_cover_model",
srcs = ["set_cover_model.cc"],
hdrs = ["set_cover_model.h"],
deps = [
":set_cover_cc_proto",
"//ortools/algorithms:radix_sort",
"//ortools/base:intops",
"//ortools/base:strong_vector",
"@com_google_absl//absl/log",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/numeric:bits",
"@com_google_absl//absl/random",
"@com_google_absl//absl/random:distributions",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/types:span",
],
)
cc_library(
name = "set_cover_invariant",
srcs = ["set_cover_invariant.cc"],
hdrs = ["set_cover_invariant.h"],
deps = [
":set_cover_cc_proto",
":set_cover_model",
"//ortools/base",
"//ortools/base:mathutil",
"@com_google_absl//absl/log",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/types:span",
],
)
cc_library(
name = "set_cover_heuristics",
srcs = ["set_cover_heuristics.cc"],
hdrs = ["set_cover_heuristics.h"],
deps = [
":set_cover_invariant",
":set_cover_model",
"//ortools/algorithms:adjustable_k_ary_heap",
"//ortools/base",
"@com_google_absl//absl/base",
"@com_google_absl//absl/log",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/numeric:bits",
"@com_google_absl//absl/random:distributions",
"@com_google_absl//absl/types:span",
],
)
cc_library(
name = "set_cover_mip",
srcs = ["set_cover_mip.cc"],
hdrs = ["set_cover_mip.h"],
deps = [
":set_cover_invariant",
":set_cover_model",
"//ortools/linear_solver",
"//ortools/lp_data:base",
"@com_google_absl//absl/log",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/types:span",
],
)
cc_library(
name = "set_cover_reader",
srcs = ["set_cover_reader.cc"],
hdrs = ["set_cover_reader.h"],
deps = [
":set_cover_cc_proto",
":set_cover_model",
"//ortools/base:file",
"//ortools/util:filelineiter",
"@com_google_absl//absl/log",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/strings:string_view",
],
)
cc_library(
name = "assignment",
srcs = ["assignment.cc"],
hdrs = ["assignment.h"],
deps = [
":capacity_invariant",
":set_cover_invariant",
":set_cover_model",
"//ortools/base:mathutil",
"@com_google_absl//absl/log",
"@com_google_absl//absl/log:check",
],
)
cc_test(
name = "assignment_test",
srcs = ["assignment_test.cc"],
deps = [
":assignment",
":set_cover_invariant",
":set_cover_model",
"//ortools/base:gmock_main",
"@com_google_absl//absl/log:check",
],
)
cc_binary(
name = "set_cover_solve",
srcs = ["set_cover_solve.cc"],
deps = [
":set_cover_heuristics",
":set_cover_invariant",
":set_cover_model",
":set_cover_reader",
"//ortools/base",
"//ortools/base:timer",
"@com_google_absl//absl/flags:flag",
"@com_google_absl//absl/log",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
],
)
cc_test(
name = "set_cover_test",
size = "medium",
timeout = "eternal",
srcs = ["set_cover_test.cc"],
deps = [
":set_cover_cc_proto",
":set_cover_heuristics",
":set_cover_invariant",
":set_cover_mip",
":set_cover_model",
"//ortools/base:gmock_main",
"//ortools/base:parse_text_proto",
"@com_google_absl//absl/log",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/strings",
"@com_google_benchmark//:benchmark",
],
)
# Side constraint: capacity.
proto_library(
name = "capacity_proto",
srcs = ["capacity.proto"],
)
cc_proto_library(
name = "capacity_cc_proto",
deps = [":capacity_proto"],
)
py_proto_library(
name = "capacity_py_pb2",
deps = [":capacity_proto"],
)
cc_library(
name = "capacity_model",
srcs = ["capacity_model.cc"],
hdrs = ["capacity_model.h"],
deps = [
":capacity_cc_proto",
":set_cover_model",
"//ortools/base:intops",
"//ortools/base:strong_vector",
"@com_google_absl//absl/log",
"@com_google_absl//absl/log:check",
],
)
cc_test(
name = "capacity_model_test",
srcs = ["capacity_model_test.cc"],
deps = [
":capacity_model",
":set_cover_model",
"//ortools/base:gmock_main",
"//ortools/base:message_matchers",
],
)
cc_library(
name = "capacity_invariant",
srcs = ["capacity_invariant.cc"],
hdrs = ["capacity_invariant.h"],
deps = [
":capacity_model",
":set_cover_model",
"@com_google_absl//absl/log",
"@com_google_absl//absl/log:check",
],
)
cc_test(
name = "capacity_invariant_test",
srcs = ["capacity_invariant_test.cc"],
deps = [
":capacity_invariant",
":capacity_model",
":set_cover_model",
"//ortools/base:gmock_main",
],
)

View File

@@ -0,0 +1,51 @@
# 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.
file(GLOB _SRCS "*.h" "*.cc")
list(FILTER _SRCS EXCLUDE REGEX ".*/.*_test.cc")
list(FILTER _SRCS EXCLUDE REGEX ".*/set_cover_solve.cc")
set(NAME ${PROJECT_NAME}_set_cover)
# Will be merge in libortools.so
#add_library(${NAME} STATIC ${_SRCS})
add_library(${NAME} OBJECT ${_SRCS})
set_target_properties(${NAME} PROPERTIES
POSITION_INDEPENDENT_CODE ON
)
target_include_directories(${NAME} PRIVATE
${PROJECT_SOURCE_DIR}
${PROJECT_BINARY_DIR})
target_link_libraries(${NAME} PRIVATE
protobuf::libprotobuf
${PROJECT_NAMESPACE}::ortools_proto)
#add_library(${PROJECT_NAMESPACE}::set_cover ALIAS ${NAME})
if(BUILD_TESTING)
file(GLOB _TEST_SRCS "*_test.cc")
foreach(_FULL_FILE_NAME IN LISTS _TEST_SRCS)
get_filename_component(_NAME ${_FULL_FILE_NAME} NAME_WE)
get_filename_component(_FILE_NAME ${_FULL_FILE_NAME} NAME)
ortools_cxx_test(
NAME
set_cover_${_NAME}
SOURCES
${_FILE_NAME}
LINK_LIBRARIES
benchmark::benchmark
GTest::gtest
GTest::gtest_main
GTest::gmock
)
endforeach()
endif()

View File

@@ -0,0 +1,126 @@
// 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/set_cover/assignment.h"
#include "absl/log/check.h"
#include "ortools/base/logging.h"
#include "ortools/base/mathutil.h"
#include "ortools/set_cover/capacity_invariant.h"
#include "ortools/set_cover/set_cover_invariant.h"
#include "ortools/set_cover/set_cover_model.h"
namespace operations_research {
void SetCoverAssignment::Clear() {
cost_ = Cost(0.0);
values_.assign(model_.subset_costs().size(), false);
DCHECK_EQ(values_.size(), model_.subset_costs().size())
<< "The cost vector (length: " << model_.subset_costs().size()
<< ") is inconsistent with the assignment (length: " << values_.size()
<< ")";
}
void SetCoverAssignment::AttachInvariant(SetCoverInvariant* i) {
CHECK(constraint_ == nullptr);
constraint_ = i;
}
void SetCoverAssignment::AttachInvariant(CapacityInvariant* i) {
CHECK(constraint_ != nullptr);
side_constraints_.push_back(i);
// TODO(user): call i->SetAssignment or similar so that each and every
// constraint uses the same solution storage.
}
void SetCoverAssignment::SetValue(
SubsetIndex subset, bool is_selected,
SetCoverInvariant::ConsistencyLevel set_cover_consistency) {
DVLOG(1) << "[Assignment] Subset " << subset << " becoming " << is_selected
<< "; used to be " << values_[subset];
DCHECK(CheckConsistency());
if (values_[subset] == is_selected) return;
values_[subset] = is_selected;
if (is_selected) {
cost_ += model_.subset_costs()[subset];
if (constraint_) {
constraint_->Select(subset, set_cover_consistency);
}
for (CapacityInvariant* const capacity_constraint : side_constraints_) {
capacity_constraint->Select(subset);
}
} else {
cost_ -= model_.subset_costs()[subset];
if (constraint_) {
constraint_->Deselect(subset, set_cover_consistency);
}
for (CapacityInvariant* const capacity_constraint : side_constraints_) {
capacity_constraint->Deselect(subset);
}
}
DCHECK(CheckConsistency());
}
SetCoverSolutionResponse SetCoverAssignment::ExportSolutionAsProto() const {
SetCoverSolutionResponse message;
message.set_num_subsets(values_.size());
message.set_cost(cost_);
for (SubsetIndex subset(0);
subset < SubsetIndex(model_.subset_costs().size()); ++subset) {
if (values_[subset]) {
message.add_subset(subset.value());
}
}
return message;
}
void SetCoverAssignment::LoadAssignment(const SubsetBoolVector& solution) {
DCHECK_EQ(solution.size(), values_.size());
values_ = solution;
cost_ = ComputeCost(values_);
}
void SetCoverAssignment::ImportSolutionFromProto(
const SetCoverSolutionResponse& message) {
values_.resize(SubsetIndex(message.num_subsets()), false);
cost_ = Cost(0.0);
for (auto s : message.subset()) {
SubsetIndex subset(s);
values_[subset] = true;
cost_ += model_.subset_costs()[subset];
}
CHECK(MathUtil::AlmostEquals(message.cost(), cost_));
DCHECK(CheckConsistency());
}
bool SetCoverAssignment::CheckConsistency() const {
Cost cst = ComputeCost(values_);
CHECK(MathUtil::AlmostEquals(cost_, cst));
return true;
}
Cost SetCoverAssignment::ComputeCost(const SubsetBoolVector& choices) const {
Cost cst = 0.0;
SubsetIndex subset(0);
for (const bool b : choices) {
if (b) {
cst += model_.subset_costs()[subset];
}
++subset;
}
return cst;
}
} // namespace operations_research

View File

@@ -0,0 +1,104 @@
// 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.
#ifndef OR_TOOLS_SET_COVER_ASSIGNMENT_H_
#define OR_TOOLS_SET_COVER_ASSIGNMENT_H_
#include <vector>
#include "ortools/set_cover/capacity_invariant.h"
#include "ortools/set_cover/set_cover_invariant.h"
#include "ortools/set_cover/set_cover_model.h"
namespace operations_research {
// `SetCoverAssignment` stores a possibly partial, possibly infeasible solution
// to a `SetCoverModel`. It only stores a solution and no metadata,
// so that it can be shared efficiently among constraints.
//
// This class is equivalent to an `Assignment` object in the CP/routing solver.
// (//ortools/routing).
class SetCoverAssignment {
public:
// Constructs an empty set covering assignment.
//
// The model size or costs must not change after the invariant was built.
// The caller must guarantee that the model outlives the assignment without
// changing its costs.
explicit SetCoverAssignment(const SetCoverModel& m)
: model_(m), constraint_(nullptr), side_constraints_({}) {
Clear();
}
// Clears the current assignment.
void Clear();
// Adds a constraint to the problem. At least one set-covering constraint is
// required; use side constraints as required (no set-covering constraint can
// be a side constraint).
void AttachInvariant(SetCoverInvariant* i);
void AttachInvariant(CapacityInvariant* i);
// Returns the cost of current solution.
Cost cost() const { return cost_; }
// Returns the subset assignment vector.
const SubsetBoolVector& assignment() const { return values_; }
// Sets the subset's assignment to the given bool.
void SetValue(SubsetIndex subset, bool is_selected,
SetCoverInvariant::ConsistencyLevel set_cover_consistency);
// Returns the current solution as a proto.
SetCoverSolutionResponse ExportSolutionAsProto() const;
// Loads the solution and recomputes the data in the invariant.
//
// The given assignment must fit the model of this assignment.
void LoadAssignment(const SubsetBoolVector& solution);
// Imports the solution from a proto.
//
// The given assignment must fit the model of this assignment.
void ImportSolutionFromProto(const SetCoverSolutionResponse& message);
// Checks the consistency of the solution (between the selected subsets and
// the solution cost).
bool CheckConsistency() const;
private:
// Computes the cost for the given choices.
Cost ComputeCost(const SubsetBoolVector& choices) const;
// The weighted set covering model on which the solver is run.
const SetCoverModel& model_;
// Current cost of the assignment.
Cost cost_;
// Current assignment. Takes |S| bits.
SubsetBoolVector values_;
// Constraints that this assignment must respect. The constraints are checked
// every time the assignment changes (with the methods `Flip`, `Select`, and
// `Deselect`).
//
// For now, the only side constraints are capacity constraints.
SetCoverInvariant* constraint_;
// TODO(user): merge the several constraints into one invariant.
std::vector<CapacityInvariant*> side_constraints_;
};
} // namespace operations_research
#endif // OR_TOOLS_SET_COVER_ASSIGNMENT_H_

View File

@@ -0,0 +1,140 @@
// 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/set_cover/assignment.h"
#include "absl/log/check.h"
#include "gtest/gtest.h"
#include "ortools/set_cover/set_cover_invariant.h"
#include "ortools/set_cover/set_cover_model.h"
namespace operations_research {
namespace {
SetCoverModel MakeBasicModel() {
// 3 elements, 4 subsets (all of unitary cost).
// Optimal cost: 2 (subsets #0 and #1).
SetCoverModel model;
model.AddEmptySubset(1);
model.AddElementToLastSubset(0);
model.AddEmptySubset(1);
model.AddElementToLastSubset(1);
model.AddElementToLastSubset(2);
model.AddEmptySubset(1);
model.AddElementToLastSubset(1);
model.AddEmptySubset(1);
model.AddElementToLastSubset(2);
CHECK(model.ComputeFeasibility());
return model;
}
TEST(SetCoverAssignment, EmbryonicModelHasZeroCost) {
SetCoverModel model;
model.AddEmptySubset(1);
model.AddElementToLastSubset(0);
SetCoverAssignment assignment(model);
EXPECT_TRUE(assignment.CheckConsistency());
EXPECT_EQ(assignment.cost(), 0.0);
EXPECT_EQ(assignment.assignment(), SubsetBoolVector(1, false));
}
TEST(SetCoverAssignment, BasicModelHasCost) {
SetCoverModel model = MakeBasicModel();
ASSERT_EQ(model.num_subsets(), 4);
ASSERT_EQ(model.num_elements(), 3);
SetCoverAssignment assignment(model);
EXPECT_TRUE(assignment.CheckConsistency());
EXPECT_EQ(assignment.cost(), 0.0);
EXPECT_EQ(assignment.assignment(), SubsetBoolVector(4, false));
assignment.SetValue(SubsetIndex(0), true,
SetCoverInvariant::ConsistencyLevel::kInconsistent);
assignment.SetValue(SubsetIndex(1), true,
SetCoverInvariant::ConsistencyLevel::kInconsistent);
EXPECT_TRUE(assignment.CheckConsistency());
EXPECT_EQ(assignment.cost(), 2.0);
EXPECT_EQ(assignment.assignment(),
SubsetBoolVector({true, true, false, false}));
assignment.SetValue(SubsetIndex(1), false,
SetCoverInvariant::ConsistencyLevel::kInconsistent);
EXPECT_TRUE(assignment.CheckConsistency());
EXPECT_EQ(assignment.cost(), 1.0);
EXPECT_EQ(assignment.assignment(),
SubsetBoolVector({true, false, false, false}));
}
TEST(SetCoverAssignment, BasicModelWorksWithSetCoverInvariant) {
// Changes to the solution imply changes in the invariant.
SetCoverModel model = MakeBasicModel();
ASSERT_EQ(model.num_subsets(), 4);
ASSERT_EQ(model.num_elements(), 3);
SetCoverAssignment assignment(model);
SetCoverInvariant inv(&model);
assignment.AttachInvariant(&inv);
ASSERT_EQ(assignment.assignment(), SubsetBoolVector(4, false));
EXPECT_EQ(inv.num_uncovered_elements(), 3);
assignment.SetValue(SubsetIndex(0), true,
SetCoverInvariant::ConsistencyLevel::kRedundancy);
assignment.SetValue(SubsetIndex(1), true,
SetCoverInvariant::ConsistencyLevel::kRedundancy);
EXPECT_EQ(inv.num_uncovered_elements(), 0);
}
TEST(SetCoverAssignment, ImportFromVector) {
SetCoverModel model = MakeBasicModel();
ASSERT_EQ(model.num_subsets(), 4);
ASSERT_EQ(model.num_elements(), 3);
const SubsetBoolVector reference_assignment = {true, false, false, false};
SetCoverAssignment assignment(model);
assignment.LoadAssignment(reference_assignment);
EXPECT_TRUE(assignment.CheckConsistency());
EXPECT_EQ(assignment.cost(), 1.0);
EXPECT_EQ(assignment.assignment(), reference_assignment);
}
TEST(SetCoverAssignment, ExportImportAllPullTogetherAsATeam) {
SetCoverModel model = MakeBasicModel();
ASSERT_EQ(model.num_subsets(), 4);
ASSERT_EQ(model.num_elements(), 3);
SetCoverAssignment assignment_1(model);
assignment_1.SetValue(SubsetIndex(0), true,
SetCoverInvariant::ConsistencyLevel::kInconsistent);
assignment_1.SetValue(SubsetIndex(1), true,
SetCoverInvariant::ConsistencyLevel::kInconsistent);
ASSERT_EQ(assignment_1.cost(), 2.0);
ASSERT_EQ(assignment_1.assignment(),
SubsetBoolVector({true, true, false, false}));
ASSERT_TRUE(assignment_1.CheckConsistency());
SetCoverSolutionResponse message = assignment_1.ExportSolutionAsProto();
SetCoverAssignment assignment_2(model);
assignment_2.ImportSolutionFromProto(message);
EXPECT_EQ(assignment_1.cost(), assignment_2.cost());
EXPECT_EQ(assignment_1.assignment(), assignment_2.assignment());
EXPECT_TRUE(assignment_2.CheckConsistency());
}
} // namespace
} // namespace operations_research

View File

@@ -0,0 +1,75 @@
// 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.
syntax = "proto3";
package operations_research;
option java_package = "com.google.ortools.setcover";
option java_multiple_files = true;
// Represents a capacity constraint to be used in conjunction with a
// SetCoverProto. This constraint only considers one dimension.
//
// Such a capacity constraint mathematically looks like:
// min_capacity <= \sum_{e in elements} weight_e * x_e <= max_capacity
// where either `min_capacity` or `max_capacity` can be omitted. `x_e` indicates
// for a given solution `x` whether the element `e` is selected and counts for
// this capacity constraint (`x_e == 1`) or not (`x_e == 0`). The weights are
// given in `capacity_term`, each of them being a reference to an element being
// present in a subset (in set-covering parlance) and its weight.
//
// For instance, this constraint can be used together with a set-covering
// problem where parcels (element) must be covered by trucks (subsets) while
// respecting truck capacities (this object). Each element can be covered by a
// given set of trucks (set-covering problem); if an element is taken within a
// truck, it uses some capacity for this truck (such as weight).
//
// In particular, this representation does not imply that a given element must
// have the same weight in all the capacity constraints of a set-covering
// problem (e.g., the same parcel might have different weights depending on
// which truck is being considered).
message CapacityConstraintProto {
message CapacityTerm {
// The subset this weight corresponds to (index of the subset in the
// `subset` repeated field in `SetCoverProto`).
int32 subset = 1;
message ElementWeightPair {
// The element this weight corresponds to (value of `element` in
// `SetCoverProto.Subset`).
int32 element = 1;
// The weight of the element.
double weight = 2;
}
repeated ElementWeightPair element_weights = 2;
}
// The list of terms in the constraint.
//
// The list is supposed to be in canonical form, which means it is sorted
// first by increasing subset index then increasing element index.
// No duplicate term is allowed (two terms for the same element in the same
// subset).
repeated CapacityTerm capacity_term = 1;
// The minimum amount of resource that must be consumed. At least one of
// `min_capacity` and `max_capacity` must be present.
double min_capacity = 2;
// The maximum amount of resource that can be consumed. At least one of
// `min_capacity` and `max_capacity` must be present.
double max_capacity = 3;
}

View File

@@ -0,0 +1,112 @@
// 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/set_cover/capacity_invariant.h"
#include "absl/log/check.h"
#include "ortools/base/logging.h"
#include "ortools/set_cover/capacity_model.h"
#include "ortools/set_cover/set_cover_model.h"
namespace operations_research {
void CapacityInvariant::Clear() {
current_slack_ = 0.0;
is_selected_.assign(set_cover_model_->num_subsets(), false);
}
double CapacityInvariant::ComputeSlackChange(const SubsetIndex subset) const {
double slack_change = 0.0;
for (CapacityTermIndex term : model_->TermRange()) {
if (model_->GetTermSubsetIndex(term) == subset) {
// Hypothesis: GetTermSubsetIndex(term) is an element of the subset.
// This information is stored in a SetCoverModel instance.
slack_change += model_->GetTermCapacityWeight(term);
}
}
return slack_change;
}
bool CapacityInvariant::SlackChangeFitsConstraint(
const double slack_change) const {
const double new_slack = current_slack_ + slack_change;
return new_slack >= model_->GetMinimumCapacity() &&
new_slack <= model_->GetMaximumCapacity();
}
bool CapacityInvariant::Flip(SubsetIndex subset) {
DCHECK_LT(subset.value(), set_cover_model_->num_subsets())
<< "Invalid subset: " << subset;
return !is_selected_[subset] ? Select(subset) : Deselect(subset);
}
bool CapacityInvariant::CanFlip(SubsetIndex subset) const {
DCHECK_LT(subset.value(), set_cover_model_->num_subsets())
<< "Invalid subset: " << subset;
return !is_selected_[subset] ? CanSelect(subset) : CanDeselect(subset);
}
bool CapacityInvariant::Select(SubsetIndex subset) {
DVLOG(1) << "[Capacity constraint] Selecting subset " << subset;
DCHECK(!is_selected_[subset]);
const double slack_change = ComputeSlackChange(subset);
if (!SlackChangeFitsConstraint(slack_change)) {
DVLOG(1) << "[Capacity constraint] Selecting subset " << subset
<< ": infeasible";
return false;
}
is_selected_[subset] = true;
current_slack_ += slack_change;
DVLOG(1) << "[Capacity constraint] New slack: " << current_slack_;
return true;
}
bool CapacityInvariant::CanSelect(SubsetIndex subset) const {
DVLOG(1) << "[Capacity constraint] Can select subset " << subset << "?";
DCHECK(!is_selected_[subset]);
const double slack_change = ComputeSlackChange(subset);
DVLOG(1) << "[Capacity constraint] New slack if selecting: "
<< current_slack_ + slack_change;
return SlackChangeFitsConstraint(slack_change);
}
bool CapacityInvariant::Deselect(SubsetIndex subset) {
DVLOG(1) << "[Capacity constraint] Deselecting subset " << subset;
DCHECK(is_selected_[subset]);
const double slack_change = -ComputeSlackChange(subset);
if (!SlackChangeFitsConstraint(slack_change)) {
DVLOG(1) << "[Capacity constraint] Deselecting subset " << subset
<< ": infeasible";
return false;
}
is_selected_[subset] = false;
current_slack_ += slack_change;
DVLOG(1) << "[Capacity constraint] New slack: " << current_slack_;
return true;
}
bool CapacityInvariant::CanDeselect(SubsetIndex subset) const {
DVLOG(1) << "[Capacity constraint] Can deselect subset " << subset << "?";
DCHECK(is_selected_[subset]);
const double slack_change = -ComputeSlackChange(subset);
DVLOG(1) << "[Capacity constraint] New slack if deselecting: "
<< current_slack_ + slack_change;
return SlackChangeFitsConstraint(slack_change);
}
} // namespace operations_research

View File

@@ -0,0 +1,107 @@
// 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.
#ifndef OR_TOOLS_SET_COVER_CAPACITY_INVARIANT_H_
#define OR_TOOLS_SET_COVER_CAPACITY_INVARIANT_H_
#include "absl/log/check.h"
#include "ortools/set_cover/capacity_model.h"
#include "ortools/set_cover/set_cover_model.h"
namespace operations_research {
class CapacityInvariant {
public:
// Constructs an empty capacity invariant state.
// The model may not change after the invariant was built.
explicit CapacityInvariant(CapacityModel* m, SetCoverModel* sc)
: model_(m), set_cover_model_(sc) {
DCHECK(model_->ComputeFeasibility());
Clear();
}
// Clears the invariant.
void Clear();
// Returns `true` when the constraint is not violated by this flipping move
// and incrementally updates the invariant. Otherwise, returns `false` and
// does not change the object.
//
// Flips is_selected_[subset] to its negation, by calling Select or Deselect
// depending on value.
bool Flip(SubsetIndex subset);
// Returns `true` when the constraint would not be violated if this flipping
// move is performed. Otherwise, returns `false`. The object never changes.
bool CanFlip(SubsetIndex subset) const;
// Returns `true` when the constraint is not violated by selecting all of the
// items in the subset and incrementally updates the invariant. Otherwise,
// returns `false` and does not change the object. (If the subset is already
// selected, the behavior is undefined.)
bool Select(SubsetIndex subset);
// Returns `true` when the constraint would not be violated by selecting all
// of the items in the subset. Otherwise, returns `false`. The object never
// changes. (If the subset is already selected, the behavior is undefined.)
bool CanSelect(SubsetIndex subset) const;
// Returns `true` when the constraint is not violated by unselecting all of
// the items in the subset and incrementally updates the invariant. Otherwise,
// returns `false` and does not change the object. (If the subset is already
// not selected, the behavior is undefined.)
bool Deselect(SubsetIndex subset);
// Returns `true` when the constraint would not be violated by unselecting all
// of the items in the subset. Otherwise, returns `false`. The object never
// changes. (If the subset is already not selected, the behavior is
// undefined.)
bool CanDeselect(SubsetIndex subset) const;
// TODO(user): implement the functions where you only select/deselect an
// item of a subset (instead of all items at once). The behavior gets much
// more interesting: if two subsets cover one item and the two item-subset
// combinations are terms in this capacity constraint, only one of them counts
// towards the capacity.
//
// The solver is not yet ready for this move: you need to
// decide which subset covers a given item, instead of ensuring that an item
// is covered by at least one subset. Currently, we could aggregate the terms
// per subset to make the code much faster when (de)selecting at the cost of
// increased initialization times.
private:
// The capacity-constraint model on which the invariant runs.
CapacityModel* model_;
// The set-cover model on which the invariant runs.
SetCoverModel* set_cover_model_;
// Current slack of the constraint.
operations_research::CapacityWeight current_slack_;
// Current solution assignment.
// TODO(user): reuse the assignment of a SetCoverInvariant.
SubsetBoolVector is_selected_;
// Determines the change in slack when (de)selecting the given subset.
// The returned value is nonnegative; add it to the slack when selecting
// and subtract it when deselecting.
double ComputeSlackChange(SubsetIndex subset) const;
// Determines whether the given slack change violates the constraint
// (`false`) or not (`true`).
bool SlackChangeFitsConstraint(double slack_change) const;
};
} // namespace operations_research
#endif // OR_TOOLS_SET_COVER_CAPACITY_INVARIANT_H_

View File

@@ -0,0 +1,55 @@
// 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/set_cover/capacity_invariant.h"
#include "gtest/gtest.h"
#include "ortools/set_cover/capacity_model.h"
#include "ortools/set_cover/set_cover_model.h"
namespace operations_research {
namespace {
TEST(CapacityModel, ChecksConstraintViolation) {
// Compatibility constraint: choose either of the two subsets.
SetCoverModel sc;
sc.AddEmptySubset(1.0);
sc.AddElementToLastSubset(ElementIndex(0));
sc.AddEmptySubset(1.0);
sc.AddElementToLastSubset(ElementIndex(0));
CapacityModel m(0.0, 1.0);
m.AddTerm(SubsetIndex(0), ElementIndex(0), CapacityWeight(1.0));
m.AddTerm(SubsetIndex(1), ElementIndex(0), CapacityWeight(1.0));
EXPECT_TRUE(m.ComputeFeasibility());
CapacityInvariant cinv(&m, &sc);
// Current assignment: [false, false]. Current activation: 0.
EXPECT_TRUE(cinv.CanFlip(SubsetIndex(0))); // All moves are possible.
EXPECT_TRUE(cinv.CanFlip(SubsetIndex(1)));
EXPECT_TRUE(cinv.Flip(SubsetIndex(0))); // Select returns true.
EXPECT_TRUE(cinv.CanFlip(SubsetIndex(0))); // Undoing: still valid.
EXPECT_FALSE(cinv.CanFlip(SubsetIndex(1))); // Impossible move.
EXPECT_FALSE(cinv.Flip(SubsetIndex(1))); // Select returns false.
// Current assignment: [true, false]. Current activation: 1.
EXPECT_TRUE(cinv.Flip(SubsetIndex(0))); // Deselect returns true.
EXPECT_TRUE(cinv.CanFlip(SubsetIndex(0))); // Undoing: still valid.
EXPECT_TRUE(cinv.CanFlip(SubsetIndex(1))); // Valid when 0 not selected.
EXPECT_TRUE(cinv.Flip(SubsetIndex(1))); // Select returns true.
EXPECT_FALSE(cinv.CanFlip(SubsetIndex(0))); // Impossible move.
EXPECT_TRUE(cinv.CanFlip(SubsetIndex(1))); // Undoing: still valid.
}
} // namespace
} // namespace operations_research

View File

@@ -0,0 +1,131 @@
// 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/set_cover/capacity_model.h"
#include <algorithm>
#include <cmath>
#include <limits>
#include <numeric>
#include <vector>
#include "absl/log/check.h"
#include "ortools/base/logging.h"
#include "ortools/set_cover/set_cover_model.h"
namespace operations_research {
void CapacityModel::AddTerm(SubsetIndex subset, ElementIndex element,
CapacityWeight weight) {
CHECK(std::isfinite(weight));
subsets_.push_back(subset);
elements_.push_back(element);
weights_.push_back(weight);
CHECK_EQ(elements_.size(), subsets_.size());
CHECK_EQ(elements_.size(), weights_.size());
}
void CapacityModel::SetMinimumCapacity(CapacityWeight min_capacity) {
CHECK(!std::isnan(min_capacity));
CHECK_NE(min_capacity, std::numeric_limits<CapacityWeight>::max());
min_capacity_ = min_capacity;
}
void CapacityModel::SetMaximumCapacity(CapacityWeight max_capacity) {
CHECK(!std::isnan(max_capacity));
CHECK_NE(max_capacity, std::numeric_limits<CapacityWeight>::min());
max_capacity_ = max_capacity;
}
bool CapacityModel::ComputeFeasibility() const {
if (weights_.empty()) {
// A sum of zero terms is zero.
return min_capacity_ <= 0.0 && max_capacity_ >= 0.0;
}
// Compute the minimum and maximum constraint activations.
CapacityWeight min_activation = 0.0;
CapacityWeight max_activation = 0.0;
for (const CapacityWeight weight : weights_) {
if (weight < 0.0) {
min_activation += weight;
} else {
max_activation += weight;
}
}
DVLOG(1) << "[Capacity constraint] Activation bounds: [" << min_activation
<< ", " << max_activation << "]";
DVLOG(1) << "[Capacity constraint] Capacity bounds: [" << min_capacity_
<< ", " << max_capacity_ << "]";
return min_activation <= max_capacity_ && max_activation >= min_capacity_;
}
std::vector<CapacityTermIndex> CapacityModel::CanonicalIndexing() {
std::vector<CapacityTermIndex> idx(num_terms());
std::iota(idx.begin(), idx.end(), CapacityTermIndex(0));
// TODO(user): use RadixSort when it's available. The implementation in
// radix_sort.h does not support a lambda for comparing.
std::sort(idx.begin(), idx.end(),
[&](CapacityTermIndex lhs, CapacityTermIndex rhs) -> bool {
return subsets_[lhs] < subsets_[rhs]
? true
: elements_[lhs] < elements_[rhs];
});
return idx;
}
CapacityConstraintProto CapacityModel::ExportModelAsProto() {
CapacityConstraintProto proto;
proto.set_min_capacity(min_capacity_);
proto.set_max_capacity(max_capacity_);
CapacityConstraintProto::CapacityTerm* current_term = nullptr;
for (CapacityTermIndex i : CanonicalIndexing()) {
if (current_term == nullptr ||
current_term->subset() != subsets_[i].value()) {
current_term = proto.add_capacity_term();
current_term->set_subset(subsets_[i].value());
}
DCHECK(current_term != nullptr);
CapacityConstraintProto::CapacityTerm::ElementWeightPair* pair =
current_term->add_element_weights();
pair->set_element(elements_[i].value());
pair->set_weight(weights_[i]);
}
return proto;
}
void CapacityModel::ImportModelFromProto(const CapacityConstraintProto& proto) {
elements_.clear();
subsets_.clear();
weights_.clear();
SetMinimumCapacity(proto.min_capacity());
SetMaximumCapacity(proto.max_capacity());
ReserveNumTerms(proto.capacity_term_size());
for (const CapacityConstraintProto::CapacityTerm& term :
proto.capacity_term()) {
for (const CapacityConstraintProto::CapacityTerm::ElementWeightPair& pair :
term.element_weights()) {
AddTerm(SubsetIndex(term.subset()), ElementIndex(pair.element()),
pair.weight());
}
}
}
} // namespace operations_research

View File

@@ -0,0 +1,156 @@
// 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.
#ifndef OR_TOOLS_SET_COVER_CAPACITY_MODEL_H_
#define OR_TOOLS_SET_COVER_CAPACITY_MODEL_H_
#include <cmath>
#include <limits>
#include <vector>
#include "absl/log/check.h"
#include "ortools/base/strong_int.h"
#include "ortools/base/strong_vector.h"
#include "ortools/set_cover/capacity.pb.h"
#include "ortools/set_cover/set_cover_model.h"
// Representation class for the capacity side-constraint for a weighted
// set-covering problem.
//
// This constraint restricts the selection of elements within subsets that
// respect the constraint. Such a constraint can mix elements in any subset.
//
// Using the same mixed-integer-programming formulation as `set_cover_model.h`,
// this class corresponds to the following constraint:
// min_capacity <= \sum_{e in elements} weight_e * x_e <= max_capacity
namespace operations_research {
// Basic type for weights. For now, the same as `Cost` for the set covering.
using CapacityWeight = double;
// Term index in a capacity constraint.
DEFINE_STRONG_INT_TYPE(CapacityTermIndex, BaseInt);
// The terms are represented as three aligned vectors: the element, the subset,
// and the weight. Each vector is indexed by the term.
using CapacityElements =
util_intops::StrongVector<CapacityTermIndex, ElementIndex>;
using CapacitySubsets =
util_intops::StrongVector<CapacityTermIndex, SubsetIndex>;
using CapacityWeights =
util_intops::StrongVector<CapacityTermIndex, CapacityWeight>;
// Main class for describing a single capacity constraint in the context of a
// set-covering problem.
class CapacityModel {
public:
// Builds an empty capacity constraint.
//
// Use either WithMinimumWeight or WithMaximumWeight to set only one of the
// two bounds.
CapacityModel(CapacityWeight min, CapacityWeight max)
: elements_(),
subsets_(),
weights_(),
min_capacity_(min),
max_capacity_(max) {
// At least one bound must be set. Otherwise, the constraint is vacuous.
CHECK(!std::isnan(min_capacity_));
CHECK(!std::isnan(max_capacity_));
CHECK(min_capacity_ != std::numeric_limits<CapacityWeight>::min() ||
max_capacity_ != std::numeric_limits<CapacityWeight>::max());
}
static CapacityModel WithMinimumWeight(CapacityWeight min) {
return CapacityModel(min, std::numeric_limits<CapacityWeight>::max());
}
static CapacityModel WithMaximumWeight(CapacityWeight max) {
return CapacityModel(std::numeric_limits<CapacityWeight>::min(), max);
}
// Returns the current number of terms in the constraint.
BaseInt num_terms() const { return elements_.size(); }
// Returns the range of terms.
util_intops::StrongIntRange<CapacityTermIndex> TermRange() const {
return util_intops::StrongIntRange<CapacityTermIndex>(
CapacityTermIndex(num_terms()));
}
// Adds a new term to the constraint.
// This will CHECK-fail if the weight is infinite or a NaN.
void AddTerm(SubsetIndex subset, ElementIndex element, CapacityWeight weight);
// Returns the element, subset, or capacity of the given term.
ElementIndex GetTermElementIndex(CapacityTermIndex term) const {
return elements_[term];
}
SubsetIndex GetTermSubsetIndex(CapacityTermIndex term) const {
return subsets_[term];
}
CapacityWeight GetTermCapacityWeight(CapacityTermIndex term) const {
return weights_[term];
}
// Sets the lower/upper bounds for the constraint.
// This will CHECK-fail if a capacity is a NaN.
void SetMinimumCapacity(CapacityWeight min_capacity);
void SetMaximumCapacity(CapacityWeight max_capacity);
// Returns the lower/upper bounds for the constraint.
CapacityWeight GetMinimumCapacity() const { return min_capacity_; }
CapacityWeight GetMaximumCapacity() const { return max_capacity_; }
// Returns true if the constraint is feasible, i.e. there is at least one
// assignment that satisfies the constraint.
bool ComputeFeasibility() const;
// Reserves num_terms terms in the model.
void ReserveNumTerms(BaseInt num_terms) {
ReserveNumTerms(CapacityTermIndex(num_terms));
}
void ReserveNumTerms(CapacityTermIndex num_terms) {
subsets_.reserve(num_terms);
elements_.reserve(num_terms);
weights_.reserve(num_terms);
}
// Returns the model as a CapacityConstraintProto.
//
// The function is not const because the terms need to be sorted for the
// representation as a protobuf to be canonical.
CapacityConstraintProto ExportModelAsProto();
// Imports the model from a CapacityConstraintProto.
void ImportModelFromProto(const CapacityConstraintProto& proto);
private:
// The terms in the constraint.
CapacityElements elements_;
CapacitySubsets subsets_;
CapacityWeights weights_;
// The bounds of the constraint. Both are always active at the same time.
// An inactive constraint corresponds to a capacity set to ±∞.
CapacityWeight min_capacity_;
CapacityWeight max_capacity_;
// Returns a canonical indexing of the constraint, i.e. reading the terms in
// this order yields the order that is explained in the proto.
std::vector<CapacityTermIndex> CanonicalIndexing();
};
} // namespace operations_research
#endif // OR_TOOLS_SET_COVER_CAPACITY_MODEL_H_

View File

@@ -0,0 +1,231 @@
// 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/set_cover/capacity_model.h"
#include <limits>
#include "gtest/gtest.h"
#include "ortools/base/gmock.h"
#include "ortools/base/message_matchers.h"
#include "ortools/set_cover/set_cover_model.h"
namespace operations_research {
namespace {
using ::testing::EqualsProto;
TEST(CapacityModel, ConstructorRequiresOneBound) {
EXPECT_DEATH(CapacityModel(std::numeric_limits<CapacityWeight>::min(),
std::numeric_limits<CapacityWeight>::max()),
"min");
}
TEST(CapacityModel, ConstructorRejectsNaN) {
EXPECT_DEATH(CapacityModel(std::numeric_limits<CapacityWeight>::quiet_NaN(),
std::numeric_limits<CapacityWeight>::quiet_NaN()),
"isnan");
}
TEST(CapacityModel, WithMinimumWeightRequiresNonVacuousMinimum) {
EXPECT_DEATH(CapacityModel::WithMinimumWeight(
std::numeric_limits<CapacityWeight>::min()),
"min");
}
TEST(CapacityModel, WithMaximumWeightRequiresNonVacuousMaximum) {
EXPECT_DEATH(CapacityModel::WithMaximumWeight(
std::numeric_limits<CapacityWeight>::max()),
"min");
}
TEST(CapacityModel, WithMinimumWeightRejectsNaN) {
EXPECT_DEATH(CapacityModel::WithMinimumWeight(
std::numeric_limits<double>::quiet_NaN()),
"isnan");
}
TEST(CapacityModel, WithMaximumWeightRejectsNaN) {
EXPECT_DEATH(CapacityModel::WithMaximumWeight(
std::numeric_limits<double>::quiet_NaN()),
"isnan");
}
TEST(CapacityModel, SetMinimumCapacityRejectsNaN) {
CapacityModel m(0.0, 1.0);
EXPECT_DEATH(m.SetMinimumCapacity(std::numeric_limits<double>::quiet_NaN()),
"isnan");
}
TEST(CapacityModel, SetMinimumCapacityRejectsPlusInfinity) {
CapacityModel m(0.0, 1.0);
EXPECT_DEATH(m.SetMinimumCapacity(std::numeric_limits<CapacityWeight>::max()),
"max");
}
TEST(CapacityModel, SetMaximumCapacityRejectsNaN) {
CapacityModel m(0.0, 1.0);
EXPECT_DEATH(m.SetMaximumCapacity(std::numeric_limits<double>::quiet_NaN()),
"isnan");
}
TEST(CapacityModel, SetMaximumCapacityRejectsMinusInfinity) {
CapacityModel m(0.0, 1.0);
EXPECT_DEATH(m.SetMaximumCapacity(std::numeric_limits<CapacityWeight>::min()),
"min");
}
TEST(CapacityModel, AddTermRejectsNaN) {
CapacityModel m(0.0, 1.0);
EXPECT_DEATH(m.AddTerm(SubsetIndex(0), ElementIndex(0),
std::numeric_limits<double>::quiet_NaN()),
"isfinite");
}
TEST(CapacityModel, AddTermRejectsPlusInf) {
CapacityModel m(0.0, 1.0);
EXPECT_DEATH(m.AddTerm(SubsetIndex(0), ElementIndex(0),
std::numeric_limits<double>::infinity()),
"isfinite");
}
TEST(CapacityModel, AddTermRejectsMinusInf) {
CapacityModel m(0.0, 1.0);
EXPECT_DEATH(m.AddTerm(SubsetIndex(0), ElementIndex(0),
-std::numeric_limits<double>::infinity()),
"isfinite");
}
TEST(CapacityModel, ComputeFeasibilityWithNoTerms) {
CapacityModel m(0.0, 1.0);
EXPECT_TRUE(m.ComputeFeasibility());
m.SetMinimumCapacity(-1.0);
EXPECT_TRUE(m.ComputeFeasibility());
m.SetMaximumCapacity(0.0);
EXPECT_TRUE(m.ComputeFeasibility());
m.SetMinimumCapacity(-2.0);
m.SetMaximumCapacity(-1.0);
EXPECT_FALSE(m.ComputeFeasibility());
}
TEST(CapacityModel, ComputeFeasibilityWithOnlyPositiveWeights) {
CapacityModel m(0.0, 1.0);
m.AddTerm(SubsetIndex(0), ElementIndex(0), 1.0);
m.AddTerm(SubsetIndex(0), ElementIndex(1), 2.0);
m.AddTerm(SubsetIndex(0), ElementIndex(2), 3.0);
// Activation bounds: [0.0, 6.0].
EXPECT_TRUE(m.ComputeFeasibility());
m.SetMinimumCapacity(-1.0);
EXPECT_TRUE(m.ComputeFeasibility());
m.SetMaximumCapacity(-1.0);
EXPECT_FALSE(m.ComputeFeasibility());
m.SetMaximumCapacity(7.0);
EXPECT_TRUE(m.ComputeFeasibility());
m.SetMinimumCapacity(7.0);
EXPECT_FALSE(m.ComputeFeasibility());
}
TEST(CapacityModel, ComputeFeasibilityWithOnlyNegativeWeights) {
CapacityModel m(0.0, 1.0);
m.AddTerm(SubsetIndex(0), ElementIndex(0), -1.0);
m.AddTerm(SubsetIndex(0), ElementIndex(1), -2.0);
m.AddTerm(SubsetIndex(0), ElementIndex(2), -3.0);
// Activation bounds: [-6.0, 0.0].
EXPECT_TRUE(m.ComputeFeasibility());
m.SetMaximumCapacity(1.0);
EXPECT_TRUE(m.ComputeFeasibility());
m.SetMinimumCapacity(1.0);
EXPECT_FALSE(m.ComputeFeasibility());
m.SetMinimumCapacity(-7.0);
EXPECT_TRUE(m.ComputeFeasibility());
m.SetMaximumCapacity(-7.0);
EXPECT_FALSE(m.ComputeFeasibility());
}
TEST(CapacityModel, ComputeFeasibilityWithOnlyMixedWeights) {
CapacityModel m(0.0, 1.0);
m.AddTerm(SubsetIndex(0), ElementIndex(0), -1.0);
m.AddTerm(SubsetIndex(0), ElementIndex(1), 2.0);
m.AddTerm(SubsetIndex(0), ElementIndex(2), -3.0);
// Activation bounds: [-4.0, 2.0].
EXPECT_TRUE(m.ComputeFeasibility());
m.SetMaximumCapacity(3.0);
EXPECT_TRUE(m.ComputeFeasibility());
m.SetMinimumCapacity(3.0);
EXPECT_FALSE(m.ComputeFeasibility());
m.SetMinimumCapacity(-5.0);
EXPECT_TRUE(m.ComputeFeasibility());
m.SetMaximumCapacity(-5.0);
EXPECT_FALSE(m.ComputeFeasibility());
}
// TEST(CapacityModel, ImportModelFromProto) {
// CapacityModel m(0.0, 1.0);
// EXPECT_THAT(m.ExportModelAsProto(), EqualsProto(R"pb(min_capacity: 0.0
// max_capacity: 1.0)pb"));
// m.AddTerm(SubsetIndex(0), ElementIndex(0), 1.0);
// EXPECT_THAT(m.ExportModelAsProto(),
// EqualsProto(R"pb(min_capacity: 0.0
// max_capacity: 1.0
// capacity_term {
// subset: 0
// element_weights { element: 0 weight: 1.0 }
// })pb"));
// m.AddTerm(SubsetIndex(0), ElementIndex(1), 1.0);
// EXPECT_THAT(m.ExportModelAsProto(),
// EqualsProto(
// R"pb(min_capacity: 0.0
// max_capacity: 1.0
// capacity_term {
// subset: 0
// element_weights { element: 0 weight: 1.0 }
// element_weights { element: 1 weight: 1.0 }
// })pb"));
// }
// TEST(CapacityModel, ImportModelFromProtoHasCanonicalOrder) {
// // Reverse order for the terms compared to
// // CapacityModel_ImportModelFromProto, same order in the proto.
// CapacityModel m(0.0, 1.0);
// m.AddTerm(SubsetIndex(0), ElementIndex(1), 1.0);
// m.AddTerm(SubsetIndex(0), ElementIndex(0), 1.0);
// EXPECT_THAT(m.ExportModelAsProto(),
// EqualsProto(
// R"pb(min_capacity: 0.0
// max_capacity: 1.0
// capacity_term {
// subset: 0
// element_weights { element: 0 weight: 1.0 }
// element_weights { element: 1 weight: 1.0 }
// })pb"));
// }
} // namespace
} // namespace operations_research

View File

@@ -0,0 +1,44 @@
# 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.
# Python wrapper for ..
load("@pip_deps//:requirements.bzl", "requirement")
load("@pybind11_bazel//:build_defs.bzl", "pybind_extension")
load("@rules_python//python:defs.bzl", "py_test")
# set_cover
pybind_extension(
name = "set_cover",
srcs = ["set_cover.cc"],
visibility = ["//visibility:public"],
deps = [
"//ortools/set_cover:set_cover_cc_proto",
"//ortools/set_cover:set_cover_heuristics",
"//ortools/set_cover:set_cover_invariant",
"//ortools/set_cover:set_cover_model",
"//ortools/set_cover:set_cover_reader",
"@com_google_absl//absl/strings",
"@pybind11_protobuf//pybind11_protobuf:native_proto_caster",
],
)
py_test(
name = "set_cover_test",
srcs = ["set_cover_test.py"],
python_version = "PY3",
deps = [
":set_cover",
"//ortools/set_cover:set_cover_py_pb2",
requirement("absl-py"),
],
)

View File

@@ -0,0 +1,42 @@
# 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.
# set_cover
pybind11_add_module(set_cover_pybind11 MODULE set_cover.cc)
set_target_properties(set_cover_pybind11 PROPERTIES
LIBRARY_OUTPUT_NAME "set_cover")
# note: macOS is APPLE and also UNIX !
if(APPLE)
set_target_properties(set_cover_pybind11 PROPERTIES
SUFFIX ".so"
INSTALL_RPATH "@loader_path;@loader_path/../../../${PYTHON_PROJECT}/.libs"
)
elseif(UNIX)
set_target_properties(set_cover_pybind11 PROPERTIES
INSTALL_RPATH "$ORIGIN:$ORIGIN/../../../${PYTHON_PROJECT}/.libs"
)
endif()
target_link_libraries(set_cover_pybind11 PRIVATE
${PROJECT_NAMESPACE}::ortools
pybind11_native_proto_caster
)
add_library(${PROJECT_NAMESPACE}::set_cover_pybind11 ALIAS set_cover_pybind11)
if(BUILD_TESTING)
file(GLOB PYTHON_SRCS "*_test.py")
foreach(FILE_NAME IN LISTS PYTHON_SRCS)
add_python_test(FILE_NAME ${FILE_NAME})
endforeach()
endif()

View File

@@ -20,10 +20,10 @@
#include <vector>
#include "absl/types/span.h"
#include "ortools/algorithms/set_cover_heuristics.h"
#include "ortools/algorithms/set_cover_invariant.h"
#include "ortools/algorithms/set_cover_model.h"
#include "ortools/algorithms/set_cover_reader.h"
#include "ortools/set_cover/set_cover_heuristics.h"
#include "ortools/set_cover/set_cover_invariant.h"
#include "ortools/set_cover/set_cover_model.h"
#include "ortools/set_cover/set_cover_reader.h"
#include "pybind11/numpy.h"
#include "pybind11/pybind11.h"
#include "pybind11/pytypes.h"
@@ -300,7 +300,7 @@ PYBIND11_MODULE(set_cover, m) {
.def(
"compute_coverage_in_focus",
[](SetCoverInvariant& invariant,
const std::vector<BaseInt>& focus) -> std::vector<BaseInt> {
absl::Span<const BaseInt> focus) -> std::vector<BaseInt> {
return invariant
.ComputeCoverageInFocus(VectorIntToVectorSubsetIndex(focus))
.get();
@@ -321,7 +321,7 @@ PYBIND11_MODULE(set_cover, m) {
.def("compress_trace", &SetCoverInvariant::CompressTrace)
.def("load_solution",
[](SetCoverInvariant& invariant,
const std::vector<bool>& solution) -> void {
absl::Span<const bool> solution) -> void {
SubsetBoolVector sol(solution.begin(), solution.end());
return invariant.LoadSolution(sol);
})
@@ -453,7 +453,7 @@ PYBIND11_MODULE(set_cover, m) {
return heuristic.NextSolution(num_iterations);
})
.def("next_solution",
[](SteepestSearch& heuristic, const std::vector<BaseInt>& focus,
[](SteepestSearch& heuristic, absl::Span<const BaseInt> focus,
int num_iterations) -> bool {
return heuristic.NextSolution(VectorIntToVectorSubsetIndex(focus),
num_iterations);
@@ -512,7 +512,7 @@ PYBIND11_MODULE(set_cover, m) {
return heuristic.NextSolution(num_iterations);
})
.def("next_solution",
[](GuidedTabuSearch& heuristic, const std::vector<BaseInt>& focus,
[](GuidedTabuSearch& heuristic, absl::Span<const BaseInt> focus,
int num_iterations) -> bool {
return heuristic.NextSolution(VectorIntToVectorSubsetIndex(focus),
num_iterations);

View File

@@ -15,7 +15,7 @@
from absl import app
from absl.testing import absltest
from ortools.algorithms.python import set_cover
from ortools.set_cover.python import set_cover
def create_initial_cover_model():

View File

@@ -0,0 +1,44 @@
# 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.
if(NOT BUILD_SAMPLES)
return()
endif()
if(BUILD_CXX_SAMPLES)
file(GLOB CXX_SRCS "*.cc")
foreach(SAMPLE IN LISTS CXX_SRCS)
add_cxx_sample(FILE_NAME ${SAMPLE})
endforeach()
endif()
if(BUILD_PYTHON_SAMPLES)
file(GLOB PYTHON_SRCS "*.py")
foreach(SAMPLE IN LISTS PYTHON_SRCS)
add_python_sample(FILE_NAME ${SAMPLE})
endforeach()
endif()
if(BUILD_JAVA_SAMPLES)
file(GLOB JAVA_SRCS "*.java")
foreach(SAMPLE IN LISTS JAVA_SRCS)
add_java_sample(FILE_NAME ${SAMPLE})
endforeach()
endif()
if(BUILD_DOTNET_SAMPLES)
file(GLOB DOTNET_SRCS "*.cs")
foreach(SAMPLE IN LISTS DOTNET_SRCS)
add_dotnet_sample(FILE_NAME ${SAMPLE})
endforeach()
endif()

View File

@@ -0,0 +1,26 @@
#!/usr/bin/env bash
# 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.
source gbash.sh || exit
source module gbash_unit.sh
DEFINE_string sample "" "sample code."
function test::operations_research_examples::code_samples_set_cover() {
declare -r DIR="${TEST_SRCDIR}/ortools/set_cover/samples"
EXPECT_SUCCEED "${DIR}/${FLAGS_sample}_cc"
}
gbash::unit::main "$@"

View File

@@ -0,0 +1,26 @@
#!/usr/bin/env bash
# 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.
source gbash.sh || exit
source module gbash_unit.sh
DEFINE_string sample "" "sample code."
function test::operations_research_examples::code_samples_set_cover_py() {
declare -r DIR="${TEST_SRCDIR}/ortools/set_cover/samples"
EXPECT_SUCCEED "${DIR}/${FLAGS_sample}_py3"
}
gbash::unit::main "$@"

View File

@@ -15,10 +15,10 @@
// [START import]
#include <cstdlib>
#include "ortools/algorithms/set_cover_heuristics.h"
#include "ortools/algorithms/set_cover_invariant.h"
#include "ortools/algorithms/set_cover_model.h"
#include "ortools/base/logging.h"
#include "ortools/set_cover/set_cover_heuristics.h"
#include "ortools/set_cover/set_cover_invariant.h"
#include "ortools/set_cover/set_cover_model.h"
// [END import]
namespace operations_research {

View File

@@ -16,7 +16,7 @@
# [START program]
# [START import]
from ortools.algorithms.python import set_cover
from ortools.set_cover.python import set_cover
# [END import]

View File

@@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "ortools/algorithms/set_cover_heuristics.h"
#include "ortools/set_cover/set_cover_heuristics.h"
#include <algorithm>
#include <climits>
@@ -28,9 +28,9 @@
#include "absl/random/random.h"
#include "absl/types/span.h"
#include "ortools/algorithms/adjustable_k_ary_heap.h"
#include "ortools/algorithms/set_cover_invariant.h"
#include "ortools/algorithms/set_cover_model.h"
#include "ortools/base/logging.h"
#include "ortools/set_cover/set_cover_invariant.h"
#include "ortools/set_cover/set_cover_model.h"
namespace operations_research {

View File

@@ -11,15 +11,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef OR_TOOLS_ALGORITHMS_SET_COVER_HEURISTICS_H_
#define OR_TOOLS_ALGORITHMS_SET_COVER_HEURISTICS_H_
#ifndef OR_TOOLS_SET_COVER_SET_COVER_HEURISTICS_H_
#define OR_TOOLS_SET_COVER_SET_COVER_HEURISTICS_H_
#include <vector>
#include "absl/types/span.h"
#include "ortools/algorithms/adjustable_k_ary_heap.h"
#include "ortools/algorithms/set_cover_invariant.h"
#include "ortools/algorithms/set_cover_model.h"
#include "ortools/set_cover/set_cover_invariant.h"
#include "ortools/set_cover/set_cover_model.h"
namespace operations_research {
@@ -499,4 +499,4 @@ std::vector<SubsetIndex> ClearMostCoveredElements(
SetCoverInvariant* inv);
} // namespace operations_research
#endif // OR_TOOLS_ALGORITHMS_SET_COVER_HEURISTICS_H_
#endif // OR_TOOLS_SET_COVER_SET_COVER_HEURISTICS_H_

View File

@@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "ortools/algorithms/set_cover_invariant.h"
#include "ortools/set_cover/set_cover_invariant.h"
#include <algorithm>
#include <limits>
@@ -20,9 +20,9 @@
#include "absl/log/check.h"
#include "absl/types/span.h"
#include "ortools/algorithms/set_cover_model.h"
#include "ortools/base/logging.h"
#include "ortools/base/mathutil.h"
#include "ortools/set_cover/set_cover_model.h"
namespace operations_research {

View File

@@ -11,16 +11,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef OR_TOOLS_ALGORITHMS_SET_COVER_INVARIANT_H_
#define OR_TOOLS_ALGORITHMS_SET_COVER_INVARIANT_H_
#ifndef OR_TOOLS_SET_COVER_SET_COVER_INVARIANT_H_
#define OR_TOOLS_SET_COVER_SET_COVER_INVARIANT_H_
#include <tuple>
#include <vector>
#include "absl/log/check.h"
#include "absl/types/span.h"
#include "ortools/algorithms/set_cover.pb.h"
#include "ortools/algorithms/set_cover_model.h"
#include "ortools/set_cover/set_cover.pb.h"
#include "ortools/set_cover/set_cover_model.h"
namespace operations_research {
@@ -288,4 +288,4 @@ class SetCoverInvariant {
};
} // namespace operations_research
#endif // OR_TOOLS_ALGORITHMS_SET_COVER_INVARIANT_H_
#endif // OR_TOOLS_SET_COVER_SET_COVER_INVARIANT_H_

View File

@@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "ortools/algorithms/set_cover_lagrangian.h"
#include "ortools/set_cover/set_cover_lagrangian.h"
#include <algorithm>
#include <cstdlib>
@@ -22,9 +22,9 @@
#include "absl/log/check.h"
#include "absl/synchronization/blocking_counter.h"
#include "ortools/algorithms/adjustable_k_ary_heap.h"
#include "ortools/algorithms/set_cover_invariant.h"
#include "ortools/algorithms/set_cover_model.h"
#include "ortools/base/threadpool.h"
#include "ortools/set_cover/set_cover_invariant.h"
#include "ortools/set_cover/set_cover_model.h"
namespace operations_research {
@@ -99,7 +99,8 @@ void FillReducedCostsSlice(SubsetIndex slice_start, SubsetIndex slice_end,
}
BaseInt BlockSize(BaseInt size, int num_threads) {
return 1 + (size - 1) / num_threads;
// Traditional formula to compute std::ceil(size / num_threads).
return (size + num_threads - 1) / num_threads;
}
} // namespace

View File

@@ -11,17 +11,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef OR_TOOLS_ALGORITHMS_SET_COVER_LAGRANGIAN_H_
#define OR_TOOLS_ALGORITHMS_SET_COVER_LAGRANGIAN_H_
#ifndef OR_TOOLS_SET_COVER_SET_COVER_LAGRANGIAN_H_
#define OR_TOOLS_SET_COVER_SET_COVER_LAGRANGIAN_H_
#include <memory>
#include <new>
#include <tuple>
#include <vector>
#include "ortools/algorithms/set_cover_invariant.h"
#include "ortools/algorithms/set_cover_model.h"
#include "ortools/base/threadpool.h"
#include "ortools/set_cover/set_cover_invariant.h"
#include "ortools/set_cover/set_cover_model.h"
namespace operations_research {
@@ -160,4 +160,4 @@ class SetCoverLagrangian {
} // namespace operations_research
#endif // OR_TOOLS_ALGORITHMS_SET_COVER_LAGRANGIAN_H_
#endif // OR_TOOLS_SET_COVER_SET_COVER_LAGRANGIAN_H_

View File

@@ -11,18 +11,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "ortools/algorithms/set_cover_mip.h"
#include "ortools/set_cover/set_cover_mip.h"
#include <cstdint>
#include <limits>
#include "absl/log/check.h"
#include "absl/types/span.h"
#include "ortools/algorithms/set_cover_invariant.h"
#include "ortools/algorithms/set_cover_model.h"
#include "ortools/base/logging.h"
#include "ortools/linear_solver/linear_solver.h"
#include "ortools/lp_data/lp_types.h"
#include "ortools/set_cover/set_cover_invariant.h"
#include "ortools/set_cover/set_cover_model.h"
namespace operations_research {

View File

@@ -11,12 +11,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef OR_TOOLS_ALGORITHMS_SET_COVER_MIP_H_
#define OR_TOOLS_ALGORITHMS_SET_COVER_MIP_H_
#ifndef OR_TOOLS_SET_COVER_SET_COVER_MIP_H_
#define OR_TOOLS_SET_COVER_SET_COVER_MIP_H_
#include "absl/types/span.h"
#include "ortools/algorithms/set_cover_invariant.h"
#include "ortools/algorithms/set_cover_model.h"
#include "ortools/set_cover/set_cover_invariant.h"
#include "ortools/set_cover/set_cover_model.h"
namespace operations_research {
enum class SetCoverMipSolver : int {
@@ -67,4 +67,4 @@ class SetCoverMip {
};
} // namespace operations_research
#endif // OR_TOOLS_ALGORITHMS_SET_COVER_MIP_H_
#endif // OR_TOOLS_SET_COVER_SET_COVER_MIP_H_

View File

@@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "ortools/algorithms/set_cover_model.h"
#include "ortools/set_cover/set_cover_model.h"
#include <algorithm>
#include <cmath>
@@ -31,8 +31,8 @@
#include "absl/strings/str_format.h"
#include "absl/types/span.h"
#include "ortools/algorithms/radix_sort.h"
#include "ortools/algorithms/set_cover.pb.h"
#include "ortools/base/logging.h"
#include "ortools/set_cover/set_cover.pb.h"
namespace operations_research {

View File

@@ -11,8 +11,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef OR_TOOLS_ALGORITHMS_SET_COVER_MODEL_H_
#define OR_TOOLS_ALGORITHMS_SET_COVER_MODEL_H_
#ifndef OR_TOOLS_SET_COVER_SET_COVER_MODEL_H_
#define OR_TOOLS_SET_COVER_SET_COVER_MODEL_H_
#include <cstdint>
#include <string>
@@ -20,9 +20,9 @@
#include "absl/log/check.h"
#include "absl/strings/str_cat.h"
#include "ortools/algorithms/set_cover.pb.h"
#include "ortools/base/strong_int.h"
#include "ortools/base/strong_vector.h"
#include "ortools/set_cover/set_cover.pb.h"
// Representation class for the weighted set-covering problem.
//
@@ -398,4 +398,4 @@ class IntersectingSubsetsIterator {
} // namespace operations_research
#endif // OR_TOOLS_ALGORITHMS_SET_COVER_MODEL_H_
#endif // OR_TOOLS_SET_COVER_SET_COVER_MODEL_H_

View File

@@ -11,10 +11,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "ortools/algorithms/set_cover_reader.h"
#include "ortools/set_cover/set_cover_reader.h"
#include <sys/types.h>
#include <algorithm>
#include <cctype>
#include <cstddef>
#include <cstdint>
@@ -28,12 +29,13 @@
#include "absl/strings/str_format.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "ortools/algorithms/set_cover.pb.h"
#include "ortools/algorithms/set_cover_model.h"
#include "ortools/base/file.h"
#include "ortools/base/filesystem.h"
#include "ortools/base/helpers.h"
#include "ortools/base/logging.h"
#include "ortools/base/options.h"
#include "ortools/set_cover/set_cover.pb.h"
#include "ortools/set_cover/set_cover_model.h"
#include "ortools/util/filelineiter.h"
namespace operations_research {
@@ -105,30 +107,42 @@ int64_t SetCoverReader::ParseNextInteger() {
return value;
}
namespace {
double Percent(SubsetIndex subset, BaseInt total) {
return subset.value() == 0 ? 0 : 100.0 * subset.value() / total;
}
double Percent(ElementIndex element, BaseInt total) {
return element.value() == 0 ? 0 : 100.0 * element.value() / total;
}
} // namespace
// This is a row-based format where the elements are 1-indexed.
SetCoverModel ReadOrlibScp(absl::string_view filename) {
CHECK_OK(file::Exists(filename, file::Defaults()));
SetCoverModel model;
File* file(file::OpenOrDie(filename, "r", file::Defaults()));
SetCoverReader reader(file);
const ElementIndex num_rows(reader.ParseNextInteger());
const SubsetIndex num_cols(reader.ParseNextInteger());
model.ReserveNumSubsets(num_cols.value());
model.ReserveNumSubsets(num_cols);
for (SubsetIndex subset : SubsetRange(num_cols)) {
const double cost(reader.ParseNextDouble());
model.SetSubsetCost(subset.value(), cost);
model.SetSubsetCost(subset, cost);
}
for (ElementIndex element : ElementRange(num_rows)) {
LOG_EVERY_N_SEC(INFO, 5)
<< absl::StrFormat("Reading element %d (%.1f%%)", element.value(),
100.0 * element.value() / model.num_elements());
Percent(element, model.num_elements()));
const RowEntryIndex row_size(reader.ParseNextInteger());
for (RowEntryIndex entry(0); entry < row_size; ++entry) {
// Correct the 1-indexing.
const int subset(reader.ParseNextInteger() - 1);
model.AddElementToSubset(element.value(), subset);
const SubsetIndex subset(reader.ParseNextInteger() - 1);
model.AddElementToSubset(element, subset);
}
}
LOG(INFO) << "Finished reading the model.";
LOG(INFO) << "Read " << model.num_subsets() << " subsets, "
<< model.num_elements() << " elements";
file->Close(file::Defaults()).IgnoreError();
model.CreateSparseRowView();
return model;
@@ -136,59 +150,96 @@ SetCoverModel ReadOrlibScp(absl::string_view filename) {
// This is a column-based format where the elements are 1-indexed.
SetCoverModel ReadOrlibRail(absl::string_view filename) {
CHECK_OK(file::Exists(filename, file::Defaults()));
SetCoverModel model;
File* file(file::OpenOrDie(filename, "r", file::Defaults()));
SetCoverReader reader(file);
const ElementIndex num_rows(reader.ParseNextInteger());
const BaseInt num_cols(reader.ParseNextInteger());
const SubsetIndex num_cols(reader.ParseNextInteger());
model.ReserveNumSubsets(num_cols);
for (BaseInt subset(0); subset < num_cols; ++subset) {
for (SubsetIndex subset : SubsetRange(num_cols)) {
LOG_EVERY_N_SEC(INFO, 5)
<< absl::StrFormat("Reading subset %d (%.1f%%)", subset,
100.0 * subset / model.num_subsets());
<< absl::StrFormat("Reading subset %d (%.1f%%)", subset.value(),
Percent(subset, model.num_subsets()));
const double cost(reader.ParseNextDouble());
model.SetSubsetCost(subset, cost);
const ColumnEntryIndex column_size(reader.ParseNextInteger());
model.ReserveNumElementsInSubset(column_size.value(), subset);
model.ReserveNumElementsInSubset(column_size.value(), subset.value());
for (const ColumnEntryIndex _ : ColumnEntryRange(column_size)) {
// Correct the 1-indexing.
const ElementIndex element(reader.ParseNextInteger() - 1);
model.AddElementToSubset(element.value(), subset);
model.AddElementToSubset(element, subset);
}
}
LOG(INFO) << "Finished reading the model.";
LOG(INFO) << "Read " << model.num_subsets() << " subsets, "
<< model.num_elements() << " elements";
file->Close(file::Defaults()).IgnoreError();
model.CreateSparseRowView();
return model;
}
SetCoverModel ReadFimiDat(absl::string_view filename) {
CHECK_OK(file::Exists(filename, file::Defaults()));
SetCoverModel model;
BaseInt subset(0);
SubsetIndex subset(0);
// Read the file once to discover the smallest element index.
BaseInt smallest_element = std::numeric_limits<BaseInt>::max();
BaseInt largest_element = 0;
for (const std::string& line : FileLines(filename)) {
std::vector<std::string> elements = absl::StrSplit(line, ' ');
if (elements.back().empty() || elements.back()[0] == '\0') {
elements.pop_back();
}
for (const std::string& number_str : elements) {
BaseInt element;
CHECK(absl::SimpleAtoi(number_str, &element));
smallest_element = std::min(smallest_element, element);
largest_element = std::max(largest_element, element);
}
}
DLOG(INFO) << "Smallest element: " << smallest_element
<< ", Largest element: " << largest_element;
ElementBoolVector element_seen(largest_element + 1, false);
for (const std::string& line : FileLines(filename)) {
LOG_EVERY_N_SEC(INFO, 5)
<< absl::StrFormat("Reading subset %d (%.1f%%)", subset,
100.0 * subset / model.num_subsets());
<< absl::StrFormat("Reading subset %d", subset.value());
std::vector<std::string> elements = absl::StrSplit(line, ' ');
if (elements.back().empty() || elements.back()[0] == '\0') {
elements.pop_back();
}
model.AddEmptySubset(1);
for (const std::string& number : elements) {
BaseInt element;
CHECK(absl::SimpleAtoi(number, &element));
CHECK_GT(element, 0);
// Correct the 1-indexing.
model.AddElementToLastSubset(ElementIndex(element - 1));
// As there can be repetitions in the data, we need to keep track of the
// elements already added to the subset.
std::vector<ElementIndex> elements_list;
for (const std::string& number_str : elements) {
BaseInt raw_element;
CHECK(absl::SimpleAtoi(number_str, &raw_element));
// Re-index the elements starting from 0.
ElementIndex element(raw_element - smallest_element);
if (element_seen[element]) {
DLOG(INFO) << "Element " << element << " already in subset "
<< subset.value();
continue;
}
element_seen[element] = true;
elements_list.push_back(element);
CHECK_GE(element.value(), 0);
model.AddElementToLastSubset(element);
}
// Clean up the list of elements.
for (const ElementIndex element : elements_list) {
element_seen[element] = false;
}
++subset;
}
LOG(INFO) << "Finished reading the model.";
LOG(INFO) << "Read " << model.num_subsets() << " subsets, "
<< model.num_elements() << " elements";
model.CreateSparseRowView();
return model;
}
SetCoverModel ReadSetCoverProto(absl::string_view filename, bool binary) {
CHECK_OK(file::Exists(filename, file::Defaults()));
SetCoverModel model;
SetCoverProto message;
if (binary) {
@@ -255,7 +306,7 @@ void WriteOrlibScp(const SetCoverModel& model, absl::string_view filename) {
for (const ElementIndex element : model.ElementRange()) {
LOG_EVERY_N_SEC(INFO, 5)
<< absl::StrFormat("Writing element %d (%.1f%%)", element.value(),
100.0 * element.value() / model.num_elements());
Percent(element, model.num_elements()));
formatter.Append(absl::StrCat(model.rows()[element].size(), "\n"));
for (const SubsetIndex subset : model.rows()[element]) {
formatter.Append(subset.value() + 1);
@@ -276,7 +327,7 @@ void WriteOrlibRail(const SetCoverModel& model, absl::string_view filename) {
for (const SubsetIndex subset : model.SubsetRange()) {
LOG_EVERY_N_SEC(INFO, 5)
<< absl::StrFormat("Writing subset %d (%.1f%%)", subset.value(),
100.0 * subset.value() / model.num_subsets());
Percent(subset, model.num_subsets()));
formatter.Append(model.subset_costs()[subset]);
formatter.Append(static_cast<BaseInt>(model.columns()[subset].size()));
for (const ElementIndex element : model.columns()[subset]) {
@@ -299,6 +350,7 @@ void WriteSetCoverProto(const SetCoverModel& model, absl::string_view filename,
}
SubsetBoolVector ReadSetCoverSolutionText(absl::string_view filename) {
CHECK_OK(file::Exists(filename, file::Defaults()));
SubsetBoolVector solution;
File* file(file::OpenOrDie(filename, "r", file::Defaults()));
SetCoverReader reader(file);
@@ -316,6 +368,7 @@ SubsetBoolVector ReadSetCoverSolutionText(absl::string_view filename) {
SubsetBoolVector ReadSetCoverSolutionProto(absl::string_view filename,
bool binary) {
CHECK_OK(file::Exists(filename, file::Defaults()));
SubsetBoolVector solution;
SetCoverSolutionResponse message;
if (binary) {

View File

@@ -11,11 +11,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef OR_TOOLS_ALGORITHMS_SET_COVER_READER_H_
#define OR_TOOLS_ALGORITHMS_SET_COVER_READER_H_
#ifndef OR_TOOLS_SET_COVER_SET_COVER_READER_H_
#define OR_TOOLS_SET_COVER_SET_COVER_READER_H_
#include "absl/strings/string_view.h"
#include "ortools/algorithms/set_cover_model.h"
#include "ortools/set_cover/set_cover_model.h"
namespace operations_research {
@@ -106,4 +106,4 @@ void WriteSetCoverSolutionProto(const SetCoverModel& model,
} // namespace operations_research
#endif // OR_TOOLS_ALGORITHMS_SET_COVER_READER_H_
#endif // OR_TOOLS_SET_COVER_SET_COVER_READER_H_

View File

@@ -20,13 +20,13 @@
#include "absl/strings/str_join.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "ortools/algorithms/set_cover_heuristics.h"
#include "ortools/algorithms/set_cover_invariant.h"
#include "ortools/algorithms/set_cover_model.h"
#include "ortools/algorithms/set_cover_reader.h"
#include "ortools/base/init_google.h"
#include "ortools/base/logging.h"
#include "ortools/base/timer.h"
#include "ortools/set_cover/set_cover_heuristics.h"
#include "ortools/set_cover/set_cover_invariant.h"
#include "ortools/set_cover/set_cover_model.h"
#include "ortools/set_cover/set_cover_reader.h"
ABSL_FLAG(std::string, input, "", "REQUIRED: Input file name.");
ABSL_FLAG(std::string, input_fmt, "",

View File

@@ -19,14 +19,14 @@
#include "absl/strings/str_cat.h"
#include "benchmark/benchmark.h"
#include "gtest/gtest.h"
#include "ortools/algorithms/set_cover.pb.h"
#include "ortools/algorithms/set_cover_heuristics.h"
#include "ortools/algorithms/set_cover_invariant.h"
#include "ortools/algorithms/set_cover_mip.h"
#include "ortools/algorithms/set_cover_model.h"
#include "ortools/base/gmock.h"
#include "ortools/base/logging.h"
#include "ortools/base/parse_text_proto.h"
#include "ortools/set_cover/set_cover.pb.h"
#include "ortools/set_cover/set_cover_heuristics.h"
#include "ortools/set_cover/set_cover_invariant.h"
#include "ortools/set_cover/set_cover_mip.h"
#include "ortools/set_cover/set_cover_model.h"
namespace operations_research {
namespace {