I am using OR tools to solve VRP without any constraints. Here is the source code:
def create_data_model():
"""Stores the data for the problem."""
data = {}
data['distance_matrix'] = [
[0, 20079, 2613, 8005, 19277, 12468, 13701],
[0, 0, 21285, 16012, 32574, 35394, 28806],
[0, 18233, 0, 5392, 19965, 19650, 13064],
[0, 15013, 5639, 0, 22883, 22570, 15982],
[0, 32991, 19256, 21815, 0, 18414, 9112],
[0, 34348, 16976, 23122, 15678, 0, 14647],
[0, 27652, 13917, 16476, 8043, 14820, 0]
]
data['time_matrix'] = [
[0, 1955, 508, 1331, 1474, 1427, 1292],
[0, 0, 1795, 1608, 2057, 2410, 2036],
[0, 1485, 0, 823, 1370, 1541, 1100],
[0, 1402, 924, 0, 1533, 1637, 1263],
[0, 2308, 1663, 1853, 0, 1766, 1104],
[0, 2231, 1373, 1660, 1441, 0, 1554],
[0, 1998, 1353, 1543, 764, 1550, 0]
]
data['num_vehicles'] = 6
data['depot'] = 0
return data
def print_solution(data, manager, routing, solution):
"""Prints solution on console."""
max_route_distance = 0
for vehicle_id in range(data['num_vehicles']):
index = routing.Start(vehicle_id)
plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
route_distance = 0
while not routing.IsEnd(index):
plan_output += ' {} -> '.format(manager.IndexToNode(index))
previous_index = index
index = solution.Value(routing.NextVar(index))
route_distance += routing.GetArcCostForVehicle(
previous_index, index, vehicle_id)
plan_output += '{}\n'.format(manager.IndexToNode(index))
plan_output += 'Distance of the route: {}m\n'.format(route_distance)
print(plan_output)
max_route_distance = max(route_distance, max_route_distance)
print('Maximum of the route distances: {}m'.format(max_route_distance))
def test(request):
# Instantiate the data problem.
data = create_data_model()
# Create the routing index manager.
manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']),
data['num_vehicles'], data['depot'])
# Create Routing Model.
routing = pywrapcp.RoutingModel(manager)
def distance_callback(from_index, to_index):
"""Returns the distance between the two nodes."""
# 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]
transit_callback_index = routing.RegisterTransitCallback(distance_callback)
routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
dimension_name = 'Distance'
routing.AddDimension(
transit_callback_index,
0, # no slack
1000000000, # vehicle maximum travel distance
True, # start cumul to zero
dimension_name)
distance_dimension = routing.GetDimensionOrDie(dimension_name)
distance_dimension.SetGlobalSpanCostCoefficient(35394)
# Setting first solution heuristic.
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
search_parameters.first_solution_strategy = (
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)
# Solve the problem.
solution = routing.SolveWithParameters(search_parameters)
# Print solution on console.
if solution:
print_solution(data, manager, routing, solution)
return HttpResponse('')
Everything above was copy-pasted from google's example, except for the following:
My own distance matrix & num of vehicles
very large vehicle maximum travel distance in AddDimension function
SetGlobalSpanCostCoefficient(35394)
I followed OR-Tools solve traveling salesman (TSP) without returning to the home node to set distance from all nodes to 0 (the depot) to 0.
The result for the above code is shown below:
Route for vehicle 0:
0 -> 1 -> 0
Distance of the route: 20079m
Route for vehicle 1:
0 -> 5 -> 0
Distance of the route: 12468m
Route for vehicle 2:
0 -> 4 -> 0
Distance of the route: 19277m
Route for vehicle 3:
0 -> 2 -> 3 -> 0
Distance of the route: 8005m
Route for vehicle 4:
0 -> 6 -> 0
Distance of the route: 13701m
Route for vehicle 5:
0 -> 0
Distance of the route: 0m
Maximum of the route distances: 20079m
To verify the above output, I marked the points in the google map. The numbering order is same as the order of the distance matrix.
Coords on map
The depot(starting point) is in Watthana, which can be seen near marker B.
Clearly, from Watthana, the cheapest path should be 2-1-3 in a single trip. But Google OR returns it as two trips (as seen in routes for vehicles 0 and 3). This can also be verified by manually adding the distances.
Dist from home to 2 to 1 to 3 = 2613+5392+15013 = 23018m
Dist of vehicles 0 and 3 = 20079+8005 = 28084m
What am I doing wrong? How can I get google to not separate out the point 1? Also please note that ideally points E,F,D could have also been grouped but they were not.
Thanks in advance!
From the question, I think what you want is to reduce the cumulative distance traveled by all vehicles.
distance_dimension.SetGlobalSpanCostCoefficient(35394)
Conversely, This code makes sure that the distance traveled by each vehicle is minimized by adding a Span Cost to the objective function weighted by 35394.
global_span_cost =
coefficient * (Max(dimension end value) - Min(dimension start value))
In your case this is not a very high priority, hence the solution would be to comment that line or reduce the coefficient to a small value like 1 or 2, to reduce the sole importance of it.
Read more about GSpanCoeff
Now the solution should be
Route for vehicle 0:
0 -> 2 -> 3 -> 1 -> 0
Distance of the route: 23018m
Route for vehicle 1:
0 -> 6 -> 4 -> 0
Distance of the route: 21744m
Route for vehicle 2:
0 -> 5 -> 0
Distance of the route: 12468m
Maximum of the route distances: 23018m
Sum of the route distances: 57230m
Related
I am trying to solve LeetCode problem 1129. Shortest Path with Alternating Colors:
You are given an integer n, the number of nodes in a directed graph where the nodes are labeled from 0 to n - 1. Each edge is red or blue in this graph, and there could be self-edges and parallel edges.
You are given two arrays redEdges and blueEdges where:
redEdges[i] = [aᵢ, bᵢ] indicates that there is a directed red edge from node aᵢ to node bᵢ in the graph, and
blueEdges[j] = [uⱼ, vⱼ] indicates that there is a directed blue edge from node uⱼ to node vⱼ in the graph.
Return an array answer of length n, where each answer[x] is the length of the shortest path from node 0 to node x such that the edge colors alternate along the path, or -1 if such a path does not exist.
Example 1:
Input: n = 3, redEdges = [[0,1],[1,2]], blueEdges = []
Output: [0,1,-1]
Here is my code for that problem:
class Solution:
def shortestAlternatingPaths(self, n: int, redEdges: List[List[int]], blueEdges: List[List[int]]) -> List[int]:
res = [0] + [-1]*(n-1)
Red = defaultdict(list)
Blue = defaultdict(list)
for x,y in redEdges:
if x!=0 or y!=0:
Red[x].append(y)
for x,y in blueEdges:
if x!=0 or y!=0:
Blue[x].append(y)
def dfs(vertex,color,cost):
if color == "red":
for x in Red[vertex]:
if res[x] != -1:
res[x] = min(cost,res[x])
else:
res[x] = cost
if vertex in Red.keys():
del Red[vertex]
dfs(x,"blue",cost+1)
else:
for x in Blue[vertex]:
if res[x] != -1:
res[x] = min(cost,res[x])
else:
res[x] = cost
if vertex in Blue.keys():
del Blue[vertex]
dfs(x,"red",cost+1)
dfs(0,"red",1)
dfs(0,"blue",1)
return res
But for the following input:
redEdges=[[2,2],[0,1],[0,3],[0,0],[0,4],[2,1],[2,0],[1,4],[3,4]]
blueEdges=[[1,3],[0,0],[0,3],[4,2],[1,0]]
...my output is:
[0,1,4,1,1]
But the correct solution is:
[0,1,2,1,1]
...because there is a path from node 0 to node 2 like this:
red blue
0 -----> 4 -----> 2
I have no idea why my code doesn't give 2 as this path should be found via my DFS algorithm.
I thought that it might be something with the [0,0] edge, but it seems that it doesn't have an impact on a solution.
What is wrong in my code?
The problem is that your code deletes the vertex it has visited, but doesn't restore it when backtracking. There is a possibility that there is another path from vertex 0 to the one you just deleted that still needs to be traversed and is a shorter path.
Here is example input that demonstrates the problem:
redEdges = [[0,1],[2,3],[0,3]]
blueEdges = [[1,2],[3,4]]
Your code will correctly create the following adjacency lists:
Red = {0: [1, 3], 2: [3]}
Blue = {1: [2], 3: [4]}
With dfs the path 0, 1, 2, 3, 4 will be visited and during this traversal all the keys in these dictionaries will be deleted. As the loop over the outgoing edges from 0 is still active, dfs will still follow the red edge from 0 to 3, but there it finds no blue edges as the key 3 is not there anymore. And so the algorithm doesn't see the shorter path from 0 to 4, which is 0, 3, 4.
Not your question, but BFS is more suitable for finding shortest paths. I would suggest you rework the algorithm and use BFS instead.
Just to have a complete answer, here is a spoiler solution using BFS:
class Solution:
def shortestAlternatingPaths(self, n, redEdges, blueEdges):
# Build a separate adjacency list for each color
adj = [[[] for _ in range(n)], [[] for _ in range(n)]]
for i, edges in enumerate((redEdges, blueEdges)):
for x, y in edges:
if x or y:
adj[i][x].append(y)
# Collect shortest distances for each color separately
res = [[0] + [-1] * (n-1), [0] + [-1] * (n-1)]
# Start BFS at node 0, traversing with either color
frontier = [(0, 0), (0, 1)]
distance = 1
while frontier: # BFS loop
nextfrontier = []
for node, color in frontier:
for neighbor in adj[color][node]:
# If not yet visited with this color...
if res[color][neighbor] == -1:
res[color][neighbor] = distance
nextfrontier.append((neighbor, 1-color))
frontier = nextfrontier
distance += 1
# Get the minimum distance per node from the two color alternatives
return [min(a, b) if min(a, b) > -1 else max(a, b)
for a, b in zip(*res)]
I'm having a go at the Google Foobar Challenge, currently on the Prepare the Bunnies' Escape problem.
Problem definition:
You're awfully close to destroying the LAMBCHOP doomsday device and
freeing Commander Lambda's bunny workers, but once they're free of the
work duties the bunnies are going to need to escape Lambda's space
station via the escape pods as quickly as possible. Unfortunately, the
halls of the space station are a maze of corridors and dead ends that
will be a deathtrap for the escaping bunnies. Fortunately, Commander
Lambda has put you in charge of a remodeling project that will give
you the opportunity to make things a little easier for the bunnies.
Unfortunately (again), you can't just remove all obstacles between the
bunnies and the escape pods - at most you can remove one wall per
escape pod path, both to maintain structural integrity of the station
and to avoid arousing Commander Lambda's suspicions.
You have maps of parts of the space station, each starting at a work
area exit and ending at the door to an escape pod. The map is
represented as a matrix of 0s and 1s, where 0s are passable space and
1s are impassable walls. The door out of the station is at the top
left (0,0) and the door into an escape pod is at the bottom right
(w-1,h-1).
Write a function solution(map) that generates the length of the
shortest path from the station door to the escape pod, where you are
allowed to remove one wall as part of your remodeling plans. The path
length is the total number of nodes you pass through, counting both
the entrance and exit nodes. The starting and ending positions are
always passable (0). The map will always be solvable, though you may
or may not need to remove a wall. The height and width of the map can
be from 2 to 20. Moves can only be made in cardinal directions; no
diagonal moves are allowed.
I thought I would have a go at solving the problem using Dijkstra's algorithm and managed to get all the test cases passing except for one hidden one.
Fortunately I have been able to come up with a test case that can replicate the situation.
My implementation is below:
#!/usr/bin/env python2.7
from typing import List, Tuple, Dict, Optional
from Queue import PriorityQueue
def neighbors(map, coord):
# type: (List[List[int]], Tuple[int, int]) -> List[Tuple[int, int]]
adjacent = []
if coord[0] > 0:
adjacent.append((coord[0] - 1, coord[1]))
if coord[0] < len(map) - 1:
adjacent.append((coord[0] + 1, coord[1]))
if coord[1] > 0:
adjacent.append((coord[0], coord[1] - 1))
if coord[1] < len(map[coord[0]]) - 1:
adjacent.append((coord[0], coord[1] + 1))
return adjacent
def heuristic(a, b):
# type: (Tuple[int, int], Tuple[int, int]) -> float
# Manhattan Distance heuristic
return abs(b[0] - a[0]) + abs(b[1] - a[1])
def reconstruct_path(came_from, current):
# type: (Tuple[int, int], Tuple[int, int]) -> List[Tuple[int, int]]
# Backtrack from 'current' back up to the start
path = [current]
while current in came_from:
current = came_from[current]
if current is not None:
path.insert(0, current)
return path
def dijkstras_algo(map, start, end, num_removable_walls):
# type: (List[List[int]], Tuple[int, int], Tuple[int, int], int) -> int
open = PriorityQueue()
open.put((0, start))
# num_removable_walls[n] is the remaining number of walls we could remove when we landed on the square
num_removable_walls = { start: num_removable_walls }
# came_from[n] is the node immediately preceding it on the cheapest path from 'start'
came_from = { start: None } # type: Dict[Tuple[int, int], Optional[Tuple[int, int]]]
# cost_so_far[n] is the cost to travel from 'start' to n
cost_so_far = { start: 0 } # type: Dict[Tuple[int, int], int]
while not open.empty():
(cost, current) = open.get()
if current == end:
return reconstruct_path(came_from, current)
for next in neighbors(map, current):
if map[next[0]][next[1]] == 1 and num_removable_walls[current] <= 0:
continue
# For this problem, distance between any two neighbouring cells is _always_ 1
new_cost = cost_so_far[current] + 1
# The problem is in this check, for the failing test case, we stop revisiting cells
if (next not in cost_so_far or new_cost <= cost_so_far[next]):
num_removable_walls[next] = num_removable_walls[current] - (1 if map[next[0]][next[1]] == 1 else 0)
cost_so_far[next] = new_cost
open.put((new_cost + heuristic(next, end), next))
came_from[next] = current
def solution(map):
# type: (List[List[int]]) -> int
num_rows = len(map)
num_cols = len(map[0])
start = (0, 0)
end = (num_rows - 1, len(map[num_rows - 1]) - 1)
path = dijkstras_algo(map, start, end, 1)
return None if path == None else len(path)
assert solution([[0, 1, 0, 0, 0, 1],
[0, 0, 0, 1, 0, 0]]) == 7, '2x6 failed!'
assert solution([[0, 1, 0, 0, 0],
[0, 1, 0, 1, 0],
[0, 0, 0, 1, 1],
[0, 0, 1, 1, 0]]) == 12, '4x5 failed!'
assert solution([[0, 1, 0, 0, 0],
[0, 0, 0, 1, 0],
[0, 0, 1, 1, 0],
[0, 1, 1, 0, 0],
[0, 1, 1, 0, 0]]) == 9, '5x5 failed!'
Running the code above will show the 4x5 array test failing.
I have ascertained that it is because the algorithm is not revisiting cells to continue searching. This is because of the if-statement if (next not in cost_so_far or new_cost <= cost_so_far[next]):. The algorithm initially finds a low-cost route to the wall cells at (0, 1) and (1, 1), and uses up it's one-time ability to remove a wall on those squares immediately. This cost is then recorded and because it is low, the aforementioned check in the algorithm will not revisit the surrounding cells as a result - as it has found the shortest path to that cell. This is a problem because we can see that we need to save our one-time ability to remove a wall until we encounter cell (2, 4) in order to reach the end.
Although I know where the problem lies, I am having a hard time coming up with a way to incorporate exploring the map while taking the wall-removing ability into account. Any ideas would be greatly appreciated.
I successfully implemented Dijkstra's algorithm to explore the grid, not passing through walls as per the vanilla implementation. However I haven't been successful incorporating the possibility of finding the shortest path, given that you can remove one wall from the map/maze.
I am modeling a Time-constrained CVRP. The problem is to minimize the total travel time (not including the package dropping time) subject to vehicle (delivery) capacity and total time spent (per vehicle) constraints. The package dropping time refers to an additional time to be spent at each node, and the total time spent equals to the travel time plus this additional time. I have the below model that works for a single vehicle-type case. I would like to introduce two-vehicle type concept in there, meaning that I have a set of V1 type vehicles and another set of V2 type vehicles. The only difference of the vehicle-types is the per time cost of travel. Let x denote the per time unit cost of travel by V1, and y denote the per time unit travel cost of V2. How can I design the model so that it incorporates this additional aspect?
from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp
def create_data_model(n_vehicles):
"""Stores the data for the problem."""
data = {}
data['time_matrix'] = TT #travel time
data['num_vehicles'] = n_vehicles
data['depot'] = 0
data['demands'] = demands
data['vehicle_capacities'] = vehicle_capacities
data['service_time'] = service_time
return data
def print_solution(data, manager, routing, solution):
"""Prints solution on console."""
print(f'Objective: {solution.ObjectiveValue()}')
max_route_time = 0; tour = {i:[] for i in range(data['num_vehicles'])}
for vehicle_id in range(data['num_vehicles']):
index = routing.Start(vehicle_id)
plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
route_time = 0
while not routing.IsEnd(index):
plan_output += ' {} -> '.format(manager.IndexToNode(index))
tour[vehicle_id].append(manager.IndexToNode(index))
previous_index = index
index = solution.Value(routing.NextVar(index))
if previous_index != 0 and previous_index <= len(data['service_time'])-1:
service_time = data['service_time'][previous_index]
else:
service_time = 0
route_time += (routing.GetArcCostForVehicle(
previous_index, index, vehicle_id) + service_time)
plan_output += '{}\n'.format(manager.IndexToNode(index))
tour[vehicle_id].append(manager.IndexToNode(index))
plan_output += 'Travel time of the route: {} sec\n'.format(route_time)
print(plan_output)
max_route_time = max(route_time, max_route_time)
print('Maximum of the route time: {} sec'.format(max_route_time))
return(tour)
def main(n_vehicles):
number_of_veh = [n_vehicles][0]
solution_found = False
while solution_found == False:
"""Entry point of the program."""
# Instantiate the data problem.
data = create_data_model(number_of_veh)
# Create the routing index manager.
manager = pywrapcp.RoutingIndexManager(len(data['time_matrix']),
data['num_vehicles'], data['depot'])
# Create Routing Model.
routing = pywrapcp.RoutingModel(manager)
# Create and register a transit callback.
def time_callback(from_index, to_index):
"""Returns the time between the two nodes."""
# 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]
transit_callback_index = routing.RegisterTransitCallback(time_callback)
# Define cost of each arc.
routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
# Create and register a transit callback.
def time_callback2(from_index, to_index):
"""Returns the time between the two nodes."""
# Convert from routing variable Index to time matrix NodeIndex.
from_node = manager.IndexToNode(from_index)
to_node = manager.IndexToNode(to_index)
if from_node != 0:
return data['time_matrix'][from_node][to_node] + data['service_time'][from_node]
else:
return data['time_matrix'][from_node][to_node]
transit_callback_index2 = routing.RegisterTransitCallback(time_callback2)
# Add Time constraint.
dimension_name = 'Time'
routing.AddDimension(
transit_callback_index2,
0, # no slack
Operational_hours*3600, # vehicle maximum travel time
True, # start cumul to zero
dimension_name)
time_dimension = routing.GetDimensionOrDie(dimension_name)
def demand_callback(from_index):
"""Returns the demand of the node."""
# Convert from routing variable Index to demands NodeIndex.
from_node = manager.IndexToNode(from_index)
return data['demands'][from_node]
demand_callback_index = routing.RegisterUnaryTransitCallback(
demand_callback)
routing.AddDimensionWithVehicleCapacity(
demand_callback_index,
0, # null capacity slack
data['vehicle_capacities'], # vehicle maximum capacities
True, # start cumul to zero
'Capacity')
# Setting first solution heuristic.
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
search_parameters.first_solution_strategy = (
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)
search_parameters.local_search_metaheuristic = (
routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)
search_parameters.time_limit.FromSeconds(VRP_time_limit)
# Solve the problem.
solution = routing.SolveWithParameters(search_parameters)
# Print solution on console.
if solution:
tour = print_solution(data, manager, routing, solution)
solution_found = True
else:
print('No solution found! Increasing the vehicle numbers by one and resolving.\n')
solution_found = False
number_of_veh += 1
return(tour, number_of_veh)
EDIT
Based on #Mizux' answer, I have written the following, which produced the below error.
from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp
def create_data_model(n_vehicles):
"""Stores the data for the problem."""
data = {}
TT = np.array(df.values)
data['time_matrix'] = TT
data['num_vehicles'] = n_vehicles
data['depot'] = 0
data['demands'] = demands
if len(vehicle_capacities) < n_vehicles:
data['vehicle_capacities'] = [vehicle_capacities[0]]*n_vehicles
else:
data['vehicle_capacities'] = vehicle_capacities
data['service_time'] = service_time
return data
def print_solution(data, manager, routing, solution):
"""Prints solution on console."""
print(f'Objective: {solution.ObjectiveValue()}')
max_route_time = 0; tour = {i:[] for i in range(data['num_vehicles'])}
for vehicle_id in range(data['num_vehicles']):
index = routing.Start(vehicle_id)
plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
route_time = 0
while not routing.IsEnd(index):
plan_output += ' {} -> '.format(manager.IndexToNode(index))
tour[vehicle_id].append(manager.IndexToNode(index))
previous_index = index
index = solution.Value(routing.NextVar(index))
if previous_index != 0 and previous_index <= len(data['service_time'])-1:
service_time = data['service_time'][previous_index]
else:
service_time = 0
route_time += (routing.GetArcCostForVehicle(
previous_index, index, vehicle_id) + service_time)
plan_output += '{}\n'.format(manager.IndexToNode(index))
tour[vehicle_id].append(manager.IndexToNode(index))
plan_output += 'Travel time of the route: {} sec\n'.format(route_time)
print(plan_output)
max_route_time = max(route_time, max_route_time)
print('Maximum of the route time: {} sec'.format(max_route_time))
return(tour)
def main(n_vehicles, cost1, cost2):
number_of_veh = [n_vehicles][0]
solution_found = False
while solution_found == False:
Num_of_Class6 = int(n_vehicles*Percent_of_Class6)
Num_of_Hybrid = n_vehicles - Num_of_Class6
V = list(range(n_vehicles))
V2 = list(set(np.random.choice(V, size=Num_of_Class6, replace=False)))
V1 = list(set(V)-set(V2))
"""Entry point of the program."""
# Instantiate the data problem.
data = create_data_model(number_of_veh)
# Create the routing index manager.
manager = pywrapcp.RoutingIndexManager(len(data['time_matrix']),
data['num_vehicles'], data['depot'])
# Create Routing Model.
routing = pywrapcp.RoutingModel(manager)
'''Major Diff Starts Here'''
# Create and register a transit callback.
def time_callback(from_index, to_index, cost):
"""Returns the time between the two nodes."""
# 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]*cost
range_extender_callback = partial(time_callback, cost=cost1)
class6_callback = partial(time_callback, cost=cost2)
transit_callback_index_V1 = routing.RegisterTransitCallback(range_extender_callback)
transit_callback_index_V2 = routing.RegisterTransitCallback(class6_callback)
'''Major Diff Ends Here'''
# Define cost of each arc.
for v in V1:
routing.SetArcCostEvaluatorOfVehicle(transit_callback_index_V1, v)
for v in V2:
routing.SetArcCostEvaluatorOfVehicle(transit_callback_index_V2, v)
# Create and register a transit callback.
def time_callback2(from_index, to_index):
"""Returns the time between the two nodes."""
# Convert from routing variable Index to time matrix NodeIndex.
from_node = manager.IndexToNode(from_index)
to_node = manager.IndexToNode(to_index)
if from_node != 0:
return data['time_matrix'][from_node][to_node] + data['service_time'][from_node]
else:
return data['time_matrix'][from_node][to_node]
transit_callback_index2 = routing.RegisterTransitCallback(time_callback2)
# Add Time constraint.
dimension_name = 'Time'
routing.AddDimension(
transit_callback_index2,
0, # no slack
Operational_hours*3600, # vehicle maximum travel time
True, # start cumul to zero
dimension_name)
time_dimension = routing.GetDimensionOrDie(dimension_name)
def demand_callback(from_index):
"""Returns the demand of the node."""
# Convert from routing variable Index to demands NodeIndex.
from_node = manager.IndexToNode(from_index)
return data['demands'][from_node]
demand_callback_index = routing.RegisterUnaryTransitCallback(
demand_callback)
routing.AddDimensionWithVehicleCapacity(
demand_callback_index,
0, # null capacity slack
data['vehicle_capacities'], # vehicle maximum capacities
True, # start cumul to zero
'Capacity')
# Setting first solution heuristic.
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
search_parameters.first_solution_strategy = (
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)
search_parameters.local_search_metaheuristic = (
routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)
search_parameters.time_limit.FromSeconds(VRP_time_limit)
# Solve the problem.
solution = routing.SolveWithParameters(search_parameters)
# Print solution on console.
if solution:
tour = print_solution(data, manager, routing, solution)
solution_found = True
else:
print('No solution found! Increasing the vehicle numbers by one and resolving.\n')
solution_found = False
number_of_veh += 1
return(tour, number_of_veh, V1, V2)
main(n_vehicles, cost1, cost2)
The output is:
Beginning the Googe OR-tools to solve the problem.
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-15-0447402a4e3d> in <module>
166 return(tour, number_of_veh, V1, V2)
167
--> 168 final_tour,number_of_veh, V1, V2 = main(n_vehicles, cost1, cost2)
<ipython-input-15-0447402a4e3d> in main(n_vehicles, cost1, cost2)
104 routing.SetArcCostEvaluatorOfVehicle(transit_callback_index_V1, v)
105 for v in V2:
--> 106 routing.SetArcCostEvaluatorOfVehicle(transit_callback_index_V2, v)
107
108 # Create and register a transit callback.
~/.local/lib/python3.7/site-packages/ortools/constraint_solver/pywrapcp.py in SetArcCostEvaluatorOfVehicle(self, evaluator_index, vehicle)
5224 def SetArcCostEvaluatorOfVehicle(self, evaluator_index: "int", vehicle: "int") -> "void":
5225 r""" Sets the cost function for a given vehicle route."""
-> 5226 return _pywrapcp.RoutingModel_SetArcCostEvaluatorOfVehicle(self, evaluator_index, vehicle)
5227
5228 def SetFixedCostOfAllVehicles(self, cost: "int64_t") -> "void":
TypeError: in method 'RoutingModel_SetArcCostEvaluatorOfVehicle', argument 3 of type 'int'
To solve the problem, I followed Mizux' answer. I have the below MWE for future consideration. I hope it helps to someone!
from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp
import numpy as np
from functools import partial
n_vehicles = 4 #Number of vehicles
max_vehicle_tt = 3600 #Maximum travel time for each vehicle (excludes service times)
data = {}
data['time_matrix'] = np.array([[ 0, 1187, 1200, 1110, 1134, 892, 1526, 903, 1482, 1544],
[1232, 0, 13, 90, 67, 426, 537, 419, 493, 555],
[1218, 57, 0, 82, 73, 412, 523, 405, 479, 541],
[1177, 90, 90, 0, 23, 370, 481, 364, 438, 500],
[1187, 80, 67, 23, 0, 380, 491, 374, 448, 509],
[ 870, 390, 403, 314, 337, 0, 729, 17, 686, 747],
[1539, 557, 543, 485, 495, 733, 0, 726, 53, 68],
[ 882, 384, 397, 307, 331, 17, 723, 0, 679, 741],
[1496, 514, 500, 442, 451, 689, 53, 683, 0, 122],
[1584, 602, 588, 530, 539, 777, 68, 771, 122, 0]])
data['num_vehicles'] = n_vehicles
data['depot'] = 0
data['demands'] = [0, 4, 4, 3, 1, 4, 12, 1, 24, 20]
data['vehicle_capacities'] = [30]*data['num_vehicles']
data['service_time'] = [0, 18, 18, 27, 25, 11, 92, 6, 239, 143]
def print_solution(data, manager, routing, solution):
"""Prints solution on console."""
print(f'Objective: {solution.ObjectiveValue()}')
max_route_time = 0; tour = {i:[] for i in range(data['num_vehicles'])}
for vehicle_id in range(data['num_vehicles']):
index = routing.Start(vehicle_id)
plan_output = 'Route for vehicle {}:\n'.format(vehicle_id)
route_time = 0
while not routing.IsEnd(index):
plan_output += ' {} -> '.format(manager.IndexToNode(index))
tour[vehicle_id].append(manager.IndexToNode(index))
previous_index = index
index = solution.Value(routing.NextVar(index))
if previous_index != 0 and previous_index <= len(data['service_time'])-1:
service_time = data['service_time'][previous_index]
else:
service_time = 0
route_time += (routing.GetArcCostForVehicle(
previous_index, index, vehicle_id) + service_time)
plan_output += '{}\n'.format(manager.IndexToNode(index))
tour[vehicle_id].append(manager.IndexToNode(index))
plan_output += 'Travel time of the route: {} sec\n'.format(route_time)
print(plan_output)
max_route_time = max(route_time, max_route_time)
print('Maximum of the route time: {} sec'.format(max_route_time))
return(tour)
def main(n_vehicles, cost1, cost2):
np.random.seed(0)
Num_of_Class6 = int(n_vehicles*0.6)
Num_of_Hybrid = n_vehicles - Num_of_Class6
V = list(range(n_vehicles))
V2 = list(set(np.random.choice(V, size=Num_of_Class6, replace=False)))
V1 = list(set(V)-set(V2))
print('V1:%s'%V1); print('V2:%s'%V2)
# Create the routing index manager.
manager = pywrapcp.RoutingIndexManager(len(data['time_matrix']),
data['num_vehicles'], data['depot'])
# Create Routing Model.
routing = pywrapcp.RoutingModel(manager)
# Create and register a transit callback.
def time_callback(from_index, to_index, cost):
"""Returns the time between the two nodes."""
# 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]*cost
range_extender_callback = partial(time_callback, cost=cost1)
class6_callback = partial(time_callback, cost=cost2)
transit_callback_index_V1 = routing.RegisterTransitCallback(range_extender_callback)
transit_callback_index_V2 = routing.RegisterTransitCallback(class6_callback)
# Define cost of each arc.
for v in V1:
routing.SetArcCostEvaluatorOfVehicle(transit_callback_index_V1, int(v))
for v in V2:
routing.SetArcCostEvaluatorOfVehicle(transit_callback_index_V2, int(v))
# Create and register a transit callback to limit the total travel+service time
def time_callback2(from_index, to_index):
"""Returns the time between the two nodes."""
# Convert from routing variable Index to time matrix NodeIndex.
from_node = manager.IndexToNode(from_index)
to_node = manager.IndexToNode(to_index)
if from_node != 0:
return data['time_matrix'][from_node][to_node] + data['service_time'][from_node]
else:
return data['time_matrix'][from_node][to_node]
transit_callback_index2 = routing.RegisterTransitCallback(time_callback2)
# Add Time constraint.
dimension_name = 'Time'
routing.AddDimensionWithVehicleCapacity(
transit_callback_index2,
0, # no slack
[max_vehicle_tt]*data['num_vehicles'], # vehicle maximum travel time
True, # start cumul to zero
dimension_name)
time_dimension = routing.GetDimensionOrDie(dimension_name)
def demand_callback(from_index):
"""Returns the demand of the node."""
# Convert from routing variable Index to demands NodeIndex.
from_node = manager.IndexToNode(from_index)
return data['demands'][from_node]
demand_callback_index = routing.RegisterUnaryTransitCallback(
demand_callback)
routing.AddDimensionWithVehicleCapacity(
demand_callback_index,
0, # null capacity slack
data['vehicle_capacities'], # vehicle maximum capacities
True, # start cumul to zero
'Capacity')
# Setting first solution heuristic.
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
search_parameters.first_solution_strategy = (
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)
search_parameters.local_search_metaheuristic = (
routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)
search_parameters.time_limit.FromSeconds(1)
# Solve the problem.
solution = routing.SolveWithParameters(search_parameters)
# Print solution on console.
if solution:
tour = print_solution(data, manager, routing, solution)
return(tour, V1, V2)
else:
print('No solution found!\n')
tour,V1,V2 = main(n_vehicles,0.5,0.7)
Bonus: Use the below function to check critical solution metrics.
pairs = {}; serv_time = {}; tt ={}; cont_to_obj = {}
for i,j in tour.items():
if len(j) > 2:
serv_time[i] = sum([data['service_time'][k] for k in j])
print('Service time for vehicle %s: %s.'%(i,serv_time[i]))
num_deliveries = sum([data['demands'][k] for k in j])
print('Number of deliveries for vehicle %s: %s.'%(i,num_deliveries))
pairs[i] = list(zip(j,j[1:]))
tt[i] = sum([data['time_matrix'][k] for k in pairs[i]])
print('Travel time for vehicle %s: %s.'%(i,tt))
print('Total time for vehicle %s: %s'%(i,serv_time[i]+tt[i]))
if i in V1:
cont_to_obj[i] = sum([int(data['time_matrix'][k]*0.002244) for k in pairs[i]])
else:
cont_to_obj[i] = sum([int(data['time_matrix'][k]*0.0080517) for k in pairs[i]])
print('Contribution to the obj. fun. for vehicle %s: %s\n'%(i,cont_to_obj[i]))
Simply register two transits callbacks (i.e. one per vehicle type)
Then use the overload of AddDimension() to pass an array of registered transit callback index.
e.G. Mizux/vrp_multiple_transit.py
My denomination "5" is not showing up and how do you reverse the dict easily. i dont want extra 5 line of code.lol
Can you guys help me out with it?
the code is working so far.
here is my code with a test case
def change(target, coins):
result = dict()
i= len(coins) -1
while i> 0:
coin = coins[i]
numOfThisCoin= target // coin
result[coin] = numOfThisCoin
target -= coin * numOfThisCoin
i-= 1
return result
print(change(185, (5, 10, 25, 100, 200)))
i am getting output
{200: 0, 100: 1, 25: 3, 10: 1}
but want it like
{5: 0, 10: 1, 25: 3, 100: 1, 200: 0}
Here is the corrected code for your problem:
from collections import OrderedDict
def change(target, coins):
result = dict()
i= len(coins) -1
while i>= 0:
coin = coins[i]
numOfThisCoin= target // coin
result[coin] = numOfThisCoin
target -= coin * numOfThisCoin
i-= 1
res = OrderedDict(sorted(result.items()))
return res
print(change(185, (5, 10, 25, 100, 200)))
If you (not only 25 you can access any coin denomination)
print(res[25])
Output will be
3
in this case.
dict- it does not keep the elements in order. You have to use OrderedDict for sorting the elements in the order you want. For more info follow the below link:
http://docs.python.org/library/collections.html#collections.OrderedDict
def change(target, coins):
result = dict()
i= len(coins) -1
while i>= 0:
coin = coins[i]
numOfThisCoin= target // coin
result[coin] = numOfThisCoin
target -= coin * numOfThisCoin
i-= 1
return dict(sorted(result.items()))
print(change(185, (5, 10, 25, 100, 200)))
My task was to convert the RGB image into LuvImage.
Perform linear stretching in this domain. And than convert it back in the RGB domain.
Original Image:
[[ 0 0 0]
[255 0 0]
[100 100 100]
[ 0 100 100]]
Luv image after linear stretching in Luv Domain
[[0 , 0, 0],
[100 , 175, 37.7],
[79.64, 0, 0],
[71.2 ,-29.29,-6.339]]
Now, I am converting it into XYZ image. The answer is,
[[0,0, 0],
[1.5, 1, 0.53],
[0.533, 0.56, 0.61],
[0.344, 0.425, 0.523]]
Now, after that I am converting it into linear sRGB image
by multiplying image with matrix:
[[3.240479, -1.53715, -0.498535],
[-0.969256, 1.875991, 0.041556],
[0.055648, -0.204043, 1.057311]]
The answer for this conversion - linear sRGB image,
[[0. 0. 0. ],
[3.07132001 0.44046801 0.44082034],
[0.55904669 0.55972465 0.55993322],
[0.20106868 0.4850426 0.48520307]]
The problem here is that for the 2nd pixel sRGB values are not in the range of [0,1]. For all other pixels I am getting the correct value.
def XYZToLinearRGB(self, XYZImage):
'''
to find linearsRGBImage, we multiply XYZImage with static array
[[3.240479, -1.53715, -0.498535],
[-0.969256, 1.875991, 0.041556],
[0.055648, -0.204043, 1.057311]]
'''
rows, cols, bands = XYZImage.shape # bands == 3
linearsRGBImage = np.zeros([rows, cols, bands], dtype=float)
multiplierMatrix = np.array([[3.240479, -1.53715, -0.498535],
[-0.969256, 1.875991, 0.041556],
[0.055648, -0.204043, 1.057311]])
for i in range(0, rows):
for j in range(0, cols):
X,Y,Z = XYZImage[i,j]
linearsRGBImage[i,j] = np.matmul(multiplierMatrix, np.array([X,Y,Z]))
#for j -ends
#for i -ends
return linearsRGBImage
The code for this conversion is as per above. Can someone point out what I am doing wrong for 2nd pixel, and how to fix it?
Well, one simple solution I found after research is just clip the values.
So, if the value is out of the range, say if r<0 than we will assign r as 0.
Same for the larger values. If r>1 (in my case 3.07) than we will assign r as 1.
So the latest version of my code:
def XYZToLinearRGB(self, XYZImage):
'''
to find linearsRGBImage, we multiply XYZImage with static array
[[3.240479, -1.53715, -0.498535],
[-0.969256, 1.875991, 0.041556],
[0.055648, -0.204043, 1.057311]]
'''
rows, cols, bands = XYZImage.shape # bands == 3
linearsRGBImage = np.zeros([rows, cols, bands], dtype=float)
multiplierMatrix = np.array([[3.240479, -1.53715, -0.498535],
[-0.969256, 1.875991, 0.041556],
[0.055648, -0.204043, 1.057311]])
for i in range(0, rows):
for j in range(0, cols):
X,Y,Z = XYZImage[i,j]
rgbList = np.matmul(multiplierMatrix, np.array([X,Y,Z]))
for index, val in enumerate(rgbList):
if val<0:
rgbList[index]=0
#if val -ends
if val>1:
rgbList[index]=1
#if val -ends
#for index, val -ends
linearsRGBImage[i,j]=rgbList
#for j -ends
#for i -ends
return linearsRGBImage
Though if anyone have better suggestion, it is most welcomed.