diff --git a/CMakeLists.txt b/CMakeLists.txt index 23ff36d929c..85014b32f6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,9 @@ cmake_minimum_required(VERSION 3.20) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +# Enable output of compile commands during generation. +option(CMAKE_EXPORT_COMPILE_COMMANDS "Export compile command" ON) + include(utils) set_version(VERSION) diff --git a/WORKSPACE b/WORKSPACE index 8d5faf7c504..464e15d0f24 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -75,7 +75,7 @@ git_repository( git_repository( name = "rules_python", - tag = "0.34.0", + tag = "0.36.0", remote = "https://github.com/bazelbuild/rules_python.git", ) @@ -105,6 +105,43 @@ git_repository( repo_mapping = {"@abseil-cpp": "@com_google_absl"}, ) +## Python +load("@rules_python//python:repositories.bzl", "py_repositories") +py_repositories() + +load("@rules_python//python:repositories.bzl", "python_register_toolchains") +DEFAULT_PYTHON = "3.11" +python_register_toolchains( + name = "python3_11", + python_version = DEFAULT_PYTHON, + ignore_root_user_error=True, +) +load("@python3_11//:defs.bzl", "interpreter") + +# Create a central external repo, @pip_deps, that contains Bazel targets for all the +# third-party packages specified in the bazel/requirements.txt file. +load("@rules_python//python:pip.bzl", "pip_parse") +pip_parse( + name = "pip_deps", + python_interpreter_target = interpreter, + requirements_lock = "//bazel:ortools_requirements.txt", +) + +load("@pip_deps//:requirements.bzl", + install_pip_deps="install_deps") +install_pip_deps() + +# Add a second repo @ortools_notebook_deps for jupyter notebooks. +pip_parse( + name = "ortools_notebook_deps", + python_interpreter_target = interpreter, + requirements_lock = "//bazel:notebook_requirements.txt", +) + +load("@ortools_notebook_deps//:requirements.bzl", + install_notebook_deps="install_deps") +install_notebook_deps() + ## Protobuf # proto_library, cc_proto_library, and java_proto_library rules implicitly # depend on @com_google_protobuf for protoc and proto runtimes. @@ -205,40 +242,6 @@ new_git_repository( remote = "https://github.com/swig/swig.git", ) -## Python -load("@rules_python//python:repositories.bzl", "py_repositories") -py_repositories() - -load("@rules_python//python:repositories.bzl", "python_register_toolchains") -DEFAULT_PYTHON = "3.12" -python_register_toolchains( - name = "python3_12", - python_version = DEFAULT_PYTHON, - ignore_root_user_error=True, -) - -# Create a central external repo, @pip_deps, that contains Bazel targets for all the -# third-party packages specified in the bazel/requirements.txt file. -load("@rules_python//python:pip.bzl", "pip_parse") -pip_parse( - name = "pip_deps", - requirements_lock = "//bazel:ortools_requirements.txt", -) - -load("@pip_deps//:requirements.bzl", - install_pip_deps="install_deps") -install_pip_deps() - -# Add a second repo @ortools_notebook_deps for jupyter notebooks. -pip_parse( - name = "ortools_notebook_deps", - requirements_lock = "//bazel:notebook_requirements.txt", -) - -load("@ortools_notebook_deps//:requirements.bzl", - install_notebook_deps="install_deps") -install_notebook_deps() - # Protobuf load("@com_google_protobuf//bazel:system_python.bzl", "system_python") system_python( @@ -283,7 +286,7 @@ new_git_repository( new_git_repository( name = "pybind11_protobuf", - commit = "84653a591aea5df482dc2bde42c19efafbd53a57", # 2024/06/28 + commit = "ed430af1814a97e4017f2f808d3ba28cc10802f1", # 2024/10/02 remote = "https://github.com/pybind/pybind11_protobuf.git", ) diff --git a/cmake/Makefile b/cmake/Makefile index a2e09550c23..d985ec0be88 100644 --- a/cmake/Makefile +++ b/cmake/Makefile @@ -632,7 +632,7 @@ TOOLCHAIN_STAGES := env devel toolchain build test define toolchain-stage-target = #$$(info STAGE: $1) #$$(info Create targets: toolchain_$1 $(addprefix toolchain_, $(addsuffix _$1, $(TOOLCHAIN_TARGETS))).) -targets_toolchain_$1 = $(addprefix toolchain_, $(addsuffix _$1, $(TOOLCHAIN_TARGETS))) +targets_toolchain_$1 := $(addprefix toolchain_, $(addsuffix _$1, $(TOOLCHAIN_TARGETS))) .PHONY: toolchain_$1 $$(targets_toolchain_$1) toolchain_$1: $$(targets_toolchain_$1) $$(targets_toolchain_$1): toolchain_%_$1: docker/toolchain/Dockerfile @@ -645,7 +645,7 @@ $$(targets_toolchain_$1): toolchain_%_$1: docker/toolchain/Dockerfile .. #$$(info Create targets: save_toolchain_$1 $(addprefix save_toolchain_, $(addsuffix _$1, $(TOOLCHAIN_TARGETS))) (debug).) -save_targets_toolchain_$1 = $(addprefix save_toolchain_, $(addsuffix _$1, $(TOOLCHAIN_TARGETS))) +save_targets_toolchain_$1 := $(addprefix save_toolchain_, $(addsuffix _$1, $(TOOLCHAIN_TARGETS))) .PHONY: save_toolchain_$1 $$(save_targets_toolchain_$1) save_toolchain_$1: $$(save_targets_toolchain_$1) $$(save_targets_toolchain_$1): save_toolchain_%_$1: cache/%/docker_$1.tar @@ -727,7 +727,7 @@ VAGRANT_VMS := \ define make-vagrant-target = #$$(info VMS: $1) #$$(info Create target: $1_.) -$1_targets = $(addprefix $1_, $(LANGUAGES)) +$1_targets := $(addprefix $1_, $(LANGUAGES)) .PHONY: $1 $$($1_targets) $1: $$($1_targets) $$($1_targets): $1_%: vagrant/$1/%/Vagrantfile @@ -736,14 +736,14 @@ $$($1_targets): $1_%: vagrant/$1/%/Vagrantfile cd vagrant/$1/$$* && vagrant up #$$(info Create targets: sh_$1_ vagrant machine (debug).) -sh_$1_targets = $(addprefix sh_$1_, $(LANGUAGES)) +sh_$1_targets := $(addprefix sh_$1_, $(LANGUAGES)) .PHONY: $$(sh_$1_targets) $$(sh_$1_targets): sh_$1_%: cd vagrant/$1/$$* && vagrant up cd vagrant/$1/$$* && vagrant ssh #$$(info Create targets: clean_$1) -clean_$1_targets = $(addprefix clean_$1_, $(LANGUAGES)) +clean_$1_targets := $(addprefix clean_$1_, $(LANGUAGES)) .PHONY: clean_$1 $(clean_$1_targets) clean_$1: $$(clean_$1_targets) $$(clean_$1_targets): clean_$1_%: diff --git a/cmake/check_deps.cmake b/cmake/check_deps.cmake index 36736909ee9..0b565d5b525 100644 --- a/cmake/check_deps.cmake +++ b/cmake/check_deps.cmake @@ -79,12 +79,36 @@ if(USE_COINOR) set(COINOR_DEPS Coin::CbcSolver Coin::OsiCbc Coin::ClpSolver Coin::OsiClp) endif() +if(USE_CPLEX) + if(NOT TARGET CPLEX::CPLEX) + message(FATAL_ERROR "Target CPLEX::CPLEX not available.") + endif() + set(CPLEX_DEPS CPLEX::CPLEX) +endif() + +if(USE_GLPK) + if(NOT TARGET GLPK::GLPK) + message(FATAL_ERROR "Target GLPK::GLPK not available.") + endif() + set(GLPK_DEPS GLPK::GLPK) +endif() + +if(USE_HIGHS) + if(NOT TARGET highs::highs) + message(FATAL_ERROR "Target highs::highs not available.") + endif() + set(HIGHS_DEPS highs::highs) +endif() + if(USE_PDLP AND BUILD_PDLP) set(PDLP_DEPS Eigen3::Eigen) endif() -if(USE_SCIP AND NOT TARGET libscip) - message(FATAL_ERROR "Target libscip not available.") +if(USE_SCIP) + if(NOT TARGET libscip) + message(FATAL_ERROR "Target libscip not available.") + endif() + set(SCIP_DEPS libscip) endif() # Check optional Dependencies diff --git a/cmake/cpp.cmake b/cmake/cpp.cmake index d8ddffa292b..2764562a2e1 100644 --- a/cmake/cpp.cmake +++ b/cmake/cpp.cmake @@ -147,70 +147,153 @@ endif() # ortools_cxx_test() # CMake function to generate and build C++ test. # Parameters: -# FILE_NAME: the C++ filename -# COMPONENT_NAME: name of the ortools/ subdir where the test is located -# note: automatically determined if located in ortools// +# NAME: CMake target name +# SOURCES: List of source files +# [COMPILE_DEFINITIONS]: List of private compile definitions +# [COMPILE_OPTIONS]: List of private compile options +# [LINK_LIBRARIES]: List of private libraries to use when linking +# note: ortools::ortools is always linked to the target +# [LINK_OPTIONS]: List of private link options # e.g.: # ortools_cxx_test( -# FILE_NAME -# ${PROJECT_SOURCE_DIR}/ortools/foo/foo_test.cc -# COMPONENT_NAME -# foo -# DEPS +# NAME +# foo_bar_test +# SOURCES +# bar_test.cc +# ${PROJECT_SOURCE_DIR}/ortools/foo/bar_test.cc +# LINK_LIBRARIES # GTest::gmock # GTest::gtest_main # ) function(ortools_cxx_test) set(options "") - set(oneValueArgs "FILE_NAME;COMPONENT_NAME") - set(multiValueArgs "DEPS") + set(oneValueArgs "NAME") + set(multiValueArgs + "SOURCES;COMPILE_DEFINITIONS;COMPILE_OPTIONS;LINK_LIBRARIES;LINK_OPTIONS") cmake_parse_arguments(TEST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) -if(NOT TEST_FILE_NAME) - message(FATAL_ERROR "no FILE_NAME provided") + if(NOT TEST_NAME) + message(FATAL_ERROR "no NAME provided") endif() - get_filename_component(TEST_NAME ${TEST_FILE_NAME} NAME_WE) - - message(STATUS "Configuring test ${TEST_FILE_NAME} ...") - - if(NOT TEST_COMPONENT_NAME) - # test is located in ortools// - get_filename_component(COMPONENT_DIR ${TEST_FILE_NAME} DIRECTORY) - get_filename_component(COMPONENT_NAME ${COMPONENT_DIR} NAME) - else() - set(COMPONENT_NAME ${TEST_COMPONENT_NAME}) + if(NOT TEST_SOURCES) + message(FATAL_ERROR "no SOURCES provided") endif() + message(STATUS "Configuring test ${TEST_NAME} ...") - add_executable(${TEST_NAME} ${TEST_FILE_NAME}) + add_executable(${TEST_NAME} "") + target_sources(${TEST_NAME} PRIVATE ${TEST_SOURCES}) target_include_directories(${TEST_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + target_compile_definitions(${TEST_NAME} PRIVATE ${TEST_COMPILE_DEFINITIONS}) target_compile_features(${TEST_NAME} PRIVATE cxx_std_17) + target_compile_options(${TEST_NAME} PRIVATE ${TEST_COMPILE_OPTIONS}) target_link_libraries(${TEST_NAME} PRIVATE ${PROJECT_NAMESPACE}::ortools - ${TEST_DEPS} + ${TEST_LINK_LIBRARIES} ) + target_link_options(${TEST_NAME} PRIVATE ${TEST_LINK_OPTIONS}) include(GNUInstallDirs) if(APPLE) - set_target_properties(${TEST_NAME} PROPERTIES INSTALL_RPATH - "@loader_path/../${CMAKE_INSTALL_LIBDIR};@loader_path") + set_target_properties(${TEST_NAME} PROPERTIES + INSTALL_RPATH "@loader_path/../${CMAKE_INSTALL_LIBDIR};@loader_path") elseif(UNIX) cmake_path(RELATIVE_PATH CMAKE_INSTALL_FULL_LIBDIR BASE_DIRECTORY ${CMAKE_INSTALL_FULL_BINDIR} OUTPUT_VARIABLE libdir_relative_path) set_target_properties(${TEST_NAME} PROPERTIES - INSTALL_RPATH "$ORIGIN/${libdir_relative_path}") + INSTALL_RPATH "$ORIGIN/${libdir_relative_path}:$ORIGIN") endif() if(BUILD_TESTING) add_test( - NAME cxx_${COMPONENT_NAME}_${TEST_NAME} - COMMAND ${TEST_NAME}) + NAME cxx_${TEST_NAME} + COMMAND ${TEST_NAME} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + ) + endif() + message(STATUS "Configuring test ${TEST_NAME} ...DONE") +endfunction() + +################### +## C++ Library ## +################### +# ortools_cxx_library() +# CMake function to generate and build C++ library. +# Parameters: +# NAME: CMake target name +# SOURCES: List of source files +# [TYPE]: SHARED or STATIC +# [COMPILE_DEFINITIONS]: List of private compile definitions +# [COMPILE_OPTIONS]: List of private compile options +# [LINK_LIBRARIES]: List of **public** libraries to use when linking +# note: ortools::ortools is always linked to the target +# [LINK_OPTIONS]: List of private link options +# e.g.: +# ortools_cxx_library( +# NAME +# foo_bar_library +# SOURCES +# bar_library.cc +# ${PROJECT_SOURCE_DIR}/ortools/foo/bar_library.cc +# TYPE +# SHARED +# LINK_LIBRARIES +# GTest::gmock +# GTest::gtest_main +# TESTING +# ) +function(ortools_cxx_library) + set(options "TESTING") + set(oneValueArgs "NAME;TYPE") + set(multiValueArgs + "SOURCES;COMPILE_DEFINITIONS;COMPILE_OPTIONS;LINK_LIBRARIES;LINK_OPTIONS") + cmake_parse_arguments(LIBRARY + "${options}" + "${oneValueArgs}" + "${multiValueArgs}" + ${ARGN} + ) + if(LIBRARY_TESTING AND NOT BUILD_TESTING) + return() + endif() + + if(NOT LIBRARY_NAME) + message(FATAL_ERROR "no NAME provided") + endif() + if(NOT LIBRARY_SOURCES) + message(FATAL_ERROR "no SOURCES provided") + endif() + message(STATUS "Configuring library ${LIBRARY_NAME} ...") + + add_library(${LIBRARY_NAME} ${LIBRARY_TYPE} "") + target_sources(${LIBRARY_NAME} PRIVATE ${LIBRARY_SOURCES}) + target_include_directories(${LIBRARY_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + target_compile_definitions(${LIBRARY_NAME} PRIVATE ${LIBRARY_COMPILE_DEFINITIONS}) + target_compile_features(${LIBRARY_NAME} PRIVATE cxx_std_17) + target_compile_options(${LIBRARY_NAME} PRIVATE ${LIBRARY_COMPILE_OPTIONS}) + target_link_libraries(${LIBRARY_NAME} PUBLIC + ${PROJECT_NAMESPACE}::ortools + ${LIBRARY_LINK_LIBRARIES} + ) + target_link_options(${LIBRARY_NAME} PRIVATE ${LIBRARY_LINK_OPTIONS}) + + include(GNUInstallDirs) + if(APPLE) + set_target_properties(${LIBRARY_NAME} PROPERTIES + INSTALL_RPATH "@loader_path/../${CMAKE_INSTALL_LIBDIR};@loader_path") + elseif(UNIX) + cmake_path(RELATIVE_PATH CMAKE_INSTALL_FULL_LIBDIR + BASE_DIRECTORY ${CMAKE_INSTALL_FULL_BINDIR} + OUTPUT_VARIABLE libdir_relative_path) + set_target_properties(${LIBRARY_NAME} PROPERTIES + INSTALL_RPATH "$ORIGIN/${libdir_relative_path}:$ORIGIN") endif() - message(STATUS "Configuring test ${TEST_FILE_NAME} ...DONE") + add_library(${PROJECT_NAMESPACE}::${LIBRARY_NAME} ALIAS ${LIBRARY_NAME}) + message(STATUS "Configuring library ${LIBRARY_NAME} ...DONE") endfunction() ################## @@ -460,11 +543,11 @@ target_link_libraries(${PROJECT_NAME} PUBLIC protobuf::libprotobuf ${RE2_DEPS} ${COINOR_DEPS} - $<$:CPLEX::CPLEX> - $<$:GLPK::GLPK> - $<$:highs::highs> + ${CPLEX_DEPS} + ${GLPK_DEPS} + ${HIGHS_DEPS} ${PDLP_DEPS} - $<$:libscip> + ${SCIP_DEPS} Threads::Threads) if(WIN32) target_link_libraries(${PROJECT_NAME} PUBLIC psapi.lib ws2_32.lib) diff --git a/cmake/dependencies/CMakeLists.txt b/cmake/dependencies/CMakeLists.txt index 7790c073aee..3cb8a62db52 100644 --- a/cmake/dependencies/CMakeLists.txt +++ b/cmake/dependencies/CMakeLists.txt @@ -129,7 +129,7 @@ if(BUILD_re2) GIT_REPOSITORY "https://github.com/google/re2.git" GIT_TAG "2024-04-01" GIT_SHALLOW TRUE - #PATCH_COMMAND git apply --ignore-whitespace "${CMAKE_CURRENT_LIST_DIR}/../../patches/re2-2024-04-01.patch" + PATCH_COMMAND git apply --ignore-whitespace "${CMAKE_CURRENT_LIST_DIR}/../../patches/re2-2024-04-01.patch" ) FetchContent_MakeAvailable(re2) list(POP_BACK CMAKE_MESSAGE_INDENT) @@ -300,7 +300,8 @@ if(BUILD_CoinUtils) FetchContent_Declare( CoinUtils GIT_REPOSITORY "https://github.com/Mizux/CoinUtils.git" - GIT_TAG "cmake/2.11.6" + GIT_TAG "stable/2.11" + #GIT_TAG "cmake/2.11.6" GIT_SHALLOW TRUE PATCH_COMMAND git apply --ignore-whitespace "${CMAKE_CURRENT_LIST_DIR}/../../patches/coinutils-2.11.patch") @@ -318,7 +319,8 @@ if(BUILD_Osi) FetchContent_Declare( Osi GIT_REPOSITORY "https://github.com/Mizux/Osi.git" - GIT_TAG "cmake/0.108.7" + GIT_TAG "stable/0.108" + #GIT_TAG "cmake/0.108.7" GIT_SHALLOW TRUE PATCH_COMMAND git apply --ignore-whitespace "${CMAKE_CURRENT_LIST_DIR}/../../patches/osi-0.108.patch") @@ -336,10 +338,11 @@ if(BUILD_Clp) FetchContent_Declare( Clp GIT_REPOSITORY "https://github.com/Mizux/Clp.git" - GIT_TAG "cmake/1.17.7" + GIT_TAG "stable/1.17" + #GIT_TAG "cmake/1.17.7" GIT_SHALLOW TRUE PATCH_COMMAND git apply --ignore-whitespace - "${CMAKE_CURRENT_LIST_DIR}/../../patches/clp-1.17.4.patch") + "${CMAKE_CURRENT_LIST_DIR}/../../patches/clp-1.17.patch") FetchContent_MakeAvailable(Clp) list(POP_BACK CMAKE_MESSAGE_INDENT) message(CHECK_PASS "fetched") @@ -354,7 +357,8 @@ if(BUILD_Cgl) FetchContent_Declare( Cgl GIT_REPOSITORY "https://github.com/Mizux/Cgl.git" - GIT_TAG "cmake/0.60.5" + GIT_TAG "stable/0.60" + #GIT_TAG "cmake/0.60.5" GIT_SHALLOW TRUE PATCH_COMMAND git apply --ignore-whitespace "${CMAKE_CURRENT_LIST_DIR}/../../patches/cgl-0.60.patch") @@ -372,7 +376,8 @@ if(BUILD_Cbc) FetchContent_Declare( Cbc GIT_REPOSITORY "https://github.com/Mizux/Cbc.git" - GIT_TAG "cmake/2.10.7" + GIT_TAG "stable/2.10" + #GIT_TAG "cmake/2.10.7" GIT_SHALLOW TRUE PATCH_COMMAND git apply --ignore-whitespace "${CMAKE_CURRENT_LIST_DIR}/../../patches/cbc-2.10.patch") @@ -398,6 +403,8 @@ if(BUILD_googletest) GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG v1.15.2 GIT_SHALLOW TRUE + PATCH_COMMAND git apply --ignore-whitespace + "${CMAKE_CURRENT_LIST_DIR}/../../patches/googletest-v1.15.2.patch" #PATCH_COMMAND git apply --ignore-whitespace "" ) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) diff --git a/cmake/python.cmake b/cmake/python.cmake index b2e5731427e..9f4201e4a36 100644 --- a/cmake/python.cmake +++ b/cmake/python.cmake @@ -463,9 +463,11 @@ add_custom_command( $ ${PYTHON_PROJECT}/linear_solver COMMAND ${CMAKE_COMMAND} -E copy $ ${PYTHON_PROJECT}/linear_solver/python - COMMAND ${CMAKE_COMMAND} -E copy + COMMAND ${CMAKE_COMMAND} -E + $,copy,true> $ ${PYTHON_PROJECT}/math_opt/core/python - COMMAND ${CMAKE_COMMAND} -E copy + COMMAND ${CMAKE_COMMAND} -E + $,copy,true> $ ${PYTHON_PROJECT}/../pybind11_abseil COMMAND ${CMAKE_COMMAND} -E $,copy,true> diff --git a/examples/tests/CMakeLists.txt b/examples/tests/CMakeLists.txt index 5d49f347e90..9ba53952a17 100644 --- a/examples/tests/CMakeLists.txt +++ b/examples/tests/CMakeLists.txt @@ -17,8 +17,15 @@ endif() if(BUILD_CXX_EXAMPLES) file(GLOB CXX_SRCS "*.cc") - foreach(FILE_NAME IN LISTS CXX_SRCS) - ortools_cxx_test(FILE_NAME ${FILE_NAME}) + foreach(_FULL_FILE_NAME IN LISTS CXX_SRCS) + get_filename_component(_NAME ${_FULL_FILE_NAME} NAME_WE) + get_filename_component(_FILE_NAME ${_FULL_FILE_NAME} NAME) + ortools_cxx_test( + NAME + tests_${_NAME} + SOURCES + ${_FULL_FILE_NAME} + ) endforeach() endif() diff --git a/ortools/algorithms/BUILD.bazel b/ortools/algorithms/BUILD.bazel index 3d7f2284f3f..970599bb977 100644 --- a/ortools/algorithms/BUILD.bazel +++ b/ortools/algorithms/BUILD.bazel @@ -385,6 +385,15 @@ cc_library( ], ) +cc_test( + name = "dense_doubly_linked_list_test", + srcs = ["dense_doubly_linked_list_test.cc"], + deps = [ + ":dense_doubly_linked_list", + "//ortools/base:gmock_main", + ], +) + cc_library( name = "dynamic_partition", srcs = ["dynamic_partition.cc"], diff --git a/ortools/algorithms/CMakeLists.txt b/ortools/algorithms/CMakeLists.txt index 8f23481d2fb..8419297312a 100644 --- a/ortools/algorithms/CMakeLists.txt +++ b/ortools/algorithms/CMakeLists.txt @@ -12,7 +12,7 @@ # limitations under the License. file(GLOB _SRCS "*.h" "*.cc") -list(FILTER _SRCS EXCLUDE REGEX "/[^/]*_test\\.cc$") +list(FILTER _SRCS EXCLUDE REGEX ".*/.*_test.cc") set(NAME ${PROJECT_NAME}_algorithms) @@ -31,3 +31,23 @@ target_link_libraries(${NAME} PRIVATE protobuf::libprotobuf ${PROJECT_NAMESPACE}::ortools_proto) #add_library(${PROJECT_NAMESPACE}::algorithms ALIAS ${NAME}) + +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) + ortools_cxx_test( + NAME + algorithms_${_NAME} + SOURCES + ${_FILE_NAME} + LINK_LIBRARIES + benchmark::benchmark + GTest::gmock + GTest::gtest_main + ) + endforeach() +endif() diff --git a/ortools/algorithms/duplicate_remover_test.cc b/ortools/algorithms/duplicate_remover_test.cc deleted file mode 100644 index b7c738b9383..00000000000 --- a/ortools/algorithms/duplicate_remover_test.cc +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2010-2024 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/algorithms/duplicate_remover.h" - -#include -#include - -#include "benchmark/benchmark.h" -#include "gtest/gtest.h" -#include "ortools/base/gmock.h" -#include "ortools/base/linked_hash_set.h" -#include "ortools/util/random_engine.h" -#include "util/tuple/dump_vars.h" - -namespace operations_research { -namespace { - -using ::testing::ElementsAre; -using ::testing::ElementsAreArray; -using ::testing::IsEmpty; - -TEST(DenseIntDuplicateRemoverTest, RemoveDuplicatesEmpty) { - std::vector v; - DenseIntDuplicateRemover deduper(10); - deduper.RemoveDuplicates(&v); - EXPECT_THAT(v, IsEmpty()); -} - -TEST(DenseIntDuplicateRemoverTest, RemoveDuplicatesNZeroAndEmpty) { - std::vector v; - DenseIntDuplicateRemover deduper(0); - deduper.RemoveDuplicates(&v); - EXPECT_THAT(v, IsEmpty()); -} - -TEST(DenseIntDuplicateRemoverTest, RemoveDuplicatesSimpleCaseWithDuplicates) { - std::vector v = {1, 8, 2, 2, 8, 4, 1, 2, 7, 0, 2}; - DenseIntDuplicateRemover deduper(9); - deduper.RemoveDuplicates(&v); - EXPECT_THAT(v, ElementsAre(1, 8, 2, 4, 7, 0)); -} - -TEST(DenseIntDuplicateRemoverTest, RemoveDuplicatesSimpleCaseWithNoDuplicates) { - std::vector v = {3, 2, 0, 5, 4, 1}; - const std::vector v_copy = v; - DenseIntDuplicateRemover deduper(6); - deduper.RemoveDuplicates(&v); - EXPECT_THAT(v, ElementsAreArray(v_copy)); -} - -TEST(DenseIntDuplicateRemoverTest, RemoveDuplicatesWithRepeatedField) { - const std::vector v = {1, 0, 1, 2, 1}; - google::protobuf::RepeatedField r(v.begin(), v.end()); - DenseIntDuplicateRemover deduper(3); - deduper.RemoveDuplicates(&r); - EXPECT_THAT(r, ElementsAre(1, 0, 2)); -} - -std::vector UniqueValues(absl::Span span) { - absl::flat_hash_set set; - std::vector out; - for (int x : span) - if (set.insert(x).second) out.push_back(x); - return out; -} - -TEST(DenseIntDuplicateRemoverTest, RemoveDuplicatesRandomizedStressTest) { - constexpr int kNumValues = 1003; - DenseIntDuplicateRemover deduper(kNumValues); - constexpr int kNumTests = 1'000'000; - absl::BitGen random; - for (int t = 0; t < kNumTests; ++t) { - const int size = absl::LogUniform(random, 0, 16); - const int domain_size = - absl::Uniform(absl::IntervalClosed, random, 1, kNumValues); - std::vector v(size); - for (int& x : v) x = absl::Uniform(random, 0, domain_size); - const std::vector v_initial = v; - const std::vector unique_values = UniqueValues(v); - deduper.RemoveDuplicates(&v); - ASSERT_THAT(v, ElementsAreArray(unique_values)) << DUMP_VARS(t, v_initial); - } -} - -TEST(DenseIntDuplicateRemoverTest, - AppendAndLazilyRemoveDuplicatesRandomizedStressTest) { - constexpr int kNumValues = 103; - constexpr int kNumTests = 1'000; - std::mt19937 random; - gtl::linked_hash_set reference; - std::vector v; - int64_t num_extra_elements = 0; - int64_t num_unique_elements = 0; - for (int t = 0; t < kNumTests; ++t) { - const int num_inserts = absl::LogUniform(random, 2, 1 << 16); - const int domain_size = - absl::Uniform(absl::IntervalClosed, random, 1, kNumValues); - v.clear(); - reference.clear(); - DenseIntDuplicateRemover deduper(domain_size); - for (int i = 0; i < num_inserts; ++i) { - const int x = absl::Uniform(random, 0, domain_size); - deduper.AppendAndLazilyRemoveDuplicates(x, &v); - reference.insert(x); - } - ASSERT_LE(v.size(), domain_size * 2 + 15); - const int old_size = v.size(); - deduper.RemoveDuplicates(&v); - num_unique_elements += v.size(); - num_extra_elements += old_size - v.size(); - ASSERT_THAT(v, ElementsAreArray(reference)) - << DUMP_VARS(t, num_inserts, domain_size, old_size, v.size()); - } - EXPECT_LE(static_cast(num_extra_elements) / num_unique_elements, 0.5); -} - -template -void BM_AppendAndLazilyRemoveDuplicates(benchmark::State& state) { - const int num_inserts = state.range(0); - const int domain_size = state.range(1); - std::vector to_insert(num_inserts); - random_engine_t random; - for (int& x : to_insert) x = absl::Uniform(random, 0, domain_size); - DenseIntDuplicateRemover deduper(domain_size); - std::vector v; - absl::flat_hash_set set; - for (auto _ : state) { - v.clear(); - set.clear(); - for (int x : to_insert) { - if (use_flat_hash_set) { - set.insert(x); - } else { - deduper.AppendAndLazilyRemoveDuplicates(x, &v); - } - } - if (!use_flat_hash_set) deduper.RemoveDuplicates(&v); - benchmark::DoNotOptimize(v); - benchmark::DoNotOptimize(set); - } - state.SetItemsProcessed(state.iterations() * num_inserts); -} - -BENCHMARK(BM_AppendAndLazilyRemoveDuplicates) - ->ArgPair(1, 10) - ->ArgPair(10, 2) - ->ArgPair(10, 10) - ->ArgPair(100, 100) - ->ArgPair(100, 10) - ->ArgPair(10'000, 10'000) - ->ArgPair(10'000, 1'000) - ->ArgPair(10'000, 100) - ->ArgPair(10'000, 10) - ->ArgPair(1'000'000, 1'000'000) - ->ArgPair(1'000'000, 10'000) - ->ArgPair(1'000'000, 100); - -BENCHMARK(BM_AppendAndLazilyRemoveDuplicates) - ->ArgPair(1, 10) - ->ArgPair(10, 2) - ->ArgPair(10, 10) - ->ArgPair(100, 100) - ->ArgPair(100, 10) - ->ArgPair(10'000, 10'000) - ->ArgPair(10'000, 1'000) - ->ArgPair(10'000, 100) - ->ArgPair(10'000, 10) - ->ArgPair(1'000'000, 1'000'000) - ->ArgPair(1'000'000, 10'000) - ->ArgPair(1'000'000, 100); - -} // namespace -} // namespace operations_research diff --git a/ortools/algorithms/set_cover_orlib_test.cc b/ortools/algorithms/set_cover_orlib_test.cc deleted file mode 100644 index 849e2b76add..00000000000 --- a/ortools/algorithms/set_cover_orlib_test.cc +++ /dev/null @@ -1,428 +0,0 @@ -// Copyright 2010-2024 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 -#include -#include - -#include "absl/log/check.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_join.h" -#include "absl/time/time.h" -#include "gtest/gtest.h" -#include "ortools/algorithms/set_cover_heuristics.h" -#include "ortools/algorithms/set_cover_invariant.h" -#include "ortools/algorithms/set_cover_lagrangian.h" -#include "ortools/algorithms/set_cover_mip.h" -#include "ortools/algorithms/set_cover_model.h" -#include "ortools/algorithms/set_cover_reader.h" -#include "ortools/base/logging.h" -#include "ortools/base/path.h" -#include "ortools/base/timer.h" - -namespace operations_research { - -void LogStats(std::string name, SetCoverModel* model) { - LOG(INFO) << ", " << name << ", num_elements, " << model->num_elements() - << ", num_subsets, " << model->num_subsets(); - LOG(INFO) << ", " << name << ", num_nonzeros, " << model->num_nonzeros() - << ", fill rate, " << model->FillRate(); - LOG(INFO) << ", " << name << ", cost, " - << model->ComputeCostStats().DebugString(); - - LOG(INFO) << ", " << name << ", num_rows, " << model->num_elements() - << ", rows sizes, " << model->ComputeRowStats().DebugString(); - LOG(INFO) << ", " << name << ", row size deciles, " - << absl::StrJoin(model->ComputeRowDeciles(), ", "); - LOG(INFO) << ", " << name << ", num_columns, " << model->num_subsets() - << ", columns sizes, " << model->ComputeColumnStats().DebugString(); - LOG(INFO) << ", " << name << ", column size deciles, " - << absl::StrJoin(model->ComputeColumnDeciles(), ", "); - SetCoverInvariant inv(model); - Preprocessor preprocessor(&inv); - preprocessor.NextSolution(); - LOG(INFO) << ", " << name << ", num_columns_fixed_by_singleton_row, " - << preprocessor.num_columns_fixed_by_singleton_row(); -} - -void LogCostAndTiming(std::string name, std::string algo, double cost, - absl::Duration duration) { - LOG(INFO) << ", " << name << ", " << algo << "_cost, " << cost << ", " - << absl::ToInt64Microseconds(duration) << "e-6, s"; -} - -SetCoverInvariant RunChvatalAndSteepest(std::string name, - SetCoverModel* model) { - SetCoverInvariant inv(model); - GreedySolutionGenerator greedy(&inv); - WallTimer timer; - timer.Start(); - CHECK(greedy.NextSolution()); - DCHECK(inv.CheckConsistency()); - LogCostAndTiming(name, "GreedySolutionGenerator", inv.cost(), - timer.GetDuration()); - SteepestSearch steepest(&inv); - steepest.NextSolution(100000); - LogCostAndTiming(name, "GreedySteepestSearch", inv.cost(), - timer.GetDuration()); - DCHECK(inv.CheckConsistency()); - return inv; -} - -SetCoverInvariant RunChvatalAndGLS(std::string name, SetCoverModel* model) { - SetCoverInvariant inv(model); - GreedySolutionGenerator greedy(&inv); - WallTimer timer; - timer.Start(); - CHECK(greedy.NextSolution()); - DCHECK(inv.CheckConsistency()); - LogCostAndTiming(name, "GreedySolutionGenerator", inv.cost(), - timer.GetDuration()); - GuidedLocalSearch gls(&inv); - gls.NextSolution(100'000); - LogCostAndTiming(name, "GLS", inv.cost(), timer.GetDuration()); - DCHECK(inv.CheckConsistency()); - return inv; -} - -SetCoverInvariant RunElementDegreeGreedyAndSteepest(std::string name, - SetCoverModel* model) { - SetCoverInvariant inv(model); - ElementDegreeSolutionGenerator element_degree(&inv); - WallTimer timer; - timer.Start(); - CHECK(element_degree.NextSolution()); - DCHECK(inv.CheckConsistency()); - LogCostAndTiming(name, "ElementDegreeSolutionGenerator", inv.cost(), - timer.GetDuration()); - SteepestSearch steepest(&inv); - steepest.NextSolution(100000); - LogCostAndTiming(name, "ElementDegreeSteepestSearch", inv.cost(), - timer.GetDuration()); - DCHECK(inv.CheckConsistency()); - return inv; -} - -void IterateClearAndMip(std::string name, SetCoverInvariant* inv) { - WallTimer timer; - timer.Start(); - std::vector focus = inv->model()->all_subsets(); - double best_cost = inv->cost(); - SubsetBoolVector best_choices = inv->is_selected(); - for (int i = 0; i < 10; ++i) { - std::vector range = - ClearMostCoveredElements(std::min(100UL, focus.size()), inv); - SetCoverMip mip(inv); - mip.NextSolution(range, true, 0.02); - DCHECK(inv->CheckConsistency()); - if (inv->cost() < best_cost) { - best_cost = inv->cost(); - best_choices = inv->is_selected(); - } - } - timer.Stop(); - LogCostAndTiming(name, "IterateClearAndMip", best_cost, timer.GetDuration()); -} - -SetCoverInvariant ComputeLPLowerBound(std::string name, SetCoverModel* model) { - SetCoverInvariant inv(model); - WallTimer timer; - timer.Start(); - SetCoverMip mip(&inv, SetCoverMipSolver::SCIP); // Use Gurobi for large pbs. - mip.NextSolution(false, .3); // Use 300s or more for large problems. - LogCostAndTiming(name, "LPLowerBound", mip.lower_bound(), - timer.GetDuration()); - return inv; -} - -void ComputeLagrangianLowerBound(std::string name, SetCoverInvariant* inv) { - const SetCoverModel* model = inv->model(); - WallTimer timer; - timer.Start(); - SetCoverLagrangian lagrangian(inv, /*num_threads=*/8); - const auto [lower_bound, reduced_costs, multipliers] = - lagrangian.ComputeLowerBound(model->subset_costs(), inv->cost()); - LogCostAndTiming(name, "LagrangianLowerBound", lower_bound, - timer.GetDuration()); -} - -SetCoverInvariant RunMip(std::string name, SetCoverModel* model) { - SetCoverInvariant inv(model); - WallTimer timer; - timer.Start(); - SetCoverMip mip(&inv, SetCoverMipSolver::SCIP); // Use Gurobi for large pbs. - mip.NextSolution(true, .5); // Use 300s or more for large problems. - timer.Stop(); - LogCostAndTiming(name, "MIP", inv.cost(), timer.GetDuration()); - return inv; -} - -void IterateClearElementDegreeAndSteepest(std::string name, - SetCoverInvariant* inv) { - WallTimer timer; - timer.Start(); - double best_cost = inv->cost(); - SubsetBoolVector best_choices = inv->is_selected(); - ElementDegreeSolutionGenerator element_degree(inv); - SteepestSearch steepest(inv); - for (int i = 0; i < 1000; ++i) { - std::vector range = - ClearRandomSubsets(0.1 * inv->trace().size(), inv); - CHECK(element_degree.NextSolution()); - steepest.NextSolution(range, 100000); - DCHECK(inv->CheckConsistency()); - if (inv->cost() < best_cost) { - best_cost = inv->cost(); - best_choices = inv->is_selected(); - } - } - timer.Stop(); - LogCostAndTiming(name, "IterateClearElementDegreeAndSteepest", best_cost, - timer.GetDuration()); -} - -double RunSolver(std::string name, SetCoverModel* model) { - LogStats(name, model); - WallTimer global_timer; - global_timer.Start(); - RunChvatalAndSteepest(name, model); - // SetCoverInvariant inv = ComputeLPLowerBound(name, model); - // RunMip(name, model); - RunChvatalAndGLS(name, model); - SetCoverInvariant inv = RunElementDegreeGreedyAndSteepest(name, model); - ComputeLagrangianLowerBound(name, &inv); - // IterateClearAndMip(name, inv); - IterateClearElementDegreeAndSteepest(name, &inv); - return inv.cost(); -} - -// We break down the ORLIB set covering problems by their expected runtime with -// our solver (as of July 2023). -enum ProblemSize { - SUBMILLI, // < 1ms - FEWMILLIS, // < 3ms - SUBHUNDREDTH, // < 10ms - FEWHUNDREDTHS, // < 30ms - SUBTENTH, // < 100ms - FEWTENTHS, // < 300ms - SUBSECOND, // < 1s - FEWSECONDS, // < 3s - MANYSECONDS, // >= 3s - UNKNOWN = 999, // Not known (i.e. not benchmarked). -}; - -// These two macros provide indirection which allows the __LINE__ macro -// to be pasted, giving the tests useful names. -#define APPEND(x, y) x##y -#define APPEND_AND_EVAL(x, y) APPEND(x, y) - -const char data_dir[] = - "operations_research_data/operations_research_data/" - "SET_COVERING"; - -// In the following, the lower bounds are taken from: -// [1] Caprara, Alberto, Matteo Fischetti, and Paolo Toth. 1999. “A Heuristic -// Method for the Set Covering Problem.” Operations Research 47 (5): 730–43. -// https://www.jstor.org/stable/223097 , and -// [2] Yagiura, Mutsunori, Masahiro Kishida, and Toshihide Ibaraki. 2006. -// “A 3-Flip Neighborhood Local Search for the Set Covering Problem.” European -// Journal of Operational Research 172 (2): 472–99. -// https://www.sciencedirect.com/science/article/pii/S0377221704008264 - -// This macro makes it possible to declare each test below with a one-liner. -// 'best_objective' denotes the best objective costs found in literature. -// These are the proven optimal values. This can be achieved with MIP. -// For the rail instances, they are the best solution found in the literature -// [1] and [2]. They are not achievable though local search or MIP or a -// combination of the two. -// 'expected_objective' are the costs currently reached by the solver. -// TODO(user): find and add values for the unit cost (aka unicost) case. - -#define ORLIB_TEST(name, best_objective, expected_objective, size, function) \ - TEST(OrlibTest, APPEND_AND_EVAL(TestOnLine, __LINE__)) { \ - auto filespec = \ - file::JoinPathRespectAbsolute(::testing::SrcDir(), data_dir, name); \ - LOG(INFO) << "Reading " << name; \ - operations_research::SetCoverModel model = function(filespec); \ - double cost = RunSolver(name, &model); \ - (void)cost; \ - } - -#define ORLIB_UNICOST_TEST(name, best_objective, expected_objective, size, \ - function) \ - TEST(OrlibUnicostTest, APPEND_AND_EVAL(TestOnLine, __LINE__)) { \ - auto filespec = \ - file::JoinPathRespectAbsolute(::testing::SrcDir(), data_dir, name); \ - LOG(INFO) << "Reading " << name; \ - operations_research::SetCoverModel model = function(filespec); \ - for (SubsetIndex i : model.SubsetRange()) { \ - model.SetSubsetCost(i, 1.0); \ - } \ - double cost = RunSolver(absl::StrCat(name, "_unicost"), &model); \ - (void)cost; \ - } - -#define SCP_TEST(name, best_objective, expected_objective, size) \ - ORLIB_TEST(name, best_objective, expected_objective, size, \ - operations_research::ReadBeasleySetCoverProblem) \ - ORLIB_UNICOST_TEST(name, best_objective, expected_objective, size, \ - operations_research::ReadBeasleySetCoverProblem) - -#define RAIL_TEST(name, best_objective, expected_objective, size) \ - ORLIB_TEST(name, best_objective, expected_objective, size, \ - operations_research::ReadRailSetCoverProblem) \ - ORLIB_UNICOST_TEST(name, best_objective, expected_objective, size, \ - operations_research::ReadRailSetCoverProblem) - -#define BASIC_SCP -#define EXTRA_SCP -#define RAIL - -#ifdef BASIC_SCP -SCP_TEST("scp41.txt", 429, 442, FEWMILLIS); -SCP_TEST("scp42.txt", 512, 555, FEWMILLIS); -SCP_TEST("scp43.txt", 516, 557, FEWMILLIS); -SCP_TEST("scp44.txt", 494, 516, FEWMILLIS); -SCP_TEST("scp45.txt", 512, 530, FEWMILLIS); -SCP_TEST("scp46.txt", 560, 594, FEWMILLIS); -SCP_TEST("scp47.txt", 430, 451, FEWMILLIS); -SCP_TEST("scp48.txt", 492, 502, FEWMILLIS); -SCP_TEST("scp49.txt", 641, 693, FEWMILLIS); -SCP_TEST("scp410.txt", 514, 525, FEWMILLIS); - -SCP_TEST("scp51.txt", 253, 274, FEWMILLIS); -SCP_TEST("scp52.txt", 302, 329, FEWMILLIS); -SCP_TEST("scp53.txt", 226, 233, FEWMILLIS); -SCP_TEST("scp54.txt", 242, 255, FEWMILLIS); -SCP_TEST("scp55.txt", 211, 222, FEWMILLIS); -SCP_TEST("scp56.txt", 213, 234, FEWMILLIS); -SCP_TEST("scp57.txt", 293, 313, FEWMILLIS); -SCP_TEST("scp58.txt", 288, 309, FEWMILLIS); -SCP_TEST("scp59.txt", 279, 292, FEWMILLIS); -SCP_TEST("scp510.txt", 265, 276, FEWMILLIS); - -SCP_TEST("scp61.txt", 138, 151, FEWMILLIS); -SCP_TEST("scp62.txt", 146, 173, FEWMILLIS); -SCP_TEST("scp63.txt", 145, 154, FEWMILLIS); -SCP_TEST("scp64.txt", 131, 137, FEWMILLIS); -SCP_TEST("scp65.txt", 161, 181, FEWMILLIS); - -SCP_TEST("scpa1.txt", 253, 275, FEWHUNDREDTHS); -SCP_TEST("scpa2.txt", 252, 268, FEWHUNDREDTHS); -SCP_TEST("scpa3.txt", 232, 244, FEWHUNDREDTHS); -SCP_TEST("scpa4.txt", 234, 253, FEWHUNDREDTHS); -SCP_TEST("scpa5.txt", 236, 249, FEWHUNDREDTHS); - -SCP_TEST("scpb1.txt", 69, 74, FEWTENTHS); -SCP_TEST("scpb2.txt", 76, 78, FEWTENTHS); -SCP_TEST("scpb3.txt", 80, 85, FEWTENTHS); -SCP_TEST("scpb4.txt", 79, 85, FEWTENTHS); -SCP_TEST("scpb5.txt", 72, 77, FEWTENTHS); - -SCP_TEST("scpc1.txt", 227, 251, FEWHUNDREDTHS); -SCP_TEST("scpc2.txt", 219, 238, FEWHUNDREDTHS); -SCP_TEST("scpc3.txt", 243, 259, FEWHUNDREDTHS); -SCP_TEST("scpc4.txt", 219, 246, FEWHUNDREDTHS); -SCP_TEST("scpc5.txt", 214, 228, FEWHUNDREDTHS); - -SCP_TEST("scpd1.txt", 60, 68, FEWHUNDREDTHS); -SCP_TEST("scpd2.txt", 66, 70, FEWHUNDREDTHS); -SCP_TEST("scpd3.txt", 72, 78, FEWHUNDREDTHS); -SCP_TEST("scpd4.txt", 62, 67, FEWHUNDREDTHS); -SCP_TEST("scpd5.txt", 61, 72, FEWHUNDREDTHS); - -SCP_TEST("scpe1.txt", 5, 5, FEWMILLIS); -SCP_TEST("scpe2.txt", 5, 6, FEWMILLIS); -SCP_TEST("scpe3.txt", 5, 5, FEWMILLIS); -SCP_TEST("scpe4.txt", 5, 6, FEWMILLIS); -SCP_TEST("scpe5.txt", 5, 5, FEWMILLIS); - -SCP_TEST("scpnre1.txt", 29, 31, SUBTENTH); -SCP_TEST("scpnre2.txt", 30, 34, SUBTENTH); -SCP_TEST("scpnre3.txt", 27, 32, SUBTENTH); -SCP_TEST("scpnre4.txt", 28, 32, SUBTENTH); -SCP_TEST("scpnre5.txt", 28, 31, SUBTENTH); - -SCP_TEST("scpnrf1.txt", 14, 17, SUBTENTH); -SCP_TEST("scpnrf2.txt", 15, 16, SUBTENTH); -SCP_TEST("scpnrf3.txt", 14, 16, SUBTENTH); -SCP_TEST("scpnrf4.txt", 14, 15, SUBTENTH); -SCP_TEST("scpnrf5.txt", 13, 15, SUBTENTH); - -SCP_TEST("scpnrg1.txt", 176, 196, SUBTENTH); -SCP_TEST("scpnrg2.txt", 154, 171, SUBTENTH); -SCP_TEST("scpnrg3.txt", 166, 182, SUBTENTH); -SCP_TEST("scpnrg4.txt", 168, 187, SUBTENTH); -SCP_TEST("scpnrg5.txt", 168, 183, SUBTENTH); - -SCP_TEST("scpnrh1.txt", 63, 71, FEWTENTHS); -SCP_TEST("scpnrh2.txt", 63, 70, FEWTENTHS); -SCP_TEST("scpnrh3.txt", 59, 65, FEWTENTHS); -SCP_TEST("scpnrh4.txt", 58, 66, FEWTENTHS); -SCP_TEST("scpnrh5.txt", 55, 62, FEWTENTHS); -#endif - -#ifdef EXTRA_SCP -SCP_TEST("scpclr10.txt", 0, 32, FEWMILLIS); -SCP_TEST("scpclr11.txt", 0, 30, FEWMILLIS); -SCP_TEST("scpclr12.txt", 0, 31, FEWMILLIS); -SCP_TEST("scpclr13.txt", 0, 33, FEWMILLIS); - -SCP_TEST("scpcyc06.txt", 0, 60, FEWMILLIS); -SCP_TEST("scpcyc07.txt", 0, 144, FEWMILLIS); -SCP_TEST("scpcyc08.txt", 0, 360, FEWMILLIS); -SCP_TEST("scpcyc09.txt", 0, 816, SUBHUNDREDTH); -SCP_TEST("scpcyc10.txt", 0, 1920, FEWHUNDREDTHS); -SCP_TEST("scpcyc11.txt", 0, 4284, SUBTENTH); -#endif - -#ifdef RAIL -RAIL_TEST("rail507.txt", 174, 218, FEWTENTHS); -RAIL_TEST("rail516.txt", 182, 204, FEWTENTHS); -RAIL_TEST("rail582.txt", 211, 250, FEWTENTHS); -RAIL_TEST("rail2536.txt", 691, 889, MANYSECONDS); -RAIL_TEST("rail2586.txt", 952, 1139, MANYSECONDS); -RAIL_TEST("rail4284.txt", 1065, 1362, MANYSECONDS); -RAIL_TEST("rail4872.txt", 1527, 1861, MANYSECONDS); // [2] -#endif - -#undef BASIC_SCP -#undef EXTRA_SCP -#undef RAIL - -#undef ORLIB_TEST -#undef ORLIB_UNICOST_TEST -#undef APPEND -#undef APPEND_AND_EVAL -#undef SCP_TEST -#undef RAIL_TEST - -TEST(SetCoverHugeTest, GenerateProblem) { - SetCoverModel seed_model = - ReadRailSetCoverProblem(file::JoinPathRespectAbsolute( - ::testing::SrcDir(), data_dir, "rail4284.txt")); - seed_model.CreateSparseRowView(); - const BaseInt num_wanted_subsets(100'000'000); - const BaseInt num_wanted_elements(40'000); - const double row_scale = 1.1; - const double column_scale = 1.1; - const double cost_scale = 10.0; - SetCoverModel model = SetCoverModel::GenerateRandomModelFrom( - seed_model, num_wanted_elements, num_wanted_subsets, row_scale, - column_scale, cost_scale); - SetCoverInvariant inv = - RunElementDegreeGreedyAndSteepest("rail4284_huge.txt", &model); - LOG(INFO) << "Cost: " << inv.cost(); -} - -} // namespace operations_research diff --git a/ortools/glop/revised_simplex.h b/ortools/glop/revised_simplex.h index 90e55698e4a..8b2ec798da5 100644 --- a/ortools/glop/revised_simplex.h +++ b/ortools/glop/revised_simplex.h @@ -187,6 +187,10 @@ class RevisedSimplex { double DeterministicTime() const; bool objective_limit_reached() const { return objective_limit_reached_; } + DenseColumn::ConstView GetDualSquaredNorms() { + return dual_edge_norms_.GetEdgeSquaredNorms(); + } + const DenseBitRow& GetNotBasicBitRow() const { return variables_info_.GetNotBasicBitRow(); } diff --git a/ortools/graph/CMakeLists.txt b/ortools/graph/CMakeLists.txt index f76659bd0ce..75bd09582df 100644 --- a/ortools/graph/CMakeLists.txt +++ b/ortools/graph/CMakeLists.txt @@ -39,3 +39,34 @@ target_link_libraries(${NAME} PRIVATE ${PROJECT_NAMESPACE}::ortools_proto $<$:Coin::Cbc>) #add_library(${PROJECT_NAMESPACE}::graph ALIAS ${NAME}) + +if(BUILD_TESTING) + file(GLOB _TEST_SRCS "*_test.cc") + list(FILTER _TEST_SRCS EXCLUDE REGEX "max_flow_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 + graph_${_NAME} + SOURCES + ${_FILE_NAME} + LINK_LIBRARIES + benchmark::benchmark + GTest::gmock + GTest::gtest_main + ) + endforeach() + ortools_cxx_test( + NAME + graph_max_flow_test + SOURCES + "max_flow_test.cc" + LINK_LIBRARIES + benchmark::benchmark + GTest::gmock + GTest::gtest_main + COMPILE_DEFINITIONS + -DROOT_DIR="../../" + ) +endif() diff --git a/ortools/graph/max_flow_test.cc b/ortools/graph/max_flow_test.cc index 5b9e0fbcceb..d7af59311a3 100644 --- a/ortools/graph/max_flow_test.cc +++ b/ortools/graph/max_flow_test.cc @@ -36,7 +36,9 @@ #include "ortools/linear_solver/linear_solver.h" #include "ortools/util/file_util.h" +#if not defined(ROOT_DIR) #define ROOT_DIR "com_google_ortools/" +#endif namespace operations_research { namespace { diff --git a/ortools/linear_solver/java/linear_solver.i b/ortools/linear_solver/java/linear_solver.i index 83d9db7e560..cfd1c1add8b 100644 --- a/ortools/linear_solver/java/linear_solver.i +++ b/ortools/linear_solver/java/linear_solver.i @@ -402,7 +402,7 @@ PROTO2_RETURN( %rename (suppressOutput) operations_research::MPSolver::SuppressOutput; // no test %rename (lookupConstraintOrNull) operations_research::MPSolver::LookupConstraintOrNull; // no test %rename (lookupVariableOrNull) operations_research::MPSolver::LookupVariableOrNull; // no test -%rename (write) operations_research::MPSolver::Write; +%rename (write) operations_research::MPSolver::Write; // no test // Expose very advanced parts of the MPSolver API. For expert users only. %rename (computeConstraintActivities) operations_research::MPSolver::ComputeConstraintActivities; diff --git a/ortools/math_opt/CMakeLists.txt b/ortools/math_opt/CMakeLists.txt index 13615e7acc5..83398885258 100644 --- a/ortools/math_opt/CMakeLists.txt +++ b/ortools/math_opt/CMakeLists.txt @@ -20,6 +20,7 @@ add_subdirectory(constraints) add_subdirectory(cpp) add_subdirectory(io) add_subdirectory(labs) +add_subdirectory(solver_tests) add_subdirectory(solvers) add_subdirectory(storage) add_subdirectory(validators) diff --git a/ortools/math_opt/constraints/indicator/CMakeLists.txt b/ortools/math_opt/constraints/indicator/CMakeLists.txt index af47b4cb6ab..5e286eee832 100644 --- a/ortools/math_opt/constraints/indicator/CMakeLists.txt +++ b/ortools/math_opt/constraints/indicator/CMakeLists.txt @@ -26,3 +26,21 @@ target_link_libraries(${NAME} PRIVATE absl::strings ${PROJECT_NAMESPACE}::math_opt_proto) #install(TARGETS ${NAME} EXPORT ${PROJECT_NAME}Targets) + +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 + math_opt_constraints_indicator_${_NAME} + SOURCES + ${_FILE_NAME} + LINK_LIBRARIES + #benchmark::benchmark + GTest::gmock + GTest::gtest_main + ) + endforeach() +endif() diff --git a/ortools/math_opt/constraints/quadratic/CMakeLists.txt b/ortools/math_opt/constraints/quadratic/CMakeLists.txt index 0240bc5296c..a58437c85cb 100644 --- a/ortools/math_opt/constraints/quadratic/CMakeLists.txt +++ b/ortools/math_opt/constraints/quadratic/CMakeLists.txt @@ -25,3 +25,21 @@ target_include_directories(${NAME} PUBLIC target_link_libraries(${NAME} PRIVATE absl::strings ${PROJECT_NAMESPACE}::math_opt_proto) + +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 + math_opt_constraints_quadratic_${_NAME} + SOURCES + ${_FILE_NAME} + LINK_LIBRARIES + #benchmark::benchmark + GTest::gmock + GTest::gtest_main + ) + endforeach() +endif() diff --git a/ortools/math_opt/constraints/second_order_cone/CMakeLists.txt b/ortools/math_opt/constraints/second_order_cone/CMakeLists.txt index c282fe1a7ef..53c8cd9ede8 100644 --- a/ortools/math_opt/constraints/second_order_cone/CMakeLists.txt +++ b/ortools/math_opt/constraints/second_order_cone/CMakeLists.txt @@ -25,3 +25,21 @@ target_include_directories(${NAME} PUBLIC target_link_libraries(${NAME} PRIVATE absl::strings ${PROJECT_NAMESPACE}::math_opt_proto) + +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 + math_opt_constraints_second_order_cone_${_NAME} + SOURCES + ${_FILE_NAME} + LINK_LIBRARIES + #benchmark::benchmark + GTest::gmock + GTest::gtest_main + ) + endforeach() +endif() diff --git a/ortools/math_opt/constraints/sos/CMakeLists.txt b/ortools/math_opt/constraints/sos/CMakeLists.txt index c0b0ff207a3..8d54916edf2 100644 --- a/ortools/math_opt/constraints/sos/CMakeLists.txt +++ b/ortools/math_opt/constraints/sos/CMakeLists.txt @@ -25,3 +25,21 @@ target_include_directories(${NAME} PUBLIC target_link_libraries(${NAME} PRIVATE absl::strings ${PROJECT_NAMESPACE}::math_opt_proto) + +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 + math_opt_constraints_sos_${_NAME} + SOURCES + ${_FILE_NAME} + LINK_LIBRARIES + #benchmark::benchmark + GTest::gmock + GTest::gtest_main + ) + endforeach() +endif() diff --git a/ortools/math_opt/constraints/util/CMakeLists.txt b/ortools/math_opt/constraints/util/CMakeLists.txt index 151755f2841..1c7aa149dcd 100644 --- a/ortools/math_opt/constraints/util/CMakeLists.txt +++ b/ortools/math_opt/constraints/util/CMakeLists.txt @@ -25,3 +25,21 @@ target_include_directories(${NAME} PUBLIC target_link_libraries(${NAME} PRIVATE absl::strings ${PROJECT_NAMESPACE}::math_opt_proto) + +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 + math_opt_constraints_util_${_NAME} + SOURCES + ${_FILE_NAME} + LINK_LIBRARIES + #benchmark::benchmark + GTest::gmock + GTest::gtest_main + ) + endforeach() +endif() diff --git a/ortools/math_opt/cpp/CMakeLists.txt b/ortools/math_opt/cpp/CMakeLists.txt index 9b38d22c55d..a4e3b0e0dd4 100644 --- a/ortools/math_opt/cpp/CMakeLists.txt +++ b/ortools/math_opt/cpp/CMakeLists.txt @@ -25,3 +25,19 @@ target_include_directories(${NAME} PUBLIC target_link_libraries(${NAME} PRIVATE absl::strings ${PROJECT_NAMESPACE}::math_opt_proto) + +ortools_cxx_library( + NAME + math_opt_matchers + SOURCES + "matchers.cc" + "matchers.h" + TYPE + SHARED + LINK_LIBRARIES + absl::log + absl::status + absl::strings + GTest::gmock + TESTING +) diff --git a/ortools/math_opt/samples/cpp/BUILD.bazel b/ortools/math_opt/samples/cpp/BUILD.bazel index 8f0d32c2c0c..c7b4dabff85 100644 --- a/ortools/math_opt/samples/cpp/BUILD.bazel +++ b/ortools/math_opt/samples/cpp/BUILD.bazel @@ -11,8 +11,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -package(default_visibility = ["//ortools/math_opt:__subpackages__"]) - cc_binary( name = "basic_example", srcs = ["basic_example.cc"], diff --git a/ortools/math_opt/solver_tests/CMakeLists.txt b/ortools/math_opt/solver_tests/CMakeLists.txt new file mode 100644 index 00000000000..2b18e7de9a5 --- /dev/null +++ b/ortools/math_opt/solver_tests/CMakeLists.txt @@ -0,0 +1,372 @@ +# Copyright 2010-2024 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(_PREFIX math_opt) + +ortools_cxx_library( + NAME + ${_PREFIX}_base_solver_test + SOURCES + "base_solver_test.cc" + "base_solver_test.h" + TYPE + STATIC + LINK_LIBRARIES + GTest::gmock + absl::log + TESTING +) + +ortools_cxx_library( + NAME + ${_PREFIX}_callback_tests + SOURCES + "callback_tests.cc" + "callback_tests.h" + TYPE + STATIC + LINK_LIBRARIES + GTest::gmock + absl::log + absl::status + absl::strings + ortools::math_opt_matchers + ortools::math_opt_base_solver_test + ortools::math_opt_test_models + TESTING +) + + +ortools_cxx_library( + NAME + ${_PREFIX}_status_tests + SOURCES + "status_tests.cc" + "status_tests.h" + TYPE + STATIC + LINK_LIBRARIES + GTest::gmock + absl::log + ortools::math_opt_matchers + ortools::math_opt_test_models + TESTING +) + +ortools_cxx_library( + NAME + ${_PREFIX}_lp_tests + SOURCES + "lp_tests.cc" + "lp_tests.h" + TYPE + STATIC + LINK_LIBRARIES + GTest::gmock + absl::log + ortools::math_opt_matchers + ortools::math_opt_base_solver_test + TESTING +) + +ortools_cxx_library( + NAME + ${_PREFIX}_lp_incomplete_solve_tests + SOURCES + "lp_incomplete_solve_tests.cc" + "lp_incomplete_solve_tests.h" + TYPE + STATIC + LINK_LIBRARIES + GTest::gmock + absl::log + ortools::math_opt_matchers + ortools::math_opt_test_models + TESTING +) + +ortools_cxx_library( + NAME + ${_PREFIX}_invalid_input_tests + SOURCES + "invalid_input_tests.cc" + "invalid_input_tests.h" + TYPE + STATIC + LINK_LIBRARIES + GTest::gmock + absl::log + ortools::math_opt_base_solver_test + TESTING +) + +# In CMake or-tools is linked with all enable solvers so this test won't work. +#ortools_cxx_test( +# NAME +# ${_PREFIX}_unregistered_solver_test +# SOURCES +# "unregistered_solver_test.cc" +# LINK_LIBRARIES +# GTest::gmock +# GTest::gmock_main +#) + +ortools_cxx_library( + NAME + ${_PREFIX}_mip_tests + SOURCES + "mip_tests.cc" + "mip_tests.h" + TYPE + STATIC + LINK_LIBRARIES + GTest::gmock + absl::log + absl::status + ortools::math_opt_matchers + ortools::math_opt_base_solver_test + TESTING +) + +ortools_cxx_library( + NAME + ${_PREFIX}_ip_model_solve_parameters_tests + SOURCES + "ip_model_solve_parameters_tests.cc" + "ip_model_solve_parameters_tests.h" + TYPE + STATIC + LINK_LIBRARIES + GTest::gmock + absl::log + ortools::math_opt_matchers + ortools::math_opt_base_solver_test + TESTING +) + +ortools_cxx_library( + NAME + ${_PREFIX}_ip_multiple_solutions_tests + SOURCES + "ip_multiple_solutions_tests.cc" + "ip_multiple_solutions_tests.h" + TYPE + STATIC + LINK_LIBRARIES + GTest::gmock + absl::strings + ortools::math_opt_matchers + TESTING +) + +ortools_cxx_library( + NAME + ${_PREFIX}_lp_model_solve_parameters_tests + SOURCES + "lp_model_solve_parameters_tests.cc" + "lp_model_solve_parameters_tests.h" + TYPE + STATIC + LINK_LIBRARIES + GTest::gmock + absl::log + ortools::math_opt_matchers + ortools::math_opt_base_solver_test + TESTING +) + +ortools_cxx_library( + NAME + ${_PREFIX}_lp_parameter_tests + SOURCES + "lp_parameter_tests.cc" + "lp_parameter_tests.h" + TYPE + STATIC + LINK_LIBRARIES + GTest::gmock + absl::log + absl::status + absl::strings + ortools::math_opt_matchers + TESTING +) + +ortools_cxx_library( + NAME + ${_PREFIX}_lp_initial_basis_tests + SOURCES + "lp_initial_basis_tests.cc" + "lp_initial_basis_tests.h" + TYPE + STATIC + LINK_LIBRARIES + GTest::gmock + absl::log + absl::status + ortools::math_opt_base_solver_test + TESTING +) + +ortools_cxx_library( + NAME + ${_PREFIX}_ip_parameter_tests + SOURCES + "ip_parameter_tests.cc" + "ip_parameter_tests.h" + TYPE + STATIC + LINK_LIBRARIES + GTest::gmock + absl::log + ortools::math_opt_matchers + ortools::math_opt_test_models + TESTING +) + +ortools_cxx_library( + NAME + ${_PREFIX}_multi_objective_tests + SOURCES + "multi_objective_tests.cc" + "multi_objective_tests.h" + TYPE + STATIC + LINK_LIBRARIES + GTest::gmock + absl::log + ortools::math_opt_matchers + TESTING +) + +ortools_cxx_library( + NAME + ${_PREFIX}_qp_tests + SOURCES + "qp_tests.cc" + "qp_tests.h" + TYPE + STATIC + LINK_LIBRARIES + GTest::gmock + absl::log + ortools::math_opt_matchers + TESTING +) + +ortools_cxx_library( + NAME + ${_PREFIX}_qc_tests + SOURCES + "qc_tests.cc" + "qc_tests.h" + TYPE + STATIC + LINK_LIBRARIES + GTest::gmock + absl::log + ortools::math_opt_matchers + TESTING +) + +ortools_cxx_library( + NAME + ${_PREFIX}_second_order_cone_tests + SOURCES + "second_order_cone_tests.cc" + "second_order_cone_tests.h" + TYPE + STATIC + LINK_LIBRARIES + GTest::gmock + absl::log + ortools::math_opt_matchers + TESTING +) + +ortools_cxx_library( + NAME + ${_PREFIX}_logical_constraint_tests + SOURCES + "logical_constraint_tests.cc" + "logical_constraint_tests.h" + TYPE + STATIC + LINK_LIBRARIES + GTest::gmock + absl::log + ortools::math_opt_matchers + TESTING +) + +ortools_cxx_library( + NAME + ${_PREFIX}_test_models + SOURCES + "test_models.cc" + "test_models.h" + TYPE + STATIC + LINK_LIBRARIES + absl::log + absl::strings + TESTING +) + +ortools_cxx_test( + NAME + ${_PREFIX}_test_models_test + SOURCES + "test_models_test.cc" + LINK_LIBRARIES + GTest::gmock + GTest::gmock_main + ortools::math_opt_test_models + ortools::math_opt_matchers + #ortools::math_opt_glop_solver + #ortools::math_opt_gscipt_solver +) + +ortools_cxx_library( + NAME + ${_PREFIX}_generic_tests + SOURCES + "generic_tests.cc" + "generic_tests.h" + TYPE + STATIC + LINK_LIBRARIES + GTest::gmock + absl::log + ortools::math_opt_matchers + ortools::math_opt_test_models + TESTING +) + +ortools_cxx_library( + NAME + ${_PREFIX}_infeasible_subsystem_tests + SOURCES + "infeasible_subsystem_tests.cc" + "infeasible_subsystem_tests.h" + TYPE + STATIC + LINK_LIBRARIES + GTest::gmock + absl::log + absl::status + absl::strings + absl::time + ortools::math_opt_matchers + TESTING +) diff --git a/ortools/math_opt/solvers/BUILD.bazel b/ortools/math_opt/solvers/BUILD.bazel index e7e8054a978..023e966a490 100644 --- a/ortools/math_opt/solvers/BUILD.bazel +++ b/ortools/math_opt/solvers/BUILD.bazel @@ -618,6 +618,7 @@ cc_test( "//ortools/math_opt/solver_tests:lp_parameter_tests", "//ortools/math_opt/solver_tests:lp_tests", "//ortools/math_opt/solver_tests:mip_tests", + "//ortools/math_opt/solver_tests:multi_objective_tests", "//ortools/math_opt/solver_tests:status_tests", "//ortools/math_opt/testing:param_name", "@com_google_absl//absl/status", diff --git a/ortools/math_opt/solvers/CMakeLists.txt b/ortools/math_opt/solvers/CMakeLists.txt index a5eac44747a..9cae3b1ea7f 100644 --- a/ortools/math_opt/solvers/CMakeLists.txt +++ b/ortools/math_opt/solvers/CMakeLists.txt @@ -52,3 +52,183 @@ target_link_libraries(${NAME} PRIVATE $<$:Eigen3::Eigen> $<$:libscip> ${PROJECT_NAMESPACE}::math_opt_proto) + +if(USE_SCIP) + ortools_cxx_test( + NAME + math_opt_solvers_gscip_solver_test + SOURCES + "gscip_solver_test.cc" + LINK_LIBRARIES + GTest::gmock + GTest::gmock_main + absl::status + ortools::math_opt_matchers + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + ) +endif() + +if(USE_GLOP) + ortools_cxx_test( + NAME + math_opt_solvers_glop_solver_test + SOURCES + "glop_solver_test.cc" + LINK_LIBRARIES + GTest::gmock + GTest::gmock_main + absl::status + ortools::math_opt_matchers + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + ) +endif() + +ortools_cxx_test( + NAME + math_opt_solvers_cp_sat_solver_test + SOURCES + "cp_sat_solver_test.cc" + LINK_LIBRARIES + GTest::gmock + GTest::gmock_main + absl::status + ortools::math_opt_matchers + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" +) + +ortools_cxx_test( + NAME + math_opt_solvers_message_callback_data_test + SOURCES + "message_callback_data_test.cc" + LINK_LIBRARIES + GTest::gmock_main + absl::cleanup + absl::synchronization +) + +if(USE_PDLP) + ortools_cxx_test( + NAME + math_opt_solvers_pdlp_solver_test + SOURCES + "pdlp_solver_test.cc" + LINK_LIBRARIES + GTest::gmock + GTest::gmock_main + absl::status + "$" + "$" + "$" + "$" + "$" + "$" + #"$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + ) +endif() + +if(USE_GLPK) + ortools_cxx_test( + NAME + math_opt_solvers_glpk_solver_test + SOURCES + "glpk_solver_test.cc" + LINK_LIBRARIES + GTest::gmock + GTest::gmock_main + absl::status + absl::time + ortools::math_opt_matchers + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" + ) +endif() + +if(USE_HIGHS) + ortools_cxx_test( + NAME + math_opt_solvers_highs_solver_test + SOURCES + "highs_solver_test.cc" + LINK_LIBRARIES + GTest::gmock + GTest::gmock_main + absl::status + ortools::math_opt_matchers + "$" + "$" + "$" + "$" + "$" + #"$" + #"$" + "$" + "$" + "$" + "$" + "$" + ) +endif() diff --git a/ortools/sat/CMakeLists.txt b/ortools/sat/CMakeLists.txt index c2a1d46fa2a..ec3a1b22c3b 100644 --- a/ortools/sat/CMakeLists.txt +++ b/ortools/sat/CMakeLists.txt @@ -42,11 +42,15 @@ target_link_libraries(${NAME} PRIVATE if(BUILD_TESTING) file(GLOB _TEST_SRCS "*_test.cc") - foreach(FILE_NAME IN LISTS _TEST_SRCS) + 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( - FILE_NAME - ${FILE_NAME} - DEPS + NAME + sat_${_NAME} + SOURCES + ${_FILE_NAME} + LINK_LIBRARIES benchmark::benchmark GTest::gmock GTest::gtest_main diff --git a/patches/cbc-2.10.patch b/patches/cbc-2.10.patch index c189c9cf39a..79830e90227 100644 --- a/patches/cbc-2.10.patch +++ b/patches/cbc-2.10.patch @@ -1,12 +1,12 @@ diff --git a/CMakeLists.txt b/CMakeLists.txt -index 22b7a17f..0a5a18dd 100644 +index be94fca..1529011 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -51,6 +51,7 @@ if(APPLE) +@@ -88,6 +88,7 @@ if(APPLE) CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-inconsistent-missing-override -Wno-unused-command-line-argument -Wno-unused-result -Wno-exceptions" ) + add_compile_options(-O1) - set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum OS X deployment version") - endif(APPLE) + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") + endif() diff --git a/patches/cgl-0.60.patch b/patches/cgl-0.60.patch index c3d8ddfa640..411ee81d4dd 100644 --- a/patches/cgl-0.60.patch +++ b/patches/cgl-0.60.patch @@ -1,12 +1,12 @@ diff --git a/CMakeLists.txt b/CMakeLists.txt -index 4a70789..d19c2a9 100644 +index 8c51561..f223f08 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -51,6 +51,7 @@ if(APPLE) +@@ -88,6 +88,7 @@ if(APPLE) CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-inconsistent-missing-override -Wno-unused-command-line-argument -Wno-unused-result -Wno-exceptions" ) + add_compile_options(-O1) - set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum OS X deployment version") - endif(APPLE) + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") + endif() diff --git a/patches/clp-1.17.4.patch b/patches/clp-1.17.patch similarity index 62% rename from patches/clp-1.17.4.patch rename to patches/clp-1.17.patch index 26d4c9dc858..c97dce747c8 100644 --- a/patches/clp-1.17.4.patch +++ b/patches/clp-1.17.patch @@ -1,12 +1,12 @@ diff --git a/CMakeLists.txt b/CMakeLists.txt -index e1d43115..6b2a17ad 100644 +index bb95c63..7fde473 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -51,6 +51,7 @@ if(APPLE) +@@ -88,6 +88,7 @@ if(APPLE) CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-inconsistent-missing-override -Wno-unused-command-line-argument -Wno-unused-result -Wno-exceptions" ) + add_compile_options(-O1) - set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum OS X deployment version") - endif(APPLE) + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") + endif() diff --git a/patches/coinutils-2.11.patch b/patches/coinutils-2.11.patch index 01958318c07..9eb47c863ef 100644 --- a/patches/coinutils-2.11.patch +++ b/patches/coinutils-2.11.patch @@ -1,12 +1,12 @@ diff --git a/CMakeLists.txt b/CMakeLists.txt -index fcfa658..267ddd9 100644 +index 3fc9cff..b2423fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -51,6 +51,7 @@ if(APPLE) +@@ -88,6 +88,7 @@ if(APPLE) CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-inconsistent-missing-override -Wno-unused-command-line-argument -Wno-unused-result -Wno-exceptions" ) + add_compile_options(-O1) - set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum OS X deployment version") - endif(APPLE) + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") + endif() diff --git a/patches/googletest-v1.15.2.patch b/patches/googletest-v1.15.2.patch new file mode 100644 index 00000000000..1d5da3c1f97 --- /dev/null +++ b/patches/googletest-v1.15.2.patch @@ -0,0 +1,18 @@ +diff --git a/googletest/cmake/internal_utils.cmake b/googletest/cmake/internal_utils.cmake +index 580ac1c..c6cc44c 100644 +--- a/googletest/cmake/internal_utils.cmake ++++ b/googletest/cmake/internal_utils.cmake +@@ -190,6 +190,13 @@ function(cxx_library_with_type name type cxx_flags) + COMPILE_DEFINITIONS "GTEST_CREATE_SHARED_LIBRARY=1") + target_compile_definitions(${name} INTERFACE + $) ++ if(APPLE) ++ set_target_properties(${name} PROPERTIES ++ INSTALL_RPATH "@loader_path") ++ elseif(UNIX) ++ set_target_properties(${name} PROPERTIES ++ INSTALL_RPATH "$ORIGIN") ++ endif() + endif() + if (DEFINED GTEST_HAS_PTHREAD) + target_link_libraries(${name} PUBLIC Threads::Threads) diff --git a/patches/osi-0.108.patch b/patches/osi-0.108.patch index a282726abf8..367f1d30fc7 100644 --- a/patches/osi-0.108.patch +++ b/patches/osi-0.108.patch @@ -1,12 +1,12 @@ diff --git a/CMakeLists.txt b/CMakeLists.txt -index 7b3cee2..2ac9c9d 100644 +index bc22fbd..1c2a604 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -51,6 +51,7 @@ if(APPLE) +@@ -88,6 +88,7 @@ if(APPLE) CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-inconsistent-missing-override -Wno-unused-command-line-argument -Wno-unused-result -Wno-exceptions" ) + add_compile_options(-O1) - set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum OS X deployment version") - endif(APPLE) + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") + endif() diff --git a/patches/pybind11_abseil.patch b/patches/pybind11_abseil.patch index 96d8d733378..69f1608f12d 100644 --- a/patches/pybind11_abseil.patch +++ b/patches/pybind11_abseil.patch @@ -55,7 +55,7 @@ index ceb65a8..e142837 100644 include_directories(${TOP_LEVEL_DIR} ${pybind11_INCLUDE_DIRS}) diff --git a/cmake/dependencies/CMakeLists.txt b/cmake/dependencies/CMakeLists.txt new file mode 100644 -index 0000000..b67d564 +index 0000000..cb13e7e --- /dev/null +++ b/cmake/dependencies/CMakeLists.txt @@ -0,0 +1,19 @@ @@ -158,7 +158,7 @@ index 791c245..33e614a 100644 ) diff --git a/pybind11_abseil/CMakeLists.txt b/pybind11_abseil/CMakeLists.txt -index d1b7483..ce7fd72 100644 +index d1b7483..74e3443 100644 --- a/pybind11_abseil/CMakeLists.txt +++ b/pybind11_abseil/CMakeLists.txt @@ -42,14 +42,19 @@ target_link_libraries(ok_status_singleton_pyinit_google3 @@ -184,7 +184,7 @@ index d1b7483..ce7fd72 100644 target_link_libraries(ok_status_singleton PUBLIC ok_status_singleton_pyinit_google3) -@@ -150,14 +155,23 @@ target_link_libraries(status_pyinit_google3 PUBLIC register_status_bindings) +@@ -150,14 +155,30 @@ target_link_libraries(status_pyinit_google3 PUBLIC register_status_bindings) # status ==================================================================== @@ -195,25 +195,32 @@ index d1b7483..ce7fd72 100644 +set_target_properties(status_py_extension_stub PROPERTIES LIBRARY_OUTPUT_NAME "status") +# note: macOS is APPLE and also UNIX ! +if(APPLE) -+ set_target_properties(status_py_extension_stub PROPERTIES SUFFIX ".so") ++ set_target_properties(status_py_extension_stub PROPERTIES ++ SUFFIX ".so" ++ INSTALL_RPATH "@loader_path;@loader_path/../ortools/.libs" ++ ) + set_property(TARGET status_py_extension_stub APPEND PROPERTY + LINK_FLAGS "-flat_namespace -undefined suppress") ++elseif(UNIX) ++ set_target_properties(status_py_extension_stub PROPERTIES ++ INSTALL_RPATH "$ORIGIN:$ORIGIN/../ortools/.libs" ++ ) +endif() -+ -+add_library(pybind11_abseil::status ALIAS status_py_extension_stub) -target_include_directories(status INTERFACE $) -+target_include_directories(status_py_extension_stub INTERFACE $) ++add_library(pybind11_abseil::status ALIAS status_py_extension_stub) -set_target_properties(status PROPERTIES PREFIX "") -+set_target_properties(status_py_extension_stub PROPERTIES PREFIX "") ++target_include_directories(status_py_extension_stub INTERFACE $) -target_link_libraries(status PUBLIC status_pyinit_google3 absl::status) ++set_target_properties(status_py_extension_stub PROPERTIES PREFIX "") ++ +target_link_libraries(status_py_extension_stub PUBLIC status_pyinit_google3 absl::status) # import_status_module ========================================================= -@@ -167,7 +181,7 @@ add_library(pybind11_abseil::import_status_module ALIAS import_status_module) +@@ -167,7 +188,7 @@ add_library(pybind11_abseil::import_status_module ALIAS import_status_module) target_include_directories(import_status_module INTERFACE $) @@ -222,7 +229,7 @@ index d1b7483..ce7fd72 100644 # status_casters =============================================================== -@@ -175,25 +189,27 @@ add_library(status_casters INTERFACE) +@@ -175,25 +196,27 @@ add_library(status_casters INTERFACE) add_library(pybind11_abseil::status_casters ALIAS status_casters) target_include_directories(status_casters diff --git a/patches/re2-2024-04-01.patch b/patches/re2-2024-04-01.patch new file mode 100644 index 00000000000..73f73a69c12 --- /dev/null +++ b/patches/re2-2024-04-01.patch @@ -0,0 +1,18 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index bdac5af..cedaf6e 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -131,6 +131,13 @@ set(RE2_HEADERS + + add_library(re2 ${RE2_SOURCES}) + target_compile_features(re2 PUBLIC cxx_std_14) ++if(APPLE) ++ set_target_properties(re2 PROPERTIES ++ INSTALL_RPATH "@loader_path") ++elseif(UNIX) ++ set_target_properties(re2 PROPERTIES ++ INSTALL_RPATH "$ORIGIN") ++endif() + target_include_directories(re2 PUBLIC $) + # CMake gives "set_target_properties called with incorrect number of arguments." + # errors if we don't quote ${RE2_HEADERS}, so quote it despite prevailing style.