#region License Information /* HeuristicLab * Copyright (C) 2002-2019 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 HEAL.Attic; using HeuristicLab.Common; using HeuristicLab.Core; using HeuristicLab.Data; using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding; using HeuristicLab.Parameters; using HeuristicLab.Random; using static HeuristicLab.Problems.DataAnalysis.Symbolic.SymbolicExpressionHashExtensions; namespace HeuristicLab.Problems.DataAnalysis.Symbolic { [Item("DiversityCrossover", "Simple crossover operator preventing swap between subtrees with the same hash value.")] [StorableType("ED35B0D9-9704-4D32-B10B-8F9870E76781")] public sealed class SymbolicDataAnalysisExpressionDiversityPreservingCrossover : SymbolicDataAnalysisExpressionCrossover where T : class, IDataAnalysisProblemData { private const string InternalCrossoverPointProbabilityParameterName = "InternalCrossoverPointProbability"; private const string WindowingParameterName = "Windowing"; private const string ProportionalSamplingParameterName = "ProportionalSampling"; private const string StrictHashingParameterName = "StrictHashing"; private static readonly Func hashFunction = HashUtil.JSHash; #region Parameter Properties public IValueLookupParameter InternalCrossoverPointProbabilityParameter { get { return (IValueLookupParameter)Parameters[InternalCrossoverPointProbabilityParameterName]; } } public IValueLookupParameter WindowingParameter { get { return (IValueLookupParameter)Parameters[WindowingParameterName]; } } public IValueLookupParameter ProportionalSamplingParameter { get { return (IValueLookupParameter)Parameters[ProportionalSamplingParameterName]; } } public IFixedValueParameter StrictHashingParameter { get { return (IFixedValueParameter)Parameters[StrictHashingParameterName]; } } #endregion #region Properties public PercentValue InternalCrossoverPointProbability { get { return InternalCrossoverPointProbabilityParameter.ActualValue; } } public BoolValue Windowing { get { return WindowingParameter.ActualValue; } } public BoolValue ProportionalSampling { get { return ProportionalSamplingParameter.ActualValue; } } bool StrictHashing { get { return StrictHashingParameter.Value.Value; } } #endregion [StorableHook(HookType.AfterDeserialization)] private void AfterDeserialization() { if (!Parameters.ContainsKey(StrictHashingParameterName)) { Parameters.Add(new FixedValueParameter(StrictHashingParameterName, "Use strict hashing when calculating subtree hash values.")); } } public SymbolicDataAnalysisExpressionDiversityPreservingCrossover() { name = "DiversityCrossover"; Parameters.Add(new ValueLookupParameter(InternalCrossoverPointProbabilityParameterName, "The probability to select an internal crossover point (instead of a leaf node).", new PercentValue(0.9))); Parameters.Add(new ValueLookupParameter(WindowingParameterName, "Use proportional sampling with windowing for cutpoint selection.", new BoolValue(false))); Parameters.Add(new ValueLookupParameter(ProportionalSamplingParameterName, "Select cutpoints proportionally using probabilities as weights instead of randomly.", new BoolValue(true))); Parameters.Add(new FixedValueParameter(StrictHashingParameterName, "Use strict hashing when calculating subtree hash values.")); } private SymbolicDataAnalysisExpressionDiversityPreservingCrossover(SymbolicDataAnalysisExpressionDiversityPreservingCrossover original, Cloner cloner) : base(original, cloner) { } public override IDeepCloneable Clone(Cloner cloner) { return new SymbolicDataAnalysisExpressionDiversityPreservingCrossover(this, cloner); } [StorableConstructor] private SymbolicDataAnalysisExpressionDiversityPreservingCrossover(StorableConstructorFlag _) : base(_) { } private static ISymbolicExpressionTreeNode ActualRoot(ISymbolicExpressionTree tree) { return tree.Root.GetSubtree(0).GetSubtree(0); } public static ISymbolicExpressionTree Cross(IRandom random, ISymbolicExpressionTree parent0, ISymbolicExpressionTree parent1, double internalCrossoverPointProbability, int maxLength, int maxDepth, bool windowing, bool proportionalSampling = false, bool strictHashing = false) { var nodes0 = ActualRoot(parent0).MakeNodes(strictHashing).Sort(hashFunction); var nodes1 = ActualRoot(parent1).MakeNodes(strictHashing).Sort(hashFunction); IList> sampled0; IList> sampled1; if (proportionalSampling) { var p = internalCrossoverPointProbability; var weights0 = nodes0.Select(x => x.IsLeaf ? 1 - p : p); sampled0 = nodes0.SampleProportionalWithoutRepetition(random, nodes0.Length, weights0, windowing: windowing).ToArray(); var weights1 = nodes1.Select(x => x.IsLeaf ? 1 - p : p); sampled1 = nodes1.SampleProportionalWithoutRepetition(random, nodes1.Length, weights1, windowing: windowing).ToArray(); } else { sampled0 = ChooseNodes(random, nodes0, internalCrossoverPointProbability).ShuffleInPlace(random); sampled1 = ChooseNodes(random, nodes1, internalCrossoverPointProbability).ShuffleInPlace(random); } foreach (var selected in sampled0) { var cutpoint = new CutPoint(selected.Data.Parent, selected.Data); var maxAllowedDepth = maxDepth - parent0.Root.GetBranchLevel(selected.Data); var maxAllowedLength = maxLength - (parent0.Length - selected.Data.GetLength()); foreach (var candidate in sampled1) { if (candidate.CalculatedHashValue == selected.CalculatedHashValue || candidate.Data.GetDepth() > maxAllowedDepth || candidate.Data.GetLength() > maxAllowedLength || !cutpoint.IsMatchingPointType(candidate.Data)) { continue; } Swap(cutpoint, candidate.Data); return parent0; } } return parent0; } public override ISymbolicExpressionTree Crossover(IRandom random, ISymbolicExpressionTree parent0, ISymbolicExpressionTree parent1) { if (this.ExecutionContext == null) { throw new InvalidOperationException("ExecutionContext not set."); } var maxDepth = MaximumSymbolicExpressionTreeDepth.Value; var maxLength = MaximumSymbolicExpressionTreeLength.Value; var internalCrossoverPointProbability = InternalCrossoverPointProbability.Value; var windowing = Windowing.Value; var proportionalSampling = ProportionalSampling.Value; return Cross(random, parent0, parent1, internalCrossoverPointProbability, maxLength, maxDepth, windowing, proportionalSampling, StrictHashing); } private static List> ChooseNodes(IRandom random, IEnumerable> nodes, double internalCrossoverPointProbability) { var list = new List>(); var chooseInternal = random.NextDouble() < internalCrossoverPointProbability; if (chooseInternal) { list.AddRange(nodes.Where(x => !x.IsLeaf && x.Data.Parent != null)); } if (!chooseInternal || list.Count == 0) { list.AddRange(nodes.Where(x => x.IsLeaf && x.Data.Parent != null)); } return list; } } }