Free cookie consent management tool by TermsFeed Policy Generator

source: branches/FunctionsAndStructIdRefactoring/HeuristicLab.StructureIdentification/TreeGardener.cs @ 145

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

more fixes after major refactoring step (ticket #112)

File size: 18.5 KB
RevLine 
[2]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.Text;
25using HeuristicLab.Core;
26using HeuristicLab.Constraints;
27using System.Diagnostics;
28using HeuristicLab.Data;
29using System.Linq;
30using HeuristicLab.Random;
31using HeuristicLab.Operators;
32using HeuristicLab.Selection;
[142]33using HeuristicLab.Functions;
[2]34
35namespace HeuristicLab.StructureIdentification {
36  internal class TreeGardener {
37    private IRandom random;
[143]38    private IOperatorLibrary funLibrary;
39    private List<IFunction> functions;
40    private List<IFunction> terminals;
[2]41
[143]42    internal IList<IFunction> Terminals {
[2]43      get { return terminals.AsReadOnly(); }
44    }
[143]45    private List<IFunction> allFunctions;
[2]46
[143]47    internal IList<IFunction> AllFunctions {
48      get { return allFunctions.AsReadOnly(); }
[2]49    }
50
[143]51    internal TreeGardener(IRandom random, IOperatorLibrary funLibrary) {
[2]52      this.random = random;
[143]53      this.funLibrary = funLibrary;
[2]54
[143]55      this.allFunctions = new List<IFunction>();
56      terminals = new List<IFunction>();
57      functions = new List<IFunction>();
[2]58
59      // init functions and terminals based on constraints
[143]60      foreach (IFunction fun in funLibrary.Group.Operators) {
[2]61        int maxA, minA;
[143]62        GetMinMaxArity(fun, out minA, out maxA);
[2]63        if (maxA == 0) {
[143]64          terminals.Add(fun);
[2]65        } else {
[143]66          functions.Add(fun);
[2]67        }
68      }
69
[143]70      allFunctions.AddRange(functions);
71      allFunctions.AddRange(terminals);
[2]72    }
73
74    #region random initialization
[143]75    internal IFunctionTree CreateRandomTree(ICollection<IFunction> allowedFunctions, int maxTreeSize, int maxTreeHeight, bool balanceTrees) {
[2]76
[143]77      int minTreeHeight = allowedFunctions.Select(f => ((IntData)f.GetVariable(GPOperatorLibrary.MIN_TREE_HEIGHT).Value).Data).Min();
[2]78      if (minTreeHeight > maxTreeHeight)
79        maxTreeHeight = minTreeHeight;
80
[143]81      int minTreeSize = allowedFunctions.Select(f => ((IntData)f.GetVariable(GPOperatorLibrary.MIN_TREE_SIZE).Value).Data).Min();
[2]82      if (minTreeSize > maxTreeSize)
83        maxTreeSize = minTreeSize;
84
85      int treeHeight = random.Next(minTreeHeight, maxTreeHeight + 1);
86      int treeSize = random.Next(minTreeSize, maxTreeSize + 1);
87
[143]88      IFunction[] possibleFunctions = allowedFunctions.Where(f => ((IntData)f.GetVariable(GPOperatorLibrary.MIN_TREE_HEIGHT).Value).Data <= treeHeight &&
89        ((IntData)f.GetVariable(GPOperatorLibrary.MIN_TREE_SIZE).Value).Data <= treeSize).ToArray();
90      IFunction selectedFunction = possibleFunctions[random.Next(possibleFunctions.Length)];
[2]91
[143]92      return CreateRandomTree(selectedFunction, treeSize, treeHeight, balanceTrees);
[2]93    }
94
[142]95    internal IFunctionTree CreateRandomTree(int maxTreeSize, int maxTreeHeight, bool balanceTrees) {
[2]96      if (balanceTrees) {
[23]97        if (maxTreeHeight == 1 || maxTreeSize==1) {
[143]98          IFunction selectedTerminal = terminals[random.Next(terminals.Count())];
[142]99          return new FunctionTree(selectedTerminal);
[2]100        } else {
[143]101          IFunction[] possibleFunctions = functions.Where(f => GetMinimalTreeHeight(f) <= maxTreeHeight &&
[2]102            GetMinimalTreeSize(f) <= maxTreeSize).ToArray();
[143]103          IFunction selectedFunction = possibleFunctions[random.Next(possibleFunctions.Length)];
[142]104          FunctionTree root = new FunctionTree(selectedFunction);
105          MakeBalancedTree(root, maxTreeSize - 1, maxTreeHeight - 1);
106          return root;
[2]107        }
108
109      } else {
[143]110        IFunction[] possibleFunctions = allFunctions.Where(f => GetMinimalTreeHeight(f) <= maxTreeHeight &&
111          GetMinimalTreeSize(f) <= maxTreeSize).ToArray();
112        IFunction selectedFunction = possibleFunctions[random.Next(possibleFunctions.Length)];
113        FunctionTree root = new FunctionTree(selectedFunction);
[142]114        MakeUnbalancedTree(root, maxTreeSize - 1, maxTreeHeight - 1);
115        return root;
[2]116      }
117    }
118
[143]119    internal IFunctionTree CreateRandomTree(IFunction rootFunction, int maxTreeSize, int maxTreeHeight, bool balanceTrees) {
120      IFunctionTree root = new FunctionTree(rootFunction);
[2]121      if (balanceTrees) {
122        MakeBalancedTree(root, maxTreeSize - 1, maxTreeHeight - 1);
123      } else {
124        MakeUnbalancedTree(root, maxTreeSize - 1, maxTreeHeight - 1);
125      }
126      if (GetTreeSize(root) > maxTreeSize ||
127         GetTreeHeight(root) > maxTreeHeight) {
128        throw new InvalidProgramException();
129      }
130      return root;
131    }
132
133
[142]134    private void MakeUnbalancedTree(IFunctionTree parent, int maxTreeSize, int maxTreeHeight) {
[2]135      if (maxTreeHeight == 0 || maxTreeSize == 0) return;
136      int minArity;
137      int maxArity;
[142]138      GetMinMaxArity(parent.Function, out minArity, out maxArity);
[2]139      if (maxArity >= maxTreeSize) {
140        maxArity = maxTreeSize;
141      }
142      int actualArity = random.Next(minArity, maxArity + 1);
143      if (actualArity > 0) {
144        int maxSubTreeSize = maxTreeSize / actualArity;
145        for (int i = 0; i < actualArity; i++) {
[143]146          IFunction[] possibleFunctions = GetAllowedSubFunctions(parent.Function, i).Where(f => GetMinimalTreeHeight(f) <= maxTreeHeight &&
147            GetMinimalTreeSize(f) <= maxSubTreeSize).ToArray();
148          IFunction selectedFunction = possibleFunctions[random.Next(possibleFunctions.Length)];
149          FunctionTree newSubTree = new FunctionTree(selectedFunction);
[142]150          MakeUnbalancedTree(newSubTree, maxSubTreeSize - 1, maxTreeHeight - 1);
151          parent.InsertSubTree(i, newSubTree);
[2]152        }
153      }
154    }
155
156    // NOTE: this method doesn't build fully balanced trees because we have constraints on the
[143]157    // types of possible sub-functions which can indirectly impose a limit for the depth of a given sub-tree
[142]158    private void MakeBalancedTree(IFunctionTree parent, int maxTreeSize, int maxTreeHeight) {
[2]159      if (maxTreeHeight == 0 || maxTreeSize == 0) return; // should never happen anyway
160      int minArity;
161      int maxArity;
[142]162      GetMinMaxArity(parent.Function, out minArity, out maxArity);
[2]163      if (maxArity >= maxTreeSize) {
164        maxArity = maxTreeSize;
165      }
166      int actualArity = random.Next(minArity, maxArity + 1);
167      if (actualArity > 0) {
168        int maxSubTreeSize = maxTreeSize / actualArity;
169        for (int i = 0; i < actualArity; i++) {
170          if (maxTreeHeight == 1 || maxSubTreeSize == 1) {
[143]171            IFunction[] possibleTerminals = GetAllowedSubFunctions(parent.Function, i).Where(
172              f => GetMinimalTreeHeight(f) <= maxTreeHeight &&
173              GetMinimalTreeSize(f) <= maxSubTreeSize &&
174              IsTerminal(f)).ToArray();
175            IFunction selectedTerminal = possibleTerminals[random.Next(possibleTerminals.Length)];
[142]176            IFunctionTree newTree = new FunctionTree(selectedTerminal);
177            parent.InsertSubTree(i, newTree);
[2]178          } else {
[143]179            IFunction[] possibleFunctions = GetAllowedSubFunctions(parent.Function, i).Where(
180              f => GetMinimalTreeHeight(f) <= maxTreeHeight &&
181              GetMinimalTreeSize(f) <= maxSubTreeSize &&
182              !IsTerminal(f)).ToArray();
183            IFunction selectedFunction = possibleFunctions[random.Next(possibleFunctions.Length)];
[142]184            FunctionTree newTree = new FunctionTree(selectedFunction);
185            parent.InsertSubTree(i, newTree);
186            MakeBalancedTree(newTree, maxSubTreeSize - 1, maxTreeHeight - 1);
[2]187          }
188        }
189      }
190    }
191
[142]192    internal CompositeOperation CreateInitializationOperation(ICollection<IFunctionTree> trees, IScope scope) {
[2]193      // needed for the parameter shaking operation
194      CompositeOperation initializationOperation = new CompositeOperation();
195      Scope tempScope = new Scope("Temp. initialization scope");
196
[143]197      var parametricTrees = trees.Where(t => t.Function.GetVariable(GPOperatorLibrary.INITIALIZATION) != null);
[2]198
[142]199      foreach (IFunctionTree tree in parametricTrees) {
[2]200        // enqueue an initialization operation for each operator with local variables
[142]201        IOperator initialization = (IOperator)tree.Function.GetVariable(GPOperatorLibrary.INITIALIZATION).Value;
[2]202        Scope initScope = new Scope();
203
204        // copy the local variables into a temporary scope used for initialization
[142]205        foreach (IVariable variable in tree.LocalVariables) {
206          initScope.AddVariable(variable);
[2]207        }
208
209        tempScope.AddSubScope(initScope);
210        initializationOperation.AddOperation(new AtomicOperation(initialization, initScope));
211      }
212
213      Scope backupScope = new Scope("backup");
214      foreach (Scope subScope in scope.SubScopes) {
215        backupScope.AddSubScope(subScope);
216      }
217
218      scope.AddSubScope(tempScope);
219      scope.AddSubScope(backupScope);
220
221      // add an operation to remove the temporary scopes       
222      initializationOperation.AddOperation(new AtomicOperation(new RightReducer(), scope));
223      return initializationOperation;
224    }
225    #endregion
226
227    #region tree information gathering
[142]228    internal int GetTreeSize(IFunctionTree tree) {
229      return 1 + tree.SubTrees.Sum(f => GetTreeSize(f));
[2]230    }
231
[142]232    internal int GetTreeHeight(IFunctionTree tree) {
233      if (tree.SubTrees.Count == 0) return 1;
234      return 1 + tree.SubTrees.Max(f => GetTreeHeight(f));
[2]235    }
236
[142]237    internal IFunctionTree GetRandomParentNode(IFunctionTree tree) {
238      List<IFunctionTree> parentNodes = new List<IFunctionTree>();
[2]239
240      // add null for the parent of the root node
241      parentNodes.Add(null);
242
[142]243      TreeForEach(tree, delegate(IFunctionTree possibleParentNode) {
244        if (possibleParentNode.SubTrees.Count > 0) {
245          parentNodes.Add(possibleParentNode);
[2]246        }
247      });
248
249      return parentNodes[random.Next(parentNodes.Count)];
250    }
251
[143]252    internal IList<IFunction> GetAllowedSubFunctions(IFunction f, int index) {
253      if (f == null) {
254        return allFunctions;
[2]255      } else {
256
257        SubOperatorsConstraintAnalyser analyser = new SubOperatorsConstraintAnalyser();
[143]258        analyser.AllPossibleOperators = allFunctions.Cast<IOperator>().ToArray<IOperator>();
[2]259
[143]260        return analyser.GetAllowedOperators(f, index).Cast<IFunction>().ToList<IFunction>();
[2]261      }
262    }
[143]263    internal void GetMinMaxArity(IFunction f, out int minArity, out int maxArity) {
264      foreach (IConstraint constraint in f.Constraints) {
[2]265        NumberOfSubOperatorsConstraint theConstraint = constraint as NumberOfSubOperatorsConstraint;
266        if (theConstraint != null) {
267          minArity = theConstraint.MinOperators.Data;
268          maxArity = theConstraint.MaxOperators.Data;
269          return;
270        }
271      }
272      // the default arity is 2
273      minArity = 2;
274      maxArity = 2;
275    }
[143]276    internal bool IsTerminal(IFunction f) {
[2]277      int minArity;
278      int maxArity;
279      GetMinMaxArity(f, out minArity, out maxArity);
280      return minArity == 0 && maxArity == 0;
281    }
282
[143]283    internal IList<IFunction> GetAllowedParents(IFunction child, int childIndex) {
284      List<IFunction> parents = new List<IFunction>();
285      foreach (IFunction function in functions) {
286        IList<IFunction> allowedSubFunctions = GetAllowedSubFunctions(function, childIndex);
287        if (allowedSubFunctions.Contains(child, new FunctionEqualityComparer())) {
[2]288          parents.Add(function);
289        }
290      }
291      return parents;
292    }
293
[143]294    internal ICollection<IFunctionTree> GetAllSubTrees(IFunctionTree root) {
295      List<IFunctionTree> allTrees = new List<IFunctionTree>();
296      TreeForEach(root, t => { allTrees.Add(t); });
297      return allTrees;
[2]298    }
299
300    /// <summary>
[143]301    /// returns the height level of branch in the tree
302    /// if the branch == tree => 1
303    /// if branch is in the sub-trees of tree => 2
[2]304    /// ...
[143]305    /// if branch is not found => -1
[2]306    /// </summary>
[143]307    /// <param name="tree">root of the function tree to process</param>
308    /// <param name="branch">branch that is searched in the tree</param>
[2]309    /// <returns></returns>
[143]310    internal int GetBranchLevel(IFunctionTree tree, IFunctionTree branch) {
311      return GetBranchLevelHelper(tree, branch, 1);
[2]312    }
313
[143]314    // 'tail-recursive' helper
315    private int GetBranchLevelHelper(IFunctionTree tree, IFunctionTree branch, int level) {
316      if (branch == tree) return level;
[2]317
[142]318      foreach (IFunctionTree subTree in tree.SubTrees) {
[143]319        int result = GetBranchLevelHelper(subTree, branch, level + 1);
[2]320        if (result != -1) return result;
321      }
322
323      return -1;
324    }
325
[142]326    internal bool IsValidTree(IFunctionTree tree) {
[145]327      foreach(IConstraint constraint in tree.Function.Constraints) {
328        if(constraint is NumberOfSubOperatorsConstraint) {
329          int max = ((NumberOfSubOperatorsConstraint)constraint).MaxOperators.Data;
330          int min = ((NumberOfSubOperatorsConstraint)constraint).MinOperators.Data;
331          if(tree.SubTrees.Count < min || tree.SubTrees.Count > max) return false;
332        }
[2]333      }
[145]334      foreach(IFunctionTree subTree in tree.SubTrees) {
335        if(!IsValidTree(subTree)) return false;
336      }
[2]337      return true;
338    }
339
[143]340    // returns a random branch from the specified level in the tree
341    internal IFunctionTree GetRandomBranch(IFunctionTree tree, int level) {
[2]342      if (level == 0) return tree;
[143]343      List<IFunctionTree> branches = GetBranchesAtLevel(tree, level);
344      return branches[random.Next(branches.Count)];
[2]345    }
346    #endregion
347
348    #region private utility methods
349
350    private int GetMinimalTreeHeight(IOperator op) {
351      return ((IntData)op.GetVariable(GPOperatorLibrary.MIN_TREE_HEIGHT).Value).Data;
352    }
353
354    private int GetMinimalTreeSize(IOperator op) {
355      return ((IntData)op.GetVariable(GPOperatorLibrary.MIN_TREE_SIZE).Value).Data;
356    }
357
[142]358    private void TreeForEach(IFunctionTree tree, Action<IFunctionTree> action) {
[2]359      action(tree);
[142]360      foreach (IFunctionTree subTree in tree.SubTrees) {
361        TreeForEach(subTree, action);
[2]362      }
363    }
364
[143]365    private List<IFunctionTree> GetBranchesAtLevel(IFunctionTree tree, int level) {
[142]366      if (level == 1) return new List<IFunctionTree>(tree.SubTrees);
[2]367
[143]368      List<IFunctionTree> branches = new List<IFunctionTree>();
[142]369      foreach (IFunctionTree subTree in tree.SubTrees) {
[143]370        branches.AddRange(GetBranchesAtLevel(subTree, level - 1));
[2]371      }
[143]372      return branches;
[2]373    }
374
375
376    #endregion
377
[143]378    internal class FunctionEqualityComparer : IEqualityComparer<IFunction> {
379      #region IEqualityComparer<IFunction> Members
380      public bool Equals(IFunction x, IFunction y) {
[142]381        return  x==y ||
382          ((StringData)x.GetVariable(GPOperatorLibrary.TYPE_ID).Value).Data ==
[2]383          ((StringData)y.GetVariable(GPOperatorLibrary.TYPE_ID).Value).Data;
384      }
385
[143]386      public int GetHashCode(IFunction obj) {
[2]387        return ((StringData)obj.GetVariable(GPOperatorLibrary.TYPE_ID).Value).Data.GetHashCode();
388      }
389      #endregion
390    }
391
[143]392    internal ICollection<IFunction> GetPossibleParents(List<IFunction> list) {
393      List<IFunction> result = new List<IFunction>();
394      foreach (IFunction f in functions) {
395        if (IsPossibleParent(f, list)) {
396          result.Add(f);
[2]397        }
398      }
399      return result;
400    }
401
[143]402    private bool IsPossibleParent(IFunction f, List<IFunction> children) {
[2]403      int minArity;
404      int maxArity;
[143]405      GetMinMaxArity(f, out minArity, out maxArity);
[2]406
407      // note: we can't assume that the operators in the children list have different types!
408
409      // when the maxArity of this function is smaller than the list of operators that
410      // should be included as sub-operators then it can't be a parent
411      if (maxArity < children.Count()) {
412        return false;
413      }
414      int nSlots = Math.Max(minArity, children.Count);
415
416      SubOperatorsConstraintAnalyser analyzer = new SubOperatorsConstraintAnalyser();
[143]417      analyzer.AllPossibleOperators = children.Cast<IOperator>().ToArray<IOperator>();
[2]418
[143]419      List<HashSet<IFunction>> slotSets = new List<HashSet<IFunction>>();
[2]420
[143]421      // we iterate through all slots for sub-trees and calculate the set of
422      // allowed functions for this slot.
[2]423      // we only count those slots that can hold at least one of the children that we should combine
424      for (int slot = 0; slot < nSlots; slot++) {
[143]425        HashSet<IFunction> functionSet = new HashSet<IFunction>(analyzer.GetAllowedOperators(f, slot).Cast<IFunction>());
426        if (functionSet.Count() > 0) {
427          slotSets.Add(functionSet);
[2]428        }
429      }
430
431      // ok at the end of this operation we know how many slots of the parent can actually
432      // hold one of our children.
433      // if the number of slots is smaller than the number of children we can be sure that
[143]434      // we can never combine all children as sub-trees of the function and thus the function
[2]435      // can't be a parent.
436      if (slotSets.Count() < children.Count()) {
437        return false;
438      }
439
440      // finally we sort the sets by size and beginning from the first set select one
[143]441      // function for the slot and thus remove it as possible sub-tree from the remaining sets.
442      // when we can successfully assign all available children to a slot the function is a valid parent
443      // when only a subset of all children can be assigned to slots the function is no valid parent
[2]444      slotSets.Sort((p, q) => p.Count() - q.Count());
445
446      int assignments = 0;
447      for (int i = 0; i < slotSets.Count() - 1; i++) {
448        if (slotSets[i].Count > 0) {
[143]449          IFunction selected = slotSets[i].ElementAt(0);
[2]450          assignments++;
451          for (int j = i + 1; j < slotSets.Count(); j++) {
452            slotSets[j].Remove(selected);
453          }
454        }
455      }
456
457      // sanity check
458      if (assignments > children.Count) throw new InvalidProgramException();
459      return assignments == children.Count - 1;
460    }
461  }
462}
Note: See TracBrowser for help on using the repository browser.