#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
}
}