Free cookie consent management tool by TermsFeed Policy Generator

source: branches/FunctionsAndStructIdRefactoring/HeuristicLab.StructureIdentification/Manipulation/ChangeNodeTypeManipulation.cs @ 144

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

fixed compile errors (#112)

File size: 10.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;
32
33namespace HeuristicLab.StructureIdentification {
34  public class ChangeNodeTypeManipulation : OperatorBase {
35    public ChangeNodeTypeManipulation()
36      : base() {
37      AddVariableInfo(new VariableInfo("Random", "Uniform random number generator", typeof(MersenneTwister), VariableKind.In));
38      AddVariableInfo(new VariableInfo("OperatorLibrary", "The operator library containing all available operators", typeof(GPOperatorLibrary), VariableKind.In));
39      AddVariableInfo(new VariableInfo("MaxTreeHeight", "The maximal allowed height of the tree", typeof(IntData), VariableKind.In));
40      AddVariableInfo(new VariableInfo("MaxTreeSize", "The maximal allowed size (number of nodes) of the tree", typeof(IntData), VariableKind.In));
41      AddVariableInfo(new VariableInfo("BalancedTreesRate", "Determines how many trees should be balanced", typeof(DoubleData), VariableKind.In));
42      AddVariableInfo(new VariableInfo("FunctionTree", "The tree to mutate", typeof(IFunctionTree), VariableKind.In | VariableKind.Out));
43      AddVariableInfo(new VariableInfo("TreeSize", "The size (number of nodes) of the tree", typeof(IntData), VariableKind.In | VariableKind.Out));
44      AddVariableInfo(new VariableInfo("TreeHeight", "The height of the tree", typeof(IntData), VariableKind.In | VariableKind.Out));
45    }
46
47
48    public override IOperation Apply(IScope scope) {
49      IFunctionTree root = GetVariableValue<IFunctionTree>("FunctionTree", scope, false);
50      MersenneTwister random = GetVariableValue<MersenneTwister>("Random", scope, true);
51      GPOperatorLibrary library = GetVariableValue<GPOperatorLibrary>("OperatorLibrary", scope, true);
52      double balancedTreesRate = GetVariableValue<DoubleData>("BalancedTreesRate", scope, true).Data;
53      IntData treeSize = GetVariableValue<IntData>("TreeSize", scope, false);
54      IntData treeHeight = GetVariableValue<IntData>("TreeHeight", scope, false);
55      int maxTreeSize = GetVariableValue<IntData>("MaxTreeSize", scope, true).Data;
56      int maxTreeHeight = GetVariableValue<IntData>("MaxTreeHeight", scope, true).Data;
57
58      TreeGardener gardener = new TreeGardener(random, library);
59      IFunctionTree parent = gardener.GetRandomParentNode(root);
60
61      IFunctionTree selectedChild;
62      int selectedChildIndex;
63      if (parent == null) {
64        selectedChildIndex = 0;
65        selectedChild = root;
66      } else {
67        selectedChildIndex = random.Next(parent.SubTrees.Count);
68        selectedChild = parent.SubTrees [selectedChildIndex];
69      }
70
71      if (selectedChild.SubTrees.Count == 0) {
72        IFunctionTree newTerminal = ChangeTerminalType(parent, selectedChild, selectedChildIndex, gardener, random);
73
74        if (parent == null) {
75          // no parent means the new child is the initial operator
76          // and we have to update the value in the variable
77          scope.GetVariable(scope.TranslateName("FunctionTree")).Value = newTerminal;
78        } else {
79          parent.RemoveSubTree(selectedChildIndex);
80          parent.InsertSubTree(selectedChildIndex, newTerminal);
81          // updating the variable is not necessary because it stays the same
82        }
83
84        // size and height stays the same when changing a terminal so no need to update the variables
85        // schedule an operation to initialize the new terminal
86        return gardener.CreateInitializationOperation(gardener.GetAllSubTrees(newTerminal), scope);
87      } else {
88        List<IFunctionTree> uninitializedBranches;
89        IFunctionTree newFunction = ChangeFunctionType(parent, selectedChild, selectedChildIndex, gardener, random, balancedTreesRate, out uninitializedBranches);
90
91        if (parent == null) {
92          // no parent means the new function is the initial operator
93          // and we have to update the value in the variable
94          scope.GetVariable(scope.TranslateName("FunctionTree")).Value = newFunction;
95          root = newFunction;
96        } else {
97          // remove the old child
98          parent.RemoveSubTree(selectedChildIndex);
99          // add the new child as sub-tree of parent
100          parent.InsertSubTree(selectedChildIndex, newFunction);
101        }
102
103        // recalculate size and height
104        treeSize.Data = gardener.GetTreeSize(root);
105        treeHeight.Data = gardener.GetTreeHeight(root);
106
107        // check if the size of the new tree is still in the allowed bounds
108        if (treeHeight.Data > maxTreeHeight ||
109          treeSize.Data > maxTreeSize) {
110          throw new InvalidProgramException();
111        }
112
113        // check if whole tree is ok
114        if (!gardener.IsValidTree(root)) {
115          throw new InvalidProgramException();
116        }
117
118        // return a composite operation that initializes all created sub-trees
119        return gardener.CreateInitializationOperation(uninitializedBranches, scope);
120      }
121    }
122
123
124    private IFunctionTree ChangeTerminalType(IFunctionTree parent, IFunctionTree child, int childIndex, TreeGardener gardener, MersenneTwister random) {
125
126      IList<IFunction> allowedChildren;
127      if (parent == null) {
128        allowedChildren = gardener.Terminals;
129      } else {
130        SubOperatorsConstraintAnalyser analyser = new SubOperatorsConstraintAnalyser();
131        analyser.AllPossibleOperators = gardener.Terminals.Cast<IOperator>().ToArray();
132        allowedChildren = analyser.GetAllowedOperators(parent.Function, childIndex).Cast<IFunction>().ToList();
133      }
134
135      // selecting from the terminals should always work since the current child was also a terminal
136      // so in the worst case we will just create a new terminal of the same type again.
137      return gardener.CreateRandomTree(allowedChildren[random.Next(allowedChildren.Count)], 1, 1, false);
138    }
139
140    private IFunctionTree ChangeFunctionType(IFunctionTree parent, IFunctionTree child, int childIndex, TreeGardener gardener, MersenneTwister random,
141      double balancedTreesRate, out List<IFunctionTree> uninitializedBranches) {
142      // since there are subtrees, we have to check which
143      // and how many of the existing subtrees we can reuse
144
145      // let's choose the function we want to use instead of the old child. For this we have to determine the
146      // pool of allowed functions based on constraints of the parent if there is one.
147      IList<IFunction> allowedFunctions;
148      SubOperatorsConstraintAnalyser analyser = new SubOperatorsConstraintAnalyser();
149      analyser.AllPossibleOperators = gardener.AllFunctions.Cast<IOperator>().ToArray();
150      if (parent == null) {
151        allowedFunctions = gardener.AllFunctions;
152      } else {
153        allowedFunctions = analyser.GetAllowedOperators(parent.Function, childIndex).Cast<IFunction>().ToList();
154      }
155
156      // try to make a tree with the same arity as the old child.
157      int actualArity = child.SubTrees.Count;
158      // arity of the selected operator
159      int minArity;
160      int maxArity;
161
162      allowedFunctions = allowedFunctions.Where(f => {
163        gardener.GetMinMaxArity(f, out minArity, out maxArity);
164        return minArity <= actualArity;
165      }).ToList();
166
167      IFunctionTree newTree = new FunctionTree(allowedFunctions[random.Next(allowedFunctions.Count)]);
168
169      gardener.GetMinMaxArity(newTree.Function, out minArity, out maxArity);
170      // if the old child had too many sub-trees then make the new child with the maximal arity
171      if (actualArity > maxArity)
172        actualArity = maxArity;
173
174      // get the allowed size and height for new sub-trees
175      // use the size of the smallest subtree as the maximal allowed size for new subtrees to
176      // prevent that we ever create trees over the MaxTreeSize limit
177      int maxSubTreeSize = child.SubTrees.Select(subTree => gardener.GetTreeSize(subTree)).Min();
178      int maxSubTreeHeight = gardener.GetTreeHeight(child) - 1;
179
180      // create a list that holds old sub-trees that we can reuse in the new tree
181      List<IFunctionTree> availableSubTrees = new List<IFunctionTree>(child.SubTrees);
182      List<IFunctionTree> freshSubTrees = new List<IFunctionTree>() { newTree };
183
184      // randomly select the sub-trees that we keep
185      for (int i = 0; i < actualArity; i++) {
186        // fill all sub-tree slots of the new tree
187        // if for a given slot i there are existing sub-trees that can be used in that slot
188        // then use a random existing sub-tree. When there are no existing sub-trees
189        // that fit in the given slot then create a new random tree and use it for the slot
190        IList<IFunction> allowedOperators = analyser.GetAllowedOperators(newTree.Function, i).Cast<IFunction>().ToList();
191        var matchingSubTrees = availableSubTrees.Where(subTree => allowedOperators.Contains(subTree.Function));
192
193        if (matchingSubTrees.Count() > 0) {
194          IFunctionTree selectedSubTree = matchingSubTrees.ElementAt(random.Next(matchingSubTrees.Count()));
195          // we can just add it as subtree
196          newTree.InsertSubTree(i, selectedSubTree);
197          availableSubTrees.Remove(selectedSubTree); // the branch shouldn't be available for the following slots
198        } else {
199          IFunctionTree freshTree;
200          if(random.NextDouble() <= balancedTreesRate) {
201            freshTree = gardener.CreateRandomTree(allowedOperators, maxSubTreeSize, maxSubTreeHeight, true);
202          } else {
203            freshTree = gardener.CreateRandomTree(allowedOperators, maxSubTreeSize, maxSubTreeHeight, false);
204          }
205          freshSubTrees.AddRange(gardener.GetAllSubTrees(freshTree));
206
207          newTree.InsertSubTree(i, freshTree);
208        }
209      }
210
211      uninitializedBranches = freshSubTrees;
212      return newTree;
213    }
214  }
215}
Note: See TracBrowser for help on using the repository browser.