Free cookie consent management tool by TermsFeed Policy Generator

Changeset 815


Ignore:
Timestamp:
11/25/08 23:20:03 (16 years ago)
Author:
gkronber
Message:

fixed #392 (GP uniform- and onepoint crossover should only create one child per crossover event)

Location:
trunk/sources/HeuristicLab.GP
Files:
1 added
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/sources/HeuristicLab.GP/HeuristicLab.GP.csproj

    r802 r815  
    7171    <Compile Include="Logging\TreeArityAnalyser.cs" />
    7272    <Compile Include="OffspringEqualiser.cs" />
     73    <Compile Include="Recombination\GPCrossoverBase.cs" />
    7374    <Compile Include="Recombination\UniformCrossover.cs" />
    7475    <Compile Include="UnknownFunctionException.cs" />
  • trunk/sources/HeuristicLab.GP/Recombination/OnePointCrossOver.cs

    r812 r815  
    3636  /// W. B. Langdon and R. Poli.  Foundations of Genetic Programming. Springer-Verlag, 2002.
    3737  /// </summary>
    38   public class OnePointCrossOver : OperatorBase {
     38  public class OnePointCrossOver : GPCrossoverBase {
     39    // internal data structure to represent crossover points
     40    private class CrossoverPoint {
     41      public IFunctionTree parent0;
     42      public IFunctionTree parent1;
     43      public int childIndex;
     44    }
    3945    public override string Description {
    4046      get {
     
    4248      }
    4349    }
    44     public OnePointCrossOver()
    45       : base() {
    46       AddVariableInfo(new VariableInfo("Random", "Pseudo random number generator", typeof(MersenneTwister), VariableKind.In));
    47       AddVariableInfo(new VariableInfo("OperatorLibrary", "The operator library containing all available operators", typeof(GPOperatorLibrary), VariableKind.In));
    48       AddVariableInfo(new VariableInfo("FunctionTree", "The tree to mutate", typeof(IFunctionTree), VariableKind.In | VariableKind.New));
    49       AddVariableInfo(new VariableInfo("TreeSize", "The size (number of nodes) of the tree", typeof(IntData), VariableKind.In | VariableKind.New));
    50       AddVariableInfo(new VariableInfo("TreeHeight", "The height of the tree", typeof(IntData), VariableKind.In | VariableKind.New));
    51     }
    5250
    53     public override IOperation Apply(IScope scope) {
    54       MersenneTwister random = GetVariableValue<MersenneTwister>("Random", scope, true);
    55       GPOperatorLibrary opLibrary = GetVariableValue<GPOperatorLibrary>("OperatorLibrary", scope, true);
    56       TreeGardener gardener = new TreeGardener(random, opLibrary);
    57 
    58       if ((scope.SubScopes.Count % 2) != 0)
    59         throw new InvalidOperationException("Number of parents is not even");
    60 
    61       int crossoverEvents = scope.SubScopes.Count / 2;
    62       for (int i = 0; i < crossoverEvents; i++) {
    63         IScope parent1 = scope.SubScopes[0];
    64         scope.RemoveSubScope(parent1);
    65         IScope parent2 = scope.SubScopes[0];
    66         scope.RemoveSubScope(parent2);
    67         IScope child0 = new Scope((i * 2).ToString());
    68         IScope child1 = new Scope((i * 2 + 1).ToString());
    69         Cross(scope, random, gardener, parent1, parent2, child0, child1);
    70         scope.AddSubScope(child0);
    71         scope.AddSubScope(child1);
    72       }
    73       return null;
    74     }
    75 
    76     private void Cross(IScope scope, MersenneTwister random, TreeGardener gardener, IScope parent1, IScope parent2, IScope child0, IScope child1) {
    77       IFunctionTree newTree0;
    78       IFunctionTree newTree1;
    79       Cross(random, gardener, parent1, parent2, out newTree0, out newTree1);
    80 
    81       child0.AddVariable(new HeuristicLab.Core.Variable(scope.TranslateName("FunctionTree"), newTree0));
    82       child0.AddVariable(new HeuristicLab.Core.Variable(scope.TranslateName("TreeSize"), new IntData(newTree0.Size)));
    83       child0.AddVariable(new HeuristicLab.Core.Variable(scope.TranslateName("TreeHeight"), new IntData(newTree0.Height)));
    84       child1.AddVariable(new HeuristicLab.Core.Variable(scope.TranslateName("FunctionTree"), newTree1));
    85       child1.AddVariable(new HeuristicLab.Core.Variable(scope.TranslateName("TreeSize"), new IntData(newTree1.Size)));
    86       child1.AddVariable(new HeuristicLab.Core.Variable(scope.TranslateName("TreeHeight"), new IntData(newTree1.Height)));
    87     }
    88 
    89 
    90     private void Cross(MersenneTwister random, TreeGardener gardener, IScope f, IScope g, out IFunctionTree child0, out IFunctionTree child1) {
    91       IFunctionTree tree0 = f.GetVariableValue<IFunctionTree>("FunctionTree", false);
    92       int tree0Height = f.GetVariableValue<IntData>("TreeHeight", false).Data;
    93       int tree0Size = f.GetVariableValue<IntData>("TreeSize", false).Data;
    94 
    95       IFunctionTree tree1 = g.GetVariableValue<IFunctionTree>("FunctionTree", false);
    96       int tree1Height = g.GetVariableValue<IntData>("TreeHeight", false).Data;
    97       int tree1Size = g.GetVariableValue<IntData>("TreeSize", false).Data;
    98 
    99       List<CrossoverPoint> allowedCrossOverPoints = GetCrossOverPoints(gardener, tree0, tree1);
     51    internal override IFunctionTree Cross(TreeGardener gardener, MersenneTwister random, IFunctionTree tree0, IFunctionTree tree1) {
     52      List<CrossoverPoint> allowedCrossOverPoints = new List<CrossoverPoint>();
     53      GetCrossOverPoints(gardener, tree0, tree1, allowedCrossOverPoints);
    10054      if (allowedCrossOverPoints.Count > 0) {
    10155        CrossoverPoint crossOverPoint = allowedCrossOverPoints[random.Next(allowedCrossOverPoints.Count)];
    10256        IFunctionTree parent0 = crossOverPoint.parent0;
    103         IFunctionTree parent1 = crossOverPoint.parent1;
    104         IFunctionTree branch0 = crossOverPoint.parent0.SubTrees[crossOverPoint.childIndex];
    10557        IFunctionTree branch1 = crossOverPoint.parent1.SubTrees[crossOverPoint.childIndex];
    10658        parent0.RemoveSubTree(crossOverPoint.childIndex);
    107         parent1.RemoveSubTree(crossOverPoint.childIndex);
    10859        parent0.InsertSubTree(crossOverPoint.childIndex, branch1);
    109         parent1.InsertSubTree(crossOverPoint.childIndex, branch0);
    11060      }
    111 
    112       child0 = tree0;
    113       child1 = tree1;
    114     }
    115     class CrossoverPoint {
    116       public IFunctionTree parent0;
    117       public IFunctionTree parent1;
    118       public int childIndex;
     61      return tree0;
    11962    }
    12063
    121     private List<CrossoverPoint> GetCrossOverPoints(TreeGardener gardener, IFunctionTree branch0, IFunctionTree branch1) {
    122       List<CrossoverPoint> results = new List<CrossoverPoint>();
    123       if (branch0.SubTrees.Count != branch1.SubTrees.Count) return results;
     64    private void GetCrossOverPoints(TreeGardener gardener, IFunctionTree branch0, IFunctionTree branch1, List<CrossoverPoint> crossoverPoints) {
     65      if (branch0.SubTrees.Count != branch1.SubTrees.Count) return;
    12466
    12567      for (int i = 0; i < branch0.SubTrees.Count; i++) {
    126         // if the branches fit to the parent
    127         if (gardener.GetAllowedSubFunctions(branch0.Function, i).Contains(branch1.SubTrees[i].Function) &&
    128           gardener.GetAllowedSubFunctions(branch1.Function, i).Contains(branch0.SubTrees[i].Function)) {
     68        // if the current branch can be attached as a sub-tree to branch0
     69        if (gardener.GetAllowedSubFunctions(branch0.Function, i).Contains(branch1.SubTrees[i].Function)) {
    12970          CrossoverPoint p = new CrossoverPoint();
    13071          p.childIndex = i;
    13172          p.parent0 = branch0;
    13273          p.parent1 = branch1;
    133           results.Add(p);
     74          crossoverPoints.Add(p);
    13475        }
    135         results.AddRange(GetCrossOverPoints(gardener, branch0.SubTrees[i], branch1.SubTrees[i]));
     76        GetCrossOverPoints(gardener, branch0.SubTrees[i], branch1.SubTrees[i], crossoverPoints);
    13677      }
    137       return results;
    13878    }
    13979  }
  • trunk/sources/HeuristicLab.GP/Recombination/UniformCrossover.cs

    r813 r815  
    3737  /// In Proceedings of Genetic Programming '98, Madison, Wisconsin, 1998.
    3838  /// </summary>
    39   public class UniformCrossover : OperatorBase {
     39  public class UniformCrossover : GPCrossoverBase {
     40    // internal datastructure to represent crossover points
     41    private class CrossoverPoint {
     42      public IFunctionTree Parent0;
     43      public IFunctionTree Parent1;
     44      public int ChildIndex;
     45      public bool IsInternal;
     46    }
     47
    4048    public override string Description {
    4149      get {
     
    4351      }
    4452    }
    45     public UniformCrossover()
    46       : base() {
    47       AddVariableInfo(new VariableInfo("Random", "Pseudo random number generator", typeof(MersenneTwister), VariableKind.In));
    48       AddVariableInfo(new VariableInfo("OperatorLibrary", "The operator library containing all available operators", typeof(GPOperatorLibrary), VariableKind.In));
    49       AddVariableInfo(new VariableInfo("FunctionTree", "The tree to mutate", typeof(IFunctionTree), VariableKind.In | VariableKind.New));
    50       AddVariableInfo(new VariableInfo("TreeSize", "The size (number of nodes) of the tree", typeof(IntData), VariableKind.In | VariableKind.New));
    51       AddVariableInfo(new VariableInfo("TreeHeight", "The height of the tree", typeof(IntData), VariableKind.In | VariableKind.New));
    52     }
    5353
    54     public override IOperation Apply(IScope scope) {
    55       MersenneTwister random = GetVariableValue<MersenneTwister>("Random", scope, true);
    56       GPOperatorLibrary opLibrary = GetVariableValue<GPOperatorLibrary>("OperatorLibrary", scope, true);
    57       TreeGardener gardener = new TreeGardener(random, opLibrary);
    58 
    59       if ((scope.SubScopes.Count % 2) != 0)
    60         throw new InvalidOperationException("Number of parents is not even");
    61 
    62       CompositeOperation initOperations = new CompositeOperation();
    63 
    64       int crossoverEvents = scope.SubScopes.Count / 2;
    65       for (int i = 0; i < crossoverEvents; i++) {
    66         IScope parent1 = scope.SubScopes[0];
    67         scope.RemoveSubScope(parent1);
    68         IScope parent2 = scope.SubScopes[0];
    69         scope.RemoveSubScope(parent2);
    70         IScope child0 = new Scope((i * 2).ToString());
    71         IScope child1 = new Scope((i * 2 + 1).ToString());
    72         Cross(scope, random, gardener, parent1, parent2, child0, child1);
    73         scope.AddSubScope(child0);
    74         scope.AddSubScope(child1);
    75       }
    76 
    77       return null;
    78     }
    79 
    80     private void Cross(IScope scope, MersenneTwister random, TreeGardener gardener, IScope parent1, IScope parent2, IScope child0, IScope child1) {
    81       IFunctionTree childTree0;
    82       IFunctionTree childTree1;
    83       Cross(random, gardener, parent1, parent2, out childTree0, out childTree1);
    84       Debug.Assert(gardener.IsValidTree(childTree0));
    85       Debug.Assert(gardener.IsValidTree(childTree1));
    86       child0.AddVariable(new HeuristicLab.Core.Variable(scope.TranslateName("FunctionTree"), childTree0));
    87       child0.AddVariable(new HeuristicLab.Core.Variable(scope.TranslateName("TreeSize"), new IntData(childTree0.Size)));
    88       child0.AddVariable(new HeuristicLab.Core.Variable(scope.TranslateName("TreeHeight"), new IntData(childTree0.Height)));
    89 
    90       child1.AddVariable(new HeuristicLab.Core.Variable(scope.TranslateName("FunctionTree"), childTree1));
    91       child1.AddVariable(new HeuristicLab.Core.Variable(scope.TranslateName("TreeSize"), new IntData(childTree1.Size)));
    92       child1.AddVariable(new HeuristicLab.Core.Variable(scope.TranslateName("TreeHeight"), new IntData(childTree1.Height)));
    93     }
    94 
    95 
    96     private void Cross(MersenneTwister random, TreeGardener gardener, IScope f, IScope g, out IFunctionTree child0, out IFunctionTree child1) {
    97       IFunctionTree tree0 = f.GetVariableValue<IFunctionTree>("FunctionTree", false);
    98       int tree0Height = f.GetVariableValue<IntData>("TreeHeight", false).Data;
    99       int tree0Size = f.GetVariableValue<IntData>("TreeSize", false).Data;
    100 
    101       IFunctionTree tree1 = g.GetVariableValue<IFunctionTree>("FunctionTree", false);
    102       int tree1Height = g.GetVariableValue<IntData>("TreeHeight", false).Data;
    103       int tree1Size = g.GetVariableValue<IntData>("TreeSize", false).Data;
    104 
    105       List<CrossoverPoint> allowedCrossOverPoints = GetCrossOverPoints(gardener, tree0, tree1);
    106       foreach (CrossoverPoint p in allowedCrossOverPoints) {
    107         Debug.Assert(gardener.GetAllowedSubFunctions(p.parent0.Function, p.childIndex).Contains(p.parent1.SubTrees[p.childIndex].Function));
    108         Debug.Assert(gardener.GetAllowedSubFunctions(p.parent1.Function, p.childIndex).Contains(p.parent0.SubTrees[p.childIndex].Function));
    109       }
     54    internal override IFunctionTree Cross(TreeGardener gardener, MersenneTwister random, IFunctionTree tree0, IFunctionTree tree1) {
     55      List<CrossoverPoint> allowedCrossOverPoints = new List<CrossoverPoint>();
     56      GetCrossOverPoints(gardener, tree0, tree1, allowedCrossOverPoints);
    11057      // iterate through the list of crossover points and swap nodes with p=0.5
    11158      foreach (CrossoverPoint crossoverPoint in allowedCrossOverPoints) {
    11259        if (random.NextDouble() < 0.5) {
    113           IFunctionTree parent0 = crossoverPoint.parent0;
    114           IFunctionTree parent1 = crossoverPoint.parent1;
    115           IFunctionTree branch0 = crossoverPoint.parent0.SubTrees[crossoverPoint.childIndex];
    116           IFunctionTree branch1 = crossoverPoint.parent1.SubTrees[crossoverPoint.childIndex];
     60          if (crossoverPoint.IsInternal) {
     61            ExchangeNodes(crossoverPoint);
     62          } else {
     63            SwapSubtrees(crossoverPoint);
     64          }
     65        }
     66      }
     67      return tree0;
     68    }
    11769
    118           // if we are at an internal node of the common region swap only the node but not the subtrees
    119           if (branch0.SubTrees.Count == branch1.SubTrees.Count) {
    120             if (parent0 != null) {
    121               Debug.Assert(parent1 != null); Debug.Assert(branch0 != null); Debug.Assert(branch0 != null);
    122               Debug.Assert(gardener.GetAllowedSubFunctions(parent0.Function, crossoverPoint.childIndex).Contains(branch1.Function));
    123               Debug.Assert(gardener.GetAllowedSubFunctions(parent1.Function, crossoverPoint.childIndex).Contains(branch0.Function));
    124               // we are not at the root => exchange the branches in the parent
    125               parent0.RemoveSubTree(crossoverPoint.childIndex);
    126               parent1.RemoveSubTree(crossoverPoint.childIndex);
    127               parent0.InsertSubTree(crossoverPoint.childIndex, branch1);
    128               parent1.InsertSubTree(crossoverPoint.childIndex, branch0);
    129             }
    130             // always exchange all children
    131             List<IFunctionTree> branch0Children = new List<IFunctionTree>(branch0.SubTrees); // create backup lists
    132             List<IFunctionTree> branch1Children = new List<IFunctionTree>(branch1.SubTrees);
    133             while (branch0.SubTrees.Count > 0) branch0.RemoveSubTree(0); // remove all children
    134             while (branch1.SubTrees.Count > 0) branch1.RemoveSubTree(0);
    135             foreach (IFunctionTree subTree in branch1Children) {
    136               Debug.Assert(gardener.GetAllowedSubFunctions(branch0.Function, branch0.SubTrees.Count).Contains(subTree.Function));
    137               branch0.AddSubTree(subTree); // append children of branch1 to branch0
    138             }
    139             foreach (IFunctionTree subTree in branch0Children) {
    140               Debug.Assert(gardener.GetAllowedSubFunctions(branch1.Function, branch1.SubTrees.Count).Contains(subTree.Function));
    141               branch1.AddSubTree(subTree); // and vice versa
    142             }
     70    private void GetCrossOverPoints(TreeGardener gardener, IFunctionTree branch0, IFunctionTree branch1, List<CrossoverPoint> crossoverPoints) {
     71      if (branch0.SubTrees.Count != branch1.SubTrees.Count) return; // branches have to have same number of sub-trees to be valid crossover points
     72      // iterate over all sub-trees
     73      for (int i = 0; i < branch0.SubTrees.Count; i++) {
     74        IFunctionTree currentSubTree0 = branch0.SubTrees[i];
     75        IFunctionTree currentSubTree1 = branch1.SubTrees[i];
     76        // when the current sub-tree in branch1 can be attached as a child of branch0
     77        // and the sub-tree of branch0 can be attached as child of branch1.
     78        // note: we have to check both cases because either branch0 or branch1 can end up in the result tree
     79        if (gardener.GetAllowedSubFunctions(branch0.Function, i).Contains(currentSubTree1.Function) &&
     80          gardener.GetAllowedSubFunctions(branch1.Function, i).Contains(currentSubTree0.Function)) {
     81          // and the sub-tree is at the border of the common region
     82          if (currentSubTree0.SubTrees.Count != currentSubTree1.SubTrees.Count) {
     83            // then we have found a valid crossover point
     84            CrossoverPoint p = new CrossoverPoint();
     85            p.ChildIndex = i;
     86            p.Parent0 = branch0;
     87            p.Parent1 = branch1;
     88            p.IsInternal = false;
     89            crossoverPoints.Add(p);
    14390          } else {
    144             // If we are at a node at the border of the common region then exchange the whole branch.
    145             // If we are at the root node and the number of children is already different we can't do anything now but
    146             // at the end either tree0 or tree1 must be returned with p=0.5.
    147 
    148             // However if we are not at the root => exchange the branches in the parent
    149             if (parent0 != null) {
    150               Debug.Assert(parent1 != null); Debug.Assert(branch0 != null); Debug.Assert(branch1 != null);
    151               Debug.Assert(gardener.GetAllowedSubFunctions(parent0.Function, crossoverPoint.childIndex).Contains(branch1.Function));
    152               Debug.Assert(gardener.GetAllowedSubFunctions(parent1.Function, crossoverPoint.childIndex).Contains(branch0.Function));
    153               parent0.RemoveSubTree(crossoverPoint.childIndex);
    154               parent1.RemoveSubTree(crossoverPoint.childIndex);
    155               parent0.InsertSubTree(crossoverPoint.childIndex, branch1);
    156               parent1.InsertSubTree(crossoverPoint.childIndex, branch0);
     91            // when the sub-trees are not on the border of the common region
     92            // we also have to check if the children of the current sub-trees of branch0 and branch1 can be exchanged
     93            if (CanHaveSubTrees(gardener, currentSubTree0, currentSubTree1.SubTrees) &&
     94              CanHaveSubTrees(gardener, currentSubTree1, currentSubTree0.SubTrees)) {
     95              CrossoverPoint p = new CrossoverPoint();
     96              p.ChildIndex = i;
     97              p.Parent0 = branch0;
     98              p.Parent1 = branch1;
     99              p.IsInternal = true;
     100              crossoverPoints.Add(p);
    157101            }
    158102          }
    159103        }
     104        GetCrossOverPoints(gardener, currentSubTree0, currentSubTree1, crossoverPoints);
    160105      }
    161       child0 = tree0;
    162       child1 = tree1;
    163106    }
    164107
    165     class CrossoverPoint {
    166       public IFunctionTree parent0;
    167       public IFunctionTree parent1;
    168       public int childIndex;
     108    private bool CanHaveSubTrees(TreeGardener gardener, IFunctionTree parent, IList<IFunctionTree> subTrees) {
     109      for (int i = 0; i < subTrees.Count; i++) {
     110        if (!gardener.GetAllowedSubFunctions(parent.Function, i).Contains(subTrees[i].Function)) return false;
     111      }
     112      return true;
    169113    }
    170114
    171     private List<CrossoverPoint> GetCrossOverPoints(TreeGardener gardener, IFunctionTree branch0, IFunctionTree branch1) {
    172       List<CrossoverPoint> results = new List<CrossoverPoint>();
    173       if (branch0.SubTrees.Count != branch1.SubTrees.Count) return results;
     115    private void ExchangeNodes(CrossoverPoint crossoverPoint) {
     116      IFunctionTree parent0 = crossoverPoint.Parent0;
     117      IFunctionTree parent1 = crossoverPoint.Parent1;
     118      int childIndex = crossoverPoint.ChildIndex;
     119      IFunctionTree branch0 = crossoverPoint.Parent0.SubTrees[childIndex];
     120      IFunctionTree branch1 = crossoverPoint.Parent1.SubTrees[childIndex];
     121      // exchange the branches in the parent
     122      parent0.RemoveSubTree(childIndex);
     123      parent0.InsertSubTree(childIndex, branch1);
     124      parent1.RemoveSubTree(childIndex);
     125      parent1.InsertSubTree(childIndex, branch0);
    174126
    175       for (int i = 0; i < branch0.SubTrees.Count; i++) {
    176         // if the branches fit to the parent
    177         if (gardener.GetAllowedSubFunctions(branch0.Function, i).Contains(branch1.SubTrees[i].Function) &&
    178           gardener.GetAllowedSubFunctions(branch1.Function, i).Contains(branch0.SubTrees[i].Function)) {
    179           // if the point is at the border of the common region we don't care about the children
    180           // however if the point is not on the border of the common region we also have to check if
    181           // the children of the branches fit together
    182           bool fit = true;
    183           if (branch0.SubTrees[i].SubTrees.Count == branch1.SubTrees[i].SubTrees.Count) {
    184             for (int j = 0; j < branch0.SubTrees[i].SubTrees.Count; j++) {
    185               fit = fit & gardener.GetAllowedSubFunctions(branch0.SubTrees[i].Function, j).Contains(branch1.SubTrees[i].SubTrees[j].Function);
    186               fit = fit & gardener.GetAllowedSubFunctions(branch1.SubTrees[i].Function, j).Contains(branch0.SubTrees[i].SubTrees[j].Function);
    187             }
    188           }
    189           if (fit) {
    190             CrossoverPoint p = new CrossoverPoint();
    191             p.childIndex = i;
    192             p.parent0 = branch0;
    193             p.parent1 = branch1;
    194             results.Add(p);
    195           }
    196         }
    197         results.AddRange(GetCrossOverPoints(gardener, branch0.SubTrees[i], branch1.SubTrees[i]));
     127      ExchangeChildren(branch0, branch1);
     128    }
     129
     130    private void SwapSubtrees(CrossoverPoint crossoverPoint) {
     131      IFunctionTree parent0 = crossoverPoint.Parent0;
     132      IFunctionTree parent1 = crossoverPoint.Parent1;
     133      int childIndex = crossoverPoint.ChildIndex;
     134      IFunctionTree branch0 = crossoverPoint.Parent0.SubTrees[childIndex];
     135      IFunctionTree branch1 = crossoverPoint.Parent1.SubTrees[childIndex];
     136      // insert branch1 into parent0 replacing branch0
     137      parent0.RemoveSubTree(childIndex);
     138      parent0.InsertSubTree(childIndex, branch1);
     139      // insert branch0 into parent1 replacing branch1
     140      parent1.RemoveSubTree(childIndex);
     141      parent1.InsertSubTree(childIndex, branch0);
     142    }
     143
     144    private void ExchangeChildren(IFunctionTree branch0, IFunctionTree branch1) {
     145      List<IFunctionTree> branch0Children = new List<IFunctionTree>(branch0.SubTrees); // lists to backup subtrees
     146      List<IFunctionTree> branch1Children = new List<IFunctionTree>(branch1.SubTrees);
     147
     148      // remove children of branch0 and branch1
     149      while (branch1.SubTrees.Count > 0) branch1.RemoveSubTree(0);
     150      while (branch0.SubTrees.Count > 0) branch0.RemoveSubTree(0);
     151
     152      // add original children of branch0 to branch1
     153      foreach (IFunctionTree subTree in branch0Children) {
     154        branch1.AddSubTree(subTree);
    198155      }
    199       return results;
     156      // add original children of branch1 to branch0
     157      foreach (IFunctionTree subTree in branch1Children) {
     158        branch0.AddSubTree(subTree);
     159      }
    200160    }
    201161  }
Note: See TracChangeset for help on using the changeset viewer.