/// <summary> /// Calculates the weight of the route given the weights. /// </summary> /// <param name="route"></param> /// <param name="is_round"></param> /// <param name="weights"></param> public static double CalculateWeight(this int[] route, bool is_round, IProblemWeights weights) { double weight = 0; int previous = -1; foreach (int customer in route) { if (previous >= 0) { // calculate the weight. weight = weight + weights.WeightMatrix[previous][customer]; } // set the previous and current. previous = customer; } // if the route is round add the last-first weight. if (is_round) { weight = weight + weights.WeightMatrix[previous][route[0]]; } return(weight); }
private void Set(IProblemWeights problem, int customer, bool value) { if (_dont_look) { _dont_look_bits[customer] = value; } }
/// <summary> /// Returns the customer that least increases the length of the given route. /// </summary> /// <param name="problem"></param> /// <param name="route"></param> /// <returns></returns> public static TwoOptResult CalculateBestPlacement( IProblemWeights problem, IRoute route) { //int previous1 = -1; //foreach (int customer1 in route) //{ // if (previous1 >= 0) // { // int previous2 = -1; // foreach (int customer2 in route) // { // if (previous1 != previous2) // { // // test the two opt move. // } // previous2 = customer2; // } // } // previous1 = customer1; //} throw new NotImplementedException(); }
public Problem(ICollection<int> depots,ICollection<int> customers, IProblemWeights weights) { this.Depots = depots; this.Customers = customers; _weights = weights; }
/// <summary> /// Improves an existing solution. /// </summary> /// <param name="problem"></param> /// <param name="route"></param> /// <param name="difference"></param> /// <returns></returns> public bool Improve(IProblemWeights problem, IRoute route, out double difference) { if (!route.IsValid()) { throw new Exception(); } _dont_look_bits = new bool[problem.Size]; double[][] weights = problem.WeightMatrix; // loop over all customers. bool any_improvement = false; bool improvement = true; difference = -1; while (improvement) { improvement = false; foreach (int v_1 in route) { if (!this.Check(problem, v_1)) { if (this.Try3OptMoves(problem, weights, route, v_1)) { any_improvement = true; improvement = true; break; } this.Set(problem, v_1, true); } } } return(any_improvement); }
public Problem(ICollection <int> depots, ICollection <int> customers, IProblemWeights weights) { this.Depots = depots; this.Customers = customers; _weights = weights; }
/// <summary> /// Considers one customer for relocation. /// </summary> /// <param name="problem"></param> /// <param name="route"></param> /// <param name="previous"></param> /// <param name="current"></param> /// <param name="next"></param> /// <param name="route_weight"></param> /// <param name="max"></param> /// <returns></returns> private bool ConsiderCustomer(IProblemWeights problem, IRoute route, int previous, int current, int next, double route_weight, double max) { // calculate the removal gain of the customer. double removal_gain = problem.WeightMatrix[previous][current] + problem.WeightMatrix[current][next] - problem.WeightMatrix[previous][next]; if (removal_gain > 0.0001) { // try and place the customer in the next route. CheapestInsertionResult result = CheapestInsertionHelper.CalculateBestPlacement(problem, route, current); if (result.Increase < removal_gain - 0.001 && route_weight + result.Increase < max) { // there is a gain in relocating this customer. int count_before = route.Count; // string route_string = route.ToString(); // and the route is still within bounds! route.ReplaceEdgeFrom(result.CustomerBefore, result.Customer); route.ReplaceEdgeFrom(result.Customer, result.CustomerAfter); int count_after = route.Count; if (count_before + 1 != count_after) { throw new Exception(); } return(true); } } return(false); }
/// <summary> /// Cuts out a part of the route and returns the customers contained. /// </summary> /// <param name="weights"></param> /// <param name="start"></param> /// <param name="weight"></param> /// <param name="length"></param> /// <returns></returns> public CutResult CutAndRemove(IProblemWeights weights, double weight, int start, int length) { double weight_difference = 0; int[] next_array = _next_array.Clone() as int[]; List <int> cut_part = new List <int>(); int position = 0; int current_customer = this.First; // keep moving next until the start. while (position < start - 1) { position++; // increase the position. current_customer = next_array[current_customer]; } // cut the actual part. int start_customer = current_customer; int previous_customer = current_customer; while (position < start + length - 1) { // move next. position++; // increase the position. current_customer = next_array[current_customer]; // set the current customer. cut_part.Add(current_customer); // add the weigth difference. weight_difference = weight_difference - weights.WeightMatrix[previous_customer][current_customer]; previous_customer = current_customer; } // move to the next customer. current_customer = next_array[current_customer]; weight_difference = weight_difference - weights.WeightMatrix[previous_customer][current_customer]; // set the next customer. next_array[start_customer] = current_customer; weight_difference = weight_difference + weights.WeightMatrix[start_customer][current_customer]; // create cut result. CutResult result = new CutResult(); result.Weight = weight + weight_difference; result.Route = new DynamicAsymmetricRoute(this.First, next_array, true); result.CutPart = cut_part; if (!result.Route.IsValid()) { throw new Exception(); } return(result); }
private bool Check(IProblemWeights problem, int customer) { if (_dont_look) { return(_dont_look_bits[customer]); } return(false); }
/// <summary> /// Returns the customer that least increases the length of the given route. /// </summary> /// <param name="problem"></param> /// <param name="route"></param> /// <param name="customers"></param> /// <returns></returns> public static CheapestInsertionResult CalculateBestPlacement( IProblemWeights problem, IRoute route, ICollection<int> customers) { IInsertionCosts costs = new BinaryHeapInsertionCosts(); return CheapestInsertionHelper.CalculateBestPlacement(problem, route, customers, costs); }
/// <summary> /// Returns the customer that least increases the length of the given route. /// </summary> /// <param name="problem"></param> /// <param name="route"></param> /// <param name="customers"></param> /// <returns></returns> public static CheapestInsertionResult CalculateBestPlacement( IProblemWeights problem, IRoute route, ICollection <int> customers) { IInsertionCosts costs = new BinaryHeapInsertionCosts(); return(CheapestInsertionHelper.CalculateBestPlacement(problem, route, customers, costs)); }
/// <summary> /// Tries all 3Opt Moves for the neighbourhood of v_1 containing v_3. /// </summary> /// <param name="problem"></param> /// <param name="weights"></param> /// <param name="route"></param> /// <param name="v1"></param> /// <param name="v_2"></param> /// <param name="weight_1_2"></param> /// <param name="v_3"></param> /// <returns></returns> public bool Try3OptMoves(IProblemWeights problem, double[][] weights, IRoute route, int v1, int v_2, double weight_1_2, int v_3) { // get v_4. int v_4 = route.GetNeigbours(v_3)[0]; double weight_1_2_plus_3_4 = weight_1_2 + weights[v_3][v_4]; double weight_1_4 = weights[v1][v_4]; double[] weights_3 = weights[v_3]; return(this.Try3OptMoves(problem, weights, route, v1, v_2, v_3, weights_3, v_4, weight_1_2_plus_3_4, weight_1_4)); }
/// <summary> /// Creates a new max time problem. /// </summary> /// <param name="weights"></param> /// <param name="max"></param> /// <param name="delivery_time"></param> /// <param name="cost_per_second"></param> /// <param name="cost_per_vehicle"></param> public MaxTimeProblem(IProblemWeights weights, Second max, Second delivery_time, double cost_per_second, double cost_per_vehicle) { this.Max = max; this.DeliveryTime = delivery_time; _cost_per_second = cost_per_second; _cost_per_vehicle = cost_per_vehicle; _weights = weights; _calculator = new MaxTimeCalculator(this); _customer_positions = new List <GeoCoordinate>(); }
/// <summary> /// Creates a new max time problem. /// </summary> /// <param name="weights"></param> /// <param name="max"></param> /// <param name="delivery_time"></param> /// <param name="cost_per_second"></param> /// <param name="cost_per_vehicle"></param> public MaxTimeProblem(IProblemWeights weights, Second max, Second delivery_time, double cost_per_second, double cost_per_vehicle) { this.Max = max; this.DeliveryTime = delivery_time; _cost_per_second = cost_per_second; _cost_per_vehicle = cost_per_vehicle; _weights = weights; _calculator = new MaxTimeCalculator(this); _customer_positions = new List<GeoCoordinate>(); }
/// <summary> /// Tries all 3Opt Moves for the neighbourhood of v_1. /// </summary> /// <param name="problem"></param> /// <param name="weights"></param> /// <param name="route"></param> /// <param name="v1"></param> /// <returns></returns> public bool Try3OptMoves(IProblemWeights problem, double[][] weights, IRoute route, int v1) { // get v_2. int v_2 = route.GetNeigbours(v1)[0]; if (v_2 < 0) { return(false); } IEnumerable <int> between_v_2_v_1 = route.Between(v_2, v1); double weight_1_2 = weights[v1][v_2]; int v_3 = -1; NearestNeighbours10 neighbours = null; if (_nearest_neighbours) { neighbours = problem.Get10NearestNeighbours(v1); } foreach (int v_4 in between_v_2_v_1) { if (v_3 >= 0 && v_3 != v1) { //if (!_nearest_neighbours || // neighbours.Max <= weight_1_4) if (!_nearest_neighbours || neighbours.Contains(v_4)) { double weight_1_4 = weights[v1][v_4]; double weight_1_2_plus_3_4 = weight_1_2 + weights[v_3][v_4]; double[] weights_3 = weights[v_3]; if (this.Try3OptMoves(problem, weights, route, v1, v_2, v_3, weights_3, v_4, weight_1_2_plus_3_4, weight_1_4)) { return(true); } } } v_3 = v_4; } return(false); }
/// <summary> /// Re-inserts a customer in the route. /// </summary> /// <param name="weights"></param> /// <param name="route"></param> /// <param name="customer"></param> /// <param name="difference"></param> /// <returns></returns> public static bool InsertOne(IProblemWeights weights, IRoute route, int customer, out double difference) { // calculate placement. CheapestInsertionResult result = CheapestInsertionHelper.CalculateBestPlacement(weights, route, customer); // place the customer. if (result.CustomerAfter >= 0 && result.CustomerBefore >= 0) { //route.InsertAfterAndRemove(result.CustomerBefore, result.Customer, result.CustomerAfter); route.InsertAfter(result.CustomerBefore, result.Customer); difference = -(weights.WeightMatrix[result.CustomerBefore][result.CustomerAfter]) + (weights.WeightMatrix[result.CustomerBefore][result.Customer]) + (weights.WeightMatrix[result.Customer][result.CustomerAfter]); return(true); } difference = 0; return(false); }
/// <summary> /// Apply some improvements within one route. /// </summary> /// <param name="problem"></param> /// <param name="route"></param> /// <param name="currentWeight"></param> private double ImproveIntraRoute(IProblemWeights problem, IRoute route, double currentWeight) { bool improvement = true; double newWeight = currentWeight; while (improvement) { // keep trying while there are still improvements. improvement = false; // loop over all improvement operations. foreach (IImprovement improvementOperation in _intra_improvements) { // try the current improvement operations. double difference; if (improvementOperation.Improve(problem, route, out difference)) { // there was an improvement. OsmSharp.Logging.Log.TraceEvent("OsmSharp.Routing.VRP.NoDepot.MaxTime.VNS.GuidedVNS", TraceEventType.Information, "Intra-improvement found {0} {1}->{2}", improvementOperation.Name, newWeight, newWeight + difference); // check if the route is valid. if (!route.IsValid()) { throw new Exception(); } // update the weight. newWeight = newWeight + difference; improvement = true; break; } } } return(newWeight); }
/// <summary> /// Tries to improve the existing route using CI and return true if succesful. /// </summary> /// <param name="problem"></param> /// <param name="route"></param> /// <param name="difference"></param> /// <returns></returns> public bool Improve(IProblemWeights problem, IRoute route, out double difference) { bool improvement = false; difference = 0; if (route.Count > 3) { // loop over all customers and try cheapest insertion. for (int customer = 0; customer < problem.Size; customer++) { //string route_string = route.ToString(); //IRoute previous = route.Clone() as IRoute; if (route.Contains(customer)) { // remove customer and keep position. int next = route.GetNeigbours(customer)[0]; route.Remove(customer); // insert again. ArbitraryInsertionSolver.InsertOne(problem, route, customer, out difference); if (!route.IsValid()) { throw new Exception(); } if (route.GetNeigbours(customer)[0] != next && difference < 0) { // another customer was found as the best, improvement is succesful. improvement = true; break; } } } } return(improvement); }
/// <summary> /// Calculates the weight of the route given the weights. /// </summary> /// <param name="route"></param> /// <param name="is_round"></param> /// <param name="weights"></param> public static double CalculateWeight(this int[] route, bool is_round, IProblemWeights weights) { double weight = 0; int previous = -1; foreach (int customer in route) { if (previous >= 0) { // calculate the weight. weight = weight + weights.WeightMatrix[previous][customer]; } // set the previous and current. previous = customer; } // if the route is round add the last-first weight. if (is_round) { weight = weight + weights.WeightMatrix[previous][route[0]]; } return weight; }
/// <summary> /// Searches for the best place to insert the given two customers abstracting the distance between them. /// </summary> /// <param name="problem"></param> /// <param name="route"></param> /// <param name="from"></param> /// <param name="to"></param> /// <returns></returns> public static CheapestInsertionResult CalculateBestPlacement( IProblemWeights problem, IRoute route, int from, int to) { // initialize the best placement result. CheapestInsertionResult result = new CheapestInsertionResult(); result.Customer = -1; // this property is useless here! result.CustomerAfter = -1; result.CustomerBefore = -1; result.Increase = double.MaxValue; if (!route.IsEmpty) { double new_weight = double.MaxValue; double old_weight = 0; int previous = -1; int first = -1; foreach (int current in route) { if (previous >= 0) { // only if the previous is known. // calculate the new weights. new_weight = problem.Weight(previous, from) + (problem.Weight(to, current)); // calculate the old weights. old_weight = problem.Weight(previous, current); // calculate the difference. double difference = new_weight - old_weight; if (result.Increase > difference) { result.CustomerAfter = current; result.CustomerBefore = previous; result.Increase = difference; } } else { // store the first city for later. first = current; } // go to the next loop. previous = current; } // test last-to-first if the route is a round. if (route.IsRound) { // calculate the new weights. new_weight = problem.Weight(previous, from) + (problem.Weight(to, first)); // calculate the old weights. old_weight = problem.Weight(previous, first); // calculate the difference. double difference = new_weight - old_weight; if (result.Increase > difference) { result.CustomerBefore = previous; result.CustomerAfter = first; result.Increase = difference; } } } else { // route needs to be initialized. throw new ArgumentException("Route needs to be initialized with at least one customer!"); } // return result. return result; }
/// <summary> /// Applies a local search strategy. /// </summary> /// <param name="problem"></param> /// <param name="route"></param> /// <returns></returns> public static void CalculateRePlaceOptHelper( IProblemWeights problem, IRoute route) { //bool improvement = true; //while (improvement) //{ //// reset improvement flag. //improvement = false; // try re-placement of one customer. int before = -1; int after = -1; int found_customer = -1; // loop over all customers and try to place it better. HashSet<int> customers_to_place = new HashSet<int>(route); foreach (int customer_to_place in customers_to_place) { // find the best place. double current_cost = -1; double other_cost = double.MaxValue; int previous_before = -1; int previous = -1; foreach (int customer in route) { if (previous >= 0 && previous_before >= 0) { if (previous == customer_to_place) { current_cost = problem.Weight(previous_before, previous) + problem.Weight(previous, customer); } else if (previous_before != customer_to_place && customer != customer_to_place) { // calculate the cost. double cost = problem.Weight(previous_before, customer_to_place) + problem.Weight(customer_to_place, previous); if (cost < other_cost) { other_cost = cost; before = previous_before; after = previous; found_customer = customer; } } } previous_before = previous; previous = customer; } // determine if the cost is better. if (current_cost > other_cost) { // the current cost is better. route.Remove(found_customer); //route.InsertAfterAndRemove(before, found_customer, after); route.InsertAfter(before, found_customer); break; } else { // current cost is not better. before = -1; after = -1; found_customer = -1; } } //if (found_customer >= 0) //{ // a found customer. // improvement = true; //} //} }
/// <summary> /// Selects seed customers. /// </summary> /// <param name="weights"></param> /// <param name="k"></param> /// <returns></returns> public ICollection<int> SelectSeeds(IProblemWeights weights, int k) { List<int> seeds = new List<int>(); // randomly select k seeds. while (seeds.Count < k) { int new_seed = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(weights.Size); while (seeds.Contains(new_seed)) { new_seed = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(weights.Size); } seeds.Add(new_seed); } // find shortest distance. double minimal = double.MaxValue; int selected_idx = -1; for (int x = 0; x < k; x++) { for (int y = 0; y < x; y++) { double weight = weights.Weight(seeds[x], seeds[y]); if (weight < minimal) { selected_idx = x; minimal = weight; } } } // select any new seed and see if replacing the selected would yield better results. int max_tries = 2000; int tries = 0; while (tries < max_tries) { int seed = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(weights.Size); while (seeds.Contains(seed)) { seed = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(weights.Size); } // check the new minimal. double new_minimal = double.MaxValue; for (int x = 0; x < k; x++) { if (x != selected_idx) { double weight = weights.Weight(seeds[x], seed); if (weight < new_minimal) { new_minimal = weight; } } } // see if the new one has a higher minimal. if (new_minimal > minimal) { // ok there is an improvement! OsmSharp.Logging.Log.TraceEvent("SimpleSeeds", System.Diagnostics.TraceEventType.Information, "Seed new minimal: {0}->{1}", minimal, new_minimal); tries = 0; minimal = new_minimal; seeds[selected_idx] = seed; } tries++; // increase the number of tries. } OsmSharp.Logging.Log.TraceEvent("SimpleSeeds", System.Diagnostics.TraceEventType.Information, "Seed distance: {0}", minimal); return seeds; }
/// <summary> /// Apply some improvements within one route. /// </summary> /// <param name="problem"></param> /// <param name="route"></param> /// <param name="current_weight"></param> private double ImproveIntraRoute(IProblemWeights problem, IRoute route, double current_weight) { bool improvement = true; double new_weight = current_weight; while (improvement) { // keep trying while there are still improvements. improvement = false; // loop over all improvement operations. foreach (IImprovement improvement_operation in _intra_improvements) { // try the current improvement operations. double difference; if (improvement_operation.Improve(problem, route, out difference)) { // there was an improvement. OsmSharp.Tools.Output.OutputStreamHost.WriteLine("Intra-improvement found {0} {1}->{2}", improvement_operation.Name, new_weight, new_weight + difference); // check if the route is valid. if (!route.IsValid()) { throw new Exception(); } // update the weight. new_weight = new_weight + difference; improvement = true; break; } } } return new_weight; }
/// <summary> /// Returns the customer that least increases the length of the given route. /// </summary> /// <param name="problem"></param> /// <param name="route"></param> /// <param name="customers"></param> /// <param name="costs"></param> /// <returns></returns> public static CheapestInsertionResult CalculateBestPlacement( IProblemWeights problem, IRoute route, ICollection<int> customers, IInsertionCosts costs) { // initialize the best placement result. CheapestInsertionResult best = new CheapestInsertionResult(); best.Increase = float.MaxValue; // loop over all customers in the route. if (route.Count > 0) { // there have to be at least two customers. IEnumerator<int> route_enumerator = route.GetEnumerator(); if (!route_enumerator.MoveNext()) { // this route is empty throw new ArgumentException("Route needs to be initialized with at least two customers!"); } int customer_before = route_enumerator.Current; int customer_after = -1; while (route_enumerator.MoveNext()) { // keep moving! customer_after = route_enumerator.Current; InsertionCost cost = costs.PopCheapest(customer_before, customer_after); bool found = false; while(!found) { // test if the costs are empty. if (cost == null) { // re-initialize the costs with all customers under consideration. foreach (int customer in customers) { costs.Add(customer_before, customer_after, customer, (float)problem.WeightMatrix[customer_before][customer] + (float)problem.WeightMatrix[customer][customer_after] - (float)problem.WeightMatrix[customer_before][customer_after]); } // pop the cheapest again! cost = costs.PopCheapest(customer_before, customer_after); } else if (customers.Contains(cost.Customer)) { // the customer is found! found = true; } else { // pop the cheapest again! cost = costs.PopCheapest(customer_before, customer_after); } } if (cost.Cost < best.Increase) { // the costs is better than the current cost! best = new CheapestInsertionResult() { Customer = cost.Customer, CustomerAfter = customer_after, CustomerBefore = customer_before, Increase = cost.Cost }; if (best.Increase == 0) { // immidiately return if smaller than epsilon. return best; } } // set the after to the before. customer_before = route_enumerator.Current; } // if the round is a round try first-last. if (route.IsRound) { // the route is a round! customer_after = route.First; InsertionCost cost = costs.PopCheapest(customer_before, customer_after); bool found = false; while (!found) { // test if the costs are empty. if (cost == null) { // re-initialize the costs with all customers under consideration. foreach (int customer in customers) { costs.Add(customer_before, customer_after, customer, (float)problem.WeightMatrix[customer_before][customer] + (float)problem.WeightMatrix[customer][customer_after] - (float)problem.WeightMatrix[customer_before][customer_after]); } // pop the cheapest again! cost = costs.PopCheapest(customer_before, customer_after); } else if (customers.Contains(cost.Customer)) { // the customer is found! found = true; } else { // pop the cheapest again! cost = costs.PopCheapest(customer_before, customer_after); } } if (cost.Cost < best.Increase) { // the costs is better than the current cost! best = new CheapestInsertionResult() { Customer = cost.Customer, CustomerAfter = customer_after, CustomerBefore = customer_before, Increase = cost.Cost }; if (best.Increase == 0) { // immidiately return if smaller than epsilon. return best; } } } } else { // route needs to be initialized. throw new ArgumentException("Route needs to be initialized with at least two customers!"); } // return result. return best; }
/// <summary> /// Tries to improve the existing route using CI and return true if succesful. /// </summary> /// <param name="problem"></param> /// <param name="route"></param> /// <param name="difference"></param> /// <returns></returns> public bool Improve(IProblemWeights problem, IRoute route, out double difference) { bool improvement = false; difference = 0; if (route.Count > 3) { // loop over all customers and try cheapest insertion. for (int customer = 0; customer < problem.Size; customer++) { //string route_string = route.ToString(); //IRoute previous = route.Clone() as IRoute; if (route.Contains(customer)) { // remove customer and keep position. int next = route.GetNeigbours(customer)[0]; route.Remove(customer); // insert again. ArbitraryInsertionSolver.InsertOne(problem, route, customer, out difference); if (!route.IsValid()) { throw new Exception(); } if (route.GetNeigbours(customer)[0] != next && difference < 0) { // another customer was found as the best, improvement is succesful. improvement = true; break; } } } } return improvement; }
/// <summary> /// Re-inserts a customer in the route. /// </summary> /// <param name="weights"></param> /// <param name="route"></param> /// <param name="customer"></param> /// <param name="difference"></param> /// <returns></returns> public static bool InsertOne(IProblemWeights weights, IRoute route, int customer, out double difference) { // calculate placement. CheapestInsertionResult result = CheapestInsertionHelper.CalculateBestPlacement(weights, route, customer); // place the customer. if (result.CustomerAfter >= 0 && result.CustomerBefore >= 0) { //route.InsertAfterAndRemove(result.CustomerBefore, result.Customer, result.CustomerAfter); route.InsertAfter(result.CustomerBefore, result.Customer); difference = -(weights.WeightMatrix[result.CustomerBefore][result.CustomerAfter]) + (weights.WeightMatrix[result.CustomerBefore][result.Customer]) + (weights.WeightMatrix[result.Customer][result.CustomerAfter]); return true; } difference = 0; return false; }
/// <summary> /// Improves an existing solution. /// </summary> /// <param name="problem"></param> /// <param name="route"></param> /// <param name="difference"></param> /// <returns></returns> public bool Improve(IProblemWeights problem, IRoute route, out double difference) { if (!route.IsValid()) { throw new Exception(); } _dont_look_bits = new bool[problem.Size]; double[][] weights = problem.WeightMatrix; // loop over all customers. bool any_improvement = false; bool improvement = true; difference = -1; while (improvement) { improvement = false; foreach (int v_1 in route) { if (!this.Check(problem, v_1)) { if (this.Try3OptMoves(problem, weights, route, v_1)) { any_improvement = true; improvement = true; break; } this.Set(problem, v_1, true); } } } return any_improvement; }
/// <summary> /// Tries all 3Opt Moves for the neighbourhood of v_1 containing v_3. /// </summary> /// <param name="problem"></param> /// <param name="weights"></param> /// <param name="route"></param> /// <param name="v1"></param> /// <param name="v_2"></param> /// <param name="v_3"></param> /// <param name="weights_3"></param> /// <param name="v_4"></param> /// <param name="weight_1_2_plus_3_4"></param> /// <param name="weight_1_4"></param> /// <returns></returns> public bool Try3OptMoves(IProblemWeights problem, double[][] weights, IRoute route, int v1, int v_2, int v_3, double[] weights_3, int v_4, double weight_1_2_plus_3_4, double weight_1_4) { //IEnumerable<int> between_v_4_v_1 = route.Between(v_4, v_1); //foreach (int v_5 in between_v_4_v_1) //{ // if (v_5 != v_1) // { // if (this.Try3OptMove(problem, weights, route, v_1, v_2, v_3, weights_3, v_4, weight_1_2_plus_3_4, weight_1_4, v_5)) // { // return true; // } // } //} //return false; IEnumerator <int> between_v_4_v_1_enumerator = route.Between(v_4, v1).GetEnumerator(); if (between_v_4_v_1_enumerator.MoveNext()) { int v_5 = between_v_4_v_1_enumerator.Current; if (v_5 != v1) { while (between_v_4_v_1_enumerator.MoveNext()) { int v_6 = between_v_4_v_1_enumerator.Current; //Console.WriteLine(v_6); //if (this.Try3OptMove(problem, weights, // route, v_1, v_2, v_3, weights_3, v_4, // weight_1_2_plus_3_4, weight_1_4, v_5, v_6)) //{ // calculate the total weight of the 'new' arcs. double weight_new = weight_1_4 + weights_3[v_6] + weights[v_5][v_2]; // calculate the total weights. double weight = weight_1_2_plus_3_4 + weights[v_5][v_6]; if (weight - weight_new > _epsilon) { // actually do replace the vertices. int count_before = route.Count; //string route_string = route.ToString(); route.ReplaceEdgeFrom(v1, v_4); route.ReplaceEdgeFrom(v_3, v_6); route.ReplaceEdgeFrom(v_5, v_2); int count_after = route.Count; if (count_before != count_after) { throw new Exception(); } // set bits. //this.Set(problem, v_1, false); this.Set(problem, v_3, false); this.Set(problem, v_5, false); if (!route.IsValid()) { throw new Exception(); } return(true); // move succeeded. } //} v_5 = v_6; } } } return(false); }
/// <summary> /// Apply some improvements within one route. /// </summary> /// <param name="problem"></param> /// <param name="route"></param> /// <param name="currentWeight"></param> private double ImproveIntraRoute(IProblemWeights problem, IRoute route, double currentWeight) { bool improvement = true; double newWeight = currentWeight; while (improvement) { // keep trying while there are still improvements. improvement = false; // loop over all improvement operations. foreach (IImprovement improvementOperation in _intra_improvements) { // try the current improvement operations. double difference; if (improvementOperation.Improve(problem, route, out difference)) { // there was an improvement. OsmSharp.Logging.Log.TraceEvent("OsmSharp.Routing.VRP.NoDepot.MaxTime.VNS.GuidedVNS", TraceEventType.Information, "Intra-improvement found {0} {1}->{2}", improvementOperation.Name, newWeight, newWeight + difference); // check if the route is valid. if (!route.IsValid()) { throw new Exception(); } // update the weight. newWeight = newWeight + difference; improvement = true; break; } } } return newWeight; }
/// <summary> /// Searches for the best place to insert the given customer. /// </summary> /// <param name="problem"></param> /// <param name="route"></param> /// <param name="customer"></param> /// <returns></returns> public static CheapestInsertionResult CalculateBestPlacement( IProblemWeights problem, IRoute route, int customer) { // initialize the best placement result. double[][] weights = problem.WeightMatrix; CheapestInsertionResult result = new CheapestInsertionResult(); result.Customer = customer; result.CustomerAfter = -1; result.CustomerBefore = -1; result.Increase = double.MaxValue; double difference = double.MaxValue; if (!route.IsEmpty) { double new_weight = double.MaxValue; double old_weight = 0; int previous = -1; int first = -1; foreach (int current in route) { if (previous >= 0) { // the previous customer exists. // only if the previous is known. // calculate the old weights. //old_weight = problem.Weight(previous, current); old_weight = weights[previous][current]; // calculate the new weights. //new_weight = problem.Weight(previous, customer); new_weight = weights[previous][customer]; //new_weight = new_weight + // problem.Weight(customer, current); new_weight = new_weight + weights[customer][current]; // calculate the difference. difference = new_weight - old_weight; if (result.Increase > difference) { result.CustomerAfter = current; result.CustomerBefore = previous; result.Increase = difference; // if the difference is equal to or smaller than epsilon we have search enough. if (difference == 0) { // result is the best we will be able to get. return(result); } } } else { // store the first city for later. first = current; } // go to the next loop. previous = current; } // set the pervious to the last. //previous = route.Last; // test last-to-first if the route is a round. if (route.IsRound) { // calculate the new weights. //new_weight = problem.Weight(previous, customer) // + (problem.Weight(customer, first)); new_weight = weights[previous][customer] + weights[customer][first]; // calculate the old weights. //old_weight = problem.Weight(previous, first); old_weight = weights[previous][first]; // calculate the difference. difference = new_weight - old_weight; if (result.Increase > difference) { result.CustomerAfter = first; result.CustomerBefore = previous; result.Increase = difference; } } } else { // route needs to be initialized. throw new ArgumentException("Route needs to be initialized with at least one customer!"); } // return result. return(result); }
/// <summary> /// Searches for the best place to insert the given two customers abstracting the distance between them. /// </summary> /// <param name="problem"></param> /// <param name="route"></param> /// <param name="from"></param> /// <param name="to"></param> /// <returns></returns> public static CheapestInsertionResult CalculateBestPlacement( IProblemWeights problem, IRoute route, int from, int to) { // initialize the best placement result. CheapestInsertionResult result = new CheapestInsertionResult(); result.Customer = -1; // this property is useless here! result.CustomerAfter = -1; result.CustomerBefore = -1; result.Increase = double.MaxValue; if (!route.IsEmpty) { double new_weight = double.MaxValue; double old_weight = 0; int previous = -1; int first = -1; foreach (int current in route) { if (previous >= 0) { // only if the previous is known. // calculate the new weights. new_weight = problem.Weight(previous, from) + (problem.Weight(to, current)); // calculate the old weights. old_weight = problem.Weight(previous, current); // calculate the difference. double difference = new_weight - old_weight; if (result.Increase > difference) { result.CustomerAfter = current; result.CustomerBefore = previous; result.Increase = difference; } } else { // store the first city for later. first = current; } // go to the next loop. previous = current; } // test last-to-first if the route is a round. if (route.IsRound) { // calculate the new weights. new_weight = problem.Weight(previous, from) + (problem.Weight(to, first)); // calculate the old weights. old_weight = problem.Weight(previous, first); // calculate the difference. double difference = new_weight - old_weight; if (result.Increase > difference) { result.CustomerBefore = previous; result.CustomerAfter = first; result.Increase = difference; } } } else { // route needs to be initialized. throw new ArgumentException("Route needs to be initialized with at least one customer!"); } // return result. return(result); }
/// <summary> /// Tries all 3Opt Moves for the neighbourhood of v_1. /// </summary> /// <param name="problem"></param> /// <param name="weights"></param> /// <param name="route"></param> /// <param name="v1"></param> /// <returns></returns> public bool Try3OptMoves(IProblemWeights problem, double[][] weights, IRoute route, int v1) { // get v_2. int v_2 = route.GetNeigbours(v1)[0]; if (v_2 < 0) { return false; } IEnumerable<int> between_v_2_v_1 = route.Between(v_2, v1); double weight_1_2 = weights[v1][v_2]; int v_3 = -1; NearestNeighbours10 neighbours = null; if (_nearest_neighbours) { neighbours = problem.Get10NearestNeighbours(v1); } foreach (int v_4 in between_v_2_v_1) { if (v_3 >= 0 && v_3 != v1) { //if (!_nearest_neighbours || // neighbours.Max <= weight_1_4) if (!_nearest_neighbours || neighbours.Contains(v_4)) { double weight_1_4 = weights[v1][v_4]; double weight_1_2_plus_3_4 = weight_1_2 + weights[v_3][v_4]; double[] weights_3 = weights[v_3]; if (this.Try3OptMoves(problem, weights, route, v1, v_2, v_3, weights_3, v_4, weight_1_2_plus_3_4, weight_1_4)) { return true; } } } v_3 = v_4; } return false; }
/// <summary> /// Returns the customer that least increases the length of the given route. /// </summary> /// <param name="problem"></param> /// <param name="route"></param> /// <param name="customers"></param> /// <param name="costs"></param> /// <returns></returns> public static CheapestInsertionResult CalculateBestPlacement( IProblemWeights problem, IRoute route, ICollection <int> customers, IInsertionCosts costs) { // initialize the best placement result. CheapestInsertionResult best = new CheapestInsertionResult(); best.Increase = float.MaxValue; // loop over all customers in the route. if (route.Count > 0) { // there have to be at least two customers. IEnumerator <int> route_enumerator = route.GetEnumerator(); if (!route_enumerator.MoveNext()) { // this route is empty throw new ArgumentException("Route needs to be initialized with at least two customers!"); } int customer_before = route_enumerator.Current; int customer_after = -1; while (route_enumerator.MoveNext()) { // keep moving! customer_after = route_enumerator.Current; InsertionCost cost = costs.PopCheapest(customer_before, customer_after); bool found = false; while (!found) { // test if the costs are empty. if (cost == null) { // re-initialize the costs with all customers under consideration. foreach (int customer in customers) { costs.Add(customer_before, customer_after, customer, (float)problem.WeightMatrix[customer_before][customer] + (float)problem.WeightMatrix[customer][customer_after] - (float)problem.WeightMatrix[customer_before][customer_after]); } // pop the cheapest again! cost = costs.PopCheapest(customer_before, customer_after); } else if (customers.Contains(cost.Customer)) { // the customer is found! found = true; } else { // pop the cheapest again! cost = costs.PopCheapest(customer_before, customer_after); } } if (cost.Cost < best.Increase) { // the costs is better than the current cost! best = new CheapestInsertionResult() { Customer = cost.Customer, CustomerAfter = customer_after, CustomerBefore = customer_before, Increase = cost.Cost }; if (best.Increase == 0) { // immidiately return if smaller than epsilon. return(best); } } // set the after to the before. customer_before = route_enumerator.Current; } // if the round is a round try first-last. if (route.IsRound) { // the route is a round! customer_after = route.First; InsertionCost cost = costs.PopCheapest(customer_before, customer_after); bool found = false; while (!found) { // test if the costs are empty. if (cost == null) { // re-initialize the costs with all customers under consideration. foreach (int customer in customers) { costs.Add(customer_before, customer_after, customer, (float)problem.WeightMatrix[customer_before][customer] + (float)problem.WeightMatrix[customer][customer_after] - (float)problem.WeightMatrix[customer_before][customer_after]); } // pop the cheapest again! cost = costs.PopCheapest(customer_before, customer_after); } else if (customers.Contains(cost.Customer)) { // the customer is found! found = true; } else { // pop the cheapest again! cost = costs.PopCheapest(customer_before, customer_after); } } if (cost.Cost < best.Increase) { // the costs is better than the current cost! best = new CheapestInsertionResult() { Customer = cost.Customer, CustomerAfter = customer_after, CustomerBefore = customer_before, Increase = cost.Cost }; if (best.Increase == 0) { // immidiately return if smaller than epsilon. return(best); } } } } else { // route needs to be initialized. throw new ArgumentException("Route needs to be initialized with at least two customers!"); } // return result. return(best); }
/// <summary> /// Applies a local search strategy. /// </summary> /// <param name="problem"></param> /// <param name="route"></param> /// <returns></returns> public static void CalculateRePlaceOptHelper( IProblemWeights problem, IRoute route) { //bool improvement = true; //while (improvement) //{ //// reset improvement flag. //improvement = false; // try re-placement of one customer. int before = -1; //int after = -1; int found_customer = -1; // loop over all customers and try to place it better. HashSet <int> customers_to_place = new HashSet <int>(route); foreach (int customer_to_place in customers_to_place) { // find the best place. double current_cost = -1; double other_cost = double.MaxValue; int previous_before = -1; int previous = -1; foreach (int customer in route) { if (previous >= 0 && previous_before >= 0) { if (previous == customer_to_place) { current_cost = problem.Weight(previous_before, previous) + problem.Weight(previous, customer); } else if (previous_before != customer_to_place && customer != customer_to_place) { // calculate the cost. double cost = problem.Weight(previous_before, customer_to_place) + problem.Weight(customer_to_place, previous); if (cost < other_cost) { other_cost = cost; before = previous_before; //after = previous; found_customer = customer; } } } previous_before = previous; previous = customer; } // determine if the cost is better. if (current_cost > other_cost) { // the current cost is better. route.Remove(found_customer); //route.InsertAfterAndRemove(before, found_customer, after); route.InsertAfter(before, found_customer); break; } else { // current cost is not better. before = -1; //after = -1; found_customer = -1; } } //if (found_customer >= 0) //{ // a found customer. // improvement = true; //} //} }
/// <summary> /// Tries all 3Opt Moves for the neighbourhood of v_1 containing v_3. /// </summary> /// <param name="problem"></param> /// <param name="weights"></param> /// <param name="route"></param> /// <param name="v1"></param> /// <param name="v_2"></param> /// <param name="weight_1_2"></param> /// <param name="v_3"></param> /// <returns></returns> public bool Try3OptMoves(IProblemWeights problem, double[][] weights, IRoute route, int v1, int v_2, double weight_1_2, int v_3) { // get v_4. int v_4 = route.GetNeigbours(v_3)[0]; double weight_1_2_plus_3_4 = weight_1_2 + weights[v_3][v_4]; double weight_1_4 = weights[v1][v_4]; double[] weights_3 = weights[v_3]; return this.Try3OptMoves(problem, weights, route, v1, v_2, v_3, weights_3, v_4, weight_1_2_plus_3_4, weight_1_4); }
/// <summary> /// Applies a local search strategy. /// </summary> /// <param name="problem"></param> /// <param name="route"></param> /// <returns></returns> public static void CalculateRePlaceOptHelper( IProblemWeights problem, IRoute route) { throw new NotImplementedException(); }
/// <summary> /// Tries all 3Opt Moves for the neighbourhood of v_1 containing v_3. /// </summary> /// <param name="problem"></param> /// <param name="weights"></param> /// <param name="route"></param> /// <param name="v1"></param> /// <param name="v_2"></param> /// <param name="v_3"></param> /// <param name="weights_3"></param> /// <param name="v_4"></param> /// <param name="weight_1_2_plus_3_4"></param> /// <param name="weight_1_4"></param> /// <returns></returns> public bool Try3OptMoves(IProblemWeights problem, double[][] weights, IRoute route, int v1, int v_2, int v_3, double[] weights_3, int v_4, double weight_1_2_plus_3_4, double weight_1_4) { //IEnumerable<int> between_v_4_v_1 = route.Between(v_4, v_1); //foreach (int v_5 in between_v_4_v_1) //{ // if (v_5 != v_1) // { // if (this.Try3OptMove(problem, weights, route, v_1, v_2, v_3, weights_3, v_4, weight_1_2_plus_3_4, weight_1_4, v_5)) // { // return true; // } // } //} //return false; IEnumerator<int> between_v_4_v_1_enumerator = route.Between(v_4, v1).GetEnumerator(); if (between_v_4_v_1_enumerator.MoveNext()) { int v_5 = between_v_4_v_1_enumerator.Current; if (v_5 != v1) { while (between_v_4_v_1_enumerator.MoveNext()) { int v_6 = between_v_4_v_1_enumerator.Current; //Console.WriteLine(v_6); //if (this.Try3OptMove(problem, weights, // route, v_1, v_2, v_3, weights_3, v_4, // weight_1_2_plus_3_4, weight_1_4, v_5, v_6)) //{ // calculate the total weight of the 'new' arcs. double weight_new = weight_1_4 + weights_3[v_6] + weights[v_5][v_2]; // calculate the total weights. double weight = weight_1_2_plus_3_4 + weights[v_5][v_6]; if (weight - weight_new > _epsilon) { // actually do replace the vertices. int count_before = route.Count; //string route_string = route.ToString(); route.ReplaceEdgeFrom(v1, v_4); route.ReplaceEdgeFrom(v_3, v_6); route.ReplaceEdgeFrom(v_5, v_2); int count_after = route.Count; if (count_before != count_after) { throw new Exception(); } // set bits. //this.Set(problem, v_1, false); this.Set(problem, v_3, false); this.Set(problem, v_5, false); if (!route.IsValid()) { throw new Exception(); } return true; // move succeeded. } //} v_5 = v_6; } } } return false; }
private bool Check(IProblemWeights problem, int customer) { if (_dont_look) { return _dont_look_bits[customer]; } return false; }
/// <summary> /// Selects seed customers. /// </summary> /// <param name="weights"></param> /// <param name="k"></param> /// <returns></returns> public ICollection <int> SelectSeeds(IProblemWeights weights, int k) { List <int> seeds = new List <int>(); // randomly select k seeds. while (seeds.Count < k) { int new_seed = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(weights.Size); while (seeds.Contains(new_seed)) { new_seed = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(weights.Size); } seeds.Add(new_seed); } // find shortest distance. double minimal = double.MaxValue; int selected_idx = -1; for (int x = 0; x < k; x++) { for (int y = 0; y < x; y++) { double weight = weights.Weight(seeds[x], seeds[y]); if (weight < minimal) { selected_idx = x; minimal = weight; } } } // select any new seed and see if replacing the selected would yield better results. int max_tries = 2000; int tries = 0; while (tries < max_tries) { int seed = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(weights.Size); while (seeds.Contains(seed)) { seed = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(weights.Size); } // check the new minimal. double new_minimal = double.MaxValue; for (int x = 0; x < k; x++) { if (x != selected_idx) { double weight = weights.Weight(seeds[x], seed); if (weight < new_minimal) { new_minimal = weight; } } } // see if the new one has a higher minimal. if (new_minimal > minimal) { // ok there is an improvement! OsmSharp.Logging.Log.TraceEvent("SimpleSeeds", System.Diagnostics.TraceEventType.Information, "Seed new minimal: {0}->{1}", minimal, new_minimal); tries = 0; minimal = new_minimal; seeds[selected_idx] = seed; } tries++; // increase the number of tries. } OsmSharp.Logging.Log.TraceEvent("SimpleSeeds", System.Diagnostics.TraceEventType.Information, "Seed distance: {0}", minimal); return(seeds); }
/// <summary> /// Considers one customer for relocation. /// </summary> /// <param name="problem"></param> /// <param name="route"></param> /// <param name="previous"></param> /// <param name="current"></param> /// <param name="next"></param> /// <param name="route_weight"></param> /// <param name="max"></param> /// <returns></returns> private bool ConsiderCustomer(IProblemWeights problem, IRoute route, int previous, int current, int next, double route_weight, double max) { // calculate the removal gain of the customer. double removal_gain = problem.WeightMatrix[previous][current] + problem.WeightMatrix[current][next] - problem.WeightMatrix[previous][next]; if (removal_gain > 0.0001) { // try and place the customer in the next route. CheapestInsertionResult result = CheapestInsertionHelper.CalculateBestPlacement(problem, route, current); if (result.Increase < removal_gain - 0.001 && route_weight + result.Increase < max) { // there is a gain in relocating this customer. int count_before = route.Count; // string route_string = route.ToString(); // and the route is still within bounds! route.ReplaceEdgeFrom(result.CustomerBefore, result.Customer); route.ReplaceEdgeFrom(result.Customer, result.CustomerAfter); int count_after = route.Count; if (count_before + 1 != count_after) { throw new Exception(); } return true; } } return false; }
/// <summary> /// Searches for the best place to insert the given customer. /// </summary> /// <param name="problem"></param> /// <param name="route"></param> /// <param name="customer"></param> /// <returns></returns> public static CheapestInsertionResult CalculateBestPlacement( IProblemWeights problem, IRoute route, int customer) { // initialize the best placement result. double[][] weights = problem.WeightMatrix; CheapestInsertionResult result = new CheapestInsertionResult(); result.Customer = customer; result.CustomerAfter = -1; result.CustomerBefore = -1; result.Increase = double.MaxValue; double difference = double.MaxValue; if (!route.IsEmpty) { double new_weight = double.MaxValue; double old_weight = 0; int previous = -1; int first = -1; foreach (int current in route) { if (previous >= 0) { // the previous customer exists. // only if the previous is known. // calculate the old weights. //old_weight = problem.Weight(previous, current); old_weight = weights[previous][current]; // calculate the new weights. //new_weight = problem.Weight(previous, customer); new_weight = weights[previous][customer]; //new_weight = new_weight + // problem.Weight(customer, current); new_weight = new_weight + weights[customer][current]; // calculate the difference. difference = new_weight - old_weight; if (result.Increase > difference) { result.CustomerAfter = current; result.CustomerBefore = previous; result.Increase = difference; // if the difference is equal to or smaller than epsilon we have search enough. if (difference == 0) { // result is the best we will be able to get. return result; } } } else { // store the first city for later. first = current; } // go to the next loop. previous = current; } // set the pervious to the last. //previous = route.Last; // test last-to-first if the route is a round. if (route.IsRound) { // calculate the new weights. //new_weight = problem.Weight(previous, customer) // + (problem.Weight(customer, first)); new_weight = weights[previous][customer] + weights[customer][first]; // calculate the old weights. //old_weight = problem.Weight(previous, first); old_weight = weights[previous][first]; // calculate the difference. difference = new_weight - old_weight; if (result.Increase > difference) { result.CustomerAfter = first; result.CustomerBefore = previous; result.Increase = difference; } } } else { // route needs to be initialized. throw new ArgumentException("Route needs to be initialized with at least one customer!"); } // return result. return result; }