Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.StructureIdentification/Recombination/SizeFairCrossOver.cs @ 324

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

added Size and Height properties to interface IFunctionTree and removed the helper methods from TreeGardener (specific implementations of size and height properties in classes implementing IFunctionTree can be more efficient than the general functions in TreeGardener)

File size: 13.7 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 SizeFairCrossOver : OperatorBase {
36
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 root 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 the operator randomly either goes
42up in P0 (parent of N0) or down in P1 (random child of N1) until a valid configuration is found.";
43      }
44    }
45    public SizeFairCrossOver()
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 size of is still in the allowed bounds
98      Debug.Assert(gardener.IsValidTree(newTree) &&
99        newTreeHeight <= maxTreeHeight && newTreeSize <= maxTreeSize);
100      return gardener.CreateInitializationOperation(newBranches, child);
101    }
102
103
104    private IFunctionTree Cross(TreeGardener gardener, IScope f, IScope g, MersenneTwister random, int maxTreeSize, int maxTreeHeight, out List<IFunctionTree> newBranches) {
105      IFunctionTree tree0 = f.GetVariableValue<IFunctionTree>("FunctionTree", false);
106      int tree0Height = f.GetVariableValue<IntData>("TreeHeight", false).Data;
107      int tree0Size = f.GetVariableValue<IntData>("TreeSize", false).Data;
108
109      IFunctionTree tree1 = g.GetVariableValue<IFunctionTree>("FunctionTree", false);
110      int tree1Height = g.GetVariableValue<IntData>("TreeHeight", false).Data;
111      int tree1Size = g.GetVariableValue<IntData>("TreeSize", false).Data;
112
113      if(tree0Size == 1 && tree1Size == 1) {
114        return CombineTerminals(gardener, tree0, tree1, random, maxTreeHeight, out newBranches);
115      } else {
116        // we are going to insert tree1 into tree0 at a random place so we have to make sure that tree0 is not a terminal
117        // in case both trees are higher than 1 we swap the trees with probability 50%
118        if(tree0Height == 1 || (tree1Height > 1 && random.Next(2) == 0)) {
119          IFunctionTree tmp = tree0; tree0 = tree1; tree1 = tmp;
120          int tmpHeight = tree0Height; tree0Height = tree1Height; tree1Height = tmpHeight;
121          int tmpSize = tree0Size; tree0Size = tree1Size; tree1Size = tmpSize;
122        }
123
124        // save the root because later on we change tree0 and tree1 while searching a valid tree configuration
125        IFunctionTree root = tree0;
126        int rootSize = tree0Size;
127
128        // select a random suboperators of the two trees at a random level
129        int tree0Level = random.Next(tree0Height - 1); // since we checked before that the height of tree0 is > 1 this is OK
130        int tree1Level = random.Next(tree1Height);
131        tree0 = gardener.GetRandomBranch(tree0, tree0Level);
132        tree1 = gardener.GetRandomBranch(tree1, tree1Level);
133
134        // recalculate the size and height of tree1 (the one that we want to insert) because we need to check constraints later on
135        tree1Size = tree1.Size;
136        tree1Height = tree1.Height;
137
138        List<int> possibleChildIndices = new List<int>();
139
140        // Now tree0 is supposed to take tree1 as one if its children. If this is not possible,
141        // then go down in either of the two trees as far as possible. If even then it is not possible
142        // to merge the trees then throw an exception
143        // find the list of allowed indices (regarding allowed sub-trees, maxTreeSize and maxTreeHeight)
144        for(int i = 0; i < tree0.SubTrees.Count; i++) {
145          int subTreeSize = tree0.SubTrees[i].Size;
146
147          // the index is ok when the function is allowed as sub-tree and we don't violate the maxSize and maxHeight constraints
148          if(gardener.GetAllowedSubFunctions(tree0.Function, i).Contains(tree1.Function) &&
149            rootSize - subTreeSize + tree1Size < maxTreeSize &&
150            tree0Level + tree1Height < maxTreeHeight) {
151            possibleChildIndices.Add(i);
152          }
153        }
154
155        while(possibleChildIndices.Count == 0) {
156          // we couln't find a possible configuration given the current tree0 and tree1
157          // possible reasons for this are:
158          //  - tree1 is not allowed as sub-tree of tree0
159          //  - appending tree1 as child of tree0 would create a tree that exceedes the maxTreeHeight
160          //  - replacing any child of tree0 with tree1 woulde create a tree that exceedes the maxTeeSize
161          // thus we have to either:
162          //  - go up in tree0 => the insert position allows larger trees
163          //  - go down in tree1 => the tree that is inserted becomes smaller
164          //  - however we have to get lucky to solve the 'allowed sub-trees' problem
165          if(tree1Height == 1 || (tree0Level > 0 && random.Next(2) == 0)) {
166            // go up in tree0
167            tree0Level--;
168            tree0 = gardener.GetRandomBranch(root, tree0Level);
169          } else if(tree1.SubTrees.Count > 0) {
170            // go down in node2:
171            tree1 = tree1.SubTrees[random.Next(tree1.SubTrees.Count)];
172            tree1Size = tree1.Size;
173            tree1Height = tree1.Height;
174          } else {
175            // could neither go up or down ... don't know what to do ... give up
176            throw new InvalidProgramException();
177          }
178
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
194        // no possible configuration found this indicates that there is a bigger problem
195        if(possibleChildIndices.Count == 0) {
196          throw new InvalidProgramException();
197        }
198
199        // replace the existing sub-tree at a random index in tree0 with tree1
200        int selectedIndex = possibleChildIndices[random.Next(possibleChildIndices.Count)];
201        tree0.RemoveSubTree(selectedIndex);
202        tree0.InsertSubTree(selectedIndex, tree1);
203
204        // no new operators where needed
205        newBranches = new List<IFunctionTree>();
206        return root;
207      }
208    }
209
210
211    // take f and g and create a tree that has f and g as sub-trees
212    // example
213    //       O
214    //      /|\
215    //     g 2 f
216    //
217    private IFunctionTree CombineTerminals(TreeGardener gardener, IFunctionTree f, IFunctionTree g, MersenneTwister random, int maxTreeHeight, out List<IFunctionTree> newBranches) {
218      newBranches = new List<IFunctionTree>();
219      // determine the set of possible parent functions
220      ICollection<IFunction> possibleParents = gardener.GetPossibleParents(new List<IFunction>() { f.Function, g.Function });
221      if(possibleParents.Count == 0) throw new InvalidProgramException();
222      // and select a random one
223      IFunctionTree parent = possibleParents.ElementAt(random.Next(possibleParents.Count())).GetTreeNode();
224
225      int minArity;
226      int maxArity;
227      gardener.GetMinMaxArity(parent.Function, out minArity, out maxArity);
228      int nSlots = Math.Max(2, minArity);
229      // determine which slot can take which sub-trees
230      List<IFunctionTree>[] slots = new List<IFunctionTree>[nSlots];
231      for(int slot = 0; slot < nSlots; slot++) {
232        ICollection<IFunction> allowedSubFunctions = gardener.GetAllowedSubFunctions(parent.Function, slot);
233        List<IFunctionTree> allowedTrees = new List<IFunctionTree>();
234        if(allowedSubFunctions.Contains(f.Function)) allowedTrees.Add(f);
235        if(allowedSubFunctions.Contains(g.Function)) allowedTrees.Add(g);
236        slots[slot] = allowedTrees;
237      }
238      // fill the slots in the order of degrees of freedom
239      int[] slotSequence = Enumerable.Range(0, slots.Count()).OrderBy(slot => slots[slot].Count()).ToArray();
240
241      // tmp arry to store the tree for each sub-tree slot of the parent
242      IFunctionTree[] selectedFunctionTrees = new IFunctionTree[nSlots];
243
244      // fill the sub-tree slots of the parent starting with the slots that can take potentially both functions (f and g)
245      for(int i = 0; i < slotSequence.Length; i++) {
246        int slot = slotSequence[i];
247        List<IFunctionTree> allowedTrees = slots[slot];
248        // when neither f nor g fit into the slot => create a new random tree
249        if(allowedTrees.Count() == 0) {
250          var allowedFunctions = gardener.GetAllowedSubFunctions(parent.Function, slot);
251          selectedFunctionTrees[slot] = gardener.CreateRandomTree(allowedFunctions, 1, 1, true);
252          newBranches.AddRange(gardener.GetAllSubTrees(selectedFunctionTrees[slot]));
253        } else {
254          // select randomly which tree to insert into this slot
255          IFunctionTree selectedTree = allowedTrees[random.Next(allowedTrees.Count())];
256          selectedFunctionTrees[slot] = selectedTree;
257          // remove the tree that we used in this slot from following function-sets
258          for(int j = i + 1; j < slotSequence.Length; j++) {
259            int otherSlot = slotSequence[j];
260            slots[otherSlot].Remove(selectedTree);
261          }
262        }
263      }
264      // actually append the sub-trees to the parent tree
265      for(int i = 0; i < selectedFunctionTrees.Length; i++) {
266        parent.InsertSubTree(i, selectedFunctionTrees[i]);
267      }
268
269      return parent;
270    }
271  }
272}
Note: See TracBrowser for help on using the repository browser.