using HeuristicLab.Operators; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using HeuristicLab.Common; using HeuristicLab.Persistence.Default.CompositeSerializers.Storable; using HeuristicLab.Core; using HeuristicLab.Data; using HeuristicLab.Parameters; using HeuristicLab.Optimization; using HeuristicLab.PluginInfrastructure; using HeuristicLab.Analysis; namespace HeuristicLab.Algorithms.SimulatedAnnealing { [Item("AcceptanceRatioReheatingOperator", "Reheats the temperature if the acceptance is below a threshold until it is above another one.")] [StorableClass] public class AcceptanceRatioReheatingOperator : SingleSuccessorOperator, IReheatingOperator { #region Strings private const string AnnealingOperatorName = "AnnealingOperator"; private const string LowerTemperatureName = "LowerTemperature"; private const string IterationsName = "Iterations"; private const string TemperatureStartIndexName = "TemperatureStartIndex"; private const string StartTemperatureName = "StartTemperature"; private const string EndTemperatureName = "EndTemperature"; private const string TemperatureName = "Temperature"; private const string MaximumIterationsName = "MaximumIterations"; private const string UpperTemperatureName = "UpperTemperature"; private const string ReheatingOperatorName = "ReheatingOperator"; private const string ThresholdName = "Threshold"; private const string MemorySizeName = "MemorySize"; private const string CoolingName = "Cooling"; private const string IsAcceptedName = "IsAccepted"; private const string AcceptanceMemoryName = "AcceptanceMemory"; private const string AverageAcceptanceRatioName = "AverageAcceptanceRatio"; private const string ReheatWindowSizeName = "ReheatWindowSize"; private const string LastAcceptedQualityName = "LastAcceptedQuality"; private const string MoveQualityName = "MoveQuality"; #endregion #region Parameters public ILookupParameter TemperatureParameter { get { return (ILookupParameter)Parameters[TemperatureName]; } } public ILookupParameter LowerTemperatureParameter { get { return (ILookupParameter)Parameters[LowerTemperatureName]; } } public ILookupParameter StartTemperatureParameter { get { return (ILookupParameter)Parameters[StartTemperatureName]; } } public ILookupParameter EndTemperatureParameter { get { return (ILookupParameter)Parameters[EndTemperatureName]; } } public ILookupParameter TemperatureStartIndexParameter { get { return (ILookupParameter)Parameters[TemperatureStartIndexName]; } } public ILookupParameter IterationsParameter { get { return (ILookupParameter)Parameters[IterationsName]; } } public ILookupParameter AnnealingOperatorParameter { get { return (ILookupParameter)Parameters[AnnealingOperatorName]; } } public ILookupParameter MaximumIterationsParameter { get { return (ILookupParameter)Parameters[MaximumIterationsName]; } } private ValueLookupParameter UpperTemperatureParameter { get { return (ValueLookupParameter)Parameters[UpperTemperatureName]; } } private IConstrainedValueParameter ReheatingOperatorParameter { get { return (IConstrainedValueParameter)Parameters[ReheatingOperatorName]; } } private ValueParameter ThresholdParameter { get { return (ValueParameter)Parameters[ThresholdName]; } } private ValueParameter MemorySizeParameter { get { return (ValueParameter)Parameters[MemorySizeName]; } } public ILookupParameter CoolingParameter { get { return (ILookupParameter)Parameters[CoolingName]; } } public ILookupParameter IsAcceptedParameter { get { return (ILookupParameter)Parameters[IsAcceptedName]; } } public ILookupParameter> AcceptanceMemoryParameter { get { return (ILookupParameter>)Parameters[AcceptanceMemoryName]; } } public ILookupParameter AverageAcceptanceRatioParameter { get { return (ILookupParameter)Parameters[AverageAcceptanceRatioName]; } } public IValueParameter ReheatWindowSizeParameter { get { return (IValueParameter)Parameters[ReheatWindowSizeName]; } } public ILookupParameter MoveQualityParameter { get { return (ILookupParameter)Parameters[MoveQualityName]; } } public ILookupParameter LastAcceptedQualityParameter { get { return (ILookupParameter)Parameters[LastAcceptedQualityName]; } } #endregion public AcceptanceRatioReheatingOperator() : base() { #region Create parameters Parameters.Add(new LookupParameter(TemperatureName, "The current temperature.")); Parameters.Add(new LookupParameter(LowerTemperatureName, "The lower bound of the temperature.")); Parameters.Add(new LookupParameter(IterationsName, "The number of iterations.")); Parameters.Add(new LookupParameter(AnnealingOperatorName, "The operator that cools the temperature.")); Parameters.Add(new LookupParameter(TemperatureStartIndexName, "The index where the annealing or heating was last changed.")); Parameters.Add(new LookupParameter(StartTemperatureName, "The temperature from which cooling or reheating should occur.")); Parameters.Add(new LookupParameter(EndTemperatureName, "The temperature to which should be cooled or heated.")); Parameters.Add(new LookupParameter(MaximumIterationsName, "The maximum number of iterations which should be processed.")); Parameters.Add(new LookupParameter(IsAcceptedName, "Whether the move was accepted or not.")); Parameters.Add(new LookupParameter>(AcceptanceMemoryName, "Memorizes the last N acceptance decisions.")); Parameters.Add(new LookupParameter(CoolingName, "True when the temperature should be cooled, false otherwise.")); Parameters.Add(new ValueLookupParameter(UpperTemperatureName, "The upper bound of the temperature.", "InitialTemperature")); Parameters.Add(new ConstrainedValueParameter(ReheatingOperatorName, "The operator that reheats the temperature.")); Parameters.Add(new ValueParameter(ThresholdName, "The threshold controls the temperature. If the average ratio of accepted moves goes below the start of the range the temperature is heated. If the the average ratio of accepted moves goes beyond the end of the range the temperature is cooled again.", new DoubleRange(0.01, 0.1))); Parameters.Add(new ValueParameter(MemorySizeName, "The maximum size of the acceptance memory.", new IntValue(100))); Parameters.Add(new LookupParameter(AverageAcceptanceRatioName, "Average acceptance over full acceptance memory.")); Parameters.Add(new ValueParameter(ReheatWindowSizeName, "The amount of iterations each reheat needs to heat the current temperature to upper temperature.", new IntValue(10000))); Parameters.Add(new LookupParameter(LastAcceptedQualityName, "Quality of last accepted solution.")); Parameters.Add(new LookupParameter(MoveQualityName, "The value which represents the quality of a move.")); #endregion foreach (var op in ApplicationManager.Manager.GetInstances().OrderBy(x => x.Name)) { ReheatingOperatorParameter.ValidValues.Add((IDiscreteDoubleValueModifier) op); } Parameterize(); } [StorableConstructor] protected AcceptanceRatioReheatingOperator(bool deserializing) : base(deserializing) { } protected AcceptanceRatioReheatingOperator(AcceptanceRatioReheatingOperator original, Cloner cloner) : base(original, cloner) { } public override IDeepCloneable Clone(Cloner cloner) { return new AcceptanceRatioReheatingOperator(this, cloner); } public void Parameterize() { foreach (var op in ReheatingOperatorParameter.ValidValues) { op.IndexParameter.ActualName = IterationsName; op.IndexParameter.Hidden = true; op.StartIndexParameter.Value = null; op.StartIndexParameter.ActualName = TemperatureStartIndexName; op.EndIndexParameter.ActualName = MaximumIterationsParameter.Name; op.ValueParameter.ActualName = TemperatureName; op.ValueParameter.Hidden = true; op.StartValueParameter.ActualName = StartTemperatureName; op.StartValueParameter.Hidden = true; op.EndValueParameter.ActualName = EndTemperatureName; op.EndValueParameter.Hidden = true; } } public override IOperation Apply() { var isAccepted = IsAcceptedParameter.ActualValue.Value && LastAcceptedQualityParameter.ActualValue.Value != MoveQualityParameter.ActualValue.Value; var acceptances = AcceptanceMemoryParameter.ActualValue; var cooling = CoolingParameter.ActualValue.Value; var ratioAmount = AverageAcceptanceRatioParameter.ActualValue; acceptances.Add(new BoolValue(isAccepted)); ratioAmount.Value += isAccepted ? 1 : 0; if (acceptances.Count > MemorySizeParameter.Value.Value) { ratioAmount.Value -= acceptances.ElementAt(0).Value ? 1 : 0; acceptances.RemoveAt(0); } // only reheat when at least MemorySizeParameter.Value iterations have passed if (acceptances.Count == MemorySizeParameter.Value.Value) { var ratio = ratioAmount.Value / MemorySizeParameter.Value.Value; var ratioStart = ThresholdParameter.Value.Start; var ratioEnd = ThresholdParameter.Value.End; if (!cooling && ratio >= ratioEnd) { // if we are heating, but should be cooling cooling = true; TemperatureStartIndexParameter.ActualValue.Value = Math.Max(0, IterationsParameter.ActualValue.Value - 1); StartTemperatureParameter.ActualValue.Value = TemperatureParameter.ActualValue.Value; EndTemperatureParameter.ActualValue.Value = LowerTemperatureParameter.ActualValue.Value; } else if (cooling && ratio <= ratioStart) { // if we are cooling but should be heating cooling = false; TemperatureStartIndexParameter.ActualValue.Value = Math.Max(0, IterationsParameter.ActualValue.Value - 1); StartTemperatureParameter.ActualValue.Value = TemperatureParameter.ActualValue.Value; EndTemperatureParameter.ActualValue.Value = UpperTemperatureParameter.ActualValue.Value; ReheatingOperatorParameter.Value.EndIndexParameter.Value = new IntValue( Math.Min(MaximumIterationsParameter.ActualValue.Value, IterationsParameter.ActualValue.Value + ReheatWindowSizeParameter.Value.Value)); } CoolingParameter.ActualValue.Value = cooling; } if (isAccepted) { LastAcceptedQualityParameter.ActualValue.Value = MoveQualityParameter.ActualValue.Value; } return cooling ? Cool() : Heat(); } private IOperation Heat() { return new OperationCollection { ExecutionContext.CreateOperation(ReheatingOperatorParameter.Value), base.Apply() }; } private IOperation Cool() { return new OperationCollection { ExecutionContext.CreateOperation(AnnealingOperatorParameter.ActualValue), base.Apply() }; } } }