/// <summary> /// Applies this crossover operation. /// </summary> /// <param name="solver"></param> /// <param name="parent1"></param> /// <param name="parent2"></param> /// <returns></returns> public Individual <List <int>, GeneticProblem, Fitness> CrossOver( Solver <List <int>, GeneticProblem, Fitness> solver, Individual <List <int>, GeneticProblem, Fitness> parent1, Individual <List <int>, GeneticProblem, Fitness> parent2) { OsmSharp.Math.TSP.Problems.IProblem tsp_problem = solver.Problem.BaseProblem; double[][] weights = tsp_problem.WeightMatrix; // first create E_a AsymmetricCycles e_a = new AsymmetricCycles(parent1.Genomes.Count + 1); e_a.AddEdge(0, parent1.Genomes[0]); for (int idx = 0; idx < parent1.Genomes.Count - 1; idx++) { int from = parent1.Genomes[idx]; int to = parent1.Genomes[idx + 1]; e_a.AddEdge(from, to); } e_a.AddEdge(parent1.Genomes[parent1.Genomes.Count - 1], 0); // create E_b int[] e_b = new int[parent2.Genomes.Count + 1]; e_b[parent2.Genomes[0]] = 0; for (int idx = 0; idx < parent2.Genomes.Count - 1; idx++) { int from = parent2.Genomes[idx]; int to = parent2.Genomes[idx + 1]; e_b[to] = from; } e_b[0] = parent2.Genomes[parent2.Genomes.Count - 1]; // create cycles. AsymmetricAlternatingCycles cycles = new AsymmetricAlternatingCycles( parent2.Genomes.Count + 1); for (int idx = 0; idx < e_b.Length; idx++) { int a = e_a[idx]; int b = e_b[a]; if (idx != b) { cycles.AddEdge(idx, a, b); } } // the cycles that can be selected. List <int> selectable_cycles = new List <int>(cycles.Cycles.Keys); int generated = 0; Individual best = null; while (generated < _max_offspring && selectable_cycles.Count > 0) { // select some random cycles. List <int> cycle_starts = this.SelectCycles(selectable_cycles); // copy if needed. AsymmetricCycles a = null; if (_max_offspring > 1) { a = e_a.Clone(); } else { a = e_a; } // take e_a and remove all edges that are in the selected cycles and replace them by the eges int[] next_array_a = a.NextArray; foreach (int start in cycle_starts) { int current = start; KeyValuePair <int, int> current_next = cycles.Next(current); do { a.AddEdge(current_next.Value, current_next.Key); current = current_next.Value; current_next = cycles.Next(current); } while(current != start); } // connect all subtoures. int cycle_count = a.Cycles.Count; while (cycle_count > 1) { // get the smallest tour. KeyValuePair <int, int> current_tour = new KeyValuePair <int, int>(-1, int.MaxValue); foreach (KeyValuePair <int, int> tour in a.Cycles) { if (tour.Value < current_tour.Value) { current_tour = tour; } } // first try nn approach. double weight = double.MaxValue; int selected_from1 = -1; int selected_from2 = -1; int selected_to1 = -1; int selected_to2 = -1; bool[] ignore_list = new bool[a.Length]; int from; int to; from = current_tour.Key; ignore_list[from] = true; //ignore_list.Add(from); to = next_array_a[from]; do { // step to the next ones. from = to; to = next_array_a[from]; //ignore_list.Add(from); ignore_list[from] = true; } while (from != current_tour.Key); if (_nn) { // only try tours containing nn. from = current_tour.Key; to = next_array_a[from]; double weight_from_to = weights[from][to]; do { // check the nearest neighbours of from foreach (int nn in tsp_problem.Get10NearestNeighbours(from)) { int nn_to = next_array_a[nn]; //if (!ignore_list.Contains(nn) && // !ignore_list.Contains(nn_to)) if (!ignore_list[nn] && !ignore_list[nn_to]) { double merge_weight = (weights[from][nn_to] + weights[nn][to]) - (weight_from_to + weights[nn][nn_to]); if (weight > merge_weight) { weight = merge_weight; selected_from1 = from; selected_from2 = nn; selected_to1 = to; selected_to2 = nn_to; } } } // step to the next ones. from = to; to = next_array_a[from]; } while (from != current_tour.Key); } if (selected_from2 < 0) { // check the nearest neighbours of from foreach (int customer in parent1.Genomes) { int customer_to = next_array_a[customer]; //if (!ignore_list.Contains(customer) && // !ignore_list.Contains(customer_to)) if (!ignore_list[customer] && !ignore_list[customer_to]) { double merge_weight = (weights[from][customer_to] + weights[customer][to]) - (weights[from][to] + weights[customer][customer_to]); if (weight > merge_weight) { weight = merge_weight; selected_from1 = from; selected_from2 = customer; selected_to1 = to; selected_to2 = customer_to; } } } } //if (selected_from1 >= 0) //{ a.AddEdge(selected_from1, selected_to2); a.AddEdge(selected_from2, selected_to1); //} //else //{ // throw new Exception(); //} cycle_count--; } //if (a.Cycles.Values.First<int>() != a.Length) //{ // throw new Exception(); //} List <int> new_genome = new List <int>(a.Length); int next = next_array_a[0]; do { new_genome.Add(next); next = next_array_a[next]; }while (next != 0); Individual individual = new Individual(new_genome); individual.CalculateFitness(solver.Problem, solver.FitnessCalculator); if (best == null || best.Fitness.CompareTo(individual.Fitness) > 0) { best = individual; } generated++; } if (best == null) { List <int> new_genome = new List <int>(); int next = e_a[0]; do { new_genome.Add(next); next = e_a[next]; }while (next != 0); Individual individual = new Individual(new_genome); individual.CalculateFitness(solver.Problem, solver.FitnessCalculator); if (best == null || best.Fitness.CompareTo(individual.Fitness) > 0) { best = individual; } } return(best); }
/// <summary> /// Applies this operator using the given solutions and produces a new solution. /// </summary> /// <returns></returns> public Tour Apply(TSProblem problem, TSPObjective objective, Tour solution1, Tour solution2, out float fitness) { if (solution1.Last != problem.Last) { throw new ArgumentException("Route and problem have to have the same last customer."); } if (solution2.Last != problem.Last) { throw new ArgumentException("Route and problem have to have the same last customer."); } var originalProblem = problem; var originalSolution1 = solution1; var originalSolution2 = solution2; if (!problem.Last.HasValue) { // convert to closed problem. Logger.Log("EAXOperator.Apply", Logging.TraceEventType.Warning, string.Format("EAX operator cannot be applied to 'open' TSP's: converting problem and routes to a closed equivalent.")); problem = problem.ToClosed(); solution1 = new Tour(solution1, 0); solution2 = new Tour(solution2, 0); } else if (problem.First != problem.Last) { // last is set but is not the same as first. Logger.Log("EAXSolver.Solve", Logging.TraceEventType.Warning, string.Format("EAX operator cannot be applied to 'closed' TSP's with a fixed endpoint: converting problem and routes to a closed equivalent.")); problem = problem.ToClosed(); solution1 = new Tour(solution1, 0); solution2 = new Tour(solution2, 0); solution1.Remove(originalProblem.Last.Value); solution2.Remove(originalProblem.Last.Value); } fitness = float.MaxValue; var weights = problem.Weights; // first create E_a var e_a = new AsymmetricCycles(solution1.Count); foreach (var edge in solution1.Pairs()) { e_a.AddEdge(edge.From, edge.To); } // create E_b var e_b = new int[solution2.Count]; foreach (var edge in solution2.Pairs()) { e_b[edge.To] = edge.From; } // create cycles. var cycles = new AsymmetricAlternatingCycles(solution2.Count); for (var idx = 0; idx < e_b.Length; idx++) { var a = e_a[idx]; if (a != Constants.NOT_SET) { var b = e_b[a]; if (idx != b && b != Constants.NOT_SET) { cycles.AddEdge(idx, a, b); } } } // the cycles that can be selected. var selectableCycles = new List <int>(cycles.Cycles.Keys); int generated = 0; Tour best = null; while (generated < _maxOffspring && selectableCycles.Count > 0) { // select some random cycles. var cycleStarts = this.SelectCycles(selectableCycles); // copy if needed. AsymmetricCycles a = null; if (_maxOffspring > 1) { a = e_a.Clone(); } else { a = e_a; } // take e_a and remove all edges that are in the selected cycles and replace them by the eges var nextArrayA = a.NextArray; foreach (int start in cycleStarts) { var current = start; var currentNext = cycles.Next(current); do { a.AddEdge(currentNext.Value, currentNext.Key); current = currentNext.Value; currentNext = cycles.Next(current); } while (current != start); } // connect all subtoures. var cycleCount = a.Cycles.Count; while (cycleCount > 1) { // get the smallest tour. var currentTour = new KeyValuePair <int, int>(-1, int.MaxValue); foreach (KeyValuePair <int, int> tour in a.Cycles) { if (tour.Value < currentTour.Value) { currentTour = tour; } } // first try nn approach. var weight = double.MaxValue; var selectedFrom1 = -1; var selectedFrom2 = -1; var selectedTo1 = -1; var selectedTo2 = -1; var ignoreList = new bool[a.Length]; int from; int to; from = currentTour.Key; ignoreList[from] = true; to = nextArrayA[from]; do { // step to the next ones. from = to; to = nextArrayA[from]; //ignore_list.Add(from); ignoreList[from] = true; } while (from != currentTour.Key); if (_nn) { // only try tours containing nn. from = currentTour.Key; to = nextArrayA[from]; var weightFromTo = weights[from][to]; do { // check the nearest neighbours of from foreach (var nn in problem.GetNNearestNeighboursForward(10, from)) { var nnTo = nextArrayA[nn]; if (nnTo != Constants.NOT_SET && !ignoreList[nn] && !ignoreList[nnTo]) { float mergeWeight = (weights[from][nnTo] + weights[nn][to]) - (weightFromTo + weights[nn][nnTo]); if (weight > mergeWeight) { weight = mergeWeight; selectedFrom1 = from; selectedFrom2 = nn; selectedTo1 = to; selectedTo2 = nnTo; } } } // step to the next ones. from = to; to = nextArrayA[from]; } while (from != currentTour.Key); } if (selectedFrom2 < 0) { // check the nearest neighbours of from foreach (var customer in solution1) { int customerTo = nextArrayA[customer]; if (!ignoreList[customer] && !ignoreList[customerTo]) { var mergeWeight = (weights[from][customerTo] + weights[customer][to]) - (weights[from][to] + weights[customer][customerTo]); if (weight > mergeWeight) { weight = mergeWeight; selectedFrom1 = from; selectedFrom2 = customer; selectedTo1 = to; selectedTo2 = customerTo; } } } } a.AddEdge(selectedFrom1, selectedTo2); a.AddEdge(selectedFrom2, selectedTo1); cycleCount--; } var newRoute = new Tour(new int[] { problem.First }, problem.Last); var previous = problem.First; var next = nextArrayA[problem.First]; do { newRoute.InsertAfter(previous, next); previous = next; next = nextArrayA[next]; }while (next != Constants.NOT_SET && next != problem.First); var newFitness = 0.0f; foreach (var edge in newRoute.Pairs()) { newFitness = newFitness + weights[edge.From][edge.To]; } if (newRoute.Count == solution1.Count) { if (best == null || fitness > newFitness) { best = newRoute; fitness = newFitness; } generated++; } } if (best == null) { best = new Tour(new int[] { problem.First }, problem.Last); var previous = problem.First; var next = e_a[problem.First]; do { best.InsertAfter(previous, next); previous = next; next = e_a[next]; }while (next != Constants.NOT_SET && next != problem.First); fitness = 0.0f; foreach (var edge in best.Pairs()) { fitness = fitness + weights[edge.From][edge.To]; } } if (!originalProblem.Last.HasValue) { // original problem as an 'open' problem, convert to an 'open' route. best = new Tour(best, null); fitness = objective.Calculate(problem, best); } else if (originalProblem.First != originalProblem.Last) { // original problem was a problem with a fixed last point different from the first point. best.InsertAfter(System.Linq.Enumerable.Last(best), originalProblem.Last.Value); best = new Tour(best, problem.Last.Value); fitness = objective.Calculate(originalProblem, best); } return(best); }