Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.EvolutionaryTracking/HeuristicLab.EvolutionaryTracking/3.4/Analyzers/SymbolicExpressionTreeGenealogyAnalyzer.cs @ 7479

Last change on this file since 7479 was 7479, checked in by bburlacu, 12 years ago

#1772: Implemented an initial set of features: individual ancestry, genealogy tracking via customized genetic operators and data structures.

File size: 15.4 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2012 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;
23using System.Collections.Generic;
24using System.Globalization;
25using System.Linq;
26using HeuristicLab.Common;
27using HeuristicLab.Core;
28using HeuristicLab.Data;
29using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
30using HeuristicLab.Operators;
31using HeuristicLab.Optimization;
32using HeuristicLab.Parameters;
33using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
34
35using TreeCacheType = HeuristicLab.Core.ItemList<HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.ISymbolicExpressionTree>;
36using CloneMapType = HeuristicLab.Core.ItemDictionary<HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.ISymbolicExpressionTree,
37                                                      HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.ISymbolicExpressionTree>;
38using TraceMapType = HeuristicLab.Core.ItemDictionary<HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.ISymbolicExpressionTree,
39                                                      HeuristicLab.Core.IItemArray<HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.ISymbolicExpressionTree>>;
40
41namespace HeuristicLab.EvolutionaryTracking {
42  /// <summary>
43  /// An operator that tracks the genealogy of every individual
44  /// </summary>
45  [Item("SymbolicExpressionTreeGenealogyAnalyzer", "An operator that tracks tree lengths of Symbolic Expression Trees")]
46  [StorableClass]
47  public sealed class SymbolicExpressionTreeGenealogyAnalyzer : SingleSuccessorOperator, IAnalyzer {
48    private const string SymbolicExpressionTreeParameterName = "SymbolicExpressionTree";
49    private const string UpdateIntervalParameterName = "UpdateInterval";
50    private const string UpdateCounterParameterName = "UpdateCounter";
51    private const string ResultsParameterName = "Results";
52    private const string ElitesParameterName = "Elites";
53    private const string GenerationsParameterName = "Generations";
54    private const string MaximumGenerationsParameterName = "MaximumGenerations";
55    private const string MaximumSelectionPressureParameterName = "MaximumSelectionPressure";
56    private const string SelectionPressureParameterName = "SelectionPressure";
57    private const string GlobalTreeCacheParameterName = "GlobalTreeCache";
58    private const string GlobalTraceMapParameterName = "GlobalTraceMap";
59    private const string GlobalCloneMapParameterName = "GlobalCloneMap";
60    private const string PopulationGraphResultParameterName = "PopulationGraph";
61    private const string PopulationGraphResultParameterDescription = "Individual lineages";
62
63    #region Parameter properties
64    public ValueParameter<IntValue> UpdateIntervalParameter {
65      get { return (ValueParameter<IntValue>)Parameters[UpdateIntervalParameterName]; }
66    }
67    public ValueParameter<IntValue> UpdateCounterParameter {
68      get { return (ValueParameter<IntValue>)Parameters[UpdateCounterParameterName]; }
69    }
70    public LookupParameter<ResultCollection> ResultsParameter {
71      get { return (LookupParameter<ResultCollection>)Parameters[ResultsParameterName]; }
72    }
73    public LookupParameter<IntValue> ElitesParameter {
74      get { return (LookupParameter<IntValue>)Parameters[ElitesParameterName]; }
75    }
76    public LookupParameter<IntValue> GenerationsParameter {
77      get { return (LookupParameter<IntValue>)Parameters[GenerationsParameterName]; }
78    }
79    public LookupParameter<IntValue> MaximumGenerationsParameter {
80      get { return (LookupParameter<IntValue>)Parameters[MaximumGenerationsParameterName]; }
81    }
82    public LookupParameter<DoubleValue> SelectionPressureParameter {
83      get { return (LookupParameter<DoubleValue>)Parameters[SelectionPressureParameterName]; }
84    }
85    public LookupParameter<DoubleValue> MaximumSelectionPressureParameter {
86      get { return (LookupParameter<DoubleValue>)Parameters[MaximumSelectionPressureParameterName]; }
87    }
88    // genealogy global parameters
89    public LookupParameter<TreeCacheType> GlobalTreeCacheParameter {
90      get { return (LookupParameter<TreeCacheType>)Parameters[GlobalTreeCacheParameterName]; }
91    }
92    public LookupParameter<TraceMapType> GlobalTraceMapParameter {
93      get { return (LookupParameter<TraceMapType>)Parameters[GlobalTraceMapParameterName]; }
94    }
95    public LookupParameter<CloneMapType> GlobalCloneMapParameter {
96      get { return (LookupParameter<CloneMapType>)Parameters[GlobalCloneMapParameterName]; }
97    }
98    #endregion
99
100    #region Properties
101    public bool EnabledByDefault {
102      get { return true; }
103    }
104    public IntValue UpdateInterval {
105      get { return UpdateIntervalParameter.Value; }
106    }
107    public IntValue UpdateCounter {
108      get { return UpdateCounterParameter.Value; }
109    }
110    public ResultCollection Results {
111      get { return ResultsParameter.ActualValue; }
112    }
113    public IntValue Elites {
114      get { return ElitesParameter.ActualValue; }
115    }
116    public IntValue Generations {
117      get { return GenerationsParameter.ActualValue; }
118    }
119    public IntValue MaximumGenerations {
120      get { return MaximumGenerationsParameter.ActualValue; }
121    }
122    public DoubleValue SelectionPressure {
123      get { return SelectionPressureParameter.ActualValue; }
124    }
125    public DoubleValue MaximumSelectionPressure {
126      get { return MaximumSelectionPressureParameter.ActualValue; }
127    }
128    public ItemList<ISymbolicExpressionTree> GlobalTreeCache {
129      get { return GlobalTreeCacheParameter.ActualValue; }
130    }
131    public ItemDictionary<ISymbolicExpressionTree, ISymbolicExpressionTree> GlobalCloneMap {
132      get { return GlobalCloneMapParameter.ActualValue; }
133    }
134    public ItemDictionary<ISymbolicExpressionTree, IItemArray<ISymbolicExpressionTree>> GlobalTraceMap {
135      get { return GlobalTraceMapParameter.ActualValue; }
136    }
137    #endregion
138
139    [StorableConstructor]
140    private SymbolicExpressionTreeGenealogyAnalyzer(bool deserializing)
141      : base() {
142    }
143    private SymbolicExpressionTreeGenealogyAnalyzer(SymbolicExpressionTreeGenealogyAnalyzer original, Cloner cloner)
144      : base(original, cloner) {
145    }
146    public override IDeepCloneable Clone(Cloner cloner) {
147      return new SymbolicExpressionTreeGenealogyAnalyzer(this, cloner);
148    }
149    public SymbolicExpressionTreeGenealogyAnalyzer()
150      : base() {
151      Parameters.Add(new ScopeTreeLookupParameter<ISymbolicExpressionTree>(SymbolicExpressionTreeParameterName, "The symbolic expression tree whose length should be calculated."));
152      Parameters.Add(new LookupParameter<IntValue>(ElitesParameterName, "The number of elites."));
153      Parameters.Add(new LookupParameter<IntValue>(GenerationsParameterName, "The number of generations so far."));
154      Parameters.Add(new LookupParameter<IntValue>(MaximumGenerationsParameterName, "The maximum number of generations"));
155      Parameters.Add(new LookupParameter<DoubleValue>(SelectionPressureParameterName, "The current selection (ony for OSGA)"));
156      Parameters.Add(new LookupParameter<DoubleValue>(MaximumSelectionPressureParameterName, "Maximum allowed value of selection pressure."));
157      Parameters.Add(new ValueParameter<IntValue>(UpdateIntervalParameterName, "The interval in which the tree length analysis should be applied.", new IntValue(1)));
158      Parameters.Add(new ValueParameter<IntValue>(UpdateCounterParameterName, "The value which counts how many times the operator was called since the last update", new IntValue(0)));
159      Parameters.Add(new LookupParameter<TreeCacheType>(GlobalTreeCacheParameterName, "A global list holding all trees from all generations."));
160      Parameters.Add(new LookupParameter<TraceMapType>(GlobalTraceMapParameterName, "A global cache containing the whole genealogy."));
161      Parameters.Add(new LookupParameter<CloneMapType>(GlobalCloneMapParameterName, "A global map keeping track of trees and their clones (made during selection)."));
162      Parameters.Add(new ValueLookupParameter<ResultCollection>(ResultsParameterName, "The results collection where the analysis values should be stored."));
163
164      UpdateCounterParameter.Hidden = true;
165      UpdateIntervalParameter.Hidden = true;
166    }
167
168    [StorableHook(HookType.AfterDeserialization)]
169    private void AfterDeserialization() {
170      // check if all the parameters are present and accounted for
171      if (!Parameters.ContainsKey(UpdateIntervalParameterName)) {
172        Parameters.Add(new ValueParameter<IntValue>(UpdateIntervalParameterName,
173                                                    "The interval in which the tree length analysis should be applied.",
174                                                    new IntValue(1)));
175      }
176      //necessary code to correct UpdateCounterParameter - type was changed from LookupParameter to ValueParameter
177      if (Parameters.ContainsKey(UpdateCounterParameterName) &&
178          (Parameters[UpdateCounterParameterName] is LookupParameter<IntValue>))
179        Parameters.Remove(UpdateCounterParameterName);
180      if (!Parameters.ContainsKey(UpdateCounterParameterName)) {
181        Parameters.Add(new ValueParameter<IntValue>(UpdateCounterParameterName,
182                                                    "The value which counts how many times the operator was called since the last update",
183                                                    new IntValue(0)));
184        UpdateCounterParameter.Hidden = true;
185      }
186    }
187
188    #region IStatefulItem members
189    public override void InitializeState() {
190      base.InitializeState();
191      UpdateCounter.Value = 0;
192    }
193
194    public override void ClearState() {
195      base.ClearState();
196      UpdateCounter.Value = 0;
197    }
198    #endregion
199
200    public override IOperation Apply() {
201      UpdateCounter.Value++;
202      // the analyzer runs periodically, every 'updateInterval' times
203      if (UpdateCounter.Value == UpdateInterval.Value) {
204        UpdateCounter.Value = 0; // reset counter
205
206        var gScope = ExecutionContext.Scope;
207        while (gScope.Parent != null) gScope = gScope.Parent;
208
209        GenealogyGraph _graph;
210        if (!Results.ContainsKey(PopulationGraphResultParameterName)) {
211          _graph = new GenealogyGraph();
212          Results.Add(new Result(PopulationGraphResultParameterName, PopulationGraphResultParameterDescription, _graph));
213        } else {
214          _graph = (GenealogyGraph)Results[PopulationGraphResultParameterName].Value;
215        }
216
217        var treeQualities = (from s in gScope.SubScopes
218                             let tree = (SymbolicExpressionTree)s.Variables["SymbolicExpressionTree"].Value
219                             let quality = (DoubleValue)s.Variables["Quality"].Value
220                             orderby quality.Value descending
221                             select new Tuple<ISymbolicExpressionTree, DoubleValue>(tree, quality)).ToList();
222        // add all individuals to the evolutionary graph
223        for (int i = 0; i != treeQualities.Count; ++i) {
224          var tree = treeQualities[i].Item1;
225          var quality = treeQualities[i].Item2;
226          if (!_graph.HasNode(tree)) {
227            _graph.AddNode(tree, quality.Value, (Generations.Value * treeQualities.Count + i + 1).ToString(CultureInfo.InvariantCulture));
228          } else {
229            _graph.GetNode(tree).Label += "\\n" + (Generations.Value * treeQualities.Count + i + 1).ToString(CultureInfo.InvariantCulture);
230          }
231
232          if (GlobalTraceMap == null || !GlobalTraceMap.ContainsKey(tree))
233            continue;
234          // get number of parents and adjust label (2 parents => crossover, 1 parent => mutation)
235          var parents = GlobalTraceMap[tree];
236          string label = (parents.Count == 2) ? "c" : "m";
237          foreach (var parent in parents) {
238            _graph.AddArc(parent, tree, label);
239          }
240        }
241        if (GlobalTraceMap != null)
242          GlobalTraceMap.Clear();
243
244        // mark elites
245        for (int i = 0; i != Elites.Value; ++i)
246          _graph.GetNode(treeQualities[i].Item1).IsElite = true;
247
248        // if we've reached the end of the run
249        if (Generations.Value == MaximumGenerations.Value ||
250           (SelectionPressureParameter != null && SelectionPressure.Value >= MaximumSelectionPressure.Value)) {
251
252          var path = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
253
254          // write whole graph to a dot file
255          WriteDot(path + @"\lineage.dot", _graph);
256
257          // get genealogy of the best solution
258          var bestSolution = treeQualities.First().Item1;
259          var genealogy = _graph.GetNode(bestSolution).Genealogy();
260          WriteDot(path + @"\bestlineage.dot", genealogy);
261
262          // trim the graph
263          // exclude the individuals of the last generation
264          var individuals = _graph.Keys.Except(treeQualities.Select(x => x.Item1)).ToList();
265          bool done = false;
266          while (!done) {
267            done = true;
268            foreach (var ind in individuals) {
269              // if node has no outgoing connections (absence of offspring), remove it from the graph
270              var node = _graph.GetNode(ind);
271              if (node == null) continue;
272              if (node.OutEdges == null) {
273                done = false; // we still have "dead" nodes
274                _graph.RemoveNode(ind);
275              }
276            }
277          }
278          WriteDot(path + @"\trimmedlineage.dot", _graph);
279        }
280      }
281      return base.Apply();
282    }
283
284    private static void WriteDot(string path, GenealogyGraph graph) {
285      using (var file = new System.IO.StreamWriter(path)) {
286        string nl = Environment.NewLine;
287        file.WriteLine("digraph \"lineage " + graph.AverageDegree.ToString() + "\" {" + nl +
288                       "ratio=auto;" + nl +
289                       "mincross=2.0");
290        file.WriteLine("\tnode [shape=circle,label=\"\",style=filled]");
291
292        foreach (var node in graph.Values) {
293          var quality = node.Quality;
294          string fillColor = String.Format("#{0:x2}{1:x2}{2:x2}", (int)(255 - 255 * quality), (int)(255 * quality), (int)(100 * quality));
295          if (node.IsElite)
296            fillColor = String.Format("#{0:x2}{1:x2}{2:x2}", (int)(255 - 255 * quality), (int)(255 * quality), (int)(255 * quality));
297          file.WriteLine("\t\"" + node.Id + "\" [fillcolor=\"" + fillColor + "\",label=\"" + node.Label + "\"];");
298          if (node.InEdges == null)
299            continue;
300          foreach (var edge in node.InEdges)
301            file.WriteLine("\t\"" + edge.Target.Id + "\" -> \"" + node.Id + "\" [arrowsize=.5, color=\"" + fillColor + "\"];");
302        }
303        file.WriteLine("}");
304      }
305    }
306  }
307}
Note: See TracBrowser for help on using the repository browser.