Free cookie consent management tool by TermsFeed Policy Generator

source: branches/3.1/sources/HeuristicLab.StructureIdentification/Manipulation/ChangeNodeTypeManipulation.cs @ 6304

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

refactoring: removed method GetMinMaxArity in TreeGardener (#263 (List of allowed sub-functions for each function should be cached))

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