#region License Information
/* HeuristicLab
* Copyright (C) 2002-2016 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 HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
using HeuristicLab.PluginInfrastructure;
namespace HeuristicLab.Encodings.RealVectorEncoding {
[Item("RealVectorSwarmUpdater", "Updates personal best point and quality as well as global best point and quality.")]
[StorableClass]
public sealed class RealVectorSwarmUpdater : 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 VelocityBoundsParameter {
get { return (ValueLookupParameter)Parameters["VelocityBounds"]; }
}
public ILookupParameter CurrentVelocityBoundsParameter {
get { return (ILookupParameter)Parameters["CurrentVelocityBounds"]; }
}
public LookupParameter ResultsParameter {
get { return (LookupParameter)Parameters["Results"]; }
}
#region Velocity Bounds Updating
public ILookupParameter VelocityBoundsScaleParameter {
get { return (ILookupParameter)Parameters["VelocityBoundsScale"]; }
}
public IConstrainedValueParameter VelocityBoundsScalingOperatorParameter {
get { return (IConstrainedValueParameter)Parameters["VelocityBoundsScalingOperator"]; }
}
public IValueLookupParameter VelocityBoundsStartValueParameter {
get { return (IValueLookupParameter)Parameters["VelocityBoundsStartValue"]; }
}
public IValueLookupParameter VelocityBoundsEndValueParameter {
get { return (IValueLookupParameter)Parameters["VelocityBoundsEndValue"]; }
}
public ILookupParameter VelocityBoundsIndexParameter {
get { return (ILookupParameter)Parameters["VelocityBoundsIndex"]; }
}
public IValueLookupParameter VelocityBoundsStartIndexParameter {
get { return (IValueLookupParameter)Parameters["VelocityBoundsStartIndex"]; }
}
public IValueLookupParameter VelocityBoundsEndIndexParameter {
get { return (IValueLookupParameter)Parameters["VelocityBoundsEndIndex"]; }
}
#endregion
#endregion
#region Construction & Cloning
[StorableConstructor]
private RealVectorSwarmUpdater(bool deserializing) : base(deserializing) { }
private RealVectorSwarmUpdater(RealVectorSwarmUpdater original, Cloner cloner)
: base(original, cloner) {
ResultsCollector = cloner.Clone(original.ResultsCollector);
}
public RealVectorSwarmUpdater()
: 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("VelocityBounds", "Maximum velocity for each dimension.", new DoubleMatrix(new double[,] { { -1, 1 } })));
Parameters.Add(new LookupParameter("CurrentVelocityBounds", "Current value of velocity bounds."));
Parameters.Add(new LookupParameter("Results", "Results"));
#region Velocity Bounds Updating
Parameters.Add(new LookupParameter("VelocityBoundsScale", "Scale parameter."));
Parameters.Add(new OptionalConstrainedValueParameter("VelocityBoundsScalingOperator", "Modifies the value"));
Parameters.Add(new ValueLookupParameter("VelocityBoundsStartValue", "The start value of 'Value'.", new DoubleValue(1)));
Parameters.Add(new ValueLookupParameter("VelocityBoundsEndValue", "The end value of 'Value'.", new DoubleValue(1E-10)));
Parameters.Add(new LookupParameter("VelocityBoundsIndex", "The current index.", "Iterations"));
Parameters.Add(new ValueLookupParameter("VelocityBoundsStartIndex", "The start index at which to start modifying 'Value'.", new IntValue(0)));
Parameters.Add(new ValueLookupParameter("VelocityBoundsEndIndex", "The end index by which 'Value' should have reached 'EndValue'.", "MaxIterations"));
VelocityBoundsStartIndexParameter.Hidden = true;
VelocityBoundsEndIndexParameter.Hidden = true;
#endregion
Initialize();
RegisterEvents();
}
public override IDeepCloneable Clone(Cloner cloner) {
return new RealVectorSwarmUpdater(this, cloner);
}
#endregion
[StorableHook(HookType.AfterDeserialization)]
private void AfterDeserialization() {
if (!Parameters.ContainsKey("SwarmBestQuality")) {
ILookupParameter oldBestQualityParameter = Parameters["BestQuality"] as ILookupParameter;
Parameters.Add(new LookupParameter("SwarmBestQuality", "Swarm's best quality."));
if (oldBestQualityParameter.ActualName != oldBestQualityParameter.Name)
SwarmBestQualityParameter.ActualName = oldBestQualityParameter.ActualName;
Parameters.Remove("BestQuality");
}
RegisterEvents();
}
private void RegisterEvents() {
VelocityBoundsStartValueParameter.ValueChanged += new EventHandler(VelocityBoundsStartValueParameter_ValueChanged);
VelocityBoundsStartValueParameter.Value.ValueChanged += new EventHandler(VelocityBoundsStartValueParameter_Value_ValueChanged);
}
void VelocityBoundsStartValueParameter_Value_ValueChanged(object sender, EventArgs e) {
UpdateVelocityBoundsParamater();
}
void UpdateVelocityBoundsParamater() {
if (VelocityBoundsParameter.Value == null) {
VelocityBoundsParameter.Value = new DoubleMatrix(1, 2);
} else if (VelocityBoundsParameter.Value.Columns != 2) {
VelocityBoundsParameter.Value = new DoubleMatrix(VelocityBoundsParameter.Value.Rows, 2);
}
if (VelocityBoundsStartValueParameter.Value != null) {
DoubleMatrix matrix = VelocityBoundsParameter.Value;
for (int i = 0; i < matrix.Rows; i++) {
matrix[i, 0] = (-1) * VelocityBoundsStartValueParameter.Value.Value;
matrix[i, 1] = VelocityBoundsStartValueParameter.Value.Value;
}
}
}
void VelocityBoundsStartValueParameter_ValueChanged(object sender, EventArgs e) {
if (VelocityBoundsStartValueParameter.Value != null) {
VelocityBoundsStartValueParameter.Value.ValueChanged += new EventHandler(VelocityBoundsStartValueParameter_Value_ValueChanged);
}
UpdateVelocityBoundsParamater();
}
private void Initialize() {
ResultsCollector = new ResultsCollector();
ResultsCollector.CollectedValues.Add(CurrentVelocityBoundsParameter);
ResultsCollector.CollectedValues.Add(VelocityBoundsParameter);
foreach (IDiscreteDoubleValueModifier op in ApplicationManager.Manager.GetInstances()) {
VelocityBoundsScalingOperatorParameter.ValidValues.Add(op);
op.ValueParameter.ActualName = VelocityBoundsScaleParameter.Name;
op.StartValueParameter.ActualName = VelocityBoundsStartValueParameter.Name;
op.EndValueParameter.ActualName = VelocityBoundsEndValueParameter.Name;
op.IndexParameter.ActualName = VelocityBoundsIndexParameter.Name;
op.StartIndexParameter.ActualName = VelocityBoundsStartIndexParameter.Name;
op.EndIndexParameter.ActualName = VelocityBoundsEndIndexParameter.Name;
}
VelocityBoundsScalingOperatorParameter.Value = null;
}
public override IOperation Apply() {
var max = MaximizationParameter.ActualValue.Value;
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();
UpdateGlobalBest(max, particles);
UpdateNeighborBest(max, particles);
UpdatePersonalBest(max, particles);
return UpdateVelocityBounds();
}
private void UpdateGlobalBest(bool maximization, IList> particles) {
var best = maximization ? particles.MaxItems(x => x.Item3).First() : particles.MinItems(x => x.Item3).First();
var bestQuality = SwarmBestQualityParameter.ActualValue;
if (bestQuality == null) {
SwarmBestQualityParameter.ActualValue = new DoubleValue(best.Item3);
} else bestQuality.Value = best.Item3;
BestRealVectorParameter.ActualValue = (RealVector)best.Item2.Clone();
}
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);
for (int n = 0; n < neighbors.Length; n++) {
var pairs = particles.Where(x => x.Item1 == n || neighbors[n].Contains(x.Item1));
var bestNeighbor = (maximization ? pairs.MaxItems(p => p.Item3)
: pairs.MinItems(p => p.Item3)).First();
neighborBest[n] = bestNeighbor.Item2;
neighborBestQuality[n] = new DoubleValue(bestNeighbor.Item3);
}
NeighborBestParameter.ActualValue = neighborBest;
NeighborBestQualityParameter.ActualValue = neighborBestQuality;
}
}
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] = p.Item2;
}
}
PersonalBestParameter.ActualValue = personalBest;
}
private IOperation UpdateVelocityBounds() {
var currentVelocityBounds = CurrentVelocityBoundsParameter.ActualValue;
if (currentVelocityBounds == null) {
currentVelocityBounds = (DoubleMatrix)VelocityBoundsParameter.ActualValue.Clone();
CurrentVelocityBoundsParameter.ActualValue = currentVelocityBounds;
}
if (VelocityBoundsScalingOperatorParameter.Value == null)
return new OperationCollection() {
ExecutionContext.CreateChildOperation(ResultsCollector),
base.Apply()
};
var velocityBoundsScale = VelocityBoundsScaleParameter.ActualValue;
var velocityBoundsStartValue = VelocityBoundsStartValueParameter.ActualValue;
if (velocityBoundsScale == null && velocityBoundsStartValue != null) {
velocityBoundsScale = new DoubleValue(velocityBoundsStartValue.Value);
VelocityBoundsScaleParameter.ActualValue = velocityBoundsScale;
}
for (int i = 0; i < currentVelocityBounds.Rows; i++) {
for (int j = 0; j < currentVelocityBounds.Columns; j++) {
if (currentVelocityBounds[i, j] >= 0) {
currentVelocityBounds[i, j] = velocityBoundsScale.Value;
} else {
currentVelocityBounds[i, j] = (-1) * velocityBoundsScale.Value;
}
}
}
return new OperationCollection() {
ExecutionContext.CreateChildOperation(ResultsCollector),
ExecutionContext.CreateChildOperation(VelocityBoundsScalingOperatorParameter.Value),
base.Apply()
};
}
}
}