Use new bintest framework (#4928)

This commit is contained in:
Guillaume Chatelet
2025-12-01 10:22:54 +01:00
committed by Mizux Seiha
parent 6555f4d2e4
commit b880e0fb64
227 changed files with 7091 additions and 1533 deletions

3
.gitignore vendored
View File

@@ -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

View File

@@ -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"],
)

View File

@@ -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
)

View File

@@ -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=$<TARGET_FILE:foo_bar_binary>"
# "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 ##
##################

View File

@@ -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()

View File

@@ -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(

425
examples/cpp/CMakeBazel.txt Normal file
View File

@@ -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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_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=$<TARGET_FILE:bzl_cc_example_variable_intervals_sat>
)
ortools_cxx_binary(
NAME bzl_cc_example_pdlp_solve
SOURCES pdlp_solve.cc
)

View File

@@ -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")

View File

@@ -0,0 +1 @@
RUN: $(binpacking_2d_sat) --input $(Class_01.2bp) --instance 2

573
examples/cpp/cgc.cc Normal file
View File

@@ -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 <algorithm>
#include <cstdint>
#include <string>
#include <vector>
#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<std::string> 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<IntVar*>& size_currently_cut,
const std::vector<IntVar*>& size_not_cut,
const std::vector<IntVar*>& parent_index,
const std::vector<int>& 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<IntVar*> 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<int>* sizes_to_pieces,
std::vector<int>* piece_length,
std::vector<int>* 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<int>& piece_length, const std::vector<int>& piece_width,
const int maximum_elements, const int root_length, const int root_width,
std::vector<IntVar*>* parent_index, std::vector<IntVar*>* rectangle_length,
std::vector<IntVar*>* 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<IntVar*> x_guillotine_cut;
std::vector<IntVar*> 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<IntVar*>& parent_index,
const std::vector<IntVar*>& rectangle_length,
const std::vector<IntVar*>& rectangle_width,
const std::vector<int>& sizes_to_pieces,
const ConstrainedGuillotineCuttingData& data,
int maximum_elements,
std::vector<IntVar*>* is_end_piece,
std::vector<IntVar*>* 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<IntVar*> 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<SearchMonitor*>* 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<IntVar*>& parent_index,
const std::vector<IntVar*>& rectangle_length,
const std::vector<IntVar*>& rectangle_width,
const std::vector<IntVar*>& was_cut, Solver* solver) {
CHECK(solver != nullptr);
std::vector<IntVar*> 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<IntVar*>& parent_index,
const std::vector<IntVar*>& rectangle_length,
const std::vector<IntVar*>& rectangle_width,
const SolutionCollector* collector, IntVar* value, int* maximum_value,
std::vector<ConstrainedGuillotineCutting::CutRectangle>* 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<IntVar*>& parent_index,
const std::vector<IntVar*>& rectangle_length,
const std::vector<IntVar*>& rectangle_width,
const std::vector<IntVar*>& is_end_piece,
absl::Span<const int> sizes_to_pieces,
const SolutionCollector* collector) {
CHECK(collector != nullptr);
absl::btree_set<int> 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<ConstrainedGuillotineCuttingData::Piece>& 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<int> 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<int> piece_length;
std::vector<int> 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<IntVar*> parent_index;
// sizes of the rectangles
std::vector<IntVar*> rectangle_length;
std::vector<IntVar*> 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<IntVar*> is_end_piece;
// For every piece it is true if the corresponding rectangle
// was cut.
std::vector<IntVar*> 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<SearchMonitor*> 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

91
examples/cpp/cgc.h Normal file
View File

@@ -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<operations_research::ConstrainedGuillotineCuttingData>
// 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 <algorithm>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#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<ConstrainedGuillotineCuttingData> 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<ConstrainedGuillotineCuttingData> data_;
Solver solver_;
bool solved_;
int maximum_value_;
std::vector<CutRectangle> solution_;
};
} // namespace operations_research
#endif // ORTOOLS_EXAMPLES_CGC_H_

70
examples/cpp/cgc_data.h Normal file
View File

@@ -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 <string>
#include <vector>
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<Piece>& pieces() const { return pieces_; }
private:
// main rectangle size
int root_length_;
int root_width_;
std::vector<Piece> pieces_;
};
} // namespace operations_research
#endif // ORTOOLS_EXAMPLES_CGC_DATA_H_

81
examples/cpp/cgc_main.cc Normal file
View File

@@ -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 <memory>
#include <string>
#include <utility>
#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<ConstrainedGuillotineCuttingData>();
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);
}
}

View File

@@ -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)"

View File

@@ -0,0 +1 @@
RUN: $(constraint_programming_cp)

View File

@@ -0,0 +1 @@
RUN: $(costas_array_sat) --minsize=6 --maxsize=6 --model=1

View File

@@ -0,0 +1 @@
RUN: $(costas_array_sat) --minsize=6 --maxsize=6 --model=2

View File

@@ -0,0 +1 @@
RUN: $(costas_array_sat) --minsize=6 --maxsize=6 --model=3

View File

@@ -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;
}

View File

@@ -0,0 +1 @@
RUN: $(cryptarithm_sat)

View File

@@ -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 <filename>";
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<NodeIndex, ArcIndex>>(
argc, argv);

View File

@@ -0,0 +1,2 @@
RUN: $(dimacs_assignment) $(dimacs_example.txt) --assignment_maximize_cost 2>&1
CHECK: "Cost of optimum assignment: -110"

View File

@@ -0,0 +1,2 @@
RUN: $(dimacs_assignment) $(dimacs_example.txt) 2>&1
CHECK: "Cost of optimum assignment: 84"

View File

@@ -0,0 +1 @@
RUN: $(dobble_ls) --time_limit_in_ms=10000

View File

@@ -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<FapConstraint>& constraints,
void FindComponents(absl::Span<const FapConstraint> constraints,
const absl::btree_map<int, FapVariable>& variables,
const int maximum_variable_id,
absl::flat_hash_map<int, FapComponent>* components) {
@@ -216,20 +217,20 @@ void FindComponents(const std::vector<FapConstraint>& 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<FapConstraint>& 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 {

View File

@@ -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<FapConstraint>& constraints,
void FindComponents(absl::Span<const FapConstraint> constraints,
const absl::btree_map<int, FapVariable>& variables,
int maximum_variable_id,
absl::flat_hash_map<int, FapComponent>* components);

View File

@@ -0,0 +1 @@
RUN: $(flow_api)

View File

@@ -0,0 +1 @@
RUN: $(golomb_sat) --size 5

View File

@@ -0,0 +1 @@
RUN: $(integer_programming)

View File

@@ -0,0 +1 @@
RUN: $(jobshop_sat) --input $(ft06)

View File

@@ -0,0 +1 @@
RUN: $(knapsack_2d_sat) --input $(Class_01.2bp) --instance 2

View File

@@ -0,0 +1 @@
RUN: $(linear_assignment_api)

View File

@@ -0,0 +1 @@
RUN: $(linear_programming)

View File

@@ -0,0 +1 @@
RUN: $(linear_solver_protocol_buffers)

View File

@@ -0,0 +1 @@
RUN: $(magic_sequence_sat)

View File

@@ -0,0 +1 @@
RUN: $(magic_square_sat)

View File

@@ -0,0 +1 @@
RUN: $(max_flow)

View File

@@ -0,0 +1 @@
RUN: $(min_cost_flow)

View File

@@ -0,0 +1 @@
RUN: $(mps_driver) --input $(maximization.mps)

View File

@@ -0,0 +1 @@
RUN: $(multi_knapsack_sat)

View File

@@ -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

View File

@@ -0,0 +1 @@
RUN: $(nqueens)

View File

@@ -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"

View File

@@ -0,0 +1,2 @@
RUN: $(pdptw) --pdp_file=$(lc102.txt) 2>&1
CHECK: "Cost: 1000828.936870"

View File

@@ -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 <algorithm>
#include <cmath>
#include <cstdint>
#include <string>
#include <utility>
#include <vector>
#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<std::pair<int, int>> 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<int64_t>(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<int64_t>* 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<int64_t>* 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<int64_t>* 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<int64_t>& 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<int64_t>* 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<std::string> 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<int64_t> 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<int> customer_ids;
std::vector<std::pair<int, int>> coords;
std::vector<int64_t> demands;
std::vector<int64_t> open_times;
std::vector<int64_t> close_times;
std::vector<int64_t> service_times;
std::vector<RoutingIndexManager::NodeIndex> pickups;
std::vector<RoutingIndexManager::NodeIndex> 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<const Coordinates*>(&coords),
manager.IndexToNode(i), manager.IndexToNode(j));
});
routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
routing.AddDimension(
routing.RegisterTransitCallback(absl::bind_front(
TravelPlusServiceTime, manager,
const_cast<const Coordinates*>(&coords),
const_cast<const std::vector<int64_t>*>(&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<std::pair<int64_t, int64_t>> 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<int64_t> group_demands(demands.size());
for (int pair_index = 0; pair_index < pickup_delivery_pairs.size();) {
std::vector<int64_t> pickup_indices;
std::vector<int64_t> delivery_indices;
std::vector<IntVar*> pickup_vehicle_variables;
std::vector<IntVar*> 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<const std::vector<int64_t>*>(&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;
}

View File

@@ -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"

View File

@@ -0,0 +1,2 @@
RUN: $(pdptw_with_alternatives) --pdp_file=$(lc102.txt) 2>&1
CHECK: "Cost: 361237"

View File

@@ -0,0 +1 @@
RUN: $(shift_minimization_sat) --input $(shift_minimization.dat)

View File

@@ -0,0 +1 @@
RUN: $(slitherlink_sat)

View File

@@ -0,0 +1 @@
RUN: $(sports_scheduling_sat)

View File

@@ -0,0 +1 @@
RUN: $(strawberry_fields_with_column_generation) --colgen_instance=4

6
examples/cpp/testdata/cgc/1.in vendored Normal file
View File

@@ -0,0 +1,6 @@
4
4 4
1 2 6 100
1 3 2 2
1 4 2 2
3 4 2 10

6
examples/cpp/testdata/cgc/2.in vendored Normal file
View File

@@ -0,0 +1,6 @@
4
4 4
1 1 1 1
2 1 1 1
3 1 1 1
4 1 1 1

3
examples/cpp/testdata/cgc/3.in vendored Normal file
View File

@@ -0,0 +1,3 @@
1
4 4
2 2 2 10

9
examples/cpp/testdata/cgc/cgcut1.in vendored Normal file
View File

@@ -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

12
examples/cpp/testdata/cgc/cgcut2.in vendored Normal file
View File

@@ -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

22
examples/cpp/testdata/cgc/cgcut3.in vendored Normal file
View File

@@ -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

View File

@@ -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

108
examples/cpp/testdata/lc102.txt vendored Normal file
View File

@@ -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

View File

@@ -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

751
examples/cpp/testdata/wt40.txt vendored Normal file
View File

@@ -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

View File

@@ -0,0 +1 @@
RUN: $(variable_intervals_sat)

View File

@@ -0,0 +1 @@
RUN: $(weighted_tardiness_sat) --input $(wt40.txt)

View File

@@ -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"},
)

View File

@@ -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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_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=$<TARGET_FILE:bzl_py_example_zebra_sat_py3>
)

View File

@@ -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()

View File

@@ -0,0 +1 @@
RUN: $(appointments_py3)

View File

@@ -0,0 +1 @@
RUN: $(assignment_with_constraints_sat_py3)

View File

@@ -0,0 +1 @@
RUN: $(balance_group_sat_py3)

View File

@@ -0,0 +1 @@
RUN: $(bus_driver_scheduling_sat_py3) --params=max_time_in_seconds:40

View File

@@ -0,0 +1 @@
RUN: $(car_sequencing_optimization_sat_py3)

View File

@@ -0,0 +1 @@
RUN: $(chemical_balance_sat_py3)

View File

@@ -0,0 +1 @@
RUN: $(clustering_sat_py3)

View File

@@ -0,0 +1 @@
RUN: $(cover_rectangle_sat_py3)

View File

@@ -0,0 +1 @@
RUN: $(cryptarithm_sat_py3)

View File

@@ -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()

View File

@@ -0,0 +1 @@
RUN: $(flexible_job_shop_sat_py3)

View File

@@ -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:

View File

@@ -0,0 +1 @@
RUN: $(gate_scheduling_sat_py3)

View File

@@ -0,0 +1 @@
RUN: $(golomb8_py3)

View File

@@ -0,0 +1 @@
RUN: $(golomb_sat_py3)

View File

@@ -0,0 +1 @@
RUN: $(hidato_sat_py3)

View File

@@ -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)

View File

@@ -0,0 +1,2 @@
RUN: $(horse_jumping_show_py3)
CHECK: "Day 3: C_1.60m_Grand_Prix"

View File

@@ -0,0 +1 @@
RUN: $(integer_programming_py3)

View File

@@ -0,0 +1 @@
RUN: $(jobshop_ft06_distance_sat_py3)

View File

@@ -0,0 +1 @@
RUN: $(jobshop_ft06_sat_py3)

View File

@@ -0,0 +1 @@
RUN: $(jobshop_with_maintenance_sat_py3)

View File

@@ -0,0 +1 @@
RUN: $(knapsack_2d_sat_py3)

View File

@@ -0,0 +1,2 @@
RUN: $(line_balancing_sat_py3) --input=$(salbp_20_1.alb)
CHECK: "objective: 3"

View File

@@ -0,0 +1 @@
RUN: $(linear_assignment_api_py3)

View File

@@ -0,0 +1 @@
RUN: $(linear_programming_py3)

View File

@@ -0,0 +1 @@
RUN: $(magic_sequence_distribute_py3)

View File

@@ -0,0 +1 @@
RUN: $(magic_sequence_distribute_py3) 5

View File

@@ -0,0 +1 @@
RUN: $(maximize_combinations_sat_py3)

View File

@@ -0,0 +1 @@
RUN: $(maze_escape_sat_py3)

View File

@@ -0,0 +1 @@
RUN: $(memory_layout_and_infeasibility_sat_py3)

View File

@@ -0,0 +1 @@
RUN: $(music_playlist_sat_py3)

View File

@@ -0,0 +1 @@
RUN: $(no_wait_baking_scheduling_sat_py3)

View File

@@ -0,0 +1 @@
RUN: $(nqueens_sat_py3)

Some files were not shown because too many files have changed in this diff Show More