Free cookie consent management tool by TermsFeed Policy Generator

source: branches/3.0/sources/HeuristicLab.StructureIdentification/Manipulation/ChangeNodeTypeManipulation.cs @ 277

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

merged changesets r154 r160 r162 from trunk into the stable branch

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