#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 HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Data;
using HeuristicLab.Operators;
using HeuristicLab.Optimization;
using HeuristicLab.Optimization.Operators;
using HeuristicLab.Parameters;
using HEAL.Attic;
using HeuristicLab.PluginInfrastructure;
namespace HeuristicLab.Encodings.RealVectorEncoding {
[Item("Swarm Updater (SPSO)", "Updates personal best point and quality as well as neighbor best point and quality.")]
[StorableType("B8244196-9DB9-477C-A0A1-C1EB5BF4E1C1")]
public sealed class SPSOSwarmUpdater : SingleSuccessorOperator, IRealVectorSwarmUpdater, ISingleObjectiveOperator {
[Storable]
private ResultsCollector ResultsCollector;
public override bool CanChangeName {
get { return false; }
}
#region Parameter properties
public IScopeTreeLookupParameter QualityParameter {
get { return (IScopeTreeLookupParameter)Parameters["Quality"]; }
}
public IScopeTreeLookupParameter PersonalBestQualityParameter {
get { return (IScopeTreeLookupParameter)Parameters["PersonalBestQuality"]; }
}
public IScopeTreeLookupParameter NeighborBestQualityParameter {
get { return (IScopeTreeLookupParameter)Parameters["NeighborBestQuality"]; }
}
public IScopeTreeLookupParameter RealVectorParameter {
get { return (IScopeTreeLookupParameter)Parameters["RealVector"]; }
}
public IScopeTreeLookupParameter PersonalBestParameter {
get { return (IScopeTreeLookupParameter)Parameters["PersonalBest"]; }
}
public IScopeTreeLookupParameter NeighborBestParameter {
get { return (IScopeTreeLookupParameter)Parameters["NeighborBest"]; }
}
public ILookupParameter MaximizationParameter {
get { return (ILookupParameter)Parameters["Maximization"]; }
}
public ILookupParameter SwarmBestQualityParameter {
get { return (ILookupParameter)Parameters["SwarmBestQuality"]; }
}
public ILookupParameter BestRealVectorParameter {
get { return (ILookupParameter)Parameters["BestRealVector"]; }
}
public IScopeTreeLookupParameter NeighborsParameter {
get { return (IScopeTreeLookupParameter)Parameters["Neighbors"]; }
}
public IValueLookupParameter MaxVelocityParameter {
get { return (ValueLookupParameter)Parameters["MaxVelocity"]; }
}
public ILookupParameter CurrentMaxVelocityParameter {
get { return (ILookupParameter)Parameters["CurrentMaxVelocity"]; }
}
public LookupParameter ResultsParameter {
get { return (LookupParameter)Parameters["Results"]; }
}
#region Max Velocity Updating
public IConstrainedValueParameter MaxVelocityScalingOperatorParameter {
get { return (IConstrainedValueParameter)Parameters["MaxVelocityScalingOperator"]; }
}
public IValueLookupParameter FinalMaxVelocityParameter {
get { return (IValueLookupParameter)Parameters["FinalMaxVelocity"]; }
}
public ILookupParameter MaxVelocityIndexParameter {
get { return (ILookupParameter)Parameters["MaxVelocityIndex"]; }
}
public IValueLookupParameter MaxVelocityStartIndexParameter {
get { return (IValueLookupParameter)Parameters["MaxVelocityStartIndex"]; }
}
public IValueLookupParameter MaxVelocityEndIndexParameter {
get { return (IValueLookupParameter)Parameters["MaxVelocityEndIndex"]; }
}
#endregion
#endregion
#region Construction & Cloning
[StorableConstructor]
private SPSOSwarmUpdater(StorableConstructorFlag _) : base(_) { }
private SPSOSwarmUpdater(SPSOSwarmUpdater original, Cloner cloner)
: base(original, cloner) {
ResultsCollector = cloner.Clone(original.ResultsCollector);
}
public SPSOSwarmUpdater()
: base() {
Parameters.Add(new LookupParameter("SwarmBestQuality", "Swarm's best quality."));
Parameters.Add(new LookupParameter("BestRealVector", "Global best particle position."));
Parameters.Add(new ScopeTreeLookupParameter("Quality", "Particles' qualities."));
Parameters.Add(new ScopeTreeLookupParameter("PersonalBestQuality", "Particles' personal best qualities."));
Parameters.Add(new ScopeTreeLookupParameter("NeighborBestQuality", "Best neighbor particles' qualities."));
Parameters.Add(new ScopeTreeLookupParameter("RealVector", "Particles' positions."));
Parameters.Add(new ScopeTreeLookupParameter("PersonalBest", "Particles' personal best positions."));
Parameters.Add(new ScopeTreeLookupParameter("NeighborBest", "Neighborhood (or global in case of totally connected neighborhood) best particle positions."));
Parameters.Add(new ScopeTreeLookupParameter("Neighbors", "The list of neighbors for each particle."));
Parameters.Add(new LookupParameter("Maximization", "True if the problem is a maximization problem, otherwise false."));
Parameters.Add(new ValueLookupParameter("MaxVelocity", "The maximum velocity for each particle and initial velocity if scaling is used.", new DoubleValue(double.MaxValue)));
Parameters.Add(new LookupParameter("CurrentMaxVelocity", "Current value of the speed limit."));
Parameters.Add(new LookupParameter("Results", "Results"));
#region Max Velocity Updating
Parameters.Add(new OptionalConstrainedValueParameter("MaxVelocityScalingOperator", "Modifies the value"));
Parameters.Add(new ValueLookupParameter("FinalMaxVelocity", "The value of maximum velocity if scaling is used and PSO has reached maximum iterations.", new DoubleValue(1E-10)));
Parameters.Add(new LookupParameter("MaxVelocityIndex", "The current index.", "Iterations"));
Parameters.Add(new ValueLookupParameter("MaxVelocityStartIndex", "The start index at which to start modifying 'Value'.", new IntValue(0)));
Parameters.Add(new ValueLookupParameter("MaxVelocityEndIndex", "The end index by which 'Value' should have reached 'EndValue'.", "MaxIterations"));
MaxVelocityStartIndexParameter.Hidden = true;
MaxVelocityEndIndexParameter.Hidden = true;
#endregion
Initialize();
}
public override IDeepCloneable Clone(Cloner cloner) {
return new SPSOSwarmUpdater(this, cloner);
}
#endregion
private void Initialize() {
ResultsCollector = new ResultsCollector();
ResultsCollector.CollectedValues.Add(CurrentMaxVelocityParameter);
ResultsCollector.CollectedValues.Add(MaxVelocityParameter);
foreach (IDiscreteDoubleValueModifier op in ApplicationManager.Manager.GetInstances()) {
MaxVelocityScalingOperatorParameter.ValidValues.Add(op);
op.ValueParameter.ActualName = CurrentMaxVelocityParameter.Name;
op.StartValueParameter.ActualName = MaxVelocityParameter.Name;
op.EndValueParameter.ActualName = FinalMaxVelocityParameter.Name;
op.IndexParameter.ActualName = MaxVelocityIndexParameter.Name;
op.StartIndexParameter.ActualName = MaxVelocityStartIndexParameter.Name;
op.EndIndexParameter.ActualName = MaxVelocityEndIndexParameter.Name;
}
MaxVelocityScalingOperatorParameter.Value = null;
}
public override IOperation Apply() {
var max = MaximizationParameter.ActualValue.Value;
// Update of the personal bests
var points = RealVectorParameter.ActualValue;
var qualities = QualityParameter.ActualValue;
var particles = points.Select((p, i) => new { Particle = p, Index = i })
.Zip(qualities, (p, q) => Tuple.Create(p.Index, p.Particle, q.Value)).ToList();
UpdatePersonalBest(max, particles);
// SPSO: update of the neighbor bests from the personal bests
var personalBestPoints = PersonalBestParameter.ActualValue;
var personalBestQualities = PersonalBestQualityParameter.ActualValue;
particles = personalBestPoints.Select((p, i) => new { Particle = p, Index = i })
.Zip(personalBestQualities, (p, q) => Tuple.Create(p.Index, p.Particle, q.Value)).ToList();
UpdateNeighborBest(max, particles);
var next = new OperationCollection() { base.Apply() };
next.Insert(0, ExecutionContext.CreateChildOperation(ResultsCollector));
if (MaxVelocityScalingOperatorParameter.Value != null) {
next.Insert(0, ExecutionContext.CreateChildOperation(MaxVelocityScalingOperatorParameter.Value));
} else CurrentMaxVelocityParameter.ActualValue = new DoubleValue(MaxVelocityParameter.ActualValue.Value);
return next;
}
private void UpdateNeighborBest(bool maximization, IList> particles) {
var neighbors = NeighborsParameter.ActualValue;
if (neighbors.Length > 0) {
var neighborBest = new ItemArray(neighbors.Length);
var neighborBestQuality = new ItemArray(neighbors.Length);
double overallBest = double.NaN;
RealVector overallBestVector = null;
for (int n = 0; n < neighbors.Length; n++) {
var neighborhood = particles.Where(x => neighbors[n].Contains(x.Item1));
var bestNeighbor = (maximization ? neighborhood.MaxItems(p => p.Item3)
: neighborhood.MinItems(p => p.Item3)).First();
neighborBest[n] = bestNeighbor.Item2;
neighborBestQuality[n] = new DoubleValue(bestNeighbor.Item3);
if (double.IsNaN(overallBest) || maximization && bestNeighbor.Item3 > overallBest
|| !maximization && bestNeighbor.Item3 < overallBest) {
overallBest = bestNeighbor.Item3;
overallBestVector = bestNeighbor.Item2;
}
}
NeighborBestParameter.ActualValue = neighborBest;
NeighborBestQualityParameter.ActualValue = neighborBestQuality;
SwarmBestQualityParameter.ActualValue = new DoubleValue(overallBest);
BestRealVectorParameter.ActualValue = overallBestVector;
} else {
// Neighbor best = Global best
var best = maximization ? particles.MaxItems(x => x.Item3).First() : particles.MinItems(x => x.Item3).First();
NeighborBestParameter.ActualValue = new ItemArray(Enumerable.Repeat(best.Item2, particles.Count));
NeighborBestQualityParameter.ActualValue = new ItemArray(Enumerable.Repeat(new DoubleValue(best.Item3), particles.Count));
SwarmBestQualityParameter.ActualValue = new DoubleValue(best.Item3);
BestRealVectorParameter.ActualValue = best.Item2;
}
}
private void UpdatePersonalBest(bool maximization, IList> particles) {
var personalBest = PersonalBestParameter.ActualValue;
var personalBestQuality = PersonalBestQualityParameter.ActualValue;
if (personalBestQuality.Length == 0) {
personalBestQuality = new ItemArray(particles.Select(x => new DoubleValue(x.Item3)));
PersonalBestQualityParameter.ActualValue = personalBestQuality;
}
foreach (var p in particles) {
if (maximization && p.Item3 > personalBestQuality[p.Item1].Value ||
!maximization && p.Item3 < personalBestQuality[p.Item1].Value) {
personalBestQuality[p.Item1].Value = p.Item3;
personalBest[p.Item1] = new RealVector(p.Item2);
}
}
PersonalBestParameter.ActualValue = personalBest;
}
}
}