def test_retrieving_single_greenyellow_interval(self): """ test retrieving a single greenyellow interval of a signal group """ # GIVEN input_dict = TestFTSMethods.get_default_fts_inputs() sg1 = SignalGroup( id="sg1", traffic_lights=[TrafficLight(capacity=800, lost_time=1)], min_greenyellow=10, max_greenyellow=80, min_red=10, max_red=80) sg2 = SignalGroup( id="sg2", traffic_lights=[TrafficLight(capacity=800, lost_time=1)], min_greenyellow=10, max_greenyellow=80, min_red=10, max_red=80) # WHEN fts = FixedTimeSchedule(**input_dict) # THEN self.assertEqual(fts.get_greenyellow_interval(signalgroup=sg1, k=0), input_dict["greenyellow_intervals"]["sg1"][0]) self.assertEqual(fts.get_greenyellow_interval(signalgroup=sg1, k=1), input_dict["greenyellow_intervals"]["sg1"][1]) self.assertEqual(fts.get_greenyellow_interval(signalgroup=sg2, k=0), input_dict["greenyellow_intervals"]["sg2"][0])
def test_retrieving_single_interval_with_id(self): # GIVEN input_dict = TestFTSMethods.get_default_fts_inputs() sg1 = SignalGroup( id="sg1", traffic_lights=[TrafficLight(capacity=800, lost_time=1)], min_greenyellow=10, max_greenyellow=80, min_red=10, max_red=80) sg2 = SignalGroup( id="sg2", traffic_lights=[TrafficLight(capacity=800, lost_time=1)], min_greenyellow=10, max_greenyellow=80, min_red=10, max_red=80) # WHEN fts = FixedTimeSchedule(**input_dict) # THEN using id should give the same information self.assertEqual(fts.get_greenyellow_interval(signalgroup=sg1, k=0), fts.get_greenyellow_interval(signalgroup=sg1.id, k=0)) self.assertEqual(fts.get_greenyellow_interval(signalgroup=sg1, k=1), fts.get_greenyellow_interval(signalgroup=sg1.id, k=1)) self.assertEqual(fts.get_greenyellow_interval(signalgroup=sg2, k=0), fts.get_greenyellow_interval(signalgroup=sg2.id, k=0))
def test_getting_non_existing_signal_group(self): """ Test retrieving signal group by id when this id does not exist """ # GIVEN signalgroup1 = SignalGroup(id="sg1", traffic_lights=[TrafficLight(capacity=1800, lost_time=1)], min_greenyellow=10, max_greenyellow=80, min_red=10, max_red=80) signalgroup2 = SignalGroup(id="sg2", traffic_lights=[TrafficLight(capacity=1800, lost_time=1)], min_greenyellow=10, max_greenyellow=80, min_red=10, max_red=80) intersection = Intersection(signalgroups=[signalgroup1, signalgroup2], conflicts=[], sync_starts=[], offsets=[], greenyellow_leads=[]) with self.assertRaises(ValueError): # WHEN intersection.get_signalgroup(signalgroup_id="sg3")
def test_getting_signal_group(self): """ Test retrieving signal group by id """ # GIVEN signalgroup1 = SignalGroup(id="sg1", traffic_lights=[TrafficLight(capacity=1800, lost_time=1)], min_greenyellow=10, max_greenyellow=80, min_red=10, max_red=80) signalgroup2 = SignalGroup(id="sg2", traffic_lights=[TrafficLight(capacity=1800, lost_time=1)], min_greenyellow=10, max_greenyellow=80, min_red=10, max_red=80) intersection = Intersection(signalgroups=[signalgroup1, signalgroup2], conflicts=[], sync_starts=[], offsets=[], greenyellow_leads=[]) # WHEN/THEN self.assertEqual(intersection.get_signalgroup(signalgroup_id="sg1"), signalgroup1) self.assertEqual(intersection.get_signalgroup(signalgroup_id="sg2"), signalgroup2)
def test_json_back_and_forth(self) -> None: """ Test converting back and forth from and to json """ # GIVEN input_dict = TestInputValidation.get_default_inputs() # WHEN traffic_light = TrafficLight(**input_dict) # THEN converting back and forth should in the end give the same result traffic_light_dict = traffic_light.to_json() traffic_light_from_json = TrafficLight.from_json( traffic_light_dict=traffic_light_dict) self.assertDictEqual(traffic_light_dict, traffic_light_from_json.to_json())
def test_retrieving_for_unkown_signalgroup(self): """ test retrieving greenyellow intervals of an unkown signal group """ # GIVEN input_dict = TestFTSMethods.get_default_fts_inputs() # unkown signal group sg3 = SignalGroup( id="sg3", traffic_lights=[TrafficLight(capacity=800, lost_time=1)], min_greenyellow=10, max_greenyellow=80, min_red=10, max_red=80) fts = FixedTimeSchedule(**input_dict) with self.assertRaises(ValueError): # WHEN trying to access greenyellow intervals of an unkown signal group fts.get_greenyellow_intervals(sg3) # THEN an error should be raised # same but using id with self.assertRaises(ValueError): # WHEN trying to access greenyellow intervals of an unkown signal group fts.get_greenyellow_intervals(sg3.id)
def get_default_signalgroup(name: str, min_greenyellow: float = 10.0, max_greenyellow: float = 80.0, min_red: float = 10.0, max_red: float = 80.0) -> SignalGroup: """ Get a default signalgroup object""" traffic_light = TrafficLight(capacity=0.5, lost_time=0.0) return SignalGroup(id=name, traffic_lights=[traffic_light], min_greenyellow=min_greenyellow, max_greenyellow=max_greenyellow, min_red=min_red, max_red=max_red, min_nr=1, max_nr=3)
def test_successful_validation(self) -> None: """ Test initializing TrafficLight object with correct input """ # GIVEN input_dict = TestInputValidation.get_default_inputs() # WHEN TrafficLight(**input_dict)
def _add_signalgroup(self, name: str) -> None: traffic_light = TrafficLight(capacity=0.5, lost_time=0.0) self._signal_groups.append( SignalGroup(id=name, traffic_lights=[traffic_light], min_greenyellow=2, max_greenyellow=20, min_red=2, max_red=50, min_nr=1, max_nr=3))
def test_zero(self) -> None: """ Test providing zero values for capacity and max_saturation """ for key in ["capacity", "max_saturation"]: with self.subTest(f"Wrong type in input '{key}'"): # GIVEN input_dict = TestInputValidation.get_default_inputs() input_dict[key] = 0.0 # argument should be positive with self.assertRaises(ValueError): # WHEN initializing the traffic light TrafficLight(**input_dict)
def test_wrong_type(self) -> None: """ Test providing the wrong type """ for key in TestInputValidation.get_default_inputs(): with self.subTest(f"Wrong type in input '{key}'"): # GIVEN input_dict = TestInputValidation.get_default_inputs() input_dict[key] = 'string' # all arguments are numbers with self.assertRaises(ValueError): # WHEN initializing the traffic light TrafficLight(**input_dict)
def test_negativity(self) -> None: """ Test providing negative values for capacity, lost_time, weight and max_saturation""" for key in TestInputValidation.get_default_inputs(): with self.subTest(f"Wrong type in input '{key}'"): # GIVEN input_dict = TestInputValidation.get_default_inputs() input_dict[ key] = -0.1 # all arguments are non-negative numbers with self.assertRaises(ValueError): # WHEN initializing the traffic light TrafficLight(**input_dict)
def from_json(signalgroup_dict: Dict) -> SignalGroup: """Loading signal group from json (expected same json structure as generated with to_json)""" return SignalGroup( id=signalgroup_dict["id"], min_greenyellow=signalgroup_dict["min_greenyellow"], max_greenyellow=signalgroup_dict["max_greenyellow"], min_red=signalgroup_dict["min_red"], max_red=signalgroup_dict["max_red"], min_nr=signalgroup_dict["min_nr"], max_nr=signalgroup_dict["max_nr"], traffic_lights=[ TrafficLight.from_json(traffic_light_dict) for traffic_light_dict in signalgroup_dict["traffic_lights"] ])
def get_default_inputs() -> Dict: """ Function to get default (valid) inputs for Intersection() """ signalgroups = [SignalGroup(id=f"sg{i+1}", traffic_lights=[TrafficLight(capacity=1800, lost_time=1)], min_greenyellow=10, max_greenyellow=80, min_red=10, max_red=80) for i in range(6)] conflicts = [Conflict(id1="sg1", id2="sg2", setup12=1, setup21=2), Conflict(id1="sg1", id2="sg6", setup12=1, setup21=2), Conflict(id1="sg2", id2="sg6", setup12=1, setup21=2)] sync_starts = [SyncStart(from_id="sg1", to_id="sg3")] offsets = [Offset(from_id="sg1", to_id="sg4", seconds=10)] periodic_orders = [PeriodicOrder(order=["sg1", "sg2", "sg6"])] greenyellow_leads = [GreenyellowLead(from_id="sg1", to_id="sg5", min_seconds=1, max_seconds=10)] greenyellow_trails = [GreenyellowTrail(from_id="sg5", to_id="sg1", min_seconds=2, max_seconds=8)] return dict(signalgroups=signalgroups, conflicts=conflicts, sync_starts=sync_starts, offsets=offsets, greenyellow_leads=greenyellow_leads, greenyellow_trails=greenyellow_trails, periodic_orders=periodic_orders)
def test_retrieving_unkown_interval(self): """ test retrieving greenyellow intervals of an unkown signal group """ # GIVEN input_dict = TestFTSMethods.get_default_fts_inputs() # unkown signal group sg1 = SignalGroup( id="sg1", traffic_lights=[TrafficLight(capacity=800, lost_time=1)], min_greenyellow=10, max_greenyellow=80, min_red=10, max_red=80) fts = FixedTimeSchedule(**input_dict) with self.assertRaises(ValueError): # WHEN trying to access greenyellow intervals of an unkown signal group fts.get_greenyellow_interval( sg1, k=2) # has only 2 intervals (with indices k=0 and k=1)
def fix_order_and_optimize(): """ This example shows how to ask for a fixed-time schedule that adheres to a specified fix order in which the signalgroups should receive their greenyellow interval. """ logging.info(f"Running example '{os.path.basename(__file__)}'") # signal group consisting of two traffic light allowing 1 or 2 greenyellow intervals per repeating period. traffic_light1 = TrafficLight(capacity=1800, lost_time=2.2) traffic_light2 = TrafficLight(capacity=1810, lost_time=2.1) signalgroup1 = SignalGroup(id="2", traffic_lights=[traffic_light1, traffic_light2], min_greenyellow=10, max_greenyellow=100, min_red=10, max_red=100, min_nr=1, max_nr=2) # signal group consisting of one traffic light allowing 1 greenyellow interval (default) per repeating period. traffic_light3 = TrafficLight(capacity=1650, lost_time=3.0) signalgroup2 = SignalGroup(id="5", traffic_lights=[traffic_light3], min_greenyellow=10, max_greenyellow=100, min_red=10, max_red=100) # signal group consisting of one traffic light allowing 1 greenyellow interval (default) per repeating period. traffic_light4 = TrafficLight(capacity=1800, lost_time=2.1) signalgroup3 = SignalGroup(id="8", traffic_lights=[traffic_light4], min_greenyellow=10, max_greenyellow=100, min_red=10, max_red=100) # conflicts & clearance times conflict12 = Conflict(id1=signalgroup1.id, id2=signalgroup2.id, setup12=1, setup21=2) conflict13 = Conflict(id1=signalgroup1.id, id2=signalgroup3.id, setup12=2, setup21=1) conflict23 = Conflict(id1=signalgroup2.id, id2=signalgroup3.id, setup12=2, setup21=3) # initialize intersection object intersection = Intersection( signalgroups=[signalgroup1, signalgroup2, signalgroup3], conflicts=[conflict12, conflict13, conflict23]) # set associated arrival rates arrival_rates = ArrivalRates(id_to_arrival_rates={ "2": [800, 700], "5": [150], "8": [180] }) logging.info( f"Not yet requesting any fixed order of greenyellow intervals") logging.info(f"Minimizing average experienced delay") # optimize fixed-time schedule fixed_time_schedule, phase_diagram, objective_value, _ = SwiftMobilityCloudApi.get_optimized_fts( intersection=intersection, arrival_rates=arrival_rates, objective=ObjectiveEnum.min_delay) logging.info(f"Average experienced delay {objective_value: .3f} seconds") logging.info(fixed_time_schedule) logging.info(phase_diagram) logging.info(f"Requesting order: 2 -> 8 -> 5 -> ") # initialize intersection object intersection = Intersection( signalgroups=[signalgroup1, signalgroup2, signalgroup3], conflicts=[conflict12, conflict13, conflict23], periodic_orders=[PeriodicOrder(order=["2", "8", "5"])]) logging.info(f"Minimizing average experienced delay") # optimize fixed-time schedule fixed_time_schedule, phase_diagram, objective_value, _ = SwiftMobilityCloudApi.get_optimized_fts( intersection=intersection, arrival_rates=arrival_rates, objective=ObjectiveEnum.min_delay) logging.info(f"Average experienced delay {objective_value: .3f} seconds") logging.info(fixed_time_schedule) logging.info(phase_diagram)
def create_intersection_and_optimize(): """ Example showing how to: - create traffic lights, signal groups and intersections, ... - optimize a fixed-time schedule for this intersection NOTE: To run the example below you need credentials to invoke the swift mobility cloud api. To this end, you need to specify the following environment variables: - smc_api_key: the access key of your swift mobility cloud api account - smc_api_secret: the secret access key of your swift mobility cloud api account If you do not have such an account yet, please contact [email protected]. """ logging.info(f"Running example '{os.path.basename(__file__)}'") # signal group consisting of two traffic light allowing 1 or 2 greenyellow intervals per repeating period. traffic_light1 = TrafficLight(capacity=1800, lost_time=2.2) traffic_light2 = TrafficLight(capacity=1810, lost_time=2.1) signalgroup1 = SignalGroup(id="2", traffic_lights=[traffic_light1, traffic_light2], min_greenyellow=10, max_greenyellow=100, min_red=10, max_red=100, min_nr=1, max_nr=2) # signal group consisting of one traffic light allowing 1 greenyellow interval (default) per repeating period. traffic_light3 = TrafficLight(capacity=1650, lost_time=3.0) signalgroup2 = SignalGroup(id="5", traffic_lights=[traffic_light3], min_greenyellow=10, max_greenyellow=100, min_red=10, max_red=100) # signal group consisting of one traffic light allowing 1 greenyellow interval (default) per repeating period. traffic_light4 = TrafficLight(capacity=1800, lost_time=2.1) signalgroup3 = SignalGroup(id="8", traffic_lights=[traffic_light4], min_greenyellow=10, max_greenyellow=100, min_red=10, max_red=100) # conflicts & clearance times conflict12 = Conflict(id1=signalgroup1.id, id2=signalgroup2.id, setup12=1, setup21=2) conflict13 = Conflict(id1=signalgroup1.id, id2=signalgroup3.id, setup12=1, setup21=2) conflict23 = Conflict(id1=signalgroup2.id, id2=signalgroup3.id, setup12=2, setup21=3) # initialize intersection object intersection = Intersection( signalgroups=[signalgroup1, signalgroup2, signalgroup3], conflicts=[conflict12, conflict13, conflict23]) # set associated arrival rates arrival_rates = ArrivalRates(id_to_arrival_rates={ "2": [800, 700], "5": [150], "8": [180] }) logging.info(f"Minimizing average experienced delay") # optimize fixed-time schedule fixed_time_schedule, phase_diagram, objective_value, _ = SwiftMobilityCloudApi.get_optimized_fts( intersection=intersection, arrival_rates=arrival_rates, objective=ObjectiveEnum.min_delay) logging.info(f"Average experienced delay {objective_value: .3f} seconds") logging.info(fixed_time_schedule) logging.info(phase_diagram) # the following code indicates how to compute a phase diagram from a fixed-time schedule (note that now it makes # no sense to do so as it was already computed above) logging.info( "Computing phase diagram from fixed-time schedule. Should be the same as before" ) phase_diagram = SwiftMobilityCloudApi.get_phase_diagram( intersection=intersection, fixed_time_schedule=fixed_time_schedule) logging.info(phase_diagram)