using System.Linq; using HeuristicLab.Common; using HeuristicLab.Core; using HeuristicLab.Data; using HeuristicLab.Operators; using HeuristicLab.Optimization; using HeuristicLab.Parameters; using HeuristicLab.Persistence.Default.CompositeSerializers.Storable; namespace HeuristicLab.EvolutionTracking { [StorableClass] [Item("GenealogyAnalyzer", "An analyzer which performs the necessary instrumentation to record the evolution of a genetic algorithm.")] public class GenealogyAnalyzer : SingleSuccessorOperator, IAnalyzer where T : class,IItem { private const string GenerationsParameterName = "Generations"; private const string ResultsParameterName = "Results"; private const string PopulationGraphParameterName = "PopulationGraph"; public const string QualityParameterName = "Quality"; public const string PopulationParameterName = "SymbolicExpressionTree"; private const string CrossoverParameterName = "Crossover"; private const string ManipulatorParameterName = "Mutator"; private const string SolutionCreatorParameterName = "SolutionCreator"; private const string BeforeCrossoverOperatorParameterName = "BeforeCrossoverOperator"; private const string AfterCrossoverOperatorParameterName = "AfterCrossoverOperator"; private const string BeforeManipulatorOperatorParameterName = "BeforeManipulatorOperator"; private const string AfterManipulatorOperatorParameterName = "AfterManipulatorOperator"; private const string EnableCrossoverTrackingParameterName = "EnableCrossoverTracking"; private const string EnableManipulatorTrackingParameterName = "EnableManipulatorTracking"; private const string EnableSolutionCreatorTrackingParameterName = "EnableSolutionCreatorTracking"; // should always be enabled. maybe superfluous public string CrossoverParentsParameterName { get; set; } public string CrossoverChildParameterName { get; set; } public string ManipulatorChildParameterName { get; set; } public IScopeTreeLookupParameter QualityParameter { get { return (IScopeTreeLookupParameter)Parameters[QualityParameterName]; } } public IScopeTreeLookupParameter PopulationParameter { get { return (IScopeTreeLookupParameter)Parameters[PopulationParameterName]; } } public IValueParameter> BeforeCrossoverOperatorParameter { get { return (IValueParameter>)Parameters[BeforeCrossoverOperatorParameterName]; } } public IValueParameter> AfterCrossoverOperatorParameter { get { return (IValueParameter>)Parameters[AfterCrossoverOperatorParameterName]; } } public IValueParameter> BeforeManipulatorOperatorParameter { get { return (IValueParameter>)Parameters[BeforeManipulatorOperatorParameterName]; } } public IValueParameter> AfterManipulatorOperatorParameter { get { return (IValueParameter>)Parameters[AfterManipulatorOperatorParameterName]; } } #region parameter properties public ILookupParameter ResultsParameter { get { return (ILookupParameter)Parameters[ResultsParameterName]; } } public ILookupParameter GenerationsParameter { get { return (ILookupParameter)Parameters[GenerationsParameterName]; } } public IValueParameter EnableCrossoverTrackingParameter { get { return (IValueParameter)Parameters[EnableCrossoverTrackingParameterName]; } } public IValueParameter EnableManipulatorTrackingParameter { get { return (IValueParameter)Parameters[EnableManipulatorTrackingParameterName]; } } public IValueParameter EnableSolutionCreatorTrackingParameter { get { return (IValueParameter)Parameters[EnableSolutionCreatorTrackingParameterName]; } } public ILookupParameter CrossoverParameter { get { return (ILookupParameter)Parameters[CrossoverParameterName]; } } public ILookupParameter ManipulatorParameter { get { return (ILookupParameter)Parameters[ManipulatorParameterName]; } } public ILookupParameter SolutionCreatorParameter { get { return (ILookupParameter)Parameters[SolutionCreatorParameterName]; } } #endregion #region properties public ICrossover Crossover { get { return CrossoverParameter.ActualValue; } } public IManipulator Manipulator { get { return ManipulatorParameter.ActualValue; } } public ICrossoverOperator BeforeCrossoverOperator { get { return BeforeCrossoverOperatorParameter.Value; } } public ICrossoverOperator AfterCrossoverOperator { get { return AfterCrossoverOperatorParameter.Value; } } public IManipulatorOperator BeforeManipulatorOperator { get { return BeforeManipulatorOperatorParameter.Value; } } public IManipulatorOperator AfterManipulatorOperator { get { return AfterManipulatorOperatorParameter.Value; } } public ISolutionCreator SolutionCreator { get { return SolutionCreatorParameter.ActualValue; } } public BoolValue EnableCrossoverTracking { get { return EnableCrossoverTrackingParameter.Value; } } public BoolValue EnableManipulatorTracking { get { return EnableManipulatorTrackingParameter.Value; } } public BoolValue EnableSolutionCreatorTracking { get { return EnableSolutionCreatorTrackingParameter.Value; } } public ResultCollection Results { get { return ResultsParameter.ActualValue; } } public IntValue Generations { get { return GenerationsParameter.ActualValue; } } public IGenealogyGraph GenealogyGraph { get { IResult result; if (!Results.ContainsKey(PopulationGraphParameterName)) { result = new Result(PopulationGraphParameterName, new GenealogyGraph()); Results.Add(result); } else { result = Results[PopulationGraphParameterName]; } var graph = (IGenealogyGraph)result.Value; return graph; } } #endregion public GenealogyAnalyzer() { // the instrumented operators Parameters.Add(new LookupParameter(CrossoverParameterName, "The crossover operator.")); Parameters.Add(new LookupParameter(ManipulatorParameterName, "The manipulator operator.")); Parameters.Add(new LookupParameter(SolutionCreatorParameterName, "The solution creator operator.")); // the analyzer parameters Parameters.Add(new ValueParameter(EnableCrossoverTrackingParameterName, new BoolValue(true))); Parameters.Add(new ValueParameter(EnableManipulatorTrackingParameterName, new BoolValue(true))); Parameters.Add(new ValueParameter(EnableSolutionCreatorTrackingParameterName, new BoolValue(true))); // parameters required by the analyzer to do its work Parameters.Add(new LookupParameter(GenerationsParameterName, "The number of generations so far.")); Parameters.Add(new LookupParameter(ResultsParameterName)); Parameters.Add(new ScopeTreeLookupParameter(PopulationParameterName, "The population of individuals.")); Parameters.Add(new ScopeTreeLookupParameter(QualityParameterName, "The individual qualities.")); Parameters.Add(new ValueParameter>(BeforeCrossoverOperatorParameterName)); Parameters.Add(new ValueParameter>(AfterCrossoverOperatorParameterName)); Parameters.Add(new ValueParameter>(BeforeManipulatorOperatorParameterName)); Parameters.Add(new ValueParameter>(AfterManipulatorOperatorParameterName)); } public override IDeepCloneable Clone(Cloner cloner) { return new GenealogyAnalyzer(this, cloner); } protected GenealogyAnalyzer(GenealogyAnalyzer original, Cloner cloner) : base(original, cloner) { } [StorableHook(HookType.AfterDeserialization)] private void AfterDeserialization() { // the instrumented operators if (!Parameters.ContainsKey(CrossoverParameterName)) Parameters.Add(new LookupParameter(CrossoverParameterName, "The crossover operator.")); if (!Parameters.ContainsKey(ManipulatorParameterName)) Parameters.Add(new LookupParameter(ManipulatorParameterName, "The manipulator operator.")); if (!Parameters.ContainsKey(SolutionCreatorParameterName)) Parameters.Add(new LookupParameter(SolutionCreatorParameterName, "The solution creator operator.")); // the analyzer parameters if (!Parameters.ContainsKey(EnableCrossoverTrackingParameterName)) Parameters.Add(new ValueParameter(EnableCrossoverTrackingParameterName, new BoolValue(true))); if (!Parameters.ContainsKey(EnableManipulatorTrackingParameterName)) Parameters.Add(new ValueParameter(EnableManipulatorTrackingParameterName, new BoolValue(true))); if (!Parameters.ContainsKey(EnableSolutionCreatorTrackingParameterName)) Parameters.Add(new ValueParameter(EnableSolutionCreatorTrackingParameterName, new BoolValue(true))); // parameters required by the analyzer to do its work if (!Parameters.ContainsKey(GenerationsParameterName)) Parameters.Add(new LookupParameter(GenerationsParameterName, "The number of generations so far.")); if (!Parameters.ContainsKey(ResultsParameterName)) Parameters.Add(new LookupParameter(ResultsParameterName)); if (!Parameters.ContainsKey(PopulationParameterName)) { Parameters.Add(new ScopeTreeLookupParameter(PopulationParameterName, "The population of individuals.")); } if (!Parameters.ContainsKey(QualityParameterName)) { Parameters.Add(new ScopeTreeLookupParameter(QualityParameterName, "The individual qualities.")); } } public bool EnabledByDefault { get { return false; } } private void ConfigureTrackingOperators() { // at the beginning we add the before/after operators to the instrumented operators if (Crossover != null) { var instrumentedCrossover = (InstrumentedOperator)Crossover; instrumentedCrossover.AfterExecutionOperators.Clear(); instrumentedCrossover.BeforeExecutionOperators.Clear(); if (EnableCrossoverTracking.Value) { if (BeforeCrossoverOperator != null) { instrumentedCrossover.BeforeExecutionOperators.Add(BeforeCrossoverOperator); } if (AfterCrossoverOperator != null) { instrumentedCrossover.AfterExecutionOperators.Add(AfterCrossoverOperator); } } } if (Manipulator != null) { var instrumentedManipulator = (InstrumentedOperator)Manipulator; instrumentedManipulator.AfterExecutionOperators.Clear(); instrumentedManipulator.BeforeExecutionOperators.Clear(); if (EnableManipulatorTracking.Value) { if (BeforeManipulatorOperator != null) { instrumentedManipulator.BeforeExecutionOperators.Add(BeforeManipulatorOperator); } if (AfterManipulatorOperator != null) { instrumentedManipulator.AfterExecutionOperators.Add(AfterManipulatorOperator); } } } } public override IOperation Apply() { var population = PopulationParameter.ActualValue.ToList(); var qualities = QualityParameter.ActualValue.ToList(); if (Generations.Value == 0) { ConfigureTrackingOperators(); for (int i = 0; i < population.Count; ++i) { var individual = population[i]; var vertex = new GenealogyGraphNode { Content = individual, Rank = Generations.Value, }; GenealogyGraph.AddVertex(vertex); // save the vertex id in the individual scope (so that we can identify graph indices) ExecutionContext.Scope.SubScopes[i].Variables.Add(new Variable("Id", new StringValue(vertex.Id))); } } else { var elite = population.FirstOrDefault(x => GenealogyGraph.Contains(x)); if (elite != null) { var vertex = new GenealogyGraphNode { Content = (T)elite.Clone(), Rank = Generations.Value, IsElite = true }; // add new vertex to the graph GenealogyGraph.AddVertex(vertex); // swap content between current vertex and previous vertex var previousVertex = (IGenealogyGraphNode)GenealogyGraph[elite]; var tmp = vertex.Content; vertex.Content = previousVertex.Content; previousVertex.Content = tmp; // adjust graph content map GenealogyGraph[vertex.Content] = vertex; GenealogyGraph[previousVertex.Content] = previousVertex; // connect current elite with previous elite previousVertex.AddForwardArc(vertex); vertex.AddReverseArc(previousVertex); vertex.Id = previousVertex.Id; // another way would be to introduce the vertex.Id into the scope of the elite vertex.Quality = previousVertex.Quality; if (previousVertex.InArcs.Any()) { vertex.InArcs.Last().Data = previousVertex.InArcs.Last().Data; // save the fragment in case there is one } } } // update qualities for (int i = 0; i < population.Count; ++i) { var vertex = (IGenealogyGraphNode)GenealogyGraph[population[i]]; vertex.Quality = qualities[i].Value; } return base.Apply(); } } }