def test_linear_estimator_estimate(self): """Asserts linear estimations are correctly calculated""" stop_1 = Stop( depot_id='depot_1', location=Location(lat=4.720634, lng=-74.037228) ) stop_2 = Stop( depot_id='depot_2', location=Location(lat=4.708958, lng=-74.035172) ) estimator = LinearEstimator() estimations = estimator.estimate(stops=[stop_1, stop_2]) self.assertEqual( len(estimations), 4, msg='Number of estimations incorrect.' ) expected_estimation = round( haversine( point1=stop_1.location.coordinates, point2=stop_2.location.coordinates ) / DEFAULT_VELOCITY ) for (origin, destination), est in estimations.items(): if origin == destination: self.assertEqual( est, 0., msg='Same origin and destination has non-zero estimation.' ) else: self.assertEqual( round(est), expected_estimation, msg='Estimated linear time differs to distance / velocity.' )
def test_build_stops_absent_depots(self): """Asserts depots are excluded if no vehicle starts or ends there""" params = get_params() estimator = LinearEstimator() builder = ProblemBuilder(params=params, estimator=estimator) rider_1 = Rider(location=Location(lat=1.234, lng=5.678), rider_id='rider_1') rider_2 = Rider(location=Location(lat=5.678, lng=1.234), rider_id='rider_2') depot_1 = Depot(depot_id='depot_1', location=Location(lat=0, lng=0)) depot_2 = Depot(depot_id='depot_2', location=Location(lat=0, lng=0)) riders = {rider_1.rider_id: rider_1, rider_2.rider_id: rider_2} depots = {depot_1.depot_id: depot_1, depot_2.depot_id: depot_2} # Case 1: all depots are needed starts = [0, 0] ends = [0, 1] stops = builder._build_stops(riders, depots, starts, ends) self.assertEqual(len(stops), 4, msg='Wrong number of stops when all depots are used.') # Case 2: some depots are needed starts = [0, 0] ends = [0, 0] stops = builder._build_stops(riders, depots, starts, ends) self.assertEqual( len(stops), 3, msg='Wrong number of stops when some depots are used.')
def test_solve(self): """Asserts an Optimization Model is correctly solved""" riders = parse_models(model_dicts=test_riders, cls=Rider) vehicles = parse_models(model_dicts=test_vehicles, cls=Vehicle) depots = parse_models(model_dicts=test_depots, cls=Depot) params = get_params() estimator = LinearEstimator() problem_builder = ProblemBuilder(params=params, estimator=estimator) model_builder = OptimizationModelBuilder( constraints=[CapacityConstraint()]) problem = problem_builder.build(riders, vehicles, depots) model = model_builder.build(problem) solution = model.solve() self.assertTrue(solution, msg='Model could not be solved.') self.assertEqual( len(solution), len(vehicles), msg='Number of routes do not match number of vehicles.') stops_in_solution = [stop for route in solution for stop in route] for stop_ix in range(len(problem.stops)): self.assertIn(stop_ix, stops_in_solution, msg=f'Stop {stop_ix} not in solution.')
def test_build_stops(self): """Assert that Stops are a grouping of Riders and Depots""" riders = parse_models(model_dicts=test_riders, cls=Rider) depots = parse_models(model_dicts=test_depots, cls=Depot) params = get_params() builder = ProblemBuilder(params=params, estimator=LinearEstimator()) stops = builder._build_stops(riders=riders, depots=depots, starts=[0, 1], ends=[0, 1]) self.assertTrue( stops, msg=f'Stops could not be built from {len(riders)} riders.') self.assertEqual( len(stops), len(riders) + len(depots) - 1, msg='Number of stops differs from expected from test input.') for stop in stops: if not stop.depot_id: self.assertEqual( stop.demand, len(stop.riders), msg='Stop has a demand that differs from the Riders.') self.assertEqual( stop.location.lat, mean([ rider.location.lat for rider in stop.riders.values() ]), msg='Latitude incorrectly calculated for Stop.') self.assertEqual( stop.location.lng, mean([ rider.location.lng for rider in stop.riders.values() ]), msg='Longitude incorrectly calculated for Stop.') first_rider = list(stop.riders.values())[0] self.assertEqual( stop.location.extract_geohash( precision=params.GEOHASH_PRECISION_GROUPING), first_rider.location.extract_geohash( precision=params.GEOHASH_PRECISION_GROUPING), msg='Geohash for the Stop differs to that of first Rider.') else: self.assertEqual(stop.demand, 0, msg='Depot stop has non-zero demand.')
def test_build(self): """Asserts that the Problem is built correctly""" params = get_params() estimator = LinearEstimator() builder = ProblemBuilder(params=params, estimator=estimator) riders = parse_models(model_dicts=test_riders, cls=Rider) vehicles = parse_models(model_dicts=test_vehicles, cls=Vehicle) depots = parse_models(model_dicts=test_depots, cls=Depot) problem = builder.build(riders, vehicles, depots) self.assertTrue(problem, msg='Problem could not be built.') self.assertEqual( len(problem.stops), len(riders) + len(depots) - 1, msg='Number of stops differs from expected from test input.') self.assertEqual(len(problem.estimations), (len(riders) + len(depots) - 1)**2, msg='Number of estimations incorrect.')
def test_route(self): """Asserts the main routing method works correctly""" params = get_params() estimator = LinearEstimator() problem_builder = ProblemBuilder(params=params, estimator=estimator) model_builder = OptimizationModelBuilder( constraints=[CapacityConstraint()]) router = Router(problem_builder=problem_builder, optimization_model_builder=model_builder) riders = parse_models(model_dicts=test_riders, cls=Rider) vehicles = parse_models(model_dicts=test_vehicles, cls=Vehicle) depots = parse_models(model_dicts=test_depots, cls=Depot) routes = router.route(riders, vehicles, depots) self.assertTrue(routes, msg='Routes could not be built.') for route in routes: self.assertTrue(route['vehicle_id'], msg='Route without vehicle.') self.assertTrue(len(route['stops']) > 1, msg='Route with single stop.')
def test_parse_routes(self): """Asserts that Routes are correctly parsed from the Opt. solution""" params = get_params() estimator = LinearEstimator() problem_builder = ProblemBuilder(params=params, estimator=estimator) model_builder = OptimizationModelBuilder( constraints=[CapacityConstraint()]) riders = parse_models(model_dicts=test_riders, cls=Rider) vehicles = parse_models(model_dicts=test_vehicles, cls=Vehicle) depots = parse_models(model_dicts=test_depots, cls=Depot) problem = problem_builder.build(riders, vehicles, depots) model = model_builder.build(problem) solution = model.solve() routes = Router._parse_routes(problem, solution) self.assertTrue(routes, msg='Routes could not be built.') for route in routes: self.assertTrue(route['vehicle_id'], msg='Route without vehicle.') self.assertTrue(len(route['stops']) > 1, msg='Route with single stop.')
from utils.logging_utils import configure_logs """Main method to execute the Router""" # CLI parsing parser = argparse.ArgumentParser(description='Route some school buses.') parser.add_argument('--input-dir', type=str, help='Directory for reading the Input. Default is ./input', default='./input') parser.add_argument( '--output-dir', type=str, help='Directory for reading the Output. Default is ./output', default='./output', ) args = parser.parse_args() input_dir = args.input_dir output_dir = args.output_dir # Method execution configure_logs() riders, vehicles, depots, params = read_entities(input_dir) estimator = LinearEstimator() problem_builder = ProblemBuilder(params=params, estimator=estimator) optimization_model_builder = OptimizationModelBuilder( constraints=[CapacityConstraint()]) router = Router(problem_builder=problem_builder, optimization_model_builder=optimization_model_builder) routes = router.route(riders, vehicles, depots) write_routes(output_dir, routes)