/// <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> /// Generates individuals based on a random first customer for each route. /// </summary> /// <param name="solver"></param> /// <returns></returns> public Individual <MaxTimeSolution, MaxTimeProblem, Fitness> Generate( Solver <MaxTimeSolution, MaxTimeProblem, Fitness> solver) { MaxTimeProblem problem = solver.Problem; MaxTimeSolution solution = new MaxTimeSolution(problem.Size, true); // create the problem for the genetic algorithm. List <int> customers = new List <int>(); for (int customer = 0; customer < problem.Size; customer++) { customers.Add(customer); } //BestPlacementHelper helper = new BestPlacementHelper(); // keep placing customer until none are left. while (customers.Count > 0) { // select a random customer. double weight = 0; int customer_idx = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(customers.Count); int customer = customers[customer_idx]; customers.RemoveAt(customer_idx); // use best placement to generate a route. IRoute current_route = solution.Add(customer); //Console.WriteLine("Starting new route with {0}", customer); while (customers.Count > 0) { // calculate the best placement. int customer_to_place = customers[OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(customers.Count)]; CheapestInsertionResult result = CheapestInsertionHelper.CalculateBestPlacement(problem, current_route, customer_to_place); // calculate the new weight. double potential_weight = result.Increase + weight + 20; // cram as many customers into one route as possible. if (potential_weight < problem.Max.Value) { // ok we are done! customers.Remove(result.Customer); //current_route.InsertAfterAndRemove(result.CustomerBefore, result.Customer, result.CustomerAfter); current_route.InsertAfter(result.CustomerBefore, result.Customer); weight = potential_weight; } else { break; } } } return(new Individual <MaxTimeSolution, MaxTimeProblem, Fitness>(solution)); }
/// <summary> /// Solves the given problem. /// </summary> /// <returns></returns> public sealed override Tour Solve(STSProblem problem, STSPObjective objective, out STSPFitness fitness) { // generate random pool to select customers from. if (_randomPool == null || _randomPool.Size < problem.Weights.Length) { _randomPool = new RandomPool(problem.Weights.Length); } else { _randomPool.Reset(); } // keep adding customers until no more space is left or no more customers available. var tour = problem.CreateEmptyTour(); fitness = new STSPFitness() { Weight = 0, Customers = 1 }; while (_randomPool.MoveNext()) { var customer = _randomPool.Current; if (customer == problem.First || customer == problem.Last) { continue; } Pair location; var cost = CheapestInsertionHelper.CalculateCheapest(tour, problem.Weights, customer, out location); if (cost + fitness.Weight < problem.Max) { tour.InsertAfter(location.From, customer); fitness.Weight = fitness.Weight + cost; fitness.Customers = fitness.Customers + 1; } } // calculate fitness. fitness = objective.Calculate(problem, tour); return(tour); }
/// <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> /// 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> /// 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> /// Generates individuals based on a random first customer for each route. /// </summary> /// <param name="solver"></param> /// <returns></returns> public Individual <List <Genome>, Problem, Fitness> Generate( Solver <List <Genome>, Problem, Fitness> solver) { Problem problem = solver.Problem; DynamicAsymmetricMultiRoute multi_route = new DynamicAsymmetricMultiRoute(problem.Size, true); // create the problem for the genetic algorithm. List <int> customers = new List <int>(); for (int customer = problem.Depots.Count; customer < problem.Size; customer++) { customers.Add(customer); } CheapestInsertionHelper helper = new CheapestInsertionHelper(); List <double> weights = new List <double>(); for (int i = 0; i < problem.Depots.Count; i++) { multi_route.Add(i); weights.Add(0); } int k = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(problem.Depots.Count); // keep placing customer until none are left. while (customers.Count > 0) { k = (k + 1) % problem.Depots.Count; // use best placement to generate a route. IRoute current_route = multi_route.Route(k); //Console.WriteLine("Starting new route with {0}", customer); while (customers.Count > 0) { // calculate the best placement. CheapestInsertionResult result = CheapestInsertionHelper.CalculateBestPlacement(problem, current_route, customers); if (result.CustomerAfter == -1 || result.CustomerBefore == -1) { customers.Remove(result.Customer); continue; } // calculate the new weight. customers.Remove(result.Customer); //current_route.InsertAfterAndRemove(result.CustomerBefore, result.Customer, result.CustomerAfter); current_route.InsertAfter(result.CustomerBefore, result.Customer); weights[k] += result.Increase + 15 * 60; if (weights[k] == weights.Max()) { break; } } } for (int i = 0; i < problem.Depots.Count; i++) { multi_route.RemoveCustomer(i); } List <Genome> genomes = new List <Genome>(); genomes.Add(Genome.CreateFrom(multi_route)); Individual <List <Genome>, Problem, Fitness> individual = new Individual <List <Genome>, Problem, Fitness>(genomes); //individual.Initialize(); return(individual); }
/// <summary> /// Does the mutation. /// </summary> /// <param name="solver"></param> /// <param name="mutating"></param> /// <returns></returns> public Individual <List <Genome>, Problem, Fitness> Mutate( Solver <List <Genome>, Problem, Fitness> solver, Individual <List <Genome>, Problem, Fitness> mutating) { // get the route information. Genome multi_route = mutating.Genomes[0]; int[] size_new = null; int[] customers_new = null; if (multi_route.Sizes.Length > 1) { // TODO: Maybe add some extra code for the the route-count is very low and chances of collisions are big. // TODO: investigate what is more expensive, and extra random generation or creating a list and remove items. // http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle // select a route. int source_route_idx = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(multi_route.Sizes.Length); int target_route_idx = -1; do { target_route_idx = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(multi_route.Sizes.Length); } while (source_route_idx == target_route_idx); int source_count = multi_route.Sizes[source_route_idx]; int target_count = multi_route.Sizes[target_route_idx]; if (target_count > 0 && source_count > 3) { // take a part out of the orginal. int size = (int)OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(source_count); int source_location = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(source_count); int first = source_location - (size / 2); if (first < 0) { first = 0; } first = multi_route.StartOf(source_route_idx) + first; int last = source_location + (size / 2); if (last > source_count - 1) { last = source_count - 1; } last = multi_route.StartOf(source_route_idx) + last; int idx = -1; List <int> part_of_orginal = new List <int>(); foreach (int customer in multi_route.Customers) { idx++; if (idx >= first && idx <= last) { part_of_orginal.Add(customer); } } // place it somewhere in the target. if (part_of_orginal.Count > 0) { IRoute target = multi_route.Route(target_route_idx); CheapestInsertionResult route_placement = CheapestInsertionHelper.CalculateBestPlacement( solver.Problem, target, part_of_orginal[0], part_of_orginal[part_of_orginal.Count - 1]); // create a new array and start copying. customers_new = new int[multi_route.Customers.Length]; int new_idx = 0; for (idx = 0; idx < multi_route.Customers.Length; idx++) { if (idx >= first && idx <= last) { // do nothing. } else { customers_new[new_idx] = multi_route.Customers[idx]; new_idx++; if (multi_route.Customers[idx] == route_placement.CustomerBefore) { // insert the entire between part here. for (int between_idx = 0; between_idx < part_of_orginal.Count; between_idx++) { customers_new[new_idx] = part_of_orginal[between_idx]; new_idx++; } } } } // adjust sizes array. size_new = new int[multi_route.Sizes.Length]; for (idx = 0; idx < multi_route.Sizes.Length; idx++) { if (idx == source_route_idx) { size_new[idx] = multi_route.Sizes[idx] - part_of_orginal.Count; } else if (idx == target_route_idx) { size_new[idx] = multi_route.Sizes[idx] + part_of_orginal.Count; } else { size_new[idx] = multi_route.Sizes[idx]; } } } } } if (size_new == null || customers_new == null) { size_new = multi_route.Sizes.Clone() as int[]; customers_new = multi_route.Customers.Clone() as int[]; } // remove all zero's. List <int> sizes = new List <int>(size_new); while (sizes.Remove(0)) { } size_new = sizes.ToArray <int>(); List <Genome> genomes = new List <Genome>(); Genome genome = new Genome(); genome.Sizes = size_new; genome.Customers = customers_new; if (!genome.IsValid()) { throw new Exception(); } genomes.Add(genome); Individual <List <Genome>, Problem, Fitness> individual = new Individual <List <Genome>, Problem, Fitness>(genomes); //individual.Initialize(); return(individual); }
/// <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); }
public Individual <List <Genome>, Problem, Fitness> CrossOver( Solver <List <Genome>, Problem, Fitness> solver, Individual <List <Genome>, Problem, Fitness> parent1, Individual <List <Genome>, Problem, Fitness> parent2) { Genome route1 = parent1.Genomes[0]; Genome route2 = parent2.Genomes[0]; // get the minimum size of both routes. int size = route1.Sizes.Length; if (route2.Sizes.Length < size) { size = route2.Sizes.Length; } // select a random number of routes. HashSet <int> selected_first = new HashSet <int>(); HashSet <int> selected_second = new HashSet <int>(); List <IRoute> selected_routes = new List <IRoute>(); bool first = true; while (selected_routes.Count < size) { // select route. int selected_route = -1; if (first) { selected_route = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(route1.Sizes.Length); while (selected_first.Contains(selected_route)) { selected_route = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(route1.Sizes.Length); } selected_first.Add(selected_route); selected_routes.Add(route1.Route(selected_route)); } else { selected_route = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(route2.Sizes.Length); while (selected_second.Contains(selected_route)) { selected_route = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(route2.Sizes.Length); } selected_second.Add(selected_route); selected_routes.Add(route2.Route(selected_route)); } first = !first; } // generate the new customer genome. HashSet <int> selected_customers = new HashSet <int>(); List <int> customers = new List <int>(); List <int> sizes = new List <int>(); foreach (IRoute route in selected_routes) { int current_size = 0; foreach (int customer in route) { if (!selected_customers.Contains(customer)) { customers.Add(customer); current_size++; selected_customers.Add(customer); } } sizes.Add(current_size); } while (sizes.Remove(0)) { } Genome genome = new Genome(); genome.Sizes = sizes.ToArray(); genome.Customers = customers.ToArray(); // insert all non-placed customers in the order of the first route. foreach (int customer in route1.Customers) { if (!selected_customers.Contains(customer)) { // try reinsertion. CheapestInsertionResult result = new CheapestInsertionResult(); result.Increase = float.MaxValue; int target_idx = -1; for (int idx = 0; idx < genome.Sizes.Length; idx++) { IRoute route = genome.Route(idx); if (genome.Sizes[idx] > 0) { CheapestInsertionResult current_result = CheapestInsertionHelper.CalculateBestPlacement(solver.Problem.Weights, route, customer); if (current_result.Increase < result.Increase) { target_idx = idx; result = current_result; if (result.Increase <= 0) { break; } } } } // insert the customer. for (int idx = 0; idx < customers.Count; idx++) { if (customers[idx] == result.CustomerBefore) { if (customers.Count - 1 == idx) { customers.Add(customer); } else { customers.Insert(idx + 1, customer); } break; } } // set the mutated. genome.Sizes[target_idx] = genome.Sizes[target_idx] + 1; genome.Customers = customers.ToArray(); } } if (!genome.IsValid()) { throw new Exception(); } List <Genome> genomes = new List <Genome>(); genomes.Add(genome); Individual <List <Genome>, Problem, Fitness> individual = new Individual <List <Genome>, Problem, Fitness>(genomes); //individual.Initialize(); return(individual); }
private void FillRoutes(MaxTimeCalculator calculator, MaxTimeSolution route1, MaxTimeSolution solution, MaxTimeProblem problem) { double[] weights = new double[solution.Count]; // insert all non-placed customers in the order of the first route. HashSet <int> unplaced = new HashSet <int>(); for (int route_idx = 0; route_idx < route1.Count; route_idx++) { IRoute route1_route = route1.Route(route_idx); foreach (int customer in route1_route) { if (!solution.Contains(customer)) { unplaced.Add(customer); } } } for (int idx = 0; idx < solution.Count; idx++) { IRoute route = solution.Route(idx); weights[idx] = calculator.CalculateOneRoute(route); } // insert all non-placed customers in the order of the first route. //for (int route_idx = 0; route_idx < route1.Count; route_idx++) //{ // IRoute route1_route = route1.Route(route_idx); // foreach (int customer in route1_route) // { // if (!solution.Contains(customer)) // { while (unplaced.Count > 0) { int customer = unplaced.First <int>(); // try reinsertion. CheapestInsertionResult result = new CheapestInsertionResult(); result.Increase = double.MaxValue; int target_idx = -1; CheapestInsertionResult unlimited_result = new CheapestInsertionResult(); unlimited_result.Increase = double.MaxValue; int unlimited_target_idx = -1; for (int idx = 0; idx < solution.Count; idx++) { IRoute route = solution.Route(idx); CheapestInsertionResult current_result = CheapestInsertionHelper.CalculateBestPlacement(problem.Weights, route, customer); if (current_result.Increase < result.Increase) { if (weights[idx] + current_result.Increase < problem.Max.Value) { target_idx = idx; result = current_result; if (result.Increase <= 0) { break; } } } if (current_result.Increase < unlimited_result.Increase) { unlimited_target_idx = idx; unlimited_result = current_result; } } if (target_idx < 0) { result = unlimited_result; target_idx = unlimited_target_idx; } // get the target route and insert. IRoute target_route = solution.Route(target_idx); weights[target_idx] = weights[target_idx] + result.Increase; //target_route.InsertAfterAndRemove(result.CustomerBefore, result.Customer, result.CustomerAfter); target_route.InsertAfter(result.CustomerBefore, result.Customer); unplaced.Remove(result.Customer); ////solution.ToString(); //if (!solution.IsValid()) //{ // throw new Exception(); //} } // } //} }
/// <summary> /// Applies this operator. /// </summary> public bool Apply(STSProblem problem, STSPObjective objective, Tour solution, out STSPFitness delta) { var before = objective.Calculate(problem, solution); var weights = problem.Weights; delta = objective.Zero; var i = _n; var toInsert = new List <int>(); if (!_insertNew) { // select existing customers, to reinsert. while (solution.Count > 1 && i > 0) { i--; var index = RandomGeneratorExtensions.GetRandom().Generate(solution.Count); var current = solution.GetCustomerAt(index); if (current != Constants.NOT_SET) { if (current != solution.First && current != solution.Last && solution.Remove(current)) { toInsert.Add(current); } } } } else if (solution.Count < problem.Weights.Length) { // select random new customer to insert. while (solution.Count > 1 && i > 0) { i--; var current = RandomGeneratorExtensions.GetRandom().Generate(problem.Weights.Length); if (!solution.Contains(current)) { if (current != solution.First && current != solution.Last && solution.Remove(current)) { toInsert.Add(current); } } } } var fitness = objective.Calculate(problem, solution); foreach (var current in toInsert) { // insert new. Pair position; var cost = CheapestInsertionHelper.CalculateCheapest(solution, weights, current, out position); if (cost + fitness.Weight < problem.Max) { solution.InsertAfter(position.From, current); fitness.Weight = fitness.Weight + cost; fitness.Customers = fitness.Customers + 1; } } var after = objective.Calculate(problem, solution); delta = objective.Subtract(problem, after, before); return(objective.CompareTo(problem, before, after) > 0); }
/// <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> /// Returns a solution found using best-placement. /// </summary> /// <returns></returns> protected override IRoute DoSolve(IProblem problem) { // build the customer list to place. List <int> customers = null; if (_customers != null) { // copy the list of the given customers and keep this order. customers = new List <int>(_customers); } else { // generate some random route. customers = new List <int>(); List <int> customers_to_place = new List <int>(); for (int customer = 0; customer < problem.Size; customer++) { customers_to_place.Add(customer); } while (customers_to_place.Count > 0) { int idx = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(customers_to_place.Count); customers.Add(customers_to_place[idx]); customers_to_place.RemoveAt(idx); } } // initialize the route based on the problem definition. IRoute route = null; double weight = double.MaxValue; if (problem.Symmetric) { // create a symmetric route that is dynamic and can accept new customers. if (problem.First.HasValue && problem.Last.HasValue && problem.First == problem.Last) { // route is a round. route = new DynamicSymmetricRoute(problem.First.Value); } else { // not a round. throw new NotImplementedException("No symmetric routes implemented that are not rounds!"); } } else { // create a asymmetric route that is dynamic and can accept new customers. if (problem.First.HasValue) { // the first customer is set. // test if the last customer is the same. if (!problem.Last.HasValue || problem.Last == problem.First) { // the route is a round. route = new DynamicAsymmetricRoute(customers.Count, problem.First.Value, true); // remove the first customer. customers.Remove(problem.First.Value); } else { // the route is not a round. route = new DynamicAsymmetricRoute(customers.Count, problem.First.Value, false); route.InsertAfter(problem.First.Value, problem.Last.Value); // remove the first customer. customers.Remove(problem.First.Value); customers.Remove(problem.Last.Value); } } else { // the first and last customer can be choosen randomly. // find two customers close together. int from = -1; int to = -1; for (int x = 0; x < customers.Count; x++) { for (int y = 0; y < customers.Count; y++) { if (x != y) { // only different customers. double current_weight = problem.WeightMatrix[x][y]; if (current_weight < weight) { // the current weight is better. from = x; to = y; weight = current_weight; if (weight == 0) { // no edge with less weight is going to be found. break; } } } } } route = new DynamicAsymmetricRoute(customers.Count, from, false); route.InsertAfter(from, to); // remove the first customer. customers.Remove(from); customers.Remove(to); } } // insert the rest of the customers. while (customers.Count > 0 && !_stopped) { // keep placing customer 0 until all customers are placed. // calculate placement. CheapestInsertionResult result = CheapestInsertionHelper.CalculateBestPlacement(problem, route, customers); // place the customer. if (result.CustomerAfter >= 0 && result.CustomerBefore >= 0) { //route.InsertAfterAndRemove(result.CustomerBefore, result.Customer, result.CustomerAfter); route.InsertAfter(result.CustomerBefore, result.Customer); customers.Remove(result.Customer); } } return(route); }
/// <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> /// Executes the RAI. /// </summary> /// <param name="problem"></param> /// <param name="solver"></param> /// <param name="initial_route"></param> /// <returns></returns> private static IRoute DoSolve(RandomizedArbitraryInsertionSolver solver, IProblem problem, IRoute initial_route) { // initialize a route using best-placement. DynamicAsymmetricRoute route = null; if (initial_route == null) { OsmSharp.Math.TSP.ArbitraryInsertion.ArbitraryInsertionSolver ai_solver = new OsmSharp.Math.TSP.ArbitraryInsertion.ArbitraryInsertionSolver(); initial_route = ai_solver.Solve(problem); } // get/create the dynamic route. if (initial_route is DynamicAsymmetricRoute) { // initial route is already the correct type. route = initial_route as DynamicAsymmetricRoute; } else { // convert the initial route to a route of the correct type. route = DynamicAsymmetricRoute.CreateFrom(initial_route); } // do the Arbitrary Insertion. double weight = route.CalculateWeight(problem); int try_count = 0; while (try_count < (route.Count * route.Count)) { // keep trying for a given number of times. int factor = 2; // cut out a part. int i = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate((route.Count / factor) - 1) + 1; int j = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate((route.Count / factor) - 1) + 1; while (i == j) { j = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate((route.Count / factor) - 1) + 1; } if (i > j) { int k = j; j = i; i = k; } // cut out the i->j part. int length = j - i; if (length > 0) { // cut and remove. DynamicAsymmetricRoute.CutResult cut_result = route.CutAndRemove( problem, weight, i, length); // calculate the weight that was removed. double new_weight = cut_result.Weight; List <int> cut_part = cut_result.CutPart; DynamicAsymmetricRoute cut_route = cut_result.Route; // use best placement to re-insert. int c = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(cut_part.Count); while (cut_part.Count > 0) { // loop until it's empty. int customer = cut_part[c]; cut_part.RemoveAt(c); // calculate the best placement. CheapestInsertionResult result = CheapestInsertionHelper.CalculateBestPlacement( problem, cut_route, customer); //cut_route.InsertAfterAndRemove(result.CustomerBefore, result.Customer, result.CustomerAfter); cut_route.InsertAfter(result.CustomerBefore, result.Customer); new_weight = new_weight + result.Increase; // choose next random customer. c = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(cut_part.Count); } // descide to keep new route or not. if (weight > new_weight) { route = cut_route; weight = new_weight; //if (this.CanRaiseIntermidiateResult()) //{ // this.RaiseIntermidiateResult(route.ToArray<int>(), weight); //} } } // increase the try count. try_count++; } if (solver != null && solver.CanRaiseIntermidiateResult()) { solver.RaiseIntermidiateResult(route.ToArray <int>()); } return(route); }
/// <summary> /// Does the mutation. /// </summary> /// <param name="solver"></param> /// <param name="mutating"></param> /// <returns></returns> public Individual <MaxTimeSolution, MaxTimeProblem, Fitness> Mutate( Solver <MaxTimeSolution, MaxTimeProblem, Fitness> solver, Individual <MaxTimeSolution, MaxTimeProblem, Fitness> mutating) { // get the route information. MaxTimeSolution multi_route = mutating.Genomes.Clone() as MaxTimeSolution; if (multi_route.Count > 1) { // TODO: Maybe add some extra code for the the route-count is very low and chances of collisions are big. // TODO: investigate what is more expensive, and extra random generation or creating a list and remove items. // http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle // select a route. int source_route_idx = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(multi_route.Count); int target_route_idx = -1; do { target_route_idx = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(multi_route.Count); } while (source_route_idx == target_route_idx); int source_count = multi_route.Sizes[source_route_idx]; int target_count = multi_route.Sizes[target_route_idx]; if (target_count > 0 && source_count > 3) { // take a part out of the orginal. int size = (int)OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(source_count); int source_location = OsmSharp.Math.Random.StaticRandomGenerator.Get().Generate(source_count); int first = source_location - (size / 2); if (first < 0) { first = 0; } //first = multi_route.StartOf(source_route_idx) + first; int last = source_location + (size / 2); if (last > source_count - 1) { last = source_count - 1; } //last = multi_route.StartOf(source_route_idx) + last; int idx = -1; IRoute source = multi_route.Route(source_route_idx); List <int> part_of_orginal = new List <int>(); foreach (int customer in source) { idx++; if (idx >= first && idx <= last) { part_of_orginal.Add(customer); } } // remove from the source. foreach (int customer in part_of_orginal) { multi_route.RemoveCustomer(customer); } // place it somewhere in the target. if (part_of_orginal.Count > 0) { IRoute target = multi_route.Route(target_route_idx); CheapestInsertionResult route_placement = CheapestInsertionHelper.CalculateBestPlacement( solver.Problem, target, part_of_orginal[0], part_of_orginal[part_of_orginal.Count - 1]); int from = route_placement.CustomerBefore; foreach (int customer in part_of_orginal) { target.InsertAfter(from, customer); //target.InsertAfterAndRemove(from, customer, -1); from = customer; } } } } return(new Individual <MaxTimeSolution, MaxTimeProblem, Fitness>(multi_route)); }