#region License Information /* HeuristicLab * Copyright (C) 2002-2012 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Linq; using System.Windows.Forms; using HeuristicLab.Common; using HeuristicLab.Data; using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding; using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views; using HeuristicLab.MainForm.WindowsForms; namespace HeuristicLab.Problems.DataAnalysis.Symbolic.Views { public abstract partial class InteractiveSymbolicDataAnalysisSolutionSimplifierView : AsynchronousContentView { private Dictionary replacementNodes; private Dictionary nodeImpacts; private Dictionary originalValues; private Dictionary originalVariableNames; private bool updateInProgress = false; private ISymbolicExpressionTree model; public InteractiveSymbolicDataAnalysisSolutionSimplifierView() { InitializeComponent(); this.replacementNodes = new Dictionary(); this.nodeImpacts = new Dictionary(); this.originalValues = new Dictionary(); this.originalVariableNames = new Dictionary(); this.Caption = "Interactive Solution Simplifier"; } public new ISymbolicDataAnalysisSolution Content { get { return (ISymbolicDataAnalysisSolution)base.Content; } set { base.Content = value; } } protected override void RegisterContentEvents() { base.RegisterContentEvents(); Content.ModelChanged += new EventHandler(Content_ModelChanged); Content.ProblemDataChanged += new EventHandler(Content_ProblemDataChanged); } protected override void DeregisterContentEvents() { base.DeregisterContentEvents(); Content.ModelChanged -= new EventHandler(Content_ModelChanged); Content.ProblemDataChanged -= new EventHandler(Content_ProblemDataChanged); } private void Content_ModelChanged(object sender, EventArgs e) { OnModelChanged(); } private void Content_ProblemDataChanged(object sender, EventArgs e) { OnProblemDataChanged(); } protected virtual void OnModelChanged() { model = (model == null) ? (ISymbolicExpressionTree)Content.Model.SymbolicExpressionTree.Clone() : Content.Model.SymbolicExpressionTree; this.CalculateReplacementNodesAndNodeImpacts(model); this.PaintModel(); } protected virtual void OnProblemDataChanged() { this.CalculateReplacementNodesAndNodeImpacts(); this.PaintModel(); } protected override void OnContentChanged() { model = (model == null) ? (ISymbolicExpressionTree)Content.Model.SymbolicExpressionTree.Clone() : Content.Model.SymbolicExpressionTree; base.OnContentChanged(); this.CalculateReplacementNodesAndNodeImpacts(model); this.PaintModel(); this.viewHost.Content = this.Content; } private void CalculateReplacementNodesAndNodeImpacts() { if (Content == null || Content.Model == null || Content.ProblemData == null) return; var tree = model; var replacementValues = CalculateReplacementValues(tree); foreach (var pair in replacementValues.Where(pair => !(pair.Key is ConstantTreeNode))) { replacementNodes[pair.Key] = MakeConstantTreeNode(pair.Value); } nodeImpacts = CalculateImpactValues(tree); if (!updateInProgress) { // automatically fold all branches with impact = 0 List nodeList = tree.Root.GetSubtree(0).IterateNodesPrefix().ToList(); foreach (var parent in nodeList) { for (int subTreeIndex = 0; subTreeIndex < parent.SubtreeCount; subTreeIndex++) { var child = parent.GetSubtree(subTreeIndex); if (!(child.Symbol is Constant) && nodeImpacts[child].IsAlmost(0.0)) { SwitchNodeWithReplacementNode(parent, subTreeIndex); } } } } PaintModel(); } private void CalculateReplacementNodesAndNodeImpacts(ISymbolicExpressionTree tree) { var replacementValues = CalculateReplacementValues(tree); foreach (var pair in replacementValues.Where(pair => !(pair.Key is ConstantTreeNode))) { replacementNodes[pair.Key] = MakeConstantTreeNode(pair.Value); } nodeImpacts = CalculateImpactValues(tree); if (!updateInProgress) { // automatically fold all branches with impact = 0 List nodeList = tree.Root.GetSubtree(0).IterateNodesPrefix().ToList(); foreach (var parent in nodeList) { for (int subTreeIndex = 0; subTreeIndex < parent.SubtreeCount; subTreeIndex++) { var child = parent.GetSubtree(subTreeIndex); if (!(child.Symbol is Constant) && nodeImpacts[child].IsAlmost(0.0)) { SwitchNodeWithReplacementNode(parent, subTreeIndex); } } } } PaintModel(); } private void PaintModel() { // show only interesting part of solution this.treeChart.Tree = model.Root.SubtreeCount > 1 ? new SymbolicExpressionTree(model.Root) : new SymbolicExpressionTree(model.Root.GetSubtree(0).GetSubtree(0)); this.PaintNodeImpacts(); } protected abstract Dictionary CalculateReplacementValues(ISymbolicExpressionTree tree); protected abstract Dictionary CalculateImpactValues(ISymbolicExpressionTree tree); protected abstract void UpdateModel(ISymbolicExpressionTree tree); private ConstantTreeNode MakeConstantTreeNode(double value) { Constant constant = new Constant(); constant.MinValue = value - 1; constant.MaxValue = value + 1; ConstantTreeNode constantTreeNode = (ConstantTreeNode)constant.CreateTreeNode(); constantTreeNode.Value = value; return constantTreeNode; } private void treeChart_SymbolicExpressionTreeNodeDoubleClicked(object sender, MouseEventArgs e) { var visualNode = (VisualSymbolicExpressionTreeNode)sender; var symbExprTreeNode = (SymbolicExpressionTreeNode)visualNode.SymbolicExpressionTreeNode; if (symbExprTreeNode == null) return; var tree = model; // check if the node value/weight has been altered // if so, the first double click will return the node to its original value/weight/variable name // the next double click will replace the ConstantNode with the original SymbolicExpressionTreeNode if (originalVariableNames.ContainsKey(symbExprTreeNode)) { var variable = (VariableTreeNode)symbExprTreeNode; variable.VariableName = originalVariableNames[symbExprTreeNode]; originalVariableNames.Remove(variable); updateInProgress = true; UpdateModel(tree); updateInProgress = false; return; } if (originalValues.ContainsKey(symbExprTreeNode)) { double value = originalValues[symbExprTreeNode]; if (symbExprTreeNode.Symbol is Constant) ((ConstantTreeNode)symbExprTreeNode).Value = value; else if (symbExprTreeNode.Symbol is Variable) ((VariableTreeNode)symbExprTreeNode).Weight = value; originalValues.Remove(symbExprTreeNode); updateInProgress = true; UpdateModel(tree); updateInProgress = false; return; } foreach (SymbolicExpressionTreeNode treeNode in tree.IterateNodesPostfix()) { for (int i = 0; i < treeNode.SubtreeCount; i++) { ISymbolicExpressionTreeNode subTree = treeNode.GetSubtree(i); // only allow to replace nodes for which a replacement value is known (replacement value for ADF nodes are not available) if (subTree == symbExprTreeNode && replacementNodes.ContainsKey(subTree)) { SwitchNodeWithReplacementNode(treeNode, i); // show only interesting part of solution treeChart.Tree = tree.Root.SubtreeCount > 1 ? new SymbolicExpressionTree(tree.Root) : new SymbolicExpressionTree(tree.Root.GetSubtree(0).GetSubtree(0)); updateInProgress = true; UpdateModel(tree); updateInProgress = false; return; // break all loops } } } } private void treeChart_SymbolicExpressionTreeNodeClicked(object sender, MouseEventArgs e) { // do stuff treeChart.Repaint(); } private void treeChart_OnInsertNodeContextMenuItemClicked(object sender, EventArgs e) { // display the insert node dialog } private void treeChart_SymbolicExpressionTreeChanged(object sender, EventArgs e) { CalculateReplacementNodesAndNodeImpacts(treeChart.Tree); PaintModel(); } private void treeChart_SymbolicExpressionTreeNodeChanged(object sender, EventArgs e) { var dialog = (ValueChangeDialog)sender; bool flag1 = false, flag2 = false; if (dialog.Content is VariableTreeNode) { var variable = (VariableTreeNode)dialog.Content; var weight = double.Parse(dialog.NewValueTextBox.Text); var name = (string)dialog.VariableNameComboBox.SelectedItem; if (!variable.Weight.Equals(weight)) { flag1 = true; originalValues[variable] = variable.Weight; variable.Weight = weight; } if (!variable.VariableName.Equals(name)) { flag2 = true; originalVariableNames[variable] = variable.VariableName; variable.VariableName = name; } } else if (dialog.Content is ConstantTreeNode) { var constant = (ConstantTreeNode)dialog.Content; var value = double.Parse(dialog.NewValueTextBox.Text); if (!constant.Value.Equals(value)) { flag1 = true; originalValues[constant] = constant.Value; constant.Value = value; } } if (flag1 || flag2) { CalculateReplacementNodesAndNodeImpacts(); PaintModel(); } } private void treeChart_SymbolicExpressionTreeNodeInserted(object sender, EventArgs e) { } private void SwitchNodeWithReplacementNode(ISymbolicExpressionTreeNode parent, int subTreeIndex) { ISymbolicExpressionTreeNode subTree = parent.GetSubtree(subTreeIndex); parent.RemoveSubtree(subTreeIndex); if (replacementNodes.ContainsKey(subTree)) { var replacementNode = replacementNodes[subTree]; parent.InsertSubtree(subTreeIndex, replacementNode); // exchange key and value replacementNodes.Remove(subTree); replacementNodes.Add(replacementNode, subTree); } } private void PaintNodeImpacts() { var impacts = nodeImpacts.Values; double max = impacts.Max(); double min = impacts.Min(); foreach (ISymbolicExpressionTreeNode treeNode in model.IterateNodesPostfix()) { VisualSymbolicExpressionTreeNode visualTree = treeChart.GetVisualSymbolicExpressionTreeNode(treeNode); bool flag1 = replacementNodes.ContainsKey(treeNode); bool flag2 = originalValues.ContainsKey(treeNode); bool flag3 = treeNode is ConstantTreeNode; if (flag2) // constant or variable node was changed visualTree.ToolTip += Environment.NewLine + "Original value: " + originalValues[treeNode]; else if (flag1 && flag3) // symbol node was folded to a constant visualTree.ToolTip += Environment.NewLine + "Original node: " + replacementNodes[treeNode]; if (!(treeNode is ConstantTreeNode) && nodeImpacts.ContainsKey(treeNode)) { double impact = nodeImpacts[treeNode]; // impact = 0 if no change // impact < 0 if new solution is better // impact > 0 if new solution is worse if (impact < 0.0) { // min is guaranteed to be < 0 visualTree.FillColor = Color.FromArgb((int)(impact / min * 255), Color.Red); } else if (impact.IsAlmost(0.0)) { visualTree.FillColor = Color.White; } else { // max is guaranteed to be > 0 visualTree.FillColor = Color.FromArgb((int)(impact / max * 255), Color.Green); } visualTree.ToolTip += Environment.NewLine + "Node impact: " + impact; var constantReplacementNode = replacementNodes[treeNode] as ConstantTreeNode; if (constantReplacementNode != null) { visualTree.ToolTip += Environment.NewLine + "Replacement value: " + constantReplacementNode.Value; } } } this.PaintCollapsedNodes(); this.treeChart.Repaint(); } private void PaintCollapsedNodes() { foreach (ISymbolicExpressionTreeNode treeNode in model.IterateNodesPostfix()) { bool flag1 = replacementNodes.ContainsKey(treeNode); bool flag2 = originalValues.ContainsKey(treeNode); if (flag1 && treeNode is ConstantTreeNode) { this.treeChart.GetVisualSymbolicExpressionTreeNode(treeNode).LineColor = flag2 ? Color.DarkViolet : Color.DarkOrange; } else if (flag2) { this.treeChart.GetVisualSymbolicExpressionTreeNode(treeNode).LineColor = Color.DodgerBlue; } } } private void btnSimplify_Click(object sender, EventArgs e) { var simplifier = new SymbolicDataAnalysisExpressionTreeSimplifier(); var simplifiedExpressionTree = simplifier.Simplify(model); UpdateModel(simplifiedExpressionTree); } protected abstract void btnOptimizeConstants_Click(object sender, EventArgs e); private void btnPrune_Click(object sender, EventArgs e) { btnPrune.Enabled = false; backgroundWorker1.RunWorkerAsync(); } private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { var worker = sender as BackgroundWorker; PruneTree(worker); } private void PruneTree(BackgroundWorker worker) { var tree = model; // get all tree nodes starting from depth 2 (below the root and start nodes) foreach (var node in GetNodesAtDepth(tree, new IntRange(2, tree.Depth))) { if (worker.CancellationPending) break; // pruning cancelled if (!(node.Symbol is Constant) && nodeImpacts.ContainsKey(node) && nodeImpacts[node] < 0.001) { SwitchNodeWithReplacementNode(node.Parent, node.Parent.IndexOfSubtree(node)); nodeImpacts = CalculateImpactValues(tree); // we do not refresh the replacementValues because they won't change when folding nodes with such low impacts (<0.001) } } } private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Cancelled) { // The user canceled the operation. } else if (e.Error != null) { // There was an error during the operation. // Error-handling } else { // The operation completed normally. We can update the model UpdateModel(model); } btnPrune.Enabled = true; } #region helpers private static IEnumerable GetNodesAtDepth(ISymbolicExpressionTree tree, IntRange depthRange) { var treeDepth = tree.Root.GetDepth(); return from node in tree.Root.IterateNodesPostfix() let depth = treeDepth - node.GetDepth() where depthRange.Start <= depth where depth <= depthRange.End select node; } #endregion } }