Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.GP/Recombination/UniformCrossover.cs @ 807

Last change on this file since 807 was 807, checked in by gkronber, 16 years ago

Changed UniformCrossover to create two new trees from two parents. This shouldn't alter the search behavior because UniformCrossover is symmetric. #382 (Uniform crossover operator for GP)

File size: 11.1 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2008 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Linq;
25using System.Text;
26using HeuristicLab.Core;
27using HeuristicLab.Operators;
28using HeuristicLab.Random;
29using HeuristicLab.Data;
30using HeuristicLab.Constraints;
31using System.Diagnostics;
32
33namespace HeuristicLab.GP {
34  /// <summary>
35  /// Implementation of a homologous uniform crossover operator as described in:
36  /// R. Poli and W. B. Langdon.  On the Search Properties of Different Crossover Operators in Genetic Programming.
37  /// In Proceedings of Genetic Programming '98, Madison, Wisconsin, 1998.
38  /// </summary>
39  public class UniformCrossover : OperatorBase {
40    public override string Description {
41      get {
42        return @"Uniform crossover as defined by Poli and Langdon";
43      }
44    }
45    public UniformCrossover()
46      : base() {
47      AddVariableInfo(new VariableInfo("Random", "Pseudo random number generator", typeof(MersenneTwister), VariableKind.In));
48      AddVariableInfo(new VariableInfo("OperatorLibrary", "The operator library containing all available operators", typeof(GPOperatorLibrary), VariableKind.In));
49      AddVariableInfo(new VariableInfo("FunctionTree", "The tree to mutate", typeof(IFunctionTree), VariableKind.In | VariableKind.New));
50      AddVariableInfo(new VariableInfo("TreeSize", "The size (number of nodes) of the tree", typeof(IntData), VariableKind.In | VariableKind.New));
51      AddVariableInfo(new VariableInfo("TreeHeight", "The height of the tree", typeof(IntData), VariableKind.In | VariableKind.New));
52    }
53
54    public override IOperation Apply(IScope scope) {
55      MersenneTwister random = GetVariableValue<MersenneTwister>("Random", scope, true);
56      GPOperatorLibrary opLibrary = GetVariableValue<GPOperatorLibrary>("OperatorLibrary", scope, true);
57      TreeGardener gardener = new TreeGardener(random, opLibrary);
58
59      if ((scope.SubScopes.Count % 2) != 0)
60        throw new InvalidOperationException("Number of parents is not even");
61
62      CompositeOperation initOperations = new CompositeOperation();
63
64      int crossoverEvents = scope.SubScopes.Count / 2;
65      for (int i = 0; i < crossoverEvents; i++) {
66        IScope parent1 = scope.SubScopes[0];
67        scope.RemoveSubScope(parent1);
68        IScope parent2 = scope.SubScopes[0];
69        scope.RemoveSubScope(parent2);
70        IScope child0 = new Scope((i*2).ToString());
71        IScope child1 = new Scope((i*2+1).ToString());
72        Cross(scope, random, gardener, parent1, parent2, child0, child1);
73        scope.AddSubScope(child0);
74        scope.AddSubScope(child1);
75      }
76
77      return null;
78    }
79
80    private void Cross(IScope scope, MersenneTwister random, TreeGardener gardener, IScope parent1, IScope parent2, IScope child0, IScope child1) {
81      IFunctionTree childTree0;
82      IFunctionTree childTree1;
83      Cross(random, gardener, parent1, parent2, out childTree0, out childTree1);
84      Debug.Assert(gardener.IsValidTree(childTree0));
85      Debug.Assert(gardener.IsValidTree(childTree1));
86      child0.AddVariable(new HeuristicLab.Core.Variable(scope.TranslateName("FunctionTree"), childTree0));
87      child0.AddVariable(new HeuristicLab.Core.Variable(scope.TranslateName("TreeSize"), new IntData(childTree0.Size)));
88      child0.AddVariable(new HeuristicLab.Core.Variable(scope.TranslateName("TreeHeight"), new IntData(childTree0.Height)));
89
90      child1.AddVariable(new HeuristicLab.Core.Variable(scope.TranslateName("FunctionTree"), childTree1));
91      child1.AddVariable(new HeuristicLab.Core.Variable(scope.TranslateName("TreeSize"), new IntData(childTree1.Size)));
92      child1.AddVariable(new HeuristicLab.Core.Variable(scope.TranslateName("TreeHeight"), new IntData(childTree1.Height)));
93    }
94
95
96    private void Cross(MersenneTwister random, TreeGardener gardener, IScope f, IScope g, out IFunctionTree child0, out IFunctionTree child1) {
97      IFunctionTree tree0 = f.GetVariableValue<IFunctionTree>("FunctionTree", false);
98      int tree0Height = f.GetVariableValue<IntData>("TreeHeight", false).Data;
99      int tree0Size = f.GetVariableValue<IntData>("TreeSize", false).Data;
100
101      IFunctionTree tree1 = g.GetVariableValue<IFunctionTree>("FunctionTree", false);
102      int tree1Height = g.GetVariableValue<IntData>("TreeHeight", false).Data;
103      int tree1Size = g.GetVariableValue<IntData>("TreeSize", false).Data;
104
105      List<CrossoverPoint> allowedCrossOverPoints = GetCrossOverPoints(gardener, tree0, tree1);
106      foreach (CrossoverPoint p in allowedCrossOverPoints) {
107        Debug.Assert(gardener.GetAllowedSubFunctions(p.parent0.Function, p.childIndex).Contains(p.parent1.SubTrees[p.childIndex].Function));
108        Debug.Assert(gardener.GetAllowedSubFunctions(p.parent1.Function, p.childIndex).Contains(p.parent0.SubTrees[p.childIndex].Function));
109      }
110      // iterate through the list of crossover points and swap nodes with p=0.5
111      foreach (CrossoverPoint crossoverPoint in allowedCrossOverPoints) {
112        if (random.NextDouble() < 0.5) {
113          IFunctionTree parent0 = crossoverPoint.parent0;
114          IFunctionTree parent1 = crossoverPoint.parent1;
115          IFunctionTree branch0 = crossoverPoint.parent0.SubTrees[crossoverPoint.childIndex];
116          IFunctionTree branch1 = crossoverPoint.parent1.SubTrees[crossoverPoint.childIndex];
117
118          // if we are at an internal node of the common region swap only the node but not the subtrees
119          if (branch0.SubTrees.Count == branch1.SubTrees.Count) {
120            if (parent0 != null) {
121              Debug.Assert(parent1 != null); Debug.Assert(branch0 != null); Debug.Assert(branch0 != null);
122              Debug.Assert(gardener.GetAllowedSubFunctions(parent0.Function, crossoverPoint.childIndex).Contains(branch1.Function));
123              Debug.Assert(gardener.GetAllowedSubFunctions(parent1.Function, crossoverPoint.childIndex).Contains(branch0.Function));
124              // we are not at the root => exchange the branches in the parent
125              parent0.RemoveSubTree(crossoverPoint.childIndex);
126              parent1.RemoveSubTree(crossoverPoint.childIndex);
127              parent0.InsertSubTree(crossoverPoint.childIndex, branch1);
128              parent1.InsertSubTree(crossoverPoint.childIndex, branch0);
129            }
130            // always exchange all children
131            List<IFunctionTree> branch0Children = new List<IFunctionTree>(branch0.SubTrees); // create backup lists
132            List<IFunctionTree> branch1Children = new List<IFunctionTree>(branch1.SubTrees);
133            while (branch0.SubTrees.Count > 0) branch0.RemoveSubTree(0); // remove all children
134            while (branch1.SubTrees.Count > 0) branch1.RemoveSubTree(0);
135            foreach (IFunctionTree subTree in branch1Children) {
136              Debug.Assert(gardener.GetAllowedSubFunctions(branch0.Function, branch0.SubTrees.Count).Contains(subTree.Function));
137              branch0.AddSubTree(subTree); // append children of branch1 to branch0
138            }
139            foreach (IFunctionTree subTree in branch0Children) {
140              Debug.Assert(gardener.GetAllowedSubFunctions(branch1.Function, branch1.SubTrees.Count).Contains(subTree.Function));
141              branch1.AddSubTree(subTree); // and vice versa
142            }
143          } else {
144            // If we are at a node at the border of the common region then exchange the whole branch.
145            // If we are at the root node and the number of children is already different we can't do anything now but
146            // at the end either tree0 or tree1 must be returned with p=0.5.
147
148            // However if we are not at the root => exchange the branches in the parent
149            if (parent0 != null) {
150              Debug.Assert(parent1 != null); Debug.Assert(branch0 != null); Debug.Assert(branch1 != null);
151              Debug.Assert(gardener.GetAllowedSubFunctions(parent0.Function, crossoverPoint.childIndex).Contains(branch1.Function));
152              Debug.Assert(gardener.GetAllowedSubFunctions(parent1.Function, crossoverPoint.childIndex).Contains(branch0.Function));
153              parent0.RemoveSubTree(crossoverPoint.childIndex);
154              parent1.RemoveSubTree(crossoverPoint.childIndex);
155              parent0.InsertSubTree(crossoverPoint.childIndex, branch1);
156              parent1.InsertSubTree(crossoverPoint.childIndex, branch0);
157            }
158          }
159        }
160      }
161      if (random.NextDouble() < 0.5) {
162        child0 = tree0;
163        child1 = tree1;
164      } else {
165        child0 = tree1;
166        child1 = tree0;
167      }
168    }
169
170    class CrossoverPoint {
171      public IFunctionTree parent0;
172      public IFunctionTree parent1;
173      public int childIndex;
174    }
175
176    private List<CrossoverPoint> GetCrossOverPoints(TreeGardener gardener, IFunctionTree branch0, IFunctionTree branch1) {
177      List<CrossoverPoint> results = new List<CrossoverPoint>();
178      if (branch0.SubTrees.Count != branch1.SubTrees.Count) return results;
179
180      for (int i = 0; i < branch0.SubTrees.Count; i++) {
181        // if the branches fit to the parent
182        if (gardener.GetAllowedSubFunctions(branch0.Function, i).Contains(branch1.SubTrees[i].Function) &&
183          gardener.GetAllowedSubFunctions(branch1.Function, i).Contains(branch0.SubTrees[i].Function)) {
184          // if the point is at the border of the common region we don't care about the children
185          // however if the point is not on the border of the common region we also have to check if
186          // the children of the branches fit together
187          bool fit = true;
188          if (branch0.SubTrees[i].SubTrees.Count == branch1.SubTrees[i].SubTrees.Count) {
189            for (int j = 0; j < branch0.SubTrees[i].SubTrees.Count; j++) {
190              fit = fit & gardener.GetAllowedSubFunctions(branch0.SubTrees[i].Function, j).Contains(branch1.SubTrees[i].SubTrees[j].Function);
191              fit = fit & gardener.GetAllowedSubFunctions(branch1.SubTrees[i].Function, j).Contains(branch0.SubTrees[i].SubTrees[j].Function);
192            }
193          }
194          if (fit) {
195            CrossoverPoint p = new CrossoverPoint();
196            p.childIndex = i;
197            p.parent0 = branch0;
198            p.parent1 = branch1;
199            results.Add(p);
200          }
201        }
202        results.AddRange(GetCrossOverPoints(gardener, branch0.SubTrees[i], branch1.SubTrees[i]));
203      }
204      return results;
205    }
206  }
207}
Note: See TracBrowser for help on using the repository browser.