Free cookie consent management tool by TermsFeed Policy Generator

source: branches/GP-Refactoring-713/sources/HeuristicLab.GP/3.3/Manipulation/ChangeNodeTypeManipulation.cs @ 2202

Last change on this file since 2202 was 2202, checked in by gkronber, 15 years ago

Created a branch for #713

File size: 10.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 System.Diagnostics;
32
33namespace HeuristicLab.GP {
34  public class ChangeNodeTypeManipulation : OperatorBase {
35
36    public override string Description {
37      get {
38        return @"This manipulation operator selects a random tree-node and changes the function type.
39If this leads to a constraint-violation (wrong number or type of sub-trees) the sub-trees are repaired
40resulting in a valid tree again.";
41      }
42    }
43
44    public ChangeNodeTypeManipulation()
45      : base() {
46      AddVariableInfo(new VariableInfo("Random", "Uniform random number generator", typeof(MersenneTwister), VariableKind.In));
47      AddVariableInfo(new VariableInfo("OperatorLibrary", "The operator library containing all available operators", typeof(FunctionLibrary), VariableKind.In));
48      AddVariableInfo(new VariableInfo("MaxTreeHeight", "The maximal allowed height of the tree", typeof(IntData), VariableKind.In));
49      AddVariableInfo(new VariableInfo("MaxTreeSize", "The maximal allowed size (number of nodes) of the tree", typeof(IntData), VariableKind.In));
50      AddVariableInfo(new VariableInfo("FunctionTree", "The tree to mutate", typeof(IFunctionTree), VariableKind.In | VariableKind.Out));
51      AddVariableInfo(new VariableInfo("TreeSize", "The size (number of nodes) of the tree", typeof(IntData), VariableKind.In | VariableKind.Out));
52      AddVariableInfo(new VariableInfo("TreeHeight", "The height of the tree", typeof(IntData), VariableKind.In | VariableKind.Out));
53    }
54
55
56    public override IOperation Apply(IScope scope) {
57      IFunctionTree root = GetVariableValue<IFunctionTree>("FunctionTree", scope, false);
58      MersenneTwister random = GetVariableValue<MersenneTwister>("Random", scope, true);
59      FunctionLibrary library = GetVariableValue<FunctionLibrary>("OperatorLibrary", scope, true);
60      IntData treeSize = GetVariableValue<IntData>("TreeSize", scope, false);
61      IntData treeHeight = GetVariableValue<IntData>("TreeHeight", scope, false);
62      int maxTreeSize = GetVariableValue<IntData>("MaxTreeSize", scope, true).Data;
63      int maxTreeHeight = GetVariableValue<IntData>("MaxTreeHeight", scope, true).Data;
64      TreeGardener gardener = new TreeGardener(random, library);
65      IFunctionTree parent = gardener.GetRandomParentNode(root);
66      IFunctionTree selectedChild;
67      int selectedChildIndex;
68      if(parent == null) {
69        selectedChildIndex = 0;
70        selectedChild = root;
71      } else {
72        selectedChildIndex = random.Next(parent.SubTrees.Count);
73        selectedChild = parent.SubTrees[selectedChildIndex];
74      }
75
76      if(selectedChild.SubTrees.Count == 0) {
77        IFunctionTree newTerminal = ChangeTerminalType(parent, selectedChild, selectedChildIndex, gardener, random);
78        if(parent == null) {
79          // no parent means the new child is the initial operator
80          // and we have to update the value in the variable
81          scope.GetVariable(scope.TranslateName("FunctionTree")).Value = newTerminal;
82        } else {
83          parent.RemoveSubTree(selectedChildIndex);
84          parent.InsertSubTree(selectedChildIndex, newTerminal);
85          // updating the variable is not necessary because it stays the same
86        }
87        Debug.Assert(gardener.IsValidTree(root));
88        // size and height stays the same when changing a terminal so no need to update the variables
89        // schedule an operation to initialize the new terminal
90        return gardener.CreateInitializationOperation(gardener.GetAllSubTrees(newTerminal), scope);
91      } else {
92        List<IFunctionTree> uninitializedBranches;
93        IFunctionTree newFunctionTree = ChangeFunctionType(parent, selectedChild, selectedChildIndex, gardener, random, out uninitializedBranches);
94        // in rare cases the function creates a tree that breaks the size limits
95        // calculate the height and size difference and
96        // check if the size of the new tree is still in the allowed bounds
97        int oldChildSize = selectedChild.Size;
98        int oldChildHeight = selectedChild.Height;
99        int newChildSize = newFunctionTree.Size;
100        int newChildHeight = newFunctionTree.Height;
101        if((treeHeight.Data - oldChildHeight) + newChildHeight > maxTreeHeight ||
102          (treeSize.Data - oldChildSize) + newChildSize > maxTreeSize) {
103          // if size-constraints are violated don't change anything
104          return null;
105        }
106        if(parent == null) {
107          // no parent means the new function is the initial operator
108          // and we have to update the value in the variable
109          scope.GetVariable(scope.TranslateName("FunctionTree")).Value = newFunctionTree;
110          root = newFunctionTree;
111        } else {
112          // remove the old child
113          parent.RemoveSubTree(selectedChildIndex);
114          // add the new child as sub-tree of parent
115          parent.InsertSubTree(selectedChildIndex, newFunctionTree);
116        }
117        // update size and height
118        treeSize.Data = (treeSize.Data - oldChildSize) + newChildSize;
119        treeHeight.Data = root.Height; // must recalculate height because we can't know wether the manipulated branch was the deepest branch
120        // check if whole tree is ok
121        Debug.Assert(gardener.IsValidTree(root));
122        // return a composite operation that initializes all created sub-trees
123        return gardener.CreateInitializationOperation(uninitializedBranches, scope);
124      }
125    }
126 
127
128    private IFunctionTree ChangeTerminalType(IFunctionTree parent, IFunctionTree child, int childIndex, TreeGardener gardener, MersenneTwister random) {
129      IList<IFunction> allowedTerminals;
130      if (parent == null) {
131        allowedTerminals = gardener.Terminals;
132      } else {
133        allowedTerminals = new List<IFunction>();
134        var allAllowedChildren = gardener.GetAllowedSubFunctions(parent.Function, childIndex);
135        foreach(IFunction c in allAllowedChildren) {
136          if(gardener.IsTerminal(c)) allowedTerminals.Add(c);
137        }
138      }
139      // selecting from the terminals should always work since the current child was also a terminal
140      // so in the worst case we will just create a new terminal of the same type again.
141      return gardener.CreateRandomTree(allowedTerminals, 1, 1);
142    }
143
144    private IFunctionTree ChangeFunctionType(IFunctionTree parent, IFunctionTree child, int childIndex, TreeGardener gardener, MersenneTwister random,
145      out List<IFunctionTree> uninitializedBranches) {
146      // since there are subtrees, we have to check which
147      // and how many of the existing subtrees we can reuse.
148      // first let's choose the function we want to use instead of the old child. For this we have to determine the
149      // pool of allowed functions based on constraints of the parent if there is one.
150      List<IFunction> allowedFunctions = new List<IFunction>(gardener.GetAllowedSubFunctions(parent != null ? parent.Function : null, childIndex));
151      // try to make a tree with the same arity as the old child.
152      int actualArity = child.SubTrees.Count;
153      // create a new tree-node for a randomly selected function
154      IFunction selectedFunction = allowedFunctions[random.Next(allowedFunctions.Count)];
155      // arity of the selected operator
156      int minArity = selectedFunction.MinArity;
157      int maxArity = selectedFunction.MaxArity;
158      // if the old child had too many sub-trees then the new child should keep as many sub-trees as possible
159      if (actualArity > maxArity)
160        actualArity = maxArity;
161      if(actualArity < minArity)
162        actualArity = minArity;
163      // create a list that holds old sub-trees that we can reuse in the new tree
164      List<IFunctionTree> availableSubTrees = new List<IFunctionTree>(child.SubTrees);
165      List<IFunctionTree> freshSubTrees = new List<IFunctionTree>();
166      IFunctionTree newTree = selectedFunction.GetTreeNode();
167      // randomly select the sub-trees that we keep
168      for (int i = 0; i < actualArity; i++) {
169        // fill all sub-tree slots of the new tree
170        // if for a given slot i there are multiple existing sub-trees that can be used in that slot
171        // then use a random existing sub-tree. When there are no existing sub-trees
172        // that fit in the given slot then create a new random tree and use it for the slot
173        ICollection<IFunction> allowedSubFunctions = gardener.GetAllowedSubFunctions(selectedFunction, i);
174        var matchingSubTrees = availableSubTrees.Where(subTree => allowedSubFunctions.Contains(subTree.Function));
175        if (matchingSubTrees.Count() > 0) {
176          IFunctionTree selectedSubTree = matchingSubTrees.ElementAt(random.Next(matchingSubTrees.Count()));
177          // we can just add it as subtree
178          newTree.InsertSubTree(i, selectedSubTree);
179          availableSubTrees.Remove(selectedSubTree); // the branch shouldn't be available for the following slots
180        } else {
181          // no existing matching tree found => create a new tree of minimal size
182          IFunctionTree freshTree = gardener.CreateRandomTree(allowedSubFunctions, 1, 1);
183          freshSubTrees.AddRange(gardener.GetAllSubTrees(freshTree));
184          newTree.InsertSubTree(i, freshTree);
185        }
186      }
187      freshSubTrees.Add(newTree);
188      uninitializedBranches = freshSubTrees;
189      return newTree;
190    }
191  }
192}
Note: See TracBrowser for help on using the repository browser.