#region License Information
/* HeuristicLab
* Copyright (C) 2002-2010 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.Linq;
using HeuristicLab.Algorithms.GeneticAlgorithm;
using HeuristicLab.Collections;
using HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Data;
using HeuristicLab.Optimization;
using HeuristicLab.Parameters;
using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
using HeuristicLab.PluginInfrastructure;
using HeuristicLab.Problems.DataAnalysis;
using HeuristicLab.Problems.TestFunctions;
namespace HeuristicLab.Problems.MetaOptimization {
[Item("Meta Optimization Problem", "Represents a Meta Optimization Problem.")]
[Creatable("Problems")]
[StorableClass]
public sealed class MetaOptimizationProblem : SingleObjectiveHeuristicOptimizationProblem, IStorableContent {
public string Filename { get; set; }
public const string AlgorithmTypeParameterName = "AlgorithmType";
public const string ProblemTypeParameterName = "ProblemType";
public const string ProblemsParameterName = "Problems";
public const string ParameterConfigurationTreeParameterName = "ParameterConfiguration";
public const string RepetitionsParameterName = "Repetitions";
public const string QualityMeasureNameName = "QualityMeasureName";
public const string IntValueManipulatorParameterName = "IntValueManipulator";
public const string DoubleValueManipulatorParameterName = "DoubleValueManipulator";
public const string IntValueCrossoverParameterName = "IntValueCrossover";
public const string DoubleValueCrossoverParameterName = "DoubleValueCrossover";
public const string QualityWeightParameterName = "QualityWeight";
public const string StandardDeviationWeightParameterName = "StandardDeviationWeight";
public const string EvaluatedSolutionsWeightParameterName = "EvaluatedSolutionsWeight";
#region Parameter Properties
public IValueParameter> AlgorithmTypeParameter {
get { return (ValueParameter>)Parameters[AlgorithmTypeParameterName]; }
}
public IValueParameter> ProblemTypeParameter {
get { return (ValueParameter>)Parameters[ProblemTypeParameterName]; }
}
public IValueParameter> ProblemsParameter {
get { return (ValueParameter>)Parameters[ProblemsParameterName]; }
}
public IValueParameter ParameterConfigurationTreeParameter {
get { return (OptionalValueParameter)Parameters[ParameterConfigurationTreeParameterName]; }
}
public IValueParameter RepetitionsParameter {
get { return (ValueParameter)Parameters[RepetitionsParameterName]; }
}
public IConstrainedValueParameter IntValueManipulatorParameter {
get { return (ConstrainedValueParameter)Parameters[IntValueManipulatorParameterName]; }
}
public IConstrainedValueParameter DoubleValueManipulatorParameter {
get { return (ConstrainedValueParameter)Parameters[DoubleValueManipulatorParameterName]; }
}
public IValueParameter QualityMeasureNameParameter {
get { return (ValueParameter)Parameters[QualityMeasureNameName]; }
}
public IConstrainedValueParameter IntValueCrossoverParameter {
get { return (ConstrainedValueParameter)Parameters[IntValueCrossoverParameterName]; }
}
public IConstrainedValueParameter DoubleValueCrossoverParameter {
get { return (ConstrainedValueParameter)Parameters[DoubleValueCrossoverParameterName]; }
}
#endregion
#region Properties
public IAlgorithm Algorithm {
get { return CreateAlgorithm(AlgorithmType.Value, Problems.FirstOrDefault()); }
}
public ConstrainedTypeValue AlgorithmType {
get { return AlgorithmTypeParameter.Value; }
set { AlgorithmTypeParameter.Value = value; }
}
public ConstrainedTypeValue ProblemType {
get { return ProblemTypeParameter.Value; }
set { ProblemTypeParameter.Value = value; }
}
public ConstrainedItemList Problems {
get { return ProblemsParameter.Value; }
set { ProblemsParameter.Value = value; }
}
public ParameterConfigurationTree ParameterConfigurationTree {
get { return ParameterConfigurationTreeParameter.Value; }
set { ParameterConfigurationTreeParameter.Value = value; }
}
public IntValue Repetitions {
get { return RepetitionsParameter.Value; }
set { RepetitionsParameter.Value = value; }
}
private BestParameterConfigurationAnalyzer BestParameterConfigurationAnalyzer {
get { return Operators.OfType().FirstOrDefault(); }
}
private ReferenceQualityAnalyzer ReferenceQualityAnalyzer {
get { return Operators.OfType().FirstOrDefault(); }
}
private SolutionCacheAnalyzer RunsAnalyzer {
get { return Operators.OfType().FirstOrDefault(); }
}
private PMOPopulationDiversityAnalyzer PMOPopulationDiversityAnalyzer {
get { return Operators.OfType().FirstOrDefault(); }
}
private PMOProblemQualitiesAnalyzer PMOProblemQualitiesAnalyzer {
get { return Operators.OfType().FirstOrDefault(); }
}
private PMOBestSolutionHistoryAnalyzer PMOBestSolutionHistoryAnalyzer {
get { return Operators.OfType().FirstOrDefault(); }
}
#endregion
public MetaOptimizationProblem()
: base() {
Parameters.Add(new ValueParameter>(AlgorithmTypeParameterName, "The algorithm which's parameters should be optimized.", new ConstrainedTypeValue(typeof(GeneticAlgorithm))));
Parameters.Add(new ValueParameter>(ProblemTypeParameterName, "The problem type.", new ConstrainedTypeValue()));
Parameters.Add(new ValueParameter>(ProblemsParameterName, "The problems that should be evaluated.", new ConstrainedItemList()));
Parameters.Add(new OptionalValueParameter(ParameterConfigurationTreeParameterName, "Tree of algorithm parameters that should be optimized.")); // needs to be optional, when last problem is removed from list, it must be set null
Parameters.Add(new ValueParameter(RepetitionsParameterName, "The number of evaluations for each problem.", new IntValue(3)));
Parameters.Add(new ValueParameter(QualityMeasureNameName, "The name of the quality result of the base-level algorithm. Subresults can be accessed by dot separator.", new StringValue("BestQuality")));
var validIntManipulators = new ItemSet(ApplicationManager.Manager.GetInstances());
var validDoubleManipulators = new ItemSet(ApplicationManager.Manager.GetInstances());
var validIntCrossovers = new ItemSet(ApplicationManager.Manager.GetInstances());
var validDoubleCrossovers = new ItemSet(ApplicationManager.Manager.GetInstances());
Parameters.Add(new ConstrainedValueParameter(IntValueManipulatorParameterName, validIntManipulators, validIntManipulators.Where(x => x.GetType() == typeof(NormalIntValueManipulator)).SingleOrDefault()));
Parameters.Add(new ConstrainedValueParameter(DoubleValueManipulatorParameterName, validDoubleManipulators, validDoubleManipulators.Where(x => x.GetType() == typeof(NormalDoubleValueManipulator)).SingleOrDefault()));
Parameters.Add(new ConstrainedValueParameter(IntValueCrossoverParameterName, validIntCrossovers, validIntCrossovers.Where(x => x.GetType() == typeof(NormalIntValueCrossover)).SingleOrDefault()));
Parameters.Add(new ConstrainedValueParameter(DoubleValueCrossoverParameterName, validDoubleCrossovers, validDoubleCrossovers.Where(x => x.GetType() == typeof(NormalDoubleValueCrossover)).SingleOrDefault()));
Parameters.Add(new ValueParameter(QualityWeightParameterName, new DoubleValue(1)));
Parameters.Add(new ValueParameter(StandardDeviationWeightParameterName, new DoubleValue(0.01)));
Parameters.Add(new ValueParameter(EvaluatedSolutionsWeightParameterName, new DoubleValue(0.0005)));
Maximization = new BoolValue(false);
SolutionCreator = new RandomParameterConfigurationCreator();
Evaluator = new PMOEvaluator();
InitializeOperators();
RegisterParameterEvents();
ParameterizeAnalyzer();
ParameterizeSolutionCreator();
ParameterizeEvaluator();
ParameterizeOperators();
AlgorithmTypeParameter_ValueChanged(this, EventArgs.Empty);
}
[StorableConstructor]
private MetaOptimizationProblem(bool deserializing) : base(deserializing) { }
private MetaOptimizationProblem(MetaOptimizationProblem original, Cloner cloner)
: base(original, cloner) {
this.RegisterParameterEvents();
}
public override IDeepCloneable Clone(Cloner cloner) {
return new MetaOptimizationProblem(this, cloner);
}
#region Helpers
[StorableHook(HookType.AfterDeserialization)]
private void AfterDeserializationHook() {
if (!Parameters.ContainsKey(QualityMeasureNameName)) Parameters.Add(new ValueParameter(QualityMeasureNameName, "The name of the quality result of the base-level algorithm. Subresults can be accessed by dot separator.", new StringValue("BestQuality"))); // backwards compatibility
RegisterParameterEvents();
}
private void RegisterParameterEvents() {
SolutionCreatorParameter.ValueChanged += new EventHandler(SolutionCreatorParameter_ValueChanged);
EvaluatorParameter.ValueChanged += new EventHandler(EvaluatorParameter_ValueChanged);
Evaluator.QualityParameter.ActualNameChanged += new EventHandler(Evaluator_QualityParameter_ActualNameChanged);
AlgorithmTypeParameter.ValueChanged += new EventHandler(AlgorithmTypeParameter_ValueChanged);
AlgorithmType.ValueChanged += new EventHandler(AlgorithmType_ValueChanged);
ProblemTypeParameter.ValueChanged += new EventHandler(ProblemTypeParameter_ValueChanged);
ProblemType.ValueChanged += new EventHandler(ProblemType_ValueChanged);
Problems.ItemsAdded += new Collections.CollectionItemsChangedEventHandler>(Problems_ItemsAdded);
Problems.ItemsRemoved += new Collections.CollectionItemsChangedEventHandler>(Problems_ItemsRemoved);
}
private void InitializeOperators() {
Operators.AddRange(ApplicationManager.Manager.GetInstances().Cast());
Operators.Add(new ReferenceQualityAnalyzer());
Operators.Add(new BestParameterConfigurationAnalyzer());
Operators.Add(new SolutionCacheAnalyzer());
Operators.Add(new PMOPopulationDiversityAnalyzer());
Operators.Add(new PMOProblemQualitiesAnalyzer());
Operators.Add(new PMOBestSolutionHistoryAnalyzer());
}
private void ParameterizeSolutionCreator() {
}
private void ParameterizeEvaluator() {
((PMOEvaluator)Evaluator).ParameterConfigurationParameter.ActualName = ((RandomParameterConfigurationCreator)SolutionCreator).ParameterConfigurationParameter.ActualName;
}
private void ParameterizeAnalyzer() {
if (BestParameterConfigurationAnalyzer != null) {
BestParameterConfigurationAnalyzer.ParameterConfigurationParameter.ActualName = ((RandomParameterConfigurationCreator)SolutionCreator).ParameterConfigurationParameter.ActualName;
}
if (ReferenceQualityAnalyzer != null) {
ReferenceQualityAnalyzer.ParameterConfigurationParameter.ActualName = ((RandomParameterConfigurationCreator)SolutionCreator).ParameterConfigurationParameter.ActualName;
}
if (RunsAnalyzer != null) {
RunsAnalyzer.ParameterConfigurationParameter.ActualName = ((RandomParameterConfigurationCreator)SolutionCreator).ParameterConfigurationParameter.ActualName;
}
if (PMOPopulationDiversityAnalyzer != null) {
PMOPopulationDiversityAnalyzer.SolutionParameter.ActualName = ((RandomParameterConfigurationCreator)SolutionCreator).ParameterConfigurationParameter.ActualName;
PMOPopulationDiversityAnalyzer.StoreHistoryParameter.Value.Value = true;
}
if (PMOProblemQualitiesAnalyzer != null) {
PMOProblemQualitiesAnalyzer.ParameterConfigurationParameter.ActualName = ((RandomParameterConfigurationCreator)SolutionCreator).ParameterConfigurationParameter.ActualName;
}
if (PMOBestSolutionHistoryAnalyzer != null) {
PMOBestSolutionHistoryAnalyzer.ParameterConfigurationParameter.ActualName = ((RandomParameterConfigurationCreator)SolutionCreator).ParameterConfigurationParameter.ActualName;
}
}
private void ParameterizeOperators() {
foreach (IParameterConfigurationCrossover op in Operators.OfType()) {
op.ParentsParameter.ActualName = ((RandomParameterConfigurationCreator)SolutionCreator).ParameterConfigurationParameter.ActualName;
op.ChildParameter.ActualName = ((RandomParameterConfigurationCreator)SolutionCreator).ParameterConfigurationParameter.ActualName;
}
foreach (IParameterConfigurationManipulator op in Operators.OfType()) {
op.ParameterConfigurationTreeParameter.ActualName = ((RandomParameterConfigurationCreator)SolutionCreator).ParameterConfigurationParameter.ActualName;
}
}
#endregion
#region Events
private void SolutionCreatorParameter_ValueChanged(object sender, EventArgs e) {
ParameterizeSolutionCreator();
ParameterizeEvaluator();
ParameterizeAnalyzer();
ParameterizeOperators();
OnSolutionCreatorChanged();
}
private void EvaluatorParameter_ValueChanged(object sender, EventArgs e) {
Evaluator.QualityParameter.ActualNameChanged += new EventHandler(Evaluator_QualityParameter_ActualNameChanged);
ParameterizeEvaluator();
ParameterizeAnalyzer();
OnEvaluatorChanged();
}
private void Evaluator_QualityParameter_ActualNameChanged(object sender, EventArgs e) {
ParameterizeAnalyzer();
}
private void AlgorithmTypeParameter_ValueChanged(object sender, EventArgs e) {
AlgorithmType_ValueChanged(sender, e);
}
private void AlgorithmType_ValueChanged(object sender, EventArgs e) {
IAlgorithm instance = (IAlgorithm)Activator.CreateInstance(AlgorithmType.Value);
this.ProblemType.ValidTypes = ApplicationManager.Manager.GetTypes(instance.ProblemType, true).ToList();
var newProblemType = this.ProblemType.ValidTypes.SingleOrDefault(t => t == typeof(SingleObjectiveTestFunctionProblem)) ?? this.ProblemType.ValidTypes.Where(t => t != typeof(MetaOptimizationProblem)).FirstOrDefault();
if (this.ProblemType.Value != newProblemType)
this.ProblemType.Value = newProblemType; // ProblemType_ValueChanged will add one problem and create ParameterConfigurationTree
else
ProblemType_ValueChanged(this, EventArgs.Empty); // call explicitly
}
private void ProblemTypeParameter_ValueChanged(object sender, EventArgs e) {
ProblemType_ValueChanged(sender, e);
}
private void ProblemType_ValueChanged(object sender, EventArgs e) {
Problems.Clear();
Problems.Type = ProblemType.Value;
Problems.Add((IProblem)Activator.CreateInstance(this.ProblemType.Value));
}
private void Problems_ItemsAdded(object sender, CollectionItemsChangedEventArgs> e) {
// the first problem in the list is always the instance for the algorithm - this way some basic wiring between problem and algorithm can be sustained
if (e.Items.Single().Index == 0) {
ParameterConfigurationTreeParameter.ActualValue = new ParameterConfigurationTree(CreateAlgorithm(AlgorithmType.Value, e.Items.Single().Value), e.Items.Single().Value);
// special for DataAnalysisProblem: Because of wiring between algorithm and problem, ParameterConfigurationTree needs to be recreated on Reset event
var dap = e.Items.Single().Value as IDataAnalysisProblem;
if (dap != null) {
dap.Reset += new EventHandler(DataAnalysisProblem_Reset);
}
}
}
private void Problems_ItemsRemoved(object sender, CollectionItemsChangedEventArgs> e) {
if (e.Items.Single().Index == 0) {
ParameterConfigurationTreeParameter.ActualValue = null;
var dap = e.Items.Single().Value as IDataAnalysisProblem;
if (dap != null) {
dap.Reset -= new EventHandler(DataAnalysisProblem_Reset);
}
}
}
private void DataAnalysisProblem_Reset(object sender, EventArgs e) {
ParameterConfigurationTreeParameter.ActualValue = new ParameterConfigurationTree(CreateAlgorithm(AlgorithmType.Value, Problems.First()), Problems.First());
}
#endregion
private IAlgorithm CreateAlgorithm(Type algorithmType, IProblem problem) {
IAlgorithm algorithm = (IAlgorithm)Activator.CreateInstance(algorithmType);
algorithm.Problem = problem;
return algorithm;
}
public void ImportAlgorithm(IAlgorithm algorithm) {
AlgorithmType.Value = algorithm.GetType();
if (algorithm.Problem != null) ProblemType.Value = algorithm.Problem.GetType();
if (algorithm.Problem != null) {
Problems.Clear();
Problems.Add((IProblem)algorithm.Problem);
}
ParameterConfigurationTreeParameter.ActualValue = new ParameterConfigurationTree(algorithm, Problems.First());
}
}
}