Changeset 11086


Ignore:
Timestamp:
07/04/14 13:38:38 (5 years ago)
Author:
bburlacu
Message:

#1763: Introduced tree editing functionality directly into the trunk code, with minor changes: simplified drawing (eg when highlighting subtrees from the clipboard), fixed small bug in MinArity function. The branch can be deleted.

Location:
trunk/sources/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/sources/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/InteractiveSymbolicDataAnalysisSolutionSimplifierView.Designer.cs

    r10545 r11086  
    5555      this.btnSimplify = new System.Windows.Forms.Button();
    5656      this.treeStatusLabel = new System.Windows.Forms.Label();
    57       this.treeChart = new HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views.SymbolicExpressionTreeChart();
     57      this.treeChart = new HeuristicLab.Problems.DataAnalysis.Symbolic.Views.InteractiveSymbolicExpressionTreeChart();
    5858      this.grpViewHost = new System.Windows.Forms.GroupBox();
    5959      ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit();
     
    230230    #endregion
    231231
    232     private HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views.SymbolicExpressionTreeChart treeChart;
     232    private HeuristicLab.Problems.DataAnalysis.Symbolic.Views.InteractiveSymbolicExpressionTreeChart treeChart;
    233233    private System.Windows.Forms.SplitContainer splitContainer;
    234234    private HeuristicLab.MainForm.WindowsForms.ViewHost viewHost;
  • trunk/sources/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/InteractiveSymbolicDataAnalysisSolutionSimplifierView.cs

    r10799 r11086  
    11#region License Information
    22/* HeuristicLab
    3  * Copyright (C) 2002-2013 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
     3 * Copyright (C) 2002-2014 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
    44 *
    55 * This file is part of HeuristicLab.
     
    3333  public abstract partial class InteractiveSymbolicDataAnalysisSolutionSimplifierView : AsynchronousContentView {
    3434    private Dictionary<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode> foldedNodes;
     35    private Dictionary<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode> changedNodes;
    3536    private Dictionary<ISymbolicExpressionTreeNode, double> nodeImpacts;
     37
    3638    private enum TreeState { Valid, Invalid }
    37 
    38     public InteractiveSymbolicDataAnalysisSolutionSimplifierView() {
     39    private TreeState treeState;
     40
     41    protected InteractiveSymbolicDataAnalysisSolutionSimplifierView() {
    3942      InitializeComponent();
    4043      foldedNodes = new Dictionary<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>();
     44      changedNodes = new Dictionary<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>();
    4145      nodeImpacts = new Dictionary<ISymbolicExpressionTreeNode, double>();
    4246      this.Caption = "Interactive Solution Simplifier";
     47
     48      // initialize the tree modifier that will be used to perform edit operations over the tree
     49      treeChart.ModifyTree = Modify;
     50    }
     51
     52    /// <summary>
     53    /// Remove, Replace or Insert subtrees
     54    /// </summary>
     55    /// <param name="tree">The symbolic expression tree</param>
     56    /// <param name="parent">The insertion point (ie, the parent node who will receive a new child)</param>
     57    /// <param name="oldChild">The subtree to be replaced</param>
     58    /// <param name="newChild">The replacement subtree</param>
     59    /// <param name="removeSubtree">Flag used to indicate if whole subtrees should be removed (default behavior), or just the subtree root</param>
     60    private void Modify(ISymbolicExpressionTree tree, ISymbolicExpressionTreeNode parent,
     61      ISymbolicExpressionTreeNode oldChild, ISymbolicExpressionTreeNode newChild, bool removeSubtree = true) {
     62      if (oldChild == null && newChild == null)
     63        throw new ArgumentNullException("Cannot deduce operation type from the arguments. Please provide non null operands.");
     64      if (oldChild == null) {
     65        // insertion operation
     66        parent.AddSubtree(newChild);
     67        newChild.Parent = parent;
     68      } else if (newChild == null) {
     69        // removal operation
     70        parent.RemoveSubtree(parent.IndexOfSubtree(oldChild));
     71        if (!removeSubtree) {
     72          for (int i = oldChild.SubtreeCount - 1; i >= 0; --i) {
     73            var subtree = oldChild.GetSubtree(i);
     74            oldChild.RemoveSubtree(i);
     75            parent.AddSubtree(subtree);
     76          }
     77        }
     78      } else {
     79        // replacement operation
     80        var replacementIndex = parent.IndexOfSubtree(oldChild);
     81        parent.RemoveSubtree(replacementIndex);
     82        parent.InsertSubtree(replacementIndex, newChild);
     83        newChild.Parent = parent;
     84        if (changedNodes.ContainsKey(oldChild)) {
     85          changedNodes.Add(newChild, changedNodes[oldChild]); // so that on double click the original node is restored
     86          changedNodes.Remove(oldChild);
     87        } else {
     88          changedNodes.Add(newChild, oldChild);
     89        }
     90      }
     91      treeState = IsValid(tree) ? TreeState.Valid : TreeState.Invalid;
     92      switch (treeState) {
     93        case TreeState.Valid:
     94          this.grpViewHost.Enabled = true;
     95          UpdateModel(Content.Model.SymbolicExpressionTree);
     96          break;
     97        case TreeState.Invalid:
     98          this.grpViewHost.Enabled = false;
     99          break;
     100      }
     101    }
     102
     103    // the optimizer always assumes 2 children for multiplication and addition nodes
     104    // thus, we enforce that the tree stays valid so that the constant optimization won't throw an exception
     105    // by returning 2 as the minimum allowed arity for addition and multiplication symbols
     106    private readonly Func<ISymbol, int> GetMinArity = symbol => {
     107      var min = symbol.MinimumArity;
     108      if (symbol is Multiplication || symbol is Division) return Math.Max(2, min);
     109      return min;
     110    };
     111    private bool IsValid(ISymbolicExpressionTree tree) {
     112      treeChart.Tree = tree;
     113      treeChart.Repaint();
     114      bool valid = !tree.IterateNodesPostfix().Any(node => node.SubtreeCount < GetMinArity(node.Symbol) || node.SubtreeCount > node.Symbol.MaximumArity);
     115      if (valid) {
     116        btnOptimizeConstants.Enabled = true;
     117        btnSimplify.Enabled = true;
     118        treeStatusValue.Text = "Valid";
     119        treeStatusValue.ForeColor = Color.Green;
     120      } else {
     121        btnOptimizeConstants.Enabled = false;
     122        btnSimplify.Enabled = false;
     123        treeStatusValue.Text = "Invalid";
     124        treeStatusValue.ForeColor = Color.Red;
     125      }
     126      this.Refresh();
     127      return valid;
    43128    }
    44129
     
    83168
    84169      var impactAndReplacementValues = CalculateImpactAndReplacementValues(tree);
    85       nodeImpacts = impactAndReplacementValues.ToDictionary(x => x.Key, x => x.Value.Item1);
    86170      var replacementValues = impactAndReplacementValues.ToDictionary(x => x.Key, x => x.Value.Item2);
    87171      foreach (var pair in replacementValues.Where(pair => !(pair.Key is ConstantTreeNode))) {
    88172        foldedNodes[pair.Key] = MakeConstantTreeNode(pair.Value);
    89173      }
     174      nodeImpacts = impactAndReplacementValues.ToDictionary(x => x.Key, x => x.Value.Item1);
    90175      PaintNodeImpacts();
    91176    }
     
    104189
    105190    private void treeChart_SymbolicExpressionTreeNodeDoubleClicked(object sender, MouseEventArgs e) {
     191      if (treeState == TreeState.Invalid) return;
    106192      var visualNode = (VisualTreeNode<ISymbolicExpressionTreeNode>)sender;
    107       if (visualNode.Content == null) { throw new Exception("Visual node content cannot be null."); }
     193      if (visualNode.Content == null) { throw new Exception("VisualNode content cannot be null."); }
    108194      var symbExprTreeNode = (SymbolicExpressionTreeNode)visualNode.Content;
    109       if (!foldedNodes.ContainsKey(symbExprTreeNode)) return; // constant nodes cannot be folded
     195      var tree = Content.Model.SymbolicExpressionTree;
    110196      var parent = symbExprTreeNode.Parent;
    111197      int indexOfSubtree = parent.IndexOfSubtree(symbExprTreeNode);
    112       SwitchNodeWithReplacementNode(parent, indexOfSubtree);
    113       UpdateModel(Content.Model.SymbolicExpressionTree);
     198      if (changedNodes.ContainsKey(symbExprTreeNode)) {
     199        // undo node change
     200        parent.RemoveSubtree(indexOfSubtree);
     201        var originalNode = changedNodes[symbExprTreeNode];
     202        parent.InsertSubtree(indexOfSubtree, originalNode);
     203        changedNodes.Remove(symbExprTreeNode);
     204      } else if (foldedNodes.ContainsKey(symbExprTreeNode)) {
     205        // undo node folding
     206        SwitchNodeWithReplacementNode(parent, indexOfSubtree);
     207      }
     208      UpdateModel(tree);
    114209    }
    115210
     
    130225      double max = impacts.Max();
    131226      double min = impacts.Min();
    132       foreach (var treeNode in Content.Model.SymbolicExpressionTree.IterateNodesPostfix()) {
     227      foreach (ISymbolicExpressionTreeNode treeNode in Content.Model.SymbolicExpressionTree.IterateNodesPostfix()) {
    133228        VisualTreeNode<ISymbolicExpressionTreeNode> visualTree = treeChart.GetVisualSymbolicExpressionTreeNode(treeNode);
    134229
    135230        if (!(treeNode is ConstantTreeNode) && nodeImpacts.ContainsKey(treeNode)) {
    136           visualTree.ToolTip = visualTree.Content.ToString(); // to avoid duplicate tooltips
     231          visualTree.ToolTip = visualTree.Content.ToString();
    137232          double impact = nodeImpacts[treeNode];
    138233
     
    156251        }
    157252        if (visualTree != null)
    158           if (treeNode is ConstantTreeNode && foldedNodes.ContainsKey(treeNode)) {
     253          if (changedNodes.ContainsKey(treeNode)) {
     254            visualTree.LineColor = Color.DodgerBlue;
     255          } else if (treeNode is ConstantTreeNode && foldedNodes.ContainsKey(treeNode)) {
    159256            visualTree.LineColor = Color.DarkOrange;
    160257          }
  • trunk/sources/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/InteractiveSymbolicExpressionTreeChart.Designer.cs

    r9456 r11086  
    5656      this.toolStripSeparator = new System.Windows.Forms.ToolStripSeparator();
    5757      this.copyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
     58      this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
     59      this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
    5860      this.SuspendLayout();
    5961      //
     
    6163      //
    6264      this.insertNodeToolStripMenuItem.Name = "insertNodeToolStripMenuItem";
    63       this.insertNodeToolStripMenuItem.Size = new System.Drawing.Size(147, 22);
     65      this.insertNodeToolStripMenuItem.Size = new System.Drawing.Size(165, 22);
    6466      this.insertNodeToolStripMenuItem.Text = "Insert Node";
    6567      this.insertNodeToolStripMenuItem.Click += new System.EventHandler(this.insertNodeToolStripMenuItem_Click);
     
    6870      //
    6971      this.changeNodeToolStripMenuItem.Name = "changeNodeToolStripMenuItem";
    70       this.changeNodeToolStripMenuItem.Size = new System.Drawing.Size(147, 22);
     72      this.changeNodeToolStripMenuItem.Size = new System.Drawing.Size(165, 22);
    7173      this.changeNodeToolStripMenuItem.Text = "Change Node";
    7274      this.changeNodeToolStripMenuItem.Click += new System.EventHandler(this.changeNodeToolStripMenuItem_Click);
     
    7577      //
    7678      this.cutToolStripMenuItem.Name = "cutToolStripMenuItem";
    77       this.cutToolStripMenuItem.Size = new System.Drawing.Size(147, 22);
    78       this.cutToolStripMenuItem.Text = "Cut";
     79      this.cutToolStripMenuItem.Size = new System.Drawing.Size(165, 22);
     80      this.cutToolStripMenuItem.Text = "Cut Subtree";
    7981      this.cutToolStripMenuItem.Click += new System.EventHandler(this.cutToolStripMenuItem_Click);
    8082      //
     
    8587            this.removeSubtreeToolStripMenuItem});
    8688      this.removeToolStripMenuItem.Name = "removeToolStripMenuItem";
    87       this.removeToolStripMenuItem.Size = new System.Drawing.Size(147, 22);
     89      this.removeToolStripMenuItem.Size = new System.Drawing.Size(165, 22);
    8890      this.removeToolStripMenuItem.Text = "Remove";
    8991      this.removeToolStripMenuItem.Click += new System.EventHandler(this.removeSubtreeToolStripMenuItem_Click);
     
    106108      //
    107109      this.pasteToolStripMenuItem.Name = "pasteToolStripMenuItem";
    108       this.pasteToolStripMenuItem.Size = new System.Drawing.Size(147, 22);
    109       this.pasteToolStripMenuItem.Text = "Paste";
     110      this.pasteToolStripMenuItem.Size = new System.Drawing.Size(165, 22);
     111      this.pasteToolStripMenuItem.Text = "Paste Subtree";
    110112      this.pasteToolStripMenuItem.Click += new System.EventHandler(this.pasteToolStripMenuItem_Clicked);
    111113      //
     
    118120      //
    119121      this.copyToolStripMenuItem.Name = "copyToolStripMenuItem";
    120       this.copyToolStripMenuItem.Size = new System.Drawing.Size(147, 22);
    121       this.copyToolStripMenuItem.Text = "Copy";
     122      this.copyToolStripMenuItem.Size = new System.Drawing.Size(165, 22);
     123      this.copyToolStripMenuItem.Text = "Copy Subtree";
    122124      this.copyToolStripMenuItem.Click += new System.EventHandler(this.copyToolStripMenuItem_Click);
     125      //
     126      // toolStripSeparator1
     127      //
     128      this.toolStripSeparator1.Name = "toolStripSeparator1";
     129      this.toolStripSeparator1.Size = new System.Drawing.Size(162, 6);
     130      //
     131      // toolStripSeparator2
     132      //
     133      this.toolStripSeparator2.Name = "toolStripSeparator2";
     134      this.toolStripSeparator2.Size = new System.Drawing.Size(162, 6);
    123135      //
    124136      // contextMenuStrip
    125137      //
    126138      this.contextMenuStrip.Opened += this.contextMenuStrip_Opened;
    127       this.contextMenuStrip.Items.AddRange(new ToolStripItem[] { insertNodeToolStripMenuItem,
     139      this.contextMenuStrip.Items.AddRange(new ToolStripItem[] { toolStripSeparator1,
     140                                                                 insertNodeToolStripMenuItem,
     141                                                                 removeToolStripMenuItem,
    128142                                                                 changeNodeToolStripMenuItem,
     143                                                                 toolStripSeparator2,
    129144                                                                 copyToolStripMenuItem,
    130145                                                                 cutToolStripMenuItem,
    131                                                                  removeToolStripMenuItem,
    132146                                                                 pasteToolStripMenuItem });
    133147      //
     
    139153      this.Name = "InteractiveSymbolicExpressionTreeChart";
    140154      this.ResumeLayout(false);
    141 
    142155    }
    143156
     
    154167    private ToolStripSeparator toolStripSeparator;
    155168    private ToolStripMenuItem copyToolStripMenuItem;
     169    private ToolStripSeparator toolStripSeparator1;
     170    private ToolStripSeparator toolStripSeparator2;
    156171  }
    157172}
  • trunk/sources/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/InteractiveSymbolicExpressionTreeChart.cs

    r10561 r11086  
    8383    protected override void OnSymbolicExpressionTreeNodeClicked(object sender, MouseEventArgs e) {
    8484      currSelected = (VisualTreeNode<ISymbolicExpressionTreeNode>)sender; ;
    85       if (currSelected != null) {
    86         currSelected.LineColor = Color.FromArgb(130, currSelected.LineColor);
    87         RepaintNode(currSelected);
    88       }
    8985      base.OnSymbolicExpressionTreeNodeClicked(sender, e);
    90     }
    91 
    92     protected override void SymbolicExpressionTreeChart_MouseClick(object sender, MouseEventArgs e) {
    93       if (currSelected != null) {
    94         currSelected.LineColor = Color.FromArgb(255, currSelected.LineColor);
    95         RepaintNode(currSelected);
    96       }
    97       base.SymbolicExpressionTreeChart_MouseClick(sender, e);
    9886    }
    9987
     
    10896
    10997      using (var dialog = new InsertNodeDialog()) {
    110         dialog.SetAllowedSymbols(parent.Grammar.AllowedSymbols.Where(s => s.Enabled && s.InitialFrequency > 0.0 && !(s is ProgramRootSymbol || s is StartSymbol || s is Defun)));
     98        dialog.SetAllowedSymbols(parent.Grammar.Symbols.Where(s => !(s is ProgramRootSymbol || s is StartSymbol || s is Defun))); // allow everything
    11199        dialog.ShowDialog(this);
    112100        if (dialog.DialogResult != DialogResult.OK) return;
     
    172160        foreach (var subtree in tempNode.IterateNodesPostfix()) {
    173161          var visualNode = GetVisualSymbolicExpressionTreeNode(subtree);
    174           visualNode.LineColor = Color.FromArgb(255, visualNode.LineColor); // reset the alpha value to 255
    175           visualNode.TextColor = Color.FromArgb(255, visualNode.TextColor);
     162          visualNode.LineColor = Color.Black;
     163          visualNode.TextColor = Color.Black;
    176164          if (subtree.Parent != null) {
    177165            var visualLine = GetVisualSymbolicExpressionTreeNodeConnection(subtree.Parent, subtree);
    178             visualLine.LineColor = Color.FromArgb(255, visualLine.LineColor);
     166            visualLine.LineColor = Color.Black;
    179167          }
    180168        }
     
    183171      foreach (var node in tempNode.IterateNodesPostfix()) {
    184172        var visualNode = GetVisualSymbolicExpressionTreeNode(node);
    185         visualNode.LineColor = Color.FromArgb(100, visualNode.LineColor);
    186         visualNode.TextColor = Color.FromArgb(100, visualNode.TextColor);
     173        visualNode.LineColor = Color.LightGray;
     174        visualNode.TextColor = Color.LightGray;
    187175        foreach (var subtree in node.Subtrees) {
    188176          var visualLine = GetVisualSymbolicExpressionTreeNodeConnection(node, subtree);
    189           visualLine.LineColor = Color.FromArgb(100, visualLine.LineColor);
    190         }
    191       }
    192       currSelected = null;
    193       Repaint();
     177          visualLine.LineColor = Color.LightGray;
     178        }
     179      }
     180      currSelected = null;
     181      RepaintNodes(); // no need to redo the layout and repaint everything since this operation does not change the tree
    194182    }
    195183    private void removeNodeToolStripMenuItem_Click(object sender, EventArgs e) {
Note: See TracChangeset for help on using the changeset viewer.