Skip to content

Commit bb61656

Browse files
committed
Completed VRPPDTW and Improved Plotting
1 parent 7bd20dd commit bb61656

4 files changed

Lines changed: 128 additions & 35 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,5 +162,6 @@ cython_debug/
162162
**/Solution
163163

164164
*.pdf
165+
*.docx
165166

166167
*test*.py

Parsing.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ def get_full_graph(base_directory):
55
nodes_file = base_directory + "Nodes.csv"
66
edges_file = base_directory + "Edges.csv"
77
requests = get_requests(base_directory + "Requests.csv")
8-
graph = create_graph(nodes_file, edges_file, requests)
8+
vehicles = get_vehicles(base_directory + "Vehicles.csv")
9+
graph = create_graph(nodes_file, edges_file, requests, vehicles)
910

1011
return graph, requests
1112

@@ -25,7 +26,7 @@ def convert_to_minutes(time_str):
2526
total_minutes = hours * 60 + minutes
2627
return total_minutes
2728

28-
def read_nodes(nodes_file, requests):
29+
def read_nodes(nodes_file, requests, vehicles):
2930
nodes = {}
3031
with open(nodes_file, 'r') as file:
3132
reader = csv.reader(file)
@@ -42,7 +43,12 @@ def read_nodes(nodes_file, requests):
4243
origin, destination, count, _, _, _, _, _, _ = request
4344
nodes[origin]['node_type'] = 'Pickup Node'
4445
nodes[destination]['node_type'] = 'Delivery Node'
45-
46+
47+
for vehicle_id, vehicle in vehicles.items():
48+
origin, destination, _ = vehicle
49+
nodes[origin]['node_type'] = 'Depot Node'
50+
nodes[destination]['node_type'] = 'Depot Node'
51+
4652
return nodes
4753

4854
def read_edges(filename):
@@ -56,10 +62,10 @@ def read_edges(filename):
5662
'destination': int(target), 'travel_time': float(weight)}
5763
return edges
5864

59-
def create_graph(nodes_file, edges_file, requests):
65+
def create_graph(nodes_file, edges_file, requests, vehicles):
6066
G = nx.DiGraph()
6167

62-
nodes = read_nodes(nodes_file, requests)
68+
nodes = read_nodes(nodes_file, requests, vehicles)
6369
edges = read_edges(edges_file)
6470

6571
for index, (key, value) in enumerate(nodes.items(), start=0):

Plot_Graph.py

Lines changed: 112 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,21 @@
1717

1818
import os
1919
from pathlib import Path
20+
import sys
2021
from PIL import Image
22+
import keyboard
2123
from Parsing import get_full_graph
2224
import matplotlib.pyplot as plt
2325
import matplotlib.patches as mpatches
2426
import networkx as nx
2527
import json
2628

29+
def get_node_label(node):
30+
if node[1]['name'] != None:
31+
return node[1]['name']
32+
else:
33+
return ""
34+
2735
def base_graph(G, ax, pos, label_edges = True):
2836
# Draw nodes with different colors based on node types
2937
node_colors = []
@@ -33,15 +41,25 @@ def base_graph(G, ax, pos, label_edges = True):
3341
node_colors.append('green')
3442
elif 'node_type' in node_data[1] and node_data[1]['node_type'] == 'Delivery Node':
3543
node_colors.append('blue')
44+
elif 'node_type' in node_data[1] and node_data[1]['node_type'] == 'Depot Node':
45+
node_colors.append('red')
3646
else:
3747
node_colors.append('gray') # Default color
3848
ax.text(pos[node][0], pos[node][1], str(node_data[1]['node_id']), fontsize=8, ha='center', va='center', color="white")
3949

4050
nx.draw_networkx_nodes(G, pos, ax=ax, node_size=100, node_color=node_colors)
41-
nx.draw_networkx_edges(G, pos, ax=ax, edge_color='black', width=1, arrows=False)
51+
nx.draw_networkx_edges(G, pos, ax=ax, edge_color='black', width=0.5, arrows=False)
4252

43-
# Draw edge labels with weights
4453
if label_edges:
54+
node_labels = {node[1]['node_id']: get_node_label(node) for node in G.nodes(data=True)}
55+
for node, label in node_labels.items():
56+
node_pos = pos[node]
57+
x_offset = 0.5
58+
y_offset = -0.8
59+
label_pos = [node_pos[0] + x_offset, node_pos[1] + y_offset]
60+
plt.text(*label_pos, label, ha='center', va='center', fontsize=6,
61+
bbox=dict(boxstyle="round", facecolor='lightblue', alpha=0.3, edgecolor="none"))
62+
4563
edge_labels = nx.get_edge_attributes(G, 'weight')
4664
nx.draw_networkx_edge_labels(G, pos, ax=ax, edge_labels=edge_labels, font_size=7)
4765

@@ -51,7 +69,7 @@ def base_graph(G, ax, pos, label_edges = True):
5169
center_x = (min(x_values) + max(x_values)) / 2
5270
center_y = (min(y_values) + max(y_values)) / 2
5371

54-
ax.grid(True, linestyle='-', linewidth=0.5, color='gray', zorder=0)
72+
ax.grid(True, linestyle='-', linewidth=0.3, color='gray', zorder=0)
5573
# Set the x and y ticks with increments of 1
5674
x_ticks = range(int(min(x_values)), int(max(x_values)) + 1)
5775
y_ticks = range(int(min(y_values)), int(max(y_values)) + 1)
@@ -65,7 +83,8 @@ def base_graph(G, ax, pos, label_edges = True):
6583
mpatches.Patch(color='blue', label='Delivery Node'),
6684
mpatches.Patch(color='red', label='Depot Node'),
6785
mpatches.Patch(color='gray', label='Junction Node')]
68-
ax.legend(handles=legend_handles)
86+
prop = {'size': 7} # Adjust font size as needed
87+
ax.legend(handles=legend_handles, prop=prop)
6988

7089
return ax
7190

@@ -80,18 +99,34 @@ def plot_overall_solution(graph, pos, solution_dir):
8099
with open(solution_dir + "chosen_x_ijk.json", "r") as json_file:
81100
chosen_x_ijk = json.load(json_file)
82101

102+
graph.remove_edges_from(list(graph.edges()))
103+
104+
bus_cnt = 0
83105
for bus, arc in chosen_x_ijk.items():
84106
fig, ax = plt.subplots(figsize=(8, 6))
85-
ax = base_graph(graph, ax, pos, True)
86-
87-
graph.remove_edges_from(list(graph.edges()))
107+
fig.suptitle('Bus ' + str(bus_cnt) , fontsize=20)
108+
ax.clear()
88109
graph.add_edges_from(arc)
110+
ax = base_graph(graph, ax, pos, False)
89111

90-
nx.draw_networkx_edges(graph, pos, ax=ax, edge_color='red', width=1, arrows=False)
112+
nx.draw_networkx_edges(graph, pos, ax=ax,
113+
edge_color='red', width=1, arrows=True)
114+
graph.remove_edges_from(arc)
115+
bus_cnt += 1
91116

92117
plt.show()
93118

119+
space_pressed = False
120+
121+
def on_key_press(event):
122+
global space_pressed
123+
if event.key == ' ':
124+
space_pressed = True
125+
elif event.key == 'q':
126+
sys.exit()
127+
94128
def plot_step_by_step(graph, pos, solution_dir, Gif=False):
129+
global space_pressed
95130

96131
# Read the JSON file
97132
with open(solution_dir + "buses_paths.json", "r") as json_file:
@@ -104,15 +139,49 @@ def plot_step_by_step(graph, pos, solution_dir, Gif=False):
104139

105140
bus_cnt = 0
106141
for bus, movements in buses_paths.items():
142+
total_cost = 0
107143
fig, ax = plt.subplots(figsize=(8, 6))
108144
fig.suptitle('Bus ' + str(bus_cnt) , fontsize=20)
145+
fig.canvas.mpl_connect('key_press_event', on_key_press)
146+
ax = base_graph(graph, ax, pos, False)
109147

110-
for idx, info in enumerate(movements):
148+
valid_movements = []
149+
for info in movements:
150+
if len(info['path']) > 1:
151+
valid_movements.append(info)
152+
153+
for idx, info in enumerate(valid_movements):
111154
paths = info['path']
112155
ax.clear()
113-
ax = base_graph(graph, ax, pos, True)
156+
ax = base_graph(graph, ax, pos, False)
157+
y_lim = plt.ylim()
158+
x_lim = plt.xlim()
159+
plt.ylim(y_lim[0], y_lim[1] * 1.2)
160+
161+
x_center = (x_lim[0] + x_lim[1]) / 2
114162

115163
if Gif:
164+
text = info['status']
165+
plt.text(x_center, y_lim[1] * 1.1, text, ha='center', va='center',
166+
fontsize=10, color='white',
167+
bbox=dict(boxstyle="round", facecolor='black', edgecolor="none"))
168+
169+
load_text = f"Load : {info['start_load']} -> {info['finish_load']}"
170+
plt.text(x_lim[0], y_lim[1] * 1.15, load_text, ha='left',
171+
va='center', fontsize=8, color='white',
172+
bbox=dict(boxstyle="round", facecolor='purple', edgecolor="none"))
173+
174+
time_text = f"Time : {info['start_time']} -> {info['finish_time']}"
175+
plt.text(x_lim[0], y_lim[1] * 1.05, time_text, ha='left',
176+
va='center', fontsize=8, color='white',
177+
bbox=dict(boxstyle="round", facecolor='blue', edgecolor="none"))
178+
179+
total_cost += int(info['path_cost'])
180+
cost_text = f"Cost : {info['path_cost']} - total Cost : {total_cost}"
181+
plt.text(x_lim[0], y_lim[1] * 0.95, cost_text, ha='left',
182+
va='center', fontsize=8, color='white',
183+
bbox=dict(boxstyle="round", facecolor='brown', edgecolor="none"))
184+
116185
edges = list(zip(paths, paths[1:]))
117186
nx.draw_networkx_edges(graph, pos, edgelist=edges, ax=ax, edge_color='r',
118187
width=2, arrows=True)
@@ -121,38 +190,53 @@ def plot_step_by_step(graph, pos, solution_dir, Gif=False):
121190
fig.savefig(frame_filename)
122191

123192
else:
124-
if idx == 0:
125-
plt.pause(2)
126-
elif idx == len(movements) - 1:
127-
edges = []
128-
for segment in movements:
129-
path = segment['path']
130-
if len(path) > 2:
131-
for i in range(len(path) - 1):
132-
edges.append((path[i],path[i+1]))
133-
elif len(path) == 2:
134-
edges.append(tuple(path))
135-
nx.draw_networkx_edges(graph, pos, edgelist=edges, ax=ax, edge_color='r', width=2, arrows=False)
136-
else:
137-
edges = list(zip(paths, paths[1:]))
138-
nx.draw_networkx_edges(graph, pos, edgelist=edges, ax=ax, edge_color='r', width=2, arrows=True)
139-
plt.pause(1)
193+
text = info['status']
194+
plt.text(x_center, y_lim[1] * 1.1, text, ha='center', va='center',
195+
fontsize=10, color='white',
196+
bbox=dict(boxstyle="round", facecolor='black', edgecolor="none"))
197+
198+
load_text = f"Load : {info['start_load']} -> {info['finish_load']}"
199+
plt.text(x_lim[0], y_lim[1] * 1.15, load_text, ha='left',
200+
va='center', fontsize=8, color='white',
201+
bbox=dict(boxstyle="round", facecolor='purple', edgecolor="none"))
202+
203+
time_text = f"Time : {info['start_time']} -> {info['finish_time']}"
204+
plt.text(x_lim[0], y_lim[1] * 1.05, time_text, ha='left',
205+
va='center', fontsize=8, color='white',
206+
bbox=dict(boxstyle="round", facecolor='blue', edgecolor="none"))
207+
208+
total_cost += int(info['path_cost'])
209+
cost_text = f"Cost : {info['path_cost']} - total Cost : {total_cost}"
210+
plt.text(x_lim[0], y_lim[1] * 0.95, cost_text, ha='left',
211+
va='center', fontsize=8, color='white',
212+
bbox=dict(boxstyle="round", facecolor='brown', edgecolor="none"))
213+
214+
edges = list(zip(paths, paths[1:]))
215+
nx.draw_networkx_edges(graph, pos, edgelist=edges, ax=ax, edge_color='r',
216+
width=2, arrows=True)
217+
218+
while not space_pressed:
219+
plt.pause(0.1)
220+
space_pressed = False
140221

141222
if Gif:
142-
frames = [Image.open(f'{gif_dir}/{frame}') for frame in sorted(os.listdir(gif_dir)) if frame.endswith(".png")]
223+
frames = [Image.open(f'{gif_dir}/{frame}') for frame in sorted(os.listdir(gif_dir))
224+
if frame.endswith(".png")]
143225
gif_filename = f'{gif_dir}/bus_{bus}.gif'
144226
frames[0].save(gif_filename, format='GIF', append_images=frames[1:],
145227
save_all=True, duration=1000, loop=0)
146228
[f.unlink() for f in Path(gif_dir).glob("*") if f.name.endswith(".png")]
147229

148230
bus_cnt += 1
231+
plt.close(fig)
149232

150233
if not Gif:
151234
plt.show()
152235

153236
def main():
154237
# Specify the folder name under the Inputs folder
155-
problem_dir = input("\nEnter folder name of the problem\n")
238+
print("-------------")
239+
problem_dir = input("Enter folder name of the problem\n")
156240

157241
base_directory = "Samples/" + problem_dir + "/"
158242
solution_dir = base_directory + "Solution/"

VRPPDTW.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@ def optimize_model(vehicles : dict, requests : dict, graph : DiGraph) -> dict:
346346
"start_load" : round(solution.get_value(L[i, k])),
347347
"finish_load" : round(solution.get_value(L[j, k])),
348348
"path" : shortest_paths_dict[V_val[k][i]][V_val[k][j]],
349+
"path_cost" : t[V_val[k][i]][V_val[k][j]],
349350
"status" : status}
350351

351352
# Storing chosen arcs from X
@@ -377,13 +378,14 @@ def optimize_model(vehicles : dict, requests : dict, graph : DiGraph) -> dict:
377378
def main():
378379

379380
# Specify the folder name under the Inputs folder
380-
problem_dir = input("\nEnter folder name of the problem\n")
381+
print("-------------")
382+
problem_dir = input("Enter folder name of the problem\n")
381383

382384
# Read input data including vehicles, request, nodes and edges
383385
base_directory = "Samples/" + problem_dir + "/"
384386
vehicles = get_vehicles(base_directory + "Vehicles.csv")
385387
requests = get_requests(base_directory + "Requests.csv")
386-
graph = create_graph(base_directory + "Nodes.csv",base_directory + "Edges.csv", requests)
388+
graph = create_graph(base_directory + "Nodes.csv",base_directory + "Edges.csv", requests, vehicles)
387389

388390
buses_paths, chosen_X, chosen_T, chosen_L = optimize_model(vehicles, requests, graph)
389391

0 commit comments

Comments
 (0)