Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 134 was 23, checked in by gkronber, 17 years ago

changed boolean variable BalanceTrees to double BalancedTreesRate (ticket #11)

File size: 10.6 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    public ChangeNodeTypeManipulation()
35      : base() {
36      AddVariableInfo(new VariableInfo("Random", "Uniform random number generator", typeof(MersenneTwister), VariableKind.In));
37      AddVariableInfo(new VariableInfo("OperatorLibrary", "The operator library containing all available operators", typeof(GPOperatorLibrary), VariableKind.In));
38      AddVariableInfo(new VariableInfo("MaxTreeHeight", "The maximal allowed height of the tree", typeof(IntData), VariableKind.In));
39      AddVariableInfo(new VariableInfo("MaxTreeSize", "The maximal allowed size (number of nodes) of the tree", typeof(IntData), VariableKind.In));
40      AddVariableInfo(new VariableInfo("BalancedTreesRate", "Determines how many trees should be balanced", typeof(DoubleData), VariableKind.In));
41      AddVariableInfo(new VariableInfo("OperatorTree", "The tree to mutate", typeof(IOperator), VariableKind.In));
42      AddVariableInfo(new VariableInfo("TreeSize", "The size (number of nodes) of the tree", typeof(IntData), VariableKind.In));
43      AddVariableInfo(new VariableInfo("TreeHeight", "The height of the tree", typeof(IntData), VariableKind.In));
44    }
45
46
47    public override IOperation Apply(IScope scope) {
48      IOperator rootOperator = GetVariableValue<IOperator>("OperatorTree", scope, false);
49      MersenneTwister random = GetVariableValue<MersenneTwister>("Random", scope, true);
50      GPOperatorLibrary library = GetVariableValue<GPOperatorLibrary>("OperatorLibrary", scope, true);
51      double balancedTreesRate = GetVariableValue<DoubleData>("BalancedTreesRate", scope, true).Data;
52      IntData treeSize = GetVariableValue<IntData>("TreeSize", scope, false);
53      IntData treeHeight = GetVariableValue<IntData>("TreeHeight", scope, false);
54      int maxTreeSize = GetVariableValue<IntData>("MaxTreeSize", scope, true).Data;
55      int maxTreeHeight = GetVariableValue<IntData>("MaxTreeHeight", scope, true).Data;
56
57      TreeGardener gardener = new TreeGardener(random, library);
58
59      IOperator parent = gardener.GetRandomParentNode(rootOperator);
60
61      IOperator selectedChild;
62      int selectedChildIndex;
63      if (parent == null) {
64        selectedChildIndex = 0;
65        selectedChild = rootOperator;
66      } else {
67        selectedChildIndex = random.Next(parent.SubOperators.Count);
68        selectedChild = parent.SubOperators[selectedChildIndex];
69      }
70
71      if (selectedChild.SubOperators.Count == 0) {
72        IOperator 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("OperatorTree").Value = newTerminal;
78        } else {
79          parent.RemoveSubOperator(selectedChildIndex);
80          parent.AddSubOperator(newTerminal, selectedChildIndex);
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.GetAllOperators(newTerminal), scope);
87      } else {
88        List<IOperator> uninitializedOperators;
89        IOperator newFunction = ChangeFunctionType(parent, selectedChild, selectedChildIndex, gardener, random, balancedTreesRate, out uninitializedOperators);
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("OperatorTree").Value = newFunction;
95          rootOperator = newFunction;
96        } else {
97          // remove the old child
98          parent.RemoveSubOperator(selectedChildIndex);
99          // add the new child as sub-tree of parent
100          parent.AddSubOperator(newFunction, selectedChildIndex);
101        }
102
103        // recalculate size and height
104        treeSize.Data = gardener.GetTreeSize(rootOperator);
105        treeHeight.Data = gardener.GetTreeHeight(rootOperator);
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(rootOperator)) {
115          throw new InvalidProgramException();
116        }
117
118        // return a composite operation that initializes all created sub-trees
119        return gardener.CreateInitializationOperation(uninitializedOperators, scope);
120      }
121    }
122
123
124    private IOperator ChangeTerminalType(IOperator parent, IOperator child, int childIndex, TreeGardener gardener, MersenneTwister random) {
125
126      IList<IOperator> allowedChildren;
127      if (parent == null) {
128        allowedChildren = gardener.Terminals;
129      } else {
130        SubOperatorsConstraintAnalyser analyser = new SubOperatorsConstraintAnalyser();
131        analyser.AllPossibleOperators = gardener.Terminals;
132        allowedChildren = analyser.GetAllowedOperators(parent, childIndex);
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((IOperator)allowedChildren[random.Next(allowedChildren.Count)].Clone(), 1, 1, false);
138    }
139
140    private IOperator ChangeFunctionType(IOperator parent, IOperator child, int childIndex, TreeGardener gardener, MersenneTwister random,
141      double balancedTreesRate, out List<IOperator> uninitializedOperators) {
142      // since there are suboperators, we have to check which
143      // and how many of the existing suboperators we can reuse
144
145      // let's choose the operator we want to use instead of the old child. For this we have to determine the
146      // pool of allowed operators based on constraints of the parent if there is one.
147      IList<IOperator> allowedSubOperators;
148      SubOperatorsConstraintAnalyser analyser = new SubOperatorsConstraintAnalyser();
149      analyser.AllPossibleOperators = gardener.AllOperators;
150      if (parent == null) {
151        allowedSubOperators = gardener.AllOperators;
152      } else {
153        allowedSubOperators = analyser.GetAllowedOperators(parent, childIndex);
154      }
155
156      // try to make a tree with the same arity as the old child.
157      int actualArity = child.SubOperators.Count;
158      // arity of the selected operator
159      int minArity;
160      int maxArity;
161
162      allowedSubOperators = allowedSubOperators.Where(f => {
163        gardener.GetMinMaxArity(f, out minArity, out maxArity);
164        return minArity <= actualArity;
165      }).ToList();
166
167      IOperator newOperator = (IOperator)allowedSubOperators[random.Next(allowedSubOperators.Count)].Clone();
168
169      gardener.GetMinMaxArity(newOperator, out minArity, out maxArity);
170      // if the old child had too many sub-operators 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.SubOperators.Select(subOp => gardener.GetTreeSize(subOp)).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<IOperator> availableSubOperators = new List<IOperator>(child.SubOperators);
182      List<IOperator> freshSubTrees = new List<IOperator>() { newOperator };
183
184      // randomly select the suboperators that we keep
185      for (int i = 0; i < actualArity; i++) {
186        // fill all sub-operator slots of the new operator
187        // if for a given slot i there are existing sub-operators that can be used in that slot
188        // then use a random existing sub-operator. When there are no existing sub-operators
189        // that fit in the given slot then create a new random tree and use it for the slot
190        IList<IOperator> allowedOperators = analyser.GetAllowedOperators(newOperator, i);
191        var matchingOperators = availableSubOperators.Where(subOp => allowedOperators.Contains(subOp, new TreeGardener.OperatorEqualityComparer()));
192
193        if (matchingOperators.Count() > 0) {
194          IOperator selectedSubOperator = matchingOperators.ElementAt(random.Next(matchingOperators.Count()));
195          // we can just add it as suboperator
196          newOperator.AddSubOperator(selectedSubOperator, i);
197          availableSubOperators.Remove(selectedSubOperator); // the operator shouldn't be available for the following slots
198        } else {
199          IOperator freshOperatorTree;
200          if(random.NextDouble() <= balancedTreesRate) {
201            freshOperatorTree = gardener.CreateRandomTree(allowedOperators, maxSubTreeSize, maxSubTreeHeight, true);
202          } else {
203            freshOperatorTree = gardener.CreateRandomTree(allowedOperators, maxSubTreeSize, maxSubTreeHeight, false);
204          }
205          freshSubTrees.AddRange(gardener.GetAllOperators(freshOperatorTree));
206
207          newOperator.AddSubOperator(freshOperatorTree, i);
208        }
209      }
210
211      uninitializedOperators = freshSubTrees;
212      return newOperator;
213    }
214  }
215}
Note: See TracBrowser for help on using the repository browser.