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.')
def test_post_init(self): """Assert the actions taken after the class is instantiated""" riders = parse_models(model_dicts=test_riders[0:2], cls=Rider) stop = Stop(riders=riders) self.assertEqual(stop.demand, len(riders), msg='Stop has demand that differs from riders.') self.assertEqual( stop.location.lat, mean([rider.location.lat for rider in riders.values()]), msg='Latitude incorrectly calculated for Stop.') self.assertEqual( stop.location.lng, mean([rider.location.lng for rider in riders.values()]), msg='Longitude incorrectly calculated for Stop.')
class TestsOptimizationModelBuilder(unittest.TestCase): """Tests for the Optimization Model Builder class""" # Base Problem riders = parse_models(model_dicts=test_riders[0:2], cls=Rider) vehicles = parse_models(model_dicts=test_vehicles, cls=Vehicle) depots = parse_models(model_dicts=test_depots, cls=Depot) stops = [ Stop(depot_id=list(depots.values())[0].depot_id, location=list(depots.values())[0].location), Stop(riders=riders) ] estimations = {(0, 0): 0., (0, 1): 85., (1, 0): 78., (1, 1): 0.} params = get_params() problem = Problem(depots=depots, estimations=estimations, params=params, riders=riders, stops=stops, vehicles=vehicles, starts=[0, 1], ends=[0, 1]) def test_build_manager(self): """Asserts that a manager is built correctly from the Problem""" manager = RoutingIndexManager( len(self.problem.stops), # Number of locations len(self.problem.vehicles), # Number of vehicles self.problem.starts, # Start list of Vehicles self.problem.ends # End list of Vehicles ) self.assertTrue(manager, msg='Opt. Manager could not be built.') self.assertEqual(manager.GetNumberOfVehicles(), len(self.vehicles), msg='Number of vehicles in manager is incorrect.') self.assertEqual(manager.GetNumberOfIndices(), len(self.vehicles) * 2 + len(self.stops) - len(self.problem.depots), msg='Number of indices in manager is incorrect.') solver = RoutingModel(manager) self.assertTrue(solver, msg='Solver could not be instantiated.') def test_build_search_parameters(self): """Asserts the heuristic search parameters are correctly created""" search_parameters = OptimizationModelBuilder._build_search_parameters( self.problem) self.assertTrue(search_parameters, msg='Search params could not be built.') self.assertEqual(search_parameters.time_limit, Duration(seconds=self.params.SEARCH_TIME_LIMIT), msg='Time limit is incorrect in the search params.') self.assertEqual( search_parameters.solution_limit, self.params.SEARCH_SOLUTIONS_LIMIT, msg='Solutions limit is incorrect in the search params.') self.assertEqual( search_parameters.first_solution_strategy, FIRST_SOLUTION_STRATEGY[self.params.FIRST_SOLUTION_STRATEGY], msg='First solution strategy is incorrect in the search params.') self.assertEqual( search_parameters.local_search_metaheuristic, LOCAL_SEARCH_METAHEURISTIC[self.params.SEARCH_METAHEURISTIC], msg='Search metaheuristic is incorrect in the search params.') def test_apply_constraints_capacity_constraint(self): """Asserts constraints are read correctly by the solver""" model_builder = OptimizationModelBuilder( constraints=[CapacityConstraint()]) problem = self.problem manager = RoutingIndexManager( len(problem.stops), # Number of locations len(problem.vehicles), # Number of vehicles problem.starts, # Start list of Vehicles problem.ends # End list of Vehicles ) solver = RoutingModel(manager) model_builder._apply_constraints(problem, manager, solver) self.assertTrue(solver, msg='Constraints added incorrectly.') self.assertTrue(solver.HasDimension('capacity_constraint'), msg='Capacity constraint not added.') def test_set_objective_function(self): """Asserts the objective function is added to the solver""" model_builder = OptimizationModelBuilder( constraints=[CapacityConstraint()]) problem = self.problem manager = RoutingIndexManager( len(problem.stops), # Number of locations len(problem.vehicles), # Number of vehicles problem.starts, # Start list of Vehicles problem.ends # End list of Vehicles ) solver = RoutingModel(manager) model_builder._set_objective_function(problem, manager, solver) self.assertTrue(solver, msg='Objective function set incorrectly.') def test_build(self): """Assert the Opt. model is built correctly""" model_builder = OptimizationModelBuilder( constraints=[CapacityConstraint()]) problem = self.problem model = model_builder.build(problem) self.assertTrue(model, msg='Opt. model built incorrectly.') self.assertEqual(model.manager.GetNumberOfVehicles(), len(self.vehicles), msg='Number of vehicles in manager is incorrect.') self.assertEqual(model.manager.GetNumberOfIndices(), len(self.vehicles) * 2 + len(self.stops) - len(problem.depots), msg='Number of indices in manager is incorrect.') self.assertTrue(model.solver, msg='Solver could not be instantiated.') self.assertTrue(model.search_parameters, msg='Search params could not be built.') self.assertEqual(model.search_parameters.time_limit, Duration(seconds=self.params.SEARCH_TIME_LIMIT), msg='Time limit is incorrect in the search params.') self.assertEqual( model.search_parameters.solution_limit, self.params.SEARCH_SOLUTIONS_LIMIT, msg='Solutions limit is incorrect in the search params.') self.assertEqual( model.search_parameters.first_solution_strategy, FIRST_SOLUTION_STRATEGY[self.params.FIRST_SOLUTION_STRATEGY], msg='First solution strategy is incorrect in the search params.') self.assertEqual( model.search_parameters.local_search_metaheuristic, LOCAL_SEARCH_METAHEURISTIC[self.params.SEARCH_METAHEURISTIC], msg='Search metaheuristic is incorrect in the search params.') self.assertTrue(model.solver.HasDimension('capacity_constraint'), msg='Capacity constraint not added.')