#region License Information /* HeuristicLab * Copyright (C) 2002-2013 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 HeuristicLab.Common; using HeuristicLab.Core; using HeuristicLab.Data; using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding; using HeuristicLab.Optimization; using HeuristicLab.Parameters; using HeuristicLab.Persistence.Default.CompositeSerializers.Storable; namespace HeuristicLab.Problems.DataAnalysis.Symbolic { [Item("SemanticSimilarityCrossover", "An operator which performs subtree swapping based on the notion of structural similarity between subtrees\n" + "(criteria: structural similarity coefficient between the subtrees must be lower than given threshold.)\n" + "- Take two parent individuals P0 and P1\n" + "- Randomly choose a node N from the P0\n" + "- Find the first node M that satisfies the structural similarity criteria\n" + "- Swap N for M and return P0")] public sealed class SymbolicDataAnalysisExpressionStructuralSimilarityCrossover : SymbolicDataAnalysisExpressionCrossover where T : class, IDataAnalysisProblemData { private const string StructuralSimilarityThresholdParameterName = "StructuralSimilarityThresholdParameterName"; private const string MatchVariablesParameterName = "MatchVariableNames"; private const string MatchVariableWeightsParameterName = "MatchVariableWeights"; private const string MatchConstantValuesParameterName = "MatchConstantValues"; private const string ResultsParameterName = "Results"; private readonly SymbolicExpressionTreeNodeSimilarityComparer comparer; #region Parameter properties private ValueParameter StructuralSimilarityThresholdParameter { get { return (ValueParameter)Parameters[StructuralSimilarityThresholdParameterName]; } } public ValueParameter MatchVariableNamesParameter { get { return (ValueParameter)Parameters[MatchVariablesParameterName]; } } public ValueParameter MatchVariableWeightsParameter { get { return (ValueParameter)Parameters[MatchVariableWeightsParameterName]; } } public ValueParameter MatchConstantValuesParameter { get { return (ValueParameter)Parameters[MatchConstantValuesParameterName]; } } public LookupParameter ResultsParameter { get { return (LookupParameter)Parameters[ResultsParameterName]; } } #endregion #region Properties private DoubleValue StructuralSimilarityThreshold { get { return StructuralSimilarityThresholdParameter.Value; } } public ResultCollection Results { get { return ResultsParameter.ActualValue; } } #endregion [StorableConstructor] private SymbolicDataAnalysisExpressionStructuralSimilarityCrossover(bool deserializing) : base(deserializing) { if (comparer == null) comparer = new SymbolicExpressionTreeNodeSimilarityComparer(); } private SymbolicDataAnalysisExpressionStructuralSimilarityCrossover( SymbolicDataAnalysisExpressionCrossover original, Cloner cloner) : base(original, cloner) { if (comparer == null) comparer = new SymbolicExpressionTreeNodeSimilarityComparer(); } public SymbolicDataAnalysisExpressionStructuralSimilarityCrossover() : base() { Parameters.Add(new ValueParameter(StructuralSimilarityThresholdParameterName, new DoubleValue(0.8))); Parameters.Add(new ValueParameter(MatchVariablesParameterName, "Specify if the symbolic expression tree comparer should match variable names.", new BoolValue(true))); Parameters.Add(new ValueParameter(MatchVariableWeightsParameterName, "Specify if the symbolic expression tree comparer should match variable weights.", new BoolValue(true))); Parameters.Add(new ValueParameter(MatchConstantValuesParameterName, "Specify if the symbolic expression tree comparer should match constant values.", new BoolValue(true))); Parameters.Add(new ValueLookupParameter(ResultsParameterName, "The results collection where the analysis values should be stored.")); name = "StructuralSimilarityCrossover"; comparer = new SymbolicExpressionTreeNodeSimilarityComparer { MatchConstantValues = true, MatchVariableNames = true, MatchVariableWeights = true }; } public override IDeepCloneable Clone(Cloner cloner) { return new SymbolicDataAnalysisExpressionStructuralSimilarityCrossover(this, cloner); } public override ISymbolicExpressionTree Crossover(IRandom random, ISymbolicExpressionTree parent0, ISymbolicExpressionTree parent1) { comparer.MatchConstantValues = MatchConstantValuesParameter.Value.Value; comparer.MatchVariableNames = MatchVariableNamesParameter.Value.Value; comparer.MatchVariableWeights = MatchVariableWeightsParameter.Value.Value; if (CalculateSimilarity(parent0.Root, parent1.Root, comparer) > StructuralSimilarityThreshold.Value) return parent0; return Cross(random, parent0, parent1, MaximumSymbolicExpressionTreeDepth.Value, MaximumSymbolicExpressionTreeLength.Value, StructuralSimilarityThreshold, comparer); } /// /// Takes two parent individuals P0 and P1. /// Randomly choose a node i from the first parent, then get a node j from the second parent that matches the semantic similarity criteria. /// public ISymbolicExpressionTree Cross(IRandom random, ISymbolicExpressionTree parent0, ISymbolicExpressionTree parent1, int maxDepth, int maxLength, DoubleValue structuralSimilarityThreshold, SymbolicExpressionTreeNodeSimilarityComparer comparer) { var crossoverPoints0 = new List(); parent0.Root.ForEachNodePostfix(n => { if (n.Parent != null && n.Parent != parent0.Root) crossoverPoints0.Add(new CutPoint(n.Parent, n)); }); var crossoverPoint0 = crossoverPoints0.SelectRandom(random); int level = parent0.Root.GetBranchLevel(crossoverPoint0.Child); int length = parent0.Root.GetLength() - crossoverPoint0.Child.GetLength(); var allowedBranches = new List(); parent1.Root.ForEachNodePostfix(n => { if (n.Parent != null && n.Parent != parent1.Root) { if (n.GetDepth() + level <= maxDepth && n.GetLength() + length <= maxLength && crossoverPoint0.IsMatchingPointType(n)) allowedBranches.Add(n); } }); ISymbolicExpressionTreeNode selectedBranch = null; if (allowedBranches.Count == 0) return parent0; var b = allowedBranches.SelectRandom(random); if (CalculateSimilarity(crossoverPoint0.Child, b, comparer) < structuralSimilarityThreshold.Value) selectedBranch = b; // perform the actual swap if (selectedBranch != null) { Swap(crossoverPoint0, selectedBranch); } return parent0; } private static double CalculateSimilarity(ISymbolicExpressionTreeNode a, ISymbolicExpressionTreeNode b, SymbolicExpressionTreeNodeSimilarityComparer comparer) { return SymbolicDataAnalysisExpressionTreeSimilarity.CalculateSimilarity(a, b, comparer); } } }