diff --git a/.gitignore b/.gitignore index 2f91a2e217..fad35df17c 100644 --- a/.gitignore +++ b/.gitignore @@ -107,8 +107,7 @@ examples/dotnet/obj CMakeCache.txt CMakeFiles DartConfiguration.tcl -*build*/* -build/ +/build*/ # Ignore Bzlmod lock file until it is more stable MODULE.bazel.lock diff --git a/bazel/BUILD.bazel b/bazel/BUILD.bazel index 9c85c96307..f3560a8f7e 100644 --- a/bazel/BUILD.bazel +++ b/bazel/BUILD.bazel @@ -36,10 +36,3 @@ compile_pip_requirements( ) package(default_visibility = ["//visibility:public"]) - -filegroup( - name = "test_runner_template", - testonly = 1, - srcs = ["test_runner_template.sh"], - visibility = ["//visibility:public"], -) diff --git a/bazel/run_binary_test.bzl b/bazel/run_binary_test.bzl deleted file mode 100644 index 000b4dc7c2..0000000000 --- a/bazel/run_binary_test.bzl +++ /dev/null @@ -1,106 +0,0 @@ -# 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. - -"""run_binary_test will run a xx_binary as test with the given args.""" - -load("@bazel_skylib//rules:expand_template.bzl", "expand_template") -load("@rules_shell//shell:sh_test.bzl", "sh_test") - -def parse_label(label): - """Parse a label into (package, name). - - Args: - label: string in relative or absolute form. - - Returns: - Pair of strings: package, relative_name - - Raises: - ValueError for malformed label (does not do an exhaustive validation) - """ - if label.startswith("//"): - label = label[2:] # drop the leading // - colon_split = label.split(":") - if len(colon_split) == 1: # no ":" in label - pkg = label - _, _, target = label.rpartition("/") - else: - pkg, target = colon_split # fails if len(colon_split) != 2 - else: - colon_split = label.split(":") - if len(colon_split) == 1: # no ":" in label - pkg, target = native.package_name(), label - else: - pkg2, target = colon_split # fails if len(colon_split) != 2 - pkg = native.package_name() + ("/" + pkg2 if pkg2 else "") - return pkg, target - -def get_check_contains_code(line): - return """ -if ! grep --quiet --fixed-strings "{line}" "${{LOGFILE}}"; then - echo "---------------------------------------------------------------" - cat "${{LOGFILE}}" - echo "---------------------------------------------------------------" - echo "FAILURE: string '{line}' was not found in the output." - echo "---------------------------------------------------------------" - exit 1 -fi -""".format(line = line) - -def run_binary_test( - name, - binary, - template = "//bazel:test_runner_template", - args = [], - data = [], - grep_lines = [], - **kwargs): - """Create a sh_test to run the given binary as test. - - Args: - name: name of the test target. - binary: name of the binary target to run. - template: template file for executing the binary target. - args: args to use to run the binary. - data: data files required by this test. - grep_lines: lines to grep for in the log file. - **kwargs: other attributes that are applicable to tests, size, tags, etc. - - """ - shell_script = name + ".sh" - - # Get the path to the binary we want to run. - binary_pkg, binary_name = parse_label(binary) - binary_path = "/".join([binary_pkg, binary_name]) - - # We would like to include args in the generated shell script, so that "blaze-bin/.../test" can - # be run manually. Unfortunately `expand_template` does not resolve $(location) and other Make - # variables so we only pass them in `sh_test` below. - expand_template( - name = name + "_gensh", - template = template, - out = shell_script, - testonly = 1, - substitutions = { - "{{binary_path}}": binary_path, - "{{post_script}}": "\n".join([get_check_contains_code(line) for line in grep_lines]), - }, - ) - sh_test( - name = name, - testonly = 1, - srcs = [shell_script], - data = data + [binary], - args = args, - **kwargs - ) diff --git a/cmake/cpp.cmake b/cmake/cpp.cmake index 19af686904..285b5dd6b3 100644 --- a/cmake/cpp.cmake +++ b/cmake/cpp.cmake @@ -307,6 +307,126 @@ function(ortools_cxx_library) message(STATUS "Configuring library ${LIBRARY_NAME} ...DONE") endfunction() + +# ortools_cxx_binary() +# CMake function to generate and build C++ library. +# Parameters: +# 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 **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_binary( +# NAME +# foo_bar_binary +# SOURCES +# bar_binary.cc +# ${PROJECT_SOURCE_DIR}/ortools/foo/bar_binary.cc +# LINK_LIBRARIES +# GTest::gmock +# GTest::gtest_main +# TESTING +# ) +function(ortools_cxx_binary) + set(options "TESTING") + set(oneValueArgs "NAME") + set(multiValueArgs + "SOURCES;COMPILE_DEFINITIONS;COMPILE_OPTIONS;LINK_LIBRARIES;LINK_OPTIONS") + cmake_parse_arguments(BINARY + "${options}" + "${oneValueArgs}" + "${multiValueArgs}" + ${ARGN} + ) + if(BINARY_TESTING AND NOT BUILD_TESTING) + return() + endif() + + if(NOT BINARY_NAME) + message(FATAL_ERROR "no NAME provided") + endif() + if(NOT BINARY_SOURCES) + message(FATAL_ERROR "no SOURCES provided") + endif() + message(STATUS "Configuring binary ${BINARY_NAME} ...") + + add_executable(${BINARY_NAME} ${BINARY_TYPE} "") + target_sources(${BINARY_NAME} PRIVATE ${BINARY_SOURCES}) + target_include_directories(${BINARY_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + target_compile_definitions(${BINARY_NAME} PRIVATE ${BINARY_COMPILE_DEFINITIONS}) + target_compile_features(${BINARY_NAME} PRIVATE cxx_std_17) + target_compile_options(${BINARY_NAME} PRIVATE ${BINARY_COMPILE_OPTIONS}) + target_link_libraries(${BINARY_NAME} PRIVATE ${PROJECT_NAMESPACE}::ortools ${BINARY_LINK_LIBRARIES}) + target_link_options(${BINARY_NAME} PRIVATE ${BINARY_LINK_OPTIONS}) + + include(GNUInstallDirs) + if(APPLE) + set_target_properties(${BINARY_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(${BINARY_NAME} PROPERTIES + INSTALL_RPATH "$ORIGIN/${libdir_relative_path}:$ORIGIN") + endif() + add_executable(${PROJECT_NAMESPACE}::${BINARY_NAME} ALIAS ${BINARY_NAME}) + message(STATUS "Configuring binary ${BINARY_NAME} ...DONE") +endfunction() + +find_package(Python3 COMPONENTS Interpreter) + +# ortools_cxx_bintest() +# CMake function to generate and build C++ test. +# Parameters: +# NAME: CMake target name +# SCRIPT: The script to run the test. +# e.g.: +# ortools_cxx_bintest( +# NAME +# foo_bar_bintest +# SCRIPT +# foo_bar.bintest +# ENVIRONMENT +# "BINTEST_foo_bar=$" +# "BINTEST_foo_bar_data=$(CMAKE_CURRENT_SOURCE_DIR)/foo_bar_data.txt" +# ) +function(ortools_cxx_bintest) + set(options "") + set(oneValueArgs "NAME;SCRIPT") + set(multiValueArgs "ENVIRONMENT") + cmake_parse_arguments(BINTEST + "${options}" + "${oneValueArgs}" + "${multiValueArgs}" + ${ARGN} + ) + if(NOT BINTEST_NAME) + message(FATAL_ERROR "no NAME provided") + endif() + if(NOT BINTEST_SCRIPT) + message(FATAL_ERROR "no SCRIPT provided") + endif() + if(NOT Python3_Interpreter_FOUND) + message(WARNING "No python3 interpreter found, the bintest ${BINTEST_NAME} is disable") + return() + endif() + + message(STATUS "Configuring bintest ${BINTEST_NAME} ...") + add_test( + NAME ${BINTEST_NAME} + COMMAND ${Python3_EXECUTABLE} -m tools.testing.bintest_script_launcher ${BINTEST_SCRIPT} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + ) + set_tests_properties(${BINTEST_NAME} PROPERTIES + ENVIRONMENT "${BINTEST_ENVIRONMENT}" + ) + message(STATUS "Configuring bintest ${BINTEST_NAME} ...DONE") +endfunction() + ################## ## PROTO FILE ## ################## diff --git a/cmake/python.cmake b/cmake/python.cmake index d9af4a65a8..6aa1796b0a 100644 --- a/cmake/python.cmake +++ b/cmake/python.cmake @@ -1002,3 +1002,44 @@ if(NOT EXAMPLE_FILE_NAME) endif() message(STATUS "Configuring example ${EXAMPLE_FILE_NAME} ...DONE") endfunction() + +# add_python_binary() +# CMake function to generate a shell wrapper to execute a Python program. +# Parameters: +# NAME: the target name +# FILE: the Python filename +# e.g.: +# add_python_binary( +# NAME +# foo_bin +# FILE +# ${PROJECT_SOURCE_DIR}/examples/foo/bar.py +# ) +function(add_python_binary) + set(options "") + set(oneValueArgs NAME;FILE) + set(multiValueArgs "") + cmake_parse_arguments(PY_BINARY + "${options}" + "${oneValueArgs}" + "${multiValueArgs}" + ${ARGN} + ) + message(STATUS "Configuring python binary ${PY_BINARY_NAME} ...") + if(NOT PY_BINARY_NAME) + message(FATAL_ERROR "no NAME provided for python binary") + endif() + if(NOT PY_BINARY_FILE) + message(FATAL_ERROR "no FILE provided for python binary") + endif() + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${PY_BINARY_NAME}" "#!/usr/bin/env sh\n${VENV_Python3_EXECUTABLE} ${PY_BINARY_FILE} \"$@\"\n") + file(CHMOD "${CMAKE_CURRENT_BINARY_DIR}/${PY_BINARY_NAME}" + FILE_PERMISSIONS + OWNER_READ OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE + ) + add_executable("${PY_BINARY_NAME}" IMPORTED) + set_target_properties("${PY_BINARY_NAME}" PROPERTIES IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/${PY_BINARY_NAME}") + message(STATUS "Configuring python binary ${PY_BINARY_NAME} ...DONE") +endfunction() diff --git a/examples/cpp/BUILD.bazel b/examples/cpp/BUILD.bazel index 52c50f822f..f351e4dbd4 100644 --- a/examples/cpp/BUILD.bazel +++ b/examples/cpp/BUILD.bazel @@ -13,7 +13,7 @@ load("@rules_cc//cc:cc_binary.bzl", "cc_binary") load("@rules_cc//cc:cc_library.bzl", "cc_library") -load("//bazel:run_binary_test.bzl", "run_binary_test") +load("//tools/testing:bintest.bzl", "bintest") # Description: # C++ examples for operations_research. @@ -44,15 +44,61 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "binpacking_2d_sat_class01_instance2_test", size = "medium", - args = [ - "--input $(rootpath //ortools/packing/testdata:Class_01.2bp)", - "--instance 2", + srcs = [":binpacking_2d_sat_class01_instance2_test.bintest"], + named_data = { + "binpacking_2d_sat": ":binpacking_2d_sat", + "Class_01.2bp": "//ortools/packing/testdata:Class_01.2bp", + }, +) + +cc_library( + name = "cgc", + srcs = ["cgc.cc"], + hdrs = [ + "cgc.h", + "cgc_data.h", ], - binary = ":binpacking_2d_sat", - data = ["//ortools/packing/testdata:Class_01.2bp"], + deps = [ + "//ortools/base", + "//ortools/base:file", + "//ortools/constraint_solver:cp", + "@abseil-cpp//absl/container:btree", + "@abseil-cpp//absl/strings", + "@abseil-cpp//absl/strings:str_format", + "@abseil-cpp//absl/time", + "@abseil-cpp//absl/types:span", + "@re2", + ], +) + +cc_binary( + name = "cgc_main", + srcs = ["cgc_main.cc"], + deps = [ + ":cgc", + "//ortools/base", + "@abseil-cpp//absl/flags:flag", + "@abseil-cpp//absl/strings:str_format", + "@abseil-cpp//absl/time", + ], +) + +bintest( + name = "cgc_test_solution", + size = "small", + srcs = [":cgc_test_solution.bintest"], + named_data = { + "cgc_main": ":cgc_main", + "1.in": "testdata/cgc/1.in", + "2.in": "testdata/cgc/2.in", + "3.in": "testdata/cgc/3.in", + "cgcut1.in": "testdata/cgc/cgcut1.in", + "cgcut2.in": "testdata/cgc/cgcut2.in", + "cgcut3.in": "testdata/cgc/cgcut3.in", + }, ) cc_binary( @@ -67,10 +113,11 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "constraint_programming_cp_test", size = "small", - binary = ":constraint_programming_cp", + srcs = [":constraint_programming_cp_test.bintest"], + named_data = {"constraint_programming_cp": ":constraint_programming_cp"}, ) cc_binary( @@ -91,37 +138,25 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "costas_array_sat_model1_test", size = "medium", - args = [ - "--minsize=6", - "--maxsize=6", - "--model=1", - ], - binary = ":costas_array_sat", + srcs = [":costas_array_sat_model1_test.bintest"], + named_data = {"costas_array_sat": ":costas_array_sat"}, ) -run_binary_test( +bintest( name = "costas_array_sat_model2_test", size = "medium", - args = [ - "--minsize=6", - "--maxsize=6", - "--model=2", - ], - binary = ":costas_array_sat", + srcs = [":costas_array_sat_model2_test.bintest"], + named_data = {"costas_array_sat": ":costas_array_sat"}, ) -run_binary_test( +bintest( name = "costas_array_sat_model3_test", size = "medium", - args = [ - "--minsize=6", - "--maxsize=6", - "--model=3", - ], - binary = ":costas_array_sat", + srcs = [":costas_array_sat_model3_test.bintest"], + named_data = {"costas_array_sat": ":costas_array_sat"}, ) cc_binary( @@ -130,10 +165,11 @@ cc_binary( deps = ["//ortools/sat:cp_model"], ) -run_binary_test( +bintest( name = "cryptarithm_sat_test", size = "small", - binary = ":cryptarithm_sat", + srcs = [":cryptarithm_sat_test.bintest"], + named_data = {"cryptarithm_sat": ":cryptarithm_sat"}, ) cc_binary( @@ -151,11 +187,11 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "dobble_ls_test", size = "medium", - args = ["--time_limit_in_ms=10000"], - binary = ":dobble_ls", + srcs = [":dobble_ls_test.bintest"], + named_data = {"dobble_ls": ":dobble_ls"}, ) cc_binary( @@ -176,11 +212,11 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "golomb_sat_test", size = "medium", - args = ["--size 5"], - binary = ":golomb_sat", + srcs = [":golomb_sat_test.bintest"], + named_data = {"golomb_sat": ":golomb_sat"}, ) cc_binary( @@ -203,15 +239,14 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "knapsack_2d_sat_class01_instance2_test", size = "medium", - args = [ - "--input $(rootpath //ortools/packing/testdata:Class_01.2bp)", - "--instance 2", - ], - binary = ":knapsack_2d_sat", - data = ["//ortools/packing/testdata:Class_01.2bp"], + srcs = [":knapsack_2d_sat_class01_instance2_test.bintest"], + named_data = { + "knapsack_2d_sat": ":knapsack_2d_sat", + "Class_01.2bp": "//ortools/packing/testdata:Class_01.2bp", + }, ) cc_binary( @@ -241,12 +276,14 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "jobshop_sat_ft06", size = "small", - args = ["--input $(rootpath //ortools/scheduling/testdata:ft06)"], - binary = ":jobshop_sat", - data = ["//ortools/scheduling/testdata:ft06"], + srcs = [":jobshop_sat_ft06.bintest"], + named_data = { + "jobshop_sat": ":jobshop_sat", + "ft06": "//ortools/scheduling/testdata:ft06", + }, ) cc_binary( @@ -264,10 +301,11 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "magic_sequence_sat_test", size = "medium", - binary = ":magic_sequence_sat", + srcs = [":magic_sequence_sat_test.bintest"], + named_data = {"magic_sequence_sat": ":magic_sequence_sat"}, ) cc_binary( @@ -284,10 +322,11 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "multi_knapsack_sat_test", size = "medium", - binary = ":multi_knapsack_sat", + srcs = [":multi_knapsack_sat_test.bintest"], + named_data = {"multi_knapsack_sat": ":multi_knapsack_sat"}, ) cc_binary( @@ -309,12 +348,14 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "shift_minimization_sat_test", size = "medium", - args = ["--input $(rootpath :shift_minimization.dat)"], - binary = ":shift_minimization_sat", - data = [":shift_minimization.dat"], + srcs = [":shift_minimization_sat_test.bintest"], + named_data = { + "shift_minimization_sat": ":shift_minimization_sat", + "shift_minimization.dat": "testdata/shift_minimization.dat", + }, ) cc_binary( @@ -339,12 +380,14 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "weighted_tardiness_sat_test", size = "medium", - args = ["--input $(rootpath :wt40.txt)"], - binary = ":weighted_tardiness_sat", - data = [":wt40.txt"], + srcs = [":weighted_tardiness_sat_test.bintest"], + named_data = { + "weighted_tardiness_sat": ":weighted_tardiness_sat", + "wt40.txt": "testdata/wt40.txt", + }, ) cc_binary( @@ -363,10 +406,11 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "magic_square_sat_test", size = "medium", - binary = ":magic_square_sat", + srcs = [":magic_square_sat_test.bintest"], + named_data = {"magic_square_sat": ":magic_square_sat"}, ) cc_binary( @@ -394,23 +438,11 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "network_routing_sat_test", size = "medium", - args = [ - "--clients=10", - "--backbones=5", - "--demands=10", - "--traffic_min=5", - "--traffic_max=10", - "--min_client_degree=2", - "--max_client_degree=5", - "--min_backbone_degree=3", - "--max_backbone_degree=5", - "--max_capacity=20", - "--fixed_charge_cost=10", - ], - binary = ":network_routing_sat", + srcs = [":network_routing_sat_test.bintest"], + named_data = {"network_routing_sat": ":network_routing_sat"}, ) cc_binary( @@ -426,10 +458,11 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "nqueens_test", size = "small", - binary = ":nqueens", + srcs = [":nqueens_test.bintest"], + named_data = {"nqueens": ":nqueens"}, ) cc_binary( @@ -451,10 +484,11 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "sports_scheduling_sat_test", size = "medium", - binary = ":sports_scheduling_sat", + srcs = [":sports_scheduling_sat_test.bintest"], + named_data = {"sports_scheduling_sat": ":sports_scheduling_sat"}, ) cc_binary( @@ -480,6 +514,26 @@ cc_binary( ], ) +bintest( + name = "pdptw_test", + size = "medium", + srcs = [":pdptw_test.bintest"], + named_data = { + "pdptw": ":pdptw", + "lc102.txt": "testdata/lc102.txt", + }, +) + +bintest( + name = "pdptw_non_homogenous_fleet_test", + size = "medium", + srcs = [":pdptw_non_homogenous_fleet_test.bintest"], + named_data = { + "pdptw": ":pdptw", + "lc102.txt": "testdata/lc102.txt", + }, +) + # Routing examples. cc_binary( name = "random_tsp", @@ -515,10 +569,11 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "integer_programming_test", size = "small", - binary = ":integer_programming", + srcs = [":integer_programming_test.bintest"], + named_data = {"integer_programming": ":integer_programming"}, ) cc_binary( @@ -535,10 +590,11 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "linear_programming_test", size = "small", - binary = ":linear_programming", + srcs = [":linear_programming_test.bintest"], + named_data = {"linear_programming": ":linear_programming"}, ) cc_binary( @@ -551,10 +607,11 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "linear_solver_protocol_buffers_test", size = "small", - binary = ":linear_solver_protocol_buffers", + srcs = [":linear_solver_protocol_buffers_test.bintest"], + named_data = {"linear_solver_protocol_buffers": ":linear_solver_protocol_buffers"}, ) cc_binary( @@ -571,11 +628,11 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "strawberry_fields_with_column_generation_test", size = "large", - args = ["--colgen_instance=4"], - binary = ":strawberry_fields_with_column_generation", + srcs = [":strawberry_fields_with_column_generation_test.bintest"], + named_data = {"strawberry_fields_with_column_generation": ":strawberry_fields_with_column_generation"}, ) # Dimacs assignment problems @@ -617,10 +674,30 @@ cc_binary( "//ortools/graph:linear_assignment", "@abseil-cpp//absl/container:flat_hash_map", "@abseil-cpp//absl/flags:flag", - "@abseil-cpp//absl/strings:str_format", + "@abseil-cpp//absl/strings:string_view", ], ) +bintest( + name = "dimacs_assignment_min_cost_test", + size = "small", + srcs = [":dimacs_assignment_min_cost_test.bintest"], + named_data = { + "dimacs_assignment": ":dimacs_assignment", + "dimacs_example.txt": "testdata/dimacs_example.txt", + }, +) + +bintest( + name = "dimacs_assignment_max_cost_test", + size = "small", + srcs = [":dimacs_assignment_max_cost_test.bintest"], + named_data = { + "dimacs_assignment": ":dimacs_assignment", + "dimacs_example.txt": "testdata/dimacs_example.txt", + }, +) + # MPS driver for LP and MIP. cc_binary( name = "mps_driver", @@ -650,12 +727,14 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "mps_driver_test", size = "small", - args = ["--input $(rootpath //ortools/linear_solver/testdata:maximization.mps)"], - binary = ":mps_driver", - data = ["//ortools/linear_solver/testdata:maximization.mps"], + srcs = [":mps_driver_test.bintest"], + named_data = { + "mps_driver": ":mps_driver", + "maximization.mps": "//ortools/linear_solver/testdata:maximization.mps", + }, ) # Linear Assignment C++ Example @@ -669,10 +748,11 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "linear_assignment_api_test", size = "small", - binary = ":linear_assignment_api", + srcs = [":linear_assignment_api_test.bintest"], + named_data = {"linear_assignment_api": ":linear_assignment_api"}, ) # Flow C++ Example @@ -687,10 +767,11 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "flow_api_test", size = "small", - binary = ":flow_api", + srcs = [":flow_api_test.bintest"], + named_data = {"flow_api": ":flow_api"}, ) cc_binary( @@ -706,10 +787,11 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "max_flow_test", size = "small", - binary = ":max_flow", + srcs = [":max_flow_test.bintest"], + named_data = {"max_flow": ":max_flow"}, ) cc_binary( @@ -724,10 +806,11 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "min_cost_flow_test", size = "small", - binary = ":min_cost_flow", + srcs = [":min_cost_flow_test.bintest"], + named_data = {"min_cost_flow": ":min_cost_flow"}, ) # Frequency Assignment Problem @@ -819,10 +902,11 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "slitherlink_sat_test", size = "small", - binary = ":slitherlink_sat", + srcs = [":slitherlink_sat_test.bintest"], + named_data = {"slitherlink_sat": ":slitherlink_sat"}, ) cc_binary( @@ -854,10 +938,11 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "variable_intervals_sat_test", size = "small", - binary = ":variable_intervals_sat", + srcs = [":variable_intervals_sat_test.bintest"], + named_data = {"variable_intervals_sat": ":variable_intervals_sat"}, ) cc_binary( diff --git a/examples/cpp/CMakeBazel.txt b/examples/cpp/CMakeBazel.txt new file mode 100644 index 0000000000..063affbafd --- /dev/null +++ b/examples/cpp/CMakeBazel.txt @@ -0,0 +1,425 @@ +# This file is auto generated by bazel2cmake.py from examples/cpp/BUILD.bazel +# Don't edit manually, your changes will be lost. +# You can update this file by running: +# python3 tools/build/bazel2cmake.py examples/cpp/BUILD.bazel + + +ortools_cxx_binary( + NAME bzl_cc_example_binpacking_2d_sat + SOURCES binpacking_2d_sat.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_binpacking_2d_sat_class01_instance2_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/binpacking_2d_sat_class01_instance2_test.bintest + ENVIRONMENT BINTEST_binpacking_2d_sat=$ BINTEST_Class_01.2bp=${CMAKE_SOURCE_DIR}/ortools/packing/testdata/Class_01.2bp +) + +ortools_cxx_library( + NAME bzl_cc_example_cgc + SOURCES cgc.cc cgc.h cgc_data.h + TYPE SHARED +) + +ortools_cxx_binary( + NAME bzl_cc_example_cgc_main + SOURCES cgc_main.cc + LINK_LIBRARIES bzl_cc_example_cgc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_cgc_test_solution + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/cgc_test_solution.bintest + ENVIRONMENT BINTEST_cgc_main=$ BINTEST_1.in=${CMAKE_CURRENT_SOURCE_DIR}/testdata/cgc/1.in BINTEST_2.in=${CMAKE_CURRENT_SOURCE_DIR}/testdata/cgc/2.in BINTEST_3.in=${CMAKE_CURRENT_SOURCE_DIR}/testdata/cgc/3.in BINTEST_cgcut1.in=${CMAKE_CURRENT_SOURCE_DIR}/testdata/cgc/cgcut1.in BINTEST_cgcut2.in=${CMAKE_CURRENT_SOURCE_DIR}/testdata/cgc/cgcut2.in BINTEST_cgcut3.in=${CMAKE_CURRENT_SOURCE_DIR}/testdata/cgc/cgcut3.in +) + +ortools_cxx_binary( + NAME bzl_cc_example_constraint_programming_cp + SOURCES constraint_programming_cp.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_constraint_programming_cp_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/constraint_programming_cp_test.bintest + ENVIRONMENT BINTEST_constraint_programming_cp=$ +) + +ortools_cxx_binary( + NAME bzl_cc_example_costas_array_sat + SOURCES costas_array_sat.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_costas_array_sat_model1_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/costas_array_sat_model1_test.bintest + ENVIRONMENT BINTEST_costas_array_sat=$ +) + +ortools_cxx_bintest( + NAME bzl_cc_example_costas_array_sat_model2_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/costas_array_sat_model2_test.bintest + ENVIRONMENT BINTEST_costas_array_sat=$ +) + +ortools_cxx_bintest( + NAME bzl_cc_example_costas_array_sat_model3_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/costas_array_sat_model3_test.bintest + ENVIRONMENT BINTEST_costas_array_sat=$ +) + +ortools_cxx_binary( + NAME bzl_cc_example_cryptarithm_sat + SOURCES cryptarithm_sat.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_cryptarithm_sat_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/cryptarithm_sat_test.bintest + ENVIRONMENT BINTEST_cryptarithm_sat=$ +) + +ortools_cxx_binary( + NAME bzl_cc_example_dobble_ls + SOURCES dobble_ls.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_dobble_ls_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/dobble_ls_test.bintest + ENVIRONMENT BINTEST_dobble_ls=$ +) + +ortools_cxx_binary( + NAME bzl_cc_example_golomb_sat + SOURCES golomb_sat.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_golomb_sat_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/golomb_sat_test.bintest + ENVIRONMENT BINTEST_golomb_sat=$ +) + +ortools_cxx_binary( + NAME bzl_cc_example_knapsack_2d_sat + SOURCES knapsack_2d_sat.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_knapsack_2d_sat_class01_instance2_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/knapsack_2d_sat_class01_instance2_test.bintest + ENVIRONMENT BINTEST_knapsack_2d_sat=$ BINTEST_Class_01.2bp=${CMAKE_SOURCE_DIR}/ortools/packing/testdata/Class_01.2bp +) + +ortools_cxx_binary( + NAME bzl_cc_example_jobshop_sat + SOURCES jobshop_sat.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_jobshop_sat_ft06 + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/jobshop_sat_ft06.bintest + ENVIRONMENT BINTEST_jobshop_sat=$ BINTEST_ft06=${CMAKE_SOURCE_DIR}/ortools/scheduling/testdata/ft06 +) + +ortools_cxx_binary( + NAME bzl_cc_example_magic_sequence_sat + SOURCES magic_sequence_sat.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_magic_sequence_sat_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/magic_sequence_sat_test.bintest + ENVIRONMENT BINTEST_magic_sequence_sat=$ +) + +ortools_cxx_binary( + NAME bzl_cc_example_multi_knapsack_sat + SOURCES multi_knapsack_sat.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_multi_knapsack_sat_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/multi_knapsack_sat_test.bintest + ENVIRONMENT BINTEST_multi_knapsack_sat=$ +) + +ortools_cxx_binary( + NAME bzl_cc_example_shift_minimization_sat + SOURCES shift_minimization_sat.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_shift_minimization_sat_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/shift_minimization_sat_test.bintest + ENVIRONMENT BINTEST_shift_minimization_sat=$ BINTEST_shift_minimization.dat=${CMAKE_CURRENT_SOURCE_DIR}/testdata/shift_minimization.dat +) + +ortools_cxx_binary( + NAME bzl_cc_example_weighted_tardiness_sat + SOURCES weighted_tardiness_sat.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_weighted_tardiness_sat_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/weighted_tardiness_sat_test.bintest + ENVIRONMENT BINTEST_weighted_tardiness_sat=$ BINTEST_wt40.txt=${CMAKE_CURRENT_SOURCE_DIR}/testdata/wt40.txt +) + +ortools_cxx_binary( + NAME bzl_cc_example_magic_square_sat + SOURCES magic_square_sat.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_magic_square_sat_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/magic_square_sat_test.bintest + ENVIRONMENT BINTEST_magic_square_sat=$ +) + +ortools_cxx_binary( + NAME bzl_cc_example_network_routing_sat + SOURCES network_routing_sat.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_network_routing_sat_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/network_routing_sat_test.bintest + ENVIRONMENT BINTEST_network_routing_sat=$ +) + +ortools_cxx_binary( + NAME bzl_cc_example_nqueens + SOURCES nqueens.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_nqueens_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/nqueens_test.bintest + ENVIRONMENT BINTEST_nqueens=$ +) + +ortools_cxx_binary( + NAME bzl_cc_example_sports_scheduling_sat + SOURCES sports_scheduling_sat.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_sports_scheduling_sat_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/sports_scheduling_sat_test.bintest + ENVIRONMENT BINTEST_sports_scheduling_sat=$ +) + +ortools_cxx_binary( + NAME bzl_cc_example_pdptw + SOURCES pdptw.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_pdptw_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/pdptw_test.bintest + ENVIRONMENT BINTEST_pdptw=$ BINTEST_lc102.txt=${CMAKE_CURRENT_SOURCE_DIR}/testdata/lc102.txt +) + +ortools_cxx_bintest( + NAME bzl_cc_example_pdptw_non_homogenous_fleet_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/pdptw_non_homogenous_fleet_test.bintest + ENVIRONMENT BINTEST_pdptw=$ BINTEST_lc102.txt=${CMAKE_CURRENT_SOURCE_DIR}/testdata/lc102.txt +) + +ortools_cxx_binary( + NAME bzl_cc_example_random_tsp + SOURCES random_tsp.cc +) + +ortools_cxx_binary( + NAME bzl_cc_example_integer_programming + SOURCES integer_programming.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_integer_programming_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/integer_programming_test.bintest + ENVIRONMENT BINTEST_integer_programming=$ +) + +ortools_cxx_binary( + NAME bzl_cc_example_linear_programming + SOURCES linear_programming.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_linear_programming_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/linear_programming_test.bintest + ENVIRONMENT BINTEST_linear_programming=$ +) + +ortools_cxx_binary( + NAME bzl_cc_example_linear_solver_protocol_buffers + SOURCES linear_solver_protocol_buffers.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_linear_solver_protocol_buffers_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/linear_solver_protocol_buffers_test.bintest + ENVIRONMENT BINTEST_linear_solver_protocol_buffers=$ +) + +ortools_cxx_binary( + NAME bzl_cc_example_strawberry_fields_with_column_generation + SOURCES strawberry_fields_with_column_generation.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_strawberry_fields_with_column_generation_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/strawberry_fields_with_column_generation_test.bintest + ENVIRONMENT BINTEST_strawberry_fields_with_column_generation=$ +) + +ortools_cxx_library( + NAME bzl_cc_example_print_dimacs_assignment + SOURCES print_dimacs_assignment.h + TYPE INTERFACE +) + +ortools_cxx_library( + NAME bzl_cc_example_parse_dimacs_assignment + SOURCES parse_dimacs_assignment.cc parse_dimacs_assignment.h + TYPE SHARED +) + +ortools_cxx_binary( + NAME bzl_cc_example_dimacs_assignment + SOURCES dimacs_assignment.cc + LINK_LIBRARIES bzl_cc_example_parse_dimacs_assignment bzl_cc_example_print_dimacs_assignment +) + +ortools_cxx_bintest( + NAME bzl_cc_example_dimacs_assignment_min_cost_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/dimacs_assignment_min_cost_test.bintest + ENVIRONMENT BINTEST_dimacs_assignment=$ BINTEST_dimacs_example.txt=${CMAKE_CURRENT_SOURCE_DIR}/testdata/dimacs_example.txt +) + +ortools_cxx_bintest( + NAME bzl_cc_example_dimacs_assignment_max_cost_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/dimacs_assignment_max_cost_test.bintest + ENVIRONMENT BINTEST_dimacs_assignment=$ BINTEST_dimacs_example.txt=${CMAKE_CURRENT_SOURCE_DIR}/testdata/dimacs_example.txt +) + +ortools_cxx_binary( + NAME bzl_cc_example_mps_driver + SOURCES mps_driver.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_mps_driver_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/mps_driver_test.bintest + ENVIRONMENT BINTEST_mps_driver=$ BINTEST_maximization.mps=${CMAKE_SOURCE_DIR}/ortools/linear_solver/testdata/maximization.mps +) + +ortools_cxx_binary( + NAME bzl_cc_example_linear_assignment_api + SOURCES linear_assignment_api.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_linear_assignment_api_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/linear_assignment_api_test.bintest + ENVIRONMENT BINTEST_linear_assignment_api=$ +) + +ortools_cxx_binary( + NAME bzl_cc_example_flow_api + SOURCES flow_api.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_flow_api_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/flow_api_test.bintest + ENVIRONMENT BINTEST_flow_api=$ +) + +ortools_cxx_binary( + NAME bzl_cc_example_max_flow + SOURCES max_flow.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_max_flow_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/max_flow_test.bintest + ENVIRONMENT BINTEST_max_flow=$ +) + +ortools_cxx_binary( + NAME bzl_cc_example_min_cost_flow + SOURCES min_cost_flow.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_min_cost_flow_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/min_cost_flow_test.bintest + ENVIRONMENT BINTEST_min_cost_flow=$ +) + +ortools_cxx_library( + NAME bzl_cc_example_fap_parser + SOURCES fap_parser.cc fap_parser.h + TYPE SHARED +) + +ortools_cxx_library( + NAME bzl_cc_example_fap_model_printer + SOURCES fap_model_printer.cc fap_model_printer.h + LINK_LIBRARIES bzl_cc_example_fap_parser + TYPE SHARED +) + +ortools_cxx_library( + NAME bzl_cc_example_fap_utilities + SOURCES fap_utilities.cc fap_utilities.h + LINK_LIBRARIES bzl_cc_example_fap_parser + TYPE SHARED +) + +ortools_cxx_binary( + NAME bzl_cc_example_frequency_assignment_problem + SOURCES frequency_assignment_problem.cc + LINK_LIBRARIES bzl_cc_example_fap_model_printer bzl_cc_example_fap_parser bzl_cc_example_fap_utilities +) + +ortools_cxx_binary( + NAME bzl_cc_example_qap_sat + SOURCES qap_sat.cc +) + +ortools_cxx_binary( + NAME bzl_cc_example_slitherlink_sat + SOURCES slitherlink_sat.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_slitherlink_sat_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/slitherlink_sat_test.bintest + ENVIRONMENT BINTEST_slitherlink_sat=$ +) + +ortools_cxx_binary( + NAME bzl_cc_example_uncapacitated_facility_location + SOURCES uncapacitated_facility_location.cc +) + +ortools_cxx_binary( + NAME bzl_cc_example_variable_intervals_sat + SOURCES variable_intervals_sat.cc +) + +ortools_cxx_bintest( + NAME bzl_cc_example_variable_intervals_sat_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/variable_intervals_sat_test.bintest + ENVIRONMENT BINTEST_variable_intervals_sat=$ +) + +ortools_cxx_binary( + NAME bzl_cc_example_pdlp_solve + SOURCES pdlp_solve.cc +) \ No newline at end of file diff --git a/examples/cpp/CMakeLists.txt b/examples/cpp/CMakeLists.txt index 3f4f651558..522f06f068 100644 --- a/examples/cpp/CMakeLists.txt +++ b/examples/cpp/CMakeLists.txt @@ -15,51 +15,4 @@ if(NOT BUILD_CXX_EXAMPLES) return() endif() -#file(GLOB_RECURSE proto_files RELATIVE ${PROJECT_SOURCE_DIR} "*.proto") -#foreach(PROTO_FILE IN LISTS proto_files) -# message(STATUS "protoc proto(cc): ${PROTO_FILE}") -# get_filename_component(PROTO_DIR ${PROTO_FILE} DIRECTORY) -# get_filename_component(PROTO_NAME ${PROTO_FILE} NAME_WE) -# set(PROTO_HDR ${PROJECT_BINARY_DIR}/${PROTO_DIR}/${PROTO_NAME}.pb.h) -# set(PROTO_SRC ${PROJECT_BINARY_DIR}/${PROTO_DIR}/${PROTO_NAME}.pb.cc) -# message(STATUS "protoc hdr: ${PROTO_HDR}") -# message(STATUS "protoc src: ${PROTO_SRC}") -# add_custom_command( -# OUTPUT ${PROTO_SRC} ${PROTO_HDR} -# COMMAND ${PROTOC_PRG} -# "--proto_path=${PROJECT_SOURCE_DIR}" -# ${PROTO_DIRS} -# "--cpp_out=${PROJECT_BINARY_DIR}" -# ${PROTO_FILE} -# DEPENDS ${PROTO_FILE} ${PROTOC_PRG} -# COMMENT "Generate C++ protocol buffer for ${PROTO_FILE}" -# VERBATIM) -# list(APPEND PROTO_HDRS ${PROTO_HDR}) -# list(APPEND PROTO_SRCS ${PROTO_SRC}) -#endforeach() - -file(GLOB CXX_SRCS "*.cc") -list(FILTER CXX_SRCS EXCLUDE REGEX ".*/binpacking_2d_sat.cc") -list(FILTER CXX_SRCS EXCLUDE REGEX ".*/course_scheduling_run.cc") # missing proto -list(FILTER CXX_SRCS EXCLUDE REGEX ".*/course_scheduling.cc") # missing proto -list(FILTER CXX_SRCS EXCLUDE REGEX ".*/dimacs_assignment.cc") # crash -list(FILTER CXX_SRCS EXCLUDE REGEX ".*/dobble_ls.cc") # Too long -list(FILTER CXX_SRCS EXCLUDE REGEX ".*/fap_model_printer.cc") # lib -list(FILTER CXX_SRCS EXCLUDE REGEX ".*/fap_parser.cc") # lib -list(FILTER CXX_SRCS EXCLUDE REGEX ".*/fap_utilities.cc") # lib -list(FILTER CXX_SRCS EXCLUDE REGEX ".*/frequency_assignment_problem.cc") # crash -list(FILTER CXX_SRCS EXCLUDE REGEX ".*/jobshop_sat.cc") # crash -list(FILTER CXX_SRCS EXCLUDE REGEX ".*/knapsack_2d_sat.cc") -list(FILTER CXX_SRCS EXCLUDE REGEX ".*/mps_driver.cc") # crash -list(FILTER CXX_SRCS EXCLUDE REGEX ".*/multi_knapsack_sat.cc") # crash -list(FILTER CXX_SRCS EXCLUDE REGEX ".*/network_routing_sat.cc") -list(FILTER CXX_SRCS EXCLUDE REGEX ".*/parse_dimacs_assignment.cc") # lib -list(FILTER CXX_SRCS EXCLUDE REGEX ".*/pdlp_solve.cc") -list(FILTER CXX_SRCS EXCLUDE REGEX ".*/pdptw.cc") -list(FILTER CXX_SRCS EXCLUDE REGEX ".*/shift_minimization_sat.cc") -list(FILTER CXX_SRCS EXCLUDE REGEX ".*/strawberry_fields_with_column_generation.cc") # Too long -list(FILTER CXX_SRCS EXCLUDE REGEX ".*/vector_bin_packing_solver.cc") -list(FILTER CXX_SRCS EXCLUDE REGEX ".*/weighted_tardiness_sat.cc") -foreach(EXAMPLE IN LISTS CXX_SRCS) - add_cxx_example(FILE_NAME ${EXAMPLE}) -endforeach() +include("CMakeBazel.txt") diff --git a/examples/cpp/binpacking_2d_sat_class01_instance2_test.bintest b/examples/cpp/binpacking_2d_sat_class01_instance2_test.bintest new file mode 100644 index 0000000000..1c18fa902f --- /dev/null +++ b/examples/cpp/binpacking_2d_sat_class01_instance2_test.bintest @@ -0,0 +1 @@ +RUN: $(binpacking_2d_sat) --input $(Class_01.2bp) --instance 2 diff --git a/examples/cpp/cgc.cc b/examples/cpp/cgc.cc new file mode 100644 index 0000000000..c23ce870ad --- /dev/null +++ b/examples/cpp/cgc.cc @@ -0,0 +1,573 @@ +// 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. + +// Two-Dimensional Constrained Guillotine Cutting + +#include "examples/cpp/cgc.h" + +#include +#include +#include +#include + +#include "absl/container/btree_set.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_split.h" +#include "absl/time/time.h" +#include "absl/types/span.h" +#include "examples/cpp/cgc_data.h" +#include "ortools/base/helpers.h" +#include "ortools/base/logging.h" +#include "ortools/base/options.h" +#include "ortools/constraint_solver/constraint_solver.h" +#include "re2/re2.h" + +namespace operations_research { + +bool ConstrainedGuillotineCuttingData::LoadFromFile( + const std::string& input_file) { + std::string buffer; + + if (!file::GetContents(input_file, &buffer, file::Defaults()).ok()) { + LOG(ERROR) << "Could not read from file " << input_file; + return false; + } + const std::vector lines = + absl::StrSplit(buffer, '\n', absl::SkipEmpty()); + + if (lines.empty()) { + LOG(ERROR) << "Empty file: " << input_file; + return false; + } + + int num_pieces; + if (!RE2::FullMatch(lines[0], "\\s*(\\d+)\\s*", &num_pieces)) { + LOG(ERROR) << "Could not parse number of pieces"; + return false; + } + + if (0 >= num_pieces) { + LOG(ERROR) << "There are no pieces in the problem specification"; + return false; + } + + if (lines.size() != num_pieces + 2) { + LOG(ERROR) << "File: " << input_file << " does not respect the format"; + return false; + } + + if (!RE2::FullMatch(lines[1], "\\s*(\\d+)\\s+(\\d+)\\s*", &root_length_, + &root_width_)) { + LOG(ERROR) << "Could not parse the size of the main rectangle"; + return false; + } + + pieces_.resize(num_pieces); + for (int i = 0; i < num_pieces; ++i) { + if (!RE2::FullMatch(lines[i + 2], + "\\s*(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s*", + &pieces_[i].length, &pieces_[i].width, + &pieces_[i].max_appearances, &pieces_[i].value)) { + LOG(ERROR) << "Could not parse piece on line " << lines[i + 2] + << ", line number " << i + 3; + return false; + } + } + + return true; +} + +namespace { + +// Depending on the part that was cut, returns an IntVar representing +// if the cut that was made on the (index / 2 + 1) rectangle is +// a guillotine cut. +IntVar* IsAGuillotineCut(int index, + const std::vector& size_currently_cut, + const std::vector& size_not_cut, + const std::vector& parent_index, + const std::vector& pieces_size, Solver* solver) { + CHECK(solver != nullptr); + // The size of the cut must be >= 1 in order for the cut to be a valid one. + IntVar* const condition_var = + solver->MakeIsGreaterOrEqualCstVar(size_currently_cut[index], 1); + + // The part that is not cut should remain the same. + IntVar* const same_uncut_size_as_sibling = + solver->MakeIsEqualVar(size_not_cut[index], size_not_cut[index + 1]); + + // The part that is not cut should remain the same with the parent. + IntVar* const same_uncut_size_as_parent = solver->MakeIsEqualVar( + size_not_cut[index], + solver->MakeElement(size_not_cut, parent_index[index / 2 + 1])); + + // We make a cut if the size of the cut matches at least one of the pieces. + IntVar* const cut_equals_piece_size = solver->MakeIsEqualCstVar( + solver->MakeElement(pieces_size, size_currently_cut[index]), 1); + + // The sum of the sizes that were cut should equal the parent size. + IntVar* const sum_of_sizes = solver->MakeIsEqualVar( + solver->MakeSum(size_currently_cut[index], size_currently_cut[index + 1]), + solver->MakeElement(size_currently_cut, parent_index[index / 2 + 1])); + + const std::vector cut_implications = { + same_uncut_size_as_sibling, same_uncut_size_as_parent, + cut_equals_piece_size, sum_of_sizes}; + + return solver + ->MakeConditionalExpression( + condition_var, + solver->MakeIsEqualCstVar(solver->MakeSum(cut_implications), + cut_implications.size()), + 0) + ->Var(); +} + +// Sets initial variables: +// stores piece sizes related variables and sets maximum value +// and maximum number of elements in a cutting path. +void SetInitialElements(const ConstrainedGuillotineCuttingData& data, + std::vector* sizes_to_pieces, + std::vector* piece_length, + std::vector* piece_width, int* maximum_value, + int* maximum_elements) { + CHECK(sizes_to_pieces != nullptr); + CHECK(piece_length != nullptr); + CHECK(piece_width != nullptr); + CHECK(maximum_value != nullptr); + CHECK(maximum_elements != nullptr); + sizes_to_pieces->resize((data.root_length() + 1) * (data.root_width() + 1), + data.pieces().size()); + piece_length->resize(data.root_length() + 1, 0); + piece_width->resize(data.root_width() + 1, 0); + + const int main_rectangle_area = data.root_length() * data.root_width(); + + // Number of elements in the path should dependent on the number + // of end pieces. Considering that at every point we could in 2 cuts + // get to an end piece, which means maximum 4 new pieces, + // a limit of 4 * number_of_end_pieces should fit the path. + static const int kMultiplyNumOfEndPiecesBy = 4; + *maximum_value = 0; + *maximum_elements = 1; + + int index = 0; + for (const ConstrainedGuillotineCuttingData::Piece& piece : data.pieces()) { + if (piece.length <= data.root_length() && + piece.width <= data.root_width()) { + (*sizes_to_pieces)[piece.length * data.root_width() + piece.width] = + index; + (*piece_length)[piece.length] = 1; + (*piece_width)[piece.width] = 1; + } + + const int number_of_appearances = + std::min(piece.max_appearances, + main_rectangle_area / (piece.length * piece.width)); + + *maximum_value += piece.value * number_of_appearances; + *maximum_elements += kMultiplyNumOfEndPiecesBy * number_of_appearances; + index++; + } + + // TODO(user): a better upper bound for value and for + // maximum_elements. +} + +void SetRectanglesVariablesAndAddConstraints( + const std::vector& piece_length, const std::vector& piece_width, + const int maximum_elements, const int root_length, const int root_width, + std::vector* parent_index, std::vector* rectangle_length, + std::vector* rectangle_width, Solver* solver) { + CHECK(parent_index != nullptr); + CHECK(rectangle_length != nullptr); + CHECK(rectangle_width != nullptr); + CHECK(solver != nullptr); + + static const int kMainRectangleIndex = 0; + + solver->MakeIntVarArray(maximum_elements / 2 + 2, -1, maximum_elements, + "parent_index_", parent_index); + solver->MakeIntVarArray(maximum_elements, 0, root_length, "length_", + rectangle_length); + solver->MakeIntVarArray(maximum_elements, 0, root_width, "width_", + rectangle_width); + + (*parent_index)[kMainRectangleIndex]->SetValue(-1); + (*rectangle_length)[kMainRectangleIndex]->SetValue(root_length); + (*rectangle_width)[kMainRectangleIndex]->SetValue(root_width); + + // Any rectangle can be cut just once. + solver->AddConstraint(solver->MakeAllDifferent(*parent_index)); + + std::vector x_guillotine_cut; + std::vector y_guillotine_cut; + + // Every 2 consecutive cuts are from the same rectangle starting with + // position 1, since at index 0 we keep information regarding the + // main rectangle. + for (int i = 1; i < maximum_elements; i += 2) { + // The rectangle from which we cut needs to be < i. In case we do not cut + // anything (the elements are all 0) the parent_index will be i. + solver->AddConstraint( + solver->MakeLessOrEqual((*parent_index)[i / 2 + 1], i)); + + // If one of the sizes is 0, then all are 0 and the parent does not point + // to a real parent, but to itself, since we cannot have a valid cut + // that leaves one size 0. + IntVar* length_is_zero = + solver->MakeIsEqualCstVar((*rectangle_length)[i], 0); + + solver->AddConstraint(solver->MakeEquality( + length_is_zero, + solver->MakeIsEqualCstVar((*rectangle_length)[i + 1], 0))); + + solver->AddConstraint(solver->MakeEquality( + length_is_zero, solver->MakeIsEqualCstVar((*rectangle_width)[i], 0))); + + solver->AddConstraint(solver->MakeEquality( + length_is_zero, + solver->MakeIsEqualCstVar((*rectangle_width)[i + 1], 0))); + + solver->AddConstraint(solver->MakeEquality( + length_is_zero, + solver->MakeIsEqualCstVar((*parent_index)[i / 2 + 1], i))); + + // Group 0-cuts together at the beginning. So after a normal cut there + // will not be any 0-cuts. + if (i > 1) { + solver->AddConstraint(solver->MakeLessOrEqual( + solver->MakeIsGreaterOrEqualCstVar((*rectangle_length)[i - 1], 1), + solver->MakeIsGreaterOrEqualCstVar((*rectangle_length)[i], 1))); + } + + // If it is an x-guillotine cut. + x_guillotine_cut.push_back(IsAGuillotineCut(i, *rectangle_length, + *rectangle_width, *parent_index, + piece_length, solver)); + + // If it is an y-guillotine cut. + y_guillotine_cut.push_back( + IsAGuillotineCut(i, *rectangle_width, *rectangle_length, *parent_index, + piece_width, solver)); + + // Every pair of rectangles should correspond to a guillotine cut + // on one of the axis or they could be 0 if there was no cut made. + solver->AddConstraint(solver->MakeEquality( + solver->MakeSum( + length_is_zero, + solver->MakeSum( + solver->MakeIsEqualCstVar(x_guillotine_cut[i / 2], 1), + solver->MakeIsEqualCstVar(y_guillotine_cut[i / 2], 1))), + 1)); + } +} + +void AddAdditionalConstraints(const std::vector& parent_index, + const std::vector& rectangle_length, + const std::vector& rectangle_width, + const std::vector& sizes_to_pieces, + const ConstrainedGuillotineCuttingData& data, + int maximum_elements, + std::vector* is_end_piece, + std::vector* was_cut, IntVar* value, + Solver* solver) { + CHECK(is_end_piece != nullptr); + CHECK(was_cut != nullptr); + CHECK(value != nullptr); + CHECK(solver != nullptr); + solver->MakeIntVarArray(maximum_elements, 0, 1, "", was_cut); + + for (int i = 0; i < maximum_elements; ++i) { + solver->AddConstraint(solver->MakeCount(parent_index, i, (*was_cut)[i])); + + is_end_piece->push_back( + solver + ->MakeConditionalExpression( + solver->MakeIsEqualCstVar((*was_cut)[i], 0), + solver->MakeElement( + sizes_to_pieces, + solver + ->MakeSum(solver->MakeProd(rectangle_length[i], + data.root_width()), + rectangle_width[i]) + ->Var()), + data.pieces().size()) + ->Var()); + } + + int index = 0; + std::vector values; + + for (const ConstrainedGuillotineCuttingData::Piece& piece : data.pieces()) { + // Number of appearances of every type should be less or equal + // to the maximum number of times a piece can appear. + IntVar* const appearances = + solver->MakeIntVar(0, (data.root_length() * data.root_width()) / + (piece.length * piece.width)); + + // The number of appearances of every piece should be equal + // to the number of times that piece appears in a path as an end piece. + solver->AddConstraint(solver->MakeCount(*is_end_piece, index, appearances)); + + // Values will contain for every piece: + // number_of_time_the_piece_appears * its value. + values.push_back( + solver + ->MakeProd(solver->MakeMin(appearances, piece.max_appearances), + piece.value) + ->Var()); + + index++; + } + + solver->AddConstraint(solver->MakeEquality(value, solver->MakeSum(values))); +} + +void CreateAdditionalMonitors(absl::Duration time_limit, + std::vector* monitors, + OptimizeVar* objective_value, Solver* solver) { + CHECK(monitors != nullptr); + CHECK(objective_value != nullptr); + CHECK(solver != nullptr); + monitors->push_back(objective_value); + + static const int kLogFrequency = 100000; + SearchMonitor* const log = + solver->MakeSearchLog(kLogFrequency, objective_value); + monitors->push_back(log); + + if (time_limit != absl::InfiniteDuration()) { + SearchLimit* const limit = solver->MakeTimeLimit(time_limit); + monitors->push_back(limit); + } +} + +DecisionBuilder* CreateDecisionBuilder( + const std::vector& parent_index, + const std::vector& rectangle_length, + const std::vector& rectangle_width, + const std::vector& was_cut, Solver* solver) { + CHECK(solver != nullptr); + std::vector decision_variables; + for (int i = 1; i < rectangle_length.size() / 2 + 1; ++i) { + decision_variables.push_back(parent_index[i]); + decision_variables.push_back(rectangle_length[2 * (i - 1) + 1]); + decision_variables.push_back(rectangle_width[2 * (i - 1) + 1]); + } + for (int i = 0; i < rectangle_length.size(); ++i) { + decision_variables.push_back(was_cut[i]); + } + + return solver->MakePhase(decision_variables, Solver::CHOOSE_FIRST_UNBOUND, + Solver::ASSIGN_MAX_VALUE); +} + +void FillSolution( + const std::vector& parent_index, + const std::vector& rectangle_length, + const std::vector& rectangle_width, + const SolutionCollector* collector, IntVar* value, int* maximum_value, + std::vector* solution) { + CHECK(collector != nullptr); + CHECK(value != nullptr); + CHECK(maximum_value != nullptr); + CHECK(solution != nullptr); + + int number_of_zero_cuts = 0; + int parent = -1; + for (int i = 0; i < rectangle_length.size(); ++i) { + if (!collector->Value(0, rectangle_length[i])) { + number_of_zero_cuts++; + continue; + } + + if (i % 2 == 1) { + parent = std::max( + collector->Value(0, parent_index[i / 2 + 1]) - number_of_zero_cuts, + int64_t{0}); + } + + solution->emplace_back(parent, collector->Value(0, rectangle_length[i]), + collector->Value(0, rectangle_width[i])); + } + + *maximum_value = collector->Value(0, value); +} + +void ValidateSolution(int num_pieces, int root_width, + const std::vector& parent_index, + const std::vector& rectangle_length, + const std::vector& rectangle_width, + const std::vector& is_end_piece, + absl::Span sizes_to_pieces, + const SolutionCollector* collector) { + CHECK(collector != nullptr); + absl::btree_set parent_ids; + for (int i = 0; i < parent_index.size(); ++i) { + parent_ids.insert(collector->Value(0, parent_index[i])); + // The rectangle from which the rectangles were cut needs to be + // <= current position. For every pair of rectangles we keep + // their parent index once. + CHECK(collector->Value(0, parent_index[i]) <= i * 2 - 1); + } + // Every piece should be cut just once. + CHECK_EQ(parent_ids.size(), parent_index.size()); + + bool guillotine_cut = false; + for (int i = 1; i < rectangle_length.size(); i += 2) { + const int parent = collector->Value(0, parent_index[i / 2 + 1]); + const int length_left_rectangle = collector->Value(0, rectangle_length[i]); + const int length_rigth_rectangle = + collector->Value(0, rectangle_length[i + 1]); + const int length_parent = collector->Value(0, rectangle_length[parent]); + const int width_parent = collector->Value(0, rectangle_width[parent]); + const int width_left_rectangle = collector->Value(0, rectangle_width[i]); + const int width_right_rectangle = + collector->Value(0, rectangle_width[i + 1]); + + const bool is_a_x_guillotine_cut = + length_left_rectangle + length_rigth_rectangle == length_parent && + length_left_rectangle && length_rigth_rectangle && + width_left_rectangle == width_right_rectangle && + width_left_rectangle == width_parent; + + const bool is_a_y_guillotine_cut = + width_left_rectangle + width_right_rectangle == width_parent && + width_left_rectangle && width_right_rectangle && + length_left_rectangle == length_rigth_rectangle && + length_left_rectangle == length_parent; + + const bool is_a_zero_cut = !length_left_rectangle && + !length_rigth_rectangle && + !width_left_rectangle && !width_right_rectangle; + + // Every cut is a guillotine cut or all elements are 0. + CHECK(is_a_x_guillotine_cut || is_a_y_guillotine_cut || is_a_zero_cut); + + // Check if it is a piece. + const int it_is_piece1 = + parent_ids.contains(i) + ? num_pieces + : sizes_to_pieces[length_left_rectangle * root_width + + width_left_rectangle]; + + const int it_is_piece2 = + parent_ids.contains(i + 1) + ? num_pieces + : sizes_to_pieces[length_rigth_rectangle * root_width + + width_right_rectangle]; + + CHECK_EQ(it_is_piece1, collector->Value(0, is_end_piece[i])); + CHECK_EQ(it_is_piece2, collector->Value(0, is_end_piece[i + 1])); + + // Check that all 0-cuts (both rectangles are 0x0) are grouped together. + CHECK_LE(guillotine_cut, is_a_x_guillotine_cut || is_a_y_guillotine_cut); + guillotine_cut |= is_a_x_guillotine_cut || is_a_y_guillotine_cut; + } +} + +} // namespace + +void ConstrainedGuillotineCutting::PrintSolution() const { + CHECK(solved_); + + absl::PrintF("Maximum value: %d\n", maximum_value_); + absl::PrintF("Main rectangle 0 sizes: %dx%d\n", data_->root_length(), + data_->root_width()); + for (int i = 1; i < solution_.size(); ++i) { + if (i % 2 == 1) { + absl::PrintF("\nRectangle %d was cut in: \n", solution_[i].parent_index); + } + absl::PrintF("Rectangle %d sizes: %dx%d\n", i, solution_[i].length, + solution_[i].width); + } +} + +void ConstrainedGuillotineCutting::Solve(absl::Duration time_limit) { + const std::vector& pieces = + data_->pieces(); + + // Depending on the size of a rectangle, it represents the index of + // the piece to which it corresponds. If it does not correspond to + // any piece, than it will remain pieces.size(). + std::vector sizes_to_pieces; + + // Depending on the length(width) of a rectangle, it will be 1 if + // there exists a piece that has that length(width). + std::vector piece_length; + std::vector piece_width; + + int maximum_value; + int maximum_elements; + SetInitialElements(*data_, &sizes_to_pieces, &piece_length, &piece_width, + &maximum_value, &maximum_elements); + + // For every pair of rectangles the index of the rectangle + // that the rectangles were cut of. + std::vector parent_index; + + // sizes of the rectangles + std::vector rectangle_length; + std::vector rectangle_width; + SetRectanglesVariablesAndAddConstraints( + piece_length, piece_width, maximum_elements, data_->root_length(), + data_->root_width(), &parent_index, &rectangle_length, &rectangle_width, + &solver_); + + // Contains the piece that this rectangle equals to if it is + // an end piece (it was not cut). + std::vector is_end_piece; + // For every piece it is true if the corresponding rectangle + // was cut. + std::vector was_cut; + IntVar* const value = solver_.MakeIntVar(0, maximum_value); + AddAdditionalConstraints(parent_index, rectangle_length, rectangle_width, + sizes_to_pieces, *data_, maximum_elements, + &is_end_piece, &was_cut, value, &solver_); + // Objective: maximize the value of the end pieces. + OptimizeVar* const objective_value = solver_.MakeMaximize(value, 1); + + DecisionBuilder* const db = CreateDecisionBuilder( + parent_index, rectangle_length, rectangle_width, was_cut, &solver_); + std::vector monitors; + + SolutionCollector* const collector = solver_.MakeLastSolutionCollector(); + collector->Add(parent_index); + collector->Add(rectangle_length); + collector->Add(rectangle_width); + collector->Add(is_end_piece); + collector->Add(value); + monitors.push_back(collector); + + CreateAdditionalMonitors(time_limit, &monitors, objective_value, &solver_); + + const int64_t start_time = solver_.wall_time(); + solver_.Solve(db, monitors); + const int64_t end_time = solver_.wall_time(); + + LOG(INFO) << "The process took: " << (end_time - start_time) / 1000.0 + << " seconds."; + + if (collector->solution_count()) { + ValidateSolution(pieces.size(), data_->root_width(), parent_index, + rectangle_length, rectangle_width, is_end_piece, + sizes_to_pieces, collector); + + solved_ = true; + FillSolution(parent_index, rectangle_length, rectangle_width, collector, + value, &maximum_value_, &solution_); + } +} + +} // namespace operations_research diff --git a/examples/cpp/cgc.h b/examples/cpp/cgc.h new file mode 100644 index 0000000000..63faed008c --- /dev/null +++ b/examples/cpp/cgc.h @@ -0,0 +1,91 @@ +// 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. + +// Two-Dimensional Constrained Guillotine Cutting +// +// This file contains a solver for the Two-Dimensional Constrained +// Guillotine Cutting Problem. The problem requires cutting a plane +// rectangle into smaller rectangular pieces of given sizes and values +// in order to maximize the sum of the values of the cut pieces in which +// there is a constraint on the maximum number of each type of piece that +// is to be produced and all cuts go from one edge of the rectangle to be +// cut to the opposite edge. +// +// If cgc_time_limit_in_ms is defined, it provides the best value +// achieved in that amount of time. +// +// Example usage: +// +// std::unique_ptr +// data(new operations_research::ConstrainedGuillotineCuttingData()); +// data->LoadFromFile(file_path); +// operations_research::ConstrainedGuillotineCutting cgc(std::move(data)); +// cgc.Solve(absl::Milliseconds(absl::GetFlag(FLAGS_time_limit_in_ms))); +// if (cgc.Solved()) { +// cgc.PrintSolution(); +// } + +#ifndef ORTOOLS_EXAMPLES_CGC_H_ +#define ORTOOLS_EXAMPLES_CGC_H_ + +#include +#include +#include +#include +#include + +#include "examples/cpp/cgc_data.h" +#include "ortools/constraint_solver/constraint_solver.h" + +namespace operations_research { + +class ConstrainedGuillotineCutting { + public: + struct CutRectangle { + CutRectangle(int parent_index, int length, int width) + : parent_index(parent_index), length(length), width(width) {} + + int parent_index; + int length; + int width; + }; + + explicit ConstrainedGuillotineCutting( + std::unique_ptr data) + : data_(std::move(data)), + solver_("ConstrainedGuillotineCutting"), + solved_(false), + maximum_value_(0) {} + + int MaximumValue() const { + DCHECK(solved_); + return maximum_value_; + } + bool Solved() const { return solved_; } + + void PrintSolution() const; + void Solve(absl::Duration time_limit); + + private: + // Contains the problem parameters. + std::unique_ptr data_; + Solver solver_; + + bool solved_; + int maximum_value_; + std::vector solution_; +}; + +} // namespace operations_research + +#endif // ORTOOLS_EXAMPLES_CGC_H_ diff --git a/examples/cpp/cgc_data.h b/examples/cpp/cgc_data.h new file mode 100644 index 0000000000..34cb86e4e4 --- /dev/null +++ b/examples/cpp/cgc_data.h @@ -0,0 +1,70 @@ +// 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. + +// Two-Dimensional Constrained Guillotine Cutting +// +// The file contains code to load the problem, in the format detailed below. +// +// Input (on different lines): +// - number of pieces +// - length and width for the plane rectangle +// - for each piece (one line for every piece): +// - length +// - width +// - maximum number of pieces of that type that can be cut +// - value of the piece +// +// For more details and sample input (and format) see: +// - http://people.brunel.ac.uk/~mastjjb/jeb/orlib/cgcutinfo.html +// - //ortools/examples/testdata/cgc contains examples +// of input files. + +#ifndef ORTOOLS_EXAMPLES_CGC_DATA_H_ +#define ORTOOLS_EXAMPLES_CGC_DATA_H_ + +#include +#include + +namespace operations_research { + +class ConstrainedGuillotineCuttingData { + public: + // Each rectangular piece from the input is represented + // as an instance of this structure. + struct Piece { + int length; + int width; + int max_appearances; + int value; + }; + + ConstrainedGuillotineCuttingData() : root_length_(0), root_width_(0) {} + + bool LoadFromFile(const std::string& input_file); + + // Accessors for problem specification data + int root_length() const { return root_length_; } + int root_width() const { return root_width_; } + const std::vector& pieces() const { return pieces_; } + + private: + // main rectangle size + int root_length_; + int root_width_; + + std::vector pieces_; +}; + +} // namespace operations_research + +#endif // ORTOOLS_EXAMPLES_CGC_DATA_H_ diff --git a/examples/cpp/cgc_main.cc b/examples/cpp/cgc_main.cc new file mode 100644 index 0000000000..8ab189e3aa --- /dev/null +++ b/examples/cpp/cgc_main.cc @@ -0,0 +1,81 @@ +// 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. + +// This file implements the main function for the Two-Dimensional +// Constrained Guillotine Cutting solver. It reads the problem +// specification from an input file specified via command-line flags, +// and prints the solution to standard output. +// +// Example usage: +// ./cgc_main --input_file=testdata/cgc/my_input_file.in +// Other examples of input files in testdata/cgc/. + +#include +#include +#include + +#include "absl/flags/flag.h" +#include "absl/strings/str_format.h" +#include "absl/time/time.h" +#include "examples/cpp/cgc.h" +#include "examples/cpp/cgc_data.h" +#include "ortools/base/init_google.h" +#include "ortools/base/logging.h" + +ABSL_FLAG(std::string, input_file, "", "Input data file"); +ABSL_FLAG(int, time_limit_in_ms, 0, + "Time limit in milliseconds. 0 means no time limit. " + "If different, the solver will provide the best solution " + "that was found in that amount of time."); +ABSL_FLAG(bool, print_maximum_value, false, + "If true, it prints the maximum value found."); +ABSL_FLAG(bool, print_solution, false, + "If true, it prints the maximum value and the cutting pattern."); + +using operations_research::ConstrainedGuillotineCutting; +using operations_research::ConstrainedGuillotineCuttingData; + +int main(int argc, char** argv) { + InitGoogle(argv[0], &argc, &argv, true); + + if (absl::GetFlag(FLAGS_input_file).empty()) { + LOG(QFATAL) << "Please supply an input file with --input_file="; + } + LOG(INFO) << "Processing file " << absl::GetFlag(FLAGS_input_file); + + auto data = std::make_unique(); + + if (!data->LoadFromFile(absl::GetFlag(FLAGS_input_file))) { + LOG(QFATAL) << "Input file " << absl::GetFlag(FLAGS_input_file) + << " was not loaded."; + } + + ConstrainedGuillotineCutting cgc(std::move(data)); + const absl::Duration time_limit = + absl::GetFlag(FLAGS_time_limit_in_ms) == 0 + ? absl::InfiniteDuration() + : absl::Milliseconds(absl::GetFlag(FLAGS_time_limit_in_ms)); + cgc.Solve(time_limit); + + if (cgc.Solved()) { + if (absl::GetFlag(FLAGS_print_solution)) { + cgc.PrintSolution(); + } else if (absl::GetFlag(FLAGS_print_maximum_value)) { + absl::PrintF("%d", cgc.MaximumValue()); + } else { + LOG(INFO) << "The maximum value found is: " << cgc.MaximumValue(); + } + } else { + absl::PrintF("There was no solution found in %v ms.\n", time_limit); + } +} diff --git a/examples/cpp/cgc_test_solution.bintest b/examples/cpp/cgc_test_solution.bintest new file mode 100644 index 0000000000..25189556b4 --- /dev/null +++ b/examples/cpp/cgc_test_solution.bintest @@ -0,0 +1,24 @@ +# Tests the Two-Dimensional Constrained Guillotine Cutting solver +# by loading example input files from the testdata directory. + +# The following tests are too big and don't converge within a second so we don't +# check for a particular solution. Instead we make sure that the value is +# greater than zero. + +RUN: $(cgc_main) --input_file=$(1.in) --time_limit_in_ms=1000 --print_maximum_value=true +CHECK: "@num(>0)" + +RUN: $(cgc_main) --input_file=$(2.in) --time_limit_in_ms=1000 --print_maximum_value=true +CHECK: "@num(>0)" + +RUN: $(cgc_main) --input_file=$(3.in) --time_limit_in_ms=1000 --print_maximum_value=true +CHECK: "@num(>0)" + +RUN: $(cgc_main) --input_file=$(cgcut1.in) --time_limit_in_ms=1000 --print_maximum_value=true +CHECK: "@num(>0)" + +RUN: $(cgc_main) --input_file=$(cgcut2.in) --time_limit_in_ms=1000 --print_maximum_value=true +CHECK: "@num(>0)" + +RUN: $(cgc_main) --input_file=$(cgcut3.in) --time_limit_in_ms=1000 --print_maximum_value=true +CHECK: "@num(>0)" diff --git a/examples/cpp/constraint_programming_cp_test.bintest b/examples/cpp/constraint_programming_cp_test.bintest new file mode 100644 index 0000000000..50a49955fa --- /dev/null +++ b/examples/cpp/constraint_programming_cp_test.bintest @@ -0,0 +1 @@ +RUN: $(constraint_programming_cp) diff --git a/examples/cpp/costas_array_sat_model1_test.bintest b/examples/cpp/costas_array_sat_model1_test.bintest new file mode 100644 index 0000000000..10978aa7c1 --- /dev/null +++ b/examples/cpp/costas_array_sat_model1_test.bintest @@ -0,0 +1 @@ +RUN: $(costas_array_sat) --minsize=6 --maxsize=6 --model=1 diff --git a/examples/cpp/costas_array_sat_model2_test.bintest b/examples/cpp/costas_array_sat_model2_test.bintest new file mode 100644 index 0000000000..279cbe8b28 --- /dev/null +++ b/examples/cpp/costas_array_sat_model2_test.bintest @@ -0,0 +1 @@ +RUN: $(costas_array_sat) --minsize=6 --maxsize=6 --model=2 diff --git a/examples/cpp/costas_array_sat_model3_test.bintest b/examples/cpp/costas_array_sat_model3_test.bintest new file mode 100644 index 0000000000..45de6bb280 --- /dev/null +++ b/examples/cpp/costas_array_sat_model3_test.bintest @@ -0,0 +1 @@ +RUN: $(costas_array_sat) --minsize=6 --maxsize=6 --model=3 diff --git a/examples/cpp/course_scheduling.proto b/examples/cpp/course_scheduling.proto deleted file mode 100644 index 167c83523c..0000000000 --- a/examples/cpp/course_scheduling.proto +++ /dev/null @@ -1,188 +0,0 @@ -// 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; - -// Information required to create a schedule for a school system. -message CourseSchedulingModel { - // Schedule name, used only for logging purposes. - string display_name = 1; - - // The number of days in a schedule rotation. If the school system uses a - // block schedule, this value should be 1. - int32 days_count = 2; - - // The number of time slots each day in a schedule rotation. If the school - // system uses a block schedule, this value is the number of blocks. - int32 daily_time_slot_count = 3; - - // List of courses that need to be scheduled. - repeated Course courses = 4; - - // List of teachers. - repeated Teacher teachers = 5; - - // List of students that need to be assigned to these courses. - repeated Student students = 6; - - // List of rooms that the courses can be assigned to. - repeated Room rooms = 7; -} - -// Holds the solution to the course scheduling problem. -message CourseSchedulingResult { - // Human readable message about the solver or given model. - string message = 1; - - // Status of the solver. - CourseSchedulingResultStatus solver_status = 2; - - // List of the time slot and room assignments for each section of a course. - repeated ClassAssignment class_assignments = 3; - - // List of course and section assignments for each student. - repeated StudentAssignment student_assignments = 4; -} - -message ClassAssignment { - // Index of the course in the CourseSchedulingModel. - int32 course_index = 1; - - // Specific section of the course in the CourseSchedulingModel. - int32 section_number = 2; - - // Time slots that this class has been assigned to in the - // CourseSchedulingModel. - repeated int32 time_slots = 3; - - // Indices of the rooms that the class is assigned to in the - // CourseSchedulingModel. If this is not empty, then the number of indices - // must match the number of time slots. - repeated int32 room_indices = 4; -} - -message StudentAssignment { - // Index of the student in the CourseSchedulingModel. - int32 student_index = 1; - - // Course indices in the CourseSchedulingModel that this student has been - // assigned to. The number of indices must match the number of section - // indices. - repeated int32 course_indices = 2; - - // Section indices for each Course in the CourseSchedulingModel this this - // student has been assigned to. The number of indices must match the number - // of course indices. - repeated int32 section_indices = 3; -} - -message Course { - // Course name, used only for logging purposes. - string display_name = 1; - - // The number of times each section of this course needs to meet during a - // schedule rotation. Each section of the course meets no more than once a - // day. If the school system uses a block schedule, then this value should - // be 1. - int32 meetings_count = 2; - - // The maximum number of students for this course. This value can be equal to - // +Infinity to encode a course has no maximum capacity. - int32 max_capacity = 3; - - // The minimum number of students for this course. - int32 min_capacity = 4; - - // The number of consecutive time slots that each section of this course needs - // to be scheduled for. This value can only be 1 or 2. If the value is 2, then - // 2 consecutive time slots in a day counts as 1 meeting time for the section. - int32 consecutive_slots_count = 5; - - // List of indices for the teachers of this course. We are assuming that each - // teacher teaches separately. Must have the same number of elements as the - // number of sections list. - repeated int32 teacher_indices = 6; - - // The number of sections each teacher teaches of this course. Must have the - // same number of elements as the teacher index list. - repeated int32 teacher_section_counts = 7; - - // List of the possible rooms that this course can be assigned to. This can - // be empty. - repeated int32 room_indices = 8; -} - -message Teacher { - // Teacher name, used only for logging purposes. - string display_name = 1; - - // List of time slots that the teacher cannot be scheduled for. These time - // slot values index to the accumulative number of time slots starting at 0. - // For example, if a schedule rotation has 5 days and 8 time slots per day, - // and a teacher cannot be scheduled for the last time slot of the fourth - // day, the number here would be 31. - repeated int32 restricted_time_slots = 2; -} - -message Student { - // Student name, used only for logging purposes. - string display_name = 1; - - // List of course indices that the student needs to be enrolled in. - repeated int32 course_indices = 2; -} - -message Room { - // Room name, used only for logging purposes. - string display_name = 1; - - // Maximum number of students that can fit into this room. - int32 capacity = 2; -} - -// Status returned by the solver. -enum CourseSchedulingResultStatus { - COURSE_SCHEDULING_RESULT_STATUS_UNSPECIFIED = 0; - - // The solver had enough time to find some solution that satisfies all - // constraints, but it did not prove optimality (which means it may or may - // not have reached the optimal). - // - // This can happen for large LP models (linear programming), and is a frequent - // response for time-limited MIPs (mixed integer programming). This is also - // what the CP (constraint programming) solver will return if there is no - // objective specified. - SOLVER_FEASIBLE = 1; - - // The solver found the proven optimal solution. - SOLVER_OPTIMAL = 2; - - // The model does not have any solution, according to the solver (which - // "proved" it, with the caveat that numerical proofs aren't actual proofs), - // or based on trivial considerations (eg. a variable whose lower bound is - // strictly greater than its upper bound). - SOLVER_INFEASIBLE = 3; - - // Model errors. These are always deterministic and repeatable. - // They should be accompanied with a string description of the error. - SOLVER_MODEL_INVALID = 4; - - // The model has not been solved in the given time or the solver was not able - // to solve the model given. - SOLVER_NOT_SOLVED = 5; - - // An error (either numerical or from a bug in the code) occurred. - ABNORMAL = 6; -} diff --git a/examples/cpp/cryptarithm_sat_test.bintest b/examples/cpp/cryptarithm_sat_test.bintest new file mode 100644 index 0000000000..b919bcc24c --- /dev/null +++ b/examples/cpp/cryptarithm_sat_test.bintest @@ -0,0 +1 @@ +RUN: $(cryptarithm_sat) diff --git a/examples/cpp/dimacs_assignment.cc b/examples/cpp/dimacs_assignment.cc index bcf43712ab..b8f1917f8c 100644 --- a/examples/cpp/dimacs_assignment.cc +++ b/examples/cpp/dimacs_assignment.cc @@ -20,7 +20,7 @@ #include "absl/container/flat_hash_map.h" #include "absl/flags/flag.h" -#include "absl/strings/str_format.h" +#include "absl/strings/string_view.h" #include "examples/cpp/parse_dimacs_assignment.h" #include "examples/cpp/print_dimacs_assignment.h" #include "ortools/algorithms/hungarian.h" @@ -156,25 +156,18 @@ int SolveDimacsAssignment(int argc, char* argv[]) { } } // namespace operations_research -static const char* const kUsageTemplate = "usage: %s "; - using ::operations_research::ArcIndex; using ::operations_research::NodeIndex; using ::operations_research::SolveDimacsAssignment; int main(int argc, char* argv[]) { - std::string usage; - if (argc < 1) { - usage = absl::StrFormat(kUsageTemplate, "solve_dimacs_assignment"); - } else { - usage = absl::StrFormat(kUsageTemplate, argv[0]); - } - InitGoogle(usage.c_str(), &argc, &argv, true); + InitGoogle(argv[0], &argc, &argv, true); if (argc < 2) { - LOG(FATAL) << usage; + LOG(FATAL) << "Missing input file."; } + absl::SetStderrThreshold(absl::LogSeverityAtLeast::kInfo); if (absl::GetFlag(FLAGS_assignment_static_graph)) { return SolveDimacsAssignment<::util::StaticGraph>( argc, argv); diff --git a/examples/cpp/dimacs_assignment_max_cost_test.bintest b/examples/cpp/dimacs_assignment_max_cost_test.bintest new file mode 100644 index 0000000000..3352b7ad85 --- /dev/null +++ b/examples/cpp/dimacs_assignment_max_cost_test.bintest @@ -0,0 +1,2 @@ +RUN: $(dimacs_assignment) $(dimacs_example.txt) --assignment_maximize_cost 2>&1 +CHECK: "Cost of optimum assignment: -110" diff --git a/examples/cpp/dimacs_assignment_min_cost_test.bintest b/examples/cpp/dimacs_assignment_min_cost_test.bintest new file mode 100644 index 0000000000..0a816928ce --- /dev/null +++ b/examples/cpp/dimacs_assignment_min_cost_test.bintest @@ -0,0 +1,2 @@ +RUN: $(dimacs_assignment) $(dimacs_example.txt) 2>&1 +CHECK: "Cost of optimum assignment: 84" diff --git a/examples/cpp/dobble_ls_test.bintest b/examples/cpp/dobble_ls_test.bintest new file mode 100644 index 0000000000..65ba1b023b --- /dev/null +++ b/examples/cpp/dobble_ls_test.bintest @@ -0,0 +1 @@ +RUN: $(dobble_ls) --time_limit_in_ms=10000 diff --git a/examples/cpp/fap_parser.cc b/examples/cpp/fap_parser.cc index 4eb1a8f423..5dc61e2b76 100644 --- a/examples/cpp/fap_parser.cc +++ b/examples/cpp/fap_parser.cc @@ -21,6 +21,7 @@ #include "absl/strings/numbers.h" #include "absl/strings/str_split.h" #include "absl/strings/string_view.h" +#include "absl/types/span.h" #include "ortools/base/helpers.h" #include "ortools/base/map_util.h" @@ -94,7 +95,7 @@ void DomainParser::Parse() { } if (!domain.empty()) { - gtl::InsertOrUpdate(&domains_, key, domain); + domains_.insert_or_assign(key, domain); } } } @@ -198,7 +199,7 @@ void ParametersParser::Parse() { } // TODO(user): Make FindComponents linear instead of quadratic. -void FindComponents(const std::vector& constraints, +void FindComponents(absl::Span constraints, const absl::btree_map& variables, const int maximum_variable_id, absl::flat_hash_map* components) { @@ -216,20 +217,20 @@ void FindComponents(const std::vector& constraints, // Create a new one. FapComponent component; const int component_index = constraint_index; - gtl::InsertOrUpdate(&(component.variables), variable_id1, variable1); - gtl::InsertOrUpdate(&(component.variables), variable_id2, variable2); + (component.variables).insert_or_assign(variable_id1, variable1); + (component.variables).insert_or_assign(variable_id2, variable2); in_component[variable_id1] = component_index; in_component[variable_id2] = component_index; component.constraints.push_back(constraint); - gtl::InsertOrUpdate(components, component_index, component); + components->insert_or_assign(component_index, component); } else if (in_component[variable_id1] >= 0 && in_component[variable_id2] < 0) { // If variable1 belongs to an existing component, variable2 should // also be included in the same component. const int component_index = in_component[variable_id1]; CHECK(components->contains(component_index)); - gtl::InsertOrUpdate(&((*components)[component_index].variables), - variable_id2, variable2); + ((*components)[component_index].variables) + .insert_or_assign(variable_id2, variable2); in_component[variable_id2] = component_index; (*components)[component_index].constraints.push_back(constraint); } else if (in_component[variable_id1] < 0 && @@ -238,8 +239,8 @@ void FindComponents(const std::vector& constraints, // also be included in the same component. const int component_index = in_component[variable_id2]; CHECK(components->contains(component_index)); - gtl::InsertOrUpdate(&((*components)[component_index].variables), - variable_id1, variable1); + ((*components)[component_index].variables) + .insert_or_assign(variable_id1, variable1); in_component[variable_id1] = component_index; (*components)[component_index].constraints.push_back(constraint); } else { diff --git a/examples/cpp/fap_parser.h b/examples/cpp/fap_parser.h index 66e9f1706e..acdb63e3b0 100644 --- a/examples/cpp/fap_parser.h +++ b/examples/cpp/fap_parser.h @@ -23,6 +23,7 @@ #include "absl/container/btree_map.h" #include "absl/container/flat_hash_map.h" #include "absl/strings/string_view.h" +#include "absl/types/span.h" namespace operations_research { @@ -214,7 +215,7 @@ class ParametersParser { }; // Function that finds the disjoint sub-graphs of the graph of the instance. -void FindComponents(const std::vector& constraints, +void FindComponents(absl::Span constraints, const absl::btree_map& variables, int maximum_variable_id, absl::flat_hash_map* components); diff --git a/examples/cpp/flow_api_test.bintest b/examples/cpp/flow_api_test.bintest new file mode 100644 index 0000000000..6bca712df4 --- /dev/null +++ b/examples/cpp/flow_api_test.bintest @@ -0,0 +1 @@ +RUN: $(flow_api) diff --git a/examples/cpp/golomb_sat_test.bintest b/examples/cpp/golomb_sat_test.bintest new file mode 100644 index 0000000000..7fd6596d7a --- /dev/null +++ b/examples/cpp/golomb_sat_test.bintest @@ -0,0 +1 @@ +RUN: $(golomb_sat) --size 5 diff --git a/examples/cpp/integer_programming_test.bintest b/examples/cpp/integer_programming_test.bintest new file mode 100644 index 0000000000..bad463547b --- /dev/null +++ b/examples/cpp/integer_programming_test.bintest @@ -0,0 +1 @@ +RUN: $(integer_programming) diff --git a/examples/cpp/jobshop_sat_ft06.bintest b/examples/cpp/jobshop_sat_ft06.bintest new file mode 100644 index 0000000000..366a359b4e --- /dev/null +++ b/examples/cpp/jobshop_sat_ft06.bintest @@ -0,0 +1 @@ +RUN: $(jobshop_sat) --input $(ft06) diff --git a/examples/cpp/knapsack_2d_sat_class01_instance2_test.bintest b/examples/cpp/knapsack_2d_sat_class01_instance2_test.bintest new file mode 100644 index 0000000000..a7695406e7 --- /dev/null +++ b/examples/cpp/knapsack_2d_sat_class01_instance2_test.bintest @@ -0,0 +1 @@ +RUN: $(knapsack_2d_sat) --input $(Class_01.2bp) --instance 2 diff --git a/examples/cpp/linear_assignment_api_test.bintest b/examples/cpp/linear_assignment_api_test.bintest new file mode 100644 index 0000000000..3cf36e00c4 --- /dev/null +++ b/examples/cpp/linear_assignment_api_test.bintest @@ -0,0 +1 @@ +RUN: $(linear_assignment_api) diff --git a/examples/cpp/linear_programming_test.bintest b/examples/cpp/linear_programming_test.bintest new file mode 100644 index 0000000000..dc7d4189d7 --- /dev/null +++ b/examples/cpp/linear_programming_test.bintest @@ -0,0 +1 @@ +RUN: $(linear_programming) diff --git a/examples/cpp/linear_solver_protocol_buffers_test.bintest b/examples/cpp/linear_solver_protocol_buffers_test.bintest new file mode 100644 index 0000000000..a8997d9878 --- /dev/null +++ b/examples/cpp/linear_solver_protocol_buffers_test.bintest @@ -0,0 +1 @@ +RUN: $(linear_solver_protocol_buffers) diff --git a/examples/cpp/magic_sequence_sat_test.bintest b/examples/cpp/magic_sequence_sat_test.bintest new file mode 100644 index 0000000000..0e9b697388 --- /dev/null +++ b/examples/cpp/magic_sequence_sat_test.bintest @@ -0,0 +1 @@ +RUN: $(magic_sequence_sat) diff --git a/examples/cpp/magic_square_sat_test.bintest b/examples/cpp/magic_square_sat_test.bintest new file mode 100644 index 0000000000..4830bec534 --- /dev/null +++ b/examples/cpp/magic_square_sat_test.bintest @@ -0,0 +1 @@ +RUN: $(magic_square_sat) diff --git a/examples/cpp/max_flow_test.bintest b/examples/cpp/max_flow_test.bintest new file mode 100644 index 0000000000..530459b682 --- /dev/null +++ b/examples/cpp/max_flow_test.bintest @@ -0,0 +1 @@ +RUN: $(max_flow) diff --git a/examples/cpp/min_cost_flow_test.bintest b/examples/cpp/min_cost_flow_test.bintest new file mode 100644 index 0000000000..322e309acf --- /dev/null +++ b/examples/cpp/min_cost_flow_test.bintest @@ -0,0 +1 @@ +RUN: $(min_cost_flow) diff --git a/examples/cpp/mps_driver_test.bintest b/examples/cpp/mps_driver_test.bintest new file mode 100644 index 0000000000..37e2f8c20d --- /dev/null +++ b/examples/cpp/mps_driver_test.bintest @@ -0,0 +1 @@ +RUN: $(mps_driver) --input $(maximization.mps) diff --git a/examples/cpp/multi_knapsack_sat_test.bintest b/examples/cpp/multi_knapsack_sat_test.bintest new file mode 100644 index 0000000000..1a51c1921b --- /dev/null +++ b/examples/cpp/multi_knapsack_sat_test.bintest @@ -0,0 +1 @@ +RUN: $(multi_knapsack_sat) diff --git a/examples/cpp/network_routing_sat_test.bintest b/examples/cpp/network_routing_sat_test.bintest new file mode 100644 index 0000000000..37387090b0 --- /dev/null +++ b/examples/cpp/network_routing_sat_test.bintest @@ -0,0 +1 @@ +RUN: $(network_routing_sat) --clients=10 --backbones=5 --demands=10 --traffic_min=5 --traffic_max=10 --min_client_degree=2 --max_client_degree=5 --min_backbone_degree=3 --max_backbone_degree=5 --max_capacity=20 --fixed_charge_cost=10 diff --git a/examples/cpp/nqueens_test.bintest b/examples/cpp/nqueens_test.bintest new file mode 100644 index 0000000000..d0c8332ca3 --- /dev/null +++ b/examples/cpp/nqueens_test.bintest @@ -0,0 +1 @@ +RUN: $(nqueens) diff --git a/examples/cpp/pdptw_non_homogenous_fleet_test.bintest b/examples/cpp/pdptw_non_homogenous_fleet_test.bintest new file mode 100644 index 0000000000..96762b4ccf --- /dev/null +++ b/examples/cpp/pdptw_non_homogenous_fleet_test.bintest @@ -0,0 +1,2 @@ +RUN: $(pdptw) --pdp_file=$(lc102.txt) --reduce_vehicle_cost_model=false --routing_search_parameters=first_solution_strategy:BEST_INSERTION 2>&1 +CHECK: "Cost: 1000828.936870" diff --git a/examples/cpp/pdptw_test.bintest b/examples/cpp/pdptw_test.bintest new file mode 100644 index 0000000000..d3a23a46c7 --- /dev/null +++ b/examples/cpp/pdptw_test.bintest @@ -0,0 +1,2 @@ +RUN: $(pdptw) --pdp_file=$(lc102.txt) 2>&1 +CHECK: "Cost: 1000828.936870" diff --git a/examples/cpp/pdptw_with_alternatives.cc b/examples/cpp/pdptw_with_alternatives.cc new file mode 100644 index 0000000000..1d485ede2c --- /dev/null +++ b/examples/cpp/pdptw_with_alternatives.cc @@ -0,0 +1,377 @@ +// 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. + +// Pickup and Delivery Problem with Time Windows and Alternatives. +// This is a variant of the mode in pdptw.cc (see that file for more details +// on pickup and delivery models). In this model both pickups and deliveries +// have alternative locations, of which one of each has to be selected. As in +// the standard pickup and delivery problem, pickups must happen before +// deliveries and must be on the same route. + +#include +#include +#include +#include +#include +#include + +#include "absl/base/log_severity.h" +#include "absl/flags/flag.h" +#include "absl/functional/bind_front.h" +#include "absl/log/check.h" +#include "absl/log/globals.h" +#include "absl/log/log.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_split.h" +#include "absl/strings/string_view.h" +#include "google/protobuf/text_format.h" +#include "ortools/base/helpers.h" +#include "ortools/base/init_google.h" +#include "ortools/base/options.h" +#include "ortools/constraint_solver/constraint_solver.h" +#include "ortools/routing/index_manager.h" +#include "ortools/routing/parameters.h" +#include "ortools/routing/parameters.pb.h" +#include "ortools/routing/routing.h" + +ABSL_FLAG(std::string, pdp_file, "", + "File containing the Pickup and Delivery Problem to solve."); +ABSL_FLAG(int, pdp_force_vehicles, 0, + "Force the number of vehicles used (maximum number of routes."); +ABSL_FLAG(bool, reduce_vehicle_cost_model, true, + "Overrides the homonymous field of " + "DefaultRoutingModelParameters()."); +ABSL_FLAG(std::string, routing_search_parameters, + "first_solution_strategy:ALL_UNPERFORMED " + "local_search_operators { use_node_pair_swap_active:BOOL_FALSE }", + "Text proto RoutingSearchParameters (possibly partial) that will " + "override the DefaultRoutingSearchParameters()"); + +using ::absl::bind_front; + +namespace operations_research::routing { + +// Scaling factor used to scale up distances, allowing a bit more precision +// from Euclidean distances. +const int64_t kScalingFactor = 1000; + +// Vector of (x,y) node coordinates, *unscaled*, in some imaginary planar, +// metric grid. +typedef std::vector> Coordinates; + +// Returns the scaled Euclidean distance between two nodes, coords holding the +// coordinates of the nodes. +int64_t Travel(const Coordinates* const coords, + RoutingIndexManager::NodeIndex from, + RoutingIndexManager::NodeIndex to) { + DCHECK(coords != nullptr); + const int xd = coords->at(from.value()).first - coords->at(to.value()).first; + const int yd = + coords->at(from.value()).second - coords->at(to.value()).second; + return static_cast(kScalingFactor * + std::sqrt(1.0L * xd * xd + yd * yd)); +} + +// Returns the scaled service time at a given node, service_times holding the +// service times. +int64_t ServiceTime(const std::vector* const service_times, + RoutingIndexManager::NodeIndex node) { + return kScalingFactor * service_times->at(node.value()); +} + +// Returns the scaled (distance plus service time) between two indices, coords +// holding the coordinates of the nodes and service_times holding the service +// times. +// The service time is the time spent to execute a delivery or a pickup. +int64_t TravelPlusServiceTime(const RoutingIndexManager& manager, + const Coordinates* const coords, + const std::vector* const service_times, + int64_t from_index, int64_t to_index) { + const RoutingIndexManager::NodeIndex from = manager.IndexToNode(from_index); + const RoutingIndexManager::NodeIndex to = manager.IndexToNode(to_index); + return ServiceTime(service_times, from) + Travel(coords, from, to); +} + +// Returns the demand (quantity picked up or delivered) of an index, demands +// holds the demand of each node. +int64_t Demand(const RoutingIndexManager& manager, + const std::vector* const demands, int64_t from_index, + int64_t to_index) { + (void)to_index; + return demands->at(manager.IndexToNode(from_index).value()); +} + +// Outputs a solution to the current model in a string. +std::string VerboseOutput(const RoutingModel& routing, + const RoutingIndexManager& manager, + const Assignment& assignment, + const Coordinates& coords, + const std::vector& service_times) { + std::string output; + const RoutingDimension& time_dimension = routing.GetDimensionOrDie("time"); + const RoutingDimension& load_dimension = routing.GetDimensionOrDie("demand"); + for (int i = 0; i < routing.vehicles(); ++i) { + absl::StrAppendFormat(&output, "Vehicle %d: ", i); + int64_t index = routing.Start(i); + if (routing.IsEnd(assignment.Value(routing.NextVar(index)))) { + output.append("empty"); + } else { + while (!routing.IsEnd(index)) { + absl::StrAppendFormat(&output, "%d ", + manager.IndexToNode(index).value()); + const IntVar* vehicle = routing.VehicleVar(index); + absl::StrAppendFormat(&output, "Vehicle(%d) ", + assignment.Value(vehicle)); + const IntVar* arrival = time_dimension.CumulVar(index); + absl::StrAppendFormat(&output, "Time(%d..%d) ", assignment.Min(arrival), + assignment.Max(arrival)); + const IntVar* load = load_dimension.CumulVar(index); + absl::StrAppendFormat(&output, "Load(%d..%d) ", assignment.Min(load), + assignment.Max(load)); + const int64_t next_index = assignment.Value(routing.NextVar(index)); + absl::StrAppendFormat( + &output, "Transit(%d) ", + TravelPlusServiceTime(manager, &coords, &service_times, index, + next_index)); + index = next_index; + } + output.append("Route end "); + const IntVar* vehicle = routing.VehicleVar(index); + absl::StrAppendFormat(&output, "Vehicle(%d) ", assignment.Value(vehicle)); + const IntVar* arrival = time_dimension.CumulVar(index); + absl::StrAppendFormat(&output, "Time(%d..%d) ", assignment.Min(arrival), + assignment.Max(arrival)); + const IntVar* load = load_dimension.CumulVar(index); + absl::StrAppendFormat(&output, "Load(%d..%d) ", assignment.Min(load), + assignment.Max(load)); + } + output.append("\n"); + } + return output; +} + +namespace { +// An inefficient but convenient method to parse a whitespace-separated list +// of integers. Returns true iff the input string was entirely valid and parsed. +bool SafeParseInt64Array(const std::string& str, + std::vector* parsed_int) { + static const char kWhiteSpaces[] = " \t\n\v\f\r"; + parsed_int->clear(); + for (absl::string_view token : + absl::StrSplit(str, absl::ByAnyChar(kWhiteSpaces), absl::SkipEmpty())) { + int value; + if (!absl::SimpleAtoi(token, &value)) return false; + parsed_int->push_back(value); + } + return true; +} +} // namespace + +// Builds and solves a model from a file in the format defined by Li & Lim +// (https://www.sintef.no/projectweb/top/pdptw/li-lim-benchmark/documentation/). +bool LoadAndSolve(const std::string& pdp_file, + const RoutingModelParameters& model_parameters, + const RoutingSearchParameters& search_parameters) { + // Load all the lines of the file in RAM (it shouldn't be too large anyway). + std::vector lines; + { + std::string contents; + CHECK_OK(file::GetContents(pdp_file, &contents, file::Defaults())); + const int64_t kMaxInputFileSize = 1 << 30; // 1GB + if (contents.size() >= kMaxInputFileSize) { + LOG(WARNING) << "Input file '" << pdp_file << "' is too large (>" + << kMaxInputFileSize << " bytes)."; + return false; + } + lines = absl::StrSplit(contents, '\n', absl::SkipEmpty()); + } + // Reading header. + if (lines.empty()) { + LOG(WARNING) << "Empty file: " << pdp_file; + return false; + } + // Parse file header. + std::vector parsed_int; + if (!SafeParseInt64Array(lines[0], &parsed_int) || parsed_int.size() != 3 || + parsed_int[0] < 0 || parsed_int[1] < 0 || parsed_int[2] < 0) { + LOG(WARNING) << "Malformed header: " << lines[0]; + return false; + } + const int num_vehicles = absl::GetFlag(FLAGS_pdp_force_vehicles) > 0 + ? absl::GetFlag(FLAGS_pdp_force_vehicles) + : parsed_int[0]; + const int64_t capacity = parsed_int[1]; + // We do not care about the 'speed' field, in third position. + + // Parse order data. + std::vector customer_ids; + std::vector> coords; + std::vector demands; + std::vector open_times; + std::vector close_times; + std::vector service_times; + std::vector pickups; + std::vector deliveries; + int64_t horizon = 0; + RoutingIndexManager::NodeIndex depot(0); + for (int line_index = 1; line_index < lines.size(); ++line_index) { + if (!SafeParseInt64Array(lines[line_index], &parsed_int) || + parsed_int.size() != 9 || parsed_int[0] < 0 || parsed_int[4] < 0 || + parsed_int[5] < 0 || parsed_int[6] < 0 || parsed_int[7] < 0 || + parsed_int[8] < 0) { + LOG(WARNING) << "Malformed line #" << line_index << ": " + << lines[line_index]; + return false; + } + const int customer_id = parsed_int[0]; + const int x = parsed_int[1]; + const int y = parsed_int[2]; + const int64_t demand = parsed_int[3]; + const int64_t open_time = parsed_int[4]; + const int64_t close_time = parsed_int[5]; + const int64_t service_time = parsed_int[6]; + const int pickup = parsed_int[7]; + const int delivery = parsed_int[8]; + customer_ids.push_back(customer_id); + coords.push_back(std::make_pair(x, y)); + demands.push_back(demand); + open_times.push_back(open_time); + close_times.push_back(close_time); + service_times.push_back(service_time); + pickups.push_back(RoutingIndexManager::NodeIndex(pickup)); + deliveries.push_back(RoutingIndexManager::NodeIndex(delivery)); + if (pickup == 0 && delivery == 0) { + depot = RoutingIndexManager::NodeIndex(pickups.size() - 1); + } + horizon = std::max(horizon, close_time); + } + + // Build pickup and delivery model. + const int num_nodes = customer_ids.size(); + RoutingIndexManager manager(num_nodes, num_vehicles, depot); + RoutingModel routing(manager, model_parameters); + const int vehicle_cost = routing.RegisterTransitCallback( + [&coords, &manager](int64_t i, int64_t j) { + return Travel(const_cast(&coords), + manager.IndexToNode(i), manager.IndexToNode(j)); + }); + routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost); + routing.AddDimension( + routing.RegisterTransitCallback(absl::bind_front( + TravelPlusServiceTime, manager, + const_cast(&coords), + const_cast*>(&service_times))), + kScalingFactor * horizon, kScalingFactor * horizon, + /*fix_start_cumul_to_zero=*/true, "time"); + const RoutingDimension& time_dimension = routing.GetDimensionOrDie("time"); + Solver* const solver = routing.solver(); + + // Collect pickup and delivery pairs and set time windows. + std::vector> pickup_delivery_pairs; + for (RoutingIndexManager::NodeIndex order(0); order < routing.nodes(); + ++order) { + const int64_t index = manager.NodeToIndex(order); + IntVar* const cumul = time_dimension.CumulVar(index); + cumul->SetMin(kScalingFactor * open_times[order.value()]); + cumul->SetMax(kScalingFactor * close_times[order.value()]); + RoutingIndexManager::NodeIndex delivery = deliveries[order.value()]; + if (pickups[order.value()] == 0 && delivery != 0) { + pickup_delivery_pairs.push_back({index, manager.NodeToIndex(delivery)}); + } + } + + // Build groups of pickup and delivery pairs representing the alternatives of + // pickup and delivery locations for a given shipment, and add the + // corresponding constraints. + const int kGroupSize = 4; + const int64_t kPenalty = 10000000; + // Collecting demands per group computed as the average demand for the group. + std::vector group_demands(demands.size()); + for (int pair_index = 0; pair_index < pickup_delivery_pairs.size();) { + std::vector pickup_indices; + std::vector delivery_indices; + std::vector pickup_vehicle_variables; + std::vector delivery_vehicle_variables; + int64_t demand_sum = 0; + int pair_start = pair_index; + for (int i = 0; i < kGroupSize && pair_index < pickup_delivery_pairs.size(); + ++i, ++pair_index) { + const int64_t pickup = pickup_delivery_pairs[pair_index].first; + const int64_t delivery = pickup_delivery_pairs[pair_index].second; + pickup_indices.push_back(pickup); + delivery_indices.push_back(delivery); + pickup_vehicle_variables.push_back(routing.VehicleVar(pickup)); + delivery_vehicle_variables.push_back(routing.VehicleVar(delivery)); + demand_sum += demands[manager.IndexToNode(pickup).value()]; + } + // Computing demand average. + int64_t demand_avg = demand_sum / (pair_index - pair_start); + for (int i = pair_start; i < pair_index; ++i) { + group_demands[pickup_delivery_pairs[i].first] = demand_avg; + group_demands[pickup_delivery_pairs[i].second] = -demand_avg; + } + // Unperformed pickups or deliveries will have their vehicle variable set + // to -1. Therefore the vehicle performing the performed pickup (resp. the + // performed delivery) is the maximum of the vehicle variables of the + // pickups (resp. deliveries). Using this to ensure the performed pickup + // and delivery are on the same route. + solver->AddConstraint( + solver->MakeEquality(solver->MakeMax(pickup_vehicle_variables), + solver->MakeMax(delivery_vehicle_variables))); + // Only one pickup and one delivery must be performed and notify the solver + // about the pickup and delivery alternatives. + routing.AddPickupAndDeliverySets( + routing.AddDisjunction(pickup_indices, kPenalty), + routing.AddDisjunction(delivery_indices, kPenalty)); + } + // Add demand dimension where the demand corresponds to the average demand + // of the group. + routing.AddDimension( + routing.RegisterTransitCallback(absl::bind_front( + Demand, manager, + const_cast*>(&group_demands))), + 0, capacity, /*fix_start_cumul_to_zero=*/true, "demand"); + + // Solve pickup and delivery problem. + const Assignment* assignment = routing.SolveWithParameters(search_parameters); + LOG(INFO) << routing.solver()->LocalSearchProfile(); + if (nullptr != assignment) { + LOG(INFO) << "Cost: " << assignment->ObjectiveValue(); + LOG(INFO) << VerboseOutput(routing, manager, *assignment, coords, + service_times); + return true; + } + return false; +} + +} // namespace operations_research::routing + +int main(int argc, char** argv) { + absl::SetStderrThreshold(absl::LogSeverityAtLeast::kInfo); + InitGoogle(argv[0], &argc, &argv, true); + // Set up model and search parameters. + operations_research::routing::RoutingModelParameters model_parameters = + operations_research::routing::DefaultRoutingModelParameters(); + model_parameters.set_reduce_vehicle_cost_model( + absl::GetFlag(FLAGS_reduce_vehicle_cost_model)); + operations_research::routing::RoutingSearchParameters search_parameters = + operations_research::routing::DefaultRoutingSearchParameters(); + CHECK(google::protobuf::TextFormat::MergeFromString( + absl::GetFlag(FLAGS_routing_search_parameters), &search_parameters)); + if (!operations_research::routing::LoadAndSolve( + absl::GetFlag(FLAGS_pdp_file), model_parameters, search_parameters)) { + LOG(INFO) << "Error solving " << absl::GetFlag(FLAGS_pdp_file); + } + return 0; +} diff --git a/examples/cpp/pdptw_with_alternatives_non_homogenous_fleet_test.bintest b/examples/cpp/pdptw_with_alternatives_non_homogenous_fleet_test.bintest new file mode 100644 index 0000000000..54406d4f3c --- /dev/null +++ b/examples/cpp/pdptw_with_alternatives_non_homogenous_fleet_test.bintest @@ -0,0 +1,2 @@ +RUN: $(pdptw_with_alternatives) --pdp_file=$(lc102.txt) --reduce_vehicle_cost_model=false --routing_search_parameters='first_solution_strategy:BEST_INSERTION local_search_operators { use_node_pair_swap_active:BOOL_FALSE }' 2>&1 +CHECK: "Cost: 362934" diff --git a/examples/cpp/pdptw_with_alternatives_test.bintest b/examples/cpp/pdptw_with_alternatives_test.bintest new file mode 100644 index 0000000000..09fb54c425 --- /dev/null +++ b/examples/cpp/pdptw_with_alternatives_test.bintest @@ -0,0 +1,2 @@ +RUN: $(pdptw_with_alternatives) --pdp_file=$(lc102.txt) 2>&1 +CHECK: "Cost: 361237" diff --git a/examples/cpp/shift_minimization_sat_test.bintest b/examples/cpp/shift_minimization_sat_test.bintest new file mode 100644 index 0000000000..3694cdf27c --- /dev/null +++ b/examples/cpp/shift_minimization_sat_test.bintest @@ -0,0 +1 @@ +RUN: $(shift_minimization_sat) --input $(shift_minimization.dat) diff --git a/examples/cpp/slitherlink_sat_test.bintest b/examples/cpp/slitherlink_sat_test.bintest new file mode 100644 index 0000000000..3561a08a6c --- /dev/null +++ b/examples/cpp/slitherlink_sat_test.bintest @@ -0,0 +1 @@ +RUN: $(slitherlink_sat) diff --git a/examples/cpp/sports_scheduling_sat_test.bintest b/examples/cpp/sports_scheduling_sat_test.bintest new file mode 100644 index 0000000000..8cb0012f86 --- /dev/null +++ b/examples/cpp/sports_scheduling_sat_test.bintest @@ -0,0 +1 @@ +RUN: $(sports_scheduling_sat) diff --git a/examples/cpp/strawberry_fields_with_column_generation_test.bintest b/examples/cpp/strawberry_fields_with_column_generation_test.bintest new file mode 100644 index 0000000000..6668965b9c --- /dev/null +++ b/examples/cpp/strawberry_fields_with_column_generation_test.bintest @@ -0,0 +1 @@ +RUN: $(strawberry_fields_with_column_generation) --colgen_instance=4 diff --git a/examples/cpp/testdata/cgc/1.in b/examples/cpp/testdata/cgc/1.in new file mode 100644 index 0000000000..00776d701a --- /dev/null +++ b/examples/cpp/testdata/cgc/1.in @@ -0,0 +1,6 @@ +4 +4 4 +1 2 6 100 +1 3 2 2 +1 4 2 2 +3 4 2 10 diff --git a/examples/cpp/testdata/cgc/2.in b/examples/cpp/testdata/cgc/2.in new file mode 100644 index 0000000000..b009f8917f --- /dev/null +++ b/examples/cpp/testdata/cgc/2.in @@ -0,0 +1,6 @@ +4 +4 4 +1 1 1 1 +2 1 1 1 +3 1 1 1 +4 1 1 1 diff --git a/examples/cpp/testdata/cgc/3.in b/examples/cpp/testdata/cgc/3.in new file mode 100644 index 0000000000..184b23bfbc --- /dev/null +++ b/examples/cpp/testdata/cgc/3.in @@ -0,0 +1,3 @@ +1 +4 4 +2 2 2 10 diff --git a/examples/cpp/testdata/cgc/cgcut1.in b/examples/cpp/testdata/cgc/cgcut1.in new file mode 100644 index 0000000000..9a3483b219 --- /dev/null +++ b/examples/cpp/testdata/cgc/cgcut1.in @@ -0,0 +1,9 @@ +7 +15 10 +8 4 2 66 +3 7 1 35 +8 2 3 24 +3 4 5 17 +3 3 2 11 +3 2 2 8 +2 1 1 2 diff --git a/examples/cpp/testdata/cgc/cgcut2.in b/examples/cpp/testdata/cgc/cgcut2.in new file mode 100644 index 0000000000..3929f10817 --- /dev/null +++ b/examples/cpp/testdata/cgc/cgcut2.in @@ -0,0 +1,12 @@ +10 +40 70 +21 22 1 582 +31 13 1 403 +9 35 3 315 +9 24 3 216 +30 7 2 210 +11 13 3 143 +10 14 1 140 +14 8 3 110 +12 8 3 94 +13 7 3 90 diff --git a/examples/cpp/testdata/cgc/cgcut3.in b/examples/cpp/testdata/cgc/cgcut3.in new file mode 100644 index 0000000000..30d4d75c0e --- /dev/null +++ b/examples/cpp/testdata/cgc/cgcut3.in @@ -0,0 +1,22 @@ +20 +40 70 +31 43 4 500 +30 41 2 480 +29 39 4 460 +28 38 4 440 +27 37 3 420 +26 36 4 410 +25 35 3 400 +24 34 4 380 +33 23 4 360 +22 32 3 340 +31 21 3 320 +29 18 3 300 +17 27 2 280 +15 24 2 240 +16 25 4 260 +15 24 1 240 +23 14 4 220 +21 12 3 180 +19 11 4 160 +9 17 1 140 diff --git a/examples/cpp/testdata/dimacs_example.txt b/examples/cpp/testdata/dimacs_example.txt new file mode 100644 index 0000000000..09f209c3fd --- /dev/null +++ b/examples/cpp/testdata/dimacs_example.txt @@ -0,0 +1,25 @@ +c Simple example file demonstrating the DIMACS data format, and for testing. +c Source: Invented from scratch by viger@google.com on 2019-05-22. +c Lines starting with 'c' (like this one) are comments. +c +c Graph description: (number of nodes) (number of arcs) +p asn 6 9 +c Note that the problems are 'perfect assignment' problems, where +c there are as many 'left' nodes as 'right' nodes, and we want to assign +c each 'left' node to exactly one 'right' node. +c +c "Left" nodes. +n 1 +n 2 +n 3 +c +c Arcs: left node, right node, cost of assigning left to right. +a 1 4 12 +a 1 5 53 +a 1 6 36 +a 2 4 14 +a 2 5 37 +a 2 6 46 +a 3 4 11 +a 3 5 52 +a 3 6 35 diff --git a/examples/cpp/testdata/lc102.txt b/examples/cpp/testdata/lc102.txt new file mode 100644 index 0000000000..0fde4b188d --- /dev/null +++ b/examples/cpp/testdata/lc102.txt @@ -0,0 +1,108 @@ +25 200 1 +0 40 50 0 0 1236 0 0 0 +1 45 68 10 0 1127 90 0 75 +2 45 70 -20 0 1125 90 8 0 +3 42 66 10 0 1129 90 0 10 +4 42 68 -20 727 782 90 6 0 +5 42 65 10 0 1130 90 0 9 +6 40 69 20 621 702 90 0 4 +7 40 66 20 0 1130 90 0 11 +8 38 68 20 255 324 90 0 2 +9 38 70 -10 534 605 90 5 0 +10 35 66 -10 357 410 90 3 0 +11 35 69 -20 448 505 90 7 0 +12 25 85 -30 0 1107 90 13 0 +13 22 75 30 30 92 90 0 12 +14 22 85 -40 567 620 90 16 0 +15 20 80 -20 384 429 90 18 0 +16 20 85 40 475 528 90 0 14 +17 18 75 20 99 148 90 0 19 +18 15 75 20 179 254 90 0 15 +19 15 80 -20 278 345 90 17 0 +20 30 50 10 10 73 90 0 22 +21 30 52 -10 0 1135 90 23 0 +22 28 52 -10 812 883 90 20 0 +23 28 55 10 732 777 90 0 21 +24 25 50 10 65 144 90 0 25 +25 25 52 -10 169 224 90 24 0 +26 25 55 -10 0 1130 90 29 0 +27 23 52 10 261 316 90 0 30 +28 23 55 20 546 593 0 0 103 +29 20 50 10 358 405 90 0 26 +30 20 55 -10 449 504 90 27 0 +31 10 35 20 0 1112 90 0 35 +32 10 40 30 31 100 90 0 33 +33 8 40 -30 87 158 90 32 0 +34 8 45 -20 0 1113 90 37 0 +35 5 35 -20 283 344 90 31 0 +36 5 45 -30 665 716 90 38 0 +37 2 40 20 0 1106 90 0 34 +38 0 40 30 479 522 90 0 36 +39 0 45 20 567 624 0 0 104 +40 35 30 -10 264 321 90 43 0 +41 35 32 10 166 235 90 0 51 +42 33 32 20 68 149 90 0 48 +43 33 35 10 16 80 90 0 40 +44 32 30 10 359 412 90 0 47 +45 30 30 -30 541 600 90 46 0 +46 30 32 30 448 509 90 0 45 +47 30 35 -10 1054 1127 90 44 0 +48 28 30 -20 0 1122 90 42 0 +49 28 35 -10 1001 1066 90 52 0 +50 26 32 10 0 1123 0 0 106 +51 25 30 -10 725 786 90 41 0 +52 25 35 10 0 1124 90 0 49 +53 44 5 -10 286 347 90 55 0 +54 42 10 40 186 257 90 0 56 +55 42 15 10 95 158 90 0 53 +56 40 5 -40 385 436 90 54 0 +57 40 15 40 35 87 90 0 60 +58 38 5 30 471 534 90 0 59 +59 38 15 -30 0 1110 90 58 0 +60 35 5 -40 562 629 90 57 0 +61 50 30 -10 531 610 90 67 0 +62 50 35 20 262 317 90 0 66 +63 50 40 50 171 218 90 0 69 +64 48 30 -50 632 693 90 74 0 +65 48 40 10 76 129 90 0 72 +66 47 35 -20 826 875 90 62 0 +67 47 40 10 12 77 90 0 61 +68 45 30 10 734 777 0 0 102 +69 45 35 -50 916 969 90 63 0 +70 95 30 -30 387 456 90 81 0 +71 95 35 20 293 360 90 0 77 +72 53 30 -10 0 1122 90 65 0 +73 92 30 -10 478 551 90 76 0 +74 53 35 50 353 412 90 0 64 +75 45 65 -10 0 1130 90 1 0 +76 90 35 10 203 260 90 0 73 +77 88 30 -20 574 643 90 71 0 +78 88 35 20 109 170 0 0 105 +79 87 30 10 668 731 90 0 80 +80 85 25 -10 769 820 90 79 0 +81 85 35 30 47 124 90 0 70 +82 75 55 20 0 1110 90 0 85 +83 72 55 10 0 1113 90 0 84 +84 70 58 -10 458 523 90 83 0 +85 68 60 -20 0 1116 90 82 0 +86 66 55 -10 173 238 90 90 0 +87 65 55 20 85 144 90 0 89 +88 65 60 30 645 708 90 0 91 +89 63 58 -20 737 802 90 87 0 +90 60 55 10 20 84 90 0 86 +91 60 60 -30 0 1123 90 88 0 +92 67 85 -10 368 441 90 96 0 +93 65 85 40 475 518 90 0 99 +94 65 82 -20 0 1105 90 98 0 +95 62 80 30 0 1108 90 0 100 +96 60 80 10 0 1109 90 0 92 +97 60 85 30 561 622 0 0 101 +98 58 75 20 0 1115 90 0 94 +99 55 80 -40 743 820 90 93 0 +100 55 85 -30 647 726 90 95 0 +101 60 85 -30 561 622 90 97 0 +102 45 30 -10 734 777 90 68 0 +103 23 55 -20 546 593 90 28 0 +104 0 45 -20 567 624 90 39 0 +105 88 35 -20 109 170 90 78 0 +106 26 32 -10 0 1123 90 50 0 diff --git a/examples/cpp/testdata/shift_minimization.dat b/examples/cpp/testdata/shift_minimization.dat new file mode 100644 index 0000000000..ddb7661046 --- /dev/null +++ b/examples/cpp/testdata/shift_minimization.dat @@ -0,0 +1,69 @@ +# Randomly generated data for apersonnel scheduling problem +# ./datagen tightness = 90 Multi-skilling level = 66 +# Random number generator seed = 0 +Type = 1 +Jobs = 40 + 43 516 + 164 746 + 75 591 + 230 718 + 839 1354 + 96 637 + 1 593 + 179 713 + 130 765 + 119 688 + 194 783 + 270 818 + 102 618 + 774 1291 + 28 550 + 56 630 + 758 1350 + 804 1320 + 16 557 + 16 565 + 30 536 + 186 752 + 677 1259 + 739 1244 + 834 1313 + 724 1346 + 761 1304 + 823 1396 + 569 1040 + 804 1340 + 125 740 + 764 1256 + 159 656 + 712 1278 + 726 1291 + 651 1235 + 750 1278 + 725 1363 + 867 1376 + 844 1319 +Qualifications = 23 + 26: 6 13 0 1 2 3 4 8 11 12 14 15 16 17 20 21 23 24 25 26 29 30 31 33 37 39 + 24: 14 16 1 4 6 8 12 15 17 18 19 20 22 23 25 27 28 31 34 35 36 37 38 39 + 28: 30 36 1 2 3 4 6 9 10 11 12 13 15 16 17 18 21 22 23 24 26 27 32 33 34 35 38 39 + 24: 7 25 0 2 4 5 6 10 13 15 16 17 21 23 26 27 29 30 31 32 34 35 36 38 + 29: 8 29 0 1 2 6 7 10 12 13 14 15 17 18 20 21 22 24 25 28 30 31 32 33 34 35 36 37 39 + 32: 5 37 0 1 2 3 4 7 9 10 11 12 13 14 15 16 19 20 22 23 24 25 26 27 28 31 32 33 35 36 38 39 + 26: 18 28 0 1 2 3 4 6 8 10 11 15 16 17 19 20 21 22 23 24 25 29 30 31 36 38 + 29: 0 26 1 2 3 4 6 7 8 10 12 13 14 15 16 17 18 19 20 23 24 27 28 31 32 34 35 37 38 + 30: 11 39 2 4 6 7 8 9 10 13 14 15 16 17 18 20 21 23 25 26 27 29 30 31 32 34 35 36 37 38 + 24: 19 24 0 1 2 5 8 9 10 11 15 17 20 21 26 27 28 29 30 31 34 36 37 38 + 30: 3 34 0 2 4 6 7 8 9 10 11 13 14 15 16 17 19 23 24 25 27 29 31 32 33 35 36 37 38 39 + 28: 21 31 0 1 3 5 8 9 12 13 16 17 18 19 20 22 24 25 26 27 28 30 32 34 35 36 37 39 + 29: 32 33 0 1 2 3 4 5 6 7 9 10 11 12 13 16 17 18 20 21 23 24 28 29 31 34 35 36 37 + 35: 9 23 1 2 4 5 6 7 8 10 11 12 14 15 16 17 18 19 20 22 24 25 26 27 28 29 30 31 32 33 34 35 36 37 39 + 31: 20 27 1 2 3 4 5 6 8 9 10 12 14 15 16 17 18 19 21 24 25 26 29 31 33 34 35 36 37 38 39 + 23: 15 38 0 1 4 5 7 11 12 13 14 16 17 18 19 20 21 25 27 31 33 35 37 + 24: 1 4 0 2 7 8 11 13 16 17 21 22 23 24 25 27 28 29 31 33 34 36 38 39 + 28: 2 22 0 1 4 5 6 7 8 10 11 12 13 15 16 18 20 21 24 26 27 30 31 33 35 37 38 39 + 29: 12 35 0 1 2 3 5 6 7 8 11 13 15 16 17 18 19 20 22 23 25 26 30 31 33 36 37 38 39 + 28: 10 17 0 2 3 5 7 8 9 13 14 15 16 19 20 22 23 24 25 27 29 30 31 32 35 36 38 39 + 33: 18 29 26 27 23 31 12 2 1 35 10 19 30 14 11 13 17 21 3 0 33 22 7 15 16 25 4 32 28 39 34 20 5 + 31: 6 5 10 30 23 34 36 20 12 8 39 16 0 18 3 17 22 21 35 25 15 37 13 2 27 29 31 11 38 33 32 + 33: 11 7 17 19 6 32 1 15 8 18 24 35 16 5 26 28 34 33 38 37 14 0 3 12 22 21 13 39 25 2 36 10 4 diff --git a/examples/cpp/testdata/wt40.txt b/examples/cpp/testdata/wt40.txt new file mode 100644 index 0000000000..c4dbb7ab17 --- /dev/null +++ b/examples/cpp/testdata/wt40.txt @@ -0,0 +1,751 @@ + 26 24 79 46 32 35 73 74 14 67 86 46 78 40 29 94 64 27 90 55 + 35 52 36 69 85 95 14 78 37 86 44 28 39 12 30 68 70 9 49 50 + 1 10 9 10 10 4 3 2 10 3 7 3 1 3 10 4 7 7 4 7 + 5 3 5 4 9 5 2 8 10 4 7 4 9 5 7 7 5 10 1 3 + 1588 1620 1731 1773 1694 1487 1566 1844 1727 1636 1599 1539 1855 1645 1709 1660 1582 1836 1484 1559 + 1772 1510 1512 1795 1522 1509 1598 1658 1826 1628 1650 1833 1627 1528 1541 1497 1481 1446 1579 1814 + 56 25 76 35 28 52 21 32 64 67 48 100 94 87 39 18 78 80 56 72 + 4 70 36 46 85 31 96 30 66 92 33 18 19 34 18 4 42 94 4 89 + 1 9 9 9 5 1 4 3 2 8 2 3 7 5 3 5 2 2 2 4 + 9 9 6 8 9 7 5 2 1 6 4 6 1 2 10 10 6 5 4 3 + 1687 1738 1663 1480 1504 1826 1722 1660 1594 1445 1704 1660 1715 1701 1679 1516 1658 1611 1502 1685 + 1614 1647 1689 1615 1524 1800 1654 1752 1456 1452 1801 1713 1761 1513 1759 1484 1821 1448 1666 1611 + 1 49 35 83 75 64 20 84 31 88 27 88 21 32 12 20 26 64 6 11 + 54 2 21 94 44 19 45 6 61 41 45 86 98 45 66 77 76 64 31 25 + 10 4 7 3 6 7 5 10 5 10 2 1 7 7 2 8 3 8 8 8 + 10 1 1 3 6 7 2 4 6 5 7 4 4 9 5 6 1 9 9 4 + 1452 1565 1588 1319 1436 1434 1573 1427 1593 1432 1428 1549 1565 1312 1614 1362 1643 1536 1372 1490 + 1631 1338 1336 1487 1361 1363 1583 1652 1396 1376 1319 1369 1341 1434 1319 1296 1644 1418 1421 1338 + 71 58 89 62 8 31 74 52 71 85 1 77 35 30 96 12 4 29 64 34 + 8 98 86 22 6 6 24 61 86 76 17 36 63 83 81 37 80 56 11 57 + 6 3 9 3 8 8 3 8 10 4 7 8 10 5 5 6 5 10 3 6 + 9 4 3 2 7 10 6 10 9 3 2 6 6 9 10 9 7 5 5 6 + 1419 1682 1683 1617 1703 1549 1741 1634 1580 1588 1694 1574 1548 1730 1535 1438 1501 1504 1587 1687 + 1472 1507 1389 1454 1404 1522 1526 1681 1506 1584 1720 1767 1621 1677 1487 1513 1591 1620 1771 1712 + 7 70 52 52 86 66 70 60 65 70 27 41 42 88 21 15 40 80 28 7 + 13 58 4 43 41 89 80 54 34 92 66 72 29 40 53 35 91 58 6 82 + 5 10 1 2 2 8 8 5 6 4 8 4 1 1 6 5 10 7 2 7 + 5 8 7 7 6 2 6 6 1 8 8 5 7 6 6 4 8 6 5 7 + 1471 1794 1514 1473 1702 1583 1640 1683 1491 1519 1702 1527 1701 1675 1421 1718 1639 1742 1749 1653 + 1441 1722 1691 1612 1424 1615 1809 1533 1648 1702 1450 1614 1675 1435 1441 1485 1746 1732 1727 1612 + 76 54 14 32 100 37 69 36 27 7 39 52 74 67 93 49 89 73 79 98 + 45 36 24 71 47 19 32 47 25 37 86 5 37 60 46 28 72 11 52 4 + 1 7 6 3 10 7 2 8 1 8 8 10 4 3 1 10 10 8 2 10 + 6 7 3 3 8 10 1 2 3 2 8 2 3 5 6 9 10 6 4 9 + 1145 1091 1222 1290 1047 1011 1058 1313 1351 1076 1163 1004 1141 1061 1259 1306 985 1173 1154 1325 + 1352 1169 1189 1241 1210 1209 1331 1128 1102 983 1232 1131 1358 1339 1260 1074 1283 1068 1008 1023 + 73 32 44 87 67 63 13 10 94 11 50 90 93 79 96 39 33 20 72 77 + 11 40 75 24 52 100 83 4 7 12 49 45 59 42 6 75 16 10 65 29 + 8 8 10 1 2 9 3 6 1 10 7 10 2 3 10 9 2 3 4 8 + 6 4 3 4 6 4 7 5 4 7 7 10 10 5 9 8 3 3 1 1 + 1034 1260 1317 1095 1026 1187 1138 1281 1063 1006 982 1169 1051 1310 1189 1125 1097 1212 1091 1063 + 1084 1336 1016 1025 1219 1186 990 1167 1216 977 1184 1155 1235 1335 1008 1329 1064 1242 1108 1328 + 22 83 90 4 33 16 21 17 34 54 51 33 88 93 94 13 85 84 41 21 + 43 30 64 25 10 89 10 96 59 70 5 8 93 37 54 37 44 4 39 65 + 7 7 10 1 5 7 1 8 10 1 6 7 6 1 6 8 6 2 4 1 + 1 6 6 6 10 10 2 6 2 7 6 3 3 9 5 1 8 9 9 5 + 1192 1284 1086 1269 1008 1002 1202 943 1153 960 1201 1204 1074 983 1115 1066 1034 981 1199 1174 + 965 1222 1242 1020 993 1298 1186 1148 1032 1132 1182 1115 1299 1062 1094 949 1175 993 1293 959 + 75 33 77 73 71 24 90 94 44 57 11 67 77 27 27 100 83 81 1 5 + 3 20 75 97 4 98 70 92 84 20 83 12 67 48 90 80 5 94 47 24 + 8 2 8 1 7 6 8 5 3 7 10 6 7 3 5 7 7 9 9 2 + 3 6 9 6 10 10 6 5 2 5 7 6 3 8 9 7 4 5 4 6 + 1472 1343 1218 1338 1495 1463 1444 1249 1121 1325 1366 1368 1186 1545 1396 1517 1141 1538 1405 1508 + 1238 1175 1238 1351 1376 1324 1450 1237 1504 1498 1148 1307 1353 1291 1404 1188 1292 1140 1315 1540 + 12 67 9 86 83 64 94 80 46 97 77 78 5 54 14 36 96 82 41 12 + 79 77 71 68 47 89 97 67 16 22 89 78 34 34 94 72 75 33 39 22 + 10 6 10 6 2 1 3 7 10 10 8 1 1 10 9 4 1 4 1 4 + 4 4 6 1 3 4 5 5 9 7 6 5 2 10 8 5 10 6 5 7 + 1366 1541 1552 1418 1301 1363 1194 1349 1480 1311 1631 1335 1378 1443 1181 1258 1205 1521 1412 1251 + 1398 1347 1620 1449 1410 1327 1466 1510 1539 1412 1515 1297 1239 1201 1213 1332 1416 1205 1285 1614 + 41 18 66 42 100 71 89 19 92 3 75 57 46 2 53 57 17 9 30 25 + 90 19 93 69 76 79 5 100 16 89 7 32 78 4 21 85 60 29 43 77 + 9 10 5 5 3 1 5 9 1 8 1 10 5 8 9 1 9 4 6 3 + 2 5 4 1 5 6 7 7 5 7 3 6 8 6 4 6 9 4 6 2 + 928 623 690 630 796 811 728 670 618 788 609 629 984 841 918 809 613 644 724 764 + 667 713 797 663 951 920 716 892 677 774 894 652 988 696 872 713 971 719 956 836 + 98 77 8 38 79 28 4 57 63 57 7 96 2 48 18 3 72 39 17 31 + 80 21 60 33 60 38 84 69 73 88 2 1 11 22 6 92 92 42 66 10 + 10 9 5 9 3 10 8 2 5 5 6 10 5 2 4 9 4 1 2 4 + 7 1 3 4 2 6 3 5 2 5 2 8 10 5 2 2 8 7 2 8 + 754 682 557 827 647 880 631 783 875 806 619 648 631 540 590 658 876 696 739 570 + 738 886 724 548 870 882 619 789 553 860 606 622 709 771 861 640 646 598 890 722 + 48 83 74 78 6 75 51 45 67 95 57 93 3 25 64 18 13 100 25 19 + 25 8 69 99 94 90 3 61 6 40 13 15 72 5 62 72 32 86 93 46 + 7 6 5 6 1 9 10 1 8 2 10 9 4 9 3 9 5 2 1 3 + 4 8 6 1 6 10 8 5 9 6 8 8 4 1 10 5 8 7 3 10 + 832 907 759 997 708 929 831 1001 763 891 871 686 838 624 690 635 630 997 724 707 + 870 961 874 637 648 881 777 836 620 803 680 955 739 945 892 687 624 774 636 765 + 82 33 79 67 96 3 16 33 40 6 82 46 7 19 22 48 18 76 59 84 + 24 59 96 5 2 9 10 61 59 15 59 8 28 23 80 5 71 29 85 12 + 3 8 6 8 7 6 1 1 6 4 7 6 3 6 1 7 1 3 7 7 + 9 10 1 3 8 10 2 1 4 1 3 7 4 6 10 9 1 5 3 8 + 756 774 770 620 720 667 808 562 728 743 794 657 817 611 562 599 799 748 517 673 + 567 749 691 590 554 724 634 790 716 540 780 627 664 556 691 660 622 541 752 503 + 23 75 17 14 92 58 65 79 46 30 21 58 100 68 1 42 97 100 1 22 + 9 8 93 95 36 26 29 60 6 42 38 18 74 98 29 75 25 88 85 39 + 8 9 9 7 7 3 6 8 2 3 1 2 3 3 5 5 8 2 1 6 + 5 6 8 4 9 5 6 9 5 7 9 4 3 4 6 9 7 5 8 9 + 872 724 826 789 878 974 680 847 796 662 639 800 717 952 742 884 735 900 987 680 + 919 909 609 884 674 830 710 924 688 649 760 724 712 966 836 689 719 905 893 712 + 17 8 61 32 63 36 43 41 77 64 90 36 96 65 89 53 85 63 9 32 + 38 38 93 16 76 22 65 51 49 99 26 84 35 7 56 70 40 38 11 62 + 10 9 10 6 7 9 2 10 9 7 3 7 2 7 5 9 2 7 5 7 + 3 1 10 5 7 9 5 8 4 7 4 1 4 8 8 1 3 9 9 3 + 602 476 347 346 321 320 277 473 492 244 595 307 293 221 208 249 406 521 497 259 + 539 384 509 450 415 541 472 269 532 264 235 304 306 288 345 244 465 270 486 290 + 44 88 60 35 10 90 72 81 55 54 83 87 38 52 53 37 47 6 21 17 + 96 90 4 17 30 34 98 30 19 19 75 51 63 80 86 78 91 5 61 16 + 8 4 9 8 1 8 9 4 6 10 4 4 8 1 6 4 7 8 3 1 + 5 5 6 2 9 1 8 6 7 1 8 1 5 7 7 9 6 4 8 1 + 442 276 351 442 237 620 484 503 402 408 473 293 252 381 379 331 506 370 377 507 + 354 422 414 276 511 480 475 293 301 259 237 545 286 582 578 338 590 371 335 556 + 6 22 44 42 58 22 52 64 28 3 72 24 97 30 36 53 75 23 54 83 + 99 54 16 51 33 49 89 97 72 86 16 63 37 97 23 95 78 67 9 42 + 6 4 9 10 1 1 3 9 4 1 10 6 6 3 5 9 5 4 5 10 + 8 7 5 10 10 8 8 1 5 4 5 7 5 3 8 3 10 3 3 3 + 498 605 344 361 429 599 436 351 308 263 570 415 274 225 586 311 501 237 518 217 + 334 289 245 523 513 292 587 581 255 371 487 538 303 541 575 510 415 600 346 355 + 37 71 18 74 62 92 61 59 73 63 7 63 72 48 60 62 90 62 2 38 + 88 75 94 73 51 9 74 54 96 39 61 71 65 95 48 15 31 57 9 84 + 1 7 6 6 8 6 2 5 9 6 2 6 10 9 3 1 6 9 5 5 + 10 7 1 4 4 2 10 4 8 6 2 3 1 9 1 10 10 5 5 3 + 655 263 510 495 668 392 574 325 588 554 666 634 397 356 649 241 429 290 687 533 + 410 686 402 633 562 431 548 601 643 521 332 267 586 482 466 600 468 541 489 247 + 100 47 68 56 6 8 57 36 94 43 17 20 88 11 25 30 41 25 36 95 + 34 52 81 43 76 10 71 8 5 71 96 27 85 62 22 39 10 61 93 87 + 3 8 10 3 2 3 6 3 7 7 2 9 2 8 5 8 8 1 7 9 + 6 6 4 1 2 10 4 6 5 4 10 4 9 8 8 9 8 6 6 3 + 197 314 578 420 325 474 260 200 227 456 435 438 369 504 493 483 234 469 535 520 + 219 481 275 206 242 252 454 202 484 517 202 420 383 415 367 405 529 469 500 281 + 82 18 55 14 1 36 73 72 26 3 8 18 2 77 11 26 5 66 7 68 + 37 35 100 21 29 98 73 67 41 26 87 87 59 41 81 69 99 17 71 2 + 5 5 4 9 10 2 1 5 3 1 6 2 4 8 8 2 1 7 1 7 + 10 8 2 1 6 6 1 9 2 2 3 7 1 4 4 10 2 5 5 7 + 0 0 123 0 5 168 104 41 0 0 0 62 170 61 163 59 0 113 76 0 + 30 55 0 136 68 179 0 0 54 0 0 0 0 0 154 33 0 28 130 0 + 1 3 22 81 86 90 22 42 28 57 66 82 96 55 73 20 86 92 43 76 + 22 28 52 75 58 76 53 43 75 2 79 11 81 25 42 11 14 17 29 81 + 5 3 5 3 9 8 5 3 10 2 4 1 5 1 4 1 9 9 1 2 + 5 3 1 1 4 10 9 6 6 8 8 7 10 4 8 6 2 1 2 2 + 0 103 0 44 0 0 38 166 0 0 57 1 0 0 159 0 71 0 0 87 + 193 0 0 0 0 0 146 148 0 159 165 36 11 19 83 0 0 0 54 0 + 4 75 87 90 29 42 96 27 92 70 52 38 81 9 47 87 17 64 52 41 + 45 90 14 71 40 97 60 51 5 50 94 59 71 62 98 74 97 5 34 80 + 3 4 3 3 2 10 9 1 8 8 9 3 2 5 8 10 7 3 2 2 + 2 9 10 1 4 7 6 1 1 6 7 4 7 1 4 7 6 5 10 3 + 88 167 228 0 0 0 56 205 0 66 0 0 0 0 0 0 0 117 0 146 + 0 0 0 77 69 185 0 105 0 0 159 0 52 66 0 0 113 20 0 179 + 63 61 47 77 25 14 63 13 33 64 7 18 98 57 45 4 60 94 17 86 + 89 30 43 81 80 69 23 10 59 73 31 97 78 55 23 70 18 80 31 57 + 4 6 1 1 4 4 8 3 7 6 9 7 2 5 8 9 7 4 4 2 + 9 7 8 3 2 9 5 9 9 7 4 7 1 7 7 6 8 7 9 7 + 154 159 70 34 60 142 172 127 0 0 163 2 0 144 0 0 189 123 95 0 + 0 0 34 0 177 0 187 0 0 13 85 0 0 0 161 81 0 0 188 47 + 81 39 78 84 99 82 71 85 98 10 52 56 12 67 58 53 5 51 1 40 + 65 11 75 80 11 52 48 41 91 31 70 94 78 57 66 13 76 92 40 75 + 4 10 6 8 5 4 1 5 5 6 2 3 9 2 6 2 2 10 1 10 + 5 7 1 7 2 4 3 5 10 8 7 4 8 5 7 9 3 2 9 2 + 0 179 19 2 119 0 40 99 24 0 0 69 0 0 0 0 151 128 171 0 + 12 147 0 0 0 0 29 145 0 110 227 45 189 0 0 0 0 10 0 0 + 87 43 33 53 1 76 44 34 60 36 82 88 21 63 54 18 68 53 46 33 + 12 52 21 45 95 60 21 69 85 32 66 21 78 75 55 23 99 47 64 98 + 5 7 10 7 6 6 1 3 9 6 4 2 6 10 6 5 9 7 9 5 + 6 2 8 8 6 4 10 6 4 2 8 8 6 8 6 8 3 9 3 6 + 1267 1914 1785 1385 1653 1344 1888 2075 1804 1297 1990 1709 1561 1719 1947 2024 2095 2021 1591 1836 + 1616 1683 1703 1655 1612 1285 2041 1964 1720 1424 1465 1940 1747 2059 1868 1946 1597 1424 1564 1404 + 41 56 43 30 12 77 87 53 22 19 74 54 47 21 82 84 95 73 70 99 + 99 21 72 58 21 60 4 20 51 41 55 52 9 69 98 20 40 100 79 96 + 1 1 5 9 5 6 2 5 4 9 9 4 6 4 7 2 8 7 8 7 + 7 2 9 7 6 10 8 1 9 6 10 9 7 3 1 1 9 6 1 1 + 1494 1558 1707 1373 1538 1603 1783 2142 1765 1811 1878 1852 1675 1785 1921 1456 1879 1505 1979 1990 + 1387 1958 1519 1470 1475 1815 1739 1907 2090 1578 2190 1797 2202 2017 1352 1372 1485 2162 2054 2134 + 27 19 18 66 40 22 81 39 12 94 80 99 43 70 67 44 60 39 70 76 + 20 41 99 1 6 70 37 26 30 75 50 64 33 54 78 29 84 63 42 38 + 10 3 8 1 9 2 2 9 2 4 10 1 6 10 2 10 5 6 2 5 + 10 5 10 10 2 7 5 7 2 6 3 10 6 3 2 4 10 5 3 6 + 1829 1980 1263 1532 1527 1377 1304 1295 1535 1844 1586 1958 1596 1809 1815 1897 1413 1694 1977 1622 + 1381 1288 1656 1747 1310 1524 1365 1364 1946 1848 1869 1620 1300 2001 1507 1693 1773 1790 1920 1566 + 60 22 47 13 21 98 85 91 90 44 32 68 89 93 11 88 66 28 23 3 + 99 88 47 62 95 65 41 21 88 2 40 40 40 97 59 78 41 100 89 1 + 1 10 2 7 2 2 6 4 7 6 9 9 7 2 5 6 4 7 8 2 + 7 2 9 2 2 6 10 4 6 1 7 5 2 9 1 5 4 5 5 3 + 1442 1639 1483 2221 2264 1743 2047 1442 2193 2139 2239 1543 1725 1378 2034 1385 1939 2135 1892 1846 + 1690 1896 1993 1711 1610 2215 1922 2179 2199 2115 1718 1850 1487 1917 1566 2132 2188 2070 1935 2228 + 83 11 94 26 20 48 38 11 42 9 40 10 92 24 97 15 41 73 80 23 + 89 93 42 31 64 70 12 42 22 46 96 62 47 16 82 98 51 26 32 61 + 3 6 9 3 9 7 7 10 7 6 5 9 5 2 5 9 10 10 6 10 + 8 2 8 10 1 3 7 3 4 8 9 9 7 5 7 2 8 9 4 6 + 1756 1615 1772 1197 1866 1894 1703 1596 1847 1700 1347 1676 1734 1532 1441 1741 1818 1588 1715 1927 + 1794 1548 1811 1788 1861 1520 1781 1838 1863 1825 1437 1566 1608 1607 1644 1763 1581 1734 1313 1758 + 66 83 4 64 29 66 54 6 82 80 92 79 88 52 84 24 44 60 75 83 + 68 36 88 2 13 64 25 29 54 84 65 17 99 85 65 22 81 11 62 100 + 9 8 7 5 2 4 6 8 3 1 7 9 5 4 5 8 4 10 10 1 + 1 7 4 1 6 3 3 4 9 9 6 6 7 3 7 5 5 9 2 7 + 1480 1684 1474 1438 1532 931 1041 1110 1219 1826 1694 998 1607 1400 1174 1018 1033 961 1708 1405 + 1424 1264 1014 1314 1350 1797 1122 1531 1424 1441 1208 1252 1022 1410 938 1703 933 1338 1518 1090 + 9 90 99 90 69 100 29 84 51 3 53 30 43 20 10 17 61 27 16 32 + 46 54 66 70 72 81 34 25 37 43 71 97 70 29 6 87 27 80 47 1 + 5 1 8 3 2 8 5 1 10 3 10 3 1 1 10 3 6 7 7 1 + 3 10 4 4 9 4 8 2 10 2 1 4 9 9 7 3 9 10 5 1 + 1503 881 1491 888 1415 1099 1158 1567 1376 1157 824 1580 1318 850 1077 1164 936 1348 1000 1277 + 1198 1023 1454 811 1510 1152 861 1300 1213 955 851 978 1493 931 1252 1068 1350 1048 1010 1416 + 37 35 80 69 88 52 35 57 22 74 68 23 49 21 90 100 6 66 91 24 + 59 35 36 35 100 4 71 76 51 8 18 93 63 29 80 34 12 7 36 51 + 2 6 5 5 6 9 10 2 5 1 2 8 5 1 10 5 5 9 8 4 + 10 5 10 7 2 2 6 7 5 1 9 10 9 8 3 10 2 4 10 4 + 833 940 1046 1433 1128 829 1582 1431 1398 1217 1180 1109 1076 994 1096 921 1488 1278 1226 903 + 1028 1217 1018 1047 1027 1341 1586 1273 920 958 1066 1013 953 1499 1494 1561 1308 1536 984 894 + 7 77 25 68 63 75 21 41 93 45 59 18 26 5 20 81 23 22 66 13 + 70 20 35 1 82 74 44 33 4 12 88 2 6 16 41 5 89 22 53 40 + 2 10 8 1 4 1 10 4 1 1 3 4 8 6 7 6 8 8 7 6 + 4 4 10 10 6 7 5 6 2 6 8 2 9 7 6 7 3 1 10 2 + 1137 1078 737 846 972 844 691 1087 852 955 895 1209 955 994 746 1032 873 1092 1208 1034 + 824 1117 1104 743 643 811 708 1259 985 880 1203 1109 1052 1234 666 667 1068 803 1135 1180 + 100 25 90 60 100 77 16 53 90 21 25 82 23 71 71 74 81 93 85 60 + 72 5 80 72 34 81 42 47 32 45 41 25 59 77 19 48 37 7 2 62 + 4 5 6 1 10 6 8 2 10 2 8 8 8 8 8 10 1 3 2 5 + 9 5 1 5 9 6 5 2 5 4 2 8 1 3 9 7 10 3 10 2 + 1009 1009 1062 897 1643 1700 1012 1627 1509 1341 1380 1530 1392 1639 1415 1246 1002 1617 940 1067 + 1506 1738 1359 1345 1528 1473 999 1593 1422 1677 1351 891 1015 1285 1535 1349 1262 1433 1055 1542 + 29 43 10 19 31 18 27 15 68 59 46 82 17 75 93 93 80 29 71 25 + 87 38 64 57 97 70 32 75 99 48 13 63 7 48 26 11 65 98 64 13 + 5 10 5 8 9 3 1 2 5 2 5 1 2 1 10 9 7 9 4 8 + 5 3 6 5 8 2 10 3 8 5 5 10 6 5 9 4 1 1 9 3 + 747 1111 1041 1032 401 1148 906 789 747 893 629 996 1100 700 893 556 1138 918 656 906 + 832 858 478 780 766 577 649 999 461 649 505 832 1106 740 563 1186 673 983 1040 963 + 32 47 97 22 17 12 39 52 12 68 78 90 13 23 14 56 45 35 23 73 + 62 19 60 69 43 40 56 75 98 17 22 22 65 23 47 48 8 84 99 92 + 2 9 3 4 3 4 9 1 4 2 5 4 8 7 9 9 10 7 7 5 + 10 5 1 10 8 4 9 5 4 8 7 9 3 1 8 4 1 1 9 3 + 538 436 960 472 537 888 1115 1066 574 817 643 599 663 834 448 886 923 831 701 509 + 755 531 960 464 660 401 665 1115 657 693 772 1029 587 843 467 745 803 780 1062 851 + 77 9 44 50 48 26 13 92 31 13 56 81 44 75 42 85 33 68 21 74 + 74 3 17 23 70 41 37 9 50 99 61 11 75 46 50 14 32 5 45 3 + 10 1 2 3 5 8 4 6 4 2 4 7 9 4 9 6 9 2 9 5 + 2 10 7 7 5 6 1 6 2 7 10 6 8 4 10 3 3 5 6 4 + 591 869 702 434 766 972 820 735 547 426 436 623 989 657 358 824 935 380 850 873 + 440 989 1044 831 357 422 792 566 874 645 777 778 404 827 876 887 403 632 968 751 + 60 49 18 87 42 79 92 64 65 11 92 31 71 47 62 4 27 40 20 99 + 29 96 66 37 4 77 75 43 3 94 2 43 8 15 79 68 28 57 17 50 + 1 5 7 8 3 2 3 6 2 5 8 4 8 5 10 6 4 8 1 9 + 7 6 5 9 10 9 1 5 3 1 1 5 7 3 1 8 10 10 9 6 + 1159 986 1117 761 428 909 599 1123 1070 960 780 891 464 484 683 507 1064 521 431 1069 + 981 473 585 811 530 612 1098 526 601 1077 660 817 634 495 1104 516 890 426 512 939 + 5 46 67 20 44 44 93 3 82 31 78 23 83 3 34 40 36 12 55 21 + 60 61 11 32 1 7 49 44 35 59 95 32 63 80 55 71 17 88 11 43 + 7 1 6 7 5 6 7 1 3 3 1 8 5 9 7 7 4 3 8 9 + 10 4 2 3 4 10 8 2 3 7 9 9 4 1 3 7 1 10 1 1 + 732 933 691 670 499 629 561 816 810 910 964 509 546 494 490 412 802 1034 911 780 + 677 691 363 404 1008 520 728 736 854 812 819 684 539 553 983 733 895 691 446 515 + 63 27 17 40 27 48 15 46 78 96 81 39 99 65 2 26 2 3 40 19 + 28 10 38 33 44 39 15 85 49 99 70 96 66 47 79 90 48 98 97 94 + 7 8 1 4 9 8 9 1 10 10 6 4 2 3 5 8 3 1 9 7 + 3 5 8 5 6 2 2 8 8 4 7 3 1 1 2 9 7 6 7 3 + 625 448 143 797 452 717 316 387 491 464 203 778 536 518 462 512 296 316 756 578 + 131 129 691 685 575 431 369 57 607 17 505 289 453 292 509 382 690 111 254 425 + 14 75 70 39 94 69 32 33 75 82 51 66 63 4 83 94 91 81 64 74 + 21 9 9 67 45 39 8 39 80 75 32 58 72 73 30 93 98 43 26 18 + 1 10 8 8 8 10 5 7 3 10 3 8 5 6 4 9 8 2 5 7 + 7 9 6 2 4 1 9 4 2 2 1 4 9 7 3 3 4 2 8 4 + 513 228 419 398 232 147 101 102 756 38 39 135 116 555 465 823 347 49 714 338 + 411 440 230 354 50 419 16 773 580 437 149 139 874 291 353 852 773 606 132 835 + 66 26 8 14 75 40 99 7 27 18 11 96 49 48 75 81 99 70 98 68 + 58 79 65 66 41 22 41 85 88 1 67 40 65 67 63 50 72 29 28 33 + 5 6 5 7 7 3 7 2 4 10 4 5 1 6 9 9 10 4 2 4 + 1 1 2 4 5 4 6 3 9 3 1 7 9 4 9 10 9 10 5 1 + 259 201 384 841 310 629 132 431 815 125 254 707 381 705 201 296 405 481 414 469 + 722 251 748 425 813 273 177 672 570 21 348 17 650 81 159 661 683 206 166 59 + 56 68 92 64 27 47 46 15 12 88 66 21 60 74 53 28 49 46 87 91 + 85 93 26 63 2 59 4 3 62 43 36 72 56 94 14 36 2 17 7 61 + 5 4 6 9 7 10 8 9 10 5 3 4 5 7 6 10 1 7 6 3 + 5 4 8 10 3 10 6 9 9 7 1 10 6 9 5 3 5 8 6 8 + 355 429 511 168 486 286 141 481 493 478 421 411 77 117 635 178 447 236 337 512 + 394 488 233 703 456 83 383 120 534 657 320 108 205 276 322 115 193 149 768 364 + 48 43 58 92 38 28 83 7 39 25 96 37 25 17 71 78 94 38 59 30 + 75 76 88 95 22 72 67 76 21 21 82 84 84 70 95 29 51 20 49 26 + 3 4 4 6 8 2 5 2 9 5 10 2 1 8 5 4 2 1 2 10 + 4 3 5 10 1 6 3 7 2 9 6 3 8 6 9 3 6 9 1 2 + 786 140 199 360 561 183 810 489 856 46 765 378 333 798 428 72 870 468 631 702 + 416 854 171 480 411 655 793 850 408 521 278 722 195 597 71 861 595 276 134 768 + 34 46 22 4 20 74 98 34 45 19 62 44 2 60 60 78 14 28 15 81 + 29 8 27 2 26 38 77 93 10 9 22 10 16 65 97 10 7 5 17 55 + 2 5 1 8 10 7 10 7 4 5 7 8 1 2 10 6 10 9 3 3 + 4 2 2 6 10 3 5 1 6 10 7 4 9 7 2 5 2 3 1 2 + 119 181 156 0 129 127 0 0 0 215 211 0 0 125 0 0 0 0 228 56 + 2 0 0 0 0 271 265 156 0 0 0 0 62 0 105 129 0 114 0 0 + 36 24 79 9 17 31 57 52 93 12 2 45 78 43 30 76 16 52 37 38 + 78 21 97 27 65 85 59 43 3 32 54 86 49 3 90 62 87 92 51 80 + 3 1 7 7 8 8 5 9 1 8 9 4 3 3 9 8 3 2 10 10 + 7 1 8 10 10 1 6 8 8 8 7 5 2 6 10 3 4 4 5 6 + 0 0 0 93 0 179 6 99 0 0 129 0 270 46 311 0 0 81 0 0 + 0 0 0 0 0 0 184 180 0 327 0 0 156 333 0 0 0 231 0 338 + 1 79 1 18 100 96 4 21 76 98 30 45 67 57 68 25 37 99 86 2 + 80 1 28 18 18 96 17 19 1 69 54 53 6 58 20 73 39 54 71 35 + 5 10 10 7 9 3 10 6 2 1 6 9 9 5 2 6 5 5 3 1 + 5 4 8 6 1 2 10 6 3 6 4 6 9 8 1 2 9 5 2 2 + 0 61 361 0 0 0 327 0 25 76 323 152 1 190 0 126 278 0 277 227 + 248 35 93 0 45 0 248 68 0 0 0 49 0 0 0 231 297 0 0 0 + 7 64 38 47 67 21 61 3 15 25 42 53 11 47 41 82 41 9 99 75 + 22 86 30 23 96 7 31 17 34 54 75 54 38 61 13 33 34 16 74 55 + 4 9 8 10 7 7 4 6 6 7 1 6 3 3 4 7 1 9 10 5 + 10 1 9 7 8 7 6 2 3 3 8 9 10 9 6 9 3 2 5 3 + 195 0 74 0 103 0 0 0 41 69 271 202 159 329 29 0 192 68 0 6 + 80 0 7 0 242 0 0 144 153 125 0 0 0 0 0 0 0 83 0 165 + 58 88 19 14 45 9 28 62 58 95 9 82 81 12 93 5 21 79 91 64 + 17 68 92 95 32 6 63 38 62 70 48 36 26 93 72 35 76 59 35 83 + 8 9 6 3 5 10 3 7 3 8 9 8 2 4 2 7 2 4 8 3 + 8 1 1 9 7 3 6 6 1 3 2 9 3 3 2 1 3 8 2 9 + 325 384 0 0 0 0 0 139 168 0 0 226 195 0 0 0 37 0 103 0 + 0 0 70 134 0 0 0 0 0 0 47 23 0 84 0 0 0 399 369 377 + 89 11 49 41 88 19 85 67 83 61 82 46 76 1 45 56 97 55 57 76 + 7 92 79 90 93 34 49 84 68 58 84 48 78 90 79 75 94 14 7 60 + 10 8 8 1 2 9 3 9 10 8 1 9 1 4 6 4 9 3 1 9 + 9 6 9 8 1 10 6 9 7 9 5 9 4 1 8 7 10 3 3 2 + 1445 1249 2181 1877 1517 1516 1253 1762 2246 2550 1652 1729 1912 2530 1272 1847 1408 2483 1939 1595 + 2624 1437 2244 2031 1653 2654 1478 1312 1962 1388 1334 1298 1683 1477 2448 1596 2347 1610 1669 1726 + 59 46 15 51 81 59 86 89 94 31 89 64 22 20 98 100 19 39 70 24 + 65 55 35 67 69 87 5 61 69 8 69 43 30 41 93 53 46 68 22 33 + 3 9 5 10 8 7 8 4 3 9 8 4 10 3 3 8 1 5 5 3 + 6 4 10 1 3 6 6 1 9 5 6 5 9 9 5 6 7 1 2 6 + 1567 1721 1314 1352 2126 1136 1462 1138 1789 1507 2085 2152 1967 1862 2260 1711 2058 1220 2164 1254 + 1929 2127 1977 1741 1146 1824 1435 2305 1458 2306 1332 2166 2106 1478 1777 1899 2321 1919 2390 1435 + 58 59 77 73 34 95 68 3 24 65 36 57 75 25 31 6 62 86 78 23 + 88 10 82 87 83 13 26 77 7 65 70 76 47 88 52 71 18 57 90 52 + 8 2 9 8 2 2 10 3 7 8 7 8 2 8 8 7 6 6 9 2 + 5 10 5 6 4 7 7 4 6 7 9 2 3 8 9 7 5 4 6 5 + 1934 1175 1115 1502 2129 1792 1230 1803 1832 2123 1134 1253 2297 1666 1659 2101 2335 1304 2360 2293 + 2138 1620 1730 1340 1179 1868 2003 1600 2038 2238 1471 2313 1730 1505 1920 2194 2097 1926 2265 1361 + 4 72 79 38 86 43 42 86 34 77 9 49 17 28 100 44 32 2 12 92 + 3 59 22 16 82 12 84 44 52 27 30 7 20 24 72 36 25 75 99 69 + 5 2 9 4 9 9 10 8 4 10 8 9 5 2 5 4 9 7 5 9 + 7 5 9 2 1 10 6 2 1 2 4 8 6 7 10 2 1 8 6 5 + 1241 1177 952 1274 1716 1866 1388 1739 1257 1384 1903 1703 1428 1738 980 1550 1451 1819 1662 1977 + 1347 1417 915 1253 1283 1459 1145 1672 1486 1548 1721 1418 1591 1275 1845 1007 1624 994 1800 1640 + 43 56 37 79 6 72 1 20 15 34 71 69 61 91 39 90 62 48 77 51 + 65 60 31 23 44 39 28 79 3 46 73 96 72 99 48 56 10 66 13 40 + 1 10 3 7 3 7 7 3 5 2 4 3 8 2 6 8 4 7 2 10 + 3 6 9 8 3 2 3 8 10 4 9 1 9 9 9 3 3 7 6 10 + 1056 1538 1837 1610 1103 1848 1441 1934 1860 1421 2026 1948 1602 1425 1406 1502 1642 2039 1501 1472 + 1393 2113 1295 1910 1267 1838 1246 2010 1360 1208 1058 1876 1937 1486 1682 1281 2119 1245 1888 1436 + 48 38 51 33 88 6 12 88 83 55 61 47 44 32 62 21 13 37 57 25 + 57 24 67 77 73 1 88 98 86 26 98 11 49 13 51 43 42 27 79 25 + 4 3 5 8 9 3 8 3 10 1 2 10 6 3 2 5 5 8 7 2 + 4 5 7 7 6 4 9 7 5 5 1 9 2 3 4 4 7 8 4 9 + 1443 1541 1612 1229 1652 1580 1612 1310 1555 975 1395 1652 1522 837 1425 1406 997 1398 1699 1366 + 715 1681 763 1024 852 917 1117 860 914 1693 1277 1722 1370 934 1176 1311 1226 1169 1181 1686 + 20 97 94 40 57 20 23 77 84 13 99 16 36 19 59 18 75 36 17 43 + 91 15 64 55 3 58 70 23 58 23 84 18 29 20 31 12 21 11 17 84 + 9 5 2 9 7 10 10 3 7 8 2 9 4 6 3 6 3 1 4 2 + 5 10 9 8 3 4 2 4 6 2 6 3 1 6 3 7 6 7 10 10 + 1171 1010 649 1448 751 1185 648 1100 676 672 1499 988 622 1362 1373 636 1191 1309 1333 577 + 701 666 1341 768 797 871 937 554 1163 1245 1482 1234 984 1293 607 837 920 1451 682 835 + 91 65 21 44 64 45 59 76 91 35 83 15 30 77 100 64 64 66 96 34 + 6 48 35 34 95 25 58 80 100 49 87 89 18 28 17 82 73 21 27 83 + 7 4 5 1 8 4 6 9 3 1 7 10 6 6 4 6 6 9 1 10 + 5 6 1 5 8 6 2 8 3 6 6 3 4 9 9 8 3 4 9 10 + 930 1059 1244 1337 1643 1557 941 1854 1158 1123 1435 1016 1771 1883 960 1119 1116 1010 1800 1102 + 1580 1245 959 1274 1313 1648 1962 1911 917 1313 1434 1166 1276 1021 904 1845 695 1499 1562 1137 + 17 28 62 91 89 61 24 81 8 99 87 82 52 81 31 48 28 98 14 32 + 69 94 71 94 38 67 16 95 24 67 41 99 48 97 16 74 54 14 90 91 + 3 6 8 8 9 2 2 10 2 1 4 6 2 6 5 1 4 5 6 4 + 6 3 5 2 2 8 1 4 4 6 7 1 3 10 9 2 9 6 9 1 + 957 2105 1120 1956 1943 1859 1043 2018 1305 1088 763 1805 895 1461 1409 896 1531 990 1491 1856 + 1429 736 1391 948 1056 863 1444 886 1564 1650 751 1549 1117 964 942 1360 1852 968 720 790 + 86 25 28 31 40 5 99 86 63 38 65 62 53 61 78 58 39 60 68 75 + 76 76 41 19 49 5 84 99 70 93 61 25 80 41 13 12 46 2 60 78 + 5 2 7 4 8 1 5 3 8 8 5 7 2 4 6 8 3 6 4 6 + 3 7 7 3 4 6 6 3 4 7 5 9 2 1 3 7 3 6 1 3 + 1484 864 1710 954 1928 1196 1211 933 1452 924 1210 759 768 901 1076 1204 1819 1324 1910 1314 + 1208 1369 1594 1594 1047 763 793 1387 1089 713 1866 771 1836 1608 686 1804 1837 1362 1029 1395 + 12 46 7 16 55 65 44 92 14 38 49 100 12 98 44 88 99 30 33 2 + 51 70 93 97 75 12 82 26 5 98 36 48 4 26 69 25 61 47 72 20 + 2 8 3 4 9 9 9 7 3 8 2 8 5 5 4 3 7 5 2 6 + 7 1 6 3 2 1 7 3 9 8 3 2 2 3 3 10 3 1 10 7 + 1309 524 939 577 378 358 220 611 813 508 587 1209 762 1001 688 452 299 1344 972 211 + 1322 1359 349 824 973 269 486 1054 933 1070 1286 993 849 209 492 851 569 1356 914 332 + 37 37 64 2 96 82 96 1 49 37 47 47 39 29 12 34 38 40 62 58 + 45 5 51 17 89 13 6 13 25 4 36 52 37 72 26 42 38 66 40 94 + 2 7 2 5 7 7 9 3 7 1 2 3 9 7 7 5 10 1 10 7 + 7 3 2 10 4 1 8 4 2 7 6 3 2 10 7 9 4 6 5 7 + 846 751 1016 643 363 1089 392 1154 1120 512 995 509 1025 290 627 731 328 302 831 616 + 521 763 1016 216 727 431 1126 820 624 347 793 391 841 1044 1005 572 873 243 232 1130 + 31 96 43 48 56 53 73 18 80 26 49 55 69 41 39 97 14 76 88 88 + 39 63 18 73 92 88 32 1 53 87 7 24 25 53 3 98 45 29 54 80 +10 8 9 3 4 4 3 1 8 4 8 7 7 8 7 3 4 9 1 2 + 3 6 2 10 3 8 6 10 3 5 2 9 1 5 4 5 3 2 7 5 + 1126 453 366 995 1142 312 616 1308 212 487 895 670 909 1450 454 1011 783 749 944 922 + 1070 213 320 1287 902 1188 1227 1457 970 394 1109 1109 253 296 607 1465 941 1175 1230 834 + 94 74 66 15 57 24 66 53 52 51 20 79 66 76 65 78 14 77 20 41 + 40 81 78 61 16 69 13 32 40 78 76 54 42 94 97 30 77 59 83 63 + 10 6 6 6 10 2 6 5 4 10 2 6 3 8 1 8 9 9 4 6 + 10 2 5 1 3 2 1 10 3 10 4 5 5 3 8 7 1 5 1 7 + 604 331 1144 648 499 636 1080 1434 342 930 1131 741 1202 353 779 1191 1026 406 996 1204 + 1313 498 376 1079 866 956 1469 230 1104 1291 1471 544 1176 486 1280 900 551 703 265 520 + 52 75 35 70 47 97 50 18 34 9 4 78 23 30 5 84 52 34 45 71 + 86 45 88 80 17 42 81 53 69 57 45 36 89 75 84 77 57 33 75 30 + 4 1 8 4 4 2 2 6 10 8 2 7 1 7 7 10 4 9 8 4 + 5 5 2 3 6 7 7 10 1 7 6 10 9 9 6 7 10 6 5 8 + 1213 263 497 1051 1192 956 820 1466 1323 1104 1339 737 529 998 1364 392 789 680 564 1290 + 631 854 449 381 763 619 1372 893 1394 932 1453 1431 1481 1067 1252 1044 1159 802 1409 1105 + 61 57 26 79 2 26 74 27 25 52 22 15 61 51 54 23 70 92 29 33 + 39 99 87 34 83 45 45 14 45 61 97 65 97 27 16 84 39 78 55 70 + 4 10 3 6 3 9 2 8 10 2 10 9 10 6 8 1 6 2 8 1 + 10 8 9 6 3 5 1 3 3 5 8 10 7 1 8 7 1 5 2 4 + 1005 694 0 268 301 136 86 41 179 0 319 0 826 0 433 269 64 0 917 376 + 629 506 560 78 810 704 973 0 497 177 606 0 448 752 495 791 946 0 753 554 + 3 29 97 16 27 17 66 100 52 83 76 55 29 38 83 7 40 50 18 87 + 93 36 92 28 27 70 93 66 6 77 90 41 23 36 26 96 1 79 26 77 + 9 6 1 3 4 4 8 8 7 3 10 9 9 7 9 5 10 7 2 5 + 9 8 6 1 2 3 4 4 4 10 2 6 10 4 8 1 10 5 6 3 + 892 585 889 788 887 0 182 643 596 743 0 571 975 136 0 1015 225 0 40 694 + 0 330 728 874 197 113 729 404 399 28 763 0 48 574 666 912 344 431 815 702 + 89 55 83 53 57 33 33 57 69 60 44 65 2 47 42 51 59 57 32 93 + 69 5 95 70 92 62 92 66 15 49 63 68 54 42 23 64 53 69 69 47 + 6 6 8 7 1 6 6 9 1 8 9 8 6 5 1 5 1 7 10 4 + 1 9 5 1 9 3 10 10 8 5 3 8 8 10 4 3 7 6 8 3 + 867 0 0 892 753 0 0 690 318 858 0 922 935 401 623 580 552 0 319 229 + 296 346 1073 0 978 25 506 559 281 729 32 384 33 43 350 419 698 380 309 1011 + 60 53 96 70 51 86 60 23 61 94 83 18 87 85 61 75 54 19 72 18 + 14 8 69 74 49 18 85 74 66 70 31 50 5 34 95 39 29 38 45 81 + 1 6 2 10 6 8 6 10 3 9 9 7 6 2 7 6 10 4 7 7 + 5 2 2 7 2 4 7 7 8 8 1 7 10 5 6 8 4 4 8 3 + 204 197 546 1039 103 484 11 443 266 0 147 0 549 0 1044 1024 997 426 195 581 + 399 100 625 0 784 661 0 445 700 729 761 542 1027 149 382 632 893 941 862 0 + 82 92 27 46 100 42 10 26 38 48 26 87 17 12 46 98 81 92 16 29 + 7 97 64 54 96 90 94 49 79 88 88 96 16 60 8 78 95 27 91 80 + 10 5 4 6 10 9 2 7 8 4 6 7 5 4 2 5 9 5 1 3 + 10 1 7 5 2 1 6 4 3 1 9 4 4 2 9 2 7 2 3 3 + 173 171 697 157 534 174 1070 590 659 0 0 547 1098 126 0 979 737 722 130 401 + 934 1089 80 0 404 758 666 0 773 361 257 11 53 513 0 0 1032 0 433 297 + 51 95 45 65 20 23 23 96 40 39 87 51 52 42 80 46 23 88 21 98 + 51 81 9 28 60 83 20 59 3 5 8 19 61 15 5 38 42 32 23 50 + 4 10 10 9 4 4 10 10 5 9 6 1 4 6 7 7 7 1 4 4 + 10 8 2 8 4 4 1 9 2 4 1 3 4 9 9 2 7 6 2 5 + 374 413 0 0 0 0 422 0 51 0 0 134 25 500 511 0 0 337 0 0 + 281 399 0 0 276 0 0 211 0 0 461 509 518 0 0 0 89 528 265 364 + 91 37 97 15 58 100 18 67 34 36 57 90 81 42 28 46 91 69 23 99 + 80 20 11 17 96 52 41 71 14 75 39 34 36 52 9 84 93 99 35 100 + 7 3 8 6 6 5 10 7 7 2 2 3 5 6 4 10 2 1 9 10 + 10 10 4 3 8 10 10 2 4 4 9 8 5 2 8 6 5 10 8 1 + 0 444 471 485 348 0 219 221 359 185 0 96 291 505 260 0 0 589 0 0 + 0 400 651 0 361 135 258 0 299 0 0 424 122 0 237 551 0 0 0 334 + 53 49 10 85 87 100 93 76 15 63 5 88 24 31 30 29 23 52 32 11 + 55 27 53 47 88 9 13 56 17 5 10 59 68 75 21 79 94 31 100 43 + 7 2 10 1 1 8 5 10 8 2 8 7 7 1 10 4 10 1 6 5 + 3 2 3 7 3 1 4 4 4 2 4 4 9 2 10 4 10 5 6 3 + 288 199 0 0 320 67 355 554 0 289 0 0 203 448 0 216 220 472 0 0 + 84 314 0 0 393 201 0 239 0 316 499 130 116 0 424 125 360 354 516 0 + 19 12 85 69 9 2 24 64 8 95 78 33 24 82 5 30 89 8 41 13 + 66 69 99 45 46 17 72 90 46 55 29 15 14 40 18 91 24 81 67 93 + 4 1 2 4 5 8 4 10 8 8 4 8 9 1 6 7 6 9 5 3 + 10 2 2 7 7 7 9 7 6 5 3 6 8 3 4 4 10 6 6 5 + 316 351 0 0 308 14 512 0 0 419 0 330 0 0 357 0 275 0 0 0 + 536 0 0 53 139 235 199 423 0 0 0 0 423 208 550 0 297 450 372 0 + 95 62 4 64 53 38 62 74 64 62 1 13 56 22 50 83 83 59 18 90 + 30 67 90 41 10 94 30 49 62 40 79 27 56 59 58 93 87 57 65 68 + 3 1 2 2 1 3 5 8 8 1 2 4 7 3 5 10 2 2 3 8 + 1 6 7 1 6 1 2 4 3 7 6 1 10 10 10 8 9 4 1 3 + 395 0 588 300 333 31 340 0 512 537 457 0 0 166 0 0 578 0 571 25 + 0 515 0 0 0 552 0 222 216 0 454 0 85 0 606 0 0 353 0 498 + 17 88 28 68 79 94 40 52 64 46 77 23 21 29 41 31 29 81 11 29 + 93 92 45 70 47 8 78 14 54 25 24 22 4 94 75 58 16 19 100 90 + 10 7 2 4 1 8 4 10 7 4 6 1 6 1 3 4 10 2 7 5 + 4 5 8 9 5 3 5 7 7 6 6 6 3 1 4 7 3 8 7 1 + 1338 2062 1669 1534 1564 1391 2193 2223 1373 1713 1026 1743 1537 2199 1243 1534 1179 1214 1727 921 + 2359 2189 2155 1015 1245 1339 871 1508 2175 1422 1456 1406 926 1038 1128 2146 1942 2078 2042 2013 + 52 31 94 85 72 9 68 47 15 83 89 59 50 36 31 17 79 3 45 19 + 28 72 60 69 53 83 85 70 40 31 4 31 99 64 81 65 72 81 58 84 + 9 1 9 10 6 3 6 7 10 6 5 8 10 4 2 2 10 9 9 10 + 5 6 10 3 7 1 3 2 6 10 9 2 3 9 4 5 5 10 4 8 + 1349 2078 1523 2126 1871 1271 1006 1749 1928 1774 1537 1783 2157 1532 1854 1665 2563 1234 1640 1226 + 2350 2052 1793 1525 2415 2488 2060 1254 1641 1408 1302 1105 1150 1980 2200 1949 2375 2320 914 2119 + 90 86 18 19 47 73 33 12 53 61 96 20 1 29 51 86 12 9 23 55 + 37 85 12 77 52 95 98 3 65 31 38 89 9 36 68 93 45 9 1 77 + 2 1 1 2 3 7 4 7 4 5 2 4 9 7 5 8 1 4 5 2 + 9 3 9 2 3 5 5 4 9 8 7 6 5 6 10 4 4 9 5 10 + 1747 2082 2255 1152 1211 1532 819 880 2087 890 916 2090 1131 1782 1415 2247 1650 2229 2271 1521 + 1258 2268 1744 1983 1566 1261 1928 1502 1991 2006 1416 2025 1878 1992 840 1960 1881 2049 1593 1427 + 52 1 94 81 46 32 2 3 44 22 35 9 60 46 12 88 38 32 27 32 + 50 63 53 38 96 73 42 4 3 50 24 32 27 35 4 28 53 23 70 100 + 10 10 2 1 10 2 1 4 2 1 3 9 5 10 9 4 9 10 4 8 + 9 4 5 10 3 5 2 10 3 8 1 9 2 8 9 10 2 2 8 6 + 1839 685 1442 1414 1747 761 1405 1888 1750 1448 1292 737 1821 1696 897 1655 653 1330 1194 1001 + 738 695 1941 1384 1101 929 1236 1250 1072 1012 912 668 816 1936 897 1301 1214 1155 716 1817 + 88 77 95 82 40 84 17 79 78 60 23 32 41 78 1 76 92 62 16 50 + 26 92 16 98 64 99 22 65 56 68 36 100 79 37 38 55 40 97 84 86 + 10 9 6 1 3 6 4 6 6 2 6 4 6 1 6 7 9 1 8 8 + 8 5 2 9 6 4 1 6 2 3 10 3 6 6 9 7 6 9 1 5 + 2380 2123 2763 2365 1172 1171 1943 1878 1732 1367 1370 986 1583 1114 1156 2097 1554 2193 1594 1622 + 1725 1454 1528 2535 2205 2885 1562 2664 1016 1537 2429 1427 2647 1422 2377 1712 2335 1377 1354 1078 + 73 68 69 37 27 80 3 2 78 24 54 97 86 71 83 5 39 41 29 12 + 50 11 9 7 65 9 18 83 53 17 29 37 16 70 93 66 81 56 40 73 + 2 3 4 6 5 4 5 1 6 4 5 2 9 3 8 3 5 9 3 2 + 5 6 8 5 8 2 9 2 10 3 3 1 6 9 2 3 2 8 8 9 + 426 1677 602 778 1508 480 1314 481 1390 1584 1019 718 1699 586 1048 758 1673 1716 1667 576 + 992 1056 823 1550 387 1735 1067 466 1497 1620 859 626 1525 1071 1501 1388 537 1036 1416 1843 + 14 82 47 64 34 24 58 37 92 15 99 18 68 22 3 35 70 63 81 23 + 37 11 21 17 34 91 90 58 54 83 33 88 19 17 74 53 78 80 13 1 + 1 3 8 10 9 7 8 8 2 4 5 5 10 3 5 8 10 3 7 10 + 10 4 4 2 5 5 10 5 8 1 5 4 1 1 2 4 6 2 7 8 + 512 426 483 1102 835 1669 1676 870 657 766 1767 1700 1635 420 1302 1545 990 1611 1546 1456 + 478 1646 1676 1729 935 573 1300 1865 611 751 695 1276 1866 1105 1798 765 391 1258 856 1706 + 43 79 34 85 17 73 61 53 96 87 10 8 83 82 84 27 8 76 66 92 + 30 37 17 85 37 88 76 92 44 4 59 4 94 14 62 13 60 100 57 14 + 10 8 5 9 6 2 1 7 2 8 5 6 2 8 6 1 4 5 4 6 + 9 2 2 6 10 6 8 8 7 9 3 9 8 2 10 8 1 9 8 5 + 1109 506 2070 665 2085 1505 533 1899 1501 724 1976 1522 581 557 2018 690 1868 1554 844 1590 + 467 1940 1763 1369 1936 1425 2013 679 1495 724 1630 1037 1915 1932 1138 1338 1323 1165 1816 2105 + 21 72 15 25 58 39 69 20 96 47 14 86 59 57 25 48 99 81 57 32 + 98 87 42 69 65 93 92 92 37 72 95 37 79 49 17 44 85 2 4 57 + 4 10 5 10 6 10 5 8 10 10 9 2 9 7 7 1 5 7 9 1 + 8 10 2 4 6 1 4 9 5 5 7 9 1 8 8 6 8 6 4 3 + 1806 1799 1583 1636 1853 2134 780 1173 490 552 782 1959 1216 2203 1886 1435 1192 1493 539 905 + 1803 1248 2125 1012 2231 586 696 1683 1072 1571 695 1184 641 663 2045 1416 1443 728 2213 1614 + 37 29 53 10 69 83 16 13 25 55 18 73 51 2 4 63 22 34 3 57 + 42 13 18 31 40 56 32 65 76 32 5 43 38 63 76 51 40 26 63 79 + 2 8 1 10 5 1 6 3 1 4 4 8 1 7 5 6 2 3 2 10 + 1 10 4 4 2 6 9 3 10 10 2 2 5 1 10 7 9 2 4 6 + 661 1410 923 401 1076 1203 573 1355 1202 647 449 846 818 1337 1200 589 1170 1086 952 664 + 1001 1267 637 1396 923 672 993 756 723 1567 1476 1369 1178 994 1102 1502 1361 678 1217 788 + 10 69 88 73 89 24 16 9 43 26 6 95 42 29 79 47 81 39 34 86 + 86 17 45 36 10 76 68 24 32 81 34 81 63 52 56 39 40 61 21 35 + 5 10 8 8 10 8 10 6 8 6 7 10 7 1 1 9 10 4 6 8 + 10 5 2 6 9 9 10 1 7 4 5 3 2 9 7 2 1 4 7 10 + 420 1309 525 1430 1497 131 131 1514 296 1456 762 1278 426 1374 41 693 575 814 596 175 + 574 446 977 1315 476 1021 1343 618 409 1383 594 1099 1208 364 492 1369 1164 995 1251 1014 + 85 65 41 46 85 31 59 91 41 26 43 78 43 1 85 72 10 59 55 39 + 88 59 1 52 51 8 50 84 3 71 5 12 75 64 50 16 26 55 46 79 + 10 4 9 5 6 3 1 8 3 5 4 7 5 7 1 1 3 10 7 1 + 6 1 5 9 3 5 5 8 10 1 7 1 2 5 10 4 2 7 8 1 + 655 170 1030 673 453 1100 1370 1455 621 209 1163 288 292 1547 506 1074 972 78 128 1240 + 143 1531 81 269 1265 886 760 1212 1146 673 29 500 1497 377 482 374 981 691 333 256 + 35 43 7 66 53 26 13 41 36 74 67 53 94 34 32 16 69 7 51 43 + 29 47 43 52 70 60 7 64 17 6 18 2 23 99 31 94 69 3 80 70 + 5 9 5 9 5 1 3 6 4 6 1 6 1 9 2 4 4 2 6 5 + 9 10 3 4 1 8 9 8 2 2 3 5 4 9 6 5 7 7 4 5 + 892 1198 538 525 1016 650 884 1099 634 992 1092 255 10 253 148 633 1093 974 662 1268 + 411 66 711 1030 787 261 342 1365 60 1361 382 1375 15 539 575 564 675 314 1190 881 + 62 83 63 75 18 87 28 46 73 44 99 55 62 99 14 61 48 50 86 24 + 20 46 6 9 98 92 45 84 27 44 51 19 83 11 49 89 68 58 46 71 + 4 8 4 3 10 4 5 4 1 9 7 8 1 8 6 3 5 10 4 7 + 6 1 2 2 6 3 7 7 3 4 10 5 5 6 4 9 10 1 6 1 + 236 261 1737 227 383 1094 776 1307 1372 671 88 172 684 1525 1628 615 870 1468 1711 1129 + 623 98 52 983 751 941 916 167 499 441 1124 218 23 609 1714 654 223 1524 1209 1551 + 97 16 62 14 29 41 26 97 26 62 36 20 43 19 99 89 64 7 99 12 + 2 73 2 1 98 17 49 69 18 94 60 35 61 9 33 20 51 23 44 46 + 10 10 7 8 10 1 8 9 8 3 6 2 6 10 9 1 5 10 9 7 + 4 8 9 6 1 5 10 2 2 8 1 2 2 9 8 1 4 8 10 8 + 553 1235 689 933 678 1103 595 145 126 988 741 1137 1095 1150 537 118 962 956 754 1100 + 611 1301 941 1056 768 686 337 1151 475 464 913 357 465 914 1257 1046 221 851 1011 1192 + 97 29 35 31 69 73 13 86 97 7 53 88 60 69 33 89 64 51 11 43 + 42 96 23 8 58 3 82 47 1 20 80 24 89 53 88 6 70 57 53 64 + 6 10 7 9 4 6 8 8 2 1 5 1 9 7 9 4 2 3 10 5 + 7 2 7 1 8 3 9 1 9 4 9 6 9 5 3 7 10 5 2 5 + 0 883 280 474 0 1053 782 2 185 0 779 873 232 0 0 332 497 45 629 734 + 360 1082 434 425 151 821 973 288 1004 616 993 1209 581 675 530 742 1155 0 1174 465 + 49 59 92 74 44 16 70 11 16 11 3 4 65 80 43 50 20 9 18 52 + 35 40 37 16 96 68 85 49 14 39 25 63 29 55 100 73 11 68 30 97 + 1 9 2 10 8 8 4 10 5 1 5 6 9 8 7 9 2 6 3 9 + 7 8 1 9 6 3 3 2 7 6 3 2 9 8 10 4 6 5 5 3 + 437 311 882 962 0 0 410 619 899 691 625 0 0 0 631 137 493 411 39 157 + 0 521 635 0 395 909 84 790 62 936 860 0 736 495 1040 0 607 1050 337 0 + 67 98 20 98 91 60 55 71 41 60 18 12 39 32 31 65 35 59 96 81 + 5 80 91 10 58 86 76 8 25 22 85 34 71 83 31 49 84 22 8 17 + 4 3 1 4 7 5 8 3 10 9 10 1 4 6 9 3 1 2 2 3 + 8 9 8 8 4 5 10 5 4 4 4 2 2 5 1 8 2 5 2 4 + 966 151 262 477 980 323 0 1172 0 848 1200 187 0 1148 1030 826 522 0 0 135 + 0 1206 150 0 0 36 0 243 1007 0 0 0 1244 413 503 0 0 882 192 603 + 94 92 86 56 74 45 8 68 59 92 8 62 89 62 80 31 38 34 97 71 + 5 46 12 81 17 84 53 62 31 90 28 21 31 57 23 64 28 34 1 4 + 3 6 10 10 6 6 6 9 7 5 6 1 3 1 5 4 1 10 10 8 + 8 3 2 3 7 6 5 10 10 4 9 1 9 4 3 10 2 2 2 1 + 628 713 0 822 0 413 488 833 0 0 736 1019 439 211 171 794 447 941 1064 845 + 310 83 654 655 30 0 263 72 497 0 712 62 916 54 155 1043 267 0 781 422 + 99 38 34 9 87 68 44 8 83 100 59 8 52 57 48 48 88 20 54 71 + 13 19 2 22 75 40 92 51 54 19 3 9 55 50 94 52 12 75 99 38 + 2 7 6 3 6 2 7 8 4 7 5 9 9 10 5 1 5 6 9 7 + 6 4 6 1 10 1 5 8 8 9 4 5 7 5 6 8 1 4 7 7 + 956 600 1039 413 130 759 56 968 702 442 413 360 985 0 986 389 870 1005 507 290 + 135 0 929 567 627 667 648 0 0 93 989 234 1137 680 989 1121 655 639 760 0 + 82 87 86 18 48 76 57 100 56 27 40 5 91 61 74 18 71 55 77 28 + 37 35 42 29 63 86 45 17 47 40 34 90 17 87 41 51 30 67 73 4 + 6 9 10 3 9 4 3 1 8 7 10 3 6 4 7 6 4 9 6 5 + 8 2 1 1 8 7 10 3 8 6 3 10 2 6 4 4 3 9 2 7 + 303 0 403 0 0 508 114 0 0 273 111 164 0 338 0 0 0 251 0 272 + 184 328 0 116 0 347 0 0 0 750 54 120 60 0 0 0 0 780 100 804 + 70 43 42 48 8 70 18 57 49 72 92 11 100 68 74 46 1 30 52 97 + 16 84 93 3 38 8 44 8 89 34 26 57 19 48 41 99 6 87 68 86 + 8 4 6 6 2 3 1 3 6 4 9 10 7 1 9 7 4 8 4 8 + 1 4 9 3 2 1 3 6 8 1 10 3 1 9 4 7 9 7 6 5 + 76 782 0 0 231 340 381 3 0 730 93 0 273 114 0 0 0 0 0 0 + 624 0 588 0 157 0 0 0 0 21 0 760 506 69 0 0 0 127 0 184 + 18 65 81 68 43 4 80 92 60 86 39 90 100 25 41 42 21 90 46 32 + 11 5 93 45 86 45 99 72 89 37 58 6 91 65 45 41 92 63 10 78 + 7 8 3 4 3 5 10 6 10 2 3 6 3 5 4 9 5 5 5 10 + 7 10 4 6 1 2 3 4 5 2 4 2 6 3 6 1 9 8 9 10 + 0 637 0 0 578 572 0 0 0 390 396 0 358 0 253 0 0 94 0 0 + 152 878 0 237 296 0 0 714 0 588 0 677 0 362 0 0 386 0 81 388 + 9 61 58 50 58 86 69 25 17 73 81 5 37 33 48 18 1 34 3 92 + 17 95 57 62 23 88 97 63 83 38 92 36 91 24 86 23 69 78 3 70 + 5 4 2 8 9 1 3 7 4 3 7 3 1 6 7 3 10 9 7 9 + 9 9 7 1 6 8 9 9 4 6 8 8 9 5 4 9 2 6 9 8 + 257 0 558 0 0 516 0 585 169 0 513 0 576 0 0 653 134 0 168 0 + 633 0 391 87 711 694 251 0 627 0 501 222 0 637 717 0 783 797 0 0 + 53 96 10 66 86 76 93 77 74 90 96 26 91 48 45 88 41 13 45 40 + 54 1 78 62 89 82 5 36 35 77 65 51 5 63 74 95 44 68 27 60 + 7 10 6 8 3 8 6 10 8 3 9 8 10 8 9 5 5 4 2 4 + 8 4 10 10 2 2 6 10 9 4 8 9 10 4 5 5 3 10 3 6 + 0 0 414 395 895 454 0 0 0 0 0 562 0 0 0 0 0 208 502 719 + 492 689 205 0 0 0 0 0 0 369 0 387 62 0 802 592 115 492 512 0 + 91 87 47 96 88 88 35 72 14 48 41 23 65 76 61 72 66 30 73 40 + 70 25 31 57 57 25 67 51 37 51 93 56 47 74 96 8 53 20 44 48 + 1 1 6 9 9 10 9 3 2 4 9 3 6 5 1 7 1 1 4 2 + 1 6 9 8 6 2 2 2 2 5 2 8 8 7 10 5 9 6 3 6 + 1702 2117 2171 2067 2477 2866 1026 1652 1803 1835 1758 2600 1385 1375 2016 1910 1018 1725 1750 2637 + 2831 2622 1326 1596 1075 1559 771 2629 1913 2043 2335 2093 2105 761 845 2121 2131 2497 1824 2775 + 90 3 63 19 54 87 60 38 49 11 50 72 93 77 2 90 32 44 64 62 + 42 20 64 62 24 69 4 68 11 6 69 59 61 2 18 19 61 78 90 35 + 10 6 2 2 7 7 6 2 4 5 3 10 2 2 1 7 10 6 10 5 + 8 10 3 6 1 4 7 5 4 2 3 5 10 8 3 4 2 7 7 6 + 1077 2349 748 1107 612 1111 2393 2411 592 757 1063 1644 1522 1852 2348 1293 1657 1341 728 2475 + 668 1616 1020 1993 2345 1324 2337 1887 959 596 2234 725 1269 1468 1814 1230 1037 1569 2184 1571 + 45 68 25 21 1 95 8 6 18 95 36 49 36 66 52 67 22 64 76 75 + 75 94 6 96 18 16 18 54 5 44 67 74 16 44 84 87 60 34 82 28 + 9 3 9 3 7 4 2 7 9 1 9 1 1 5 9 1 1 3 3 2 + 3 7 6 1 10 6 6 6 3 5 7 5 2 4 2 9 6 10 2 8 + 2017 780 2268 1232 1486 1892 1167 1097 1071 1186 1160 1453 1512 1372 1281 1940 691 1653 1349 1911 + 1161 1034 2267 1328 1293 663 1519 708 878 1372 1157 2024 1908 2167 1378 942 689 1616 816 1200 + 80 57 81 87 75 84 97 9 88 69 90 36 83 9 75 30 48 53 43 56 + 3 7 64 81 94 25 86 76 96 20 35 14 11 52 17 74 72 39 13 35 + 8 3 6 8 8 1 8 7 2 1 8 1 6 1 7 7 2 9 6 5 + 1 5 3 7 3 8 8 5 7 10 4 1 7 2 8 9 1 9 1 4 + 1406 2620 1708 868 2449 1982 2382 2807 2593 1857 767 2280 2118 941 2734 1902 1750 2773 2456 850 + 2453 2152 2229 2372 1984 750 902 734 1181 2180 710 1712 2678 1651 2201 882 2048 2665 2413 1948 + 47 99 55 8 49 80 25 85 56 58 82 16 51 25 11 11 48 30 78 81 + 86 73 22 40 34 7 65 6 88 39 4 99 74 30 16 45 44 92 55 69 + 8 5 5 2 5 7 8 3 6 1 6 2 4 7 1 2 8 9 8 8 + 5 8 4 3 1 10 2 4 1 8 6 8 9 1 5 2 5 3 1 6 + 2131 2372 2386 1464 1665 2256 633 2265 2299 664 1505 1065 1060 2075 2130 791 708 1656 1194 2338 + 1625 2549 1222 1537 634 2421 1959 977 725 843 676 1033 1948 1700 1397 1779 1233 748 1337 1940 + 27 77 52 78 26 63 68 74 44 36 58 77 88 88 80 43 90 63 71 31 + 14 58 43 1 50 24 89 18 16 64 79 50 59 35 60 68 53 48 60 45 + 7 2 1 3 3 4 8 6 3 5 1 2 5 4 4 8 4 3 1 3 + 5 4 4 4 10 1 4 4 1 6 10 1 7 8 1 9 6 9 10 9 + 237 1217 1587 1994 1991 1195 1477 1525 968 1952 2371 822 413 1314 2139 1235 492 2238 755 940 + 259 1356 635 294 701 295 1559 1768 2369 726 1007 2290 1925 507 2085 1849 606 1743 859 2344 + 26 27 54 92 35 64 78 18 5 34 43 8 65 24 79 64 70 76 92 90 + 5 83 69 95 81 93 72 90 40 7 45 91 49 78 6 14 5 13 55 80 + 1 4 4 10 7 1 10 9 3 10 6 6 5 8 6 4 4 8 10 5 + 2 9 7 1 8 6 3 7 9 9 6 8 2 5 7 9 1 7 2 8 + 894 2007 1726 868 1628 2120 505 575 2001 1808 1630 2012 2263 248 1103 2307 1114 1751 1595 914 + 1848 367 2220 600 535 1488 2027 1040 610 2228 334 1923 1054 340 228 769 422 2079 441 264 + 82 36 18 50 34 69 88 66 19 33 42 4 85 12 82 13 12 44 88 45 + 4 20 17 56 95 85 5 4 49 36 64 50 55 23 89 58 32 50 47 97 + 7 8 5 1 7 4 6 4 4 10 4 2 9 4 7 2 9 6 3 10 + 4 5 5 2 8 5 10 8 6 5 7 6 10 1 9 6 2 6 4 8 + 674 456 1540 1236 1816 2008 878 1173 422 1376 244 1143 883 602 1167 285 1934 783 241 823 + 194 1543 568 1513 211 1447 1829 1291 1282 1186 572 2010 1924 275 722 947 1558 241 867 963 + 58 47 84 55 10 17 6 65 93 69 35 59 79 84 44 33 66 97 85 86 + 84 96 86 21 88 22 55 42 62 69 79 57 18 85 20 78 97 70 21 20 + 7 6 10 7 4 3 4 6 3 6 6 8 9 9 5 4 9 4 2 4 + 6 8 1 9 3 4 9 1 1 10 9 3 9 1 4 10 4 8 4 7 + 1786 896 2051 2195 254 2157 2573 2021 795 341 585 1241 1619 1215 1151 568 2437 2473 1213 1246 + 1907 2446 1672 1456 922 613 1896 929 2394 1623 2428 2047 490 1764 1840 397 1497 2348 1784 2266 + 48 54 68 13 77 100 22 69 65 31 83 74 3 26 96 72 14 17 85 7 + 48 89 50 30 82 80 72 91 23 81 7 47 4 35 91 55 87 41 39 76 + 3 4 8 9 7 5 4 10 5 3 4 3 9 10 6 3 2 8 3 4 + 5 2 1 2 6 5 10 1 3 4 2 10 2 4 4 7 7 4 5 1 + 1579 287 1273 2303 1093 2152 1853 299 1321 1075 1570 957 1683 718 697 2018 2311 1762 1056 764 + 1694 1301 2055 1484 1854 1925 2096 344 1691 351 1064 1454 2161 807 1741 1745 1769 1515 1533 580 + 72 6 63 32 78 62 99 33 97 60 22 52 45 76 47 78 63 40 70 4 + 26 32 63 26 30 88 79 43 6 96 46 56 8 96 72 71 21 83 42 97 + 7 8 2 9 3 4 2 2 3 2 3 8 8 7 6 4 5 6 5 10 + 3 6 7 3 6 7 4 5 9 6 1 7 3 7 4 10 1 8 6 9 + 1023 0 0 660 228 0 732 1145 1008 1912 1104 1101 861 1191 372 1551 1685 958 0 0 + 846 969 1185 1958 798 141 767 0 143 302 1448 1685 1068 123 655 649 414 1046 1680 251 + 78 3 41 24 83 100 84 16 84 51 69 97 18 27 86 4 50 54 100 76 + 75 11 57 56 13 30 80 89 14 95 71 25 56 44 8 15 45 69 50 15 + 7 6 8 10 7 7 4 9 1 3 2 3 4 6 5 2 4 5 9 9 + 2 4 2 7 1 3 8 10 2 9 2 4 4 2 3 6 4 2 5 1 + 94 1227 63 140 429 625 1510 1616 370 1643 1336 103 1152 1814 1462 1506 1339 195 0 1240 + 754 912 1291 1690 1319 1245 0 1841 677 1349 330 299 1271 589 483 1420 0 1076 39 1839 + 43 100 53 5 90 67 98 80 45 52 19 20 65 22 78 6 39 1 13 26 + 82 63 41 69 37 39 58 27 76 9 36 97 98 8 26 77 31 24 59 69 + 1 5 5 5 4 10 1 10 8 8 4 1 6 1 9 3 1 7 1 2 + 2 8 3 6 8 5 9 4 7 1 5 5 4 8 3 9 2 3 7 8 + 422 532 1715 1540 598 781 1339 326 1439 39 296 800 938 1552 89 1275 1747 389 823 989 + 1257 426 1513 1014 182 1139 1359 307 425 947 51 514 28 1541 999 64 779 1484 0 929 + 73 4 28 92 28 67 40 91 29 63 42 32 57 60 67 43 22 75 24 60 + 61 82 3 18 39 5 71 56 50 24 72 18 14 70 29 49 83 28 45 98 + 4 5 7 10 5 3 9 3 3 8 6 4 8 1 4 3 9 10 2 1 + 2 5 2 8 3 6 10 8 5 7 4 3 1 8 8 9 10 4 8 1 + 422 192 488 1319 1672 163 137 427 1141 674 1429 1364 676 587 993 839 363 417 179 1366 + 263 0 1537 691 1188 241 511 1292 697 0 1032 1714 1521 666 1478 335 0 1297 710 83 + 100 75 71 8 49 56 90 17 13 41 90 48 93 92 76 77 21 25 28 86 + 55 10 9 58 84 70 57 79 22 79 88 38 13 66 99 28 29 13 61 46 + 2 5 9 10 2 2 4 8 4 7 6 7 5 10 2 2 9 7 2 6 + 10 8 2 7 9 1 6 10 7 9 7 2 3 2 9 3 2 4 8 1 + 1246 152 965 1255 353 1193 1701 1284 1061 960 1505 1466 318 1651 1082 282 1048 985 400 1832 + 1151 243 1182 1721 861 411 1850 0 382 1578 17 0 766 1244 394 1199 1550 200 0 1856 + 86 27 68 77 39 46 5 28 2 91 91 86 99 21 4 73 83 52 97 18 + 35 47 3 49 64 28 32 1 8 65 74 9 31 84 94 14 28 65 39 58 + 5 8 2 3 4 1 8 5 7 8 7 4 9 9 7 7 10 7 9 2 + 10 4 10 10 9 5 8 1 1 3 5 7 4 6 6 10 10 8 2 3 + 0 0 152 1288 16 275 1087 0 462 1130 1134 805 798 1273 370 1290 0 0 495 0 + 199 751 1299 334 1309 735 0 0 1240 1183 1096 78 319 0 459 10 767 0 879 160 + 20 27 81 26 8 99 18 100 29 73 52 68 30 7 31 99 86 9 81 69 + 99 44 7 52 100 27 91 31 49 27 93 88 32 67 54 9 66 22 89 85 + 7 3 8 6 8 7 7 8 6 9 6 1 10 4 7 9 9 8 2 3 + 7 9 8 6 2 3 7 5 5 2 10 4 9 2 7 1 4 8 7 1 + 1338 923 0 770 1335 300 0 251 1167 1117 326 518 198 951 909 1494 241 0 7 360 + 1072 795 1055 1145 1299 0 728 0 303 785 0 0 1254 1142 1225 1178 194 467 0 1221 + 20 95 51 86 95 33 43 11 55 3 56 19 70 58 12 27 34 5 8 58 + 73 4 21 33 40 68 5 82 16 63 92 33 9 8 93 20 58 76 97 79 + 6 5 4 6 2 6 7 7 4 1 3 5 9 9 3 8 1 10 3 2 + 4 4 1 4 8 6 9 4 3 5 2 1 9 9 9 6 1 9 3 10 + 0 407 1105 76 733 1032 363 397 0 403 65 811 529 525 835 607 0 0 82 0 + 1011 710 0 747 0 1073 880 1134 393 864 882 0 1175 0 0 378 113 887 473 1207 + 30 3 97 91 68 17 70 40 71 46 68 65 29 38 83 31 61 100 86 6 + 14 85 24 60 61 6 24 68 7 24 91 5 82 56 6 54 45 71 31 96 + 1 7 1 5 9 7 1 7 2 6 10 4 3 10 1 7 2 10 9 6 + 10 5 6 3 4 7 4 2 1 7 1 5 5 9 7 8 1 5 5 5 + 1068 1151 1235 0 0 14 0 283 0 0 1263 269 229 0 0 23 907 79 0 0 + 191 683 698 1288 278 486 0 538 822 0 958 0 0 0 82 0 1142 0 359 34 + 26 77 17 78 22 74 68 72 92 21 27 38 47 65 56 89 28 6 98 38 + 3 98 73 4 69 48 78 7 51 15 91 71 100 13 65 24 100 7 34 94 + 7 4 1 3 3 6 5 2 10 8 5 6 8 3 8 7 4 3 10 1 + 6 5 4 4 6 10 5 3 9 7 1 5 7 2 4 9 7 5 10 5 + 557 1428 0 0 0 759 0 0 0 0 0 0 904 909 0 500 0 1016 0 1431 + 0 916 719 290 1306 0 19 543 64 599 891 67 150 548 609 0 1338 493 1399 739 + 8 97 86 87 82 81 86 93 65 3 53 72 92 8 22 81 24 92 8 83 + 53 6 1 40 96 54 73 22 100 92 71 99 40 31 21 45 19 33 28 37 + 10 10 2 10 4 7 5 9 5 8 8 10 8 9 6 3 8 8 9 6 + 8 1 1 9 9 1 8 4 10 4 1 1 10 5 2 4 5 3 9 6 + 378 913 0 0 534 0 91 0 0 461 0 191 0 48 690 945 0 0 0 0 + 0 140 0 0 261 719 927 0 0 0 0 881 241 0 0 0 0 0 0 675 + 7 95 56 62 98 66 47 95 63 17 43 4 58 89 50 92 67 76 56 69 + 2 26 23 46 3 38 93 37 40 67 86 59 7 19 1 35 31 54 14 88 + 4 5 5 9 4 6 5 2 4 4 1 8 7 2 10 9 4 9 2 9 + 4 9 4 1 5 4 3 1 3 9 1 4 2 8 3 7 6 9 7 9 + 266 139 686 373 572 37 0 376 716 0 558 0 0 0 0 0 0 0 63 0 + 473 0 819 0 316 0 634 151 0 574 52 0 0 0 796 0 665 0 140 401 + 81 57 60 48 52 13 56 70 89 43 72 73 71 86 88 8 3 32 61 32 + 19 27 28 1 81 66 1 78 80 2 73 27 34 36 13 16 90 27 70 16 + 10 3 2 4 10 9 10 10 2 9 2 10 7 7 8 10 10 8 1 9 + 8 9 6 3 5 8 4 2 5 1 7 5 7 9 3 5 2 2 2 2 + 0 350 627 0 383 122 936 804 0 875 357 252 0 0 0 0 111 0 935 0 + 871 0 0 0 0 0 0 131 282 0 0 416 848 0 0 542 247 0 376 0 + 15 21 8 96 50 55 94 15 72 12 94 23 81 26 60 20 4 86 22 7 + 98 32 86 91 46 59 38 78 60 14 42 80 82 98 41 13 79 63 53 93 + 8 5 2 9 9 4 9 1 3 2 2 9 6 9 3 4 2 3 7 6 + 3 7 10 9 10 8 6 5 2 3 7 8 7 2 2 3 1 2 4 7 + 0 740 0 0 571 272 0 0 0 0 0 870 845 765 668 208 0 281 745 0 + 0 0 0 799 555 537 595 752 572 0 41 0 258 61 0 0 933 1021 0 778 + 26 78 87 57 31 82 20 28 71 10 41 1 69 94 71 21 74 17 12 37 + 63 19 43 1 44 89 23 69 49 74 86 99 41 69 47 52 10 25 97 93 + 7 8 5 8 6 9 3 4 8 8 4 8 4 5 1 6 5 7 8 3 + 10 5 7 6 6 7 9 10 9 8 2 6 3 6 4 10 2 9 9 5 + 506 0 0 197 0 0 772 818 660 0 801 420 0 593 0 249 0 0 292 0 + 469 122 0 719 42 152 0 824 241 0 0 966 0 0 0 0 430 0 814 0 + diff --git a/examples/cpp/variable_intervals_sat_test.bintest b/examples/cpp/variable_intervals_sat_test.bintest new file mode 100644 index 0000000000..51d3dbf2eb --- /dev/null +++ b/examples/cpp/variable_intervals_sat_test.bintest @@ -0,0 +1 @@ +RUN: $(variable_intervals_sat) diff --git a/examples/cpp/weighted_tardiness_sat_test.bintest b/examples/cpp/weighted_tardiness_sat_test.bintest new file mode 100644 index 0000000000..5c95404e75 --- /dev/null +++ b/examples/cpp/weighted_tardiness_sat_test.bintest @@ -0,0 +1 @@ +RUN: $(weighted_tardiness_sat) --input $(wt40.txt) diff --git a/examples/python/BUILD.bazel b/examples/python/BUILD.bazel index 03313e71d6..3798011fd2 100644 --- a/examples/python/BUILD.bazel +++ b/examples/python/BUILD.bazel @@ -13,7 +13,7 @@ load("@pip_deps//:requirements.bzl", "requirement") load("@rules_python//python:py_binary.bzl", "py_binary") -load("//bazel:run_binary_test.bzl", "run_binary_test") +load("//tools/testing:bintest.bzl", "bintest") package(default_visibility = ["//visibility:public"]) @@ -39,10 +39,11 @@ py_binary( ], ) -run_binary_test( +bintest( name = "assignment_with_constraints_sat_py_test", size = "medium", - binary = ":assignment_with_constraints_sat_py3", + srcs = [":assignment_with_constraints_sat_py_test.bintest"], + named_data = {"assignment_with_constraints_sat_py3": ":assignment_with_constraints_sat_py3"}, ) py_binary( @@ -55,10 +56,11 @@ py_binary( ], ) -run_binary_test( +bintest( name = "balance_group_sat_py_test", size = "medium", - binary = ":balance_group_sat_py3", + srcs = [":balance_group_sat_py_test.bintest"], + named_data = {"balance_group_sat_py3": ":balance_group_sat_py3"}, ) py_binary( @@ -71,11 +73,11 @@ py_binary( ], ) -run_binary_test( +bintest( name = "bus_driver_scheduling_sat_py_test", size = "medium", - args = ["--params=max_time_in_seconds:40"], - binary = ":bus_driver_scheduling_sat_py3", + srcs = [":bus_driver_scheduling_sat_py_test.bintest"], + named_data = {"bus_driver_scheduling_sat_py3": ":bus_driver_scheduling_sat_py3"}, ) py_binary( @@ -88,10 +90,11 @@ py_binary( ], ) -run_binary_test( +bintest( name = "car_sequencing_optimization_sat_py_test", size = "small", - binary = ":car_sequencing_optimization_sat_py3", + srcs = [":car_sequencing_optimization_sat_py_test.bintest"], + named_data = {"car_sequencing_optimization_sat_py3": ":car_sequencing_optimization_sat_py3"}, ) py_binary( @@ -104,10 +107,11 @@ py_binary( ], ) -run_binary_test( +bintest( name = "chemical_balance_sat_py_test", size = "medium", - binary = ":chemical_balance_sat_py3", + srcs = [":chemical_balance_sat_py_test.bintest"], + named_data = {"chemical_balance_sat_py3": ":chemical_balance_sat_py3"}, ) py_binary( @@ -120,10 +124,11 @@ py_binary( ], ) -run_binary_test( +bintest( name = "clustering_sat_py_test", size = "medium", - binary = ":clustering_sat_py3", + srcs = [":clustering_sat_py_test.bintest"], + named_data = {"clustering_sat_py3": ":clustering_sat_py3"}, ) py_binary( @@ -136,10 +141,11 @@ py_binary( ], ) -run_binary_test( +bintest( name = "cover_rectangle_sat_py_test", size = "medium", - binary = ":cover_rectangle_sat_py3", + srcs = [":cover_rectangle_sat_py_test.bintest"], + named_data = {"cover_rectangle_sat_py3": ":cover_rectangle_sat_py3"}, ) py_binary( @@ -149,9 +155,10 @@ py_binary( deps = ["//ortools/sat/python:cp_model"], ) -run_binary_test( +bintest( name = "flexible_job_shop_sat_py_test", - binary = ":flexible_job_shop_sat_py3", + srcs = [":flexible_job_shop_sat_py_test.bintest"], + named_data = {"flexible_job_shop_sat_py3": ":flexible_job_shop_sat_py3"}, ) py_binary( @@ -165,9 +172,10 @@ py_binary( ], ) -run_binary_test( +bintest( name = "gate_scheduling_sat_py_test", - binary = ":gate_scheduling_sat_py3", + srcs = [":gate_scheduling_sat_py_test.bintest"], + named_data = {"gate_scheduling_sat_py3": ":gate_scheduling_sat_py3"}, ) py_binary( @@ -180,10 +188,11 @@ py_binary( ], ) -run_binary_test( +bintest( name = "golomb_sat_py_test", size = "medium", - binary = ":golomb_sat_py3", + srcs = [":golomb_sat_py_test.bintest"], + named_data = {"golomb_sat_py3": ":golomb_sat_py3"}, ) py_binary( @@ -197,9 +206,10 @@ py_binary( ], ) -run_binary_test( +bintest( name = "hidato_sat_py_test", - binary = ":hidato_sat_py3", + srcs = [":hidato_sat_py_test.bintest"], + named_data = {"hidato_sat_py3": ":hidato_sat_py3"}, ) py_binary( @@ -209,9 +219,10 @@ py_binary( deps = ["//ortools/sat/python:cp_model"], ) -run_binary_test( +bintest( name = "jobshop_ft06_distance_sat_py_test", - binary = ":jobshop_ft06_distance_sat_py3", + srcs = [":jobshop_ft06_distance_sat_py_test.bintest"], + named_data = {"jobshop_ft06_distance_sat_py3": ":jobshop_ft06_distance_sat_py3"}, ) py_binary( @@ -224,9 +235,10 @@ py_binary( ], ) -run_binary_test( +bintest( name = "jobshop_ft06_sat_py_test", - binary = ":jobshop_ft06_sat_py3", + srcs = [":jobshop_ft06_sat_py_test.bintest"], + named_data = {"jobshop_ft06_sat_py3": ":jobshop_ft06_sat_py3"}, ) py_binary( @@ -239,10 +251,11 @@ py_binary( ], ) -run_binary_test( +bintest( name = "jobshop_with_maintenance_sat_py_test", size = "medium", - binary = ":jobshop_with_maintenance_sat_py3", + srcs = [":jobshop_with_maintenance_sat_py_test.bintest"], + named_data = {"jobshop_with_maintenance_sat_py3": ":jobshop_with_maintenance_sat_py3"}, ) py_binary( @@ -257,10 +270,11 @@ py_binary( ], ) -run_binary_test( +bintest( name = "knapsack_2d_sat_py_test", size = "medium", - binary = ":knapsack_2d_sat_py3", + srcs = [":knapsack_2d_sat_py_test.bintest"], + named_data = {"knapsack_2d_sat_py3": ":knapsack_2d_sat_py3"}, ) py_binary( @@ -273,12 +287,13 @@ py_binary( ], ) -run_binary_test( +bintest( name = "line_balancing_sat_salbp_20_1_py_test", - args = ["--input=$(rootpath //examples/python/testdata:salbp_20_1.alb)"], - binary = ":line_balancing_sat_py3", - data = ["//examples/python/testdata:salbp_20_1.alb"], - grep_lines = ["objective: 3"], + srcs = [":line_balancing_sat_salbp_20_1_py_test.bintest"], + named_data = { + "line_balancing_sat_py3": ":line_balancing_sat_py3", + "salbp_20_1.alb": "//examples/python/testdata:salbp_20_1.alb", + }, ) py_binary( @@ -291,10 +306,11 @@ py_binary( ], ) -run_binary_test( +bintest( name = "maximize_combinations_sat_py_test", size = "medium", - binary = ":maximize_combinations_sat_py3", + srcs = [":maximize_combinations_sat_py_test.bintest"], + named_data = {"maximize_combinations_sat_py3": ":maximize_combinations_sat_py3"}, ) py_binary( @@ -307,9 +323,10 @@ py_binary( ], ) -run_binary_test( +bintest( name = "maze_escape_sat_py_test", - binary = ":maze_escape_sat_py3", + srcs = [":maze_escape_sat_py_test.bintest"], + named_data = {"maze_escape_sat_py3": ":maze_escape_sat_py3"}, ) py_binary( @@ -322,9 +339,10 @@ py_binary( ], ) -run_binary_test( +bintest( name = "music_playlist_sat_py_test", - binary = ":music_playlist_sat_py3", + srcs = [":music_playlist_sat_py_test.bintest"], + named_data = {"music_playlist_sat_py3": ":music_playlist_sat_py3"}, ) py_binary( @@ -337,10 +355,11 @@ py_binary( ], ) -run_binary_test( +bintest( name = "no_wait_baking_scheduling_sat_py_test", size = "medium", - binary = ":no_wait_baking_scheduling_sat_py3", + srcs = [":no_wait_baking_scheduling_sat_py_test.bintest"], + named_data = {"no_wait_baking_scheduling_sat_py3": ":no_wait_baking_scheduling_sat_py3"}, ) py_binary( @@ -353,10 +372,11 @@ py_binary( ], ) -run_binary_test( +bintest( name = "pell_equation_sat_py_test", size = "medium", - binary = ":pell_equation_sat_py3", + srcs = [":pell_equation_sat_py_test.bintest"], + named_data = {"pell_equation_sat_py3": ":pell_equation_sat_py3"}, ) py_binary( @@ -369,10 +389,11 @@ py_binary( ], ) -run_binary_test( +bintest( name = "pentominoes_sat_py_test", size = "medium", - binary = ":pentominoes_sat_py3", + srcs = [":pentominoes_sat_py_test.bintest"], + named_data = {"pentominoes_sat_py3": ":pentominoes_sat_py3"}, ) py_binary( @@ -385,10 +406,11 @@ py_binary( ], ) -run_binary_test( +bintest( name = "prize_collecting_tsp_sat_py_test", size = "medium", - binary = ":prize_collecting_tsp_sat_py3", + srcs = [":prize_collecting_tsp_sat_py_test.bintest"], + named_data = {"prize_collecting_tsp_sat_py3": ":prize_collecting_tsp_sat_py3"}, ) py_binary( @@ -401,10 +423,11 @@ py_binary( ], ) -run_binary_test( +bintest( name = "prize_collecting_vrp_sat_py_test", size = "medium", - binary = ":prize_collecting_vrp_sat_py3", + srcs = [":prize_collecting_vrp_sat_py_test.bintest"], + named_data = {"prize_collecting_vrp_sat_py3": ":prize_collecting_vrp_sat_py3"}, ) py_binary( @@ -417,26 +440,29 @@ py_binary( ], ) -run_binary_test( +bintest( name = "qubo_sat_py_test", size = "medium", - binary = ":qubo_sat_py3", + srcs = [":qubo_sat_py_test.bintest"], + named_data = {"qubo_sat_py3": ":qubo_sat_py3"}, ) -run_binary_test( +bintest( name = "rcpsp_sat_c1510_1_py_test", - args = ["--input=$(rootpath //ortools/scheduling/testdata:c1510_1.mm.txt)"], - binary = ":rcpsp_sat_py3", - data = ["//ortools/scheduling/testdata:c1510_1.mm.txt"], - grep_lines = ["objective: 21"], + srcs = [":rcpsp_sat_c1510_1_py_test.bintest"], + named_data = { + "rcpsp_sat_py3": ":rcpsp_sat_py3", + "c1510_1.mm.txt": "//ortools/scheduling/testdata:c1510_1.mm.txt", + }, ) -run_binary_test( +bintest( name = "rcpsp_sat_j301_1_py_test", - args = ["--input=$(rootpath //ortools/scheduling/testdata:j301_1.sm)"], - binary = ":rcpsp_sat_py3", - data = ["//ortools/scheduling/testdata:j301_1.sm"], - grep_lines = ["objective: 43"], + srcs = [":rcpsp_sat_j301_1_py_test.bintest"], + named_data = { + "rcpsp_sat_py3": ":rcpsp_sat_py3", + "j301_1.sm": "//ortools/scheduling/testdata:j301_1.sm", + }, ) py_binary( @@ -461,10 +487,10 @@ py_binary( ], ) -run_binary_test( +bintest( name = "shift_scheduling_sat_py_test", - args = ["--params=max_time_in_seconds:10"], - binary = ":shift_scheduling_sat_py3", + srcs = [":shift_scheduling_sat_py_test.bintest"], + named_data = {"shift_scheduling_sat_py3": ":shift_scheduling_sat_py3"}, ) py_binary( @@ -477,10 +503,11 @@ py_binary( ], ) -run_binary_test( +bintest( name = "single_machine_scheduling_with_setup_release_due_dates_sat_py_test", size = "medium", - binary = ":single_machine_scheduling_with_setup_release_due_dates_sat_py3", + srcs = [":single_machine_scheduling_with_setup_release_due_dates_sat_py_test.bintest"], + named_data = {"single_machine_scheduling_with_setup_release_due_dates_sat_py3": ":single_machine_scheduling_with_setup_release_due_dates_sat_py3"}, ) py_binary( @@ -493,9 +520,10 @@ py_binary( ], ) -run_binary_test( +bintest( name = "spread_robots_sat_py_test", - binary = ":spread_robots_sat_py3", + srcs = [":spread_robots_sat_py_test.bintest"], + named_data = {"spread_robots_sat_py3": ":spread_robots_sat_py3"}, ) py_binary( @@ -508,9 +536,10 @@ py_binary( ], ) -run_binary_test( +bintest( name = "steel_mill_slab_sat_py_test", - binary = ":steel_mill_slab_sat_py3", + srcs = [":steel_mill_slab_sat_py_test.bintest"], + named_data = {"steel_mill_slab_sat_py3": ":steel_mill_slab_sat_py3"}, ) py_binary( @@ -520,9 +549,10 @@ py_binary( deps = ["//ortools/sat/python:cp_model"], ) -run_binary_test( +bintest( name = "sudoku_sat_py_test", - binary = ":sudoku_sat_py3", + srcs = [":sudoku_sat_py_test.bintest"], + named_data = {"sudoku_sat_py3": ":sudoku_sat_py3"}, ) py_binary( @@ -535,10 +565,11 @@ py_binary( ], ) -run_binary_test( +bintest( name = "task_allocation_sat_py_test", size = "medium", - binary = ":task_allocation_sat_py3", + srcs = [":task_allocation_sat_py_test.bintest"], + named_data = {"task_allocation_sat_py3": ":task_allocation_sat_py3"}, ) py_binary( @@ -551,10 +582,11 @@ py_binary( ], ) -run_binary_test( +bintest( name = "tasks_and_workers_assignment_sat_py_test", size = "medium", - binary = ":tasks_and_workers_assignment_sat_py3", + srcs = [":tasks_and_workers_assignment_sat_py_test.bintest"], + named_data = {"tasks_and_workers_assignment_sat_py3": ":tasks_and_workers_assignment_sat_py3"}, ) py_binary( @@ -568,10 +600,11 @@ py_binary( ], ) -run_binary_test( +bintest( name = "test_scheduling_sat_py_test", size = "medium", - binary = ":test_scheduling_sat_py3", + srcs = [":test_scheduling_sat_py_test.bintest"], + named_data = {"test_scheduling_sat_py3": ":test_scheduling_sat_py3"}, ) py_binary( @@ -581,10 +614,11 @@ py_binary( deps = ["//ortools/sat/python:cp_model"], ) -run_binary_test( +bintest( name = "tsp_sat_py_test", size = "medium", - binary = ":tsp_sat_py3", + srcs = [":tsp_sat_py_test.bintest"], + named_data = {"tsp_sat_py3": ":tsp_sat_py3"}, ) py_binary( @@ -597,10 +631,11 @@ py_binary( ], ) -run_binary_test( +bintest( name = "vendor_scheduling_sat_py_test", size = "medium", - binary = ":vendor_scheduling_sat_py3", + srcs = [":vendor_scheduling_sat_py_test.bintest"], + named_data = {"vendor_scheduling_sat_py3": ":vendor_scheduling_sat_py3"}, ) py_binary( @@ -613,10 +648,11 @@ py_binary( ], ) -run_binary_test( +bintest( name = "wedding_optimal_chart_sat_py_test", size = "medium", - binary = ":wedding_optimal_chart_sat_py3", + srcs = [":wedding_optimal_chart_sat_py_test.bintest"], + named_data = {"wedding_optimal_chart_sat_py3": ":wedding_optimal_chart_sat_py3"}, ) py_binary( @@ -626,7 +662,8 @@ py_binary( deps = ["//ortools/sat/python:cp_model"], ) -run_binary_test( +bintest( name = "zebra_sat_py_test", - binary = ":zebra_sat_py3", + srcs = [":zebra_sat_py_test.bintest"], + named_data = {"zebra_sat_py3": ":zebra_sat_py3"}, ) diff --git a/examples/python/CMakeBazel.txt b/examples/python/CMakeBazel.txt new file mode 100644 index 0000000000..5aacc94907 --- /dev/null +++ b/examples/python/CMakeBazel.txt @@ -0,0 +1,434 @@ +# This file is auto generated by bazel2cmake.py from examples/python/BUILD.bazel +# Don't edit manually, your changes will be lost. +# You can update this file by running: +# python3 tools/build/bazel2cmake.py examples/python/BUILD.bazel + + +add_python_binary( + NAME bzl_py_example_arc_flow_cutting_stock_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/arc_flow_cutting_stock_sat.py +) + +add_python_binary( + NAME bzl_py_example_assignment_with_constraints_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/assignment_with_constraints_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_assignment_with_constraints_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/assignment_with_constraints_sat_py_test.bintest + ENVIRONMENT BINTEST_assignment_with_constraints_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_balance_group_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/balance_group_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_balance_group_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/balance_group_sat_py_test.bintest + ENVIRONMENT BINTEST_balance_group_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_bus_driver_scheduling_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/bus_driver_scheduling_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_bus_driver_scheduling_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/bus_driver_scheduling_sat_py_test.bintest + ENVIRONMENT BINTEST_bus_driver_scheduling_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_car_sequencing_optimization_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/car_sequencing_optimization_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_car_sequencing_optimization_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/car_sequencing_optimization_sat_py_test.bintest + ENVIRONMENT BINTEST_car_sequencing_optimization_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_chemical_balance_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/chemical_balance_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_chemical_balance_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/chemical_balance_sat_py_test.bintest + ENVIRONMENT BINTEST_chemical_balance_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_clustering_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/clustering_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_clustering_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/clustering_sat_py_test.bintest + ENVIRONMENT BINTEST_clustering_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_cover_rectangle_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/cover_rectangle_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_cover_rectangle_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/cover_rectangle_sat_py_test.bintest + ENVIRONMENT BINTEST_cover_rectangle_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_flexible_job_shop_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/flexible_job_shop_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_flexible_job_shop_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/flexible_job_shop_sat_py_test.bintest + ENVIRONMENT BINTEST_flexible_job_shop_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_gate_scheduling_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/gate_scheduling_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_gate_scheduling_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/gate_scheduling_sat_py_test.bintest + ENVIRONMENT BINTEST_gate_scheduling_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_golomb_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/golomb_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_golomb_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/golomb_sat_py_test.bintest + ENVIRONMENT BINTEST_golomb_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_hidato_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/hidato_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_hidato_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/hidato_sat_py_test.bintest + ENVIRONMENT BINTEST_hidato_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_jobshop_ft06_distance_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/jobshop_ft06_distance_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_jobshop_ft06_distance_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/jobshop_ft06_distance_sat_py_test.bintest + ENVIRONMENT BINTEST_jobshop_ft06_distance_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_jobshop_ft06_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/jobshop_ft06_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_jobshop_ft06_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/jobshop_ft06_sat_py_test.bintest + ENVIRONMENT BINTEST_jobshop_ft06_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_jobshop_with_maintenance_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/jobshop_with_maintenance_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_jobshop_with_maintenance_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/jobshop_with_maintenance_sat_py_test.bintest + ENVIRONMENT BINTEST_jobshop_with_maintenance_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_knapsack_2d_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/knapsack_2d_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_knapsack_2d_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/knapsack_2d_sat_py_test.bintest + ENVIRONMENT BINTEST_knapsack_2d_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_line_balancing_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/line_balancing_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_line_balancing_sat_salbp_20_1_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/line_balancing_sat_salbp_20_1_py_test.bintest + ENVIRONMENT BINTEST_line_balancing_sat_py3=$ BINTEST_salbp_20_1.alb=${CMAKE_SOURCE_DIR}/examples/python/testdata/salbp_20_1.alb +) + +add_python_binary( + NAME bzl_py_example_maximize_combinations_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/maximize_combinations_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_maximize_combinations_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/maximize_combinations_sat_py_test.bintest + ENVIRONMENT BINTEST_maximize_combinations_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_maze_escape_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/maze_escape_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_maze_escape_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/maze_escape_sat_py_test.bintest + ENVIRONMENT BINTEST_maze_escape_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_music_playlist_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/music_playlist_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_music_playlist_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/music_playlist_sat_py_test.bintest + ENVIRONMENT BINTEST_music_playlist_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_no_wait_baking_scheduling_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/no_wait_baking_scheduling_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_no_wait_baking_scheduling_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/no_wait_baking_scheduling_sat_py_test.bintest + ENVIRONMENT BINTEST_no_wait_baking_scheduling_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_pell_equation_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/pell_equation_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_pell_equation_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/pell_equation_sat_py_test.bintest + ENVIRONMENT BINTEST_pell_equation_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_pentominoes_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/pentominoes_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_pentominoes_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/pentominoes_sat_py_test.bintest + ENVIRONMENT BINTEST_pentominoes_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_prize_collecting_tsp_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/prize_collecting_tsp_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_prize_collecting_tsp_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/prize_collecting_tsp_sat_py_test.bintest + ENVIRONMENT BINTEST_prize_collecting_tsp_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_prize_collecting_vrp_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/prize_collecting_vrp_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_prize_collecting_vrp_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/prize_collecting_vrp_sat_py_test.bintest + ENVIRONMENT BINTEST_prize_collecting_vrp_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_qubo_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/qubo_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_qubo_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/qubo_sat_py_test.bintest + ENVIRONMENT BINTEST_qubo_sat_py3=$ +) + +ortools_cxx_bintest( + NAME bzl_py_example_rcpsp_sat_c1510_1_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/rcpsp_sat_c1510_1_py_test.bintest + ENVIRONMENT BINTEST_rcpsp_sat_py3=$ BINTEST_c1510_1.mm.txt=${CMAKE_SOURCE_DIR}/ortools/scheduling/testdata/c1510_1.mm.txt +) + +ortools_cxx_bintest( + NAME bzl_py_example_rcpsp_sat_j301_1_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/rcpsp_sat_j301_1_py_test.bintest + ENVIRONMENT BINTEST_rcpsp_sat_py3=$ BINTEST_j301_1.sm=${CMAKE_SOURCE_DIR}/ortools/scheduling/testdata/j301_1.sm +) + +add_python_binary( + NAME bzl_py_example_rcpsp_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/rcpsp_sat.py +) + +add_python_binary( + NAME bzl_py_example_shift_scheduling_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/shift_scheduling_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_shift_scheduling_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/shift_scheduling_sat_py_test.bintest + ENVIRONMENT BINTEST_shift_scheduling_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_single_machine_scheduling_with_setup_release_due_dates_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/single_machine_scheduling_with_setup_release_due_dates_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_single_machine_scheduling_with_setup_release_due_dates_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/single_machine_scheduling_with_setup_release_due_dates_sat_py_test.bintest + ENVIRONMENT BINTEST_single_machine_scheduling_with_setup_release_due_dates_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_spread_robots_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/spread_robots_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_spread_robots_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/spread_robots_sat_py_test.bintest + ENVIRONMENT BINTEST_spread_robots_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_steel_mill_slab_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/steel_mill_slab_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_steel_mill_slab_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/steel_mill_slab_sat_py_test.bintest + ENVIRONMENT BINTEST_steel_mill_slab_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_sudoku_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/sudoku_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_sudoku_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/sudoku_sat_py_test.bintest + ENVIRONMENT BINTEST_sudoku_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_task_allocation_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/task_allocation_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_task_allocation_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/task_allocation_sat_py_test.bintest + ENVIRONMENT BINTEST_task_allocation_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_tasks_and_workers_assignment_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/tasks_and_workers_assignment_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_tasks_and_workers_assignment_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/tasks_and_workers_assignment_sat_py_test.bintest + ENVIRONMENT BINTEST_tasks_and_workers_assignment_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_test_scheduling_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/test_scheduling_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_test_scheduling_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/test_scheduling_sat_py_test.bintest + ENVIRONMENT BINTEST_test_scheduling_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_tsp_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/tsp_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_tsp_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/tsp_sat_py_test.bintest + ENVIRONMENT BINTEST_tsp_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_vendor_scheduling_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/vendor_scheduling_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_vendor_scheduling_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/vendor_scheduling_sat_py_test.bintest + ENVIRONMENT BINTEST_vendor_scheduling_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_wedding_optimal_chart_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/wedding_optimal_chart_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_wedding_optimal_chart_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/wedding_optimal_chart_sat_py_test.bintest + ENVIRONMENT BINTEST_wedding_optimal_chart_sat_py3=$ +) + +add_python_binary( + NAME bzl_py_example_zebra_sat_py3 + FILE ${CMAKE_CURRENT_SOURCE_DIR}/zebra_sat.py +) + +ortools_cxx_bintest( + NAME bzl_py_example_zebra_sat_py_test + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/zebra_sat_py_test.bintest + ENVIRONMENT BINTEST_zebra_sat_py3=$ +) \ No newline at end of file diff --git a/examples/python/CMakeLists.txt b/examples/python/CMakeLists.txt index 85f6eff816..14c812cd90 100644 --- a/examples/python/CMakeLists.txt +++ b/examples/python/CMakeLists.txt @@ -15,12 +15,6 @@ if(NOT BUILD_PYTHON_EXAMPLES) return() endif() -file(GLOB PYTHON_SRCS "*.py") -# Remove too long examples -list(FILTER PYTHON_SRCS EXCLUDE REGEX ".*/line_balancing_sat.py") # need input file -list(FILTER PYTHON_SRCS EXCLUDE REGEX ".*/bus_driver_scheduling_sat.py") # too long -list(FILTER PYTHON_SRCS EXCLUDE REGEX ".*/cvrptw_plot.py") # depend on numpy - -foreach(FILE_NAME IN LISTS PYTHON_SRCS) - add_python_example(FILE_NAME ${FILE_NAME}) -endforeach() +if(NOT WIN32) +include("CMakeBazel.txt") +endif() diff --git a/examples/python/appointments_py_test.bintest b/examples/python/appointments_py_test.bintest new file mode 100644 index 0000000000..367623a5cf --- /dev/null +++ b/examples/python/appointments_py_test.bintest @@ -0,0 +1 @@ +RUN: $(appointments_py3) diff --git a/examples/python/assignment_with_constraints_sat_py_test.bintest b/examples/python/assignment_with_constraints_sat_py_test.bintest new file mode 100644 index 0000000000..340ba4e309 --- /dev/null +++ b/examples/python/assignment_with_constraints_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(assignment_with_constraints_sat_py3) diff --git a/examples/python/balance_group_sat_py_test.bintest b/examples/python/balance_group_sat_py_test.bintest new file mode 100644 index 0000000000..97692558c5 --- /dev/null +++ b/examples/python/balance_group_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(balance_group_sat_py3) diff --git a/examples/python/bus_driver_scheduling_sat_py_test.bintest b/examples/python/bus_driver_scheduling_sat_py_test.bintest new file mode 100644 index 0000000000..229e11c410 --- /dev/null +++ b/examples/python/bus_driver_scheduling_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(bus_driver_scheduling_sat_py3) --params=max_time_in_seconds:40 diff --git a/examples/python/car_sequencing_optimization_sat_py_test.bintest b/examples/python/car_sequencing_optimization_sat_py_test.bintest new file mode 100644 index 0000000000..6a95ef22b1 --- /dev/null +++ b/examples/python/car_sequencing_optimization_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(car_sequencing_optimization_sat_py3) diff --git a/examples/python/chemical_balance_sat_py_test.bintest b/examples/python/chemical_balance_sat_py_test.bintest new file mode 100644 index 0000000000..470ce4c855 --- /dev/null +++ b/examples/python/chemical_balance_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(chemical_balance_sat_py3) diff --git a/examples/python/clustering_sat_py_test.bintest b/examples/python/clustering_sat_py_test.bintest new file mode 100644 index 0000000000..8f29b62d31 --- /dev/null +++ b/examples/python/clustering_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(clustering_sat_py3) diff --git a/examples/python/cover_rectangle_sat_py_test.bintest b/examples/python/cover_rectangle_sat_py_test.bintest new file mode 100644 index 0000000000..0022f14479 --- /dev/null +++ b/examples/python/cover_rectangle_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(cover_rectangle_sat_py3) diff --git a/examples/python/cryptarithm_sat_py_test.bintest b/examples/python/cryptarithm_sat_py_test.bintest new file mode 100644 index 0000000000..29accdda2b --- /dev/null +++ b/examples/python/cryptarithm_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(cryptarithm_sat_py3) diff --git a/examples/python/cvrptw_plot.py b/examples/python/cvrptw_plot.py deleted file mode 100644 index 074cd5ea34..0000000000 --- a/examples/python/cvrptw_plot.py +++ /dev/null @@ -1,753 +0,0 @@ -# This Python file uses the following encoding: utf-8 -# Copyright 2015 Tin Arm Engineering AB -# 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. -"""Capacitated Vehicle Routing Problem with Time Windows (and optional orders). - - This is a sample using the routing library python wrapper to solve a - CVRPTW problem. - A description of the problem can be found here: - http://en.wikipedia.org/wiki/Vehicle_routing_problem. - The variant which is tackled by this model includes a capacity dimension, - time windows and optional orders, with a penalty cost if orders are not - performed. - To help explore the problem, two classes are provided Customers() and - Vehicles(): used to randomly locate orders and depots, and to randomly - generate demands, time-window constraints and vehicles. - Distances are computed using the Great Circle distances. Distances are in km - and times in seconds. - - A function for the displaying of the vehicle plan - display_vehicle_output - - The optimization engine uses local search to improve solutions, first - solutions being generated using a cheapest addition heuristic. - Numpy and Matplotlib are required for the problem creation and display. - -""" -import os -import numpy as np -from matplotlib import pyplot as plt -from collections import namedtuple -from ortools.constraint_solver import pywrapcp -from ortools.constraint_solver import routing_enums_pb2 -from datetime import datetime, timedelta - - -class Customers(): - """ - A class that generates and holds customers information. - - Randomly normally distribute a number of customers and locations within - a region described by a rectangle. Generate a random demand for each - customer. Generate a random time window for each customer. - May either be initiated with the extents, as a dictionary describing - two corners of a rectangle in latitude and longitude OR as a center - point (lat, lon), and box_size in km. The default arguments are for a - 10 x 10 km square centered in Sheffield). - - Args: extents (Optional[Dict]): A dictionary describing a rectangle in - latitude and longitude with the keys 'llcrnrlat', 'llcrnrlon' & - 'urcrnrlat' & 'urcrnrlat' center (Optional(Tuple): A tuple of - (latitude, longitude) describing the centre of the rectangle. box_size - (Optional float: The length in km of the box's sides. num_stops (int): - The number of customers, including the depots that are placed normally - distributed in the rectangle. min_demand (int): Lower limit on the - randomly generated demand at each customer. max_demand (int): Upper - limit on the randomly generated demand at each customer. - min_tw: shortest random time window for a customer, in hours. - max_tw: longest random time window for a customer, in hours. - Examples: To place 100 customers randomly within 100 km x 100 km - rectangle, centered in the default location, with a random demand of - between 5 and 10 units: >>> customers = Customers(num_stops=100, - box_size=100, ... min_demand=5, max_demand=10) - alternatively, to place 75 customers in the same area with default - arguments for demand: >>> extents = {'urcrnrlon': 0.03403, 'llcrnrlon': - -2.98325, ... 'urcrnrlat': 54.28127, 'llcrnrlat': 52.48150} >>> - customers = Customers(num_stops=75, extents=extents) - """ - - def __init__(self, - extents=None, - center=(53.381393, -1.474611), - box_size=10, - num_stops=100, - min_demand=0, - max_demand=25, - min_tw=1, - max_tw=5): - self.number = num_stops #: The number of customers and depots - #: Location, a named tuple for locations. - Location = namedtuple('Location', ['lat', 'lon']) - if extents is not None: - self.extents = extents #: The lower left and upper right points - #: Location[lat,lon]: the centre point of the area. - self.center = Location( - extents['urcrnrlat'] - 0.5 * - (extents['urcrnrlat'] - extents['llcrnrlat']), - extents['urcrnrlon'] - 0.5 * - (extents['urcrnrlon'] - extents['llcrnrlon'])) - else: - #: Location[lat,lon]: the centre point of the area. - (clat, clon) = self.center = Location(center[0], center[1]) - rad_earth = 6367 # km - circ_earth = np.pi * rad_earth - #: The lower left and upper right points - self.extents = { - 'llcrnrlon': (clon - 180 * box_size / - (circ_earth * np.cos(np.deg2rad(clat)))), - 'llcrnrlat': - clat - 180 * box_size / circ_earth, - 'urcrnrlon': (clon + 180 * box_size / - (circ_earth * np.cos(np.deg2rad(clat)))), - 'urcrnrlat': - clat + 180 * box_size / circ_earth - } - # The 'name' of the stop, indexed from 0 to num_stops-1 - stops = np.array(range(0, num_stops)) - # normaly distributed random distribution of stops within the box - stdv = 6 # the number of standard deviations 99.9% will be within +-3 - lats = (self.extents['llcrnrlat'] + np.random.randn(num_stops) * - (self.extents['urcrnrlat'] - self.extents['llcrnrlat']) / stdv) - lons = (self.extents['llcrnrlon'] + np.random.randn(num_stops) * - (self.extents['urcrnrlon'] - self.extents['llcrnrlon']) / stdv) - # uniformly distributed integer demands. - demands = np.random.randint(min_demand, max_demand, num_stops) - - self.time_horizon = 24 * 60**2 # A 24 hour period. - - # The customers demand min_tw to max_tw hour time window for each - # delivery - time_windows = np.random.randint(min_tw * 3600, max_tw * 3600, - num_stops) - # The last time a delivery window can start - latest_time = self.time_horizon - time_windows - start_times = [None for o in time_windows] - stop_times = [None for o in time_windows] - # Make random timedeltas, nominally from the start of the day. - for idx in range(self.number): - stime = int(np.random.randint(0, latest_time[idx])) - start_times[idx] = timedelta(seconds=stime) - stop_times[idx] = ( - start_times[idx] + timedelta(seconds=int(time_windows[idx]))) - # A named tuple for the customer - Customer = namedtuple( - 'Customer', - [ - 'index', # the index of the stop - 'demand', # the demand for the stop - 'lat', # the latitude of the stop - 'lon', # the longitude of the stop - 'tw_open', # timedelta window open - 'tw_close' - ]) # timedelta window cls - - self.customers = [ - Customer(idx, dem, lat, lon, tw_open, tw_close) - for idx, dem, lat, lon, tw_open, tw_close in zip( - stops, demands, lats, lons, start_times, stop_times) - ] - - # The number of seconds needed to 'unload' 1 unit of goods. - self.service_time_per_dem = 300 # seconds - - def set_manager(self, manager): - self.manager = manager - - def central_start_node(self, invert=False): - """ - Return a random starting node, with probability weighted by distance - from the centre of the extents, so that a central starting node is - likely. - - Args: invert (Optional bool): When True, a peripheral starting node is - most likely. - - Returns: - int: a node index. - - Examples: - >>> customers.central_start_node(invert=True) - 42 - """ - num_nodes = len(self.customers) - dist = np.empty((num_nodes, 1)) - for idx_to in range(num_nodes): - dist[idx_to] = self._haversine(self.center.lon, self.center.lat, - self.customers[idx_to].lon, - self.customers[idx_to].lat) - furthest = np.max(dist) - - if invert: - prob = dist * 1.0 / sum(dist) - else: - prob = (furthest - dist * 1.0) / sum(furthest - dist) - indexes = np.array([range(num_nodes)]) - start_node = np.random.choice( - indexes.flatten(), size=1, replace=True, p=prob.flatten()) - return start_node[0] - - def make_distance_mat(self, method='haversine'): - """ - Return a distance matrix and make it a member of Customer, using the - method given in the call. Currently only Haversine (GC distance) is - implemented, but Manhattan, or using a maps API could be added here. - Raises an AssertionError for all other methods. - - Args: method (Optional[str]): method of distance calculation to use. The - Haversine formula is the only method implemented. - - Returns: - Numpy array of node to node distances. - - Examples: - >>> dist_mat = customers.make_distance_mat(method='haversine') - >>> dist_mat = customers.make_distance_mat(method='manhattan') - AssertionError - """ - self.distmat = np.zeros((self.number, self.number)) - methods = {'haversine': self._haversine} - assert (method in methods) - for frm_idx in range(self.number): - for to_idx in range(self.number): - if frm_idx != to_idx: - frm_c = self.customers[frm_idx] - to_c = self.customers[to_idx] - self.distmat[frm_idx, to_idx] = self._haversine( - frm_c.lon, frm_c.lat, to_c.lon, to_c.lat) - return (self.distmat) - - def _haversine(self, lon1, lat1, lon2, lat2): - """ - Calculate the great circle distance between two points - on the earth specified in decimal degrees of latitude and longitude. - https://en.wikipedia.org/wiki/Haversine_formula - - Args: - lon1: longitude of pt 1, - lat1: latitude of pt 1, - lon2: longitude of pt 2, - lat2: latitude of pt 2 - - Returns: - the distace in km between pt1 and pt2 - """ - # convert decimal degrees to radians - lon1, lat1, lon2, lat2 = map(np.radians, [lon1, lat1, lon2, lat2]) - - # haversine formula - dlon = lon2 - lon1 - dlat = lat2 - lat1 - a = (np.sin(dlat / 2)**2 + - np.cos(lat1) * np.cos(lat2) * np.sin(dlon / 2)**2) - c = 2 * np.arcsin(np.sqrt(a)) - - # 6367 km is the radius of the Earth - km = 6367 * c - return km - - def get_total_demand(self): - """ - Return the total demand of all customers. - """ - return (sum([c.demand for c in self.customers])) - - def return_dist_callback(self, **kwargs): - """ - Return a callback function for the distance matrix. - - Args: **kwargs: Arbitrary keyword arguments passed on to - make_distance_mat() - - Returns: - function: dist_return(a,b) A function that takes the 'from' node - index and the 'to' node index and returns the distance in km. - """ - self.make_distance_mat(**kwargs) - - def dist_return(from_index, to_index): - # Convert from routing variable Index to distance matrix NodeIndex. - from_node = self.manager.IndexToNode(from_index) - to_node = self.manager.IndexToNode(to_index) - return (self.distmat[from_node][to_node]) - - return dist_return - - def return_dem_callback(self): - """ - Return a callback function that gives the demands. - - Returns: - function: dem_return(a) A function that takes the 'from' node - index and returns the distance in km. - """ - - def dem_return(from_index): - # Convert from routing variable Index to distance matrix NodeIndex. - from_node = self.manager.IndexToNode(from_index) - return (self.customers[from_node].demand) - - return dem_return - - def zero_depot_demands(self, depot): - """ - Zero out the demands and time windows of depot. The Depots do not have - demands or time windows so this function clears them. - - Args: depot (int): index of the stop to modify into a depot. - Examples: >>> customers.zero_depot_demands(5) >>> - customers.customers[5].demand == 0 True - """ - start_depot = self.customers[depot] - self.customers[depot] = start_depot._replace( - demand=0, tw_open=None, tw_close=None) - - def make_service_time_call_callback(self): - """ - Return a callback function that provides the time spent servicing the - customer. Here is it proportional to the demand given by - self.service_time_per_dem, default 300 seconds per unit demand. - - Returns: - function [dem_return(a, b)]: A function that takes the from/a node - index and the to/b node index and returns the service time at a - - """ - - def service_time_return(a, b): - return (self.customers[a].demand * self.service_time_per_dem) - - return service_time_return - - def make_transit_time_callback(self, speed_kmph=10): - """ - Creates a callback function for transit time. Assuming an average - speed of speed_kmph - Args: - speed_kmph: the average speed in km/h - - Returns: - function [transit_time_return(a, b)]: A function that takes the - from/a node index and the to/b node index and returns the - transit time from a to b. - """ - - def transit_time_return(a, b): - return (self.distmat[a][b] / (speed_kmph * 1.0 / 60**2)) - - return transit_time_return - - -class Vehicles(): - """ - A Class to create and hold vehicle information. - - The Vehicles in a CVRPTW problem service the customers and belong to a - depot. The class Vehicles creates a list of named tuples describing the - Vehicles. The main characteristics are the vehicle capacity, fixed cost, - and cost per km. The fixed cost of using a certain type of vehicles can be - higher or lower than others. If a vehicle is used, i.e. this vehicle serves - at least one node, then this cost is added to the objective function. - - Note: - If numpy arrays are given for capacity and cost, then they must be of - the same length, and the number of vehicles are inferred from them. - If scalars are given, the fleet is homogeneous, and the number of - vehicles is determined by number. - - Args: capacity (scalar or numpy array): The integer capacity of demand - units. cost (scalar or numpy array): The fixed cost of the vehicle. number - (Optional [int]): The number of vehicles in a homogeneous fleet. - """ - - def __init__(self, capacity=100, cost=100, number=None): - - Vehicle = namedtuple('Vehicle', ['index', 'capacity', 'cost']) - - if number is None: - self.number = np.size(capacity) - else: - self.number = number - idxs = np.array(range(0, self.number)) - - if np.isscalar(capacity): - capacities = capacity * np.ones_like(idxs) - elif np.size(capacity) != self.number: - print('capacity is neither scalar, nor the same size as num!') - else: - capacities = capacity - - if np.isscalar(cost): - costs = cost * np.ones_like(idxs) - elif np.size(cost) != self.number: - print(np.size(cost)) - print('cost is neither scalar, nor the same size as num!') - else: - costs = cost - - self.vehicles = [ - Vehicle(idx, capacity, cost) - for idx, capacity, cost in zip(idxs, capacities, costs) - ] - - def get_total_capacity(self): - return (sum([c.capacity for c in self.vehicles])) - - def return_starting_callback(self, customers, sameStartFinish=False): - # create a different starting and finishing depot for each vehicle - self.starts = [ - int(customers.central_start_node()) for o in range(self.number) - ] - if sameStartFinish: - self.ends = self.starts - else: - self.ends = [ - int(customers.central_start_node(invert=True)) - for o in range(self.number) - ] - # the depots will not have demands, so zero them. - for depot in self.starts: - customers.zero_depot_demands(depot) - for depot in self.ends: - customers.zero_depot_demands(depot) - - def start_return(v): - return (self.starts[v]) - - return start_return - - -def discrete_cmap(N, base_cmap=None): - """ - Create an N-bin discrete colormap from the specified input map - """ - # Note that if base_cmap is a string or None, you can simply do - # return plt.cm.get_cmap(base_cmap, N) - # The following works for string, None, or a colormap instance: - - base = plt.cm.get_cmap(base_cmap) - color_list = base(np.linspace(0, 1, N)) - cmap_name = base.name + str(N) - return base.from_list(cmap_name, color_list, N) - - -def vehicle_output_string(manager, routing, plan): - """ - Return a string displaying the output of the routing instance and - assignment (plan). - - Args: routing (ortools.constraint_solver.pywrapcp.RoutingModel): routing. - plan (ortools.constraint_solver.pywrapcp.Assignment): the assignment. - - Returns: - (string) plan_output: describing each vehicle's plan. - - (List) dropped: list of dropped orders. - - """ - dropped = [] - for order in range(routing.Size()): - if (plan.Value(routing.NextVar(order)) == order): - dropped.append(str(order)) - - capacity_dimension = routing.GetDimensionOrDie('Capacity') - time_dimension = routing.GetDimensionOrDie('Time') - plan_output = '' - - for route_number in range(routing.vehicles()): - order = routing.Start(route_number) - plan_output += 'Route {0}:'.format(route_number) - if routing.IsEnd(plan.Value(routing.NextVar(order))): - plan_output += ' Empty \n' - else: - while True: - load_var = capacity_dimension.CumulVar(order) - time_var = time_dimension.CumulVar(order) - node = manager.IndexToNode(order) - plan_output += \ - ' {node} Load({load}) Time({tmin}, {tmax}) -> '.format( - node=node, - load=plan.Value(load_var), - tmin=str(timedelta(seconds=plan.Min(time_var))), - tmax=str(timedelta(seconds=plan.Max(time_var)))) - - if routing.IsEnd(order): - plan_output += ' EndRoute {0}. \n'.format(route_number) - break - order = plan.Value(routing.NextVar(order)) - plan_output += '\n' - - return (plan_output, dropped) - - -def build_vehicle_route(manager, routing, plan, customers, veh_number): - """ - Build a route for a vehicle by starting at the strat node and - continuing to the end node. - - Args: routing (ortools.constraint_solver.pywrapcp.RoutingModel): routing. - plan (ortools.constraint_solver.pywrapcp.Assignment): the assignment. - customers (Customers): the customers instance. veh_number (int): index of - the vehicle - - Returns: - (List) route: indexes of the customers for vehicle veh_number - """ - veh_used = routing.IsVehicleUsed(plan, veh_number) - print('Vehicle {0} is used {1}'.format(veh_number, veh_used)) - if veh_used: - route = [] - node = routing.Start(veh_number) # Get the starting node index - route.append(customers.customers[manager.IndexToNode(node)]) - while not routing.IsEnd(node): - route.append(customers.customers[manager.IndexToNode(node)]) - node = plan.Value(routing.NextVar(node)) - - route.append(customers.customers[manager.IndexToNode(node)]) - return route - else: - return None - - -def plot_vehicle_routes(veh_route, ax1, customers, vehicles): - """ - Plot the vehicle routes on matplotlib axis ax1. - - Args: veh_route (dict): a dictionary of routes keyed by vehicle idx. ax1 - (matplotlib.axes._subplots.AxesSubplot): Matplotlib axes customers - (Customers): the customers instance. vehicles (Vehicles): the vehicles - instance. - """ - veh_used = [v for v in veh_route if veh_route[v] is not None] - - cmap = discrete_cmap(vehicles.number + 2, 'nipy_spectral') - - for veh_number in veh_used: - - lats, lons = zip(*[(c.lat, c.lon) for c in veh_route[veh_number]]) - lats = np.array(lats) - lons = np.array(lons) - s_dep = customers.customers[vehicles.starts[veh_number]] - s_fin = customers.customers[vehicles.ends[veh_number]] - ax1.annotate( - 'v({veh}) S @ {node}'.format( - veh=veh_number, node=vehicles.starts[veh_number]), - xy=(s_dep.lon, s_dep.lat), - xytext=(10, 10), - xycoords='data', - textcoords='offset points', - arrowprops=dict( - arrowstyle='->', - connectionstyle='angle3,angleA=90,angleB=0', - shrinkA=0.05), - ) - ax1.annotate( - 'v({veh}) F @ {node}'.format( - veh=veh_number, node=vehicles.ends[veh_number]), - xy=(s_fin.lon, s_fin.lat), - xytext=(10, -20), - xycoords='data', - textcoords='offset points', - arrowprops=dict( - arrowstyle='->', - connectionstyle='angle3,angleA=-90,angleB=0', - shrinkA=0.05), - ) - ax1.plot(lons, lats, 'o', mfc=cmap(veh_number + 1)) - ax1.quiver( - lons[:-1], - lats[:-1], - lons[1:] - lons[:-1], - lats[1:] - lats[:-1], - scale_units='xy', - angles='xy', - scale=1, - color=cmap(veh_number + 1)) - - -def main(): - # Create a set of customer, (and depot) stops. - customers = Customers( - num_stops=50, - min_demand=1, - max_demand=15, - box_size=40, - min_tw=3, - max_tw=6) - - # Create a list of inhomgenious vehicle capacities as integer units. - capacity = [50, 75, 100, 125, 150, 175, 200, 250] - - # Create a list of inhomogeneous fixed vehicle costs. - cost = [int(100 + 2 * np.sqrt(c)) for c in capacity] - - # Create a set of vehicles, the number set by the length of capacity. - vehicles = Vehicles(capacity=capacity, cost=cost) - - # check to see that the problem is feasible, if we don't have enough - # vehicles to cover the demand, there is no point in going further. - assert (customers.get_total_demand() < vehicles.get_total_capacity()) - - # Set the starting nodes, and create a callback fn for the starting node. - start_fn = vehicles.return_starting_callback( - customers, sameStartFinish=False) - - # Create the routing index manager. - manager = pywrapcp.RoutingIndexManager( - customers.number, # int number - vehicles.number, # int number - vehicles.starts, # List of int start depot - vehicles.ends) # List of int end depot - - customers.set_manager(manager) - - # Set model parameters - model_parameters = pywrapcp.DefaultRoutingModelParameters() - - # The solver parameters can be accessed from the model parameters. For example : - # model_parameters.solver_parameters.CopyFrom( - # pywrapcp.Solver.DefaultSolverParameters()) - # model_parameters.solver_parameters.trace_propagation = True - - # Make the routing model instance. - routing = pywrapcp.RoutingModel(manager, model_parameters) - - parameters = pywrapcp.DefaultRoutingSearchParameters() - # Setting first solution heuristic (cheapest addition). - parameters.first_solution_strategy = ( - routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) - # Routing: forbids use of TSPOpt neighborhood, (this is the default behaviour) - parameters.local_search_operators.use_tsp_opt = pywrapcp.BOOL_FALSE - # Disabling Large Neighborhood Search, (this is the default behaviour) - parameters.local_search_operators.use_path_lns = pywrapcp.BOOL_FALSE - parameters.local_search_operators.use_inactive_lns = pywrapcp.BOOL_FALSE - - parameters.time_limit.seconds = 10 - parameters.use_full_propagation = True - #parameters.log_search = True - - # Create callback fns for distances, demands, service and transit-times. - dist_fn = customers.return_dist_callback() - dist_fn_index = routing.RegisterTransitCallback(dist_fn) - - dem_fn = customers.return_dem_callback() - dem_fn_index = routing.RegisterUnaryTransitCallback(dem_fn) - - # Create and register a transit callback. - serv_time_fn = customers.make_service_time_call_callback() - transit_time_fn = customers.make_transit_time_callback() - def tot_time_fn(from_index, to_index): - """ - The time function we want is both transit time and service time. - """ - # Convert from routing variable Index to distance matrix NodeIndex. - from_node = manager.IndexToNode(from_index) - to_node = manager.IndexToNode(to_index) - return serv_time_fn(from_node, to_node) + transit_time_fn(from_node, to_node) - - tot_time_fn_index = routing.RegisterTransitCallback(tot_time_fn) - - # Set the cost function (distance callback) for each arc, homogeneous for - # all vehicles. - routing.SetArcCostEvaluatorOfAllVehicles(dist_fn_index) - - # Set vehicle costs for each vehicle, not homogeneous. - for veh in vehicles.vehicles: - routing.SetFixedCostOfVehicle(veh.cost, int(veh.index)) - - # Add a dimension for vehicle capacities - null_capacity_slack = 0 - routing.AddDimensionWithVehicleCapacity( - dem_fn_index, # demand callback - null_capacity_slack, - capacity, # capacity array - True, - 'Capacity') - # Add a dimension for time and a limit on the total time_horizon - routing.AddDimension( - tot_time_fn_index, # total time function callback - customers.time_horizon, - customers.time_horizon, - True, - 'Time') - - time_dimension = routing.GetDimensionOrDie('Time') - for cust in customers.customers: - if cust.tw_open is not None: - time_dimension.CumulVar(manager.NodeToIndex(cust.index)).SetRange( - cust.tw_open.seconds, cust.tw_close.seconds) - """ - To allow the dropping of orders, we add disjunctions to all the customer - nodes. Each disjunction is a list of 1 index, which allows that customer to - be active or not, with a penalty if not. The penalty should be larger - than the cost of servicing that customer, or it will always be dropped! - """ - # To add disjunctions just to the customers, make a list of non-depots. - non_depot = set(range(customers.number)) - non_depot.difference_update(vehicles.starts) - non_depot.difference_update(vehicles.ends) - penalty = 400000 # The cost for dropping a node from the plan. - nodes = [routing.AddDisjunction([manager.NodeToIndex(c)], penalty) for c in non_depot] - - # This is how you would implement partial routes if you already knew part - # of a feasible solution for example: - # partial = np.random.choice(list(non_depot), size=(4,5), replace=False) - - # routing.CloseModel() - # partial_list = [partial[0,:].tolist(), - # partial[1,:].tolist(), - # partial[2,:].tolist(), - # partial[3,:].tolist(), - # [],[],[],[]] - # print(routing.ApplyLocksToAllVehicles(partial_list, False)) - - # Solve the problem ! - assignment = routing.SolveWithParameters(parameters) - - # The rest is all optional for saving, printing or plotting the solution. - if assignment: - ## save the assignment, (Google Protobuf format) - #save_file_base = os.path.realpath(__file__).split('.')[0] - #if routing.WriteAssignment(save_file_base + '_assignment.ass'): - # print('succesfully wrote assignment to file ' + save_file_base + - # '_assignment.ass') - - print('The Objective Value is {0}'.format(assignment.ObjectiveValue())) - - plan_output, dropped = vehicle_output_string(manager, routing, assignment) - print(plan_output) - print('dropped nodes: ' + ', '.join(dropped)) - - # you could print debug information like this: - # print(routing.DebugOutputAssignment(assignment, 'Capacity')) - - vehicle_routes = {} - for veh in range(vehicles.number): - vehicle_routes[veh] = build_vehicle_route(manager, routing, assignment, - customers, veh) - - # Plotting of the routes in matplotlib. - fig = plt.figure() - ax = fig.add_subplot(111) - # Plot all the nodes as black dots. - clon, clat = zip(*[(c.lon, c.lat) for c in customers.customers]) - ax.plot(clon, clat, 'k.') - # plot the routes as arrows - plot_vehicle_routes(vehicle_routes, ax, customers, vehicles) - plt.show() - - else: - print('No assignment') - - -if __name__ == '__main__': - main() diff --git a/examples/python/flexible_job_shop_sat_py_test.bintest b/examples/python/flexible_job_shop_sat_py_test.bintest new file mode 100644 index 0000000000..eed68ac56e --- /dev/null +++ b/examples/python/flexible_job_shop_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(flexible_job_shop_sat_py3) diff --git a/examples/python/gate_scheduling_sat.py b/examples/python/gate_scheduling_sat.py index 9cea61deb7..84c6a083d3 100644 --- a/examples/python/gate_scheduling_sat.py +++ b/examples/python/gate_scheduling_sat.py @@ -24,9 +24,8 @@ The objective is to minimize the max end time of all jobs. """ from absl import app - -from ortools.sat.colab import visualization from ortools.sat.python import cp_model +from ortools.sat.colab import visualization def main(_) -> None: diff --git a/examples/python/gate_scheduling_sat_py_test.bintest b/examples/python/gate_scheduling_sat_py_test.bintest new file mode 100644 index 0000000000..279fc094b0 --- /dev/null +++ b/examples/python/gate_scheduling_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(gate_scheduling_sat_py3) diff --git a/examples/python/golomb8_py_test.bintest b/examples/python/golomb8_py_test.bintest new file mode 100644 index 0000000000..e65bf5dc1e --- /dev/null +++ b/examples/python/golomb8_py_test.bintest @@ -0,0 +1 @@ +RUN: $(golomb8_py3) diff --git a/examples/python/golomb_sat_py_test.bintest b/examples/python/golomb_sat_py_test.bintest new file mode 100644 index 0000000000..3ab2199d13 --- /dev/null +++ b/examples/python/golomb_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(golomb_sat_py3) diff --git a/examples/python/hidato_sat_py_test.bintest b/examples/python/hidato_sat_py_test.bintest new file mode 100644 index 0000000000..a5e01c6f50 --- /dev/null +++ b/examples/python/hidato_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(hidato_sat_py3) diff --git a/examples/python/horse_jumping_show.py b/examples/python/horse_jumping_show.py new file mode 100644 index 0000000000..bff393a6fe --- /dev/null +++ b/examples/python/horse_jumping_show.py @@ -0,0 +1,297 @@ +#!/usr/bin/env python3 +# 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. + +"""Horse Jumping Show. + +A major three-day horse jumping competition is scheduled next winter in Geneva. +The show features riders and horses from all over the world, competing in +several different competitions throughout the show. Six months before the show, +riders submit the entries (i.e., rider name, horse, competition) to the +organizers. Riders can submit multiple entries, for example, to compete in the +same competition with multiple horses, or to compete in several competitions. + +There are additional space limitations. For example, the venue has 100 stalls, +4 arenas (where competitions can be scheduled), and 6 paddocks (where riders +warm up before their turn). It is also ideal that paddocks are not overloaded by +riders from multiple competitions. + +The organizer's goal is find a schedule in which competitions don't overlap, and +the times at which they happen are scattered throughout the day (and hopefully +not that early in the morning). The starting times of the competitions should be +at the hour or 30 minutes past the hour (e.g. 9:30, 10:00, 10:30, etc.). +Competitions can only be scheduled while there is daylight, except for +competitions scheduled in the Main Stage arena, which is covered and has proper +lighting. Also, beginner competitions (1.10m or less) are scheduled on the first +day, and advanced competitions (1.50m or more) are scheduled on the last day. + +The information for next winter's show is as follows: +Available stalls: 100 +Number of riders: 100 +Number of horses: 130 +Number of requested Entries: 200 +Number of competitions: 15 + +Venue: +- Main Stage arena: Covered (9AM-11PM) +- Highlands arena: Daylight Only (9AM-5PM) +- Sawdust arena: Daylight Only (9AM-5PM) +- Paddock1 has capacity for 10 riders and serves Main Stage +- Paddock2 has capacity for 6 riders and serves Main Stage +- Paddock3 has capacity for 8 riders and serves Main Stage, Highlands +- Paddock4 has capacity for 8 riders and serves Highlands, Sawdust +- Paddock5 has capacity for 9 riders and serves Sawdust +- Paddock6 has capacity for 7 riders and serves Sawdust + +competitions: +- C_5_1.10m_Year_Olds 1.10m - 60 minutes +- C_6_1.25m_Year_Olds 1.25m - 90 minutes +- C_7_1.35m_Year_Olds 1.35m - 120 minutes +- C_0.8m_Jumpers 0.80m - 240 minutes +- C_1.0m_Jumpers 1.00m - 180 minutes +- C_1.10m_Jumpers 1.10m - 180 minutes +- C_1.20m_Jumpers 1.20m - 120 minutes +- C_1.30m_Jumpers 1.30m - 120 minutes +- C_1.40m_Jumpers 1.40m - 120 minutes +- C_1.20m_Derby 1.20m - 180 minutes +- C_1.35m_Derby 1.35m - 180 minutes +- C_1.45m_Derby 1.45m - 180 minutes +- C_1.40m_Open 1.40m - 120 minutes +- C_1.50m_Open 1.50m - 180 minutes +- C_1.60m_Grand_Prix 1.60m - 240 minutes +""" + +import dataclasses +from absl import app +import numpy as np +from ortools.sat.python import cp_model + + +@dataclasses.dataclass(frozen=True) +class Arena: + """Data for an arena.""" + + id: str + hours: str + + +@dataclasses.dataclass(frozen=True) +class Competition: + """Data for a competition.""" + + id: str + height: float + duration: int + + +@dataclasses.dataclass(frozen=True) +class HorseJumpingShowData: + """Horse Jumping Show Data.""" + + num_days: int + competitions: list[Competition] + arenas: list[Arena] + + +@dataclasses.dataclass(frozen=True) +class ScheduledCompetition: + """Horse Jumping Show Schedule.""" + + completion: str + day: int + arena: str + start_time: str + end_time: str + + +def generate_horse_jumping_show_data() -> HorseJumpingShowData: + """Generates the horse jumping show data.""" + arenas = [ + Arena(id="Main Stage", hours="9AM-9PM"), + Arena(id="Highlands", hours="9AM-5PM"), + Arena(id="Sawdust", hours="9AM-5PM"), + ] + competitions = [ + Competition(id="C_5_1.10m_Year_Olds", height=1.1, duration=60), + Competition(id="C_6_1.25m_Year_Olds", height=1.25, duration=90), + Competition(id="C_7_1.35m_Year_Olds", height=1.35, duration=120), + Competition(id="C_0.8m_Jumpers", height=0.8, duration=240), + Competition(id="C_1.0m_Jumpers", height=1.0, duration=180), + Competition(id="C_1.10m_Jumpers", height=1.10, duration=180), + Competition(id="C_1.20m_Jumpers", height=1.20, duration=120), + Competition(id="C_1.30m_Jumpers", height=1.30, duration=120), + Competition(id="C_1.40m_Jumpers", height=1.40, duration=120), + Competition(id="C_1.20m_Derby", height=1.20, duration=180), + Competition(id="C_1.35m_Derby", height=1.35, duration=180), + Competition(id="C_1.45m_Derby", height=1.45, duration=180), + Competition(id="C_1.40m_Open", height=1.40, duration=120), + Competition(id="C_1.50m_Open", height=1.50, duration=180), + Competition(id="C_1.60m_Grand_Prix", height=1.60, duration=240), + ] + return HorseJumpingShowData(num_days=3, competitions=competitions, arenas=arenas) + + +def solve() -> list[ScheduledCompetition]: + """Solves the horse jumping show problem.""" + data = generate_horse_jumping_show_data() + num_days = data.num_days + competitions = data.competitions + arenas = data.arenas + day_index = list(range(num_days)) + + # Time parser. + def parse_time(t_str): + hour = int(t_str[:-2]) + if "PM" in t_str and hour != 12: + hour += 12 + if "AM" in t_str and hour == 12: + hour = 0 + return hour * 60 + + # Schedule time intervals for each arena. + schedule_interval_by_arena = {} + for arena in arenas: + start_h_str, end_h_str = arena.hours.split("-") + start_time = parse_time(start_h_str) + end_time = parse_time(end_h_str) + schedule_interval_by_arena[arena.id] = (start_time, end_time) + + # Map time to 30-minute intervals and back. + time_slot_size = 30 + + def time_to_slot(time_in_minutes: int): + return time_in_minutes // time_slot_size + + def slot_to_time(slot_index: int): + return slot_index * time_slot_size + + # --- Model Creation --- + model = cp_model.CpModel() + + # --- Variables --- + # Competition scheduling variables per arena and day. + competition_assignments = np.empty( + (len(competitions), len(arenas), num_days), dtype=object + ) + for c, comp in enumerate(competitions): + for a, arena in enumerate(arenas): + for d in day_index: + competition_assignments[c, a, d] = model.new_bool_var( + f"competition_scheduled_{comp.id}_{arena.id}_{d}" + ) + # Time intervals and start times for each competition. We model time steps + # 0,1,2,... to represent the start times in 30 minutes intervals, as opposed + # to represent the start times in minutes. + competition_start_times = np.empty( + (len(competitions), len(arenas), num_days), dtype=object + ) + competition_intervals = np.empty( + (len(competitions), len(arenas), num_days), dtype=object + ) + for c, comp in enumerate(competitions): + for a, arena in enumerate(arenas): + earliest_start_time, latest_end_time = schedule_interval_by_arena[arena.id] + latest_start_time = latest_end_time - comp.duration + for d in day_index: + competition_start_times[c, a, d] = model.new_int_var( + time_to_slot(earliest_start_time), + time_to_slot(latest_start_time), + f"start_time_{comp.id}_{arena.id}_{d}", + ) + competition_intervals[c, a, d] = ( + model.new_optional_fixed_size_interval_var( + competition_start_times[c, a, d], + time_to_slot(comp.duration), + competition_assignments[c, a, d], + f"task_{comp.id}_{arena.id}_{d}", + ) + ) + + # --- Constraints --- + # Every competition must be scheduled, enforcing that beginner competitions + # are on day 1, and advanced competitions are on day 3. + for c, comp in enumerate(competitions): + model.add(np.sum(competition_assignments[c, :, :]) == 1) + # Beginner competitions are on the first day. + if comp.height <= 1.10: + beginners_day = 0 + model.add(np.sum(competition_assignments[c, :, beginners_day]) == 1) + # Advanced competitions are on the last day. + if comp.height >= 1.50: + advanced_day = num_days - 1 + model.add(np.sum(competition_assignments[c, :, advanced_day]) == 1) + + # Competitions scheduled on the same arena and on the same day can't overlap. + for a, _ in enumerate(arenas): + for day in range(num_days): + model.add_no_overlap(competition_intervals[:, a, day]) + + # Start times should be scattered across the day. + for a, _ in enumerate(arenas): + for day in day_index: + model.add_all_different(competition_start_times[:, a, day]) + + # --- Objective --- + model.maximize(np.sum(competition_start_times)) + + # --- Solve --- + solver = cp_model.CpSolver() + solver.parameters.max_time_in_seconds = 30.0 + solver.parameters.log_search_progress = True + solver.parameters.num_workers = 16 + status = solver.solve(model) + + # --- Print Solution --- + if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE: + schedule = [] + for day in range(num_days): + for c, comp in enumerate(competitions): + for a, arena in enumerate(arenas): + if solver.value(competition_assignments[c, a, day]): + start_time_minutes = slot_to_time( + solver.value(competition_start_times[c, a, day]) + ) + start_h, start_m = divmod(start_time_minutes, 60) + end_h, end_m = divmod(start_time_minutes + comp.duration, 60) + schedule.append( + ScheduledCompetition( + completion=comp.id, + day=day + 1, + arena=arena.id, + start_time=f"{start_h:02d}:{start_m:02d}", + end_time=f"{end_h:02d}:{end_m:02d}", + ) + ) + # Sort and print schedule for readability. + schedule.sort(key=lambda x: (x.day, x.start_time)) + print("Schedule:") + for item in schedule: + print( + f"Day {item.day}: {item.completion} in {item.arena} from" + f" {item.start_time} to {item.end_time}." + ) + return schedule + elif status == cp_model.INFEASIBLE: + print("Problem is infeasible.") + else: + print("No solution found.") + # Return an empty schedule if no solution is found. + return [] + + +def main(_): + solve() + + +if __name__ == "__main__": + app.run(main) diff --git a/examples/python/horse_jumping_show_py_test.bintest b/examples/python/horse_jumping_show_py_test.bintest new file mode 100644 index 0000000000..d8ed39bb39 --- /dev/null +++ b/examples/python/horse_jumping_show_py_test.bintest @@ -0,0 +1,2 @@ +RUN: $(horse_jumping_show_py3) +CHECK: "Day 3: C_1.60m_Grand_Prix" diff --git a/examples/python/integer_programming_py_test.bintest b/examples/python/integer_programming_py_test.bintest new file mode 100644 index 0000000000..b6741b163f --- /dev/null +++ b/examples/python/integer_programming_py_test.bintest @@ -0,0 +1 @@ +RUN: $(integer_programming_py3) diff --git a/examples/python/jobshop_ft06_distance_sat_py_test.bintest b/examples/python/jobshop_ft06_distance_sat_py_test.bintest new file mode 100644 index 0000000000..311b9f8eff --- /dev/null +++ b/examples/python/jobshop_ft06_distance_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(jobshop_ft06_distance_sat_py3) diff --git a/examples/python/jobshop_ft06_sat_py_test.bintest b/examples/python/jobshop_ft06_sat_py_test.bintest new file mode 100644 index 0000000000..3a2dd1304d --- /dev/null +++ b/examples/python/jobshop_ft06_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(jobshop_ft06_sat_py3) diff --git a/examples/python/jobshop_with_maintenance_sat_py_test.bintest b/examples/python/jobshop_with_maintenance_sat_py_test.bintest new file mode 100644 index 0000000000..c8fe05b4a1 --- /dev/null +++ b/examples/python/jobshop_with_maintenance_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(jobshop_with_maintenance_sat_py3) diff --git a/examples/python/knapsack_2d_sat_py_test.bintest b/examples/python/knapsack_2d_sat_py_test.bintest new file mode 100644 index 0000000000..4ab15a54c1 --- /dev/null +++ b/examples/python/knapsack_2d_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(knapsack_2d_sat_py3) diff --git a/examples/python/line_balancing_sat_salbp_20_1_py_test.bintest b/examples/python/line_balancing_sat_salbp_20_1_py_test.bintest new file mode 100644 index 0000000000..10d11bef99 --- /dev/null +++ b/examples/python/line_balancing_sat_salbp_20_1_py_test.bintest @@ -0,0 +1,2 @@ +RUN: $(line_balancing_sat_py3) --input=$(salbp_20_1.alb) +CHECK: "objective: 3" diff --git a/examples/python/linear_assignment_api_py_test.bintest b/examples/python/linear_assignment_api_py_test.bintest new file mode 100644 index 0000000000..aa508f3bce --- /dev/null +++ b/examples/python/linear_assignment_api_py_test.bintest @@ -0,0 +1 @@ +RUN: $(linear_assignment_api_py3) diff --git a/examples/python/linear_programming_py_test.bintest b/examples/python/linear_programming_py_test.bintest new file mode 100644 index 0000000000..6c7b0be1be --- /dev/null +++ b/examples/python/linear_programming_py_test.bintest @@ -0,0 +1 @@ +RUN: $(linear_programming_py3) diff --git a/examples/python/magic_sequence_distribute_py_test.bintest b/examples/python/magic_sequence_distribute_py_test.bintest new file mode 100644 index 0000000000..8ab9383e2b --- /dev/null +++ b/examples/python/magic_sequence_distribute_py_test.bintest @@ -0,0 +1 @@ +RUN: $(magic_sequence_distribute_py3) diff --git a/examples/python/magic_sequence_distribute_with_arg_py_test.bintest b/examples/python/magic_sequence_distribute_with_arg_py_test.bintest new file mode 100644 index 0000000000..b200a93a35 --- /dev/null +++ b/examples/python/magic_sequence_distribute_with_arg_py_test.bintest @@ -0,0 +1 @@ +RUN: $(magic_sequence_distribute_py3) 5 diff --git a/examples/python/maximize_combinations_sat_py_test.bintest b/examples/python/maximize_combinations_sat_py_test.bintest new file mode 100644 index 0000000000..cb4b60da6b --- /dev/null +++ b/examples/python/maximize_combinations_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(maximize_combinations_sat_py3) diff --git a/examples/python/maze_escape_sat_py_test.bintest b/examples/python/maze_escape_sat_py_test.bintest new file mode 100644 index 0000000000..201abe1c10 --- /dev/null +++ b/examples/python/maze_escape_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(maze_escape_sat_py3) diff --git a/examples/python/memory_layout_and_infeasibility_sat_py_test.bintest b/examples/python/memory_layout_and_infeasibility_sat_py_test.bintest new file mode 100644 index 0000000000..98284d635a --- /dev/null +++ b/examples/python/memory_layout_and_infeasibility_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(memory_layout_and_infeasibility_sat_py3) diff --git a/examples/python/music_playlist_sat_py_test.bintest b/examples/python/music_playlist_sat_py_test.bintest new file mode 100644 index 0000000000..d0947fd305 --- /dev/null +++ b/examples/python/music_playlist_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(music_playlist_sat_py3) diff --git a/examples/python/no_wait_baking_scheduling_sat_py_test.bintest b/examples/python/no_wait_baking_scheduling_sat_py_test.bintest new file mode 100644 index 0000000000..1e8edde5a0 --- /dev/null +++ b/examples/python/no_wait_baking_scheduling_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(no_wait_baking_scheduling_sat_py3) diff --git a/examples/python/nqueens_sat_py_test.bintest b/examples/python/nqueens_sat_py_test.bintest new file mode 100644 index 0000000000..5d7cc4f6e3 --- /dev/null +++ b/examples/python/nqueens_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(nqueens_sat_py3) diff --git a/examples/python/pell_equation_sat_py_test.bintest b/examples/python/pell_equation_sat_py_test.bintest new file mode 100644 index 0000000000..7d83a74a24 --- /dev/null +++ b/examples/python/pell_equation_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(pell_equation_sat_py3) diff --git a/examples/python/pentominoes_sat_py_test.bintest b/examples/python/pentominoes_sat_py_test.bintest new file mode 100644 index 0000000000..3ce95b060c --- /dev/null +++ b/examples/python/pentominoes_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(pentominoes_sat_py3) diff --git a/examples/python/prize_collecting_tsp_sat_py_test.bintest b/examples/python/prize_collecting_tsp_sat_py_test.bintest new file mode 100644 index 0000000000..1ed6cb2648 --- /dev/null +++ b/examples/python/prize_collecting_tsp_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(prize_collecting_tsp_sat_py3) diff --git a/examples/python/prize_collecting_vrp_sat_py_test.bintest b/examples/python/prize_collecting_vrp_sat_py_test.bintest new file mode 100644 index 0000000000..1789eec0fd --- /dev/null +++ b/examples/python/prize_collecting_vrp_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(prize_collecting_vrp_sat_py3) diff --git a/examples/python/pyflow_example_py_test.bintest b/examples/python/pyflow_example_py_test.bintest new file mode 100644 index 0000000000..a9af12919e --- /dev/null +++ b/examples/python/pyflow_example_py_test.bintest @@ -0,0 +1 @@ +RUN: $(pyflow_example_py3) diff --git a/examples/python/qubo_sat_py_test.bintest b/examples/python/qubo_sat_py_test.bintest new file mode 100644 index 0000000000..6dc654a121 --- /dev/null +++ b/examples/python/qubo_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(qubo_sat_py3) diff --git a/examples/python/rcpsp_sat_c1510_1_py_test.bintest b/examples/python/rcpsp_sat_c1510_1_py_test.bintest new file mode 100644 index 0000000000..48a07a0554 --- /dev/null +++ b/examples/python/rcpsp_sat_c1510_1_py_test.bintest @@ -0,0 +1,2 @@ +RUN: $(rcpsp_sat_py3) --input=$(c1510_1.mm.txt) +CHECK: "objective: 21" diff --git a/examples/python/rcpsp_sat_j301_1_py_test.bintest b/examples/python/rcpsp_sat_j301_1_py_test.bintest new file mode 100644 index 0000000000..9df595ccd4 --- /dev/null +++ b/examples/python/rcpsp_sat_j301_1_py_test.bintest @@ -0,0 +1,2 @@ +RUN: $(rcpsp_sat_py3) --input=$(j301_1.sm) +CHECK: "objective: 43" diff --git a/examples/python/rcpsp_sat_rip1_py_test.bintest b/examples/python/rcpsp_sat_rip1_py_test.bintest new file mode 100644 index 0000000000..3b3f0c7dfb --- /dev/null +++ b/examples/python/rcpsp_sat_rip1_py_test.bintest @@ -0,0 +1,2 @@ +RUN: $(rcpsp_sat_py3) --input=$(rip1.sch) +CHECK: "objective: 100" diff --git a/examples/python/rcpsp_sat_testset_mm30_psp3_py_test.bintest b/examples/python/rcpsp_sat_testset_mm30_psp3_py_test.bintest new file mode 100644 index 0000000000..801590ce88 --- /dev/null +++ b/examples/python/rcpsp_sat_testset_mm30_psp3_py_test.bintest @@ -0,0 +1 @@ +RUN: $(rcpsp_sat_py3) --input=$(testset_mm30_psp3.sch) --params=max_time_in_seconds:8.0 diff --git a/examples/python/rcpsp_sat_ubo_10_psp2_py_test.bintest b/examples/python/rcpsp_sat_ubo_10_psp2_py_test.bintest new file mode 100644 index 0000000000..47fad4820b --- /dev/null +++ b/examples/python/rcpsp_sat_ubo_10_psp2_py_test.bintest @@ -0,0 +1,2 @@ +RUN: $(rcpsp_sat_py3) --input=$(ubo_10_psp2.sch) +CHECK: "objective: 45" diff --git a/examples/python/shift_scheduling_sat_py_test.bintest b/examples/python/shift_scheduling_sat_py_test.bintest new file mode 100644 index 0000000000..a54e4fa4b5 --- /dev/null +++ b/examples/python/shift_scheduling_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(shift_scheduling_sat_py3) --params=max_time_in_seconds:10 diff --git a/examples/python/single_machine_scheduling_with_setup_release_due_dates_sat_py_test.bintest b/examples/python/single_machine_scheduling_with_setup_release_due_dates_sat_py_test.bintest new file mode 100644 index 0000000000..62c965b85c --- /dev/null +++ b/examples/python/single_machine_scheduling_with_setup_release_due_dates_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(single_machine_scheduling_with_setup_release_due_dates_sat_py3) diff --git a/examples/python/spillover_sat.py b/examples/python/spillover_sat.py new file mode 100644 index 0000000000..02609f30b7 --- /dev/null +++ b/examples/python/spillover_sat.py @@ -0,0 +1,360 @@ +#!/usr/bin/env python3 +# 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. + +"""Solves the problem of buying physical machines to meet VM demand. + +The Spillover problem is defined as follows: + +You have M types of physical machines and V types of Virtual Machines (VMs). You +can use a physical machine of type m to get n_mv copies of VM v. Each physical +machine m has a cost of c_m. Each VM has a demand of d_v. VMs are assigned to +physical machines by the following rule. The demand for each VM type arrives +equally spaced out over the interval [0, 1]. For each VM type, there is a +priority order over the physical machine types that you must follow. When a +demand arrives, if there are any machines of the highest priority type +available, you use them first, then you move on to the second priority machine +type, and so on. Each VM type has a list of compatible physical machine types, +and when the list is exhausted, the remaining demand is not met. Your goal is +to pick quantities of the physical machines to buy (minimizing cost) so that at +least some target service level (e.g. 95%) of the total demand of all VM is met. + +The number of machines bought of each type and the number of VMs demanded of +each type is large enough that you can solve an approximate problem instead, +where the number of machines purchased and the assignment of machines to VMs is +fractional, if it is helpful to do so. + +The problem is not particularly interesting in isolation, it is more interesting +to embed this LP inside a larger optimization problem (e.g. consider a two stage +problem where in stage one, you buy machines, then in stage two, you realize VM +demand). + +The continuous approximation of this problem can be solved by LP (see the +MathOpt python examples). Doing this, instead of using MIP, is nontrivial. +Below, we show that continuous relaxation can be approximately solved by CP-SAT +as well, despite not having continuous variables. If you were solving the +problem in isolation, you should just use an LP solver, but if you were to add +side constraints or embed this within a more complex model, using CP-SAT could +be appropriate. + +If for each VM type, the physical machines that are most cost effective are the +highest priority, AND the target service level is 100%, then the problem has a +trivial optimal solution: + 1. Rank the VMs by lowest cost to meet a unit of demand with the #1 preferred + machine type. + 2. For each VM type in the order above, buy machines from #1 preferred machine + type, until either you have met all demand for the VM type. + +MOE:begin_strip +This example is motivated by the Cloudy problem, see go/fluid-model. +MOE:end_strip +""" + +from collections.abc import Sequence +import dataclasses +import math +import random + +from absl import app +from absl import flags +from ortools.sat.python import cp_model + +_MACHINE_TYPES = flags.DEFINE_integer( + "machine_types", + 100, + "How many types of machines we can fulfill demand with.", +) + +_VM_TYPES = flags.DEFINE_integer( + "vm_types", 500, "How many types of VMs we need to supply." +) + +_FUNGIBILITY = flags.DEFINE_integer( + "fungibility", + 10, + "Each VM type can be satisfied with this many machine types, selected" + " uniformly at random.", +) + +_MAX_DEMAND = flags.DEFINE_integer( + "max_demand", + 100, + "Demand for each VM type is in [max_demand//2, max_demand], uniformly at" + " random.", +) + +_TEST_DATA = flags.DEFINE_bool( + "test_data", False, "Use small test instance instead of random data." +) + +_SEED = flags.DEFINE_integer("seed", 13, "RNG seed for instance creation.") + +_TIME_STEPS = flags.DEFINE_integer("time_steps", 100, "How much to discretize time.") + + +@dataclasses.dataclass(frozen=True) +class MachineUse: + machine_type: int + vms_per_machine: int + + +@dataclasses.dataclass(frozen=True) +class VmDemand: + compatible_machines: tuple[MachineUse, ...] + vm_quantity: int + + +@dataclasses.dataclass(frozen=True) +class SpilloverProblem: + machine_cost: tuple[float, ...] + machine_limit: tuple[int, ...] + vm_demands: tuple[VmDemand, ...] + service_level: float + time_horizon: int + + +def _random_spillover_problem( + num_machines: int, + num_vms: int, + fungibility: int, + max_vm_demand: int, + horizon: int, +) -> SpilloverProblem: + """Generates a random SpilloverProblem.""" + machine_costs = tuple(random.random() for _ in range(num_machines)) + vm_demands = [] + all_machines = list(range(num_machines)) + min_vm_demand = max_vm_demand // 2 + for _ in range(num_vms): + vm_use = [] + for machine in random.sample(all_machines, fungibility): + vm_use.append( + MachineUse(machine_type=machine, vms_per_machine=random.randint(1, 10)) + ) + vm_demands.append( + VmDemand( + compatible_machines=tuple(vm_use), + vm_quantity=random.randint(min_vm_demand, max_vm_demand), + ) + ) + machine_need_ub = num_vms * max_vm_demand + machine_limit = (machine_need_ub,) * num_machines + return SpilloverProblem( + machine_cost=machine_costs, + machine_limit=machine_limit, + vm_demands=tuple(vm_demands), + service_level=0.95, + time_horizon=horizon, + ) + + +def _test_problem() -> SpilloverProblem: + """Creates a small SpilloverProblem with optimal objective of 360.""" + # To avoid machine type 2, ensure we buy enough of 1 to not stock out, cost + # 20 + vm_a = VmDemand( + vm_quantity=10, + compatible_machines=( + MachineUse(machine_type=1, vms_per_machine=1), + MachineUse(machine_type=2, vms_per_machine=1), + ), + ) + # machine type 0 is cheaper, but we don't want to stock out of machine type 1, + # so use all machine type 1, cost 40. + vm_b = VmDemand( + vm_quantity=20, + compatible_machines=( + MachineUse(machine_type=1, vms_per_machine=1), + MachineUse(machine_type=0, vms_per_machine=1), + ), + ) + # Will use 3 copies of machine type 2, cost 300 + vm_c = VmDemand( + vm_quantity=30, + compatible_machines=(MachineUse(machine_type=2, vms_per_machine=10),), + ) + return SpilloverProblem( + machine_cost=(1.0, 2.0, 100.0), + machine_limit=(60, 60, 60), + vm_demands=(vm_a, vm_b, vm_c), + service_level=1.0, + time_horizon=100, + ) + + +# Indices: +# * i in I, the VM demands +# * j in J, the machines supplied +# +# Data: +# * c_j: cost of a machine of type j +# * l_j: a limit of how many machines of type j you can buy. +# * n_ij: how many VMs of type i you get from a machine of type j +# * d_i: the total demand for VMs of type i +# * service_level: the target fraction of demand that is met. +# * P_i subset J: the compatible machine types for VM demand i. +# * UP_i(j) subset P_i, for j in P_i: for VM demand type i, the machines of +# priority higher than j +# * T: the number of integer time steps. +# +# Note: when d_i/n_ij is not integer, some approximation error is introduced in +# constraint 6 below. +# +# Decision variables: +# * s_j: the supply of machine type j +# * w_j: the time we run out of machine j, or 1 if we never run out +# * v_ij: when we start using supply j to meet demand i, or w_j if we never use +# this machine type for this demand. +# * o_i: the time we start failing to meet vm demand i +# * m_i: the total demand met for vm type i. +# +# Model the problem: +# min sum_{j in J} c_j s_j +# s.t. +# 1: sum_i m_i >= service_level * sum_{i in I} d_i +# 2: T * m_i <= o_i * d_i for all i in I +# 3: v_ij >= w_r for all i in I, j in C_i, r in UP_i(j) +# 4: v_ij <= w_j for all i in I, j in C_i +# 5: o_i = sum_{j in P_i} (w_j - v_ij) for all i in I +# 6: sum_{i in I: j in P_i}ceil(d_i/n_ij)(w_j - v_ij)<=T*s_j for all j in J +# o_i, w_j, v_ij in [0, T] +# 0 <= m_i <= d_i +# 0 <= s_j <= l_j +# +# The constraints say: +# 1. The amount of demand served must be at least 95% of total demand. +# 2. The demand served for VM type i is linear in the time we fail to keep +# serving demand. +# 3. Don't start using machine type j for demand i until all higher priority +# machine types r are used up. +# 4. The time we run out of machine type j must be after we start using it for +# VM demand type i. +# 5. The time we are unable to serve further VM demand i is the sum of the +# time spent serving the demand with each eligible machine type. +# 6. The total use of machine type j to serve demand does not exceed the +# supply. The ceil function above introduces some approximation error when +# d_i/n_ij is not integer. +def _solve_spillover_problem(problem: SpilloverProblem) -> None: + """Solves the spillover problem and prints the optimal objective.""" + model = cp_model.CpModel() + num_machines = len(problem.machine_cost) + num_vms = len(problem.vm_demands) + horizon = problem.time_horizon + s = [ + model.new_int_var(lb=0, ub=problem.machine_limit[j], name=f"s_{j}") + for j in range(num_machines) + ] + w = [ + model.new_int_var(lb=0, ub=horizon, name=f"w_{i}") for i in range(num_machines) + ] + o = [model.new_int_var(lb=0, ub=horizon, name=f"o_{j}") for j in range(num_vms)] + m = [ + model.new_int_var(lb=0, ub=problem.vm_demands[j].vm_quantity, name=f"m_{j}") + for j in range(num_vms) + ] + v = [ + { + compat.machine_type: model.new_int_var( + lb=0, ub=horizon, name=f"v_{i}_{compat.machine_type}" + ) + for compat in vm_demand.compatible_machines + } + for i, vm_demand in enumerate(problem.vm_demands) + ] + + obj = 0 + for j in range(num_machines): + obj += s[j] * problem.machine_cost[j] + model.minimize(obj) + + # Constraint 1: demand served is at least service_level fraction of total. + total_vm_demand = sum(vm_demand.vm_quantity for vm_demand in problem.vm_demands) + model.add(sum(m) >= int(math.ceil(problem.service_level * total_vm_demand))) + + # Constraint 2: demand served is linear in time we stop serving. + for i in range(num_vms): + model.add( + problem.time_horizon * m[i] <= o[i] * problem.vm_demands[i].vm_quantity + ) + + # Constraint 3: use machine type j for demand i after all higher priority + # machine types r are used up. + for i in range(num_vms): + for k, meet_demand in enumerate(problem.vm_demands[i].compatible_machines): + j = meet_demand.machine_type + for l in range(k): + r = problem.vm_demands[i].compatible_machines[l].machine_type + model.add(v[i][j] >= w[r]) + + # Constraint 4: outage time of machine j is after start time for using j to + # meet VM demand i. + for i in range(num_vms): + for meet_demand in problem.vm_demands[i].compatible_machines: + j = meet_demand.machine_type + model.add(v[i][j] <= w[j]) + + # Constraint 5: For VM demand i, time service ends is the sum of the time + # spent serving with each eligible machine type. + for i in range(num_vms): + sum_serving = 0 + for meet_demand in problem.vm_demands[i].compatible_machines: + j = meet_demand.machine_type + sum_serving += w[j] - v[i][j] + model.add(o[i] == sum_serving) + + # Constraint 6: Total use of machine type j is at most the supply. + # + # We build the constraints in bulk because our data is transposed. + total_machine_use = [0 for _ in range(num_machines)] + for i in range(num_vms): + for meet_demand in problem.vm_demands[i].compatible_machines: + j = meet_demand.machine_type + nij = meet_demand.vms_per_machine + vm_quantity = problem.vm_demands[i].vm_quantity + # Want vm_quantity/nij, over estimate with ceil(vm_quantity/nij) to use + # integer coefficients. + rate = (vm_quantity + nij - 1) // nij + total_machine_use[j] += rate * (w[j] - v[i][j]) + for j in range(num_machines): + model.add(total_machine_use[j] <= horizon * s[j]) + + solver = cp_model.CpSolver() + solver.parameters.num_workers = 16 + solver.parameters.log_search_progress = True + solver.max_time_in_seconds = 30.0 + status = solver.solve(model) + if status != cp_model.OPTIMAL: + raise RuntimeError(f"expected optimal, found: {status}") + print(f"objective: {solver.objective_value}") + + +def main(argv: Sequence[str]) -> None: + del argv # Unused. + random.seed(_SEED.value) + if _TEST_DATA.value: + problem = _test_problem() + else: + problem = _random_spillover_problem( + _MACHINE_TYPES.value, + _VM_TYPES.value, + _FUNGIBILITY.value, + _MAX_DEMAND.value, + _TIME_STEPS.value, + ) + print(problem) + + _solve_spillover_problem(problem) + + +if __name__ == "__main__": + app.run(main) diff --git a/examples/python/spillover_sat_test_py_test.bintest b/examples/python/spillover_sat_test_py_test.bintest new file mode 100644 index 0000000000..9a0b29ef22 --- /dev/null +++ b/examples/python/spillover_sat_test_py_test.bintest @@ -0,0 +1,2 @@ +RUN: $(spillover_sat) --test_data +CHECK: "objective: 360.0" diff --git a/examples/python/spread_robots_sat_py_test.bintest b/examples/python/spread_robots_sat_py_test.bintest new file mode 100644 index 0000000000..038d178ca8 --- /dev/null +++ b/examples/python/spread_robots_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(spread_robots_sat_py3) diff --git a/examples/python/steel_mill_slab_sat_py_test.bintest b/examples/python/steel_mill_slab_sat_py_test.bintest new file mode 100644 index 0000000000..0f4fdecb94 --- /dev/null +++ b/examples/python/steel_mill_slab_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(steel_mill_slab_sat_py3) diff --git a/examples/python/sudoku_sat_py_test.bintest b/examples/python/sudoku_sat_py_test.bintest new file mode 100644 index 0000000000..d65f3ef701 --- /dev/null +++ b/examples/python/sudoku_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(sudoku_sat_py3) diff --git a/examples/python/task_allocation_sat_py_test.bintest b/examples/python/task_allocation_sat_py_test.bintest new file mode 100644 index 0000000000..b2510011e7 --- /dev/null +++ b/examples/python/task_allocation_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(task_allocation_sat_py3) diff --git a/examples/python/tasks_and_workers_assignment_sat_py_test.bintest b/examples/python/tasks_and_workers_assignment_sat_py_test.bintest new file mode 100644 index 0000000000..e5713b78e4 --- /dev/null +++ b/examples/python/tasks_and_workers_assignment_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(tasks_and_workers_assignment_sat_py3) diff --git a/examples/python/test_scheduling_sat_py_test.bintest b/examples/python/test_scheduling_sat_py_test.bintest new file mode 100644 index 0000000000..71bd923c0f --- /dev/null +++ b/examples/python/test_scheduling_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(test_scheduling_sat_py3) diff --git a/examples/python/tsp_norandom_py_test.bintest b/examples/python/tsp_norandom_py_test.bintest new file mode 100644 index 0000000000..13fc0ce75d --- /dev/null +++ b/examples/python/tsp_norandom_py_test.bintest @@ -0,0 +1 @@ +RUN: $(tsp_py3) --tsp_use_random_matrix=false diff --git a/examples/python/tsp_py_test.bintest b/examples/python/tsp_py_test.bintest new file mode 100644 index 0000000000..b60c8f2cb3 --- /dev/null +++ b/examples/python/tsp_py_test.bintest @@ -0,0 +1 @@ +RUN: $(tsp_py3) diff --git a/examples/python/tsp_sat_py_test.bintest b/examples/python/tsp_sat_py_test.bintest new file mode 100644 index 0000000000..538bc482ea --- /dev/null +++ b/examples/python/tsp_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(tsp_sat_py3) diff --git a/examples/python/vendor_scheduling_sat_py_test.bintest b/examples/python/vendor_scheduling_sat_py_test.bintest new file mode 100644 index 0000000000..7fbd519cd6 --- /dev/null +++ b/examples/python/vendor_scheduling_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(vendor_scheduling_sat_py3) diff --git a/examples/python/wedding_optimal_chart_sat_py_test.bintest b/examples/python/wedding_optimal_chart_sat_py_test.bintest new file mode 100644 index 0000000000..c33c3fd061 --- /dev/null +++ b/examples/python/wedding_optimal_chart_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(wedding_optimal_chart_sat_py3) diff --git a/examples/python/weighted_latency_problem_sat_py_test.bintest b/examples/python/weighted_latency_problem_sat_py_test.bintest new file mode 100644 index 0000000000..97347db8dc --- /dev/null +++ b/examples/python/weighted_latency_problem_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(weighted_latency_problem_sat_py3) diff --git a/examples/python/zebra_sat_py_test.bintest b/examples/python/zebra_sat_py_test.bintest new file mode 100644 index 0000000000..92374872f1 --- /dev/null +++ b/examples/python/zebra_sat_py_test.bintest @@ -0,0 +1 @@ +RUN: $(zebra_sat_py3) diff --git a/ortools/algorithms/samples/BUILD.bazel b/ortools/algorithms/samples/BUILD.bazel index a845ec9aec..4152d7ced5 100644 --- a/ortools/algorithms/samples/BUILD.bazel +++ b/ortools/algorithms/samples/BUILD.bazel @@ -14,7 +14,7 @@ load("@rules_cc//cc:cc_test.bzl", "cc_test") load("@rules_java//java:java_binary.bzl", "java_binary") load("@rules_python//python:py_test.bzl", "py_test") -load("//bazel:run_binary_test.bzl", "run_binary_test") +load("//tools/testing:bintest.bzl", "bintest") package(default_visibility = ["//visibility:public"]) @@ -65,7 +65,8 @@ java_binary( ], ) -run_binary_test( +bintest( name = "KnapsackTest", - binary = ":Knapsack", + srcs = [":KnapsackTest.bintest"], + named_data = {"Knapsack": ":Knapsack"}, ) diff --git a/ortools/algorithms/samples/KnapsackTest.bintest b/ortools/algorithms/samples/KnapsackTest.bintest new file mode 100644 index 0000000000..759db770f5 --- /dev/null +++ b/ortools/algorithms/samples/KnapsackTest.bintest @@ -0,0 +1 @@ +RUN: $(Knapsack) diff --git a/ortools/constraint_solver/samples/BUILD.bazel b/ortools/constraint_solver/samples/BUILD.bazel index ad8757479f..fcfe067b7c 100644 --- a/ortools/constraint_solver/samples/BUILD.bazel +++ b/ortools/constraint_solver/samples/BUILD.bazel @@ -98,3 +98,405 @@ cc_test( "@abseil-cpp//absl/log:globals", ], ) + +cc_test( + name = "simple_routing_program_cc", + size = "medium", + srcs = ["simple_routing_program.cc"], + deps = [ + "//ortools/base", + "//ortools/constraint_solver:cp", + "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_enums_cc_proto", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:flags", + "@abseil-cpp//absl/log:globals", + ], +) + +cc_test( + name = "tsp_cc", + size = "medium", + srcs = ["tsp.cc"], + deps = [ + "//ortools/base", + "//ortools/constraint_solver:cp", + "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_enums_cc_proto", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:flags", + "@abseil-cpp//absl/log:globals", + ], +) + +cc_test( + name = "tsp_circuit_board_cc", + size = "medium", + srcs = ["tsp_circuit_board.cc"], + deps = [ + "//ortools/base", + "//ortools/constraint_solver:cp", + "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_enums_cc_proto", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:flags", + "@abseil-cpp//absl/log:globals", + ], +) + +cc_test( + name = "tsp_cities_cc", + size = "medium", + srcs = ["tsp_cities.cc"], + deps = [ + "//ortools/base", + "//ortools/constraint_solver:cp", + "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_enums_cc_proto", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:flags", + "@abseil-cpp//absl/log:globals", + ], +) + +cc_test( + name = "tsp_cities_routes_cc", + size = "medium", + srcs = ["tsp_cities_routes.cc"], + deps = [ + "//ortools/base", + "//ortools/constraint_solver:cp", + "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_enums_cc_proto", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:flags", + "@abseil-cpp//absl/log:globals", + ], +) + +cc_test( + name = "tsp_distance_matrix_cc", + size = "medium", + srcs = ["tsp_distance_matrix.cc"], + deps = [ + "//ortools/base", + "//ortools/constraint_solver:cp", + "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_enums_cc_proto", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:flags", + "@abseil-cpp//absl/log:globals", + ], +) + +cc_test( + name = "vrp_cc", + size = "medium", + srcs = ["vrp.cc"], + deps = [ + "//ortools/base", + "//ortools/constraint_solver:cp", + "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_enums_cc_proto", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:flags", + "@abseil-cpp//absl/log:globals", + ], +) + +cc_test( + name = "vrp_breaks_cc", + size = "medium", + srcs = ["vrp_breaks.cc"], + deps = [ + "//ortools/base", + "//ortools/constraint_solver:cp", + "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_enums_cc_proto", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:flags", + "@abseil-cpp//absl/log:globals", + "@abseil-cpp//absl/strings", + ], +) + +cc_test( + name = "vrp_capacity_cc", + size = "medium", + srcs = ["vrp_capacity.cc"], + deps = [ + "//ortools/base", + "//ortools/constraint_solver:cp", + "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_enums_cc_proto", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:flags", + "@abseil-cpp//absl/log:globals", + "@protobuf//:duration_cc_proto", + ], +) + +cc_test( + name = "vrp_drop_nodes_cc", + size = "medium", + srcs = ["vrp_drop_nodes.cc"], + deps = [ + "//ortools/base", + "//ortools/constraint_solver:cp", + "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_enums_cc_proto", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:flags", + "@abseil-cpp//absl/log:globals", + "@protobuf//:duration_cc_proto", + ], +) + +cc_test( + name = "vrp_global_span_cc", + size = "medium", + srcs = ["vrp_global_span.cc"], + deps = [ + "//ortools/base", + "//ortools/constraint_solver:cp", + "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_enums_cc_proto", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:flags", + "@abseil-cpp//absl/log:globals", + ], +) + +cc_test( + name = "vrp_initial_routes_cc", + size = "medium", + srcs = ["vrp_initial_routes.cc"], + deps = [ + "//ortools/base", + "//ortools/constraint_solver:cp", + "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_enums_cc_proto", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:flags", + "@abseil-cpp//absl/log:globals", + "@protobuf//:duration_cc_proto", + ], +) + +cc_test( + name = "vrp_starts_ends_cc", + size = "medium", + srcs = ["vrp_starts_ends.cc"], + deps = [ + "//ortools/base", + "//ortools/constraint_solver:cp", + "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_enums_cc_proto", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:flags", + "@abseil-cpp//absl/log:globals", + ], +) + +cc_test( + name = "vrp_pickup_delivery_cc", + size = "medium", + srcs = ["vrp_pickup_delivery.cc"], + deps = [ + "//ortools/base", + "//ortools/constraint_solver:cp", + "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_enums_cc_proto", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:flags", + "@abseil-cpp//absl/log:globals", + ], +) + +cc_test( + name = "vrp_pickup_delivery_fifo_cc", + size = "medium", + srcs = ["vrp_pickup_delivery_fifo.cc"], + deps = [ + "//ortools/base", + "//ortools/constraint_solver:cp", + "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_enums_cc_proto", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:flags", + "@abseil-cpp//absl/log:globals", + ], +) + +cc_test( + name = "vrp_pickup_delivery_lifo_cc", + size = "medium", + srcs = ["vrp_pickup_delivery_lifo.cc"], + deps = [ + "//ortools/base", + "//ortools/constraint_solver:cp", + "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_enums_cc_proto", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:flags", + "@abseil-cpp//absl/log:globals", + ], +) + +cc_test( + name = "vrp_resources_cc", + size = "medium", + srcs = ["vrp_resources.cc"], + deps = [ + "//ortools/base", + "//ortools/constraint_solver:cp", + "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_enums_cc_proto", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:flags", + "@abseil-cpp//absl/log:globals", + ], +) + +cc_test( + name = "vrp_routes_cc", + size = "medium", + srcs = ["vrp_routes.cc"], + deps = [ + "//ortools/base", + "//ortools/constraint_solver:cp", + "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_enums_cc_proto", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:flags", + "@abseil-cpp//absl/log:globals", + ], +) + +cc_test( + name = "vrp_solution_callback_cc", + size = "medium", + srcs = ["vrp_solution_callback.cc"], + deps = [ + "//ortools/base", + "//ortools/constraint_solver:cp", + "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_enums_cc_proto", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:flags", + "@abseil-cpp//absl/log:globals", + "@protobuf//:duration_cc_proto", + ], +) + +cc_test( + name = "vrp_time_windows_cc", + size = "medium", + srcs = ["vrp_time_windows.cc"], + deps = [ + "//ortools/base", + "//ortools/constraint_solver:cp", + "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_enums_cc_proto", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:flags", + "@abseil-cpp//absl/log:globals", + ], +) + +cc_test( + name = "vrp_with_time_limit_cc", + size = "medium", + srcs = ["vrp_with_time_limit.cc"], + deps = [ + "//ortools/base", + "//ortools/constraint_solver:cp", + "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_enums_cc_proto", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:flags", + "@abseil-cpp//absl/log:globals", + "@protobuf//:duration_cc_proto", + ], +) + +cc_test( + name = "vrptw_store_solution_data_cc", + size = "medium", + srcs = ["vrptw_store_solution_data.cc"], + deps = [ + "//ortools/base", + "//ortools/constraint_solver:cp", + "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_enums_cc_proto", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:flags", + "@abseil-cpp//absl/log:globals", + ], +) diff --git a/ortools/glop/samples/BUILD.bazel b/ortools/glop/samples/BUILD.bazel index cddf4f30b1..7afa51de2e 100644 --- a/ortools/glop/samples/BUILD.bazel +++ b/ortools/glop/samples/BUILD.bazel @@ -12,7 +12,7 @@ # limitations under the License. load("@rules_cc//cc:cc_binary.bzl", "cc_binary") -load("//bazel:run_binary_test.bzl", "run_binary_test") +load("//tools/testing:bintest.bzl", "bintest") package(default_visibility = ["//visibility:public"]) @@ -28,8 +28,9 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "simple_glop_program_cc_test", size = "small", - binary = ":simple_glop_program_cc", + srcs = [":simple_glop_program_cc_test.bintest"], + named_data = {"simple_glop_program_cc": ":simple_glop_program_cc"}, ) diff --git a/ortools/glop/samples/simple_glop_program_cc_test.bintest b/ortools/glop/samples/simple_glop_program_cc_test.bintest new file mode 100644 index 0000000000..d17178feb4 --- /dev/null +++ b/ortools/glop/samples/simple_glop_program_cc_test.bintest @@ -0,0 +1 @@ +RUN: $(simple_glop_program_cc) diff --git a/ortools/graph/samples/AssignmentLinearSumAssignmentTest.bintest b/ortools/graph/samples/AssignmentLinearSumAssignmentTest.bintest new file mode 100644 index 0000000000..d6d7060d7a --- /dev/null +++ b/ortools/graph/samples/AssignmentLinearSumAssignmentTest.bintest @@ -0,0 +1 @@ +RUN: $(AssignmentLinearSumAssignment) diff --git a/ortools/graph/samples/AssignmentMinFlowTest.bintest b/ortools/graph/samples/AssignmentMinFlowTest.bintest new file mode 100644 index 0000000000..4d3c0911d2 --- /dev/null +++ b/ortools/graph/samples/AssignmentMinFlowTest.bintest @@ -0,0 +1 @@ +RUN: $(AssignmentMinFlow) diff --git a/ortools/graph/samples/BUILD.bazel b/ortools/graph/samples/BUILD.bazel index 37d8c63ff0..8f96ea4c25 100644 --- a/ortools/graph/samples/BUILD.bazel +++ b/ortools/graph/samples/BUILD.bazel @@ -16,7 +16,7 @@ load("@rules_cc//cc:cc_binary.bzl", "cc_binary") load("@rules_cc//cc:cc_test.bzl", "cc_test") load("@rules_java//java:java_binary.bzl", "java_binary") load("@rules_python//python:py_test.bzl", "py_test") -load("//bazel:run_binary_test.bzl", "run_binary_test") +load("//tools/testing:bintest.bzl", "bintest") package(default_visibility = ["//visibility:public"]) @@ -52,9 +52,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "AssignmentLinearSumAssignmentTest", - binary = ":AssignmentLinearSumAssignment", + srcs = [":AssignmentLinearSumAssignmentTest.bintest"], + named_data = {"AssignmentLinearSumAssignment": ":AssignmentLinearSumAssignment"}, ) cc_test( @@ -86,9 +87,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "AssignmentMinFlowTest", - binary = ":AssignmentMinFlow", + srcs = [":AssignmentMinFlowTest.bintest"], + named_data = {"AssignmentMinFlow": ":AssignmentMinFlow"}, ) cc_test( @@ -120,9 +122,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "BalanceMinFlowTest", - binary = ":BalanceMinFlow", + srcs = [":BalanceMinFlowTest.bintest"], + named_data = {"BalanceMinFlow": ":BalanceMinFlow"}, ) cc_test( @@ -157,9 +160,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "SimpleMaxFlowProgramTest", - binary = ":SimpleMaxFlowProgram", + srcs = [":SimpleMaxFlowProgramTest.bintest"], + named_data = {"SimpleMaxFlowProgram": ":SimpleMaxFlowProgram"}, ) cc_test( @@ -194,9 +198,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "SimpleMinCostFlowProgramTest", - binary = ":SimpleMinCostFlowProgram", + srcs = [":SimpleMinCostFlowProgramTest.bintest"], + named_data = {"SimpleMinCostFlowProgram": ":SimpleMinCostFlowProgram"}, ) cc_binary( @@ -209,10 +214,10 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "dijkstra_directed_test", - binary = ":dijkstra_directed", - grep_lines = ["Shortest path length: 8"], + srcs = [":dijkstra_directed_test.bintest"], + named_data = {"dijkstra_directed": ":dijkstra_directed"}, ) cc_binary( @@ -225,10 +230,10 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "dijkstra_undirected_test", - binary = ":dijkstra_undirected", - grep_lines = ["Shortest path length: 4"], + srcs = [":dijkstra_undirected_test.bintest"], + named_data = {"dijkstra_undirected": ":dijkstra_undirected"}, ) cc_binary( @@ -242,14 +247,10 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "dijkstra_one_to_all_test", - binary = ":dijkstra_one_to_all", - grep_lines = [ - "Distance to 1: 2", - "Distance to 2: 6", - "Distance to 3: 2", - ], + srcs = [":dijkstra_one_to_all_test.bintest"], + named_data = {"dijkstra_one_to_all": ":dijkstra_one_to_all"}, ) cc_binary( @@ -264,15 +265,10 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "dijkstra_sequential_test", - binary = ":dijkstra_sequential", - grep_lines = [ - "Initial distance: 200", - "Distance_2_4: 2", - "Distance_8_1: 3", - "Distance_3_7: 4", - ], + srcs = [":dijkstra_sequential_test.bintest"], + named_data = {"dijkstra_sequential": ":dijkstra_sequential"}, ) cc_binary( @@ -290,9 +286,10 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "dijkstra_all_pairs_shortest_paths_test", - binary = ":dijkstra_all_pairs_shortest_paths", + srcs = [":dijkstra_all_pairs_shortest_paths_test.bintest"], + named_data = {"dijkstra_all_pairs_shortest_paths": ":dijkstra_all_pairs_shortest_paths"}, ) cc_binary( @@ -305,10 +302,10 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "dag_simple_shortest_path_test", - binary = ":dag_simple_shortest_path", - grep_lines = ["Shortest path length: 2"], + srcs = [":dag_simple_shortest_path_test.bintest"], + named_data = {"dag_simple_shortest_path": ":dag_simple_shortest_path"}, ) cc_binary( @@ -326,10 +323,10 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "dag_shortest_path_one_to_all_test", - binary = ":dag_shortest_path_one_to_all", - grep_lines = ["Length of shortest path to node 4: 2"], + srcs = [":dag_shortest_path_one_to_all_test.bintest"], + named_data = {"dag_shortest_path_one_to_all": ":dag_shortest_path_one_to_all"}, ) cc_binary( @@ -343,15 +340,10 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "dag_shortest_path_sequential_test", - binary = ":dag_shortest_path_sequential", - grep_lines = [ - "Initial distance: 200", - "Distance_2_4: 2", - "Distance_8_1: 100", - "Distance_3_7: 4", - ], + srcs = [":dag_shortest_path_sequential_test.bintest"], + named_data = {"dag_shortest_path_sequential": ":dag_shortest_path_sequential"}, ) cc_binary( @@ -367,10 +359,10 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "bfs_directed_test", - binary = ":bfs_directed", - grep_lines = ["Shortest path length (in arcs): 2"], + srcs = [":bfs_directed_test.bintest"], + named_data = {"bfs_directed": ":bfs_directed"}, ) cc_binary( @@ -386,10 +378,10 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "bfs_undirected_test", - binary = ":bfs_undirected", - grep_lines = ["Shortest path length (in arcs): 2"], + srcs = [":bfs_undirected_test.bintest"], + named_data = {"bfs_undirected": ":bfs_undirected"}, ) cc_binary( @@ -405,10 +397,10 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "bfs_one_to_all_test", - binary = ":bfs_one_to_all", - grep_lines = ["Shortest path from 0 to 2 has length: 2"], + srcs = [":bfs_one_to_all_test.bintest"], + named_data = {"bfs_one_to_all": ":bfs_one_to_all"}, ) cc_binary( @@ -425,10 +417,10 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "root_a_tree_test", - binary = ":root_a_tree", - grep_lines = ["Depths:\n 0 -> 2"], + srcs = [":root_a_tree_test.bintest"], + named_data = {"root_a_tree": ":root_a_tree"}, ) cc_binary( @@ -444,10 +436,10 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "rooted_tree_paths_test", - binary = ":rooted_tree_paths", - grep_lines = ["0 -> 4 [0, 1, 4]"], + srcs = [":rooted_tree_paths_test.bintest"], + named_data = {"rooted_tree_paths": ":rooted_tree_paths"}, ) cc_binary( @@ -460,13 +452,10 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "dag_simple_multiple_shortest_paths_test", - binary = ":dag_simple_multiple_shortest_paths", - grep_lines = [ - "#1 shortest path has length: 2", - "#2 shortest path has length: 3", - ], + srcs = [":dag_simple_multiple_shortest_paths_test.bintest"], + named_data = {"dag_simple_multiple_shortest_paths": ":dag_simple_multiple_shortest_paths"}, ) cc_binary( @@ -484,13 +473,10 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "dag_multiple_shortest_paths_one_to_all_test", - binary = ":dag_multiple_shortest_paths_one_to_all", - grep_lines = [ - "\t#1 shortest path to node 4 has length: 2", - "\t#2 shortest path to node 4 has length: 3", - ], + srcs = [":dag_multiple_shortest_paths_one_to_all_test.bintest"], + named_data = {"dag_multiple_shortest_paths_one_to_all": ":dag_multiple_shortest_paths_one_to_all"}, ) cc_binary( @@ -504,21 +490,10 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "dag_multiple_shortest_paths_sequential_test", - binary = ":dag_multiple_shortest_paths_sequential", - grep_lines = [ - "\t#1 shortest path has length: 200", - "\t#2 shortest path has length: 202", - "\t#1 shortest path (2, 4) has length: 20", - "\t#2 shortest path (2, 4) has length: 102", - "\t#1 shortest path (8, 1) has length: 101", - "\t#2 shortest path (8, 1) has length: 108", - "\t#1 shortest path (3, 3) has length: 0", - "\t#2 shortest path (3, 3) has length: 112", - "\t#1 shortest path (0, 0) has length: 0", - "\t#2 shortest path (0, 0) has length: 111", - ], + srcs = [":dag_multiple_shortest_paths_sequential_test.bintest"], + named_data = {"dag_multiple_shortest_paths_sequential": ":dag_multiple_shortest_paths_sequential"}, ) cc_binary( @@ -532,10 +507,10 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "dag_simple_constrained_shortest_path_test", - binary = ":dag_simple_constrained_shortest_path", - grep_lines = ["Constrained shortest path length: 4"], + srcs = [":dag_simple_constrained_shortest_path_test.bintest"], + named_data = {"dag_simple_constrained_shortest_path": ":dag_simple_constrained_shortest_path"}, ) cc_binary( @@ -549,13 +524,8 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "dag_constrained_shortest_path_sequential_test", - binary = ":dag_constrained_shortest_path_sequential", - grep_lines = [ - "Initial distance: 200", - "Distance_2_3: 1", - "Distance_8_1: 100", - "Distance_3_7: 100", - ], + srcs = [":dag_constrained_shortest_path_sequential_test.bintest"], + named_data = {"dag_constrained_shortest_path_sequential": ":dag_constrained_shortest_path_sequential"}, ) diff --git a/ortools/graph/samples/BalanceMinFlowTest.bintest b/ortools/graph/samples/BalanceMinFlowTest.bintest new file mode 100644 index 0000000000..42a9ecd4e9 --- /dev/null +++ b/ortools/graph/samples/BalanceMinFlowTest.bintest @@ -0,0 +1 @@ +RUN: $(BalanceMinFlow) diff --git a/ortools/graph/samples/SimpleMaxFlowProgramTest.bintest b/ortools/graph/samples/SimpleMaxFlowProgramTest.bintest new file mode 100644 index 0000000000..0634bc265a --- /dev/null +++ b/ortools/graph/samples/SimpleMaxFlowProgramTest.bintest @@ -0,0 +1 @@ +RUN: $(SimpleMaxFlowProgram) diff --git a/ortools/graph/samples/SimpleMinCostFlowProgramTest.bintest b/ortools/graph/samples/SimpleMinCostFlowProgramTest.bintest new file mode 100644 index 0000000000..f3ca754ec8 --- /dev/null +++ b/ortools/graph/samples/SimpleMinCostFlowProgramTest.bintest @@ -0,0 +1 @@ +RUN: $(SimpleMinCostFlowProgram) diff --git a/ortools/graph/samples/bfs_directed_test.bintest b/ortools/graph/samples/bfs_directed_test.bintest new file mode 100644 index 0000000000..0c243abb19 --- /dev/null +++ b/ortools/graph/samples/bfs_directed_test.bintest @@ -0,0 +1,2 @@ +RUN: $(bfs_directed) +CHECK: "Shortest path length (in arcs): 2" diff --git a/ortools/graph/samples/bfs_one_to_all_test.bintest b/ortools/graph/samples/bfs_one_to_all_test.bintest new file mode 100644 index 0000000000..2831ab5fed --- /dev/null +++ b/ortools/graph/samples/bfs_one_to_all_test.bintest @@ -0,0 +1,2 @@ +RUN: $(bfs_one_to_all) +CHECK: "Shortest path from 0 to 2 has length: 2" diff --git a/ortools/graph/samples/bfs_undirected_test.bintest b/ortools/graph/samples/bfs_undirected_test.bintest new file mode 100644 index 0000000000..e8c2534c4c --- /dev/null +++ b/ortools/graph/samples/bfs_undirected_test.bintest @@ -0,0 +1,2 @@ +RUN: $(bfs_undirected) +CHECK: "Shortest path length (in arcs): 2" diff --git a/ortools/graph/samples/dag_constrained_shortest_path_sequential_test.bintest b/ortools/graph/samples/dag_constrained_shortest_path_sequential_test.bintest new file mode 100644 index 0000000000..9af86fd995 --- /dev/null +++ b/ortools/graph/samples/dag_constrained_shortest_path_sequential_test.bintest @@ -0,0 +1,5 @@ +RUN: $(dag_constrained_shortest_path_sequential) +CHECK: "Initial distance: 200" +CHECK: "Distance_2_3: 1" +CHECK: "Distance_8_1: 100" +CHECK: "Distance_3_7: 100" diff --git a/ortools/graph/samples/dag_multiple_shortest_paths_one_to_all_test.bintest b/ortools/graph/samples/dag_multiple_shortest_paths_one_to_all_test.bintest new file mode 100644 index 0000000000..cc9d8cfa7b --- /dev/null +++ b/ortools/graph/samples/dag_multiple_shortest_paths_one_to_all_test.bintest @@ -0,0 +1,3 @@ +RUN: $(dag_multiple_shortest_paths_one_to_all) +CHECK: "#1 shortest path to node 4 has length: 2" +CHECK: "#2 shortest path to node 4 has length: 3" diff --git a/ortools/graph/samples/dag_multiple_shortest_paths_sequential_test.bintest b/ortools/graph/samples/dag_multiple_shortest_paths_sequential_test.bintest new file mode 100644 index 0000000000..3f10b3e45f --- /dev/null +++ b/ortools/graph/samples/dag_multiple_shortest_paths_sequential_test.bintest @@ -0,0 +1,11 @@ +RUN: $(dag_multiple_shortest_paths_sequential) +CHECK: "#1 shortest path has length: 200" +CHECK: "#2 shortest path has length: 202" +CHECK: "#1 shortest path (2, 4) has length: 20" +CHECK: "#2 shortest path (2, 4) has length: 102" +CHECK: "#1 shortest path (8, 1) has length: 101" +CHECK: "#2 shortest path (8, 1) has length: 108" +CHECK: "#1 shortest path (3, 3) has length: 0" +CHECK: "#2 shortest path (3, 3) has length: 112" +CHECK: "#1 shortest path (0, 0) has length: 0" +CHECK: "#2 shortest path (0, 0) has length: 111" diff --git a/ortools/graph/samples/dag_shortest_path_one_to_all_test.bintest b/ortools/graph/samples/dag_shortest_path_one_to_all_test.bintest new file mode 100644 index 0000000000..d779206661 --- /dev/null +++ b/ortools/graph/samples/dag_shortest_path_one_to_all_test.bintest @@ -0,0 +1,2 @@ +RUN: $(dag_shortest_path_one_to_all) +CHECK: "Length of shortest path to node 4: 2" diff --git a/ortools/graph/samples/dag_shortest_path_sequential_test.bintest b/ortools/graph/samples/dag_shortest_path_sequential_test.bintest new file mode 100644 index 0000000000..e10fe2767b --- /dev/null +++ b/ortools/graph/samples/dag_shortest_path_sequential_test.bintest @@ -0,0 +1,5 @@ +RUN: $(dag_shortest_path_sequential) +CHECK: "Initial distance: 200" +CHECK: "Distance_2_4: 2" +CHECK: "Distance_8_1: 100" +CHECK: "Distance_3_7: 4" diff --git a/ortools/graph/samples/dag_simple_constrained_shortest_path_test.bintest b/ortools/graph/samples/dag_simple_constrained_shortest_path_test.bintest new file mode 100644 index 0000000000..40a291cdae --- /dev/null +++ b/ortools/graph/samples/dag_simple_constrained_shortest_path_test.bintest @@ -0,0 +1,2 @@ +RUN: $(dag_simple_constrained_shortest_path) +CHECK: "Constrained shortest path length: 4" diff --git a/ortools/graph/samples/dag_simple_multiple_shortest_paths_test.bintest b/ortools/graph/samples/dag_simple_multiple_shortest_paths_test.bintest new file mode 100644 index 0000000000..89e39b9603 --- /dev/null +++ b/ortools/graph/samples/dag_simple_multiple_shortest_paths_test.bintest @@ -0,0 +1,3 @@ +RUN: $(dag_simple_multiple_shortest_paths) +CHECK: "#1 shortest path has length: 2" +CHECK: "#2 shortest path has length: 3" diff --git a/ortools/graph/samples/dag_simple_shortest_path_test.bintest b/ortools/graph/samples/dag_simple_shortest_path_test.bintest new file mode 100644 index 0000000000..37b5c4daf5 --- /dev/null +++ b/ortools/graph/samples/dag_simple_shortest_path_test.bintest @@ -0,0 +1,2 @@ +RUN: $(dag_simple_shortest_path) +CHECK: "Shortest path length: 2" diff --git a/ortools/graph/samples/dijkstra_all_pairs_shortest_paths_test.bintest b/ortools/graph/samples/dijkstra_all_pairs_shortest_paths_test.bintest new file mode 100644 index 0000000000..fff8e2c486 --- /dev/null +++ b/ortools/graph/samples/dijkstra_all_pairs_shortest_paths_test.bintest @@ -0,0 +1 @@ +RUN: $(dijkstra_all_pairs_shortest_paths) diff --git a/ortools/graph/samples/dijkstra_directed_test.bintest b/ortools/graph/samples/dijkstra_directed_test.bintest new file mode 100644 index 0000000000..0966f4e0aa --- /dev/null +++ b/ortools/graph/samples/dijkstra_directed_test.bintest @@ -0,0 +1,2 @@ +RUN: $(dijkstra_directed) +CHECK: "Shortest path length: 8" diff --git a/ortools/graph/samples/dijkstra_one_to_all_test.bintest b/ortools/graph/samples/dijkstra_one_to_all_test.bintest new file mode 100644 index 0000000000..7939cd1b47 --- /dev/null +++ b/ortools/graph/samples/dijkstra_one_to_all_test.bintest @@ -0,0 +1,4 @@ +RUN: $(dijkstra_one_to_all) +CHECK: "Distance to 1: 2" +CHECK: "Distance to 2: 6" +CHECK: "Distance to 3: 2" diff --git a/ortools/graph/samples/dijkstra_sequential_test.bintest b/ortools/graph/samples/dijkstra_sequential_test.bintest new file mode 100644 index 0000000000..d32c3dcd28 --- /dev/null +++ b/ortools/graph/samples/dijkstra_sequential_test.bintest @@ -0,0 +1,5 @@ +RUN: $(dijkstra_sequential) +CHECK: "Initial distance: 200" +CHECK: "Distance_2_4: 2" +CHECK: "Distance_8_1: 3" +CHECK: "Distance_3_7: 4" diff --git a/ortools/graph/samples/dijkstra_undirected_test.bintest b/ortools/graph/samples/dijkstra_undirected_test.bintest new file mode 100644 index 0000000000..9daf3d2cde --- /dev/null +++ b/ortools/graph/samples/dijkstra_undirected_test.bintest @@ -0,0 +1,2 @@ +RUN: $(dijkstra_undirected) +CHECK: "Shortest path length: 4" diff --git a/ortools/graph/samples/root_a_tree_test.bintest b/ortools/graph/samples/root_a_tree_test.bintest new file mode 100644 index 0000000000..cabeefcf2b --- /dev/null +++ b/ortools/graph/samples/root_a_tree_test.bintest @@ -0,0 +1,2 @@ +RUN: $(root_a_tree) +CHECK: "Depths:" "0 -> 2" diff --git a/ortools/graph/samples/rooted_tree_paths_test.bintest b/ortools/graph/samples/rooted_tree_paths_test.bintest new file mode 100644 index 0000000000..0e5d8dd32d --- /dev/null +++ b/ortools/graph/samples/rooted_tree_paths_test.bintest @@ -0,0 +1,2 @@ +RUN: $(rooted_tree_paths) +CHECK: "0 -> 4 [0, 1, 4]" diff --git a/ortools/linear_solver/samples/AssignmentMbTest.bintest b/ortools/linear_solver/samples/AssignmentMbTest.bintest new file mode 100644 index 0000000000..81907f9146 --- /dev/null +++ b/ortools/linear_solver/samples/AssignmentMbTest.bintest @@ -0,0 +1 @@ +RUN: $(AssignmentMb) diff --git a/ortools/linear_solver/samples/BUILD.bazel b/ortools/linear_solver/samples/BUILD.bazel index c2498d96d9..dce7e57652 100644 --- a/ortools/linear_solver/samples/BUILD.bazel +++ b/ortools/linear_solver/samples/BUILD.bazel @@ -15,7 +15,7 @@ load("@pip_deps//:requirements.bzl", "requirement") load("@rules_cc//cc:cc_test.bzl", "cc_test") load("@rules_java//java:java_binary.bzl", "java_binary") load("@rules_python//python:py_test.bzl", "py_test") -load("//bazel:run_binary_test.bzl", "run_binary_test") +load("//tools/testing:bintest.bzl", "bintest") package(default_visibility = ["//visibility:public"]) @@ -224,9 +224,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "AssignmentMbTest", - binary = ":AssignmentMb", + srcs = [":AssignmentMbTest.bintest"], + named_data = {"AssignmentMb": ":AssignmentMb"}, ) java_binary( @@ -240,9 +241,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "BinPackingMbTest", - binary = ":BinPackingMb", + srcs = [":BinPackingMbTest.bintest"], + named_data = {"BinPackingMb": ":BinPackingMb"}, ) java_binary( @@ -256,9 +258,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "CloneModelMbTest", - binary = ":CloneModelMb", + srcs = [":CloneModelMbTest.bintest"], + named_data = {"CloneModelMb": ":CloneModelMb"}, ) java_binary( @@ -272,9 +275,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "SimpleLpProgramMbTest", - binary = ":SimpleLpProgramMb", + srcs = [":SimpleLpProgramMbTest.bintest"], + named_data = {"SimpleLpProgramMb": ":SimpleLpProgramMb"}, ) java_binary( @@ -288,7 +292,8 @@ java_binary( ], ) -run_binary_test( +bintest( name = "SimpleMipProgramMbTest", - binary = ":SimpleMipProgramMb", + srcs = [":SimpleMipProgramMbTest.bintest"], + named_data = {"SimpleMipProgramMb": ":SimpleMipProgramMb"}, ) diff --git a/ortools/linear_solver/samples/BinPackingMbTest.bintest b/ortools/linear_solver/samples/BinPackingMbTest.bintest new file mode 100644 index 0000000000..cdd153dcbc --- /dev/null +++ b/ortools/linear_solver/samples/BinPackingMbTest.bintest @@ -0,0 +1 @@ +RUN: $(BinPackingMb) diff --git a/ortools/linear_solver/samples/CloneModelMbTest.bintest b/ortools/linear_solver/samples/CloneModelMbTest.bintest new file mode 100644 index 0000000000..378e330a0c --- /dev/null +++ b/ortools/linear_solver/samples/CloneModelMbTest.bintest @@ -0,0 +1 @@ +RUN: $(CloneModelMb) diff --git a/ortools/linear_solver/samples/SimpleLpProgramMbTest.bintest b/ortools/linear_solver/samples/SimpleLpProgramMbTest.bintest new file mode 100644 index 0000000000..663992df3a --- /dev/null +++ b/ortools/linear_solver/samples/SimpleLpProgramMbTest.bintest @@ -0,0 +1 @@ +RUN: $(SimpleLpProgramMb) diff --git a/ortools/linear_solver/samples/SimpleMipProgramMbTest.bintest b/ortools/linear_solver/samples/SimpleMipProgramMbTest.bintest new file mode 100644 index 0000000000..332f65ea24 --- /dev/null +++ b/ortools/linear_solver/samples/SimpleMipProgramMbTest.bintest @@ -0,0 +1 @@ +RUN: $(SimpleMipProgramMb) diff --git a/ortools/math_opt/core/c_api/BUILD.bazel b/ortools/math_opt/core/c_api/BUILD.bazel index e77505e7fe..ee31a18d86 100644 --- a/ortools/math_opt/core/c_api/BUILD.bazel +++ b/ortools/math_opt/core/c_api/BUILD.bazel @@ -14,7 +14,7 @@ load("@rules_cc//cc:cc_binary.bzl", "cc_binary") load("@rules_cc//cc:cc_library.bzl", "cc_library") load("@rules_cc//cc:cc_test.bzl", "cc_test") -load("//bazel:run_binary_test.bzl", "run_binary_test") +load("//tools/testing:bintest.bzl", "bintest") cc_library( name = "solver", @@ -75,11 +75,8 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "cpp_example_test", - binary = ":cpp_example", - grep_lines = [ - "Termination is optimal: 1", - "Objective value: 1", - ], + srcs = [":cpp_example_test.bintest"], + named_data = {"cpp_example": ":cpp_example"}, ) diff --git a/ortools/math_opt/core/c_api/cpp_example_test.bintest b/ortools/math_opt/core/c_api/cpp_example_test.bintest new file mode 100644 index 0000000000..92cf27cd7b --- /dev/null +++ b/ortools/math_opt/core/c_api/cpp_example_test.bintest @@ -0,0 +1,3 @@ +RUN: $(cpp_example) +CHECK: "Termination is optimal: 1" +CHECK: "Objective value: 1" diff --git a/ortools/routing/samples/BUILD.bazel b/ortools/routing/samples/BUILD.bazel index a32d5c89ed..3fc67f443a 100644 --- a/ortools/routing/samples/BUILD.bazel +++ b/ortools/routing/samples/BUILD.bazel @@ -11,35 +11,117 @@ # See the License for the specific language governing permissions and # limitations under the License. +# Routing utilities, mostly parsers for routing problems. + +load("@rules_cc//cc:cc_binary.bzl", "cc_binary") +load("@rules_cc//cc:cc_test.bzl", "cc_test") +load("//tools/testing:bintest.bzl", "bintest") + +package(default_visibility = ["//visibility:public"]) + +# cvrptw samples +cc_binary( + name = "cvrptw_soft_capacity", + srcs = ["cvrptw_soft_capacity.cc"], + deps = [ + "//ortools/base", + "//ortools/constraint_solver:cp", + "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "//ortools/constraint_solver:routing_parameters_cc_proto", + "//ortools/constraint_solver:routing_types", + "//ortools/routing/parsers:cvrptw_lib", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/flags:flag", + "@abseil-cpp//absl/log:globals", + "@abseil-cpp//absl/random", + "@protobuf", + ], +) + cc_binary( name = "cvrptw", srcs = ["cvrptw.cc"], deps = [ "//ortools/base", + "//ortools/constraint_solver:cp", "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "//ortools/constraint_solver:routing_parameters_cc_proto", + "//ortools/constraint_solver:routing_types", "//ortools/routing/parsers:cvrptw_lib", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/flags:flag", + "@abseil-cpp//absl/log:globals", + "@abseil-cpp//absl/random", + "@protobuf", ], ) +bintest( + name = "cvrptw_test", + size = "large", + srcs = ["cvrptw_test.bintest"], + named_data = {"cvrptw": ":cvrptw"}, +) + cc_binary( name = "cvrp_disjoint_tw", srcs = ["cvrp_disjoint_tw.cc"], deps = [ "//ortools/base", + "//ortools/constraint_solver:cp", "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "//ortools/constraint_solver:routing_parameters_cc_proto", + "//ortools/constraint_solver:routing_types", "//ortools/routing/parsers:cvrptw_lib", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/flags:flag", + "@abseil-cpp//absl/log:globals", + "@abseil-cpp//absl/random", + "@protobuf", ], ) +bintest( + name = "cvrp_disjoint_tw_test", + size = "large", + srcs = ["cvrp_disjoint_tw_test.bintest"], + named_data = {"cvrp_disjoint_tw": ":cvrp_disjoint_tw"}, +) + +# This test is temporarily down because the time dependent functionality is +# being revised. +# bintest( +# name = "cvrptw_with_time_dependent_costs_test", +# srcs = ["cvrptw_with_time_dependent_costs_test.bintest"], +# size = "large", +# named_data = {"cvrptw_with_time_dependent_costs": ":cvrptw_with_time_dependent_costs"}, +# ) + cc_binary( name = "cvrptw_with_breaks", srcs = ["cvrptw_with_breaks.cc"], deps = [ "//ortools/base", + "//ortools/constraint_solver:cp", "//ortools/constraint_solver:routing", "//ortools/constraint_solver:routing_enums_cc_proto", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "//ortools/constraint_solver:routing_parameters_cc_proto", + "//ortools/constraint_solver:routing_types", "//ortools/routing/parsers:cvrptw_lib", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/flags:flag", + "@abseil-cpp//absl/log:globals", + "@abseil-cpp//absl/random", "@abseil-cpp//absl/strings", + "@protobuf", ], ) @@ -48,28 +130,79 @@ cc_binary( srcs = ["cvrptw_with_resources.cc"], deps = [ "//ortools/base", + "//ortools/constraint_solver:cp", "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "//ortools/constraint_solver:routing_parameters_cc_proto", + "//ortools/constraint_solver:routing_types", "//ortools/routing/parsers:cvrptw_lib", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/flags:flag", + "@abseil-cpp//absl/log:globals", + "@abseil-cpp//absl/random", + "@protobuf", ], ) +bintest( + name = "cvrptw_with_resources_test", + size = "large", + srcs = ["cvrptw_with_resources_test.bintest"], + named_data = {"cvrptw_with_resources": ":cvrptw_with_resources"}, +) + cc_binary( name = "cvrptw_with_stop_times_and_resources", srcs = ["cvrptw_with_stop_times_and_resources.cc"], deps = [ "//ortools/base", + "//ortools/constraint_solver:cp", "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "//ortools/constraint_solver:routing_parameters_cc_proto", + "//ortools/constraint_solver:routing_types", "//ortools/routing/parsers:cvrptw_lib", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/flags:flag", + "@abseil-cpp//absl/log:globals", + "@abseil-cpp//absl/random", "@abseil-cpp//absl/strings", + "@protobuf", ], ) +bintest( + name = "cvrptw_with_stop_times_and_resources_test", + size = "large", + srcs = ["cvrptw_with_stop_times_and_resources_test.bintest"], + named_data = {"cvrptw_with_stop_times_and_resources": ":cvrptw_with_stop_times_and_resources"}, +) + cc_binary( name = "cvrptw_with_refueling", srcs = ["cvrptw_with_refueling.cc"], deps = [ "//ortools/base", + "//ortools/constraint_solver:cp", "//ortools/constraint_solver:routing", + "//ortools/constraint_solver:routing_index_manager", + "//ortools/constraint_solver:routing_parameters", + "//ortools/constraint_solver:routing_parameters_cc_proto", + "//ortools/constraint_solver:routing_types", "//ortools/routing/parsers:cvrptw_lib", + "@abseil-cpp//absl/base:log_severity", + "@abseil-cpp//absl/flags:flag", + "@abseil-cpp//absl/log:globals", + "@abseil-cpp//absl/random", + "@protobuf", ], ) + +bintest( + name = "cvrptw_with_refueling_test", + size = "large", + srcs = ["cvrptw_with_refueling_test.bintest"], + named_data = {"cvrptw_with_refueling": ":cvrptw_with_refueling"}, +) diff --git a/ortools/routing/samples/cvrp_disjoint_tw_test.bintest b/ortools/routing/samples/cvrp_disjoint_tw_test.bintest new file mode 100644 index 0000000000..bcd3d72e9e --- /dev/null +++ b/ortools/routing/samples/cvrp_disjoint_tw_test.bintest @@ -0,0 +1 @@ +RUN: $(cvrp_disjoint_tw) --vrp_use_deterministic_random_seed diff --git a/ortools/routing/samples/cvrptw_test.bintest b/ortools/routing/samples/cvrptw_test.bintest new file mode 100644 index 0000000000..f7c5020d13 --- /dev/null +++ b/ortools/routing/samples/cvrptw_test.bintest @@ -0,0 +1 @@ +RUN: $(cvrptw) --vrp_use_deterministic_random_seed diff --git a/ortools/routing/samples/cvrptw_with_precedences_test.bintest b/ortools/routing/samples/cvrptw_with_precedences_test.bintest new file mode 100644 index 0000000000..4fcb634833 --- /dev/null +++ b/ortools/routing/samples/cvrptw_with_precedences_test.bintest @@ -0,0 +1 @@ +RUN: $(cvrptw_with_precedences) --vrp_use_deterministic_random_seed diff --git a/ortools/routing/samples/cvrptw_with_refueling_test.bintest b/ortools/routing/samples/cvrptw_with_refueling_test.bintest new file mode 100644 index 0000000000..6fb876f5c8 --- /dev/null +++ b/ortools/routing/samples/cvrptw_with_refueling_test.bintest @@ -0,0 +1 @@ +RUN: $(cvrptw_with_refueling) --vrp_use_deterministic_random_seed --cp_random_seed=144 diff --git a/ortools/routing/samples/cvrptw_with_resources_test.bintest b/ortools/routing/samples/cvrptw_with_resources_test.bintest new file mode 100644 index 0000000000..b699662c6a --- /dev/null +++ b/ortools/routing/samples/cvrptw_with_resources_test.bintest @@ -0,0 +1 @@ +RUN: $(cvrptw_with_resources) --vrp_use_deterministic_random_seed diff --git a/ortools/routing/samples/cvrptw_with_stop_times_and_resources_test.bintest b/ortools/routing/samples/cvrptw_with_stop_times_and_resources_test.bintest new file mode 100644 index 0000000000..fe75f294fa --- /dev/null +++ b/ortools/routing/samples/cvrptw_with_stop_times_and_resources_test.bintest @@ -0,0 +1 @@ +RUN: $(cvrptw_with_stop_times_and_resources) --vrp_use_deterministic_random_seed diff --git a/ortools/sat/samples/AssignmentGroupsSatTest.bintest b/ortools/sat/samples/AssignmentGroupsSatTest.bintest new file mode 100644 index 0000000000..f625fc9c30 --- /dev/null +++ b/ortools/sat/samples/AssignmentGroupsSatTest.bintest @@ -0,0 +1 @@ +RUN: $(AssignmentGroupsSat) diff --git a/ortools/sat/samples/AssignmentSatTest.bintest b/ortools/sat/samples/AssignmentSatTest.bintest new file mode 100644 index 0000000000..5aa64bfc6c --- /dev/null +++ b/ortools/sat/samples/AssignmentSatTest.bintest @@ -0,0 +1 @@ +RUN: $(AssignmentSat) diff --git a/ortools/sat/samples/AssignmentTaskSizesSatTest.bintest b/ortools/sat/samples/AssignmentTaskSizesSatTest.bintest new file mode 100644 index 0000000000..5f312b1ebc --- /dev/null +++ b/ortools/sat/samples/AssignmentTaskSizesSatTest.bintest @@ -0,0 +1 @@ +RUN: $(AssignmentTaskSizesSat) diff --git a/ortools/sat/samples/AssignmentTeamsSatTest.bintest b/ortools/sat/samples/AssignmentTeamsSatTest.bintest new file mode 100644 index 0000000000..cf7d2a1abb --- /dev/null +++ b/ortools/sat/samples/AssignmentTeamsSatTest.bintest @@ -0,0 +1 @@ +RUN: $(AssignmentTeamsSat) diff --git a/ortools/sat/samples/AssumptionsSampleSatTest.bintest b/ortools/sat/samples/AssumptionsSampleSatTest.bintest new file mode 100644 index 0000000000..7fad3a5dbd --- /dev/null +++ b/ortools/sat/samples/AssumptionsSampleSatTest.bintest @@ -0,0 +1 @@ +RUN: $(AssumptionsSampleSat) diff --git a/ortools/sat/samples/BUILD.bazel b/ortools/sat/samples/BUILD.bazel index c3290ba560..1402370f79 100644 --- a/ortools/sat/samples/BUILD.bazel +++ b/ortools/sat/samples/BUILD.bazel @@ -16,7 +16,7 @@ load("@rules_cc//cc:cc_test.bzl", "cc_test") load("@rules_go//go:def.bzl", "go_binary") load("@rules_java//java:java_binary.bzl", "java_binary") load("@rules_python//python:py_test.bzl", "py_test") -load("//bazel:run_binary_test.bzl", "run_binary_test") +load("//tools/testing:bintest.bzl", "bintest") py_test( name = "all_different_except_zero_sample_sat_py3", @@ -1053,9 +1053,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "AssignmentGroupsSatTest", - binary = ":AssignmentGroupsSat", + srcs = ["AssignmentGroupsSatTest.bintest"], + named_data = {"AssignmentGroupsSat": ":AssignmentGroupsSat"}, ) java_binary( @@ -1069,9 +1070,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "AssignmentSatTest", - binary = ":AssignmentSat", + srcs = ["AssignmentSatTest.bintest"], + named_data = {"AssignmentSat": ":AssignmentSat"}, ) java_binary( @@ -1085,9 +1087,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "AssignmentTaskSizesSatTest", - binary = ":AssignmentTaskSizesSat", + srcs = ["AssignmentTaskSizesSatTest.bintest"], + named_data = {"AssignmentTaskSizesSat": ":AssignmentTaskSizesSat"}, ) java_binary( @@ -1101,9 +1104,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "AssignmentTeamsSatTest", - binary = ":AssignmentTeamsSat", + srcs = ["AssignmentTeamsSatTest.bintest"], + named_data = {"AssignmentTeamsSat": ":AssignmentTeamsSat"}, ) java_binary( @@ -1117,9 +1121,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "AssumptionsSampleSatTest", - binary = ":AssumptionsSampleSat", + srcs = ["AssumptionsSampleSatTest.bintest"], + named_data = {"AssumptionsSampleSat": ":AssumptionsSampleSat"}, ) java_binary( @@ -1133,9 +1138,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "BinPackingProblemSatTest", - binary = ":BinPackingProblemSat", + srcs = ["BinPackingProblemSatTest.bintest"], + named_data = {"BinPackingProblemSat": ":BinPackingProblemSat"}, ) java_binary( @@ -1148,9 +1154,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "BoolOrSampleSatTest", - binary = ":BoolOrSampleSat", + srcs = ["BoolOrSampleSatTest.bintest"], + named_data = {"BoolOrSampleSat": ":BoolOrSampleSat"}, ) java_binary( @@ -1165,9 +1172,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "ChannelingSampleSatTest", - binary = ":ChannelingSampleSat", + srcs = ["ChannelingSampleSatTest.bintest"], + named_data = {"ChannelingSampleSat": ":ChannelingSampleSat"}, ) java_binary( @@ -1181,9 +1189,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "CloneModelSampleSatTest", - binary = ":CloneModelSampleSat", + srcs = ["CloneModelSampleSatTest.bintest"], + named_data = {"CloneModelSampleSat": ":CloneModelSampleSat"}, ) java_binary( @@ -1197,9 +1206,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "CpIsFunSatTest", - binary = ":CpIsFunSat", + srcs = ["CpIsFunSatTest.bintest"], + named_data = {"CpIsFunSat": ":CpIsFunSat"}, ) java_binary( @@ -1213,9 +1223,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "CpSatExampleTest", - binary = ":CpSatExample", + srcs = ["CpSatExampleTest.bintest"], + named_data = {"CpSatExample": ":CpSatExample"}, ) java_binary( @@ -1230,9 +1241,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "EarlinessTardinessCostSampleSatTest", - binary = ":EarlinessTardinessCostSampleSat", + srcs = ["EarlinessTardinessCostSampleSatTest.bintest"], + named_data = {"EarlinessTardinessCostSampleSat": ":EarlinessTardinessCostSampleSat"}, ) java_binary( @@ -1245,9 +1257,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "IntervalSampleSatTest", - binary = ":IntervalSampleSat", + srcs = ["IntervalSampleSatTest.bintest"], + named_data = {"IntervalSampleSat": ":IntervalSampleSat"}, ) java_binary( @@ -1260,9 +1273,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "LiteralSampleSatTest", - binary = ":LiteralSampleSat", + srcs = ["LiteralSampleSatTest.bintest"], + named_data = {"LiteralSampleSat": ":LiteralSampleSat"}, ) java_binary( @@ -1276,9 +1290,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "MinimalJobshopSatTest", - binary = ":MinimalJobshopSat", + srcs = ["MinimalJobshopSatTest.bintest"], + named_data = {"MinimalJobshopSat": ":MinimalJobshopSat"}, ) java_binary( @@ -1292,9 +1307,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "MultipleKnapsackSatTest", - binary = ":MultipleKnapsackSat", + srcs = ["MultipleKnapsackSatTest.bintest"], + named_data = {"MultipleKnapsackSat": ":MultipleKnapsackSat"}, ) java_binary( @@ -1308,9 +1324,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "NQueensSatTest", - binary = ":NQueensSat", + srcs = ["NQueensSatTest.bintest"], + named_data = {"NQueensSat": ":NQueensSat"}, ) java_binary( @@ -1324,9 +1341,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "NoOverlapSampleSatTest", - binary = ":NoOverlapSampleSat", + srcs = ["NoOverlapSampleSatTest.bintest"], + named_data = {"NoOverlapSampleSat": ":NoOverlapSampleSat"}, ) java_binary( @@ -1340,9 +1358,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "NonLinearSatTest", - binary = ":NonLinearSat", + srcs = ["NonLinearSatTest.bintest"], + named_data = {"NonLinearSat": ":NonLinearSat"}, ) java_binary( @@ -1356,9 +1375,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "NursesSatTest", - binary = ":NursesSat", + srcs = ["NursesSatTest.bintest"], + named_data = {"NursesSat": ":NursesSat"}, ) java_binary( @@ -1371,9 +1391,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "OptionalIntervalSampleSatTest", - binary = ":OptionalIntervalSampleSat", + srcs = ["OptionalIntervalSampleSatTest.bintest"], + named_data = {"OptionalIntervalSampleSat": ":OptionalIntervalSampleSat"}, ) java_binary( @@ -1387,9 +1408,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "RabbitsAndPheasantsSatTest", - binary = ":RabbitsAndPheasantsSat", + srcs = ["RabbitsAndPheasantsSatTest.bintest"], + named_data = {"RabbitsAndPheasantsSat": ":RabbitsAndPheasantsSat"}, ) java_binary( @@ -1403,9 +1425,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "RankingSampleSatTest", - binary = ":RankingSampleSat", + srcs = ["RankingSampleSatTest.bintest"], + named_data = {"RankingSampleSat": ":RankingSampleSat"}, ) java_binary( @@ -1418,9 +1441,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "ReifiedSampleSatTest", - binary = ":ReifiedSampleSat", + srcs = ["ReifiedSampleSatTest.bintest"], + named_data = {"ReifiedSampleSat": ":ReifiedSampleSat"}, ) java_binary( @@ -1434,9 +1458,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "ScheduleRequestsSatTest", - binary = ":ScheduleRequestsSat", + srcs = ["ScheduleRequestsSatTest.bintest"], + named_data = {"ScheduleRequestsSat": ":ScheduleRequestsSat"}, ) java_binary( @@ -1450,9 +1475,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "SearchForAllSolutionsSampleSatTest", - binary = ":SearchForAllSolutionsSampleSat", + srcs = ["SearchForAllSolutionsSampleSatTest.bintest"], + named_data = {"SearchForAllSolutionsSampleSat": ":SearchForAllSolutionsSampleSat"}, ) java_binary( @@ -1466,9 +1492,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "SimpleSatProgramTest", - binary = ":SimpleSatProgram", + srcs = ["SimpleSatProgramTest.bintest"], + named_data = {"SimpleSatProgram": ":SimpleSatProgram"}, ) java_binary( @@ -1482,9 +1509,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "SolutionHintingSampleSatTest", - binary = ":SolutionHintingSampleSat", + srcs = ["SolutionHintingSampleSatTest.bintest"], + named_data = {"SolutionHintingSampleSat": ":SolutionHintingSampleSat"}, ) java_binary( @@ -1498,9 +1526,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "SolveAndPrintIntermediateSolutionsSampleSatTest", - binary = ":SolveAndPrintIntermediateSolutionsSampleSat", + srcs = ["SolveAndPrintIntermediateSolutionsSampleSatTest.bintest"], + named_data = {"SolveAndPrintIntermediateSolutionsSampleSat": ":SolveAndPrintIntermediateSolutionsSampleSat"}, ) java_binary( @@ -1514,9 +1543,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "SolveWithTimeLimitSampleSatTest", - binary = ":SolveWithTimeLimitSampleSat", + srcs = ["SolveWithTimeLimitSampleSatTest.bintest"], + named_data = {"SolveWithTimeLimitSampleSat": ":SolveWithTimeLimitSampleSat"}, ) java_binary( @@ -1532,9 +1562,10 @@ java_binary( ], ) -run_binary_test( +bintest( name = "StepFunctionSampleSatTest", - binary = ":StepFunctionSampleSat", + srcs = ["StepFunctionSampleSatTest.bintest"], + named_data = {"StepFunctionSampleSat": ":StepFunctionSampleSat"}, ) java_binary( @@ -1548,7 +1579,8 @@ java_binary( ], ) -run_binary_test( +bintest( name = "StopAfterNSolutionsSampleSatTest", - binary = ":StopAfterNSolutionsSampleSat", + srcs = ["StopAfterNSolutionsSampleSatTest.bintest"], + named_data = {"StopAfterNSolutionsSampleSat": ":StopAfterNSolutionsSampleSat"}, ) diff --git a/ortools/sat/samples/BinPackingProblemSatTest.bintest b/ortools/sat/samples/BinPackingProblemSatTest.bintest new file mode 100644 index 0000000000..9669ea8530 --- /dev/null +++ b/ortools/sat/samples/BinPackingProblemSatTest.bintest @@ -0,0 +1 @@ +RUN: $(BinPackingProblemSat) diff --git a/ortools/sat/samples/BoolOrSampleSatTest.bintest b/ortools/sat/samples/BoolOrSampleSatTest.bintest new file mode 100644 index 0000000000..dbc32c3c74 --- /dev/null +++ b/ortools/sat/samples/BoolOrSampleSatTest.bintest @@ -0,0 +1 @@ +RUN: $(BoolOrSampleSat) diff --git a/ortools/sat/samples/ChannelingSampleSatTest.bintest b/ortools/sat/samples/ChannelingSampleSatTest.bintest new file mode 100644 index 0000000000..9d92868476 --- /dev/null +++ b/ortools/sat/samples/ChannelingSampleSatTest.bintest @@ -0,0 +1 @@ +RUN: $(ChannelingSampleSat) diff --git a/ortools/sat/samples/CloneModelSampleSatTest.bintest b/ortools/sat/samples/CloneModelSampleSatTest.bintest new file mode 100644 index 0000000000..94c93cd09f --- /dev/null +++ b/ortools/sat/samples/CloneModelSampleSatTest.bintest @@ -0,0 +1 @@ +RUN: $(CloneModelSampleSat) diff --git a/ortools/sat/samples/CpIsFunSatTest.bintest b/ortools/sat/samples/CpIsFunSatTest.bintest new file mode 100644 index 0000000000..f9162043c9 --- /dev/null +++ b/ortools/sat/samples/CpIsFunSatTest.bintest @@ -0,0 +1 @@ +RUN: $(CpIsFunSat) diff --git a/ortools/sat/samples/CpSatExampleTest.bintest b/ortools/sat/samples/CpSatExampleTest.bintest new file mode 100644 index 0000000000..88f9dd7842 --- /dev/null +++ b/ortools/sat/samples/CpSatExampleTest.bintest @@ -0,0 +1 @@ +RUN: $(CpSatExample) diff --git a/ortools/sat/samples/EarlinessTardinessCostSampleSatTest.bintest b/ortools/sat/samples/EarlinessTardinessCostSampleSatTest.bintest new file mode 100644 index 0000000000..776b428f2c --- /dev/null +++ b/ortools/sat/samples/EarlinessTardinessCostSampleSatTest.bintest @@ -0,0 +1 @@ +RUN: $(EarlinessTardinessCostSampleSat) diff --git a/ortools/sat/samples/IntervalSampleSatTest.bintest b/ortools/sat/samples/IntervalSampleSatTest.bintest new file mode 100644 index 0000000000..41ec2dce98 --- /dev/null +++ b/ortools/sat/samples/IntervalSampleSatTest.bintest @@ -0,0 +1 @@ +RUN: $(IntervalSampleSat) diff --git a/ortools/sat/samples/LiteralSampleSatTest.bintest b/ortools/sat/samples/LiteralSampleSatTest.bintest new file mode 100644 index 0000000000..1c4892f5c9 --- /dev/null +++ b/ortools/sat/samples/LiteralSampleSatTest.bintest @@ -0,0 +1 @@ +RUN: $(LiteralSampleSat) diff --git a/ortools/sat/samples/MinimalJobshopSatTest.bintest b/ortools/sat/samples/MinimalJobshopSatTest.bintest new file mode 100644 index 0000000000..46e31b0b0e --- /dev/null +++ b/ortools/sat/samples/MinimalJobshopSatTest.bintest @@ -0,0 +1 @@ +RUN: $(MinimalJobshopSat) diff --git a/ortools/sat/samples/MultipleKnapsackSatTest.bintest b/ortools/sat/samples/MultipleKnapsackSatTest.bintest new file mode 100644 index 0000000000..606d4b4fc0 --- /dev/null +++ b/ortools/sat/samples/MultipleKnapsackSatTest.bintest @@ -0,0 +1 @@ +RUN: $(MultipleKnapsackSat) diff --git a/ortools/sat/samples/NQueensSatTest.bintest b/ortools/sat/samples/NQueensSatTest.bintest new file mode 100644 index 0000000000..8c1aab0fed --- /dev/null +++ b/ortools/sat/samples/NQueensSatTest.bintest @@ -0,0 +1 @@ +RUN: $(NQueensSat) diff --git a/ortools/sat/samples/NoOverlapSampleSatTest.bintest b/ortools/sat/samples/NoOverlapSampleSatTest.bintest new file mode 100644 index 0000000000..7937a6d4c6 --- /dev/null +++ b/ortools/sat/samples/NoOverlapSampleSatTest.bintest @@ -0,0 +1 @@ +RUN: $(NoOverlapSampleSat) diff --git a/ortools/sat/samples/NonLinearSatTest.bintest b/ortools/sat/samples/NonLinearSatTest.bintest new file mode 100644 index 0000000000..2e6af3f6b7 --- /dev/null +++ b/ortools/sat/samples/NonLinearSatTest.bintest @@ -0,0 +1 @@ +RUN: $(NonLinearSat) diff --git a/ortools/sat/samples/NursesSatTest.bintest b/ortools/sat/samples/NursesSatTest.bintest new file mode 100644 index 0000000000..6509325ad0 --- /dev/null +++ b/ortools/sat/samples/NursesSatTest.bintest @@ -0,0 +1 @@ +RUN: $(NursesSat) diff --git a/ortools/sat/samples/OptionalIntervalSampleSatTest.bintest b/ortools/sat/samples/OptionalIntervalSampleSatTest.bintest new file mode 100644 index 0000000000..d48ffd7ad5 --- /dev/null +++ b/ortools/sat/samples/OptionalIntervalSampleSatTest.bintest @@ -0,0 +1 @@ +RUN: $(OptionalIntervalSampleSat) diff --git a/ortools/sat/samples/RabbitsAndPheasantsSatTest.bintest b/ortools/sat/samples/RabbitsAndPheasantsSatTest.bintest new file mode 100644 index 0000000000..0732a387dd --- /dev/null +++ b/ortools/sat/samples/RabbitsAndPheasantsSatTest.bintest @@ -0,0 +1 @@ +RUN: $(RabbitsAndPheasantsSat) diff --git a/ortools/sat/samples/RankingSampleSatTest.bintest b/ortools/sat/samples/RankingSampleSatTest.bintest new file mode 100644 index 0000000000..ea24bdd25e --- /dev/null +++ b/ortools/sat/samples/RankingSampleSatTest.bintest @@ -0,0 +1 @@ +RUN: $(RankingSampleSat) diff --git a/ortools/sat/samples/ReifiedSampleSatTest.bintest b/ortools/sat/samples/ReifiedSampleSatTest.bintest new file mode 100644 index 0000000000..2c8de55e8a --- /dev/null +++ b/ortools/sat/samples/ReifiedSampleSatTest.bintest @@ -0,0 +1 @@ +RUN: $(ReifiedSampleSat) diff --git a/ortools/sat/samples/ScheduleRequestsSatTest.bintest b/ortools/sat/samples/ScheduleRequestsSatTest.bintest new file mode 100644 index 0000000000..bfe90f49c9 --- /dev/null +++ b/ortools/sat/samples/ScheduleRequestsSatTest.bintest @@ -0,0 +1 @@ +RUN: $(ScheduleRequestsSat) diff --git a/ortools/sat/samples/SearchForAllSolutionsSampleSatTest.bintest b/ortools/sat/samples/SearchForAllSolutionsSampleSatTest.bintest new file mode 100644 index 0000000000..783ddf6d49 --- /dev/null +++ b/ortools/sat/samples/SearchForAllSolutionsSampleSatTest.bintest @@ -0,0 +1 @@ +RUN: $(SearchForAllSolutionsSampleSat) diff --git a/ortools/sat/samples/SimpleSatProgramTest.bintest b/ortools/sat/samples/SimpleSatProgramTest.bintest new file mode 100644 index 0000000000..e55bf14ef7 --- /dev/null +++ b/ortools/sat/samples/SimpleSatProgramTest.bintest @@ -0,0 +1 @@ +RUN: $(SimpleSatProgram) diff --git a/ortools/sat/samples/SolutionHintingSampleSatTest.bintest b/ortools/sat/samples/SolutionHintingSampleSatTest.bintest new file mode 100644 index 0000000000..fef6413c5d --- /dev/null +++ b/ortools/sat/samples/SolutionHintingSampleSatTest.bintest @@ -0,0 +1 @@ +RUN: $(SolutionHintingSampleSat) diff --git a/ortools/sat/samples/SolveAndPrintIntermediateSolutionsSampleSatTest.bintest b/ortools/sat/samples/SolveAndPrintIntermediateSolutionsSampleSatTest.bintest new file mode 100644 index 0000000000..891ed37e69 --- /dev/null +++ b/ortools/sat/samples/SolveAndPrintIntermediateSolutionsSampleSatTest.bintest @@ -0,0 +1 @@ +RUN: $(SolveAndPrintIntermediateSolutionsSampleSat) diff --git a/ortools/sat/samples/SolveWithTimeLimitSampleSatTest.bintest b/ortools/sat/samples/SolveWithTimeLimitSampleSatTest.bintest new file mode 100644 index 0000000000..049deecb0f --- /dev/null +++ b/ortools/sat/samples/SolveWithTimeLimitSampleSatTest.bintest @@ -0,0 +1 @@ +RUN: $(SolveWithTimeLimitSampleSat) diff --git a/ortools/sat/samples/StepFunctionSampleSatTest.bintest b/ortools/sat/samples/StepFunctionSampleSatTest.bintest new file mode 100644 index 0000000000..fc98cf7a59 --- /dev/null +++ b/ortools/sat/samples/StepFunctionSampleSatTest.bintest @@ -0,0 +1 @@ +RUN: $(StepFunctionSampleSat) diff --git a/ortools/sat/samples/StopAfterNSolutionsSampleSatTest.bintest b/ortools/sat/samples/StopAfterNSolutionsSampleSatTest.bintest new file mode 100644 index 0000000000..75c8a4132a --- /dev/null +++ b/ortools/sat/samples/StopAfterNSolutionsSampleSatTest.bintest @@ -0,0 +1 @@ +RUN: $(StopAfterNSolutionsSampleSat) diff --git a/ortools/set_cover/samples/BUILD.bazel b/ortools/set_cover/samples/BUILD.bazel index b4515ce264..ba4284be4a 100644 --- a/ortools/set_cover/samples/BUILD.bazel +++ b/ortools/set_cover/samples/BUILD.bazel @@ -13,7 +13,7 @@ load("@rules_cc//cc:cc_binary.bzl", "cc_binary") load("@rules_python//python:py_binary.bzl", "py_binary") -load("//bazel:run_binary_test.bzl", "run_binary_test") +load("//tools/testing:bintest.bzl", "bintest") package(default_visibility = ["//visibility:public"]) @@ -31,10 +31,11 @@ cc_binary( ], ) -run_binary_test( +bintest( name = "set_cover_cc_test", size = "small", - binary = ":set_cover_cc", + srcs = [":set_cover_cc_test.bintest"], + named_data = {"set_cover_cc": ":set_cover_cc"}, ) py_binary( @@ -47,8 +48,9 @@ py_binary( ], ) -run_binary_test( +bintest( name = "set_cover_py_test", size = "small", - binary = ":set_cover_py3", + srcs = [":set_cover_py_test.bintest"], + named_data = {"set_cover_py3": ":set_cover_py3"}, ) diff --git a/ortools/set_cover/samples/set_cover_cc_test.bintest b/ortools/set_cover/samples/set_cover_cc_test.bintest new file mode 100644 index 0000000000..a0555f5689 --- /dev/null +++ b/ortools/set_cover/samples/set_cover_cc_test.bintest @@ -0,0 +1 @@ +RUN: $(set_cover_cc) diff --git a/ortools/set_cover/samples/set_cover_py_test.bintest b/ortools/set_cover/samples/set_cover_py_test.bintest new file mode 100644 index 0000000000..5da2026573 --- /dev/null +++ b/ortools/set_cover/samples/set_cover_py_test.bintest @@ -0,0 +1 @@ +RUN: $(set_cover_py3) diff --git a/tools/build/BUILD.bazel b/tools/build/BUILD.bazel new file mode 100644 index 0000000000..6b8d65e59e --- /dev/null +++ b/tools/build/BUILD.bazel @@ -0,0 +1,26 @@ +# 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("@pip_deps//:requirements.bzl", "requirement") +load("@rules_python//python:py_binary.bzl", "py_binary") + +package( + default_applicable_licenses = ["//third_party/ortools:license"], + default_visibility = ["//visibility:public"], +) + +py_binary( + name = "bazel2cmake", + srcs = ["bazel2cmake.py"], + deps = [requirement("absl-py")], +) diff --git a/tools/build/bazel2cmake.py b/tools/build/bazel2cmake.py new file mode 100644 index 0000000000..dfaef7f25d --- /dev/null +++ b/tools/build/bazel2cmake.py @@ -0,0 +1,273 @@ +#!/usr/bin/env python3 +# 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. + +"""Converts bazel BUILD files to CMakeBazel.txt snippets. + +This script processes BUILD.bazel files and generates CMakeBazel.txt files +containing CMake commands that mirror the bazel build rules. + +Starlark is a subset of Python which allows "executing" them as Python code. +This script "executes" the BUILD.bazel file but provides different definitions +for the target we wants to export to CMake. These new definitions are +responsible for exporting writing the CMake equivalent of Bazel commands. +""" + +from collections.abc import Sequence +import dataclasses +import os + +from absl import app + +# The following global variables are used to interact with the "exec" call. +CURRENT_CMAKE_PIECES = None # The generated cmake is appended to this list. +ROOT_FOLDER = os.getcwd() # The project root. +CURRENT_FOLDER = None # The folder of currently processed BUILD.bazel file. +CURRENT_TARGET_PREFIX = None # The prefix to use for exported targets. + + +@dataclasses.dataclass +class Label: + """Helper class to manipulate bazel labels.""" + + path: str + root: str + cmake_root: str + + def __init__(self, label: str): + """Creates a Label object from a string.""" + if label.startswith("//"): + self.path = label[2:].replace(":", "/") + self.root = ROOT_FOLDER + self.cmake_root = "${CMAKE_SOURCE_DIR}" + elif label.startswith(":"): + self.path = label[1:] + self.root = CURRENT_FOLDER + self.cmake_root = "${CMAKE_CURRENT_SOURCE_DIR}" + else: + assert not label.startswith("/") + self.path = label + self.root = CURRENT_FOLDER + self.cmake_root = "${CMAKE_CURRENT_SOURCE_DIR}" + + def is_file(self) -> bool: + """Returns true if the label is a file.""" + return os.path.isfile(os.path.join(self.root, self.path)) + + def is_target(self) -> bool: + """Returns true if the label is a target.""" + return not self.is_file() + + def as_cmake_target(self) -> str: + """Returns the label as a cmake target.""" + assert self.is_target() + return f"$" + + def as_cmake_file(self) -> str: + """Returns the label as a cmake file.""" + assert self.is_file() + return os.path.join(self.cmake_root, self.path) + + def as_cmake(self) -> str: + """Returns the label as a cmake string.""" + return self.as_cmake_file() if self.is_file() else self.as_cmake_target() + + def as_target_name(self) -> str: + """Returns the label as a target name.""" + assert self.is_target() + return CURRENT_TARGET_PREFIX + self.path + + +@dataclasses.dataclass +class Attr: + """Helper class to manipulate cmake attributes.""" + + name: str + values: list[str] + + def __init__(self, name: str, *values: str): + """Creates an Attr object from a name and a list of values.""" + self.name = name + self.values = values + + def __str__(self): + return f" {self.name} {" ".join(self.values)}" + + def __bool__(self): + return bool(self.values) + + +def name_attr(name: str) -> Attr: + """Returns a NAME attribute.""" + return Attr("NAME", Label(name).as_target_name()) + + +def sources_attr(srcs: Sequence[str], hdrs: Sequence[str]) -> Attr: + """Returns a SOURCES attribute.""" + values = sorted(srcs + hdrs) + return Attr("SOURCES", *values) + + +def link_libraries_attr(deps: Sequence[str]) -> Attr: + """Returns a LINK_LIBRARIES attribute.""" + values = [] + for dep in deps: + if not dep.startswith(":"): + continue + label = Label(dep) + if label.is_target(): + values.append(label.as_target_name()) + return Attr("LINK_LIBRARIES", *values) + + +def type_attr(value: str) -> Attr: + """Returns a TYPE attribute.""" + return Attr("TYPE", value) + + +def env_attr(named_data: dict[str, str]) -> Attr: + """Returns an ENVIRONMENT attribute.""" + values = [] + for key, target in named_data.items(): + label = Label(target) + values.append(f"BINTEST_{key}={label.as_cmake()}") + return Attr("ENVIRONMENT", *values) + + +def script_attr(script: str) -> Attr: + """Returns a SCRIPT attribute.""" + label = Label(script) + assert label.is_file() + return Attr("SCRIPT", label.as_cmake_file()) + + +def add_call(call: str, attrs: Sequence[Attr]) -> str: + """Adds a cmake call to the current cmake pieces.""" + CURRENT_CMAKE_PIECES.append( + f"""{call}( +{'\n'.join(str(a) for a in filter(None, attrs))} +)""" + ) + + +# The functions below are the one replacing the bazel functions. + + +def cc_library( + name, srcs=[], hdrs=[], deps=[], **kwargs +): # pylint: disable=dangerous-default-value + """Adds a cc_library to the current cmake pieces.""" + del kwargs + add_call( + "ortools_cxx_library", + [ + name_attr(name), + sources_attr(srcs, hdrs), + link_libraries_attr(deps), + type_attr("INTERFACE" if not srcs else "SHARED"), + ], + ) + + +def cc_test( + name, srcs=[], hdrs=[], deps=[], **kwargs +): # pylint: disable=dangerous-default-value + """Adds a cc_test to the current cmake pieces.""" + del kwargs + add_call( + "ortools_cxx_test", + [name_attr(name), sources_attr(srcs, hdrs), link_libraries_attr(deps)], + ) + + +def cc_binary( + name, srcs=[], hdrs=[], deps=[], **kwargs +): # pylint: disable=dangerous-default-value + """Adds a cc_binary to the current cmake pieces.""" + del kwargs + add_call( + "ortools_cxx_binary", + [name_attr(name), sources_attr(srcs, hdrs), link_libraries_attr(deps)], + ) + + +def bintest( + name, srcs=[], named_data={}, **kwargs +): # pylint: disable=dangerous-default-value + """Adds a bintest to the current cmake pieces.""" + del kwargs + add_call( + "ortools_cxx_bintest", + [name_attr(name), script_attr(srcs[0]), env_attr(named_data)], + ) + + +# The functions above are the only one accessible when executing the bazel file. +EXEC_GLOBALS = { + "bintest": bintest, + "cc_binary": cc_binary, + "cc_library": cc_library, + "cc_test": cc_test, +} | { + # The function below are ignored and doesn't produce any CMake commands. + name: lambda *kargs, **kwargs: None + for name in [ + # keep sorted go/buildifier#keep-sorted + "build_test", + "cc_proto_library", + "cc_stubby_library", + "java_proto_library", + "java_stubby_library", + "load", + "package", + "proto_library", + "sh_binary", + "sh_test", + ] +} + + +def process_file(prefix: str, file: str): + """Processes a BUILD file and generates a CMakeBazel.txt file.""" + assert os.path.isfile(file) + assert os.path.basename(file) == "BUILD.bazel" + with open(file, "r") as f: + lines = f.read() + global CURRENT_CMAKE_PIECES + CURRENT_CMAKE_PIECES = [] + CURRENT_CMAKE_PIECES.append( + f"# This file is auto generated by bazel2cmake.py from {file}\n" + "# Don't edit manually, your changes will be lost.\n" + "# You can update this file by running:\n" + f"# python3 tools/build/bazel2cmake.py {file}\n" + ) + global CURRENT_FOLDER + CURRENT_FOLDER = os.path.dirname(file) + global CURRENT_TARGET_PREFIX + CURRENT_TARGET_PREFIX = prefix + exec(lines, EXEC_GLOBALS) # pylint: disable=exec-used + output_file = os.path.join(os.path.dirname(file), "CMakeBazel.txt") + with open(output_file, "w") as f: + f.write("\n\n".join(CURRENT_CMAKE_PIECES)) + + +def main(argv: Sequence[str]) -> None: + if len(argv) > 1: + print("bazel2cmake takes no arguments") + return + # TODO: Add more bazel files to autogenerate. + process_file("bzl_cc_example_", "examples/cpp/BUILD.bazel") + + +if __name__ == "__main__": + app.run(main) diff --git a/tools/testing/BUILD.bazel b/tools/testing/BUILD.bazel new file mode 100644 index 0000000000..93fc205879 --- /dev/null +++ b/tools/testing/BUILD.bazel @@ -0,0 +1,134 @@ +# 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("@pip_deps//:requirements.bzl", "requirement") +load("@rules_cc//cc:cc_binary.bzl", "cc_binary") +load("@rules_python//python:py_binary.bzl", "py_binary") +load("@rules_python//python:py_library.bzl", "py_library") +load("@rules_python//python:py_test.bzl", "py_test") +load("//tools/testing:bintest.bzl", "bintest", "py_bintest") + +package(default_visibility = ["//visibility:public"]) + +exports_files(["bintest_script_launcher.py"]) + +# The following libraries are used to implement the `bintest` and `py_bintest` rules. + +py_library( + name = "bintest_run_utils", + srcs = ["bintest_run_utils.py"], +) + +py_test( + name = "bintest_run_utils_test", + srcs = ["bintest_run_utils_test.py"], + deps = [ + ":bintest_run_utils", + requirement("absl-py"), + ], +) + +py_library( + name = "bintest_matchers", + srcs = ["bintest_matchers.py"], +) + +py_test( + name = "bintest_matchers_test", + srcs = ["bintest_matchers_test.py"], + deps = [ + ":bintest_matchers", + requirement("absl-py"), + ], +) + +py_library( + name = "binary_test", + srcs = ["binary_test.py"], + deps = [ + ":bintest_matchers", + ":bintest_run_utils", + requirement("absl-py"), + ], +) + +py_library( + name = "bintest_script_runner", + srcs = ["bintest_script_runner.py"], + deps = [ + ":bintest_matchers", + ":bintest_run_utils", + requirement("absl-py"), + ], +) + +py_binary( + name = "bintest_script_launcher", + srcs = ["bintest_script_launcher.py"], + deps = [":bintest_script_runner"], +) + +cc_binary( + name = "echo", + testonly = True, + srcs = ["echo.cc"], +) + +cc_binary( + name = "fail", + testonly = True, + srcs = ["fail.cc"], +) + +py_test( + name = "bintest_script_runner_test", + srcs = ["bintest_script_runner_test.py"], + data = [ + ":echo", + ":fail", + ], + env = { + "BINTEST_ECHO": "$(rootpath :echo)", + "BINTEST_FAIL": "$(rootpath :fail)", + }, + deps = [ + ":bintest_script_runner", + requirement("absl-py"), + ], +) + +# The following targets demonstrate the usage of the `bintest` and `py_bintest` rules. + +cc_binary( + name = "print_args", + srcs = ["print_args.cc"], +) + +bintest( + name = "print_args_bintest", + srcs = ["print_args.bintest"], + named_data = { + "print_args": ":print_args", + "data_file": ":print_args_data.txt", + }, +) + +py_bintest( + name = "print_args_test", + srcs = ["print_args_test.py"], + named_data = { + "print_args": ":print_args", + "data_file": ":print_args_data.txt", + }, + deps = [requirement("absl-py")], +) diff --git a/tools/testing/README.md b/tools/testing/README.md new file mode 100644 index 0000000000..0e2dbfba32 --- /dev/null +++ b/tools/testing/README.md @@ -0,0 +1,230 @@ +# Binary testing + +This folder contains facilities to **test executable files**. +We offer two APIs: + +* `bintest`: A simple scripting language to write simple tests such as + checking execution success or asserting the presence of text or numbers + within bounds, +* `py_bintest`: An extension of the unit testing framework that makes it easy + to invoke the binary under test, extract values from its output, and check + them within the unittest framework. + +## `bintest` + +It offers two commands `RUN:` and `CHECK:`. + +* The `RUN:` command executes the binary and asserts it ran successfully. \ + The passed arguments can use the `$(