def test_redistribute_to_one_route(self): r1 = [0, 1, 4, 6, 0] rd1_redistribute = RouteData(r1, route_l(r1, self.D), route_d(r1, self.d), None) r2 = [0, 2, 3, 5, 7, 0] rd2_recieving = RouteData(r2, route_l(r2, self.D), route_d(r2, self.d), None) result = do_redistribute_move(rd1_redistribute, rd2_recieving, self.D, strategy=LSOPT.FIRST_ACCEPT) self.assertEqual( len(result), 3, "The redistribute operator should return the redistributed and the new combined routes and the delta" ) self.assertEqual( result[1].route, [0, 2, 3, 5, 7, 1, 4, 6, 0], "It should be possible to insert all and they sould be appended to the route" ) result = do_redistribute_move(rd1_redistribute, rd2_recieving, self.D, strategy=LSOPT.BEST_ACCEPT) self.assertEqual( len(result), 3, "The redistribute operator should return the redistributed and the new combined routes and the delta" ) self.assertEqual(result[1].route, [0, 1, 2, 3, 4, 5, 6, 7, 0], "It should be possible to insert all")
def _insert_one(self, strategy, result_target, msg_if_fail, C=None, L=None, to_insert=2): r1 = [0, 1, 3, 4, 0] rd1 = RouteData(r1, route_l(r1, self.D), route_d(r1, self.d), None) _, result_rd, result_delta = do_insert_move(to_insert, rd1, self.D, C=C, d=self.d, L=L, strategy=strategy) result_route = None if (result_rd is None) else result_rd.route self.assertEqual(result_route, result_target, msg_if_fail) if result_delta is not None: self.assertAlmostEqual( rd1.cost + result_delta, result_rd.cost, msg="The delta based and stored objective functions differ") self.assertAlmostEqual( rd1.cost + result_delta, route_l(result_rd.route, self.D), msg= "The delta based and recalculated objective functions differ")
def test_updated_route_cost(self): new_r1d, new_r2d, delta = self._make_improving_move() self.assertEqual( route_l(new_r1d[0], self.D), new_r1d[1], "original route cost + savings should match recalculated route cost" ) self.assertEqual( route_l(new_r2d[0], self.D), new_r2d[1], "original route cost + savings should match recalculated route cost" )
def _make_improving_move(self, r1, r2, C=None, d=None, L=None): D = self.D r1rd = RouteData(r1, route_l(r1, D), route_d(r1, d), None) r2rd = RouteData(r2, route_l(r2, D), route_d(r2, d), None) return do_2optstar_move(r1rd, r2rd, D, d=d, C=C, L=L, strategy=LSOPT.BEST_ACCEPT)
def _make_improving_move(self, C=None, d=None, L=None): D = self.D r1 = [0, 5, 6, 4, 0] r2 = [0, 1, 2, 3, 7, 0] r1rd = RouteData(r1, route_l(r1, D), route_d(r1, d), None) r2rd = RouteData(r2, route_l(r2, D), route_d(r2, d), None) return do_2point_move(r1rd, r2rd, D, d=d, C=C, L=L, strategy=LSOPT.BEST_ACCEPT)
def test_non_improving_move(self): D = self.D r1 = [0, 5, 6, 7, 0] r2 = [0, 1, 2, 3, 4, 0] r1d = RouteData(r1, route_l(r1, D), 3, r1[:-1]) r2d = RouteData(r2, route_l(r2, D), 4, r2[:-1]) result = do_2point_move(r1d, r2d, D) self.assertEqual( result, (None, None, None), "r1, r2 configuration should already be at local optima") result = do_2point_move(r2d, r1d, D) self.assertEqual( result, (None, None, None), "r1, r2 configuration should already be at local optima")
def test_until_no_improvements_move(self): D = self.D r1 = [0, 4, 6, 1, 0] r2 = [0, 7, 2, 3, 5, 0] r1d = RouteData(r1, route_l(r1, D), -1, r1[:-1]) r2d = RouteData(r2, route_l(r2, D), -1, r2[:-1]) while True: result = do_2point_move(r1d, r2d, D) if result[0] is None: break # unpack result r1d, r2d, delta = result # unconstrained should run until other route is empty self.assertEqual(r2d[0], [0, 1, 2, 3, 4, 0], "should reach a local optima")
def test_insert_from_empty(self): rd1 = RouteData() r2 = [0, 1, 3, 4, 0] rd2 = RouteData(r2, route_l(r2, self.D), route_d(r2, self.d), None) _, result_rd, result_delta = do_insert_move(rd1, rd2, self.D) self.assertEqual(result_rd.route, [0, 1, 3, 4, 0], "All should be inserted")
def test_redistribute_to_two_routes(self): r1 = [0, 1, 4, 6, 0] rd1_redistribute = RouteData(r1, route_l(r1, self.D), route_d(r1, self.d), None) r2 = [0, 2, 3, 0] rd2_recieving = RouteData(r2, route_l(r2, self.D), route_d(r2, self.d), None) r3 = [0, 7, 5, 0] rd3_recieving = RouteData(r3, route_l(r3, self.D), route_d(r3, self.d), None) # depending on how the recombinations are made, the results differ FI = LSOPT.FIRST_ACCEPT BE = LSOPT.BEST_ACCEPT tests = [(0, "FIRST", FI, ([0, 2, 3, 1, 4, 0], [0, 7, 5, 6, 0])), (0, "BEST", BE, ([0, 1, 2, 3, 4, 0], [0, 7, 6, 5, 0])), (1, "FIRST", FI, ([0, 2, 3, 4, 1, 0], [0, 7, 5, 6, 0])), (1, "BEST", BE, ([0, 1, 2, 3, 4, 0], [0, 7, 6, 5, 0])), (2, "FIRST", FI, ([0, 2, 3, 1, 4, 0], [0, 7, 5, 6, 0])), (2, "BEST", BE, ([0, 1, 2, 3, 4, 0], [0, 7, 6, 5, 0])), (3, "FIRST", FI, ([0, 2, 3, 4, 1, 0], [0, 7, 5, 6, 0])), (3, "BEST", BE, ([0, 1, 2, 3, 4, 0], [0, 7, 6, 5, 0]))] for recombination_level, strategy_name, strategy, target_result in tests: result = do_redistribute_move( rd1_redistribute, [rd2_recieving, rd3_recieving], self.D, C=4.0, d=self.d, strategy=strategy, recombination_level=recombination_level) #print("QUALITY", strategy_name, "/", recombination_level, "delta", result[-1]) self.assertEqual( len(result), 4, "The redistribute operator should return the redistributed and the new combined routes and the delta" ) self.assertEqual( result[1].route, target_result[0], "It should be possible to insert all and they sould be appended to the route on recombination level %d with strategy %s" % (recombination_level, strategy_name)) self.assertEqual( result[2].route, target_result[1], "It should be possible to insert all and they sould be appended to the route on recombination level %d with strategy %s" % (recombination_level, strategy_name))
def _insert_many(self, strategy, result_target, msg_if_fail, C=None, L=None): to_insert = [3, 2] r1 = [0] + to_insert + [0] rd1 = RouteData(r1, route_l(r1, self.D), route_d(r1, self.d), None) r2 = [0, 1, 4, 0] rd2 = RouteData(r2, route_l(r2, self.D), route_d(r2, self.d), None) _, list_input_result_rd, list_input_result_delta = do_insert_move( to_insert, rd2, self.D, C=C, d=self.d, L=L, strategy=strategy) _, rd_input_result_rd, rd_input_result_delta = do_insert_move( rd1, rd2, self.D, C=C, d=self.d, L=L, strategy=strategy) list_input_result_route = None if ( list_input_result_rd is None) else list_input_result_rd.route rd_input_result_route = None if ( rd_input_result_rd is None) else rd_input_result_rd.route self.assertEqual(list_input_result_route, result_target, msg_if_fail) self.assertEqual( list_input_result_route, rd_input_result_route, "Inserting from a list or RouteData should lead to same result") if list_input_result_delta is not None: self.assertAlmostEqual( rd2.cost + list_input_result_delta, list_input_result_rd.cost, msg="The delta based and stored objective functions differ") self.assertAlmostEqual( rd2.cost + list_input_result_delta, route_l(list_input_result_rd.route, self.D), msg= "The delta based and recalculated objective functions differ") if rd_input_result_delta is not None: self.assertAlmostEqual( rd2.cost + rd_input_result_delta, rd_input_result_rd.cost, msg="The delta based and stored objective functions differ") self.assertAlmostEqual( rd2.cost + rd_input_result_delta, route_l(rd_input_result_rd.route, self.D), msg= "The delta based and recalculated objective functions differ")
def test_redistribute_does_not_fit(self): demands = [0.0, 1, 1, 1, 1, 1, 1, 1] r1 = [0, 1, 2, 0] rd1_redistribute = RouteData(r1, route_l(r1, self.D), route_d(r1, demands), None) r2 = [0, 3, 4, 5, 6, 7, 0] rd2_recieving = RouteData(r2, route_l(r2, self.D), route_d(r2, demands), None) result = do_redistribute_move(rd1_redistribute, [rd2_recieving], self.D, C=6.0, d=self.d, strategy=LSOPT.BEST_ACCEPT, recombination_level=1) # Fails -> None,None...None is returned (no partial fits) self.assertTrue( all(rp == None for rp in result), "The redistribute operator should return the redistributed and the new combined routes and the delta" )
def test_redistribute_find_best_fit(self): demands = [0.0, 1, 1, 2, 3, 1, 3, 1] r1 = [0, 1, 4, 6, 0] rd1_redistribute = RouteData(r1, route_l(r1, self.D), route_d(r1, demands), None) r2 = [0, 2, 3, 0] rd2_recieving = RouteData(r2, route_l(r2, self.D), route_d(r2, demands), None) r3 = [0, 5, 0] rd3_recieving = RouteData(r3, route_l(r3, self.D), route_d(r3, demands), None) r4 = [0, 7, 0] rd4_recieving = RouteData(r4, route_l(r4, self.D), route_d(r4, demands), None) result = do_redistribute_move( rd1_redistribute, [rd2_recieving, rd3_recieving, rd4_recieving], self.D, C=4.0, d=demands, strategy=LSOPT.BEST_ACCEPT, recombination_level=1) self.assertEqual( len(result), 5, "The redistribute operator should return the redistributed and the new combined routes and the delta" ) self.assertEqual(result[0].route, [0, 0], "It should be possible to insert all customers") self.assertEqual(_normalise_route_order(result[1].route), [0, 1, 2, 3, 0], "n1 should be redistributed to first route") self.assertEqual(_normalise_route_order(result[2].route), [0, 4, 5, 0], "n4 should be redistributed to first route") self.assertEqual(_normalise_route_order(result[3].route), [0, 6, 7, 0], "n6 should be redistributed to first route")
def test_until_no_improvements_move(self): D = self.D r1 = [0, 1, 2, 3, 5, 6, 7, 0] r2 = [0, 4, 0] r1d = (r1, route_l(r1, D), 6, None) r2d = (r2, route_l(r2, D), 1, None) loop_count = 0 while True: result = do_1point_move(r1d, r2d, D) if result[2] is None: break # unpack result r1d, r2d, delta = result loop_count += 1 self.assertTrue( loop_count < 8, "Too many operations, potential " + "infinite improvement loop") # unconstrained should run until other route is empty self.assertEqual(r2d[0], [0, 1, 2, 3, 4, 0], "should reach a local optima")
def test_respect_route_cost_constraint(self): r2 = [0, 1, 2, 3, 0] result = self._make_improving_move(L=route_l(r2, self.D) + 1.0) self.assertEqual( result, (None, None, None), "no move should be made due to violation of C constraint")