Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding/3.3/ArchitectureManipulators/SubroutineCreater.cs @ 4501

Last change on this file since 4501 was 4249, checked in by mkommend, 14 years ago

Refactored grammars (ticket #1028).

File size: 13.0 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2010 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.Data;
28using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Symbols;
29using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
30
31namespace HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.ArchitectureManipulators {
32  /// <summary>
33  /// Manipulates a symbolic expression by adding one new function-defining branch containing
34  /// a proportion of a preexisting branch and by creating a reference to the new branch.
35  /// As described in Koza, Bennett, Andre, Keane, Genetic Programming III - Darwinian Invention and Problem Solving, 1999, pp. 97
36  /// </summary>
37  [Item("SubroutineCreater", "Manipulates a symbolic expression by adding one new function-defining branch containing a proportion of a preexisting branch and by creating a reference to the new branch.")]
38  [StorableClass]
39  public sealed class SubroutineCreater : SymbolicExpressionTreeArchitectureManipulator {
40    private const double ARGUMENT_CUTOFF_PROBABILITY = 0.05;
41
42    public override sealed void ModifyArchitecture(
43      IRandom random,
44      SymbolicExpressionTree symbolicExpressionTree,
45      ISymbolicExpressionGrammar grammar,
46      IntValue maxTreeSize, IntValue maxTreeHeight,
47      IntValue maxFunctionDefiningBranches, IntValue maxFunctionArguments,
48      out bool success) {
49      success = CreateSubroutine(random, symbolicExpressionTree, grammar, maxTreeSize.Value, maxTreeHeight.Value, maxFunctionDefiningBranches.Value, maxFunctionArguments.Value);
50    }
51
52    public static bool CreateSubroutine(
53      IRandom random,
54      SymbolicExpressionTree symbolicExpressionTree,
55      ISymbolicExpressionGrammar grammar,
56      int maxTreeSize, int maxTreeHeight,
57      int maxFunctionDefiningBranches, int maxFunctionArguments) {
58      var functionDefiningBranches = symbolicExpressionTree.IterateNodesPrefix().OfType<DefunTreeNode>();
59      if (functionDefiningBranches.Count() >= maxFunctionDefiningBranches)
60        // allowed maximum number of ADF reached => abort
61        return false;
62      if (symbolicExpressionTree.Size + 4 > maxTreeSize)
63        // defining a new function causes an size increase by 4 nodes (max) if the max tree size is reached => abort
64        return false;
65      string formatString = new StringBuilder().Append('0', (int)Math.Log10(maxFunctionDefiningBranches * 10 - 1)).ToString(); // >= 100 functions => ###
66      var allowedFunctionNames = from index in Enumerable.Range(0, maxFunctionDefiningBranches)
67                                 select "ADF" + index.ToString(formatString);
68
69      // select a random body (either the result producing branch or an ADF branch)
70      var bodies = from node in symbolicExpressionTree.Root.SubTrees
71                   select new { Tree = node, Size = node.GetSize() };
72      var totalNumberOfBodyNodes = bodies.Select(x => x.Size).Sum();
73      int r = random.Next(totalNumberOfBodyNodes);
74      int aggregatedNumberOfBodyNodes = 0;
75      SymbolicExpressionTreeNode selectedBody = null;
76      foreach (var body in bodies) {
77        aggregatedNumberOfBodyNodes += body.Size;
78        if (aggregatedNumberOfBodyNodes > r)
79          selectedBody = body.Tree;
80      }
81      // sanity check
82      if (selectedBody == null) throw new InvalidOperationException();
83
84      // select a random cut point in the selected branch
85      var allCutPoints = from parent in selectedBody.IterateNodesPrefix()
86                         from subtree in parent.SubTrees
87                         select new { Parent = parent, ReplacedBranchIndex = parent.SubTrees.IndexOf(subtree), ReplacedBranch = subtree };
88      if (allCutPoints.Count() == 0)
89        // no cut points => abort
90        return false;
91      string newFunctionName = allowedFunctionNames.Except(functionDefiningBranches.Select(x => x.FunctionName)).First();
92      var selectedCutPoint = allCutPoints.SelectRandom(random);
93      // select random branches as argument cut-off points (replaced by argument terminal nodes in the function)
94      List<SymbolicExpressionTreeNode> argumentBranches = SelectRandomArgumentBranches(selectedCutPoint.ReplacedBranch, random, ARGUMENT_CUTOFF_PROBABILITY, maxFunctionArguments);
95      SymbolicExpressionTreeNode functionBody = selectedCutPoint.ReplacedBranch;
96      // disconnect the function body from the tree
97      selectedCutPoint.Parent.RemoveSubTree(selectedCutPoint.ReplacedBranchIndex);
98      // disconnect the argument branches from the function
99      functionBody = DisconnectBranches(functionBody, argumentBranches);
100      // insert a function invocation symbol instead
101      var invokeNode = (InvokeFunctionTreeNode)(new InvokeFunction(newFunctionName)).CreateTreeNode();
102      selectedCutPoint.Parent.InsertSubTree(selectedCutPoint.ReplacedBranchIndex, invokeNode);
103      // add the branches selected as argument as subtrees of the function invocation node
104      foreach (var argumentBranch in argumentBranches)
105        invokeNode.AddSubTree(argumentBranch);
106
107      // insert a new function defining branch
108      var defunNode = (DefunTreeNode)(new Defun()).CreateTreeNode();
109      defunNode.FunctionName = newFunctionName;
110      defunNode.AddSubTree(functionBody);
111      symbolicExpressionTree.Root.AddSubTree(defunNode);
112      // the grammar in the newly defined function is a clone of the grammar of the originating branch
113      defunNode.SetGrammar((ISymbolicExpressionGrammar)selectedBody.Grammar.Clone());
114      // remove all argument symbols from grammar
115      var oldArgumentSymbols = defunNode.Grammar.Symbols.OfType<Argument>().ToList();
116      foreach (var oldArgSymb in oldArgumentSymbols)
117        defunNode.Grammar.RemoveSymbol(oldArgSymb);
118      // find unique argument indexes and matching symbols in the function defining branch
119      var newArgumentIndexes = (from node in defunNode.IterateNodesPrefix().OfType<ArgumentTreeNode>()
120                                select node.Symbol.ArgumentIndex).Distinct();
121      // add argument symbols to grammar of function defining branch
122      GrammarModifier.AddDynamicArguments(defunNode.Grammar, defunNode.Symbol, newArgumentIndexes);
123      defunNode.NumberOfArguments = newArgumentIndexes.Count();
124      if (defunNode.NumberOfArguments != argumentBranches.Count) throw new InvalidOperationException();
125      // add invoke symbol for newly defined function to the original branch
126      var allowedParents = from symb in selectedBody.Grammar.Symbols
127                           where !(symb is ProgramRootSymbol)
128                           select symb;
129      var allowedChildren = from symb in selectedBody.Grammar.Symbols
130                            where selectedBody.Grammar.IsAllowedChild(selectedBody.Symbol, symb, 0)
131                            select symb;
132      GrammarModifier.AddDynamicSymbol(selectedBody.Grammar, selectedBody.Symbol, defunNode.FunctionName, defunNode.NumberOfArguments);
133
134      // when the new function body was taken from another function definition
135      // add invoke symbol for newly defined function to all branches that are allowed to invoke the original branch
136      if (selectedBody.Symbol is Defun) {
137        var originalFunctionDefinition = selectedBody as DefunTreeNode;
138        foreach (var subtree in symbolicExpressionTree.Root.SubTrees) {
139          var originalBranchInvokeSymbol = (from symb in subtree.Grammar.Symbols.OfType<InvokeFunction>()
140                                            where symb.FunctionName == originalFunctionDefinition.FunctionName
141                                            select symb).SingleOrDefault();
142          // when the original branch can be invoked from the subtree then also allow invocation of the function
143          if (originalBranchInvokeSymbol != null) {
144            allowedParents = from symb in subtree.Grammar.Symbols
145                             where !(symb is ProgramRootSymbol)
146                             select symb;
147            allowedChildren = from symb in subtree.Grammar.Symbols
148                              where subtree.Grammar.IsAllowedChild(subtree.Symbol, symb, 0)
149                              select symb;
150            GrammarModifier.AddDynamicSymbol(subtree.Grammar, subtree.Symbol, defunNode.FunctionName, defunNode.NumberOfArguments);
151          }
152        }
153      }
154      return true;
155    }
156
157    private static SymbolicExpressionTreeNode DisconnectBranches(SymbolicExpressionTreeNode node, List<SymbolicExpressionTreeNode> argumentBranches) {
158      if (argumentBranches.Contains(node)) {
159        var argumentIndex = argumentBranches.IndexOf(node);
160        var argSymbol = new Argument(argumentIndex);
161        return argSymbol.CreateTreeNode();
162      }
163      // remove the subtrees so that we can clone only the root node
164      List<SymbolicExpressionTreeNode> subtrees = new List<SymbolicExpressionTreeNode>(node.SubTrees);
165      while (node.SubTrees.Count > 0) node.RemoveSubTree(0);
166      // recursively apply function for subtrees or append a argument terminal node
167      foreach (var subtree in subtrees) {
168        node.AddSubTree(DisconnectBranches(subtree, argumentBranches));
169      }
170      return node;
171    }
172
173    private static List<SymbolicExpressionTreeNode> SelectRandomArgumentBranches(SymbolicExpressionTreeNode selectedRoot,
174      IRandom random,
175      double cutProbability,
176      int maxArguments) {
177      // breadth first determination of argument cut-off points
178      // we must make sure that we cut off all original argument nodes and that the number of new argument is smaller than the limit
179      List<SymbolicExpressionTreeNode> argumentBranches = new List<SymbolicExpressionTreeNode>();
180      if (selectedRoot is ArgumentTreeNode) {
181        argumentBranches.Add(selectedRoot);
182        return argumentBranches;
183      } else {
184        // get the number of argument nodes (which must be cut-off) in the sub-trees
185        var numberOfArgumentsInSubtrees = (from subtree in selectedRoot.SubTrees
186                                           let nArgumentsInTree = subtree.IterateNodesPrefix().OfType<ArgumentTreeNode>().Count()
187                                           select nArgumentsInTree).ToList();
188        // determine the minimal number of new argument nodes for each sub-tree
189        var minNewArgumentsForSubtrees = numberOfArgumentsInSubtrees.Select(x => x > 0 ? 1 : 0).ToList();
190        if (minNewArgumentsForSubtrees.Sum() > maxArguments) {
191          argumentBranches.Add(selectedRoot);
192          return argumentBranches;
193        }
194        // cut-off in the sub-trees in random order
195        var randomIndexes = (from index in Enumerable.Range(0, selectedRoot.SubTrees.Count)
196                             select new { Index = index, OrderValue = random.NextDouble() }).OrderBy(x => x.OrderValue).Select(x => x.Index);
197        foreach (var subtreeIndex in randomIndexes) {
198          var subtree = selectedRoot.SubTrees[subtreeIndex];
199          minNewArgumentsForSubtrees[subtreeIndex] = 0;
200          // => cut-off at 0..n points somewhere in the current sub-tree
201          // determine the maximum number of new arguments that should be created in the branch
202          // as the maximum for the whole branch minus already added arguments minus minimal number of arguments still left
203          int maxArgumentsFromBranch = maxArguments - argumentBranches.Count - minNewArgumentsForSubtrees.Sum();
204          // when no argument is allowed from the current branch then we have to include the whole branch into the function
205          // otherwise: choose randomly wether to cut off immediately or wether to extend the function body into the branch
206          if (maxArgumentsFromBranch == 0) {
207            // don't cut at all => the whole sub-tree branch is included in the function body
208            // (we already checked ahead of time that there are no arguments left over in the subtree)
209          } else if (random.NextDouble() >= cutProbability) {
210            argumentBranches.AddRange(SelectRandomArgumentBranches(subtree, random, cutProbability, maxArgumentsFromBranch));
211          } else {
212            // cut-off at current sub-tree
213            argumentBranches.Add(subtree);
214          }
215        }
216        return argumentBranches;
217      }
218    }
219  }
220}
Note: See TracBrowser for help on using the repository browser.