Free cookie consent management tool by TermsFeed Policy Generator

Changeset 180


Ignore:
Timestamp:
04/24/08 14:29:31 (17 years ago)
Author:
gkronber
Message:
  • minor refactoring of TreeGardener
  • added descriptive comments for tree-creation methods
Location:
trunk/sources/HeuristicLab.StructureIdentification
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/sources/HeuristicLab.StructureIdentification/Manipulation/ChangeNodeTypeManipulation.cs

    r167 r180  
    128128
    129129    private IFunctionTree ChangeTerminalType(IFunctionTree parent, IFunctionTree child, int childIndex, TreeGardener gardener, MersenneTwister random) {
    130       IList<IFunction> allowedChildren;
     130      IList<IFunction> allowedTerminals;
    131131      if (parent == null) {
    132         allowedChildren = gardener.Terminals;
     132        allowedTerminals = gardener.Terminals;
    133133      } else {
    134         allowedChildren = new List<IFunction>();
     134        allowedTerminals = new List<IFunction>();
    135135        var allAllowedChildren = gardener.GetAllowedSubFunctions(parent.Function, childIndex);
    136136        foreach(IFunction c in allAllowedChildren) {
    137           if(gardener.IsTerminal(c)) allowedChildren.Add(c);
     137          if(gardener.IsTerminal(c)) allowedTerminals.Add(c);
    138138        }
    139139      }
    140140      // selecting from the terminals should always work since the current child was also a terminal
    141141      // so in the worst case we will just create a new terminal of the same type again.
    142       return gardener.CreateRandomTree(allowedChildren[random.Next(allowedChildren.Count)], 1, 1, false);
     142      return gardener.CreateRandomTree(allowedTerminals, 1, 1);
    143143    }
    144144
  • trunk/sources/HeuristicLab.StructureIdentification/TreeGardener.cs

    r179 r180  
    5050    }
    5151
     52    #region constructors
    5253    internal TreeGardener(IRandom random, GPOperatorLibrary funLibrary) {
    5354      this.random = random;
     
    6970      }
    7071    }
     72    #endregion
    7173
    7274    #region random initialization
     75    /// <summary>
     76    /// Creates a random balanced tree with a maximal size and height. When the max-height or max-size are 1 it will return a random terminal.
     77    /// In other cases it will return either a terminal (tree of size 1) or any other tree with a function in it's root (at least height 2).
     78    /// </summary>
     79    /// <param name="maxTreeSize">Maximal size of the tree (number of nodes).</param>
     80    /// <param name="maxTreeHeight">Maximal height of the tree.</param>
     81    /// <returns></returns>
    7382    internal IFunctionTree CreateBalancedRandomTree(int maxTreeSize, int maxTreeHeight) {
     83      IFunctionTree root = CreateRandomRoot(maxTreeSize, maxTreeHeight);
     84      MakeBalancedTree(root, maxTreeSize - 1, maxTreeHeight - 1);
     85      return root;
     86    }
     87
     88    /// <summary>
     89    /// Creates a random (unbalanced) tree with a maximal size and height. When the max-height or max-size are 1 it will return a random terminal.
     90    /// In other cases it will return either a terminal (tree of size 1) or any other tree with a function in it's root (at least height 2).
     91    /// </summary>
     92    /// <param name="maxTreeSize">Maximal size of the tree (number of nodes).</param>
     93    /// <param name="maxTreeHeight">Maximal height of the tree.</param>
     94    /// <returns></returns>
     95    internal IFunctionTree CreateUnbalancedRandomTree(int maxTreeSize, int maxTreeHeight) {
     96      IFunctionTree root = CreateRandomRoot(maxTreeSize, maxTreeHeight);
     97      MakeUnbalancedTree(root, maxTreeSize - 1, maxTreeHeight - 1);
     98      return root;
     99    }
     100
     101    /// <summary>
     102    /// selects a random function from allowedFunctions and creates a random (unbalanced) tree with maximal size and height.
     103    /// </summary>
     104    /// <param name="allowedFunctions">Set of allowed functions.</param>
     105    /// <param name="maxTreeSize">Maximal size of the tree (number of nodes).</param>
     106    /// <param name="maxTreeHeight">Maximal height of the tree.</param>
     107    /// <returns>New random unbalanced tree</returns>
     108    internal IFunctionTree CreateRandomTree(ICollection<IFunction> allowedFunctions, int maxTreeSize, int maxTreeHeight) {
     109      // default is non-balanced trees
     110      return CreateRandomTree(allowedFunctions, maxTreeSize, maxTreeHeight, false);
     111    }
     112
     113    /// <summary>
     114    /// selects a random function from allowedFunctions and creates a (un)balanced random tree with maximal size and height.
     115    /// </summary>
     116    /// <param name="allowedFunctions">Set of allowed functions.</param>
     117    /// <param name="maxTreeSize">Maximal size of the tree (number of nodes).</param>
     118    /// <param name="maxTreeHeight">Maximal height of the tree.</param>
     119    /// <param name="balanceTrees">Flag determining whether the tree should be balanced or not.</param>
     120    /// <returns>New random tree</returns>
     121    internal IFunctionTree CreateRandomTree(ICollection<IFunction> allowedFunctions, int maxTreeSize, int maxTreeHeight, bool balanceTrees) {
     122      int minTreeHeight = allowedFunctions.Select(f => ((IntData)f.GetVariable(GPOperatorLibrary.MIN_TREE_HEIGHT).Value).Data).Min();
     123      if(minTreeHeight > maxTreeHeight)
     124        maxTreeHeight = minTreeHeight;
     125
     126      int minTreeSize = allowedFunctions.Select(f => ((IntData)f.GetVariable(GPOperatorLibrary.MIN_TREE_SIZE).Value).Data).Min();
     127      if(minTreeSize > maxTreeSize)
     128        maxTreeSize = minTreeSize;
     129
     130      int treeHeight = random.Next(minTreeHeight, maxTreeHeight + 1);
     131      int treeSize = random.Next(minTreeSize, maxTreeSize + 1);
     132
     133      IFunction[] possibleFunctions = allowedFunctions.Where(f => ((IntData)f.GetVariable(GPOperatorLibrary.MIN_TREE_HEIGHT).Value).Data <= treeHeight &&
     134        ((IntData)f.GetVariable(GPOperatorLibrary.MIN_TREE_SIZE).Value).Data <= treeSize).ToArray();
     135      IFunction selectedFunction = possibleFunctions[random.Next(possibleFunctions.Length)];
     136
     137      IFunctionTree root = new FunctionTree(selectedFunction);
     138      if(balanceTrees) {
     139        MakeBalancedTree(root, maxTreeSize - 1, maxTreeHeight - 1);
     140      } else {
     141        MakeUnbalancedTree(root, maxTreeSize - 1, maxTreeHeight - 1);
     142      }
     143      return root;
     144    }
     145
     146    internal CompositeOperation CreateInitializationOperation(ICollection<IFunctionTree> trees, IScope scope) {
     147      // needed for the parameter shaking operation
     148      CompositeOperation initializationOperation = new CompositeOperation();
     149      Scope tempScope = new Scope("Temp. initialization scope");
     150
     151      var parametricTrees = trees.Where(t => t.Function.GetVariable(GPOperatorLibrary.INITIALIZATION) != null);
     152      foreach(IFunctionTree tree in parametricTrees) {
     153        // enqueue an initialization operation for each operator with local variables
     154        IOperator initialization = (IOperator)tree.Function.GetVariable(GPOperatorLibrary.INITIALIZATION).Value;
     155        Scope initScope = new Scope();
     156        // copy the local variables into a temporary scope used for initialization
     157        foreach(IVariable variable in tree.LocalVariables) {
     158          initScope.AddVariable(variable);
     159        }
     160        tempScope.AddSubScope(initScope);
     161        initializationOperation.AddOperation(new AtomicOperation(initialization, initScope));
     162      }
     163      Scope backupScope = new Scope("backup");
     164      foreach(Scope subScope in scope.SubScopes) {
     165        backupScope.AddSubScope(subScope);
     166      }
     167      scope.AddSubScope(tempScope);
     168      scope.AddSubScope(backupScope);
     169      // add an operation to remove the temporary scopes       
     170      initializationOperation.AddOperation(new AtomicOperation(new RightReducer(), scope));
     171      return initializationOperation;
     172    }
     173    #endregion
     174
     175    #region tree information gathering
     176    internal int GetTreeSize(IFunctionTree tree) {
     177      return 1 + tree.SubTrees.Sum(f => GetTreeSize(f));
     178    }
     179
     180    internal int GetTreeHeight(IFunctionTree tree) {
     181      if(tree.SubTrees.Count == 0) return 1;
     182      return 1 + tree.SubTrees.Max(f => GetTreeHeight(f));
     183    }
     184
     185    internal IFunctionTree GetRandomParentNode(IFunctionTree tree) {
     186      List<IFunctionTree> parentNodes = new List<IFunctionTree>();
     187
     188      // add null for the parent of the root node
     189      parentNodes.Add(null);
     190
     191      TreeForEach(tree, delegate(IFunctionTree possibleParentNode) {
     192        if(possibleParentNode.SubTrees.Count > 0) {
     193          parentNodes.Add(possibleParentNode);
     194        }
     195      });
     196
     197      return parentNodes[random.Next(parentNodes.Count)];
     198    }
     199
     200    internal ICollection<IFunctionTree> GetAllSubTrees(IFunctionTree root) {
     201      List<IFunctionTree> allTrees = new List<IFunctionTree>();
     202      TreeForEach(root, t => { allTrees.Add(t); });
     203      return allTrees;
     204    }
     205
     206    /// <summary>
     207    /// returns the height level of branch in the tree
     208    /// if the branch == tree => 1
     209    /// if branch is in the sub-trees of tree => 2
     210    /// ...
     211    /// if branch is not found => -1
     212    /// </summary>
     213    /// <param name="tree">root of the function tree to process</param>
     214    /// <param name="branch">branch that is searched in the tree</param>
     215    /// <returns></returns>
     216    internal int GetBranchLevel(IFunctionTree tree, IFunctionTree branch) {
     217      return GetBranchLevelHelper(tree, branch, 1);
     218    }
     219
     220    // 'tail-recursive' helper
     221    private int GetBranchLevelHelper(IFunctionTree tree, IFunctionTree branch, int level) {
     222      if(branch == tree) return level;
     223
     224      foreach(IFunctionTree subTree in tree.SubTrees) {
     225        int result = GetBranchLevelHelper(subTree, branch, level + 1);
     226        if(result != -1) return result;
     227      }
     228
     229      return -1;
     230    }
     231
     232    internal bool IsValidTree(IFunctionTree tree) {
     233      foreach(IConstraint constraint in tree.Function.Constraints) {
     234        if(constraint is NumberOfSubOperatorsConstraint) {
     235          int max = ((NumberOfSubOperatorsConstraint)constraint).MaxOperators.Data;
     236          int min = ((NumberOfSubOperatorsConstraint)constraint).MinOperators.Data;
     237          if(tree.SubTrees.Count < min || tree.SubTrees.Count > max)
     238            return false;
     239        }
     240      }
     241      foreach(IFunctionTree subTree in tree.SubTrees) {
     242        if(!IsValidTree(subTree)) return false;
     243      }
     244      return true;
     245    }
     246
     247    // returns a random branch from the specified level in the tree
     248    internal IFunctionTree GetRandomBranch(IFunctionTree tree, int level) {
     249      if(level == 0) return tree;
     250      List<IFunctionTree> branches = GetBranchesAtLevel(tree, level);
     251      return branches[random.Next(branches.Count)];
     252    }
     253    #endregion
     254
     255    #region function information (arity, allowed childs and parents)
     256    internal ICollection<IFunction> GetPossibleParents(List<IFunction> list) {
     257      List<IFunction> result = new List<IFunction>();
     258      foreach(IFunction f in functions) {
     259        if(IsPossibleParent(f, list)) {
     260          result.Add(f);
     261        }
     262      }
     263      return result;
     264    }
     265
     266    private bool IsPossibleParent(IFunction f, List<IFunction> children) {
     267      int minArity;
     268      int maxArity;
     269      GetMinMaxArity(f, out minArity, out maxArity);
     270
     271      // note: we can't assume that the operators in the children list have different types!
     272
     273      // when the maxArity of this function is smaller than the list of operators that
     274      // should be included as sub-operators then it can't be a parent
     275      if(maxArity < children.Count()) {
     276        return false;
     277      }
     278      int nSlots = Math.Max(minArity, children.Count);
     279
     280      SubOperatorsConstraintAnalyser analyzer = new SubOperatorsConstraintAnalyser();
     281      analyzer.AllPossibleOperators = children.Cast<IOperator>().ToArray<IOperator>();
     282
     283      List<HashSet<IFunction>> slotSets = new List<HashSet<IFunction>>();
     284
     285      // we iterate through all slots for sub-trees and calculate the set of
     286      // allowed functions for this slot.
     287      // we only count those slots that can hold at least one of the children that we should combine
     288      for(int slot = 0; slot < nSlots; slot++) {
     289        HashSet<IFunction> functionSet = new HashSet<IFunction>(analyzer.GetAllowedOperators(f, slot).Cast<IFunction>());
     290        if(functionSet.Count() > 0) {
     291          slotSets.Add(functionSet);
     292        }
     293      }
     294
     295      // ok at the end of this operation we know how many slots of the parent can actually
     296      // hold one of our children.
     297      // if the number of slots is smaller than the number of children we can be sure that
     298      // we can never combine all children as sub-trees of the function and thus the function
     299      // can't be a parent.
     300      if(slotSets.Count() < children.Count()) {
     301        return false;
     302      }
     303
     304      // finally we sort the sets by size and beginning from the first set select one
     305      // function for the slot and thus remove it as possible sub-tree from the remaining sets.
     306      // when we can successfully assign all available children to a slot the function is a valid parent
     307      // when only a subset of all children can be assigned to slots the function is no valid parent
     308      slotSets.Sort((p, q) => p.Count() - q.Count());
     309
     310      int assignments = 0;
     311      for(int i = 0; i < slotSets.Count() - 1; i++) {
     312        if(slotSets[i].Count > 0) {
     313          IFunction selected = slotSets[i].ElementAt(0);
     314          assignments++;
     315          for(int j = i + 1; j < slotSets.Count(); j++) {
     316            slotSets[j].Remove(selected);
     317          }
     318        }
     319      }
     320
     321      // sanity check
     322      if(assignments > children.Count) throw new InvalidProgramException();
     323      return assignments == children.Count - 1;
     324    }
     325    internal IList<IFunction> GetAllowedParents(IFunction child, int childIndex) {
     326      List<IFunction> parents = new List<IFunction>();
     327      foreach(IFunction function in functions) {
     328        ICollection<IFunction> allowedSubFunctions = GetAllowedSubFunctions(function, childIndex);
     329        if(allowedSubFunctions.Contains(child)) {
     330          parents.Add(function);
     331        }
     332      }
     333      return parents;
     334    }
     335    internal bool IsTerminal(IFunction f) {
     336      int minArity;
     337      int maxArity;
     338      GetMinMaxArity(f, out minArity, out maxArity);
     339      return minArity == 0 && maxArity == 0;
     340    }
     341    internal IList<IFunction> GetAllowedSubFunctions(IFunction f, int index) {
     342      if(f == null) {
     343        return allFunctions;
     344      } else {
     345        ItemList slotList = (ItemList)f.GetVariable(GPOperatorLibrary.ALLOWED_SUBOPERATORS).Value;
     346        List<IFunction> result = new List<IFunction>();
     347        foreach(IFunction function in (ItemList)slotList[index]) {
     348          result.Add(function);
     349        }
     350        return result;
     351      }
     352    }
     353    internal void GetMinMaxArity(IFunction f, out int minArity, out int maxArity) {
     354      foreach(IConstraint constraint in f.Constraints) {
     355        NumberOfSubOperatorsConstraint theConstraint = constraint as NumberOfSubOperatorsConstraint;
     356        if(theConstraint != null) {
     357          minArity = theConstraint.MinOperators.Data;
     358          maxArity = theConstraint.MaxOperators.Data;
     359          return;
     360        }
     361      }
     362      // the default arity is 2
     363      minArity = 2;
     364      maxArity = 2;
     365    }
     366    #endregion
     367
     368    #region private utility methods
     369    private IFunctionTree CreateRandomRoot(int maxTreeSize, int maxTreeHeight) {
    74370      if(maxTreeHeight == 1 || maxTreeSize == 1) {
    75371        IFunction selectedTerminal = terminals[random.Next(terminals.Count())];
    76372        return new FunctionTree(selectedTerminal);
    77373      } else {
    78         IFunction[] possibleFunctions = functions.Where(f => GetMinimalTreeHeight(f) <= maxTreeHeight &&
     374        IFunction[] possibleFunctions = allFunctions.Where(f => GetMinimalTreeHeight(f) <= maxTreeHeight &&
    79375          GetMinimalTreeSize(f) <= maxTreeSize).ToArray();
    80376        IFunction selectedFunction = possibleFunctions[random.Next(possibleFunctions.Length)];
    81         FunctionTree root = new FunctionTree(selectedFunction);
    82         MakeBalancedTree(root, maxTreeSize - 1, maxTreeHeight - 1);
    83         return root;
    84       }
    85     }
    86 
    87     internal IFunctionTree CreateUnbalancedRandomTree(int maxTreeSize, int maxTreeHeight) {
    88       IFunction[] possibleFunctions = allFunctions.Where(f => GetMinimalTreeHeight(f) <= maxTreeHeight &&
    89         GetMinimalTreeSize(f) <= maxTreeSize).ToArray();
    90       IFunction selectedFunction = possibleFunctions[random.Next(possibleFunctions.Length)];
    91       FunctionTree root = new FunctionTree(selectedFunction);
    92       MakeUnbalancedTree(root, maxTreeSize - 1, maxTreeHeight - 1);
    93       return root;
    94     }
    95 
    96     internal IFunctionTree CreateRandomTree(ICollection<IFunction> allowedFunctions, int maxTreeSize, int maxTreeHeight) {
    97       // default is non-balanced trees
    98       return CreateRandomTree(allowedFunctions, maxTreeSize, maxTreeHeight, false);
    99     }
    100 
    101     internal IFunctionTree CreateRandomTree(ICollection<IFunction> allowedFunctions, int maxTreeSize, int maxTreeHeight, bool balanceTrees) {
    102 
    103       int minTreeHeight = allowedFunctions.Select(f => ((IntData)f.GetVariable(GPOperatorLibrary.MIN_TREE_HEIGHT).Value).Data).Min();
    104       if(minTreeHeight > maxTreeHeight)
    105         maxTreeHeight = minTreeHeight;
    106 
    107       int minTreeSize = allowedFunctions.Select(f => ((IntData)f.GetVariable(GPOperatorLibrary.MIN_TREE_SIZE).Value).Data).Min();
    108       if(minTreeSize > maxTreeSize)
    109         maxTreeSize = minTreeSize;
    110 
    111       int treeHeight = random.Next(minTreeHeight, maxTreeHeight + 1);
    112       int treeSize = random.Next(minTreeSize, maxTreeSize + 1);
    113 
    114       IFunction[] possibleFunctions = allowedFunctions.Where(f => ((IntData)f.GetVariable(GPOperatorLibrary.MIN_TREE_HEIGHT).Value).Data <= treeHeight &&
    115         ((IntData)f.GetVariable(GPOperatorLibrary.MIN_TREE_SIZE).Value).Data <= treeSize).ToArray();
    116       IFunction selectedFunction = possibleFunctions[random.Next(possibleFunctions.Length)];
    117 
    118       return CreateRandomTree(selectedFunction, treeSize, treeHeight, balanceTrees);
    119     }
    120 
    121     internal IFunctionTree CreateRandomTree(IFunction rootFunction, int maxTreeSize, int maxTreeHeight, bool balanceTrees) {
    122       IFunctionTree root = new FunctionTree(rootFunction);
    123       if(balanceTrees) {
    124         MakeBalancedTree(root, maxTreeSize - 1, maxTreeHeight - 1);
    125       } else {
    126         MakeUnbalancedTree(root, maxTreeSize - 1, maxTreeHeight - 1);
    127       }
    128       if(GetTreeSize(root) > maxTreeSize ||
    129          GetTreeHeight(root) > maxTreeHeight) {
    130         throw new InvalidProgramException();
    131       }
    132       return root;
     377        return new FunctionTree(selectedFunction);
     378      }
    133379    }
    134380
     
    158404    // types of possible sub-functions which can indirectly impose a limit for the depth of a given sub-tree
    159405    private void MakeBalancedTree(IFunctionTree parent, int maxTreeSize, int maxTreeHeight) {
    160       if(maxTreeHeight == 0 || maxTreeSize == 0) return; // should never happen anyway
     406      if(maxTreeHeight == 0 || maxTreeSize == 0) return;
    161407      int minArity;
    162408      int maxArity;
     
    191437    }
    192438
    193     internal CompositeOperation CreateInitializationOperation(ICollection<IFunctionTree> trees, IScope scope) {
    194       // needed for the parameter shaking operation
    195       CompositeOperation initializationOperation = new CompositeOperation();
    196       Scope tempScope = new Scope("Temp. initialization scope");
    197 
    198       var parametricTrees = trees.Where(t => t.Function.GetVariable(GPOperatorLibrary.INITIALIZATION) != null);
    199 
    200       foreach(IFunctionTree tree in parametricTrees) {
    201         // enqueue an initialization operation for each operator with local variables
    202         IOperator initialization = (IOperator)tree.Function.GetVariable(GPOperatorLibrary.INITIALIZATION).Value;
    203         Scope initScope = new Scope();
    204 
    205         // copy the local variables into a temporary scope used for initialization
    206         foreach(IVariable variable in tree.LocalVariables) {
    207           initScope.AddVariable(variable);
    208         }
    209 
    210         tempScope.AddSubScope(initScope);
    211         initializationOperation.AddOperation(new AtomicOperation(initialization, initScope));
    212       }
    213 
    214       Scope backupScope = new Scope("backup");
    215       foreach(Scope subScope in scope.SubScopes) {
    216         backupScope.AddSubScope(subScope);
    217       }
    218 
    219       scope.AddSubScope(tempScope);
    220       scope.AddSubScope(backupScope);
    221 
    222       // add an operation to remove the temporary scopes       
    223       initializationOperation.AddOperation(new AtomicOperation(new RightReducer(), scope));
    224       return initializationOperation;
    225     }
    226     #endregion
    227 
    228     #region tree information gathering
    229     internal int GetTreeSize(IFunctionTree tree) {
    230       return 1 + tree.SubTrees.Sum(f => GetTreeSize(f));
    231     }
    232 
    233     internal int GetTreeHeight(IFunctionTree tree) {
    234       if(tree.SubTrees.Count == 0) return 1;
    235       return 1 + tree.SubTrees.Max(f => GetTreeHeight(f));
    236     }
    237 
    238     internal IFunctionTree GetRandomParentNode(IFunctionTree tree) {
    239       List<IFunctionTree> parentNodes = new List<IFunctionTree>();
    240 
    241       // add null for the parent of the root node
    242       parentNodes.Add(null);
    243 
    244       TreeForEach(tree, delegate(IFunctionTree possibleParentNode) {
    245         if(possibleParentNode.SubTrees.Count > 0) {
    246           parentNodes.Add(possibleParentNode);
    247         }
    248       });
    249 
    250       return parentNodes[random.Next(parentNodes.Count)];
    251     }
    252 
    253     internal ICollection<IFunctionTree> GetAllSubTrees(IFunctionTree root) {
    254       List<IFunctionTree> allTrees = new List<IFunctionTree>();
    255       TreeForEach(root, t => { allTrees.Add(t); });
    256       return allTrees;
    257     }
    258 
    259     /// <summary>
    260     /// returns the height level of branch in the tree
    261     /// if the branch == tree => 1
    262     /// if branch is in the sub-trees of tree => 2
    263     /// ...
    264     /// if branch is not found => -1
    265     /// </summary>
    266     /// <param name="tree">root of the function tree to process</param>
    267     /// <param name="branch">branch that is searched in the tree</param>
    268     /// <returns></returns>
    269     internal int GetBranchLevel(IFunctionTree tree, IFunctionTree branch) {
    270       return GetBranchLevelHelper(tree, branch, 1);
    271     }
    272 
    273     // 'tail-recursive' helper
    274     private int GetBranchLevelHelper(IFunctionTree tree, IFunctionTree branch, int level) {
    275       if(branch == tree) return level;
    276 
    277       foreach(IFunctionTree subTree in tree.SubTrees) {
    278         int result = GetBranchLevelHelper(subTree, branch, level + 1);
    279         if(result != -1) return result;
    280       }
    281 
    282       return -1;
    283     }
    284 
    285     internal bool IsValidTree(IFunctionTree tree) {
    286       foreach(IConstraint constraint in tree.Function.Constraints) {
    287         if(constraint is NumberOfSubOperatorsConstraint) {
    288           int max = ((NumberOfSubOperatorsConstraint)constraint).MaxOperators.Data;
    289           int min = ((NumberOfSubOperatorsConstraint)constraint).MinOperators.Data;
    290           if(tree.SubTrees.Count < min || tree.SubTrees.Count > max)
    291             return false;
    292         }
    293       }
    294       foreach(IFunctionTree subTree in tree.SubTrees) {
    295         if(!IsValidTree(subTree)) return false;
    296       }
    297       return true;
    298     }
    299 
    300     // returns a random branch from the specified level in the tree
    301     internal IFunctionTree GetRandomBranch(IFunctionTree tree, int level) {
    302       if(level == 0) return tree;
    303       List<IFunctionTree> branches = GetBranchesAtLevel(tree, level);
    304       return branches[random.Next(branches.Count)];
    305     }
    306     #endregion
    307 
    308     #region function information (arity, allowed childs and parents)
    309     internal ICollection<IFunction> GetPossibleParents(List<IFunction> list) {
    310       List<IFunction> result = new List<IFunction>();
    311       foreach(IFunction f in functions) {
    312         if(IsPossibleParent(f, list)) {
    313           result.Add(f);
    314         }
    315       }
    316       return result;
    317     }
    318 
    319     private bool IsPossibleParent(IFunction f, List<IFunction> children) {
    320       int minArity;
    321       int maxArity;
    322       GetMinMaxArity(f, out minArity, out maxArity);
    323 
    324       // note: we can't assume that the operators in the children list have different types!
    325 
    326       // when the maxArity of this function is smaller than the list of operators that
    327       // should be included as sub-operators then it can't be a parent
    328       if(maxArity < children.Count()) {
    329         return false;
    330       }
    331       int nSlots = Math.Max(minArity, children.Count);
    332 
    333       SubOperatorsConstraintAnalyser analyzer = new SubOperatorsConstraintAnalyser();
    334       analyzer.AllPossibleOperators = children.Cast<IOperator>().ToArray<IOperator>();
    335 
    336       List<HashSet<IFunction>> slotSets = new List<HashSet<IFunction>>();
    337 
    338       // we iterate through all slots for sub-trees and calculate the set of
    339       // allowed functions for this slot.
    340       // we only count those slots that can hold at least one of the children that we should combine
    341       for(int slot = 0; slot < nSlots; slot++) {
    342         HashSet<IFunction> functionSet = new HashSet<IFunction>(analyzer.GetAllowedOperators(f, slot).Cast<IFunction>());
    343         if(functionSet.Count() > 0) {
    344           slotSets.Add(functionSet);
    345         }
    346       }
    347 
    348       // ok at the end of this operation we know how many slots of the parent can actually
    349       // hold one of our children.
    350       // if the number of slots is smaller than the number of children we can be sure that
    351       // we can never combine all children as sub-trees of the function and thus the function
    352       // can't be a parent.
    353       if(slotSets.Count() < children.Count()) {
    354         return false;
    355       }
    356 
    357       // finally we sort the sets by size and beginning from the first set select one
    358       // function for the slot and thus remove it as possible sub-tree from the remaining sets.
    359       // when we can successfully assign all available children to a slot the function is a valid parent
    360       // when only a subset of all children can be assigned to slots the function is no valid parent
    361       slotSets.Sort((p, q) => p.Count() - q.Count());
    362 
    363       int assignments = 0;
    364       for(int i = 0; i < slotSets.Count() - 1; i++) {
    365         if(slotSets[i].Count > 0) {
    366           IFunction selected = slotSets[i].ElementAt(0);
    367           assignments++;
    368           for(int j = i + 1; j < slotSets.Count(); j++) {
    369             slotSets[j].Remove(selected);
    370           }
    371         }
    372       }
    373 
    374       // sanity check
    375       if(assignments > children.Count) throw new InvalidProgramException();
    376       return assignments == children.Count - 1;
    377     }
    378     internal IList<IFunction> GetAllowedParents(IFunction child, int childIndex) {
    379       List<IFunction> parents = new List<IFunction>();
    380       foreach(IFunction function in functions) {
    381         ICollection<IFunction> allowedSubFunctions = GetAllowedSubFunctions(function, childIndex);
    382         if(allowedSubFunctions.Contains(child)) {
    383           parents.Add(function);
    384         }
    385       }
    386       return parents;
    387     }
    388     internal bool IsTerminal(IFunction f) {
    389       int minArity;
    390       int maxArity;
    391       GetMinMaxArity(f, out minArity, out maxArity);
    392       return minArity == 0 && maxArity == 0;
    393     }
    394     internal IList<IFunction> GetAllowedSubFunctions(IFunction f, int index) {
    395       if(f == null) {
    396         return allFunctions;
    397       } else {
    398         ItemList slotList = (ItemList)f.GetVariable(GPOperatorLibrary.ALLOWED_SUBOPERATORS).Value;
    399         List<IFunction> result = new List<IFunction>();
    400         foreach(IFunction function in (ItemList)slotList[index]) {
    401           result.Add(function);
    402         }
    403         return result;
    404       }
    405     }
    406     internal void GetMinMaxArity(IFunction f, out int minArity, out int maxArity) {
    407       foreach(IConstraint constraint in f.Constraints) {
    408         NumberOfSubOperatorsConstraint theConstraint = constraint as NumberOfSubOperatorsConstraint;
    409         if(theConstraint != null) {
    410           minArity = theConstraint.MinOperators.Data;
    411           maxArity = theConstraint.MaxOperators.Data;
    412           return;
    413         }
    414       }
    415       // the default arity is 2
    416       minArity = 2;
    417       maxArity = 2;
    418     }
    419     #endregion
    420 
    421     #region private utility methods
    422 
    423439    private int GetMinimalTreeHeight(IOperator op) {
    424440      return ((IntData)op.GetVariable(GPOperatorLibrary.MIN_TREE_HEIGHT).Value).Data;
Note: See TracChangeset for help on using the changeset viewer.