/// <summary> /// Tests all enumerations of a route. /// </summary> protected void DoTestEnumerateBetween() { int count = 10; IRoute route = this.BuildRoute(true); if (route != null) { // this part needs testing. for (int customer = 0; customer < count; customer++) { route.InsertAfter(customer - 1, customer); //route.InsertAfterAndRemove(customer - 1, customer, -1); } } for (int from = 0; from < count; from++) { for (int to = 0; to < count; to++) { IEnumerator <int> enumerator = route.Between(from, to).GetEnumerator(); for (int customer = from; customer - 1 != to; customer++) { if (customer == count) { customer = 0; } Assert.IsTrue(enumerator.MoveNext()); Assert.AreEqual(customer, enumerator.Current); } } } }
/// <summary> /// Tries all 3Opt Moves for the neighbourhood of v_1. /// </summary> /// <param name="problem"></param> /// <param name="weights"></param> /// <param name="route"></param> /// <param name="v1"></param> /// <returns></returns> public bool Try3OptMoves(IProblemWeights problem, double[][] weights, IRoute route, int v1) { // get v_2. int v_2 = route.GetNeigbours(v1)[0]; if (v_2 < 0) { return(false); } IEnumerable <int> between_v_2_v_1 = route.Between(v_2, v1); double weight_1_2 = weights[v1][v_2]; int v_3 = -1; NearestNeighbours10 neighbours = null; if (_nearest_neighbours) { neighbours = problem.Get10NearestNeighbours(v1); } foreach (int v_4 in between_v_2_v_1) { if (v_3 >= 0 && v_3 != v1) { //if (!_nearest_neighbours || // neighbours.Max <= weight_1_4) if (!_nearest_neighbours || neighbours.Contains(v_4)) { double weight_1_4 = weights[v1][v_4]; double weight_1_2_plus_3_4 = weight_1_2 + weights[v_3][v_4]; double[] weights_3 = weights[v_3]; if (this.Try3OptMoves(problem, weights, route, v1, v_2, v_3, weights_3, v_4, weight_1_2_plus_3_4, weight_1_4)) { return(true); } } } v_3 = v_4; } return(false); }
/// <summary> /// Applies inter-improvements by exchanging parts of the route(s). /// </summary> /// <param name="problem"></param> /// <param name="solution"></param> /// <param name="route1_idx"></param> /// <param name="route2_idx"></param> /// <param name="max"></param> /// <returns></returns> public bool Improve(MaxTimeProblem problem, MaxTimeSolution solution, int route1_idx, int route2_idx, double max) { int max_window = 40; IRoute route1 = solution.Route(route1_idx); IRoute route2 = solution.Route(route2_idx); int route1_customers = route1.Count; int route2_customers = route2.Count; double[] route1_cumul = problem.TimeCumul(route1); double[] route2_cumul = problem.TimeCumul(route2); // build all edge weights. List <Edge> route1_edges = new List <Edge>(route1.Edges()); List <Edge> route2_edges = new List <Edge>(route2.Edges()); double[] route1_weights = new double[route1_edges.Count]; for (int idx = 0; idx < route1_edges.Count; idx++) { Edge edge = route1_edges[idx]; route1_weights[idx] = problem.WeightMatrix[edge.From][edge.To]; } double[] route2_weights = new double[route2_edges.Count]; for (int idx = 0; idx < route2_edges.Count; idx++) { Edge edge = route2_edges[idx]; route2_weights[idx] = problem.WeightMatrix[edge.From][edge.To]; } List <EdgePair> route2_pairs = new List <EdgePair>(); for (int i_idx = 0; i_idx < route2_edges.Count - 2; i_idx++) { Edge i = route2_edges[i_idx]; double i_weight = route2_weights[i_idx]; double weight_before_i = route2_cumul[i_idx]; int k_idx_max = route2_edges.Count; if (k_idx_max > i_idx + 2 + max_window) { k_idx_max = i_idx + 2 + max_window; } for (int k_idx = i_idx + 2; k_idx < k_idx_max; k_idx++) { Edge k = route2_edges[k_idx]; double k_weight = route2_weights[k_idx]; double weight_after_k = route2_cumul[route2_cumul.Length - 1] - route2_cumul[k_idx + 1]; double weight_between_route = route2_cumul[k_idx] - route2_cumul[i_idx + 1]; route2_pairs.Add(new EdgePair() { First = i, FirstWeight = i_weight, Second = k, SecondWeight = k_weight, Between = new List <int>(route2.Between(i.To, k.From)), WeightTotal = i_weight + k_weight, WeightAfter = weight_after_k, WeightBefore = weight_before_i, WeightBetween = weight_between_route, CustomersBetween = k_idx - i_idx }); } } // build all edge pairs. for (int i_idx = 0; i_idx < route1_edges.Count - 2; i_idx++) { Edge i = route1_edges[i_idx]; double i_weight = route1_weights[i_idx]; double weight_before_i = route1_cumul[i_idx]; int k_idx_max = route1_edges.Count; if (k_idx_max > i_idx + 2 + max_window) { k_idx_max = i_idx + 2 + max_window; } for (int k_idx = i_idx + 2; k_idx < k_idx_max; k_idx++) { Edge k = route1_edges[k_idx]; double k_weight = route1_weights[k_idx]; double weight_after_k = route1_cumul[route1_cumul.Length - 1] - route1_cumul[k_idx + 1]; double weight_between_route = route1_cumul[k_idx] - route1_cumul[i_idx + 1]; EdgePair pair1 = new EdgePair() { First = i, FirstWeight = i_weight, Second = k, SecondWeight = k_weight, Between = new List <int>(route1.Between(i.To, k.From)), WeightTotal = i_weight + k_weight, WeightAfter = weight_after_k, WeightBefore = weight_before_i, WeightBetween = weight_between_route, CustomersBetween = k_idx - i_idx }; foreach (EdgePair pair2 in route2_pairs) { double existing_weight = pair1.WeightTotal + pair2.WeightTotal; //double new_weight = 0; // get first route new. double new_weight = problem.WeightMatrix[pair1.First.From][pair2.First.To]; //new_weight = first_route1_new; if (new_weight > existing_weight - 0.001) { continue; } double first_route2_new = problem.WeightMatrix[pair2.First.From][pair1.First.To]; new_weight = new_weight + first_route2_new; if (new_weight > existing_weight - 0.001) { continue; } double second_route1_new = problem.WeightMatrix[pair1.Second.From][pair2.Second.To]; new_weight = new_weight + second_route1_new; if (new_weight > existing_weight - 0.001) { continue; } double second_route2_new = problem.WeightMatrix[pair2.Second.From][pair1.Second.To]; new_weight = new_weight + second_route2_new; if (new_weight < existing_weight - 0.001) { // there is a decrease in total weight; check bounds. double route1_weight = pair1.WeightBefore + pair2.WeightBetween + pair1.WeightAfter; double route2_weight = pair2.WeightBefore + pair1.WeightBetween + pair2.WeightAfter; // calculate the maximum. int route1_customers_between = pair1.CustomersBetween; int route2_customers_between = pair1.CustomersBetween; route1_weight = problem.Time(route1_weight, route1_customers - route1_customers_between + route2_customers_between); route2_weight = problem.Time(route2_weight, route2_customers - route2_customers_between + route1_customers_between); if (route1_weight < max && route2_weight < max) { MaxTimeSolution solution_copy = solution.Clone() as MaxTimeSolution; List <int> route1_between = pair1.Between; List <int> route2_between = pair2.Between; route1.ReplaceEdgeFrom(pair1.First.From, pair1.Second.To); route2.ReplaceEdgeFrom(pair2.First.From, pair2.Second.To); int previous = pair1.First.From; for (int idx = 0; idx < route2_between.Count; idx++) { route1.ReplaceEdgeFrom(previous, route2_between[idx]); previous = route2_between[idx]; } route1.ReplaceEdgeFrom(previous, pair1.Second.To); previous = pair2.First.From; for (int idx = 0; idx < route1_between.Count; idx++) { route2.ReplaceEdgeFrom(previous, route1_between[idx]); previous = route1_between[idx]; } route2.ReplaceEdgeFrom(previous, pair2.Second.To); if (!solution.IsValid()) { throw new Exception(); } return(true); } } } } } return(false); }
/// <summary> /// Tries all 3Opt Moves for the neighbourhood of v_1 containing v_3. /// </summary> /// <param name="problem"></param> /// <param name="weights"></param> /// <param name="route"></param> /// <param name="v1"></param> /// <param name="v_2"></param> /// <param name="v_3"></param> /// <param name="weights_3"></param> /// <param name="v_4"></param> /// <param name="weight_1_2_plus_3_4"></param> /// <param name="weight_1_4"></param> /// <returns></returns> public bool Try3OptMoves(IProblemWeights problem, double[][] weights, IRoute route, int v1, int v_2, int v_3, double[] weights_3, int v_4, double weight_1_2_plus_3_4, double weight_1_4) { //IEnumerable<int> between_v_4_v_1 = route.Between(v_4, v_1); //foreach (int v_5 in between_v_4_v_1) //{ // if (v_5 != v_1) // { // if (this.Try3OptMove(problem, weights, route, v_1, v_2, v_3, weights_3, v_4, weight_1_2_plus_3_4, weight_1_4, v_5)) // { // return true; // } // } //} //return false; IEnumerator <int> between_v_4_v_1_enumerator = route.Between(v_4, v1).GetEnumerator(); if (between_v_4_v_1_enumerator.MoveNext()) { int v_5 = between_v_4_v_1_enumerator.Current; if (v_5 != v1) { while (between_v_4_v_1_enumerator.MoveNext()) { int v_6 = between_v_4_v_1_enumerator.Current; //Console.WriteLine(v_6); //if (this.Try3OptMove(problem, weights, // route, v_1, v_2, v_3, weights_3, v_4, // weight_1_2_plus_3_4, weight_1_4, v_5, v_6)) //{ // calculate the total weight of the 'new' arcs. double weight_new = weight_1_4 + weights_3[v_6] + weights[v_5][v_2]; // calculate the total weights. double weight = weight_1_2_plus_3_4 + weights[v_5][v_6]; if (weight - weight_new > _epsilon) { // actually do replace the vertices. int count_before = route.Count; //string route_string = route.ToString(); route.ReplaceEdgeFrom(v1, v_4); route.ReplaceEdgeFrom(v_3, v_6); route.ReplaceEdgeFrom(v_5, v_2); int count_after = route.Count; if (count_before != count_after) { throw new Exception(); } // set bits. //this.Set(problem, v_1, false); this.Set(problem, v_3, false); this.Set(problem, v_5, false); if (!route.IsValid()) { throw new Exception(); } return(true); // move succeeded. } //} v_5 = v_6; } } } return(false); }
/// <summary> /// Tries to relocate customers using the source the same as the CrossExchange but places customers using cheapest insertion. /// </summary> /// <param name="problem"></param> /// <param name="solution"></param> /// <param name="route1_idx"></param> /// <param name="route2_idx"></param> /// <param name="max"></param> /// <returns></returns> public bool Improve(MaxTimeProblem problem, MaxTimeSolution solution, int route1_idx, int route2_idx, double max) { int max_window = 10; IRoute route1 = solution.Route(route1_idx); IRoute route2 = solution.Route(route2_idx); double total_before = problem.Time(solution.Route(route1_idx)) + problem.Time(solution.Route(route2_idx)); int route1_customers = route1.Count; int route2_customers = route2.Count; double[] route1_cumul = problem.TimeCumul(route1); double[] route2_cumul = problem.TimeCumul(route2); // build all edge weights. List <Edge> route1_edges = new List <Edge>(route1.Edges()); List <Edge> route2_edges = new List <Edge>(route2.Edges()); double[] route1_weights = new double[route1_edges.Count]; for (int idx = 0; idx < route1_edges.Count; idx++) { Edge edge = route1_edges[idx]; route1_weights[idx] = problem.WeightMatrix[edge.From][edge.To]; } double[] route2_weights = new double[route2_edges.Count]; for (int idx = 0; idx < route2_edges.Count; idx++) { Edge edge = route2_edges[idx]; route2_weights[idx] = problem.WeightMatrix[edge.From][edge.To]; } List <EdgePair> route2_pairs = new List <EdgePair>(); for (int i_idx = 0; i_idx < route2_edges.Count - 2; i_idx++) { Edge i = route2_edges[i_idx]; double i_weight = route2_weights[i_idx]; double weight_before_i = route2_cumul[i_idx]; int k_idx_max = route2_edges.Count; if (k_idx_max > i_idx + 2 + max_window) { k_idx_max = i_idx + 2 + max_window; } for (int k_idx = i_idx + 2; k_idx < k_idx_max; k_idx++) { Edge k = route2_edges[k_idx]; double k_weight = route2_weights[k_idx]; double weight_after_k = route2_cumul[route2_cumul.Length - 1] - route2_cumul[k_idx + 1]; double weight_between_route = route2_cumul[k_idx] - route2_cumul[i_idx + 1]; route2_pairs.Add(new EdgePair() { First = i, FirstWeight = i_weight, Second = k, SecondWeight = k_weight, Between = new List <int>(route2.Between(i.To, k.From)), WeightTotal = i_weight + k_weight, WeightAfter = weight_after_k, WeightBefore = weight_before_i, WeightBetween = weight_between_route, CustomersBetween = k_idx - i_idx }); } } // try to relocate all and find best pair. double route1_weight = route1_cumul[route1_cumul.Length - 1]; EdgePair best = null; CheapestInsertionResult best_result = new CheapestInsertionResult(); double best_extra = double.MaxValue; foreach (EdgePair pair2 in route2_pairs) { // calculate cheapest insertion. CheapestInsertionResult result = CheapestInsertionHelper.CalculateBestPlacement(problem, route1, pair2.First.To, pair2.Second.From); double extra_route2 = problem.WeightMatrix[pair2.First.From][pair2.Second.To]; // check if the result has a net-decrease. if (result.Increase + extra_route2 < pair2.WeightTotal - 0.01) { // there is a net decrease. // calculate the real increase. double new_weight = problem.Time(route1_weight + result.Increase + pair2.WeightBetween, route1_customers + pair2.CustomersBetween); // check the max. if (new_weight < max && new_weight < best_extra) { // the route is smaller than max. best_extra = new_weight; best_result = result; best = pair2; } } } if (best != null) { if (route2.Last == best.Second.To) { //throw new Exception(); } route2.ReplaceEdgeFrom(best.First.From, best.Second.To); int previous = best_result.CustomerBefore; foreach (int customer in best.Between) { route1.ReplaceEdgeFrom(previous, customer); previous = customer; } route1.ReplaceEdgeFrom(previous, best_result.CustomerAfter); // check validity. if (!route2.IsValid()) { throw new Exception(); } if (!route1.IsValid()) { throw new Exception(); } if (route1.Count + route2.Count != route1_customers + route2_customers) { throw new Exception(); } double total_after = problem.Time(solution.Route(route1_idx)) + problem.Time(solution.Route(route2_idx)); if (total_after >= total_before) { throw new Exception("this is not an improvement!"); } return(true); } return(false); }
/// <summary> /// Tries all 3Opt Moves for the neighbourhood of v_1 containing v_3. /// </summary> /// <param name="problem"></param> /// <param name="weights"></param> /// <param name="route"></param> /// <param name="v1"></param> /// <param name="v_2"></param> /// <param name="v_3"></param> /// <param name="weights_3"></param> /// <param name="v_4"></param> /// <param name="weight_1_2_plus_3_4"></param> /// <param name="weight_1_4"></param> /// <returns></returns> public bool Try3OptMoves(IProblemWeights problem, double[][] weights, IRoute route, int v1, int v_2, int v_3, double[] weights_3, int v_4, double weight_1_2_plus_3_4, double weight_1_4) { //IEnumerable<int> between_v_4_v_1 = route.Between(v_4, v_1); //foreach (int v_5 in between_v_4_v_1) //{ // if (v_5 != v_1) // { // if (this.Try3OptMove(problem, weights, route, v_1, v_2, v_3, weights_3, v_4, weight_1_2_plus_3_4, weight_1_4, v_5)) // { // return true; // } // } //} //return false; IEnumerator<int> between_v_4_v_1_enumerator = route.Between(v_4, v1).GetEnumerator(); if (between_v_4_v_1_enumerator.MoveNext()) { int v_5 = between_v_4_v_1_enumerator.Current; if (v_5 != v1) { while (between_v_4_v_1_enumerator.MoveNext()) { int v_6 = between_v_4_v_1_enumerator.Current; //Console.WriteLine(v_6); //if (this.Try3OptMove(problem, weights, // route, v_1, v_2, v_3, weights_3, v_4, // weight_1_2_plus_3_4, weight_1_4, v_5, v_6)) //{ // calculate the total weight of the 'new' arcs. double weight_new = weight_1_4 + weights_3[v_6] + weights[v_5][v_2]; // calculate the total weights. double weight = weight_1_2_plus_3_4 + weights[v_5][v_6]; if (weight - weight_new > _epsilon) { // actually do replace the vertices. int count_before = route.Count; //string route_string = route.ToString(); route.ReplaceEdgeFrom(v1, v_4); route.ReplaceEdgeFrom(v_3, v_6); route.ReplaceEdgeFrom(v_5, v_2); int count_after = route.Count; if (count_before != count_after) { throw new Exception(); } // set bits. //this.Set(problem, v_1, false); this.Set(problem, v_3, false); this.Set(problem, v_5, false); if (!route.IsValid()) { throw new Exception(); } return true; // move succeeded. } //} v_5 = v_6; } } } return false; }
/// <summary> /// Tries all 3Opt Moves for the neighbourhood of v_1. /// </summary> /// <param name="problem"></param> /// <param name="weights"></param> /// <param name="route"></param> /// <param name="v1"></param> /// <returns></returns> public bool Try3OptMoves(IProblemWeights problem, double[][] weights, IRoute route, int v1) { // get v_2. int v_2 = route.GetNeigbours(v1)[0]; if (v_2 < 0) { return false; } IEnumerable<int> between_v_2_v_1 = route.Between(v_2, v1); double weight_1_2 = weights[v1][v_2]; int v_3 = -1; NearestNeighbours10 neighbours = null; if (_nearest_neighbours) { neighbours = problem.Get10NearestNeighbours(v1); } foreach (int v_4 in between_v_2_v_1) { if (v_3 >= 0 && v_3 != v1) { //if (!_nearest_neighbours || // neighbours.Max <= weight_1_4) if (!_nearest_neighbours || neighbours.Contains(v_4)) { double weight_1_4 = weights[v1][v_4]; double weight_1_2_plus_3_4 = weight_1_2 + weights[v_3][v_4]; double[] weights_3 = weights[v_3]; if (this.Try3OptMoves(problem, weights, route, v1, v_2, v_3, weights_3, v_4, weight_1_2_plus_3_4, weight_1_4)) { return true; } } } v_3 = v_4; } return false; }
/// <summary> /// Calculates a solution. /// </summary> /// <param name="problem"></param> /// <returns></returns> internal override MaxTimeSolution Solve(MaxTimeProblem problem) { // create the calculator. MaxTimeCalculator calculator = new MaxTimeCalculator(problem); // create the solution. MaxTimeSolution solution = new MaxTimeSolution(problem.Size, true); double max = problem.Max.Value; // keep placing customer until none are left. List <int> customers = new List <int>(problem.Customers); // create n routes. for (int customer = 0; customer < customers.Count; customer++) { solution.Add(customer); solution[solution.Count - 1] = calculator.CalculateOneRouteIncrease( 0, 0); } // creates a result. MergeResult result = new MergeResult(); result.Weight = double.MaxValue; // loop over all route pairs and merge the smallest merge. while (result != null) { // keep looping until there is no result anymore. result = new MergeResult(); result.Weight = double.MaxValue; for (int route1_idx = 1; route1_idx < solution.Count; route1_idx++) { // keep looping over all routes. for (int route2_idx = 0; route2_idx < solution.Count; route2_idx++) { // keep looping over all routes. if (route1_idx == route2_idx) { // only consider different routes. break; } // calculate the merge result. MergeResult current_result = this.TryMerge(problem, solution, route1_idx, route2_idx, problem.Max.Value); // evaluate the current result. if (current_result != null && current_result.Weight < result.Weight) { // current result is best. result = current_result; } } } // evaluate the result. if (result.Weight < double.MaxValue) { // there is a result; apply it! IRoute source = solution.Route(result.RouteSourceId); IRoute target = solution.Route(result.RouteTargetId); //string source_string = source.ToString(); //string target_string = target.ToString(); if (target.Count > 1 && target.First == target.GetNeigbours(result.CustomerTargetSource)[0]) { //throw new Exception(); } // create an enumeration of all customers of source in the correct order. IEnumerable <int> source_between = new List <int>( source.Between(result.CustomerSourceSource, result.CustomerSourceTarget)); // insert after the complete source. int previous = result.CustomerTargetSource; int next = target.GetNeigbours(result.CustomerTargetSource)[0]; foreach (int source_customer in source_between) { // insert. target.ReplaceEdgeFrom(previous, source_customer); previous = source_customer; // update previous. } target.ReplaceEdgeFrom(previous, next); // remove the source route. solution.Remove(result.RouteSourceId); solution.RemoveWeight(result.RouteTargetId); // calculate the weight of the new route. solution[result.RouteTargetId] = solution[result.RouteTargetId] + result.Weight + solution[result.RouteSourceId]; if (!solution.IsValid()) { throw new Exception(); } } else { // set the result null. result = null; } } return(solution); }
public void TestDepotDynamicAsymmetricMultiRouteExchanges() { // create two routes. // 0->11->12->13->14->15->0 // 0->21->22->23->24->25->0 var multiRoute = new MaxTimeSolution(0); IRoute route1 = multiRoute.Add(); var customers = new List <int>(route1); Assert.AreEqual(1, customers.Count); Assert.AreEqual(0, customers[0]); route1.InsertAfter(0, 11); Assert.AreEqual("0->11", route1.ToString()); route1.InsertAfter(11, 12); route1.InsertAfter(12, 13); route1.InsertAfter(13, 14); route1.InsertAfter(14, 15); IRoute route2 = multiRoute.Add(); customers = new List <int>(route2); Assert.AreEqual(1, customers.Count); Assert.AreEqual(0, customers[0]); route2.InsertAfter(0, 21); Assert.AreEqual("0->21", route2.ToString()); route2.InsertAfter(21, 22); route2.InsertAfter(22, 23); route2.InsertAfter(23, 24); route2.InsertAfter(24, 25); customers = new List <int>(route1); Assert.AreEqual(6, customers.Count); Assert.AreEqual(0, customers[0]); Assert.AreEqual(11, customers[1]); Assert.AreEqual(12, customers[2]); Assert.AreEqual(13, customers[3]); Assert.AreEqual(14, customers[4]); Assert.AreEqual(15, customers[5]); customers = new List <int>(route2); Assert.AreEqual(6, customers.Count); Assert.AreEqual(0, customers[0]); Assert.AreEqual(21, customers[1]); Assert.AreEqual(22, customers[2]); Assert.AreEqual(23, customers[3]); Assert.AreEqual(24, customers[4]); Assert.AreEqual(25, customers[5]); // replace the entire first route. route1.ReplaceEdgeFrom(0, 0); route2.ReplaceEdgeFrom(25, 11); route2.ReplaceEdgeFrom(11, 12); route2.ReplaceEdgeFrom(12, 13); route2.ReplaceEdgeFrom(13, 14); route2.ReplaceEdgeFrom(14, 15); Assert.IsTrue(multiRoute.IsValid()); // create two routes. // 0->11->12->13->14->15->0 // 0->21->22->23->24->25->0 multiRoute = new MaxTimeSolution(0); route1 = multiRoute.Add(); customers = new List <int>(route1); Assert.AreEqual(1, customers.Count); Assert.AreEqual(0, customers[0]); route1.InsertAfter(0, 11); Assert.AreEqual("0->11", route1.ToString()); route1.InsertAfter(11, 12); route1.InsertAfter(12, 13); route1.InsertAfter(13, 14); route1.InsertAfter(14, 15); route2 = multiRoute.Add(); customers = new List <int>(route2); Assert.AreEqual(1, customers.Count); Assert.AreEqual(0, customers[0]); route2.InsertAfter(0, 21); Assert.AreEqual("0->21", route2.ToString()); route2.InsertAfter(21, 22); route2.InsertAfter(22, 23); route2.InsertAfter(23, 24); route2.InsertAfter(24, 25); // exchange parts. var part1 = new List <int>(route1.Between(11, 13)); var part2 = new List <int>(route2.Between(23, 25)); route1.ReplaceEdgeFrom(0, 14); route2.ReplaceEdgeFrom(22, 0); int previous = 0; for (int idx = 0; idx < part2.Count; idx++) { route1.ReplaceEdgeFrom(previous, part2[idx]); previous = part2[idx]; } route1.ReplaceEdgeFrom(previous, 14); previous = 22; for (int idx = 0; idx < part1.Count; idx++) { route2.ReplaceEdgeFrom(previous, part1[idx]); previous = part1[idx]; } route2.ReplaceEdgeFrom(previous, 0); Assert.IsTrue(multiRoute.IsValid()); customers = new List <int>(route1); Assert.AreEqual(6, customers.Count); Assert.AreEqual(0, customers[0]); Assert.AreEqual(23, customers[1]); Assert.AreEqual(24, customers[2]); Assert.AreEqual(25, customers[3]); Assert.AreEqual(14, customers[4]); Assert.AreEqual(15, customers[5]); customers = new List <int>(route2); Assert.AreEqual(6, customers.Count); Assert.AreEqual(0, customers[0]); Assert.AreEqual(21, customers[1]); Assert.AreEqual(22, customers[2]); Assert.AreEqual(11, customers[3]); Assert.AreEqual(12, customers[4]); Assert.AreEqual(13, customers[5]); // create two routes. // 0->11->12->13->14->15->0 // 0->21->22->23->24->25->0 multiRoute = new MaxTimeSolution(0); route1 = multiRoute.Add(); customers = new List <int>(route1); Assert.AreEqual(1, customers.Count); Assert.AreEqual(0, customers[0]); route1.InsertAfter(0, 11); Assert.AreEqual("0->11", route1.ToString()); route1.InsertAfter(11, 12); route1.InsertAfter(12, 13); route1.InsertAfter(13, 14); route1.InsertAfter(14, 15); route2 = multiRoute.Add(); customers = new List <int>(route2); Assert.AreEqual(1, customers.Count); Assert.AreEqual(0, customers[0]); route2.InsertAfter(0, 21); Assert.AreEqual("0->21", route2.ToString()); route2.InsertAfter(21, 22); route2.InsertAfter(22, 23); route2.InsertAfter(23, 24); route2.InsertAfter(24, 25); route1.ReplaceEdgeFrom(12, 15); route1.ReplaceEdgeFrom(14, 11); route1.ReplaceEdgeFrom(0, 13); customers = new List <int>(route1); Assert.AreEqual(6, customers.Count); Assert.AreEqual(0, customers[0]); Assert.AreEqual(13, customers[1]); Assert.AreEqual(14, customers[2]); Assert.AreEqual(11, customers[3]); Assert.AreEqual(12, customers[4]); Assert.AreEqual(15, customers[5]); route1.ToString(); }