/// <summary> /// Tests enumeration all edges. /// </summary> protected void DoTestEnumeratePairs() { // create a new empty route. IRoute route = this.BuildRoute(0, true); // do the enumerable. List <Edge> edges = new List <Edge>(route.Edges()); Assert.AreEqual(0, edges.Count); if (route != null) { // this part needs testing! Assert.AreEqual(1, route.Count); Assert.AreEqual(false, route.IsEmpty); Assert.AreEqual(true, route.IsRound); for (int customer = 1; customer < 100; customer++) { route.InsertAfter(customer - 1, customer); //route.InsertAfterAndRemove(customer - 1, customer, -1); edges = new List <Edge>(route.Edges()); Assert.AreEqual(customer + 1, edges.Count); for (int edge_from = 0; edge_from < customer; edge_from++) { Assert.AreEqual(edges[edge_from].From, edge_from); Assert.AreEqual(edges[edge_from].To, edge_from + 1); } Assert.AreEqual(edges[edges.Count - 1].From, edges.Count - 1); Assert.AreEqual(edges[edges.Count - 1].To, 0); } } }
/// <summary> /// Calculates the shortest route along all given points starting and ending at the given points. /// </summary> /// <param name="vehicle">The vehicle type.</param> /// <param name="points">The points to travel along.</param> /// <param name="first">The index of the point to start from.</param> /// <param name="isRound">Return back to the first point or not.</param> /// <returns></returns> public Route CalculateTSP(Vehicle vehicle, RouterPoint[] points, int first, bool isRound) { // calculate the weights. double[][] weights = this.CalculateManyToManyWeight(vehicle, points); // build the points array. var locations = new GeoCoordinate[points.Length]; for (int idx = 0; idx < points.Length; idx++) { locations[idx] = points[idx].Location; } // calculates the TSP solution. IRoute tspSolution = _routerTSP.CalculateTSP(weights, locations, first, isRound); // calculate weight double weight = 0; foreach (Edge edge in tspSolution.Edges()) { weight = weight + weights[edge.From][edge.To]; } // concatenate the route(s). return(this.BuildRoute(vehicle, points, tspSolution, weight)); }
/// <summary> /// Calculates the time of one route. /// </summary> /// <param name="route"></param> /// <returns></returns> public double Time(IRoute route) { double time = 0; foreach (Edge edge in route.Edges()) { time = time + this.WeightMatrix[edge.From][edge.To]; } return(this.Time(time, route.Count)); }
/// <summary> /// Calculates the time of one route. /// </summary> /// <param name="route"></param> /// <returns></returns> public double Time(IRoute route) { double time = 0; //Edge? first = null; //Edge? last = null; foreach (Edge edge in route.Edges()) { //if (!first.HasValue) //{ // first = edge; // time = time + this.WeightMatrix[0][edge.From]; //} time = time + this.WeightMatrix[edge.From][edge.To]; //last = edge; } //if (last.HasValue) //{ // time = time + this.WeightMatrix[last.Value.To][0]; //} return(this.Time(time, route.Count)); }
/// <summary> /// Executes a solver procedure. /// </summary> /// <param name="problem"></param> /// <returns></returns> internal override MaxTimeSolution Solve(MaxTimeProblem problem) { float lambda = _lambda; var vrpRouter = new CheapestInsertionSolverWithImprovements(problem.Max.Value, problem.DeliveryTime.Value, 10, 0.10f, true, _thresholdPercentage, true, 0.75f); MaxTimeSolution originalSolution = vrpRouter.Solve( problem); for (int roundX = 0; roundX < originalSolution.Count; roundX++) { // keep looping on rounds. for (int roundY = 0; roundY < roundX; roundY++) { // keep looping on rounds with a smaller index not equal to the current round. if (roundX != roundY) { // routes are different. if (this.Overlaps(problem, originalSolution.Route(roundX), originalSolution.Route(roundY))) { // only check routes that overlap. double tau = double.MinValue; var penalizations = new Dictionary <Edge, int>(); //bool improvement = true; //while (improvement) //{ // keep looping until no more improvement is found. // improvement = false; while (true) { // keep trying to improve until the tau limit is exceeded. // calculate the route sizes before. double route1ActualBefore = problem.Time(originalSolution.Route(roundX)); double route2ActualBefore = problem.Time(originalSolution.Route(roundY)); // copy orignal solution. var solution = (originalSolution.Clone() as MaxTimeSolution); // apply penalties. foreach (KeyValuePair <Edge, int> penalty in penalizations) { problem.Penalize(penalty.Key, (double)penalty.Value * lambda); } // apply the inter-route improvements. int countBefore = solution.Route(roundX).Count + solution.Route(roundY).Count; int countAfter; if (this.ImproveInterRoute(problem, solution, roundX, roundY, problem.Max.Value)) { // the improve inter route succeeded. if (!solution.IsValid()) { throw new Exception(); } //improvement_inter = true; countAfter = solution.Route(roundX).Count + solution.Route(roundY).Count; if (countBefore != countAfter) { throw new Exception(); } // apply the intra-route improvements. solution[roundX] = this.ImproveIntraRoute(problem, solution.Route(roundX), solution[roundX]); solution[roundY] = this.ImproveIntraRoute(problem, solution.Route(roundY), solution[roundY]); // recalculate weights. solution[roundX] = problem.Time(solution.Route(roundX)); solution[roundY] = problem.Time(solution.Route(roundY)); } // check customer counts. countAfter = solution.Route(roundX).Count + solution.Route(roundY).Count; if (countBefore != countAfter) { throw new Exception(); } // undo the penalizations. problem.ResetPenalizations(); // check against the orginal objective function. double route1ActualAfter = problem.Time(solution.Route(roundX)); double route2ActualAfter = problem.Time(solution.Route(roundY)); if (route1ActualAfter + route2ActualAfter < route1ActualBefore + route2ActualBefore - 0.001 && route1ActualAfter < problem.Max.Value && route2ActualAfter < problem.Max.Value) { // there is improvement! originalSolution = solution; //improvement = true; OsmSharp.Logging.Log.TraceEvent("OsmSharp.Routing.VRP.NoDepot.MaxTime.VNS.GuidedVNS", TraceEventType.Information, "IMPROVEMENT: {0}->{1}", route1ActualBefore + route2ActualBefore, route1ActualAfter + route2ActualAfter); } // select arc to be penalized. IRoute route1 = originalSolution.Route(roundX); IRoute route2 = originalSolution.Route(roundY); double u = double.MinValue; var penalizingEdge = new Edge(); double totalP = 0; foreach (Edge edge in route1.Edges()) { int edgeP; if (!penalizations.TryGetValue(edge, out edgeP)) { edgeP = 0; } totalP = totalP + edgeP; double edgeU = ((lambda * (double)edgeP) + problem.WeightMatrix[edge.From][edge.To]) / ((double)edgeP + 1.0); if (u <= edgeU) { penalizingEdge = edge; u = edgeU; } } foreach (Edge edge in route2.Edges()) { int edgeP; if (!penalizations.TryGetValue(edge, out edgeP)) { edgeP = 0; } totalP = totalP + edgeP; double edgeU = ((lambda * (double)edgeP) + problem.WeightMatrix[edge.From][edge.To]) / ((double)edgeP + 1.0); if (u <= edgeU) { penalizingEdge = edge; u = edgeU; } } // actually penalize the edge. int p; if (!penalizations.TryGetValue(penalizingEdge, out p)) { p = 1; } else { p++; } penalizations[penalizingEdge] = p; // evaluate or set tau. if (tau > double.MinValue) { // evaluate if penalizations should end. if (tau <= lambda * totalP) { // the penalization should end! break; } } else { // first edge being penalized. tau = lambda * problem.WeightMatrix[penalizingEdge.From][penalizingEdge.To] / 10; } } } } } } return(originalSolution); }
/// <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 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> /// Calculates the time of one route. /// </summary> /// <param name="route"></param> /// <returns></returns> public double Time(IRoute route) { double time = 0; foreach(Edge edge in route.Edges()) { time = time + this.WeightMatrix[edge.From][edge.To]; } return this.Time(time, route.Count); }
/// <summary> /// Try and merge route2 into route1. /// </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> private MergeResult TryMerge(MaxTimeProblem problem, MaxTimeSolution solution, int route1_idx, int route2_idx, double max) { // get the route weights. double route1_weight = solution[route1_idx]; double route2_weight = solution[route2_idx]; // creates a result. MergeResult result = new MergeResult(); result.Weight = double.MaxValue; // get the two routes. IRoute route1 = solution.Route(route1_idx); IRoute route2 = solution.Route(route2_idx); // just first do the case where both routes are of zero length. if (route1.Count == 1 && route2.Count == 1) { // calculate the increase when joining the two points. foreach (int customer1 in route1) { foreach (int customer2 in route2) { double difference = problem.WeightMatrix[customer1][customer2] + problem.WeightMatrix[customer2][customer1]; double new_route_weight = route1_weight + difference + route2_weight; if (new_route_weight < max) { result.Weight = difference; result.RouteSourceId = route2_idx; result.RouteTargetId = route1_idx; result.CustomerSourceSource = customer2; result.CustomerSourceTarget = customer2; result.CustomerTargetSource = customer1; return(result); } } } } foreach (Edge route1_edge in route1.Edges()) { // loop over all route1 edges. // calculate weight of the current edge. double route1_edge_weight = problem.WeightMatrix[route1_edge.From][route1_edge.To]; double route1_edge_without = route1_weight - route1_edge_weight; if (route2.Count == 1) { // there is only one customer. foreach (int customer2 in route2) { //// calculate weight of the current edge. //double route2_edge_weight = problem.WeightMatrix[route2_edge.From][route2_edge.To]; //double route2_edge_without = route2_weight - route2_edge_weight; double new_edges_weight = problem.WeightMatrix[route1_edge.From][customer2] + problem.WeightMatrix[customer2][route1_edge.To]; double difference = problem.WeightDifferenceAfterMerge(solution, new_edges_weight - (route1_edge_weight)); // check if the max bound is not violated. double new_route_weight = route1_edge_without + difference + route2_weight; // the customer remain the same. if (new_route_weight < max) { // the difference is smaller than the current result. if (difference < result.Weight) { result.Weight = difference; result.RouteSourceId = route2_idx; result.RouteTargetId = route1_idx; result.CustomerSourceSource = customer2; result.CustomerSourceTarget = customer2; result.CustomerTargetSource = route1_edge.From; } } } } else { // there is at least one edge. foreach (Edge route2_edge in route2.Edges()) { // loop over all route2 edges. // calculate weight of the current edge. double route2_edge_weight = problem.WeightMatrix[route2_edge.From][route2_edge.To]; double route2_edge_without = route2_weight - route2_edge_weight; double new_edges_weight = problem.WeightMatrix[route1_edge.From][route2_edge.To] + problem.WeightMatrix[route2_edge.From][route1_edge.To]; double difference = problem.WeightDifferenceAfterMerge(solution, new_edges_weight - (route1_edge_weight + route2_edge_weight)); // check if the max bound is not violated. double new_route_weight = route1_edge_weight + route2_edge_without + new_edges_weight; // the customer remain the same. if (new_route_weight < max) { // the difference is smaller than the current result. if (difference < result.Weight) { result.Weight = difference; result.RouteSourceId = route2_idx; result.RouteTargetId = route1_idx; result.CustomerSourceSource = route2_edge.To; result.CustomerSourceTarget = route2_edge.From; result.CustomerTargetSource = route1_edge.From; } } } } } return(result); }
/// <summary> /// Calculates the time of one route. /// </summary> /// <param name="route"></param> /// <returns></returns> public double Time(IRoute route) { double time = 0; //Edge? first = null; //Edge? last = null; foreach (Edge edge in route.Edges()) { //if (!first.HasValue) //{ // first = edge; // time = time + this.WeightMatrix[0][edge.From]; //} time = time + this.WeightMatrix[edge.From][edge.To]; //last = edge; } //if (last.HasValue) //{ // time = time + this.WeightMatrix[last.Value.To][0]; //} return this.Time(time, route.Count); }