Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.GP.Operators/3.3/Manipulation/ChangeNodeTypeManipulation.cs @ 2222

Last change on this file since 2222 was 2222, checked in by gkronber, 15 years ago

Merged changes from GP-refactoring branch back into the trunk #713.

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