python: run black on all files
This commit is contained in:
committed by
Corentin Le Molgat
parent
8c51109f2d
commit
be220948a3
@@ -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)
|
||||
|
||||
1
examples/python/testdata/BUILD.bazel
vendored
1
examples/python/testdata/BUILD.bazel
vendored
@@ -18,4 +18,3 @@ exports_files(
|
||||
"salbp_20_1.alb",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -208,6 +208,7 @@ class ProductCst(LinearExpr):
|
||||
|
||||
|
||||
class Constant(LinearExpr):
|
||||
|
||||
def __init__(self, val):
|
||||
self.__val = val
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user