Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.EvolutionTracking/HeuristicLab.EvolutionTracking/3.4/Analyzers/GenealogyAnalyzer.cs @ 11769

Last change on this file since 11769 was 11769, checked in by bburlacu, 10 years ago

#1772: Changed the GenealogyAnalyzer according to the changes in IGenealogyGraph.

File size: 20.0 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2014 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System.Collections.Generic;
23using System.Linq;
24using HeuristicLab.Analysis;
25using HeuristicLab.Common;
26using HeuristicLab.Core;
27using HeuristicLab.Data;
28using HeuristicLab.Operators;
29using HeuristicLab.Optimization;
30using HeuristicLab.Parameters;
31using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
32
33namespace HeuristicLab.EvolutionTracking {
34  [StorableClass]
35  [Item("GenealogyAnalyzer", "An analyzer which performs the necessary instrumentation to record the evolution of a genetic algorithm.")]
36  public class GenealogyAnalyzer<T> : SingleSuccessorOperator, IAnalyzer
37  where T : class,IItem {
38    #region parameter names
39    private const string GenerationsParameterName = "Generations";
40    private const string ResultsParameterName = "Results";
41    private const string PopulationGraphParameterName = "PopulationGraph";
42    public const string QualityParameterName = "Quality";
43    public const string PopulationParameterName = "SymbolicExpressionTree";
44
45    private const string CrossoverParameterName = "Crossover";
46    private const string ManipulatorParameterName = "Mutator";
47    private const string SolutionCreatorParameterName = "SolutionCreator";
48
49    private const string BeforeCrossoverOperatorParameterName = "BeforeCrossoverOperator";
50    private const string AfterCrossoverOperatorParameterName = "AfterCrossoverOperator";
51
52    private const string BeforeManipulatorOperatorParameterName = "BeforeManipulatorOperator";
53    private const string AfterManipulatorOperatorParameterName = "AfterManipulatorOperator";
54
55    private const string EnableCrossoverTrackingParameterName = "EnableCrossoverTracking";
56    private const string EnableManipulatorTrackingParameterName = "EnableManipulatorTracking";
57    private const string EnableSolutionCreatorTrackingParameterName = "EnableSolutionCreatorTracking"; // should always be enabled. maybe superfluous
58    #endregion
59
60    #region parameter properties
61    public IScopeTreeLookupParameter<DoubleValue> QualityParameter {
62      get { return (IScopeTreeLookupParameter<DoubleValue>)Parameters[QualityParameterName]; }
63    }
64    public IScopeTreeLookupParameter<T> PopulationParameter {
65      get { return (IScopeTreeLookupParameter<T>)Parameters[PopulationParameterName]; }
66    }
67    public IValueParameter<ICrossoverOperator<T>> BeforeCrossoverOperatorParameter {
68      get { return (IValueParameter<ICrossoverOperator<T>>)Parameters[BeforeCrossoverOperatorParameterName]; }
69    }
70    public IValueParameter<ICrossoverOperator<T>> AfterCrossoverOperatorParameter {
71      get { return (IValueParameter<ICrossoverOperator<T>>)Parameters[AfterCrossoverOperatorParameterName]; }
72    }
73    public IValueParameter<IManipulatorOperator<T>> BeforeManipulatorOperatorParameter {
74      get { return (IValueParameter<IManipulatorOperator<T>>)Parameters[BeforeManipulatorOperatorParameterName]; }
75    }
76    public IValueParameter<IManipulatorOperator<T>> AfterManipulatorOperatorParameter {
77      get { return (IValueParameter<IManipulatorOperator<T>>)Parameters[AfterManipulatorOperatorParameterName]; }
78    }
79    public ILookupParameter<ResultCollection> ResultsParameter {
80      get { return (ILookupParameter<ResultCollection>)Parameters[ResultsParameterName]; }
81    }
82    public ILookupParameter<IntValue> GenerationsParameter {
83      get { return (ILookupParameter<IntValue>)Parameters[GenerationsParameterName]; }
84    }
85    public IValueParameter<BoolValue> EnableCrossoverTrackingParameter {
86      get { return (IValueParameter<BoolValue>)Parameters[EnableCrossoverTrackingParameterName]; }
87    }
88    public IValueParameter<BoolValue> EnableManipulatorTrackingParameter {
89      get { return (IValueParameter<BoolValue>)Parameters[EnableManipulatorTrackingParameterName]; }
90    }
91    public IValueParameter<BoolValue> EnableSolutionCreatorTrackingParameter {
92      get { return (IValueParameter<BoolValue>)Parameters[EnableSolutionCreatorTrackingParameterName]; }
93    }
94    public ILookupParameter<ICrossover> CrossoverParameter {
95      get { return (ILookupParameter<ICrossover>)Parameters[CrossoverParameterName]; }
96    }
97    public ILookupParameter<IManipulator> ManipulatorParameter {
98      get { return (ILookupParameter<IManipulator>)Parameters[ManipulatorParameterName]; }
99    }
100
101    public ILookupParameter<ISolutionCreator> SolutionCreatorParameter {
102      get { return (ILookupParameter<ISolutionCreator>)Parameters[SolutionCreatorParameterName]; }
103    }
104    #endregion
105
106    #region properties
107    public ICrossoverOperator<T> BeforeCrossoverOperator {
108      get { return BeforeCrossoverOperatorParameter.Value; }
109    }
110    public ICrossoverOperator<T> AfterCrossoverOperator {
111      get { return AfterCrossoverOperatorParameter.Value; }
112    }
113    public IManipulatorOperator<T> BeforeManipulatorOperator {
114      get { return BeforeManipulatorOperatorParameter.Value; }
115    }
116    public IManipulatorOperator<T> AfterManipulatorOperator {
117      get { return AfterManipulatorOperatorParameter.Value; }
118    }
119    public BoolValue EnableCrossoverTracking {
120      get { return EnableCrossoverTrackingParameter.Value; }
121    }
122    public BoolValue EnableManipulatorTracking {
123      get { return EnableManipulatorTrackingParameter.Value; }
124    }
125    public BoolValue EnableSolutionCreatorTracking {
126      get { return EnableSolutionCreatorTrackingParameter.Value; }
127    }
128    #endregion
129
130    public GenealogyAnalyzer() {
131      #region add parameters
132      // the instrumented operators
133      Parameters.Add(new LookupParameter<ICrossover>(CrossoverParameterName, "The crossover operator."));
134      Parameters.Add(new LookupParameter<IManipulator>(ManipulatorParameterName, "The manipulator operator."));
135      Parameters.Add(new LookupParameter<ISolutionCreator>(SolutionCreatorParameterName, "The solution creator operator."));
136      // the analyzer parameters
137      Parameters.Add(new ValueParameter<BoolValue>(EnableCrossoverTrackingParameterName, new BoolValue(true)));
138      Parameters.Add(new ValueParameter<BoolValue>(EnableManipulatorTrackingParameterName, new BoolValue(true)));
139      Parameters.Add(new ValueParameter<BoolValue>(EnableSolutionCreatorTrackingParameterName, new BoolValue(true)));
140      // parameters required by the analyzer to do its work
141      Parameters.Add(new LookupParameter<IntValue>(GenerationsParameterName, "The number of generations so far."));
142      Parameters.Add(new LookupParameter<ResultCollection>(ResultsParameterName));
143      Parameters.Add(new ScopeTreeLookupParameter<T>(PopulationParameterName, "The population of individuals."));
144      Parameters.Add(new ScopeTreeLookupParameter<DoubleValue>(QualityParameterName, "The individual qualities."));
145      Parameters.Add(new ValueParameter<ICrossoverOperator<T>>(BeforeCrossoverOperatorParameterName));
146      Parameters.Add(new ValueParameter<ICrossoverOperator<T>>(AfterCrossoverOperatorParameterName));
147      Parameters.Add(new ValueParameter<IManipulatorOperator<T>>(BeforeManipulatorOperatorParameterName));
148      Parameters.Add(new ValueParameter<IManipulatorOperator<T>>(AfterManipulatorOperatorParameterName));
149      #endregion
150    }
151    public override IDeepCloneable Clone(Cloner cloner) {
152      return new GenealogyAnalyzer<T>(this, cloner);
153    }
154    protected GenealogyAnalyzer(GenealogyAnalyzer<T> original, Cloner cloner)
155      : base(original, cloner) {
156    }
157
158    [StorableConstructor]
159    protected GenealogyAnalyzer(bool deserializing) : base(deserializing) { }
160
161    [StorableHook(HookType.AfterDeserialization)]
162    private void AfterDeserialization() {
163      // the instrumented operators
164      if (!Parameters.ContainsKey(CrossoverParameterName))
165        Parameters.Add(new LookupParameter<ICrossover>(CrossoverParameterName, "The crossover operator."));
166      if (!Parameters.ContainsKey(ManipulatorParameterName))
167        Parameters.Add(new LookupParameter<IManipulator>(ManipulatorParameterName, "The manipulator operator."));
168      if (!Parameters.ContainsKey(SolutionCreatorParameterName))
169        Parameters.Add(new LookupParameter<ISolutionCreator>(SolutionCreatorParameterName, "The solution creator operator."));
170      // the analyzer parameters
171      if (!Parameters.ContainsKey(EnableCrossoverTrackingParameterName))
172        Parameters.Add(new ValueParameter<BoolValue>(EnableCrossoverTrackingParameterName, new BoolValue(true)));
173      if (!Parameters.ContainsKey(EnableManipulatorTrackingParameterName))
174        Parameters.Add(new ValueParameter<BoolValue>(EnableManipulatorTrackingParameterName, new BoolValue(true)));
175      if (!Parameters.ContainsKey(EnableSolutionCreatorTrackingParameterName))
176        Parameters.Add(new ValueParameter<BoolValue>(EnableSolutionCreatorTrackingParameterName, new BoolValue(true)));
177      // parameters required by the analyzer to do its work
178      if (!Parameters.ContainsKey(GenerationsParameterName))
179        Parameters.Add(new LookupParameter<IntValue>(GenerationsParameterName, "The number of generations so far."));
180      if (!Parameters.ContainsKey(ResultsParameterName))
181        Parameters.Add(new LookupParameter<ResultCollection>(ResultsParameterName));
182      if (!Parameters.ContainsKey(PopulationParameterName)) {
183        Parameters.Add(new ScopeTreeLookupParameter<T>(PopulationParameterName, "The population of individuals."));
184      }
185      if (!Parameters.ContainsKey(QualityParameterName)) {
186        Parameters.Add(new ScopeTreeLookupParameter<DoubleValue>(QualityParameterName, "The individual qualities."));
187      }
188    }
189
190    public bool EnabledByDefault {
191      get { return false; }
192    }
193
194    private void ConfigureTrackingOperators() {
195      // at the beginning we add the before/after operators to the instrumented operators
196      var crossover = CrossoverParameter.ActualValue;
197      if (crossover != null) {
198        var instrumentedCrossover = (InstrumentedOperator)crossover;
199        instrumentedCrossover.AfterExecutionOperators.Clear();
200        instrumentedCrossover.BeforeExecutionOperators.Clear();
201
202        if (EnableCrossoverTracking.Value) {
203          if (BeforeCrossoverOperator != null) {
204            instrumentedCrossover.BeforeExecutionOperators.Add(BeforeCrossoverOperator);
205          }
206          if (AfterCrossoverOperator != null) {
207            instrumentedCrossover.AfterExecutionOperators.Add(AfterCrossoverOperator);
208          }
209        }
210      }
211      var manipulator = ManipulatorParameter.ActualValue;
212      if (manipulator != null) {
213        var instrumentedManipulator = (InstrumentedOperator)manipulator;
214        instrumentedManipulator.AfterExecutionOperators.Clear();
215        instrumentedManipulator.BeforeExecutionOperators.Clear();
216
217        if (EnableManipulatorTracking.Value) {
218          if (BeforeManipulatorOperator != null) {
219            instrumentedManipulator.BeforeExecutionOperators.Add(BeforeManipulatorOperator);
220          }
221          if (AfterManipulatorOperator != null) {
222            instrumentedManipulator.AfterExecutionOperators.Add(AfterManipulatorOperator);
223          }
224        }
225      }
226    }
227
228    public override IOperation Apply() {
229      IGenealogyGraph<T> genealogyGraph;
230      var results = ResultsParameter.ActualValue;
231      if (!results.ContainsKey(PopulationGraphParameterName)) {
232        genealogyGraph = new GenealogyGraph<T>();
233        results.Add(new Result(PopulationGraphParameterName, genealogyGraph));
234      } else {
235        genealogyGraph = (IGenealogyGraph<T>)results[PopulationGraphParameterName].Value;
236      }
237
238      var population = PopulationParameter.ActualValue;
239      var qualities = QualityParameter.ActualValue.ToList();
240
241      int generation = GenerationsParameter.ActualValue.Value;
242      if (generation == 0) {
243        ConfigureTrackingOperators();
244
245        for (int i = 0; i < population.Length; ++i) {
246          var individual = population[i];
247          var vertex = new GenealogyGraphNode<T>(individual) { Rank = generation };
248          genealogyGraph.AddVertex(vertex);
249          // save the vertex id in the individual scope (so that we can identify graph indices)
250          ExecutionContext.Scope.SubScopes[i].Variables.Add(new Variable("Id", new StringValue(vertex.Id)));
251        }
252      } else {
253        int index = 0;
254        T elite = null;
255
256        for (int i = 0; i < population.Length; ++i) {
257          if (genealogyGraph.GetByContent(population[i]).Rank.Equals(generation - 1)) {
258            elite = population[i];
259            index = i;
260            break;
261          }
262        }
263
264        #region add elite in the graph and connect it with the previous elite
265        if (elite != null) {
266          var prevVertex = genealogyGraph.GetByContent(elite);
267          prevVertex.IsElite = true; // mark elites in the graph retroactively
268          var v = (IGenealogyGraphNode<T>)prevVertex.Clone();
269          v.Rank = generation;
270          v.IsElite = false;
271          population[index] = v.Data;
272          genealogyGraph.AddVertex(v);
273          genealogyGraph.AddArc(prevVertex, v);
274          // inject the graph node unique id to the scope
275          ExecutionContext.Scope.SubScopes[index].Variables[BeforeManipulatorOperator.ChildParameter.ActualName].Value = v.Data;
276          ExecutionContext.Scope.SubScopes[index].Variables["Id"].Value = new StringValue(v.Id);
277        }
278        #endregion
279
280        //        ComputeSuccessRatios(genealogyGraph);
281      }
282      // update qualities
283      for (int i = 0; i < population.Length; ++i) {
284        var vertex = genealogyGraph.GetByContent(population[i]);
285        vertex.Quality = qualities[i].Value;
286      }
287
288      // remove extra graph nodes (added by the instrumented operators in the case of offspring selection)
289      var pop = new HashSet<T>(population);
290      var discarded = genealogyGraph.GetByRank(generation).Where(x => !pop.Contains(x.Data)).ToList();
291      for (int i = 0; i < discarded.Count; ++i) {
292        var v = discarded[i];
293        if (v.InDegree == 1) {
294          var p = v.Parents.First();
295          if (p.Rank.Equals(generation - 0.5))
296            // the individual was a product of mutation. since it wasn't selected, we must remove both it and its parent
297            // ("intermediate" vertex result of crossover)
298            discarded.Add(v.Parents.First());
299        }
300      }
301      genealogyGraph.RemoveVertices(discarded);
302
303      return base.Apply();
304    }
305
306    // for accurate statistics this method should be called before the discarded offspring are removed from the genealogy graph
307    private void ComputeSuccessRatios(IGenealogyGraph<T> genealogyGraph) {
308      const string successfulOffspringRatioTableHistoryName = "Successful offspring ratios history";
309      const string successfulOffspringAbsoluteValuesTableHistoryName = "Successful offspring values history";
310      const string successfulOffspringRatioHeatMapName = "Successful offspring ratios heatmap";
311      const string successfulOffspringValuesHeatMapName = "Successful offspring values heatmap";
312
313      var population = PopulationParameter.ActualValue;
314      var generation = GenerationsParameter.ActualValue.Value;
315      // compute the weight of each genealogy graph node as the ratio (produced offspring) / (surviving offspring)
316      foreach (var ind in population) {
317        var v = genealogyGraph.GetByContent(ind);
318        if (v.Parents.Count() == 1) {
319          var p = v.Parents.First();
320          foreach (var pp in p.Parents)
321            pp.Weight++;
322        } else {
323          foreach (var p in v.Parents) {
324            p.Weight++;
325          }
326        }
327      }
328
329      var results = ResultsParameter.ActualValue;
330
331      DataTableHistory successfulOffspringRatioHistory;
332      DataTableHistory successfulOffspringAbsoluteHistory;
333      if (!results.ContainsKey(successfulOffspringRatioTableHistoryName)) {
334        successfulOffspringRatioHistory = new DataTableHistory();
335        successfulOffspringAbsoluteHistory = new DataTableHistory();
336        results.Add(new Result(successfulOffspringRatioTableHistoryName, successfulOffspringRatioHistory));
337        results.Add(new Result(successfulOffspringAbsoluteValuesTableHistoryName, successfulOffspringAbsoluteHistory));
338      } else {
339        successfulOffspringRatioHistory = (DataTableHistory)results[successfulOffspringRatioTableHistoryName].Value;
340        successfulOffspringAbsoluteHistory = (DataTableHistory)results[successfulOffspringAbsoluteValuesTableHistoryName].Value;
341      }
342      var successfulOffspringRatioTable = new DataTable();
343      var successfulOffspringRatioRow = new DataRow("Successful Offspring Ratio") {
344        VisualProperties = { ChartType = DataRowVisualProperties.DataRowChartType.Columns, StartIndexZero = true }
345      };
346      successfulOffspringRatioRow.Values.Replace(genealogyGraph.GetByRank(generation).OrderByDescending(x => x.Quality).Select(x => x.OutDegree > 0 ? x.Weight / x.OutDegree : 0));
347      successfulOffspringRatioTable.Rows.Add(successfulOffspringRatioRow);
348      successfulOffspringRatioHistory.Add(successfulOffspringRatioTable);
349      // create heatmap
350      double[,] ratios = new double[population.Length, successfulOffspringRatioHistory.Count];
351      var ratiosHistoryList = successfulOffspringRatioHistory.AsReadOnly().ToList();
352      for (int i = 0; i < ratiosHistoryList.Count; ++i) {
353        var values = ratiosHistoryList[i].Rows.First().Values;
354        for (int j = 0; j < values.Count; ++j) {
355          ratios[j, i] = values[j];
356        }
357      }
358      var successfulOffspringRatios = new HeatMap(ratios);
359      if (!results.ContainsKey(successfulOffspringRatioHeatMapName)) {
360        results.Add(new Result(successfulOffspringRatioHeatMapName, successfulOffspringRatios));
361      } else {
362        results[successfulOffspringRatioHeatMapName].Value = successfulOffspringRatios;
363      }
364
365      var successfulOffspringValuesTable = new DataTable();
366      var successfulOffspringValuesRow = new DataRow("Successful Offspring Values") {
367        VisualProperties = { ChartType = DataRowVisualProperties.DataRowChartType.Columns, StartIndexZero = true }
368      };
369      successfulOffspringValuesRow.Values.Replace(genealogyGraph.GetByRank(generation).OrderByDescending(x => x.Quality).Select(x => x.Weight));
370      successfulOffspringValuesTable.Rows.Add(successfulOffspringValuesRow);
371      successfulOffspringAbsoluteHistory.Add(successfulOffspringValuesTable);
372      // create heatmap
373      double min = 0, max = 0;
374      double[,] elements = new double[population.Length, successfulOffspringAbsoluteHistory.Count];
375      var absoluteHistoryList = successfulOffspringAbsoluteHistory.AsReadOnly().ToList();
376      for (int i = 0; i < absoluteHistoryList.Count; ++i) {
377        var values = absoluteHistoryList[i].Rows.First().Values;
378        for (int j = 0; j < values.Count; ++j) {
379          elements[j, i] = values[j];
380          if (max < values[j])
381            max = values[j];
382          if (min > values[j])
383            min = values[j];
384        }
385      }
386      var heatmap = new HeatMap(elements);
387      heatmap.Maximum = max;
388      heatmap.Minimum = min;
389      if (!results.ContainsKey(successfulOffspringValuesHeatMapName)) {
390        results.Add(new Result(successfulOffspringValuesHeatMapName, heatmap));
391      } else {
392        results[successfulOffspringValuesHeatMapName].Value = heatmap;
393      }
394    }
395  }
396}
Note: See TracBrowser for help on using the repository browser.