Free cookie consent management tool by TermsFeed Policy Generator

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

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

#1763: The simplifier view now uses a clone of the current model on which the actual simplification or tree pruning is applied. This fixes a crash when the user clicks on the Symbolic Regression Model while the background thread is pruning the tree (because it was trying to display the tree while it was being changed). Also took out the display part from the CalculateReplacementNodesAndNodeImpacts method and put it in a separate method.

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