python: run black on all files

This commit is contained in:
Mizux Seiha
2024-04-11 10:56:30 +02:00
committed by Corentin Le Molgat
parent 8c51109f2d
commit be220948a3
17 changed files with 574 additions and 523 deletions

View File

@@ -1,3 +1,4 @@
#!/usr/bin/env python3
# Copyright 2010-2024 Google LLC
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -10,15 +11,16 @@
# 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.
"""Cutting stock problem with the objective to minimize wasted space."""
"""Cutting stock problem with the objective to minimize wasted space."""
import collections
import time
import numpy as np
from absl import app
from absl import flags
import numpy as np
from google.protobuf import text_format
from ortools.linear_solver.python import model_builder as mb
from ortools.sat.python import cp_model
@@ -26,13 +28,14 @@ from ortools.sat.python import cp_model
FLAGS = flags.FLAGS
_OUTPUT_PROTO = flags.DEFINE_string(
'output_proto', '', 'Output file to write the cp_model proto to.')
"output_proto", "", "Output file to write the cp_model proto to."
)
_PARAMS = flags.DEFINE_string(
'params',
'num_search_workers:8,log_search_progress:true,max_time_in_seconds:10',
'Sat solver parameters.')
_SOLVER = flags.DEFINE_string(
'solver', 'sat', 'Method used to solve: sat, mip.')
"params",
"num_search_workers:8,log_search_progress:true,max_time_in_seconds:10",
"Sat solver parameters.",
)
_SOLVER = flags.DEFINE_string("solver", "sat", "Method used to solve: sat, mip.")
DESIRED_LENGTHS = [
@@ -104,9 +107,9 @@ def create_state_graph(items, max_capacity):
states.append(new_state)
state_to_index[new_state] = new_state_index
# Add the transition
transitions.append([
current_state_index, new_state_index, item_index, card + 1
])
transitions.append(
[current_state_index, new_state_index, item_index, card + 1]
)
return states, transitions
@@ -114,14 +117,19 @@ def create_state_graph(items, max_capacity):
def solve_cutting_stock_with_arc_flow_and_sat(output_proto_file: str, params: str):
"""Solve the cutting stock with arc-flow and the CP-SAT solver."""
items = regroup_and_count(DESIRED_LENGTHS)
print('Items:', items)
print("Items:", items)
num_items = len(DESIRED_LENGTHS)
max_capacity = max(POSSIBLE_CAPACITIES)
states, transitions = create_state_graph(items, max_capacity)
print('Dynamic programming has generated', len(states), 'states and',
len(transitions), 'transitions')
print(
"Dynamic programming has generated",
len(states),
"states and",
len(transitions),
"transitions",
)
incoming_vars = collections.defaultdict(list)
outgoing_vars = collections.defaultdict(list)
@@ -139,8 +147,8 @@ def solve_cutting_stock_with_arc_flow_and_sat(output_proto_file: str, params: st
count = items[item_index][1]
max_count = count // card
count_var = model.NewIntVar(
0, max_count,
'i%i_f%i_t%i_C%s' % (item_index, incoming, outgoing, card))
0, max_count, "i%i_f%i_t%i_C%s" % (item_index, incoming, outgoing, card)
)
incoming_vars[incoming].append(count_var)
outgoing_vars[outgoing].append(count_var)
item_vars[item_index].append(count_var)
@@ -150,7 +158,7 @@ def solve_cutting_stock_with_arc_flow_and_sat(output_proto_file: str, params: st
for state_index, state in enumerate(states):
if state_index == 0:
continue
exit_var = model.NewIntVar(0, num_items, 'e%i' % state_index)
exit_var = model.NewIntVar(0, num_items, "e%i" % state_index)
outgoing_vars[state_index].append(exit_var)
incoming_sink_vars.append(exit_var)
price = price_usage(state, POSSIBLE_CAPACITIES)
@@ -159,8 +167,7 @@ def solve_cutting_stock_with_arc_flow_and_sat(output_proto_file: str, params: st
# Flow conservation
for state_index in range(1, len(states)):
model.Add(
sum(incoming_vars[state_index]) == sum(outgoing_vars[state_index]))
model.Add(sum(incoming_vars[state_index]) == sum(outgoing_vars[state_index]))
# Flow going out of the source must go in the sink
model.Add(sum(outgoing_vars[0]) == sum(incoming_sink_vars))
@@ -169,13 +176,17 @@ def solve_cutting_stock_with_arc_flow_and_sat(output_proto_file: str, params: st
for item_index, size_and_count in enumerate(items):
num_arcs = len(item_vars[item_index])
model.Add(
sum(item_vars[item_index][i] * item_coeffs[item_index][i]
for i in range(num_arcs)) == size_and_count[1])
sum(
item_vars[item_index][i] * item_coeffs[item_index][i]
for i in range(num_arcs)
)
== size_and_count[1]
)
# Objective is the sum of waste
model.Minimize(
sum(objective_vars[i] * objective_coeffs[i]
for i in range(len(objective_vars))))
sum(objective_vars[i] * objective_coeffs[i] for i in range(len(objective_vars)))
)
# Output model proto to file.
if output_proto_file:
@@ -192,13 +203,18 @@ def solve_cutting_stock_with_arc_flow_and_sat(output_proto_file: str, params: st
def solve_cutting_stock_with_arc_flow_and_mip():
"""Solve the cutting stock with arc-flow and a MIP solver."""
items = regroup_and_count(DESIRED_LENGTHS)
print('Items:', items)
print("Items:", items)
num_items = len(DESIRED_LENGTHS)
max_capacity = max(POSSIBLE_CAPACITIES)
states, transitions = create_state_graph(items, max_capacity)
print('Dynamic programming has generated', len(states), 'states and',
len(transitions), 'transitions')
print(
"Dynamic programming has generated",
len(states),
"states and",
len(transitions),
"transitions",
)
incoming_vars = collections.defaultdict(list)
outgoing_vars = collections.defaultdict(list)
@@ -216,8 +232,10 @@ def solve_cutting_stock_with_arc_flow_and_mip():
for outgoing, incoming, item_index, card in transitions:
count = items[item_index][1]
count_var = model.new_int_var(
0, count, 'a%i_i%i_f%i_t%i_c%i' % (var_index, item_index, incoming,
outgoing, card))
0,
count,
"a%i_i%i_f%i_t%i_c%i" % (var_index, item_index, incoming, outgoing, card),
)
var_index += 1
incoming_vars[incoming].append(count_var)
outgoing_vars[outgoing].append(count_var)
@@ -227,7 +245,7 @@ def solve_cutting_stock_with_arc_flow_and_mip():
for state_index, state in enumerate(states):
if state_index == 0:
continue
exit_var = model.new_int_var(0, num_items, 'e%i' % state_index)
exit_var = model.new_int_var(0, num_items, "e%i" % state_index)
outgoing_vars[state_index].append(exit_var)
incoming_sink_vars.append(exit_var)
price = price_usage(state, POSSIBLE_CAPACITIES)
@@ -237,44 +255,52 @@ def solve_cutting_stock_with_arc_flow_and_mip():
# Flow conservation
for state_index in range(1, len(states)):
model.add(
mb.LinearExpr.sum(incoming_vars[state_index]) == mb.LinearExpr.sum(
outgoing_vars[state_index]))
mb.LinearExpr.sum(incoming_vars[state_index])
== mb.LinearExpr.sum(outgoing_vars[state_index])
)
# Flow going out of the source must go in the sink
model.add(
mb.LinearExpr.sum(outgoing_vars[0]) == mb.LinearExpr.sum(
incoming_sink_vars))
mb.LinearExpr.sum(outgoing_vars[0]) == mb.LinearExpr.sum(incoming_sink_vars)
)
# Items must be placed
for item_index, size_and_count in enumerate(items):
num_arcs = len(item_vars[item_index])
model.add(
mb.LinearExpr.sum([item_vars[item_index][i] * item_coeffs[item_index][i]
for i in range(num_arcs)]) == size_and_count[1])
mb.LinearExpr.sum(
[
item_vars[item_index][i] * item_coeffs[item_index][i]
for i in range(num_arcs)
]
)
== size_and_count[1]
)
# Objective is the sum of waste
model.minimize(np.dot(objective_vars, objective_coeffs))
solver = mb.ModelSolver('scip')
solver = mb.ModelSolver("scip")
solver.enable_output(True)
status = solver.solve(model)
### Output the solution.
if status == mb.SolveStatus.OPTIMAL or status == mb.SolveStatus.FEASIBLE:
print('Objective value = %f found in %.2f s' %
(solver.objective_value, time.time() - start_time))
print(
"Objective value = %f found in %.2f s"
% (solver.objective_value, time.time() - start_time)
)
else:
print('No solution')
print("No solution")
def main(_):
"""Main function"""
if _SOLVER.value == 'sat':
solve_cutting_stock_with_arc_flow_and_sat(_OUTPUT_PROTO.value,
_PARAMS.value)
"""Main function."""
if _SOLVER.value == "sat":
solve_cutting_stock_with_arc_flow_and_sat(_OUTPUT_PROTO.value, _PARAMS.value)
else: # 'mip'
solve_cutting_stock_with_arc_flow_and_mip()
if __name__ == '__main__':
if __name__ == "__main__":
app.run(main)

View File

@@ -18,4 +18,3 @@ exports_files(
"salbp_20_1.alb",
],
)

View File

@@ -78,52 +78,69 @@ def create_data_model():
(3, 7),
(6, 7),
(0, 8),
(7, 8)
(7, 8),
]
# Compute locations in meters using the block dimension defined as follow
# Manhattan average block: 750ft x 264ft -> 228m x 80m
# here we use: 114m x 80m city block
# src: https://nyti.ms/2GDoRIe 'NY Times: Know Your distance'
data['locations'] = [(l[0] * 114, l[1] * 80) for l in _locations]
data['num_locations'] = len(data['locations'])
data['demands'] = \
[0, # depot
-_capacity, # unload depot_first
-_capacity, # unload depot_second
-_capacity, # unload depot_third
-_capacity, # unload depot_fourth
-_capacity, # unload depot_fifth
3, 3, # 1, 2
3, 4, # 3, 4
3, 4, # 5, 6
8, 8, # 7, 8
3, 3, # 9,10
3, 3, # 11,12
4, 4, # 13, 14
8, 8] # 15, 16
data['time_per_demand_unit'] = 5 # 5 minutes/unit
data['time_windows'] = \
[(0, 0), # depot
(0, 1000), # unload depot_first
(0, 1000), # unload depot_second
(0, 1000), # unload depot_third
(0, 1000), # unload depot_fourth
(0, 1000), # unload depot_fifth
(75, 850), (75, 850), # 1, 2
(60, 700), (45, 550), # 3, 4
(0, 800), (50, 600), # 5, 6
(0, 1000), (10, 200), # 7, 8
(0, 1000), (75, 850), # 9, 10
(85, 950), (5, 150), # 11, 12
(15, 250), (10, 200), # 13, 14
(45, 550), (30, 400)] # 15, 16
data['num_vehicles'] = 3
data['vehicle_capacity'] = _capacity
data['vehicle_max_distance'] = 10_000
data['vehicle_max_time'] = 1_500
data[
'vehicle_speed'] = 5 * 60 / 3.6 # Travel speed: 5km/h to convert in m/min
data['depot'] = 0
data["locations"] = [(l[0] * 114, l[1] * 80) for l in _locations]
data["num_locations"] = len(data["locations"])
data["demands"] = [
0, # depot
-_capacity, # unload depot_first
-_capacity, # unload depot_second
-_capacity, # unload depot_third
-_capacity, # unload depot_fourth
-_capacity, # unload depot_fifth
3,
3, # 1, 2
3,
4, # 3, 4
3,
4, # 5, 6
8,
8, # 7, 8
3,
3, # 9,10
3,
3, # 11,12
4,
4, # 13, 14
8,
8,
] # 15, 16
data["time_per_demand_unit"] = 5 # 5 minutes/unit
data["time_windows"] = [
(0, 0), # depot
(0, 1000), # unload depot_first
(0, 1000), # unload depot_second
(0, 1000), # unload depot_third
(0, 1000), # unload depot_fourth
(0, 1000), # unload depot_fifth
(75, 850),
(75, 850), # 1, 2
(60, 700),
(45, 550), # 3, 4
(0, 800),
(50, 600), # 5, 6
(0, 1000),
(10, 200), # 7, 8
(0, 1000),
(75, 850), # 9, 10
(85, 950),
(5, 150), # 11, 12
(15, 250),
(10, 200), # 13, 14
(45, 550),
(30, 400),
] # 15, 16
data["num_vehicles"] = 3
data["vehicle_capacity"] = _capacity
data["vehicle_max_distance"] = 10_000
data["vehicle_max_time"] = 1_500
data["vehicle_speed"] = 5 * 60 / 3.6 # Travel speed: 5km/h to convert in m/min
data["depot"] = 0
return data
@@ -132,30 +149,29 @@ def create_data_model():
#######################
def manhattan_distance(position_1, position_2):
"""Computes the Manhattan distance between two points"""
return (abs(position_1[0] - position_2[0]) +
abs(position_1[1] - position_2[1]))
return abs(position_1[0] - position_2[0]) + abs(position_1[1] - position_2[1])
def create_distance_evaluator(data):
"""Creates callback to return distance between points."""
_distances = {}
# precompute distance between location to have distance callback in O(1)
for from_node in range(data['num_locations']):
for from_node in range(data["num_locations"]):
_distances[from_node] = {}
for to_node in range(data['num_locations']):
for to_node in range(data["num_locations"]):
if from_node == to_node:
_distances[from_node][to_node] = 0
# Forbid start/end/reload node to be consecutive.
elif from_node in range(6) and to_node in range(6):
_distances[from_node][to_node] = data['vehicle_max_distance']
_distances[from_node][to_node] = data["vehicle_max_distance"]
else:
_distances[from_node][to_node] = (manhattan_distance(
data['locations'][from_node], data['locations'][to_node]))
_distances[from_node][to_node] = manhattan_distance(
data["locations"][from_node], data["locations"][to_node]
)
def distance_evaluator(manager, from_node, to_node):
"""Returns the manhattan distance between the two nodes"""
return _distances[manager.IndexToNode(from_node)][manager.IndexToNode(
to_node)]
return _distances[manager.IndexToNode(from_node)][manager.IndexToNode(to_node)]
return distance_evaluator
@@ -163,13 +179,14 @@ def create_distance_evaluator(data):
def add_distance_dimension(routing, manager, data, distance_evaluator_index):
"""Add Global Span constraint"""
del manager
distance = 'Distance'
distance = "Distance"
routing.AddDimension(
distance_evaluator_index,
0, # null slack
data['vehicle_max_distance'], # maximum distance per vehicle
data["vehicle_max_distance"], # maximum distance per vehicle
True, # start cumul to zero
distance)
distance,
)
distance_dimension = routing.GetDimensionOrDie(distance)
# Try to minimize the max distance among vehicles.
# /!\ It doesn't mean the standard deviation is minimized
@@ -178,7 +195,7 @@ def add_distance_dimension(routing, manager, data, distance_evaluator_index):
def create_demand_evaluator(data):
"""Creates callback to get demands at each location."""
_demands = data['demands']
_demands = data["demands"]
def demand_evaluator(manager, from_node):
"""Returns the demand of the current node"""
@@ -189,14 +206,15 @@ def create_demand_evaluator(data):
def add_capacity_constraints(routing, manager, data, demand_evaluator_index):
"""Adds capacity constraint"""
vehicle_capacity = data['vehicle_capacity']
capacity = 'Capacity'
vehicle_capacity = data["vehicle_capacity"]
capacity = "Capacity"
routing.AddDimension(
demand_evaluator_index,
vehicle_capacity,
vehicle_capacity,
True, # start cumul to zero
capacity)
capacity,
)
# Add Slack for reseting to zero unload depot nodes.
# e.g. vehicle with load 10/15 arrives at node 1 (depot unload)
@@ -208,7 +226,7 @@ def add_capacity_constraints(routing, manager, data, demand_evaluator_index):
routing.AddDisjunction([node_index], 0)
# Allow to drop regular node with a cost.
for node in range(6, len(data['demands'])):
for node in range(6, len(data["demands"])):
node_index = manager.NodeToIndex(node)
capacity_dimension.SlackVar(node_index).SetValue(0)
routing.AddDisjunction([node_index], 100_000)
@@ -219,52 +237,56 @@ def create_time_evaluator(data):
def service_time(data, node):
"""Gets the service time for the specified location."""
return abs(data['demands'][node]) * data['time_per_demand_unit']
return abs(data["demands"][node]) * data["time_per_demand_unit"]
def travel_time(data, from_node, to_node):
"""Gets the travel times between two locations."""
if from_node == to_node:
travel_time = 0
else:
travel_time = manhattan_distance(
data['locations'][from_node],
data['locations'][to_node]) / data['vehicle_speed']
travel_time = (
manhattan_distance(
data["locations"][from_node], data["locations"][to_node]
)
/ data["vehicle_speed"]
)
return travel_time
_total_time = {}
# precompute total time to have time callback in O(1)
for from_node in range(data['num_locations']):
for from_node in range(data["num_locations"]):
_total_time[from_node] = {}
for to_node in range(data['num_locations']):
for to_node in range(data["num_locations"]):
if from_node == to_node:
_total_time[from_node][to_node] = 0
else:
_total_time[from_node][to_node] = int(
service_time(data, from_node) +
travel_time(data, from_node, to_node))
service_time(data, from_node)
+ travel_time(data, from_node, to_node)
)
def time_evaluator(manager, from_node, to_node):
"""Returns the total time between the two nodes"""
return _total_time[manager.IndexToNode(from_node)][manager.IndexToNode(
to_node)]
return _total_time[manager.IndexToNode(from_node)][manager.IndexToNode(to_node)]
return time_evaluator
def add_time_window_constraints(routing, manager, data, time_evaluator):
"""Add Time windows constraint"""
time = 'Time'
max_time = data['vehicle_max_time']
time = "Time"
max_time = data["vehicle_max_time"]
routing.AddDimension(
time_evaluator,
max_time, # allow waiting time
max_time, # maximum time per vehicle
False, # don't force start cumul to zero since we are giving TW to start nodes
time)
time,
)
time_dimension = routing.GetDimensionOrDie(time)
# Add time window constraints for each location except depot
# and 'copy' the slack var in the solution object (aka Assignment) to print it
for location_idx, time_window in enumerate(data['time_windows']):
for location_idx, time_window in enumerate(data["time_windows"]):
if location_idx == 0:
continue
index = manager.NodeToIndex(location_idx)
@@ -272,70 +294,73 @@ def add_time_window_constraints(routing, manager, data, time_evaluator):
routing.AddToAssignment(time_dimension.SlackVar(index))
# Add time window constraints for each vehicle start node
# and 'copy' the slack var in the solution object (aka Assignment) to print it
for vehicle_id in range(data['num_vehicles']):
for vehicle_id in range(data["num_vehicles"]):
index = routing.Start(vehicle_id)
time_dimension.CumulVar(index).SetRange(data['time_windows'][0][0],
data['time_windows'][0][1])
time_dimension.CumulVar(index).SetRange(
data["time_windows"][0][0], data["time_windows"][0][1]
)
routing.AddToAssignment(time_dimension.SlackVar(index))
# Warning: Slack var is not defined for vehicle's end node
#routing.AddToAssignment(time_dimension.SlackVar(self.routing.End(vehicle_id)))
# routing.AddToAssignment(time_dimension.SlackVar(self.routing.End(vehicle_id)))
###########
# Printer #
###########
def print_solution(data, manager, routing, assignment): # pylint:disable=too-many-locals
def print_solution(
data, manager, routing, assignment
): # pylint:disable=too-many-locals
"""Prints assignment on console"""
print(f'Objective: {assignment.ObjectiveValue()}')
print(f"Objective: {assignment.ObjectiveValue()}")
total_distance = 0
total_load = 0
total_time = 0
capacity_dimension = routing.GetDimensionOrDie('Capacity')
time_dimension = routing.GetDimensionOrDie('Time')
capacity_dimension = routing.GetDimensionOrDie("Capacity")
time_dimension = routing.GetDimensionOrDie("Time")
dropped = []
for order in range(6, routing.nodes()):
index = manager.NodeToIndex(order)
if assignment.Value(routing.NextVar(index)) == index:
dropped.append(order)
print(f'dropped orders: {dropped}')
print(f"dropped orders: {dropped}")
for reload in range(1, 6):
index = manager.NodeToIndex(reload)
if assignment.Value(routing.NextVar(index)) == index:
dropped.append(reload)
print(f'dropped reload stations: {dropped}')
print(f"dropped reload stations: {dropped}")
for vehicle_id in range(data['num_vehicles']):
for vehicle_id in range(data["num_vehicles"]):
index = routing.Start(vehicle_id)
plan_output = f'Route for vehicle {vehicle_id}:\n'
plan_output = f"Route for vehicle {vehicle_id}:\n"
distance = 0
while not routing.IsEnd(index):
load_var = capacity_dimension.CumulVar(index)
time_var = time_dimension.CumulVar(index)
plan_output += (
f' {manager.IndexToNode(index)} '
f'Load({assignment.Min(load_var)}) '
f'Time({assignment.Min(time_var)},{assignment.Max(time_var)}) ->'
f" {manager.IndexToNode(index)} "
f"Load({assignment.Min(load_var)}) "
f"Time({assignment.Min(time_var)},{assignment.Max(time_var)}) ->"
)
previous_index = index
index = assignment.Value(routing.NextVar(index))
distance += routing.GetArcCostForVehicle(previous_index, index,
vehicle_id)
distance += routing.GetArcCostForVehicle(previous_index, index, vehicle_id)
load_var = capacity_dimension.CumulVar(index)
time_var = time_dimension.CumulVar(index)
plan_output += (
f' {manager.IndexToNode(index)} '
f'Load({assignment.Min(load_var)}) '
f'Time({assignment.Min(time_var)},{assignment.Max(time_var)})\n')
plan_output += f'Distance of the route: {distance}m\n'
plan_output += f'Load of the route: {assignment.Min(load_var)}\n'
plan_output += f'Time of the route: {assignment.Min(time_var)}min\n'
f" {manager.IndexToNode(index)} "
f"Load({assignment.Min(load_var)}) "
f"Time({assignment.Min(time_var)},{assignment.Max(time_var)})\n"
)
plan_output += f"Distance of the route: {distance}m\n"
plan_output += f"Load of the route: {assignment.Min(load_var)}\n"
plan_output += f"Time of the route: {assignment.Min(time_var)}min\n"
print(plan_output)
total_distance += distance
total_load += assignment.Min(load_var)
total_time += assignment.Min(time_var)
print(f'Total Distance of all routes: {total_distance}m')
print(f'Total Load of all routes: {total_load}')
print(f'Total Time of all routes: {total_time}min')
print(f"Total Distance of all routes: {total_distance}m")
print(f"Total Load of all routes: {total_load}")
print(f"Total Time of all routes: {total_time}min")
########
@@ -347,15 +372,17 @@ def main():
data = create_data_model()
# Create the routing index manager
manager = pywrapcp.RoutingIndexManager(data['num_locations'],
data['num_vehicles'], data['depot'])
manager = pywrapcp.RoutingIndexManager(
data["num_locations"], data["num_vehicles"], data["depot"]
)
# Create Routing Model
routing = pywrapcp.RoutingModel(manager)
# Define weight of each edge
distance_evaluator_index = routing.RegisterTransitCallback(
partial(create_distance_evaluator(data), manager))
partial(create_distance_evaluator(data), manager)
)
routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator_index)
# Add Distance constraint to minimize the longuest route
@@ -363,20 +390,24 @@ def main():
# Add Capacity constraint
demand_evaluator_index = routing.RegisterUnaryTransitCallback(
partial(create_demand_evaluator(data), manager))
partial(create_demand_evaluator(data), manager)
)
add_capacity_constraints(routing, manager, data, demand_evaluator_index)
# Add Time Window constraint
time_evaluator_index = routing.RegisterTransitCallback(
partial(create_time_evaluator(data), manager))
partial(create_time_evaluator(data), manager)
)
add_time_window_constraints(routing, manager, data, time_evaluator_index)
# Setting first solution heuristic (cheapest addition).
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
search_parameters.first_solution_strategy = (
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC
) # pylint: disable=no-member
search_parameters.local_search_metaheuristic = (
routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)
routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH
)
search_parameters.time_limit.FromSeconds(3)
# Solve the problem.
@@ -387,5 +418,5 @@ def main():
print("No solution found !")
if __name__ == '__main__':
if __name__ == "__main__":
main()

View File

@@ -33,9 +33,9 @@ from ortools.constraint_solver import pywrapcp
def create_data_model():
"""Stores the data for the problem."""
data = {}
data['num_vehicles'] = 4
data['depot'] = 0
data['time_matrix'] = [
data["num_vehicles"] = 4
data["depot"] = 0
data["time_matrix"] = [
[0, 27, 38, 34, 29, 13, 25, 9, 15, 9, 26, 25, 19, 17, 23, 38, 33],
[27, 0, 34, 15, 9, 25, 36, 17, 34, 37, 54, 29, 24, 33, 50, 43, 60],
[38, 34, 0, 49, 43, 25, 13, 40, 23, 37, 20, 63, 58, 56, 39, 77, 37],
@@ -55,9 +55,9 @@ def create_data_model():
[33, 60, 37, 67, 62, 35, 24, 42, 25, 23, 17, 42, 36, 26, 9, 39, 0],
]
# 15 min of service time
data['service_time'] = [15] * len(data['time_matrix'])
data['service_time'][data['depot']] = 0
assert len(data['time_matrix']) == len(data['service_time'])
data["service_time"] = [15] * len(data["time_matrix"])
data["service_time"][data["depot"]] = 0
assert len(data["time_matrix"]) == len(data["service_time"])
return data
# [END data_model]
@@ -65,38 +65,40 @@ def create_data_model():
# [START solution_printer]
def print_solution(manager, routing, solution):
"""Prints solution on console."""
print(f'Objective: {solution.ObjectiveValue()}')
print(f"Objective: {solution.ObjectiveValue()}")
print('Breaks:')
print("Breaks:")
intervals = solution.IntervalVarContainer()
for i in range(intervals.Size()):
brk = intervals.Element(i)
if brk.PerformedValue() == 1:
print(f'{brk.Var().Name()}: ' +
f'Start({brk.StartValue()}) Duration({brk.DurationValue()})')
print(
f"{brk.Var().Name()}: "
+ f"Start({brk.StartValue()}) Duration({brk.DurationValue()})"
)
else:
print(f'{brk.Var().Name()}: Unperformed')
print(f"{brk.Var().Name()}: Unperformed")
time_dimension = routing.GetDimensionOrDie('Time')
time_dimension = routing.GetDimensionOrDie("Time")
total_time = 0
for vehicle_id in range(manager.GetNumberOfVehicles()):
index = routing.Start(vehicle_id)
plan_output = f'Route for vehicle {vehicle_id}:\n'
plan_output = f"Route for vehicle {vehicle_id}:\n"
while not routing.IsEnd(index):
time_var = time_dimension.CumulVar(index)
if routing.IsStart(index):
start_time = solution.Value(time_var)
plan_output += f'{manager.IndexToNode(index)} '
plan_output += f'Time({solution.Value(time_var)}) -> '
plan_output += f"{manager.IndexToNode(index)} "
plan_output += f"Time({solution.Value(time_var)}) -> "
index = solution.Value(routing.NextVar(index))
time_var = time_dimension.CumulVar(index)
plan_output += f'{manager.IndexToNode(index)} '
plan_output += f'Time({solution.Value(time_var)})'
plan_output += f"{manager.IndexToNode(index)} "
plan_output += f"Time({solution.Value(time_var)})"
print(plan_output)
route_time = solution.Value(time_var) - start_time
print(f'Time of the route: {route_time}min\n')
print(f"Time of the route: {route_time}min\n")
total_time += route_time
print(f'Total time of all routes: {total_time}min')
print(f"Total time of all routes: {total_time}min")
# [END solution_printer]
@@ -109,8 +111,9 @@ def main():
# Create the routing index manager.
# [START index_manager]
manager = pywrapcp.RoutingIndexManager(len(data['time_matrix']),
data['num_vehicles'], data['depot'])
manager = pywrapcp.RoutingIndexManager(
len(data["time_matrix"]), data["num_vehicles"], data["depot"]
)
# [END index_manager]
# Create Routing Model.
@@ -118,7 +121,6 @@ def main():
routing = pywrapcp.RoutingModel(manager)
# [END routing_model]
# Create and register a transit callback.
# [START transit_callback]
def time_callback(from_index, to_index):
@@ -126,7 +128,7 @@ def main():
# Convert from routing variable Index to time matrix NodeIndex.
from_node = manager.IndexToNode(from_index)
to_node = manager.IndexToNode(to_index)
return data['time_matrix'][from_node][to_node]
return data["time_matrix"][from_node][to_node]
transit_callback_index = routing.RegisterTransitCallback(time_callback)
# [END transit_callback]
@@ -137,13 +139,14 @@ def main():
# [END arc_cost]
# Add Time Windows constraint.
time = 'Time'
time = "Time"
routing.AddDimension(
transit_callback_index,
10, # need optional waiting time to place break
180, # maximum time per vehicle
False, # Don't force start cumul to zero.
time)
time,
)
time_dimension = routing.GetDimensionOrDie(time)
time_dimension.SetGlobalSpanCostCoefficient(10)
@@ -158,29 +161,32 @@ def main():
node_visit_transit = [0] * routing.Size()
for index in range(routing.Size()):
node = manager.IndexToNode(index)
node_visit_transit[index] = data['service_time'][node]
node_visit_transit[index] = data["service_time"][node]
# Add a break lasting 5 minutes, start between 25 and 45 minutes after route start
for v in range(manager.GetNumberOfVehicles()):
start_var = time_dimension.CumulVar(routing.Start(v))
break_start = routing.solver().Sum(
[routing.solver().IntVar(25, 45), start_var])
break_start = routing.solver().Sum([routing.solver().IntVar(25, 45), start_var])
break_intervals = [
routing.solver().FixedDurationIntervalVar(break_start, 5,
f'Break for vehicle {v}')
routing.solver().FixedDurationIntervalVar(
break_start, 5, f"Break for vehicle {v}"
)
]
time_dimension.SetBreakIntervalsOfVehicle(break_intervals, v,
node_visit_transit)
time_dimension.SetBreakIntervalsOfVehicle(
break_intervals, v, node_visit_transit
)
# [END break_constraint]
# Setting first solution heuristic.
# [START parameters]
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
search_parameters.first_solution_strategy = (
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC
)
search_parameters.local_search_metaheuristic = (
routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)
routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH
)
# search_parameters.log_search = True
search_parameters.time_limit.FromSeconds(2)
# [END parameters]
@@ -195,10 +201,10 @@ def main():
if solution:
print_solution(manager, routing, solution)
else:
print('No solution found !')
print("No solution found !")
# [END print_solution]
if __name__ == '__main__':
if __name__ == "__main__":
main()
# [END program]

View File

@@ -24,17 +24,17 @@ from ortools.constraint_solver import pywrapcp
def create_data_model():
"""Stores the data for the problem."""
data = {}
data['num_vehicles'] = 2
data["num_vehicles"] = 2
# [START starts_ends]
data['starts'] = [0] * data['num_vehicles']
data['ends'] = [1] * data['num_vehicles']
assert len(data['starts']) == data['num_vehicles']
assert len(data['ends']) == data['num_vehicles']
data["starts"] = [0] * data["num_vehicles"]
data["ends"] = [1] * data["num_vehicles"]
assert len(data["starts"]) == data["num_vehicles"]
assert len(data["ends"]) == data["num_vehicles"]
# [END starts_ends]
# [START demands_capacities]
# Need 11 X and 13 Y
data['providers_x'] = [
data["providers_x"] = [
0, # start
-11, # end
2, # X supply 1
@@ -53,7 +53,7 @@ def create_data_model():
0, # Y supply 5
0, # Y supply 6
]
data['providers_y'] = [
data["providers_y"] = [
0, # start
-13, # ends
0, # X supply 1
@@ -72,12 +72,12 @@ def create_data_model():
3, # Y supply 5
5, # Y supply 6
]
data['vehicle_capacities_x'] = [15] * data['num_vehicles']
data['vehicle_capacities_y'] = [15] * data['num_vehicles']
assert len(data['vehicle_capacities_x']) == data['num_vehicles']
assert len(data['vehicle_capacities_y']) == data['num_vehicles']
data["vehicle_capacities_x"] = [15] * data["num_vehicles"]
data["vehicle_capacities_y"] = [15] * data["num_vehicles"]
assert len(data["vehicle_capacities_x"]) == data["num_vehicles"]
assert len(data["vehicle_capacities_y"]) == data["num_vehicles"]
# [END demands_capacities]
data['distance_matrix'] = [
data["distance_matrix"] = [
[
0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354,
468, 776, 662
@@ -208,9 +208,9 @@ def main():
# Create the routing index manager.
# [START index_manager]
manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']),
data['num_vehicles'], data['starts'],
data['ends'])
manager = pywrapcp.RoutingIndexManager(
len(data["distance_matrix"]), data["num_vehicles"], data["starts"], data["ends"]
)
# [END index_manager]
# Create Routing Model.
@@ -226,7 +226,7 @@ def main():
# Convert from routing variable Index to distance matrix NodeIndex.
from_node = manager.IndexToNode(from_index)
to_node = manager.IndexToNode(to_index)
return data['distance_matrix'][from_node][to_node]
return data["distance_matrix"][from_node][to_node]
transit_callback_index = routing.RegisterTransitCallback(distance_callback)
# [END transit_callback]
@@ -238,13 +238,14 @@ def main():
# Add Distance constraint.
# [START distance_constraint]
dimension_name = 'Distance'
dimension_name = "Distance"
routing.AddDimension(
transit_callback_index,
0, # no slack
2000, # vehicle maximum travel distance
True, # start cumul to zero
dimension_name)
dimension_name,
)
distance_dimension = routing.GetDimensionOrDie(dimension_name)
# Minimize the longest road
distance_dimension.SetGlobalSpanCostCoefficient(100)
@@ -257,65 +258,69 @@ def main():
"""Returns the demand of the node."""
# Convert from routing variable Index to demands NodeIndex.
from_node = manager.IndexToNode(from_index)
return data['providers_x'][from_node]
return data["providers_x"][from_node]
demand_callback_x_index = routing.RegisterUnaryTransitCallback(
demand_callback_x)
demand_callback_x_index = routing.RegisterUnaryTransitCallback(demand_callback_x)
routing.AddDimensionWithVehicleCapacity(
demand_callback_x_index,
0, # null capacity slack
data['vehicle_capacities_x'], # vehicle maximum capacities
data["vehicle_capacities_x"], # vehicle maximum capacities
True, # start cumul to zero
'Load_x')
"Load_x",
)
def demand_callback_y(from_index):
"""Returns the demand of the node."""
# Convert from routing variable Index to demands NodeIndex.
from_node = manager.IndexToNode(from_index)
return data['providers_y'][from_node]
return data["providers_y"][from_node]
demand_callback_y_index = routing.RegisterUnaryTransitCallback(
demand_callback_y)
demand_callback_y_index = routing.RegisterUnaryTransitCallback(demand_callback_y)
routing.AddDimensionWithVehicleCapacity(
demand_callback_y_index,
0, # null capacity slack
data['vehicle_capacities_y'], # vehicle maximum capacities
data["vehicle_capacities_y"], # vehicle maximum capacities
True, # start cumul to zero
'Load_y')
"Load_y",
)
# [END capacity_constraint]
# Add constraint at end
solver = routing.solver()
load_x_dim = routing.GetDimensionOrDie('Load_x')
load_y_dim = routing.GetDimensionOrDie('Load_y')
load_x_dim = routing.GetDimensionOrDie("Load_x")
load_y_dim = routing.GetDimensionOrDie("Load_y")
ends = []
for v in range(manager.GetNumberOfVehicles()):
ends.append(routing.End(v))
node_end = data['ends'][0]
node_end = data["ends"][0]
solver.Add(
solver.Sum([load_x_dim.CumulVar(l)
for l in ends]) >= -data['providers_x'][node_end])
solver.Sum([load_x_dim.CumulVar(l) for l in ends])
>= -data["providers_x"][node_end]
)
solver.Add(
solver.Sum([load_y_dim.CumulVar(l)
for l in ends]) >= -data['providers_y'][node_end])
#solver.Add(load_y_dim.CumulVar(end) >= -data['providers_y'][node_end])
solver.Sum([load_y_dim.CumulVar(l) for l in ends])
>= -data["providers_y"][node_end]
)
# solver.Add(load_y_dim.CumulVar(end) >= -data['providers_y'][node_end])
# Allow to freely drop any nodes.
penalty = 0
for node in range(0, len(data['distance_matrix'])):
if node not in data['starts'] and node not in data['ends']:
for node in range(0, len(data["distance_matrix"])):
if node not in data["starts"] and node not in data["ends"]:
routing.AddDisjunction([manager.NodeToIndex(node)], penalty)
# Setting first solution heuristic.
# [START parameters]
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
search_parameters.first_solution_strategy = (
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC
)
search_parameters.local_search_metaheuristic = (
routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)
routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH
)
# Sets a time limit; default is 100 milliseconds.
#search_parameters.log_search = True
# search_parameters.log_search = True
search_parameters.time_limit.FromSeconds(1)
# [END parameters]
@@ -329,10 +334,10 @@ def main():
if solution:
print_solution(data, manager, routing, solution)
else:
print('no solution found !')
print("no solution found !")
# [END print_solution]
if __name__ == '__main__':
if __name__ == "__main__":
main()
# [END program]

View File

@@ -137,7 +137,6 @@ def main():
# Create Routing Model.
# [START routing_model]
routing = pywrapcp.RoutingModel(manager)
# [END routing_model]
# Create and register a transit callback.

View File

@@ -51,60 +51,58 @@ def main():
manager = pywrapcp.RoutingIndexManager(locations, vehicles, starts, ends)
routing = pywrapcp.RoutingModel(manager)
print('Starts/Ends:')
header = '| |'
separator = '|---|'
v_starts = '| start |'
v_ends = '| end |'
print("Starts/Ends:")
header = "| |"
separator = "|---|"
v_starts = "| start |"
v_ends = "| end |"
for v in range(manager.GetNumberOfVehicles()):
header += f' vehicle {v} |'
separator += '---|'
v_starts += f' {starts[v]} |'
v_ends += f' {ends[v]} |'
header += f" vehicle {v} |"
separator += "---|"
v_starts += f" {starts[v]} |"
v_ends += f" {ends[v]} |"
print(header)
print(separator)
print(v_starts)
print(v_ends)
print('\nNodes:')
print("\nNodes:")
print(
'| locations | manager.GetNumberOfNodes | manager.GetNumberOfIndices | routing.nodes | routing.Size |'
"| locations | manager.GetNumberOfNodes | manager.GetNumberOfIndices | routing.nodes | routing.Size |"
)
print('|---|---|---|---|---|')
print("|---|---|---|---|---|")
print(
f'| {locations} | {manager.GetNumberOfNodes()} | {manager.GetNumberOfIndices()} | {routing.nodes()} | {routing.Size()} |'
f"| {locations} | {manager.GetNumberOfNodes()} | {manager.GetNumberOfIndices()} | {routing.nodes()} | {routing.Size()} |"
)
print('\nLocations:')
print('| node | index | routing.IsStart | routing.IsEnd |')
print('|---|---|---|---|')
print("\nLocations:")
print("| node | index | routing.IsStart | routing.IsEnd |")
print("|---|---|---|---|")
for node in range(manager.GetNumberOfNodes()):
if node in starts or node in ends:
continue
index = manager.NodeToIndex(node)
print(
f'| {node} | {index} | {routing.IsStart(index)} | {routing.IsEnd(index)} |'
f"| {node} | {index} | {routing.IsStart(index)} | {routing.IsEnd(index)} |"
)
print('\nStart/End:')
print(
'| vehicle | Start/end | node | index | routing.IsStart | routing.IsEnd |'
)
print('|---|---|---|---|---|---|')
print("\nStart/End:")
print("| vehicle | Start/end | node | index | routing.IsStart | routing.IsEnd |")
print("|---|---|---|---|---|---|")
for v in range(manager.GetNumberOfVehicles()):
start_index = routing.Start(v)
start_node = manager.IndexToNode(start_index)
print(
f'| {v} | start | {start_node} | {start_index} | {routing.IsStart(start_index)} | {routing.IsEnd(start_index)} |'
f"| {v} | start | {start_node} | {start_index} | {routing.IsStart(start_index)} | {routing.IsEnd(start_index)} |"
)
for v in range(manager.GetNumberOfVehicles()):
end_index = routing.End(v)
end_node = manager.IndexToNode(end_index)
print(
f'| {v} | end | {end_node} | {end_index} | {routing.IsStart(end_index)} | {routing.IsEnd(end_index)} |'
f"| {v} | end | {end_node} | {end_index} | {routing.IsStart(end_index)} | {routing.IsEnd(end_index)} |"
)
if __name__ == '__main__':
if __name__ == "__main__":
main()
# [END program]

View File

@@ -39,7 +39,7 @@ from ortools.constraint_solver import pywrapcp
def create_data_model():
"""Stores the data for the problem."""
data = {}
data['time_matrix'] = [
data["time_matrix"] = [
[0, 6, 9, 8, 7, 3, 6, 2, 3, 2, 6, 6, 4, 4, 5, 9, 7],
[6, 0, 8, 3, 2, 6, 8, 4, 8, 8, 13, 7, 5, 8, 12, 10, 14],
[9, 8, 0, 11, 10, 6, 3, 9, 5, 8, 4, 15, 14, 13, 9, 18, 9],
@@ -58,8 +58,8 @@ def create_data_model():
[9, 10, 18, 6, 8, 12, 15, 8, 13, 9, 13, 3, 4, 5, 9, 0, 9],
[7, 14, 9, 16, 14, 8, 5, 10, 6, 5, 4, 10, 8, 6, 2, 9, 0],
]
data['num_vehicles'] = 4
data['depot'] = 0
data["num_vehicles"] = 4
data["depot"] = 0
return data
# [END data_model]
@@ -67,9 +67,9 @@ def create_data_model():
# [START solution_printer]
def print_solution(manager, routing, assignment):
"""Prints solution on console."""
print(f'Objective: {assignment.ObjectiveValue()}')
print(f"Objective: {assignment.ObjectiveValue()}")
# Display dropped nodes.
dropped_nodes = 'Dropped nodes:'
dropped_nodes = "Dropped nodes:"
for index in range(routing.Size()):
if routing.IsStart(index) or routing.IsEnd(index):
continue
@@ -79,15 +79,15 @@ def print_solution(manager, routing, assignment):
original = node
while original > 16:
original = original - 16
dropped_nodes += f' {node}({original})'
dropped_nodes += f" {node}({original})"
else:
dropped_nodes += f' {node}'
dropped_nodes += f" {node}"
print(dropped_nodes)
# Display routes
time_dimension = routing.GetDimensionOrDie('Time')
time_dimension = routing.GetDimensionOrDie("Time")
total_time = 0
for vehicle_id in range(manager.GetNumberOfVehicles()):
plan_output = f'Route for vehicle {vehicle_id}:\n'
plan_output = f"Route for vehicle {vehicle_id}:\n"
index = routing.Start(vehicle_id)
start_time = 0
while not routing.IsEnd(index):
@@ -97,22 +97,22 @@ def print_solution(manager, routing, assignment):
original = node
while original > 16:
original = original - 16
plan_output += f'{node}({original})'
plan_output += f"{node}({original})"
else:
plan_output += f'{node}'
plan_output += f' Time:{assignment.Value(time_var)} -> '
plan_output += f"{node}"
plan_output += f" Time:{assignment.Value(time_var)} -> "
if start_time == 0:
start_time = assignment.Value(time_var)
index = assignment.Value(routing.NextVar(index))
time_var = time_dimension.CumulVar(index)
node = manager.IndexToNode(index)
plan_output += f'{node} Time:{assignment.Value(time_var)}\n'
plan_output += f"{node} Time:{assignment.Value(time_var)}\n"
end_time = assignment.Value(time_var)
duration = end_time - start_time
plan_output += f'Duration of the route:{duration}min\n'
plan_output += f"Duration of the route:{duration}min\n"
print(plan_output)
total_time += duration
print(f'Total duration of all routes: {total_time}min')
print(f"Total duration of all routes: {total_time}min")
# [END solution_printer]
@@ -126,9 +126,8 @@ def main():
# Create the routing index manager.
# [START index_manager]
manager = pywrapcp.RoutingIndexManager(
1 + 16 * 4, # number of locations
data['num_vehicles'],
data['depot'])
1 + 16 * 4, data["num_vehicles"], data["depot"] # number of locations
)
# [END index_manager]
# Create Routing Model.
@@ -152,9 +151,9 @@ def main():
to_node = to_node - 16
# add service of 25min for each location (except depot)
service_time = 0
if from_node != data['depot']:
if from_node != data["depot"]:
service_time = 25
return data['time_matrix'][from_node][to_node] + service_time
return data["time_matrix"][from_node][to_node] + service_time
transit_callback_index = routing.RegisterTransitCallback(time_callback)
# [END transit_callback]
@@ -166,17 +165,18 @@ def main():
# Add Time Windows constraint.
# [START time_windows_constraint]
time = 'Time'
time = "Time"
routing.AddDimension(
transit_callback_index,
0, # allow waiting time (0 min)
1020, # maximum time per vehicle (9 hours)
False, # Don't force start cumul to zero.
time)
time,
)
time_dimension = routing.GetDimensionOrDie(time)
# Add time window constraints for each location except depot.
for location_idx in range(17):
if location_idx == data['depot']:
if location_idx == data["depot"]:
continue
# Vehicle 0 location TW: [9am, 11am]
index_0 = manager.NodeToIndex(location_idx)
@@ -203,34 +203,36 @@ def main():
routing.AddDisjunction([index_0, index_1, index_2, index_3], penalty, 1)
# Add time window constraints for each vehicle start node.
depot_idx = data['depot']
for vehicle_id in range(data['num_vehicles']):
depot_idx = data["depot"]
for vehicle_id in range(data["num_vehicles"]):
index = routing.Start(vehicle_id)
time_dimension.CumulVar(index).SetRange(480, 1020) # (8am, 5pm)
# Add time window constraints for each vehicle end node.
depot_idx = data['depot']
for vehicle_id in range(data['num_vehicles']):
depot_idx = data["depot"]
for vehicle_id in range(data["num_vehicles"]):
index = routing.End(vehicle_id)
time_dimension.CumulVar(index).SetRange(480, 1020) # (8am, 5pm)
# [END time_windows_constraint]
# Instantiate route start and end times to produce feasible times.
# [START depot_start_end_times]
for i in range(data['num_vehicles']):
for i in range(data["num_vehicles"]):
routing.AddVariableMinimizedByFinalizer(
time_dimension.CumulVar(routing.Start(i)))
routing.AddVariableMinimizedByFinalizer(
time_dimension.CumulVar(routing.End(i)))
time_dimension.CumulVar(routing.Start(i))
)
routing.AddVariableMinimizedByFinalizer(time_dimension.CumulVar(routing.End(i)))
# [END depot_start_end_times]
# Setting first solution heuristic.
# [START parameters]
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
search_parameters.first_solution_strategy = (
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC
)
search_parameters.local_search_metaheuristic = (
routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)
routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH
)
search_parameters.time_limit.FromSeconds(1)
# [END parameters]
@@ -248,6 +250,6 @@ def main():
# [END print_solution]
if __name__ == '__main__':
if __name__ == "__main__":
main()
# [END program]

View File

@@ -208,6 +208,7 @@ class ProductCst(LinearExpr):
class Constant(LinearExpr):
def __init__(self, val):
self.__val = val

View File

@@ -7,18 +7,18 @@ import types
def Sum(arg):
if type(arg) is types.GeneratorType:
arg = [x for x in arg]
sum = 0
for i in arg:
sum += i
print('sum(%s) = %d' % (str(arg), sum))
if type(arg) is types.GeneratorType:
arg = [x for x in arg]
sum = 0
for i in arg:
sum += i
print("sum(%s) = %d" % (str(arg), sum))
def test_sum_no_brackets():
Sum(x for x in range(10) if x % 2 == 0)
Sum([x for x in range(10) if x % 2 == 0])
Sum(x for x in range(10) if x % 2 == 0)
Sum([x for x in range(10) if x % 2 == 0])
text_model = """
solver_type:GLOP_LINEAR_PROGRAMMING
@@ -37,24 +37,24 @@ model <
def test_proto():
input_proto = linear_solver_pb2.MPModelRequest()
text_format.Merge(text_model, input_proto)
solver = pywraplp.Solver.CreateSolver('glop')
print(input_proto)
# For now, create the model from the proto by parsing the proto
solver.LoadModelFromProto(input_proto.model)
solver.EnableOutput()
solver.Solve()
# Fill solution
solution = linear_solver_pb2.MPSolutionResponse()
solver.FillSolutionResponseProto(solution)
print(solution)
input_proto = linear_solver_pb2.MPModelRequest()
text_format.Merge(text_model, input_proto)
solver = pywraplp.Solver.CreateSolver("glop")
print(input_proto)
# For now, create the model from the proto by parsing the proto
solver.LoadModelFromProto(input_proto.model)
solver.EnableOutput()
solver.Solve()
# Fill solution
solution = linear_solver_pb2.MPSolutionResponse()
solver.FillSolutionResponseProto(solution)
print(solution)
def main():
test_sum_no_brackets()
test_proto()
test_sum_no_brackets()
test_proto()
if __name__ == '__main__':
main()
if __name__ == "__main__":
main()

View File

@@ -22,36 +22,39 @@ from ortools.linear_solver import pywraplp
class PyWrapLpTest(unittest.TestCase):
def RunLinearExampleNaturalLanguageAPI(self, optimization_problem_type):
"""Example of simple linear program with natural language API."""
solver = pywraplp.Solver('RunLinearExampleNaturalLanguageAPI',
optimization_problem_type)
solver = pywraplp.Solver(
"RunLinearExampleNaturalLanguageAPI", optimization_problem_type
)
infinity = solver.infinity()
# x1, x2 and x3 are continuous non-negative variables.
x1 = solver.NumVar(0.0, infinity, 'x1')
x2 = solver.NumVar(0.0, infinity, 'x2')
x3 = solver.NumVar(0.0, infinity, 'x3')
x1 = solver.NumVar(0.0, infinity, "x1")
x2 = solver.NumVar(0.0, infinity, "x2")
x3 = solver.NumVar(0.0, infinity, "x3")
solver.Maximize(10 * x1 + 6 * x2 + 4 * x3)
c0 = solver.Add(10 * x1 + 4 * x2 + 5 * x3 <= 600, 'ConstraintName0')
c0 = solver.Add(10 * x1 + 4 * x2 + 5 * x3 <= 600, "ConstraintName0")
c1 = solver.Add(2 * x1 + 2 * x2 + 6 * x3 <= 300)
sum_of_vars = sum([x1, x2, x3])
c2 = solver.Add(sum_of_vars <= 100.0, 'OtherConstraintName')
c2 = solver.Add(sum_of_vars <= 100.0, "OtherConstraintName")
self.SolveAndPrint(solver, [x1, x2, x3], [c0, c1, c2],
optimization_problem_type != pywraplp.Solver.PDLP_LINEAR_PROGRAMMING)
self.SolveAndPrint(
solver,
[x1, x2, x3],
[c0, c1, c2],
optimization_problem_type != pywraplp.Solver.PDLP_LINEAR_PROGRAMMING,
)
# Print a linear expression's solution value.
print(('Sum of vars: %s = %s' % (sum_of_vars,
sum_of_vars.solution_value())))
print(("Sum of vars: %s = %s" % (sum_of_vars, sum_of_vars.solution_value())))
def RunLinearExampleCppStyleAPI(self, optimization_problem_type):
"""Example of simple linear program with the C++ style API."""
solver = pywraplp.Solver('RunLinearExampleCppStyle',
optimization_problem_type)
solver = pywraplp.Solver("RunLinearExampleCppStyle", optimization_problem_type)
infinity = solver.infinity()
# x1, x2 and x3 are continuous non-negative variables.
x1 = solver.NumVar(0.0, infinity, 'x1')
x2 = solver.NumVar(0.0, infinity, 'x2')
x3 = solver.NumVar(0.0, infinity, 'x3')
x1 = solver.NumVar(0.0, infinity, "x1")
x2 = solver.NumVar(0.0, infinity, "x2")
x3 = solver.NumVar(0.0, infinity, "x3")
# Maximize 10 * x1 + 6 * x2 + 4 * x3.
objective = solver.Objective()
@@ -61,34 +64,39 @@ class PyWrapLpTest(unittest.TestCase):
objective.SetMaximization()
# x1 + x2 + x3 <= 100.
c0 = solver.Constraint(-infinity, 100.0, 'c0')
c0 = solver.Constraint(-infinity, 100.0, "c0")
c0.SetCoefficient(x1, 1)
c0.SetCoefficient(x2, 1)
c0.SetCoefficient(x3, 1)
# 10 * x1 + 4 * x2 + 5 * x3 <= 600.
c1 = solver.Constraint(-infinity, 600.0, 'c1')
c1 = solver.Constraint(-infinity, 600.0, "c1")
c1.SetCoefficient(x1, 10)
c1.SetCoefficient(x2, 4)
c1.SetCoefficient(x3, 5)
# 2 * x1 + 2 * x2 + 6 * x3 <= 300.
c2 = solver.Constraint(-infinity, 300.0, 'c2')
c2 = solver.Constraint(-infinity, 300.0, "c2")
c2.SetCoefficient(x1, 2)
c2.SetCoefficient(x2, 2)
c2.SetCoefficient(x3, 6)
self.SolveAndPrint(solver, [x1, x2, x3], [c0, c1, c2],
optimization_problem_type != pywraplp.Solver.PDLP_LINEAR_PROGRAMMING)
self.SolveAndPrint(
solver,
[x1, x2, x3],
[c0, c1, c2],
optimization_problem_type != pywraplp.Solver.PDLP_LINEAR_PROGRAMMING,
)
def RunMixedIntegerExampleCppStyleAPI(self, optimization_problem_type):
"""Example of simple mixed integer program with the C++ style API."""
solver = pywraplp.Solver('RunMixedIntegerExampleCppStyle',
optimization_problem_type)
solver = pywraplp.Solver(
"RunMixedIntegerExampleCppStyle", optimization_problem_type
)
infinity = solver.infinity()
# x1 and x2 are integer non-negative variables.
x1 = solver.IntVar(0.0, infinity, 'x1')
x2 = solver.IntVar(0.0, infinity, 'x2')
x1 = solver.IntVar(0.0, infinity, "x1")
x2 = solver.IntVar(0.0, infinity, "x2")
# Maximize x1 + 10 * x2.
objective = solver.Objective()
@@ -97,12 +105,12 @@ class PyWrapLpTest(unittest.TestCase):
objective.SetMaximization()
# x1 + 7 * x2 <= 17.5.
c0 = solver.Constraint(-infinity, 17.5, 'c0')
c0 = solver.Constraint(-infinity, 17.5, "c0")
c0.SetCoefficient(x1, 1)
c0.SetCoefficient(x2, 7)
# x1 <= 3.5.
c1 = solver.Constraint(-infinity, 3.5, 'c1')
c1 = solver.Constraint(-infinity, 3.5, "c1")
c1.SetCoefficient(x1, 1)
c1.SetCoefficient(x2, 0)
@@ -110,11 +118,10 @@ class PyWrapLpTest(unittest.TestCase):
def RunBooleanExampleCppStyleAPI(self, optimization_problem_type):
"""Example of simple boolean program with the C++ style API."""
solver = pywraplp.Solver('RunBooleanExampleCppStyle',
optimization_problem_type)
solver = pywraplp.Solver("RunBooleanExampleCppStyle", optimization_problem_type)
# x1 and x2 are integer non-negative variables.
x1 = solver.BoolVar('x1')
x2 = solver.BoolVar('x2')
x1 = solver.BoolVar("x1")
x2 = solver.BoolVar("x2")
# Minimize 2 * x1 + x2.
objective = solver.Objective()
@@ -123,7 +130,7 @@ class PyWrapLpTest(unittest.TestCase):
objective.SetMinimization()
# 1 <= x1 + 2 * x2 <= 3.
c0 = solver.Constraint(1, 3, 'c0')
c0 = solver.Constraint(1, 3, "c0")
c0.SetCoefficient(x1, 1)
c0.SetCoefficient(x2, 2)
@@ -131,10 +138,10 @@ class PyWrapLpTest(unittest.TestCase):
def SolveAndPrint(self, solver, variable_list, constraint_list, is_precise):
"""Solve the problem and print the solution."""
print(('Number of variables = %d' % solver.NumVariables()))
print(("Number of variables = %d" % solver.NumVariables()))
self.assertEqual(solver.NumVariables(), len(variable_list))
print(('Number of constraints = %d' % solver.NumConstraints()))
print(("Number of constraints = %d" % solver.NumConstraints()))
self.assertEqual(solver.NumConstraints(), len(constraint_list))
result_status = solver.Solve()
@@ -147,72 +154,84 @@ class PyWrapLpTest(unittest.TestCase):
if is_precise:
self.assertTrue(solver.VerifySolution(1e-7, True))
print(('Problem solved in %f milliseconds' % solver.wall_time()))
print(("Problem solved in %f milliseconds" % solver.wall_time()))
# The objective value of the solution.
print(('Optimal objective value = %f' % solver.Objective().Value()))
print(("Optimal objective value = %f" % solver.Objective().Value()))
# The value of each variable in the solution.
for variable in variable_list:
print(('%s = %f' % (variable.name(), variable.solution_value())))
print(("%s = %f" % (variable.name(), variable.solution_value())))
print('Advanced usage:')
print(('Problem solved in %d iterations' % solver.iterations()))
print("Advanced usage:")
print(("Problem solved in %d iterations" % solver.iterations()))
if not solver.IsMip():
for variable in variable_list:
print(('%s: reduced cost = %f' % (variable.name(),
variable.reduced_cost())))
print(
(
"%s: reduced cost = %f"
% (variable.name(), variable.reduced_cost())
)
)
activities = solver.ComputeConstraintActivities()
for i, constraint in enumerate(constraint_list):
print(
('constraint %d: dual value = %f\n'
' activity = %f' %
(i, constraint.dual_value(), activities[constraint.index()])))
(
"constraint %d: dual value = %f\n"
" activity = %f"
% (i, constraint.dual_value(), activities[constraint.index()])
)
)
def testApi(self):
print('testApi', flush=True)
all_names_and_problem_types = (list(
linear_solver_pb2.MPModelRequest.SolverType.items()))
print("testApi", flush=True)
all_names_and_problem_types = list(
linear_solver_pb2.MPModelRequest.SolverType.items()
)
for name, problem_type in all_names_and_problem_types:
with self.subTest(f'{name}: {problem_type}'):
print(f'######## {name}:{problem_type} #######', flush=True)
with self.subTest(f"{name}: {problem_type}"):
print(f"######## {name}:{problem_type} #######", flush=True)
if not pywraplp.Solver.SupportsProblemType(problem_type):
continue
if name.startswith('GUROBI'):
if name.startswith("GUROBI"):
continue
if name.startswith('KNAPSACK'):
if name.startswith("KNAPSACK"):
continue
if not name.startswith('SCIP'):
if not name.startswith("SCIP"):
continue
if name.endswith('LINEAR_PROGRAMMING'):
print(('\n------ Linear programming example with %s ------' %
name))
print('\n*** Natural language API ***')
if name.endswith("LINEAR_PROGRAMMING"):
print(("\n------ Linear programming example with %s ------" % name))
print("\n*** Natural language API ***")
self.RunLinearExampleNaturalLanguageAPI(problem_type)
print('\n*** C++ style API ***')
print("\n*** C++ style API ***")
self.RunLinearExampleCppStyleAPI(problem_type)
elif name.endswith('MIXED_INTEGER_PROGRAMMING'):
print((
'\n------ Mixed Integer programming example with %s ------'
% name))
print('\n*** C++ style API ***')
elif name.endswith("MIXED_INTEGER_PROGRAMMING"):
print(
(
"\n------ Mixed Integer programming example with %s ------"
% name
)
)
print("\n*** C++ style API ***")
self.RunMixedIntegerExampleCppStyleAPI(problem_type)
elif name.endswith('INTEGER_PROGRAMMING'):
print(('\n------ Boolean programming example with %s ------' %
name))
print('\n*** C++ style API ***')
elif name.endswith("INTEGER_PROGRAMMING"):
print(
("\n------ Boolean programming example with %s ------" % name)
)
print("\n*** C++ style API ***")
self.RunBooleanExampleCppStyleAPI(problem_type)
else:
print('ERROR: %s unsupported' % name)
print("ERROR: %s unsupported" % name)
def testSetHint(self):
print('testSetHint', flush=True)
solver = pywraplp.Solver('RunBooleanExampleCppStyle',
pywraplp.Solver.GLOP_LINEAR_PROGRAMMING)
print("testSetHint", flush=True)
solver = pywraplp.Solver(
"RunBooleanExampleCppStyle", pywraplp.Solver.GLOP_LINEAR_PROGRAMMING
)
infinity = solver.infinity()
# x1 and x2 are integer non-negative variables.
x1 = solver.BoolVar('x1')
x2 = solver.BoolVar('x2')
x1 = solver.BoolVar("x1")
x2 = solver.BoolVar("x2")
# Minimize 2 * x1 + x2.
objective = solver.Objective()
@@ -221,7 +240,7 @@ class PyWrapLpTest(unittest.TestCase):
objective.SetMinimization()
# 1 <= x1 + 2 * x2 <= 3.
c0 = solver.Constraint(1, 3, 'c0')
c0 = solver.Constraint(1, 3, "c0")
c0.SetCoefficient(x1, 1)
c0.SetCoefficient(x2, 2)
@@ -230,24 +249,24 @@ class PyWrapLpTest(unittest.TestCase):
self.assertEqual(1, len(solver.constraints()))
def testBopInfeasible(self):
print('testBopInfeasible', flush=True)
solver = pywraplp.Solver('test', pywraplp.Solver.BOP_INTEGER_PROGRAMMING)
print("testBopInfeasible", flush=True)
solver = pywraplp.Solver("test", pywraplp.Solver.BOP_INTEGER_PROGRAMMING)
solver.EnableOutput()
x = solver.IntVar(0, 10, "")
solver.Add(x >= 20)
result_status = solver.Solve()
print(result_status) # outputs: 0
print(result_status) # outputs: 0
def testLoadSolutionFromProto(self):
print('testLoadSolutionFromProto', flush=True)
solver = pywraplp.Solver('', pywraplp.Solver.GLOP_LINEAR_PROGRAMMING)
print("testLoadSolutionFromProto", flush=True)
solver = pywraplp.Solver("", pywraplp.Solver.GLOP_LINEAR_PROGRAMMING)
solver.LoadSolutionFromProto(linear_solver_pb2.MPSolutionResponse())
def testSolveFromProto(self):
print('testSolveFromProto', flush=True)
request_str = '''
print("testSolveFromProto", flush=True)
request_str = """
model {
maximize: false
objective_offset: 0
@@ -304,36 +323,35 @@ class PyWrapLpTest(unittest.TestCase):
solver_type: GLOP_LINEAR_PROGRAMMING
solver_time_limit_seconds: 1.0
solver_specific_parameters: ""
'''
"""
request = linear_solver_pb2.MPModelRequest()
text_format.Parse(request_str, request)
response = linear_solver_pb2.MPSolutionResponse()
self.assertEqual(len(request.model.variable), 3)
pywraplp.Solver.SolveWithProto(model_request=request, response=response)
self.assertEqual(
linear_solver_pb2.MPSolverResponseStatus.MPSOLVER_OPTIMAL,
response.status)
linear_solver_pb2.MPSolverResponseStatus.MPSOLVER_OPTIMAL, response.status
)
def testExportToMps(self):
"""Test MPS export."""
print('testExportToMps', flush=True)
solver = pywraplp.Solver('ExportMps',
pywraplp.Solver.GLOP_LINEAR_PROGRAMMING)
print("testExportToMps", flush=True)
solver = pywraplp.Solver("ExportMps", pywraplp.Solver.GLOP_LINEAR_PROGRAMMING)
infinity = solver.infinity()
# x1, x2 and x3 are continuous non-negative variables.
x1 = solver.NumVar(0.0, infinity, 'x1')
x2 = solver.NumVar(0.0, infinity, 'x2')
x3 = solver.NumVar(0.0, infinity, 'x3')
x1 = solver.NumVar(0.0, infinity, "x1")
x2 = solver.NumVar(0.0, infinity, "x2")
x3 = solver.NumVar(0.0, infinity, "x3")
solver.Maximize(10 * x1 + 6 * x2 + 4 * x3)
c0 = solver.Add(10 * x1 + 4 * x2 + 5 * x3 <= 600, 'ConstraintName0')
c0 = solver.Add(10 * x1 + 4 * x2 + 5 * x3 <= 600, "ConstraintName0")
c1 = solver.Add(2 * x1 + 2 * x2 + 6 * x3 <= 300)
sum_of_vars = sum([x1, x2, x3])
c2 = solver.Add(sum_of_vars <= 100.0, 'OtherConstraintName')
c2 = solver.Add(sum_of_vars <= 100.0, "OtherConstraintName")
mps_str = solver.ExportModelAsMpsFormat(fixed_format=False, obfuscated=False)
self.assertIn('ExportMps', mps_str)
self.assertIn("ExportMps", mps_str)
if __name__ == '__main__':
if __name__ == "__main__":
unittest.main()

View File

@@ -72,13 +72,13 @@ class ModelSubset:
constraints are included in the subset.
"""
variable_bounds: Mapping[
model.Variable, ModelSubsetBounds
] = immutabledict.immutabledict()
variable_bounds: Mapping[model.Variable, ModelSubsetBounds] = (
immutabledict.immutabledict()
)
variable_integrality: FrozenSet[model.Variable] = frozenset()
linear_constraints: Mapping[
model.LinearConstraint, ModelSubsetBounds
] = immutabledict.immutabledict()
linear_constraints: Mapping[model.LinearConstraint, ModelSubsetBounds] = (
immutabledict.immutabledict()
)
def empty(self) -> bool:
"""Returns true if all the nested constraint collections are empty.

View File

@@ -20,15 +20,13 @@ from ortools.math_opt.python import model
@typing.overload
def fast_sum(summands: Iterable[model.LinearTypes]) -> model.LinearSum:
...
def fast_sum(summands: Iterable[model.LinearTypes]) -> model.LinearSum: ...
@typing.overload
def fast_sum(
summands: Iterable[model.QuadraticTypes],
) -> Union[model.LinearSum, model.QuadraticSum]:
...
) -> Union[model.LinearSum, model.QuadraticSum]: ...
# TODO(b/312200030): There is a pytype bug so that for the code:

View File

@@ -180,12 +180,12 @@ class _UpdateTracker(model_storage.StorageUpdateTracker):
*quadratic_objective_updates
)
result.objective_updates.quadratic_coefficients.row_ids[:] = first_var_ids
result.objective_updates.quadratic_coefficients.column_ids[
:
] = second_var_ids
result.objective_updates.quadratic_coefficients.coefficients[
:
] = coefficients
result.objective_updates.quadratic_coefficients.column_ids[:] = (
second_var_ids
)
result.objective_updates.quadratic_coefficients.coefficients[:] = (
coefficients
)
# Linear constraint matrix updates
matrix_updates = [
(l, v, self.model.get_linear_constraint_coefficient(l, v))

View File

@@ -481,16 +481,13 @@ class LinearBase(metaclass=abc.ABCMeta):
_raise_ne_not_supported()
@typing.overload
def __le__(self, rhs: float) -> "UpperBoundedLinearExpression":
...
def __le__(self, rhs: float) -> "UpperBoundedLinearExpression": ...
@typing.overload
def __le__(self, rhs: "LinearBase") -> "BoundedLinearExpression":
...
def __le__(self, rhs: "LinearBase") -> "BoundedLinearExpression": ...
@typing.overload
def __le__(self, rhs: "BoundedLinearExpression") -> NoReturn:
...
def __le__(self, rhs: "BoundedLinearExpression") -> NoReturn: ...
def __le__(self, rhs):
if isinstance(rhs, (int, float)):
@@ -504,16 +501,13 @@ class LinearBase(metaclass=abc.ABCMeta):
_raise_binary_operator_type_error("<=", type(self), type(rhs))
@typing.overload
def __ge__(self, lhs: float) -> "LowerBoundedLinearExpression":
...
def __ge__(self, lhs: float) -> "LowerBoundedLinearExpression": ...
@typing.overload
def __ge__(self, lhs: "LinearBase") -> "BoundedLinearExpression":
...
def __ge__(self, lhs: "LinearBase") -> "BoundedLinearExpression": ...
@typing.overload
def __ge__(self, lhs: "BoundedLinearExpression") -> NoReturn:
...
def __ge__(self, lhs: "BoundedLinearExpression") -> NoReturn: ...
def __ge__(self, lhs):
if isinstance(lhs, (int, float)):
@@ -547,12 +541,10 @@ class LinearBase(metaclass=abc.ABCMeta):
return LinearSum((expr, -self))
@typing.overload
def __mul__(self, other: float) -> "LinearProduct":
...
def __mul__(self, other: float) -> "LinearProduct": ...
@typing.overload
def __mul__(self, other: "LinearBase") -> "LinearLinearProduct":
...
def __mul__(self, other: "LinearBase") -> "LinearLinearProduct": ...
def __mul__(self, other):
if not isinstance(other, (int, float, LinearBase)):
@@ -764,12 +756,10 @@ class Variable(LinearBase):
return f"<Variable id: {self.id}, name: {self.name!r}>"
@typing.overload
def __eq__(self, rhs: "Variable") -> "VarEqVar":
...
def __eq__(self, rhs: "Variable") -> "VarEqVar": ...
@typing.overload
def __eq__(self, rhs: LinearTypesExceptVariable) -> "BoundedLinearExpression":
...
def __eq__(self, rhs: LinearTypesExceptVariable) -> "BoundedLinearExpression": ...
def __eq__(self, rhs):
if isinstance(rhs, Variable):
@@ -777,12 +767,10 @@ class Variable(LinearBase):
return super().__eq__(rhs)
@typing.overload
def __ne__(self, rhs: "Variable") -> bool:
...
def __ne__(self, rhs: "Variable") -> bool: ...
@typing.overload
def __ne__(self, rhs: LinearTypesExceptVariable) -> NoReturn:
...
def __ne__(self, rhs: LinearTypesExceptVariable) -> NoReturn: ...
def __ne__(self, rhs):
if isinstance(rhs, Variable):
@@ -793,16 +781,13 @@ class Variable(LinearBase):
return hash((self.model, self.id))
@typing.overload
def __mul__(self, other: float) -> "LinearTerm":
...
def __mul__(self, other: float) -> "LinearTerm": ...
@typing.overload
def __mul__(self, other: Union["Variable", "LinearTerm"]) -> "QuadraticTerm":
...
def __mul__(self, other: Union["Variable", "LinearTerm"]) -> "QuadraticTerm": ...
@typing.overload
def __mul__(self, other: "LinearBase") -> "LinearLinearProduct":
...
def __mul__(self, other: "LinearBase") -> "LinearLinearProduct": ...
def __mul__(self, other):
if not isinstance(other, (int, float, LinearBase)):
@@ -869,16 +854,13 @@ class LinearTerm(LinearBase):
processed_elements.terms[self._variable] += self._coefficient * scale
@typing.overload
def __mul__(self, other: float) -> "LinearTerm":
...
def __mul__(self, other: float) -> "LinearTerm": ...
@typing.overload
def __mul__(self, other: Union["Variable", "LinearTerm"]) -> "QuadraticTerm":
...
def __mul__(self, other: Union["Variable", "LinearTerm"]) -> "QuadraticTerm": ...
@typing.overload
def __mul__(self, other: "LinearBase") -> "LinearLinearProduct":
...
def __mul__(self, other: "LinearBase") -> "LinearLinearProduct": ...
def __mul__(self, other):
if not isinstance(other, (int, float, LinearBase)):
@@ -1080,9 +1062,9 @@ class QuadraticExpression(QuadraticBase):
if isinstance(other, (int, float)):
self._offset = float(other)
self._linear_terms: Mapping[Variable, float] = immutabledict.immutabledict()
self._quadratic_terms: Mapping[
QuadraticTermKey, float
] = immutabledict.immutabledict()
self._quadratic_terms: Mapping[QuadraticTermKey, float] = (
immutabledict.immutabledict()
)
return
to_process: _QuadraticToProcessElements = _QuadraticToProcessElements(
@@ -1103,9 +1085,9 @@ class QuadraticExpression(QuadraticBase):
self._linear_terms: Mapping[Variable, float] = immutabledict.immutabledict(
processed_elements.terms
)
self._quadratic_terms: Mapping[
QuadraticTermKey, float
] = immutabledict.immutabledict(processed_elements.quadratic_terms)
self._quadratic_terms: Mapping[QuadraticTermKey, float] = (
immutabledict.immutabledict(processed_elements.quadratic_terms)
)
self._offset = processed_elements.offset
@property
@@ -1563,9 +1545,9 @@ class Model:
# detection). Do not access a variable or constraint from these maps
# directly, as they may no longer exist, use _get_or_make_variable() and
# _get_or_make_linear_constraint() defined below instead.
self._variable_ids: weakref.WeakValueDictionary[
int, Variable
] = weakref.WeakValueDictionary()
self._variable_ids: weakref.WeakValueDictionary[int, Variable] = (
weakref.WeakValueDictionary()
)
self._linear_constraint_ids: weakref.WeakValueDictionary[
int, LinearConstraint
] = weakref.WeakValueDictionary()

View File

@@ -483,16 +483,13 @@ class SolveResult:
return self.termination.objective_bounds.dual_bound
@overload
def variable_values(self, variables: None = ...) -> Dict[model.Variable, float]:
...
def variable_values(self, variables: None = ...) -> Dict[model.Variable, float]: ...
@overload
def variable_values(self, variables: model.Variable) -> float:
...
def variable_values(self, variables: model.Variable) -> float: ...
@overload
def variable_values(self, variables: Iterable[model.Variable]) -> List[float]:
...
def variable_values(self, variables: Iterable[model.Variable]) -> List[float]: ...
def variable_values(self, variables=None):
"""The variable values from the best primal feasible solution.
@@ -549,16 +546,17 @@ class SolveResult:
return bool(self.primal_rays)
@overload
def ray_variable_values(self, variables: None = ...) -> Dict[model.Variable, float]:
...
def ray_variable_values(
self, variables: None = ...
) -> Dict[model.Variable, float]: ...
@overload
def ray_variable_values(self, variables: model.Variable) -> float:
...
def ray_variable_values(self, variables: model.Variable) -> float: ...
@overload
def ray_variable_values(self, variables: Iterable[model.Variable]) -> List[float]:
...
def ray_variable_values(
self, variables: Iterable[model.Variable]
) -> List[float]: ...
def ray_variable_values(self, variables=None):
"""The variable values from the first primal ray.
@@ -613,18 +611,15 @@ class SolveResult:
@overload
def dual_values(
self, linear_constraints: None = ...
) -> Dict[model.LinearConstraint, float]:
...
) -> Dict[model.LinearConstraint, float]: ...
@overload
def dual_values(self, linear_constraints: model.LinearConstraint) -> float:
...
def dual_values(self, linear_constraints: model.LinearConstraint) -> float: ...
@overload
def dual_values(
self, linear_constraints: Iterable[model.LinearConstraint]
) -> List[float]:
...
) -> List[float]: ...
def dual_values(self, linear_constraints=None):
"""The dual values associated to the best solution.
@@ -670,16 +665,13 @@ class SolveResult:
)
@overload
def reduced_costs(self, variables: None = ...) -> Dict[model.Variable, float]:
...
def reduced_costs(self, variables: None = ...) -> Dict[model.Variable, float]: ...
@overload
def reduced_costs(self, variables: model.Variable) -> float:
...
def reduced_costs(self, variables: model.Variable) -> float: ...
@overload
def reduced_costs(self, variables: Iterable[model.Variable]) -> List[float]:
...
def reduced_costs(self, variables: Iterable[model.Variable]) -> List[float]: ...
def reduced_costs(self, variables=None):
"""The reduced costs associated to the best solution.
@@ -732,18 +724,15 @@ class SolveResult:
@overload
def ray_dual_values(
self, linear_constraints: None = ...
) -> Dict[model.LinearConstraint, float]:
...
) -> Dict[model.LinearConstraint, float]: ...
@overload
def ray_dual_values(self, linear_constraints: model.LinearConstraint) -> float:
...
def ray_dual_values(self, linear_constraints: model.LinearConstraint) -> float: ...
@overload
def ray_dual_values(
self, linear_constraints: Iterable[model.LinearConstraint]
) -> List[float]:
...
) -> List[float]: ...
def ray_dual_values(self, linear_constraints=None):
"""The dual values from the first dual ray.
@@ -781,16 +770,15 @@ class SolveResult:
)
@overload
def ray_reduced_costs(self, variables: None = ...) -> Dict[model.Variable, float]:
...
def ray_reduced_costs(
self, variables: None = ...
) -> Dict[model.Variable, float]: ...
@overload
def ray_reduced_costs(self, variables: model.Variable) -> float:
...
def ray_reduced_costs(self, variables: model.Variable) -> float: ...
@overload
def ray_reduced_costs(self, variables: Iterable[model.Variable]) -> List[float]:
...
def ray_reduced_costs(self, variables: Iterable[model.Variable]) -> List[float]: ...
def ray_reduced_costs(self, variables=None):
"""The reduced costs from the first dual ray.
@@ -841,20 +829,17 @@ class SolveResult:
@overload
def constraint_status(
self, linear_constraints: None = ...
) -> Dict[model.LinearConstraint, solution.BasisStatus]:
...
) -> Dict[model.LinearConstraint, solution.BasisStatus]: ...
@overload
def constraint_status(
self, linear_constraints: model.LinearConstraint
) -> solution.BasisStatus:
...
) -> solution.BasisStatus: ...
@overload
def constraint_status(
self, linear_constraints: Iterable[model.LinearConstraint]
) -> List[solution.BasisStatus]:
...
) -> List[solution.BasisStatus]: ...
def constraint_status(self, linear_constraints=None):
"""The constraint basis status associated to the best solution.
@@ -900,18 +885,15 @@ class SolveResult:
@overload
def variable_status(
self, variables: None = ...
) -> Dict[model.Variable, solution.BasisStatus]:
...
) -> Dict[model.Variable, solution.BasisStatus]: ...
@overload
def variable_status(self, variables: model.Variable) -> solution.BasisStatus:
...
def variable_status(self, variables: model.Variable) -> solution.BasisStatus: ...
@overload
def variable_status(
self, variables: Iterable[model.Variable]
) -> List[solution.BasisStatus]:
...
) -> List[solution.BasisStatus]: ...
def variable_status(self, variables=None):
"""The variable basis status associated to the best solution.

View File

@@ -390,12 +390,16 @@ class Solution:
def to_proto(self) -> solution_pb2.SolutionProto:
"""Returns an equivalent proto for a solution."""
return solution_pb2.SolutionProto(
primal_solution=self.primal_solution.to_proto()
if self.primal_solution is not None
else None,
dual_solution=self.dual_solution.to_proto()
if self.dual_solution is not None
else None,
primal_solution=(
self.primal_solution.to_proto()
if self.primal_solution is not None
else None
),
dual_solution=(
self.dual_solution.to_proto()
if self.dual_solution is not None
else None
),
basis=self.basis.to_proto() if self.basis is not None else None,
)