using System; using System.Linq; using HEAL.Attic; using HeuristicLab.Collections; using HeuristicLab.Common; using HeuristicLab.Core; using HeuristicLab.Optimization; using HeuristicLab.Parameters; namespace HeuristicLab.Problems.Modifiers { [StorableType("4C46E221-E7E6-41B4-8CFB-35920CDCE6E8")] [Creatable(CreatableAttribute.Categories.Problems, Priority = 90)] [Item("SingleObjectiveModifiedProblem", "A wrapper problem for single-objective problems that should be evaluated using Hive.")] public class SingleObjectiveModifiedProblem : SingleObjectiveBasicProblem, IStatefulItem { private readonly SingleObjectiveEndPointProblemModifier endPoint = new SingleObjectiveEndPointProblemModifier(); private ProblemModifier activeStart; //this is to avoid attaching to multiple modifiers public override bool Maximization => false; // this is a fixed and readonly parameter in BasicProblem, we cannot change it dynamically (after the constructor). #region Parameter Names private const string ProblemParameterName = "Problem"; private const string ModifierParameterName = "Modifiers"; #endregion #region Parameters public IValueParameter> ProblemParameter => (IValueParameter>)Parameters[ProblemParameterName]; public IFixedValueParameter> ModifierParameter => (IFixedValueParameter>)Parameters[ModifierParameterName]; #endregion #region Parameter Properties public SingleObjectiveBasicProblem Problem { get => ProblemParameter.Value; set => ProblemParameter.Value = value; } public CheckedItemList Modifiers => ModifierParameter.Value; #endregion #region Constructors & Cloning [StorableConstructor] protected SingleObjectiveModifiedProblem(StorableConstructorFlag _) : base(_) { } protected SingleObjectiveModifiedProblem(SingleObjectiveModifiedProblem original, Cloner cloner) : base(original, cloner) { Initialize(); } public SingleObjectiveModifiedProblem() { var defaultModifiers = new CheckedItemList { {new EvaluationCacheProblemModifier(), false}, {new EvaluationRepetitionProblemModifier(), false}, {new EvaluationLoggingProblemModifier(), false} }; Parameters.Add(new ValueParameter>(ProblemParameterName, "The actual problem that should used to evaluate individuals.")); Parameters.Add(new FixedValueParameter>(ModifierParameterName, "The modifiers applied to the problem (order can be important)", defaultModifiers)); Initialize(); } public override IDeepCloneable Clone(Cloner cloner) { return new SingleObjectiveModifiedProblem(this, cloner); } #endregion [StorableHook(HookType.AfterDeserialization)] private void AfterDeserialization() { Initialize(); } private void Initialize() { if (Problem != null) { Problem.OperatorsChanged -= Problem_OperatorsChanged; Problem.OperatorsChanged += Problem_OperatorsChanged; } ProblemParameter.ValueChanged -= ProblemParameter_ValueChanged; ProblemParameter.ValueChanged += ProblemParameter_ValueChanged; Modifiers.CheckedItemsChanged -= ActiveModifiersChanged; Modifiers.CheckedItemsChanged += ActiveModifiersChanged; Modifiers.ItemsMoved -= ActiveModifiersChanged; //This my fire unnecessarily but CheckedItemsChanged does not account for order ... Modifiers.ItemsMoved += ActiveModifiersChanged; UpdateModifiers(); } public override double Evaluate(Individual individual, IRandom random) { // this must be a minimization problem. If the wrapped problem is a maximization problem then just return the negative quality. var wrappedQuality = activeStart.ModifiedEvaluate(individual, random)[0]; return Problem.Maximization ? -wrappedQuality : wrappedQuality; } public override void Analyze(Individual[] individuals, double[] qualities, ResultCollection results, IRandom random) { activeStart.ModifiedAnalyze(individuals, qualities.Select(x => new[] { x }).ToArray(), results, random); } private bool updating; private void UpdateModifiers() { if (Problem == null || updating) return; updating = true; var am = Modifiers.CheckedItems.Select(x => x.Value).ToArray(); //unwire modifiers to prevent cycles while setting and superfluous listeners foreach (var problemModifier in Modifiers) problemModifier.NextModifier = null; //wire modifiers for (var i = 0; i < am.Length - 1; i++) am[i].NextModifier = am[i + 1]; if (am.Length != 0) am[am.Length - 1].NextModifier = endPoint; endPoint.Problem = Problem; //listen to activeStart changes if (activeStart != null) { activeStart.EncodingChanged -= StartEncodingChanged; } activeStart = am.Length == 0 ? endPoint : am[0]; activeStart.EncodingChanged += StartEncodingChanged; foreach (var m in Modifiers) m.Initialize(); updating = false; if (Encoding == activeStart.Encoding || activeStart.Encoding == null) return; Encoding = activeStart.Encoding; } private void StartEncodingChanged(object sender, EventArgs e) { if (Encoding == activeStart.Encoding || activeStart.Encoding == null) return; Encoding = activeStart.Encoding; } private void ActiveModifiersChanged(object sender, CollectionItemsChangedEventArgs> e) { UpdateModifiers(); } private void ProblemParameter_ValueChanged(object sender, EventArgs e) { if (Problem != null) { Problem.OperatorsChanged -= Problem_OperatorsChanged; Problem.OperatorsChanged += Problem_OperatorsChanged; } endPoint.Problem = Problem; UpdateModifiers(); } private void Problem_OperatorsChanged(object sender, EventArgs e) { UpdateModifiers(); } void IStatefulItem.InitializeState() { UpdateModifiers(); } void IStatefulItem.ClearState() { UpdateModifiers(); } } }