diff --git a/examples/python/rcpsp_sat.py b/examples/python/rcpsp_sat.py index b586146310..d09ff67020 100755 --- a/examples/python/rcpsp_sat.py +++ b/examples/python/rcpsp_sat.py @@ -27,18 +27,18 @@ 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 pywraprcpsp from ortools.scheduling import rcpsp_pb2 - -FLAGS = flags.FLAGS +from ortools.scheduling import pywraprcpsp _INPUT = flags.DEFINE_string('input', '', 'Input file to parse and solve.') _OUTPUT_PROTO = flags.DEFINE_string( 'output_proto', '', 'Output file to write the cp_model proto to.') _PARAMS = flags.DEFINE_string('params', '', 'Sat solver parameters.') _USE_INTERVAL_MAKESPAN = flags.DEFINE_bool( - 'use_interval_makespan', True, - 'Whether we encode the makespan using an interval or not.') + 'use_interval_makespan', + False, + 'Whether we encode the makespan using an interval or not.', +) _HORIZON = flags.DEFINE_integer('horizon', -1, 'Force horizon.') _ADD_REDUNDANT_ENERGETIC_CONSTRAINTS = flags.DEFINE_bool( 'add_redundant_energetic_constraints', False, @@ -213,7 +213,7 @@ def SolveRcpsp(problem, lower_bound: A valid lower bound of the makespan objective. Returns: - (lower_bound of the objective, best solution found, asssignment) + (lower_bound of the objective, best solution found, assignment) """ # Create the model. model = cp_model.CpModel() @@ -350,8 +350,7 @@ def SolveRcpsp(problem, task = problem.tasks[task_id] num_modes = len(task.recipes) - for successor_index in range(len(task.successors)): - next_id = task.successors[successor_index] + for successor_index, next_id in enumerate(task.successors): delay_matrix = task.successor_delays[successor_index] num_next_modes = len(problem.tasks[next_id].recipes) for m1 in range(num_modes): @@ -510,6 +509,9 @@ def SolveRcpsp(problem, # Solve model. solver = cp_model.CpSolver() + if not _USE_INTERVAL_MAKESPAN.value: + solver.parameters.exploit_all_precedences = True + solver.parameters.use_hard_precedences_in_cumulative = True if params: text_format.Parse(params, solver.parameters) if in_main_solve: @@ -517,7 +519,7 @@ def SolveRcpsp(problem, status = solver.Solve(model) if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE: assignment = rcpsp_pb2.RcpspAssignment() - for t in range(len(problem.tasks)): + for t, _ in enumerate(problem.tasks): if t in task_starts: assignment.start_of_task.append(solver.Value(task_starts[t])) for r in range(len(task_to_presence_literals[t])): @@ -666,7 +668,7 @@ def ComputePreemptiveLowerBound(problem, after, lower_bound): min_duration = max_duration for t in c: min_duration = min(min_duration, duration_map[t]) - count = model.NewIntVar(0, min_duration, f'count_{t}') + count = model.NewIntVar(0, min_duration, f'count_{c}') all_vars.append(count) for t in c: vars_per_task[t].append(count) diff --git a/ortools/algorithms/samples/knapsack.py b/ortools/algorithms/samples/knapsack.py index 5564ac5896..9f2b87c201 100644 --- a/ortools/algorithms/samples/knapsack.py +++ b/ortools/algorithms/samples/knapsack.py @@ -11,6 +11,7 @@ # 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. + """A simple knapsack problem.""" # [START program] # [START import] @@ -22,22 +23,118 @@ def main(): # Create the solver. # [START solver] solver = pywrapknapsack_solver.KnapsackSolver( - pywrapknapsack_solver.KnapsackSolver. - KNAPSACK_MULTIDIMENSION_BRANCH_AND_BOUND_SOLVER, 'KnapsackExample') + pywrapknapsack_solver.KnapsackSolver.KNAPSACK_MULTIDIMENSION_BRANCH_AND_BOUND_SOLVER, + "KnapsackExample", + ) # [END solver] # [START data] values = [ - 360, 83, 59, 130, 431, 67, 230, 52, 93, 125, 670, 892, 600, 38, 48, 147, - 78, 256, 63, 17, 120, 164, 432, 35, 92, 110, 22, 42, 50, 323, 514, 28, - 87, 73, 78, 15, 26, 78, 210, 36, 85, 189, 274, 43, 33, 10, 19, 389, 276, - 312 + 360, + 83, + 59, + 130, + 431, + 67, + 230, + 52, + 93, + 125, + 670, + 892, + 600, + 38, + 48, + 147, + 78, + 256, + 63, + 17, + 120, + 164, + 432, + 35, + 92, + 110, + 22, + 42, + 50, + 323, + 514, + 28, + 87, + 73, + 78, + 15, + 26, + 78, + 210, + 36, + 85, + 189, + 274, + 43, + 33, + 10, + 19, + 389, + 276, + 312, + ] + weights = [ + [ + 7, + 0, + 30, + 22, + 80, + 94, + 11, + 81, + 70, + 64, + 59, + 18, + 0, + 36, + 3, + 8, + 15, + 42, + 9, + 0, + 42, + 47, + 52, + 32, + 26, + 48, + 55, + 6, + 29, + 84, + 2, + 4, + 18, + 56, + 7, + 29, + 93, + 44, + 71, + 3, + 86, + 66, + 31, + 65, + 0, + 79, + 20, + 65, + 52, + 13, + ] ] - weights = [[ - 7, 0, 30, 22, 80, 94, 11, 81, 70, 64, 59, 18, 0, 36, 3, 8, 15, 42, 9, 0, - 42, 47, 52, 32, 26, 48, 55, 6, 29, 84, 2, 4, 18, 56, 7, 29, 93, 44, 71, - 3, 86, 66, 31, 65, 0, 79, 20, 65, 52, 13 - ]] capacities = [850] # [END data] @@ -50,18 +147,18 @@ def main(): packed_items = [] packed_weights = [] total_weight = 0 - print('Total value =', computed_value) + print("Total value =", computed_value) for i in range(len(values)): if solver.BestSolutionContains(i): packed_items.append(i) packed_weights.append(weights[0][i]) total_weight += weights[0][i] - print('Total weight:', total_weight) - print('Packed items:', packed_items) - print('Packed_weights:', packed_weights) + print("Total weight:", total_weight) + print("Packed items:", packed_items) + print("Packed_weights:", packed_weights) # [END print_solution] -if __name__ == '__main__': +if __name__ == "__main__": main() # [END program] diff --git a/ortools/algorithms/samples/simple_knapsack_program.py b/ortools/algorithms/samples/simple_knapsack_program.py index f5e5160a4e..0b09915337 100644 --- a/ortools/algorithms/samples/simple_knapsack_program.py +++ b/ortools/algorithms/samples/simple_knapsack_program.py @@ -23,15 +23,33 @@ def main(): # Create the solver. # [START solver] solver = pywrapknapsack_solver.KnapsackSolver( - pywrapknapsack_solver.KnapsackSolver. - KNAPSACK_DYNAMIC_PROGRAMMING_SOLVER, "test") + pywrapknapsack_solver.KnapsackSolver.KNAPSACK_DYNAMIC_PROGRAMMING_SOLVER, "test" + ) # [END solver] # [START data] - weights = [[ - 565, 406, 194, 130, 435, 367, 230, 315, 393, 125, 670, 892, 600, 293, - 712, 147, 421, 255 - ]] + weights = [ + [ + 565, + 406, + 194, + 130, + 435, + 367, + 230, + 315, + 393, + 125, + 670, + 892, + 600, + 293, + 712, + 147, + 421, + 255, + ] + ] capacities = [850] values = weights[0] # [END data] diff --git a/ortools/graph/samples/assignment_linear_sum_assignment.py b/ortools/graph/samples/assignment_linear_sum_assignment.py index eb6a822e2d..2339fd7311 100755 --- a/ortools/graph/samples/assignment_linear_sum_assignment.py +++ b/ortools/graph/samples/assignment_linear_sum_assignment.py @@ -28,17 +28,20 @@ def main(): # [END solver] # [START data] - costs = np.array([ - [90, 76, 75, 70], - [35, 85, 55, 65], - [125, 95, 90, 105], - [45, 110, 95, 115], - ]) + costs = np.array( + [ + [90, 76, 75, 70], + [35, 85, 55, 65], + [125, 95, 90, 105], + [45, 110, 95, 115], + ] + ) # Let's transform this into 3 parallel vectors (start_nodes, end_nodes, # arc_costs) end_nodes_unraveled, start_nodes_unraveled = np.meshgrid( - np.arange(costs.shape[1]), np.arange(costs.shape[0])) + np.arange(costs.shape[1]), np.arange(costs.shape[0]) + ) start_nodes = start_nodes_unraveled.ravel() end_nodes = end_nodes_unraveled.ravel() arc_costs = costs.ravel() @@ -54,18 +57,19 @@ def main(): # [START print_solution] if status == assignment.OPTIMAL: - print(f'Total cost = {assignment.optimal_cost()}\n') + print(f"Total cost = {assignment.optimal_cost()}\n") for i in range(0, assignment.num_nodes()): - print(f'Worker {i} assigned to task {assignment.right_mate(i)}.' + - f' Cost = {assignment.assignment_cost(i)}') + print( + f"Worker {i} assigned to task {assignment.right_mate(i)}." + + f" Cost = {assignment.assignment_cost(i)}" + ) elif status == assignment.INFEASIBLE: - print('No assignment is possible.') + print("No assignment is possible.") elif status == assignment.POSSIBLE_OVERFLOW: - print( - 'Some input costs are too large and may cause an integer overflow.') + print("Some input costs are too large and may cause an integer overflow.") # [END print_solution] -if __name__ == '__main__': +if __name__ == "__main__": main() # [END Program] diff --git a/ortools/graph/samples/assignment_min_flow.py b/ortools/graph/samples/assignment_min_flow.py index 3e1b574956..8c36c42684 100755 --- a/ortools/graph/samples/assignment_min_flow.py +++ b/ortools/graph/samples/assignment_min_flow.py @@ -28,18 +28,20 @@ def main(): # [START data] # Define the directed graph for the flow. - start_nodes = [0, 0, 0, 0] + [ - 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4 - ] + [5, 6, 7, 8] - end_nodes = [1, 2, 3, 4] + [5, 6, 7, 8, 5, 6, 7, 8, 5, 6, 7, 8, 5, 6, 7, 8 - ] + [9, 9, 9, 9] - capacities = [1, 1, 1, 1] + [ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 - ] + [1, 1, 1, 1] + start_nodes = ( + [0, 0, 0, 0] + [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4] + [5, 6, 7, 8] + ) + end_nodes = ( + [1, 2, 3, 4] + [5, 6, 7, 8, 5, 6, 7, 8, 5, 6, 7, 8, 5, 6, 7, 8] + [9, 9, 9, 9] + ) + capacities = ( + [1, 1, 1, 1] + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + [1, 1, 1, 1] + ) costs = ( - [0, 0, 0, 0] + - [90, 76, 75, 70, 35, 85, 55, 65, 125, 95, 90, 105, 45, 110, 95, 115] + - [0, 0, 0, 0]) + [0, 0, 0, 0] + + [90, 76, 75, 70, 35, 85, 55, 65, 125, 95, 90, 105, 45, 110, 95, 115] + + [0, 0, 0, 0] + ) source = 0 sink = 9 @@ -50,8 +52,9 @@ def main(): # [START constraints] # Add each arc. for i in range(len(start_nodes)): - smcf.add_arc_with_capacity_and_unit_cost(start_nodes[i], end_nodes[i], - capacities[i], costs[i]) + smcf.add_arc_with_capacity_and_unit_cost( + start_nodes[i], end_nodes[i], capacities[i], costs[i] + ) # Add node supplies. for i in range(len(supplies)): smcf.set_node_supply(i, supplies[i]) @@ -64,23 +67,24 @@ def main(): # [START print_solution] if status == smcf.OPTIMAL: - print('Total cost = ', smcf.optimal_cost()) + print("Total cost = ", smcf.optimal_cost()) print() for arc in range(smcf.num_arcs()): # Can ignore arcs leading out of source or into sink. if smcf.tail(arc) != source and smcf.head(arc) != sink: - # Arcs in the solution have a flow value of 1. Their start and end nodes # give an assignment of worker to task. if smcf.flow(arc) > 0: - print('Worker %d assigned to task %d. Cost = %d' % - (smcf.tail(arc), smcf.head(arc), smcf.unit_cost(arc))) + print( + "Worker %d assigned to task %d. Cost = %d" + % (smcf.tail(arc), smcf.head(arc), smcf.unit_cost(arc)) + ) else: - print('There was an issue with the min cost flow input.') - print(f'Status: {status}') + print("There was an issue with the min cost flow input.") + print(f"Status: {status}") # [END print_solution] -if __name__ == '__main__': +if __name__ == "__main__": main() # [END program] diff --git a/ortools/graph/samples/balance_min_flow.py b/ortools/graph/samples/balance_min_flow.py index 57b924377c..b2c2e8bd26 100755 --- a/ortools/graph/samples/balance_min_flow.py +++ b/ortools/graph/samples/balance_min_flow.py @@ -30,20 +30,59 @@ def main(): team_a = [1, 3, 5] team_b = [2, 4, 6] - start_nodes = ([0, 0] + [11, 11, 11] + [12, 12, 12] + [ - 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6 - ] + [7, 8, 9, 10]) - end_nodes = ([11, 12] + team_a + team_b + [ - 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, - 9, 10 - ] + [13, 13, 13, 13]) - capacities = ([2, 2] + [1, 1, 1] + [1, 1, 1] + [ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 - ] + [1, 1, 1, 1]) - costs = ([0, 0] + [0, 0, 0] + [0, 0, 0] + [ - 90, 76, 75, 70, 35, 85, 55, 65, 125, 95, 90, 105, 45, 110, 95, 115, 60, - 105, 80, 75, 45, 65, 110, 95 - ] + [0, 0, 0, 0]) + start_nodes = ( + [0, 0] + + [11, 11, 11] + + [12, 12, 12] + + [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6] + + [7, 8, 9, 10] + ) + end_nodes = ( + [11, 12] + + team_a + + team_b + + [7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10] + + [13, 13, 13, 13] + ) + capacities = ( + [2, 2] + + [1, 1, 1] + + [1, 1, 1] + + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + + [1, 1, 1, 1] + ) + costs = ( + [0, 0] + + [0, 0, 0] + + [0, 0, 0] + + [ + 90, + 76, + 75, + 70, + 35, + 85, + 55, + 65, + 125, + 95, + 90, + 105, + 45, + 110, + 95, + 115, + 60, + 105, + 80, + 75, + 45, + 65, + 110, + 95, + ] + + [0, 0, 0, 0] + ) source = 0 sink = 13 @@ -55,8 +94,9 @@ def main(): # [START constraints] # Add each arc. for i in range(0, len(start_nodes)): - smcf.add_arc_with_capacity_and_unit_cost(start_nodes[i], end_nodes[i], - capacities[i], costs[i]) + smcf.add_arc_with_capacity_and_unit_cost( + start_nodes[i], end_nodes[i], capacities[i], costs[i] + ) # Add node supplies. for i in range(0, len(supplies)): @@ -70,24 +110,29 @@ def main(): # [START print_solution] if status == smcf.OPTIMAL: - print('Total cost = ', smcf.optimal_cost()) + print("Total cost = ", smcf.optimal_cost()) print() for arc in range(smcf.num_arcs()): # Can ignore arcs leading out of source or intermediate, or into sink. - if (smcf.tail(arc) != source and smcf.tail(arc) != 11 and - smcf.tail(arc) != 12 and smcf.head(arc) != sink): - + if ( + smcf.tail(arc) != source + and smcf.tail(arc) != 11 + and smcf.tail(arc) != 12 + and smcf.head(arc) != sink + ): # Arcs in the solution will have a flow value of 1. # There start and end nodes give an assignment of worker to task. if smcf.flow(arc) > 0: - print('Worker %d assigned to task %d. Cost = %d' % - (smcf.tail(arc), smcf.head(arc), smcf.unit_cost(arc))) + print( + "Worker %d assigned to task %d. Cost = %d" + % (smcf.tail(arc), smcf.head(arc), smcf.unit_cost(arc)) + ) else: - print('There was an issue with the min cost flow input.') - print(f'Status: {status}') + print("There was an issue with the min cost flow input.") + print(f"Status: {status}") # [END print_solution] -if __name__ == '__main__': +if __name__ == "__main__": main() # [END program] diff --git a/ortools/graph/samples/simple_max_flow_program.py b/ortools/graph/samples/simple_max_flow_program.py index a936c61d4f..10cb6c0c9c 100755 --- a/ortools/graph/samples/simple_max_flow_program.py +++ b/ortools/graph/samples/simple_max_flow_program.py @@ -50,20 +50,20 @@ def main(): # [START print_solution] if status != smf.OPTIMAL: - print('There was an issue with the max flow input.') - print(f'Status: {status}') + print("There was an issue with the max flow input.") + print(f"Status: {status}") exit(1) - print('Max flow:', smf.optimal_flow()) - print('') - print(' Arc Flow / Capacity') + print("Max flow:", smf.optimal_flow()) + print("") + print(" Arc Flow / Capacity") solution_flows = smf.flows(all_arcs) for arc, flow, capacity in zip(all_arcs, solution_flows, capacities): - print(f'{smf.tail(arc)} / {smf.head(arc)} {flow:3} / {capacity:3}') - print('Source side min-cut:', smf.get_source_side_min_cut()) - print('Sink side min-cut:', smf.get_sink_side_min_cut()) + print(f"{smf.tail(arc)} / {smf.head(arc)} {flow:3} / {capacity:3}") + print("Source side min-cut:", smf.get_source_side_min_cut()) + print("Sink side min-cut:", smf.get_sink_side_min_cut()) # [END print_solution] -if __name__ == '__main__': +if __name__ == "__main__": main() # [END program] diff --git a/ortools/graph/samples/simple_min_cost_flow_program.py b/ortools/graph/samples/simple_min_cost_flow_program.py index 7c7d067b48..e2d1d18dcf 100755 --- a/ortools/graph/samples/simple_min_cost_flow_program.py +++ b/ortools/graph/samples/simple_min_cost_flow_program.py @@ -44,7 +44,8 @@ def main(): # [START constraints] # Add arcs, capacities and costs in bulk using numpy. all_arcs = smcf.add_arcs_with_capacity_and_unit_cost( - start_nodes, end_nodes, capacities, unit_costs) + start_nodes, end_nodes, capacities, unit_costs + ) # Add supply for each nodes. smcf.set_nodes_supplies(np.arange(0, len(supplies)), supplies) @@ -57,21 +58,21 @@ def main(): # [START print_solution] if status != smcf.OPTIMAL: - print('There was an issue with the min cost flow input.') - print(f'Status: {status}') + print("There was an issue with the min cost flow input.") + print(f"Status: {status}") exit(1) - print(f'Minimum cost: {smcf.optimal_cost()}') - print('') - print(' Arc Flow / Capacity Cost') + print(f"Minimum cost: {smcf.optimal_cost()}") + print("") + print(" Arc Flow / Capacity Cost") solution_flows = smcf.flows(all_arcs) costs = solution_flows * unit_costs for arc, flow, cost in zip(all_arcs, solution_flows, costs): print( - f'{smcf.tail(arc):1} -> {smcf.head(arc)} {flow:3} / {smcf.capacity(arc):3} {cost}' + f"{smcf.tail(arc):1} -> {smcf.head(arc)} {flow:3} / {smcf.capacity(arc):3} {cost}" ) # [END print_solution] -if __name__ == '__main__': +if __name__ == "__main__": main() # [END program] diff --git a/ortools/init/python/pywrapinit_test.py b/ortools/init/python/pywrapinit_test.py index 185ceac9e0..ff227ef552 100755 --- a/ortools/init/python/pywrapinit_test.py +++ b/ortools/init/python/pywrapinit_test.py @@ -11,6 +11,7 @@ # 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. + """Simple unit tests for python/init.i. Not exhaustive.""" import unittest @@ -18,26 +19,25 @@ from ortools.init import pywrapinit class PyWrapInit(unittest.TestCase): - def test_logging(self): - print('test_logging') - pywrapinit.CppBridge.InitLogging('pywrapinit_test.py') + print("test_logging") + pywrapinit.CppBridge.InitLogging("pywrapinit_test.py") pywrapinit.CppBridge.ShutdownLogging() def test_flags(self): - print('test_cpp_flags') + print("test_cpp_flags") cpp_flags = pywrapinit.CppFlags() # print(f'{dir(cpp_flags)}') - assert hasattr(cpp_flags, 'stderrthreshold') - assert hasattr(cpp_flags, 'log_prefix') - assert hasattr(cpp_flags, 'cp_model_dump_prefix') - assert hasattr(cpp_flags, 'cp_model_dump_models') - assert hasattr(cpp_flags, 'cp_model_dump_lns') - assert hasattr(cpp_flags, 'cp_model_dump_response') + assert hasattr(cpp_flags, "stderrthreshold") + assert hasattr(cpp_flags, "log_prefix") + assert hasattr(cpp_flags, "cp_model_dump_prefix") + assert hasattr(cpp_flags, "cp_model_dump_models") + assert hasattr(cpp_flags, "cp_model_dump_lns") + assert hasattr(cpp_flags, "cp_model_dump_response") pywrapinit.CppBridge.SetFlags(cpp_flags) def test_version(self): - print('test_version') + print("test_version") major = pywrapinit.OrToolsVersion.MajorNumber() self.assertIsInstance(major, int) minor = pywrapinit.OrToolsVersion.MinorNumber() @@ -46,9 +46,9 @@ class PyWrapInit(unittest.TestCase): self.assertIsInstance(patch, int) version = pywrapinit.OrToolsVersion.VersionString() self.assertIsInstance(version, str) - string = f'{major}.{minor}.{patch}' + string = f"{major}.{minor}.{patch}" self.assertEqual(version, string) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/ortools/sat/python/cp_model.py b/ortools/sat/python/cp_model.py index 0b9e7172d1..019feeb3c7 100644 --- a/ortools/sat/python/cp_model.py +++ b/ortools/sat/python/cp_model.py @@ -10,6 +10,7 @@ # 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. + """Methods for building and solving CP-SAT models. The following two sections describe the main @@ -78,10 +79,8 @@ OPTIMAL = cp_model_pb2.OPTIMAL CHOOSE_FIRST = cp_model_pb2.DecisionStrategyProto.CHOOSE_FIRST CHOOSE_LOWEST_MIN = cp_model_pb2.DecisionStrategyProto.CHOOSE_LOWEST_MIN CHOOSE_HIGHEST_MAX = cp_model_pb2.DecisionStrategyProto.CHOOSE_HIGHEST_MAX -CHOOSE_MIN_DOMAIN_SIZE = ( - cp_model_pb2.DecisionStrategyProto.CHOOSE_MIN_DOMAIN_SIZE) -CHOOSE_MAX_DOMAIN_SIZE = ( - cp_model_pb2.DecisionStrategyProto.CHOOSE_MAX_DOMAIN_SIZE) +CHOOSE_MIN_DOMAIN_SIZE = cp_model_pb2.DecisionStrategyProto.CHOOSE_MIN_DOMAIN_SIZE +CHOOSE_MAX_DOMAIN_SIZE = cp_model_pb2.DecisionStrategyProto.CHOOSE_MAX_DOMAIN_SIZE # Domain reduction strategy SELECT_MIN_VALUE = cp_model_pb2.DecisionStrategyProto.SELECT_MIN_VALUE @@ -98,28 +97,28 @@ LP_SEARCH = sat_parameters_pb2.SatParameters.LP_SEARCH def DisplayBounds(bounds): """Displays a flattened list of intervals.""" - out = '' + out = "" for i in range(0, len(bounds), 2): if i != 0: - out += ', ' + out += ", " if bounds[i] == bounds[i + 1]: out += str(bounds[i]) else: - out += str(bounds[i]) + '..' + str(bounds[i + 1]) + out += str(bounds[i]) + ".." + str(bounds[i + 1]) return out def ShortName(model, i): """Returns a short name of an integer variable, or its negation.""" if i < 0: - return 'Not(%s)' % ShortName(model, -i - 1) + return "Not(%s)" % ShortName(model, -i - 1) v = model.variables[i] if v.name: return v.name elif len(v.domain) == 2 and v.domain[0] == v.domain[1]: return str(v.domain[0]) else: - return '[%s]' % DisplayBounds(v.domain) + return "[%s]" % DisplayBounds(v.domain) def ShortExprName(model, e): @@ -129,17 +128,17 @@ def ShortExprName(model, e): if len(e.vars) == 1: var_name = ShortName(model, e.vars[0]) coeff = e.coeffs[0] - result = '' + result = "" if coeff == 1: result = var_name elif coeff == -1: - result = f'-{var_name}' + result = f"-{var_name}" elif coeff != 0: - result = f'{coeff} * {var_name}' + result = f"{coeff} * {var_name}" if e.offset > 0: - result = f'{result} + {e.offset}' + result = f"{result} + {e.offset}" elif e.offset < 0: - result = f'{result} - {-e.offset}' + result = f"{result} - {-e.offset}" return result # TODO(user): Support more than affine expressions. return str(e) @@ -148,34 +147,34 @@ def ShortExprName(model, e): class LinearExpr(object): """Holds an integer linear expression. - A linear expression is built from integer constants and variables. - For example, `x + 2 * (y - z + 1)`. + A linear expression is built from integer constants and variables. + For example, `x + 2 * (y - z + 1)`. - Linear expressions are used in CP-SAT models in constraints and in the - objective: + Linear expressions are used in CP-SAT models in constraints and in the + objective: - * You can define linear constraints as in: + * You can define linear constraints as in: - ``` - model.Add(x + 2 * y <= 5) - model.Add(sum(array_of_vars) == 5) - ``` + ``` + model.Add(x + 2 * y <= 5) + model.Add(sum(array_of_vars) == 5) + ``` - * In CP-SAT, the objective is a linear expression: + * In CP-SAT, the objective is a linear expression: - ``` - model.Minimize(x + 2 * y + z) - ``` + ``` + model.Minimize(x + 2 * y + z) + ``` - * For large arrays, using the LinearExpr class is faster that using the python - `sum()` function. You can create constraints and the objective from lists of - linear expressions or coefficients as follows: + * For large arrays, using the LinearExpr class is faster that using the python + `sum()` function. You can create constraints and the objective from lists of + linear expressions or coefficients as follows: - ``` - model.Minimize(cp_model.LinearExpr.Sum(expressions)) - model.Add(cp_model.LinearExpr.WeightedSum(expressions, coefficients) >= 0) - ``` - """ + ``` + model.Minimize(cp_model.LinearExpr.Sum(expressions)) + model.Add(cp_model.LinearExpr.WeightedSum(expressions, coefficients) >= 0) + ``` + """ @classmethod def Sum(cls, expressions): @@ -242,8 +241,7 @@ class LinearExpr(object): if cmh.is_integral(expr): constant += coeff * int(expr) elif isinstance(expr, _ProductCst): - to_process.append( - (expr.Expression(), coeff * expr.Coefficient())) + to_process.append((expr.Expression(), coeff * expr.Coefficient())) elif isinstance(expr, _Sum): to_process.append((expr.Left(), coeff)) to_process.append((expr.Right(), coeff)) @@ -261,7 +259,7 @@ class LinearExpr(object): constant += coeff coeffs[expr.Not()] -= coeff else: - raise TypeError('Unrecognized linear expression: ' + str(expr)) + raise TypeError("Unrecognized linear expression: " + str(expr)) return coeffs, constant @@ -277,8 +275,7 @@ class LinearExpr(object): elif cmh.is_a_number(expr): constant += coeff * float(expr) elif isinstance(expr, _ProductCst): - to_process.append( - (expr.Expression(), coeff * expr.Coefficient())) + to_process.append((expr.Expression(), coeff * expr.Coefficient())) elif isinstance(expr, _Sum): to_process.append((expr.Left(), coeff)) to_process.append((expr.Right(), coeff)) @@ -302,7 +299,7 @@ class LinearExpr(object): else: coeffs[expr.Not()] = -coeff else: - raise TypeError('Unrecognized linear expression: ' + str(expr)) + raise TypeError("Unrecognized linear expression: " + str(expr)) is_integer = cmh.is_integral(constant) if is_integer: for coeff in coeffs.values(): @@ -316,8 +313,9 @@ class LinearExpr(object): def __abs__(self): raise NotImplementedError( - 'calling abs() on a linear expression is not supported, ' - 'please use CpModel.AddAbsEquality') + "calling abs() on a linear expression is not supported, " + "please use CpModel.AddAbsEquality" + ) def __add__(self, arg): if cmh.is_zero(arg): @@ -355,53 +353,63 @@ class LinearExpr(object): def __div__(self, _): raise NotImplementedError( - 'calling / on a linear expression is not supported, ' - 'please use CpModel.AddDivisionEquality') + "calling / on a linear expression is not supported, " + "please use CpModel.AddDivisionEquality" + ) def __truediv__(self, _): raise NotImplementedError( - 'calling // on a linear expression is not supported, ' - 'please use CpModel.AddDivisionEquality') + "calling // on a linear expression is not supported, " + "please use CpModel.AddDivisionEquality" + ) def __mod__(self, _): raise NotImplementedError( - 'calling %% on a linear expression is not supported, ' - 'please use CpModel.AddModuloEquality') + "calling %% on a linear expression is not supported, " + "please use CpModel.AddModuloEquality" + ) def __pow__(self, _): raise NotImplementedError( - 'calling ** on a linear expression is not supported, ' - 'please use CpModel.AddMultiplicationEquality') + "calling ** on a linear expression is not supported, " + "please use CpModel.AddMultiplicationEquality" + ) def __lshift__(self, _): raise NotImplementedError( - 'calling left shift on a linear expression is not supported') + "calling left shift on a linear expression is not supported" + ) def __rshift__(self, _): raise NotImplementedError( - 'calling right shift on a linear expression is not supported') + "calling right shift on a linear expression is not supported" + ) def __and__(self, _): raise NotImplementedError( - 'calling and on a linear expression is not supported, ' - 'please use CpModel.AddBoolAnd') + "calling and on a linear expression is not supported, " + "please use CpModel.AddBoolAnd" + ) def __or__(self, _): raise NotImplementedError( - 'calling or on a linear expression is not supported, ' - 'please use CpModel.AddBoolOr') + "calling or on a linear expression is not supported, " + "please use CpModel.AddBoolOr" + ) def __xor__(self, _): raise NotImplementedError( - 'calling xor on a linear expression is not supported, ' - 'please use CpModel.AddBoolXor') + "calling xor on a linear expression is not supported, " + "please use CpModel.AddBoolXor" + ) def __neg__(self): return _ProductCst(self, -1) def __bool__(self): raise NotImplementedError( - 'Evaluating a LinearExpr instance as a Boolean is not implemented.') + "Evaluating a LinearExpr instance as a Boolean is not implemented." + ) def __eq__(self, arg): if arg is None: @@ -430,7 +438,7 @@ class LinearExpr(object): if cmh.is_integral(arg): arg = cmh.assert_is_int64(arg) if arg == INT_MIN: - raise ArithmeticError('< INT_MIN is not supported') + raise ArithmeticError("< INT_MIN is not supported") return BoundedLinearExpression(self, [INT_MIN, arg - 1]) else: return BoundedLinearExpression(self - arg, [INT_MIN, -1]) @@ -439,7 +447,7 @@ class LinearExpr(object): if cmh.is_integral(arg): arg = cmh.assert_is_int64(arg) if arg == INT_MAX: - raise ArithmeticError('> INT_MAX is not supported') + raise ArithmeticError("> INT_MAX is not supported") return BoundedLinearExpression(self, [arg + 1, INT_MAX]) else: return BoundedLinearExpression(self - arg, [1, INT_MAX]) @@ -455,10 +463,10 @@ class LinearExpr(object): return BoundedLinearExpression(self, [INT_MIN + 1, INT_MAX]) else: return BoundedLinearExpression( - self, [INT_MIN, arg - 1, arg + 1, INT_MAX]) + self, [INT_MIN, arg - 1, arg + 1, INT_MAX] + ) else: - return BoundedLinearExpression(self - arg, - [INT_MIN, -1, 1, INT_MAX]) + return BoundedLinearExpression(self - arg, [INT_MIN, -1, 1, INT_MAX]) class _Sum(LinearExpr): @@ -467,7 +475,7 @@ class _Sum(LinearExpr): def __init__(self, left, right): for x in [left, right]: if not cmh.is_a_number(x) and not isinstance(x, LinearExpr): - raise TypeError('Not an linear expression: ' + str(x)) + raise TypeError("Not an linear expression: " + str(x)) self.__left = left self.__right = right @@ -478,10 +486,10 @@ class _Sum(LinearExpr): return self.__right def __str__(self): - return f'({self.__left} + {self.__right})' + return f"({self.__left} + {self.__right})" def __repr__(self): - return f'Sum({repr(self.__left)}, {repr(self.__right)})' + return f"Sum({repr(self.__left)}, {repr(self.__right)})" class _ProductCst(LinearExpr): @@ -498,13 +506,12 @@ class _ProductCst(LinearExpr): def __str__(self): if self.__coef == -1: - return '-' + str(self.__expr) + return "-" + str(self.__expr) else: - return '(' + str(self.__coef) + ' * ' + str(self.__expr) + ')' + return "(" + str(self.__coef) + " * " + str(self.__expr) + ")" def __repr__(self): - return 'ProductCst(' + repr(self.__expr) + ', ' + repr( - self.__coef) + ')' + return "ProductCst(" + repr(self.__expr) + ", " + repr(self.__coef) + ")" def Coefficient(self): return self.__coef @@ -528,18 +535,20 @@ class _SumArray(LinearExpr): elif isinstance(x, LinearExpr): self.__expressions.append(x) else: - raise TypeError('Not an linear expression: ' + str(x)) + raise TypeError("Not an linear expression: " + str(x)) def __str__(self): if self.__constant == 0: - return '({})'.format(' + '.join(map(str, self.__expressions))) + return "({})".format(" + ".join(map(str, self.__expressions))) else: - return '({} + {})'.format(' + '.join(map(str, self.__expressions)), - self.__constant) + return "({} + {})".format( + " + ".join(map(str, self.__expressions)), self.__constant + ) def __repr__(self): - return 'SumArray({}, {})'.format( - ', '.join(map(repr, self.__expressions)), self.__constant) + return "SumArray({}, {})".format( + ", ".join(map(repr, self.__expressions)), self.__constant + ) def Expressions(self): return self.__expressions @@ -557,8 +566,9 @@ class _WeightedSum(LinearExpr): self.__constant = constant if len(expressions) != len(coefficients): raise TypeError( - 'In the LinearExpr.WeightedSum method, the expression array and the ' - ' coefficient array must have the same length.') + "In the LinearExpr.WeightedSum method, the expression array and the " + " coefficient array must have the same length." + ) for e, c in zip(expressions, coefficients): c = cmh.assert_is_a_number(c) if cmh.is_zero(c): @@ -570,7 +580,7 @@ class _WeightedSum(LinearExpr): self.__expressions.append(e) self.__coefficients.append(c) else: - raise TypeError('Not an linear expression: ' + str(e)) + raise TypeError("Not an linear expression: " + str(e)) def __str__(self): output = None @@ -578,29 +588,31 @@ class _WeightedSum(LinearExpr): if not output and cmh.is_one(coeff): output = str(expr) elif not output and cmh.is_minus_one(coeff): - output = '-' + str(expr) + output = "-" + str(expr) elif not output: - output = '{} * {}'.format(coeff, str(expr)) + output = "{} * {}".format(coeff, str(expr)) elif cmh.is_one(coeff): - output += ' + {}'.format(str(expr)) + output += " + {}".format(str(expr)) elif cmh.is_minus_one(coeff): - output += ' - {}'.format(str(expr)) + output += " - {}".format(str(expr)) elif coeff > 1: - output += ' + {} * {}'.format(coeff, str(expr)) + output += " + {} * {}".format(coeff, str(expr)) elif coeff < -1: - output += ' - {} * {}'.format(-coeff, str(expr)) + output += " - {} * {}".format(-coeff, str(expr)) if self.__constant > 0: - output += ' + {}'.format(self.__constant) + output += " + {}".format(self.__constant) elif self.__constant < 0: - output += ' - {}'.format(-self.__constant) + output += " - {}".format(-self.__constant) if output is None: - output = '0' + output = "0" return output def __repr__(self): - return 'WeightedSum([{}], [{}], {})'.format( - ', '.join(map(repr, self.__expressions)), - ', '.join(map(repr, self.__coefficients)), self.__constant) + return "WeightedSum([{}], [{}], {})".format( + ", ".join(map(repr, self.__expressions)), + ", ".join(map(repr, self.__coefficients)), + self.__constant, + ) def Expressions(self): return self.__expressions @@ -615,16 +627,16 @@ class _WeightedSum(LinearExpr): class IntVar(LinearExpr): """An integer variable. - An IntVar is an object that can take on any integer value within defined - ranges. Variables appear in constraint like: + An IntVar is an object that can take on any integer value within defined + ranges. Variables appear in constraint like: - x + y >= 5 - AllDifferent([x, y, z]) + x + y >= 5 + AllDifferent([x, y, z]) - Solving a model is equivalent to finding, for each variable, a single value - from the set of initial values (called the initial domain), such that the - model is feasible, or optimal if you provided an objective function. - """ + Solving a model is equivalent to finding, for each variable, a single value + from the set of initial values (called the initial domain), such that the + model is feasible, or optimal if you provided an objective function. + """ def __init__(self, model, domain, name): """See CpModel.NewIntVar below.""" @@ -661,16 +673,18 @@ class IntVar(LinearExpr): def __str__(self): if not self.__var.name: - if len(self.__var.domain - ) == 2 and self.__var.domain[0] == self.__var.domain[1]: + if ( + len(self.__var.domain) == 2 + and self.__var.domain[0] == self.__var.domain[1] + ): # Special case for constants. return str(self.__var.domain[0]) else: - return 'unnamed_var_%i' % self.__index + return "unnamed_var_%i" % self.__index return self.__var.name def __repr__(self): - return '%s(%s)' % (self.__var.name, DisplayBounds(self.__var.domain)) + return "%s(%s)" % (self.__var.name, DisplayBounds(self.__var.domain)) def Name(self): return self.__var.name @@ -678,16 +692,15 @@ class IntVar(LinearExpr): def Not(self): """Returns the negation of a Boolean variable. - This method implements the logical negation of a Boolean variable. - It is only valid if the variable has a Boolean domain (0 or 1). + This method implements the logical negation of a Boolean variable. + It is only valid if the variable has a Boolean domain (0 or 1). - Note that this method is nilpotent: `x.Not().Not() == x`. - """ + Note that this method is nilpotent: `x.Not().Not() == x`. + """ for bound in self.__var.domain: if bound < 0 or bound > 1: - raise TypeError( - 'Cannot call Not on a non boolean variable: %s' % self) + raise TypeError("Cannot call Not on a non boolean variable: %s" % self) if self.__negation is None: self.__negation = _NotBooleanVariable(self) return self.__negation @@ -706,21 +719,22 @@ class _NotBooleanVariable(LinearExpr): return self.__boolvar def __str__(self): - return 'not(%s)' % str(self.__boolvar) + return "not(%s)" % str(self.__boolvar) def __bool__(self): raise NotImplementedError( - 'Evaluating a literal as a Boolean value is not implemented.') + "Evaluating a literal as a Boolean value is not implemented." + ) class BoundedLinearExpression(object): """Represents a linear constraint: `lb <= linear expression <= ub`. - The only use of this class is to be added to the CpModel through - `CpModel.Add(expression)`, as in: + The only use of this class is to be added to the CpModel through + `CpModel.Add(expression)`, as in: - model.Add(x + 2 * y -1 >= z) - """ + model.Add(x + 2 * y -1 >= z) + """ def __init__(self, expr, bounds): self.__expr = expr @@ -732,23 +746,24 @@ class BoundedLinearExpression(object): ub = self.__bounds[1] if lb > INT_MIN and ub < INT_MAX: if lb == ub: - return str(self.__expr) + ' == ' + str(lb) + return str(self.__expr) + " == " + str(lb) else: - return str(lb) + ' <= ' + str( - self.__expr) + ' <= ' + str(ub) + return str(lb) + " <= " + str(self.__expr) + " <= " + str(ub) elif lb > INT_MIN: - return str(self.__expr) + ' >= ' + str(lb) + return str(self.__expr) + " >= " + str(lb) elif ub < INT_MAX: - return str(self.__expr) + ' <= ' + str(ub) + return str(self.__expr) + " <= " + str(ub) else: - return 'True (unbounded expr ' + str(self.__expr) + ')' - elif (len(self.__bounds) == 4 and self.__bounds[0] == INT_MIN and - self.__bounds[1] + 2 == self.__bounds[2] and - self.__bounds[3] == INT_MAX): - return str(self.__expr) + ' != ' + str(self.__bounds[1] + 1) + return "True (unbounded expr " + str(self.__expr) + ")" + elif ( + len(self.__bounds) == 4 + and self.__bounds[0] == INT_MIN + and self.__bounds[1] + 2 == self.__bounds[2] + and self.__bounds[3] == INT_MAX + ): + return str(self.__expr) + " != " + str(self.__bounds[1] + 1) else: - return str(self.__expr) + ' in [' + DisplayBounds( - self.__bounds) + ']' + return str(self.__expr) + " in [" + DisplayBounds(self.__bounds) + "]" def Expression(self): return self.__expr @@ -763,34 +778,41 @@ class BoundedLinearExpression(object): eq_bounds = [0, 0] different_vars = set([-1, 1]) ne_bounds = [INT_MIN, -1, 1, INT_MAX] - if (len(coeffs_map) == 1 and all_coeffs == same_var and - constant == 0 and - (self.__bounds == eq_bounds or self.__bounds == ne_bounds)): + if ( + len(coeffs_map) == 1 + and all_coeffs == same_var + and constant == 0 + and (self.__bounds == eq_bounds or self.__bounds == ne_bounds) + ): return self.__bounds == eq_bounds - if (len(coeffs_map) == 2 and all_coeffs == different_vars and - constant == 0 and - (self.__bounds == eq_bounds or self.__bounds == ne_bounds)): + if ( + len(coeffs_map) == 2 + and all_coeffs == different_vars + and constant == 0 + and (self.__bounds == eq_bounds or self.__bounds == ne_bounds) + ): return self.__bounds == ne_bounds raise NotImplementedError( - f'Evaluating a BoundedLinearExpression \'{self}\' as a Boolean value' - + ' is not supported.') + f"Evaluating a BoundedLinearExpression '{self}' as a Boolean value" + + " is not supported." + ) class Constraint(object): """Base class for constraints. - Constraints are built by the CpModel through the Add methods. - Once created by the CpModel class, they are automatically added to the model. - The purpose of this class is to allow specification of enforcement literals - for this constraint. + Constraints are built by the CpModel through the Add methods. + Once created by the CpModel class, they are automatically added to the model. + The purpose of this class is to allow specification of enforcement literals + for this constraint. - b = model.NewBoolVar('b') - x = model.NewIntVar(0, 10, 'x') - y = model.NewIntVar(0, 10, 'y') + b = model.NewBoolVar('b') + x = model.NewIntVar(0, 10, 'x') + y = model.NewIntVar(0, 10, 'y') - model.Add(x + 2 * y == 5).OnlyEnforceIf(b.Not()) - """ + model.Add(x + 2 * y == 5).OnlyEnforceIf(b.Not()) + """ def __init__(self, constraints): self.__index = len(constraints) @@ -799,23 +821,24 @@ class Constraint(object): def OnlyEnforceIf(self, *boolvar): """Adds an enforcement literal to the constraint. - This method adds one or more literals (that is, a boolean variable or its - negation) as enforcement literals. The conjunction of all these literals - determines whether the constraint is active or not. It acts as an - implication, so if the conjunction is true, it implies that the constraint - must be enforced. If it is false, then the constraint is ignored. + This method adds one or more literals (that is, a boolean variable or its + negation) as enforcement literals. The conjunction of all these literals + determines whether the constraint is active or not. It acts as an + implication, so if the conjunction is true, it implies that the constraint + must be enforced. If it is false, then the constraint is ignored. - BoolOr, BoolAnd, and linear constraints all support enforcement literals. + BoolOr, BoolAnd, and linear constraints all support enforcement literals. - Args: - *boolvar: One or more Boolean literals. + Args: + *boolvar: One or more Boolean literals. - Returns: - self. - """ + Returns: + self. + """ for lit in ExpandGeneratorOrTuple(boolvar): - if (isinstance(lit, bool) and - bool(lit)) or (cmh.is_integral(lit) and int(lit) == 1): + if (isinstance(lit, bool) and bool(lit)) or ( + cmh.is_integral(lit) and int(lit) == 1 + ): # Always true. Do nothing. pass else: @@ -827,7 +850,7 @@ class Constraint(object): if name: self.__constraint.name = name else: - self.__constraint.ClearField('name') + self.__constraint.ClearField("name") return self def Name(self): @@ -846,20 +869,20 @@ class Constraint(object): class IntervalVar(object): """Represents an Interval variable. - An interval variable is both a constraint and a variable. It is defined by - three integer variables: start, size, and end. + An interval variable is both a constraint and a variable. It is defined by + three integer variables: start, size, and end. - It is a constraint because, internally, it enforces that start + size == end. + It is a constraint because, internally, it enforces that start + size == end. - It is also a variable as it can appear in specific scheduling constraints: - NoOverlap, NoOverlap2D, Cumulative. + It is also a variable as it can appear in specific scheduling constraints: + NoOverlap, NoOverlap2D, Cumulative. - Optionally, an enforcement literal can be added to this constraint, in which - case these scheduling constraints will ignore interval variables with - enforcement literals assigned to false. Conversely, these constraints will - also set these enforcement literals to false if they cannot fit these - intervals into the schedule. - """ + Optionally, an enforcement literal can be added to this constraint, in which + case these scheduling constraints will ignore interval variables with + enforcement literals assigned to false. Conversely, these constraints will + also set these enforcement literals to false if they cannot fit these + intervals into the schedule. + """ def __init__(self, model, start, size, end, is_present_index, name): self.__model = model @@ -870,8 +893,7 @@ class IntervalVar(object): # None or the index of a Boolean literal. name is a string # case 2: called when querying an existing interval variable. # start_index is an int, all parameters after are None. - if (size is None and end is None and is_present_index is None and - name is None): + if size is None and end is None and is_present_index is None and name is None: self.__index = start self.__ct = model.constraints[start] else: @@ -899,43 +921,48 @@ class IntervalVar(object): def __repr__(self): interval = self.__ct.interval if self.__ct.enforcement_literal: - return '%s(start = %s, size = %s, end = %s, is_present = %s)' % ( - self.__ct.name, ShortExprName(self.__model, interval.start), + return "%s(start = %s, size = %s, end = %s, is_present = %s)" % ( + self.__ct.name, + ShortExprName(self.__model, interval.start), ShortExprName(self.__model, interval.size), ShortExprName(self.__model, interval.end), - ShortName(self.__model, self.__ct.enforcement_literal[0])) + ShortName(self.__model, self.__ct.enforcement_literal[0]), + ) else: - return '%s(start = %s, size = %s, end = %s)' % ( - self.__ct.name, ShortExprName(self.__model, interval.start), + return "%s(start = %s, size = %s, end = %s)" % ( + self.__ct.name, + ShortExprName(self.__model, interval.start), ShortExprName(self.__model, interval.size), - ShortExprName(self.__model, interval.end)) + ShortExprName(self.__model, interval.end), + ) def Name(self): return self.__ct.name def StartExpr(self): return LinearExpr.RebuildFromLinearExpressionProto( - self.__model, self.__ct.interval.start) + self.__model, self.__ct.interval.start + ) def SizeExpr(self): return LinearExpr.RebuildFromLinearExpressionProto( - self.__model, self.__ct.interval.size) + self.__model, self.__ct.interval.size + ) def EndExpr(self): return LinearExpr.RebuildFromLinearExpressionProto( - self.__model, self.__ct.interval.end) + self.__model, self.__ct.interval.end + ) def ObjectIsATrueLiteral(literal): """Checks if literal is either True, or a Boolean literals fixed to True.""" if isinstance(literal, IntVar): proto = literal.Proto() - return (len(proto.domain) == 2 and proto.domain[0] == 1 and - proto.domain[1] == 1) + return len(proto.domain) == 2 and proto.domain[0] == 1 and proto.domain[1] == 1 if isinstance(literal, _NotBooleanVariable): proto = literal.Not().Proto() - return (len(proto.domain) == 2 and proto.domain[0] == 0 and - proto.domain[1] == 0) + return len(proto.domain) == 2 and proto.domain[0] == 0 and proto.domain[1] == 0 if cmh.is_integral(literal): return int(literal) == 1 return False @@ -945,12 +972,10 @@ def ObjectIsAFalseLiteral(literal): """Checks if literal is either False, or a Boolean literals fixed to False.""" if isinstance(literal, IntVar): proto = literal.Proto() - return (len(proto.domain) == 2 and proto.domain[0] == 0 and - proto.domain[1] == 0) + return len(proto.domain) == 2 and proto.domain[0] == 0 and proto.domain[1] == 0 if isinstance(literal, _NotBooleanVariable): proto = literal.Not().Proto() - return (len(proto.domain) == 2 and proto.domain[0] == 1 and - proto.domain[1] == 1) + return len(proto.domain) == 2 and proto.domain[0] == 1 and proto.domain[1] == 1 if cmh.is_integral(literal): return int(literal) == 0 return False @@ -959,11 +984,11 @@ def ObjectIsAFalseLiteral(literal): class CpModel(object): """Methods for building a CP model. - Methods beginning with: + Methods beginning with: - * ```New``` create integer, boolean, or interval variables. - * ```Add``` create new constraints and add them to the model. - """ + * ```New``` create integer, boolean, or interval variables. + * ```Add``` create new constraints and add them to the model. + """ def __init__(self): self.__model = cp_model_pb2.CpModelProto() @@ -983,35 +1008,35 @@ class CpModel(object): def NewIntVar(self, lb, ub, name): """Create an integer variable with domain [lb, ub]. - The CP-SAT solver is limited to integer variables. If you have fractional - values, scale them up so that they become integers; if you have strings, - encode them as integers. + The CP-SAT solver is limited to integer variables. If you have fractional + values, scale them up so that they become integers; if you have strings, + encode them as integers. - Args: - lb: Lower bound for the variable. - ub: Upper bound for the variable. - name: The name of the variable. + Args: + lb: Lower bound for the variable. + ub: Upper bound for the variable. + name: The name of the variable. - Returns: - a variable whose domain is [lb, ub]. - """ + Returns: + a variable whose domain is [lb, ub]. + """ return IntVar(self.__model, Domain(lb, ub), name) def NewIntVarFromDomain(self, domain, name): """Create an integer variable from a domain. - A domain is a set of integers specified by a collection of intervals. - For example, `model.NewIntVarFromDomain(cp_model. - Domain.FromIntervals([[1, 2], [4, 6]]), 'x')` + A domain is a set of integers specified by a collection of intervals. + For example, `model.NewIntVarFromDomain(cp_model. + Domain.FromIntervals([[1, 2], [4, 6]]), 'x')` - Args: - domain: An instance of the Domain class. - name: The name of the variable. + Args: + domain: An instance of the Domain class. + name: The name of the variable. - Returns: - a variable whose domain is the given domain. - """ + Returns: + a variable whose domain is the given domain. + """ return IntVar(self.__model, domain, name) def NewBoolVar(self, name): @@ -1020,8 +1045,7 @@ class CpModel(object): def NewConstant(self, value): """Declares a constant integer.""" - return IntVar(self.__model, self.GetOrMakeIndexFromConstant(value), - None) + return IntVar(self.__model, self.GetOrMakeIndexFromConstant(value), None) # Linear constraints. @@ -1037,14 +1061,16 @@ class CpModel(object): coeffs_map, constant = linear_expr.GetIntegerVarValueMap() for t in coeffs_map.items(): if not isinstance(t[0], IntVar): - raise TypeError('Wrong argument' + str(t)) + raise TypeError("Wrong argument" + str(t)) c = cmh.assert_is_int64(t[1]) model_ct.linear.vars.append(t[0].Index()) model_ct.linear.coeffs.append(c) - model_ct.linear.domain.extend([ - cmh.capped_subtraction(x, constant) - for x in domain.FlattenedIntervals() - ]) + model_ct.linear.domain.extend( + [ + cmh.capped_subtraction(x, constant) + for x in domain.FlattenedIntervals() + ] + ) return ct elif cmh.is_integral(linear_expr): if not domain.Contains(int(linear_expr)): @@ -1052,62 +1078,68 @@ class CpModel(object): # Nothing to do otherwise. else: raise TypeError( - 'Not supported: CpModel.AddLinearExpressionInDomain(' + - str(linear_expr) + ' ' + str(domain) + ')') + "Not supported: CpModel.AddLinearExpressionInDomain(" + + str(linear_expr) + + " " + + str(domain) + + ")" + ) def Add(self, ct): """Adds a `BoundedLinearExpression` to the model. - Args: - ct: A [`BoundedLinearExpression`](#boundedlinearexpression). + Args: + ct: A [`BoundedLinearExpression`](#boundedlinearexpression). - Returns: - An instance of the `Constraint` class. - """ + Returns: + An instance of the `Constraint` class. + """ if isinstance(ct, BoundedLinearExpression): return self.AddLinearExpressionInDomain( - ct.Expression(), Domain.FromFlatIntervals(ct.Bounds())) + ct.Expression(), Domain.FromFlatIntervals(ct.Bounds()) + ) elif ct and isinstance(ct, bool): return self.AddBoolOr([True]) elif not ct and isinstance(ct, bool): return self.AddBoolOr([]) # Evaluate to false. else: - raise TypeError('Not supported: CpModel.Add(' + str(ct) + ')') + raise TypeError("Not supported: CpModel.Add(" + str(ct) + ")") # General Integer Constraints. def AddAllDifferent(self, *expressions): """Adds AllDifferent(expressions). - This constraint forces all expressions to have different values. + This constraint forces all expressions to have different values. - Args: - *expressions: simple expressions of the form a * var + constant. + Args: + *expressions: simple expressions of the form a * var + constant. - Returns: - An instance of the `Constraint` class. - """ + Returns: + An instance of the `Constraint` class. + """ ct = Constraint(self.__model.constraints) model_ct = self.__model.constraints[ct.Index()] expanded = ExpandGeneratorOrTuple(expressions) model_ct.all_diff.exprs.extend( - [self.ParseLinearExpression(x) for x in expanded]) + [self.ParseLinearExpression(x) for x in expanded] + ) return ct def AddElement(self, index, variables, target): """Adds the element constraint: `variables[index] == target`. - Args: - index: The index of the variable that's being constrained. - variables: A list of variables. - target: The value that the variable must be equal to. + Args: + index: The index of the variable that's being constrained. + variables: A list of variables. + target: The value that the variable must be equal to. - Returns: - An instance of the `Constraint` class. - """ + Returns: + An instance of the `Constraint` class. + """ if not variables: - raise ValueError('AddElement expects a non-empty variables array') + raise ValueError("AddElement expects a non-empty variables array") if cmh.is_integral(index): return self.Add(list(variables)[int(index)] == target) @@ -1115,35 +1147,34 @@ class CpModel(object): ct = Constraint(self.__model.constraints) model_ct = self.__model.constraints[ct.Index()] model_ct.element.index = self.GetOrMakeIndex(index) - model_ct.element.vars.extend( - [self.GetOrMakeIndex(x) for x in variables]) + model_ct.element.vars.extend([self.GetOrMakeIndex(x) for x in variables]) model_ct.element.target = self.GetOrMakeIndex(target) return ct def AddCircuit(self, arcs): """Adds Circuit(arcs). - Adds a circuit constraint from a sparse list of arcs that encode the graph. + Adds a circuit constraint from a sparse list of arcs that encode the graph. - A circuit is a unique Hamiltonian path in a subgraph of the total - graph. In case a node 'i' is not in the path, then there must be a - loop arc 'i -> i' associated with a true literal. Otherwise - this constraint will fail. + A circuit is a unique Hamiltonian path in a subgraph of the total + graph. In case a node 'i' is not in the path, then there must be a + loop arc 'i -> i' associated with a true literal. Otherwise + this constraint will fail. - Args: - arcs: a list of arcs. An arc is a tuple (source_node, destination_node, - literal). The arc is selected in the circuit if the literal is true. - Both source_node and destination_node must be integers between 0 and the - number of nodes - 1. + Args: + arcs: a list of arcs. An arc is a tuple (source_node, destination_node, + literal). The arc is selected in the circuit if the literal is true. + Both source_node and destination_node must be integers between 0 and the + number of nodes - 1. - Returns: - An instance of the `Constraint` class. + Returns: + An instance of the `Constraint` class. - Raises: - ValueError: If the list of arcs is empty. - """ + Raises: + ValueError: If the list of arcs is empty. + """ if not arcs: - raise ValueError('AddCircuit expects a non-empty array of arcs') + raise ValueError("AddCircuit expects a non-empty array of arcs") ct = Constraint(self.__model.constraints) model_ct = self.__model.constraints[ct.Index()] for arc in arcs: @@ -1158,30 +1189,29 @@ class CpModel(object): def AddMultipleCircuit(self, arcs): """Adds a multiple circuit constraint, aka the "VRP" constraint. - The direct graph where arc #i (from tails[i] to head[i]) is present iff - literals[i] is true must satisfy this set of properties: - - #incoming arcs == 1 except for node 0. - - #outgoing arcs == 1 except for node 0. - - for node zero, #incoming arcs == #outgoing arcs. - - There are no duplicate arcs. - - Self-arcs are allowed except for node 0. - - There is no cycle in this graph, except through node 0. + The direct graph where arc #i (from tails[i] to head[i]) is present iff + literals[i] is true must satisfy this set of properties: + - #incoming arcs == 1 except for node 0. + - #outgoing arcs == 1 except for node 0. + - for node zero, #incoming arcs == #outgoing arcs. + - There are no duplicate arcs. + - Self-arcs are allowed except for node 0. + - There is no cycle in this graph, except through node 0. - Args: - arcs: a list of arcs. An arc is a tuple (source_node, destination_node, - literal). The arc is selected in the circuit if the literal is true. - Both source_node and destination_node must be integers between 0 and the - number of nodes - 1. + Args: + arcs: a list of arcs. An arc is a tuple (source_node, destination_node, + literal). The arc is selected in the circuit if the literal is true. + Both source_node and destination_node must be integers between 0 and the + number of nodes - 1. - Returns: - An instance of the `Constraint` class. + Returns: + An instance of the `Constraint` class. - Raises: - ValueError: If the list of arcs is empty. - """ + Raises: + ValueError: If the list of arcs is empty. + """ if not arcs: - raise ValueError( - 'AddMultipleCircuit expects a non-empty array of arcs') + raise ValueError("AddMultipleCircuit expects a non-empty array of arcs") ct = Constraint(self.__model.constraints) model_ct = self.__model.constraints[ct.Index()] for arc in arcs: @@ -1196,29 +1226,29 @@ class CpModel(object): def AddAllowedAssignments(self, variables, tuples_list): """Adds AllowedAssignments(variables, tuples_list). - An AllowedAssignments constraint is a constraint on an array of variables, - which requires that when all variables are assigned values, the resulting - array equals one of the tuples in `tuple_list`. + An AllowedAssignments constraint is a constraint on an array of variables, + which requires that when all variables are assigned values, the resulting + array equals one of the tuples in `tuple_list`. - Args: - variables: A list of variables. - tuples_list: A list of admissible tuples. Each tuple must have the same - length as the variables, and the ith value of a tuple corresponds to the - ith variable. + Args: + variables: A list of variables. + tuples_list: A list of admissible tuples. Each tuple must have the same + length as the variables, and the ith value of a tuple corresponds to the + ith variable. - Returns: - An instance of the `Constraint` class. + Returns: + An instance of the `Constraint` class. - Raises: - TypeError: If a tuple does not have the same size as the list of - variables. - ValueError: If the array of variables is empty. - """ + Raises: + TypeError: If a tuple does not have the same size as the list of + variables. + ValueError: If the array of variables is empty. + """ if not variables: raise ValueError( - 'AddAllowedAssignments expects a non-empty variables ' - 'array') + "AddAllowedAssignments expects a non-empty variables " "array" + ) ct = Constraint(self.__model.constraints) model_ct = self.__model.constraints[ct.Index()] @@ -1226,7 +1256,7 @@ class CpModel(object): arity = len(variables) for t in tuples_list: if len(t) != arity: - raise TypeError('Tuple ' + str(t) + ' has the wrong arity') + raise TypeError("Tuple " + str(t) + " has the wrong arity") ar = [] for v in t: ar.append(cmh.assert_is_int64(v)) @@ -1236,90 +1266,92 @@ class CpModel(object): def AddForbiddenAssignments(self, variables, tuples_list): """Adds AddForbiddenAssignments(variables, [tuples_list]). - A ForbiddenAssignments constraint is a constraint on an array of variables - where the list of impossible combinations is provided in the tuples list. + A ForbiddenAssignments constraint is a constraint on an array of variables + where the list of impossible combinations is provided in the tuples list. - Args: - variables: A list of variables. - tuples_list: A list of forbidden tuples. Each tuple must have the same - length as the variables, and the *i*th value of a tuple corresponds to - the *i*th variable. + Args: + variables: A list of variables. + tuples_list: A list of forbidden tuples. Each tuple must have the same + length as the variables, and the *i*th value of a tuple corresponds to + the *i*th variable. - Returns: - An instance of the `Constraint` class. + Returns: + An instance of the `Constraint` class. - Raises: - TypeError: If a tuple does not have the same size as the list of - variables. - ValueError: If the array of variables is empty. - """ + Raises: + TypeError: If a tuple does not have the same size as the list of + variables. + ValueError: If the array of variables is empty. + """ if not variables: raise ValueError( - 'AddForbiddenAssignments expects a non-empty variables ' - 'array') + "AddForbiddenAssignments expects a non-empty variables " "array" + ) index = len(self.__model.constraints) ct = self.AddAllowedAssignments(variables, tuples_list) self.__model.constraints[index].table.negated = True return ct - def AddAutomaton(self, transition_variables, starting_state, final_states, - transition_triples): + def AddAutomaton( + self, transition_variables, starting_state, final_states, transition_triples + ): """Adds an automaton constraint. - An automaton constraint takes a list of variables (of size *n*), an initial - state, a set of final states, and a set of transitions. A transition is a - triplet (*tail*, *transition*, *head*), where *tail* and *head* are states, - and *transition* is the label of an arc from *head* to *tail*, - corresponding to the value of one variable in the list of variables. + An automaton constraint takes a list of variables (of size *n*), an initial + state, a set of final states, and a set of transitions. A transition is a + triplet (*tail*, *transition*, *head*), where *tail* and *head* are states, + and *transition* is the label of an arc from *head* to *tail*, + corresponding to the value of one variable in the list of variables. - This automaton will be unrolled into a flow with *n* + 1 phases. Each phase - contains the possible states of the automaton. The first state contains the - initial state. The last phase contains the final states. + This automaton will be unrolled into a flow with *n* + 1 phases. Each phase + contains the possible states of the automaton. The first state contains the + initial state. The last phase contains the final states. - Between two consecutive phases *i* and *i* + 1, the automaton creates a set - of arcs. For each transition (*tail*, *transition*, *head*), it will add - an arc from the state *tail* of phase *i* and the state *head* of phase - *i* + 1. This arc is labeled by the value *transition* of the variables - `variables[i]`. That is, this arc can only be selected if `variables[i]` - is assigned the value *transition*. + Between two consecutive phases *i* and *i* + 1, the automaton creates a set + of arcs. For each transition (*tail*, *transition*, *head*), it will add + an arc from the state *tail* of phase *i* and the state *head* of phase + *i* + 1. This arc is labeled by the value *transition* of the variables + `variables[i]`. That is, this arc can only be selected if `variables[i]` + is assigned the value *transition*. - A feasible solution of this constraint is an assignment of variables such - that, starting from the initial state in phase 0, there is a path labeled by - the values of the variables that ends in one of the final states in the - final phase. + A feasible solution of this constraint is an assignment of variables such + that, starting from the initial state in phase 0, there is a path labeled by + the values of the variables that ends in one of the final states in the + final phase. - Args: - transition_variables: A non-empty list of variables whose values - correspond to the labels of the arcs traversed by the automaton. - starting_state: The initial state of the automaton. - final_states: A non-empty list of admissible final states. - transition_triples: A list of transitions for the automaton, in the - following format (current_state, variable_value, next_state). + Args: + transition_variables: A non-empty list of variables whose values + correspond to the labels of the arcs traversed by the automaton. + starting_state: The initial state of the automaton. + final_states: A non-empty list of admissible final states. + transition_triples: A list of transitions for the automaton, in the + following format (current_state, variable_value, next_state). - Returns: - An instance of the `Constraint` class. + Returns: + An instance of the `Constraint` class. - Raises: - ValueError: if `transition_variables`, `final_states`, or - `transition_triples` are empty. - """ + Raises: + ValueError: if `transition_variables`, `final_states`, or + `transition_triples` are empty. + """ if not transition_variables: raise ValueError( - 'AddAutomaton expects a non-empty transition_variables ' - 'array') + "AddAutomaton expects a non-empty transition_variables " "array" + ) if not final_states: - raise ValueError('AddAutomaton expects some final states') + raise ValueError("AddAutomaton expects some final states") if not transition_triples: - raise ValueError('AddAutomaton expects some transition triples') + raise ValueError("AddAutomaton expects some transition triples") ct = Constraint(self.__model.constraints) model_ct = self.__model.constraints[ct.Index()] model_ct.automaton.vars.extend( - [self.GetOrMakeIndex(x) for x in transition_variables]) + [self.GetOrMakeIndex(x) for x in transition_variables] + ) starting_state = cmh.assert_is_int64(starting_state) model_ct.automaton.starting_state = starting_state for v in final_states: @@ -1327,8 +1359,7 @@ class CpModel(object): model_ct.automaton.final_states.append(v) for t in transition_triples: if len(t) != 3: - raise TypeError('Tuple ' + str(t) + - ' has the wrong arity (!= 3)') + raise TypeError("Tuple " + str(t) + " has the wrong arity (!= 3)") tail = cmh.assert_is_int64(t[0]) label = cmh.assert_is_int64(t[1]) head = cmh.assert_is_int64(t[2]) @@ -1340,157 +1371,160 @@ class CpModel(object): def AddInverse(self, variables, inverse_variables): """Adds Inverse(variables, inverse_variables). - An inverse constraint enforces that if `variables[i]` is assigned a value - `j`, then `inverse_variables[j]` is assigned a value `i`. And vice versa. + An inverse constraint enforces that if `variables[i]` is assigned a value + `j`, then `inverse_variables[j]` is assigned a value `i`. And vice versa. - Args: - variables: An array of integer variables. - inverse_variables: An array of integer variables. + Args: + variables: An array of integer variables. + inverse_variables: An array of integer variables. - Returns: - An instance of the `Constraint` class. + Returns: + An instance of the `Constraint` class. - Raises: - TypeError: if variables and inverse_variables have different lengths, or - if they are empty. - """ + Raises: + TypeError: if variables and inverse_variables have different lengths, or + if they are empty. + """ if not variables or not inverse_variables: - raise TypeError( - 'The Inverse constraint does not accept empty arrays') + raise TypeError("The Inverse constraint does not accept empty arrays") if len(variables) != len(inverse_variables): raise TypeError( - 'In the inverse constraint, the two array variables and' - ' inverse_variables must have the same length.') + "In the inverse constraint, the two array variables and" + " inverse_variables must have the same length." + ) ct = Constraint(self.__model.constraints) model_ct = self.__model.constraints[ct.Index()] - model_ct.inverse.f_direct.extend( - [self.GetOrMakeIndex(x) for x in variables]) + model_ct.inverse.f_direct.extend([self.GetOrMakeIndex(x) for x in variables]) model_ct.inverse.f_inverse.extend( - [self.GetOrMakeIndex(x) for x in inverse_variables]) + [self.GetOrMakeIndex(x) for x in inverse_variables] + ) return ct - def AddReservoirConstraint(self, times, level_changes, min_level, - max_level): + def AddReservoirConstraint(self, times, level_changes, min_level, max_level): """Adds Reservoir(times, level_changes, min_level, max_level). - Maintains a reservoir level within bounds. The water level starts at 0, and - at any time, it must be between min_level and max_level. + Maintains a reservoir level within bounds. The water level starts at 0, and + at any time, it must be between min_level and max_level. - If the affine expression `times[i]` is assigned a value t, then the current - level changes by `level_changes[i]`, which is constant, at time t. + If the affine expression `times[i]` is assigned a value t, then the current + level changes by `level_changes[i]`, which is constant, at time t. - Note that min level must be <= 0, and the max level must be >= 0. Please - use fixed level_changes to simulate initial state. + Note that min level must be <= 0, and the max level must be >= 0. Please + use fixed level_changes to simulate initial state. - Therefore, at any time: - sum(level_changes[i] if times[i] <= t) in [min_level, max_level] + Therefore, at any time: + sum(level_changes[i] if times[i] <= t) in [min_level, max_level] - Args: - times: A list of affine expressions which specify the time of the filling - or emptying the reservoir. - level_changes: A list of integer values that specifies the amount of the - emptying or filling. - min_level: At any time, the level of the reservoir must be greater or - equal than the min level. - max_level: At any time, the level of the reservoir must be less or equal - than the max level. + Args: + times: A list of affine expressions which specify the time of the filling + or emptying the reservoir. + level_changes: A list of integer values that specifies the amount of the + emptying or filling. + min_level: At any time, the level of the reservoir must be greater or + equal than the min level. + max_level: At any time, the level of the reservoir must be less or equal + than the max level. - Returns: - An instance of the `Constraint` class. + Returns: + An instance of the `Constraint` class. - Raises: - ValueError: if max_level < min_level. + Raises: + ValueError: if max_level < min_level. - ValueError: if max_level < 0. + ValueError: if max_level < 0. - ValueError: if min_level > 0 - """ + ValueError: if min_level > 0 + """ if max_level < min_level: - raise ValueError( - 'Reservoir constraint must have a max_level >= min_level') + raise ValueError("Reservoir constraint must have a max_level >= min_level") if max_level < 0: - raise ValueError('Reservoir constraint must have a max_level >= 0') + raise ValueError("Reservoir constraint must have a max_level >= 0") if min_level > 0: - raise ValueError('Reservoir constraint must have a min_level <= 0') + raise ValueError("Reservoir constraint must have a min_level <= 0") ct = Constraint(self.__model.constraints) model_ct = self.__model.constraints[ct.Index()] model_ct.reservoir.time_exprs.extend( - [self.ParseLinearExpression(x) for x in times]) + [self.ParseLinearExpression(x) for x in times] + ) model_ct.reservoir.level_changes.extend( - [self.ParseLinearExpression(x) for x in level_changes]) + [self.ParseLinearExpression(x) for x in level_changes] + ) model_ct.reservoir.min_level = min_level model_ct.reservoir.max_level = max_level return ct - def AddReservoirConstraintWithActive(self, times, level_changes, actives, - min_level, max_level): + def AddReservoirConstraintWithActive( + self, times, level_changes, actives, min_level, max_level + ): """Adds Reservoir(times, level_changes, actives, min_level, max_level). - Maintains a reservoir level within bounds. The water level starts at 0, and - at any time, it must be between min_level and max_level. + Maintains a reservoir level within bounds. The water level starts at 0, and + at any time, it must be between min_level and max_level. - If the variable `times[i]` is assigned a value t, and `actives[i]` is - `True`, then the current level changes by `level_changes[i]`, which is - constant, - at time t. + If the variable `times[i]` is assigned a value t, and `actives[i]` is + `True`, then the current level changes by `level_changes[i]`, which is + constant, + at time t. - Note that min level must be <= 0, and the max level must be >= 0. Please - use fixed level_changes to simulate initial state. + Note that min level must be <= 0, and the max level must be >= 0. Please + use fixed level_changes to simulate initial state. - Therefore, at any time: - sum(level_changes[i] * actives[i] if times[i] <= t) in [min_level, - max_level] + Therefore, at any time: + sum(level_changes[i] * actives[i] if times[i] <= t) in [min_level, + max_level] - The array of boolean variables 'actives', if defined, indicates which - actions are actually performed. + The array of boolean variables 'actives', if defined, indicates which + actions are actually performed. - Args: - times: A list of affine expressions which specify the time of the filling - or emptying the reservoir. - level_changes: A list of integer values that specifies the amount of the - emptying or filling. - actives: a list of boolean variables. They indicates if the - emptying/refilling events actually take place. - min_level: At any time, the level of the reservoir must be greater or - equal than the min level. - max_level: At any time, the level of the reservoir must be less or equal - than the max level. + Args: + times: A list of affine expressions which specify the time of the filling + or emptying the reservoir. + level_changes: A list of integer values that specifies the amount of the + emptying or filling. + actives: a list of boolean variables. They indicates if the + emptying/refilling events actually take place. + min_level: At any time, the level of the reservoir must be greater or + equal than the min level. + max_level: At any time, the level of the reservoir must be less or equal + than the max level. - Returns: - An instance of the `Constraint` class. + Returns: + An instance of the `Constraint` class. - Raises: - ValueError: if max_level < min_level. + Raises: + ValueError: if max_level < min_level. - ValueError: if max_level < 0. + ValueError: if max_level < 0. - ValueError: if min_level > 0 - """ + ValueError: if min_level > 0 + """ if max_level < min_level: - raise ValueError( - 'Reservoir constraint must have a max_level >= min_level') + raise ValueError("Reservoir constraint must have a max_level >= min_level") if max_level < 0: - raise ValueError('Reservoir constraint must have a max_level >= 0') + raise ValueError("Reservoir constraint must have a max_level >= 0") if min_level > 0: - raise ValueError('Reservoir constraint must have a min_level <= 0') + raise ValueError("Reservoir constraint must have a min_level <= 0") ct = Constraint(self.__model.constraints) model_ct = self.__model.constraints[ct.Index()] model_ct.reservoir.time_exprs.extend( - [self.ParseLinearExpression(x) for x in times]) + [self.ParseLinearExpression(x) for x in times] + ) model_ct.reservoir.level_changes.extend( - [self.ParseLinearExpression(x) for x in level_changes]) + [self.ParseLinearExpression(x) for x in level_changes] + ) model_ct.reservoir.active_literals.extend( - [self.GetOrMakeIndex(x) for x in actives]) + [self.GetOrMakeIndex(x) for x in actives] + ) model_ct.reservoir.min_level = min_level model_ct.reservoir.max_level = max_level return ct @@ -1528,10 +1562,9 @@ class CpModel(object): """Adds `Or(literals) == true`: Sum(literals) >= 1.""" ct = Constraint(self.__model.constraints) model_ct = self.__model.constraints[ct.Index()] - model_ct.bool_or.literals.extend([ - self.GetOrMakeBooleanIndex(x) - for x in ExpandGeneratorOrTuple(literals) - ]) + model_ct.bool_or.literals.extend( + [self.GetOrMakeBooleanIndex(x) for x in ExpandGeneratorOrTuple(literals)] + ) return ct def AddAtLeastOne(self, *literals): @@ -1542,50 +1575,46 @@ class CpModel(object): """Adds `AtMostOne(literals)`: `Sum(literals) <= 1`.""" ct = Constraint(self.__model.constraints) model_ct = self.__model.constraints[ct.Index()] - model_ct.at_most_one.literals.extend([ - self.GetOrMakeBooleanIndex(x) - for x in ExpandGeneratorOrTuple(literals) - ]) + model_ct.at_most_one.literals.extend( + [self.GetOrMakeBooleanIndex(x) for x in ExpandGeneratorOrTuple(literals)] + ) return ct def AddExactlyOne(self, *literals): """Adds `ExactlyOne(literals)`: `Sum(literals) == 1`.""" ct = Constraint(self.__model.constraints) model_ct = self.__model.constraints[ct.Index()] - model_ct.exactly_one.literals.extend([ - self.GetOrMakeBooleanIndex(x) - for x in ExpandGeneratorOrTuple(literals) - ]) + model_ct.exactly_one.literals.extend( + [self.GetOrMakeBooleanIndex(x) for x in ExpandGeneratorOrTuple(literals)] + ) return ct def AddBoolAnd(self, *literals): """Adds `And(literals) == true`.""" ct = Constraint(self.__model.constraints) model_ct = self.__model.constraints[ct.Index()] - model_ct.bool_and.literals.extend([ - self.GetOrMakeBooleanIndex(x) - for x in ExpandGeneratorOrTuple(literals) - ]) + model_ct.bool_and.literals.extend( + [self.GetOrMakeBooleanIndex(x) for x in ExpandGeneratorOrTuple(literals)] + ) return ct def AddBoolXOr(self, *literals): """Adds `XOr(literals) == true`. - In contrast to AddBoolOr and AddBoolAnd, it does not support - .OnlyEnforceIf(). + In contrast to AddBoolOr and AddBoolAnd, it does not support + .OnlyEnforceIf(). - Args: - *literals: the list of literals in the constraint. + Args: + *literals: the list of literals in the constraint. - Returns: - An `Constraint` object. - """ + Returns: + An `Constraint` object. + """ ct = Constraint(self.__model.constraints) model_ct = self.__model.constraints[ct.Index()] - model_ct.bool_xor.literals.extend([ - self.GetOrMakeBooleanIndex(x) - for x in ExpandGeneratorOrTuple(literals) - ]) + model_ct.bool_xor.literals.extend( + [self.GetOrMakeBooleanIndex(x) for x in ExpandGeneratorOrTuple(literals)] + ) return ct def AddMinEquality(self, target, exprs): @@ -1593,17 +1622,16 @@ class CpModel(object): ct = Constraint(self.__model.constraints) model_ct = self.__model.constraints[ct.Index()] model_ct.lin_max.exprs.extend( - [self.ParseLinearExpression(x, True) for x in exprs]) - model_ct.lin_max.target.CopyFrom( - self.ParseLinearExpression(target, True)) + [self.ParseLinearExpression(x, True) for x in exprs] + ) + model_ct.lin_max.target.CopyFrom(self.ParseLinearExpression(target, True)) return ct def AddMaxEquality(self, target, exprs): """Adds `target == Max(exprs)`.""" ct = Constraint(self.__model.constraints) model_ct = self.__model.constraints[ct.Index()] - model_ct.lin_max.exprs.extend( - [self.ParseLinearExpression(x) for x in exprs]) + model_ct.lin_max.exprs.extend([self.ParseLinearExpression(x) for x in exprs]) model_ct.lin_max.target.CopyFrom(self.ParseLinearExpression(target)) return ct @@ -1638,10 +1666,12 @@ class CpModel(object): """Adds `target == expressions[0] * .. * expressions[n]`.""" ct = Constraint(self.__model.constraints) model_ct = self.__model.constraints[ct.Index()] - model_ct.int_prod.exprs.extend([ - self.ParseLinearExpression(expr) - for expr in ExpandGeneratorOrTuple(expressions) - ]) + model_ct.int_prod.exprs.extend( + [ + self.ParseLinearExpression(expr) + for expr in ExpandGeneratorOrTuple(expressions) + ] + ) model_ct.int_prod.target.CopyFrom(self.ParseLinearExpression(target)) return ct @@ -1650,22 +1680,22 @@ class CpModel(object): def NewIntervalVar(self, start, size, end, name): """Creates an interval variable from start, size, and end. - An interval variable is a constraint, that is itself used in other - constraints like NoOverlap. + An interval variable is a constraint, that is itself used in other + constraints like NoOverlap. - Internally, it ensures that `start + size == end`. + Internally, it ensures that `start + size == end`. - Args: - start: The start of the interval. It can be an affine or constant - expression. - size: The size of the interval. It can be an affine or constant - expression. - end: The end of the interval. It can be an affine or constant expression. - name: The name of the interval variable. + Args: + start: The start of the interval. It can be an affine or constant + expression. + size: The size of the interval. It can be an affine or constant + expression. + end: The end of the interval. It can be an affine or constant expression. + name: The name of the interval variable. - Returns: - An `IntervalVar` object. - """ + Returns: + An `IntervalVar` object. + """ self.Add(start + size == end) @@ -1674,64 +1704,62 @@ class CpModel(object): end_expr = self.ParseLinearExpression(end) if len(start_expr.vars) > 1: raise TypeError( - 'cp_model.NewIntervalVar: start must be affine or constant.') + "cp_model.NewIntervalVar: start must be affine or constant." + ) if len(size_expr.vars) > 1: - raise TypeError( - 'cp_model.NewIntervalVar: size must be affine or constant.') + raise TypeError("cp_model.NewIntervalVar: size must be affine or constant.") if len(end_expr.vars) > 1: - raise TypeError( - 'cp_model.NewIntervalVar: end must be affine or constant.') - return IntervalVar(self.__model, start_expr, size_expr, end_expr, None, - name) + raise TypeError("cp_model.NewIntervalVar: end must be affine or constant.") + return IntervalVar(self.__model, start_expr, size_expr, end_expr, None, name) def NewFixedSizeIntervalVar(self, start, size, name): """Creates an interval variable from start, and a fixed size. - An interval variable is a constraint, that is itself used in other - constraints like NoOverlap. + An interval variable is a constraint, that is itself used in other + constraints like NoOverlap. - Args: - start: The start of the interval. It can be an affine or constant - expression. - size: The size of the interval. It must be an integer value. - name: The name of the interval variable. + Args: + start: The start of the interval. It can be an affine or constant + expression. + size: The size of the interval. It must be an integer value. + name: The name of the interval variable. - Returns: - An `IntervalVar` object. - """ + Returns: + An `IntervalVar` object. + """ size = cmh.assert_is_int64(size) start_expr = self.ParseLinearExpression(start) size_expr = self.ParseLinearExpression(size) end_expr = self.ParseLinearExpression(start + size) if len(start_expr.vars) > 1: raise TypeError( - 'cp_model.NewIntervalVar: start must be affine or constant.') - return IntervalVar(self.__model, start_expr, size_expr, end_expr, None, - name) + "cp_model.NewIntervalVar: start must be affine or constant." + ) + return IntervalVar(self.__model, start_expr, size_expr, end_expr, None, name) def NewOptionalIntervalVar(self, start, size, end, is_present, name): """Creates an optional interval var from start, size, end, and is_present. - An optional interval variable is a constraint, that is itself used in other - constraints like NoOverlap. This constraint is protected by an is_present - literal that indicates if it is active or not. + An optional interval variable is a constraint, that is itself used in other + constraints like NoOverlap. This constraint is protected by an is_present + literal that indicates if it is active or not. - Internally, it ensures that `is_present` implies `start + size == end`. + Internally, it ensures that `is_present` implies `start + size == end`. - Args: - start: The start of the interval. It can be an integer value, or an - integer variable. - size: The size of the interval. It can be an integer value, or an integer - variable. - end: The end of the interval. It can be an integer value, or an integer - variable. - is_present: A literal that indicates if the interval is active or not. A - inactive interval is simply ignored by all constraints. - name: The name of the interval variable. + Args: + start: The start of the interval. It can be an integer value, or an + integer variable. + size: The size of the interval. It can be an integer value, or an integer + variable. + end: The end of the interval. It can be an integer value, or an integer + variable. + is_present: A literal that indicates if the interval is active or not. A + inactive interval is simply ignored by all constraints. + name: The name of the interval variable. - Returns: - An `IntervalVar` object. - """ + Returns: + An `IntervalVar` object. + """ # Add the linear constraint. self.Add(start + size == end).OnlyEnforceIf(is_present) @@ -1743,115 +1771,120 @@ class CpModel(object): end_expr = self.ParseLinearExpression(end) if len(start_expr.vars) > 1: raise TypeError( - 'cp_model.NewIntervalVar: start must be affine or constant.') + "cp_model.NewIntervalVar: start must be affine or constant." + ) if len(size_expr.vars) > 1: - raise TypeError( - 'cp_model.NewIntervalVar: size must be affine or constant.') + raise TypeError("cp_model.NewIntervalVar: size must be affine or constant.") if len(end_expr.vars) > 1: - raise TypeError( - 'cp_model.NewIntervalVar: end must be affine or constant.') - return IntervalVar(self.__model, start_expr, size_expr, end_expr, - is_present_index, name) + raise TypeError("cp_model.NewIntervalVar: end must be affine or constant.") + return IntervalVar( + self.__model, start_expr, size_expr, end_expr, is_present_index, name + ) def NewOptionalFixedSizeIntervalVar(self, start, size, is_present, name): """Creates an interval variable from start, and a fixed size. - An interval variable is a constraint, that is itself used in other - constraints like NoOverlap. + An interval variable is a constraint, that is itself used in other + constraints like NoOverlap. - Args: - start: The start of the interval. It can be an affine or constant - expression. - size: The size of the interval. It must be an integer value. - is_present: A literal that indicates if the interval is active or not. A - inactive interval is simply ignored by all constraints. - name: The name of the interval variable. + Args: + start: The start of the interval. It can be an affine or constant + expression. + size: The size of the interval. It must be an integer value. + is_present: A literal that indicates if the interval is active or not. A + inactive interval is simply ignored by all constraints. + name: The name of the interval variable. - Returns: - An `IntervalVar` object. - """ + Returns: + An `IntervalVar` object. + """ size = cmh.assert_is_int64(size) start_expr = self.ParseLinearExpression(start) size_expr = self.ParseLinearExpression(size) end_expr = self.ParseLinearExpression(start + size) if len(start_expr.vars) > 1: raise TypeError( - 'cp_model.NewIntervalVar: start must be affine or constant.') + "cp_model.NewIntervalVar: start must be affine or constant." + ) is_present_index = self.GetOrMakeBooleanIndex(is_present) - return IntervalVar(self.__model, start_expr, size_expr, end_expr, - is_present_index, name) + return IntervalVar( + self.__model, start_expr, size_expr, end_expr, is_present_index, name + ) def AddNoOverlap(self, interval_vars): """Adds NoOverlap(interval_vars). - A NoOverlap constraint ensures that all present intervals do not overlap - in time. + A NoOverlap constraint ensures that all present intervals do not overlap + in time. - Args: - interval_vars: The list of interval variables to constrain. + Args: + interval_vars: The list of interval variables to constrain. - Returns: - An instance of the `Constraint` class. - """ + Returns: + An instance of the `Constraint` class. + """ ct = Constraint(self.__model.constraints) model_ct = self.__model.constraints[ct.Index()] model_ct.no_overlap.intervals.extend( - [self.GetIntervalIndex(x) for x in interval_vars]) + [self.GetIntervalIndex(x) for x in interval_vars] + ) return ct def AddNoOverlap2D(self, x_intervals, y_intervals): """Adds NoOverlap2D(x_intervals, y_intervals). - A NoOverlap2D constraint ensures that all present rectangles do not overlap - on a plane. Each rectangle is aligned with the X and Y axis, and is defined - by two intervals which represent its projection onto the X and Y axis. + A NoOverlap2D constraint ensures that all present rectangles do not overlap + on a plane. Each rectangle is aligned with the X and Y axis, and is defined + by two intervals which represent its projection onto the X and Y axis. - Furthermore, one box is optional if at least one of the x or y interval is - optional. + Furthermore, one box is optional if at least one of the x or y interval is + optional. - Args: - x_intervals: The X coordinates of the rectangles. - y_intervals: The Y coordinates of the rectangles. + Args: + x_intervals: The X coordinates of the rectangles. + y_intervals: The Y coordinates of the rectangles. - Returns: - An instance of the `Constraint` class. - """ + Returns: + An instance of the `Constraint` class. + """ ct = Constraint(self.__model.constraints) model_ct = self.__model.constraints[ct.Index()] model_ct.no_overlap_2d.x_intervals.extend( - [self.GetIntervalIndex(x) for x in x_intervals]) + [self.GetIntervalIndex(x) for x in x_intervals] + ) model_ct.no_overlap_2d.y_intervals.extend( - [self.GetIntervalIndex(x) for x in y_intervals]) + [self.GetIntervalIndex(x) for x in y_intervals] + ) return ct def AddCumulative(self, intervals, demands, capacity): """Adds Cumulative(intervals, demands, capacity). - This constraint enforces that: + This constraint enforces that: - for all t: - sum(demands[i] - if (start(intervals[i]) <= t < end(intervals[i])) and - (intervals[i] is present)) <= capacity + for all t: + sum(demands[i] + if (start(intervals[i]) <= t < end(intervals[i])) and + (intervals[i] is present)) <= capacity - Args: - intervals: The list of intervals. - demands: The list of demands for each interval. Each demand must be >= 0. - Each demand can be an integer value, or an integer variable. - capacity: The maximum capacity of the cumulative constraint. It must be a - positive integer value or variable. + Args: + intervals: The list of intervals. + demands: The list of demands for each interval. Each demand must be >= 0. + Each demand can be an integer value, or an integer variable. + capacity: The maximum capacity of the cumulative constraint. It must be a + positive integer value or variable. - Returns: - An instance of the `Constraint` class. - """ + Returns: + An instance of the `Constraint` class. + """ ct = Constraint(self.__model.constraints) model_ct = self.__model.constraints[ct.Index()] model_ct.cumulative.intervals.extend( - [self.GetIntervalIndex(x) for x in intervals]) + [self.GetIntervalIndex(x) for x in intervals] + ) for d in demands: model_ct.cumulative.demands.append(self.ParseLinearExpression(d)) - model_ct.cumulative.capacity.CopyFrom( - self.ParseLinearExpression(capacity)) + model_ct.cumulative.capacity.CopyFrom(self.ParseLinearExpression(capacity)) return ct # Support for deep copy. @@ -1868,33 +1901,34 @@ class CpModel(object): def GetBoolVarFromProtoIndex(self, index): """Returns an already created Boolean variable from its index.""" if index < 0 or index >= len(self.__model.variables): - raise ValueError( - f'GetBoolVarFromProtoIndex: out of bound index {index}') + raise ValueError(f"GetBoolVarFromProtoIndex: out of bound index {index}") var = self.__model.variables[index] if len(var.domain) != 2 or var.domain[0] < 0 or var.domain[1] > 1: raise ValueError( - f'GetBoolVarFromProtoIndex: index {index} does not reference' + - ' a Boolean variable') + f"GetBoolVarFromProtoIndex: index {index} does not reference" + + " a Boolean variable" + ) return IntVar(self.__model, index, None) def GetIntVarFromProtoIndex(self, index): """Returns an already created integer variable from its index.""" if index < 0 or index >= len(self.__model.variables): - raise ValueError( - f'GetIntVarFromProtoIndex: out of bound index {index}') + raise ValueError(f"GetIntVarFromProtoIndex: out of bound index {index}") return IntVar(self.__model, index, None) def GetIntervalVarFromProtoIndex(self, index): """Returns an already created interval variable from its index.""" if index < 0 or index >= len(self.__model.constraints): raise ValueError( - f'GetIntervalVarFromProtoIndex: out of bound index {index}') + f"GetIntervalVarFromProtoIndex: out of bound index {index}" + ) ct = self.__model.constraints[index] - if not ct.HasField('interval'): + if not ct.HasField("interval"): raise ValueError( - f'GetIntervalVarFromProtoIndex: index {index} does not reference an' - + ' interval variable') + f"GetIntervalVarFromProtoIndex: index {index} does not reference an" + + " interval variable" + ) return IntervalVar(self.__model, index, None, None, None, None) @@ -1914,15 +1948,17 @@ class CpModel(object): """Returns the index of a variable, its negation, or a number.""" if isinstance(arg, IntVar): return arg.Index() - elif (isinstance(arg, _ProductCst) and - isinstance(arg.Expression(), IntVar) and arg.Coefficient() == -1): + elif ( + isinstance(arg, _ProductCst) + and isinstance(arg.Expression(), IntVar) + and arg.Coefficient() == -1 + ): return -arg.Expression().Index() - 1 elif cmh.is_integral(arg): arg = cmh.assert_is_int64(arg) return self.GetOrMakeIndexFromConstant(arg) else: - raise TypeError('NotSupported: model.GetOrMakeIndex(' + str(arg) + - ')') + raise TypeError("NotSupported: model.GetOrMakeIndex(" + str(arg) + ")") def GetOrMakeBooleanIndex(self, arg): """Returns an index from a boolean expression.""" @@ -1936,12 +1972,13 @@ class CpModel(object): cmh.assert_is_boolean(arg) return self.GetOrMakeIndexFromConstant(int(arg)) else: - raise TypeError('NotSupported: model.GetOrMakeBooleanIndex(' + - str(arg) + ')') + raise TypeError( + "NotSupported: model.GetOrMakeBooleanIndex(" + str(arg) + ")" + ) def GetIntervalIndex(self, arg): if not isinstance(arg, IntervalVar): - raise TypeError('NotSupported: model.GetIntervalIndex(%s)' % arg) + raise TypeError("NotSupported: model.GetIntervalIndex(%s)" % arg) return arg.Index() def GetOrMakeIndexFromConstant(self, value): @@ -1976,7 +2013,7 @@ class CpModel(object): result.offset = constant * mult for t in coeffs_map.items(): if not isinstance(t[0], IntVar): - raise TypeError('Wrong argument' + str(t)) + raise TypeError("Wrong argument" + str(t)) c = cmh.assert_is_int64(t[1]) result.vars.append(t[0].Index()) result.coeffs.append(c * mult) @@ -2003,25 +2040,29 @@ class CpModel(object): else: self.__model.objective.scaling_factor = -1 self.__model.objective.offset = -constant - for v, c, in coeffs_map.items(): + for ( + v, + c, + ) in coeffs_map.items(): self.__model.objective.coeffs.append(c) if minimize: self.__model.objective.vars.append(v.Index()) else: - self.__model.objective.vars.append( - self.Negated(v.Index())) + self.__model.objective.vars.append(self.Negated(v.Index())) else: self.__model.floating_point_objective.maximize = not minimize self.__model.floating_point_objective.offset = constant - for v, c, in coeffs_map.items(): + for ( + v, + c, + ) in coeffs_map.items(): self.__model.floating_point_objective.coeffs.append(c) self.__model.floating_point_objective.vars.append(v.Index()) elif cmh.is_integral(obj): self.__model.objective.offset = int(obj) self.__model.objective.scaling_factor = 1 else: - raise TypeError('TypeError: ' + str(obj) + - ' is not a valid objective') + raise TypeError("TypeError: " + str(obj) + " is not a valid objective") def Minimize(self, obj): """Sets the objective of the model to minimize(obj).""" @@ -2032,24 +2073,25 @@ class CpModel(object): self._SetObjective(obj, minimize=False) def HasObjective(self): - return (self.__model.HasField('objective') or - self.__model.HasField('floating_point_objective')) + return self.__model.HasField("objective") or self.__model.HasField( + "floating_point_objective" + ) def ClearObjective(self): - self.__model.ClearField('objective') - self.__model.ClearField('floating_point_objective') + self.__model.ClearField("objective") + self.__model.ClearField("floating_point_objective") def AddDecisionStrategy(self, variables, var_strategy, domain_strategy): """Adds a search strategy to the model. - Args: - variables: a list of variables this strategy will assign. - var_strategy: heuristic to choose the next variable to assign. - domain_strategy: heuristic to reduce the domain of the selected variable. - Currently, this is advanced code: the union of all strategies added to - the model must be complete, i.e. instantiates all variables. Otherwise, - Solve() will fail. - """ + Args: + variables: a list of variables this strategy will assign. + var_strategy: heuristic to choose the next variable to assign. + domain_strategy: heuristic to reduce the domain of the selected variable. + Currently, this is advanced code: the union of all strategies added to + the model must be complete, i.e. instantiates all variables. Otherwise, + Solve() will fail. + """ strategy = self.__model.search_strategy.add() for v in variables: @@ -2060,36 +2102,37 @@ class CpModel(object): def ModelStats(self): """Returns a string containing some model statistics.""" return swig_helper.CpSatHelper.SerializedModelStats( - self.__model.SerializeToString()) + self.__model.SerializeToString() + ) def Validate(self): """Returns a string indicating that the model is invalid.""" return swig_helper.CpSatHelper.SerializedValidateModel( - self.__model.SerializeToString()) + self.__model.SerializeToString() + ) def ExportToFile(self, file): """Write the model as a protocol buffer to 'file'. - Args: - file: file to write the model to. If the filename ends with 'txt', the - model will be written as a text file, otherwise, the binary format will - be used. + Args: + file: file to write the model to. If the filename ends with 'txt', the + model will be written as a text file, otherwise, the binary format will + be used. - Returns: - True if the model was correctly written. - """ + Returns: + True if the model was correctly written. + """ return swig_helper.CpSatHelper.SerializedWriteModelToFile( - self.__model.SerializeToString(), file) + self.__model.SerializeToString(), file + ) def AssertIsBooleanVariable(self, x): if isinstance(x, IntVar): var = self.__model.variables[x.Index()] if len(var.domain) != 2 or var.domain[0] < 0 or var.domain[1] > 1: - raise TypeError('TypeError: ' + str(x) + - ' is not a boolean variable') + raise TypeError("TypeError: " + str(x) + " is not a boolean variable") elif not isinstance(x, _NotBooleanVariable): - raise TypeError('TypeError: ' + str(x) + - ' is not a boolean variable') + raise TypeError("TypeError: " + str(x) + " is not a boolean variable") def AddHint(self, var, value): """Adds 'var == value' as a hint to the solver.""" @@ -2098,7 +2141,7 @@ class CpModel(object): def ClearHints(self): """Remove any solution hint from the model.""" - self.__model.ClearField('solution_hint') + self.__model.ClearField("solution_hint") def AddAssumption(self, lit): """Add the literal 'lit' to the model as assumptions.""" @@ -2111,11 +2154,11 @@ class CpModel(object): def ClearAssumptions(self): """Remove all assumptions from the model.""" - self.__model.ClearField('assumptions') + self.__model.ClearField("assumptions") def ExpandGeneratorOrTuple(args): - if hasattr(args, '__len__'): # Tuple + if hasattr(args, "__len__"): # Tuple if len(args) != 1: return args if cmh.is_a_number(args[0]) or isinstance(args[0], LinearExpr): @@ -2129,8 +2172,7 @@ def EvaluateLinearExpr(expression, solution): if cmh.is_integral(expression): return int(expression) if not isinstance(expression, LinearExpr): - raise TypeError('Cannot interpret %s as a linear expression.' % - expression) + raise TypeError("Cannot interpret %s as a linear expression." % expression) value = 0 to_process = [(expression, 1)] @@ -2156,7 +2198,7 @@ def EvaluateLinearExpr(expression, solution): elif isinstance(expr, _NotBooleanVariable): value += coeff * (1 - solution.solution[expr.Not().Index()]) else: - raise TypeError(f'Cannot interpret {expr} as a linear expression.') + raise TypeError(f"Cannot interpret {expr} as a linear expression.") return value @@ -2165,27 +2207,26 @@ def EvaluateBooleanExpression(literal, solution): """Evaluate a boolean expression against a solution.""" if cmh.is_integral(literal): return bool(literal) - elif isinstance(literal, IntVar) or isinstance(literal, - _NotBooleanVariable): + elif isinstance(literal, IntVar) or isinstance(literal, _NotBooleanVariable): index = literal.Index() if index >= 0: return bool(solution.solution[index]) else: return not solution.solution[-index - 1] else: - raise TypeError(f'Cannot interpret {literal} as a boolean expression.') + raise TypeError(f"Cannot interpret {literal} as a boolean expression.") class CpSolver(object): """Main solver class. - The purpose of this class is to search for a solution to the model provided - to the Solve() method. + The purpose of this class is to search for a solution to the model provided + to the Solve() method. - Once Solve() is called, this class allows inspecting the solution found - with the Value() and BooleanValue() methods, as well as general statistics - about the solve procedure. - """ + Once Solve() is called, this class allows inspecting the solution found + with the Value() and BooleanValue() methods, as well as general statistics + about the solve procedure. + """ def __init__(self): self.__solution: Optional[cp_model_pb2.CpSolverResponse] = None @@ -2200,7 +2241,8 @@ class CpSolver(object): self.__solve_wrapper = swig_helper.SolveWrapper() swig_helper.SolveWrapper.SetSerializedParameters( - self.parameters.SerializeToString(), self.__solve_wrapper) + self.parameters.SerializeToString(), self.__solve_wrapper + ) if solution_callback is not None: self.__solve_wrapper.AddSolutionCallback(solution_callback) @@ -2209,7 +2251,9 @@ class CpSolver(object): self.__solution = cp_model_pb2.CpSolverResponse.FromString( swig_helper.SolveWrapper.SerializedSolve( - model.Proto().SerializeToString(), self.__solve_wrapper)) + model.Proto().SerializeToString(), self.__solve_wrapper + ) + ) if solution_callback is not None: self.__solve_wrapper.ClearSolutionCallback(solution_callback) @@ -2222,37 +2266,42 @@ class CpSolver(object): def SolveWithSolutionCallback(self, model, callback): """DEPRECATED Use Solve() with the callback argument.""" warnings.warn( - 'SolveWithSolutionCallback is deprecated; use Solve() with' + - 'the callback argument.', DeprecationWarning) + "SolveWithSolutionCallback is deprecated; use Solve() with" + + "the callback argument.", + DeprecationWarning, + ) return self.Solve(model, callback) def SearchForAllSolutions(self, model, callback): """DEPRECATED Use Solve() with the right parameter. - Search for all solutions of a satisfiability problem. + Search for all solutions of a satisfiability problem. - This method searches for all feasible solutions of a given model. - Then it feeds the solution to the callback. + This method searches for all feasible solutions of a given model. + Then it feeds the solution to the callback. - Note that the model cannot contain an objective. + Note that the model cannot contain an objective. - Args: - model: The model to solve. - callback: The callback that will be called at each solution. + Args: + model: The model to solve. + callback: The callback that will be called at each solution. - Returns: - The status of the solve: + Returns: + The status of the solve: - * *FEASIBLE* if some solutions have been found - * *INFEASIBLE* if the solver has proved there are no solution - * *OPTIMAL* if all solutions have been found - """ + * *FEASIBLE* if some solutions have been found + * *INFEASIBLE* if the solver has proved there are no solution + * *OPTIMAL* if all solutions have been found + """ warnings.warn( - 'SearchForAllSolutions is deprecated; use Solve() with' + - 'enumerate_all_solutions = True.', DeprecationWarning) + "SearchForAllSolutions is deprecated; use Solve() with" + + "enumerate_all_solutions = True.", + DeprecationWarning, + ) if model.HasObjective(): - raise TypeError('Search for all solutions is only defined on ' - 'satisfiability problems') + raise TypeError( + "Search for all solutions is only defined on " "satisfiability problems" + ) # Store old parameter. enumerate_all = self.parameters.enumerate_all_solutions self.parameters.enumerate_all_solutions = True @@ -2272,13 +2321,13 @@ class CpSolver(object): def Value(self, expression): """Returns the value of a linear expression after solve.""" if not self.__solution: - raise RuntimeError('Solve() has not been called.') + raise RuntimeError("Solve() has not been called.") return EvaluateLinearExpr(expression, self.__solution) def BooleanValue(self, literal): """Returns the boolean value of a literal after solve.""" if not self.__solution: - raise RuntimeError('Solve() has not been called.') + raise RuntimeError("Solve() has not been called.") return EvaluateBooleanExpression(literal, self.__solution) def ObjectiveValue(self): @@ -2318,7 +2367,8 @@ class CpSolver(object): def ResponseStats(self): """Returns some statistics on the solution found as a string.""" return swig_helper.CpSatHelper.SerializedSolverResponseStats( - self.__solution.SerializeToString()) + self.__solution.SerializeToString() + ) def ResponseProto(self): """Returns the response object.""" @@ -2331,35 +2381,35 @@ class CpSolver(object): def SolutionInfo(self): """Returns some information on the solve process. - Returns some information on how the solution was found, or the reason - why the model or the parameters are invalid. - """ + Returns some information on how the solution was found, or the reason + why the model or the parameters are invalid. + """ return self.__solution.solution_info class CpSolverSolutionCallback(swig_helper.SolutionCallback): """Solution callback. - This class implements a callback that will be called at each new solution - found during search. + This class implements a callback that will be called at each new solution + found during search. - The method OnSolutionCallback() will be called by the solver, and must be - implemented. The current solution can be queried using the BooleanValue() - and Value() methods. + The method OnSolutionCallback() will be called by the solver, and must be + implemented. The current solution can be queried using the BooleanValue() + and Value() methods. - It inherits the following methods from its base class: + It inherits the following methods from its base class: - * `ObjectiveValue(self)` - * `BestObjectiveBound(self)` - * `NumBooleans(self)` - * `NumConflicts(self)` - * `NumBranches(self)` - * `WallTime(self)` - * `UserTime(self)` + * `ObjectiveValue(self)` + * `BestObjectiveBound(self)` + * `NumBooleans(self)` + * `NumConflicts(self)` + * `NumBranches(self)` + * `WallTime(self)` + * `UserTime(self)` - These methods returns the same information as their counterpart in the - `CpSolver` class. - """ + These methods returns the same information as their counterpart in the + `CpSolver` class. + """ def __init__(self): swig_helper.SolutionCallback.__init__(self) @@ -2371,40 +2421,40 @@ class CpSolverSolutionCallback(swig_helper.SolutionCallback): def BooleanValue(self, lit): """Returns the boolean value of a boolean literal. - Args: - lit: A boolean variable or its negation. + Args: + lit: A boolean variable or its negation. - Returns: - The Boolean value of the literal in the solution. + Returns: + The Boolean value of the literal in the solution. - Raises: - RuntimeError: if `lit` is not a boolean variable or its negation. - """ + Raises: + RuntimeError: if `lit` is not a boolean variable or its negation. + """ if not self.HasResponse(): - raise RuntimeError('Solve() has not been called.') + raise RuntimeError("Solve() has not been called.") if cmh.is_integral(lit): return bool(lit) elif isinstance(lit, IntVar) or isinstance(lit, _NotBooleanVariable): index = lit.Index() return self.SolutionBooleanValue(index) else: - raise TypeError(f'Cannot interpret {lit} as a boolean expression.') + raise TypeError(f"Cannot interpret {lit} as a boolean expression.") def Value(self, expression): """Evaluates an linear expression in the current solution. - Args: - expression: a linear expression of the model. + Args: + expression: a linear expression of the model. - Returns: - An integer value equal to the evaluation of the linear expression - against the current solution. + Returns: + An integer value equal to the evaluation of the linear expression + against the current solution. - Raises: - RuntimeError: if 'expression' is not a LinearExpr. - """ + Raises: + RuntimeError: if 'expression' is not a LinearExpr. + """ if not self.HasResponse(): - raise RuntimeError('Solve() has not been called.') + raise RuntimeError("Solve() has not been called.") value = 0 to_process = [(expression, 1)] @@ -2413,8 +2463,7 @@ class CpSolverSolutionCallback(swig_helper.SolutionCallback): if cmh.is_integral(expr): value += int(expr) * coeff elif isinstance(expr, _ProductCst): - to_process.append( - (expr.Expression(), coeff * expr.Coefficient())) + to_process.append((expr.Expression(), coeff * expr.Coefficient())) elif isinstance(expr, _Sum): to_process.append((expr.Left(), coeff)) to_process.append((expr.Right(), coeff)) @@ -2429,18 +2478,19 @@ class CpSolverSolutionCallback(swig_helper.SolutionCallback): elif isinstance(expr, IntVar): value += coeff * self.SolutionIntegerValue(expr.Index()) elif isinstance(expr, _NotBooleanVariable): - value += coeff * (1 - - self.SolutionIntegerValue(expr.Not().Index())) + value += coeff * (1 - self.SolutionIntegerValue(expr.Not().Index())) else: raise TypeError( - f'Cannot interpret {expression} as a linear expression.') + f"Cannot interpret {expression} as a linear expression." + ) return value def Response(self): """Returns the current solution response.""" return cp_model_pb2.CpSolverResponse.FromString( - swig_helper.SolutionCallback.SerializedResponse(self)) + swig_helper.SolutionCallback.SerializedResponse(self) + ) class ObjectiveSolutionPrinter(CpSolverSolutionCallback): @@ -2455,8 +2505,10 @@ class ObjectiveSolutionPrinter(CpSolverSolutionCallback): """Called on each new solution.""" current_time = time.time() obj = self.ObjectiveValue() - print('Solution %i, time = %0.2f s, objective = %i' % - (self.__solution_count, current_time - self.__start_time, obj)) + print( + "Solution %i, time = %0.2f s, objective = %i" + % (self.__solution_count, current_time - self.__start_time, obj) + ) self.__solution_count += 1 def solution_count(self): @@ -2477,10 +2529,12 @@ class VarArrayAndObjectiveSolutionPrinter(CpSolverSolutionCallback): """Called on each new solution.""" current_time = time.time() obj = self.ObjectiveValue() - print('Solution %i, time = %0.2f s, objective = %i' % - (self.__solution_count, current_time - self.__start_time, obj)) + print( + "Solution %i, time = %0.2f s, objective = %i" + % (self.__solution_count, current_time - self.__start_time, obj) + ) for v in self.__variables: - print(' %s = %i' % (v, self.Value(v)), end=' ') + print(" %s = %i" % (v, self.Value(v)), end=" ") print() self.__solution_count += 1 @@ -2501,10 +2555,12 @@ class VarArraySolutionPrinter(CpSolverSolutionCallback): def on_solution_callback(self): """Called on each new solution.""" current_time = time.time() - print('Solution %i, time = %0.2f s' % - (self.__solution_count, current_time - self.__start_time)) + print( + "Solution %i, time = %0.2f s" + % (self.__solution_count, current_time - self.__start_time) + ) for v in self.__variables: - print(' %s = %i' % (v, self.Value(v)), end=' ') + print(" %s = %i" % (v, self.Value(v)), end=" ") print() self.__solution_count += 1 diff --git a/ortools/sat/python/cp_model_helper.py b/ortools/sat/python/cp_model_helper.py index 43a7e8aeb3..5bfc3b5c60 100644 --- a/ortools/sat/python/cp_model_helper.py +++ b/ortools/sat/python/cp_model_helper.py @@ -10,6 +10,7 @@ # 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. + """helpers methods for the cp_model module.""" import numbers @@ -28,56 +29,56 @@ def is_integral(x): def is_a_number(x): """Checks if x has either a number.Number or a np.double type.""" - return isinstance(x, numbers.Number) or isinstance( - x, np.double) or isinstance(x, np.integer) + return ( + isinstance(x, numbers.Number) + or isinstance(x, np.double) + or isinstance(x, np.integer) + ) def is_zero(x): """Checks if the x is 0 or 0.0.""" - return (is_integral(x) and int(x) == 0) or (is_a_number(x) and - float(x) == 0.0) + return (is_integral(x) and int(x) == 0) or (is_a_number(x) and float(x) == 0.0) def is_one(x): """Checks if x is 1 or 1.0.""" - return (is_integral(x) and int(x) == 1) or (is_a_number(x) and - float(x) == 1.0) + return (is_integral(x) and int(x) == 1) or (is_a_number(x) and float(x) == 1.0) def is_minus_one(x): """Checks if x is -1 or -1.0.""" - return (is_integral(x) and int(x) == -1) or (is_a_number(x) and - float(x) == -1.0) + return (is_integral(x) and int(x) == -1) or (is_a_number(x) and float(x) == -1.0) def assert_is_int64(x): """Asserts that x is integer and x is in [min_int_64, max_int_64].""" if not is_integral(x): - raise TypeError('Not an integer: %s' % x) + raise TypeError("Not an integer: %s" % x) if x < INT_MIN or x > INT_MAX: - raise OverflowError('Does not fit in an int64_t: %s' % x) + raise OverflowError("Does not fit in an int64_t: %s" % x) return int(x) def assert_is_int32(x): """Asserts that x is integer and x is in [min_int_32, max_int_32].""" if not is_integral(x): - raise TypeError('Not an integer: %s' % x) + raise TypeError("Not an integer: %s" % x) if x < INT32_MIN or x > INT32_MAX: - raise OverflowError('Does not fit in an int32_t: %s' % x) + raise OverflowError("Does not fit in an int32_t: %s" % x) return int(x) def assert_is_boolean(x): """Asserts that x is 0 or 1.""" if not is_integral(x) or x < 0 or x > 1: - raise TypeError('Not a boolean: %s' % x) + raise TypeError("Not a boolean: %s" % x) def assert_is_a_number(x): """Asserts that x is a number and returns it.""" if not is_a_number(x): - raise TypeError('Not a number: %s' % x) + raise TypeError("Not a number: %s" % x) elif is_integral(x): return int(x) else: @@ -101,8 +102,7 @@ def capped_subtraction(x, y): return x if x == y: if x == INT_MAX or x == INT_MIN: - raise OverflowError( - 'Integer NaN: subtracting INT_MAX or INT_MIN to itself') + raise OverflowError("Integer NaN: subtracting INT_MAX or INT_MIN to itself") return 0 if x == INT_MAX or x == INT_MIN: return x diff --git a/ortools/sat/python/cp_model_helper_test.py b/ortools/sat/python/cp_model_helper_test.py index fac70a597f..feee22d486 100644 --- a/ortools/sat/python/cp_model_helper_test.py +++ b/ortools/sat/python/cp_model_helper_test.py @@ -11,6 +11,7 @@ # 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. + """Tests for ortools.sat.python.cp_model_helper.""" from absl.testing import absltest @@ -18,64 +19,80 @@ from ortools.sat.python import cp_model_helper class CpModelHelperTest(absltest.TestCase): - def testassert_is_int64(self): - print('testassert_is_int64') - self.assertRaises(TypeError, cp_model_helper.assert_is_int64, 'Hello') + print("testassert_is_int64") + self.assertRaises(TypeError, cp_model_helper.assert_is_int64, "Hello") self.assertRaises(TypeError, cp_model_helper.assert_is_int64, 1.2) self.assertRaises(OverflowError, cp_model_helper.assert_is_int64, 2**63) - self.assertRaises(OverflowError, cp_model_helper.assert_is_int64, - -2**63 - 1) + self.assertRaises( + OverflowError, cp_model_helper.assert_is_int64, -(2**63) - 1 + ) cp_model_helper.assert_is_int64(123) cp_model_helper.assert_is_int64(2**63 - 1) - cp_model_helper.assert_is_int64(-2**63) + cp_model_helper.assert_is_int64(-(2**63)) def testto_capped_int64(self): - print('testto_capped_int64') + print("testto_capped_int64") self.assertEqual( cp_model_helper.to_capped_int64(cp_model_helper.INT_MAX), - cp_model_helper.INT_MAX) + cp_model_helper.INT_MAX, + ) self.assertEqual( cp_model_helper.to_capped_int64(cp_model_helper.INT_MAX + 1), - cp_model_helper.INT_MAX) + cp_model_helper.INT_MAX, + ) self.assertEqual( cp_model_helper.to_capped_int64(cp_model_helper.INT_MIN), - cp_model_helper.INT_MIN) + cp_model_helper.INT_MIN, + ) self.assertEqual( cp_model_helper.to_capped_int64(cp_model_helper.INT_MIN - 1), - cp_model_helper.INT_MIN) + cp_model_helper.INT_MIN, + ) self.assertEqual(cp_model_helper.to_capped_int64(15), 15) def testcapped_subtraction(self): - print('testcapped_subtraction') + print("testcapped_subtraction") self.assertEqual(cp_model_helper.capped_subtraction(10, 5), 5) self.assertEqual( cp_model_helper.capped_subtraction(cp_model_helper.INT_MIN, 5), - cp_model_helper.INT_MIN) + cp_model_helper.INT_MIN, + ) self.assertEqual( cp_model_helper.capped_subtraction(cp_model_helper.INT_MIN, -5), - cp_model_helper.INT_MIN) + cp_model_helper.INT_MIN, + ) self.assertEqual( cp_model_helper.capped_subtraction(cp_model_helper.INT_MAX, 5), - cp_model_helper.INT_MAX) + cp_model_helper.INT_MAX, + ) self.assertEqual( cp_model_helper.capped_subtraction(cp_model_helper.INT_MAX, -5), - cp_model_helper.INT_MAX) + cp_model_helper.INT_MAX, + ) self.assertEqual( cp_model_helper.capped_subtraction(2, cp_model_helper.INT_MIN), - cp_model_helper.INT_MAX) + cp_model_helper.INT_MAX, + ) self.assertEqual( cp_model_helper.capped_subtraction(2, cp_model_helper.INT_MAX), - cp_model_helper.INT_MIN) - self.assertRaises(OverflowError, cp_model_helper.capped_subtraction, - cp_model_helper.INT_MAX, cp_model_helper.INT_MAX) - self.assertRaises(OverflowError, cp_model_helper.capped_subtraction, - cp_model_helper.INT_MIN, cp_model_helper.INT_MIN) - self.assertRaises(TypeError, cp_model_helper.capped_subtraction, 5, - 'dummy') - self.assertRaises(TypeError, cp_model_helper.capped_subtraction, - 'dummy', 5) + cp_model_helper.INT_MIN, + ) + self.assertRaises( + OverflowError, + cp_model_helper.capped_subtraction, + cp_model_helper.INT_MAX, + cp_model_helper.INT_MAX, + ) + self.assertRaises( + OverflowError, + cp_model_helper.capped_subtraction, + cp_model_helper.INT_MIN, + cp_model_helper.INT_MIN, + ) + self.assertRaises(TypeError, cp_model_helper.capped_subtraction, 5, "dummy") + self.assertRaises(TypeError, cp_model_helper.capped_subtraction, "dummy", 5) -if __name__ == '__main__': +if __name__ == "__main__": absltest.main() diff --git a/ortools/sat/python/cp_model_test.py b/ortools/sat/python/cp_model_test.py index 7dbf43b2bf..328cd5a764 100644 --- a/ortools/sat/python/cp_model_test.py +++ b/ortools/sat/python/cp_model_test.py @@ -11,6 +11,7 @@ # 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. + """Tests for ortools.sat.python.cp_model.""" from absl.testing import absltest @@ -64,44 +65,42 @@ class LogToString(object): """Record log in a string.""" def __init__(self): - self.__log = '' + self.__log = "" def NewMessage(self, message: str): self.__log += message - self.__log += '\n' + self.__log += "\n" def Log(self): return self.__log class CpModelTest(absltest.TestCase): - def testCreateIntegerVariable(self): - print('testCreateIntegerVariable') + print("testCreateIntegerVariable") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') - self.assertEqual('x', str(x)) - self.assertEqual('x(-10..10)', repr(x)) - y = model.NewIntVarFromDomain( - cp_model.Domain.FromIntervals([[2, 4], [7]]), 'y') - self.assertEqual('y', str(y)) - self.assertEqual('y(2..4, 7)', repr(y)) - z = model.NewIntVarFromDomain(cp_model.Domain.FromValues([2, 3, 4, 7]), - 'z') - self.assertEqual('z', str(z)) - self.assertEqual('z(2..4, 7)', repr(z)) + x = model.NewIntVar(-10, 10, "x") + self.assertEqual("x", str(x)) + self.assertEqual("x(-10..10)", repr(x)) + y = model.NewIntVarFromDomain(cp_model.Domain.FromIntervals([[2, 4], [7]]), "y") + self.assertEqual("y", str(y)) + self.assertEqual("y(2..4, 7)", repr(y)) + z = model.NewIntVarFromDomain(cp_model.Domain.FromValues([2, 3, 4, 7]), "z") + self.assertEqual("z", str(z)) + self.assertEqual("z(2..4, 7)", repr(z)) t = model.NewIntVarFromDomain( - cp_model.Domain.FromFlatIntervals([2, 4, 7, 7]), 't') - self.assertEqual('t', str(t)) - self.assertEqual('t(2..4, 7)', repr(t)) + cp_model.Domain.FromFlatIntervals([2, 4, 7, 7]), "t" + ) + self.assertEqual("t", str(t)) + self.assertEqual("t(2..4, 7)", repr(t)) cst = model.NewConstant(5) - self.assertEqual('5', str(cst)) + self.assertEqual("5", str(cst)) def testNegation(self): - print('testNegation') + print("testNegation") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') - b = model.NewBoolVar('b') + x = model.NewIntVar(-10, 10, "x") + b = model.NewBoolVar("b") nb = b.Not() self.assertEqual(b.Not(), nb) self.assertEqual(b.Not().Not(), b) @@ -109,18 +108,18 @@ class CpModelTest(absltest.TestCase): self.assertRaises(TypeError, x.Not) def testEqualityOverload(self): - print('testEqualityOverload') + print("testEqualityOverload") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') - y = model.NewIntVar(0, 5, 'y') + x = model.NewIntVar(-10, 10, "x") + y = model.NewIntVar(0, 5, "y") self.assertEqual(x, x) self.assertNotEqual(x, y) def testLinear(self): - print('testLinear') + print("testLinear") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') - y = model.NewIntVar(-10, 10, 'y') + x = model.NewIntVar(-10, 10, "x") + y = model.NewIntVar(-10, 10, "y") model.AddLinearConstraint(x + 2 * y, 0, 10) model.Minimize(y) solver = cp_model.CpSolver() @@ -129,10 +128,10 @@ class CpModelTest(absltest.TestCase): self.assertEqual(-5, solver.Value(y)) def testLinearNonEqual(self): - print('testLinearNonEqual') + print("testLinearNonEqual") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') - y = model.NewIntVar(-10, 10, 'y') + x = model.NewIntVar(-10, 10, "x") + y = model.NewIntVar(-10, 10, "y") ct = model.Add(-x + y != 3).Proto() self.assertLen(ct.linear.domain, 4) self.assertEqual(cp_model.INT_MIN, ct.linear.domain[0]) @@ -141,9 +140,9 @@ class CpModelTest(absltest.TestCase): self.assertEqual(cp_model.INT_MAX, ct.linear.domain[3]) def testEq(self): - print('testEq') + print("testEq") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') + x = model.NewIntVar(-10, 10, "x") ct = model.Add(x == 2).Proto() self.assertLen(ct.linear.vars, 1) self.assertLen(ct.linear.coeffs, 1) @@ -152,9 +151,9 @@ class CpModelTest(absltest.TestCase): self.assertEqual(2, ct.linear.domain[1]) def testGe(self): - print('testGe') + print("testGe") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') + x = model.NewIntVar(-10, 10, "x") ct = model.Add(x >= 2).Proto() self.assertLen(ct.linear.vars, 1) self.assertLen(ct.linear.coeffs, 1) @@ -163,9 +162,9 @@ class CpModelTest(absltest.TestCase): self.assertEqual(cp_model.INT_MAX, ct.linear.domain[1]) def testGt(self): - print('testGt') + print("testGt") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') + x = model.NewIntVar(-10, 10, "x") ct = model.Add(x > 2).Proto() self.assertLen(ct.linear.vars, 1) self.assertLen(ct.linear.coeffs, 1) @@ -174,9 +173,9 @@ class CpModelTest(absltest.TestCase): self.assertEqual(cp_model.INT_MAX, ct.linear.domain[1]) def testLe(self): - print('testLe') + print("testLe") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') + x = model.NewIntVar(-10, 10, "x") ct = model.Add(x <= 2).Proto() self.assertLen(ct.linear.vars, 1) self.assertLen(ct.linear.coeffs, 1) @@ -185,9 +184,9 @@ class CpModelTest(absltest.TestCase): self.assertEqual(2, ct.linear.domain[1]) def testLt(self): - print('testLt') + print("testLt") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') + x = model.NewIntVar(-10, 10, "x") ct = model.Add(x < 2).Proto() self.assertLen(ct.linear.vars, 1) self.assertLen(ct.linear.coeffs, 1) @@ -196,10 +195,10 @@ class CpModelTest(absltest.TestCase): self.assertEqual(1, ct.linear.domain[1]) def testEqVar(self): - print('testEqVar') + print("testEqVar") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') - y = model.NewIntVar(-10, 10, 'y') + x = model.NewIntVar(-10, 10, "x") + y = model.NewIntVar(-10, 10, "y") ct = model.Add(x == y + 2).Proto() self.assertLen(ct.linear.vars, 2) self.assertEqual(1, ct.linear.vars[0] + ct.linear.vars[1]) @@ -210,10 +209,10 @@ class CpModelTest(absltest.TestCase): self.assertEqual(2, ct.linear.domain[1]) def testGeVar(self): - print('testGeVar') + print("testGeVar") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') - y = model.NewIntVar(-10, 10, 'y') + x = model.NewIntVar(-10, 10, "x") + y = model.NewIntVar(-10, 10, "y") ct = model.Add(x >= 1 - y).Proto() self.assertLen(ct.linear.vars, 2) self.assertEqual(1, ct.linear.vars[0] + ct.linear.vars[1]) @@ -225,10 +224,10 @@ class CpModelTest(absltest.TestCase): self.assertEqual(cp_model.INT_MAX, ct.linear.domain[1]) def testGtVar(self): - print('testGeVar') + print("testGeVar") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') - y = model.NewIntVar(-10, 10, 'y') + x = model.NewIntVar(-10, 10, "x") + y = model.NewIntVar(-10, 10, "y") ct = model.Add(x > 1 - y).Proto() self.assertLen(ct.linear.vars, 2) self.assertEqual(1, ct.linear.vars[0] + ct.linear.vars[1]) @@ -240,10 +239,10 @@ class CpModelTest(absltest.TestCase): self.assertEqual(cp_model.INT_MAX, ct.linear.domain[1]) def testLeVar(self): - print('testLeVar') + print("testLeVar") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') - y = model.NewIntVar(-10, 10, 'y') + x = model.NewIntVar(-10, 10, "x") + y = model.NewIntVar(-10, 10, "y") ct = model.Add(x <= 1 - y).Proto() self.assertLen(ct.linear.vars, 2) self.assertEqual(1, ct.linear.vars[0] + ct.linear.vars[1]) @@ -255,10 +254,10 @@ class CpModelTest(absltest.TestCase): self.assertEqual(1, ct.linear.domain[1]) def testLtVar(self): - print('testLtVar') + print("testLtVar") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') - y = model.NewIntVar(-10, 10, 'y') + x = model.NewIntVar(-10, 10, "x") + y = model.NewIntVar(-10, 10, "y") ct = model.Add(x < 1 - y).Proto() self.assertLen(ct.linear.vars, 2) self.assertEqual(1, ct.linear.vars[0] + ct.linear.vars[1]) @@ -270,42 +269,42 @@ class CpModelTest(absltest.TestCase): self.assertEqual(0, ct.linear.domain[1]) def testSimplification1(self): - print('testSimplification1') + print("testSimplification1") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') + x = model.NewIntVar(-10, 10, "x") prod = (x * 2) * 2 self.assertEqual(x, prod.Expression()) self.assertEqual(4, prod.Coefficient()) def testSimplification2(self): - print('testSimplification2') + print("testSimplification2") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') + x = model.NewIntVar(-10, 10, "x") prod = 2 * (x * 2) self.assertEqual(x, prod.Expression()) self.assertEqual(4, prod.Coefficient()) def testSimplification3(self): - print('testSimplification3') + print("testSimplification3") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') + x = model.NewIntVar(-10, 10, "x") prod = (2 * x) * 2 self.assertEqual(x, prod.Expression()) self.assertEqual(4, prod.Coefficient()) def testSimplification4(self): - print('testSimplification4') + print("testSimplification4") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') + x = model.NewIntVar(-10, 10, "x") prod = 2 * (2 * x) self.assertEqual(x, prod.Expression()) self.assertEqual(4, prod.Coefficient()) def testLinearNonEqualWithConstant(self): - print('testLinearNonEqualWithConstant') + print("testLinearNonEqualWithConstant") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') - y = model.NewIntVar(-10, 10, 'y') + x = model.NewIntVar(-10, 10, "x") + y = model.NewIntVar(-10, 10, "y") ct = model.Add(x + y + 5 != 3).Proto() self.assertLen(ct.linear.domain, 4) # Checks that saturated arithmetics worked. @@ -315,142 +314,141 @@ class CpModelTest(absltest.TestCase): self.assertEqual(cp_model.INT_MAX, ct.linear.domain[3]) def testLinearWithEnforcement(self): - print('testLinearWithEnforcement') + print("testLinearWithEnforcement") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') - y = model.NewIntVar(-10, 10, 'y') - b = model.NewBoolVar('b') + x = model.NewIntVar(-10, 10, "x") + y = model.NewIntVar(-10, 10, "y") + b = model.NewBoolVar("b") model.AddLinearConstraint(x + 2 * y, 0, 10).OnlyEnforceIf(b.Not()) model.Minimize(y) self.assertLen(model.Proto().constraints, 1) - self.assertEqual(-3, - model.Proto().constraints[0].enforcement_literal[0]) - c = model.NewBoolVar('c') + self.assertEqual(-3, model.Proto().constraints[0].enforcement_literal[0]) + c = model.NewBoolVar("c") model.AddLinearConstraint(x + 4 * y, 0, 10).OnlyEnforceIf([b, c]) self.assertLen(model.Proto().constraints, 2) self.assertEqual(2, model.Proto().constraints[1].enforcement_literal[0]) self.assertEqual(3, model.Proto().constraints[1].enforcement_literal[1]) model.AddLinearConstraint(x + 5 * y, 0, 10).OnlyEnforceIf(c.Not(), b) self.assertLen(model.Proto().constraints, 3) - self.assertEqual(-4, - model.Proto().constraints[2].enforcement_literal[0]) + self.assertEqual(-4, model.Proto().constraints[2].enforcement_literal[0]) self.assertEqual(2, model.Proto().constraints[2].enforcement_literal[1]) def testConstraintWithName(self): - print('testConstraintWithName') + print("testConstraintWithName") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') - y = model.NewIntVar(-10, 10, 'y') - ct = model.AddLinearConstraint(x + 2 * y, 0, - 10).WithName('test_constraint') - self.assertEqual('test_constraint', ct.Name()) + x = model.NewIntVar(-10, 10, "x") + y = model.NewIntVar(-10, 10, "y") + ct = model.AddLinearConstraint(x + 2 * y, 0, 10).WithName("test_constraint") + self.assertEqual("test_constraint", ct.Name()) def testNaturalApiMinimize(self): - print('testNaturalApiMinimize') + print("testNaturalApiMinimize") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') - y = model.NewIntVar(-10, 10, 'y') + x = model.NewIntVar(-10, 10, "x") + y = model.NewIntVar(-10, 10, "y") model.Add(x * 2 - 1 * y == 1) model.Minimize(x * 1 - 2 * y + 3) solver = cp_model.CpSolver() - self.assertEqual('OPTIMAL', solver.StatusName(solver.Solve(model))) + self.assertEqual("OPTIMAL", solver.StatusName(solver.Solve(model))) self.assertEqual(5, solver.Value(x)) self.assertEqual(15, solver.Value(x * 3)) self.assertEqual(6, solver.Value(1 + x)) self.assertEqual(-10.0, solver.ObjectiveValue()) def testNaturalApiMaximizeFloat(self): - print('testNaturalApiMaximizeFloat') + print("testNaturalApiMaximizeFloat") model = cp_model.CpModel() - x = model.NewBoolVar('x') - y = model.NewIntVar(0, 10, 'y') + x = model.NewBoolVar("x") + y = model.NewIntVar(0, 10, "y") model.Maximize(x.Not() * 3.5 + x.Not() - y + 2 * y + 1.6) solver = cp_model.CpSolver() - self.assertEqual('OPTIMAL', solver.StatusName(solver.Solve(model))) + self.assertEqual("OPTIMAL", solver.StatusName(solver.Solve(model))) self.assertFalse(solver.BooleanValue(x)) self.assertTrue(solver.BooleanValue(x.Not())) self.assertEqual(-10, solver.Value(-y)) self.assertEqual(16.1, solver.ObjectiveValue()) def testNaturalApiMaximizeComplex(self): - print('testNaturalApiMaximizeFloat') + print("testNaturalApiMaximizeFloat") model = cp_model.CpModel() - x1 = model.NewBoolVar('x1') - x2 = model.NewBoolVar('x1') - x3 = model.NewBoolVar('x1') - x4 = model.NewBoolVar('x1') + x1 = model.NewBoolVar("x1") + x2 = model.NewBoolVar("x1") + x3 = model.NewBoolVar("x1") + x4 = model.NewBoolVar("x1") model.Maximize( - cp_model.LinearExpr.Sum([x1, x2]) + - cp_model.LinearExpr.WeightedSum([x3, x4.Not()], [2, 4])) + cp_model.LinearExpr.Sum([x1, x2]) + + cp_model.LinearExpr.WeightedSum([x3, x4.Not()], [2, 4]) + ) solver = cp_model.CpSolver() - self.assertEqual('OPTIMAL', solver.StatusName(solver.Solve(model))) + self.assertEqual("OPTIMAL", solver.StatusName(solver.Solve(model))) self.assertEqual(5, solver.Value(3 + 2 * x1)) self.assertEqual(3, solver.Value(x1 + x2 + x3)) - self.assertEqual( - 1, solver.Value(cp_model.LinearExpr.Sum([x1, x2, x3, 0, -2]))) + self.assertEqual(1, solver.Value(cp_model.LinearExpr.Sum([x1, x2, x3, 0, -2]))) self.assertEqual( 7, solver.Value( - cp_model.LinearExpr.WeightedSum([x1, x2, x4, 3], [2, 2, 2, 1]))) + cp_model.LinearExpr.WeightedSum([x1, x2, x4, 3], [2, 2, 2, 1]) + ), + ) self.assertEqual(5, solver.Value(5 * x4.Not())) self.assertEqual(8, solver.ObjectiveValue()) def testNaturalApiMaximize(self): - print('testNaturalApiMaximize') + print("testNaturalApiMaximize") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') - y = model.NewIntVar(-10, 10, 'y') + x = model.NewIntVar(-10, 10, "x") + y = model.NewIntVar(-10, 10, "y") model.Add(2 * x - y == 1) model.Maximize(x - 2 * y + 3) solver = cp_model.CpSolver() - self.assertEqual('OPTIMAL', solver.StatusName(solver.Solve(model))) + self.assertEqual("OPTIMAL", solver.StatusName(solver.Solve(model))) self.assertEqual(-4, solver.Value(x)) self.assertEqual(-9, solver.Value(y)) self.assertEqual(17, solver.ObjectiveValue()) def testMinimizeConstant(self): - print('testMinimizeConstant') + print("testMinimizeConstant") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') + x = model.NewIntVar(-10, 10, "x") model.Add(x >= -1) model.Minimize(10) solver = cp_model.CpSolver() - self.assertEqual('OPTIMAL', solver.StatusName(solver.Solve(model))) + self.assertEqual("OPTIMAL", solver.StatusName(solver.Solve(model))) self.assertEqual(10, solver.ObjectiveValue()) def testMaximizeConstant(self): - print('testMinimizeConstant') + print("testMinimizeConstant") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') + x = model.NewIntVar(-10, 10, "x") model.Add(x >= -1) model.Maximize(5) solver = cp_model.CpSolver() - self.assertEqual('OPTIMAL', solver.StatusName(solver.Solve(model))) + self.assertEqual("OPTIMAL", solver.StatusName(solver.Solve(model))) self.assertEqual(5, solver.ObjectiveValue()) def testAddTrue(self): - print('testAddTrue') + print("testAddTrue") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') + x = model.NewIntVar(-10, 10, "x") model.Add(3 >= -1) model.Minimize(x) solver = cp_model.CpSolver() - self.assertEqual('OPTIMAL', solver.StatusName(solver.Solve(model))) + self.assertEqual("OPTIMAL", solver.StatusName(solver.Solve(model))) self.assertEqual(-10, solver.Value(x)) def testAddFalse(self): - print('testAddFalse') + print("testAddFalse") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') + x = model.NewIntVar(-10, 10, "x") model.Add(3 <= -1) model.Minimize(x) solver = cp_model.CpSolver() - self.assertEqual('INFEASIBLE', solver.StatusName(solver.Solve(model))) + self.assertEqual("INFEASIBLE", solver.StatusName(solver.Solve(model))) def testSum(self): - print('testSum') + print("testSum") model = cp_model.CpModel() - x = [model.NewIntVar(0, 2, 'x%i' % i) for i in range(100)] + x = [model.NewIntVar(0, 2, "x%i" % i) for i in range(100)] model.Add(sum(x) <= 1) model.Maximize(x[99]) solver = cp_model.CpSolver() @@ -460,9 +458,9 @@ class CpModelTest(absltest.TestCase): self.assertEqual(solver.Value(x[i]), 1 if i == 99 else 0) def testSumWithApi(self): - print('testSumWithApi') + print("testSumWithApi") model = cp_model.CpModel() - x = [model.NewIntVar(0, 2, 'x%i' % i) for i in range(100)] + x = [model.NewIntVar(0, 2, "x%i" % i) for i in range(100)] model.Add(cp_model.LinearExpr.Sum(x) <= 1) model.Maximize(x[99]) solver = cp_model.CpSolver() @@ -472,9 +470,9 @@ class CpModelTest(absltest.TestCase): self.assertEqual(solver.Value(x[i]), 1 if i == 99 else 0) def testWeightedSum(self): - print('testWeightedSum') + print("testWeightedSum") model = cp_model.CpModel() - x = [model.NewIntVar(0, 2, 'x%i' % i) for i in range(100)] + x = [model.NewIntVar(0, 2, "x%i" % i) for i in range(100)] c = [2 for i in range(100)] model.Add(cp_model.LinearExpr.WeightedSum(x, c) <= 3) model.Maximize(x[99]) @@ -485,36 +483,35 @@ class CpModelTest(absltest.TestCase): self.assertEqual(solver.Value(x[i]), 1 if i == 99 else 0) def testAllDifferent(self): - print('testAllDifferent') + print("testAllDifferent") model = cp_model.CpModel() - x = [model.NewIntVar(0, 4, 'x%i' % i) for i in range(5)] + x = [model.NewIntVar(0, 4, "x%i" % i) for i in range(5)] model.AddAllDifferent(x) self.assertLen(model.Proto().variables, 5) self.assertLen(model.Proto().constraints, 1) self.assertLen(model.Proto().constraints[0].all_diff.exprs, 5) def testAllDifferentGen(self): - print('testAllDifferentGen') + print("testAllDifferentGen") model = cp_model.CpModel() - model.AddAllDifferent( - model.NewIntVar(0, 4, 'x%i' % i) for i in range(5)) + model.AddAllDifferent(model.NewIntVar(0, 4, "x%i" % i) for i in range(5)) self.assertLen(model.Proto().variables, 5) self.assertLen(model.Proto().constraints, 1) self.assertLen(model.Proto().constraints[0].all_diff.exprs, 5) def testAllDifferentList(self): - print('testAllDifferentList') + print("testAllDifferentList") model = cp_model.CpModel() - x = [model.NewIntVar(0, 4, 'x%i' % i) for i in range(5)] + x = [model.NewIntVar(0, 4, "x%i" % i) for i in range(5)] model.AddAllDifferent(x[0], x[1], x[2], x[3], x[4]) self.assertLen(model.Proto().variables, 5) self.assertLen(model.Proto().constraints, 1) self.assertLen(model.Proto().constraints[0].all_diff.exprs, 5) def testElement(self): - print('testElement') + print("testElement") model = cp_model.CpModel() - x = [model.NewIntVar(0, 4, 'x%i' % i) for i in range(5)] + x = [model.NewIntVar(0, 4, "x%i" % i) for i in range(5)] model.AddElement(x[0], [x[1], 2, 4, x[2]], x[4]) self.assertLen(model.Proto().variables, 7) self.assertLen(model.Proto().constraints, 1) @@ -524,9 +521,9 @@ class CpModelTest(absltest.TestCase): self.assertRaises(ValueError, model.AddElement, x[0], [], x[4]) def testCircuit(self): - print('testCircuit') + print("testCircuit") model = cp_model.CpModel() - x = [model.NewBoolVar(f'x{i}') for i in range(5)] + x = [model.NewBoolVar(f"x{i}") for i in range(5)] model.AddCircuit((i, i + 1, x[i]) for i in range(5)) self.assertLen(model.Proto().variables, 5) self.assertLen(model.Proto().constraints, 1) @@ -536,9 +533,9 @@ class CpModelTest(absltest.TestCase): self.assertRaises(ValueError, model.AddCircuit, []) def testMultipleCircuit(self): - print('testMultipleCircuit') + print("testMultipleCircuit") model = cp_model.CpModel() - x = [model.NewBoolVar(f'x{i}') for i in range(5)] + x = [model.NewBoolVar(f"x{i}") for i in range(5)] model.AddMultipleCircuit((i, i + 1, x[i]) for i in range(5)) self.assertLen(model.Proto().variables, 5) self.assertLen(model.Proto().constraints, 1) @@ -548,68 +545,93 @@ class CpModelTest(absltest.TestCase): self.assertRaises(ValueError, model.AddMultipleCircuit, []) def testAllowedAssignments(self): - print('testAllowedAssignments') + print("testAllowedAssignments") model = cp_model.CpModel() - x = [model.NewIntVar(0, 4, 'x%i' % i) for i in range(5)] - model.AddAllowedAssignments(x, [(0, 1, 2, 3, 4), (4, 3, 2, 1, 1), - (0, 0, 0, 0, 0)]) + x = [model.NewIntVar(0, 4, "x%i" % i) for i in range(5)] + model.AddAllowedAssignments( + x, [(0, 1, 2, 3, 4), (4, 3, 2, 1, 1), (0, 0, 0, 0, 0)] + ) self.assertLen(model.Proto().variables, 5) self.assertLen(model.Proto().constraints, 1) self.assertLen(model.Proto().constraints[0].table.vars, 5) self.assertLen(model.Proto().constraints[0].table.values, 15) - self.assertRaises(TypeError, model.AddAllowedAssignments, x, - [(0, 1, 2, 3, 4), (4, 3, 2, 1, 1), (0, 0, 0, 0)]) - self.assertRaises(ValueError, model.AddAllowedAssignments, [], - [(0, 1, 2, 3, 4), (4, 3, 2, 1, 1), (0, 0, 0, 0)]) + self.assertRaises( + TypeError, + model.AddAllowedAssignments, + x, + [(0, 1, 2, 3, 4), (4, 3, 2, 1, 1), (0, 0, 0, 0)], + ) + self.assertRaises( + ValueError, + model.AddAllowedAssignments, + [], + [(0, 1, 2, 3, 4), (4, 3, 2, 1, 1), (0, 0, 0, 0)], + ) def testForbiddenAssignments(self): - print('testForbiddenAssignments') + print("testForbiddenAssignments") model = cp_model.CpModel() - x = [model.NewIntVar(0, 4, 'x%i' % i) for i in range(5)] - model.AddForbiddenAssignments(x, [(0, 1, 2, 3, 4), (4, 3, 2, 1, 1), - (0, 0, 0, 0, 0)]) + x = [model.NewIntVar(0, 4, "x%i" % i) for i in range(5)] + model.AddForbiddenAssignments( + x, [(0, 1, 2, 3, 4), (4, 3, 2, 1, 1), (0, 0, 0, 0, 0)] + ) self.assertLen(model.Proto().variables, 5) self.assertLen(model.Proto().constraints, 1) self.assertLen(model.Proto().constraints[0].table.vars, 5) self.assertLen(model.Proto().constraints[0].table.values, 15) self.assertTrue(model.Proto().constraints[0].table.negated) - self.assertRaises(TypeError, model.AddForbiddenAssignments, x, - [(0, 1, 2, 3, 4), (4, 3, 2, 1, 1), (0, 0, 0, 0)]) - self.assertRaises(ValueError, model.AddForbiddenAssignments, [], - [(0, 1, 2, 3, 4), (4, 3, 2, 1, 1), (0, 0, 0, 0)]) + self.assertRaises( + TypeError, + model.AddForbiddenAssignments, + x, + [(0, 1, 2, 3, 4), (4, 3, 2, 1, 1), (0, 0, 0, 0)], + ) + self.assertRaises( + ValueError, + model.AddForbiddenAssignments, + [], + [(0, 1, 2, 3, 4), (4, 3, 2, 1, 1), (0, 0, 0, 0)], + ) def testAutomaton(self): - print('testAutomaton') + print("testAutomaton") model = cp_model.CpModel() - x = [model.NewIntVar(0, 4, 'x%i' % i) for i in range(5)] - model.AddAutomaton(x, 0, [2, 3], [(0, 0, 0), (0, 1, 1), (1, 2, 2), - (2, 3, 3)]) + x = [model.NewIntVar(0, 4, "x%i" % i) for i in range(5)] + model.AddAutomaton(x, 0, [2, 3], [(0, 0, 0), (0, 1, 1), (1, 2, 2), (2, 3, 3)]) self.assertLen(model.Proto().variables, 5) self.assertLen(model.Proto().constraints, 1) self.assertLen(model.Proto().constraints[0].automaton.vars, 5) - self.assertLen(model.Proto().constraints[0].automaton.transition_tail, - 4) - self.assertLen(model.Proto().constraints[0].automaton.transition_head, - 4) - self.assertLen(model.Proto().constraints[0].automaton.transition_label, - 4) + self.assertLen(model.Proto().constraints[0].automaton.transition_tail, 4) + self.assertLen(model.Proto().constraints[0].automaton.transition_head, 4) + self.assertLen(model.Proto().constraints[0].automaton.transition_label, 4) self.assertLen(model.Proto().constraints[0].automaton.final_states, 2) - self.assertEqual(0, - model.Proto().constraints[0].automaton.starting_state) - self.assertRaises(TypeError, model.AddAutomaton, x, 0, [2, 3], - [(0, 0, 0), (0, 1, 1), (2, 2), (2, 3, 3)]) - self.assertRaises(ValueError, model.AddAutomaton, [], 0, [2, 3], - [(0, 0, 0), (0, 1, 1), (2, 3, 3)]) - self.assertRaises(ValueError, model.AddAutomaton, x, 0, [], [(0, 0, 0), - (0, 1, 1), - (2, 3, 3)]) + self.assertEqual(0, model.Proto().constraints[0].automaton.starting_state) + self.assertRaises( + TypeError, + model.AddAutomaton, + x, + 0, + [2, 3], + [(0, 0, 0), (0, 1, 1), (2, 2), (2, 3, 3)], + ) + self.assertRaises( + ValueError, + model.AddAutomaton, + [], + 0, + [2, 3], + [(0, 0, 0), (0, 1, 1), (2, 3, 3)], + ) + self.assertRaises( + ValueError, model.AddAutomaton, x, 0, [], [(0, 0, 0), (0, 1, 1), (2, 3, 3)] + ) self.assertRaises(ValueError, model.AddAutomaton, x, 0, [2, 3], []) def testInverse(self): - print('testInverse') + print("testInverse") model = cp_model.CpModel() - x = [model.NewIntVar(0, 4, 'x%i' % i) for i in range(5)] - y = [model.NewIntVar(0, 4, 'y%i' % i) for i in range(5)] + x = [model.NewIntVar(0, 4, "x%i" % i) for i in range(5)] + y = [model.NewIntVar(0, 4, "y%i" % i) for i in range(5)] model.AddInverse(x, y) self.assertLen(model.Proto().variables, 10) self.assertLen(model.Proto().constraints, 1) @@ -617,71 +639,66 @@ class CpModelTest(absltest.TestCase): self.assertLen(model.Proto().constraints[0].inverse.f_inverse, 5) def testMaxEquality(self): - print('testMaxEquality') + print("testMaxEquality") model = cp_model.CpModel() - x = model.NewIntVar(0, 4, 'x') - y = [model.NewIntVar(0, 4, 'y%i' % i) for i in range(5)] + x = model.NewIntVar(0, 4, "x") + y = [model.NewIntVar(0, 4, "y%i" % i) for i in range(5)] model.AddMaxEquality(x, y) self.assertLen(model.Proto().variables, 6) self.assertLen(model.Proto().constraints, 1) self.assertLen(model.Proto().constraints[0].lin_max.exprs, 5) self.assertEqual(0, model.Proto().constraints[0].lin_max.target.vars[0]) - self.assertEqual(1, - model.Proto().constraints[0].lin_max.target.coeffs[0]) + self.assertEqual(1, model.Proto().constraints[0].lin_max.target.coeffs[0]) def testMinEquality(self): - print('testMinEquality') + print("testMinEquality") model = cp_model.CpModel() - x = model.NewIntVar(0, 4, 'x') - y = [model.NewIntVar(0, 4, 'y%i' % i) for i in range(5)] + x = model.NewIntVar(0, 4, "x") + y = [model.NewIntVar(0, 4, "y%i" % i) for i in range(5)] model.AddMinEquality(x, y) self.assertLen(model.Proto().variables, 6) self.assertLen(model.Proto().constraints[0].lin_max.exprs, 5) self.assertEqual(0, model.Proto().constraints[0].lin_max.target.vars[0]) - self.assertEqual(-1, - model.Proto().constraints[0].lin_max.target.coeffs[0]) + self.assertEqual(-1, model.Proto().constraints[0].lin_max.target.coeffs[0]) def testMinEqualityList(self): - print('testMinEqualityList') + print("testMinEqualityList") model = cp_model.CpModel() - x = model.NewIntVar(0, 4, 'x') - y = [model.NewIntVar(0, 4, 'y%i' % i) for i in range(5)] + x = model.NewIntVar(0, 4, "x") + y = [model.NewIntVar(0, 4, "y%i" % i) for i in range(5)] model.AddMinEquality(x, [y[0], y[2], y[1], y[3]]) self.assertLen(model.Proto().variables, 6) self.assertLen(model.Proto().constraints[0].lin_max.exprs, 4) self.assertEqual(0, model.Proto().constraints[0].lin_max.target.vars[0]) - self.assertEqual(-1, - model.Proto().constraints[0].lin_max.target.coeffs[0]) + self.assertEqual(-1, model.Proto().constraints[0].lin_max.target.coeffs[0]) def testMinEqualityTuple(self): - print('testMinEqualityTuple') + print("testMinEqualityTuple") model = cp_model.CpModel() - x = model.NewIntVar(0, 4, 'x') - y = [model.NewIntVar(0, 4, 'y%i' % i) for i in range(5)] + x = model.NewIntVar(0, 4, "x") + y = [model.NewIntVar(0, 4, "y%i" % i) for i in range(5)] model.AddMinEquality(x, (y[0], y[2], y[1], y[3])) self.assertLen(model.Proto().variables, 6) self.assertLen(model.Proto().constraints[0].lin_max.exprs, 4) self.assertEqual(0, model.Proto().constraints[0].lin_max.target.vars[0]) - self.assertEqual(-1, - model.Proto().constraints[0].lin_max.target.coeffs[0]) + self.assertEqual(-1, model.Proto().constraints[0].lin_max.target.coeffs[0]) def testMinEqualityGenerator(self): - print('testMinEqualityGenerator') + print("testMinEqualityGenerator") model = cp_model.CpModel() - x = model.NewIntVar(0, 4, 'x') - y = [model.NewIntVar(0, 4, 'y%i' % i) for i in range(5)] + x = model.NewIntVar(0, 4, "x") + y = [model.NewIntVar(0, 4, "y%i" % i) for i in range(5)] model.AddMinEquality(x, (z for z in y)) self.assertLen(model.Proto().variables, 6) self.assertLen(model.Proto().constraints[0].lin_max.exprs, 5) self.assertEqual(0, model.Proto().constraints[0].lin_max.target.vars[0]) - self.assertEqual(-1, - model.Proto().constraints[0].lin_max.target.coeffs[0]) + self.assertEqual(-1, model.Proto().constraints[0].lin_max.target.coeffs[0]) def testMinEqualityWithConstant(self): - print('testMinEqualityWithConstant') + print("testMinEqualityWithConstant") model = cp_model.CpModel() - x = model.NewIntVar(0, 4, 'x') - y = model.NewIntVar(0, 4, 'y') + x = model.NewIntVar(0, 4, "x") + y = model.NewIntVar(0, 4, "y") model.AddMinEquality(x, [y, 3]) self.assertLen(model.Proto().variables, 2) self.assertLen(model.Proto().constraints, 1) @@ -695,24 +712,18 @@ class CpModelTest(absltest.TestCase): self.assertEqual(-3, lin_max.exprs[1].offset) def testAbs(self): - print('testAbs') + print("testAbs") model = cp_model.CpModel() - x = model.NewIntVar(0, 4, 'x') - y = model.NewIntVar(-5, 5, 'y') + x = model.NewIntVar(0, 4, "x") + y = model.NewIntVar(-5, 5, "y") model.AddAbsEquality(x, y) self.assertLen(model.Proto().variables, 2) self.assertLen(model.Proto().constraints, 1) self.assertLen(model.Proto().constraints[0].lin_max.exprs, 2) - self.assertEqual(1, - model.Proto().constraints[0].lin_max.exprs[0].vars[0]) - self.assertEqual( - 1, - model.Proto().constraints[0].lin_max.exprs[0].coeffs[0]) - self.assertEqual(1, - model.Proto().constraints[0].lin_max.exprs[1].vars[0]) - self.assertEqual( - -1, - model.Proto().constraints[0].lin_max.exprs[1].coeffs[0]) + self.assertEqual(1, model.Proto().constraints[0].lin_max.exprs[0].vars[0]) + self.assertEqual(1, model.Proto().constraints[0].lin_max.exprs[0].coeffs[0]) + self.assertEqual(1, model.Proto().constraints[0].lin_max.exprs[1].vars[0]) + self.assertEqual(-1, model.Proto().constraints[0].lin_max.exprs[1].coeffs[0]) passed = False error_msg = None try: @@ -721,26 +732,25 @@ class CpModelTest(absltest.TestCase): error_msg = str(e) passed = True self.assertEqual( - 'calling abs() on a linear expression is not supported, ' - 'please use CpModel.AddAbsEquality', error_msg) + "calling abs() on a linear expression is not supported, " + "please use CpModel.AddAbsEquality", + error_msg, + ) self.assertTrue(passed) def testDivision(self): - print('testDivision') + print("testDivision") model = cp_model.CpModel() - x = model.NewIntVar(0, 10, 'x') - y = model.NewIntVar(0, 50, 'y') + x = model.NewIntVar(0, 10, "x") + y = model.NewIntVar(0, 50, "y") model.AddDivisionEquality(x, y, 6) self.assertLen(model.Proto().variables, 2) self.assertLen(model.Proto().constraints, 1) self.assertLen(model.Proto().constraints[0].int_div.exprs, 2) - self.assertEqual(model.Proto().constraints[0].int_div.exprs[0].vars[0], - 1) - self.assertEqual( - model.Proto().constraints[0].int_div.exprs[0].coeffs[0], 1) + self.assertEqual(model.Proto().constraints[0].int_div.exprs[0].vars[0], 1) + self.assertEqual(model.Proto().constraints[0].int_div.exprs[0].coeffs[0], 1) self.assertEmpty(model.Proto().constraints[0].int_div.exprs[1].vars) - self.assertEqual(model.Proto().constraints[0].int_div.exprs[1].offset, - 6) + self.assertEqual(model.Proto().constraints[0].int_div.exprs[1].offset, 6) passed = False error_msg = None try: @@ -749,26 +759,25 @@ class CpModelTest(absltest.TestCase): error_msg = str(e) passed = True self.assertEqual( - 'calling // on a linear expression is not supported, ' - 'please use CpModel.AddDivisionEquality', error_msg) + "calling // on a linear expression is not supported, " + "please use CpModel.AddDivisionEquality", + error_msg, + ) self.assertTrue(passed) def testModulo(self): - print('testModulo') + print("testModulo") model = cp_model.CpModel() - x = model.NewIntVar(0, 10, 'x') - y = model.NewIntVar(0, 50, 'y') + x = model.NewIntVar(0, 10, "x") + y = model.NewIntVar(0, 50, "y") model.AddModuloEquality(x, y, 6) self.assertLen(model.Proto().variables, 2) self.assertLen(model.Proto().constraints, 1) self.assertLen(model.Proto().constraints[0].int_mod.exprs, 2) - self.assertEqual(model.Proto().constraints[0].int_mod.exprs[0].vars[0], - 1) - self.assertEqual( - model.Proto().constraints[0].int_mod.exprs[0].coeffs[0], 1) + self.assertEqual(model.Proto().constraints[0].int_mod.exprs[0].vars[0], 1) + self.assertEqual(model.Proto().constraints[0].int_mod.exprs[0].coeffs[0], 1) self.assertEmpty(model.Proto().constraints[0].int_mod.exprs[1].vars) - self.assertEqual(model.Proto().constraints[0].int_mod.exprs[1].offset, - 6) + self.assertEqual(model.Proto().constraints[0].int_mod.exprs[1].offset, 6) passed = False error_msg = None try: @@ -777,41 +786,40 @@ class CpModelTest(absltest.TestCase): error_msg = str(e) passed = True self.assertEqual( - 'calling %% on a linear expression is not supported, ' - 'please use CpModel.AddModuloEquality', error_msg) + "calling %% on a linear expression is not supported, " + "please use CpModel.AddModuloEquality", + error_msg, + ) self.assertTrue(passed) def testMultiplicationEquality(self): - print('testMultiplicationEquality') + print("testMultiplicationEquality") model = cp_model.CpModel() - x = model.NewIntVar(0, 4, 'x') - y = [model.NewIntVar(0, 4, 'y%i' % i) for i in range(5)] + x = model.NewIntVar(0, 4, "x") + y = [model.NewIntVar(0, 4, "y%i" % i) for i in range(5)] model.AddMultiplicationEquality(x, y) self.assertLen(model.Proto().variables, 6) self.assertLen(model.Proto().constraints, 1) self.assertLen(model.Proto().constraints[0].int_prod.exprs, 5) - self.assertEqual(0, - model.Proto().constraints[0].int_prod.target.vars[0]) + self.assertEqual(0, model.Proto().constraints[0].int_prod.target.vars[0]) def testImplication(self): - print('testImplication') + print("testImplication") model = cp_model.CpModel() - x = model.NewBoolVar('x') - y = model.NewBoolVar('y') + x = model.NewBoolVar("x") + y = model.NewBoolVar("y") model.AddImplication(x, y) self.assertLen(model.Proto().variables, 2) self.assertLen(model.Proto().constraints, 1) self.assertLen(model.Proto().constraints[0].bool_or.literals, 1) self.assertLen(model.Proto().constraints[0].enforcement_literal, 1) - self.assertEqual(x.Index(), - model.Proto().constraints[0].enforcement_literal[0]) - self.assertEqual(y.Index(), - model.Proto().constraints[0].bool_or.literals[0]) + self.assertEqual(x.Index(), model.Proto().constraints[0].enforcement_literal[0]) + self.assertEqual(y.Index(), model.Proto().constraints[0].bool_or.literals[0]) def testBoolOr(self): - print('testBoolOr') + print("testBoolOr") model = cp_model.CpModel() - x = [model.NewBoolVar('x%i' % i) for i in range(5)] + x = [model.NewBoolVar("x%i" % i) for i in range(5)] model.AddBoolOr(x) self.assertLen(model.Proto().variables, 5) self.assertLen(model.Proto().constraints, 1) @@ -819,13 +827,13 @@ class CpModelTest(absltest.TestCase): model.AddBoolOr([x[0], x[1], False]) self.assertLen(model.Proto().variables, 6) self.assertRaises(TypeError, model.AddBoolOr, [x[2], 2]) - y = model.NewIntVar(0, 4, 'y') + y = model.NewIntVar(0, 4, "y") self.assertRaises(TypeError, model.AddBoolOr, [y, False]) def testBoolOrListOrGet(self): - print('testBoolOrListOrGet') + print("testBoolOrListOrGet") model = cp_model.CpModel() - x = [model.NewBoolVar('x%i' % i) for i in range(5)] + x = [model.NewBoolVar("x%i" % i) for i in range(5)] model.AddBoolOr(x) model.AddBoolOr(True, x[0], x[2]) model.AddBoolOr(False, x[0]) @@ -838,9 +846,9 @@ class CpModelTest(absltest.TestCase): self.assertLen(model.Proto().constraints[3].bool_or.literals, 4) def testAtLeastOne(self): - print('testAtLeastOne') + print("testAtLeastOne") model = cp_model.CpModel() - x = [model.NewBoolVar('x%i' % i) for i in range(5)] + x = [model.NewBoolVar("x%i" % i) for i in range(5)] model.AddAtLeastOne(x) self.assertLen(model.Proto().variables, 5) self.assertLen(model.Proto().constraints, 1) @@ -848,13 +856,13 @@ class CpModelTest(absltest.TestCase): model.AddAtLeastOne([x[0], x[1], False]) self.assertLen(model.Proto().variables, 6) self.assertRaises(TypeError, model.AddAtLeastOne, [x[2], 2]) - y = model.NewIntVar(0, 4, 'y') + y = model.NewIntVar(0, 4, "y") self.assertRaises(TypeError, model.AddAtLeastOne, [y, False]) def testAtMostOne(self): - print('testAtMostOne') + print("testAtMostOne") model = cp_model.CpModel() - x = [model.NewBoolVar('x%i' % i) for i in range(5)] + x = [model.NewBoolVar("x%i" % i) for i in range(5)] model.AddAtMostOne(x) self.assertLen(model.Proto().variables, 5) self.assertLen(model.Proto().constraints, 1) @@ -862,13 +870,13 @@ class CpModelTest(absltest.TestCase): model.AddAtMostOne([x[0], x[1], False]) self.assertLen(model.Proto().variables, 6) self.assertRaises(TypeError, model.AddAtMostOne, [x[2], 2]) - y = model.NewIntVar(0, 4, 'y') + y = model.NewIntVar(0, 4, "y") self.assertRaises(TypeError, model.AddAtMostOne, [y, False]) def testExactlyOne(self): - print('testExactlyOne') + print("testExactlyOne") model = cp_model.CpModel() - x = [model.NewBoolVar('x%i' % i) for i in range(5)] + x = [model.NewBoolVar("x%i" % i) for i in range(5)] model.AddExactlyOne(x) self.assertLen(model.Proto().variables, 5) self.assertLen(model.Proto().constraints, 1) @@ -876,13 +884,13 @@ class CpModelTest(absltest.TestCase): model.AddExactlyOne([x[0], x[1], False]) self.assertLen(model.Proto().variables, 6) self.assertRaises(TypeError, model.AddExactlyOne, [x[2], 2]) - y = model.NewIntVar(0, 4, 'y') + y = model.NewIntVar(0, 4, "y") self.assertRaises(TypeError, model.AddExactlyOne, [y, False]) def testBoolAnd(self): - print('testBoolAnd') + print("testBoolAnd") model = cp_model.CpModel() - x = [model.NewBoolVar('x%i' % i) for i in range(5)] + x = [model.NewBoolVar("x%i" % i) for i in range(5)] model.AddBoolAnd(x) self.assertLen(model.Proto().variables, 5) self.assertLen(model.Proto().constraints, 1) @@ -893,69 +901,68 @@ class CpModelTest(absltest.TestCase): self.assertEqual(5, model.Proto().constraints[1].bool_and.literals[2]) def testBoolXOr(self): - print('testBoolXOr') + print("testBoolXOr") model = cp_model.CpModel() - x = [model.NewBoolVar('x%i' % i) for i in range(5)] + x = [model.NewBoolVar("x%i" % i) for i in range(5)] model.AddBoolXOr(x) self.assertLen(model.Proto().variables, 5) self.assertLen(model.Proto().constraints, 1) self.assertLen(model.Proto().constraints[0].bool_xor.literals, 5) def testMapDomain(self): - print('testMapDomain') + print("testMapDomain") model = cp_model.CpModel() - x = [model.NewBoolVar('x%i' % i) for i in range(5)] - y = model.NewIntVar(0, 10, 'y') + x = [model.NewBoolVar("x%i" % i) for i in range(5)] + y = model.NewIntVar(0, 10, "y") model.AddMapDomain(y, x, 2) self.assertLen(model.Proto().variables, 6) self.assertLen(model.Proto().constraints, 10) def testInterval(self): - print('testInterval') + print("testInterval") model = cp_model.CpModel() - x = model.NewIntVar(0, 4, 'x') - y = model.NewIntVar(0, 3, 'y') - i = model.NewIntervalVar(x, 3, y, 'i') + x = model.NewIntVar(0, 4, "x") + y = model.NewIntVar(0, 3, "y") + i = model.NewIntervalVar(x, 3, y, "i") self.assertEqual(1, i.Index()) - j = model.NewFixedSizeIntervalVar(x, 2, 'j') + j = model.NewFixedSizeIntervalVar(x, 2, "j") self.assertEqual(2, j.Index()) start_expr = j.StartExpr() size_expr = j.SizeExpr() end_expr = j.EndExpr() self.assertEqual(x.Index(), start_expr.Index()) self.assertEqual(size_expr, 2) - self.assertEqual(str(end_expr), '(x + 2)') + self.assertEqual(str(end_expr), "(x + 2)") def testOptionalInterval(self): - print('testOptionalInterval') + print("testOptionalInterval") model = cp_model.CpModel() - b = model.NewBoolVar('b') - x = model.NewIntVar(0, 4, 'x') - y = model.NewIntVar(0, 3, 'y') - i = model.NewOptionalIntervalVar(x, 3, y, b, 'i') - j = model.NewOptionalIntervalVar(x, y, 10, b, 'j') - k = model.NewOptionalIntervalVar(x, -y, 10, b, 'k') - l = model.NewOptionalIntervalVar(x, 10, -y, b, 'l') + b = model.NewBoolVar("b") + x = model.NewIntVar(0, 4, "x") + y = model.NewIntVar(0, 3, "y") + i = model.NewOptionalIntervalVar(x, 3, y, b, "i") + j = model.NewOptionalIntervalVar(x, y, 10, b, "j") + k = model.NewOptionalIntervalVar(x, -y, 10, b, "k") + l = model.NewOptionalIntervalVar(x, 10, -y, b, "l") self.assertEqual(1, i.Index()) self.assertEqual(3, j.Index()) self.assertEqual(5, k.Index()) self.assertEqual(7, l.Index()) - self.assertRaises(TypeError, model.NewOptionalIntervalVar, 1, 2, 3, x, - 'x') - self.assertRaises(TypeError, model.NewOptionalIntervalVar, b + x, 2, 3, - b, 'x') - self.assertRaises(AttributeError, model.NewOptionalIntervalVar, 1, 2, 3, - b + 1, 'x') + self.assertRaises(TypeError, model.NewOptionalIntervalVar, 1, 2, 3, x, "x") + self.assertRaises(TypeError, model.NewOptionalIntervalVar, b + x, 2, 3, b, "x") + self.assertRaises( + AttributeError, model.NewOptionalIntervalVar, 1, 2, 3, b + 1, "x" + ) def testNoOverlap(self): - print('testNoOverlap') + print("testNoOverlap") model = cp_model.CpModel() - x = model.NewIntVar(0, 4, 'x') - y = model.NewIntVar(0, 3, 'y') - z = model.NewIntVar(0, 3, 'y') - i = model.NewIntervalVar(x, 3, y, 'i') - j = model.NewIntervalVar(x, 5, z, 'j') + x = model.NewIntVar(0, 4, "x") + y = model.NewIntVar(0, 3, "y") + z = model.NewIntVar(0, 3, "y") + i = model.NewIntervalVar(x, 3, y, "i") + j = model.NewIntervalVar(x, 5, z, "j") ct = model.AddNoOverlap([i, j]) self.assertEqual(4, ct.Index()) self.assertLen(ct.Proto().no_overlap.intervals, 2) @@ -963,13 +970,13 @@ class CpModelTest(absltest.TestCase): self.assertEqual(3, ct.Proto().no_overlap.intervals[1]) def testNoOverlap2D(self): - print('testNoOverlap2D') + print("testNoOverlap2D") model = cp_model.CpModel() - x = model.NewIntVar(0, 4, 'x') - y = model.NewIntVar(0, 3, 'y') - z = model.NewIntVar(0, 3, 'y') - i = model.NewIntervalVar(x, 3, y, 'i') - j = model.NewIntervalVar(x, 5, z, 'j') + x = model.NewIntVar(0, 4, "x") + y = model.NewIntVar(0, 3, "y") + z = model.NewIntVar(0, 3, "y") + i = model.NewIntervalVar(x, 3, y, "i") + j = model.NewIntervalVar(x, 5, z, "j") ct = model.AddNoOverlap2D([i, j], [j, i]) self.assertEqual(4, ct.Index()) self.assertLen(ct.Proto().no_overlap_2d.x_intervals, 2) @@ -980,23 +987,26 @@ class CpModelTest(absltest.TestCase): self.assertEqual(1, ct.Proto().no_overlap_2d.y_intervals[1]) def testCumulative(self): - print('testCumulative') + print("testCumulative") model = cp_model.CpModel() intervals = [ - model.NewIntervalVar(model.NewIntVar(0, 10, f's_{i}'), 5, - model.NewIntVar(5, 15, f'e_{i}'), - f'interval[{i}]') for i in range(10) + model.NewIntervalVar( + model.NewIntVar(0, 10, f"s_{i}"), + 5, + model.NewIntVar(5, 15, f"e_{i}"), + f"interval[{i}]", + ) + for i in range(10) ] demands = [1, 3, 5, 2, 4, 5, 3, 4, 2, 3] capacity = 4 ct = model.AddCumulative(intervals, demands, capacity) self.assertEqual(20, ct.Index()) self.assertLen(ct.Proto().cumulative.intervals, 10) - self.assertRaises(TypeError, model.AddCumulative, [intervals[0], 3], - [2, 3], 3) + self.assertRaises(TypeError, model.AddCumulative, [intervals[0], 3], [2, 3], 3) def testGetOrMakeIndexFromConstant(self): - print('testGetOrMakeIndexFromConstant') + print("testGetOrMakeIndexFromConstant") model = cp_model.CpModel() self.assertEqual(0, model.GetOrMakeIndexFromConstant(3)) self.assertEqual(0, model.GetOrMakeIndexFromConstant(3)) @@ -1007,86 +1017,87 @@ class CpModelTest(absltest.TestCase): self.assertEqual(3, model_var.domain[1]) def testStr(self): - print('testStr') + print("testStr") model = cp_model.CpModel() - x = model.NewIntVar(0, 4, 'x') - self.assertEqual(str(x == 2), 'x == 2') - self.assertEqual(str(x >= 2), 'x >= 2') - self.assertEqual(str(x <= 2), 'x <= 2') - self.assertEqual(str(x > 2), 'x >= 3') - self.assertEqual(str(x < 2), 'x <= 1') - self.assertEqual(str(x != 2), 'x != 2') - self.assertEqual(str(x * 3), '(3 * x)') - self.assertEqual(str(-x), '-x') - self.assertEqual(str(x + 3), '(x + 3)') - self.assertEqual(str(x <= cp_model.INT_MAX), 'True (unbounded expr x)') - self.assertEqual(str(x != 9223372036854775807), - 'x <= 9223372036854775806') - self.assertEqual(str(x != -9223372036854775808), - 'x >= -9223372036854775807') - y = model.NewIntVar(0, 4, 'y') + x = model.NewIntVar(0, 4, "x") + self.assertEqual(str(x == 2), "x == 2") + self.assertEqual(str(x >= 2), "x >= 2") + self.assertEqual(str(x <= 2), "x <= 2") + self.assertEqual(str(x > 2), "x >= 3") + self.assertEqual(str(x < 2), "x <= 1") + self.assertEqual(str(x != 2), "x != 2") + self.assertEqual(str(x * 3), "(3 * x)") + self.assertEqual(str(-x), "-x") + self.assertEqual(str(x + 3), "(x + 3)") + self.assertEqual(str(x <= cp_model.INT_MAX), "True (unbounded expr x)") + self.assertEqual(str(x != 9223372036854775807), "x <= 9223372036854775806") + self.assertEqual(str(x != -9223372036854775808), "x >= -9223372036854775807") + y = model.NewIntVar(0, 4, "y") self.assertEqual( str(cp_model.LinearExpr.WeightedSum([x, y + 1, 2], [1, -2, 3])), - 'x - 2 * (y + 1) + 6') - self.assertEqual(str(cp_model.LinearExpr.Term(x, 3)), '(3 * x)') - self.assertEqual(str(x != y), '(x + -y) != 0') - self.assertEqual('0 <= x <= 10', - str(cp_model.BoundedLinearExpression(x, [0, 10]))) + "x - 2 * (y + 1) + 6", + ) + self.assertEqual(str(cp_model.LinearExpr.Term(x, 3)), "(3 * x)") + self.assertEqual(str(x != y), "(x + -y) != 0") + self.assertEqual( + "0 <= x <= 10", str(cp_model.BoundedLinearExpression(x, [0, 10])) + ) print(str(model)) - b = model.NewBoolVar('b') - self.assertEqual(str(cp_model.LinearExpr.Term(b.Not(), 3)), - '(3 * not(b))') + b = model.NewBoolVar("b") + self.assertEqual(str(cp_model.LinearExpr.Term(b.Not(), 3)), "(3 * not(b))") - i = model.NewIntervalVar(x, 2, y, 'i') - self.assertEqual(str(i), 'i') + i = model.NewIntervalVar(x, 2, y, "i") + self.assertEqual(str(i), "i") def testRepr(self): - print('testRepr') + print("testRepr") model = cp_model.CpModel() - x = model.NewIntVar(0, 4, 'x') - y = model.NewIntVar(0, 3, 'y') - z = model.NewIntVar(0, 3, 'z') - self.assertEqual(repr(x), 'x(0..4)') - self.assertEqual(repr(x * 2), 'ProductCst(x(0..4), 2)') - self.assertEqual(repr(x + y), 'Sum(x(0..4), y(0..3))') - self.assertEqual(repr(cp_model.LinearExpr.Sum([x, y, z])), - 'SumArray(x(0..4), y(0..3), z(0..3), 0)') + x = model.NewIntVar(0, 4, "x") + y = model.NewIntVar(0, 3, "y") + z = model.NewIntVar(0, 3, "z") + self.assertEqual(repr(x), "x(0..4)") + self.assertEqual(repr(x * 2), "ProductCst(x(0..4), 2)") + self.assertEqual(repr(x + y), "Sum(x(0..4), y(0..3))") + self.assertEqual( + repr(cp_model.LinearExpr.Sum([x, y, z])), + "SumArray(x(0..4), y(0..3), z(0..3), 0)", + ) self.assertEqual( repr(cp_model.LinearExpr.WeightedSum([x, y, 2], [1, 2, 3])), - 'WeightedSum([x(0..4), y(0..3)], [1, 2], 6)') - i = model.NewIntervalVar(x, 2, y, 'i') - self.assertEqual(repr(i), 'i(start = x, size = 2, end = y)') - b = model.NewBoolVar('b') - x1 = model.NewIntVar(0, 4, 'x1') - y1 = model.NewIntVar(0, 3, 'y1') - j = model.NewOptionalIntervalVar(x1, 2, y1, b, 'j') - self.assertEqual(repr(j), - 'j(start = x1, size = 2, end = y1, is_present = b)') - x2 = model.NewIntVar(0, 4, 'x2') - y2 = model.NewIntVar(0, 3, 'y2') - k = model.NewOptionalIntervalVar(x2, 2, y2, b.Not(), 'k') + "WeightedSum([x(0..4), y(0..3)], [1, 2], 6)", + ) + i = model.NewIntervalVar(x, 2, y, "i") + self.assertEqual(repr(i), "i(start = x, size = 2, end = y)") + b = model.NewBoolVar("b") + x1 = model.NewIntVar(0, 4, "x1") + y1 = model.NewIntVar(0, 3, "y1") + j = model.NewOptionalIntervalVar(x1, 2, y1, b, "j") + self.assertEqual(repr(j), "j(start = x1, size = 2, end = y1, is_present = b)") + x2 = model.NewIntVar(0, 4, "x2") + y2 = model.NewIntVar(0, 3, "y2") + k = model.NewOptionalIntervalVar(x2, 2, y2, b.Not(), "k") self.assertEqual( - repr(k), 'k(start = x2, size = 2, end = y2, is_present = Not(b))') + repr(k), "k(start = x2, size = 2, end = y2, is_present = Not(b))" + ) def testDisplayBounds(self): - print('testDisplayBounds') - self.assertEqual('10..20', cp_model.DisplayBounds([10, 20])) - self.assertEqual('10', cp_model.DisplayBounds([10, 10])) - self.assertEqual('10..15, 20..30', - cp_model.DisplayBounds([10, 15, 20, 30])) + print("testDisplayBounds") + self.assertEqual("10..20", cp_model.DisplayBounds([10, 20])) + self.assertEqual("10", cp_model.DisplayBounds([10, 10])) + self.assertEqual("10..15, 20..30", cp_model.DisplayBounds([10, 15, 20, 30])) def testShortName(self): - print('testShortName') + print("testShortName") model = cp_model.CpModel() v = model.Proto().variables.add() v.domain.extend([5, 10]) - self.assertEqual('[5..10]', cp_model.ShortName(model.Proto(), 0)) + self.assertEqual("[5..10]", cp_model.ShortName(model.Proto(), 0)) def testIntegerExpressionErrors(self): - print('testIntegerExpressionErrors') + print("testIntegerExpressionErrors") model = cp_model.CpModel() - x = model.NewIntVar(0, 1, 'x') - y = model.NewIntVar(0, 3, 'y') + x = model.NewIntVar(0, 1, "x") + y = model.NewIntVar(0, 3, "y") self.assertRaises(TypeError, x.__mul__, y) self.assertRaises(NotImplementedError, x.__div__, y) self.assertRaises(NotImplementedError, x.__truediv__, y) @@ -1099,54 +1110,54 @@ class CpModelTest(absltest.TestCase): self.assertRaises(NotImplementedError, x.__xor__, y) self.assertRaises(ArithmeticError, x.__lt__, cp_model.INT_MIN) self.assertRaises(ArithmeticError, x.__gt__, cp_model.INT_MAX) - self.assertRaises(TypeError, x.__add__, 'dummy') - self.assertRaises(TypeError, x.__mul__, 'dummy') + self.assertRaises(TypeError, x.__add__, "dummy") + self.assertRaises(TypeError, x.__mul__, "dummy") def testModelErrors(self): - print('testModelErrors') + print("testModelErrors") model = cp_model.CpModel() - self.assertRaises(TypeError, model.Add, 'dummy') - self.assertRaises(TypeError, model.GetOrMakeIndex, 'dummy') - self.assertRaises(TypeError, model.Minimize, 'dummy') + self.assertRaises(TypeError, model.Add, "dummy") + self.assertRaises(TypeError, model.GetOrMakeIndex, "dummy") + self.assertRaises(TypeError, model.Minimize, "dummy") def testSolverErrors(self): - print('testSolverErrors') + print("testSolverErrors") model = cp_model.CpModel() - x = model.NewIntVar(0, 1, 'x') - y = model.NewIntVar(-10, 10, 'y') + x = model.NewIntVar(0, 1, "x") + y = model.NewIntVar(-10, 10, "y") model.AddLinearConstraint(x + 2 * y, 0, 10) model.Minimize(y) solver = cp_model.CpSolver() self.assertRaises(RuntimeError, solver.Value, x) solver.Solve(model) - self.assertRaises(TypeError, solver.Value, 'not_a_variable') + self.assertRaises(TypeError, solver.Value, "not_a_variable") self.assertRaises(TypeError, model.AddBoolOr, [x, y]) def testHasObjectiveMinimize(self): - print('testHasObjectiveMinimizs') + print("testHasObjectiveMinimizs") model = cp_model.CpModel() - x = model.NewIntVar(0, 1, 'x') - y = model.NewIntVar(-10, 10, 'y') + x = model.NewIntVar(0, 1, "x") + y = model.NewIntVar(-10, 10, "y") model.AddLinearConstraint(x + 2 * y, 0, 10) self.assertFalse(model.HasObjective()) model.Minimize(y) self.assertTrue(model.HasObjective()) def testHasObjectiveMaximize(self): - print('testHasObjectiveMaximizs') + print("testHasObjectiveMaximizs") model = cp_model.CpModel() - x = model.NewIntVar(0, 1, 'x') - y = model.NewIntVar(-10, 10, 'y') + x = model.NewIntVar(0, 1, "x") + y = model.NewIntVar(-10, 10, "y") model.AddLinearConstraint(x + 2 * y, 0, 10) self.assertFalse(model.HasObjective()) model.Maximize(y) self.assertTrue(model.HasObjective()) def testSearchForAllSolutions(self): - print('testSearchForAllSolutions') + print("testSearchForAllSolutions") model = cp_model.CpModel() - x = model.NewIntVar(0, 5, 'x') - y = model.NewIntVar(0, 5, 'y') + x = model.NewIntVar(0, 5, "x") + y = model.NewIntVar(0, 5, "y") model.AddLinearConstraint(x + y, 6, 6) solver = cp_model.CpSolver() @@ -1155,14 +1166,15 @@ class CpModelTest(absltest.TestCase): self.assertEqual(cp_model.OPTIMAL, status) self.assertEqual(5, solution_counter.SolutionCount()) model.Minimize(x) - self.assertRaises(TypeError, solver.SearchForAllSolutions, model, - solution_counter) + self.assertRaises( + TypeError, solver.SearchForAllSolutions, model, solution_counter + ) def testSolveWithSolutionCallback(self): - print('testSolveWithSolutionCallback') + print("testSolveWithSolutionCallback") model = cp_model.CpModel() - x = model.NewIntVar(0, 5, 'x') - y = model.NewIntVar(0, 5, 'y') + x = model.NewIntVar(0, 5, "x") + y = model.NewIntVar(0, 5, "y") model.AddLinearConstraint(x + y, 6, 6) solver = cp_model.CpSolver() @@ -1173,10 +1185,10 @@ class CpModelTest(absltest.TestCase): self.assertEqual(6, solution_sum.Sum()) def testValue(self): - print('testValue') + print("testValue") model = cp_model.CpModel() - x = model.NewIntVar(0, 10, 'x') - y = model.NewIntVar(0, 10, 'y') + x = model.NewIntVar(0, 10, "x") + y = model.NewIntVar(0, 10, "y") model.Add(x + 2 * y == 29) solver = cp_model.CpSolver() status = solver.Solve(model) @@ -1186,11 +1198,11 @@ class CpModelTest(absltest.TestCase): self.assertEqual(solver.Value(2), 2) def testBooleanValue(self): - print('testBooleanValue') + print("testBooleanValue") model = cp_model.CpModel() - x = model.NewBoolVar('x') - y = model.NewBoolVar('y') - z = model.NewBoolVar('z') + x = model.NewBoolVar("x") + y = model.NewBoolVar("y") + z = model.NewBoolVar("z") model.AddBoolOr([x, z.Not()]) model.AddBoolOr([x, z]) model.AddBoolOr([x.Not(), y.Not()]) @@ -1208,23 +1220,23 @@ class CpModelTest(absltest.TestCase): self.assertEqual(solver.BooleanValue(0), False) def testUnsupportedOperators(self): - print('testUnsupportedOperators') + print("testUnsupportedOperators") model = cp_model.CpModel() - x = model.NewIntVar(0, 10, 'x') - y = model.NewIntVar(0, 10, 'y') - z = model.NewIntVar(0, 10, 'z') + x = model.NewIntVar(0, 10, "x") + y = model.NewIntVar(0, 10, "y") + z = model.NewIntVar(0, 10, "z") with self.assertRaises(NotImplementedError): model.Add(x == min(y, z)) with self.assertRaises(NotImplementedError): if x > y: - print('passed1') + print("passed1") with self.assertRaises(NotImplementedError): if x == 2: - print('passed2') + print("passed2") def testIsLiteralTrueFalse(self): - print('testIsLiteralTrueFalse') + print("testIsLiteralTrueFalse") model = cp_model.CpModel() x = model.NewConstant(0) self.assertFalse(cp_model.ObjectIsATrueLiteral(x)) @@ -1237,10 +1249,10 @@ class CpModelTest(absltest.TestCase): self.assertFalse(cp_model.ObjectIsAFalseLiteral(True)) def testSolveMinimizeWithSolutionCallback(self): - print('testSolveMinimizeWithSolutionCallback') + print("testSolveMinimizeWithSolutionCallback") model = cp_model.CpModel() - x = model.NewIntVar(0, 5, 'x') - y = model.NewIntVar(0, 5, 'y') + x = model.NewIntVar(0, 5, "x") + y = model.NewIntVar(0, 5, "y") model.AddLinearConstraint(x + y, 6, 6) model.Maximize(x + 2 * y) @@ -1248,14 +1260,14 @@ class CpModelTest(absltest.TestCase): solution_obj = SolutionObjective() status = solver.SolveWithSolutionCallback(model, solution_obj) self.assertEqual(cp_model.OPTIMAL, status) - print('obj = ', solution_obj.Obj()) + print("obj = ", solution_obj.Obj()) self.assertEqual(11, solution_obj.Obj()) def testSolutionHinting(self): - print('testSolutionHinting') + print("testSolutionHinting") model = cp_model.CpModel() - x = model.NewIntVar(0, 5, 'x') - y = model.NewIntVar(0, 5, 'y') + x = model.NewIntVar(0, 5, "x") + y = model.NewIntVar(0, 5, "y") model.AddLinearConstraint(x + y, 6, 6) model.AddHint(x, 2) model.AddHint(y, 4) @@ -1267,10 +1279,10 @@ class CpModelTest(absltest.TestCase): self.assertEqual(4, solver.Value(y)) def testStats(self): - print('testStats') + print("testStats") model = cp_model.CpModel() - x = model.NewIntVar(0, 5, 'x') - y = model.NewIntVar(0, 5, 'y') + x = model.NewIntVar(0, 5, "x") + y = model.NewIntVar(0, 5, "y") model.AddLinearConstraint(x + y, 4, 6) model.AddLinearConstraint(2 * x + y, 0, 10) model.Maximize(x + 2 * y) @@ -1284,27 +1296,28 @@ class CpModelTest(absltest.TestCase): self.assertGreater(solver.WallTime(), 0.0) def testSearchStrategy(self): - print('testSearchStrategy') + print("testSearchStrategy") model = cp_model.CpModel() - x = model.NewIntVar(0, 5, 'x') - y = model.NewIntVar(0, 5, 'y') - model.AddDecisionStrategy([y, x], cp_model.CHOOSE_MIN_DOMAIN_SIZE, - cp_model.SELECT_MAX_VALUE) + x = model.NewIntVar(0, 5, "x") + y = model.NewIntVar(0, 5, "y") + model.AddDecisionStrategy( + [y, x], cp_model.CHOOSE_MIN_DOMAIN_SIZE, cp_model.SELECT_MAX_VALUE + ) self.assertLen(model.Proto().search_strategy, 1) strategy = model.Proto().search_strategy[0] self.assertLen(strategy.variables, 2) self.assertEqual(y.Index(), strategy.variables[0]) self.assertEqual(x.Index(), strategy.variables[1]) - self.assertEqual(cp_model.CHOOSE_MIN_DOMAIN_SIZE, - strategy.variable_selection_strategy) - self.assertEqual(cp_model.SELECT_MAX_VALUE, - strategy.domain_reduction_strategy) + self.assertEqual( + cp_model.CHOOSE_MIN_DOMAIN_SIZE, strategy.variable_selection_strategy + ) + self.assertEqual(cp_model.SELECT_MAX_VALUE, strategy.domain_reduction_strategy) def testModelAndResponseStats(self): - print('testStats') + print("testStats") model = cp_model.CpModel() - x = model.NewIntVar(0, 5, 'x') - y = model.NewIntVar(0, 5, 'y') + x = model.NewIntVar(0, 5, "x") + y = model.NewIntVar(0, 5, "y") model.AddLinearConstraint(x + y, 6, 6) model.Maximize(x + 2 * y) self.assertTrue(model.ModelStats()) @@ -1314,30 +1327,30 @@ class CpModelTest(absltest.TestCase): self.assertTrue(solver.ResponseStats()) def testValidateModel(self): - print('testValidateModel') + print("testValidateModel") model = cp_model.CpModel() - x = model.NewIntVar(0, 5, 'x') - y = model.NewIntVar(0, 5, 'y') + x = model.NewIntVar(0, 5, "x") + y = model.NewIntVar(0, 5, "y") model.AddLinearConstraint(x + y, 6, 6) model.Maximize(x + 2 * y) self.assertFalse(model.Validate()) def testValidateModelWithOverflow(self): - print('testValidateModel') + print("testValidateModel") model = cp_model.CpModel() - x = model.NewIntVar(0, cp_model.INT_MAX, 'x') - y = model.NewIntVar(0, 10, 'y') + x = model.NewIntVar(0, cp_model.INT_MAX, "x") + y = model.NewIntVar(0, 10, "y") model.AddLinearConstraint(x + y, 6, cp_model.INT_MAX) model.Maximize(x + 2 * y) self.assertTrue(model.Validate()) def testCopyModel(self): - print('testCopyModel') + print("testCopyModel") model = cp_model.CpModel() - b = model.NewBoolVar('b') - x = model.NewIntVar(0, 4, 'x') - y = model.NewIntVar(0, 3, 'y') - i = model.NewOptionalIntervalVar(x, 12, y, b, 'i') + b = model.NewBoolVar("b") + x = model.NewIntVar(0, 4, "x") + y = model.NewIntVar(0, 3, "y") + i = model.NewOptionalIntervalVar(x, 12, y, b, "i") lin = model.Add(x + y <= 10) new_model = cp_model.CpModel() @@ -1371,10 +1384,10 @@ class CpModelTest(absltest.TestCase): self.assertEqual(12, interval_ct.size.offset) def testCustomLog(self): - print('testCustomLog') + print("testCustomLog") model = cp_model.CpModel() - x = model.NewIntVar(-10, 10, 'x') - y = model.NewIntVar(-10, 10, 'y') + x = model.NewIntVar(-10, 10, "x") + y = model.NewIntVar(-10, 10, "y") model.AddLinearConstraint(x + 2 * y, 0, 10) model.Minimize(y) solver = cp_model.CpSolver() @@ -1387,27 +1400,26 @@ class CpModelTest(absltest.TestCase): self.assertEqual(10, solver.Value(x)) self.assertEqual(-5, solver.Value(y)) - self.assertRegex(log_callback.Log(), 'Parameters.*log_to_stdout.*') + self.assertRegex(log_callback.Log(), "Parameters.*log_to_stdout.*") def testIssue2762(self): - print('testIssue2762') + print("testIssue2762") model = cp_model.CpModel() - x = [model.NewBoolVar('a'), model.NewBoolVar('b')] + x = [model.NewBoolVar("a"), model.NewBoolVar("b")] with self.assertRaises(NotImplementedError): model.Add((x[0] != 0) or (x[1] != 0)) def testModelError(self): - print('TestModelError') + print("TestModelError") model = cp_model.CpModel() - x = [model.NewIntVar(0, -2, 'x%i' % i) for i in range(100)] + x = [model.NewIntVar(0, -2, "x%i" % i) for i in range(100)] model.Add(sum(x) <= 1) solver = cp_model.CpSolver() solver.parameters.log_search_progress = True self.assertEqual(cp_model.MODEL_INVALID, solver.Solve(model)) - self.assertEqual(solver.SolutionInfo(), - 'var #0 has no domain(): name: "x0"') + self.assertEqual(solver.SolutionInfo(), 'var #0 has no domain(): name: "x0"') -if __name__ == '__main__': +if __name__ == "__main__": absltest.main() diff --git a/ortools/sat/python/swig_helper_test.py b/ortools/sat/python/swig_helper_test.py index 975d745019..3506715708 100644 --- a/ortools/sat/python/swig_helper_test.py +++ b/ortools/sat/python/swig_helper_test.py @@ -11,6 +11,7 @@ # 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. + """Tests for ortools.sat.python.swig_helper.""" from absl.testing import absltest @@ -21,13 +22,12 @@ from ortools.sat.python import swig_helper class Callback(swig_helper.SolutionCallback): - def __init__(self): swig_helper.SolutionCallback.__init__(self) self.__solution_count = 0 def OnSolutionCallback(self): - print('New Solution') + print("New Solution") self.__solution_count += 1 def SolutionCount(self): @@ -35,7 +35,6 @@ class Callback(swig_helper.SolutionCallback): class SwigHelperTest(absltest.TestCase): - def testVariableDomain(self): model_string = """ variables { domain: [ -10, 10 ] } @@ -45,9 +44,11 @@ class SwigHelperTest(absltest.TestCase): self.assertTrue(text_format.Parse(model_string, model)) d0 = swig_helper.CpSatHelper.SerializedVariableDomain( - model.variables[0].SerializeToString()) + model.variables[0].SerializeToString() + ) d1 = swig_helper.CpSatHelper.SerializedVariableDomain( - model.variables[1].SerializeToString()) + model.variables[1].SerializeToString() + ) self.assertEqual(d0.FlattenedIntervals(), [-10, 10]) self.assertEqual(d1.FlattenedIntervals(), [-5, -5, 3, 6]) @@ -89,8 +90,10 @@ class SwigHelperTest(absltest.TestCase): solve_wrapper = swig_helper.SolveWrapper() solution = cp_model_pb2.CpSolverResponse.FromString( - swig_helper.SolveWrapper.SerializedSolve(model.SerializeToString(), - solve_wrapper)) + swig_helper.SolveWrapper.SerializedSolve( + model.SerializeToString(), solve_wrapper + ) + ) self.assertEqual(cp_model_pb2.OPTIMAL, solution.status) self.assertEqual(30.0, solution.objective_value) @@ -135,10 +138,13 @@ class SwigHelperTest(absltest.TestCase): solve_wrapper = swig_helper.SolveWrapper() swig_helper.SolveWrapper.SetSerializedParameters( - parameters.SerializeToString(), solve_wrapper) + parameters.SerializeToString(), solve_wrapper + ) solution = cp_model_pb2.CpSolverResponse.FromString( - swig_helper.SolveWrapper.SerializedSolve(model.SerializeToString(), - solve_wrapper)) + swig_helper.SolveWrapper.SerializedSolve( + model.SerializeToString(), solve_wrapper + ) + ) self.assertEqual(cp_model_pb2.OPTIMAL, solution.status) self.assertEqual(30.0, solution.objective_value) @@ -161,8 +167,10 @@ class SwigHelperTest(absltest.TestCase): solve_wrapper = swig_helper.SolveWrapper() solution = cp_model_pb2.CpSolverResponse.FromString( - swig_helper.SolveWrapper.SerializedSolve(model.SerializeToString(), - solve_wrapper)) + swig_helper.SolveWrapper.SerializedSolve( + model.SerializeToString(), solve_wrapper + ) + ) self.assertEqual(cp_model_pb2.OPTIMAL, solution.status) self.assertEqual(30.0, solution.objective_value) @@ -184,10 +192,13 @@ class SwigHelperTest(absltest.TestCase): params = sat_parameters_pb2.SatParameters() params.enumerate_all_solutions = True swig_helper.SolveWrapper.SetSerializedParameters( - params.SerializeToString(), solve_wrapper) + params.SerializeToString(), solve_wrapper + ) solution = cp_model_pb2.CpSolverResponse.FromString( - swig_helper.SolveWrapper.SerializedSolve(model.SerializeToString(), - solve_wrapper)) + swig_helper.SolveWrapper.SerializedSolve( + model.SerializeToString(), solve_wrapper + ) + ) self.assertEqual(5, callback.SolutionCount()) self.assertEqual(cp_model_pb2.OPTIMAL, solution.status) @@ -228,10 +239,9 @@ class SwigHelperTest(absltest.TestCase): """ model = cp_model_pb2.CpModelProto() self.assertTrue(text_format.Parse(model_string, model)) - stats = swig_helper.CpSatHelper.SerializedModelStats( - model.SerializeToString()) + stats = swig_helper.CpSatHelper.SerializedModelStats(model.SerializeToString()) self.assertTrue(stats) -if __name__ == '__main__': +if __name__ == "__main__": absltest.main() diff --git a/ortools/util/python/sorted_interval_list_test.py b/ortools/util/python/sorted_interval_list_test.py index 108ff85d90..2f0396cc49 100755 --- a/ortools/util/python/sorted_interval_list_test.py +++ b/ortools/util/python/sorted_interval_list_test.py @@ -11,6 +11,7 @@ # 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. + """Tests for ortools.util.python.sorted_interval_list.""" import unittest @@ -18,22 +19,20 @@ from ortools.util.python import sorted_interval_list class SortedIntervalListTest(unittest.TestCase): - def testCtorAndGetter(self): bool_domain = sorted_interval_list.Domain(0, 1) self.assertEqual(2, bool_domain.Size()) self.assertEqual(0, bool_domain.Min()) self.assertEqual(1, bool_domain.Max()) self.assertFalse(bool_domain.IsEmpty()) - self.assertEqual(str(bool_domain), '[0,1]') + self.assertEqual(str(bool_domain), "[0,1]") def testFromValues(self): domain = sorted_interval_list.Domain.FromValues([1, 3, -5, 5]) self.assertEqual(4, domain.Size()) self.assertEqual(-5, domain.Min()) self.assertEqual(5, domain.Max()) - self.assertEqual([-5, -5, 1, 1, 3, 3, 5, 5], - domain.FlattenedIntervals()) + self.assertEqual([-5, -5, 1, 1, 3, 3, 5, 5], domain.FlattenedIntervals()) self.assertTrue(domain.Contains(1)) self.assertFalse(domain.Contains(0)) @@ -86,5 +85,5 @@ class SortedIntervalListTest(unittest.TestCase): self.assertEqual([6, 9223372036854775807], d2.FlattenedIntervals()) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main()