From f0d0fe71ba21ba4343a15992e64b4e6b55ad9bbf Mon Sep 17 00:00:00 2001 From: Corentin Le Molgat Date: Wed, 23 Jul 2025 17:38:49 +0200 Subject: [PATCH] examples: backport from main --- examples/contrib/permutation_flow_shop.py | 3 +- .../scheduling_with_transitions_sat.py | 3 +- examples/python/BUILD.bazel | 2 + examples/python/appointments.py | 2 +- examples/python/arc_flow_cutting_stock_sat.py | 3 +- examples/python/balance_group_sat.py | 1 + examples/python/bus_driver_scheduling_sat.py | 12 +- .../python/car_sequencing_optimization_sat.py | 271 ++++++++++++++++++ examples/python/cryptarithm_sat.py | 3 +- examples/python/golomb8.py | 2 +- examples/python/golomb_sat.py | 4 +- examples/python/knapsack_2d_sat.py | 8 +- examples/python/line_balancing_sat.py | 6 +- examples/python/linear_assignment_api.py | 13 +- examples/python/maximize_combinations_sat.py | 1 + examples/python/maze_escape_sat.py | 4 +- .../memory_layout_and_infeasibility_sat.py | 5 +- .../python/no_wait_baking_scheduling_sat.py | 3 +- examples/python/nqueens_sat.py | 1 + examples/python/pell_equation_sat.py | 1 + examples/python/pentominoes_sat.py | 3 +- examples/python/prize_collecting_vrp.py | 2 + examples/python/pyflow_example.py | 7 +- examples/python/rcpsp_sat.py | 3 +- examples/python/shift_scheduling_sat.py | 3 +- ...duling_with_setup_release_due_dates_sat.py | 3 +- examples/python/spread_robots_sat.py | 4 +- examples/python/steel_mill_slab_sat.py | 8 +- examples/python/test_scheduling_sat.py | 3 +- examples/python/testdata/BUILD.bazel | 6 +- examples/python/transit_time.py | 1 - .../python/weighted_latency_problem_sat.py | 8 +- 32 files changed, 335 insertions(+), 64 deletions(-) create mode 100644 examples/python/car_sequencing_optimization_sat.py diff --git a/examples/contrib/permutation_flow_shop.py b/examples/contrib/permutation_flow_shop.py index 505ce7dbfd..738d314399 100644 --- a/examples/contrib/permutation_flow_shop.py +++ b/examples/contrib/permutation_flow_shop.py @@ -27,7 +27,6 @@ import numpy as np from absl import app from absl import flags -from google.protobuf import text_format from ortools.sat.python import cp_model _PARAMS = flags.DEFINE_string( @@ -149,7 +148,7 @@ def permutation_flow_shop( solver = cp_model.CpSolver() if params: - text_format.Parse(params, solver.parameters) + solver.parameters.parse_text_format(params) solver.parameters.log_search_progress = log solver.parameters.max_time_in_seconds = time_limit diff --git a/examples/contrib/scheduling_with_transitions_sat.py b/examples/contrib/scheduling_with_transitions_sat.py index cbaaf78b94..abf4461a65 100644 --- a/examples/contrib/scheduling_with_transitions_sat.py +++ b/examples/contrib/scheduling_with_transitions_sat.py @@ -9,7 +9,6 @@ import argparse import collections from ortools.sat.python import cp_model -from google.protobuf import text_format #---------------------------------------------------------------------------- # Command line arguments. @@ -295,7 +294,7 @@ def main(args): solver = cp_model.CpSolver() solver.parameters.max_time_in_seconds = 60 * 60 * 2 if parameters: - text_format.Merge(parameters, solver.parameters) + solver.parameters.merge_text_format(parameters) solution_printer = SolutionPrinter(makespan) status = solver.Solve(model, solution_printer) diff --git a/examples/python/BUILD.bazel b/examples/python/BUILD.bazel index b1989553a9..632e68bbb6 100644 --- a/examples/python/BUILD.bazel +++ b/examples/python/BUILD.bazel @@ -23,6 +23,8 @@ code_sample_py("balance_group_sat") code_sample_py("bus_driver_scheduling_sat") +code_sample_py("car_sequencing_optimization_sat") + code_sample_py("chemical_balance_sat") code_sample_py("clustering_sat") diff --git a/examples/python/appointments.py b/examples/python/appointments.py index 328ac691f0..65fbc66031 100644 --- a/examples/python/appointments.py +++ b/examples/python/appointments.py @@ -180,7 +180,7 @@ def aggregate_item_collections_optimally( def get_optimal_schedule( - demand: list[tuple[float, str, int]] + demand: list[tuple[float, str, int]], ) -> list[tuple[int, list[tuple[int, str]]]]: """Computes the optimal schedule for the installation input. diff --git a/examples/python/arc_flow_cutting_stock_sat.py b/examples/python/arc_flow_cutting_stock_sat.py index 9b70f2c2ca..2cad61f567 100644 --- a/examples/python/arc_flow_cutting_stock_sat.py +++ b/examples/python/arc_flow_cutting_stock_sat.py @@ -21,7 +21,6 @@ from absl import app from absl import flags import numpy as np -from google.protobuf import text_format from ortools.linear_solver.python import model_builder as mb from ortools.sat.python import cp_model @@ -319,7 +318,7 @@ def solve_cutting_stock_with_arc_flow_and_sat(output_proto_file: str, params: st # Solve model. solver = cp_model.CpSolver() if params: - text_format.Parse(params, solver.parameters) + solver.parameters.parse_text_format(params) solver.parameters.log_search_progress = True solver.Solve(model) diff --git a/examples/python/balance_group_sat.py b/examples/python/balance_group_sat.py index 5f37f95605..10d0725075 100644 --- a/examples/python/balance_group_sat.py +++ b/examples/python/balance_group_sat.py @@ -19,6 +19,7 @@ be as close to the average as possible. Furthermore, if one color is an a group, at least k items with this color must be in that group. """ + from typing import Dict, Sequence from absl import app diff --git a/examples/python/bus_driver_scheduling_sat.py b/examples/python/bus_driver_scheduling_sat.py index 0a957febd2..73a1e55217 100644 --- a/examples/python/bus_driver_scheduling_sat.py +++ b/examples/python/bus_driver_scheduling_sat.py @@ -29,7 +29,7 @@ import math from absl import app from absl import flags -from google.protobuf import text_format + from ortools.sat.python import cp_model _OUTPUT_PROTO = flags.DEFINE_string( @@ -81,7 +81,7 @@ SAMPLE_SHIFTS_TINY = [ [25, "15:40", "15:56", 940, 956, 16], [26, "15:58", "16:45", 958, 1005, 47], [27, "16:04", "17:30", 964, 1050, 86], -] # yapf:disable +] SAMPLE_SHIFTS_SMALL = [ # @@ -143,7 +143,7 @@ SAMPLE_SHIFTS_SMALL = [ [47, "18:34", "19:58", 1114, 1198, 84], [48, "19:56", "20:34", 1196, 1234, 38], [49, "20:05", "20:48", 1205, 1248, 43], -] # yapf:disable +] SAMPLE_SHIFTS_MEDIUM = [ [0, "04:30", "04:53", 270, 293, 23], @@ -346,7 +346,7 @@ SAMPLE_SHIFTS_MEDIUM = [ [197, "00:02", "00:12", 1442, 1452, 10], [198, "00:07", "00:39", 1447, 1479, 32], [199, "00:25", "01:12", 1465, 1512, 47], -] # yapf:disable +] SAMPLE_SHIFTS_LARGE = [ [0, "04:18", "05:00", 258, 300, 42], @@ -1705,7 +1705,7 @@ SAMPLE_SHIFTS_LARGE = [ [1353, "00:47", "01:26", 1487, 1526, 39], [1354, "00:54", "01:04", 1494, 1504, 10], [1355, "00:57", "01:07", 1497, 1507, 10], -] # yapf:disable +] def bus_driver_scheduling(minimize_drivers: bool, max_num_drivers: int) -> int: @@ -1981,7 +1981,7 @@ def bus_driver_scheduling(minimize_drivers: bool, max_num_drivers: int) -> int: # Solve model. solver = cp_model.CpSolver() if _PARAMS.value: - text_format.Parse(_PARAMS.value, solver.parameters) + solver.parameters.parse_text_format(_PARAMS.value) status = solver.solve(model) diff --git a/examples/python/car_sequencing_optimization_sat.py b/examples/python/car_sequencing_optimization_sat.py new file mode 100644 index 0000000000..81e030d7fc --- /dev/null +++ b/examples/python/car_sequencing_optimization_sat.py @@ -0,0 +1,271 @@ +#!/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. + +"""Solve the car sequencing problem as an optimization problem. + +Problem Description: The Car Sequencing Problem with Optimization +----------------------------------------------------------------- + +See https://en.wikipedia.org/wiki/Car_sequencing_problem for more details. + +We are tasked with determining the optimal production sequence for a set of cars +on an assembly line. This is a classic and challenging combinatorial +optimization problem with the following characteristics: + +Fixed Production Demand: There is a specific, non-negotiable number of cars of +different types (or 'classes') that must be produced. In our case, we have 6 +distinct classes of cars, and we must produce exactly 5 of each, for a total of +30 'real' cars. + +Diverse Car Configurations: Each car class is defined by a unique combination of +optional features. For example, 'Class 1' might require a sunroof (Option 1) and +a special engine (Option 4), while 'Class 3' only requires air conditioning +(Option 2). + +Specialized Assembly Stations: The assembly line is composed of a series of +specialized stations. Each station is responsible for installing one specific +option. For example, there is one station for sunroofs, one for special engines, +and so on. + +Capacity-Limited Stations: The core challenge of the problem lies here. The +stations cannot handle an unlimited, dense flow of cars requiring their specific +option. Their capacity is defined by a 'sliding window' constraint. For example, +the sunroof station might have a constraint of 'at most 1 car with a sunroof in +any sequence of 3 consecutive cars'. This means sequences like [Sunroof, No, No, +Sunroof] are valid, but [Sunroof, No, Sunroof, No] are not. + +The Need for Spacing (Optimization): The combination of high demand for certain +options and tight capacity constraints may make it impossible to produce the 30 +real cars consecutively. To create a valid sequence, we may need to insert +'dummy' or 'filler' cars into the production line. These dummy cars have no +options and therefore do not consume capacity at any station. They serve purely +as spacers to break up dense sequences of option-heavy cars. + +The Goal: The objective is to find a production sequence that fulfills the +demand for all 30 real cars while using the minimum number of dummy cars. This +is equivalent to finding the shortest possible total production schedule (real +cars + dummy cars). + +Modeling and Solution Approach with CP-SAT +------------------------------------------ + +To solve this problem, we use the CP-SAT solver from Google's OR-Tools library. +This is a constraint programming approach, which works by defining variables, +constraints, and an objective function. + +1. Decision Variables +The fundamental decision the solver must make is: 'Which class of car should be +placed in each production slot?' +We define a large number of boolean variables: produces[c][s]. This variable is +True if a car of class c is scheduled in slot s, and False otherwise. We create +these for all car classes (including the dummy class) and for an extended number +of slots (30 real + a buffer of 20 for dummies). +We introduce a key integer variable: makespan. This variable represents the +total length of the 'meaningful' part of our schedule. It's the slot number +where the first dummy car appears, after which all subsequent cars are also +dummies. + +2. Constraints (The Rules of the Game) +We translate the problem's rules into mathematical constraints that the solver +must obey: + +One Car Per Slot: For every production slot s, exactly one car class can be +assigned. We enforce this using an AddExactlyOne constraint over all +produces[c][s] variables for that slot. + +Fulfill Real Car Demand: The total number of times each real car class c appears +across all slots must equal its required demand (5 in our case). This is a +simple Add(sum(...) == 5) constraint. + +Station Capacity (Sliding Window): This is the most critical constraint. For +each option (e.g., 'sunroof') and its capacity rule (e.g., '1 in 3'), we create +constraints for every possible sliding window. For every subsequence of 3 slots, +we sum up the produces variables corresponding to car classes that require that +option and constrain this sum to be less than or equal to 1. + +Makespan Definition: This is the clever part of the model. We link our makespan +objective variable to the placement of dummy cars using logical equivalences for +each slot s: +(makespan <= s) is equivalent to (slot s contains a dummy car) +This ensures that if the solver chooses a makespan of 32, for example, it is +forced to place dummy cars in slots 32, 33, 34, and so on. Conversely, if the +solver is forced to place a dummy car in slot 32 to satisfy a capacity +constraint, the makespan must be at most 32. + +3. The Objective Function + +The objective is simple and directly tied to our goal: + +Minimize makespan: By instructing the solver to find a solution with the +smallest possible value for the makespan variable, we are asking it to find the +shortest possible production schedule that satisfies all the rules. This +inherently minimizes the number of dummy cars used. + +By defining the problem in this way, we let the CP-SAT solver explore the vast +search space of possible sequences efficiently, using its powerful constraint +propagation and search techniques to find an optimal arrangement that meets all +our complex requirements. +""" + +from collections.abc import Sequence + +from absl import app + +from ortools.sat.python import cp_model + + +def solve_car_sequencing_optimization() -> None: + """Solves the car sequencing problem with an optimization approach.""" + + # -------------------- + # 1. Data + # -------------------- + num_real_cars: int = 30 + max_dummy_cars: int = 20 + num_slots = num_real_cars + max_dummy_cars + all_slots = range(num_slots) + + class_options = [ + # Options: 1 2 3 4 5 + [0, 0, 0, 0, 0], # Class 0 (Dummy) + [1, 0, 0, 1, 0], # Class 1 + [0, 1, 0, 0, 1], # Class 2 + [0, 1, 0, 0, 0], # Class 3 + [0, 0, 1, 1, 0], # Class 4 + [0, 0, 1, 0, 0], # Class 5 + [0, 0, 0, 0, 1], # Class 6 + ] + num_classes = len(class_options) + all_classes = range(num_classes) + real_classes = range(1, num_classes) + dummy_class = 0 + + demands = [5, 5, 5, 5, 5, 5] + + capacity_constraints = [(1, 3), (1, 2), (1, 3), (2, 5), (1, 5)] + num_options = len(capacity_constraints) + all_options = range(num_options) + + classes_with_option = [ + [c for c in real_classes if class_options[c][o] == 1] for o in all_options + ] + + # -------------------- + # 2. Model Creation + # -------------------- + model = cp_model.CpModel() + + # -------------------- + # 3. Decision Variables + # -------------------- + produces = {} + for c in all_classes: + for s in all_slots: + produces[(c, s)] = model.new_bool_var(f"produces_c{c}_s{s}") + + makespan = model.new_int_var(num_real_cars, num_slots, "makespan") + + # -------------------- + # 4. Constraints + # -------------------- + + # Constraint 1: Only one car produced per slot. + for s in all_slots: + model.add_exactly_one([produces[(c, s)] for c in all_classes]) + + # Constraint 2: Meet the demand of real cars. + for i, c in enumerate(real_classes): + model.add(sum(produces[(c, s)] for s in all_slots) == demands[i]) + + # Constraint 3: Enforce the capacity constraints on options. + for o in all_options: + max_cars, subsequence_len = capacity_constraints[o] + for start in range(num_slots - subsequence_len + 1): + window = range(start, start + subsequence_len) + cars_with_option_in_window = [] + for c in classes_with_option[o]: + for s in window: + cars_with_option_in_window.append(produces[(c, s)]) + model.add(sum(cars_with_option_in_window) <= max_cars) + + # Constraint 4 (Link objective and dummy cars at the end of the schedule) + for s in all_slots: + makespan_le_s = model.new_bool_var(f"makespan_le_{s}") + + # Enforce makespan_le_s <=> (makespan <= s) + model.add(makespan <= s).only_enforce_if(makespan_le_s) + # Use ~ for negation + model.add(makespan > s).only_enforce_if(~makespan_le_s) + + # Enforce makespan_le_s => produces[dummy_class, s] + model.add_implication(makespan_le_s, produces[dummy_class, s]) + + # -------------------- + # 5. Objective + # -------------------- + model.minimize(makespan) + + # -------------------- + # 6. Solve and Print Solution + # -------------------- + solver = cp_model.CpSolver() + solver.parameters.max_time_in_seconds = 30.0 + solver.parameters.num_search_workers = 1 # The problem is easy to solve. + # solver.parameters.log_search_progress = True # uncomment to see the log. + + status = solver.Solve(model) + + if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE: + final_makespan = int(solver.ObjectiveValue()) + num_dummies_needed = final_makespan - num_real_cars + + print( + f'\n{"Optimal" if status == cp_model.OPTIMAL else "Feasible"}' + f" solution found with a makespan of {final_makespan}." + ) + print( + f"This requires the conceptual equivalent of {num_dummies_needed} dummy" + " car(s) to be used as spacers." + ) + + sequence = [-1] * num_slots + for s in all_slots: + for c in all_classes: + if solver.Value(produces[(c, s)]) == 1: + sequence[s] = c + break + + print("\nFull Production Sequence (Class 0 is dummy):") + print("Slot: | " + " | ".join(f"{i:2}" for i in range(num_slots)) + " |") + print("-------|-" + "--|-" * num_slots) + print("Class: | " + " | ".join(f"{c:2}" for c in sequence) + " |") + + elif status == cp_model.INFEASIBLE: + print("\nNo solution found.") + + else: + print(f"\nSomething went wrong. Solver status: {status}") + + print("\nSolver statistics:") + print(solver.response_stats()) + + +def main(argv: Sequence[str]) -> None: + if len(argv) > 1: + raise app.UsageError("Too many command-line arguments.") + solve_car_sequencing_optimization() + + +if __name__ == "__main__": + app.run(main) diff --git a/examples/python/cryptarithm_sat.py b/examples/python/cryptarithm_sat.py index c4e49e0873..9b28dbc97b 100644 --- a/examples/python/cryptarithm_sat.py +++ b/examples/python/cryptarithm_sat.py @@ -12,8 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Use CP-SAT to solve a simple cryptarithmetic problem: SEND+MORE=MONEY. -""" +"""Use CP-SAT to solve a simple cryptarithmetic problem: SEND+MORE=MONEY.""" from absl import app from ortools.sat.python import cp_model diff --git a/examples/python/golomb8.py b/examples/python/golomb8.py index cb2a2423ca..ec720e3191 100755 --- a/examples/python/golomb8.py +++ b/examples/python/golomb8.py @@ -69,7 +69,7 @@ def main(_) -> None: branches = collector.Branches(i) failures = collector.Failures(i) print( - ("Solution #%i: value = %i, failures = %i, branches = %i," "time = %i ms") + "Solution #%i: value = %i, failures = %i, branches = %i,time = %i ms" % (i, obj_value, failures, branches, time) ) time = solver.WallTime() diff --git a/examples/python/golomb_sat.py b/examples/python/golomb_sat.py index 6b4e19cc06..f1451314a4 100644 --- a/examples/python/golomb_sat.py +++ b/examples/python/golomb_sat.py @@ -24,10 +24,10 @@ see: https://en.wikipedia.org/wiki/Golomb_ruler """ 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 _ORDER = flags.DEFINE_integer("order", 8, "Order of the ruler.") @@ -70,7 +70,7 @@ def solve_golomb_ruler(order: int, params: str) -> None: # Solve the model. solver = cp_model.CpSolver() if params: - text_format.Parse(params, solver.parameters) + solver.parameters.parse_text_format(params) solution_printer = cp_model.ObjectiveSolutionPrinter() print(f"Golomb ruler(order={order})") status = solver.solve(model, solution_printer) diff --git a/examples/python/knapsack_2d_sat.py b/examples/python/knapsack_2d_sat.py index e771821552..e66457a7ce 100644 --- a/examples/python/knapsack_2d_sat.py +++ b/examples/python/knapsack_2d_sat.py @@ -25,8 +25,6 @@ from absl import flags import numpy as np import pandas as pd -from google.protobuf import text_format - from ortools.sat.python import cp_model @@ -159,7 +157,7 @@ def solve_with_duplicate_items( # Solve model. solver = cp_model.CpSolver() if _PARAMS.value: - text_format.Parse(_PARAMS.value, solver.parameters) + solver.parameters.parse_text_format(_PARAMS.value) status = solver.solve(model) @@ -261,7 +259,7 @@ def solve_with_duplicate_optional_items( # solve model. solver = cp_model.CpSolver() if _PARAMS.value: - text_format.Parse(_PARAMS.value, solver.parameters) + solver.parameters.parse_text_format(_PARAMS.value) status = solver.solve(model) @@ -382,7 +380,7 @@ def solve_with_rotations(data: pd.Series, max_height: int, max_width: int): # solve model. solver = cp_model.CpSolver() if _PARAMS.value: - text_format.Parse(_PARAMS.value, solver.parameters) + solver.parameters.parse_text_format(_PARAMS.value) status = solver.solve(model) diff --git a/examples/python/line_balancing_sat.py b/examples/python/line_balancing_sat.py index c80a747d3a..064884a64c 100644 --- a/examples/python/line_balancing_sat.py +++ b/examples/python/line_balancing_sat.py @@ -33,7 +33,7 @@ from typing import Dict, Sequence from absl import app from absl import flags -from google.protobuf import text_format + from ortools.sat.python import cp_model @@ -272,7 +272,7 @@ def solve_problem_with_boolean_model( # solve model. solver = cp_model.CpSolver() if _PARAMS.value: - text_format.Parse(_PARAMS.value, solver.parameters) + solver.parameters.parse_text_format(_PARAMS.value) solver.parameters.log_search_progress = True solver.solve(model) @@ -339,7 +339,7 @@ def solve_problem_with_scheduling_model( # solve model. solver = cp_model.CpSolver() if _PARAMS.value: - text_format.Parse(_PARAMS.value, solver.parameters) + solver.parameters.parse_text_format(_PARAMS.value) solver.parameters.log_search_progress = True solver.solve(model) diff --git a/examples/python/linear_assignment_api.py b/examples/python/linear_assignment_api.py index 34514974e4..64c4de0532 100644 --- a/examples/python/linear_assignment_api.py +++ b/examples/python/linear_assignment_api.py @@ -14,9 +14,9 @@ """Test linear sum assignment on a 4x4 matrix. - Example taken from: - http://www.ee.oulu.fi/~mpa/matreng/eem1_2-1.htm with kCost[0][1] - modified so the optimum solution is unique. +Example taken from: +http://www.ee.oulu.fi/~mpa/matreng/eem1_2-1.htm with kCost[0][1] +modified so the optimum solution is unique. """ from typing import Sequence @@ -28,7 +28,12 @@ def run_assignment_on_4x4_matrix(): """Test linear sum assignment on a 4x4 matrix.""" num_sources = 4 num_targets = 4 - cost = [[90, 76, 75, 80], [35, 85, 55, 65], [125, 95, 90, 105], [45, 110, 95, 115]] + cost = [ + [90, 76, 75, 80], + [35, 85, 55, 65], + [125, 95, 90, 105], + [45, 110, 95, 115], + ] expected_cost = cost[0][3] + cost[1][2] + cost[2][1] + cost[3][0] assignment = linear_sum_assignment.SimpleLinearSumAssignment() diff --git a/examples/python/maximize_combinations_sat.py b/examples/python/maximize_combinations_sat.py index a23e90d384..853107deaf 100644 --- a/examples/python/maximize_combinations_sat.py +++ b/examples/python/maximize_combinations_sat.py @@ -15,6 +15,7 @@ """Maximize the number of valid combinations of Boolean variables.""" from typing import Sequence + from absl import app from ortools.sat.python import cp_model diff --git a/examples/python/maze_escape_sat.py b/examples/python/maze_escape_sat.py index 7a96e453d8..30d2cf5042 100644 --- a/examples/python/maze_escape_sat.py +++ b/examples/python/maze_escape_sat.py @@ -20,12 +20,12 @@ visit all boxes in order, and walk on each block in a 4x4x4 map exactly once. Admissible moves are one step in one of the 6 directions: x+, x-, y+, y-, z+(up), z-(down) """ + from typing import Dict, Sequence, Tuple from absl import app from absl import flags -from google.protobuf import text_format from ortools.sat.python import cp_model _OUTPUT_PROTO = flags.DEFINE_string( @@ -140,7 +140,7 @@ def escape_the_maze(params: str, output_proto: str) -> None: # Solve model. solver = cp_model.CpSolver() if params: - text_format.Parse(params, solver.parameters) + solver.parameters.parse_text_format(params) solver.parameters.log_search_progress = True result = solver.solve(model) diff --git a/examples/python/memory_layout_and_infeasibility_sat.py b/examples/python/memory_layout_and_infeasibility_sat.py index 9956700c22..77b11e82c5 100644 --- a/examples/python/memory_layout_and_infeasibility_sat.py +++ b/examples/python/memory_layout_and_infeasibility_sat.py @@ -20,7 +20,6 @@ from typing import List from absl import app from absl import flags -from google.protobuf import text_format from ortools.sat.python import cp_model @@ -72,7 +71,7 @@ def solve_hard_model(output_proto: str, params: str) -> bool: solver = cp_model.CpSolver() if params: - text_format.Parse(params, solver.parameters) + solver.parameters.parse_text_format(params) status = solver.solve(model) print(solver.response_stats()) @@ -158,7 +157,7 @@ def solve_soft_model_with_maximization(params: str) -> None: solver = cp_model.CpSolver() if params: - text_format.Parse(params, solver.parameters) + solver.parameters.parse_text_format(params) status = solver.solve(model) print(solver.response_stats()) if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE: diff --git a/examples/python/no_wait_baking_scheduling_sat.py b/examples/python/no_wait_baking_scheduling_sat.py index 9c81d75674..56b869f1f8 100644 --- a/examples/python/no_wait_baking_scheduling_sat.py +++ b/examples/python/no_wait_baking_scheduling_sat.py @@ -26,7 +26,6 @@ from typing import List, Sequence, Tuple from absl import app from absl import flags -from google.protobuf import text_format from ortools.sat.python import cp_model _PARAMS = flags.DEFINE_string( @@ -287,7 +286,7 @@ def solve_with_cp_sat( # Solve model. solver = cp_model.CpSolver() if _PARAMS.value: - text_format.Parse(_PARAMS.value, solver.parameters) + solver.parameters.parse_text_format(_PARAMS.value) solver.parameters.log_search_progress = True status = solver.solve(model) diff --git a/examples/python/nqueens_sat.py b/examples/python/nqueens_sat.py index e2f29542a5..6fd102dcdc 100644 --- a/examples/python/nqueens_sat.py +++ b/examples/python/nqueens_sat.py @@ -18,6 +18,7 @@ import time from absl import app from absl import flags + from ortools.sat.python import cp_model _SIZE = flags.DEFINE_integer("size", 8, "Number of queens.") diff --git a/examples/python/pell_equation_sat.py b/examples/python/pell_equation_sat.py index 3583c5557d..75554e6a40 100644 --- a/examples/python/pell_equation_sat.py +++ b/examples/python/pell_equation_sat.py @@ -18,6 +18,7 @@ from collections.abc import Sequence from absl import app from absl import flags + from ortools.sat.python import cp_model diff --git a/examples/python/pentominoes_sat.py b/examples/python/pentominoes_sat.py index 01479ec6ac..4e041d3272 100644 --- a/examples/python/pentominoes_sat.py +++ b/examples/python/pentominoes_sat.py @@ -31,7 +31,6 @@ from typing import Dict, List from absl import app from absl import flags -from google.protobuf import text_format from ortools.sat.python import cp_model @@ -144,7 +143,7 @@ def generate_and_solve_problem(pieces: Dict[str, List[List[int]]]) -> None: # Solve the model. solver = cp_model.CpSolver() if _PARAMS.value: - text_format.Parse(_PARAMS.value, solver.parameters) + solver.parameters.parse_text_format(_PARAMS.value) status = solver.solve(model) print( diff --git a/examples/python/prize_collecting_vrp.py b/examples/python/prize_collecting_vrp.py index 493f763e83..0883a1412e 100755 --- a/examples/python/prize_collecting_vrp.py +++ b/examples/python/prize_collecting_vrp.py @@ -82,6 +82,8 @@ def print_solution(manager, routing, assignment): total_distance = 0 total_value_collected = 0 for v in range(manager.GetNumberOfVehicles()): + if not routing.IsVehicleUsed(assignment, v): + continue index = routing.Start(v) plan_output = f'Route for vehicle {v}:\n' route_distance = 0 diff --git a/examples/python/pyflow_example.py b/examples/python/pyflow_example.py index 55db850530..44f0498d4c 100644 --- a/examples/python/pyflow_example.py +++ b/examples/python/pyflow_example.py @@ -52,7 +52,12 @@ def min_cost_flow_api(): print("MinCostFlow on 4x4 matrix.") num_sources = 4 num_targets = 4 - costs = [[90, 75, 75, 80], [35, 85, 55, 65], [125, 95, 90, 105], [45, 110, 95, 115]] + costs = [ + [90, 75, 75, 80], + [35, 85, 55, 65], + [125, 95, 90, 105], + [45, 110, 95, 115], + ] expected_cost = 275 smcf = min_cost_flow.SimpleMinCostFlow() for source in range(0, num_sources): diff --git a/examples/python/rcpsp_sat.py b/examples/python/rcpsp_sat.py index 2b78e3d049..6d413d4057 100644 --- a/examples/python/rcpsp_sat.py +++ b/examples/python/rcpsp_sat.py @@ -26,7 +26,6 @@ import collections from absl import app from absl import flags -from google.protobuf import text_format from ortools.sat.python import cp_model from ortools.scheduling import rcpsp_pb2 from ortools.scheduling.python import rcpsp @@ -361,7 +360,7 @@ def solve_rcpsp( # Parse user specified parameters. if params: - text_format.Parse(params, solver.parameters) + solver.parameters.parse_text_format(params) # Favor objective_shaving over objective_lb_search. if solver.parameters.num_workers >= 16 and solver.parameters.num_workers < 24: diff --git a/examples/python/shift_scheduling_sat.py b/examples/python/shift_scheduling_sat.py index a81c083991..9883124176 100644 --- a/examples/python/shift_scheduling_sat.py +++ b/examples/python/shift_scheduling_sat.py @@ -17,7 +17,6 @@ from absl import app from absl import flags -from google.protobuf import text_format from ortools.sat.python import cp_model _OUTPUT_PROTO = flags.DEFINE_string( @@ -410,7 +409,7 @@ def solve_shift_scheduling(params: str, output_proto: str): # Solve the model. solver = cp_model.CpSolver() if params: - text_format.Parse(params, solver.parameters) + solver.parameters.parse_text_format(params) solution_printer = cp_model.ObjectiveSolutionPrinter() status = solver.solve(model, solution_printer) diff --git a/examples/python/single_machine_scheduling_with_setup_release_due_dates_sat.py b/examples/python/single_machine_scheduling_with_setup_release_due_dates_sat.py index c54a67d26a..d1d2039b86 100644 --- a/examples/python/single_machine_scheduling_with_setup_release_due_dates_sat.py +++ b/examples/python/single_machine_scheduling_with_setup_release_due_dates_sat.py @@ -17,7 +17,6 @@ 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 # ---------------------------------------------------------------------------- @@ -498,7 +497,7 @@ def single_machine_scheduling(): # Solve. solver = cp_model.CpSolver() if parameters: - text_format.Parse(parameters, solver.parameters) + solver.parameters.parse_text_format(parameters) solution_printer = SolutionPrinter() solver.best_bound_callback = lambda a: print(f"New objective lower bound: {a}") solver.solve(model, solution_printer) diff --git a/examples/python/spread_robots_sat.py b/examples/python/spread_robots_sat.py index 27da1d65b9..476de60c68 100644 --- a/examples/python/spread_robots_sat.py +++ b/examples/python/spread_robots_sat.py @@ -18,8 +18,6 @@ import math 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 _NUM_ROBOTS = flags.DEFINE_integer("num_robots", 8, "Number of robots to place.") @@ -94,7 +92,7 @@ def spread_robots(num_robots: int, room_size: int, params: str) -> None: # Creates a solver and solves the model. solver = cp_model.CpSolver() if params: - text_format.Parse(params, solver.parameters) + solver.parameters.parse_text_format(params) solver.parameters.log_search_progress = True status = solver.solve(model) diff --git a/examples/python/steel_mill_slab_sat.py b/examples/python/steel_mill_slab_sat.py index 6f79d85fbe..6be2e85287 100644 --- a/examples/python/steel_mill_slab_sat.py +++ b/examples/python/steel_mill_slab_sat.py @@ -21,7 +21,7 @@ import time from absl import app from absl import flags -from google.protobuf import text_format + from ortools.sat.python import cp_model @@ -293,7 +293,7 @@ def steel_mill_slab(problem_id: int, break_symmetries: bool) -> None: ### Solve model. solver = cp_model.CpSolver() if _PARAMS.value: - text_format.Parse(_PARAMS.value, solver.parameters) + solver.parameters.parse_text_format(_PARAMS.value) objective_printer = cp_model.ObjectiveSolutionPrinter() status = solver.solve(model, objective_printer) @@ -477,7 +477,7 @@ def steel_mill_slab_with_valid_slabs(problem_id: int, break_symmetries: bool) -> ### Solve model. solver = cp_model.CpSolver() if _PARAMS.value: - text_format.Parse(_PARAMS.value, solver.parameters) + solver.parameters.parse_text_format(_PARAMS.value) solution_printer = SteelMillSlabSolutionPrinter(orders, assign, loads, losses) status = solver.solve(model, solution_printer) @@ -547,7 +547,7 @@ def steel_mill_slab_with_column_generation(problem_id: int) -> None: ### Solve model. solver = cp_model.CpSolver() if _PARAMS.value: - text_format.Parse(_PARAMS.value, solver.parameters) + solver.parameters.parse_text_format(_PARAMS.value) solution_printer = cp_model.ObjectiveSolutionPrinter() status = solver.solve(model, solution_printer) diff --git a/examples/python/test_scheduling_sat.py b/examples/python/test_scheduling_sat.py index fdec2ba13b..e3feb1cfed 100644 --- a/examples/python/test_scheduling_sat.py +++ b/examples/python/test_scheduling_sat.py @@ -33,7 +33,6 @@ from absl import app from absl import flags import pandas as pd -from google.protobuf import text_format from ortools.sat.python import cp_model @@ -141,7 +140,7 @@ def solve( # Solve model. solver = cp_model.CpSolver() if _PARAMS.value: - text_format.Parse(_PARAMS.value, solver.parameters) + solver.parameters.parse_text_format(_PARAMS.value) status = solver.solve(model) # Report solution. diff --git a/examples/python/testdata/BUILD.bazel b/examples/python/testdata/BUILD.bazel index 2062112da5..8196c990d1 100644 --- a/examples/python/testdata/BUILD.bazel +++ b/examples/python/testdata/BUILD.bazel @@ -13,8 +13,4 @@ package(default_visibility = ["//visibility:public"]) -exports_files( - [ - "salbp_20_1.alb", - ], -) +exports_files(["salbp_20_1.alb"]) diff --git a/examples/python/transit_time.py b/examples/python/transit_time.py index bfc5551e2c..4a3144c129 100755 --- a/examples/python/transit_time.py +++ b/examples/python/transit_time.py @@ -22,7 +22,6 @@ """ from ortools.constraint_solver import pywrapcp -from ortools.constraint_solver import routing_enums_pb2 ########################### diff --git a/examples/python/weighted_latency_problem_sat.py b/examples/python/weighted_latency_problem_sat.py index 36616bb26b..fbc1bdd173 100644 --- a/examples/python/weighted_latency_problem_sat.py +++ b/examples/python/weighted_latency_problem_sat.py @@ -16,10 +16,10 @@ import random 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 _NUM_NODES = flags.DEFINE_integer("num_nodes", 12, "Number of nodes to visit.") @@ -27,7 +27,9 @@ _GRID_SIZE = flags.DEFINE_integer("grid_size", 20, "Size of the grid where nodes _PROFIT_RANGE = flags.DEFINE_integer("profit_range", 50, "Range of profit.") _SEED = flags.DEFINE_integer("seed", 0, "Random seed.") _PARAMS = flags.DEFINE_string( - "params", "num_search_workers:16, max_time_in_seconds:5", "Sat solver parameters." + "params", + "num_search_workers:16, max_time_in_seconds:5", + "Sat solver parameters.", ) _PROTO_FILE = flags.DEFINE_string( "proto_file", "", "If not empty, output the proto to this file." @@ -96,7 +98,7 @@ def solve_with_cp_sat(x, y, profits) -> None: # Solve model. solver = cp_model.CpSolver() if _PARAMS.value: - text_format.Parse(_PARAMS.value, solver.parameters) + solver.parameters.parse_text_format(_PARAMS.value) solver.parameters.log_search_progress = True solver.solve(model)