source: branches/HeuristicLab.TreeSimplifier/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/InteractiveSymbolicDataAnalysisSolutionSimplifierView.cs @ 8916

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

#1763: Added HeuristicLab.Problems.DataAnalysis.Symbolic.Classification.Views and HeuristicLab.Problems.DataAnalysis.Symbolic.Regression.Views to the branch. Removed prune button, minor functionality improvements.

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