Use new bintest framework (#4928)
This commit is contained in:
committed by
Mizux Seiha
parent
6555f4d2e4
commit
b880e0fb64
@@ -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"},
|
||||
)
|
||||
|
||||
434
examples/python/CMakeBazel.txt
Normal file
434
examples/python/CMakeBazel.txt
Normal 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>
|
||||
)
|
||||
@@ -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()
|
||||
|
||||
1
examples/python/appointments_py_test.bintest
Normal file
1
examples/python/appointments_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(appointments_py3)
|
||||
@@ -0,0 +1 @@
|
||||
RUN: $(assignment_with_constraints_sat_py3)
|
||||
1
examples/python/balance_group_sat_py_test.bintest
Normal file
1
examples/python/balance_group_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(balance_group_sat_py3)
|
||||
@@ -0,0 +1 @@
|
||||
RUN: $(bus_driver_scheduling_sat_py3) --params=max_time_in_seconds:40
|
||||
@@ -0,0 +1 @@
|
||||
RUN: $(car_sequencing_optimization_sat_py3)
|
||||
1
examples/python/chemical_balance_sat_py_test.bintest
Normal file
1
examples/python/chemical_balance_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(chemical_balance_sat_py3)
|
||||
1
examples/python/clustering_sat_py_test.bintest
Normal file
1
examples/python/clustering_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(clustering_sat_py3)
|
||||
1
examples/python/cover_rectangle_sat_py_test.bintest
Normal file
1
examples/python/cover_rectangle_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(cover_rectangle_sat_py3)
|
||||
1
examples/python/cryptarithm_sat_py_test.bintest
Normal file
1
examples/python/cryptarithm_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(cryptarithm_sat_py3)
|
||||
@@ -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()
|
||||
1
examples/python/flexible_job_shop_sat_py_test.bintest
Normal file
1
examples/python/flexible_job_shop_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(flexible_job_shop_sat_py3)
|
||||
@@ -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:
|
||||
|
||||
1
examples/python/gate_scheduling_sat_py_test.bintest
Normal file
1
examples/python/gate_scheduling_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(gate_scheduling_sat_py3)
|
||||
1
examples/python/golomb8_py_test.bintest
Normal file
1
examples/python/golomb8_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(golomb8_py3)
|
||||
1
examples/python/golomb_sat_py_test.bintest
Normal file
1
examples/python/golomb_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(golomb_sat_py3)
|
||||
1
examples/python/hidato_sat_py_test.bintest
Normal file
1
examples/python/hidato_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(hidato_sat_py3)
|
||||
297
examples/python/horse_jumping_show.py
Normal file
297
examples/python/horse_jumping_show.py
Normal 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)
|
||||
2
examples/python/horse_jumping_show_py_test.bintest
Normal file
2
examples/python/horse_jumping_show_py_test.bintest
Normal file
@@ -0,0 +1,2 @@
|
||||
RUN: $(horse_jumping_show_py3)
|
||||
CHECK: "Day 3: C_1.60m_Grand_Prix"
|
||||
1
examples/python/integer_programming_py_test.bintest
Normal file
1
examples/python/integer_programming_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(integer_programming_py3)
|
||||
@@ -0,0 +1 @@
|
||||
RUN: $(jobshop_ft06_distance_sat_py3)
|
||||
1
examples/python/jobshop_ft06_sat_py_test.bintest
Normal file
1
examples/python/jobshop_ft06_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(jobshop_ft06_sat_py3)
|
||||
@@ -0,0 +1 @@
|
||||
RUN: $(jobshop_with_maintenance_sat_py3)
|
||||
1
examples/python/knapsack_2d_sat_py_test.bintest
Normal file
1
examples/python/knapsack_2d_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(knapsack_2d_sat_py3)
|
||||
@@ -0,0 +1,2 @@
|
||||
RUN: $(line_balancing_sat_py3) --input=$(salbp_20_1.alb)
|
||||
CHECK: "objective: 3"
|
||||
1
examples/python/linear_assignment_api_py_test.bintest
Normal file
1
examples/python/linear_assignment_api_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(linear_assignment_api_py3)
|
||||
1
examples/python/linear_programming_py_test.bintest
Normal file
1
examples/python/linear_programming_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(linear_programming_py3)
|
||||
@@ -0,0 +1 @@
|
||||
RUN: $(magic_sequence_distribute_py3)
|
||||
@@ -0,0 +1 @@
|
||||
RUN: $(magic_sequence_distribute_py3) 5
|
||||
@@ -0,0 +1 @@
|
||||
RUN: $(maximize_combinations_sat_py3)
|
||||
1
examples/python/maze_escape_sat_py_test.bintest
Normal file
1
examples/python/maze_escape_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(maze_escape_sat_py3)
|
||||
@@ -0,0 +1 @@
|
||||
RUN: $(memory_layout_and_infeasibility_sat_py3)
|
||||
1
examples/python/music_playlist_sat_py_test.bintest
Normal file
1
examples/python/music_playlist_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(music_playlist_sat_py3)
|
||||
@@ -0,0 +1 @@
|
||||
RUN: $(no_wait_baking_scheduling_sat_py3)
|
||||
1
examples/python/nqueens_sat_py_test.bintest
Normal file
1
examples/python/nqueens_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(nqueens_sat_py3)
|
||||
1
examples/python/pell_equation_sat_py_test.bintest
Normal file
1
examples/python/pell_equation_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(pell_equation_sat_py3)
|
||||
1
examples/python/pentominoes_sat_py_test.bintest
Normal file
1
examples/python/pentominoes_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(pentominoes_sat_py3)
|
||||
1
examples/python/prize_collecting_tsp_sat_py_test.bintest
Normal file
1
examples/python/prize_collecting_tsp_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(prize_collecting_tsp_sat_py3)
|
||||
1
examples/python/prize_collecting_vrp_sat_py_test.bintest
Normal file
1
examples/python/prize_collecting_vrp_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(prize_collecting_vrp_sat_py3)
|
||||
1
examples/python/pyflow_example_py_test.bintest
Normal file
1
examples/python/pyflow_example_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(pyflow_example_py3)
|
||||
1
examples/python/qubo_sat_py_test.bintest
Normal file
1
examples/python/qubo_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(qubo_sat_py3)
|
||||
2
examples/python/rcpsp_sat_c1510_1_py_test.bintest
Normal file
2
examples/python/rcpsp_sat_c1510_1_py_test.bintest
Normal file
@@ -0,0 +1,2 @@
|
||||
RUN: $(rcpsp_sat_py3) --input=$(c1510_1.mm.txt)
|
||||
CHECK: "objective: 21"
|
||||
2
examples/python/rcpsp_sat_j301_1_py_test.bintest
Normal file
2
examples/python/rcpsp_sat_j301_1_py_test.bintest
Normal file
@@ -0,0 +1,2 @@
|
||||
RUN: $(rcpsp_sat_py3) --input=$(j301_1.sm)
|
||||
CHECK: "objective: 43"
|
||||
2
examples/python/rcpsp_sat_rip1_py_test.bintest
Normal file
2
examples/python/rcpsp_sat_rip1_py_test.bintest
Normal file
@@ -0,0 +1,2 @@
|
||||
RUN: $(rcpsp_sat_py3) --input=$(rip1.sch)
|
||||
CHECK: "objective: 100"
|
||||
@@ -0,0 +1 @@
|
||||
RUN: $(rcpsp_sat_py3) --input=$(testset_mm30_psp3.sch) --params=max_time_in_seconds:8.0
|
||||
2
examples/python/rcpsp_sat_ubo_10_psp2_py_test.bintest
Normal file
2
examples/python/rcpsp_sat_ubo_10_psp2_py_test.bintest
Normal file
@@ -0,0 +1,2 @@
|
||||
RUN: $(rcpsp_sat_py3) --input=$(ubo_10_psp2.sch)
|
||||
CHECK: "objective: 45"
|
||||
1
examples/python/shift_scheduling_sat_py_test.bintest
Normal file
1
examples/python/shift_scheduling_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(shift_scheduling_sat_py3) --params=max_time_in_seconds:10
|
||||
@@ -0,0 +1 @@
|
||||
RUN: $(single_machine_scheduling_with_setup_release_due_dates_sat_py3)
|
||||
360
examples/python/spillover_sat.py
Normal file
360
examples/python/spillover_sat.py
Normal file
@@ -0,0 +1,360 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright 2010-2025 Google LLC
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Solves the problem of buying physical machines to meet VM demand.
|
||||
|
||||
The Spillover problem is defined as follows:
|
||||
|
||||
You have M types of physical machines and V types of Virtual Machines (VMs). You
|
||||
can use a physical machine of type m to get n_mv copies of VM v. Each physical
|
||||
machine m has a cost of c_m. Each VM has a demand of d_v. VMs are assigned to
|
||||
physical machines by the following rule. The demand for each VM type arrives
|
||||
equally spaced out over the interval [0, 1]. For each VM type, there is a
|
||||
priority order over the physical machine types that you must follow. When a
|
||||
demand arrives, if there are any machines of the highest priority type
|
||||
available, you use them first, then you move on to the second priority machine
|
||||
type, and so on. Each VM type has a list of compatible physical machine types,
|
||||
and when the list is exhausted, the remaining demand is not met. Your goal is
|
||||
to pick quantities of the physical machines to buy (minimizing cost) so that at
|
||||
least some target service level (e.g. 95%) of the total demand of all VM is met.
|
||||
|
||||
The number of machines bought of each type and the number of VMs demanded of
|
||||
each type is large enough that you can solve an approximate problem instead,
|
||||
where the number of machines purchased and the assignment of machines to VMs is
|
||||
fractional, if it is helpful to do so.
|
||||
|
||||
The problem is not particularly interesting in isolation, it is more interesting
|
||||
to embed this LP inside a larger optimization problem (e.g. consider a two stage
|
||||
problem where in stage one, you buy machines, then in stage two, you realize VM
|
||||
demand).
|
||||
|
||||
The continuous approximation of this problem can be solved by LP (see the
|
||||
MathOpt python examples). Doing this, instead of using MIP, is nontrivial.
|
||||
Below, we show that continuous relaxation can be approximately solved by CP-SAT
|
||||
as well, despite not having continuous variables. If you were solving the
|
||||
problem in isolation, you should just use an LP solver, but if you were to add
|
||||
side constraints or embed this within a more complex model, using CP-SAT could
|
||||
be appropriate.
|
||||
|
||||
If for each VM type, the physical machines that are most cost effective are the
|
||||
highest priority, AND the target service level is 100%, then the problem has a
|
||||
trivial optimal solution:
|
||||
1. Rank the VMs by lowest cost to meet a unit of demand with the #1 preferred
|
||||
machine type.
|
||||
2. For each VM type in the order above, buy machines from #1 preferred machine
|
||||
type, until either you have met all demand for the VM type.
|
||||
|
||||
MOE:begin_strip
|
||||
This example is motivated by the Cloudy problem, see go/fluid-model.
|
||||
MOE:end_strip
|
||||
"""
|
||||
|
||||
from collections.abc import Sequence
|
||||
import dataclasses
|
||||
import math
|
||||
import random
|
||||
|
||||
from absl import app
|
||||
from absl import flags
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
_MACHINE_TYPES = flags.DEFINE_integer(
|
||||
"machine_types",
|
||||
100,
|
||||
"How many types of machines we can fulfill demand with.",
|
||||
)
|
||||
|
||||
_VM_TYPES = flags.DEFINE_integer(
|
||||
"vm_types", 500, "How many types of VMs we need to supply."
|
||||
)
|
||||
|
||||
_FUNGIBILITY = flags.DEFINE_integer(
|
||||
"fungibility",
|
||||
10,
|
||||
"Each VM type can be satisfied with this many machine types, selected"
|
||||
" uniformly at random.",
|
||||
)
|
||||
|
||||
_MAX_DEMAND = flags.DEFINE_integer(
|
||||
"max_demand",
|
||||
100,
|
||||
"Demand for each VM type is in [max_demand//2, max_demand], uniformly at"
|
||||
" random.",
|
||||
)
|
||||
|
||||
_TEST_DATA = flags.DEFINE_bool(
|
||||
"test_data", False, "Use small test instance instead of random data."
|
||||
)
|
||||
|
||||
_SEED = flags.DEFINE_integer("seed", 13, "RNG seed for instance creation.")
|
||||
|
||||
_TIME_STEPS = flags.DEFINE_integer("time_steps", 100, "How much to discretize time.")
|
||||
|
||||
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class MachineUse:
|
||||
machine_type: int
|
||||
vms_per_machine: int
|
||||
|
||||
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class VmDemand:
|
||||
compatible_machines: tuple[MachineUse, ...]
|
||||
vm_quantity: int
|
||||
|
||||
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class SpilloverProblem:
|
||||
machine_cost: tuple[float, ...]
|
||||
machine_limit: tuple[int, ...]
|
||||
vm_demands: tuple[VmDemand, ...]
|
||||
service_level: float
|
||||
time_horizon: int
|
||||
|
||||
|
||||
def _random_spillover_problem(
|
||||
num_machines: int,
|
||||
num_vms: int,
|
||||
fungibility: int,
|
||||
max_vm_demand: int,
|
||||
horizon: int,
|
||||
) -> SpilloverProblem:
|
||||
"""Generates a random SpilloverProblem."""
|
||||
machine_costs = tuple(random.random() for _ in range(num_machines))
|
||||
vm_demands = []
|
||||
all_machines = list(range(num_machines))
|
||||
min_vm_demand = max_vm_demand // 2
|
||||
for _ in range(num_vms):
|
||||
vm_use = []
|
||||
for machine in random.sample(all_machines, fungibility):
|
||||
vm_use.append(
|
||||
MachineUse(machine_type=machine, vms_per_machine=random.randint(1, 10))
|
||||
)
|
||||
vm_demands.append(
|
||||
VmDemand(
|
||||
compatible_machines=tuple(vm_use),
|
||||
vm_quantity=random.randint(min_vm_demand, max_vm_demand),
|
||||
)
|
||||
)
|
||||
machine_need_ub = num_vms * max_vm_demand
|
||||
machine_limit = (machine_need_ub,) * num_machines
|
||||
return SpilloverProblem(
|
||||
machine_cost=machine_costs,
|
||||
machine_limit=machine_limit,
|
||||
vm_demands=tuple(vm_demands),
|
||||
service_level=0.95,
|
||||
time_horizon=horizon,
|
||||
)
|
||||
|
||||
|
||||
def _test_problem() -> SpilloverProblem:
|
||||
"""Creates a small SpilloverProblem with optimal objective of 360."""
|
||||
# To avoid machine type 2, ensure we buy enough of 1 to not stock out, cost
|
||||
# 20
|
||||
vm_a = VmDemand(
|
||||
vm_quantity=10,
|
||||
compatible_machines=(
|
||||
MachineUse(machine_type=1, vms_per_machine=1),
|
||||
MachineUse(machine_type=2, vms_per_machine=1),
|
||||
),
|
||||
)
|
||||
# machine type 0 is cheaper, but we don't want to stock out of machine type 1,
|
||||
# so use all machine type 1, cost 40.
|
||||
vm_b = VmDemand(
|
||||
vm_quantity=20,
|
||||
compatible_machines=(
|
||||
MachineUse(machine_type=1, vms_per_machine=1),
|
||||
MachineUse(machine_type=0, vms_per_machine=1),
|
||||
),
|
||||
)
|
||||
# Will use 3 copies of machine type 2, cost 300
|
||||
vm_c = VmDemand(
|
||||
vm_quantity=30,
|
||||
compatible_machines=(MachineUse(machine_type=2, vms_per_machine=10),),
|
||||
)
|
||||
return SpilloverProblem(
|
||||
machine_cost=(1.0, 2.0, 100.0),
|
||||
machine_limit=(60, 60, 60),
|
||||
vm_demands=(vm_a, vm_b, vm_c),
|
||||
service_level=1.0,
|
||||
time_horizon=100,
|
||||
)
|
||||
|
||||
|
||||
# Indices:
|
||||
# * i in I, the VM demands
|
||||
# * j in J, the machines supplied
|
||||
#
|
||||
# Data:
|
||||
# * c_j: cost of a machine of type j
|
||||
# * l_j: a limit of how many machines of type j you can buy.
|
||||
# * n_ij: how many VMs of type i you get from a machine of type j
|
||||
# * d_i: the total demand for VMs of type i
|
||||
# * service_level: the target fraction of demand that is met.
|
||||
# * P_i subset J: the compatible machine types for VM demand i.
|
||||
# * UP_i(j) subset P_i, for j in P_i: for VM demand type i, the machines of
|
||||
# priority higher than j
|
||||
# * T: the number of integer time steps.
|
||||
#
|
||||
# Note: when d_i/n_ij is not integer, some approximation error is introduced in
|
||||
# constraint 6 below.
|
||||
#
|
||||
# Decision variables:
|
||||
# * s_j: the supply of machine type j
|
||||
# * w_j: the time we run out of machine j, or 1 if we never run out
|
||||
# * v_ij: when we start using supply j to meet demand i, or w_j if we never use
|
||||
# this machine type for this demand.
|
||||
# * o_i: the time we start failing to meet vm demand i
|
||||
# * m_i: the total demand met for vm type i.
|
||||
#
|
||||
# Model the problem:
|
||||
# min sum_{j in J} c_j s_j
|
||||
# s.t.
|
||||
# 1: sum_i m_i >= service_level * sum_{i in I} d_i
|
||||
# 2: T * m_i <= o_i * d_i for all i in I
|
||||
# 3: v_ij >= w_r for all i in I, j in C_i, r in UP_i(j)
|
||||
# 4: v_ij <= w_j for all i in I, j in C_i
|
||||
# 5: o_i = sum_{j in P_i} (w_j - v_ij) for all i in I
|
||||
# 6: sum_{i in I: j in P_i}ceil(d_i/n_ij)(w_j - v_ij)<=T*s_j for all j in J
|
||||
# o_i, w_j, v_ij in [0, T]
|
||||
# 0 <= m_i <= d_i
|
||||
# 0 <= s_j <= l_j
|
||||
#
|
||||
# The constraints say:
|
||||
# 1. The amount of demand served must be at least 95% of total demand.
|
||||
# 2. The demand served for VM type i is linear in the time we fail to keep
|
||||
# serving demand.
|
||||
# 3. Don't start using machine type j for demand i until all higher priority
|
||||
# machine types r are used up.
|
||||
# 4. The time we run out of machine type j must be after we start using it for
|
||||
# VM demand type i.
|
||||
# 5. The time we are unable to serve further VM demand i is the sum of the
|
||||
# time spent serving the demand with each eligible machine type.
|
||||
# 6. The total use of machine type j to serve demand does not exceed the
|
||||
# supply. The ceil function above introduces some approximation error when
|
||||
# d_i/n_ij is not integer.
|
||||
def _solve_spillover_problem(problem: SpilloverProblem) -> None:
|
||||
"""Solves the spillover problem and prints the optimal objective."""
|
||||
model = cp_model.CpModel()
|
||||
num_machines = len(problem.machine_cost)
|
||||
num_vms = len(problem.vm_demands)
|
||||
horizon = problem.time_horizon
|
||||
s = [
|
||||
model.new_int_var(lb=0, ub=problem.machine_limit[j], name=f"s_{j}")
|
||||
for j in range(num_machines)
|
||||
]
|
||||
w = [
|
||||
model.new_int_var(lb=0, ub=horizon, name=f"w_{i}") for i in range(num_machines)
|
||||
]
|
||||
o = [model.new_int_var(lb=0, ub=horizon, name=f"o_{j}") for j in range(num_vms)]
|
||||
m = [
|
||||
model.new_int_var(lb=0, ub=problem.vm_demands[j].vm_quantity, name=f"m_{j}")
|
||||
for j in range(num_vms)
|
||||
]
|
||||
v = [
|
||||
{
|
||||
compat.machine_type: model.new_int_var(
|
||||
lb=0, ub=horizon, name=f"v_{i}_{compat.machine_type}"
|
||||
)
|
||||
for compat in vm_demand.compatible_machines
|
||||
}
|
||||
for i, vm_demand in enumerate(problem.vm_demands)
|
||||
]
|
||||
|
||||
obj = 0
|
||||
for j in range(num_machines):
|
||||
obj += s[j] * problem.machine_cost[j]
|
||||
model.minimize(obj)
|
||||
|
||||
# Constraint 1: demand served is at least service_level fraction of total.
|
||||
total_vm_demand = sum(vm_demand.vm_quantity for vm_demand in problem.vm_demands)
|
||||
model.add(sum(m) >= int(math.ceil(problem.service_level * total_vm_demand)))
|
||||
|
||||
# Constraint 2: demand served is linear in time we stop serving.
|
||||
for i in range(num_vms):
|
||||
model.add(
|
||||
problem.time_horizon * m[i] <= o[i] * problem.vm_demands[i].vm_quantity
|
||||
)
|
||||
|
||||
# Constraint 3: use machine type j for demand i after all higher priority
|
||||
# machine types r are used up.
|
||||
for i in range(num_vms):
|
||||
for k, meet_demand in enumerate(problem.vm_demands[i].compatible_machines):
|
||||
j = meet_demand.machine_type
|
||||
for l in range(k):
|
||||
r = problem.vm_demands[i].compatible_machines[l].machine_type
|
||||
model.add(v[i][j] >= w[r])
|
||||
|
||||
# Constraint 4: outage time of machine j is after start time for using j to
|
||||
# meet VM demand i.
|
||||
for i in range(num_vms):
|
||||
for meet_demand in problem.vm_demands[i].compatible_machines:
|
||||
j = meet_demand.machine_type
|
||||
model.add(v[i][j] <= w[j])
|
||||
|
||||
# Constraint 5: For VM demand i, time service ends is the sum of the time
|
||||
# spent serving with each eligible machine type.
|
||||
for i in range(num_vms):
|
||||
sum_serving = 0
|
||||
for meet_demand in problem.vm_demands[i].compatible_machines:
|
||||
j = meet_demand.machine_type
|
||||
sum_serving += w[j] - v[i][j]
|
||||
model.add(o[i] == sum_serving)
|
||||
|
||||
# Constraint 6: Total use of machine type j is at most the supply.
|
||||
#
|
||||
# We build the constraints in bulk because our data is transposed.
|
||||
total_machine_use = [0 for _ in range(num_machines)]
|
||||
for i in range(num_vms):
|
||||
for meet_demand in problem.vm_demands[i].compatible_machines:
|
||||
j = meet_demand.machine_type
|
||||
nij = meet_demand.vms_per_machine
|
||||
vm_quantity = problem.vm_demands[i].vm_quantity
|
||||
# Want vm_quantity/nij, over estimate with ceil(vm_quantity/nij) to use
|
||||
# integer coefficients.
|
||||
rate = (vm_quantity + nij - 1) // nij
|
||||
total_machine_use[j] += rate * (w[j] - v[i][j])
|
||||
for j in range(num_machines):
|
||||
model.add(total_machine_use[j] <= horizon * s[j])
|
||||
|
||||
solver = cp_model.CpSolver()
|
||||
solver.parameters.num_workers = 16
|
||||
solver.parameters.log_search_progress = True
|
||||
solver.max_time_in_seconds = 30.0
|
||||
status = solver.solve(model)
|
||||
if status != cp_model.OPTIMAL:
|
||||
raise RuntimeError(f"expected optimal, found: {status}")
|
||||
print(f"objective: {solver.objective_value}")
|
||||
|
||||
|
||||
def main(argv: Sequence[str]) -> None:
|
||||
del argv # Unused.
|
||||
random.seed(_SEED.value)
|
||||
if _TEST_DATA.value:
|
||||
problem = _test_problem()
|
||||
else:
|
||||
problem = _random_spillover_problem(
|
||||
_MACHINE_TYPES.value,
|
||||
_VM_TYPES.value,
|
||||
_FUNGIBILITY.value,
|
||||
_MAX_DEMAND.value,
|
||||
_TIME_STEPS.value,
|
||||
)
|
||||
print(problem)
|
||||
|
||||
_solve_spillover_problem(problem)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(main)
|
||||
2
examples/python/spillover_sat_test_py_test.bintest
Normal file
2
examples/python/spillover_sat_test_py_test.bintest
Normal file
@@ -0,0 +1,2 @@
|
||||
RUN: $(spillover_sat) --test_data
|
||||
CHECK: "objective: 360.0"
|
||||
1
examples/python/spread_robots_sat_py_test.bintest
Normal file
1
examples/python/spread_robots_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(spread_robots_sat_py3)
|
||||
1
examples/python/steel_mill_slab_sat_py_test.bintest
Normal file
1
examples/python/steel_mill_slab_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(steel_mill_slab_sat_py3)
|
||||
1
examples/python/sudoku_sat_py_test.bintest
Normal file
1
examples/python/sudoku_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(sudoku_sat_py3)
|
||||
1
examples/python/task_allocation_sat_py_test.bintest
Normal file
1
examples/python/task_allocation_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(task_allocation_sat_py3)
|
||||
@@ -0,0 +1 @@
|
||||
RUN: $(tasks_and_workers_assignment_sat_py3)
|
||||
1
examples/python/test_scheduling_sat_py_test.bintest
Normal file
1
examples/python/test_scheduling_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(test_scheduling_sat_py3)
|
||||
1
examples/python/tsp_norandom_py_test.bintest
Normal file
1
examples/python/tsp_norandom_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(tsp_py3) --tsp_use_random_matrix=false
|
||||
1
examples/python/tsp_py_test.bintest
Normal file
1
examples/python/tsp_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(tsp_py3)
|
||||
1
examples/python/tsp_sat_py_test.bintest
Normal file
1
examples/python/tsp_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(tsp_sat_py3)
|
||||
1
examples/python/vendor_scheduling_sat_py_test.bintest
Normal file
1
examples/python/vendor_scheduling_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(vendor_scheduling_sat_py3)
|
||||
@@ -0,0 +1 @@
|
||||
RUN: $(wedding_optimal_chart_sat_py3)
|
||||
@@ -0,0 +1 @@
|
||||
RUN: $(weighted_latency_problem_sat_py3)
|
||||
1
examples/python/zebra_sat_py_test.bintest
Normal file
1
examples/python/zebra_sat_py_test.bintest
Normal file
@@ -0,0 +1 @@
|
||||
RUN: $(zebra_sat_py3)
|
||||
Reference in New Issue
Block a user