Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.StructureIdentification/Manipulation/ChangeNodeTypeManipulation.cs @ 237

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

bug fixes in struct-id operators

File size: 10.5 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;
32
33namespace HeuristicLab.StructureIdentification {
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(GPOperatorLibrary), 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      GPOperatorLibrary library = GetVariableValue<GPOperatorLibrary>("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        if(!gardener.IsValidTree(root)) throw new InvalidProgramException();
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 = gardener.GetTreeSize(selectedChild);
98        int oldChildHeight = gardener.GetTreeSize(selectedChild);
99        int newChildSize = gardener.GetTreeSize(newFunctionTree);
100        int newChildHeight = gardener.GetTreeHeight(newFunctionTree);
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 = gardener.GetTreeHeight(root); // must recalculate height because we can't know wether the manipulated branch was the deepest branch
120        // check if whole tree is ok
121        if(!gardener.IsValidTree(root))
122          throw new InvalidProgramException();
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      // arity of the selected operator
155      int minArity;
156      int maxArity;
157      // create a new tree-node for a randomly selected function
158      IFunction selectedFunction = allowedFunctions[random.Next(allowedFunctions.Count)];
159      gardener.GetMinMaxArity(selectedFunction, out minArity, out maxArity);
160      // if the old child had too many sub-trees then the new child should keep as many sub-trees as possible
161      if (actualArity > maxArity)
162        actualArity = maxArity;
163      if(actualArity < minArity)
164        actualArity = minArity;
165      // create a list that holds old sub-trees that we can reuse in the new tree
166      List<IFunctionTree> availableSubTrees = new List<IFunctionTree>(child.SubTrees);
167      List<IFunctionTree> freshSubTrees = new List<IFunctionTree>();
168      IFunctionTree newTree = selectedFunction.GetTreeNode();
169      // randomly select the sub-trees that we keep
170      for (int i = 0; i < actualArity; i++) {
171        // fill all sub-tree slots of the new tree
172        // if for a given slot i there are multiple existing sub-trees that can be used in that slot
173        // then use a random existing sub-tree. When there are no existing sub-trees
174        // that fit in the given slot then create a new random tree and use it for the slot
175        ICollection<IFunction> allowedSubFunctions = gardener.GetAllowedSubFunctions(selectedFunction, i);
176        var matchingSubTrees = availableSubTrees.Where(subTree => allowedSubFunctions.Contains(subTree.Function));
177        if (matchingSubTrees.Count() > 0) {
178          IFunctionTree selectedSubTree = matchingSubTrees.ElementAt(random.Next(matchingSubTrees.Count()));
179          // we can just add it as subtree
180          newTree.InsertSubTree(i, selectedSubTree);
181          availableSubTrees.Remove(selectedSubTree); // the branch shouldn't be available for the following slots
182        } else {
183          // no existing matching tree found => create a new tree of minimal size
184          IFunctionTree freshTree = gardener.CreateRandomTree(allowedSubFunctions, 1, 1);
185          freshSubTrees.AddRange(gardener.GetAllSubTrees(freshTree));
186          newTree.InsertSubTree(i, freshTree);
187        }
188      }
189      freshSubTrees.Add(newTree);
190      uninitializedBranches = freshSubTrees;
191      return newTree;
192    }
193  }
194}
Note: See TracBrowser for help on using the repository browser.