private IRoute Improve(MaxTimeProblem problem, MaxTimeSolution solution, double max, int currentRouteIdx) { // the current route. IRoute currentRoute = solution.Route(currentRouteIdx); for (int routeIdx = 0; routeIdx < solution.Count; routeIdx++) { // apply the intra-route heurstic between the new and all existing routes. if (routeIdx != currentRouteIdx && this.Overlaps(problem, solution.Route(routeIdx), solution.Route(currentRouteIdx))) { // only check routes that overlap. if (this.ImproveInterRoute(problem, solution, routeIdx, currentRouteIdx, max)) { // an improvement was found, again to the intra operators. if (!solution.IsValid()) { throw new Exception(); } solution[currentRouteIdx] = this.ImproveIntraRoute(problem, currentRoute, solution[currentRouteIdx]); solution[routeIdx] = this.ImproveIntraRoute(problem, solution.Route(routeIdx), solution[routeIdx]); // recalculate weights. solution[currentRouteIdx] = problem.Time(solution.Route(currentRouteIdx)); solution[routeIdx] = problem.Time(solution.Route(routeIdx)); } } } return(solution.Route(solution.Count - 1)); }
/// <summary> /// Selects a new seed customer. /// </summary> /// <param name="problem"></param> /// <param name="calculator"></param> /// <param name="solution"></param> /// <param name="customers"></param> /// <returns></returns> private int SelectSeed(MaxTimeProblem problem, MaxTimeCalculator calculator, MaxTimeSolution solution, List <int> customers) { int selected_customer = -1; double max_distance = double.MaxValue; foreach (int customer_to_check in customers) { SortedDictionary <double, List <int> > neighbours = new SortedDictionary <double, List <int> >(); for (int idx = 0; idx < customers.Count; idx++) { int customer = customers[idx]; if (customer != customer_to_check) { double weight = problem.WeightMatrix[customer_to_check][customer] + problem.WeightMatrix[customer_to_check][customer]; List <int> customers_list = null; if (!neighbours.TryGetValue(weight, out customers_list)) { customers_list = new List <int>(); neighbours.Add(weight, customers_list); } customers_list.Add(customer); } } double nearest_neighbour_average = 0; int neighbour_count = 20; int neighbour_counted = 0; foreach (KeyValuePair <double, List <int> > pair in neighbours) { foreach (int customer in pair.Value) { if (neighbour_counted < neighbour_count) { neighbour_counted++; nearest_neighbour_average = nearest_neighbour_average + pair.Key; } else { break; } } } if (max_distance > nearest_neighbour_average) { max_distance = nearest_neighbour_average; selected_customer = customer_to_check; } } return(selected_customer); }
/// <summary> /// Tries to improve the existing routes by re-inserting a customer from one route into another. /// </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) { if (this.RelocateFromTo(problem, solution, route1_idx, route2_idx, max)) { return true; } if (this.RelocateFromTo(problem, solution, route2_idx, route1_idx, max)) { return true; } return false; }
/// <summary> /// Tries to improve the existing routes by re-inserting a customer from one route into another. /// </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) { if (this.RelocateFromTo(problem, solution, route1_idx, route2_idx, max)) { return(true); } if (this.RelocateFromTo(problem, solution, route2_idx, route1_idx, max)) { return(true); } return(false); }
void solver_NewFittest(Individual <MaxTimeSolution, MaxTimeProblem, Fitness> solution) { long ticks_after = DateTime.Now.Ticks; MaxTimeSolution routes = solution.Genomes; if (output_each) { StringBuilder sizes = new StringBuilder(); foreach (int size in routes.Sizes) { sizes.Append(size); sizes.Append(" "); } StringBuilder weights = new StringBuilder(); foreach (double weight in solution.Fitness.Weights) { weights.Append(weight.ToString(CultureInfo.InvariantCulture)); weights.Append(" "); } StringBuilder probalities = new StringBuilder(); foreach (double prod in _probabilities) { probalities.Append(prod.ToString(CultureInfo.InvariantCulture)); probalities.Append(";"); } string settings_string = string.Format("{0};{1};{2};{3};{4};{5};{6};{7};", _customers.Count, this.Max.Value, _stagnation, _population, _max_generations, _elitism_percentage, _cross_percentage, _mutation_percentage); //output.Close(); //output.Dispose(); //output_file = null; } //Genome routes = individual.Genomes[0]; // TODO: convert solution. int[][] result = new int[routes.Count][]; for (int idx = 0; idx < routes.Count; idx++) { IRoute route = routes.Route(idx); result[idx] = route.ToArray(); } this.RaiseIntermidiateResult(result); }
/// <summary> /// Selects a new seed customer. /// </summary> /// <param name="problem"></param> /// <param name="calculator"></param> /// <param name="solution"></param> /// <param name="customers"></param> /// <returns></returns> private int SelectSeed(MaxTimeProblem problem, MaxTimeCalculator calculator, MaxTimeSolution solution, List <int> customers) { // select the customer farthest from the depot. int selectedCustomer = -1; double maxDistance = double.MinValue; foreach (int customerToCheck in customers) { double distance = problem.WeightMatrix[0][customerToCheck] + problem.WeightMatrix[customerToCheck][0]; if (distance > maxDistance) { maxDistance = distance; selectedCustomer = customerToCheck; } } return(selectedCustomer); }
void solver_NewGeneration(int generation, int stagnation_count, Population <MaxTimeSolution, MaxTimeProblem, Fitness> population) { generations++; long ticks_after = DateTime.Now.Ticks; Individual <MaxTimeSolution, MaxTimeProblem, Fitness> solution = population[0]; MaxTimeSolution routes = solution.Genomes; if (output_each) { StringBuilder sizes = new StringBuilder(); foreach (int size in routes.Sizes) { sizes.Append(size); sizes.Append(" "); } StringBuilder weights = new StringBuilder(); foreach (double weight in solution.Fitness.Weights) { weights.Append(weight.ToString(CultureInfo.InvariantCulture)); weights.Append(" "); } StringBuilder probalities = new StringBuilder(); foreach (double prod in _probabilities) { probalities.Append(prod.ToString(CultureInfo.InvariantCulture)); probalities.Append(";"); } string settings_string = string.Format("{0};{1};{2};{3};{4};{5};{6};{7};", _customers.Count, this.Max.Value, _stagnation, _population, _max_generations, _elitism_percentage, _cross_percentage, _mutation_percentage); //output.Close(); //output.Dispose(); //output_file = null; } }
/// <summary> /// Calculates the tot weight of one solution. /// </summary> /// <param name="solution"></param> /// <returns></returns> public double Calculate(MaxTimeSolution solution) { int vehicles = solution.Count; // List<double> above_max = new List<double>(); double total = 0; double total_above_max = 0; int total_count_above_max = 0; double max = -1; List<double> weights = new List<double>(); for (int route_idx = 0; route_idx < solution.Count; route_idx++) { double weight = this.CalculateOneRoute(solution.Route( route_idx)); weights.Add(weight); total = total + weight; if (weight > _problem.Max.Value) { total_above_max = total_above_max + (weight - _problem.Max.Value); total_count_above_max++; } if (max < weight) { max = weight; } } //double above_max_factor = 3; // multiply with the maximum. //fitness.ActualFitness = (vehicles * ((total_above_max) + total)); //fitness.ActualFitness = (vehicles * ((total_above_max * max) + total)); //fitness.ActualFitness = (vehicles * ((System.Math.Pow(total_above_max, 1.28)) + total + max)); return (vehicles * System.Math.Pow(total_above_max, 4)) + total; ////fitness.ActualFitness = (vehicles * (total + ((total_count_above_max * above_max_factor) * max))); ////fitness.ActualFitness = (total + ((total_count_above_max * above_max_factor) * max)); //fitness.Vehicles = vehicles; //fitness.TotalTime = total; //fitness.TotalAboveMax = total_above_max; //return fitness; }
/// <summary> /// Tries a relocation of the customers in route1 to route2. /// </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 bool RelocateFromTo(MaxTimeProblem problem, MaxTimeSolution solution, int route1_idx, int route2_idx, double max) { int previous = -1; int current = -1; IRoute route1 = solution.Route(route1_idx); IRoute route2 = solution.Route(route2_idx); double route2_weight = solution[route2_idx]; foreach (int next in route1) { if (previous >= 0 && current >= 0) { // consider the next customer. int count_before1 = route1.Count; int count_before2 = route2.Count; // string route1_string = route1.ToString(); // string route2_string = route2.ToString(); if (this.ConsiderCustomer(problem, route2, previous, current, next, route2_weight, max)) { route1.ReplaceEdgeFrom(previous, next); int count_after = route1.Count + route2.Count; if ((count_before1 + count_before2) != count_after) { throw new Exception(); } return(true); } } previous = current; current = next; } return(false); }
/// <summary> /// Executes a solver procedure. /// </summary> /// <param name="problem"></param> /// <returns></returns> internal override MaxTimeSolution Solve(MaxTimeProblem problem) { // create the solution. var solution = new MaxTimeSolution(problem.Size); // keep placing customer until none are left. var customers = new List<int>(problem.Customers); customers.RemoveAt(0); double max = problem.Max.Value - (problem.Max.Value * _deltaPercentage); // keep a list of cheapest insertions. IInsertionCosts costs = new BinaryHeapInsertionCosts(); // double percentage = _thresholdPercentage; while (customers.Count > 0) { //// try and distribute the remaining customers if there are only a few left. //if (customers.Count < problem.Size * percentage) //{ // bool succes = true; // while (succes && customers.Count > 0) // { // succes = false; // CheapestInsertionResult best = new CheapestInsertionResult(); // best.Increase = float.MaxValue; // int best_idx = -1; // for (int route_idx = 0; route_idx < solution.Count; route_idx++) // { // IRoute route = solution.Route(route_idx); // CheapestInsertionResult result = // CheapestInsertionHelper.CalculateBestPlacement(problem, route, customers); // if (best.Increase > result.Increase) // { // best = result; // best_idx = route_idx; // } // } // IRoute best_route = solution.Route(best_idx); // double route_time = problem.Time(best_route); // if (route_time + best.Increase < max) // { // insert the customer. // best_route.InsertAfter(best.CustomerBefore, best.Customer); // customers.Remove(best.Customer); // this.Improve(problem, solution, max, best_idx); // succes = true; // } // } //} // select a customer using some heuristic. int customer = this.SelectSeed(problem, problem.MaxTimeCalculator, solution, customers); customers.Remove(customer); // start a route r. IRoute currentRoute = solution.Add(customer); solution[solution.Count - 1] = 0; while (customers.Count > 0) { // calculate the best placement. CheapestInsertionResult result; if (_useSeedCost) { // use the seed cost; the cost to the seed customer. result = CheapestInsertionHelper.CalculateBestPlacement(problem, currentRoute, customers, customer, 0.7); // calculate the 'real' increase. result.Increase = (problem.WeightMatrix[result.CustomerBefore][result.Customer] + problem.WeightMatrix[result.Customer][result.CustomerAfter]) - problem.WeightMatrix[result.CustomerBefore][result.CustomerAfter]; } else { // just use cheapest insertion. result = CheapestInsertionHelper.CalculateBestPlacement(problem, currentRoute, customers, costs); } // calculate the new weight. solution[solution.Count - 1] = problem.Time(solution.Route(solution.Count - 1)); double potentialWeight = problem.MaxTimeCalculator.CalculateOneRouteIncrease(solution[solution.Count - 1], result.Increase); // cram as many customers into one route as possible. if (potentialWeight < max) { // insert the customer, it is customers.Remove(result.Customer); currentRoute.InsertAfter(result.CustomerBefore, result.Customer); // free some memory in the costs list. costs.Remove(result.CustomerBefore, result.CustomerAfter); // update the cost of the route. solution[solution.Count - 1] = potentialWeight; // improve if needed. if (((problem.Size - customers.Count) % _k) == 0) { // an improvement is decided. // apply the inter-route improvements. // var copy = (solution.Clone() as MaxTimeSolution); int countBefore = solution.Route(solution.Count - 1).Count; solution[solution.Count - 1] = this.ImproveIntraRoute(problem, solution.Route(solution.Count - 1), solution[solution.Count - 1]); if (!solution.IsValid()) { throw new Exception(); } int countAfter = solution.Route(solution.Count - 1).Count; if (countAfter != countBefore) { throw new Exception(); } // also to the inter-improvements. currentRoute = this.Improve(problem, solution, max, solution.Count - 1); } } else {// ok we are done! this.Improve(problem, solution, max, solution.Count - 1); // break the route. break; } } } // remove empty routes. for (int routeIdx = solution.Count - 1; routeIdx >= 0; routeIdx--) { if (solution.Route(routeIdx).IsEmpty) { solution.Remove(routeIdx); } } return solution; }
/// <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> /// Executes a solver procedure. /// </summary> /// <param name="problem"></param> /// <returns></returns> internal override MaxTimeSolution Solve(MaxTimeProblem problem) { // create the solution. var solution = new MaxTimeSolution(problem.Size); // keep placing customer until none are left. var customers = new List <int>(problem.Customers); customers.RemoveAt(0); double max = problem.Max.Value - (problem.Max.Value * _deltaPercentage); // keep a list of cheapest insertions. IInsertionCosts costs = new BinaryHeapInsertionCosts(); double percentage = _thresholdPercentage; while (customers.Count > 0) { //// try and distribute the remaining customers if there are only a few left. //if (customers.Count < problem.Size * percentage) //{ // bool succes = true; // while (succes && customers.Count > 0) // { // succes = false; // CheapestInsertionResult best = new CheapestInsertionResult(); // best.Increase = float.MaxValue; // int best_idx = -1; // for (int route_idx = 0; route_idx < solution.Count; route_idx++) // { // IRoute route = solution.Route(route_idx); // CheapestInsertionResult result = // CheapestInsertionHelper.CalculateBestPlacement(problem, route, customers); // if (best.Increase > result.Increase) // { // best = result; // best_idx = route_idx; // } // } // IRoute best_route = solution.Route(best_idx); // double route_time = problem.Time(best_route); // if (route_time + best.Increase < max) // { // insert the customer. // best_route.InsertAfter(best.CustomerBefore, best.Customer); // customers.Remove(best.Customer); // this.Improve(problem, solution, max, best_idx); // succes = true; // } // } //} // select a customer using some heuristic. int customer = this.SelectSeed(problem, problem.MaxTimeCalculator, solution, customers); customers.Remove(customer); // start a route r. IRoute currentRoute = solution.Add(customer); solution[solution.Count - 1] = 0; while (customers.Count > 0) { // calculate the best placement. CheapestInsertionResult result; if (_useSeedCost) { // use the seed cost; the cost to the seed customer. result = CheapestInsertionHelper.CalculateBestPlacement(problem, currentRoute, customers, customer, 0.7); // calculate the 'real' increase. result.Increase = (problem.WeightMatrix[result.CustomerBefore][result.Customer] + problem.WeightMatrix[result.Customer][result.CustomerAfter]) - problem.WeightMatrix[result.CustomerBefore][result.CustomerAfter]; } else { // just use cheapest insertion. result = CheapestInsertionHelper.CalculateBestPlacement(problem, currentRoute, customers, costs); } // calculate the new weight. solution[solution.Count - 1] = problem.Time(solution.Route(solution.Count - 1)); double potentialWeight = problem.MaxTimeCalculator.CalculateOneRouteIncrease(solution[solution.Count - 1], result.Increase); // cram as many customers into one route as possible. if (potentialWeight < max) { // insert the customer, it is customers.Remove(result.Customer); currentRoute.InsertAfter(result.CustomerBefore, result.Customer); // free some memory in the costs list. costs.Remove(result.CustomerBefore, result.CustomerAfter); // update the cost of the route. solution[solution.Count - 1] = potentialWeight; // improve if needed. if (((problem.Size - customers.Count) % _k) == 0) { // an improvement is decided. // apply the inter-route improvements. var copy = (solution.Clone() as MaxTimeSolution); int countBefore = solution.Route(solution.Count - 1).Count; solution[solution.Count - 1] = this.ImproveIntraRoute(problem, solution.Route(solution.Count - 1), solution[solution.Count - 1]); if (!solution.IsValid()) { throw new Exception(); } int countAfter = solution.Route(solution.Count - 1).Count; if (countAfter != countBefore) { throw new Exception(); } // also to the inter-improvements. currentRoute = this.Improve(problem, solution, max, solution.Count - 1); } } else {// ok we are done! this.Improve(problem, solution, max, solution.Count - 1); // break the route. break; } } } // remove empty routes. for (int routeIdx = solution.Count - 1; routeIdx >= 0; routeIdx--) { if (solution.Route(routeIdx).IsEmpty) { solution.Remove(routeIdx); } } return(solution); }
/// <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> /// 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 a relocation of the customers in route1 to route2. /// </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 bool RelocateFromTo(MaxTimeProblem problem, MaxTimeSolution solution, int route1_idx, int route2_idx, double max) { int previous = -1; int current = -1; IRoute route1 = solution.Route(route1_idx); IRoute route2 = solution.Route(route2_idx); double route2_weight = solution[route2_idx]; foreach (int next in route1) { if (previous >= 0 && current >= 0) { // consider the next customer. int count_before1 = route1.Count; int count_before2 = route2.Count; // string route1_string = route1.ToString(); // string route2_string = route2.ToString(); if (this.ConsiderCustomer(problem, route2, previous, current, next, route2_weight, max)) { route1.ReplaceEdgeFrom(previous, next); int count_after = route1.Count + route2.Count; if ((count_before1 + count_before2) != count_after) { throw new Exception(); } return true; } } previous = current; current = next; } return false; }
/// <summary> /// Executes a solver procedure. /// </summary> /// <param name="problem"></param> /// <returns></returns> internal override MaxTimeSolution Solve(MaxTimeProblem problem) { _customers = problem.Customers; //float[] solutions = OsmSharp.Math.VRP.Core.BestPlacement.CheapestInsertionHelper.CalculateBestValues( // problem, _customers); generations = 0; _max_generations = 10000000; // calculate one tsp solution. //Tools.Math.TSP.ISolver tsp_solver = new OsmSharp.Math.TSP.EdgeAssemblyGenetic.EdgeAssemblyCrossOverSolver(_population, _stagnation, // new OsmSharp.Math.TSP.Genetic.Solver.Operations.Generation._3OptGenerationOperation(), // new OsmSharp.Math.TSP.Genetic.Solver.Operations.CrossOver.EdgeAssemblyCrossover(30, // OsmSharp.Math.TSP.Genetic.Solver.Operations.CrossOver.EdgeAssemblyCrossover.EdgeAssemblyCrossoverSelectionStrategyEnum.SingleRandom, // true)); //IRoute tsp_solution = tsp_solver.Solve(new OsmSharp.Routing.VRP.NoDepot.MaxTime.TSPPlacement.TSPProblem( // problem)); // initialize the generation. IGenerationOperation <MaxTimeSolution, MaxTimeProblem, Fitness> generation = //new SolverGenerationOperation(new TSPPlacement.TSPPlacementSolver<ResolvedType>( // this.Router, this.Max, this.DeliveryTime, tsp_solution)); new OsmSharp.Routing.VRP.NoDepot.MaxTime.Genetic.Generation.RandomBestPlacement(); //new SolverGenerationOperation(new CheapestInsertionSolverWithImprovements<ResolvedType>( // this.Router, this.Max, this.DeliveryTime, 5, 0.1f, true, 0.1f, false, 1f, null, null)); //new SolverGenerationOperation(new CheapestInsertionSolverWithImprovements<ResolvedType>( // this.Router, this.Max, this.DeliveryTime, 5, 0.1f, true, 0.1f, true, 1f)); // initialize the crossover. ICrossOverOperation <MaxTimeSolution, MaxTimeProblem, Fitness> cross_over = new OsmSharp.Routing.VRP.NoDepot.MaxTime.Genetic.CrossOver.RouteExchangeOperation(); // initialize the mutation. //IMutationOperation<MaxTimeSolution, MaxTimeProblem, Fitness> mutation = // new VehicleMutation(); List <IMutationOperation <MaxTimeSolution, MaxTimeProblem, Fitness> > mutators = new List <IMutationOperation <MaxTimeSolution, MaxTimeProblem, Fitness> >(); mutators.Add(new VehicleMutation()); //mutators.Add(new ThreeOptMutation()); //mutators.Add(new RedivideRouteMutation()); mutators.Add(new RoutePartExchangeMutation()); if (_probabilities == null) { _probabilities = new List <double>(); _probabilities.Add(0.2); _probabilities.Add(0.6); _probabilities.Add(0.2); } CombinedMutation <MaxTimeSolution, MaxTimeProblem, Fitness> mutation = new CombinedMutation <MaxTimeSolution, MaxTimeProblem, Fitness>( StaticRandomGenerator.Get(), mutators, _probabilities); SolverSettings settings = new SolverSettings(_stagnation, _population, _max_generations, _elitism_percentage, _cross_percentage, _mutation_percentage); MaxTimeProblem genetic_problem = problem;// new MaxTimeProblem(max, problem, solutions); Solver <MaxTimeSolution, MaxTimeProblem, Fitness> solver = new Solver <MaxTimeSolution, MaxTimeProblem, Fitness>(genetic_problem, settings, new TournamentBasedSelector <MaxTimeSolution, MaxTimeProblem, Fitness>(5, 0.5), mutation, //new ThreeOptMutation(), cross_over, // new RouteExchangeOperation(), //new RouteExchangeOperation(), //new RouteExchangeAndVehicleOperation(), // Order1CrossoverOperation() generation, //new RandomBestPlacement(),//new RandomGeneration(), //new RandomBestPlacement(), new FitnessCalculator()); solver.NewFittest += new Solver <MaxTimeSolution, MaxTimeProblem, Fitness> .NewFittestDelegate(solver_NewFittest); //solver.NewGeneration += new Solver<MaxTimeSolution, Problem, Fitness>.NewGenerationDelegate(solver_NewGeneration); Individual <MaxTimeSolution, MaxTimeProblem, Fitness> solution = solver.Start(null); //this.solver_NewFittest(solution); MaxTimeSolution routes = solution.Genomes; long ticks_after = DateTime.Now.Ticks; StringBuilder sizes = new StringBuilder(); foreach (int size in routes.Sizes) { sizes.Append(size); sizes.Append(" "); } StringBuilder weights = new StringBuilder(); foreach (double weight in solution.Fitness.Weights) { weights.Append(weight.ToString(CultureInfo.InvariantCulture)); weights.Append(" "); } return(routes); }
/// <summary> /// Executes a solver procedure. /// </summary> /// <param name="problem"></param> /// <returns></returns> internal override MaxTimeSolution Solve(MaxTimeProblem problem) { MaxTimeCalculator calculator = new MaxTimeCalculator(problem); // get the seed customers. ICollection <int> seeds = _seed_selector.SelectSeeds( problem, _k); double[] weights = new double[seeds.Count]; // start the seed routes. List <int> selectable_customers = problem.Customers; MaxTimeSolution routes = new MaxTimeSolution( problem.Size, true); foreach (int seed in seeds) { routes.Add(seed); selectable_customers.Remove(seed); } if (!routes.IsValid()) { throw new Exception(); } // keep a list of cheapest insertions. IInsertionCosts costs = new BinaryHeapInsertionCosts(); // keep looping until all customers have been placed. while (selectable_customers.Count > 0) { // try and place into every route. CheapestInsertionResult best_result = new CheapestInsertionResult(); best_result.Increase = float.MaxValue; int best_route_idx = -1; CheapestInsertionResult best_result_above_max = new CheapestInsertionResult(); best_result_above_max.Increase = float.MaxValue; int best_route_above_max_idx = -1; for (int route_idx = 0; route_idx < routes.Count; route_idx++) { IRoute current_route = routes.Route(route_idx); // choose the next customer. CheapestInsertionResult result = CheapestInsertionHelper.CalculateBestPlacement(problem, current_route, selectable_customers, costs); if (result.Customer == result.CustomerAfter) { throw new Exception(); } // get the current weight double weight = weights[route_idx]; if (result.Increase < best_result.Increase) { if (weight + result.Increase + calculator.DeliveryTime < problem.Max.Value) { // route will still be inside bounds. best_result = result; best_route_idx = route_idx; } else { // route will become above max. if (result.Increase < best_result_above_max.Increase) { best_result_above_max = result; best_route_above_max_idx = route_idx; } } } } // do the placement if a placement is found without max violation. // else do the placement in the above max route. CheapestInsertionResult placement_result = new CheapestInsertionResult(); placement_result.Increase = double.MaxValue; int placement_result_idx = -1; if (best_route_idx >= 0) { // best placement found. placement_result = best_result; placement_result_idx = best_route_idx; } else { // best placement found but only above max. placement_result = best_result_above_max; placement_result_idx = best_route_above_max_idx; } // do the actual placement. weights[placement_result_idx] = calculator.CalculateOneRouteIncrease( weights[placement_result_idx], placement_result.Increase); selectable_customers.Remove(placement_result.Customer); //routes.Route(placement_result_idx).InsertAfterAndRemove( // placement_result.CustomerBefore, placement_result.Customer, placement_result.CustomerAfter); routes.Route(placement_result_idx).InsertAfter( placement_result.CustomerBefore, placement_result.Customer); if (!routes.IsValid()) { throw new Exception(); } } return(routes); }
/// <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); }
/// <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> /// Selects a new seed customer. /// </summary> /// <param name="problem"></param> /// <param name="calculator"></param> /// <param name="solution"></param> /// <param name="customers"></param> /// <returns></returns> private int SelectSeed(MaxTimeProblem problem, MaxTimeCalculator calculator, MaxTimeSolution solution, List<int> customers) { // select the customer farthest from the depot. int selectedCustomer = -1; double maxDistance = double.MinValue; foreach (int customerToCheck in customers) { double distance = problem.WeightMatrix[0][customerToCheck] + problem.WeightMatrix[customerToCheck][0]; if (distance > maxDistance) { maxDistance = distance; selectedCustomer = customerToCheck; } } return selectedCustomer; }
/// <summary> /// Calculates the total weight. /// </summary> /// <param name="solution"></param> /// <returns></returns> public double Weight(MaxTimeSolution solution) { double time = 0; double time_above_max = 0; for (int idx = 0; idx < solution.Count; idx++) { // calculate one route. double route_time = this.Time(solution.Route(idx)); // add the total time about max. if (route_time > this.Max.Value) { // route time too big! time_above_max = time_above_max + (route_time - this.Max.Value); } // add to the total time. time = time + route_time; } // the route count. double route_count = solution.Count; // the punished for breaking max. double punishment = System.Math.Pow(time_above_max, 4) * route_count; return route_count * time * _cost_per_second + route_count * _cost_per_vehicle + punishment; }
/// <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 = 5; 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; }
internal override MaxTimeSolution Solve(MaxTimeProblem problem) { MaxTimeCalculator calculator = new MaxTimeCalculator( problem); // generate a ATSP solution. IRoute tsp_solution = _tsp_solution; if (tsp_solution == null) { tsp_solution = _tsp_solver.Solve(new TSPProblem(problem)); } // generate subtours from this solution. MaxTimeSolution solution = new MaxTimeSolution(problem.Size, true); // select a random start point. int start = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(problem.Size); // start the first tour. int placed = 0; int previous = -1; // place the first customer. double weight = 0; double total_weight = 0; IRoute route = solution.Add(start); previous = start; while (placed < problem.Size) { // get the next customer from the tsp solution. int next = tsp_solution.GetNeigbours(previous)[0]; // get the weight to the current start. double weight_to_next = problem.WeightMatrix[previous][next]; double weight_to_start = problem.WeightMatrix[next][start]; total_weight = calculator.CalculateOneRouteIncrease( weight, weight_to_next + weight_to_start); weight = calculator.CalculateOneRouteIncrease( weight, weight_to_next); if (total_weight > problem.Max.Value) { // start a new route. route = solution.Add(next); weight = 0; } else { // just insert the next customer. route.InsertAfter(previous, next); //route.InsertAfterAndRemove(previous, next, -1); } // set the previous. previous = next; placed++; } if (!solution.IsValid()) { throw new Exception(); } StringBuilder builder = new StringBuilder(); builder.Append("["); total_weight = 0; for (int idx = 0; idx < solution.Count; idx++) { //IRoute route = routes.Route(idx); route = solution.Route(idx); weight = calculator.CalculateOneRoute(route); builder.Append(" "); builder.Append(weight); builder.Append(" "); total_weight = total_weight + weight; } builder.Append("]"); builder.Append(total_weight); builder.Append(": "); builder.Append(calculator.Calculate(solution)); Console.WriteLine(builder.ToString()); return(solution); }
internal override MaxTimeSolution Solve(MaxTimeProblem problem) { MaxTimeCalculator calculator = new MaxTimeCalculator( problem); // generate a ATSP solution. IRoute tsp_solution = _tsp_solution; if (tsp_solution == null) { tsp_solution = _tsp_solver.Solve(new TSPProblem(problem)); } // generate subtours from this solution. MaxTimeSolution solution = new MaxTimeSolution(problem.Size, true); // select a random start point. int start = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(problem.Size); // start the first tour. int placed = 0; int previous = -1; // place the first customer. double weight = 0; double total_weight = 0; IRoute route = solution.Add(start); previous = start; while (placed < problem.Size) { // get the next customer from the tsp solution. int next = tsp_solution.GetNeigbours(previous)[0]; // get the weight to the current start. double weight_to_next = problem.WeightMatrix[previous][next]; double weight_to_start = problem.WeightMatrix[next][start]; total_weight = calculator.CalculateOneRouteIncrease( weight, weight_to_next + weight_to_start); weight = calculator.CalculateOneRouteIncrease( weight, weight_to_next); if (total_weight > problem.Max.Value) { // start a new route. route = solution.Add(next); weight = 0; } else { // just insert the next customer. route.InsertAfter(previous, next); //route.InsertAfterAndRemove(previous, next, -1); } // set the previous. previous = next; placed++; } if (!solution.IsValid()) { throw new Exception(); } StringBuilder builder = new StringBuilder(); builder.Append("["); total_weight = 0; for (int idx = 0; idx < solution.Count; idx++) { //IRoute route = routes.Route(idx); route = solution.Route(idx); weight = calculator.CalculateOneRoute(route); builder.Append(" "); builder.Append(weight); builder.Append(" "); total_weight = total_weight + weight; } builder.Append("]"); builder.Append(total_weight); builder.Append(": "); builder.Append(calculator.Calculate(solution)); OsmSharp.Logging.Log.TraceEvent("TSPPlacementSolver", TraceEventType.Information, builder.ToString()); return solution; }
///// <summary> ///// Apply some improvements between the given routes and returns the resulting weight. ///// </summary> ///// <param name="problem"></param> ///// <param name="route"></param> ///// <returns></returns> //private bool ImproveInterRoute(MaxTimeProblem problem, MaxTimeSolution solution, int route1_idx, int route2_idx, double max) //{ // // get the routes. // IRoute route1 = solution.Route(route1_idx); // IRoute route2 = solution.Route(route2_idx); // int count_before = route1.Count + route2.Count; // // get the weights. // double route1_weight = solution[route1_idx]; // double route2_weight = solution[route2_idx]; // // loop over all improvement operations. // bool global_improvement = false; // foreach (IInterImprovement improvement_operation in _inter_improvements) // { // try the current improvement operations. // bool improvement = true; // while (improvement) // { // keep looping when there is improvement. // improvement = false; // if (improvement_operation.Improve(problem, solution, route1_idx, route2_idx, max)) // { // there was an improvement. // improvement = true; // global_improvement = true; // OsmSharp.Tools.Output.OutputStreamHost.WriteLine("Inter-improvement found {0}<->{1}: {2}", // route1_idx, route2_idx, improvement_operation.Name); // // check if the route is valid. // if (!route1.IsValid()) // { // throw new Exception(); // } // if (!route2.IsValid()) // { // throw new Exception(); // } // int count_after = route1.Count + route2.Count; // if (count_before != count_after) // { // throw new Exception(); // } // // recalculate weights. // solution[route1_idx] = problem.Time(solution.Route(route1_idx)); // solution[route2_idx] = problem.Time(solution.Route(route2_idx)); // //break; // } // } // } // return global_improvement; //} /// <summary> /// Apply some improvements between the given routes and returns the resulting weight. /// </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 bool ImproveInterRoute(MaxTimeProblem problem, MaxTimeSolution solution, int route1_idx, int route2_idx, double max) { // get the routes. IRoute route1 = solution.Route(route1_idx); IRoute route2 = solution.Route(route2_idx); int count_before = route1.Count + route2.Count; //// get the weights. //double route1_weight = solution[route1_idx]; //double route2_weight = solution[route2_idx]; // loop over all improvement operations. bool global_improvement = false; foreach (IInterImprovement improvement_operation in _inter_improvements) { // try the current improvement operations. bool improvement = true; while (improvement) { // keep looping when there is improvement. improvement = false; double total_before = problem.Time(solution.Route(route1_idx)) + problem.Time(solution.Route(route2_idx)); if (improvement_operation.Improve(problem, solution, route1_idx, route2_idx, max)) { // there was an improvement. improvement = true; global_improvement = true; // check if the route is valid. if (!route1.IsValid()) { throw new Exception(); } if (!route2.IsValid()) { throw new Exception(); } int count_after = route1.Count + route2.Count; if (count_before != count_after) { 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!"); } OsmSharp.Tools.Output.OutputStreamHost.WriteLine("Inter-improvement found {0}<->{1}: {2} ({3}->{4})", route1_idx, route2_idx, improvement_operation.Name, total_before, total_after); // recalculate weights. solution[route1_idx] = problem.Time(solution.Route(route1_idx)); solution[route2_idx] = problem.Time(solution.Route(route2_idx)); //break; } else if (!improvement_operation.IsSymmetric && improvement_operation.Improve(problem, solution, route2_idx, route1_idx, max)) { // also do the improvement the other way around when not symmetric. improvement = true; global_improvement = true; // check if the route is valid. if (!route1.IsValid()) { throw new Exception(); } if (!route2.IsValid()) { throw new Exception(); } int count_after = route1.Count + route2.Count; if (count_before != count_after) { 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!"); } OsmSharp.Tools.Output.OutputStreamHost.WriteLine("Inter-improvement found {0}<->{1}: {2} ({3}->{4})", route2_idx, route1_idx, improvement_operation.Name, total_before, total_after); // recalculate weights. solution[route1_idx] = problem.Time(solution.Route(route1_idx)); solution[route2_idx] = problem.Time(solution.Route(route2_idx)); //break; } } } return global_improvement; }
/// <summary> /// Executes a solver procedure. /// </summary> /// <param name="problem"></param> /// <returns></returns> internal override MaxTimeSolution Solve(MaxTimeProblem problem) { // create the solution. MaxTimeSolution solution = new MaxTimeSolution(problem.Size, true); // keep placing customer until none are left. List<int> customers = new List<int>(problem.Customers); double max = problem.Max.Value - (problem.Max.Value * _delta_percentage); // keep a list of cheapest insertions. IInsertionCosts costs = new BinaryHeapInsertionCosts(); double percentage = _threshold_percentage; while (customers.Count > 0) { // try and distribute the remaining customers if there are only a few left. if (customers.Count < problem.Size * percentage) { bool succes = true; while (succes && customers.Count > 0) { succes = false; CheapestInsertionResult best = new CheapestInsertionResult(); best.Increase = float.MaxValue; int best_idx = -1; for (int route_idx = 0; route_idx < solution.Count; route_idx++) { IRoute route = solution.Route(route_idx); CheapestInsertionResult result = CheapestInsertionHelper.CalculateBestPlacement(problem, route, customers); if (best.Increase > result.Increase) { best = result; best_idx = route_idx; } } IRoute best_route = solution.Route(best_idx); double route_time = problem.Time(best_route); if (route_time + best.Increase < max) { // insert the customer. best_route.InsertAfter(best.CustomerBefore, best.Customer); customers.Remove(best.Customer); this.Improve(problem, solution, max, best_idx); succes = true; } } } // select a customer using some heuristic. if (customers.Count > 0) { // select a customer using some heuristic. int customer = -1; if (_use_seed) { // use a seeding heuristic. customer = this.SelectSeed(problem, problem.MaxTimeCalculator, solution, customers); } else { // just select a random customer. customer = customers[Math.Random.StaticRandomGenerator.Get().Generate(customers.Count)]; } customers.Remove(customer); // start a route r. IRoute current_route = solution.Add(customer); solution[solution.Count - 1] = 0; while (customers.Count > 0) { //OsmSharp.IO.Output.OutputStreamHost.WriteLine("{0}/{1} placed!", // customers.Count, problem.Size); // calculate the best placement. CheapestInsertionResult result; if (_use_seed_cost) { // use the seed cost; the cost to the seed customer. result = CheapestInsertionHelper.CalculateBestPlacement(problem, current_route, customers, customer, _lambda); // calculate the 'real' increase. result.Increase = (problem.WeightMatrix[result.CustomerBefore][result.Customer] + problem.WeightMatrix[result.Customer][result.CustomerAfter]) - problem.WeightMatrix[result.CustomerBefore][result.CustomerAfter]; } else { // just use cheapest insertion. result = CheapestInsertionHelper.CalculateBestPlacement(problem, current_route, customers, costs); } // calculate the new weight. solution[solution.Count - 1] = problem.Time(solution.Route(solution.Count - 1)); double potential_weight = problem.MaxTimeCalculator.CalculateOneRouteIncrease(solution[solution.Count - 1], result.Increase); // cram as many customers into one route as possible. if (potential_weight < max) { // insert the customer, it is customers.Remove(result.Customer); current_route.InsertAfter(result.CustomerBefore, result.Customer); // free some memory in the costs list. costs.Remove(result.CustomerBefore, result.CustomerAfter); // update the cost of the route. solution[solution.Count - 1] = potential_weight; // improve if needed. if (((problem.Size - customers.Count) % _k) == 0) { // an improvement is descided. // apply the inter-route improvements. int count_before = solution.Route(solution.Count - 1).Count; solution[solution.Count - 1] = this.ImproveIntraRoute(problem, current_route, solution[solution.Count - 1]); if (!solution.IsValid()) { throw new Exception(); } int count_after = solution.Route(solution.Count - 1).Count; // also to the inter-improvements. current_route = this.Improve(problem, solution, max, solution.Count - 1); } } else {// ok we are done! this.Improve(problem, solution, max, solution.Count - 1); // break the route. break; } } //else //{// ok we are done! // solution[solution.Count - 1] = this.ImproveIntraRoute(problem, // current_route, solution[solution.Count - 1]); // if (!solution.IsValid()) // { // throw new Exception(); // } // int count_after = solution.Route(solution.Count - 1).Count; // this.Improve(problem, solution, max, solution.Count - 1); // // break the route. // break; //} } } // remove empty routes. for (int route_idx = solution.Count - 1; route_idx >= 0; route_idx--) { if (solution.Route(route_idx).IsEmpty) { solution.Remove(route_idx); } } return solution; }
/// <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; }
private IRoute Improve(MaxTimeProblem problem, MaxTimeSolution solution, double max, int current_route_idx) { // the current route. IRoute current_route = solution.Route(current_route_idx); for (int route_idx = 0; route_idx < solution.Count; route_idx++) { // apply the intra-route heurstic between the new and all existing routes. if (route_idx != current_route_idx && this.Overlaps(problem, solution.Route(route_idx), solution.Route(current_route_idx))) { // only check routes that overlap. if (this.ImproveInterRoute(problem, solution, route_idx, current_route_idx, max)) { // an improvement was found, again to the intra operators. if (!solution.IsValid()) { throw new Exception(); } solution[current_route_idx] = this.ImproveIntraRoute(problem, current_route, solution[current_route_idx]); solution[route_idx] = this.ImproveIntraRoute(problem, solution.Route(route_idx), solution[route_idx]); // recalculate weights. solution[current_route_idx] = problem.Time(solution.Route(current_route_idx)); solution[route_idx] = problem.Time(solution.Route(route_idx)); } } } return solution.Route(solution.Count - 1); }
/// <summary> /// Calculates the weight difference after merging two routes given the cost to merge them. /// </summary> /// <param name="solution"></param> /// <param name="merge_costs"></param> /// <returns></returns> public double WeightDifferenceAfterMerge(MaxTimeSolution solution, double merge_costs) { if (solution.Count < 2) { // the solution routes cannot be merged. return double.MaxValue; } // the route count. double route_count = solution.Count - 1; return route_count * merge_costs * _cost_per_second + route_count * _cost_per_vehicle; }
/// <summary> /// Selects a new seed customer. /// </summary> /// <param name="problem"></param> /// <param name="calculator"></param> /// <param name="solution"></param> /// <param name="customers"></param> /// <returns></returns> private int SelectSeed(MaxTimeProblem problem, MaxTimeCalculator calculator, MaxTimeSolution solution, List<int> customers) { int selected_customer = -1; double max_distance = double.MaxValue; foreach (int customer_to_check in customers) { SortedDictionary<double, List<int>> neighbours = new SortedDictionary<double, List<int>>(); for (int idx = 0; idx < customers.Count; idx++) { int customer = customers[idx]; if (customer != customer_to_check) { double weight = problem.WeightMatrix[customer_to_check][customer] + problem.WeightMatrix[customer_to_check][customer]; List<int> customers_list = null; if (!neighbours.TryGetValue(weight, out customers_list)) { customers_list = new List<int>(); neighbours.Add(weight, customers_list); } customers_list.Add(customer); } } double nearest_neighbour_average = 0; int neighbour_count = 20; int neighbour_counted = 0; foreach (KeyValuePair<double, List<int>> pair in neighbours) { foreach (int customer in pair.Value) { if (neighbour_counted < neighbour_count) { neighbour_counted++; nearest_neighbour_average = nearest_neighbour_average + pair.Key; } else { break; } } } if (max_distance > nearest_neighbour_average) { max_distance = nearest_neighbour_average; selected_customer = customer_to_check; } } return selected_customer; }
/// <summary> /// Executes a solver procedure. /// </summary> /// <param name="problem"></param> /// <returns></returns> internal override MaxTimeSolution Solve(MaxTimeProblem problem) { // create the solution. MaxTimeSolution solution = new MaxTimeSolution(problem.Size, true); // keep placing customer until none are left. List <int> customers = new List <int>(problem.Customers); double max = problem.Max.Value - (problem.Max.Value * _delta_percentage); // keep a list of cheapest insertions. IInsertionCosts costs = new BinaryHeapInsertionCosts(); double percentage = _threshold_percentage; while (customers.Count > 0) { // try and distribute the remaining customers if there are only a few left. if (customers.Count < problem.Size * percentage) { bool succes = true; while (succes && customers.Count > 0) { succes = false; CheapestInsertionResult best = new CheapestInsertionResult(); best.Increase = float.MaxValue; int best_idx = -1; for (int route_idx = 0; route_idx < solution.Count; route_idx++) { IRoute route = solution.Route(route_idx); CheapestInsertionResult result = CheapestInsertionHelper.CalculateBestPlacement(problem, route, customers); if (best.Increase > result.Increase) { best = result; best_idx = route_idx; } } IRoute best_route = solution.Route(best_idx); double route_time = problem.Time(best_route); if (route_time + best.Increase < max) { // insert the customer. best_route.InsertAfter(best.CustomerBefore, best.Customer); customers.Remove(best.Customer); this.Improve(problem, solution, max, best_idx); succes = true; } } } // select a customer using some heuristic. if (customers.Count > 0) { // select a customer using some heuristic. int customer = -1; if (_use_seed) { // use a seeding heuristic. customer = this.SelectSeed(problem, problem.MaxTimeCalculator, solution, customers); } else { // just select a random customer. customer = customers[Math.Random.StaticRandomGenerator.Get().Generate(customers.Count)]; } customers.Remove(customer); // start a route r. IRoute current_route = solution.Add(customer); solution[solution.Count - 1] = 0; while (customers.Count > 0) { //OsmSharp.IO.Output.OutputStreamHost.WriteLine("{0}/{1} placed!", // customers.Count, problem.Size); // calculate the best placement. CheapestInsertionResult result; if (_use_seed_cost) { // use the seed cost; the cost to the seed customer. result = CheapestInsertionHelper.CalculateBestPlacement(problem, current_route, customers, customer, _lambda); // calculate the 'real' increase. result.Increase = (problem.WeightMatrix[result.CustomerBefore][result.Customer] + problem.WeightMatrix[result.Customer][result.CustomerAfter]) - problem.WeightMatrix[result.CustomerBefore][result.CustomerAfter]; } else { // just use cheapest insertion. result = CheapestInsertionHelper.CalculateBestPlacement(problem, current_route, customers, costs); } // calculate the new weight. solution[solution.Count - 1] = problem.Time(solution.Route(solution.Count - 1)); double potential_weight = problem.MaxTimeCalculator.CalculateOneRouteIncrease(solution[solution.Count - 1], result.Increase); // cram as many customers into one route as possible. if (potential_weight < max) { // insert the customer, it is customers.Remove(result.Customer); current_route.InsertAfter(result.CustomerBefore, result.Customer); // free some memory in the costs list. costs.Remove(result.CustomerBefore, result.CustomerAfter); // update the cost of the route. solution[solution.Count - 1] = potential_weight; // improve if needed. if (((problem.Size - customers.Count) % _k) == 0) { // an improvement is descided. // apply the inter-route improvements. int count_before = solution.Route(solution.Count - 1).Count; solution[solution.Count - 1] = this.ImproveIntraRoute(problem, current_route, solution[solution.Count - 1]); if (!solution.IsValid()) { throw new Exception(); } int count_after = solution.Route(solution.Count - 1).Count; // also to the inter-improvements. current_route = this.Improve(problem, solution, max, solution.Count - 1); } } else {// ok we are done! this.Improve(problem, solution, max, solution.Count - 1); // break the route. break; } } //else //{// ok we are done! // solution[solution.Count - 1] = this.ImproveIntraRoute(problem, // current_route, solution[solution.Count - 1]); // if (!solution.IsValid()) // { // throw new Exception(); // } // int count_after = solution.Route(solution.Count - 1).Count; // this.Improve(problem, solution, max, solution.Count - 1); // // break the route. // break; //} } } // remove empty routes. for (int route_idx = solution.Count - 1; route_idx >= 0; route_idx--) { if (solution.Route(route_idx).IsEmpty) { solution.Remove(route_idx); } } return(solution); }
/// <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 = 50; 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> /// Apply some improvements between the given routes and returns the resulting weight. /// </summary> /// <param name="problem"></param> /// <param name="solution"></param> /// <param name="route1Idx"></param> /// <param name="route2Idx"></param> /// <param name="max"></param> /// <returns></returns> private bool ImproveInterRoute(MaxTimeProblem problem, MaxTimeSolution solution, int route1Idx, int route2Idx, double max) { // get the routes. IRoute route1 = solution.Route(route1Idx); IRoute route2 = solution.Route(route2Idx); int countBefore = route1.Count + route2.Count; // loop over all improvement operations. bool globalImprovement = false; foreach (IInterImprovement improvementOperation in _inter_improvements) { // try the current improvement operations. bool improvement = true; while (improvement) { // keep looping when there is improvement. improvement = false; double totalBefore = problem.Time(solution.Route(route1Idx)) + problem.Time(solution.Route(route2Idx)); if (improvementOperation.Improve(problem, solution, route1Idx, route2Idx, max)) { // there was an improvement. improvement = true; globalImprovement = true; // check if the route is valid. if (!route1.IsValid()) { throw new Exception(); } if (!route2.IsValid()) { throw new Exception(); } int countAfter = route1.Count + route2.Count; if (countBefore != countAfter) { throw new Exception(); } double totalAfter = problem.Time(solution.Route(route1Idx)) + problem.Time(solution.Route(route2Idx)); if (totalAfter >= totalBefore) { throw new Exception("this is not an improvement!"); } OsmSharp.Logging.Log.TraceEvent("OsmSharp.Routing.VRP.NoDepot.MaxTime.VNS.GuidedVNS", TraceEventType.Information, "Inter-improvement found {0}<->{1}: {2} ({3}->{4})", route1Idx, route2Idx, improvementOperation.Name, totalBefore, totalAfter); // recalculate weights. solution[route1Idx] = problem.Time(solution.Route(route1Idx)); solution[route2Idx] = problem.Time(solution.Route(route2Idx)); //break; } else if (!improvementOperation.IsSymmetric && improvementOperation.Improve(problem, solution, route2Idx, route1Idx, max)) { // also do the improvement the other way around when not symmetric. improvement = true; globalImprovement = true; // check if the route is valid. if (!route1.IsValid()) { throw new Exception(); } if (!route2.IsValid()) { throw new Exception(); } int countAfter = route1.Count + route2.Count; if (countBefore != countAfter) { throw new Exception(); } double totalAfter = problem.Time(solution.Route(route1Idx)) + problem.Time(solution.Route(route2Idx)); if (totalAfter >= totalBefore) { throw new Exception("this is not an improvement!"); } OsmSharp.Logging.Log.TraceEvent("OsmSharp.Routing.VRP.NoDepot.MaxTime.VNS.GuidedVNS", TraceEventType.Information, "Inter-improvement found {0}<->{1}: {2} ({3}->{4})", route2Idx, route1Idx, improvementOperation.Name, totalBefore, totalAfter); // recalculate weights. solution[route1Idx] = problem.Time(solution.Route(route1Idx)); solution[route2Idx] = problem.Time(solution.Route(route2Idx)); //break; } } } return globalImprovement; }
/// <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 customers. /// </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) { 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)); double route1_size = solution[route1_idx]; double route2_size = solution[route2_idx]; // this heuristic removes a customer1 from route1 and a customer2 from route2 and inserts the customers again // but swappes them; customer1 in route2 and customer2 in route1. int previous_customer1 = -1; //if (route1.IsRound) //{ // previous_customer1= route1.Last; // set the previous customer. //} foreach (int customer1 in route1) { // loop over all customers in route1. if (previous_customer1 >= 0) { // the previous customer is set. int next_customer1 = route1.GetNeigbours(customer1)[0]; if (next_customer1 < 0) { continue; } int previous_customer2 = -1; //if (route2.IsRound) //{ // previous_customer2 = route2.Last; // set the previous customer. //} foreach (int customer2 in route2) { // loop over all customers in route2. int next_customer2 = route2.GetNeigbours(customer2)[0]; if (previous_customer2 >= 0 && next_customer2 >= 0) { // the previous customer is set. float weight1 = (float)problem.WeightMatrix[previous_customer1][customer1] + (float)problem.WeightMatrix[customer1][next_customer1]; float weight2 = (float)problem.WeightMatrix[previous_customer2][customer2] + (float)problem.WeightMatrix[customer2][next_customer2]; float weight1_after = (float)problem.WeightMatrix[previous_customer1][customer2] + (float)problem.WeightMatrix[customer2][next_customer1]; float weight2_after = (float)problem.WeightMatrix[previous_customer2][customer1] + (float)problem.WeightMatrix[customer1][next_customer2]; double difference = (weight1_after + weight2_after) - (weight1 + weight2); if (difference < -0.01) { // the old weights are bigger! // check if the new routes are bigger than max. if (route1_size + (weight1_after - weight1) <= max && route2_size + (weight2_after - weight1) <= max) { // the exchange can happen, both routes stay within bound! // exchange customer. int count_before = route1.Count + route2.Count; //route1.Remove(customer1); //route2.Remove(customer2); if (previous_customer1 == next_customer2) { throw new Exception(); } //route1.InsertAfter(previous_customer1, customer2); //route2.InsertAfter(previous_customer2, customer1); route1.ReplaceEdgeFrom(previous_customer1, customer2); route1.ReplaceEdgeFrom(customer2, next_customer1); route2.ReplaceEdgeFrom(previous_customer2, customer1); route2.ReplaceEdgeFrom(customer1, next_customer2); int count_after = route1.Count + route2.Count; if (count_before != count_after) { 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); } } } previous_customer2 = customer2; // set the previous customer. } } previous_customer1 = customer1; // set the previous customer. } return(false); }
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(); }
/// <summary> /// Executes a solver procedure. /// </summary> /// <param name="problem"></param> /// <returns></returns> internal override MaxTimeSolution Solve(MaxTimeProblem problem) { MaxTimeCalculator calculator = new MaxTimeCalculator(problem); // get the seed customers. ICollection<int> seeds = _seed_selector.SelectSeeds( problem, _k); double[] weights = new double[seeds.Count]; // start the seed routes. List<int> selectable_customers = problem.Customers; MaxTimeSolution routes = new MaxTimeSolution( problem.Size, true); foreach (int seed in seeds) { routes.Add(seed); selectable_customers.Remove(seed); } if (!routes.IsValid()) { throw new Exception(); } // keep a list of cheapest insertions. IInsertionCosts costs = new BinaryHeapInsertionCosts(); // keep looping until all customers have been placed. while (selectable_customers.Count > 0) { // try and place into every route. CheapestInsertionResult best_result = new CheapestInsertionResult(); best_result.Increase = float.MaxValue; int best_route_idx = -1; CheapestInsertionResult best_result_above_max = new CheapestInsertionResult(); best_result_above_max.Increase = float.MaxValue; int best_route_above_max_idx = -1; for (int route_idx = 0; route_idx < routes.Count; route_idx++) { IRoute current_route = routes.Route(route_idx); // choose the next customer. CheapestInsertionResult result = CheapestInsertionHelper.CalculateBestPlacement(problem, current_route, selectable_customers, costs); if (result.Customer == result.CustomerAfter) { throw new Exception(); } // get the current weight double weight = weights[route_idx]; if (result.Increase < best_result.Increase) { if (weight + result.Increase + calculator.DeliveryTime < problem.Max.Value) { // route will still be inside bounds. best_result = result; best_route_idx = route_idx; } else { // route will become above max. if (result.Increase < best_result_above_max.Increase) { best_result_above_max = result; best_route_above_max_idx = route_idx; } } } } // do the placement if a placement is found without max violation. // else do the placement in the above max route. CheapestInsertionResult placement_result = new CheapestInsertionResult(); placement_result.Increase = double.MaxValue; int placement_result_idx = -1; if (best_route_idx >= 0) { // best placement found. placement_result = best_result; placement_result_idx = best_route_idx; } else { // best placement found but only above max. placement_result = best_result_above_max; placement_result_idx = best_route_above_max_idx; } // do the actual placement. weights[placement_result_idx] = calculator.CalculateOneRouteIncrease( weights[placement_result_idx], placement_result.Increase); selectable_customers.Remove(placement_result.Customer); //routes.Route(placement_result_idx).InsertAfterAndRemove( // placement_result.CustomerBefore, placement_result.Customer, placement_result.CustomerAfter); routes.Route(placement_result_idx).InsertAfter( placement_result.CustomerBefore, placement_result.Customer); if (!routes.IsValid()) { throw new Exception(); } } return routes; }
/// <summary> /// Executes a solver procedure. /// </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); // keep placing customer until none are left. List<int> customers = new List<int>(problem.Customers); while (customers.Count > 0) { // select a customer using some heuristic. int customer_idx = OsmSharp.Tools.Math.Random.StaticRandomGenerator.Get().Generate(customers.Count); int customer = customers[customer_idx]; customers.Remove(customer); // start a route r. double current_route_weight = 0; IRoute current_route = solution.Add(customer); //Console.WriteLine("Starting new route with {0}", customer); while (customers.Count > 0) { // calculate the best placement. CheapestInsertionResult result = CheapestInsertionHelper.CalculateBestPlacement(problem, current_route, customers); // calculate the new weight. double potential_weight = calculator.CalculateOneRouteIncrease(current_route_weight, result.Increase); // cram as many customers into one route as possible. if (potential_weight < problem.Max.Value) { // insert the customer, it is customers.Remove(result.Customer); //current_route.InsertAfterAndRemove(result.CustomerBefore, result.Customer, result.CustomerAfter); current_route.InsertAfter(result.CustomerBefore, result.Customer); current_route_weight = potential_weight; //// improve if needed. ////if (improvement_probalitity > OsmSharp.Tools.Math.Random.StaticRandomGenerator.Get().Generate(1)) //if (((problem.Size - customers.Count) % _k) == 0) //{ // an improvement is descided. // current_route_weight = this.ImproveIntraRoute(problem, // current_route, current_route_weight); //} } else {// ok we are done! //// apply the intra-route heuristics. //for (int route_idx = 0; route_idx < solution.Count - 1; route_idx++) //{ // apply the intra-route heurstic between the new and all existing routes. // this.ImproveInterRoute(problem, solution.Route(route_idx), current_route); //} //// apply the inter-route heuristics. //for (int route_idx = 0; route_idx < solution.Count; route_idx++) //{ // apply heurstic for each route. // IRoute route = solution.Route(route_idx); // this.ImproveIntraRoute(problem, current_route, current_route_weight); //} // break the route. break; } } } return solution; }
/// <summary> /// Apply some improvements between the given routes and returns the resulting weight. /// </summary> /// <param name="problem"></param> /// <param name="solution"></param> /// <param name="route1Idx"></param> /// <param name="route2Idx"></param> /// <param name="max"></param> /// <returns></returns> private bool ImproveInterRoute(MaxTimeProblem problem, MaxTimeSolution solution, int route1Idx, int route2Idx, double max) { // get the routes. IRoute route1 = solution.Route(route1Idx); IRoute route2 = solution.Route(route2Idx); int countBefore = route1.Count + route2.Count; // loop over all improvement operations. bool globalImprovement = false; foreach (IInterImprovement improvementOperation in _inter_improvements) { // try the current improvement operations. bool improvement = true; while (improvement) { // keep looping when there is improvement. improvement = false; double totalBefore = problem.Time(solution.Route(route1Idx)) + problem.Time(solution.Route(route2Idx)); if (improvementOperation.Improve(problem, solution, route1Idx, route2Idx, max)) { // there was an improvement. improvement = true; globalImprovement = true; // check if the route is valid. if (!route1.IsValid()) { throw new Exception(); } if (!route2.IsValid()) { throw new Exception(); } int countAfter = route1.Count + route2.Count; if (countBefore != countAfter) { throw new Exception(); } double totalAfter = problem.Time(solution.Route(route1Idx)) + problem.Time(solution.Route(route2Idx)); if (totalAfter >= totalBefore) { throw new Exception("this is not an improvement!"); } OsmSharp.Logging.Log.TraceEvent("OsmSharp.Routing.VRP.NoDepot.MaxTime.VNS.GuidedVNS", TraceEventType.Information, "Inter-improvement found {0}<->{1}: {2} ({3}->{4})", route1Idx, route2Idx, improvementOperation.Name, totalBefore, totalAfter); // recalculate weights. solution[route1Idx] = problem.Time(solution.Route(route1Idx)); solution[route2Idx] = problem.Time(solution.Route(route2Idx)); //break; } else if (!improvementOperation.IsSymmetric && improvementOperation.Improve(problem, solution, route2Idx, route1Idx, max)) { // also do the improvement the other way around when not symmetric. improvement = true; globalImprovement = true; // check if the route is valid. if (!route1.IsValid()) { throw new Exception(); } if (!route2.IsValid()) { throw new Exception(); } int countAfter = route1.Count + route2.Count; if (countBefore != countAfter) { throw new Exception(); } double totalAfter = problem.Time(solution.Route(route1Idx)) + problem.Time(solution.Route(route2Idx)); if (totalAfter >= totalBefore) { throw new Exception("this is not an improvement!"); } OsmSharp.Logging.Log.TraceEvent("OsmSharp.Routing.VRP.NoDepot.MaxTime.VNS.GuidedVNS", TraceEventType.Information, "Inter-improvement found {0}<->{1}: {2} ({3}->{4})", route2Idx, route1Idx, improvementOperation.Name, totalBefore, totalAfter); // recalculate weights. solution[route1Idx] = problem.Time(solution.Route(route1Idx)); solution[route2Idx] = problem.Time(solution.Route(route2Idx)); //break; } } } return(globalImprovement); }
/// <summary> /// Applies inter-improvements by exchanging customers. /// </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) { 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)); double route1_size = solution[route1_idx]; double route2_size = solution[route2_idx]; // this heuristic removes a customer1 from route1 and a customer2 from route2 and inserts the customers again // but swappes them; customer1 in route2 and customer2 in route1. int previous_customer1 = -1; //if (route1.IsRound) //{ // previous_customer1= route1.Last; // set the previous customer. //} foreach (int customer1 in route1) { // loop over all customers in route1. if (previous_customer1 >= 0) { // the previous customer is set. int next_customer1 = route1.GetNeigbours(customer1)[0]; if (next_customer1 < 0) { continue; } int previous_customer2 = -1; //if (route2.IsRound) //{ // previous_customer2 = route2.Last; // set the previous customer. //} foreach (int customer2 in route2) { // loop over all customers in route2. int next_customer2 = route2.GetNeigbours(customer2)[0]; if (previous_customer2 >= 0 && next_customer2 >= 0) { // the previous customer is set. float weight1 = (float)problem.WeightMatrix[previous_customer1][customer1] + (float)problem.WeightMatrix[customer1][next_customer1]; float weight2 = (float)problem.WeightMatrix[previous_customer2][customer2] + (float)problem.WeightMatrix[customer2][next_customer2]; float weight1_after = (float)problem.WeightMatrix[previous_customer1][customer2] + (float)problem.WeightMatrix[customer2][next_customer1]; float weight2_after = (float)problem.WeightMatrix[previous_customer2][customer1] + (float)problem.WeightMatrix[customer1][next_customer2]; double difference = (weight1_after + weight2_after) - (weight1 + weight2); if (difference < -0.01) { // the old weights are bigger! // check if the new routes are bigger than max. if (route1_size + (weight1_after - weight1) <= max && route2_size + (weight2_after - weight1) <= max) { // the exchange can happen, both routes stay within bound! // exchange customer. int count_before = route1.Count + route2.Count; //route1.Remove(customer1); //route2.Remove(customer2); if (previous_customer1 == next_customer2) { throw new Exception(); } //route1.InsertAfter(previous_customer1, customer2); //route2.InsertAfter(previous_customer2, customer1); route1.ReplaceEdgeFrom(previous_customer1, customer2); route1.ReplaceEdgeFrom(customer2, next_customer1); route2.ReplaceEdgeFrom(previous_customer2, customer1); route2.ReplaceEdgeFrom(customer1, next_customer2); int count_after = route1.Count + route2.Count; if (count_before != count_after) { 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; } } } previous_customer2 = customer2; // set the previous customer. } } previous_customer1 = customer1; // set the previous customer. } return false; }
/// <summary> /// Executes a solver procedure. /// </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); // keep placing customer until none are left. List <int> customers = new List <int>(problem.Customers); while (customers.Count > 0) { // select a customer using some heuristic. int customer_idx = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(customers.Count); int customer = customers[customer_idx]; customers.Remove(customer); // start a route r. double current_route_weight = 0; IRoute current_route = solution.Add(customer); //Console.WriteLine("Starting new route with {0}", customer); while (customers.Count > 0) { // calculate the best placement. CheapestInsertionResult result = CheapestInsertionHelper.CalculateBestPlacement(problem, current_route, customers); // calculate the new weight. double potential_weight = calculator.CalculateOneRouteIncrease(current_route_weight, result.Increase); // cram as many customers into one route as possible. if (potential_weight < problem.Max.Value) { // insert the customer, it is customers.Remove(result.Customer); //current_route.InsertAfterAndRemove(result.CustomerBefore, result.Customer, result.CustomerAfter); current_route.InsertAfter(result.CustomerBefore, result.Customer); current_route_weight = potential_weight; //// improve if needed. ////if (improvement_probalitity > OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(1)) //if (((problem.Size - customers.Count) % _k) == 0) //{ // an improvement is descided. // current_route_weight = this.ImproveIntraRoute(problem, // current_route, current_route_weight); //} } else {// ok we are done! //// apply the intra-route heuristics. //for (int route_idx = 0; route_idx < solution.Count - 1; route_idx++) //{ // apply the intra-route heurstic between the new and all existing routes. // this.ImproveInterRoute(problem, solution.Route(route_idx), current_route); //} //// apply the inter-route heuristics. //for (int route_idx = 0; route_idx < solution.Count; route_idx++) //{ // apply heurstic for each route. // IRoute route = solution.Route(route_idx); // this.ImproveIntraRoute(problem, current_route, current_route_weight); //} // break the route. break; } } } return(solution); }
/// <summary> /// Calculates the total time. /// </summary> /// <param name="solution"></param> /// <returns></returns> public double Time(MaxTimeSolution solution) { double time = 0; for (int idx = 0; idx < solution.Count; idx++) { time = time + this.Time(solution.Route(idx)); } return time; }