Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.TreeSimplifierView/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/InteractiveSymbolicDataAnalysisSolutionSimplifierView.cs @ 7411

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

#1763: Improved ValueChangeDialog and overall behavior. Implemented pruning operation on background thread. TODO: Test, improve. This is a work-in-progress.

File size: 16.0 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.ComponentModel;
25using System.Drawing;
26using System.Linq;
27using System.Windows.Forms;
28using HeuristicLab.Common;
29using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
30using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views;
31using HeuristicLab.MainForm.WindowsForms;
32
33namespace HeuristicLab.Problems.DataAnalysis.Symbolic.Views {
34  public abstract partial class InteractiveSymbolicDataAnalysisSolutionSimplifierView : AsynchronousContentView {
35    private Dictionary<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode> replacementNodes;
36    private Dictionary<ISymbolicExpressionTreeNode, double> nodeImpacts;
37    private Dictionary<ISymbolicExpressionTreeNode, double> originalValues;
38    private bool updateInProgress = false;
39    private VisualSymbolicExpressionTreeNode visualTreeNode; // need some kind of state for correctly handling events
40
41    public InteractiveSymbolicDataAnalysisSolutionSimplifierView() {
42      InitializeComponent();
43      this.replacementNodes = new Dictionary<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>();
44      this.nodeImpacts = new Dictionary<ISymbolicExpressionTreeNode, double>();
45      this.originalValues = new Dictionary<ISymbolicExpressionTreeNode, double>();
46      this.Caption = "Interactive Solution Simplifier";
47    }
48
49    public new ISymbolicDataAnalysisSolution Content {
50      get { return (ISymbolicDataAnalysisSolution)base.Content; }
51      set { base.Content = value; }
52    }
53
54    protected override void RegisterContentEvents() {
55      base.RegisterContentEvents();
56      Content.ModelChanged += new EventHandler(Content_ModelChanged);
57      Content.ProblemDataChanged += new EventHandler(Content_ProblemDataChanged);
58    }
59    protected override void DeregisterContentEvents() {
60      base.DeregisterContentEvents();
61      Content.ModelChanged -= new EventHandler(Content_ModelChanged);
62      Content.ProblemDataChanged -= new EventHandler(Content_ProblemDataChanged);
63    }
64
65    private void Content_ModelChanged(object sender, EventArgs e) {
66      OnModelChanged();
67    }
68    private void Content_ProblemDataChanged(object sender, EventArgs e) {
69      OnProblemDataChanged();
70    }
71
72    protected virtual void OnModelChanged() {
73      this.CalculateReplacementNodesAndNodeImpacts();
74    }
75
76    protected virtual void OnProblemDataChanged() {
77      this.CalculateReplacementNodesAndNodeImpacts();
78    }
79
80    protected override void OnContentChanged() {
81      base.OnContentChanged();
82      this.CalculateReplacementNodesAndNodeImpacts();
83      this.viewHost.Content = this.Content;
84    }
85
86    private void CalculateReplacementNodesAndNodeImpacts() {
87      if (Content != null && Content.Model != null && Content.ProblemData != null) {
88        var tree = Content.Model.SymbolicExpressionTree;
89        var replacementValues = CalculateReplacementValues(tree);
90        foreach (var pair in replacementValues) {
91          if (!(pair.Key is ConstantTreeNode)) {
92            replacementNodes[pair.Key] = MakeConstantTreeNode(pair.Value);
93          }
94        }
95        nodeImpacts = CalculateImpactValues(Content.Model.SymbolicExpressionTree);
96
97        if (!updateInProgress) {
98          // automatically fold all branches with impact = 0
99          List<ISymbolicExpressionTreeNode> nodeList = Content.Model.SymbolicExpressionTree.Root.GetSubtree(0).IterateNodesPrefix().ToList();
100          foreach (var parent in nodeList) {
101            for (int subTreeIndex = 0; subTreeIndex < parent.SubtreeCount; subTreeIndex++) {
102              var child = parent.GetSubtree(subTreeIndex);
103              if (!(child.Symbol is Constant) && nodeImpacts[child].IsAlmost(0.0)) {
104                SwitchNodeWithReplacementNode(parent, subTreeIndex);
105              }
106            }
107          }
108        }
109
110        // show only interesting part of solution
111        if (tree.Root.SubtreeCount > 1)
112          this.treeChart.Tree = new SymbolicExpressionTree(tree.Root); // RPB + ADFs
113        else
114          this.treeChart.Tree = new SymbolicExpressionTree(tree.Root.GetSubtree(0).GetSubtree(0)); // 1st child of RPB
115        this.PaintNodeImpacts();
116      }
117    }
118
119    protected abstract Dictionary<ISymbolicExpressionTreeNode, double> CalculateReplacementValues(ISymbolicExpressionTree tree);
120    protected abstract Dictionary<ISymbolicExpressionTreeNode, double> CalculateImpactValues(ISymbolicExpressionTree tree);
121    protected abstract void UpdateModel(ISymbolicExpressionTree tree);
122
123    private ConstantTreeNode MakeConstantTreeNode(double value) {
124      Constant constant = new Constant();
125      constant.MinValue = value - 1;
126      constant.MaxValue = value + 1;
127      ConstantTreeNode constantTreeNode = (ConstantTreeNode)constant.CreateTreeNode();
128      constantTreeNode.Value = value;
129      return constantTreeNode;
130    }
131
132    private void treeChart_SymbolicExpressionTreeNodeDoubleClicked(object sender, MouseEventArgs e) {
133      visualTreeNode = (VisualSymbolicExpressionTreeNode)sender;
134      var tree = Content.Model.SymbolicExpressionTree;
135      // check if the node value/weight has been altered
136      // if so, the first double click will return the node to its original value/weight
137      // the next double click will replace the ConstantNode with the original SymbolicExpressionTreeNode
138      if (originalValues.ContainsKey(visualTreeNode.SymbolicExpressionTreeNode)) {
139        double value = originalValues[visualTreeNode.SymbolicExpressionTreeNode];
140        var subTree = visualTreeNode.SymbolicExpressionTreeNode;
141        if (subTree.Symbol is Constant)
142          (subTree as ConstantTreeNode).Value = value;
143        else if (subTree.Symbol is Variable)
144          (subTree as VariableTreeNode).Weight = value;
145        originalValues.Remove(subTree);
146        updateInProgress = true;
147        UpdateModel(tree);
148        updateInProgress = false;
149        return;
150      }
151
152      foreach (SymbolicExpressionTreeNode treeNode in tree.IterateNodesPostfix()) {
153        for (int i = 0; i < treeNode.SubtreeCount; i++) {
154          ISymbolicExpressionTreeNode subTree = treeNode.GetSubtree(i);
155          // only allow to replace nodes for which a replacement value is known (replacement value for ADF nodes are not available)
156          if (subTree == visualTreeNode.SymbolicExpressionTreeNode && replacementNodes.ContainsKey(subTree)) {
157            SwitchNodeWithReplacementNode(treeNode, i);
158
159            // show only interesting part of solution
160            if (tree.Root.SubtreeCount > 1)
161              this.treeChart.Tree = new SymbolicExpressionTree(tree.Root); // RPB + ADFs
162            else
163              this.treeChart.Tree = new SymbolicExpressionTree(tree.Root.GetSubtree(0).GetSubtree(0)); // 1st child of RPB
164
165            updateInProgress = true;
166            UpdateModel(tree);
167            updateInProgress = false;
168            return; // break all loops
169          }
170        }
171      }
172    }
173
174    private void treeChart_SymbolicExpressionTreeNodeClicked(object sender, MouseEventArgs e) {
175      if (e.Button == MouseButtons.Right) {
176        var node = (sender as VisualSymbolicExpressionTreeNode).SymbolicExpressionTreeNode;
177        var menu = treeChart.ContextMenuStrip;
178
179        bool flag = (node.Symbol is Constant || node.Symbol is Variable);
180
181        ToolStripItem[] items = menu.Items.Find("changeValueToolStripMenuItem", false);
182        ToolStripItem changeValueMenuItem = null;
183        if (items.Any())
184          changeValueMenuItem = items[0];
185        if (changeValueMenuItem != null) {
186          changeValueMenuItem.Enabled = flag;
187          changeValueMenuItem.Visible = flag;
188        }
189      }
190    }
191
192    private void treeChart_SymbolicExpressionTreeNodeChanged(object sender, EventArgs e) {
193      visualTreeNode = sender as VisualSymbolicExpressionTreeNode;
194      var subTree = visualTreeNode.SymbolicExpressionTreeNode;
195      string title = String.Empty;
196      double value = 0.0;
197
198      if (subTree.Symbol is Constant) {
199        title = "Change Constant Value";
200        value = (subTree as ConstantTreeNode).Value;
201      } else if (subTree.Symbol is Variable) {
202        title = "Change Weight Value";
203        value = (subTree as VariableTreeNode).Weight;
204      }
205
206      double originalValue = value;
207
208      if (!originalValues.ContainsKey(subTree))
209        originalValues.Add(subTree, value);
210      else originalValue = originalValues[subTree];
211
212      var dialog = new ValueChangeDialog(title, Math.Round(originalValue, 4).ToString(), Math.Round(value, 4).ToString());
213      dialog.NewValueTextBox.Validated += OnNewValueValidated;
214      dialog.ShowDialog(this);
215    }
216
217    private void OnNewValueValidated(object sender, EventArgs e) {
218      var textBox = sender as TextBox;
219      var value = double.Parse(textBox.Text);
220      var subTree = visualTreeNode.SymbolicExpressionTreeNode;
221      var tree = Content.Model.SymbolicExpressionTree;
222
223      if (subTree.Symbol is Constant)
224        (subTree as ConstantTreeNode).Value = value;
225      else if (subTree.Symbol is Variable)
226        (subTree as VariableTreeNode).Weight = value;
227
228      // show only interesting part of solution
229      treeChart.Tree = tree.Root.SubtreeCount > 1 ? new SymbolicExpressionTree(tree.Root) : new SymbolicExpressionTree(tree.Root.GetSubtree(0).GetSubtree(0));
230      updateInProgress = true;
231      UpdateModel(tree);
232      updateInProgress = false;
233    }
234
235    private void SwitchNodeWithReplacementNode(ISymbolicExpressionTreeNode parent, int subTreeIndex) {
236      ISymbolicExpressionTreeNode subTree = parent.GetSubtree(subTreeIndex);
237      parent.RemoveSubtree(subTreeIndex);
238      if (replacementNodes.ContainsKey(subTree)) {
239        var replacementNode = replacementNodes[subTree];
240        parent.InsertSubtree(subTreeIndex, replacementNode);
241        // exchange key and value
242        replacementNodes.Remove(subTree);
243        replacementNodes.Add(replacementNode, subTree);
244      }
245    }
246
247    private void PaintNodeImpacts() {
248      var impacts = nodeImpacts.Values;
249      double max = impacts.Max();
250      double min = impacts.Min();
251      foreach (ISymbolicExpressionTreeNode treeNode in Content.Model.SymbolicExpressionTree.IterateNodesPostfix()) {
252        VisualSymbolicExpressionTreeNode visualTree = treeChart.GetVisualSymbolicExpressionTreeNode(treeNode);
253        bool flag1 = replacementNodes.ContainsKey(treeNode);
254        bool flag2 = originalValues.ContainsKey(treeNode);
255        bool flag3 = treeNode is ConstantTreeNode;
256
257        if (flag2) // constant or variable node was changed
258          visualTree.ToolTip += Environment.NewLine + "Original value: " + originalValues[treeNode];
259        else if (flag1 && flag3) // node was folded to a constant
260          visualTree.ToolTip += Environment.NewLine + "Original node: " + replacementNodes[treeNode];
261
262        if (!(treeNode is ConstantTreeNode) && nodeImpacts.ContainsKey(treeNode)) {
263          double impact = nodeImpacts[treeNode];
264
265          // impact = 0 if no change
266          // impact < 0 if new solution is better
267          // impact > 0 if new solution is worse
268          if (impact < 0.0) {
269            // min is guaranteed to be < 0
270            visualTree.FillColor = Color.FromArgb((int)(impact / min * 255), Color.Red);
271          } else if (impact.IsAlmost(0.0)) {
272            visualTree.FillColor = Color.White;
273          } else {
274            // max is guaranteed to be > 0
275            visualTree.FillColor = Color.FromArgb((int)(impact / max * 255), Color.Green);
276          }
277          visualTree.ToolTip += Environment.NewLine + "Node impact: " + impact;
278          var constantReplacementNode = replacementNodes[treeNode] as ConstantTreeNode;
279          if (constantReplacementNode != null) {
280            visualTree.ToolTip += Environment.NewLine + "Replacement value: " + constantReplacementNode.Value;
281          }
282        }
283      }
284      this.PaintCollapsedNodes();
285      this.treeChart.Repaint();
286    }
287
288    private void PaintCollapsedNodes() {
289      foreach (ISymbolicExpressionTreeNode treeNode in Content.Model.SymbolicExpressionTree.IterateNodesPostfix()) {
290        bool flag1 = replacementNodes.ContainsKey(treeNode);
291        bool flag2 = originalValues.ContainsKey(treeNode);
292        if (flag1 && treeNode is ConstantTreeNode) {
293          this.treeChart.GetVisualSymbolicExpressionTreeNode(treeNode).LineColor = flag2 ? Color.DarkViolet : Color.DarkOrange;
294        } else if (flag2) {
295          this.treeChart.GetVisualSymbolicExpressionTreeNode(treeNode).LineColor = Color.DodgerBlue;
296        }
297      }
298    }
299
300    private void btnSimplify_Click(object sender, EventArgs e) {
301      SymbolicDataAnalysisExpressionTreeSimplifier simplifier = new SymbolicDataAnalysisExpressionTreeSimplifier();
302      var simplifiedExpressionTree = simplifier.Simplify(Content.Model.SymbolicExpressionTree);
303      UpdateModel(simplifiedExpressionTree);
304    }
305
306    private void btnPrune_Click(object sender, EventArgs e) {
307      backgroundWorker1.RunWorkerAsync();
308    }
309
310    private static IEnumerable<ISymbolicExpressionTreeNode> GetNodesAtDepth(ISymbolicExpressionTreeNode root, Data.IntRange range) {
311      var list = new List<Tuple<ISymbolicExpressionTreeNode, int>> { new Tuple<ISymbolicExpressionTreeNode, int>(root, 0) };
312      int offset = 0;
313      int level = 0;
314      while (level < range.End) {
315        ++level;
316        int count = list.Count;
317        for (int i = offset; i != count; ++i) {
318          if (list[i].Item1.Subtrees.Any())
319            list.AddRange(from s in list[i].Item1.Subtrees select new Tuple<ISymbolicExpressionTreeNode, int>(s, level));
320        }
321        offset = count;
322      }
323      // taking advantage of the fact that the list is already sorted by level
324      for (int i = list.Count - 1; i >= 0; --i) {
325        if (list[i].Item2 >= range.Start)
326          yield return list[i].Item1;
327        else break;
328      }
329    }
330
331    protected abstract void btnOptimizeConstants_Click(object sender, EventArgs e);
332
333    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
334      BackgroundWorker worker = sender as BackgroundWorker;
335      PruneTree(worker);
336
337    }
338
339    private void PruneTree(BackgroundWorker worker) {
340      var tree = Content.Model.SymbolicExpressionTree;
341      // get all tree nodes
342      foreach (var node in GetNodesAtDepth(tree.Root, new Data.IntRange(2, tree.Depth))) {
343        if (worker.CancellationPending)
344          break; // pruning cancelled
345        if (!(node.Symbol is Constant) && nodeImpacts.ContainsKey(node) && nodeImpacts[node] < 0.001) {
346          SwitchNodeWithReplacementNode(node.Parent, node.Parent.IndexOfSubtree(node));
347          CalculateReplacementNodesAndNodeImpacts();
348        }
349      }
350    }
351
352    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
353      if (e.Cancelled) {
354        // The user canceled the operation.
355      } else if (e.Error != null) {
356        // There was an error during the operation.
357        // Error-handling
358      } else {
359        // The operation completed normally. We can update the model
360        UpdateModel(Content.Model.SymbolicExpressionTree);
361      }
362    }
363  }
364}
Note: See TracBrowser for help on using the repository browser.