Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding/3.3/ArchitectureAlteringOperators/SubroutineCreater.cs @ 3369

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

Fixed bugs in ADF operators and added test classes for ADF operators. #290 (Implement ADFs)

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