Skip to content

Commit 2637789

Browse files
committed
create SolverDSO_ALNS class
1 parent cdf43b5 commit 2637789

File tree

2 files changed

+205
-3
lines changed

2 files changed

+205
-3
lines changed

uxsim/DTAsolvers/ALNS.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,7 @@ def init_alns(
610610
k_max: int = 5,
611611

612612
# 焼きなまし
613+
p0: float=0.2, #目標初期エラー
613614
T0: Optional[float] = None,
614615
Tend: float = 1e-3,
615616
total_iters: int = 10000,
@@ -759,7 +760,7 @@ def _eval(xx: Sequence[Any]) -> float:
759760
if not deltas:
760761
deltas = [1.0]
761762
med = sorted(deltas)[len(deltas)//2]
762-
p0 = 0.2 # 初期に悪化を割合p0前後で受理したい:かなりずれる...
763+
p0 = p0 # 初期に悪化を割合p0前後で受理したい:かなりずれる...
763764
T0 = max(1e-6, med / max(1e-12, math.log(1.0/p0)))
764765

765766
# 適応初期値(初期重みは定数から取得、定義されていなければ1.0)

uxsim/DTAsolvers/DTAsolvers.py

Lines changed: 203 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@
44
import random
55
import time
66
import warnings
7+
from pprint import pprint
8+
from collections import defaultdict
9+
710
import matplotlib.pyplot as plt
811
import numpy as np
9-
from collections import defaultdict
12+
1013
from ..Utilities import enumerate_k_shortest_routes, enumerate_k_random_routes
1114
from ..utils import *
15+
from . import ALNS
1216

1317
import warnings
1418

@@ -601,7 +605,7 @@ def plot_vehicle_stats(s, orig=None, dest=None):
601605
class SolverDSO_GA:
602606
def __init__(s, func_World):
603607
"""
604-
Solve Dynamic System Optimum (DSO) problem using genetic algorithm. WIP
608+
Solve Dynamic System Optimum (DSO) problem using genetic algorithm.
605609
606610
Parameters
607611
----------
@@ -981,6 +985,203 @@ def plot_vehicle_stats(s, orig=None, dest=None):
981985
plt.legend()
982986
plt.show()
983987

988+
989+
class SolverDSO_ALNS:
990+
def __init__(s, func_World):
991+
992+
"""
993+
Solve Dynamic System Optimum (DSO) problem using a customized Adaptive Large Neighborhood Search (ADLS).
994+
995+
Parameters
996+
----------
997+
func_World : function
998+
function that returns a World object with nodes, links, and demand specifications
999+
"""
1000+
s.func_World = func_World
1001+
s.W_sol = None #final solution
1002+
s.W_intermid_solution = None
1003+
s.dfs_link = []
1004+
1005+
def solve(s, max_iter, n_routes_per_od=10, k_max=None, p0=0.2, print_progress=True, initial_solution_World=None):
1006+
1007+
s.print_progress = print_progress
1008+
s.n_routes_per_od = n_routes_per_od
1009+
s.max_iter = max_iter
1010+
if k_max == None:
1011+
k_max = max([1, int(len(initial_solution_World.VEHICLES)*0.01)])
1012+
1013+
departure_times = []
1014+
1015+
s.start_time = time.time()
1016+
1017+
##############################################################
1018+
# initial solution
1019+
1020+
if initial_solution_World == None:
1021+
W = s.func_World()
1022+
W.exec_simulation()
1023+
if print_progress:
1024+
print(W.analyzer.basic_to_pandas())
1025+
else:
1026+
W = initial_solution_World
1027+
dict_od_to_routes = W.dict_od_to_routes
1028+
1029+
if print_progress:
1030+
W.print_scenario_stats()
1031+
1032+
dict_od_to_vehid = defaultdict(lambda: [])
1033+
for key, veh in W.VEHICLES.items():
1034+
o = veh.orig.name
1035+
d = veh.dest.name
1036+
dict_od_to_vehid[o,d].append(key)
1037+
departure_times.append(veh.departure_time)
1038+
1039+
departure_times = np.array(departure_times)/max(departure_times)
1040+
1041+
##############################################################
1042+
# enumerate some routes between each OD pair
1043+
1044+
dict_od_to_vehid = defaultdict(lambda: [])
1045+
for key, veh in W.VEHICLES.items():
1046+
o = veh.orig.name
1047+
d = veh.dest.name
1048+
dict_od_to_vehid[o,d].append(key)
1049+
1050+
if initial_solution_World != None and hasattr(initial_solution_World, "dict_od_to_routes"):
1051+
print("Initial solusion is used")
1052+
routes = initial_solution_World.dict_od_to_routes
1053+
xx0 = [0 for i in range(len(W.VEHICLES))]
1054+
for i,veh in enumerate(W.VEHICLES.values()):
1055+
route_id = 0
1056+
for j,r in enumerate(routes[(veh.orig.name, veh.dest.name)]):
1057+
if W.defRoute(r) == veh.traveled_route()[0]:
1058+
route_id = j
1059+
break
1060+
xx0[i] = route_id
1061+
else:
1062+
print("No initial solution")
1063+
routes = enumerate_k_random_routes(W, k=n_routes_per_od)
1064+
xx0 = [random.randint(0, len(xx_domain[i])) for i in range(len(W.VEHICLES))]
1065+
1066+
##############################################################
1067+
# Prepare ALNS
1068+
1069+
# specify routing based on x vector
1070+
def specify_routes(W, xx):
1071+
veh_list = list(W.VEHICLES.values())
1072+
for i, value in enumerate(xx):
1073+
veh = veh_list[i]
1074+
if value < len(routes[(veh.orig.name, veh.dest.name)]):
1075+
veh.set_links_prefer(routes[(veh.orig.name, veh.dest.name)][value])
1076+
else:
1077+
veh.set_links_prefer(routes[(veh.orig.name, veh.dest.name)][-1])
1078+
1079+
xx_domain = []
1080+
for i, veh in enumerate(W.VEHICLES.values()):
1081+
actual_n_route = len(routes[(veh.orig.name, veh.dest.name)])
1082+
xx_domain.append([i for i in range(actual_n_route)])
1083+
1084+
s.W_intermid_solution = W
1085+
s.best_obj_value = None
1086+
s.iter = 0
1087+
s.ttts = []
1088+
s.ttts_best = []
1089+
s.route_log = []
1090+
s.cost_log = []
1091+
s.dfs_link = []
1092+
1093+
s.best_iter = []
1094+
s.best_obj = []
1095+
1096+
print("solving DSO by ALNS...")
1097+
print("k_max:", k_max)
1098+
1099+
def objective_function_by_UXsim(xx):
1100+
W = s.func_World()
1101+
specify_routes(W, xx)
1102+
W.exec_simulation()
1103+
obj_value = W.analyzer.total_travel_time - (W.analyzer.trip_all-W.analyzer.trip_completed)*W.TMAX
1104+
1105+
route_actual = {}
1106+
cost_actual = {}
1107+
for key,veh in W.VEHICLES.items():
1108+
if veh.state != "end":
1109+
continue
1110+
1111+
route, ts = veh.traveled_route()
1112+
travel_time = ts[-1]-ts[0]
1113+
1114+
route_actual[key] = [rr.name for rr in route]
1115+
cost_actual[key] = travel_time
1116+
s.route_log.append(route_actual)
1117+
s.cost_log.append(cost_actual)
1118+
s.ttts.append(int(W.analyzer.total_travel_time))
1119+
s.dfs_link.append(W.analyzer.link_to_pandas())
1120+
1121+
1122+
if s.best_obj_value == None or obj_value < s.best_obj_value:
1123+
s.best_obj_value = obj_value
1124+
s.W_intermid_solution = W
1125+
s.W_intermid_solution.dict_od_to_routes = dict_od_to_routes
1126+
1127+
s.ttts_best.append(int(W.analyzer.total_travel_time))
1128+
1129+
s.best_iter.append(s.iter)
1130+
s.best_obj.append(s.best_obj_value)
1131+
1132+
if s.iter%20 == 0:
1133+
print(f"iter: {s.iter} - TTT: {W.analyzer.total_travel_time}, trips:{W.analyzer.trip_completed}/{W.analyzer.trip_all}, Obj:{obj_value}, Best Obj:{s.best_obj_value}")
1134+
1135+
s.iter += 1
1136+
return obj_value
1137+
1138+
1139+
state = ALNS.init_alns(
1140+
xx0, objective_function_by_UXsim,
1141+
domains=xx_domain,
1142+
k_max=k_max,
1143+
total_iters=max_iter,
1144+
p0=p0,
1145+
seed=0,
1146+
additional_info={"departure_times":departure_times, "W":W}
1147+
)
1148+
1149+
ALNS.run_auto(state, n=max_iter)
1150+
1151+
s.xx_star, s.run = ALNS.finalize(state)
1152+
1153+
s.W_sol = s.W_intermid_solution
1154+
1155+
s.end_time = time.time()
1156+
print("DSO summary:")
1157+
print(f" total travel time: initial {s.ttts[0]:.1f} -> last {s.ttts_best[-1]:.1f}")
1158+
print(f" computation time: {s.end_time - s.start_time:.1f} seconds")
1159+
1160+
def print_solution_process(s, full=False):
1161+
df = s.run.to_dataframe()
1162+
df = df.drop(columns=["changed_idx"])
1163+
if full:
1164+
print(df.to_string())
1165+
else:
1166+
print(df[df["accepted"]==True].to_string())
1167+
pprint(s.run.operator_stats)
1168+
1169+
def plot_convergence(s):
1170+
plt.figure()
1171+
plt.plot(s.ttts, "b-", lw=0.5, label="search trace")
1172+
plt.plot(s.best_iter, s.best_obj, "r.", label="updated solution")
1173+
obj_init = s.best_obj[0]
1174+
obj_min = s.best_obj[-1]
1175+
plt.ylim([obj_min-(obj_init-obj_min)*0.1, obj_init+(obj_init-obj_min)*0.5])
1176+
plt.xlabel("iteration")
1177+
plt.ylabel("total travel time (s)")
1178+
plt.legend()
1179+
plt.grid()
1180+
plt.show()
1181+
1182+
1183+
1184+
9841185
# does not work very well
9851186
# class SolverDUE_departure_time_choice:
9861187
# def __init__(s, func_World, desired_arrival_time, time_windows, alpha=1, beta=0.3, gamma=0.9, insensitive_ratio=0.333):

0 commit comments

Comments
 (0)