예제 #1
0
    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.'
                )
예제 #2
0
    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.')
예제 #3
0
    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.')
예제 #4
0
    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.')
예제 #5
0
    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.')
예제 #6
0
    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.')
예제 #7
0
    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.')
예제 #8
0
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)