Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding/3.3/Creators/ProbabilisticTreeCreator.cs @ 3610

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

Cosmetic name-space rename. #937 (Data types and operators for symbolic expression tree encoding)

File size: 14.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;
23using System.Linq;
24using System.Collections.Generic;
25using System.Text;
26using HeuristicLab.Common;
27using HeuristicLab.Core;
28using HeuristicLab.Data;
29using HeuristicLab.Random;
30using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
31using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.ArchitectureManipulators;
32using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Symbols;
33
34namespace HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Creators {
35  [StorableClass]
36  [Item("ProbabilisticTreeCreator", "An operator that creates new symbolic expression trees with uniformly distributed size")]
37  public class ProbabilisticTreeCreator : SymbolicExpressionTreeCreator {
38    private const int MAX_TRIES = 100;
39
40    public ProbabilisticTreeCreator()
41      : base() {
42    }
43
44    protected override SymbolicExpressionTree Create(
45      IRandom random,
46      ISymbolicExpressionGrammar grammar,
47      IntValue maxTreeSize, IntValue maxTreeHeight,
48      IntValue maxFunctionDefinitions, IntValue maxFunctionArguments) {
49      return Create(random, grammar, maxTreeSize.Value, maxTreeHeight.Value, maxFunctionDefinitions.Value, maxFunctionArguments.Value);
50    }
51
52    public static SymbolicExpressionTree Create(IRandom random, ISymbolicExpressionGrammar grammar,
53      int maxTreeSize, int maxTreeHeight,
54      int maxFunctionDefinitions, int maxFunctionArguments
55      ) {
56      SymbolicExpressionTree tree = new SymbolicExpressionTree();
57      var rootNode = grammar.StartSymbol.CreateTreeNode();
58      if (rootNode.HasLocalParameters) rootNode.ResetLocalParameters(random);
59      rootNode.Grammar = grammar;
60      tree.Root = PTC2(random, rootNode, maxTreeSize, maxTreeHeight, maxFunctionDefinitions, maxFunctionArguments);
61      return tree;
62    }
63
64    private class TreeExtensionPoint {
65      public SymbolicExpressionTreeNode Parent { get; set; }
66      public int ChildIndex { get; set; }
67      public int ExtensionPointDepth { get; set; }
68    }
69
70    public static SymbolicExpressionTreeNode PTC2(IRandom random, SymbolicExpressionTreeNode seedNode,
71      int maxTreeSize, int maxDepth, int maxFunctionDefinitions, int maxFunctionArguments) {
72      // tree size is limited by the grammar and by the explicit size constraints
73      int allowedMinSize = seedNode.Grammar.GetMinExpressionLength(seedNode.Symbol);
74      int allowedMaxSize = Math.Min(maxTreeSize, seedNode.Grammar.GetMaxExpressionLength(seedNode.Symbol));
75      int tries = 0;
76      while (tries++ < MAX_TRIES) {
77        // select a target tree size uniformly in the possible range (as determined by explicit limits and limits of the grammar)
78        int treeSize = random.Next(allowedMinSize, allowedMaxSize + 1);
79        if (treeSize <= 1 || maxDepth <= 1) return seedNode;
80
81        bool success = CreateFullTreeFromSeed(random, seedNode, seedNode.Grammar, treeSize, maxDepth, maxFunctionDefinitions, maxFunctionArguments);
82
83        // if successfull => check constraints and return the tree if everything looks ok       
84        if (success && seedNode.GetSize() <= maxTreeSize && seedNode.GetHeight() <= maxDepth) {
85          return seedNode;
86        } else {
87          // clean seedNode
88          while (seedNode.SubTrees.Count > 0) seedNode.RemoveSubTree(0);
89        }
90        // try a different size MAX_TRIES times
91      }
92      throw new ArgumentException("Couldn't create a valid tree with the specified constraints.");
93    }
94
95    private static bool CreateFullTreeFromSeed(IRandom random, SymbolicExpressionTreeNode root, ISymbolicExpressionGrammar globalGrammar,
96      int size, int maxDepth, int maxFunctionDefinitions, int maxFunctionArguments) {
97      try {
98        TryCreateFullTreeFromSeed(random, root, globalGrammar, size, maxDepth, maxFunctionDefinitions, maxFunctionArguments);
99        return true;
100      }
101      catch (ArgumentException) { return false; }
102    }
103
104    private static void TryCreateFullTreeFromSeed(IRandom random, SymbolicExpressionTreeNode root, ISymbolicExpressionGrammar globalGrammar,
105      int size, int maxDepth, int maxFunctionDefinitions, int maxFunctionArguments) {
106      List<TreeExtensionPoint> extensionPoints = new List<TreeExtensionPoint>();
107      int currentSize = 1;
108      int totalListMinSize = globalGrammar.GetMinExpressionLength(root.Symbol) - 1;
109      int actualArity = SampleArity(random, root, size);
110      for (int i = 0; i < actualArity; i++) {
111        // insert a dummy sub-tree and add the pending extension to the list
112        var dummy = new SymbolicExpressionTreeNode();
113        root.AddSubTree(dummy);
114        extensionPoints.Add(new TreeExtensionPoint { Parent = root, ChildIndex = i, ExtensionPointDepth = 2 });
115      }
116      // while there are pending extension points and we have not reached the limit of adding new extension points
117      while (extensionPoints.Count > 0 && totalListMinSize + currentSize < size) {
118        int randomIndex = random.Next(extensionPoints.Count);
119        TreeExtensionPoint nextExtension = extensionPoints[randomIndex];
120        extensionPoints.RemoveAt(randomIndex);
121        SymbolicExpressionTreeNode parent = nextExtension.Parent;
122        int argumentIndex = nextExtension.ChildIndex;
123        int extensionDepth = nextExtension.ExtensionPointDepth;
124        if (extensionDepth + parent.Grammar.GetMinExpressionDepth(parent.Symbol) >= maxDepth) {
125          ReplaceWithMinimalTree(random, root, parent, argumentIndex, maxFunctionDefinitions, maxFunctionArguments);
126        } else {
127          var allowedSymbols = from s in parent.Grammar.Symbols
128                               where parent.Grammar.IsAllowedChild(parent.Symbol, s, argumentIndex)
129                               where parent.Grammar.GetMinExpressionDepth(s) + extensionDepth - 1 < maxDepth
130                               where parent.Grammar.GetMaxExpressionLength(s) > size - totalListMinSize - currentSize
131                               select s;
132          Symbol selectedSymbol = SelectRandomSymbol(random, allowedSymbols);
133          SymbolicExpressionTreeNode newTree = selectedSymbol.CreateTreeNode();
134          if (newTree.HasLocalParameters) newTree.ResetLocalParameters(random);
135          parent.RemoveSubTree(argumentIndex);
136          parent.InsertSubTree(argumentIndex, newTree);
137
138          InitializeNewTreeNode(random, root, newTree, maxFunctionDefinitions, maxFunctionArguments);
139
140          currentSize++;
141          totalListMinSize--;
142
143          actualArity = SampleArity(random, newTree, size - currentSize);
144          for (int i = 0; i < actualArity; i++) {
145            // insert a dummy sub-tree and add the pending extension to the list
146            var dummy = new SymbolicExpressionTreeNode();
147            newTree.AddSubTree(dummy);
148            extensionPoints.Add(new TreeExtensionPoint { Parent = newTree, ChildIndex = i, ExtensionPointDepth = extensionDepth + 1 });
149          }
150          totalListMinSize += newTree.Grammar.GetMinExpressionLength(newTree.Symbol);
151        }
152      }
153      // fill all pending extension points
154      while (extensionPoints.Count > 0) {
155        int randomIndex = random.Next(extensionPoints.Count);
156        TreeExtensionPoint nextExtension = extensionPoints[randomIndex];
157        extensionPoints.RemoveAt(randomIndex);
158        SymbolicExpressionTreeNode parent = nextExtension.Parent;
159        int a = nextExtension.ChildIndex;
160        int d = nextExtension.ExtensionPointDepth;
161        ReplaceWithMinimalTree(random, root, parent, a, maxFunctionDefinitions, maxFunctionArguments);
162      }
163    }
164
165    private static void ReplaceWithMinimalTree(IRandom random, SymbolicExpressionTreeNode root, SymbolicExpressionTreeNode parent, int argumentIndex, int maxFunctionDefinitions, int maxFunctionArguments) {
166      // determine possible symbols that will lead to the smallest possible tree
167      var possibleSymbols = (from s in parent.GetAllowedSymbols(argumentIndex)
168                             group s by parent.Grammar.GetMinExpressionLength(s) into g
169                             orderby g.Key
170                             select g).First();
171      var selectedSymbol = SelectRandomSymbol(random, possibleSymbols);
172      var tree = selectedSymbol.CreateTreeNode();
173      if (tree.HasLocalParameters) tree.ResetLocalParameters(random);
174      parent.RemoveSubTree(argumentIndex);
175      parent.InsertSubTree(argumentIndex, tree);
176      InitializeNewTreeNode(random, root, tree, maxFunctionDefinitions, maxFunctionArguments);
177      for (int i = 0; i < tree.GetMinSubtreeCount(); i++) {
178        // insert a dummy sub-tree and add the pending extension to the list
179        var dummy = new SymbolicExpressionTreeNode();
180        tree.AddSubTree(dummy);
181        // replace the just inserted dummy by recursive application
182        ReplaceWithMinimalTree(random, root, tree, i, maxFunctionDefinitions, maxFunctionArguments);
183      }
184    }
185
186    private static void InitializeNewTreeNode(IRandom random, SymbolicExpressionTreeNode root, SymbolicExpressionTreeNode newTree, int maxFunctionDefinitions, int maxFunctionArguments) {
187      // NB it is assumed that defuns are only allowed as children of root and nowhere else
188      // also assumes that newTree is already attached to root somewhere
189      if (IsTopLevelBranch(root, newTree)) {
190        newTree.Grammar = (ISymbolicExpressionGrammar)root.Grammar.Clone();
191
192        // allow invokes of existing ADFs with higher index
193        int argIndex = root.SubTrees.IndexOf(newTree);
194        for (int i = argIndex + 1; i < root.SubTrees.Count; i++) {
195          var otherDefunNode = root.SubTrees[i] as DefunTreeNode;
196          if (otherDefunNode != null) {
197            GrammarModifier.AddDynamicSymbol(newTree.Grammar, newTree.Symbol, otherDefunNode.FunctionName, otherDefunNode.NumberOfArguments);
198          }
199        }
200      }
201      if (newTree.Symbol is Defun) {
202        var defunTree = newTree as DefunTreeNode;
203        string formatString = new StringBuilder().Append('0', (int)Math.Log10(maxFunctionDefinitions * 10 - 1)).ToString(); // >= 100 functions => ###
204        var allowedNames = from index in Enumerable.Range(0, maxFunctionDefinitions)
205                           select "ADF" + index.ToString(formatString);
206        var takenNames = (from node in root.IterateNodesPrefix().OfType<DefunTreeNode>()
207                          select node.FunctionName).Distinct();
208        var remainingNames = allowedNames.Except(takenNames).ToList();
209        string functionName = remainingNames[random.Next(remainingNames.Count)];
210        // set name and number of arguments of the ADF
211        int nArgs = random.Next(maxFunctionArguments);
212        defunTree.FunctionName = functionName;
213        defunTree.NumberOfArguments = nArgs;
214        if (nArgs > 0) {
215          GrammarModifier.AddDynamicArguments(defunTree.Grammar, defunTree.Symbol, Enumerable.Range(0, nArgs));
216        }
217        // in existing branches with smaller index allow invoke of current function
218        int argIndex = root.SubTrees.IndexOf(newTree);
219        for (int i = 0; i < argIndex; i++) {
220          // if not dummy node
221          if (root.SubTrees[i].Symbol != null) {
222            var existingBranch = root.SubTrees[i];
223            GrammarModifier.AddDynamicSymbol(existingBranch.Grammar, existingBranch.Symbol, functionName, nArgs);
224          }
225        }
226      }
227    }
228
229    private static bool IsTopLevelBranch(SymbolicExpressionTreeNode root, SymbolicExpressionTreeNode branch) {
230      return branch is SymbolicExpressionTreeTopLevelNode;
231    }
232
233    private static Symbol SelectRandomSymbol(IRandom random, IEnumerable<Symbol> symbols) {
234      var symbolList = symbols.ToList();
235      var ticketsSum = symbolList.Select(x => x.InitialFrequency).Sum();
236      var r = random.NextDouble() * ticketsSum;
237      double aggregatedTickets = 0;
238      for (int i = 0; i < symbolList.Count; i++) {
239        aggregatedTickets += symbolList[i].InitialFrequency;
240        if (aggregatedTickets >= r) {
241          return symbolList[i];
242        }
243      }
244      // this should never happen
245      throw new ArgumentException();
246    }
247
248    private static int SampleArity(IRandom random, SymbolicExpressionTreeNode node, int targetSize) {
249      // select actualArity randomly with the constraint that the sub-trees in the minimal arity can become large enough
250      int minArity = node.GetMinSubtreeCount();
251      int maxArity = node.GetMaxSubtreeCount();
252      if (maxArity > targetSize) {
253        maxArity = targetSize;
254      }
255      // the min number of sub-trees has to be set to a value that is large enough so that the largest possible tree is at least tree size
256      // if 1..3 trees are possible and the largest possible first sub-tree is smaller larger than the target size then minArity should be at least 2
257      long aggregatedLongestExpressionLength = 0;
258      for (int i = 0; i < maxArity; i++) {
259        aggregatedLongestExpressionLength += (from s in node.GetAllowedSymbols(i)
260                                              select node.Grammar.GetMaxExpressionLength(s)).Max();
261        if (aggregatedLongestExpressionLength < targetSize) minArity = i;
262        else break;
263      }
264
265      // the max number of sub-trees has to be set to a value that is small enough so that the smallest possible tree is at most tree size
266      // if 1..3 trees are possible and the smallest possible first sub-tree is already larger than the target size then maxArity should be at most 0
267      long aggregatedShortestExpressionLength = 0;
268      for (int i = 0; i < maxArity; i++) {
269        aggregatedShortestExpressionLength += (from s in node.GetAllowedSymbols(i)
270                                               select node.Grammar.GetMinExpressionLength(s)).Min();
271        if (aggregatedShortestExpressionLength > targetSize) {
272          maxArity = i;
273          break;
274        }
275      }
276      if (minArity > maxArity) throw new ArgumentException();
277      return random.Next(minArity, maxArity + 1);
278    }
279  }
280}
Note: See TracBrowser for help on using the repository browser.