6
6
7
7
import numpy as np
8
8
import pandas as pd
9
+ from pymoo .core .problem import Problem
9
10
10
11
# from carmodel_calibration.exceptions import OptimizationFailed
11
12
from carmodel_calibration .fileaccess .parameter import ModelParameters
12
13
from carmodel_calibration .sumo .sumo_project import SumoProject
13
14
14
15
_LOGGER = logging .getLogger (__name__ )
15
16
17
+
16
18
def measure_of_performance_factory (objectives = ["distance" ],
17
- weights = None , gof = "rmse" , ** __ ):
19
+ weights = None , gof = "rmse" , reduce_mmop2smop = True ,
20
+ ** __ ):
18
21
"""
19
22
this function will return the appropiate objective function handle
20
23
depending on the desired MOP and the calibration object
@@ -58,6 +61,7 @@ def measure_of_performance_factory(objectives=["distance"],
58
61
objectives = weights_renamed
59
62
for weights_value , objective in zip (weights_values , objectives ):
60
63
weights [objective ] = weights_value
64
+
61
65
def rmse (ground_truth , prediction ):
62
66
"""
63
67
RMSE(v)
@@ -70,7 +74,31 @@ def rmse(ground_truth, prediction):
70
74
prediction [sop ].values
71
75
- ground_truth [sop ].values ))
72
76
sums [idx ] = np .sqrt (sums [idx ] / len (ground_truth )) * weights [sop ]
73
- return np .sum (sums )
77
+ if reduce_mmop2smop :
78
+ return np .sum (sums )
79
+ else :
80
+ return sums
81
+
82
+ def nrmse (ground_truth , prediction ):
83
+ """
84
+ NRMSE(v)
85
+ see 'About calibration of car-following dynamics of automated and
86
+ human-driven vehicles: Methodology, guidelines and codes'
87
+ """
88
+ sums = np .zeros (len (objectives ))
89
+ for idx , sop in enumerate (objectives ):
90
+ rmse_error = np .sum (np .square (
91
+ prediction [sop ].values
92
+ - ground_truth [sop ].values ))
93
+ rmse_error = np .sqrt (rmse_error / len (ground_truth ))
94
+ gt_root = np .sqrt (np .sum (np .square (ground_truth [sop ].values ))
95
+ / len (ground_truth ))
96
+ sums [idx ] = rmse_error / (gt_root ) * weights [sop ]
97
+ if reduce_mmop2smop :
98
+ return np .sum (sums )
99
+ else :
100
+ return sums
101
+
74
102
def rmspe (ground_truth , prediction ):
75
103
"""
76
104
RMSPE(v)
@@ -84,7 +112,11 @@ def rmspe(ground_truth, prediction):
84
112
- ground_truth [sop ].values )
85
113
/ ground_truth [sop ].values ))
86
114
sums [idx ] = np .sqrt (sums [idx ] / len (ground_truth )) * weights [sop ]
87
- return np .sum (sums )
115
+ if reduce_mmop2smop :
116
+ return np .sum (sums )
117
+ else :
118
+ return sums
119
+
88
120
def theils_u (ground_truth , prediction ):
89
121
"""
90
122
theils U(v)
@@ -102,16 +134,23 @@ def theils_u(ground_truth, prediction):
102
134
gt_root = np .sqrt (np .sum (np .square (ground_truth [sop ].values ))
103
135
/ len (ground_truth ))
104
136
sums [idx ] = rmse_error / (sim_root + gt_root ) * weights [sop ]
105
- return np .sum (sums )
137
+ if reduce_mmop2smop :
138
+ return np .sum (sums )
139
+ else :
140
+ return sums
106
141
107
142
def model_output (_ , prediction ):
108
143
"""only sum model outputs"""
109
144
sums = np .zeros (len (objectives ))
110
145
for idx , sop in enumerate (objectives ):
111
146
sums [idx ] = prediction [sop ].values [- 1 ] * weights [sop ]
112
- return np .sum (sums )
113
- gof_handles = {"rmse" : rmse , "rmsep" : rmspe , "modelOutput" : model_output ,
114
- "theils_u" : theils_u }
147
+ if reduce_mmop2smop :
148
+ return np .sum (sums )
149
+ else :
150
+ return sums
151
+
152
+ gof_handles = {"rmse" : rmse , "nrmse" : nrmse , "rmsep" : rmspe ,
153
+ "modelOutput" : model_output , "theils_u" : theils_u }
115
154
116
155
def get_weighted_error (ground_truth , prediction ):
117
156
"""calculate weighted error on case1"""
@@ -124,6 +163,7 @@ def get_weighted_error(ground_truth, prediction):
124
163
125
164
return get_weighted_error
126
165
166
+
127
167
def factory_wrapper (factory_kwargs ):
128
168
"""invokes factory from results data"""
129
169
if isinstance (factory_kwargs , pd .DataFrame ):
@@ -142,31 +182,33 @@ def factory_wrapper(factory_kwargs):
142
182
else :
143
183
raise TypeError (
144
184
"`factory_kwargs` must either be of type dict or pandas DattaFrame"
145
- )
185
+ )
186
+
146
187
147
188
def _get_results (simulation_results , identification , lane ):
148
189
"""returns the simulation result for specific identification"""
149
190
edge_name = f"B{ lane } A{ lane } "
150
191
condition = (
151
- (simulation_results [1 ]["prediction" ]["edgeName" ]== edge_name )
192
+ (simulation_results [1 ]["prediction" ]["edgeName" ] == edge_name )
152
193
)
153
194
prediction_result = simulation_results [1 ]["prediction" ][condition ]
154
195
leader = identification [0 ]
155
196
recording_id = identification [2 ]
156
197
follower_condition = (
157
- simulation_results [1 ]["ground_truth" ]["follower" ]== identification [1 ])
198
+ simulation_results [1 ]["ground_truth" ]["follower" ] == identification [1 ])
158
199
condition = (
159
- (simulation_results [1 ]["ground_truth" ]["leader" ]== leader )
200
+ (simulation_results [1 ]["ground_truth" ]["leader" ] == leader )
160
201
& (follower_condition )
161
202
& (simulation_results [1 ]["ground_truth" ]
162
- ["recordingId" ]== recording_id )
203
+ ["recordingId" ] == recording_id )
163
204
& (simulation_results [1 ]["ground_truth" ]
164
- ["counter" ]== lane )
205
+ ["counter" ] == lane )
165
206
)
166
207
ground_truth_result = (
167
208
simulation_results [1 ]["ground_truth" ][condition ])
168
209
return prediction_result , ground_truth_result
169
210
211
+
170
212
def _run_sumo (identification , sumo_interface , param_sets , project_path , model , timestep ):
171
213
cfmodels = []
172
214
route_count = SumoProject .get_number_routes (
@@ -177,7 +219,7 @@ def _run_sumo(identification, sumo_interface, param_sets, project_path, model, t
177
219
sumo_interface .start_simulation_module ()
178
220
for idx , param_set in enumerate (param_sets ):
179
221
cfmodel = ModelParameters .create_parameter_set (f"set{ idx } .json" , model ,
180
- ** param_set )
222
+ ** param_set )
181
223
cfmodels .append (cfmodel )
182
224
SumoProject .write_followers_leader (
183
225
project_path / "calibration_routes.rou.xml" ,
@@ -186,13 +228,15 @@ def _run_sumo(identification, sumo_interface, param_sets, project_path, model, t
186
228
identification = identification )
187
229
return simulation_results
188
230
231
+
189
232
def _calculate_performance (idx , simulation_results , identification ,
190
- objective_function ):
233
+ objective_function ):
191
234
prediction , ground_truth = _get_results (
192
- simulation_results , identification , idx )
235
+ simulation_results , identification , idx )
193
236
objective_function = factory_wrapper (objective_function )
194
237
return objective_function (ground_truth , prediction )
195
238
239
+
196
240
def _vectorized_target (params , * data ):
197
241
# params.shape = (Number params, number of solutions)
198
242
params = np .atleast_2d (params ).T
@@ -206,7 +250,7 @@ def _vectorized_target(params, *data):
206
250
# keys of the paramters that are optimized
207
251
param_names = data ["param_names" ]
208
252
param_sets = []
209
- if params .shape [1 ]== 1 :
253
+ if params .shape [1 ] == 1 :
210
254
params = params .T
211
255
for solution in params :
212
256
params_dict = data ["default_parameters" ].copy ()
@@ -215,22 +259,36 @@ def _vectorized_target(params, *data):
215
259
param_sets .append (params_dict )
216
260
simulation_results = _run_sumo (
217
261
identification , sumo_interface , param_sets , project_path , model , timestep )
218
- with Pool (os .cpu_count ()// 2 ) as pool :
262
+ with Pool (os .cpu_count () // 2 ) as pool :
219
263
results = []
220
264
for idx in range (len (params )):
221
265
results .append ((idx , simulation_results , identification ,
222
266
objective_function ))
223
267
performance = list (pool .starmap (_calculate_performance , results ))
224
- # performance = []
225
- # for idx in range(len(params)):
226
- # performance.append(_calculate_performance(idx, simulation_results, identification,
227
- # objective_function))
268
+ performance = []
269
+ for idx in range (len (params )):
270
+ performance .append (_calculate_performance (idx , simulation_results , identification ,
271
+ objective_function ))
228
272
return performance
229
273
230
274
231
275
def target_factory (data , invert_error = False ):
232
276
"""factory for creating a target callback"""
233
- def _vectorized_wrapper (params , solution_indexes ):
277
+ def _vectorized_wrapper (ga_instance , params , solution_indexes ):
278
+ solutions = _vectorized_target (params .T , data )
279
+ # OutComment this when using adaptive mutation
280
+ # if solution_indexes is None:
281
+ # return None
282
+ if invert_error :
283
+ return [1 / solution for solution in solutions ]
284
+ else :
285
+ return solutions
286
+ return _vectorized_wrapper
287
+
288
+
289
+ def target_factory_nsga2 (data , invert_error = False ):
290
+ """factory for creating a target callback"""
291
+ def _vectorized_wrapper (ga_instance , params , solution_indexes ):
234
292
solutions = _vectorized_target (params .T , data )
235
293
if solution_indexes is None :
236
294
return None
@@ -239,3 +297,14 @@ def _vectorized_wrapper(params, solution_indexes):
239
297
else :
240
298
return solutions
241
299
return _vectorized_wrapper
300
+
301
+
302
+ class target_factory_mo_de (Problem ):
303
+
304
+ def __init__ (self , data , ** kwargs ):
305
+ super ().__init__ (** kwargs )
306
+ self .data = data
307
+
308
+ def _evaluate (self , X , out , * args , ** kwargs ):
309
+ # store the function values and return them.
310
+ out ["F" ] = np .array (_vectorized_target (X .T , self .data ))
0 commit comments