Free cookie consent management tool by TermsFeed Policy Generator

source: branches/3040_VectorBasedGP/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/InteractiveSymbolicDataAnalysisSolutionSimplifierView.cs @ 17604

Last change on this file since 17604 was 17604, checked in by pfleck, 4 years ago

#3040 Stores the datatype of a tree node (e.g. variable nodes) in the tree itself for the interpreter to derive the datatypes for subtrees. This way, the interpreter (and simplifier) do not need an actual dataset to figure out datatypes for subtrees.

File size: 15.6 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 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.Drawing;
25using System.Linq;
26using System.Threading;
27using System.Threading.Tasks;
28using System.Windows.Forms;
29using HeuristicLab.Common;
30using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
31using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views;
32using HeuristicLab.MainForm;
33using HeuristicLab.MainForm.WindowsForms;
34
35namespace HeuristicLab.Problems.DataAnalysis.Symbolic.Views {
36  public abstract partial class InteractiveSymbolicDataAnalysisSolutionSimplifierView : AsynchronousContentView {
37    private Dictionary<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode> foldedNodes;
38    private Dictionary<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode> changedNodes;
39    private Dictionary<ISymbolicExpressionTreeNode, double> nodeImpacts;
40
41    private readonly ISymbolicDataAnalysisSolutionImpactValuesCalculator impactCalculator;
42
43    private readonly Progress progress = new Progress();
44    private CancellationTokenSource cancellationTokenSource;
45
46    private enum TreeState { Valid, Invalid }
47    private TreeState treeState;
48
49    protected InteractiveSymbolicDataAnalysisSolutionSimplifierView(ISymbolicDataAnalysisSolutionImpactValuesCalculator impactCalculator) {
50      InitializeComponent();
51      foldedNodes = new Dictionary<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>();
52      changedNodes = new Dictionary<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>();
53      nodeImpacts = new Dictionary<ISymbolicExpressionTreeNode, double>();
54      this.Caption = "Interactive Solution Simplifier";
55      this.impactCalculator = impactCalculator;
56
57      // initialize the tree modifier that will be used to perform edit operations over the tree
58      treeChart.ModifyTree = Modify;
59    }
60
61    /// <summary>
62    /// Remove, Replace or Insert subtrees
63    /// </summary>
64    /// <param name="tree">The symbolic expression tree</param>
65    /// <param name="parent">The insertion point (ie, the parent node who will receive a new child)</param>
66    /// <param name="oldChild">The subtree to be replaced</param>
67    /// <param name="newChild">The replacement subtree</param>
68    /// <param name="removeSubtree">Flag used to indicate if whole subtrees should be removed (default behavior), or just the subtree root</param>
69    private void Modify(ISymbolicExpressionTree tree, ISymbolicExpressionTreeNode parent,
70      ISymbolicExpressionTreeNode oldChild, ISymbolicExpressionTreeNode newChild, bool removeSubtree = true) {
71      if (oldChild == null && newChild == null)
72        throw new ArgumentNullException("Cannot deduce operation type from the arguments. Please provide non null operands.");
73      if (oldChild == null) {
74        // insertion operation
75        parent.AddSubtree(newChild);
76        newChild.Parent = parent;
77      } else if (newChild == null) {
78        // removal operation
79        parent.RemoveSubtree(parent.IndexOfSubtree(oldChild));
80        if (!removeSubtree) {
81          for (int i = oldChild.SubtreeCount - 1; i >= 0; --i) {
82            var subtree = oldChild.GetSubtree(i);
83            oldChild.RemoveSubtree(i);
84            parent.AddSubtree(subtree);
85          }
86        }
87      } else {
88        // replacement operation
89        var replacementIndex = parent.IndexOfSubtree(oldChild);
90        parent.RemoveSubtree(replacementIndex);
91        parent.InsertSubtree(replacementIndex, newChild);
92        newChild.Parent = parent;
93        if (changedNodes.ContainsKey(oldChild)) {
94          changedNodes.Add(newChild, changedNodes[oldChild]); // so that on double click the original node is restored
95          changedNodes.Remove(oldChild);
96        } else {
97          changedNodes.Add(newChild, oldChild);
98        }
99      }
100      treeState = IsValid(tree) ? TreeState.Valid : TreeState.Invalid;
101      switch (treeState) {
102        case TreeState.Valid:
103          this.grpViewHost.Enabled = true;
104          UpdateModel(Content.Model.SymbolicExpressionTree);
105          break;
106        case TreeState.Invalid:
107          this.grpViewHost.Enabled = false;
108          break;
109      }
110    }
111
112    // the optimizer always assumes 2 children for multiplication and addition nodes
113    // thus, we enforce that the tree stays valid so that the constant optimization won't throw an exception
114    // by returning 2 as the minimum allowed arity for addition and multiplication symbols
115    private readonly Func<ISymbol, int> GetMinArity = symbol => {
116      var min = symbol.MinimumArity;
117      if (symbol is Multiplication || symbol is Division) return Math.Max(2, min);
118      return min;
119    };
120    private bool IsValid(ISymbolicExpressionTree tree) {
121      treeChart.Tree = tree;
122      treeChart.Repaint();
123      // check if all nodes have a legal arity
124      var nodes = tree.IterateNodesPostfix().ToList();
125      bool valid = !nodes.Any(node => node.SubtreeCount < GetMinArity(node.Symbol) || node.SubtreeCount > node.Symbol.MaximumArity);
126
127      if (valid) {
128        // check if all variables are contained in the dataset
129        var variables = new HashSet<string>(Content.ProblemData.Dataset.VariableNames);
130        valid = nodes.OfType<VariableTreeNode>().All(x => variables.Contains(x.VariableName));
131      }
132
133      if (valid) {
134        btnOptimizeConstants.Enabled = true;
135        btnSimplify.Enabled = true;
136        treeStatusValue.Visible = false;
137      } else {
138        btnOptimizeConstants.Enabled = false;
139        btnSimplify.Enabled = false;
140        treeStatusValue.Visible = true;
141      }
142      this.Refresh();
143      return valid;
144    }
145
146    public new ISymbolicDataAnalysisSolution Content {
147      get { return (ISymbolicDataAnalysisSolution)base.Content; }
148      set { base.Content = value; }
149    }
150
151    protected override void RegisterContentEvents() {
152      base.RegisterContentEvents();
153      Content.ModelChanged += Content_Changed;
154      Content.ProblemDataChanged += Content_Changed;
155      treeChart.Repainted += treeChart_Repainted;
156      Progress.ShowOnControl(grpSimplify, progress);
157      progress.StopRequested += progress_StopRequested;
158    }
159    protected override void DeregisterContentEvents() {
160      base.DeregisterContentEvents();
161      Content.ModelChanged -= Content_Changed;
162      Content.ProblemDataChanged -= Content_Changed;
163      treeChart.Repainted -= treeChart_Repainted;
164      Progress.HideFromControl(grpSimplify, false);
165      progress.StopRequested -= progress_StopRequested;
166    }
167
168    private void Content_Changed(object sender, EventArgs e) {
169      UpdateView();
170      SetEnabledStateOfControls();
171    }
172
173    protected override void OnContentChanged() {
174      base.OnContentChanged();
175      foldedNodes = new Dictionary<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>();
176      UpdateView();
177      viewHost.Content = this.Content;
178    }
179
180    private void treeChart_Repainted(object sender, EventArgs e) {
181      if (nodeImpacts != null && nodeImpacts.Count > 0)
182        PaintNodeImpacts();
183    }
184
185    private void progress_StopRequested(object sender, EventArgs e) {
186      cancellationTokenSource.Cancel();
187    }
188
189    private async void UpdateView() {
190      if (Content == null || Content.Model == null || Content.ProblemData == null) return;
191      var tree = Content.Model.SymbolicExpressionTree;
192      treeChart.Tree = tree.Root.SubtreeCount > 1 ? new SymbolicExpressionTree(tree.Root) : new SymbolicExpressionTree(tree.Root.GetSubtree(0).GetSubtree(0));
193
194      progress.Start("Calculate Impact and Replacement Values ...");
195      cancellationTokenSource = new CancellationTokenSource();
196      progress.CanBeStopped = true;
197      try {
198        var impactAndReplacementValues = await Task.Run(() => CalculateImpactAndReplacementValues(tree));
199        try {
200          await Task.Delay(300, cancellationTokenSource.Token); // wait for progressbar to finish animation
201        } catch (OperationCanceledException) { }
202
203        var replacementValues = impactAndReplacementValues.ToDictionary(x => x.Key, x => x.Value.Item2);
204        foreach (var pair in replacementValues.Where(pair => !(pair.Key is ConstantTreeNode))) {
205          foldedNodes[pair.Key] = MakeConstantTreeNode(pair.Value);
206        }
207
208        nodeImpacts = impactAndReplacementValues.ToDictionary(x => x.Key, x => x.Value.Item1);
209      } finally {
210        progress.Finish();
211      }
212
213      progress.CanBeStopped = false;
214      PaintNodeImpacts();
215    }
216
217    protected virtual Dictionary<ISymbolicExpressionTreeNode, Tuple<double, double>> CalculateImpactAndReplacementValues(ISymbolicExpressionTree tree) {
218      var impactAndReplacementValues = new Dictionary<ISymbolicExpressionTreeNode, Tuple<double, double>>();
219      foreach (var node in tree.Root.GetSubtree(0).GetSubtree(0).IterateNodesPrefix()) {
220        if (progress.ProgressState == ProgressState.StopRequested) continue;
221        double impactValue, replacementValue, newQualityForImpactsCalculation;
222        impactCalculator.CalculateImpactAndReplacementValues(Content.Model, node, Content.ProblemData, Content.ProblemData.TrainingIndices, out impactValue, out replacementValue, out newQualityForImpactsCalculation);
223        double newProgressValue = progress.ProgressValue + 1.0 / (tree.Length - 2);
224        progress.ProgressValue = Math.Min(newProgressValue, 1);
225        impactAndReplacementValues.Add(node, new Tuple<double, double>(impactValue, replacementValue));
226      }
227      return impactAndReplacementValues;
228    }
229
230    protected abstract void UpdateModel(ISymbolicExpressionTree tree);
231
232    protected virtual ISymbolicExpressionTree OptimizeConstants(ISymbolicExpressionTree tree, CancellationToken cancellationToken, IProgress progress) {
233      return tree;
234    }
235
236    private static ConstantTreeNode MakeConstantTreeNode(double value) {
237      var constant = new Constant { MinValue = value - 1, MaxValue = value + 1 };
238      var constantTreeNode = (ConstantTreeNode)constant.CreateTreeNode();
239      constantTreeNode.Value = value;
240      return constantTreeNode;
241    }
242
243    private void treeChart_SymbolicExpressionTreeNodeDoubleClicked(object sender, MouseEventArgs e) {
244      if (treeState == TreeState.Invalid) return;
245      var visualNode = (VisualTreeNode<ISymbolicExpressionTreeNode>)sender;
246      if (visualNode.Content == null) { throw new Exception("VisualNode content cannot be null."); }
247      var symbExprTreeNode = (SymbolicExpressionTreeNode)visualNode.Content;
248      var tree = Content.Model.SymbolicExpressionTree;
249      var parent = symbExprTreeNode.Parent;
250      int indexOfSubtree = parent.IndexOfSubtree(symbExprTreeNode);
251      if (changedNodes.ContainsKey(symbExprTreeNode)) {
252        // undo node change
253        parent.RemoveSubtree(indexOfSubtree);
254        var originalNode = changedNodes[symbExprTreeNode];
255        parent.InsertSubtree(indexOfSubtree, originalNode);
256        changedNodes.Remove(symbExprTreeNode);
257      } else if (foldedNodes.ContainsKey(symbExprTreeNode)) {
258        // undo node folding
259        SwitchNodeWithReplacementNode(parent, indexOfSubtree);
260      }
261      UpdateModel(tree);
262    }
263
264    private void SwitchNodeWithReplacementNode(ISymbolicExpressionTreeNode parent, int subTreeIndex) {
265      ISymbolicExpressionTreeNode subTree = parent.GetSubtree(subTreeIndex);
266      if (foldedNodes.ContainsKey(subTree)) {
267        parent.RemoveSubtree(subTreeIndex);
268        var replacementNode = foldedNodes[subTree];
269        parent.InsertSubtree(subTreeIndex, replacementNode);
270        // exchange key and value
271        foldedNodes.Remove(subTree);
272        foldedNodes.Add(replacementNode, subTree);
273      }
274    }
275
276    private void PaintNodeImpacts() {
277      var impacts = nodeImpacts.Values;
278      double max = impacts.Max();
279      double min = impacts.Min();
280      foreach (ISymbolicExpressionTreeNode treeNode in Content.Model.SymbolicExpressionTree.IterateNodesPostfix()) {
281        VisualTreeNode<ISymbolicExpressionTreeNode> visualTree = treeChart.GetVisualSymbolicExpressionTreeNode(treeNode);
282
283        if (!(treeNode is ConstantTreeNode) && nodeImpacts.ContainsKey(treeNode)) {
284          visualTree.ToolTip = visualTree.Content.ToString();
285          double impact = nodeImpacts[treeNode];
286
287          // impact = 0 if no change
288          // impact < 0 if new solution is better
289          // impact > 0 if new solution is worse
290          if (impact < 0.0) {
291            // min is guaranteed to be < 0
292            visualTree.FillColor = Color.FromArgb((int)(impact / min * 255), Color.Red);
293          } else if (impact.IsAlmost(0.0)) {
294            visualTree.FillColor = Color.White;
295          } else {
296            // max is guaranteed to be > 0
297            visualTree.FillColor = Color.FromArgb((int)(impact / max * 255), Color.Green);
298          }
299          visualTree.ToolTip += Environment.NewLine + "Node impact: " + impact;
300          var constantReplacementNode = foldedNodes[treeNode] as ConstantTreeNode;
301          if (constantReplacementNode != null) {
302            visualTree.ToolTip += Environment.NewLine + "Replacement value: " + constantReplacementNode.Value;
303          }
304        }
305        if (visualTree != null)
306          if (changedNodes.ContainsKey(treeNode)) {
307            visualTree.LineColor = Color.DodgerBlue;
308          } else if (treeNode is ConstantTreeNode && foldedNodes.ContainsKey(treeNode)) {
309            visualTree.LineColor = Color.DarkOrange;
310          }
311      }
312      treeChart.RepaintNodes();
313    }
314
315    private void btnSimplify_Click(object sender, EventArgs e) {
316      if (Content.Model.Interpreter is SymbolicDataAnalysisExpressionTreeVectorInterpreter interpreter) { // TODO: Own interface for data-dependent node-types-interpreter?
317        var simplifier = new VectorTreeSimplifier(interpreter);
318        var simplifiedExpressionTree = simplifier.Simplify(Content.Model.SymbolicExpressionTree);
319        UpdateModel(simplifiedExpressionTree);
320      } else {
321        var simplifiedExpressionTree = TreeSimplifier.Simplify(Content.Model.SymbolicExpressionTree);
322        UpdateModel(simplifiedExpressionTree);
323      }
324    }
325
326    private async void btnOptimizeConstants_Click(object sender, EventArgs e) {
327      progress.Start("Optimizing Constants ...");
328      cancellationTokenSource = new CancellationTokenSource();
329      progress.CanBeStopped = true;
330      try {
331        var tree = (ISymbolicExpressionTree)Content.Model.SymbolicExpressionTree.Clone();
332
333        var newTree = await Task.Run(() => OptimizeConstants(tree, cancellationTokenSource.Token, progress), cancellationTokenSource.Token);
334        try {
335          await Task.Delay(300, cancellationTokenSource.Token); // wait for progressbar to finish animation
336        } catch (OperationCanceledException) { }
337        UpdateModel(newTree); // triggers progress.Finish after calculating the node impacts when model is changed
338      } catch {
339        progress.Finish();
340      }
341    }
342  }
343}
Note: See TracBrowser for help on using the repository browser.