#region License Information
/* HeuristicLab
* Copyright (C) 2002-2010 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
*
* This file is part of HeuristicLab.
*
* HeuristicLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HeuristicLab is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HeuristicLab. If not, see .
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Data;
using HeuristicLab.Parameters;
using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
namespace HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Crossovers {
///
/// Takes two parent individuals P0 and P1 each. Selects a random node N0 of P0 and a random node N1 of P1.
/// And replaces the branch with root0 N0 in P0 with N1 from P1 if the tree-size limits are not violated.
/// When recombination with N0 and N1 would create a tree that is too large or invalid the operator randomly selects new N0 and N1
/// until a valid configuration is found.
///
[Item("SubtreeCrossover", "An operator which performs subtree swapping crossover.")]
[StorableClass]
public sealed class SubtreeCrossover : SymbolicExpressionTreeCrossover {
public IValueLookupParameter InternalCrossoverPointProbabilityParameter {
get { return (IValueLookupParameter)Parameters["InternalCrossoverPointProbability"]; }
}
[StorableConstructor]
private SubtreeCrossover(bool deserializing) : base(deserializing) { }
private SubtreeCrossover(SubtreeCrossover original, Cloner cloner) : base(original, cloner) { }
public SubtreeCrossover()
: base() {
Parameters.Add(new ValueLookupParameter("InternalCrossoverPointProbability", "The probability to select an internal crossover point (instead of a leaf node).", new PercentValue(0.9)));
}
public override IDeepCloneable Clone(Cloner cloner) {
return new SubtreeCrossover(this, cloner);
}
protected override SymbolicExpressionTree Cross(IRandom random,
SymbolicExpressionTree parent0, SymbolicExpressionTree parent1,
IntValue maxTreeSize, IntValue maxTreeHeight, out bool success) {
return Cross(random, parent0, parent1, InternalCrossoverPointProbabilityParameter.ActualValue.Value, maxTreeSize.Value, maxTreeHeight.Value, out success);
}
public static SymbolicExpressionTree Cross(IRandom random,
SymbolicExpressionTree parent0, SymbolicExpressionTree parent1,
double internalCrossoverPointProbability, int maxTreeSize, int maxTreeHeight, out bool success) {
// select a random crossover point in the first parent
SymbolicExpressionTreeNode crossoverPoint0;
int replacedSubtreeIndex;
SelectCrossoverPoint(random, parent0, internalCrossoverPointProbability, maxTreeSize - 1, maxTreeHeight - 1, out crossoverPoint0, out replacedSubtreeIndex);
// calculate the max size and height that the inserted branch can have
int maxInsertedBranchSize = maxTreeSize - (parent0.Size - crossoverPoint0.SubTrees[replacedSubtreeIndex].GetSize());
int maxInsertedBranchHeight = maxTreeHeight - GetBranchLevel(parent0.Root, crossoverPoint0);
List allowedBranches = new List();
parent1.Root.ForEachNodePostfix((n) => {
if (n.GetSize() < maxInsertedBranchSize &&
n.GetHeight() < maxInsertedBranchHeight &&
IsMatchingPointType(crossoverPoint0, replacedSubtreeIndex, n))
allowedBranches.Add(n);
});
if (allowedBranches.Count == 0) {
success = false;
return parent0;
} else {
var selectedBranch = SelectRandomBranch(random, allowedBranches, internalCrossoverPointProbability);
// manipulate the tree of parent0 in place
// replace the branch in tree0 with the selected branch from tree1
crossoverPoint0.RemoveSubTree(replacedSubtreeIndex);
crossoverPoint0.InsertSubTree(replacedSubtreeIndex, selectedBranch);
success = true;
return parent0;
}
}
private static bool IsMatchingPointType(SymbolicExpressionTreeNode parent, int replacedSubtreeIndex, SymbolicExpressionTreeNode branch) {
// check syntax constraints of direct parent - child relation
if (!parent.Grammar.ContainsSymbol(branch.Symbol) ||
!parent.Grammar.IsAllowedChild(parent.Symbol, branch.Symbol, replacedSubtreeIndex)) return false;
bool result = true;
// check point type for the whole branch
branch.ForEachNodePostfix((n) => {
result =
result &&
parent.Grammar.ContainsSymbol(n.Symbol) &&
n.SubTrees.Count >= parent.Grammar.GetMinSubtreeCount(n.Symbol) &&
n.SubTrees.Count <= parent.Grammar.GetMaxSubtreeCount(n.Symbol);
});
return result;
}
private static void SelectCrossoverPoint(IRandom random, SymbolicExpressionTree parent0, double internalNodeProbability, int maxBranchSize, int maxBranchHeight, out SymbolicExpressionTreeNode crossoverPoint, out int subtreeIndex) {
if (internalNodeProbability < 0.0 || internalNodeProbability > 1.0) throw new ArgumentException("internalNodeProbability");
List internalCrossoverPoints = new List();
List leafCrossoverPoints = new List();
parent0.Root.ForEachNodePostfix((n) => {
if (n.SubTrees.Count > 0 &&
n.GetSize() < maxBranchSize &&
n.GetHeight() < maxBranchHeight &&
n != parent0.Root
) {
foreach (var child in n.SubTrees) {
if (child.SubTrees.Count > 0)
internalCrossoverPoints.Add(new CrossoverPoint(n, child));
else
leafCrossoverPoints.Add(new CrossoverPoint(n, child));
}
}
});
if (random.NextDouble() < internalNodeProbability) {
// select from internal node if possible
if (internalCrossoverPoints.Count > 0) {
// select internal crossover point or leaf
var selectedCrossoverPoint = internalCrossoverPoints[random.Next(internalCrossoverPoints.Count)];
crossoverPoint = selectedCrossoverPoint.Parent;
subtreeIndex = selectedCrossoverPoint.SubtreeIndex;
} else {
// otherwise select external node
var selectedCrossoverPoint = leafCrossoverPoints[random.Next(leafCrossoverPoints.Count)];
crossoverPoint = selectedCrossoverPoint.Parent;
subtreeIndex = selectedCrossoverPoint.SubtreeIndex;
}
} else if (leafCrossoverPoints.Count > 0) {
// select from leaf crossover point if possible
var selectedCrossoverPoint = leafCrossoverPoints[random.Next(leafCrossoverPoints.Count)];
crossoverPoint = selectedCrossoverPoint.Parent;
subtreeIndex = selectedCrossoverPoint.SubtreeIndex;
} else {
// otherwise select internal crossover point
var selectedCrossoverPoint = internalCrossoverPoints[random.Next(internalCrossoverPoints.Count)];
crossoverPoint = selectedCrossoverPoint.Parent;
subtreeIndex = selectedCrossoverPoint.SubtreeIndex;
}
}
private static SymbolicExpressionTreeNode SelectRandomBranch(IRandom random, IEnumerable branches, double internalNodeProbability) {
if (internalNodeProbability < 0.0 || internalNodeProbability > 1.0) throw new ArgumentException("internalNodeProbability");
List allowedInternalBranches;
List allowedLeafBranches;
if (random.NextDouble() < internalNodeProbability) {
// select internal node if possible
allowedInternalBranches = (from branch in branches
where branch.SubTrees.Count > 0
select branch).ToList();
if (allowedInternalBranches.Count > 0) {
return allowedInternalBranches.SelectRandom(random);
} else {
// no internal nodes allowed => select leaf nodes
allowedLeafBranches = (from branch in branches
where branch.SubTrees.Count == 0
select branch).ToList();
return allowedLeafBranches.SelectRandom(random);
}
} else {
// select leaf node if possible
allowedLeafBranches = (from branch in branches
where branch.SubTrees.Count == 0
select branch).ToList();
if (allowedLeafBranches.Count > 0) {
return allowedLeafBranches.SelectRandom(random);
} else {
allowedInternalBranches = (from branch in branches
where branch.SubTrees.Count > 0
select branch).ToList();
return allowedInternalBranches.SelectRandom(random);
}
}
}
private static int GetBranchLevel(SymbolicExpressionTreeNode root, SymbolicExpressionTreeNode point) {
if (root == point) return 0;
foreach (var subtree in root.SubTrees) {
int branchLevel = GetBranchLevel(subtree, point);
if (branchLevel < int.MaxValue) return 1 + branchLevel;
}
return int.MaxValue;
}
}
}