#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 HEAL.Attic; using HeuristicLab.Common; using HeuristicLab.Core; using HeuristicLab.Data; using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding; using HeuristicLab.Optimization; using HeuristicLab.Parameters; using HeuristicLab.Selection; namespace HeuristicLab.Problems.DataAnalysis.Symbolic { [StorableType("3E8EA052-3B86-4609-BD38-E3FE78DAD2FF")] [Item("DiversitySelector", "A selection operator that applies a diversity penalty to the objective function before applying an inner selector.")] public sealed class DiversitySelector : StochasticSingleObjectiveSelector, ISingleObjectiveSelector { private const string StrictSimilarityParameterName = "StrictSimilarity"; private const string SimilarityWeightParameterName = "SimilarityWeight"; private const string SymbolicExpressionTreeParameterName = "SymbolicExpressionTree"; private const string SelectorParameterName = "Selector"; private const string DiversityParameterName = "Diversity"; public IValueParameter SelectorParameter { get { return (IValueParameter)Parameters[SelectorParameterName]; } } public ISingleObjectiveSelector Selector { get { return SelectorParameter.Value; } set { SelectorParameter.Value = value; } } public IScopeTreeLookupParameter SymbolicExpressionTreeParameter { get { return (IScopeTreeLookupParameter)Parameters[SymbolicExpressionTreeParameterName]; } } public IScopeTreeLookupParameter DiversityParameter { get { return (IScopeTreeLookupParameter)Parameters[DiversityParameterName]; } } public IFixedValueParameter StrictSimilarityParameter { get { return (IFixedValueParameter)Parameters[StrictSimilarityParameterName]; } } public IFixedValueParameter SimilarityWeightParameter { get { return (IFixedValueParameter)Parameters[SimilarityWeightParameterName]; } } public bool StrictSimilarity { get { return StrictSimilarityParameter.Value.Value; } } public double SimilarityWeight { get { return SimilarityWeightParameter.Value.Value; } } public DiversitySelector() : base() { Parameters.Add(new FixedValueParameter(StrictSimilarityParameterName, "Calculate strict similarity.", new BoolValue(true))); Parameters.Add(new FixedValueParameter(SimilarityWeightParameterName, "Weight of the diversity term.", new DoubleValue(1))); Parameters.Add(new ScopeTreeLookupParameter(SymbolicExpressionTreeParameterName, "The symbolic expression trees that should be analyzed.")); Parameters.Add(new ScopeTreeLookupParameter(DiversityParameterName)); Parameters.Add(new ValueParameter(SelectorParameterName, "The inner selection operator to select the parents.", new TournamentSelector())); RegisterParameterEventHandlers(); } [StorableConstructor] private DiversitySelector(StorableConstructorFlag deserializing) : base(deserializing) { } private DiversitySelector(DiversitySelector original, Cloner cloner) : base(original, cloner) { } public override IDeepCloneable Clone(Cloner cloner) { return new DiversitySelector(this, cloner); } [StorableHook(HookType.AfterDeserialization)] private void AfterDeserialization() { RegisterParameterEventHandlers(); if (!Parameters.ContainsKey(DiversityParameterName)) { Parameters.Add(new ScopeTreeLookupParameter(DiversityParameterName)); } } #region Events private void RegisterParameterEventHandlers() { SelectorParameter.ValueChanged += SelectorParameter_ValueChanged; CopySelectedParameter.ValueChanged += CopySelectedParameter_ValueChanged; CopySelected.ValueChanged += CopySelected_ValueChanged; } private void CopySelectedParameter_ValueChanged(object sender, EventArgs e) { if (CopySelected.Value != true) { CopySelected.Value = true; } CopySelected.ValueChanged += CopySelected_ValueChanged; } private void SelectorParameter_ValueChanged(object sender, EventArgs e) { ParameterizeSelector(Selector); } private void CopySelected_ValueChanged(object sender, EventArgs e) { if (CopySelected.Value != true) { CopySelected.Value = true; } } #endregion protected override IScope[] Select(List scopes) { var w = SimilarityWeight; if (w.IsAlmost(0)) { ApplyInnerSelector(); return CurrentScope.SubScopes[1].SubScopes.ToArray(); } var trees = SymbolicExpressionTreeParameter.ActualValue; var qualities = QualityParameter.ActualValue; // calculate average similarity for each tree var similarityMatrix = SymbolicExpressionTreeHash.ComputeSimilarityMatrix(trees, simplify: false, strict: StrictSimilarity); var similarities = new double[trees.Length]; for (int i = 0; i < trees.Length; ++i) { for (int j = 0; j < trees.Length; ++j) { if (i != j) { similarities[i] += similarityMatrix[i, j]; } } similarities[i] /= (trees.Length - 1); } var v = 1 - w; var maximization = MaximizationParameter.ActualValue.Value; var diversities = new ItemArray(trees.Length); for (int i = 0; i < trees.Length; ++i) { var q = qualities[i].Value; var d = 1 - similarities[i]; // average distance // assuming both q and d are in the interval [0, 1] var value = maximization ? (v * q) + (w * d) : (v * q) - (w * d); diversities[i] = new DoubleValue(value); } Selector.QualityParameter.ActualName = "Diversity"; DiversityParameter.ActualValue = diversities; ApplyInnerSelector(); // apply inner selector return CurrentScope.SubScopes[1].SubScopes.ToArray(); } private void ParameterizeSelector(ISingleObjectiveSelector selector) { selector.CopySelected = new BoolValue(true); // must always be true selector.MaximizationParameter.ActualName = MaximizationParameter.Name; selector.QualityParameter.ActualName = QualityParameter.Name; IStochasticOperator stoOp = (selector as IStochasticOperator); if (stoOp != null) stoOp.RandomParameter.ActualName = RandomParameter.Name; } private void ApplyInnerSelector() { // necessary for inner GenderSpecificSelector to execute all operations in OperationCollection Stack executionStack = new Stack(); executionStack.Push(ExecutionContext.CreateChildOperation(Selector)); while (executionStack.Count > 0) { CancellationToken.ThrowIfCancellationRequested(); IOperation next = executionStack.Pop(); if (next is OperationCollection) { OperationCollection coll = (OperationCollection)next; for (int i = coll.Count - 1; i >= 0; i--) if (coll[i] != null) executionStack.Push(coll[i]); } else if (next is IAtomicOperation) { IAtomicOperation operation = (IAtomicOperation)next; next = operation.Operator.Execute((IExecutionContext)operation, CancellationToken); if (next != null) executionStack.Push(next); } } } } }