Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HL-3.2-MonoMigration/HeuristicLab.StructureIdentification/Recombination/StandardCrossOver.cs @ 3199

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

renamed the crossover operator that we used as default in HL2 and HL3 to StandardCrossOver and implemented SizeFairCrossOver (#108)

File size: 13.3 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 HeuristicLab.Functions;
32using System.Diagnostics;
33
34namespace HeuristicLab.StructureIdentification {
35  public class StandardCrossOver : OperatorBase {
36    private const int MAX_RECOMBINATION_TRIES = 20;
37    public override string Description {
38      get {
39        return @"Takes two parent individuals P0 and P1 each. Selects a random node N0 of P0 and a random node N1 of P1.
40And replaces the branch with root0 N0 in P0 with N1 from P1 if the tree-size limits are not violated.
41When recombination with N0 and N1 would create a tree that is too large or invalid the operator randomly selects new N0 and N1
42until a valid configuration is found.";
43      }
44    }
45    public StandardCrossOver()
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("MaxTreeHeight", "The maximal allowed height of the tree", typeof(IntData), VariableKind.In));
50      AddVariableInfo(new VariableInfo("MaxTreeSize", "The maximal allowed size (number of nodes) of the tree", typeof(IntData), VariableKind.In));
51      AddVariableInfo(new VariableInfo("FunctionTree", "The tree to mutate", typeof(IFunctionTree), VariableKind.In | VariableKind.New));
52      AddVariableInfo(new VariableInfo("TreeSize", "The size (number of nodes) of the tree", typeof(IntData), VariableKind.New));
53      AddVariableInfo(new VariableInfo("TreeHeight", "The height of the tree", typeof(IntData), VariableKind.New));
54    }
55
56    public override IOperation Apply(IScope scope) {
57      MersenneTwister random = GetVariableValue<MersenneTwister>("Random", scope, true);
58      GPOperatorLibrary opLibrary = GetVariableValue<GPOperatorLibrary>("OperatorLibrary", scope, true);
59      int maxTreeHeight = GetVariableValue<IntData>("MaxTreeHeight", scope, true).Data;
60      int maxTreeSize = GetVariableValue<IntData>("MaxTreeSize", scope, true).Data;
61
62      TreeGardener gardener = new TreeGardener(random, opLibrary);
63
64      if((scope.SubScopes.Count % 2) != 0)
65        throw new InvalidOperationException("Number of parents is not even");
66
67      CompositeOperation initOperations = new CompositeOperation();
68
69      int children = scope.SubScopes.Count / 2;
70      for(int i = 0; i < children; i++) {
71        IScope parent1 = scope.SubScopes[0];
72        scope.RemoveSubScope(parent1);
73        IScope parent2 = scope.SubScopes[0];
74        scope.RemoveSubScope(parent2);
75        IScope child = new Scope(i.ToString());
76        IOperation childInitOperation = Cross(gardener, maxTreeSize, maxTreeHeight, scope, random, parent1, parent2, child);
77        initOperations.AddOperation(childInitOperation);
78        scope.AddSubScope(child);
79      }
80
81      return initOperations;
82    }
83
84    private IOperation Cross(TreeGardener gardener, int maxTreeSize, int maxTreeHeight,
85      IScope scope, MersenneTwister random, IScope parent1, IScope parent2, IScope child) {
86      List<IFunctionTree> newBranches;
87      IFunctionTree newTree = Cross(gardener, parent1, parent2,
88        random, maxTreeSize, maxTreeHeight, out newBranches);
89
90
91      int newTreeSize = newTree.Size;
92      int newTreeHeight = newTree.Height;
93      child.AddVariable(new HeuristicLab.Core.Variable(scope.TranslateName("FunctionTree"), newTree));
94      child.AddVariable(new HeuristicLab.Core.Variable(scope.TranslateName("TreeSize"), new IntData(newTreeSize)));
95      child.AddVariable(new HeuristicLab.Core.Variable(scope.TranslateName("TreeHeight"), new IntData(newTreeHeight)));
96
97      // check if the new tree is valid and if the height of is still in the allowed bounds (we are not so strict for the max-size)
98      Debug.Assert(gardener.IsValidTree(newTree) && newTreeHeight <= maxTreeHeight && newTreeSize <= maxTreeSize);
99      return gardener.CreateInitializationOperation(newBranches, child);
100    }
101
102
103    private IFunctionTree Cross(TreeGardener gardener, IScope f, IScope g, MersenneTwister random, int maxTreeSize, int maxTreeHeight, out List<IFunctionTree> newBranches) {
104      IFunctionTree tree0 = f.GetVariableValue<IFunctionTree>("FunctionTree", false);
105      int tree0Height = f.GetVariableValue<IntData>("TreeHeight", false).Data;
106      int tree0Size = f.GetVariableValue<IntData>("TreeSize", false).Data;
107
108      IFunctionTree tree1 = g.GetVariableValue<IFunctionTree>("FunctionTree", false);
109      int tree1Height = g.GetVariableValue<IntData>("TreeHeight", false).Data;
110      int tree1Size = g.GetVariableValue<IntData>("TreeSize", false).Data;
111
112      if(tree0Size == 1 && tree1Size == 1) {
113        return CombineTerminals(gardener, tree0, tree1, random, maxTreeHeight, out newBranches);
114      } else {
115        newBranches = new List<IFunctionTree>();
116
117        // we are going to insert tree1 into tree0 at a random place so we have to make sure that tree0 is not a terminal
118        // in case both trees are higher than 1 we swap the trees with probability 50%
119        if(tree0Height == 1 || (tree1Height > 1 && random.Next(2) == 0)) {
120          IFunctionTree tmp = tree0; tree0 = tree1; tree1 = tmp;
121          int tmpHeight = tree0Height; tree0Height = tree1Height; tree1Height = tmpHeight;
122          int tmpSize = tree0Size; tree0Size = tree1Size; tree1Size = tmpSize;
123        }
124
125        // save the roots because later on we change tree0 and tree1 while searching a valid tree configuration
126        IFunctionTree root0 = tree0;
127        IFunctionTree root1 = tree1;
128        int root0Height = tree0Height;
129        int root1Height = tree1Height;
130        int rootSize = tree0Size;
131
132        // select a random suboperators of the two trees at a random level
133        int tree0Level = random.Next(root0Height - 1); // since we checked before that the height of tree0 is > 1 this is OK
134        int tree1Level = random.Next(root1Height);
135        tree0 = gardener.GetRandomBranch(tree0, tree0Level);
136        tree1 = gardener.GetRandomBranch(tree1, tree1Level);
137
138        // recalculate the size and height of tree1 (the one that we want to insert) because we need to check constraints later on
139        tree1Size = tree1.Size;
140        tree1Height = tree1.Height;
141
142        List<int> possibleChildIndices = new List<int>();
143
144        // Now tree0 is supposed to take tree1 as one if its children. If this is not possible,
145        // then go down in either of the two trees as far as possible. If even then it is not possible
146        // to merge the trees then throw an exception
147        // find the list of allowed indices (regarding allowed sub-trees, maxTreeSize and maxTreeHeight)
148        for(int i = 0; i < tree0.SubTrees.Count; i++) {
149          int subTreeSize = tree0.SubTrees[i].Size;
150
151          // the index is ok when the function is allowed as sub-tree and we don't violate the maxSize and maxHeight constraints
152          if(gardener.GetAllowedSubFunctions(tree0.Function, i).Contains(tree1.Function) &&
153            rootSize - subTreeSize + tree1Size < maxTreeSize &&
154            tree0Level + tree1Height < maxTreeHeight) {
155            possibleChildIndices.Add(i);
156          }
157        }
158        int tries = 0;
159        while(possibleChildIndices.Count == 0) {
160          if(tries++ > MAX_RECOMBINATION_TRIES) {
161            if(random.Next() > 0.5) return root1;
162            else return root0;
163          }
164          // we couln't find a possible configuration given the current tree0 and tree1
165          // possible reasons for this are:
166          //  - tree1 is not allowed as sub-tree of tree0
167          //  - appending tree1 as child of tree0 would create a tree that exceedes the maxTreeHeight
168          //  - replacing any child of tree0 with tree1 woulde create a tree that exceedes the maxTeeSize
169          // thus we just try until we find a valid configuration
170
171          tree0Level = random.Next(root0Height - 1);
172          tree1Level = random.Next(root1Height);
173          tree0 = gardener.GetRandomBranch(root0, tree0Level);
174          tree1 = gardener.GetRandomBranch(root1, tree1Level);
175
176          // recalculate the size and height of tree1 (the one that we want to insert) because we need to check constraints later on
177          tree1Size = tree1.Size;
178          tree1Height = tree1.Height;
179          // recalculate the list of possible indices
180          possibleChildIndices.Clear();
181          for(int i = 0; i < tree0.SubTrees.Count; i++) {
182            int subTreeSize = tree0.SubTrees[i].Size;
183
184            // when the function is allowed as sub-tree and we don't violate the maxSize and maxHeight constraints
185            // the index is ok
186            if(gardener.GetAllowedSubFunctions(tree0.Function, i).Contains(tree1.Function) &&
187              rootSize - subTreeSize + tree1Size < maxTreeSize &&
188              tree0Level + tree1Height < maxTreeHeight) {
189              possibleChildIndices.Add(i);
190            }
191          }
192        }
193        // replace the existing sub-tree at a random index in tree0 with tree1
194        int selectedIndex = possibleChildIndices[random.Next(possibleChildIndices.Count)];
195        tree0.RemoveSubTree(selectedIndex);
196        tree0.InsertSubTree(selectedIndex, tree1);
197        return root0;
198      }
199    }
200
201
202    // take f and g and create a tree that has f and g as sub-trees
203    // example
204    //       O
205    //      /|\
206    //     g 2 f
207    //
208    private IFunctionTree CombineTerminals(TreeGardener gardener, IFunctionTree f, IFunctionTree g, MersenneTwister random, int maxTreeHeight, out List<IFunctionTree> newBranches) {
209      newBranches = new List<IFunctionTree>();
210      // determine the set of possible parent functions
211      ICollection<IFunction> possibleParents = gardener.GetPossibleParents(new List<IFunction>() { f.Function, g.Function });
212      if(possibleParents.Count == 0) throw new InvalidProgramException();
213      // and select a random one
214      IFunctionTree parent = possibleParents.ElementAt(random.Next(possibleParents.Count())).GetTreeNode();
215
216      int nSlots = Math.Max(2, parent.Function.MinArity);
217      // determine which slot can take which sub-trees
218      List<IFunctionTree>[] slots = new List<IFunctionTree>[nSlots];
219      for(int slot = 0; slot < nSlots; slot++) {
220        ICollection<IFunction> allowedSubFunctions = gardener.GetAllowedSubFunctions(parent.Function, slot);
221        List<IFunctionTree> allowedTrees = new List<IFunctionTree>();
222        if(allowedSubFunctions.Contains(f.Function)) allowedTrees.Add(f);
223        if(allowedSubFunctions.Contains(g.Function)) allowedTrees.Add(g);
224        slots[slot] = allowedTrees;
225      }
226      // fill the slots in the order of degrees of freedom
227      int[] slotSequence = Enumerable.Range(0, slots.Count()).OrderBy(slot => slots[slot].Count()).ToArray();
228
229      // tmp arry to store the tree for each sub-tree slot of the parent
230      IFunctionTree[] selectedFunctionTrees = new IFunctionTree[nSlots];
231
232      // fill the sub-tree slots of the parent starting with the slots that can take potentially both functions (f and g)
233      for(int i = 0; i < slotSequence.Length; i++) {
234        int slot = slotSequence[i];
235        List<IFunctionTree> allowedTrees = slots[slot];
236        // when neither f nor g fit into the slot => create a new random tree
237        if(allowedTrees.Count() == 0) {
238          var allowedFunctions = gardener.GetAllowedSubFunctions(parent.Function, slot);
239          selectedFunctionTrees[slot] = gardener.CreateRandomTree(allowedFunctions, 1, 1);
240          newBranches.AddRange(gardener.GetAllSubTrees(selectedFunctionTrees[slot]));
241        } else {
242          // select randomly which tree to insert into this slot
243          IFunctionTree selectedTree = allowedTrees[random.Next(allowedTrees.Count())];
244          selectedFunctionTrees[slot] = selectedTree;
245          // remove the tree that we used in this slot from following function-sets
246          for(int j = i + 1; j < slotSequence.Length; j++) {
247            int otherSlot = slotSequence[j];
248            slots[otherSlot].Remove(selectedTree);
249          }
250        }
251      }
252      // actually append the sub-trees to the parent tree
253      for(int i = 0; i < selectedFunctionTrees.Length; i++) {
254        parent.InsertSubTree(i, selectedFunctionTrees[i]);
255      }
256
257      return parent;
258    }
259  }
260}
Note: See TracBrowser for help on using the repository browser.