update vrp samples
This commit is contained in:
@@ -15,31 +15,31 @@
|
||||
# limitations under the License.
|
||||
"""Capacitated Vehicle Routing Problem (CVRP).
|
||||
|
||||
This is a sample using the routing library python wrapper to solve a CVRP
|
||||
problem while allowing multiple trips, i.e., vehicles can return to a depot
|
||||
to reset their load ("reload").
|
||||
This is a sample using the routing library python wrapper to solve a CVRP
|
||||
problem while allowing multiple trips, i.e., vehicles can return to a depot
|
||||
to reset their load ("reload").
|
||||
|
||||
A description of the CVRP problem can be found here:
|
||||
http://en.wikipedia.org/wiki/Vehicle_routing_problem.
|
||||
A description of the CVRP problem can be found here:
|
||||
http://en.wikipedia.org/wiki/Vehicle_routing_problem.
|
||||
|
||||
Distances are in meters.
|
||||
Distances are in meters.
|
||||
|
||||
In order to implement multiple trips, new nodes are introduced at the same
|
||||
locations of the original depots. These additional nodes can be dropped
|
||||
from the schedule at 0 cost.
|
||||
In order to implement multiple trips, new nodes are introduced at the same
|
||||
locations of the original depots. These additional nodes can be dropped
|
||||
from the schedule at 0 cost.
|
||||
|
||||
The max_slack parameter associated to the capacity constraints of all nodes
|
||||
can be set to be the maximum of the vehicles' capacities, rather than 0 like
|
||||
in a traditional CVRP. Slack is required since before a solution is found,
|
||||
it is not known how much capacity will be transferred at the new nodes. For
|
||||
all the other (original) nodes, the slack is then re-set to 0.
|
||||
The max_slack parameter associated to the capacity constraints of all nodes
|
||||
can be set to be the maximum of the vehicles' capacities, rather than 0 like
|
||||
in a traditional CVRP. Slack is required since before a solution is found,
|
||||
it is not known how much capacity will be transferred at the new nodes. For
|
||||
all the other (original) nodes, the slack is then re-set to 0.
|
||||
|
||||
The above two considerations are implemented in `add_capacity_constraints()`.
|
||||
The above two considerations are implemented in `add_capacity_constraints()`.
|
||||
|
||||
Last, it is useful to set a large distance between the initial depot and the
|
||||
new nodes introduced, to avoid schedules having spurious transits through
|
||||
those new nodes unless it's necessary to reload. This consideration is taken
|
||||
into account in `create_distance_evaluator()`.
|
||||
Last, it is useful to set a large distance between the initial depot and the
|
||||
new nodes introduced, to avoid schedules having spurious transits through
|
||||
those new nodes unless it's necessary to reload. This consideration is taken
|
||||
into account in `create_distance_evaluator()`.
|
||||
"""
|
||||
|
||||
from functools import partial
|
||||
@@ -336,31 +336,34 @@ def print_solution(
|
||||
continue
|
||||
index = routing.Start(vehicle_id)
|
||||
plan_output = f"Route for vehicle {vehicle_id}:\n"
|
||||
load_value = 0
|
||||
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"Load({assignment.Min(capacity_dimension.CumulVar(index))}) "
|
||||
f"Time({assignment.Min(time_var)},{assignment.Max(time_var)}) ->"
|
||||
)
|
||||
previous_index = index
|
||||
index = assignment.Value(routing.NextVar(index))
|
||||
distance += distance_dimension.GetTransitValue(previous_index, index, vehicle_id)
|
||||
load_var = capacity_dimension.CumulVar(index)
|
||||
# capacity dimension TransitVar is negative at reload stations during replenishment
|
||||
# don't want to consider those values when calculating the total load of the route
|
||||
# hence only considering the positive values
|
||||
load_value += max(0, capacity_dimension.GetTransitValue(previous_index, index, vehicle_id))
|
||||
time_var = time_dimension.CumulVar(index)
|
||||
plan_output += (
|
||||
f" {manager.IndexToNode(index)} "
|
||||
f"Load({assignment.Min(load_var)}) "
|
||||
f"Load({assignment.Min(capacity_dimension.CumulVar(index))}) "
|
||||
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"Load of the route: {load_value}\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_load += load_value
|
||||
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}")
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
# [START program]
|
||||
"""Capacitated Vehicle Routing Problem with Time Windows (CVRPTW).
|
||||
|
||||
This is a sample using the routing library python wrapper to solve a CVRPTW
|
||||
problem.
|
||||
A description of the problem can be found here:
|
||||
http://en.wikipedia.org/wiki/Vehicle_routing_problem.
|
||||
This is a sample using the routing library python wrapper to solve a CVRPTW
|
||||
problem.
|
||||
A description of the problem can be found here:
|
||||
http://en.wikipedia.org/wiki/Vehicle_routing_problem.
|
||||
|
||||
Distances are in meters and time in minutes.
|
||||
Distances are in meters and time in minutes.
|
||||
"""
|
||||
|
||||
# [START import]
|
||||
@@ -329,7 +329,11 @@ def main():
|
||||
vehicle_break = data["breaks"][v]
|
||||
break_intervals[v] = [
|
||||
routing.solver().FixedDurationIntervalVar(
|
||||
15, 100, vehicle_break[0], vehicle_break[1], f"Break for vehicle {v}"
|
||||
15,
|
||||
100,
|
||||
vehicle_break[0],
|
||||
vehicle_break[1],
|
||||
f"Break for vehicle {v}",
|
||||
)
|
||||
]
|
||||
time_dimension.SetBreakIntervalsOfVehicle(
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
# [START program]
|
||||
"""Simple Vehicles Routing Problem (VRP).
|
||||
|
||||
This is a sample using the routing library python wrapper to solve a VRP
|
||||
problem.
|
||||
A description of the problem can be found here:
|
||||
http://en.wikipedia.org/wiki/Vehicle_routing_problem.
|
||||
This is a sample using the routing library python wrapper to solve a VRP
|
||||
problem.
|
||||
A description of the problem can be found here:
|
||||
http://en.wikipedia.org/wiki/Vehicle_routing_problem.
|
||||
|
||||
Distances are in meters.
|
||||
Distances are in meters.
|
||||
"""
|
||||
|
||||
# [START import]
|
||||
|
||||
@@ -13,14 +13,15 @@
|
||||
# limitations under the License.
|
||||
# [START program]
|
||||
"""Vehicles Routing Problem (VRP) with breaks relative to the vehicle start time.
|
||||
Each vehicles start at T:15min, T:30min, T:45min and T:60min respectively.
|
||||
|
||||
Each vehicle must perform a break lasting 5 minutes,
|
||||
starting between 25 and 45 minutes after route start.
|
||||
e.g. vehicle 2 starting a T:45min must start a 5min breaks
|
||||
between [45+25,45+45] i.e. in the range [70, 90].
|
||||
Each vehicles start at T:15min, T:30min, T:45min and T:60min respectively.
|
||||
|
||||
Durations are in minutes.
|
||||
Each vehicle must perform a break lasting 5 minutes,
|
||||
starting between 25 and 45 minutes after route start.
|
||||
e.g. vehicle 2 starting a T:45min must start a 5min breaks
|
||||
between [45+25,45+45] i.e. in the range [70, 90].
|
||||
|
||||
Durations are in minutes.
|
||||
"""
|
||||
|
||||
# [START import]
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
# [START program]
|
||||
"""Simple Vehicles Routing Problem (VRP).
|
||||
|
||||
This is a sample using the routing library python wrapper to solve a VRP
|
||||
problem.
|
||||
A description of the problem can be found here:
|
||||
http://en.wikipedia.org/wiki/Vehicle_routing_problem.
|
||||
This is a sample using the routing library python wrapper to solve a VRP
|
||||
problem.
|
||||
A description of the problem can be found here:
|
||||
http://en.wikipedia.org/wiki/Vehicle_routing_problem.
|
||||
|
||||
Distances are in meters.
|
||||
Distances are in meters.
|
||||
"""
|
||||
|
||||
# [START import]
|
||||
@@ -85,7 +85,6 @@ def print_solution(data, manager, routing, solution):
|
||||
max_route_distance = max(route_distance, max_route_distance)
|
||||
print(f"Maximum of the route distances: {max_route_distance}m")
|
||||
|
||||
|
||||
# [END solution_printer]
|
||||
|
||||
|
||||
|
||||
@@ -87,7 +87,6 @@ def print_solution(data, manager, routing, solution):
|
||||
max_route_distance = max(route_distance, max_route_distance)
|
||||
print(f"Maximum of the route distances: {max_route_distance}m")
|
||||
|
||||
|
||||
# [END solution_printer]
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#!/usr/bin/env python3
|
||||
# [START program]
|
||||
"""Vehicles Routing Problem (VRP) for delivering items from any suppliers.
|
||||
Description:
|
||||
Need to deliver some item X and Y at end nodes (at least 11 X and 13 Y).
|
||||
Several locations provide them and even few provide both.
|
||||
|
||||
Description: Need to deliver some item X and Y at end nodes (at least 11 X and
|
||||
13 Y). Several locations provide them and even few provide both.
|
||||
|
||||
fleet:
|
||||
* vehicles: 2
|
||||
@@ -465,8 +465,11 @@ 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 = pywraprouting.RoutingIndexManager(
|
||||
len(data["distance_matrix"]),
|
||||
data["num_vehicles"],
|
||||
data["starts"],
|
||||
data["ends"],
|
||||
)
|
||||
# [END index_manager]
|
||||
|
||||
|
||||
@@ -75,7 +75,6 @@ def create_data_model():
|
||||
data["depot"] = 0
|
||||
return data
|
||||
|
||||
|
||||
# [END data_model]
|
||||
|
||||
|
||||
@@ -121,7 +120,6 @@ def print_solution(data, manager, routing, solution):
|
||||
max_route_distance = max(route_distance, max_route_distance)
|
||||
print(f"Maximum of the route distances: {max_route_distance}m")
|
||||
|
||||
|
||||
# [END solution_printer]
|
||||
|
||||
|
||||
|
||||
@@ -18,11 +18,15 @@ This script generate few markdown tables to better understand
|
||||
the relation between nodes and indices.
|
||||
|
||||
Things to notice:
|
||||
* Since we have two duplicates (node 5 and node 4) solver need 2 extra indices to have an unique index for each vehicle start/stop and locations.
|
||||
* Solver needs to "create" an index for a vehicle 1 start since solver need an unique start index per vehicle.
|
||||
* Since we have two duplicates (node 5 and node 4) solver need 2 extra indices
|
||||
to have an unique index for each vehicle start/stop and locations.
|
||||
* Solver needs to "create" an index for a vehicle 1 start since solver need an
|
||||
unique start index per vehicle.
|
||||
* All end nodes are moved to the end of the index list aka [15, 16, 17, 18].
|
||||
* routing.Size() return the number of node which are not end nodes (here 15 aka [0-14])
|
||||
note: using the two properties above, we know that any index in range(routing.Size()) is not a vehicle end node.
|
||||
* routing.Size() return the number of node which are not end nodes (here 15 aka
|
||||
[0-14])
|
||||
note: using the two properties above, we know that any index in
|
||||
range(routing.Size()) is not a vehicle end node.
|
||||
|
||||
* Since end nodes are moved to the end, their respective "empty" node index are
|
||||
reused so all locations indices are "shifted"
|
||||
@@ -31,9 +35,11 @@ e.g. node 9 is mapped to index 6
|
||||
e.g. start node 7 mapped to index 4
|
||||
|
||||
Takeaway:
|
||||
* Allways use routing.Start(), routing.End(), manager.IndexToNode() or manager.NodeToIndex().
|
||||
* Allways use routing.Start(), routing.End(), manager.IndexToNode() or
|
||||
manager.NodeToIndex().
|
||||
* Location node is not necessarily equal to its index.
|
||||
* To loop through ALL indices use manager.GetNumberOfIndices() (Python) or manager::num_indices() (C++)
|
||||
* To loop through ALL indices use manager.GetNumberOfIndices() (Python) or
|
||||
manager::num_indices() (C++)
|
||||
"""
|
||||
|
||||
from ortools.constraint_solver import routing_enums_pb2
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
# [START program]
|
||||
"""Simple Vehicles Routing Problem (VRP).
|
||||
|
||||
This is a sample using the routing library python wrapper to solve a VRP
|
||||
problem.
|
||||
This is a sample using the routing library python wrapper to solve a VRP
|
||||
problem.
|
||||
|
||||
The solver stop after improving its solution 15 times or after 5 seconds.
|
||||
The solver stop after improving its solution 15 times or after 5 seconds.
|
||||
|
||||
Distances are in meters.
|
||||
Distances are in meters.
|
||||
"""
|
||||
|
||||
# [START import]
|
||||
@@ -90,7 +90,6 @@ def print_solution(
|
||||
total_distance += route_distance
|
||||
print(f"Total Distance of all routes: {total_distance}m")
|
||||
|
||||
|
||||
# [END solution_callback_printer]
|
||||
|
||||
|
||||
@@ -112,7 +111,9 @@ class SolutionCallback:
|
||||
self.objectives = []
|
||||
|
||||
def __call__(self):
|
||||
objective = int(self._routing_model_ref().CostVar().Value())
|
||||
objective = int(
|
||||
self._routing_model_ref().CostVar().Value()
|
||||
) # pytype: disable=attribute-error
|
||||
if not self.objectives or objective < self.objectives[-1]:
|
||||
self.objectives.append(objective)
|
||||
print_solution(self._routing_manager_ref(), self._routing_model_ref())
|
||||
|
||||
@@ -14,19 +14,19 @@
|
||||
# [START program]
|
||||
"""Vehicles Routing Problem (VRP) with Time Window (TW) per vehicle.
|
||||
|
||||
All time are in minutes using 0am as origin
|
||||
e.g. 8am = 480, 11am = 660, 1pm = 780 ...
|
||||
All time are in minutes using 0am as origin
|
||||
e.g. 8am = 480, 11am = 660, 1pm = 780 ...
|
||||
|
||||
We have 1 depot (0) and 16 locations (1-16).
|
||||
We have a fleet of 4 vehicles (0-3) whose working time is [480, 1020] (8am-5pm)
|
||||
We have the distance matrix between these locations and depot.
|
||||
We have a service time of 25min at each location.
|
||||
We have 1 depot (0) and 16 locations (1-16).
|
||||
We have a fleet of 4 vehicles (0-3) whose working time is [480, 1020] (8am-5pm)
|
||||
We have the distance matrix between these locations and depot.
|
||||
We have a service time of 25min at each location.
|
||||
|
||||
Locations are duplicated so we can simulate a TW per vehicle.
|
||||
location: [01-16] vehicle: 0 TW: [540, 660] (9am-11am)
|
||||
location: [17-32] vehicle: 1 TW: [660, 780] (11am-1pm)
|
||||
location: [33-48] vehicle: 2 TW: [780, 900] (1pm-3pm)
|
||||
location: [49-64] vehicle: 3 TW: [900, 1020] (3pm-5pm)
|
||||
Locations are duplicated so we can simulate a TW per vehicle.
|
||||
location: [01-16] vehicle: 0 TW: [540, 660] (9am-11am)
|
||||
location: [17-32] vehicle: 1 TW: [660, 780] (11am-1pm)
|
||||
location: [33-48] vehicle: 2 TW: [780, 900] (1pm-3pm)
|
||||
location: [49-64] vehicle: 3 TW: [900, 1020] (3pm-5pm)
|
||||
"""
|
||||
|
||||
# [START import]
|
||||
@@ -88,6 +88,8 @@ def print_solution(manager, routing, assignment):
|
||||
time_dimension = routing.GetDimensionOrDie("Time")
|
||||
total_time = 0
|
||||
for vehicle_id in range(manager.GetNumberOfVehicles()):
|
||||
if not routing.IsVehicleUsed(assignment, vehicle_id):
|
||||
continue
|
||||
plan_output = f"Route for vehicle {vehicle_id}:\n"
|
||||
index = routing.Start(vehicle_id)
|
||||
start_time = 0
|
||||
|
||||
@@ -69,7 +69,6 @@ def create_data_model():
|
||||
data["depot"] = 0
|
||||
return data
|
||||
|
||||
|
||||
# [END data_model]
|
||||
|
||||
|
||||
@@ -110,7 +109,6 @@ def print_solution(routes, cumul_data):
|
||||
route_str += f"Total time: {total_time}min"
|
||||
print(route_str)
|
||||
|
||||
|
||||
# [END solution_printer]
|
||||
|
||||
|
||||
@@ -129,7 +127,6 @@ def get_routes(solution, routing, manager):
|
||||
routes.append(route)
|
||||
return routes
|
||||
|
||||
|
||||
# [END get_routes]
|
||||
|
||||
|
||||
@@ -154,7 +151,6 @@ def get_cumul_data(solution, routing, dimension):
|
||||
cumul_data.append(route_data)
|
||||
return cumul_data
|
||||
|
||||
|
||||
# [END get_cumulative_data]
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user