#region License Information
/* HeuristicLab
* Copyright (C) 2002-2008 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.Collections.Generic;
using System.Linq;
using HeuristicLab.Core;
using HeuristicLab.Data;
using HeuristicLab.Operators;
using HeuristicLab.Random;
using System;
namespace HeuristicLab.StructureIdentification {
public class CutOutNodeManipulation : OperatorBase {
public override string Description {
get {
return @"Takes a tree, selects a random node of the tree and then tries to replace a random child
of that node with one of the childs of the selected child.
O O
/ \ / \
O X O 2
/ \ 2 is selected => / \
1 2 4 5
/ / \
3 4 5
";
}
}
public CutOutNodeManipulation()
: base() {
AddVariableInfo(new VariableInfo("Random", "Uniform random number generator", typeof(MersenneTwister), VariableKind.In));
AddVariableInfo(new VariableInfo("OperatorLibrary", "The operator library containing all available operators", typeof(GPOperatorLibrary), VariableKind.In));
AddVariableInfo(new VariableInfo("MaxTreeHeight", "The maximal allowed height of the tree", typeof(IntData), VariableKind.In));
AddVariableInfo(new VariableInfo("MaxTreeSize", "The maximal allowed size (number of nodes) of the tree", typeof(IntData), VariableKind.In));
AddVariableInfo(new VariableInfo("BalancedTreesRate", "Determines how many trees should be balanced", typeof(DoubleData), VariableKind.In));
AddVariableInfo(new VariableInfo("OperatorTree", "The tree to mutate", typeof(IOperator), VariableKind.In));
AddVariableInfo(new VariableInfo("TreeSize", "The size (number of nodes) of the tree", typeof(IntData), VariableKind.In));
AddVariableInfo(new VariableInfo("TreeHeight", "The height of the tree", typeof(IntData), VariableKind.In));
}
public override IOperation Apply(IScope scope) {
IOperator rootOperator = GetVariableValue("OperatorTree", scope, true);
MersenneTwister random = GetVariableValue("Random", scope, true);
GPOperatorLibrary library = GetVariableValue("OperatorLibrary", scope, true);
int maxTreeHeight = GetVariableValue("MaxTreeHeight", scope, true).Data;
int maxTreeSize = GetVariableValue("MaxTreeSize", scope, true).Data;
double balancedTreesRate = GetVariableValue("BalancedTreesRate", scope, true).Data;
TreeGardener gardener = new TreeGardener(random, library);
IOperator parent = gardener.GetRandomParentNode(rootOperator);
// parent == null means we should cut out the root node
// => return a random suboperator of the root
if (parent == null) {
// when there are suboperators then replace the old operator with a random suboperator
if (rootOperator.SubOperators.Count > 0) {
rootOperator = rootOperator.SubOperators[random.Next(rootOperator.SubOperators.Count)];
GetVariableValue("TreeSize", scope, true).Data = gardener.GetTreeSize(rootOperator);
GetVariableValue("TreeHeight", scope, true).Data = gardener.GetTreeHeight(rootOperator);
// this is not really necessary (we can leave it in until the operator is stable)
if (!gardener.IsValidTree(rootOperator)) {
throw new InvalidProgramException();
}
// update the variable
scope.GetVariable("OperatorTree").Value = rootOperator;
if (!gardener.IsValidTree(rootOperator)) {
throw new InvalidProgramException();
}
// the tree is already initialized so we don't have to schedule initialization operations
return null;
} else {
// create a new random tree
IOperator newOperator;
if(random.NextDouble() <= balancedTreesRate) {
newOperator = gardener.CreateRandomTree(gardener.AllOperators, maxTreeSize, maxTreeHeight, true);
} else {
newOperator = gardener.CreateRandomTree(gardener.AllOperators, maxTreeSize, maxTreeHeight, false);
}
GetVariableValue("TreeSize", scope, true).Data = gardener.GetTreeSize(newOperator);
GetVariableValue("TreeHeight", scope, true).Data = gardener.GetTreeHeight(newOperator);
if (!gardener.IsValidTree(newOperator)) {
throw new InvalidProgramException();
}
// update the variable
scope.GetVariable("OperatorTree").Value = newOperator;
if (!gardener.IsValidTree(newOperator)) {
throw new InvalidProgramException();
}
// schedule an operation to initialize the whole operator graph
return gardener.CreateInitializationOperation(gardener.GetAllOperators(newOperator), scope);
}
}
int childIndex = random.Next(parent.SubOperators.Count);
IOperator child = parent.SubOperators[childIndex];
// match the suboperators of the child with the allowed suboperators of the parent
IOperator[] possibleChilds = gardener.GetAllowedSubOperators(parent, childIndex).SelectMany(allowedOp => child.SubOperators
.Where(subOp => ((StringData)subOp.GetVariable(GPOperatorLibrary.TYPE_ID).Value).Data ==
((StringData)allowedOp.GetVariable(GPOperatorLibrary.TYPE_ID).Value).Data)).ToArray();
if (possibleChilds.Length > 0) {
// replace child with a random child of the child
// make a clone to simplify removing obsolete operators from the operator-graph
IOperator selectedChild = (IOperator)possibleChilds[random.Next(possibleChilds.Length)].Clone();
parent.RemoveSubOperator(childIndex);
parent.AddSubOperator(selectedChild, childIndex);
if (!gardener.IsValidTree(rootOperator)) {
throw new InvalidProgramException();
}
// update the size and height of our tree
GetVariableValue("TreeSize", scope, true).Data = gardener.GetTreeSize(rootOperator);
GetVariableValue("TreeHeight", scope, true).Data = gardener.GetTreeHeight(rootOperator);
// don't need to schedule initialization operations
return null;
} else {
// determine the level of the parent
int parentLevel = gardener.GetNodeLevel(rootOperator, parent);
// first remove the old child (first step essential!)
parent.RemoveSubOperator(childIndex);
// then determine the number of nodes left over after the child has been removed!
int remainingNodes = gardener.GetTreeSize(rootOperator);
IList allowedOperators = gardener.GetAllowedSubOperators(parent, childIndex);
IOperator newOperatorTree = gardener.CreateRandomTree(allowedOperators, maxTreeSize - remainingNodes, maxTreeHeight - parentLevel, true);
parent.AddSubOperator(newOperatorTree, childIndex);
GetVariableValue("TreeSize", scope, true).Data = gardener.GetTreeSize(rootOperator);
GetVariableValue("TreeHeight", scope, true).Data = gardener.GetTreeHeight(rootOperator);
if (!gardener.IsValidTree(rootOperator)) {
throw new InvalidProgramException();
}
// schedule an initialization operation for the new operator
return gardener.CreateInitializationOperation(gardener.GetAllOperators(newOperatorTree), scope);
}
}
}
}