Free cookie consent management tool by TermsFeed Policy Generator

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

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

improved UniformCrossover #382 (Uniform crossover operator for GP)

File size: 10.6 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 children = scope.SubScopes.Count / 2;
65      for (int i = 0; i < children; i++) {
66        IScope parent1 = scope.SubScopes[0];
67        scope.RemoveSubScope(parent1);
68        IScope parent2 = scope.SubScopes[0];
69        scope.RemoveSubScope(parent2);
70        IScope child = new Scope(i.ToString());
71        IOperation childInitOperation = Cross(scope, random, gardener, parent1, parent2, child);
72        initOperations.AddOperation(childInitOperation);
73        scope.AddSubScope(child);
74      }
75
76      return initOperations;
77    }
78
79    private IOperation Cross(IScope scope, MersenneTwister random, TreeGardener gardener, IScope parent1, IScope parent2, IScope child) {
80      IFunctionTree newTree = Cross(random, gardener, parent1, parent2);
81      Debug.Assert(gardener.IsValidTree(newTree));
82      int newTreeSize = newTree.Size;
83      int newTreeHeight = newTree.Height;
84      child.AddVariable(new HeuristicLab.Core.Variable(scope.TranslateName("FunctionTree"), newTree));
85      child.AddVariable(new HeuristicLab.Core.Variable(scope.TranslateName("TreeSize"), new IntData(newTreeSize)));
86      child.AddVariable(new HeuristicLab.Core.Variable(scope.TranslateName("TreeHeight"), new IntData(newTreeHeight)));
87
88      return null;
89    }
90
91
92    private IFunctionTree Cross(MersenneTwister random, TreeGardener gardener, IScope f, IScope g) {
93      IFunctionTree tree0 = f.GetVariableValue<IFunctionTree>("FunctionTree", false);
94      int tree0Height = f.GetVariableValue<IntData>("TreeHeight", false).Data;
95      int tree0Size = f.GetVariableValue<IntData>("TreeSize", false).Data;
96
97      IFunctionTree tree1 = g.GetVariableValue<IFunctionTree>("FunctionTree", false);
98      int tree1Height = g.GetVariableValue<IntData>("TreeHeight", false).Data;
99      int tree1Size = g.GetVariableValue<IntData>("TreeSize", false).Data;
100
101      List<CrossoverPoint> allowedCrossOverPoints = GetCrossOverPoints(gardener, tree0, tree1);
102      foreach (CrossoverPoint p in allowedCrossOverPoints) {
103        Debug.Assert(gardener.GetAllowedSubFunctions(p.parent0.Function, p.childIndex).Contains(p.parent1.SubTrees[p.childIndex].Function));
104        Debug.Assert(gardener.GetAllowedSubFunctions(p.parent1.Function, p.childIndex).Contains(p.parent0.SubTrees[p.childIndex].Function));
105      }
106      // iterate through the list of crossover points and swap nodes with p=0.5
107      foreach (CrossoverPoint crossoverPoint in allowedCrossOverPoints) {
108        if (random.NextDouble() < 0.5) {
109          IFunctionTree parent0 = crossoverPoint.parent0;
110          IFunctionTree parent1 = crossoverPoint.parent1;
111          IFunctionTree branch0 = crossoverPoint.parent0.SubTrees[crossoverPoint.childIndex];
112          IFunctionTree branch1 = crossoverPoint.parent1.SubTrees[crossoverPoint.childIndex];
113
114          // if we are at an internal node of the common region swap only the node but not the subtrees
115          if (branch0.SubTrees.Count == branch1.SubTrees.Count) {
116            if (parent0 != null) {
117              Debug.Assert(parent1 != null); Debug.Assert(branch0 != null); Debug.Assert(branch0 != null);
118              Debug.Assert(gardener.GetAllowedSubFunctions(parent0.Function, crossoverPoint.childIndex).Contains(branch1.Function));
119              Debug.Assert(gardener.GetAllowedSubFunctions(parent1.Function, crossoverPoint.childIndex).Contains(branch0.Function));
120              // we are not at the root => exchange the branches in the parent
121              parent0.RemoveSubTree(crossoverPoint.childIndex);
122              parent1.RemoveSubTree(crossoverPoint.childIndex);
123              parent0.InsertSubTree(crossoverPoint.childIndex, branch1);
124              parent1.InsertSubTree(crossoverPoint.childIndex, branch0);
125            }
126            // always exchange all children
127            List<IFunctionTree> branch0Children = new List<IFunctionTree>(branch0.SubTrees); // create backup lists
128            List<IFunctionTree> branch1Children = new List<IFunctionTree>(branch1.SubTrees);
129            while (branch0.SubTrees.Count > 0) branch0.RemoveSubTree(0); // remove all children
130            while (branch1.SubTrees.Count > 0) branch1.RemoveSubTree(0);
131            foreach (IFunctionTree subTree in branch1Children) {
132              Debug.Assert(gardener.GetAllowedSubFunctions(branch0.Function, branch0.SubTrees.Count).Contains(subTree.Function));
133              branch0.AddSubTree(subTree); // append children of branch1 to branch0
134            }
135            foreach (IFunctionTree subTree in branch0Children) {
136              Debug.Assert(gardener.GetAllowedSubFunctions(branch1.Function, branch1.SubTrees.Count).Contains(subTree.Function));
137              branch1.AddSubTree(subTree); // and vice versa
138            }
139          } else {
140            // If we are at a node at the border of the common region then exchange the whole branch.
141            // If we are at the root node and the number of children is already different we can't do anything now but
142            // at the end either tree0 or tree1 must be returned with p=0.5.
143
144            // However if we are not at the root => exchange the branches in the parent
145            if (parent0 != null) {
146              Debug.Assert(parent1 != null); Debug.Assert(branch0 != null); Debug.Assert(branch1 != null);
147              Debug.Assert(gardener.GetAllowedSubFunctions(parent0.Function, crossoverPoint.childIndex).Contains(branch1.Function));
148              Debug.Assert(gardener.GetAllowedSubFunctions(parent1.Function, crossoverPoint.childIndex).Contains(branch0.Function));
149              parent0.RemoveSubTree(crossoverPoint.childIndex);
150              parent1.RemoveSubTree(crossoverPoint.childIndex);
151              parent0.InsertSubTree(crossoverPoint.childIndex, branch1);
152              parent1.InsertSubTree(crossoverPoint.childIndex, branch0);
153            }
154          }
155        }
156      }
157      if (random.NextDouble() < 0.5) return tree0; else return tree1;
158    }
159
160    class CrossoverPoint {
161      public IFunctionTree parent0;
162      public IFunctionTree parent1;
163      public int childIndex;
164    }
165
166    private List<CrossoverPoint> GetCrossOverPoints(TreeGardener gardener, IFunctionTree branch0, IFunctionTree branch1) {
167      List<CrossoverPoint> results = new List<CrossoverPoint>();
168      if (branch0.SubTrees.Count != branch1.SubTrees.Count) return results;
169
170      for (int i = 0; i < branch0.SubTrees.Count; i++) {
171        // if the branches fit to the parent
172        if (gardener.GetAllowedSubFunctions(branch0.Function, i).Contains(branch1.SubTrees[i].Function) &&
173          gardener.GetAllowedSubFunctions(branch1.Function, i).Contains(branch0.SubTrees[i].Function)) {
174          // if the point is at the border of the common region we don't care about the children
175          // however if the point is not on the border of the common region we also have to check if
176          // the children of the branches fit together
177          bool fit = true;
178          if (branch0.SubTrees[i].SubTrees.Count == branch1.SubTrees[i].SubTrees.Count) {
179            for (int j = 0; j < branch0.SubTrees[i].SubTrees.Count; j++) {
180              fit = fit & gardener.GetAllowedSubFunctions(branch0.SubTrees[i].Function, j).Contains(branch1.SubTrees[i].SubTrees[j].Function);
181              fit = fit & gardener.GetAllowedSubFunctions(branch1.SubTrees[i].Function, j).Contains(branch0.SubTrees[i].SubTrees[j].Function);
182            }
183          }
184          if (fit) {
185            CrossoverPoint p = new CrossoverPoint();
186            p.childIndex = i;
187            p.parent0 = branch0;
188            p.parent1 = branch1;
189            results.Add(p);
190          }
191        }
192        results.AddRange(GetCrossOverPoints(gardener, branch0.SubTrees[i], branch1.SubTrees[i]));
193      }
194      return results;
195    }
196  }
197}
Note: See TracBrowser for help on using the repository browser.