run tests in examples/python under bazel; sync/clean examples

This commit is contained in:
Laurent Perron
2023-01-26 13:46:40 +01:00
parent 49a72a8d67
commit f460e9b0fc
11 changed files with 201 additions and 47 deletions

View File

@@ -1,4 +1,8 @@
absl-py==1.4.0
numpy==1.24.1
pandas==1.5.3
protobuf==4.21.12
python_dateutil==2.8.2
pytz==2022.7.1
scipy==1.10.0
six==1.16

View File

@@ -0,0 +1,90 @@
# Copyright 2010-2022 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.
# BUILD file to run python examples.
load(":code_samples.bzl", "code_sample_compile_py", "code_sample_py")
# code_sample_py("arc_flow_cutting_stock_sat") # using pywraplp
code_sample_py("assignment2_sat")
code_sample_py("assignment_with_constraints_sat")
code_sample_py("balance_group_sat")
code_sample_py("bus_driver_scheduling_flow_sat")
code_sample_py("bus_driver_scheduling_sat")
code_sample_py("chemical_balance_sat")
code_sample_py("clustering_sat")
code_sample_py("cover_rectangle_sat")
code_sample_py("flexible_job_shop_sat")
code_sample_py("gate_scheduling_sat")
code_sample_py("golomb_sat")
code_sample_py("hidato_sat")
code_sample_py("jobshop_ft06_distance_sat")
code_sample_py("jobshop_ft06_sat")
code_sample_py("jobshop_with_maintenance_sat")
code_sample_py("knapsack_2d_sat")
code_sample_compile_py("line_balancing_sat") # no input
code_sample_py("maze_escape_sat")
code_sample_py("no_wait_baking_scheduling_sat")
code_sample_py("prize_collecting_tsp_sat")
code_sample_py("prize_collecting_vrp_sat")
code_sample_py("qubo_sat")
code_sample_compile_py("rcpsp_sat") # no input
code_sample_py("reallocate_sat")
code_sample_py("shift_scheduling_sat")
code_sample_py("single_machine_scheduling_with_setup_release_due_dates_sat")
# code_sample_py("steel_mill_slab_sat") # using pywraplp
code_sample_py("sudoku_sat")
code_sample_py("task_allocation_sat")
code_sample_py("tasks_and_workers_assignment_sat")
code_sample_py("tsp_sat")
code_sample_py("vendor_scheduling_sat")
code_sample_py("wedding_optimal_chart_sat")
code_sample_py("weighted_latency_problem_sat")
code_sample_py("worker_schedule_sat")
code_sample_py("zebra_sat")

View File

@@ -100,14 +100,13 @@ def AggregateItemCollectionsOptimally(item_collections, max_num_collections,
Each collection may be selected more than one time.
Args:
item_collections: a list of item collections. Each item collection is a
list of integers [#item0, ..., #itemN-1], where #itemK is the number
of times item #K appears in the collection, and N is the number of
distinct items.
max_num_collections: an integer, the maximum number of item collections
that may be selected (counting repetitions of the same collection).
item_collections: a list of item collections. Each item collection is a list
of integers [#item0, ..., #itemN-1], where #itemK is the number of times
item #K appears in the collection, and N is the number of distinct items.
max_num_collections: an integer, the maximum number of item collections that
may be selected (counting repetitions of the same collection).
ideal_item_ratios: A list of N float which sums to 1.0: the K-th element is
the ideal ratio of item #K in the whole aggregated selection.
the ideal ratio of item #K in the whole aggregated selection.
Returns:
A pair (objective value, list of pairs (item collection, num_selections)),
@@ -184,10 +183,10 @@ def GetOptimalSchedule(demand):
"""Computes the optimal schedule for the installation input.
Args:
demand: a list of "appointment types". Each "appointment type" is
a triple (ideal_ratio_pct, name, duration_minutes), where
ideal_ratio_pct is the ideal percentage (in [0..100.0]) of that
type of appointment among all appointments scheduled.
demand: a list of "appointment types". Each "appointment type" is a triple
(ideal_ratio_pct, name, duration_minutes), where ideal_ratio_pct is the
ideal percentage (in [0..100.0]) of that type of appointment among all
appointments scheduled.
Returns:
The same output type as EnumerateAllKnapsacksWithRepetition.

View File

@@ -0,0 +1,56 @@
# Copyright 2010-2022 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.
"""Helper macro to compile and test code samples."""
load("@ortools_deps//:requirements.bzl", "requirement")
PYTHON_DEPS = [
"//ortools/sat/python:cp_model",
"//ortools/sat/python:visualization",
requirement("absl-py"),
requirement("numpy"),
requirement("pandas"),
requirement("protobuf"),
requirement("python_dateutil"),
requirement("pytz"),
requirement("six"),
]
def code_sample_compile_py(name):
native.py_binary(
name = name + "_py3",
srcs = [name + ".py"],
main = name + ".py",
deps = PYTHON_DEPS,
python_version = "PY3",
srcs_version = "PY3",
)
def code_sample_test_py(name):
native.py_test(
name = name + "_py_test",
size = "small",
srcs = [name + ".py"],
main = name + ".py",
data = [
"//ortools/sat/python:cp_model",
],
deps = PYTHON_DEPS,
python_version = "PY3",
srcs_version = "PY3",
)
def code_sample_py(name):
code_sample_compile_py(name)
code_sample_test_py(name)

View File

@@ -30,10 +30,9 @@ from ortools.sat.python import cp_model
_OUTPUT_PROTO = flags.DEFINE_string(
'output_proto', '', 'Output file to write the cp_model proto to.')
_PARAMS = flags.DEFINE_string(
'params',
'num_search_workers:16,log_search_progress:true,max_time_in_seconds:10.0',
'Sat solver parameters.')
_PARAMS = flags.DEFINE_string('params',
'num_search_workers:16,log_search_progress:true',
'Sat solver parameters.')
_MODEL = flags.DEFINE_string('model', 'rotation',
'\'duplicate\' or \'rotation\' or \'optional\'')

View File

@@ -33,6 +33,7 @@ from typing import Sequence
from absl import app
from absl import flags
from google.protobuf import text_format
from ortools.sat.python import cp_model
_INPUT = flags.DEFINE_string('input', '', 'Input file to parse and solve.')
@@ -259,14 +260,14 @@ def solve_boolean_model(model, hint):
for t in all_tasks:
model.AddHint(assign[t, hint[t]], 1)
if FLAGS.output_proto:
print(f'Writing proto to {FLAGS.output_proto}')
model.ExportToFile(FLAGS.output_proto)
if _OUTPUT_PROTO.value:
print(f'Writing proto to {_OUTPUT_PROTO.value}')
model.ExportToFile(_OUTPUT_PROTO.value)
# Solve model.
solver = cp_model.CpSolver()
if FLAGS.params:
text_format.Parse(FLAGS.params, solver.parameters)
if _PARAMS.value:
text_format.Parse(_PARAMS.value, solver.parameters)
solver.parameters.log_search_progress = True
solver.Solve(model)
@@ -321,32 +322,31 @@ def solve_scheduling_model(model, hint):
for t in all_tasks:
model.AddHint(pods[t], hint[t])
if FLAGS.output_proto:
print(f'Writing proto to{FLAGS.output_proto}')
model.ExportToFile(FLAGS.output_proto)
if _OUTPUT_PROTO.value:
print(f'Writing proto to{_OUTPUT_PROTO.value}')
model.ExportToFile(_OUTPUT_PROTO.value)
# Solve model.
solver = cp_model.CpSolver()
if FLAGS.params:
text_format.Parse(FLAGS.params, solver.parameters)
if _PARAMS.value:
text_format.Parse(_PARAMS.value, solver.parameters)
solver.parameters.log_search_progress = True
solver.parameters.exploit_all_precedences = True # Helps with the lower bound.
solver.Solve(model)
def main(argv: Sequence[str]) -> None:
if len(argv) > 1:
raise app.UsageError('Too many command-line arguments.')
if FLAGS.input == '':
if _INPUT.value == '':
raise app.UsageError('Missing input file.')
model = read_model(FLAGS.input)
model = read_model(_INPUT.value)
print_stats(model)
greedy_solution = solve_model_greedily(model)
if FLAGS.model == 'boolean':
if _MODEL.value == 'boolean':
solve_boolean_model(model, greedy_solution)
elif FLAGS.model == 'scheduling':
elif _MODEL.value == 'scheduling':
solve_scheduling_model(model, greedy_solution)

View File

@@ -26,12 +26,11 @@ from absl import flags
from google.protobuf import text_format
from ortools.sat.python import cp_model
FLAGS = flags.FLAGS
flags.DEFINE_string('output_proto', '',
'Output file to write the cp_model proto to.')
flags.DEFINE_string('params', 'num_search_workers:8,log_search_progress:true',
'Sat solver parameters.')
_OUTPUT_PROTO = flags.DEFINE_string(
'output_proto', '', 'Output file to write the cp_model proto to.')
_PARAMS = flags.DEFINE_string('params',
'num_search_workers:8,log_search_progress:true',
'Sat solver parameters.')
def add_neighbor(size, x, y, z, dx, dy, dz, model, index_map, position_to_rank,
@@ -142,7 +141,7 @@ def escape_the_maze(params, output_proto):
def main(argv: Sequence[str]) -> None:
if len(argv) > 1:
raise app.UsageError('Too many command-line arguments.')
escape_the_maze(FLAGS.params, FLAGS.output_proto)
escape_the_maze(_PARAMS.value, _OUTPUT_PROTO.value)
if __name__ == '__main__':

View File

@@ -27,12 +27,11 @@ from absl import flags
from google.protobuf import text_format
from ortools.sat.python import cp_model
FLAGS = flags.FLAGS
flags.DEFINE_string('params', 'num_search_workers:16, max_time_in_seconds:30',
'Sat solver parameters.')
flags.DEFINE_string('proto_file', '',
'If not empty, output the proto to this file.')
_PARAMS = flags.DEFINE_string('params',
'num_search_workers:16, max_time_in_seconds:30',
'Sat solver parameters.')
_PROTO_FILE = flags.DEFINE_string(
'proto_file', '', 'If not empty, output the proto to this file.')
# Recipes
CROISSANT = 'croissant'
@@ -277,8 +276,8 @@ def solve_with_cp_sat(recipes, resources, orders):
# Solve model.
solver = cp_model.CpSolver()
if FLAGS.params:
text_format.Parse(FLAGS.params, solver.parameters)
if _PARAMS.value:
text_format.Parse(_PARAMS.value, solver.parameters)
solver.parameters.log_search_progress = True
status = solver.Solve(model)

View File

@@ -575,6 +575,7 @@ def solve_qubo():
solver = cp_model.CpSolver()
solver.parameters.num_search_workers = 16
solver.parameters.log_search_progress = True
solver.parameters.max_time_in_seconds = 30
solver.Solve(model)

View File

@@ -58,8 +58,8 @@ def add_soft_sequence_constraint(model, works, hard_min, soft_min, min_cost,
soft_max, hard_max, max_cost, prefix):
"""Sequence constraint on true variables with soft and hard bounds.
This constraint looks at every maximal contiguous sequence of variables
assigned to true. It forbids sequence of length < hard_min or > hard_max.
This constraint look at every maximal contiguous sequence of variables
assigned to true. If forbids sequence of length < hard_min or > hard_max.
Then it creates penalty terms if the length is < soft_min or > soft_max.
Args:

View File

@@ -88,3 +88,10 @@ py_test(
requirement("absl-py"),
],
)
py_library(
name = "visualization",
srcs = ["visualization.py"],
visibility = ["//visibility:public"],
)