#region License Information /* HeuristicLab * Copyright (C) 2002-2012 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 HeuristicLab.Common; using HeuristicLab.Core; using HeuristicLab.Data; using HeuristicLab.Parameters; using HeuristicLab.Persistence.Default.CompositeSerializers.Storable; using HeuristicLab.Selection; namespace HeuristicLab.Optimization.Operators.LCS { [Item("NichingTournamentSelector", "Description missing")] [StorableClass] public class NichingTournamentSelector : StochasticSingleObjectiveSelector, INichingSingleObjectiveSelector, IHierarchicalSingleObjectiveSelector { #region Parameter Properties public ValueLookupParameter GroupSizeParameter { get { return (ValueLookupParameter)Parameters["GroupSize"]; } } public ILookupParameter GAssistNichesProblemDataParameter { get { return (LookupParameter)Parameters["GAssistNichesProblemData"]; } } public ILookupParameter NichingParameter { get { return (LookupParameter)Parameters["Niching"]; } } public IValueLookupParameter ParentsPerChildParameter { get { return (IValueLookupParameter)Parameters["ParentsPerChild"]; } } public ILookupParameter> IndividualParameter { get { return (ILookupParameter>)Parameters["Individual"]; } } public ILookupParameter> LengthParameter { get { return (ILookupParameter>)Parameters["Length"]; } } public IValueLookupParameter LengthThresholdParameter { get { return (IValueLookupParameter)Parameters["LengthThreshold"]; } } public IValueLookupParameter IterationHirachicalSelectionParameter { get { return (IValueLookupParameter)Parameters["IterationHirachicalSelection"]; } } public ILookupParameter IterationsParameter { get { return (ILookupParameter)Parameters["Iterations"]; } } public IValueLookupParameter MaximumIterationsParameter { get { return (IValueLookupParameter)Parameters["MaximumIterations"]; } } #endregion [StorableConstructor] protected NichingTournamentSelector(bool deserializing) : base(deserializing) { } protected NichingTournamentSelector(NichingTournamentSelector original, Cloner cloner) : base(original, cloner) { } public NichingTournamentSelector() : base() { Parameters.Add(new ValueLookupParameter("GroupSize", "The size of the tournament group.", new IntValue(2))); Parameters.Add(new LookupParameter("GAssistNichesProblemData", "")); Parameters.Add(new LookupParameter("Niching", "")); Parameters.Add(new ValueLookupParameter("ParentsPerChild", "")); Parameters.Add(new ScopeTreeLookupParameter("Individual", "")); Parameters.Add(new ScopeTreeLookupParameter("Length", "The length value contained in each sub-scope which is used for selection.")); Parameters.Add(new ValueLookupParameter("LengthThreshold", "", new DoubleValue(0.000001))); Parameters.Add(new ValueLookupParameter("IterationHirachicalSelection", "", new IntValue(24))); Parameters.Add(new LookupParameter("Iterations", "")); Parameters.Add(new ValueLookupParameter("MaximumIterations", "")); } public override IDeepCloneable Clone(Cloner cloner) { return new NichingTournamentSelector(this, cloner); } protected override IScope[] Select(List scopes) { int count = NumberOfSelectedSubScopesParameter.ActualValue.Value; bool copy = CopySelectedParameter.Value.Value; IRandom random = RandomParameter.ActualValue; bool maximization = MaximizationParameter.ActualValue.Value; List qualities = QualityParameter.ActualValue.Where(x => IsValidQuality(x.Value)).Select(x => x.Value).ToList(); List lengths = LengthParameter.ActualValue.Select(x => x.Value).ToList(); bool useLength = lengths.Count > 0; double lengtThreshold = LengthThresholdParameter.ActualValue.Value; List individuals = IndividualParameter.ActualValue.ToList(); int groupSize = GroupSizeParameter.ActualValue.Value; IScope[] selected = new IScope[count]; bool doNiching = NichingParameter.ActualValue.Value; //check if list with indexes is as long as the original scope list //otherwise invalid quality values were filtered if (qualities.Count != scopes.Count || individuals.Count != scopes.Count || (useLength && lengths.Count != scopes.Count)) { throw new ArgumentException("The scopes contain invalid quality values (either infinity or double.NaN) on which the selector cannot operate."); } int parentsPerChild = ParentsPerChildParameter.ActualValue.Value; if (individuals.Any(x => x.Niche == null) && individuals.Any(x => x.Niche != null)) { throw new ArgumentException("Either all individuals have a default action or none."); } //null cannot be a key in a dictionary (nicheScope), therefore torunament selection has to be done differently //to keep it a little maintainable, the case that no default rule is used has been separated if (individuals.Any(x => x.Niche == null)) { //normal tournament selection for (int i = 0; i < count; i++) { int best = random.Next(scopes.Count); int index; for (int j = 1; j < groupSize; j++) { index = random.Next(scopes.Count); if (IsBetterHirachical(index, best, qualities, lengths, lengtThreshold, useLength, maximization)) { best = index; } } if (copy) selected[i] = (IScope)scopes[best].Clone(); else { selected[i] = scopes[best]; scopes.RemoveAt(best); qualities.RemoveAt(best); } } } else { var nicheComparer = GAssistNichesProblemDataParameter.ActualValue.GetPossibleNiches().First().Comparer; var selectPerNiche = new Dictionary(nicheComparer); var nicheScope = new Dictionary>(nicheComparer); //niching tournament selection for (int i = 0; i < individuals.Count; i++) { if (!nicheScope.ContainsKey(individuals[i].Niche)) { nicheScope.Add(individuals[i].Niche, new List()); } nicheScope[individuals[i].Niche].Add(i); } var possibleNiches = nicheScope.Keys.ToList(); foreach (var niche in possibleNiches) { selectPerNiche.Add(niche, count / possibleNiches.Count); } int curCount = 0; while (curCount < count) { IGAssistNiche niche = null; int best = -1; if (doNiching) { niche = GetNiche(random, selectPerNiche, possibleNiches); } else { best = random.Next(scopes.Count); } for (int i = 0; i < parentsPerChild; i++) { int index; if (doNiching) { best = nicheScope[niche][random.Next(nicheScope[niche].Count)]; } for (int j = 1; j < groupSize; j++) { if (niche != null) { index = nicheScope[niche][random.Next(nicheScope[niche].Count)]; } else { index = random.Next(scopes.Count); } if (IsBetterHirachical(index, best, qualities, lengths, lengtThreshold, useLength, maximization)) { best = index; } } niche = individuals[best].Niche; if (copy) selected[curCount] = (IScope)scopes[best].Clone(); else { selected[curCount] = scopes[best]; scopes.RemoveAt(best); qualities.RemoveAt(best); } selectPerNiche[niche]--; curCount++; } } } return selected; } private bool IsBetterHirachical(int indexTrue, int indexFalse, IList qualities, IList length, double hierarchicalThreshold, bool useLength, bool maximization) { if (useLength && IterationsParameter.ActualValue.Value >= IterationHirachicalSelectionParameter.ActualValue.Value && Math.Abs(qualities[indexTrue] - qualities[indexFalse]) <= hierarchicalThreshold && length[indexTrue] != length[indexFalse]) { return length[indexTrue] < length[indexFalse]; } return IsBetter(indexTrue, indexFalse, qualities, maximization); } private bool IsBetter(int indexTrue, int indexFalse, IList qualities, bool maximization) { return ((maximization) && (qualities[indexTrue] > qualities[indexFalse])) || ((!maximization) && (qualities[indexTrue] < qualities[indexFalse])); } private IGAssistNiche GetNiche(IRandom random, Dictionary selectPerNiche, List possibleNiches) { int sum = selectPerNiche.Values.Sum(); if (sum <= 0) { return possibleNiches[random.Next(possibleNiches.Count)]; } int pos = random.Next(sum); int total = 0; IGAssistNiche niche = selectPerNiche.Keys.First(); foreach (var item in selectPerNiche) { total += item.Value; niche = item.Key; if (pos < total) { return niche; } } throw new ArgumentException("error in code"); } } }