diff --git a/examples/python/BUILD.bazel b/examples/python/BUILD.bazel index 4a82eb9555..452ad05a6a 100644 --- a/examples/python/BUILD.bazel +++ b/examples/python/BUILD.bazel @@ -54,6 +54,8 @@ code_sample_test_arg_py( suffix = "salbp_20_1", ) +code_sample_py("maximize_combinations_sat") + code_sample_py("maze_escape_sat") code_sample_py("no_wait_baking_scheduling_sat") diff --git a/examples/python/clustering_sat.py b/examples/python/clustering_sat.py index 86754e5cd6..14701b3801 100644 --- a/examples/python/clustering_sat.py +++ b/examples/python/clustering_sat.py @@ -65,7 +65,7 @@ distance_matrix = [ ] -def clustering_sat(): +def clustering_sat() -> None: """Entry point of the program.""" num_nodes = len(distance_matrix) print("Num nodes =", num_nodes) diff --git a/examples/python/cryptarithm_sat.py b/examples/python/cryptarithm_sat.py index 083e89e3d0..39e98e5e0f 100644 --- a/examples/python/cryptarithm_sat.py +++ b/examples/python/cryptarithm_sat.py @@ -19,7 +19,7 @@ from absl import app from ortools.sat.python import cp_model -def send_more_money(): +def send_more_money() -> None: """solve the cryptarithmic puzzle SEND+MORE=MONEY.""" model = cp_model.CpModel() @@ -74,7 +74,7 @@ def send_more_money(): print("y:", solver.value(y)) -def main(_): +def main(_) -> None: send_more_money() diff --git a/examples/python/flexible_job_shop_sat.py b/examples/python/flexible_job_shop_sat.py index b69a9fb0a4..7aa76932f4 100644 --- a/examples/python/flexible_job_shop_sat.py +++ b/examples/python/flexible_job_shop_sat.py @@ -46,7 +46,7 @@ class SolutionPrinter(cp_model.CpSolverSolutionCallback): self.__solution_count += 1 -def flexible_jobshop(): +def flexible_jobshop() -> None: """solve a small flexible jobshop problem.""" # Data part. jobs = [ # task = (processing_time, machine_id) diff --git a/examples/python/gate_scheduling_sat.py b/examples/python/gate_scheduling_sat.py index ecf9d0fc03..6b46dbe41e 100644 --- a/examples/python/gate_scheduling_sat.py +++ b/examples/python/gate_scheduling_sat.py @@ -29,7 +29,7 @@ from ortools.sat.colab import visualization from ortools.sat.python import cp_model -def main(_): +def main(_) -> None: """Solves the gate scheduling problem.""" model = cp_model.CpModel() diff --git a/examples/python/golomb8.py b/examples/python/golomb8.py index c648bf93dd..427f20f293 100755 --- a/examples/python/golomb8.py +++ b/examples/python/golomb8.py @@ -30,7 +30,7 @@ from ortools.constraint_solver import pywrapcp # pylint: disable=g-explicit-bool-comparison -def main(_): +def main(_) -> None: # Create the solver. solver = pywrapcp.Solver("golomb ruler") diff --git a/examples/python/golomb_sat.py b/examples/python/golomb_sat.py index 43ca39f03c..dec429b31d 100644 --- a/examples/python/golomb_sat.py +++ b/examples/python/golomb_sat.py @@ -38,7 +38,7 @@ _PARAMS = flags.DEFINE_string( ) -def solve_golomb_ruler(order: int, params: str): +def solve_golomb_ruler(order: int, params: str) -> None: """Solve the Golomb ruler problem.""" # Create the model. model = cp_model.CpModel() diff --git a/examples/python/hidato_sat.py b/examples/python/hidato_sat.py index cd20c50650..360276f7e4 100755 --- a/examples/python/hidato_sat.py +++ b/examples/python/hidato_sat.py @@ -147,7 +147,7 @@ def build_puzzle(problem: int) -> Union[None, list[list[int]]]: return puzzle -def solve_hidato(puzzle: list[list[int]], index: int): +def solve_hidato(puzzle: list[list[int]], index: int) -> None: """solve the given hidato table.""" # Create the model. model = cp_model.CpModel() diff --git a/examples/python/jobshop_ft06_distance_sat.py b/examples/python/jobshop_ft06_distance_sat.py index 60ce5be641..430c241e47 100755 --- a/examples/python/jobshop_ft06_distance_sat.py +++ b/examples/python/jobshop_ft06_distance_sat.py @@ -36,7 +36,7 @@ def distance_between_jobs(x: int, y: int) -> int: return abs(x - y) -def jobshop_ft06_distance(): +def jobshop_ft06_distance() -> None: """Solves the ft06 jobshop with distances between tasks.""" # Creates the model. model = cp_model.CpModel() diff --git a/examples/python/jobshop_with_maintenance_sat.py b/examples/python/jobshop_with_maintenance_sat.py index 778e826194..e42a4bba5e 100644 --- a/examples/python/jobshop_with_maintenance_sat.py +++ b/examples/python/jobshop_with_maintenance_sat.py @@ -36,7 +36,7 @@ class SolutionPrinter(cp_model.CpSolverSolutionCallback): self.__solution_count += 1 -def jobshop_with_maintenance(): +def jobshop_with_maintenance() -> None: """Solves a jobshop with maintenance on one machine.""" # Create the model. model = cp_model.CpModel() diff --git a/examples/python/knapsack_2d_sat.py b/examples/python/knapsack_2d_sat.py index b9b8521905..9adaa83f52 100644 --- a/examples/python/knapsack_2d_sat.py +++ b/examples/python/knapsack_2d_sat.py @@ -71,7 +71,9 @@ def build_data() -> tuple[pd.Series, int, int]: return (data, max_height, max_width) -def solve_with_duplicate_items(data: pd.Series, max_height: int, max_width: int): +def solve_with_duplicate_items( + data: pd.Series, max_height: int, max_width: int +) -> None: """solve the problem by building 2 items (rotated or not) for each item.""" # Derived data (expanded to individual items). data_widths = data["width"].to_numpy() diff --git a/examples/python/line_balancing_sat.py b/examples/python/line_balancing_sat.py index af97d80a2c..e2db9543e8 100644 --- a/examples/python/line_balancing_sat.py +++ b/examples/python/line_balancing_sat.py @@ -115,7 +115,7 @@ def read_model(filename): return model -def print_stats(model): +def print_stats(model) -> None: print("Model Statistics") for key, value in model.items(): print(f" - {key}: {value}") @@ -183,7 +183,7 @@ def solve_model_greedily(model): return assignment -def solve_boolean_model(model, hint): +def solve_boolean_model(model, hint) -> None: """solve the given model.""" print("Solving using the Boolean model") diff --git a/examples/python/maximize_combinations_sat.py b/examples/python/maximize_combinations_sat.py new file mode 100644 index 0000000000..2667b03b00 --- /dev/null +++ b/examples/python/maximize_combinations_sat.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +# Copyright 2010-2024 Google LLC +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Maximize the number of valid combinations of Boolean variables.""" + +from typing import Sequence +from absl import app + +from ortools.sat.python import cp_model + + +def maximize_combinations_sat() -> None: + """Maximize the number of valid combinations of Boolean variables.""" + model = cp_model.CpModel() + cards: list[cp_model.IntVar] = [ + model.new_bool_var("card1"), + model.new_bool_var("card2"), + model.new_bool_var("card3"), + model.new_bool_var("card4"), + ] + + combos: list[list[cp_model.IntVar]] = [ + [cards[0], cards[1]], + [cards[0], cards[2]], + [cards[1], cards[3]], + [cards[0], cards[2], cards[3]], + ] + + deck_size: int = 3 + model.add(sum(cards) == deck_size) + + valid_combos: list[cp_model.IntVar] = [] + for combination in combos: + is_valid = model.new_bool_var("") + + # All true implies is_valid. + model.add_bool_and(is_valid).only_enforce_if(combination) + + # is_valid implies all true. + for literal in combination: + model.add_implication(is_valid, literal) + valid_combos.append(is_valid) + + model.maximize(sum(valid_combos)) + + solver = cp_model.CpSolver() + solver.parameters.log_search_progress = True + status = solver.solve(model) + + if status == cp_model.OPTIMAL: + print( + "chosen cards:", + [card.name for card in cards if solver.boolean_value(card)], + ) + + +def main(argv: Sequence[str]) -> None: + if len(argv) > 1: + raise app.UsageError("Too many command-line arguments.") + maximize_combinations_sat() + + +if __name__ == "__main__": + app.run(main) diff --git a/examples/python/maze_escape_sat.py b/examples/python/maze_escape_sat.py index 833cdfe926..e4aac1870a 100644 --- a/examples/python/maze_escape_sat.py +++ b/examples/python/maze_escape_sat.py @@ -35,7 +35,9 @@ _PARAMS = flags.DEFINE_string( ) -def add_neighbor(size, x, y, z, dx, dy, dz, model, index_map, position_to_rank, arcs): +def add_neighbor( + size, x, y, z, dx, dy, dz, model, index_map, position_to_rank, arcs +) -> None: """Checks if the neighbor is valid, and adds it to the model.""" if ( x + dx < 0 @@ -55,7 +57,7 @@ def add_neighbor(size, x, y, z, dx, dy, dz, model, index_map, position_to_rank, arcs.append((before_index, after_index, move_literal)) -def escape_the_maze(params, output_proto): +def escape_the_maze(params, output_proto) -> None: """Escapes the maze.""" size = 4 boxes = [(0, 1, 0), (2, 0, 1), (1, 3, 1), (3, 1, 3)] diff --git a/examples/python/no_wait_baking_scheduling_sat.py b/examples/python/no_wait_baking_scheduling_sat.py index 3031aa19f4..1bd1cd775b 100644 --- a/examples/python/no_wait_baking_scheduling_sat.py +++ b/examples/python/no_wait_baking_scheduling_sat.py @@ -21,7 +21,8 @@ We are scheduling a full day of baking: """ import collections -from typing import Sequence +from typing import List, Sequence, Tuple + from absl import app from absl import flags @@ -124,7 +125,7 @@ class Order: self.quantity = quantity -def set_up_data(): +def set_up_data() -> Tuple[List[Recipe], List[Resource], List[Order]]: """Set up the bakery problem data.""" # Recipes. @@ -193,7 +194,9 @@ def set_up_data(): return recipes, resources, orders -def solve_with_cp_sat(recipes, resources, orders): +def solve_with_cp_sat( + recipes: List[Recipe], resources: List[Resource], orders: List[Order] +) -> None: """Build the optimization model, and solve the problem.""" model = cp_model.CpModel() diff --git a/examples/python/pell_equation_sat.py b/examples/python/pell_equation_sat.py index 09d12c50c0..29f3df5467 100644 --- a/examples/python/pell_equation_sat.py +++ b/examples/python/pell_equation_sat.py @@ -25,7 +25,7 @@ _COEFF = flags.DEFINE_integer("coeff", 1, "The Pell equation coefficient.") _MAX_VALUE = flags.DEFINE_integer("max_value", 5000_000, "The maximum value.") -def solve_pell(coeff: int, max_value: int): +def solve_pell(coeff: int, max_value: int) -> None: """Solves Pell's equation x^2 - coeff * y^2 = 1.""" model = cp_model.CpModel() diff --git a/examples/python/spread_robots_sat.py b/examples/python/spread_robots_sat.py index 835e9c23f2..1d7f9d4513 100644 --- a/examples/python/spread_robots_sat.py +++ b/examples/python/spread_robots_sat.py @@ -33,7 +33,7 @@ _PARAMS = flags.DEFINE_string( ) -def spread_robots(num_robots: int, room_size: int, params: str): +def spread_robots(num_robots: int, room_size: int, params: str) -> None: """Optimize robots placement.""" model = cp_model.CpModel() diff --git a/examples/python/steel_mill_slab_sat.py b/examples/python/steel_mill_slab_sat.py index a0f2b43973..bfcb4614d7 100644 --- a/examples/python/steel_mill_slab_sat.py +++ b/examples/python/steel_mill_slab_sat.py @@ -158,7 +158,7 @@ class SteelMillSlabSolutionPrinter(cp_model.CpSolverSolutionCallback): print(line) -def steel_mill_slab(problem, break_symmetries): +def steel_mill_slab(problem, break_symmetries) -> None: """Solves the Steel Mill Slab Problem.""" ### Load problem. (num_slabs, capacities, num_colors, orders) = build_problem(problem) diff --git a/examples/python/sudoku_sat.py b/examples/python/sudoku_sat.py index a6d5fb7f12..664b36bba3 100755 --- a/examples/python/sudoku_sat.py +++ b/examples/python/sudoku_sat.py @@ -17,7 +17,7 @@ from ortools.sat.python import cp_model -def solve_sudoku(): +def solve_sudoku() -> None: """Solves the sudoku problem with the CP-SAT solver.""" # Create the model. model = cp_model.CpModel() diff --git a/examples/python/task_allocation_sat.py b/examples/python/task_allocation_sat.py index f75d190674..8026a71c1d 100644 --- a/examples/python/task_allocation_sat.py +++ b/examples/python/task_allocation_sat.py @@ -23,7 +23,7 @@ from absl import app from ortools.sat.python import cp_model -def task_allocation_sat(): +def task_allocation_sat() -> None: """Solves the task allocation problem.""" # Availability matrix. available = [ diff --git a/examples/python/tasks_and_workers_assignment_sat.py b/examples/python/tasks_and_workers_assignment_sat.py index 0a3631c470..2675bb1a4f 100644 --- a/examples/python/tasks_and_workers_assignment_sat.py +++ b/examples/python/tasks_and_workers_assignment_sat.py @@ -34,7 +34,7 @@ class ObjectivePrinter(cp_model.CpSolverSolutionCallback): self.__solution_count += 1 -def tasks_and_workers_assignment_sat(): +def tasks_and_workers_assignment_sat() -> None: """solve the assignment problem.""" model = cp_model.CpModel() diff --git a/examples/python/vendor_scheduling_sat.py b/examples/python/vendor_scheduling_sat.py index f8c9912373..6f7c9b377a 100644 --- a/examples/python/vendor_scheduling_sat.py +++ b/examples/python/vendor_scheduling_sat.py @@ -63,7 +63,7 @@ class SolutionPrinter(cp_model.CpSolverSolutionCallback): return self.__solution_count -def vendor_scheduling_sat(): +def vendor_scheduling_sat() -> None: """Create the shift scheduling model and solve it.""" # Create the model. model = cp_model.CpModel() diff --git a/examples/python/wedding_optimal_chart_sat.py b/examples/python/wedding_optimal_chart_sat.py index f86471a78b..df7419154c 100644 --- a/examples/python/wedding_optimal_chart_sat.py +++ b/examples/python/wedding_optimal_chart_sat.py @@ -130,7 +130,7 @@ def build_data(): return num_tables, table_capacity, min_known_neighbors, connections, names -def solve_with_discrete_model(): +def solve_with_discrete_model() -> None: """Discrete approach.""" num_tables, table_capacity, min_known_neighbors, connections, names = build_data() diff --git a/examples/python/weighted_latency_problem_sat.py b/examples/python/weighted_latency_problem_sat.py index 73758fb32e..3e66b61b6e 100644 --- a/examples/python/weighted_latency_problem_sat.py +++ b/examples/python/weighted_latency_problem_sat.py @@ -55,7 +55,7 @@ def build_model(): return x, y, profits -def solve_with_cp_sat(x, y, profits): +def solve_with_cp_sat(x, y, profits) -> None: """Solves the problem with the CP-SAT solver.""" model = cp_model.CpModel()