#region License Information /* HeuristicLab * Copyright (C) 2002-2018 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.Linq; using HEAL.Attic; using HeuristicLab.Common; using HeuristicLab.Core; using HeuristicLab.Encodings.IntegerVectorEncoding; using HeuristicLab.Optimization; using HeuristicLab.Parameters; namespace HeuristicLab.Problems.Modifiers { [StorableType("28866F64-5BB4-41A0-95E9-7DF9A7C0B376")] [Item("ClusteringTranscodingProblemModifier", "A problem modifier that groups input parameters into clusters and sets them to equal values. Values assigned to cluster -1 will be fixed to their default values")] public class ClusteringTranscodingProblemModifier : TranscodingProblemModifier { #region ParameterNames private const string DefaultVectorParameterName = "DefaultVector"; private const string ClusterAssignmentParameterName = "ClusterAssignment"; #endregion #region Parameters public IValueParameter DefaultVectorParameter => (IValueParameter)Parameters[DefaultVectorParameterName]; public IValueParameter ClusterAssignmentParameter => (IValueParameter)Parameters[ClusterAssignmentParameterName]; #endregion #region ParameterNames public IntegerVector DefaultVector { get => DefaultVectorParameter.Value; set => DefaultVectorParameter.Value = value; } public IntegerVector ClusterAssignment { get => ClusterAssignmentParameter.Value; set => ClusterAssignmentParameter.Value = value; } #endregion #region Constructors & Cloning [StorableConstructor] protected ClusteringTranscodingProblemModifier(StorableConstructorFlag _) : base(_) { } protected ClusteringTranscodingProblemModifier(ClusteringTranscodingProblemModifier original, Cloner cloner) : base(original, cloner) { RegisterEvents(); } public ClusteringTranscodingProblemModifier() { Parameters.Add(new ValueParameter(DefaultVectorParameterName, "The default values for every dimension of the original encoding", new IntegerVector(2))); Parameters.Add(new ValueParameter(ClusterAssignmentParameterName, "The cluster assignments for every dimension of the original encoding", new IntegerVector(2))); RegisterEvents(); } [StorableHook(HookType.AfterDeserialization)] private void AfterDeserialization() { RegisterEvents(); } public override IDeepCloneable Clone(Cloner cloner) { return new ClusteringTranscodingProblemModifier(this, cloner); } #endregion #region ParameterEvents private void RegisterEvents() { ClusterAssignmentParameter.ValueChanged -= ClusterAssignmentValueChanged; ClusterAssignmentParameter.ValueChanged += ClusterAssignmentValueChanged; ClusterAssignmentParameter.Value.ToStringChanged -= RedoEncoding; ClusterAssignmentParameter.Value.ToStringChanged += RedoEncoding; DefaultVectorParameter.ValueChanged -= DefaultVectorValueChanged; DefaultVectorParameter.ValueChanged += DefaultVectorValueChanged; DefaultVectorParameter.Value.ToStringChanged -= RedoEncoding; DefaultVectorParameter.Value.ToStringChanged += RedoEncoding; } private void ClusterAssignmentValueChanged(object sender, EventArgs e) { ClusterAssignmentParameter.Value.ToStringChanged -= RedoEncoding; ClusterAssignmentParameter.Value.ToStringChanged += RedoEncoding; RedoEncoding(sender, e); } private void DefaultVectorValueChanged(object sender, EventArgs e) { DefaultVectorParameter.Value.ToStringChanged -= RedoEncoding; DefaultVectorParameter.Value.ToStringChanged += RedoEncoding; RedoEncoding(sender, e); } private void RedoEncoding(object sender, EventArgs e) { InvokeEncodingChanged(); } #endregion #region TranscodingProblemModifier protected override ISolutionCreator ChangeSolutionCreator(ISolutionCreator oldCreator) { if (oldCreator == null || encoding == null) return null; var nc = Encoding.Operators.SingleOrDefault(o => o.GetType() == oldCreator.GetType()); return (ISolutionCreator)nc ?? throw new ArgumentException($"Could not transcode {oldCreator.GetType()}. Please extend {GetType()} to handle your specific solution creator."); } protected override IEncoding ChangeEncoding(IEncoding oldEncoding) { if (oldEncoding == null) { return null; } if (!(oldEncoding is IntegerVectorEncoding iEncoding)) throw new ArgumentException("This form of transcoding is only valid for IntegerVectorEncoding"); if (iEncoding.Length != ClusterAssignment.Length) throw new ArgumentException("ClusterAssignment and original Encoding need to have equal length"); if (iEncoding.Length != DefaultVector.Length) throw new ArgumentException("ClusterAssignment and default Vector need to have equal length"); var clusters = ClusterAssignment.Max() + 1; var min = new int[clusters]; var max = new int[clusters]; var steps = new int[clusters]; var b = new bool[clusters]; for (var i = 0; i < iEncoding.Length; i++) { var t = ClusterAssignment[i]; if (t < 0) continue; var i2 = i % iEncoding.Length; var iMin = iEncoding.Bounds[i2, 0]; var iMax = iEncoding.Bounds[i2, 1]; var iSteps = iEncoding.Bounds.Columns > 2 ? iEncoding.Bounds[i2, 2] : 1; //check if (!b[t] || min[t] < iMin) min[t] = iMin; //biggest possible minimum if (!b[t] || max[t] > iMax) max[t] = iMax; //smallest possible maximum if (!b[t]) steps[t] = iSteps; if (steps[t] != iSteps && b[t]) throw new ArgumentException("step sizes for variables within clusters must not differ"); b[t] = true; } if (!b.All(x => x)) throw new ArgumentException("At least one cluster is not mapped to any input variable"); return new IntegerVectorEncoding(iEncoding.Name, clusters, min, max, steps); } protected override Individual ChangeIndividual(Individual newIndividual) { var intVec = newIndividual.IntegerVector(); var enc = newIndividual.GetEncoding(); var individualScope = new Scope(); foreach (var v in newIndividual.Values) //intentional shallow copy individualScope.Variables.Add(new Variable(v.Key, v.Value)); var oldIndividual = new SingleEncodingIndividual(enc, individualScope) { [enc.Name] = TransCode(intVec) }; return oldIndividual; } #endregion #region Helpers private IntegerVector TransCode(IntegerVector newVector) { return new IntegerVector(ClusterAssignment.Select((v, i) => v >= 0 ? newVector[v] : DefaultVector[i]).ToArray()); } #endregion } }