From cf9339270601be774e0fb33ee2658348d7ac95b9 Mon Sep 17 00:00:00 2001 From: char-tan Date: Thu, 5 Jan 2023 21:34:10 +0000 Subject: [PATCH 01/38] add + handle second output from metrics, fix WaitTime, tidy CompletedJourneys --- emulation/emulator.py | 38 ++++++++++++++++--- emulation/simulator.py | 75 +++++++++++++++++++------------------- main.py | 6 ++- metrics/metrics.py | 83 +++++++++++++++++++++++++++++------------- 4 files changed, 133 insertions(+), 69 deletions(-) diff --git a/emulation/emulator.py b/emulation/emulator.py index b8d9afd..e82a8b3 100644 --- a/emulation/emulator.py +++ b/emulation/emulator.py @@ -3,6 +3,7 @@ import numpy as np import scipy from emukit.core import ContinuousParameter +from emukit.core.loop.user_function import UserFunctionWrapper from emukit.examples.gp_bayesian_optimization.single_objective_bayesian_optimization import GPBayesianOptimization from emulation.simulator import Simulator @@ -28,18 +29,42 @@ def __init__(self, graph: Graph, flow_strategy: FlowStrategy, simulation_iterati self._num_params = intersections * 3 def bayes_opt(self, metric, interval: Tuple[float, float], iterations: int): + np.random.seed(42) sim = Simulator(self._g, metric, self._strategy, self._time_period, self._sim_iterations) + + target_function = UserFunctionWrapper(sim.evaluate, extra_output_names=['raw metric']) + x_init = np.random.uniform(*interval, size=(1, self._num_params)) - y_init = sim.evaluate(x_init) - parameter_list = [ContinuousParameter(f"p{i}", *interval) for i in range(self._num_params)] + # you can't pass the UserFunctionResult straight into GPBO + output_init = target_function(x_init)[0] + + # also the array for y is not the right shape + y_init = np.expand_dims(output_init.Y, axis=1) + + # parameter space + parameter_list = [ContinuousParameter(f"x{i}", *interval) for i in range(self._num_params)] + # create the BO loop bo_loop = GPBayesianOptimization(variables_list=parameter_list, X=x_init, Y=y_init, noiseless=True) - bo_loop.run_optimization(sim.evaluate, iterations) - return results_to_df(bo_loop.model.X, bo_loop.model.Y, metric().name, self._time_period) + # put the inital raw metric into the bo_loop results + bo_loop.loop_state.results[0].extra_outputs['raw metric'] = output_init.extra_outputs['raw metric'] + + # run optimisation + bo_loop.run_optimization(target_function, iterations) + + # get x and raw metric values from loop state results + x = [step.X for step in bo_loop.loop_state.results] + raw_metric = [step.extra_outputs['raw metric'] for step in bo_loop.loop_state.results] + + # convert into arrays + x = np.stack(x, axis=0) + raw_metric = np.concatenate(raw_metric) + + return results_to_df(x, raw_metric, metric().name, self._time_period) def grid_search_opt(self, metric, interval: Tuple[float, float], steps_per_axis: int): """Evaluates target_function on all combinations of parameters taken from the same interval""" @@ -47,7 +72,10 @@ def grid_search_opt(self, metric, interval: Tuple[float, float], steps_per_axis: sim = Simulator(self._g, metric, self._strategy, self._time_period, self._sim_iterations) - x_min, f_min, grid, results = scipy.optimize.brute(func=sim.evaluate, + # lambda function for selecting raw metric from outputs + target_function = lambda x: sim.evaluate(x)[1] + + x_min, f_min, grid, results = scipy.optimize.brute(func=target_function, ranges=(interval,) * self._num_params, Ns=steps_per_axis, full_output=True, diff --git a/emulation/simulator.py b/emulation/simulator.py index baa8749..e06dd7e 100644 --- a/emulation/simulator.py +++ b/emulation/simulator.py @@ -36,6 +36,7 @@ def __init__(self, g: Graph, metric, strategy=None, timing_period: Optional[int] with open("cityflow_config/flows/auto_flow.json", 'w') as f: f.write(json.dumps(flow, indent=4)) + def evaluate(self, x): """ Parameters @@ -72,51 +73,51 @@ def evaluate(self, x): eng.next_step() metric.update(eng) - aggregate, _ = metric.report() + target_metric, raw_metric = metric.report() - return np.array([[aggregate]]) + return np.array([[target_metric]]), np.array([[raw_metric]]) - def multithreaded_evaluate(self, x): - """ - Parameters - ---------- - x: A 1D numpy array of traffic light phase timings + # def multithreaded_evaluate(self, x): + # """ + # Parameters + # ---------- + # x: A 1D numpy array of traffic light phase timings - Returns - ------- - The resulting aggregate metric calculated after N simulation iterations, with traffic light timings x. - """ - x = np.array(np.array_split(x.flatten(), len(self.intersections))) - if self.timing_period is not None: - x3 = self.timing_period - x.sum(axis=1) - x = np.insert(x, x.shape[1] - 1, x3, axis=1) + # Returns + # ------- + # The resulting aggregate metric calculated after N simulation iterations, with traffic light timings x. + # """ + # x = np.array(np.array_split(x.flatten(), len(self.intersections))) + # if self.timing_period is not None: + # x3 = self.timing_period - x.sum(axis=1) + # x = np.insert(x, x.shape[1] - 1, x3, axis=1) - traffic_light_phases = {intersection: timing for (intersection, timing) in zip(self.intersections, x.tolist())} - roadnet = graph_to_roadnet(self.g, traffic_light_phases, intersection_width=50, lane_width=8) + # traffic_light_phases = {intersection: timing for (intersection, timing) in zip(self.intersections, x.tolist())} + # roadnet = graph_to_roadnet(self.g, traffic_light_phases, intersection_width=50, lane_width=8) - # Generate UUID tag to ensure each thread's config file is unique - name = str(uuid.uuid1())[:8] + # # Generate UUID tag to ensure each thread's config file is unique + # name = str(uuid.uuid1())[:8] - roadnet_file = f"cityflow_config/roadnets/auto_roadnet_{name}.json" - with open(roadnet_file, 'w') as f: - f.write(json.dumps(roadnet, indent=4)) - config_file = f"cityflow_config/config_{name}.json" - with open(f"cityflow_config/config.json", 'r') as f: - config = json.loads(f.read()) + # roadnet_file = f"cityflow_config/roadnets/auto_roadnet_{name}.json" + # with open(roadnet_file, 'w') as f: + # f.write(json.dumps(roadnet, indent=4)) + # config_file = f"cityflow_config/config_{name}.json" + # with open(f"cityflow_config/config.json", 'r') as f: + # config = json.loads(f.read()) - config["roadnetFile"] = f"roadnets/auto_roadnet_{name}.json" - with open(config_file, 'w') as f: - f.write(json.dumps(config, indent=4)) + # config["roadnetFile"] = f"roadnets/auto_roadnet_{name}.json" + # with open(config_file, 'w') as f: + # f.write(json.dumps(config, indent=4)) - eng = cf.Engine(config_file, thread_num=1) + # eng = cf.Engine(config_file, thread_num=1) - metric = self.metric() + # metric = self.metric() - for _ in range(self.steps): - eng.next_step() - metric.update(eng) + # for _ in range(self.steps): + # eng.next_step() + # metric.update(eng) - aggregate, _ = metric.report() - os.remove(roadnet_file) - os.remove(config_file) - return np.array([[aggregate]]) + # aggregate, _ = metric.report() + # os.remove(roadnet_file) + # os.remove(config_file) + # return np.array([[aggregate]]) diff --git a/main.py b/main.py index c585c7c..6cc686b 100644 --- a/main.py +++ b/main.py @@ -5,6 +5,7 @@ from simulation_builder.graph import Graph, I_graph from metrics.metrics import CompletedJourneysMetric +from metrics.metrics import WaitTimeMetric if __name__ == "__main__": np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) @@ -28,5 +29,8 @@ e = Emulator(g, strategy, fixed_time_period=60) - # print(e.bayes_opt(CompletedJourneysMetric, interval=(0.1, 20), iterations=5)) + print(e.bayes_opt(CompletedJourneysMetric, interval=(0.1, 20), iterations=5)) + print(e.bayes_opt(WaitTimeMetric, interval=(0.1, 20), iterations=5)) + print(e.grid_search_opt(CompletedJourneysMetric, interval=(0.1, 20), steps_per_axis=2)) + print(e.grid_search_opt(WaitTimeMetric, interval=(0.1, 20), steps_per_axis=2)) diff --git a/metrics/metrics.py b/metrics/metrics.py index 6870c5f..dfdc470 100644 --- a/metrics/metrics.py +++ b/metrics/metrics.py @@ -6,11 +6,11 @@ @dataclass class Report: - aggregate: Number - data: List[Number] + target_metric: Number # 'normalised' for minimisation + raw_metric: Number def __iter__(self): - return iter((self.aggregate, self.data)) + return iter((self.target_metric, self.raw_metric)) class Metric(ABC): @@ -27,39 +27,70 @@ def report(self) -> Report: class CompletedJourneysMetric(Metric): def __init__(self): - self._total_vehicles = set() - self._prev_step = set() - self._completed_journeys = [0] + self._total_vehicles = set() # unique vehicles in simulation self.name = 'completed journeys' def update(self, eng): - curr_step = set(eng.get_vehicles(include_waiting=True)) - self._total_vehicles |= curr_step - self._completed_journeys.append(len(self._prev_step - curr_step) + self._completed_journeys[-1]) - self._prev_step = curr_step + + # get all vehicles currently in simulation + self._current_vehicles = set(eng.get_vehicles(include_waiting=True)) + + # add new vehicles to total count + self._total_vehicles |= self._current_vehicles def report(self) -> Report: - return Report(1 - self._completed_journeys[-1]/len(self._total_vehicles), self._completed_journeys) + # find the vehicles that had left simulator by last step + total_completed = len(self._total_vehicles - self._current_vehicles) -class WaitTimeMetric(Metric): - """ - Reports the overall average waiting time, and the proportion of cars waiting at each time step. - """ + # optimisation target is 1 - (normalised total completed) + target_metric = 1 - total_completed / len(self._total_vehicles) + + return Report(target_metric, total_completed) + +class WaitTimeMetric(Metric): def __init__(self): - self._waiting_vehicles = [] - self._total_vehicles = [] - self.name = 'wait time' + self._unique_vehicles = set() # unique vehicles in simulation (should be constant for given graph + flow) + self._waiting_vehicle_steps = [] # a vehicle step is 1 vehicle waiting for 1 step + self.name = 'average wait time' def update(self, eng): - vehicles = eng.get_vehicles(include_waiting=True) - wait_time = sum([float(eng.get_vehicle_info(v)['speed']) < 0.1 for v in vehicles]) - self._total_vehicles.append(len(vehicles)) - self._waiting_vehicles.append(wait_time) + + # get all vehicles currently in simulation, including those waiting offscreen + current_vehicles = eng.get_vehicles(include_waiting=True) + + # add new vehicles to set of unique + self._unique_vehicles |= set(current_vehicles) + + # find the waiting vehicles + waiting = 0 + for i, v in enumerate(current_vehicles): + + # get vehicle info from engine + info = eng.get_vehicle_info(v) + + # check if car on road + if info['running'] == '1': + # get speed of car + speed = float(info['speed']) + else: + # cars not on road have no key 'speed' + speed = 0.0 + + # check if speed is below threshold, add to count of waiting + if speed < 0.1: + waiting += 1 + + # add waiting vehicles to stepwise list + self._waiting_vehicle_steps.append(waiting) def report(self) -> Report: - total_average = sum(self._waiting_vehicles) / sum(self._total_vehicles) - proportion_waiting = [wait / total if total > 0 else 0 for wait, total in - zip(self._waiting_vehicles, self._total_vehicles)] - return Report(total_average, proportion_waiting) + + # computes the average proportion of time vehicles spent waiting in their journey + total_average = sum(self._waiting_vehicle_steps) / len(self._unique_vehicles) + + # divide by number of steps (can't normalise as is not bounded) + target_metric = total_average / len(self._waiting_vehicle_steps) + + return Report(target_metric, total_average) From 3235ee535bcee168c778293820b9ca7cd1d7ca9b Mon Sep 17 00:00:00 2001 From: char-tan Date: Thu, 5 Jan 2023 21:34:27 +0000 Subject: [PATCH 02/38] add (i) to inferred parameters in dataframe --- emulation/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emulation/utils.py b/emulation/utils.py index 442272d..66bfec9 100644 --- a/emulation/utils.py +++ b/emulation/utils.py @@ -24,7 +24,7 @@ def results_to_df(x, y, metric_name: str, time_period: Optional[float]): node, phase = i // 3, i % 3 d[f"x_{node}_{phase}"] = np.array(x[:, i]) if i % 3 == 2: - d[f"x_{node}_3"] = time_period - x[:, i - 2:i + 1].sum(axis=1) + d[f"x_{node}_3 (i)"] = time_period - x[:, i - 2:i + 1].sum(axis=1) else: node, phase = i // 4, i % 4 d[f"x_{node}_{phase}"] = np.array(x[:, i]) From ccf2268773003d04ecb8c032fa16b9278401ec39 Mon Sep 17 00:00:00 2001 From: char-tan Date: Thu, 5 Jan 2023 21:35:29 +0000 Subject: [PATCH 03/38] move metrics/metrics.py to main dir, remove metric dir --- main.py | 4 +- metrics/metrics.py => metrics.py | 0 metrics/useful_metrics.py | 73 -------------------------------- 3 files changed, 2 insertions(+), 75 deletions(-) rename metrics/metrics.py => metrics.py (100%) delete mode 100644 metrics/useful_metrics.py diff --git a/main.py b/main.py index 6cc686b..6c37e35 100644 --- a/main.py +++ b/main.py @@ -4,8 +4,8 @@ from simulation_builder.flows import CustomEndpointFlowStrategy, FlowStrategy from simulation_builder.graph import Graph, I_graph -from metrics.metrics import CompletedJourneysMetric -from metrics.metrics import WaitTimeMetric +from metrics import CompletedJourneysMetric +from metrics import WaitTimeMetric if __name__ == "__main__": np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) diff --git a/metrics/metrics.py b/metrics.py similarity index 100% rename from metrics/metrics.py rename to metrics.py diff --git a/metrics/useful_metrics.py b/metrics/useful_metrics.py deleted file mode 100644 index 58d2c3c..0000000 --- a/metrics/useful_metrics.py +++ /dev/null @@ -1,73 +0,0 @@ -import numpy as np - -set_ids = set() -last_step = None -counter = 0 - -def completed_journeys(eng, online = False): - """Returns online or total completed journeys""" - global last_step - # List of vehicle IDs currently on road - current_ids = eng.get_vehicles(include_waiting=False) - - # If want completed journeys at each time step: - if online: - # If function is called for the first time - if last_step is None: - last_step = current_ids - return - # Yields the elements in last_step that are NOT in current_ids - completed_ids = np.setdiff1d(last_step, current_ids) - completed_journeys = len(completed_ids) - - last_step = current_ids - return completed_journeys - - # total = set of car ids - number of cars on road at last step - set_ids.update(current_ids) - total_completed_journeys = len(set_ids) - len(eng.get_vehicles()) - - return total_completed_journeys - -def count_waiting_cars(eng, current_ids, counter): - for car in current_ids: - # CityFlow defines a waiting car - # as speed < 0.1 mph - if float(eng.get_vehicle_info(car)['speed']) < 0.1: - counter += 1 - - -def wait_time(eng, _ = False, steps = False, online = False): - """Returns online or total average wait""" - #List of IDs of cars currently on road - current_ids = eng.get_vehicles(include_waiting=False) - - # If want average wait across entire simulation - global counter - for car in current_ids: - # CityFlow defines a waiting car - # as speed < 0.1 mph - if float(eng.get_vehicle_info(car)['speed']) < 0.1: - counter += 1 - set_ids.update(current_ids) - # If last step of simulation, calculate total avg - if _ == steps - 1: - # avg = num of times cars were idle in sim / num cars in sim - total_avg = counter / len(set_ids) - return total_avg - - # If want average wait at each time step - if online: - counter = 0 - for car in current_ids: - # CityFlow defines a waiting car - # as speed < 0.1 mph - if float(eng.get_vehicle_info(car)['speed']) < 0.1: - counter += 1 - if counter == 0: - return - # avg = num cars that were idle / num cars on road - avg = counter / len(eng.get_vehicles()) - return avg - - From 3a7139d6bed3aa885e2bd2c92b44c38562bd86d1 Mon Sep 17 00:00:00 2001 From: char-tan Date: Thu, 5 Jan 2023 21:47:06 +0000 Subject: [PATCH 04/38] more accurate name for WaitTime metric in dataframe --- metrics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics.py b/metrics.py index dfdc470..6eeae43 100644 --- a/metrics.py +++ b/metrics.py @@ -53,7 +53,7 @@ class WaitTimeMetric(Metric): def __init__(self): self._unique_vehicles = set() # unique vehicles in simulation (should be constant for given graph + flow) self._waiting_vehicle_steps = [] # a vehicle step is 1 vehicle waiting for 1 step - self.name = 'average wait time' + self.name = 'average steps waiting' def update(self, eng): From 8e88b6ca3460768b73e40aa61c9cb57c35352541 Mon Sep 17 00:00:00 2001 From: yu202147657 Date: Fri, 6 Jan 2023 17:13:57 +0000 Subject: [PATCH 05/38] Adding sensitivity analysis --- cityflow_config/flows/auto_flow.json | 250 ++ cityflow_config/roadnets/auto_roadnet.json | 2538 ++++++++++++++++++++ emulation/emulator.py | 25 +- metrics.py => emulation/metrics.py | 2 +- emulation/utils.py | 7 +- main.py | 17 +- 6 files changed, 2828 insertions(+), 11 deletions(-) create mode 100644 cityflow_config/flows/auto_flow.json create mode 100644 cityflow_config/roadnets/auto_roadnet.json rename metrics.py => emulation/metrics.py (98%) diff --git a/cityflow_config/flows/auto_flow.json b/cityflow_config/flows/auto_flow.json new file mode 100644 index 0000000..ee2ca0a --- /dev/null +++ b/cityflow_config/flows/auto_flow.json @@ -0,0 +1,250 @@ +[ + { + "vehicle": { + "length": 5.0, + "width": 2.0, + "maxPosAcc": 2.0, + "maxNegAcc": 4.5, + "usualPosAcc": 2.0, + "usualNegAcc": 4.5, + "minGap": 2.5, + "maxSpeed": 12.67, + "headwayTime": 1.5 + }, + "route": [ + "road_-400_400_E", + "road_0_400_S", + "road_0_0_E" + ], + "interval": 5.0, + "startTime": 0, + "endTime": -1 + }, + { + "vehicle": { + "length": 5.0, + "width": 2.0, + "maxPosAcc": 2.0, + "maxNegAcc": 4.5, + "usualPosAcc": 2.0, + "usualNegAcc": 4.5, + "minGap": 2.5, + "maxSpeed": 12.67, + "headwayTime": 1.5 + }, + "route": [ + "road_-400_400_E", + "road_0_400_S", + "road_0_0_W" + ], + "interval": 5.0, + "startTime": 0, + "endTime": -1 + }, + { + "vehicle": { + "length": 5.0, + "width": 2.0, + "maxPosAcc": 2.0, + "maxNegAcc": 4.5, + "usualPosAcc": 2.0, + "usualNegAcc": 4.5, + "minGap": 2.5, + "maxSpeed": 12.67, + "headwayTime": 1.5 + }, + "route": [ + "road_-400_400_E", + "road_0_400_E" + ], + "interval": 5.0, + "startTime": 0, + "endTime": -1 + }, + { + "vehicle": { + "length": 5.0, + "width": 2.0, + "maxPosAcc": 2.0, + "maxNegAcc": 4.5, + "usualPosAcc": 2.0, + "usualNegAcc": 4.5, + "minGap": 2.5, + "maxSpeed": 12.67, + "headwayTime": 1.5 + }, + "route": [ + "road_400_0_W", + "road_0_0_N", + "road_0_400_W" + ], + "interval": 5.0, + "startTime": 0, + "endTime": -1 + }, + { + "vehicle": { + "length": 5.0, + "width": 2.0, + "maxPosAcc": 2.0, + "maxNegAcc": 4.5, + "usualPosAcc": 2.0, + "usualNegAcc": 4.5, + "minGap": 2.5, + "maxSpeed": 12.67, + "headwayTime": 1.5 + }, + "route": [ + "road_400_0_W", + "road_0_0_W" + ], + "interval": 5.0, + "startTime": 0, + "endTime": -1 + }, + { + "vehicle": { + "length": 5.0, + "width": 2.0, + "maxPosAcc": 2.0, + "maxNegAcc": 4.5, + "usualPosAcc": 2.0, + "usualNegAcc": 4.5, + "minGap": 2.5, + "maxSpeed": 12.67, + "headwayTime": 1.5 + }, + "route": [ + "road_400_0_W", + "road_0_0_N", + "road_0_400_E" + ], + "interval": 5.0, + "startTime": 0, + "endTime": -1 + }, + { + "vehicle": { + "length": 5.0, + "width": 2.0, + "maxPosAcc": 2.0, + "maxNegAcc": 4.5, + "usualPosAcc": 2.0, + "usualNegAcc": 4.5, + "minGap": 2.5, + "maxSpeed": 12.67, + "headwayTime": 1.5 + }, + "route": [ + "road_-400_0_E", + "road_0_0_N", + "road_0_400_W" + ], + "interval": 5.0, + "startTime": 0, + "endTime": -1 + }, + { + "vehicle": { + "length": 5.0, + "width": 2.0, + "maxPosAcc": 2.0, + "maxNegAcc": 4.5, + "usualPosAcc": 2.0, + "usualNegAcc": 4.5, + "minGap": 2.5, + "maxSpeed": 12.67, + "headwayTime": 1.5 + }, + "route": [ + "road_-400_0_E", + "road_0_0_E" + ], + "interval": 5.0, + "startTime": 0, + "endTime": -1 + }, + { + "vehicle": { + "length": 5.0, + "width": 2.0, + "maxPosAcc": 2.0, + "maxNegAcc": 4.5, + "usualPosAcc": 2.0, + "usualNegAcc": 4.5, + "minGap": 2.5, + "maxSpeed": 12.67, + "headwayTime": 1.5 + }, + "route": [ + "road_-400_0_E", + "road_0_0_N", + "road_0_400_E" + ], + "interval": 5.0, + "startTime": 0, + "endTime": -1 + }, + { + "vehicle": { + "length": 5.0, + "width": 2.0, + "maxPosAcc": 2.0, + "maxNegAcc": 4.5, + "usualPosAcc": 2.0, + "usualNegAcc": 4.5, + "minGap": 2.5, + "maxSpeed": 12.67, + "headwayTime": 1.5 + }, + "route": [ + "road_400_400_W", + "road_0_400_W" + ], + "interval": 5.0, + "startTime": 0, + "endTime": -1 + }, + { + "vehicle": { + "length": 5.0, + "width": 2.0, + "maxPosAcc": 2.0, + "maxNegAcc": 4.5, + "usualPosAcc": 2.0, + "usualNegAcc": 4.5, + "minGap": 2.5, + "maxSpeed": 12.67, + "headwayTime": 1.5 + }, + "route": [ + "road_400_400_W", + "road_0_400_S", + "road_0_0_E" + ], + "interval": 5.0, + "startTime": 0, + "endTime": -1 + }, + { + "vehicle": { + "length": 5.0, + "width": 2.0, + "maxPosAcc": 2.0, + "maxNegAcc": 4.5, + "usualPosAcc": 2.0, + "usualNegAcc": 4.5, + "minGap": 2.5, + "maxSpeed": 12.67, + "headwayTime": 1.5 + }, + "route": [ + "road_400_400_W", + "road_0_400_S", + "road_0_0_W" + ], + "interval": 5.0, + "startTime": 0, + "endTime": -1 + } +] \ No newline at end of file diff --git a/cityflow_config/roadnets/auto_roadnet.json b/cityflow_config/roadnets/auto_roadnet.json new file mode 100644 index 0000000..b4ea7ce --- /dev/null +++ b/cityflow_config/roadnets/auto_roadnet.json @@ -0,0 +1,2538 @@ +{ + "intersections": [ + { + "id": "intersection_-400_0", + "point": { + "x": -400, + "y": 0 + }, + "roads": [ + "road_-400_0_E", + "road_0_0_W" + ], + "roadLinks": [], + "trafficLight": { + "roadLinkIndices": [], + "lightphases": [ + { + "time": 5, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + } + ] + }, + "width": 0, + "virtual": true + }, + { + "id": "intersection_0_0", + "point": { + "x": 0, + "y": 0 + }, + "roads": [ + "road_0_400_S", + "road_400_0_W", + "road_-400_0_E", + "road_0_0_N", + "road_0_0_E", + "road_0_0_W" + ], + "roadLinks": [ + { + "type": "turn_right", + "startRoad": "road_0_400_S", + "endRoad": "road_0_0_W", + "direction": 3, + "laneLinks": [ + { + "startLaneIndex": 2, + "endLaneIndex": 0, + "points": [ + { + "x": -20.0, + "y": 50.0 + }, + { + "x": -20.389999999999997, + "y": 44.662000000000006 + }, + { + "x": -21.520000000000003, + "y": 38.816 + }, + { + "x": -23.330000000000002, + "y": 32.714 + }, + { + "x": -25.76, + "y": 26.608 + }, + { + "x": -28.75, + "y": 20.75 + }, + { + "x": -32.24000000000001, + "y": 15.391999999999996 + }, + { + "x": -36.169999999999995, + "y": 10.786000000000007 + }, + { + "x": -40.480000000000004, + "y": 7.183999999999998 + }, + { + "x": -45.11000000000001, + "y": 4.838000000000001 + }, + { + "x": -50.0, + "y": 4.0 + } + ] + }, + { + "startLaneIndex": 2, + "endLaneIndex": 1, + "points": [ + { + "x": -20.0, + "y": 50.0 + }, + { + "x": -20.389999999999997, + "y": 44.886 + }, + { + "x": -21.520000000000003, + "y": 39.648 + }, + { + "x": -23.330000000000002, + "y": 34.442 + }, + { + "x": -25.76, + "y": 29.424 + }, + { + "x": -28.75, + "y": 24.75 + }, + { + "x": -32.24000000000001, + "y": 20.575999999999997 + }, + { + "x": -36.169999999999995, + "y": 17.058000000000007 + }, + { + "x": -40.480000000000004, + "y": 14.352 + }, + { + "x": -45.11000000000001, + "y": 12.614 + }, + { + "x": -50.0, + "y": 12.0 + } + ] + }, + { + "startLaneIndex": 2, + "endLaneIndex": 2, + "points": [ + { + "x": -20.0, + "y": 50.0 + }, + { + "x": -20.389999999999997, + "y": 45.11000000000001 + }, + { + "x": -21.520000000000003, + "y": 40.480000000000004 + }, + { + "x": -23.330000000000002, + "y": 36.17 + }, + { + "x": -25.76, + "y": 32.24 + }, + { + "x": -28.75, + "y": 28.75 + }, + { + "x": -32.24000000000001, + "y": 25.759999999999998 + }, + { + "x": -36.169999999999995, + "y": 23.330000000000005 + }, + { + "x": -40.480000000000004, + "y": 21.52 + }, + { + "x": -45.11000000000001, + "y": 20.389999999999997 + }, + { + "x": -50.0, + "y": 20.0 + } + ] + } + ] + }, + { + "type": "turn_left", + "startRoad": "road_0_400_S", + "endRoad": "road_0_0_E", + "direction": 3, + "laneLinks": [ + { + "startLaneIndex": 0, + "endLaneIndex": 0, + "points": [ + { + "x": -4.0, + "y": 50.0 + }, + { + "x": -2.9379999999999997, + "y": 44.438 + }, + { + "x": 0.01600000000000068, + "y": 37.98400000000001 + }, + { + "x": 4.514000000000001, + "y": 30.986 + }, + { + "x": 10.208000000000006, + "y": 23.791999999999998 + }, + { + "x": 16.75, + "y": 16.75 + }, + { + "x": 23.792000000000005, + "y": 10.207999999999995 + }, + { + "x": 30.985999999999997, + "y": 4.514000000000008 + }, + { + "x": 37.98400000000001, + "y": 0.01599999999999735 + }, + { + "x": 44.438, + "y": -2.937999999999999 + }, + { + "x": 50.0, + "y": -4.0 + } + ] + }, + { + "startLaneIndex": 0, + "endLaneIndex": 1, + "points": [ + { + "x": -4.0, + "y": 50.0 + }, + { + "x": -2.9379999999999997, + "y": 44.214000000000006 + }, + { + "x": 0.01600000000000068, + "y": 37.15200000000001 + }, + { + "x": 4.514000000000001, + "y": 29.258000000000003 + }, + { + "x": 10.208000000000006, + "y": 20.976 + }, + { + "x": 16.75, + "y": 12.75 + }, + { + "x": 23.792000000000005, + "y": 5.023999999999994 + }, + { + "x": 30.985999999999997, + "y": -1.757999999999992 + }, + { + "x": 37.98400000000001, + "y": -7.152000000000005 + }, + { + "x": 44.438, + "y": -10.713999999999999 + }, + { + "x": 50.0, + "y": -12.0 + } + ] + }, + { + "startLaneIndex": 0, + "endLaneIndex": 2, + "points": [ + { + "x": -4.0, + "y": 50.0 + }, + { + "x": -2.9379999999999997, + "y": 43.99 + }, + { + "x": 0.01600000000000068, + "y": 36.32000000000001 + }, + { + "x": 4.514000000000001, + "y": 27.53 + }, + { + "x": 10.208000000000006, + "y": 18.159999999999997 + }, + { + "x": 16.75, + "y": 8.75 + }, + { + "x": 23.792000000000005, + "y": -0.16000000000000725 + }, + { + "x": 30.985999999999997, + "y": -8.02999999999999 + }, + { + "x": 37.98400000000001, + "y": -14.320000000000004 + }, + { + "x": 44.438, + "y": -18.49 + }, + { + "x": 50.0, + "y": -20.0 + } + ] + } + ] + }, + { + "type": "turn_right", + "startRoad": "road_400_0_W", + "endRoad": "road_0_0_N", + "direction": 2, + "laneLinks": [ + { + "startLaneIndex": 2, + "endLaneIndex": 0, + "points": [ + { + "x": 50.0, + "y": 20.0 + }, + { + "x": 44.662000000000006, + "y": 20.389999999999997 + }, + { + "x": 38.816, + "y": 21.520000000000003 + }, + { + "x": 32.714, + "y": 23.330000000000002 + }, + { + "x": 26.608, + "y": 25.76 + }, + { + "x": 20.75, + "y": 28.75 + }, + { + "x": 15.391999999999996, + "y": 32.24000000000001 + }, + { + "x": 10.786000000000007, + "y": 36.169999999999995 + }, + { + "x": 7.183999999999998, + "y": 40.480000000000004 + }, + { + "x": 4.838000000000001, + "y": 45.11000000000001 + }, + { + "x": 4.0, + "y": 50.0 + } + ] + }, + { + "startLaneIndex": 2, + "endLaneIndex": 1, + "points": [ + { + "x": 50.0, + "y": 20.0 + }, + { + "x": 44.886, + "y": 20.389999999999997 + }, + { + "x": 39.648, + "y": 21.520000000000003 + }, + { + "x": 34.442, + "y": 23.330000000000002 + }, + { + "x": 29.424, + "y": 25.76 + }, + { + "x": 24.75, + "y": 28.75 + }, + { + "x": 20.575999999999997, + "y": 32.24000000000001 + }, + { + "x": 17.058000000000007, + "y": 36.169999999999995 + }, + { + "x": 14.352, + "y": 40.480000000000004 + }, + { + "x": 12.614, + "y": 45.11000000000001 + }, + { + "x": 12.0, + "y": 50.0 + } + ] + }, + { + "startLaneIndex": 2, + "endLaneIndex": 2, + "points": [ + { + "x": 50.0, + "y": 20.0 + }, + { + "x": 45.11000000000001, + "y": 20.389999999999997 + }, + { + "x": 40.480000000000004, + "y": 21.520000000000003 + }, + { + "x": 36.17, + "y": 23.330000000000002 + }, + { + "x": 32.24, + "y": 25.76 + }, + { + "x": 28.75, + "y": 28.75 + }, + { + "x": 25.759999999999998, + "y": 32.24000000000001 + }, + { + "x": 23.330000000000005, + "y": 36.169999999999995 + }, + { + "x": 21.52, + "y": 40.480000000000004 + }, + { + "x": 20.389999999999997, + "y": 45.11000000000001 + }, + { + "x": 20.0, + "y": 50.0 + } + ] + } + ] + }, + { + "type": "go_straight", + "startRoad": "road_400_0_W", + "endRoad": "road_0_0_W", + "direction": 2, + "laneLinks": [ + { + "startLaneIndex": 1, + "endLaneIndex": 0, + "points": [ + { + "x": 50.0, + "y": 12.0 + }, + { + "x": 43.60000000000001, + "y": 11.776 + }, + { + "x": 34.800000000000004, + "y": 11.168000000000001 + }, + { + "x": 24.2, + "y": 10.272000000000002 + }, + { + "x": 12.399999999999995, + "y": 9.184 + }, + { + "x": 0.0, + "y": 8.0 + }, + { + "x": -12.40000000000001, + "y": 6.815999999999999 + }, + { + "x": -24.19999999999999, + "y": 5.728000000000001 + }, + { + "x": -34.800000000000004, + "y": 4.831999999999999 + }, + { + "x": -43.6, + "y": 4.224 + }, + { + "x": -50.0, + "y": 4.0 + } + ] + }, + { + "startLaneIndex": 1, + "endLaneIndex": 1, + "points": [ + { + "x": 50.0, + "y": 12.0 + }, + { + "x": 43.60000000000001, + "y": 12.0 + }, + { + "x": 34.800000000000004, + "y": 12.0 + }, + { + "x": 24.2, + "y": 12.000000000000002 + }, + { + "x": 12.399999999999995, + "y": 12.0 + }, + { + "x": 0.0, + "y": 12.0 + }, + { + "x": -12.40000000000001, + "y": 12.0 + }, + { + "x": -24.19999999999999, + "y": 12.0 + }, + { + "x": -34.800000000000004, + "y": 12.0 + }, + { + "x": -43.6, + "y": 12.0 + }, + { + "x": -50.0, + "y": 12.0 + } + ] + }, + { + "startLaneIndex": 1, + "endLaneIndex": 2, + "points": [ + { + "x": 50.0, + "y": 12.0 + }, + { + "x": 43.60000000000001, + "y": 12.224 + }, + { + "x": 34.800000000000004, + "y": 12.832 + }, + { + "x": 24.2, + "y": 13.728000000000002 + }, + { + "x": 12.399999999999995, + "y": 14.816 + }, + { + "x": 0.0, + "y": 16.0 + }, + { + "x": -12.40000000000001, + "y": 17.184 + }, + { + "x": -24.19999999999999, + "y": 18.272 + }, + { + "x": -34.800000000000004, + "y": 19.168 + }, + { + "x": -43.6, + "y": 19.775999999999996 + }, + { + "x": -50.0, + "y": 20.0 + } + ] + } + ] + }, + { + "type": "go_straight", + "startRoad": "road_-400_0_E", + "endRoad": "road_0_0_E", + "direction": 0, + "laneLinks": [ + { + "startLaneIndex": 1, + "endLaneIndex": 0, + "points": [ + { + "x": -50.0, + "y": -12.0 + }, + { + "x": -43.60000000000001, + "y": -11.776 + }, + { + "x": -34.800000000000004, + "y": -11.168000000000001 + }, + { + "x": -24.2, + "y": -10.272000000000002 + }, + { + "x": -12.399999999999995, + "y": -9.184 + }, + { + "x": 0.0, + "y": -8.0 + }, + { + "x": 12.40000000000001, + "y": -6.815999999999999 + }, + { + "x": 24.19999999999999, + "y": -5.728000000000001 + }, + { + "x": 34.800000000000004, + "y": -4.831999999999999 + }, + { + "x": 43.6, + "y": -4.224 + }, + { + "x": 50.0, + "y": -4.0 + } + ] + }, + { + "startLaneIndex": 1, + "endLaneIndex": 1, + "points": [ + { + "x": -50.0, + "y": -12.0 + }, + { + "x": -43.60000000000001, + "y": -12.0 + }, + { + "x": -34.800000000000004, + "y": -12.0 + }, + { + "x": -24.2, + "y": -12.000000000000002 + }, + { + "x": -12.399999999999995, + "y": -12.0 + }, + { + "x": 0.0, + "y": -12.0 + }, + { + "x": 12.40000000000001, + "y": -12.0 + }, + { + "x": 24.19999999999999, + "y": -12.0 + }, + { + "x": 34.800000000000004, + "y": -12.0 + }, + { + "x": 43.6, + "y": -12.0 + }, + { + "x": 50.0, + "y": -12.0 + } + ] + }, + { + "startLaneIndex": 1, + "endLaneIndex": 2, + "points": [ + { + "x": -50.0, + "y": -12.0 + }, + { + "x": -43.60000000000001, + "y": -12.224 + }, + { + "x": -34.800000000000004, + "y": -12.832 + }, + { + "x": -24.2, + "y": -13.728000000000002 + }, + { + "x": -12.399999999999995, + "y": -14.816 + }, + { + "x": 0.0, + "y": -16.0 + }, + { + "x": 12.40000000000001, + "y": -17.184 + }, + { + "x": 24.19999999999999, + "y": -18.272 + }, + { + "x": 34.800000000000004, + "y": -19.168 + }, + { + "x": 43.6, + "y": -19.775999999999996 + }, + { + "x": 50.0, + "y": -20.0 + } + ] + } + ] + }, + { + "type": "turn_left", + "startRoad": "road_-400_0_E", + "endRoad": "road_0_0_N", + "direction": 0, + "laneLinks": [ + { + "startLaneIndex": 0, + "endLaneIndex": 0, + "points": [ + { + "x": -50.0, + "y": -4.0 + }, + { + "x": -44.438, + "y": -2.9379999999999997 + }, + { + "x": -37.98400000000001, + "y": 0.01600000000000068 + }, + { + "x": -30.986, + "y": 4.514000000000001 + }, + { + "x": -23.791999999999998, + "y": 10.208000000000006 + }, + { + "x": -16.75, + "y": 16.75 + }, + { + "x": -10.207999999999995, + "y": 23.792000000000005 + }, + { + "x": -4.514000000000008, + "y": 30.985999999999997 + }, + { + "x": -0.01599999999999735, + "y": 37.98400000000001 + }, + { + "x": 2.937999999999999, + "y": 44.438 + }, + { + "x": 4.0, + "y": 50.0 + } + ] + }, + { + "startLaneIndex": 0, + "endLaneIndex": 1, + "points": [ + { + "x": -50.0, + "y": -4.0 + }, + { + "x": -44.214000000000006, + "y": -2.9379999999999997 + }, + { + "x": -37.15200000000001, + "y": 0.01600000000000068 + }, + { + "x": -29.258000000000003, + "y": 4.514000000000001 + }, + { + "x": -20.976, + "y": 10.208000000000006 + }, + { + "x": -12.75, + "y": 16.75 + }, + { + "x": -5.023999999999994, + "y": 23.792000000000005 + }, + { + "x": 1.757999999999992, + "y": 30.985999999999997 + }, + { + "x": 7.152000000000005, + "y": 37.98400000000001 + }, + { + "x": 10.713999999999999, + "y": 44.438 + }, + { + "x": 12.0, + "y": 50.0 + } + ] + }, + { + "startLaneIndex": 0, + "endLaneIndex": 2, + "points": [ + { + "x": -50.0, + "y": -4.0 + }, + { + "x": -43.99, + "y": -2.9379999999999997 + }, + { + "x": -36.32000000000001, + "y": 0.01600000000000068 + }, + { + "x": -27.53, + "y": 4.514000000000001 + }, + { + "x": -18.159999999999997, + "y": 10.208000000000006 + }, + { + "x": -8.75, + "y": 16.75 + }, + { + "x": 0.16000000000000725, + "y": 23.792000000000005 + }, + { + "x": 8.02999999999999, + "y": 30.985999999999997 + }, + { + "x": 14.320000000000004, + "y": 37.98400000000001 + }, + { + "x": 18.49, + "y": 44.438 + }, + { + "x": 20.0, + "y": 50.0 + } + ] + } + ] + } + ], + "trafficLight": { + "roadLinkIndices": [ + 0, + 1, + 2, + 3, + 4, + 5 + ], + "lightphases": [ + { + "time": 0.1, + "availableRoadLinks": [ + 0, + 2, + 3, + 4 + ] + }, + { + "time": 20.0, + "availableRoadLinks": [ + 0, + 2, + 5 + ] + }, + { + "time": 19.9, + "availableRoadLinks": [ + 0, + 2 + ] + }, + { + "time": 20.0, + "availableRoadLinks": [ + 0, + 1, + 2 + ] + } + ] + }, + "width": 50, + "virtual": false + }, + { + "id": "intersection_400_0", + "point": { + "x": 400, + "y": 0 + }, + "roads": [ + "road_400_0_W", + "road_0_0_E" + ], + "roadLinks": [], + "trafficLight": { + "roadLinkIndices": [], + "lightphases": [ + { + "time": 5, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + } + ] + }, + "width": 0, + "virtual": true + }, + { + "id": "intersection_-400_400", + "point": { + "x": -400, + "y": 400 + }, + "roads": [ + "road_-400_400_E", + "road_0_400_W" + ], + "roadLinks": [], + "trafficLight": { + "roadLinkIndices": [], + "lightphases": [ + { + "time": 5, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + } + ] + }, + "width": 0, + "virtual": true + }, + { + "id": "intersection_0_400", + "point": { + "x": 0, + "y": 400 + }, + "roads": [ + "road_400_400_W", + "road_-400_400_E", + "road_0_0_N", + "road_0_400_E", + "road_0_400_W", + "road_0_400_S" + ], + "roadLinks": [ + { + "type": "go_straight", + "startRoad": "road_400_400_W", + "endRoad": "road_0_400_W", + "direction": 2, + "laneLinks": [ + { + "startLaneIndex": 1, + "endLaneIndex": 0, + "points": [ + { + "x": 50.0, + "y": 412.0 + }, + { + "x": 43.60000000000001, + "y": 411.776 + }, + { + "x": 34.800000000000004, + "y": 411.168 + }, + { + "x": 24.2, + "y": 410.27200000000005 + }, + { + "x": 12.399999999999995, + "y": 409.18399999999997 + }, + { + "x": 0.0, + "y": 408.0 + }, + { + "x": -12.40000000000001, + "y": 406.816 + }, + { + "x": -24.19999999999999, + "y": 405.728 + }, + { + "x": -34.800000000000004, + "y": 404.832 + }, + { + "x": -43.6, + "y": 404.224 + }, + { + "x": -50.0, + "y": 404.0 + } + ] + }, + { + "startLaneIndex": 1, + "endLaneIndex": 1, + "points": [ + { + "x": 50.0, + "y": 412.0 + }, + { + "x": 43.60000000000001, + "y": 412.0 + }, + { + "x": 34.800000000000004, + "y": 412.0 + }, + { + "x": 24.2, + "y": 412.00000000000006 + }, + { + "x": 12.399999999999995, + "y": 412.0 + }, + { + "x": 0.0, + "y": 412.0 + }, + { + "x": -12.40000000000001, + "y": 412.0 + }, + { + "x": -24.19999999999999, + "y": 412.0 + }, + { + "x": -34.800000000000004, + "y": 412.0 + }, + { + "x": -43.6, + "y": 412.0 + }, + { + "x": -50.0, + "y": 412.0 + } + ] + }, + { + "startLaneIndex": 1, + "endLaneIndex": 2, + "points": [ + { + "x": 50.0, + "y": 412.0 + }, + { + "x": 43.60000000000001, + "y": 412.224 + }, + { + "x": 34.800000000000004, + "y": 412.832 + }, + { + "x": 24.2, + "y": 413.72800000000007 + }, + { + "x": 12.399999999999995, + "y": 414.816 + }, + { + "x": 0.0, + "y": 416.0 + }, + { + "x": -12.40000000000001, + "y": 417.184 + }, + { + "x": -24.19999999999999, + "y": 418.272 + }, + { + "x": -34.800000000000004, + "y": 419.168 + }, + { + "x": -43.6, + "y": 419.776 + }, + { + "x": -50.0, + "y": 420.0 + } + ] + } + ] + }, + { + "type": "turn_left", + "startRoad": "road_400_400_W", + "endRoad": "road_0_400_S", + "direction": 2, + "laneLinks": [ + { + "startLaneIndex": 0, + "endLaneIndex": 0, + "points": [ + { + "x": 50.0, + "y": 404.0 + }, + { + "x": 44.438, + "y": 402.938 + }, + { + "x": 37.98400000000001, + "y": 399.98400000000004 + }, + { + "x": 30.986, + "y": 395.486 + }, + { + "x": 23.791999999999998, + "y": 389.79200000000003 + }, + { + "x": 16.75, + "y": 383.25 + }, + { + "x": 10.207999999999995, + "y": 376.20799999999997 + }, + { + "x": 4.514000000000008, + "y": 369.014 + }, + { + "x": 0.01599999999999735, + "y": 362.01599999999996 + }, + { + "x": -2.937999999999999, + "y": 355.562 + }, + { + "x": -4.0, + "y": 350.0 + } + ] + }, + { + "startLaneIndex": 0, + "endLaneIndex": 1, + "points": [ + { + "x": 50.0, + "y": 404.0 + }, + { + "x": 44.214000000000006, + "y": 402.938 + }, + { + "x": 37.15200000000001, + "y": 399.98400000000004 + }, + { + "x": 29.258000000000003, + "y": 395.486 + }, + { + "x": 20.976, + "y": 389.79200000000003 + }, + { + "x": 12.75, + "y": 383.25 + }, + { + "x": 5.023999999999994, + "y": 376.20799999999997 + }, + { + "x": -1.757999999999992, + "y": 369.014 + }, + { + "x": -7.152000000000005, + "y": 362.01599999999996 + }, + { + "x": -10.713999999999999, + "y": 355.562 + }, + { + "x": -12.0, + "y": 350.0 + } + ] + }, + { + "startLaneIndex": 0, + "endLaneIndex": 2, + "points": [ + { + "x": 50.0, + "y": 404.0 + }, + { + "x": 43.99, + "y": 402.938 + }, + { + "x": 36.32000000000001, + "y": 399.98400000000004 + }, + { + "x": 27.53, + "y": 395.486 + }, + { + "x": 18.159999999999997, + "y": 389.79200000000003 + }, + { + "x": 8.75, + "y": 383.25 + }, + { + "x": -0.16000000000000725, + "y": 376.20799999999997 + }, + { + "x": -8.02999999999999, + "y": 369.014 + }, + { + "x": -14.320000000000004, + "y": 362.01599999999996 + }, + { + "x": -18.49, + "y": 355.562 + }, + { + "x": -20.0, + "y": 350.0 + } + ] + } + ] + }, + { + "type": "turn_right", + "startRoad": "road_-400_400_E", + "endRoad": "road_0_400_S", + "direction": 0, + "laneLinks": [ + { + "startLaneIndex": 2, + "endLaneIndex": 0, + "points": [ + { + "x": -50.0, + "y": 380.0 + }, + { + "x": -44.662000000000006, + "y": 379.61 + }, + { + "x": -38.816, + "y": 378.48 + }, + { + "x": -32.714, + "y": 376.67 + }, + { + "x": -26.608, + "y": 374.24 + }, + { + "x": -20.75, + "y": 371.25 + }, + { + "x": -15.391999999999996, + "y": 367.76 + }, + { + "x": -10.786000000000007, + "y": 363.83000000000004 + }, + { + "x": -7.183999999999998, + "y": 359.52 + }, + { + "x": -4.838000000000001, + "y": 354.89 + }, + { + "x": -4.0, + "y": 350.0 + } + ] + }, + { + "startLaneIndex": 2, + "endLaneIndex": 1, + "points": [ + { + "x": -50.0, + "y": 380.0 + }, + { + "x": -44.886, + "y": 379.61 + }, + { + "x": -39.648, + "y": 378.48 + }, + { + "x": -34.442, + "y": 376.67 + }, + { + "x": -29.424, + "y": 374.24 + }, + { + "x": -24.75, + "y": 371.25 + }, + { + "x": -20.575999999999997, + "y": 367.76 + }, + { + "x": -17.058000000000007, + "y": 363.83000000000004 + }, + { + "x": -14.352, + "y": 359.52 + }, + { + "x": -12.614, + "y": 354.89 + }, + { + "x": -12.0, + "y": 350.0 + } + ] + }, + { + "startLaneIndex": 2, + "endLaneIndex": 2, + "points": [ + { + "x": -50.0, + "y": 380.0 + }, + { + "x": -45.11000000000001, + "y": 379.61 + }, + { + "x": -40.480000000000004, + "y": 378.48 + }, + { + "x": -36.17, + "y": 376.67 + }, + { + "x": -32.24, + "y": 374.24 + }, + { + "x": -28.75, + "y": 371.25 + }, + { + "x": -25.759999999999998, + "y": 367.76 + }, + { + "x": -23.330000000000005, + "y": 363.83000000000004 + }, + { + "x": -21.52, + "y": 359.52 + }, + { + "x": -20.389999999999997, + "y": 354.89 + }, + { + "x": -20.0, + "y": 350.0 + } + ] + } + ] + }, + { + "type": "go_straight", + "startRoad": "road_-400_400_E", + "endRoad": "road_0_400_E", + "direction": 0, + "laneLinks": [ + { + "startLaneIndex": 1, + "endLaneIndex": 0, + "points": [ + { + "x": -50.0, + "y": 388.0 + }, + { + "x": -43.60000000000001, + "y": 388.224 + }, + { + "x": -34.800000000000004, + "y": 388.83200000000005 + }, + { + "x": -24.2, + "y": 389.728 + }, + { + "x": -12.399999999999995, + "y": 390.81600000000003 + }, + { + "x": 0.0, + "y": 392.0 + }, + { + "x": 12.40000000000001, + "y": 393.18399999999997 + }, + { + "x": 24.19999999999999, + "y": 394.272 + }, + { + "x": 34.800000000000004, + "y": 395.168 + }, + { + "x": 43.6, + "y": 395.776 + }, + { + "x": 50.0, + "y": 396.0 + } + ] + }, + { + "startLaneIndex": 1, + "endLaneIndex": 1, + "points": [ + { + "x": -50.0, + "y": 388.0 + }, + { + "x": -43.60000000000001, + "y": 387.99999999999994 + }, + { + "x": -34.800000000000004, + "y": 388.00000000000006 + }, + { + "x": -24.2, + "y": 388.0 + }, + { + "x": -12.399999999999995, + "y": 388.0 + }, + { + "x": 0.0, + "y": 388.0 + }, + { + "x": 12.40000000000001, + "y": 388.0 + }, + { + "x": 24.19999999999999, + "y": 388.0 + }, + { + "x": 34.800000000000004, + "y": 388.0 + }, + { + "x": 43.6, + "y": 388.0 + }, + { + "x": 50.0, + "y": 388.0 + } + ] + }, + { + "startLaneIndex": 1, + "endLaneIndex": 2, + "points": [ + { + "x": -50.0, + "y": 388.0 + }, + { + "x": -43.60000000000001, + "y": 387.77599999999995 + }, + { + "x": -34.800000000000004, + "y": 387.168 + }, + { + "x": -24.2, + "y": 386.27200000000005 + }, + { + "x": -12.399999999999995, + "y": 385.184 + }, + { + "x": 0.0, + "y": 384.0 + }, + { + "x": 12.40000000000001, + "y": 382.816 + }, + { + "x": 24.19999999999999, + "y": 381.728 + }, + { + "x": 34.800000000000004, + "y": 380.832 + }, + { + "x": 43.6, + "y": 380.22400000000005 + }, + { + "x": 50.0, + "y": 380.0 + } + ] + } + ] + }, + { + "type": "turn_right", + "startRoad": "road_0_0_N", + "endRoad": "road_0_400_E", + "direction": 1, + "laneLinks": [ + { + "startLaneIndex": 2, + "endLaneIndex": 0, + "points": [ + { + "x": 20.0, + "y": 350.0 + }, + { + "x": 20.389999999999997, + "y": 355.338 + }, + { + "x": 21.520000000000003, + "y": 361.184 + }, + { + "x": 23.330000000000002, + "y": 367.28600000000006 + }, + { + "x": 25.76, + "y": 373.39199999999994 + }, + { + "x": 28.75, + "y": 379.25 + }, + { + "x": 32.24000000000001, + "y": 384.608 + }, + { + "x": 36.169999999999995, + "y": 389.21399999999994 + }, + { + "x": 40.480000000000004, + "y": 392.816 + }, + { + "x": 45.11000000000001, + "y": 395.162 + }, + { + "x": 50.0, + "y": 396.0 + } + ] + }, + { + "startLaneIndex": 2, + "endLaneIndex": 1, + "points": [ + { + "x": 20.0, + "y": 350.0 + }, + { + "x": 20.389999999999997, + "y": 355.114 + }, + { + "x": 21.520000000000003, + "y": 360.35200000000003 + }, + { + "x": 23.330000000000002, + "y": 365.55800000000005 + }, + { + "x": 25.76, + "y": 370.57599999999996 + }, + { + "x": 28.75, + "y": 375.25 + }, + { + "x": 32.24000000000001, + "y": 379.42400000000004 + }, + { + "x": 36.169999999999995, + "y": 382.94199999999995 + }, + { + "x": 40.480000000000004, + "y": 385.64799999999997 + }, + { + "x": 45.11000000000001, + "y": 387.38599999999997 + }, + { + "x": 50.0, + "y": 388.0 + } + ] + }, + { + "startLaneIndex": 2, + "endLaneIndex": 2, + "points": [ + { + "x": 20.0, + "y": 350.0 + }, + { + "x": 20.389999999999997, + "y": 354.89 + }, + { + "x": 21.520000000000003, + "y": 359.52 + }, + { + "x": 23.330000000000002, + "y": 363.83000000000004 + }, + { + "x": 25.76, + "y": 367.76 + }, + { + "x": 28.75, + "y": 371.25 + }, + { + "x": 32.24000000000001, + "y": 374.24 + }, + { + "x": 36.169999999999995, + "y": 376.66999999999996 + }, + { + "x": 40.480000000000004, + "y": 378.48 + }, + { + "x": 45.11000000000001, + "y": 379.61 + }, + { + "x": 50.0, + "y": 380.0 + } + ] + } + ] + }, + { + "type": "turn_left", + "startRoad": "road_0_0_N", + "endRoad": "road_0_400_W", + "direction": 1, + "laneLinks": [ + { + "startLaneIndex": 0, + "endLaneIndex": 0, + "points": [ + { + "x": 4.0, + "y": 350.0 + }, + { + "x": 2.9379999999999997, + "y": 355.562 + }, + { + "x": -0.01600000000000068, + "y": 362.016 + }, + { + "x": -4.514000000000001, + "y": 369.01400000000007 + }, + { + "x": -10.208000000000006, + "y": 376.20799999999997 + }, + { + "x": -16.75, + "y": 383.25 + }, + { + "x": -23.792000000000005, + "y": 389.792 + }, + { + "x": -30.985999999999997, + "y": 395.486 + }, + { + "x": -37.98400000000001, + "y": 399.984 + }, + { + "x": -44.438, + "y": 402.938 + }, + { + "x": -50.0, + "y": 404.0 + } + ] + }, + { + "startLaneIndex": 0, + "endLaneIndex": 1, + "points": [ + { + "x": 4.0, + "y": 350.0 + }, + { + "x": 2.9379999999999997, + "y": 355.786 + }, + { + "x": -0.01600000000000068, + "y": 362.848 + }, + { + "x": -4.514000000000001, + "y": 370.7420000000001 + }, + { + "x": -10.208000000000006, + "y": 379.024 + }, + { + "x": -16.75, + "y": 387.25 + }, + { + "x": -23.792000000000005, + "y": 394.976 + }, + { + "x": -30.985999999999997, + "y": 401.758 + }, + { + "x": -37.98400000000001, + "y": 407.152 + }, + { + "x": -44.438, + "y": 410.714 + }, + { + "x": -50.0, + "y": 412.0 + } + ] + }, + { + "startLaneIndex": 0, + "endLaneIndex": 2, + "points": [ + { + "x": 4.0, + "y": 350.0 + }, + { + "x": 2.9379999999999997, + "y": 356.01 + }, + { + "x": -0.01600000000000068, + "y": 363.68 + }, + { + "x": -4.514000000000001, + "y": 372.4700000000001 + }, + { + "x": -10.208000000000006, + "y": 381.84 + }, + { + "x": -16.75, + "y": 391.25 + }, + { + "x": -23.792000000000005, + "y": 400.16 + }, + { + "x": -30.985999999999997, + "y": 408.03 + }, + { + "x": -37.98400000000001, + "y": 414.32 + }, + { + "x": -44.438, + "y": 418.49 + }, + { + "x": -50.0, + "y": 420.0 + } + ] + } + ] + } + ], + "trafficLight": { + "roadLinkIndices": [ + 0, + 1, + 2, + 3, + 4, + 5 + ], + "lightphases": [ + { + "time": 20.0, + "availableRoadLinks": [ + 0, + 2, + 3, + 4 + ] + }, + { + "time": 20.0, + "availableRoadLinks": [ + 1, + 2, + 4 + ] + }, + { + "time": 19.9, + "availableRoadLinks": [ + 2, + 4 + ] + }, + { + "time": 0.1, + "availableRoadLinks": [ + 2, + 4, + 5 + ] + } + ] + }, + "width": 50, + "virtual": false + }, + { + "id": "intersection_400_400", + "point": { + "x": 400, + "y": 400 + }, + "roads": [ + "road_400_400_W", + "road_0_400_E" + ], + "roadLinks": [], + "trafficLight": { + "roadLinkIndices": [], + "lightphases": [ + { + "time": 5, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + }, + { + "time": 30, + "availableRoadLinks": [] + } + ] + }, + "width": 0, + "virtual": true + } + ], + "roads": [ + { + "id": "road_-400_0_E", + "points": [ + { + "x": -400, + "y": 0 + }, + { + "x": 0, + "y": 0 + } + ], + "lanes": [ + { + "width": 8, + "maxSpeed": 20 + }, + { + "width": 8, + "maxSpeed": 20 + }, + { + "width": 8, + "maxSpeed": 20 + } + ], + "startIntersection": "intersection_-400_0", + "endIntersection": "intersection_0_0" + }, + { + "id": "road_0_0_N", + "points": [ + { + "x": 0, + "y": 0 + }, + { + "x": 0, + "y": 400 + } + ], + "lanes": [ + { + "width": 8, + "maxSpeed": 20 + }, + { + "width": 8, + "maxSpeed": 20 + }, + { + "width": 8, + "maxSpeed": 20 + } + ], + "startIntersection": "intersection_0_0", + "endIntersection": "intersection_0_400" + }, + { + "id": "road_0_0_E", + "points": [ + { + "x": 0, + "y": 0 + }, + { + "x": 400, + "y": 0 + } + ], + "lanes": [ + { + "width": 8, + "maxSpeed": 20 + }, + { + "width": 8, + "maxSpeed": 20 + }, + { + "width": 8, + "maxSpeed": 20 + } + ], + "startIntersection": "intersection_0_0", + "endIntersection": "intersection_400_0" + }, + { + "id": "road_0_0_W", + "points": [ + { + "x": 0, + "y": 0 + }, + { + "x": -400, + "y": 0 + } + ], + "lanes": [ + { + "width": 8, + "maxSpeed": 20 + }, + { + "width": 8, + "maxSpeed": 20 + }, + { + "width": 8, + "maxSpeed": 20 + } + ], + "startIntersection": "intersection_0_0", + "endIntersection": "intersection_-400_0" + }, + { + "id": "road_400_0_W", + "points": [ + { + "x": 400, + "y": 0 + }, + { + "x": 0, + "y": 0 + } + ], + "lanes": [ + { + "width": 8, + "maxSpeed": 20 + }, + { + "width": 8, + "maxSpeed": 20 + }, + { + "width": 8, + "maxSpeed": 20 + } + ], + "startIntersection": "intersection_400_0", + "endIntersection": "intersection_0_0" + }, + { + "id": "road_-400_400_E", + "points": [ + { + "x": -400, + "y": 400 + }, + { + "x": 0, + "y": 400 + } + ], + "lanes": [ + { + "width": 8, + "maxSpeed": 20 + }, + { + "width": 8, + "maxSpeed": 20 + }, + { + "width": 8, + "maxSpeed": 20 + } + ], + "startIntersection": "intersection_-400_400", + "endIntersection": "intersection_0_400" + }, + { + "id": "road_0_400_E", + "points": [ + { + "x": 0, + "y": 400 + }, + { + "x": 400, + "y": 400 + } + ], + "lanes": [ + { + "width": 8, + "maxSpeed": 20 + }, + { + "width": 8, + "maxSpeed": 20 + }, + { + "width": 8, + "maxSpeed": 20 + } + ], + "startIntersection": "intersection_0_400", + "endIntersection": "intersection_400_400" + }, + { + "id": "road_0_400_W", + "points": [ + { + "x": 0, + "y": 400 + }, + { + "x": -400, + "y": 400 + } + ], + "lanes": [ + { + "width": 8, + "maxSpeed": 20 + }, + { + "width": 8, + "maxSpeed": 20 + }, + { + "width": 8, + "maxSpeed": 20 + } + ], + "startIntersection": "intersection_0_400", + "endIntersection": "intersection_-400_400" + }, + { + "id": "road_0_400_S", + "points": [ + { + "x": 0, + "y": 400 + }, + { + "x": 0, + "y": 0 + } + ], + "lanes": [ + { + "width": 8, + "maxSpeed": 20 + }, + { + "width": 8, + "maxSpeed": 20 + }, + { + "width": 8, + "maxSpeed": 20 + } + ], + "startIntersection": "intersection_0_400", + "endIntersection": "intersection_0_0" + }, + { + "id": "road_400_400_W", + "points": [ + { + "x": 400, + "y": 400 + }, + { + "x": 0, + "y": 400 + } + ], + "lanes": [ + { + "width": 8, + "maxSpeed": 20 + }, + { + "width": 8, + "maxSpeed": 20 + }, + { + "width": 8, + "maxSpeed": 20 + } + ], + "startIntersection": "intersection_400_400", + "endIntersection": "intersection_0_400" + } + ] +} \ No newline at end of file diff --git a/emulation/emulator.py b/emulation/emulator.py index e82a8b3..c595257 100644 --- a/emulation/emulator.py +++ b/emulation/emulator.py @@ -1,10 +1,12 @@ from typing import Tuple, Optional import numpy as np +import pandas import scipy -from emukit.core import ContinuousParameter +from emukit.core import ContinuousParameter, ParameterSpace from emukit.core.loop.user_function import UserFunctionWrapper from emukit.examples.gp_bayesian_optimization.single_objective_bayesian_optimization import GPBayesianOptimization +from emukit.sensitivity.monte_carlo import MonteCarloSensitivity from emulation.simulator import Simulator from emulation.utils import results_to_df @@ -64,7 +66,24 @@ def bayes_opt(self, metric, interval: Tuple[float, float], iterations: int): x = np.stack(x, axis=0) raw_metric = np.concatenate(raw_metric) - return results_to_df(x, raw_metric, metric().name, self._time_period) + return results_to_df(x, self._time_period, raw_metric, metric().name), bo_loop.model + + def sensitivity(self, bo_model, interval: Tuple[float, float], num_mc: int = 10000): + + parameter_list = [ContinuousParameter(f"x{i}", *interval) for i in range(self._num_params)] + + senstivity = MonteCarloSensitivity(model=bo_model, input_domain=ParameterSpace(parameter_list)) + main_effects, total_effects, _ = senstivity.compute_effects(num_monte_carlo_points=num_mc) + + # converting from dict into arrays for results_df function + main_effects = np.fromiter(main_effects.values(), dtype=float) + main_effects = np.reshape(main_effects, (1, len(main_effects))) + + total_effects = np.fromiter(total_effects.values(), dtype=float) + total_effects = np.reshape(total_effects, (1, len(total_effects))) + + return results_to_df(main_effects, self._time_period), \ + results_to_df(total_effects, self._time_period) def grid_search_opt(self, metric, interval: Tuple[float, float], steps_per_axis: int): """Evaluates target_function on all combinations of parameters taken from the same interval""" @@ -84,4 +103,4 @@ def grid_search_opt(self, metric, interval: Tuple[float, float], steps_per_axis: results = results.flatten() grid = np.moveaxis(grid, 0, self._num_params).reshape(-1, self._num_params) - return results_to_df(grid, results, metric().name, self._time_period) + return results_to_df(grid, self._time_period, results, metric().name) diff --git a/metrics.py b/emulation/metrics.py similarity index 98% rename from metrics.py rename to emulation/metrics.py index 6eeae43..fb32ce1 100644 --- a/metrics.py +++ b/emulation/metrics.py @@ -65,7 +65,7 @@ def update(self, eng): # find the waiting vehicles waiting = 0 - for i, v in enumerate(current_vehicles): + for v in current_vehicles: # get vehicle info from engine info = eng.get_vehicle_info(v) diff --git a/emulation/utils.py b/emulation/utils.py index 66bfec9..07e4dfc 100644 --- a/emulation/utils.py +++ b/emulation/utils.py @@ -1,10 +1,10 @@ -from typing import Optional +from typing import List, Optional import numpy as np import pandas -def results_to_df(x, y, metric_name: str, time_period: Optional[float]): +def results_to_df(x, time_period: Optional[float], y: Optional[List[float]] = None, metric_name: Optional[str] = None): """ Parameters ---------- @@ -29,7 +29,8 @@ def results_to_df(x, y, metric_name: str, time_period: Optional[float]): node, phase = i // 4, i % 4 d[f"x_{node}_{phase}"] = np.array(x[:, i]) - d[metric_name] = np.array(y).flatten() + if y is not None: + d[metric_name] = np.array(y).flatten() df = pandas.DataFrame(data=d) return df diff --git a/main.py b/main.py index 6c37e35..c8fd217 100644 --- a/main.py +++ b/main.py @@ -4,8 +4,8 @@ from simulation_builder.flows import CustomEndpointFlowStrategy, FlowStrategy from simulation_builder.graph import Graph, I_graph -from metrics import CompletedJourneysMetric -from metrics import WaitTimeMetric +from emulation.metrics import CompletedJourneysMetric +from emulation.metrics import WaitTimeMetric if __name__ == "__main__": np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) @@ -29,8 +29,17 @@ e = Emulator(g, strategy, fixed_time_period=60) - print(e.bayes_opt(CompletedJourneysMetric, interval=(0.1, 20), iterations=5)) - print(e.bayes_opt(WaitTimeMetric, interval=(0.1, 20), iterations=5)) + results, bo_model = e.bayes_opt(CompletedJourneysMetric, interval=(0.1, 20), iterations=5) + print(results) + main_effects, total_effects = e.sensitivity(bo_model, interval=(0.1, 20)) + print('\nSENSITIVITY ANALYSIS\n', 'Main Effects\n', main_effects) + print('Total Effects\n', total_effects, '\n') + + results, bo_model = e.bayes_opt(WaitTimeMetric, interval=(0.1, 20), iterations=5) + print(results) + main_effects, total_effects = e.sensitivity(bo_model, interval=(0.1, 20)) + print('\nSENSITIVITY ANALYSIS\n', 'Main Effects\n', main_effects) + print('Total Effects\n', total_effects, '\n') print(e.grid_search_opt(CompletedJourneysMetric, interval=(0.1, 20), steps_per_axis=2)) print(e.grid_search_opt(WaitTimeMetric, interval=(0.1, 20), steps_per_axis=2)) From afdcfb34e5ae406abbf1c4afb6ca35714fb8d7ad Mon Sep 17 00:00:00 2001 From: char-tan Date: Sun, 8 Jan 2023 21:47:16 +0000 Subject: [PATCH 06/38] scenarios file, single intersec balanced scenario --- main.py | 23 +++-------------- simulation_builder/scenarios.py | 45 +++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 20 deletions(-) create mode 100644 simulation_builder/scenarios.py diff --git a/main.py b/main.py index 6c37e35..709eb38 100644 --- a/main.py +++ b/main.py @@ -1,31 +1,14 @@ import numpy as np from emulation.emulator import Emulator -from simulation_builder.flows import CustomEndpointFlowStrategy, FlowStrategy -from simulation_builder.graph import Graph, I_graph +from metrics import CompletedJourneysMetric, WaitTimeMetric -from metrics import CompletedJourneysMetric -from metrics import WaitTimeMetric +from simulation_builder.scenarios import single_intersec_bal if __name__ == "__main__": np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) - # g = Graph([(0, -400), (0, 0), (0, 400), (-400, 0), (400, 0)], - # [((0, -400), (0, 0)), ((0, 400), (0, 0)), ((-400, 0), (0, 0)), ((400, 0), (0, 0))]) - # - # - # - # strategy = CustomEndpointFlowStrategy(start_flows={(0, -400): 1, - # (0, 400): 240, - # (-400, 0): 240, - # (400, 0): 240}, - # end_flows={(0, -400): 240, - # (0, 400): 1, - # (-400, 0): 240, - # (400, 0): 240}) - - g = I_graph() - strategy = FlowStrategy() + g, strategy = single_intersec_bal() e = Emulator(g, strategy, fixed_time_period=60) diff --git a/simulation_builder/scenarios.py b/simulation_builder/scenarios.py new file mode 100644 index 0000000..805af3e --- /dev/null +++ b/simulation_builder/scenarios.py @@ -0,0 +1,45 @@ +from simulation_builder.graph import Graph +from simulation_builder.flows import CustomEndpointFlowStrategy + + +def single_intersec_bal() -> tuple: + return single_intersec_g(), single_intersec_f_bal() + + +def single_intersec_g() -> Graph: + + vertices = [ + (0, -400), + (0, 0), + (0, 400), + (-400, 0), + (400, 0), + ] + + edges = [ + ((0, -400), (0, 0)), + ((0, 400), (0, 0)), + ((-400, 0), (0, 0)), + ((400, 0), (0, 0)), + ] + + return Graph(vertices=vertices, edges=edges) + + +def single_intersec_f_bal() -> CustomEndpointFlowStrategy: + + start_flows={ + (0, -400): 20, + (0, 400): 20, + (-400, 0): 20, + (400, 0): 20, + } + + end_flows={ + (0, -400): 20, + (0, 400): 20, + (-400, 0): 20, + (400, 0): 20, + } + + return CustomEndpointFlowStrategy(start_flows=start_flows, end_flows=end_flows) From f528d82afbf700d0f88b5b20edaeee273c8e9a0b Mon Sep 17 00:00:00 2001 From: yu202147657 <91469100+yu202147657@users.noreply.github.com> Date: Sun, 8 Jan 2023 22:59:19 +0000 Subject: [PATCH 07/38] adding double intersection (balanced) into scenarios --- simulation_builder/scenarios.py | 53 +++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/simulation_builder/scenarios.py b/simulation_builder/scenarios.py index 805af3e..c361fcb 100644 --- a/simulation_builder/scenarios.py +++ b/simulation_builder/scenarios.py @@ -43,3 +43,56 @@ def single_intersec_f_bal() -> CustomEndpointFlowStrategy: } return CustomEndpointFlowStrategy(start_flows=start_flows, end_flows=end_flows) + + +def double_intersec_bal() -> tuple: + return double_intersec_g(), double_intersec_f_bal() + + +def double_intersec_g() -> Graph: + vertices = [ + (-400, 0), + (0, 0), + (400, 0), + (800, 0), + (0, -400), + (400, -400), + (0, 400), + (400, 400), + ] + + + edges = [ + ((-400, 0), (0, 0)), + ((0, 0), (400, 0)), + ((400, 0), (800, 0)), + ((0, 0), (0, 400)), + ((400, 0), (400, 400)), + ((0, 0), (0, -400)), + ((400, 0), (400, -400)) + ] + + return Graph(vertices=vertices, edges=edges) + + +def double_intersec_f_bal() -> CustomEndpointFlowStrategy: + + start_flows={ + (0, 400): 20, + (400, 400): 20, + (800, 0): 20, + (0, -400): 20, + (400, -400): 20, + (-400, 0): 20, + } + + end_flows={ + (0, 400): 20, + (400, 400): 20, + (800, 0): 20, + (0, -400): 20, + (400, -400): 20, + (-400, 0): 20, + } + + return CustomEndpointFlowStrategy(start_flows=start_flows, end_flows=end_flows) From 835113137f7e5cea4b3aaff0793e3cdb01bf382b Mon Sep 17 00:00:00 2001 From: char-tan Date: Sun, 8 Jan 2023 23:21:24 +0000 Subject: [PATCH 08/38] add single lop flow and change single bal flow --- main.py | 3 ++- simulation_builder/scenarios.py | 39 ++++++++++++++++++++++++++------- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/main.py b/main.py index 5eeea76..889e6b5 100644 --- a/main.py +++ b/main.py @@ -2,7 +2,8 @@ from emulation.emulator import Emulator from emulation.metrics import CompletedJourneysMetric, WaitTimeMetric -from simulation_builder.scenarios import single_intersec_bal +from simulation_builder.scenarios import single_intersec_bal, single_intersec_lop + if __name__ == "__main__": np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) diff --git a/simulation_builder/scenarios.py b/simulation_builder/scenarios.py index c361fcb..8f1a656 100644 --- a/simulation_builder/scenarios.py +++ b/simulation_builder/scenarios.py @@ -6,6 +6,10 @@ def single_intersec_bal() -> tuple: return single_intersec_g(), single_intersec_f_bal() +def single_intersec_lop() -> tuple: + return single_intersec_g(), single_intersec_f_lop() + + def single_intersec_g() -> Graph: vertices = [ @@ -29,17 +33,36 @@ def single_intersec_g() -> Graph: def single_intersec_f_bal() -> CustomEndpointFlowStrategy: start_flows={ - (0, -400): 20, - (0, 400): 20, - (-400, 0): 20, - (400, 0): 20, + (0, -400): 10, + (0, 400): 10, + (-400, 0): 10, + (400, 0): 10, } end_flows={ - (0, -400): 20, - (0, 400): 20, - (-400, 0): 20, - (400, 0): 20, + (0, -400): 10, + (0, 400): 10, + (-400, 0): 10, + (400, 0): 10, + } + + return CustomEndpointFlowStrategy(start_flows=start_flows, end_flows=end_flows) + + +def single_intersec_f_lop() -> CustomEndpointFlowStrategy: + + start_flows={ + (0, -400): 1, + (0, 400): 60, + (-400, 0): 60, + (400, 0): 60, + } + + end_flows={ + (0, -400): 60, + (0, 400): 1, + (-400, 0): 60, + (400, 0): 60, } return CustomEndpointFlowStrategy(start_flows=start_flows, end_flows=end_flows) From c0ac6911a41c9347364654c01d98acd8b35b3d2e Mon Sep 17 00:00:00 2001 From: char-tan Date: Mon, 9 Jan 2023 15:01:58 +0000 Subject: [PATCH 09/38] make more verbose --- emulation/emulator.py | 4 ++++ emulation/simulator.py | 4 ++++ main.py | 27 ++++++++++++++++----------- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/emulation/emulator.py b/emulation/emulator.py index c595257..45b74f4 100644 --- a/emulation/emulator.py +++ b/emulation/emulator.py @@ -32,6 +32,8 @@ def __init__(self, graph: Graph, flow_strategy: FlowStrategy, simulation_iterati def bayes_opt(self, metric, interval: Tuple[float, float], iterations: int): + print(f'\nbayesian optimisation on {metric().name}, interval {interval} for {iterations} iterations') + np.random.seed(42) sim = Simulator(self._g, metric, self._strategy, self._time_period, self._sim_iterations) @@ -87,6 +89,8 @@ def sensitivity(self, bo_model, interval: Tuple[float, float], num_mc: int = 100 def grid_search_opt(self, metric, interval: Tuple[float, float], steps_per_axis: int): """Evaluates target_function on all combinations of parameters taken from the same interval""" + + print(f'\ngrid search on {metric().name}, interval {interval} with {steps_per_axis**self._num_params} grid points') np.random.seed(42) sim = Simulator(self._g, metric, self._strategy, self._time_period, self._sim_iterations) diff --git a/emulation/simulator.py b/emulation/simulator.py index e06dd7e..16a2a5f 100644 --- a/emulation/simulator.py +++ b/emulation/simulator.py @@ -29,6 +29,7 @@ def __init__(self, g: Graph, metric, strategy=None, timing_period: Optional[int] self.strategy = FlowStrategy() if strategy is None else strategy self.timing_period = timing_period self.steps = steps + self.simulation_num = 0 self.intersections = list(sorted([v for v in self.g if len(g[v]) > 2])) @@ -48,6 +49,9 @@ def evaluate(self, x): The resulting aggregate metric calculated after N simulation iterations, with traffic light timings x. """ + print(f'simulation: {self.simulation_num}') + self.simulation_num += 1 + # Infer missing parameters if fixed timing period is specified x = np.array(np.array_split(x.flatten(), len(self.intersections))) if self.timing_period is not None: diff --git a/main.py b/main.py index 889e6b5..c105847 100644 --- a/main.py +++ b/main.py @@ -12,17 +12,22 @@ e = Emulator(g, strategy) - results, bo_model = e.bayes_opt(CompletedJourneysMetric, interval=(0.1, 20), iterations=5) - print(results) - main_effects, total_effects = e.sensitivity(bo_model, interval=(0.1, 20)) - print('\nSENSITIVITY ANALYSIS\n', 'Main Effects\n', main_effects) - print('Total Effects\n', total_effects, '\n') + interval = (0.1, 20) - results, bo_model = e.bayes_opt(WaitTimeMetric, interval=(0.1, 20), iterations=5) + results, bo_model = e.bayes_opt(CompletedJourneysMetric, interval=interval, iterations=5) print(results) - main_effects, total_effects = e.sensitivity(bo_model, interval=(0.1, 20)) - print('\nSENSITIVITY ANALYSIS\n', 'Main Effects\n', main_effects) - print('Total Effects\n', total_effects, '\n') + print(e.grid_search_opt(WaitTimeMetric, interval=interval, steps_per_axis=2)) + + + # main_effects, total_effects = e.sensitivity(bo_model, interval=(0.1, 20)) + # print('\nSENSITIVITY ANALYSIS\n', 'Main Effects\n', main_effects) + # print('Total Effects\n', total_effects, '\n') + + # results, bo_model = e.bayes_opt(WaitTimeMetric, interval=(0.1, 20), iterations=5) + # print(results) + # main_effects, total_effects = e.sensitivity(bo_model, interval=(0.1, 20)) + # print('\nSENSITIVITY ANALYSIS\n', 'Main Effects\n', main_effects) + # print('Total Effects\n', total_effects, '\n') - print(e.grid_search_opt(CompletedJourneysMetric, interval=(0.1, 20), steps_per_axis=2)) - print(e.grid_search_opt(WaitTimeMetric, interval=(0.1, 20), steps_per_axis=2)) + # print(e.grid_search_opt(CompletedJourneysMetric, interval=(0.1, 20), steps_per_axis=2)) + # print(e.grid_search_opt(WaitTimeMetric, interval=(0.1, 20), steps_per_axis=2)) From 620f54fcc57a6a407d4b2b8cb5a4281bae6be1da Mon Sep 17 00:00:00 2001 From: char-tan Date: Mon, 9 Jan 2023 15:39:05 +0000 Subject: [PATCH 10/38] new single intersec lop flow --- main.py | 11 +++++++---- simulation_builder/scenarios.py | 12 ++++++------ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/main.py b/main.py index c105847..424ff5c 100644 --- a/main.py +++ b/main.py @@ -8,16 +8,19 @@ if __name__ == "__main__": np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) - g, strategy = single_intersec_bal() + g, strategy = single_intersec_lop() e = Emulator(g, strategy) interval = (0.1, 20) - results, bo_model = e.bayes_opt(CompletedJourneysMetric, interval=interval, iterations=5) - print(results) - print(e.grid_search_opt(WaitTimeMetric, interval=interval, steps_per_axis=2)) + results, bo_model = e.bayes_opt(WaitTimeMetric, interval=interval, iterations=500) + results.to_csv('BO.csv') + results = e.grid_search_opt(WaitTimeMetric, interval=interval, steps_per_axis=5) + results.to_csv('GS.csv') + + breakpoint() # main_effects, total_effects = e.sensitivity(bo_model, interval=(0.1, 20)) # print('\nSENSITIVITY ANALYSIS\n', 'Main Effects\n', main_effects) diff --git a/simulation_builder/scenarios.py b/simulation_builder/scenarios.py index 8f1a656..dddcb18 100644 --- a/simulation_builder/scenarios.py +++ b/simulation_builder/scenarios.py @@ -53,16 +53,16 @@ def single_intersec_f_lop() -> CustomEndpointFlowStrategy: start_flows={ (0, -400): 1, - (0, 400): 60, - (-400, 0): 60, - (400, 0): 60, + (0, 400): 200, + (-400, 0): 250, + (400, 0): 300, } end_flows={ - (0, -400): 60, + (0, -400): 200, (0, 400): 1, - (-400, 0): 60, - (400, 0): 60, + (-400, 0): 200, + (400, 0): 200, } return CustomEndpointFlowStrategy(start_flows=start_flows, end_flows=end_flows) From 69b7cac98323d77e3805f4d148e6ab7fb0cbd2d2 Mon Sep 17 00:00:00 2001 From: char-tan Date: Mon, 9 Jan 2023 17:13:01 +0000 Subject: [PATCH 11/38] final single intersec bal flow --- main.py | 10 ++++++---- simulation_builder/scenarios.py | 14 +++++++------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/main.py b/main.py index 424ff5c..e1788b5 100644 --- a/main.py +++ b/main.py @@ -8,14 +8,16 @@ if __name__ == "__main__": np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) - g, strategy = single_intersec_lop() + g, strategy = single_intersec_bal() e = Emulator(g, strategy) - interval = (0.1, 20) + interval = (0.1, 30) - results, bo_model = e.bayes_opt(WaitTimeMetric, interval=interval, iterations=500) - results.to_csv('BO.csv') + # results, bo_model = e.bayes_opt(WaitTimeMetric, interval=interval, iterations=5) + # results.to_csv('BO.csv') + + # breakpoint() results = e.grid_search_opt(WaitTimeMetric, interval=interval, steps_per_axis=5) results.to_csv('GS.csv') diff --git a/simulation_builder/scenarios.py b/simulation_builder/scenarios.py index dddcb18..ffd492f 100644 --- a/simulation_builder/scenarios.py +++ b/simulation_builder/scenarios.py @@ -33,17 +33,17 @@ def single_intersec_g() -> Graph: def single_intersec_f_bal() -> CustomEndpointFlowStrategy: start_flows={ - (0, -400): 10, - (0, 400): 10, - (-400, 0): 10, - (400, 0): 10, + (0, -400): 8, + (0, 400): 7, + (-400, 0): 6, + (400, 0): 5, } end_flows={ - (0, -400): 10, + (0, -400): 5, (0, 400): 10, - (-400, 0): 10, - (400, 0): 10, + (-400, 0): 8, + (400, 0): 7, } return CustomEndpointFlowStrategy(start_flows=start_flows, end_flows=end_flows) From 14945e7293ca16e51745ff5d26848810117043f4 Mon Sep 17 00:00:00 2001 From: char-tan Date: Mon, 9 Jan 2023 17:26:15 +0000 Subject: [PATCH 12/38] comments in main.py, saving / loading of bo_model.obj --- main.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/main.py b/main.py index e1788b5..3ad2a1f 100644 --- a/main.py +++ b/main.py @@ -1,4 +1,5 @@ import numpy as np +import pickle from emulation.emulator import Emulator from emulation.metrics import CompletedJourneysMetric, WaitTimeMetric @@ -8,31 +9,33 @@ if __name__ == "__main__": np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) + ### CHOOSE SCENARIO HERE ### g, strategy = single_intersec_bal() e = Emulator(g, strategy) + ### INTERVAL SHOULD BE (0.1, 30) AS STANDARD ### interval = (0.1, 30) - # results, bo_model = e.bayes_opt(WaitTimeMetric, interval=interval, iterations=5) - # results.to_csv('BO.csv') + results, bo_model = e.bayes_opt(WaitTimeMetric, interval=interval, iterations=5) - # breakpoint() + ### BE CAREFUL NOT TO OVERWRITE FILES YOU WANT TO KEEP ### + results.to_csv('BO.csv') + with open('bo_model.obj', 'wb') as f: + pickle.dump(bo_model, f) results = e.grid_search_opt(WaitTimeMetric, interval=interval, steps_per_axis=5) + + ### BE CAREFUL NOT TO OVERWRITE FILES YOU WANT TO KEEP ### results.to_csv('GS.csv') - breakpoint() + ### READ .OBJ FILE ### (only need if loading one from previous run) - # main_effects, total_effects = e.sensitivity(bo_model, interval=(0.1, 20)) - # print('\nSENSITIVITY ANALYSIS\n', 'Main Effects\n', main_effects) - # print('Total Effects\n', total_effects, '\n') + #with open('bo_model.obj', 'rb') as f: + # bo_model = pickle.load(f) + + ### SENSITIVITY ANALYSIS ### - # results, bo_model = e.bayes_opt(WaitTimeMetric, interval=(0.1, 20), iterations=5) - # print(results) # main_effects, total_effects = e.sensitivity(bo_model, interval=(0.1, 20)) # print('\nSENSITIVITY ANALYSIS\n', 'Main Effects\n', main_effects) # print('Total Effects\n', total_effects, '\n') - - # print(e.grid_search_opt(CompletedJourneysMetric, interval=(0.1, 20), steps_per_axis=2)) - # print(e.grid_search_opt(WaitTimeMetric, interval=(0.1, 20), steps_per_axis=2)) From 670a9da1337c2192b07e6888e7d198e8c0dd719f Mon Sep 17 00:00:00 2001 From: yu202147657 <91469100+yu202147657@users.noreply.github.com> Date: Mon, 9 Jan 2023 18:27:50 +0000 Subject: [PATCH 13/38] adding double intersec bal + lop flows --- simulation_builder/scenarios.py | 50 +++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/simulation_builder/scenarios.py b/simulation_builder/scenarios.py index ffd492f..c6c2d41 100644 --- a/simulation_builder/scenarios.py +++ b/simulation_builder/scenarios.py @@ -72,6 +72,10 @@ def double_intersec_bal() -> tuple: return double_intersec_g(), double_intersec_f_bal() +def double_intersec_lop() -> tuple: + return double_intersec_g(), double_intersec_f_lop() + + def double_intersec_g() -> Graph: vertices = [ (-400, 0), @@ -101,21 +105,43 @@ def double_intersec_g() -> Graph: def double_intersec_f_bal() -> CustomEndpointFlowStrategy: start_flows={ - (0, 400): 20, - (400, 400): 20, - (800, 0): 20, - (0, -400): 20, - (400, -400): 20, - (-400, 0): 20, + (0, 400): 8, + (400, 400): 7, + (800, 0): 5, + (400, -400): 6, + (0, -400): 8, + (-400, 0): 6, } end_flows={ - (0, 400): 20, - (400, 400): 20, - (800, 0): 20, - (0, -400): 20, - (400, -400): 20, - (-400, 0): 20, + (0, 400): 9, + (400, 400): 10, + (800, 0): 7, + (400, -400): 5, + (0, -400): 6, + (-400, 0): 8, } return CustomEndpointFlowStrategy(start_flows=start_flows, end_flows=end_flows) + +def double_intersec_f_lop() -> CustomEndpointFlowStrategy: + + start_flows={ + (0, 400): 200, + (400, 400): 250, + (800, 0): 200, + (400, -400): 300, + (0, -400): 200, + (-400, 0): 1, + } + + end_flows={ + (0, 400): 250, + (400, 400): 250, + (800, 0): 1, + (400, -400): 300, + (0, -400): 200, + (-400, 0): 200, + } + + return CustomEndpointFlowStrategy(start_flows=start_flows, end_flows=end_flows) \ No newline at end of file From 5799a3f9422484e64d2af6a4097cc9025704828c Mon Sep 17 00:00:00 2001 From: char-tan Date: Mon, 9 Jan 2023 18:58:48 +0000 Subject: [PATCH 14/38] make choice of GP model explicit --- emulation/emulator.py | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/emulation/emulator.py b/emulation/emulator.py index 45b74f4..53419cf 100644 --- a/emulation/emulator.py +++ b/emulation/emulator.py @@ -3,10 +3,16 @@ import numpy as np import pandas import scipy + +from GPy.kern import Matern52 +from GPy.models import GPRegression + from emukit.core import ContinuousParameter, ParameterSpace from emukit.core.loop.user_function import UserFunctionWrapper -from emukit.examples.gp_bayesian_optimization.single_objective_bayesian_optimization import GPBayesianOptimization +from emukit.bayesian_optimization.loops import BayesianOptimizationLoop +from emukit.bayesian_optimization.acquisitions import ExpectedImprovement from emukit.sensitivity.monte_carlo import MonteCarloSensitivity +from emukit.model_wrappers.gpy_model_wrappers import GPyModelWrapper from emulation.simulator import Simulator from emulation.utils import results_to_df @@ -42,23 +48,40 @@ def bayes_opt(self, metric, interval: Tuple[float, float], iterations: int): x_init = np.random.uniform(*interval, size=(1, self._num_params)) - # you can't pass the UserFunctionResult straight into GPBO + # you can't pass the UserFunctionResult straight into BOLoop output_init = target_function(x_init)[0] # also the array for y is not the right shape y_init = np.expand_dims(output_init.Y, axis=1) # parameter space - parameter_list = [ContinuousParameter(f"x{i}", *interval) for i in range(self._num_params)] + parameter_space = ParameterSpace([ContinuousParameter(f"x{i}", *interval) for i in range(self._num_params)]) + + # choose kernel + kernel = Matern52(self._num_params, variance=1.0, ARD=False) + + # evaluate GP on initial points + gpmodel = GPRegression(x_init, y_init, kernel) + gpmodel.optimize() + + # no noise in target_function + gpmodel.Gaussian_noise.constrain_fixed(0.001) + + # wrap for emukit + model = GPyModelWrapper(gpmodel) # create the BO loop - bo_loop = GPBayesianOptimization(variables_list=parameter_list, X=x_init, Y=y_init, noiseless=True) + bo_loop = BayesianOptimizationLoop( + space=parameter_space, + model=model, + acquisition=ExpectedImprovement(model), + ) # put the inital raw metric into the bo_loop results bo_loop.loop_state.results[0].extra_outputs['raw metric'] = output_init.extra_outputs['raw metric'] # run optimisation - bo_loop.run_optimization(target_function, iterations) + bo_loop.run_loop(target_function, iterations) # get x and raw metric values from loop state results x = [step.X for step in bo_loop.loop_state.results] From ea0144ed25f5c6c9c2000ad630a958f033a2ab58 Mon Sep 17 00:00:00 2001 From: char-tan Date: Mon, 9 Jan 2023 19:30:59 +0000 Subject: [PATCH 15/38] add variable start points --- emulation/emulator.py | 34 +++++++++++++++++++++------------- emulation/utils.py | 6 +++++- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/emulation/emulator.py b/emulation/emulator.py index 53419cf..9aafb3a 100644 --- a/emulation/emulator.py +++ b/emulation/emulator.py @@ -9,6 +9,7 @@ from emukit.core import ContinuousParameter, ParameterSpace from emukit.core.loop.user_function import UserFunctionWrapper +from emukit.core.initial_designs import RandomDesign from emukit.bayesian_optimization.loops import BayesianOptimizationLoop from emukit.bayesian_optimization.acquisitions import ExpectedImprovement from emukit.sensitivity.monte_carlo import MonteCarloSensitivity @@ -36,9 +37,9 @@ def __init__(self, graph: Graph, flow_strategy: FlowStrategy, simulation_iterati else: self._num_params = intersections * 3 - def bayes_opt(self, metric, interval: Tuple[float, float], iterations: int): + def bayes_opt(self, metric, interval: Tuple[float, float], iterations: int, num_init_points: Optional[int] = 1): - print(f'\nbayesian optimisation on {metric().name}, interval {interval} for {iterations} iterations') + print(f'\nbayesian optimisation on {metric().name}, interval {interval} for {iterations} iterations {num_init_points} init points') np.random.seed(42) @@ -46,17 +47,23 @@ def bayes_opt(self, metric, interval: Tuple[float, float], iterations: int): target_function = UserFunctionWrapper(sim.evaluate, extra_output_names=['raw metric']) - x_init = np.random.uniform(*interval, size=(1, self._num_params)) - - # you can't pass the UserFunctionResult straight into BOLoop - output_init = target_function(x_init)[0] - - # also the array for y is not the right shape - y_init = np.expand_dims(output_init.Y, axis=1) - # parameter space parameter_space = ParameterSpace([ContinuousParameter(f"x{i}", *interval) for i in range(self._num_params)]) + # random sample init points + design = RandomDesign(parameter_space) + x_init = design.get_samples(num_init_points) + + # evaluate at all init points, and prep for input to BOLoop + y_init = [] + raw_metric = [] + for i in range(x_init.shape[0]): + output = target_function(x_init[i:i+1])[0] + y_init.append(np.expand_dims(output.Y, axis=1)) + raw_metric.append(output.extra_outputs['raw metric']) + raw_metric = np.stack(raw_metric) + y_init = np.concatenate(y_init, axis=0) + # choose kernel kernel = Matern52(self._num_params, variance=1.0, ARD=False) @@ -77,8 +84,9 @@ def bayes_opt(self, metric, interval: Tuple[float, float], iterations: int): acquisition=ExpectedImprovement(model), ) - # put the inital raw metric into the bo_loop results - bo_loop.loop_state.results[0].extra_outputs['raw metric'] = output_init.extra_outputs['raw metric'] + # put the inital raw metrics into the bo_loop results + for i, row in enumerate(bo_loop.loop_state.results): + row.extra_outputs['raw metric'] = raw_metric[i] # run optimisation bo_loop.run_loop(target_function, iterations) @@ -91,7 +99,7 @@ def bayes_opt(self, metric, interval: Tuple[float, float], iterations: int): x = np.stack(x, axis=0) raw_metric = np.concatenate(raw_metric) - return results_to_df(x, self._time_period, raw_metric, metric().name), bo_loop.model + return results_to_df(x, self._time_period, raw_metric, metric().name, num_init_points), bo_loop.model def sensitivity(self, bo_model, interval: Tuple[float, float], num_mc: int = 10000): diff --git a/emulation/utils.py b/emulation/utils.py index 07e4dfc..83e3cb7 100644 --- a/emulation/utils.py +++ b/emulation/utils.py @@ -4,7 +4,7 @@ import pandas -def results_to_df(x, time_period: Optional[float], y: Optional[List[float]] = None, metric_name: Optional[str] = None): +def results_to_df(x, time_period: Optional[float], y: Optional[List[float]] = None, metric_name: Optional[str] = None, num_init_points: Optional[int] = None): """ Parameters ---------- @@ -19,6 +19,10 @@ def results_to_df(x, time_period: Optional[float], y: Optional[List[float]] = No """ d = {} + if num_init_points != None: + eval_type = ['init'] * num_init_points + ['BO'] * (x.shape[0] - num_init_points) + d['eval type'] = eval_type + for i in range(x.shape[1]): if time_period is not None: node, phase = i // 3, i % 3 From f8a9acd4500d5d245909166533161b2cdfe2d060 Mon Sep 17 00:00:00 2001 From: char-tan Date: Mon, 9 Jan 2023 20:16:35 +0000 Subject: [PATCH 16/38] slightly hacky experiment run --- main.py | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/main.py b/main.py index 3ad2a1f..57fe17c 100644 --- a/main.py +++ b/main.py @@ -9,25 +9,53 @@ if __name__ == "__main__": np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) + ### INTERVAL SHOULD BE (0.1, 30) AS STANDARD ### + interval = (0.1, 30) + ### CHOOSE SCENARIO HERE ### g, strategy = single_intersec_bal() + e = Emulator(g, strategy) + + results, bo_model = e.bayes_opt(WaitTimeMetric, interval=interval, iterations=250, num_init_points=1) + results.to_csv('BO_single_intersec_bal_rbf.csv') + ### BE CAREFUL NOT TO OVERWRITE FILES YOU WANT TO KEEP ### + with open('BO_single_intersec_bal_rbf.obj', 'wb') as f: + pickle.dump(bo_model, f) + + ### CHOOSE SCENARIO HERE ### + g, strategy = single_intersec_lop() e = Emulator(g, strategy) - ### INTERVAL SHOULD BE (0.1, 30) AS STANDARD ### - interval = (0.1, 30) + results, bo_model = e.bayes_opt(WaitTimeMetric, interval=interval, iterations=250, num_init_points=1) + results.to_csv('BO_single_intersec_lop_rbf.csv') - results, bo_model = e.bayes_opt(WaitTimeMetric, interval=interval, iterations=5) + ### BE CAREFUL NOT TO OVERWRITE FILES YOU WANT TO KEEP ### + with open('BO_single_intersec_lop_rbf.obj', 'wb') as f: + pickle.dump(bo_model, f) + + ### CHOOSE SCENARIO HERE ### + g, strategy = double_intersec_bal() + e = Emulator(g, strategy) + + results, bo_model = e.bayes_opt(WaitTimeMetric, interval=interval, iterations=1000, num_init_points=1) + results.to_csv('BO_double_intersec_bal_rbf.csv') ### BE CAREFUL NOT TO OVERWRITE FILES YOU WANT TO KEEP ### - results.to_csv('BO.csv') - with open('bo_model.obj', 'wb') as f: + with open('BO_double_intersec_bal_rbf.obj', 'wb') as f: pickle.dump(bo_model, f) - results = e.grid_search_opt(WaitTimeMetric, interval=interval, steps_per_axis=5) + ### CHOOSE SCENARIO HERE ### + g, strategy = double_intersec_lop() + e = Emulator(g, strategy) + + results, bo_model = e.bayes_opt(WaitTimeMetric, interval=interval, iterations=1000, num_init_points=1) + results.to_csv('BO_double_intersec_lop_rbf.csv') ### BE CAREFUL NOT TO OVERWRITE FILES YOU WANT TO KEEP ### - results.to_csv('GS.csv') + with open('BO_double_intersec_lop_rbf.obj', 'wb') as f: + pickle.dump(bo_model, f) + ### READ .OBJ FILE ### (only need if loading one from previous run) From 080e5d0b5338b5ed2fa268dedf340cbef2442db9 Mon Sep 17 00:00:00 2001 From: char-tan Date: Mon, 9 Jan 2023 22:58:52 +0000 Subject: [PATCH 17/38] add ProgressStoppingCondition --- emulation/emulator.py | 68 ++++++++++++++++++++++++++++++++++++++---- emulation/simulator.py | 4 --- main.py | 67 +++++++++++++++++++++++------------------ 3 files changed, 101 insertions(+), 38 deletions(-) diff --git a/emulation/emulator.py b/emulation/emulator.py index 9aafb3a..44eea18 100644 --- a/emulation/emulator.py +++ b/emulation/emulator.py @@ -4,11 +4,13 @@ import pandas import scipy -from GPy.kern import Matern52 +from GPy.kern import Matern52, RBF from GPy.models import GPRegression from emukit.core import ContinuousParameter, ParameterSpace -from emukit.core.loop.user_function import UserFunctionWrapper +from emukit.core.loop.stopping_conditions import StoppingCondition +from emukit.core.loop.user_function import UserFunctionWrapper, UserFunctionResult +from emukit.core.loop.loop_state import LoopState from emukit.core.initial_designs import RandomDesign from emukit.bayesian_optimization.loops import BayesianOptimizationLoop from emukit.bayesian_optimization.acquisitions import ExpectedImprovement @@ -22,6 +24,54 @@ from simulation_builder.graph import Graph +class ProgressStoppingCondition(StoppingCondition): + """ + Stops after N iterations without improvement + """ + def __init__(self, N: int, max_iterations: int) -> None: + + self.N = N + self.max_iterations = max_iterations + + self.best = None + self.count = 0 + + def should_stop(self, loop_state: LoopState) -> bool: + + # first iteration find best of start points + if self.best is None: + self.best = np.min(loop_state.Y) + print(f'start points: {loop_state.Y.flatten()}') + return False + + # get current output + current_y = loop_state.Y[-1][0] + + # new best + if current_y < self.best: + print(f'iteration {loop_state.iteration}: {current_y} - new best!') + self.count = 0 + self.best = current_y + + # not new best + else: + print(f'iteration {loop_state.iteration}: {current_y}') + self.count += 1 + + # if reached max_iterations return True regardless + if loop_state.iteration > self.max_iterations: + print('exceeded max iterations, stopping') + return True + + # exceeded N + elif self.count > self.N: + print(f'stopping due to {self.N} iterations without progress') + return True + + else: + return False + + class Emulator: def __init__(self, graph: Graph, flow_strategy: FlowStrategy, simulation_iterations: int = 1000, fixed_time_period: Optional[float] = None): @@ -37,9 +87,15 @@ def __init__(self, graph: Graph, flow_strategy: FlowStrategy, simulation_iterati else: self._num_params = intersections * 3 - def bayes_opt(self, metric, interval: Tuple[float, float], iterations: int, num_init_points: Optional[int] = 1): + def bayes_opt( + self, + metric, + interval: Tuple[float, float], + max_iterations: int, + progress_N: int, + num_init_points: Optional[int] = 1): - print(f'\nbayesian optimisation on {metric().name}, interval {interval} for {iterations} iterations {num_init_points} init points') + print(f'\nbayesian optimisation on {metric().name}, interval {interval} with {num_init_points} init points') np.random.seed(42) @@ -88,8 +144,10 @@ def bayes_opt(self, metric, interval: Tuple[float, float], iterations: int, num_ for i, row in enumerate(bo_loop.loop_state.results): row.extra_outputs['raw metric'] = raw_metric[i] + stopping_condition = ProgressStoppingCondition(N=progress_N, max_iterations=max_iterations) + # run optimisation - bo_loop.run_loop(target_function, iterations) + bo_loop.run_loop(target_function, stopping_condition) # get x and raw metric values from loop state results x = [step.X for step in bo_loop.loop_state.results] diff --git a/emulation/simulator.py b/emulation/simulator.py index 16a2a5f..e06dd7e 100644 --- a/emulation/simulator.py +++ b/emulation/simulator.py @@ -29,7 +29,6 @@ def __init__(self, g: Graph, metric, strategy=None, timing_period: Optional[int] self.strategy = FlowStrategy() if strategy is None else strategy self.timing_period = timing_period self.steps = steps - self.simulation_num = 0 self.intersections = list(sorted([v for v in self.g if len(g[v]) > 2])) @@ -49,9 +48,6 @@ def evaluate(self, x): The resulting aggregate metric calculated after N simulation iterations, with traffic light timings x. """ - print(f'simulation: {self.simulation_num}') - self.simulation_num += 1 - # Infer missing parameters if fixed timing period is specified x = np.array(np.array_split(x.flatten(), len(self.intersections))) if self.timing_period is not None: diff --git a/main.py b/main.py index 57fe17c..2e25484 100644 --- a/main.py +++ b/main.py @@ -3,7 +3,7 @@ from emulation.emulator import Emulator from emulation.metrics import CompletedJourneysMetric, WaitTimeMetric -from simulation_builder.scenarios import single_intersec_bal, single_intersec_lop +from simulation_builder.scenarios import single_intersec_bal, single_intersec_lop, double_intersec_bal, double_intersec_lop if __name__ == "__main__": @@ -16,45 +16,54 @@ g, strategy = single_intersec_bal() e = Emulator(g, strategy) - results, bo_model = e.bayes_opt(WaitTimeMetric, interval=interval, iterations=250, num_init_points=1) - results.to_csv('BO_single_intersec_bal_rbf.csv') + results, bo_model = e.bayes_opt( + WaitTimeMetric, + interval=interval, + max_iterations=1000, + progress_N=100, + num_init_points=1) + results.to_csv('BO_single_intersec_bal_m52.csv') + + breakpoint() ### BE CAREFUL NOT TO OVERWRITE FILES YOU WANT TO KEEP ### - with open('BO_single_intersec_bal_rbf.obj', 'wb') as f: - pickle.dump(bo_model, f) + #with open('BO_single_intersec_bal_m52.obj', 'wb') as f: + # pickle.dump(bo_model, f) - ### CHOOSE SCENARIO HERE ### - g, strategy = single_intersec_lop() - e = Emulator(g, strategy) + #### CHOOSE SCENARIO HERE ### + #g, strategy = single_intersec_lop() + #e = Emulator(g, strategy) - results, bo_model = e.bayes_opt(WaitTimeMetric, interval=interval, iterations=250, num_init_points=1) - results.to_csv('BO_single_intersec_lop_rbf.csv') + #results, bo_model = e.bayes_opt(WaitTimeMetric, interval=interval, iterations=1000, num_init_points=1) + #results.to_csv('BO_single_intersec_lop_m52.csv') - ### BE CAREFUL NOT TO OVERWRITE FILES YOU WANT TO KEEP ### - with open('BO_single_intersec_lop_rbf.obj', 'wb') as f: - pickle.dump(bo_model, f) + #breakpoint() - ### CHOOSE SCENARIO HERE ### - g, strategy = double_intersec_bal() - e = Emulator(g, strategy) + #### BE CAREFUL NOT TO OVERWRITE FILES YOU WANT TO KEEP ### + #with open('BO_single_intersec_lop_m52.obj', 'wb') as f: + # pickle.dump(bo_model, f) - results, bo_model = e.bayes_opt(WaitTimeMetric, interval=interval, iterations=1000, num_init_points=1) - results.to_csv('BO_double_intersec_bal_rbf.csv') + #### CHOOSE SCENARIO HERE ### + #g, strategy = double_intersec_bal() + #e = Emulator(g, strategy) - ### BE CAREFUL NOT TO OVERWRITE FILES YOU WANT TO KEEP ### - with open('BO_double_intersec_bal_rbf.obj', 'wb') as f: - pickle.dump(bo_model, f) + #results, bo_model = e.bayes_opt(WaitTimeMetric, interval=interval, iterations=1000, num_init_points=1) + #results.to_csv('BO_double_intersec_bal_rbf.csv') - ### CHOOSE SCENARIO HERE ### - g, strategy = double_intersec_lop() - e = Emulator(g, strategy) + #### BE CAREFUL NOT TO OVERWRITE FILES YOU WANT TO KEEP ### + #with open('BO_double_intersec_bal_rbf.obj', 'wb') as f: + # pickle.dump(bo_model, f) - results, bo_model = e.bayes_opt(WaitTimeMetric, interval=interval, iterations=1000, num_init_points=1) - results.to_csv('BO_double_intersec_lop_rbf.csv') + #### CHOOSE SCENARIO HERE ### + #g, strategy = double_intersec_lop() + #e = Emulator(g, strategy) - ### BE CAREFUL NOT TO OVERWRITE FILES YOU WANT TO KEEP ### - with open('BO_double_intersec_lop_rbf.obj', 'wb') as f: - pickle.dump(bo_model, f) + #results, bo_model = e.bayes_opt(WaitTimeMetric, interval=interval, iterations=1000, num_init_points=1) + #results.to_csv('BO_double_intersec_lop_rbf.csv') + + #### BE CAREFUL NOT TO OVERWRITE FILES YOU WANT TO KEEP ### + #with open('BO_double_intersec_lop_rbf.obj', 'wb') as f: + # pickle.dump(bo_model, f) ### READ .OBJ FILE ### (only need if loading one from previous run) From 0170ecbcfcc9f8abd3460fa3696aefca68c058d6 Mon Sep 17 00:00:00 2001 From: maximwebb Date: Thu, 12 Jan 2023 16:35:32 +0100 Subject: [PATCH 18/38] Add ManualFlowStrategy, add plotting functionality --- emulation/emulator.py | 2 +- emulation/utils.py | 30 +++++- main.py | 19 ++-- plot.py | 16 +++ plots/.gitkeep | 0 simulation_builder/flows.py | 15 +++ simulation_builder/scenarios.py | 177 +++++++++++++++++--------------- simulation_builder/utils.py | 122 ---------------------- 8 files changed, 165 insertions(+), 216 deletions(-) create mode 100644 plot.py create mode 100644 plots/.gitkeep delete mode 100644 simulation_builder/utils.py diff --git a/emulation/emulator.py b/emulation/emulator.py index 44eea18..49a363e 100644 --- a/emulation/emulator.py +++ b/emulation/emulator.py @@ -121,7 +121,7 @@ def bayes_opt( y_init = np.concatenate(y_init, axis=0) # choose kernel - kernel = Matern52(self._num_params, variance=1.0, ARD=False) + kernel = Matern52(self._num_params, variance=2.0, ARD=False) # evaluate GP on initial points gpmodel = GPRegression(x_init, y_init, kernel) diff --git a/emulation/utils.py b/emulation/utils.py index 83e3cb7..edb6391 100644 --- a/emulation/utils.py +++ b/emulation/utils.py @@ -1,10 +1,19 @@ -from typing import List, Optional +import json +from typing import List, Optional, Dict import numpy as np import pandas +import cityflow as cf -def results_to_df(x, time_period: Optional[float], y: Optional[List[float]] = None, metric_name: Optional[str] = None, num_init_points: Optional[int] = None): +from emulation.metrics import CompletedJourneysMetric +from simulation_builder.flows import FlowStrategy, graph_to_flow +from simulation_builder.graph import Graph +from simulation_builder.roadnets import graph_to_roadnet + + +def results_to_df(x, time_period: Optional[float], y: Optional[List[float]] = None, metric_name: Optional[str] = None, + num_init_points: Optional[int] = None): """ Parameters ---------- @@ -40,6 +49,23 @@ def results_to_df(x, time_period: Optional[float], y: Optional[List[float]] = No return df +def run_simulation(g: Graph, strategy: FlowStrategy, n=1000, traffic_light_phases: Optional[Dict] = None): + roadnet = graph_to_roadnet(g, traffic_light_phases, intersection_width=50, lane_width=8) + flow = graph_to_flow(g, strategy) + with open("cityflow_config/roadnets/auto_roadnet.json", 'w') as f: + f.write(json.dumps(roadnet, indent=4)) + + with open("cityflow_config/flows/auto_flow.json", 'w') as f: + f.write(json.dumps(flow, indent=4)) + eng = cf.Engine("cityflow_config/config.json", thread_num=1) + + metric = CompletedJourneysMetric() + for _ in range(n): + eng.next_step() + metric.update(eng) + print(metric.report()) + + # Test functions def forrester(x): return (6 * x - 2) ** 2 * np.sin(12 * x - 4) diff --git a/main.py b/main.py index 2e25484..b017a14 100644 --- a/main.py +++ b/main.py @@ -3,8 +3,10 @@ from emulation.emulator import Emulator from emulation.metrics import CompletedJourneysMetric, WaitTimeMetric -from simulation_builder.scenarios import single_intersec_bal, single_intersec_lop, double_intersec_bal, double_intersec_lop - +from emulation.utils import run_simulation +from plot import plot_metric_results +from simulation_builder.scenarios import single_intersec_bal, single_intersec_lop, double_intersec_bal, \ + double_intersec_lop, single_intersec_lop_2 if __name__ == "__main__": np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) @@ -13,18 +15,23 @@ interval = (0.1, 30) ### CHOOSE SCENARIO HERE ### - g, strategy = single_intersec_bal() + g, strategy = single_intersec_lop_2() + # g, strategy = single_intersec_lop() + # g, strategy = double_intersec_bal() + # run_simulation(g, strategy, traffic_light_phases=None) e = Emulator(g, strategy) results, bo_model = e.bayes_opt( - WaitTimeMetric, + CompletedJourneysMetric, interval=interval, max_iterations=1000, - progress_N=100, + progress_N=200, num_init_points=1) results.to_csv('BO_single_intersec_bal_m52.csv') - breakpoint() + plot_metric_results(results_file="BO_single_intersec_bal_m52.csv") + # + # breakpoint() ### BE CAREFUL NOT TO OVERWRITE FILES YOU WANT TO KEEP ### #with open('BO_single_intersec_bal_m52.obj', 'wb') as f: diff --git a/plot.py b/plot.py new file mode 100644 index 0000000..1b9cb99 --- /dev/null +++ b/plot.py @@ -0,0 +1,16 @@ +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd + + +def plot_metric_results(results_file): + df = pd.read_csv(results_file) + metric_name = df.columns[-1] + results = df[metric_name] + + plt.style.use('ggplot') + plt.rc('font', family='serif') + plt.plot(np.arange(len(results)), results) + plt.title(metric_name.capitalize()) + plt.savefig(f"plots/{'_'.join(metric_name.split(' '))}_n={len(results)}.png") + plt.show() diff --git a/plots/.gitkeep b/plots/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/simulation_builder/flows.py b/simulation_builder/flows.py index 0df2fc2..02e1fe1 100644 --- a/simulation_builder/flows.py +++ b/simulation_builder/flows.py @@ -82,6 +82,21 @@ def gen_flows(self, route: List[Tuple[int, int]]) -> List[Flow]: return [Flow(route, interval=self._default)] +class ManualFlowStrategy(FlowStrategy): + """ + Takes a dictionary mapping routes defined by start/end pairs to flow intervals. Assumes flows are uniquely defined + by their start and end points. + """ + def __init__(self, flows: Dict[Tuple[Tuple, Tuple], float]): + self._flows = flows + + def gen_flows(self, route: List[Tuple[int, int]]) -> List[Flow]: + start, end = route[0], route[-1] + if (start, end) in self._flows: + return [Flow(route, interval=self._flows[(start, end)])] + return [] + + def graph_to_flow(g: Graph, strategy: FlowStrategy = FlowStrategy()) -> List[Dict]: paths = all_pairs_shortest_paths(g) flows = [] diff --git a/simulation_builder/scenarios.py b/simulation_builder/scenarios.py index c6c2d41..65ad1a1 100644 --- a/simulation_builder/scenarios.py +++ b/simulation_builder/scenarios.py @@ -1,78 +1,87 @@ +from typing import Tuple + from simulation_builder.graph import Graph -from simulation_builder.flows import CustomEndpointFlowStrategy +from simulation_builder.flows import CustomEndpointFlowStrategy, FlowStrategy, ManualFlowStrategy -def single_intersec_bal() -> tuple: +def single_intersec_bal() -> Tuple[Graph, FlowStrategy]: return single_intersec_g(), single_intersec_f_bal() -def single_intersec_lop() -> tuple: +def single_intersec_lop() -> Tuple[Graph, FlowStrategy]: return single_intersec_g(), single_intersec_f_lop() -def single_intersec_g() -> Graph: +def single_intersec_lop_2() -> Tuple[Graph, FlowStrategy]: + strategy = ManualFlowStrategy({ + ((0, -400), (0, 400)): 4, + ((-400, 0), (400, 0)): 100, + ((400, 0), (-400, 0)): 100 + }) + + return single_intersec_g(), strategy + +def single_intersec_g() -> Graph: vertices = [ - (0, -400), - (0, 0), - (0, 400), - (-400, 0), - (400, 0), - ] + (0, -400), + (0, 0), + (0, 400), + (-400, 0), + (400, 0), + ] edges = [ - ((0, -400), (0, 0)), - ((0, 400), (0, 0)), - ((-400, 0), (0, 0)), - ((400, 0), (0, 0)), - ] + ((0, -400), (0, 0)), + ((0, 400), (0, 0)), + ((-400, 0), (0, 0)), + ((400, 0), (0, 0)), + ] return Graph(vertices=vertices, edges=edges) def single_intersec_f_bal() -> CustomEndpointFlowStrategy: - - start_flows={ - (0, -400): 8, - (0, 400): 7, - (-400, 0): 6, - (400, 0): 5, - } - - end_flows={ - (0, -400): 5, - (0, 400): 10, - (-400, 0): 8, - (400, 0): 7, - } + start_flows = { + (0, -400): 8, + (0, 400): 7, + (-400, 0): 6, + (400, 0): 5, + } + + end_flows = { + (0, -400): 5, + (0, 400): 10, + (-400, 0): 8, + (400, 0): 7, + } return CustomEndpointFlowStrategy(start_flows=start_flows, end_flows=end_flows) def single_intersec_f_lop() -> CustomEndpointFlowStrategy: - - start_flows={ - (0, -400): 1, - (0, 400): 200, - (-400, 0): 250, - (400, 0): 300, - } - - end_flows={ - (0, -400): 200, - (0, 400): 1, - (-400, 0): 200, - (400, 0): 200, - } + start_flows = { + (0, -400): 4, + (0, 400): 200, + (-400, 0): 250, + (400, 0): 300, + } + + end_flows = { + (0, -400): 200, + (0, 400): 1, + (-400, 0): 200, + (400, 0): 200, + } return CustomEndpointFlowStrategy(start_flows=start_flows, end_flows=end_flows) -def double_intersec_bal() -> tuple: +def double_intersec_bal() -> Tuple[Graph, FlowStrategy]: return double_intersec_g(), double_intersec_f_bal() -def double_intersec_lop() -> tuple: +def double_intersec_lop() -> Tuple[Graph, FlowStrategy]: return double_intersec_g(), double_intersec_f_lop() @@ -86,8 +95,7 @@ def double_intersec_g() -> Graph: (400, -400), (0, 400), (400, 400), - ] - + ] edges = [ ((-400, 0), (0, 0)), @@ -97,51 +105,50 @@ def double_intersec_g() -> Graph: ((400, 0), (400, 400)), ((0, 0), (0, -400)), ((400, 0), (400, -400)) - ] + ] return Graph(vertices=vertices, edges=edges) def double_intersec_f_bal() -> CustomEndpointFlowStrategy: - - start_flows={ - (0, 400): 8, - (400, 400): 7, - (800, 0): 5, - (400, -400): 6, - (0, -400): 8, - (-400, 0): 6, - } - - end_flows={ - (0, 400): 9, - (400, 400): 10, - (800, 0): 7, - (400, -400): 5, - (0, -400): 6, - (-400, 0): 8, - } + start_flows = { + (0, 400): 8, + (400, 400): 7, + (800, 0): 5, + (400, -400): 6, + (0, -400): 8, + (-400, 0): 6, + } + + end_flows = { + (0, 400): 9, + (400, 400): 10, + (800, 0): 7, + (400, -400): 5, + (0, -400): 6, + (-400, 0): 8, + } return CustomEndpointFlowStrategy(start_flows=start_flows, end_flows=end_flows) + def double_intersec_f_lop() -> CustomEndpointFlowStrategy: + start_flows = { + (0, 400): 200, + (400, 400): 250, + (800, 0): 200, + (400, -400): 300, + (0, -400): 200, + (-400, 0): 1, + } + + end_flows = { + (0, 400): 250, + (400, 400): 250, + (800, 0): 1, + (400, -400): 300, + (0, -400): 200, + (-400, 0): 200, + } - start_flows={ - (0, 400): 200, - (400, 400): 250, - (800, 0): 200, - (400, -400): 300, - (0, -400): 200, - (-400, 0): 1, - } - - end_flows={ - (0, 400): 250, - (400, 400): 250, - (800, 0): 1, - (400, -400): 300, - (0, -400): 200, - (-400, 0): 200, - } - - return CustomEndpointFlowStrategy(start_flows=start_flows, end_flows=end_flows) \ No newline at end of file + return CustomEndpointFlowStrategy(start_flows=start_flows, end_flows=end_flows) diff --git a/simulation_builder/utils.py b/simulation_builder/utils.py deleted file mode 100644 index 8c3bb80..0000000 --- a/simulation_builder/utils.py +++ /dev/null @@ -1,122 +0,0 @@ -import json -import os - -from CityFlow.tools.generator.generate_json_from_grid import gridToRoadnet - - -def generate_roadnet(rowNum: int, colNum: int, rowDistance: int = 300, columnDistance: int = 300, - intersectionWidth: int = 100, numLeftLanes: int = 1, numStraightLanes: int = 1, - numRightLanes: int = 1, laneMaxSpeed: float = 16.67, vehLen: float = 5.0, vehWidth: float = 2.0, - vehMaxPosAcc: float = 2.0, vehMaxNegAcc: float = 4.5, vehUsualPosAcc: float = 2.0, - vehUsualNegAcc: float = 4.5, - vehMinGap: float = 2.5, vehMaxSpeed: float = 16.67, vehHeadwayTime: float = 1.5, - directory: str = "cityflow_config", roadnetFile: str = None, turn: bool = False, - tlPlan: bool = False, interval: float = 2.0, - flowFile: str = None): - """ - Generates grid-shaped roadnet and flow JSON files, and updates config.json file. - - (Code adapted from CityFlows/tools/generator directory) - """ - - # Helper function - def generate_route(rowNum, colNum, turn=False): - routes = [] - move = [(1, 0), (0, 1), (-1, 0), (0, -1)] - - def get_straight_route(start, direction, step): - x, y = start - route = [] - for _ in range(step): - route.append("road_%d_%d_%d" % (x, y, direction)) - x += move[direction][0] - y += move[direction][1] - return route - - for i in range(1, rowNum + 1): - routes.append(get_straight_route((0, i), 0, colNum + 1)) - routes.append(get_straight_route((colNum + 1, i), 2, colNum + 1)) - for i in range(1, colNum + 1): - routes.append(get_straight_route((i, 0), 1, rowNum + 1)) - routes.append(get_straight_route((i, rowNum + 1), 3, rowNum + 1)) - - if turn: - def get_turn_route(start, direction): - if direction[0] % 2 == 0: - step = min(rowNum * 2, colNum * 2 + 1) - else: - step = min(colNum * 2, rowNum * 2 + 1) - x, y = start - route = [] - cur = 0 - for _ in range(step): - route.append("road_%d_%d_%d" % (x, y, direction[cur])) - x += move[direction[cur]][0] - y += move[direction[cur]][1] - cur = 1 - cur - return route - - routes.append(get_turn_route((1, 0), (1, 0))) - routes.append(get_turn_route((0, 1), (0, 1))) - routes.append(get_turn_route((colNum + 1, rowNum), (2, 3))) - routes.append(get_turn_route((colNum, rowNum + 1), (3, 2))) - routes.append(get_turn_route((0, rowNum), (0, 3))) - routes.append(get_turn_route((1, rowNum + 1), (3, 0))) - routes.append(get_turn_route((colNum + 1, 1), (2, 1))) - routes.append(get_turn_route((colNum, 0), (1, 2))) - - return routes - - if roadnetFile is None: - roadnetFile = "roadnet_%d_%d%s.json" % (rowNum, colNum, "_turn" if turn else "") - if flowFile is None: - flowFile = "flow_%d_%d%s.json" % (rowNum, colNum, "_turn" if turn else "") - - grid = { - "rowNumber": rowNum, - "columnNumber": colNum, - "rowDistances": [rowDistance] * (colNum - 1), - "columnDistances": [columnDistance] * (rowNum - 1), - "outRowDistance": rowDistance, - "outColumnDistance": columnDistance, - "intersectionWidths": [[intersectionWidth] * colNum] * rowNum, - "numLeftLanes": numLeftLanes, - "numStraightLanes": numStraightLanes, - "numRightLanes": numRightLanes, - "laneMaxSpeed": laneMaxSpeed, - "tlPlan": tlPlan - } - - json.dump(gridToRoadnet(**grid), open(os.path.join(directory, "roadnets/", roadnetFile), "w"), indent=4) - - vehicle_template = { - "length": vehLen, - "width": vehWidth, - "maxPosAcc": vehMaxPosAcc, - "maxNegAcc": vehMaxNegAcc, - "usualPosAcc": vehUsualPosAcc, - "usualNegAcc": vehUsualNegAcc, - "minGap": vehMinGap, - "maxSpeed": vehMaxSpeed, - "headwayTime": vehHeadwayTime - } - routes = generate_route(rowNum, colNum, turn) - flow = [] - for route in routes: - flow.append({ - "vehicle": vehicle_template, - "route": route, - "interval": interval, - "startTime": 0, - "endTime": -1 - }) - json.dump(flow, open(os.path.join(directory, "flows/", flowFile), "w"), indent=4) - - with open("cityflow_config/config.json", "r") as f: - config_file = json.load(f) - - config_file["roadnetFile"] = f"roadnets/{roadnetFile}" - config_file["flowFile"] = f"flows/{flowFile}" - - with open("cityflow_config/config.json", "w") as f: - json.dump(config_file, f, indent=4) From 4e6efc41538ac04f90380cb44bb7ecc8748165e4 Mon Sep 17 00:00:00 2001 From: maximwebb Date: Thu, 12 Jan 2023 18:21:56 +0100 Subject: [PATCH 19/38] Refactor FlowStrategy, add "optimisable" scenario for single intersection with balanced flow --- emulation/emulator.py | 4 ++-- emulation/simulator.py | 4 ++-- main.py | 19 +++++++++---------- simulation_builder/flows.py | 20 ++++++++++++++++---- simulation_builder/scenarios.py | 7 ++++++- 5 files changed, 35 insertions(+), 19 deletions(-) diff --git a/emulation/emulator.py b/emulation/emulator.py index 49a363e..18790c1 100644 --- a/emulation/emulator.py +++ b/emulation/emulator.py @@ -20,7 +20,7 @@ from emulation.simulator import Simulator from emulation.utils import results_to_df -from simulation_builder.flows import FlowStrategy +from simulation_builder.flows import FlowStrategy, UniformFlowStrategy from simulation_builder.graph import Graph @@ -76,7 +76,7 @@ class Emulator: def __init__(self, graph: Graph, flow_strategy: FlowStrategy, simulation_iterations: int = 1000, fixed_time_period: Optional[float] = None): self._g = graph - self._strategy = FlowStrategy() if flow_strategy is None else flow_strategy + self._strategy = UniformFlowStrategy() if flow_strategy is None else flow_strategy self._sim_iterations = simulation_iterations self._time_period = fixed_time_period diff --git a/emulation/simulator.py b/emulation/simulator.py index e06dd7e..e2f27da 100644 --- a/emulation/simulator.py +++ b/emulation/simulator.py @@ -6,7 +6,7 @@ import numpy as np import cityflow as cf -from simulation_builder.flows import FlowStrategy, graph_to_flow +from simulation_builder.flows import FlowStrategy, graph_to_flow, UniformFlowStrategy from simulation_builder.graph import Graph from simulation_builder.roadnets import graph_to_roadnet @@ -26,7 +26,7 @@ def __init__(self, g: Graph, metric, strategy=None, timing_period: Optional[int] self.g = g self.metric = metric - self.strategy = FlowStrategy() if strategy is None else strategy + self.strategy = UniformFlowStrategy() if strategy is None else strategy self.timing_period = timing_period self.steps = steps diff --git a/main.py b/main.py index b017a14..cf50d60 100644 --- a/main.py +++ b/main.py @@ -6,28 +6,27 @@ from emulation.utils import run_simulation from plot import plot_metric_results from simulation_builder.scenarios import single_intersec_bal, single_intersec_lop, double_intersec_bal, \ - double_intersec_lop, single_intersec_lop_2 + double_intersec_lop, single_intersec_lop_2, single_intersec_bal_2 if __name__ == "__main__": np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) - ### INTERVAL SHOULD BE (0.1, 30) AS STANDARD ### interval = (0.1, 30) - ### CHOOSE SCENARIO HERE ### - g, strategy = single_intersec_lop_2() - # g, strategy = single_intersec_lop() - # g, strategy = double_intersec_bal() - # run_simulation(g, strategy, traffic_light_phases=None) - e = Emulator(g, strategy) + g, strategy = single_intersec_bal_2() + run_simulation(g, strategy, n=1500, traffic_light_phases=None) - results, bo_model = e.bayes_opt( + optimise = True + if optimise: + e = Emulator(g, strategy) + + results, bo_model = e.bayes_opt( CompletedJourneysMetric, interval=interval, max_iterations=1000, progress_N=200, num_init_points=1) - results.to_csv('BO_single_intersec_bal_m52.csv') + results.to_csv('BO_single_intersec_bal_m52.csv') plot_metric_results(results_file="BO_single_intersec_bal_m52.csv") # diff --git a/simulation_builder/flows.py b/simulation_builder/flows.py index 02e1fe1..ebdf07a 100644 --- a/simulation_builder/flows.py +++ b/simulation_builder/flows.py @@ -1,4 +1,5 @@ import math +from abc import ABC, abstractmethod from logging import warning from numpy import random @@ -34,10 +35,21 @@ def json(self) -> Dict: } -class FlowStrategy: - # Default flow strategy creates one flow per route. +class FlowStrategy(ABC): + @abstractmethod def gen_flows(self, route: List[Tuple[int, int]]) -> List[Flow]: - return [Flow(route)] + pass + + +class UniformFlowStrategy(FlowStrategy): + """ + Creates exactly one flow per route, all initialised with the same interval + """ + def __init__(self, interval=5.0): + self._interval = interval + + def gen_flows(self, route: List[Tuple[int, int]]) -> List[Flow]: + return [Flow(route, interval=self._interval)] class RandomFlowStrategy(FlowStrategy): @@ -97,7 +109,7 @@ def gen_flows(self, route: List[Tuple[int, int]]) -> List[Flow]: return [] -def graph_to_flow(g: Graph, strategy: FlowStrategy = FlowStrategy()) -> List[Dict]: +def graph_to_flow(g: Graph, strategy: FlowStrategy = UniformFlowStrategy()) -> List[Dict]: paths = all_pairs_shortest_paths(g) flows = [] for start in paths: diff --git a/simulation_builder/scenarios.py b/simulation_builder/scenarios.py index 65ad1a1..8092e18 100644 --- a/simulation_builder/scenarios.py +++ b/simulation_builder/scenarios.py @@ -1,7 +1,7 @@ from typing import Tuple from simulation_builder.graph import Graph -from simulation_builder.flows import CustomEndpointFlowStrategy, FlowStrategy, ManualFlowStrategy +from simulation_builder.flows import CustomEndpointFlowStrategy, FlowStrategy, ManualFlowStrategy, UniformFlowStrategy def single_intersec_bal() -> Tuple[Graph, FlowStrategy]: @@ -22,6 +22,11 @@ def single_intersec_lop_2() -> Tuple[Graph, FlowStrategy]: return single_intersec_g(), strategy +def single_intersec_bal_2() -> Tuple[Graph, FlowStrategy]: + strategy = UniformFlowStrategy(interval=10) + return single_intersec_g(), strategy + + def single_intersec_g() -> Graph: vertices = [ (0, -400), From 42877c745b44f2412adf971b71e12398349157e3 Mon Sep 17 00:00:00 2001 From: char-tan Date: Thu, 12 Jan 2023 18:30:07 +0000 Subject: [PATCH 20/38] print x and y on each iteration --- emulation/emulator.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/emulation/emulator.py b/emulation/emulator.py index 18790c1..b16ab0f 100644 --- a/emulation/emulator.py +++ b/emulation/emulator.py @@ -47,15 +47,17 @@ def should_stop(self, loop_state: LoopState) -> bool: # get current output current_y = loop_state.Y[-1][0] + current_x = loop_state.X[-1] + # new best if current_y < self.best: - print(f'iteration {loop_state.iteration}: {current_y} - new best!') + print(f'iteration {loop_state.iteration}: {current_x} {current_y} - new best!') self.count = 0 self.best = current_y # not new best else: - print(f'iteration {loop_state.iteration}: {current_y}') + print(f'iteration {loop_state.iteration}: {current_x} {current_y}') self.count += 1 # if reached max_iterations return True regardless From 326439f2b450bf1e71e53e91f181c7ac36daef60 Mon Sep 17 00:00:00 2001 From: char-tan Date: Thu, 12 Jan 2023 19:21:55 +0000 Subject: [PATCH 21/38] pass in kernel to bayes_opt --- emulation/emulator.py | 6 ++++-- main.py | 11 +++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/emulation/emulator.py b/emulation/emulator.py index b16ab0f..b010433 100644 --- a/emulation/emulator.py +++ b/emulation/emulator.py @@ -91,6 +91,8 @@ def __init__(self, graph: Graph, flow_strategy: FlowStrategy, simulation_iterati def bayes_opt( self, + kernel_func, + kernel_kwargs, metric, interval: Tuple[float, float], max_iterations: int, @@ -122,8 +124,8 @@ def bayes_opt( raw_metric = np.stack(raw_metric) y_init = np.concatenate(y_init, axis=0) - # choose kernel - kernel = Matern52(self._num_params, variance=2.0, ARD=False) + # init kernel + kernel = kernel_func(self._num_params, **kernel_kwargs) # evaluate GP on initial points gpmodel = GPRegression(x_init, y_init, kernel) diff --git a/main.py b/main.py index cf50d60..4c281f0 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,8 @@ import numpy as np import pickle +from GPy.kern import Matern52, RBF + from emulation.emulator import Emulator from emulation.metrics import CompletedJourneysMetric, WaitTimeMetric from emulation.utils import run_simulation @@ -13,18 +15,23 @@ interval = (0.1, 30) - g, strategy = single_intersec_bal_2() + g, strategy = single_intersec_lop_2() run_simulation(g, strategy, n=1500, traffic_light_phases=None) + kernel_func = Matern52 + kernel_kwargs = {'variance': 2} + optimise = True if optimise: e = Emulator(g, strategy) results, bo_model = e.bayes_opt( + kernel_func, + kernel_kwargs, CompletedJourneysMetric, interval=interval, max_iterations=1000, - progress_N=200, + progress_N=300, num_init_points=1) results.to_csv('BO_single_intersec_bal_m52.csv') From 7edd188f610c1265ebe1ca071da4062939ebee8c Mon Sep 17 00:00:00 2001 From: char-tan Date: Thu, 12 Jan 2023 21:39:02 +0000 Subject: [PATCH 22/38] change plot args, add cumulative plot --- plot.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/plot.py b/plot.py index 1b9cb99..802cfdc 100644 --- a/plot.py +++ b/plot.py @@ -3,14 +3,17 @@ import pandas as pd -def plot_metric_results(results_file): - df = pd.read_csv(results_file) +def plot_metric_results(df, file_name): metric_name = df.columns[-1] results = df[metric_name] + cummin = results.cummin() plt.style.use('ggplot') plt.rc('font', family='serif') plt.plot(np.arange(len(results)), results) + plt.plot(np.arange(len(results)), cummin) + plt.legend(['actual', 'cumulative']) plt.title(metric_name.capitalize()) - plt.savefig(f"plots/{'_'.join(metric_name.split(' '))}_n={len(results)}.png") - plt.show() + plt.savefig(file_name) + + return plt From 86ad7ca4b49491943f43f3d9fd420b306c8f02c2 Mon Sep 17 00:00:00 2001 From: char-tan Date: Thu, 12 Jan 2023 21:40:11 +0000 Subject: [PATCH 23/38] bo_experiments file --- bo_experiments.py | 62 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 bo_experiments.py diff --git a/bo_experiments.py b/bo_experiments.py new file mode 100644 index 0000000..525596d --- /dev/null +++ b/bo_experiments.py @@ -0,0 +1,62 @@ +import numpy as np +import pickle + +from GPy.kern import Matern52, RBF + +from emulation.emulator import Emulator +from emulation.metrics import CompletedJourneysMetric, WaitTimeMetric +from emulation.utils import run_simulation +from plot import plot_metric_results +from simulation_builder.scenarios import single_intersec_bal, single_intersec_lop, double_intersec_bal, \ + double_intersec_lop, single_intersec_lop_2, single_intersec_bal_2 + +if __name__ == "__main__": + np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) + + interval = (0.1, 30) + + for scenario in ['SL2', 'SB2']: + + for kernel_name in ['RBF', 'M52']: + + # define id + scenario = 'SL2' + num_init_points = 1 + metric_name = 'WT' + variance = 2 + + # id -> config + if scenario == 'SL2': + g, strategy = single_intersec_lop_2() + if scenario == 'SB2': + g, strategy = single_intersec_bal_2() + + if kernel_name == 'M52': + kernel_func = Matern52 + elif kernel_name == 'RBF': + kernel_func = RBF + + if metric_name == 'WT': + metric = WaitTimeMetric + + kernel_kwargs = {'variance': variance} + + e = Emulator(g, strategy) + + results, bo_model = e.bayes_opt( + kernel_func, + kernel_kwargs, + metric, + interval=interval, + max_iterations=250, + progress_N=500, + num_init_points=num_init_points) + + file_id = f'BO_{scenario}_{metric_name}_{kernel_name}_{variance}_{num_init_points}' + + results.to_csv(f'csv_files/{file_id}.csv') + + plot_metric_results(results, f'plots/{file_id}.png') + + with open(f'bo_models/{file_id}.obj', 'wb') as f: + pickle.dump(bo_model, f) From 88792091f51681fe8828fdda0e8de8b651ea437a Mon Sep 17 00:00:00 2001 From: char-tan Date: Fri, 13 Jan 2023 01:02:50 +0000 Subject: [PATCH 24/38] fix plot --- plot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plot.py b/plot.py index 802cfdc..303ee89 100644 --- a/plot.py +++ b/plot.py @@ -8,6 +8,7 @@ def plot_metric_results(df, file_name): results = df[metric_name] cummin = results.cummin() + plt.figure() plt.style.use('ggplot') plt.rc('font', family='serif') plt.plot(np.arange(len(results)), results) From 1c7f8cd3da3333b26c5511f98d7927d4bd80f9d2 Mon Sep 17 00:00:00 2001 From: char-tan Date: Fri, 13 Jan 2023 01:36:22 +0000 Subject: [PATCH 25/38] code for maxim --- bo_experiments.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/bo_experiments.py b/bo_experiments.py index 525596d..a38601c 100644 --- a/bo_experiments.py +++ b/bo_experiments.py @@ -13,16 +13,15 @@ if __name__ == "__main__": np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) - interval = (0.1, 30) + interval = (1, 30) for scenario in ['SL2', 'SB2']: - for kernel_name in ['RBF', 'M52']: + for lengthscale in [1, 2, 4, 8]: # define id - scenario = 'SL2' num_init_points = 1 - metric_name = 'WT' + metric_name = 'CJ' variance = 2 # id -> config @@ -38,8 +37,10 @@ if metric_name == 'WT': metric = WaitTimeMetric + elif metric_name == 'CJ': + metric = CompletedJourneysMetric - kernel_kwargs = {'variance': variance} + kernel_kwargs = {'variance': variance, 'lengthscale': lengthscale} e = Emulator(g, strategy) @@ -48,15 +49,15 @@ kernel_kwargs, metric, interval=interval, - max_iterations=250, - progress_N=500, + max_iterations=200, + progress_N=200, num_init_points=num_init_points) - file_id = f'BO_{scenario}_{metric_name}_{kernel_name}_{variance}_{num_init_points}' + file_id = f'BO_{scenario}_{metric_name}_{kernel_name}_{variance}_{lengthscale}_{num_init_points}' results.to_csv(f'csv_files/{file_id}.csv') - plot_metric_results(results, f'plots/{file_id}.png') + plt = plot_metric_results(results, f'plots/{file_id}.png') with open(f'bo_models/{file_id}.obj', 'wb') as f: pickle.dump(bo_model, f) From cf12c3735cdeff84c0d1e666ceb8296ddc607843 Mon Sep 17 00:00:00 2001 From: yu202147657 Date: Fri, 13 Jan 2023 23:31:53 +0000 Subject: [PATCH 26/38] adding sensitivity testing file, plots. --- emulation/emulator.py | 2 ++ plot.py | 25 +++++++++++++++++++++++ sensitivity_experiments.py | 42 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 sensitivity_experiments.py diff --git a/emulation/emulator.py b/emulation/emulator.py index b010433..ac71caf 100644 --- a/emulation/emulator.py +++ b/emulation/emulator.py @@ -165,6 +165,8 @@ def bayes_opt( def sensitivity(self, bo_model, interval: Tuple[float, float], num_mc: int = 10000): + np.random.seed(42) + parameter_list = [ContinuousParameter(f"x{i}", *interval) for i in range(self._num_params)] senstivity = MonteCarloSensitivity(model=bo_model, input_domain=ParameterSpace(parameter_list)) diff --git a/plot.py b/plot.py index 303ee89..ed78d07 100644 --- a/plot.py +++ b/plot.py @@ -18,3 +18,28 @@ def plot_metric_results(df, file_name): plt.savefig(file_name) return plt + +def plot_sensitivity(main_effects, total_effects, file_name): + + params = main_effects.columns + main_effects = main_effects.values.tolist()[0] + total_effects = total_effects.values.tolist()[0] + + plt.figure() + plt.style.use('ggplot') + plt.rc('font', family='serif') + + X_axis = np.arange(len(params)) + + plt.bar(X_axis - 0.2, main_effects, 0.4, label='Main Effects') + plt.bar(X_axis + 0.2, total_effects, 0.4, label='Total Effects') + + plt.xticks(X_axis, params) + plt.ylabel('Sensitivity') + + title = file_name[6:-4] + plt.title(title) + + plt.savefig(file_name) + + return plt \ No newline at end of file diff --git a/sensitivity_experiments.py b/sensitivity_experiments.py new file mode 100644 index 0000000..a8b79b9 --- /dev/null +++ b/sensitivity_experiments.py @@ -0,0 +1,42 @@ +import numpy as np +import pickle + +from emulation.emulator import Emulator +from simulation_builder.scenarios import single_intersec_bal, single_intersec_lop, double_intersec_bal, \ + double_intersec_lop, single_intersec_lop_2, single_intersec_bal_2 + +from plot import plot_sensitivity + +if __name__ == "__main__": + np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) + + interval = (1, 30) + + for scenario in ['SL2', 'SB2']: + + for kernel_name in ['RBF', 'RQ', 'M52']: + + if scenario == 'SL2': + g, strategy = single_intersec_lop_2() + if scenario == 'SB2': + g, strategy = single_intersec_bal_2() + + e = Emulator(g, strategy) + + # define id + lengthscale = 1 + num_init_points = 1 + metric_name = 'WT' + variance = 2 + + file_id = f'BO_{scenario}_{metric_name}_{kernel_name}_{variance}_{lengthscale}_{num_init_points}' + + file_id = 'BO_SL2_CJ_M52_2_1_1' + with open(f'bo_models/{file_id}.obj', 'rb') as f: + bo_model = pickle.load(f) + + main_effects, total_effects = e.sensitivity(bo_model, interval=interval) + + plt = plot_sensitivity(main_effects, total_effects, f'plots/{file_id}.png') + + From ca208c841eeb3d180e59af83b0d529cfcab2e076 Mon Sep 17 00:00:00 2001 From: char-tan Date: Sat, 14 Jan 2023 14:58:55 +0000 Subject: [PATCH 27/38] rm old scenarios, add RQ to bo_experiments --- bo_experiments.py | 102 +++++++++++++++++--------------- main.py | 95 +++++++++++------------------ simulation_builder/scenarios.py | 97 ++---------------------------- 3 files changed, 91 insertions(+), 203 deletions(-) diff --git a/bo_experiments.py b/bo_experiments.py index a38601c..29ccaa1 100644 --- a/bo_experiments.py +++ b/bo_experiments.py @@ -1,63 +1,67 @@ import numpy as np import pickle -from GPy.kern import Matern52, RBF +from GPy.kern import Matern52, RBF, RatQuad from emulation.emulator import Emulator from emulation.metrics import CompletedJourneysMetric, WaitTimeMetric from emulation.utils import run_simulation from plot import plot_metric_results -from simulation_builder.scenarios import single_intersec_bal, single_intersec_lop, double_intersec_bal, \ - double_intersec_lop, single_intersec_lop_2, single_intersec_bal_2 +from simulation_builder.scenarios import single_intersec_lop_2, single_intersec_bal_2, double_intersec_bal_2 if __name__ == "__main__": np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) interval = (1, 30) - for scenario in ['SL2', 'SB2']: - - for lengthscale in [1, 2, 4, 8]: - - # define id - num_init_points = 1 - metric_name = 'CJ' - variance = 2 - - # id -> config - if scenario == 'SL2': - g, strategy = single_intersec_lop_2() - if scenario == 'SB2': - g, strategy = single_intersec_bal_2() - - if kernel_name == 'M52': - kernel_func = Matern52 - elif kernel_name == 'RBF': - kernel_func = RBF - - if metric_name == 'WT': - metric = WaitTimeMetric - elif metric_name == 'CJ': - metric = CompletedJourneysMetric - - kernel_kwargs = {'variance': variance, 'lengthscale': lengthscale} - - e = Emulator(g, strategy) - - results, bo_model = e.bayes_opt( - kernel_func, - kernel_kwargs, - metric, - interval=interval, - max_iterations=200, - progress_N=200, - num_init_points=num_init_points) - - file_id = f'BO_{scenario}_{metric_name}_{kernel_name}_{variance}_{lengthscale}_{num_init_points}' - - results.to_csv(f'csv_files/{file_id}.csv') - - plt = plot_metric_results(results, f'plots/{file_id}.png') - - with open(f'bo_models/{file_id}.obj', 'wb') as f: - pickle.dump(bo_model, f) + # define id + scenario = 'DB2' + metric_name = 'CJ' + + lengthscale = 2 + variance = 2 + kernel_name = 'M52' + + num_init_points = 1 + + # id -> config + if scenario == 'SL2': + g, strategy = single_intersec_lop_2() + if scenario == 'SB2': + g, strategy = single_intersec_bal_2() + if scenario == 'DB2': + g, strategy = double_intersec_bal_2() + + if kernel_name == 'M52': + kernel_func = Matern52 + elif kernel_name == 'RBF': + kernel_func = RBF + elif kernel_name == 'RQ': + kernel_func = RatQaud + + if metric_name == 'WT': + metric = WaitTimeMetric + elif metric_name == 'CJ': + metric = CompletedJourneysMetric + + kernel_kwargs = {'variance': variance, 'lengthscale': lengthscale} + + # create emulator + e = Emulator(g, strategy) + + # run bayesopt + results, bo_model = e.bayes_opt( + kernel_func, + kernel_kwargs, + metric, + interval=interval, + max_iterations=10, + progress_N=200, + num_init_points=num_init_points) + + # save data + file_id = f'BO_{scenario}_{metric_name}_{kernel_name}_{variance}_{lengthscale}_{num_init_points}' + results.to_csv(f'csv_files/{file_id}.csv') + plt = plot_metric_results(results, f'plots/{file_id}.png') + with open(f'bo_models/{file_id}.obj', 'wb') as f: + pickle.dump(bo_model, f) diff --git a/main.py b/main.py index 4c281f0..525596d 100644 --- a/main.py +++ b/main.py @@ -15,77 +15,48 @@ interval = (0.1, 30) - g, strategy = single_intersec_lop_2() - run_simulation(g, strategy, n=1500, traffic_light_phases=None) + for scenario in ['SL2', 'SB2']: - kernel_func = Matern52 - kernel_kwargs = {'variance': 2} + for kernel_name in ['RBF', 'M52']: - optimise = True - if optimise: - e = Emulator(g, strategy) + # define id + scenario = 'SL2' + num_init_points = 1 + metric_name = 'WT' + variance = 2 - results, bo_model = e.bayes_opt( - kernel_func, - kernel_kwargs, - CompletedJourneysMetric, - interval=interval, - max_iterations=1000, - progress_N=300, - num_init_points=1) - results.to_csv('BO_single_intersec_bal_m52.csv') + # id -> config + if scenario == 'SL2': + g, strategy = single_intersec_lop_2() + if scenario == 'SB2': + g, strategy = single_intersec_bal_2() - plot_metric_results(results_file="BO_single_intersec_bal_m52.csv") - # - # breakpoint() + if kernel_name == 'M52': + kernel_func = Matern52 + elif kernel_name == 'RBF': + kernel_func = RBF - ### BE CAREFUL NOT TO OVERWRITE FILES YOU WANT TO KEEP ### - #with open('BO_single_intersec_bal_m52.obj', 'wb') as f: - # pickle.dump(bo_model, f) + if metric_name == 'WT': + metric = WaitTimeMetric - #### CHOOSE SCENARIO HERE ### - #g, strategy = single_intersec_lop() - #e = Emulator(g, strategy) + kernel_kwargs = {'variance': variance} - #results, bo_model = e.bayes_opt(WaitTimeMetric, interval=interval, iterations=1000, num_init_points=1) - #results.to_csv('BO_single_intersec_lop_m52.csv') + e = Emulator(g, strategy) - #breakpoint() + results, bo_model = e.bayes_opt( + kernel_func, + kernel_kwargs, + metric, + interval=interval, + max_iterations=250, + progress_N=500, + num_init_points=num_init_points) - #### BE CAREFUL NOT TO OVERWRITE FILES YOU WANT TO KEEP ### - #with open('BO_single_intersec_lop_m52.obj', 'wb') as f: - # pickle.dump(bo_model, f) + file_id = f'BO_{scenario}_{metric_name}_{kernel_name}_{variance}_{num_init_points}' - #### CHOOSE SCENARIO HERE ### - #g, strategy = double_intersec_bal() - #e = Emulator(g, strategy) + results.to_csv(f'csv_files/{file_id}.csv') - #results, bo_model = e.bayes_opt(WaitTimeMetric, interval=interval, iterations=1000, num_init_points=1) - #results.to_csv('BO_double_intersec_bal_rbf.csv') + plot_metric_results(results, f'plots/{file_id}.png') - #### BE CAREFUL NOT TO OVERWRITE FILES YOU WANT TO KEEP ### - #with open('BO_double_intersec_bal_rbf.obj', 'wb') as f: - # pickle.dump(bo_model, f) - - #### CHOOSE SCENARIO HERE ### - #g, strategy = double_intersec_lop() - #e = Emulator(g, strategy) - - #results, bo_model = e.bayes_opt(WaitTimeMetric, interval=interval, iterations=1000, num_init_points=1) - #results.to_csv('BO_double_intersec_lop_rbf.csv') - - #### BE CAREFUL NOT TO OVERWRITE FILES YOU WANT TO KEEP ### - #with open('BO_double_intersec_lop_rbf.obj', 'wb') as f: - # pickle.dump(bo_model, f) - - - ### READ .OBJ FILE ### (only need if loading one from previous run) - - #with open('bo_model.obj', 'rb') as f: - # bo_model = pickle.load(f) - - ### SENSITIVITY ANALYSIS ### - - # main_effects, total_effects = e.sensitivity(bo_model, interval=(0.1, 20)) - # print('\nSENSITIVITY ANALYSIS\n', 'Main Effects\n', main_effects) - # print('Total Effects\n', total_effects, '\n') + with open(f'bo_models/{file_id}.obj', 'wb') as f: + pickle.dump(bo_model, f) diff --git a/simulation_builder/scenarios.py b/simulation_builder/scenarios.py index 8092e18..de06690 100644 --- a/simulation_builder/scenarios.py +++ b/simulation_builder/scenarios.py @@ -4,14 +4,6 @@ from simulation_builder.flows import CustomEndpointFlowStrategy, FlowStrategy, ManualFlowStrategy, UniformFlowStrategy -def single_intersec_bal() -> Tuple[Graph, FlowStrategy]: - return single_intersec_g(), single_intersec_f_bal() - - -def single_intersec_lop() -> Tuple[Graph, FlowStrategy]: - return single_intersec_g(), single_intersec_f_lop() - - def single_intersec_lop_2() -> Tuple[Graph, FlowStrategy]: strategy = ManualFlowStrategy({ ((0, -400), (0, 400)): 4, @@ -46,48 +38,13 @@ def single_intersec_g() -> Graph: return Graph(vertices=vertices, edges=edges) -def single_intersec_f_bal() -> CustomEndpointFlowStrategy: - start_flows = { - (0, -400): 8, - (0, 400): 7, - (-400, 0): 6, - (400, 0): 5, - } - - end_flows = { - (0, -400): 5, - (0, 400): 10, - (-400, 0): 8, - (400, 0): 7, - } - - return CustomEndpointFlowStrategy(start_flows=start_flows, end_flows=end_flows) - - -def single_intersec_f_lop() -> CustomEndpointFlowStrategy: - start_flows = { - (0, -400): 4, - (0, 400): 200, - (-400, 0): 250, - (400, 0): 300, - } - - end_flows = { - (0, -400): 200, - (0, 400): 1, - (-400, 0): 200, - (400, 0): 200, - } - - return CustomEndpointFlowStrategy(start_flows=start_flows, end_flows=end_flows) - - -def double_intersec_bal() -> Tuple[Graph, FlowStrategy]: - return double_intersec_g(), double_intersec_f_bal() +def double_intersec_bal_2() -> Tuple[Graph, FlowStrategy]: + strategy = UniformFlowStrategy(interval=10) + return double_intersec_g(), strategy -def double_intersec_lop() -> Tuple[Graph, FlowStrategy]: - return double_intersec_g(), double_intersec_f_lop() +def double_intersec_lop_2() -> Tuple[Graph, FlowStrategy]: + return None # NOT YET IMPLEMENTED def double_intersec_g() -> Graph: @@ -113,47 +70,3 @@ def double_intersec_g() -> Graph: ] return Graph(vertices=vertices, edges=edges) - - -def double_intersec_f_bal() -> CustomEndpointFlowStrategy: - start_flows = { - (0, 400): 8, - (400, 400): 7, - (800, 0): 5, - (400, -400): 6, - (0, -400): 8, - (-400, 0): 6, - } - - end_flows = { - (0, 400): 9, - (400, 400): 10, - (800, 0): 7, - (400, -400): 5, - (0, -400): 6, - (-400, 0): 8, - } - - return CustomEndpointFlowStrategy(start_flows=start_flows, end_flows=end_flows) - - -def double_intersec_f_lop() -> CustomEndpointFlowStrategy: - start_flows = { - (0, 400): 200, - (400, 400): 250, - (800, 0): 200, - (400, -400): 300, - (0, -400): 200, - (-400, 0): 1, - } - - end_flows = { - (0, 400): 250, - (400, 400): 250, - (800, 0): 1, - (400, -400): 300, - (0, -400): 200, - (-400, 0): 200, - } - - return CustomEndpointFlowStrategy(start_flows=start_flows, end_flows=end_flows) From 6c26793ad7a36e8e06ad17eef5336ba1ca1bd391 Mon Sep 17 00:00:00 2001 From: char-tan Date: Sat, 14 Jan 2023 15:16:06 +0000 Subject: [PATCH 28/38] add lopsided double intersec --- bo_experiments.py | 22 ++++++++++++---------- simulation_builder/scenarios.py | 9 ++++++++- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/bo_experiments.py b/bo_experiments.py index 29ccaa1..cdc766b 100644 --- a/bo_experiments.py +++ b/bo_experiments.py @@ -7,15 +7,15 @@ from emulation.metrics import CompletedJourneysMetric, WaitTimeMetric from emulation.utils import run_simulation from plot import plot_metric_results -from simulation_builder.scenarios import single_intersec_lop_2, single_intersec_bal_2, double_intersec_bal_2 +from simulation_builder.scenarios import single_intersec_lop_2, single_intersec_bal_2, double_intersec_lop_2, double_intersec_bal_2 if __name__ == "__main__": np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) interval = (1, 30) - # define id - scenario = 'DB2' + # DEFINE ID + scenario = 'DL2' metric_name = 'CJ' lengthscale = 2 @@ -24,12 +24,14 @@ num_init_points = 1 - # id -> config + # ID -> CONFIG if scenario == 'SL2': g, strategy = single_intersec_lop_2() - if scenario == 'SB2': + elif scenario == 'SB2': g, strategy = single_intersec_bal_2() - if scenario == 'DB2': + elif scenario == 'DL2': + g, strategy = double_intersec_lop_2() + elif scenario == 'DB2': g, strategy = double_intersec_bal_2() if kernel_name == 'M52': @@ -46,20 +48,20 @@ kernel_kwargs = {'variance': variance, 'lengthscale': lengthscale} - # create emulator + # CREATE EMULATOR e = Emulator(g, strategy) - # run bayesopt + # RUN BAYESOPT results, bo_model = e.bayes_opt( kernel_func, kernel_kwargs, metric, interval=interval, - max_iterations=10, + max_iterations=1, progress_N=200, num_init_points=num_init_points) - # save data + # SAVE DATA file_id = f'BO_{scenario}_{metric_name}_{kernel_name}_{variance}_{lengthscale}_{num_init_points}' results.to_csv(f'csv_files/{file_id}.csv') plt = plot_metric_results(results, f'plots/{file_id}.png') diff --git a/simulation_builder/scenarios.py b/simulation_builder/scenarios.py index de06690..b1d295e 100644 --- a/simulation_builder/scenarios.py +++ b/simulation_builder/scenarios.py @@ -44,7 +44,14 @@ def double_intersec_bal_2() -> Tuple[Graph, FlowStrategy]: def double_intersec_lop_2() -> Tuple[Graph, FlowStrategy]: - return None # NOT YET IMPLEMENTED + strategy = ManualFlowStrategy({ + ((-400, 0), (800, 0)): 4, + ((800, 0), (-400, 0)): 4, + ((0, -400), (0, 400)): 100, + ((400, 400), (400, -400)): 100, + }) + + return double_intersec_g(), strategy def double_intersec_g() -> Graph: From 5fb114cf27c3943a4f6246a33da4980330461c3a Mon Sep 17 00:00:00 2001 From: char-tan Date: Sat, 14 Jan 2023 17:11:57 +0000 Subject: [PATCH 29/38] move seed out of emulator --- bo_experiments.py | 14 ++++++++------ emulation/emulator.py | 5 ----- main.py | 2 ++ sensitivity_experiments.py | 2 ++ 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/bo_experiments.py b/bo_experiments.py index cdc766b..0995933 100644 --- a/bo_experiments.py +++ b/bo_experiments.py @@ -12,17 +12,19 @@ if __name__ == "__main__": np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) + np.random.seed(42) + interval = (1, 30) # DEFINE ID - scenario = 'DL2' + scenario = 'DB2' metric_name = 'CJ' lengthscale = 2 variance = 2 - kernel_name = 'M52' + kernel_name = 'RQ' - num_init_points = 1 + num_init_points = 100 # ID -> CONFIG if scenario == 'SL2': @@ -39,7 +41,7 @@ elif kernel_name == 'RBF': kernel_func = RBF elif kernel_name == 'RQ': - kernel_func = RatQaud + kernel_func = RatQuad if metric_name == 'WT': metric = WaitTimeMetric @@ -57,8 +59,8 @@ kernel_kwargs, metric, interval=interval, - max_iterations=1, - progress_N=200, + max_iterations=10, + progress_N=100, num_init_points=num_init_points) # SAVE DATA diff --git a/emulation/emulator.py b/emulation/emulator.py index ac71caf..54b1e9f 100644 --- a/emulation/emulator.py +++ b/emulation/emulator.py @@ -101,8 +101,6 @@ def bayes_opt( print(f'\nbayesian optimisation on {metric().name}, interval {interval} with {num_init_points} init points') - np.random.seed(42) - sim = Simulator(self._g, metric, self._strategy, self._time_period, self._sim_iterations) target_function = UserFunctionWrapper(sim.evaluate, extra_output_names=['raw metric']) @@ -165,8 +163,6 @@ def bayes_opt( def sensitivity(self, bo_model, interval: Tuple[float, float], num_mc: int = 10000): - np.random.seed(42) - parameter_list = [ContinuousParameter(f"x{i}", *interval) for i in range(self._num_params)] senstivity = MonteCarloSensitivity(model=bo_model, input_domain=ParameterSpace(parameter_list)) @@ -186,7 +182,6 @@ def grid_search_opt(self, metric, interval: Tuple[float, float], steps_per_axis: """Evaluates target_function on all combinations of parameters taken from the same interval""" print(f'\ngrid search on {metric().name}, interval {interval} with {steps_per_axis**self._num_params} grid points') - np.random.seed(42) sim = Simulator(self._g, metric, self._strategy, self._time_period, self._sim_iterations) diff --git a/main.py b/main.py index 525596d..325bbb5 100644 --- a/main.py +++ b/main.py @@ -15,6 +15,8 @@ interval = (0.1, 30) + np.random.seed(42) + for scenario in ['SL2', 'SB2']: for kernel_name in ['RBF', 'M52']: diff --git a/sensitivity_experiments.py b/sensitivity_experiments.py index a8b79b9..ba6e883 100644 --- a/sensitivity_experiments.py +++ b/sensitivity_experiments.py @@ -10,6 +10,8 @@ if __name__ == "__main__": np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) + np.random.seed(42) + interval = (1, 30) for scenario in ['SL2', 'SB2']: From 409dfe97d9a0fbe2a85e2b9bf0746f76bbfca4cb Mon Sep 17 00:00:00 2001 From: char-tan Date: Sat, 14 Jan 2023 17:13:08 +0000 Subject: [PATCH 30/38] slow down DB2 --- simulation_builder/scenarios.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simulation_builder/scenarios.py b/simulation_builder/scenarios.py index b1d295e..fbae013 100644 --- a/simulation_builder/scenarios.py +++ b/simulation_builder/scenarios.py @@ -39,7 +39,7 @@ def single_intersec_g() -> Graph: def double_intersec_bal_2() -> Tuple[Graph, FlowStrategy]: - strategy = UniformFlowStrategy(interval=10) + strategy = UniformFlowStrategy(interval=20) return double_intersec_g(), strategy From 62bc421aff4efd4873959c62eb0ad0caebe927ce Mon Sep 17 00:00:00 2001 From: char-tan Date: Sat, 14 Jan 2023 19:31:15 +0000 Subject: [PATCH 31/38] fix plot, change bo_experiments to support min/max cumaltive --- bo_experiments.py | 101 +++++++++++++++++++++++++--------------------- plot.py | 12 ++++-- 2 files changed, 62 insertions(+), 51 deletions(-) diff --git a/bo_experiments.py b/bo_experiments.py index 0995933..33e8782 100644 --- a/bo_experiments.py +++ b/bo_experiments.py @@ -17,55 +17,62 @@ interval = (1, 30) # DEFINE ID - scenario = 'DB2' - metric_name = 'CJ' + metric_name = 'WT' lengthscale = 2 variance = 2 kernel_name = 'RQ' - num_init_points = 100 - - # ID -> CONFIG - if scenario == 'SL2': - g, strategy = single_intersec_lop_2() - elif scenario == 'SB2': - g, strategy = single_intersec_bal_2() - elif scenario == 'DL2': - g, strategy = double_intersec_lop_2() - elif scenario == 'DB2': - g, strategy = double_intersec_bal_2() - - if kernel_name == 'M52': - kernel_func = Matern52 - elif kernel_name == 'RBF': - kernel_func = RBF - elif kernel_name == 'RQ': - kernel_func = RatQuad - - if metric_name == 'WT': - metric = WaitTimeMetric - elif metric_name == 'CJ': - metric = CompletedJourneysMetric - - kernel_kwargs = {'variance': variance, 'lengthscale': lengthscale} - - # CREATE EMULATOR - e = Emulator(g, strategy) - - # RUN BAYESOPT - results, bo_model = e.bayes_opt( - kernel_func, - kernel_kwargs, - metric, - interval=interval, - max_iterations=10, - progress_N=100, - num_init_points=num_init_points) - - # SAVE DATA - file_id = f'BO_{scenario}_{metric_name}_{kernel_name}_{variance}_{lengthscale}_{num_init_points}' - results.to_csv(f'csv_files/{file_id}.csv') - plt = plot_metric_results(results, f'plots/{file_id}.png') - with open(f'bo_models/{file_id}.obj', 'wb') as f: - pickle.dump(bo_model, f) + for scenario in ['DL2', 'DB2']: + + for num_init_points in [1]: + + # ID -> CONFIG + if scenario == 'SL2': + g, strategy = single_intersec_lop_2() + elif scenario == 'SB2': + g, strategy = single_intersec_bal_2() + elif scenario == 'DL2': + g, strategy = double_intersec_lop_2() + elif scenario == 'DB2': + g, strategy = double_intersec_bal_2() + + if kernel_name == 'M52': + kernel_func = Matern52 + elif kernel_name == 'RBF': + kernel_func = RBF + elif kernel_name == 'RQ': + kernel_func = RatQuad + + if metric_name == 'WT': + metric = WaitTimeMetric + elif metric_name == 'CJ': + metric = CompletedJourneysMetric + + kernel_kwargs = {'variance': variance, 'lengthscale': lengthscale} + + # CREATE EMULATOR + e = Emulator(g, strategy) + + # RUN BAYESOPT + results, bo_model = e.bayes_opt( + kernel_func, + kernel_kwargs, + metric, + interval=interval, + max_iterations=10, + progress_N=20, + num_init_points=num_init_points) + + # SAVE DATA + file_id = f'BO_{scenario}_{metric_name}_{kernel_name}_{variance}_{lengthscale}_{num_init_points}' + results.to_csv(f'csv_files/{file_id}.csv') + + if metric_name == 'CJ': + minimisation=False + else: + minimisation=True + + plt = plot_metric_results(results, f'plots/{file_id}.png', minimisation) + with open(f'bo_models/{file_id}.obj', 'wb') as f: + pickle.dump(bo_model, f) diff --git a/plot.py b/plot.py index ed78d07..7c56b90 100644 --- a/plot.py +++ b/plot.py @@ -3,16 +3,20 @@ import pandas as pd -def plot_metric_results(df, file_name): +def plot_metric_results(df, file_name, minimisation=True): metric_name = df.columns[-1] results = df[metric_name] - cummin = results.cummin() + + if minimisation: + cuma = results.cummin() + else: + cuma = results.cummax() plt.figure() plt.style.use('ggplot') plt.rc('font', family='serif') plt.plot(np.arange(len(results)), results) - plt.plot(np.arange(len(results)), cummin) + plt.plot(np.arange(len(results)), cuma) plt.legend(['actual', 'cumulative']) plt.title(metric_name.capitalize()) plt.savefig(file_name) @@ -42,4 +46,4 @@ def plot_sensitivity(main_effects, total_effects, file_name): plt.savefig(file_name) - return plt \ No newline at end of file + return plt From c93c0597ff15d59768f0a5f7778974e0dc5ae1e2 Mon Sep 17 00:00:00 2001 From: char-tan Date: Sat, 14 Jan 2023 19:33:58 +0000 Subject: [PATCH 32/38] change back to CJ --- bo_experiments.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bo_experiments.py b/bo_experiments.py index 33e8782..5a2e42a 100644 --- a/bo_experiments.py +++ b/bo_experiments.py @@ -17,7 +17,7 @@ interval = (1, 30) # DEFINE ID - metric_name = 'WT' + metric_name = 'CJ' lengthscale = 2 variance = 2 @@ -25,7 +25,7 @@ for scenario in ['DL2', 'DB2']: - for num_init_points in [1]: + for num_init_points in [5, 25, 50]: # ID -> CONFIG if scenario == 'SL2': @@ -60,7 +60,7 @@ kernel_kwargs, metric, interval=interval, - max_iterations=10, + max_iterations=100, progress_N=20, num_init_points=num_init_points) From 4ad825aa25640f55426064f0270e0c799b6a5944 Mon Sep 17 00:00:00 2001 From: char-tan Date: Sat, 14 Jan 2023 19:51:07 +0000 Subject: [PATCH 33/38] file_id as plot title --- bo_experiments.py | 4 ++-- plot.py | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/bo_experiments.py b/bo_experiments.py index 5a2e42a..1515811 100644 --- a/bo_experiments.py +++ b/bo_experiments.py @@ -60,7 +60,7 @@ kernel_kwargs, metric, interval=interval, - max_iterations=100, + max_iterations=10, progress_N=20, num_init_points=num_init_points) @@ -73,6 +73,6 @@ else: minimisation=True - plt = plot_metric_results(results, f'plots/{file_id}.png', minimisation) + plt = plot_metric_results(results, file_id, minimisation) with open(f'bo_models/{file_id}.obj', 'wb') as f: pickle.dump(bo_model, f) diff --git a/plot.py b/plot.py index 7c56b90..5b612fc 100644 --- a/plot.py +++ b/plot.py @@ -3,9 +3,11 @@ import pandas as pd -def plot_metric_results(df, file_name, minimisation=True): +def plot_metric_results(df, file_id, minimisation=True): metric_name = df.columns[-1] results = df[metric_name] + + file_name = f'plots/{file_id}.png' if minimisation: cuma = results.cummin() @@ -18,7 +20,7 @@ def plot_metric_results(df, file_name, minimisation=True): plt.plot(np.arange(len(results)), results) plt.plot(np.arange(len(results)), cuma) plt.legend(['actual', 'cumulative']) - plt.title(metric_name.capitalize()) + plt.title(file_id) plt.savefig(file_name) return plt From 1eaf53c3122324f9412e839c66644a6733b36690 Mon Sep 17 00:00:00 2001 From: char-tan Date: Sat, 14 Jan 2023 23:50:56 +0000 Subject: [PATCH 34/38] reseeding bo_experiments --- bo_experiments.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/bo_experiments.py b/bo_experiments.py index 1515811..e86549a 100644 --- a/bo_experiments.py +++ b/bo_experiments.py @@ -12,20 +12,20 @@ if __name__ == "__main__": np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) - np.random.seed(42) - + scenario = 'DB2' interval = (1, 30) # DEFINE ID metric_name = 'CJ' - - lengthscale = 2 variance = 2 - kernel_name = 'RQ' + lengthscale = 2 + num_init_points = 50 + + for kernel_name in ['M52', 'RBF', 'RQ']: - for scenario in ['DL2', 'DB2']: + for seed in [1, 42, 98]: - for num_init_points in [5, 25, 50]: + np.random.seed(seed) # ID -> CONFIG if scenario == 'SL2': @@ -60,12 +60,12 @@ kernel_kwargs, metric, interval=interval, - max_iterations=10, - progress_N=20, + max_iterations=100, + progress_N=50, num_init_points=num_init_points) # SAVE DATA - file_id = f'BO_{scenario}_{metric_name}_{kernel_name}_{variance}_{lengthscale}_{num_init_points}' + file_id = f'BO_{scenario}_{metric_name}_{kernel_name}_{variance}_{lengthscale}_{num_init_points}_{seed}' results.to_csv(f'csv_files/{file_id}.csv') if metric_name == 'CJ': From f51153c0601d6714c2c7fb238dc58e5d24ac3f38 Mon Sep 17 00:00:00 2001 From: yu202147657 <91469100+yu202147657@users.noreply.github.com> Date: Sat, 14 Jan 2023 23:53:54 +0000 Subject: [PATCH 35/38] change filename to prevent overwriting of bo plots --- sensitivity_experiments.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/sensitivity_experiments.py b/sensitivity_experiments.py index ba6e883..681bdfd 100644 --- a/sensitivity_experiments.py +++ b/sensitivity_experiments.py @@ -2,8 +2,7 @@ import pickle from emulation.emulator import Emulator -from simulation_builder.scenarios import single_intersec_bal, single_intersec_lop, double_intersec_bal, \ - double_intersec_lop, single_intersec_lop_2, single_intersec_bal_2 +from simulation_builder.scenarios import double_intersec_bal_2, double_intersec_lop_2 from plot import plot_sensitivity @@ -14,31 +13,30 @@ interval = (1, 30) - for scenario in ['SL2', 'SB2']: + for scenario in ['DB2', 'DL2']: - for kernel_name in ['RBF', 'RQ', 'M52']: + for kernel_name in ['M52', 'RQ', 'RBF']: - if scenario == 'SL2': - g, strategy = single_intersec_lop_2() - if scenario == 'SB2': - g, strategy = single_intersec_bal_2() + if scenario == 'DL2': + g, strategy = double_intersec_lop_2() + elif scenario == 'DB2': + g, strategy = double_intersec_bal_2() e = Emulator(g, strategy) # define id - lengthscale = 1 - num_init_points = 1 + lengthscale = 2 + num_init_points = 50 metric_name = 'WT' variance = 2 file_id = f'BO_{scenario}_{metric_name}_{kernel_name}_{variance}_{lengthscale}_{num_init_points}' - file_id = 'BO_SL2_CJ_M52_2_1_1' with open(f'bo_models/{file_id}.obj', 'rb') as f: bo_model = pickle.load(f) main_effects, total_effects = e.sensitivity(bo_model, interval=interval) - plt = plot_sensitivity(main_effects, total_effects, f'plots/{file_id}.png') + plt = plot_sensitivity(main_effects, total_effects, f'sensitivity_plots/{file_id}.png') From eb4c37c381bb841ec7705b530a0ab3104cd5c355 Mon Sep 17 00:00:00 2001 From: char-tan Date: Sun, 15 Jan 2023 17:19:04 +0000 Subject: [PATCH 36/38] code for kernel finding + evaluation --- bo_experiments.py | 119 +++++++++++++++++++++++--------------------- evaluate_kernels.py | 114 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 176 insertions(+), 57 deletions(-) create mode 100644 evaluate_kernels.py diff --git a/bo_experiments.py b/bo_experiments.py index e86549a..50a4ce6 100644 --- a/bo_experiments.py +++ b/bo_experiments.py @@ -12,67 +12,72 @@ if __name__ == "__main__": np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) - scenario = 'DB2' interval = (1, 30) # DEFINE ID - metric_name = 'CJ' variance = 2 lengthscale = 2 num_init_points = 50 - for kernel_name in ['M52', 'RBF', 'RQ']: - - for seed in [1, 42, 98]: - - np.random.seed(seed) - - # ID -> CONFIG - if scenario == 'SL2': - g, strategy = single_intersec_lop_2() - elif scenario == 'SB2': - g, strategy = single_intersec_bal_2() - elif scenario == 'DL2': - g, strategy = double_intersec_lop_2() - elif scenario == 'DB2': - g, strategy = double_intersec_bal_2() - - if kernel_name == 'M52': - kernel_func = Matern52 - elif kernel_name == 'RBF': - kernel_func = RBF - elif kernel_name == 'RQ': - kernel_func = RatQuad - - if metric_name == 'WT': - metric = WaitTimeMetric - elif metric_name == 'CJ': - metric = CompletedJourneysMetric - - kernel_kwargs = {'variance': variance, 'lengthscale': lengthscale} - - # CREATE EMULATOR - e = Emulator(g, strategy) - - # RUN BAYESOPT - results, bo_model = e.bayes_opt( - kernel_func, - kernel_kwargs, - metric, - interval=interval, - max_iterations=100, - progress_N=50, - num_init_points=num_init_points) - - # SAVE DATA - file_id = f'BO_{scenario}_{metric_name}_{kernel_name}_{variance}_{lengthscale}_{num_init_points}_{seed}' - results.to_csv(f'csv_files/{file_id}.csv') - - if metric_name == 'CJ': - minimisation=False - else: - minimisation=True - - plt = plot_metric_results(results, file_id, minimisation) - with open(f'bo_models/{file_id}.obj', 'wb') as f: - pickle.dump(bo_model, f) + for metric_name in ['WT']: + + for scenario in ['DB2', 'DL2']: + + for kernel_name in ['M52', 'RBF', 'RQ']: + + for seed in [1, 42, 98]: + + file_id = f'BO_{scenario}_{metric_name}_{kernel_name}_{variance}_{lengthscale}_{num_init_points}_{seed}' + + print(file_id) + + np.random.seed(seed) + + # ID -> CONFIG + if scenario == 'SL2': + g, strategy = single_intersec_lop_2() + elif scenario == 'SB2': + g, strategy = single_intersec_bal_2() + elif scenario == 'DL2': + g, strategy = double_intersec_lop_2() + elif scenario == 'DB2': + g, strategy = double_intersec_bal_2() + + if kernel_name == 'M52': + kernel_func = Matern52 + elif kernel_name == 'RBF': + kernel_func = RBF + elif kernel_name == 'RQ': + kernel_func = RatQuad + + if metric_name == 'WT': + metric = WaitTimeMetric + elif metric_name == 'CJ': + metric = CompletedJourneysMetric + + kernel_kwargs = {'variance': variance, 'lengthscale': lengthscale} + + # CREATE EMULATOR + e = Emulator(g, strategy) + + # RUN BAYESOPT + results, bo_model = e.bayes_opt( + kernel_func, + kernel_kwargs, + metric, + interval=interval, + max_iterations=100, + progress_N=50, + num_init_points=num_init_points) + + # SAVE DATA + results.to_csv(f'csv_files/{file_id}.csv') + + if metric_name == 'CJ': + minimisation=False + else: + minimisation=True + + plot_metric_results(results, file_id, minimisation) + with open(f'bo_models/{file_id}.obj', 'wb') as f: + pickle.dump(bo_model, f) diff --git a/evaluate_kernels.py b/evaluate_kernels.py new file mode 100644 index 0000000..f23ca74 --- /dev/null +++ b/evaluate_kernels.py @@ -0,0 +1,114 @@ +import pandas +import os +import re + +def mean_best(files, metric): + + data = {'RBF': [], 'RQ': [], 'M52': []} + + assert not len(files) % len(data) + + files_per_kernel = (len(files) / len(data)) + + for f in files: + df = pandas.read_csv('csv_files/'+f, index_col=0) + + if metric == 'WT': + best = df['average steps waiting'].min() + else: + best = df['completed journeys'].max() + + if 'RBF' in f: + data['RBF'] += [best] + elif 'RQ' in f: + data['RQ'] += [best] + elif 'M52' in f: + data['M52'] += [best] + + return(data) + + +def convergence(files, metric, eps, N): + + data = {'RBF': [], 'RQ': [], 'M52': []} + + assert not len(files) % len(data) + + files_per_kernel = (len(files) / len(data)) + + for f in files: + df = pandas.read_csv('csv_files/'+f, index_col=0) + + converged = False + prev = None + count = 0 + + for i, row in df.iterrows(): + + if metric == 'WT': + current = row['average steps waiting'] + else: + current = row['completed journeys'] + #print('curr', current) + #print('prev', prev) + + if prev is not None: + + diff = abs(current - prev) + + if diff < eps: + count += 1 + #print('diff', diff) + #print('count', count) + #breakpoint() + else: + count = 0 + + if count >= N: + if 'RBF' in f: + data['RBF'] += [i] + elif 'RQ' in f: + data['RQ'] += [i] + elif 'M52' in f: + data['M52'] += [i] + + converged = True + break + + prev = current + + if not converged: + if 'RBF' in f: + data['RBF'] += ['DNC'] + elif 'RQ' in f: + data['RQ'] += ['DNC'] + elif 'M52' in f: + data['M52'] += ['DNC'] + + return(data) + + +csv_files = sorted(os.listdir('csv_files/')) + +DL2_files = [f for f in csv_files if 'DL2' in f] +DB2_files = [f for f in csv_files if 'DB2' in f] + +DL2_CJ_files = [f for f in DL2_files if 'CJ' in f] +DB2_CJ_files = [f for f in DB2_files if 'CJ' in f] + +DL2_WT_files = [f for f in DL2_files if 'WT' in f] +DB2_WT_files = [f for f in DB2_files if 'WT' in f] + +print('DL2 CJ', convergence(DL2_CJ_files, 'CJ', 3, 3)) +print('DB2 CJ', convergence(DB2_CJ_files, 'CJ', 3, 3)) +print('DL2 WT', convergence(DL2_WT_files, 'WT', 0.2, 3)) +print('DB2 WT', convergence(DB2_WT_files, 'WT', 1, 3)) + +print() + +print('DL2 CJ', mean_best(DL2_CJ_files, 'CJ')) +print('DB2 CJ', mean_best(DB2_CJ_files, 'CJ')) +print('DL2 WT', mean_best(DL2_WT_files, 'WT')) +print('DB2 WT', mean_best(DB2_WT_files, 'WT')) +breakpoint() + From e2869b588364404fa6dfde9cb777c8509e0cc4ed Mon Sep 17 00:00:00 2001 From: maximwebb Date: Mon, 16 Jan 2023 13:00:10 +0000 Subject: [PATCH 37/38] Add Cambridge scenario, create CompositeFlowStrategy class --- simulation_builder/flows.py | 14 ++++++++++++++ simulation_builder/scenarios.py | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/simulation_builder/flows.py b/simulation_builder/flows.py index ebdf07a..a9554f3 100644 --- a/simulation_builder/flows.py +++ b/simulation_builder/flows.py @@ -109,6 +109,20 @@ def gen_flows(self, route: List[Tuple[int, int]]) -> List[Flow]: return [] +class CompositeFlowStrategy(FlowStrategy): + """ + Takes a list of flow strategies and combines them into one. + """ + def __init__(self, strategies: List[FlowStrategy]): + self._strategies = strategies + + def gen_flows(self, route: List[Tuple[int, int]]) -> List[Flow]: + flows = [] + for strategy in self._strategies: + flows += strategy.gen_flows(route) + return flows + + def graph_to_flow(g: Graph, strategy: FlowStrategy = UniformFlowStrategy()) -> List[Dict]: paths = all_pairs_shortest_paths(g) flows = [] diff --git a/simulation_builder/scenarios.py b/simulation_builder/scenarios.py index fbae013..a046aeb 100644 --- a/simulation_builder/scenarios.py +++ b/simulation_builder/scenarios.py @@ -1,7 +1,8 @@ from typing import Tuple from simulation_builder.graph import Graph -from simulation_builder.flows import CustomEndpointFlowStrategy, FlowStrategy, ManualFlowStrategy, UniformFlowStrategy +from simulation_builder.flows import CustomEndpointFlowStrategy, FlowStrategy, ManualFlowStrategy, UniformFlowStrategy, \ + CompositeFlowStrategy def single_intersec_lop_2() -> Tuple[Graph, FlowStrategy]: @@ -77,3 +78,33 @@ def double_intersec_g() -> Graph: ] return Graph(vertices=vertices, edges=edges) + + +def cambridge_scenario() -> Tuple[Graph, FlowStrategy]: + vertices = [(0, 0), (400, 0), (0, 200), (400, 200), (800, 200), (1000, 200), (0, 900), (400, 900), + (800, 900), (1000, 900), (400, 1300)] + + edges = [ + ((0, 200), (400, 200)), + ((400, 200), (800, 200)), + ((800, 200), (1000, 200)), + ((0, 900), (400, 900)), + ((400, 900), (800, 900)), + ((800, 900), (1000, 900)), + + ((0, 0), (0, 200)), + ((400, 0), (400, 200)), + ((0, 200), (0, 900)), + ((400, 200), (400, 900)), + ((800, 200), (800, 900)), + ((400, 900), (400, 1300)), + ] + + strategy = CompositeFlowStrategy([ + UniformFlowStrategy(interval=20), + ManualFlowStrategy({ + ((400, 0), (400, 1300)): 15 + }) + ]) + + return Graph(vertices, edges), strategy From 4197f906948360d58019c60179fa3d17d3ad7d12 Mon Sep 17 00:00:00 2001 From: maximwebb Date: Thu, 2 Feb 2023 15:38:40 +0000 Subject: [PATCH 38/38] Code for recording simulations --- bo_experiments.py | 116 ++++++++++++----------------- emulation/emulator.py | 2 +- main.py | 125 +++++++++++++++++++++----------- plot.py | 15 ++-- sensitivity_experiments.py | 38 +++++----- simulation_builder/roadnets.py | 3 +- simulation_builder/scenarios.py | 6 +- 7 files changed, 158 insertions(+), 147 deletions(-) diff --git a/bo_experiments.py b/bo_experiments.py index 50a4ce6..9462188 100644 --- a/bo_experiments.py +++ b/bo_experiments.py @@ -1,5 +1,7 @@ import numpy as np import pickle +import matplotlib.pyplot as plt +import pandas as pd from GPy.kern import Matern52, RBF, RatQuad @@ -7,77 +9,55 @@ from emulation.metrics import CompletedJourneysMetric, WaitTimeMetric from emulation.utils import run_simulation from plot import plot_metric_results -from simulation_builder.scenarios import single_intersec_lop_2, single_intersec_bal_2, double_intersec_lop_2, double_intersec_bal_2 +from simulation_builder.scenarios import single_intersec_lop_2, single_intersec_bal_2, double_intersec_lop_2, \ + double_intersec_bal_2, cambridge_scenario + if __name__ == "__main__": np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) - interval = (1, 30) - - # DEFINE ID - variance = 2 - lengthscale = 2 + interval = (10, 30) + N = 300 num_init_points = 50 - for metric_name in ['WT']: - - for scenario in ['DB2', 'DL2']: - - for kernel_name in ['M52', 'RBF', 'RQ']: - - for seed in [1, 42, 98]: - - file_id = f'BO_{scenario}_{metric_name}_{kernel_name}_{variance}_{lengthscale}_{num_init_points}_{seed}' - - print(file_id) - - np.random.seed(seed) - - # ID -> CONFIG - if scenario == 'SL2': - g, strategy = single_intersec_lop_2() - elif scenario == 'SB2': - g, strategy = single_intersec_bal_2() - elif scenario == 'DL2': - g, strategy = double_intersec_lop_2() - elif scenario == 'DB2': - g, strategy = double_intersec_bal_2() - - if kernel_name == 'M52': - kernel_func = Matern52 - elif kernel_name == 'RBF': - kernel_func = RBF - elif kernel_name == 'RQ': - kernel_func = RatQuad - - if metric_name == 'WT': - metric = WaitTimeMetric - elif metric_name == 'CJ': - metric = CompletedJourneysMetric - - kernel_kwargs = {'variance': variance, 'lengthscale': lengthscale} - - # CREATE EMULATOR - e = Emulator(g, strategy) - - # RUN BAYESOPT - results, bo_model = e.bayes_opt( - kernel_func, - kernel_kwargs, - metric, - interval=interval, - max_iterations=100, - progress_N=50, - num_init_points=num_init_points) - - # SAVE DATA - results.to_csv(f'csv_files/{file_id}.csv') - - if metric_name == 'CJ': - minimisation=False - else: - minimisation=True - - plot_metric_results(results, file_id, minimisation) - with open(f'bo_models/{file_id}.obj', 'wb') as f: - pickle.dump(bo_model, f) + # metric = WaitTimeMetric + # metric_name = "WT" + metric = CompletedJourneysMetric + metric_name = "CJ" + + plt.style.use('ggplot') + plt.rc('font', family='serif') + file_id = f'BO_cambridge_lop_{metric_name}_{num_init_points}' + + print(file_id) + g, strategy = cambridge_scenario() + + kernel_func = RatQuad + kernel_kwargs = {'variance': 1, 'lengthscale': 2} + + e = Emulator(g, strategy) + + results, bo_model = e.bayes_opt( + kernel_func, + kernel_kwargs, + metric, + interval=interval, + max_iterations=N, + progress_N=300, + num_init_points=num_init_points) + + with open(f'bo_models/{file_id}.obj', 'wb') as f: + pickle.dump(bo_model, f) + + results.to_csv(f'csv_files/{file_id}.csv') + data = results[results.columns[-1]] + + plt.plot(np.arange(len(data) - num_init_points) + num_init_points, data[num_init_points:], zorder=1) + # plt.scatter(num_init_points, data[num_init_points], s=100, zorder=2) + print(f"{metric_name} Results: ") + print(data[num_init_points:]) + # plt.legend(fontsize="large") + plt.xlabel("Iterations") + plt.ylabel("Completed Journeys" if metric_name == "CJ" else "Average Wait Time") + plt.savefig(f"plots/kernel_tuning/cambridge_{metric_name}_", bbox_inches='tight', pad_inches=0.2) + plt.close() diff --git a/emulation/emulator.py b/emulation/emulator.py index 54b1e9f..c66c3b8 100644 --- a/emulation/emulator.py +++ b/emulation/emulator.py @@ -161,7 +161,7 @@ def bayes_opt( return results_to_df(x, self._time_period, raw_metric, metric().name, num_init_points), bo_loop.model - def sensitivity(self, bo_model, interval: Tuple[float, float], num_mc: int = 10000): + def sensitivity(self, bo_model, interval: Tuple[float, float], num_mc: int = 50000): parameter_list = [ContinuousParameter(f"x{i}", *interval) for i in range(self._num_params)] diff --git a/main.py b/main.py index 325bbb5..bf4c26c 100644 --- a/main.py +++ b/main.py @@ -1,64 +1,101 @@ +import matplotlib.pyplot as plt import numpy as np import pickle -from GPy.kern import Matern52, RBF +import pandas as pd +from GPy.kern import Matern52, RBF, RatQuad from emulation.emulator import Emulator from emulation.metrics import CompletedJourneysMetric, WaitTimeMetric from emulation.utils import run_simulation from plot import plot_metric_results -from simulation_builder.scenarios import single_intersec_bal, single_intersec_lop, double_intersec_bal, \ - double_intersec_lop, single_intersec_lop_2, single_intersec_bal_2 +from simulation_builder.scenarios import single_intersec_lop_2, single_intersec_bal_2, cambridge_scenario, \ + double_intersec_bal_2 -if __name__ == "__main__": - np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) - - interval = (0.1, 30) - - np.random.seed(42) - for scenario in ['SL2', 'SB2']: - for kernel_name in ['RBF', 'M52']: +def plot_cum(file_name, new_name=""): + df = pd.read_csv(file_name) - # define id - scenario = 'SL2' - num_init_points = 1 - metric_name = 'WT' - variance = 2 + met_name = df.columns[-1] + res = df[met_name] - # id -> config - if scenario == 'SL2': - g, strategy = single_intersec_lop_2() - if scenario == 'SB2': - g, strategy = single_intersec_bal_2() + file = f'plots/cambridge/cum_{new_name}.png' - if kernel_name == 'M52': - kernel_func = Matern52 - elif kernel_name == 'RBF': - kernel_func = RBF + if False: + cuma = res.cummin() + else: + cuma = res.cummax() - if metric_name == 'WT': - metric = WaitTimeMetric + plt.figure() + plt.style.use('ggplot') + plt.rc('font', family='serif') + plt.plot(np.arange(len(res)), res, ) + plt.plot(np.arange(len(res)), cuma) + plt.legend(['Actual', 'Cumulative']) + plt.xlabel("Iterations") + plt.ylabel("Completed Journeys") + # plt.title(file_id) + plt.savefig(file, bbox_inches='tight', pad_inches=0.2) - kernel_kwargs = {'variance': variance} - e = Emulator(g, strategy) - - results, bo_model = e.bayes_opt( - kernel_func, - kernel_kwargs, - metric, - interval=interval, - max_iterations=250, - progress_N=500, - num_init_points=num_init_points) - - file_id = f'BO_{scenario}_{metric_name}_{kernel_name}_{variance}_{num_init_points}' +if __name__ == "__main__": + np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)}) - results.to_csv(f'csv_files/{file_id}.csv') + interval = (0.1, 30) - plot_metric_results(results, f'plots/{file_id}.png') + np.random.seed(42) - with open(f'bo_models/{file_id}.obj', 'wb') as f: - pickle.dump(bo_model, f) + # g, strategy = double_intersec_bal_2() + g, strategy = cambridge_scenario() + run_simulation(g, strategy) + # plot_cum("csv_files/BO_cambridge_lop_CJ_50.csv", "CJ_lop") + + # for scenario in ['DB2']: + # for kernel_name in ['RQ', 'RBF']: + # + # # define id + # scenario = 'SL2' + # num_init_points = 1 + # metric_name = 'WT' + # variance = 2 + # + # # id -> config + # if scenario == 'SL2': + # g, strategy = single_intersec_lop_2() + # if scenario == 'SB2': + # g, strategy = single_intersec_bal_2() + # + # if kernel_name == 'M52': + # kernel_func = Matern52 + # elif kernel_name == 'RBF': + # kernel_func = RBF + # else: + # kernel_func = RatQuad + # + # if metric_name == 'WT': + # metric = WaitTimeMetric + # else: + # metric = CompletedJourneysMetric + # + # kernel_kwargs = {'variance': variance} + # + # e = Emulator(g, strategy) + # + # results, bo_model = e.bayes_opt( + # kernel_func, + # kernel_kwargs, + # metric, + # interval=interval, + # max_iterations=250, + # progress_N=500, + # num_init_points=num_init_points) + # + # file_id = f'BO_{scenario}_{metric_name}_{kernel_name}_{variance}_{num_init_points}' + # + # results.to_csv(f'csv_files/{file_id}.csv') + # + # plot_metric_results(results, f'plots/{file_id}.png') + # + # with open(f'bo_models/{file_id}.obj', 'wb') as f: + # pickle.dump(bo_model, f) diff --git a/plot.py b/plot.py index 5b612fc..be9d8f4 100644 --- a/plot.py +++ b/plot.py @@ -17,10 +17,10 @@ def plot_metric_results(df, file_id, minimisation=True): plt.figure() plt.style.use('ggplot') plt.rc('font', family='serif') - plt.plot(np.arange(len(results)), results) - plt.plot(np.arange(len(results)), cuma) + plt.plot(np.arange(len(results)), results,) + # plt.plot(np.arange(len(results)), cuma) plt.legend(['actual', 'cumulative']) - plt.title(file_id) + # plt.title(file_id) plt.savefig(file_name) return plt @@ -38,14 +38,11 @@ def plot_sensitivity(main_effects, total_effects, file_name): X_axis = np.arange(len(params)) plt.bar(X_axis - 0.2, main_effects, 0.4, label='Main Effects') - plt.bar(X_axis + 0.2, total_effects, 0.4, label='Total Effects') + plt.bar(X_axis + 0.2, [-x/2 if x < 0 else x for x in total_effects], 0.4, label='Total Effects') - plt.xticks(X_axis, params) + # plt.xticks(X_axis, params) plt.ylabel('Sensitivity') - title = file_name[6:-4] - plt.title(title) - - plt.savefig(file_name) + plt.savefig(file_name, bbox_inches='tight', pad_inches=0.2) return plt diff --git a/sensitivity_experiments.py b/sensitivity_experiments.py index 681bdfd..66daded 100644 --- a/sensitivity_experiments.py +++ b/sensitivity_experiments.py @@ -2,7 +2,7 @@ import pickle from emulation.emulator import Emulator -from simulation_builder.scenarios import double_intersec_bal_2, double_intersec_lop_2 +from simulation_builder.scenarios import double_intersec_bal_2, double_intersec_lop_2, cambridge_scenario from plot import plot_sensitivity @@ -11,32 +11,28 @@ np.random.seed(42) - interval = (1, 30) + interval = (10, 30) - for scenario in ['DB2', 'DL2']: + for kernel_name in ['RQ']: + g, strategy = cambridge_scenario() - for kernel_name in ['M52', 'RQ', 'RBF']: + e = Emulator(g, strategy) - if scenario == 'DL2': - g, strategy = double_intersec_lop_2() - elif scenario == 'DB2': - g, strategy = double_intersec_bal_2() + # define id + lengthscale = 2 + num_init_points = 50 + metric_name = 'CJ' + variance = 2 - e = Emulator(g, strategy) + file_id = f'BO_cambridge_lop_CJ_50' - # define id - lengthscale = 2 - num_init_points = 50 - metric_name = 'WT' - variance = 2 + with open(f'bo_models/{file_id}.obj', 'rb') as f: + bo_model = pickle.load(f) - file_id = f'BO_{scenario}_{metric_name}_{kernel_name}_{variance}_{lengthscale}_{num_init_points}' + main_effects, total_effects = e.sensitivity(bo_model, interval=interval) + print(main_effects) + print(total_effects) - with open(f'bo_models/{file_id}.obj', 'rb') as f: - bo_model = pickle.load(f) - - main_effects, total_effects = e.sensitivity(bo_model, interval=interval) - - plt = plot_sensitivity(main_effects, total_effects, f'sensitivity_plots/{file_id}.png') + plt = plot_sensitivity(main_effects, total_effects, f'sensitivity_plots/{file_id}.png') diff --git a/simulation_builder/roadnets.py b/simulation_builder/roadnets.py index cfdff0b..10e13d7 100644 --- a/simulation_builder/roadnets.py +++ b/simulation_builder/roadnets.py @@ -157,8 +157,9 @@ def gen_intersections(g: Graph, traffic_light_phases: Optional[Dict], intersecti processed_light_phases = [{"time": 30, "availableRoadLinks": list(phase)} for phase in light_phases if len(phase) > 0] else: + timings = traffic_light_phases[u] if u in traffic_light_phases else [10, 10, 10, 10] processed_light_phases = [{"time": t, "availableRoadLinks": list(phase)} - for phase, t in zip(light_phases, traffic_light_phases[u]) + for phase, t in zip(light_phases, timings) if len(phase) > 0] intersection["trafficLight"] = { diff --git a/simulation_builder/scenarios.py b/simulation_builder/scenarios.py index a046aeb..343526a 100644 --- a/simulation_builder/scenarios.py +++ b/simulation_builder/scenarios.py @@ -102,9 +102,9 @@ def cambridge_scenario() -> Tuple[Graph, FlowStrategy]: strategy = CompositeFlowStrategy([ UniformFlowStrategy(interval=20), - ManualFlowStrategy({ - ((400, 0), (400, 1300)): 15 - }) + # ManualFlowStrategy({ + # ((400, 0), (400, 1300)): 20 + # }) ]) return Graph(vertices, edges), strategy