#region License Information
/* HeuristicLab
* Copyright (C) 2002-2014 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.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using HeuristicLab.Core.Views;
using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views;
using HeuristicLab.EvolutionTracking;
using HeuristicLab.EvolutionTracking.Views;
using HeuristicLab.MainForm;
using FragmentGraph = HeuristicLab.EvolutionTracking.IGenealogyGraph>;
using FragmentNode = HeuristicLab.EvolutionTracking.GenealogyGraphNode>;
namespace HeuristicLab.Problems.DataAnalysis.Symbolic.Views {
[View("SymboldDataAnalysisGenealogyView")]
[Content(typeof(IGenealogyGraph), IsDefaultView = true)]
public partial class SymboldDataAnalysisGenealogyView : ItemView {
private readonly ISymbolicExpressionTreeNodeSimilarityComparer comparer;
public SymboldDataAnalysisGenealogyView() {
InitializeComponent();
comparer = new SymbolicExpressionTreeNodeSimilarityComparer();
}
public new IGenealogyGraph Content {
get { return (IGenealogyGraph)base.Content; }
set { base.Content = value; }
}
#region event handlers
protected override void OnContentChanged() {
base.OnContentChanged();
if (Content != null) {
genealogyGraphChart.GenealogyGraph = Content;
}
}
#endregion
protected override void RegisterContentEvents() {
genealogyGraphChart.GenealogyGraphNodeClicked += graphChart_GenealogyGraphNodeClicked;
symbolicExpressionTreeChart.SymbolicExpressionTreeNodeClicked += treeChart_SymbolicExpressionNodeClicked;
base.RegisterContentEvents();
}
protected override void DeregisterContentEvents() {
base.DeregisterContentEvents();
genealogyGraphChart.GenealogyGraphNodeClicked -= graphChart_GenealogyGraphNodeClicked;
symbolicExpressionTreeChart.SymbolicExpressionTreeNodeClicked -= treeChart_SymbolicExpressionNodeClicked;
}
public void graphChart_GenealogyGraphNodeClicked(object sender, MouseEventArgs args) {
var visualNode = (VisualGenealogyGraphNode)sender;
var graphNode = (IGenealogyGraphNode)visualNode.Data;
var tree = graphNode.Content;
symbolicExpressionTreeChart.Tree = tree;
if (graphNode.InArcs != null) {
var fragment = (IFragment)graphNode.InArcs.Last().Data;
treeChart_HighlightSubtree(fragment.Root);
}
}
public void treeChart_SymbolicExpressionNodeClicked(object sender, MouseEventArgs args) {
var visualNode = (VisualTreeNode)sender;
var subtree = visualNode.Content;
// highlight the selected subtree inside the displayed tree on the right hand side
treeChart_ClearColors();
treeChart_HighlightSubtree(subtree);
bool trace = genealogyGraphChart.TraceFragments; // check whether the mode is 'trace' or 'match'
if (trace) {
// perform fragment tracing
var fragmentGraph = TraceSubtree(subtree);
MainFormManager.MainForm.ShowContent(fragmentGraph);
// var fragmentGraphView = MainFormManager.MainForm.ShowContent(fragmentGraph);
// fragmentGraphView.Content = fragmentGraph;
// fragmentGraphView.Show();
// open a FragmentGraphView displaying this fragmentGraph
} else {
// perform matching like it was done before
// currently there is no possibility to specify the subtree matching criteria
var trees = Content.Nodes.Select(x => (ISymbolicExpressionTree)x.Content);
var matchingTrees = trees.Where(x => x.Root.ContainsSubtree(subtree, comparer));
var matchingVertices = matchingTrees.SelectMany(x => Content[x]).Cast>();
graphChart_highlightMatchingVertices(matchingVertices);
}
}
private void graphChart_highlightMatchingVertices(IEnumerable vertices) {
genealogyGraphChart.Chart.UpdateEnabled = false;
genealogyGraphChart.ClearPrimitives();
genealogyGraphChart.HighlightNodes(vertices);
genealogyGraphChart.Chart.UpdateEnabled = true;
genealogyGraphChart.Chart.EnforceUpdate();
}
private void treeChart_ClearColors() {
foreach (var node in symbolicExpressionTreeChart.Tree.IterateNodesPrefix()) {
var visualNode = symbolicExpressionTreeChart.GetVisualSymbolicExpressionTreeNode(node);
if (visualNode != null) {
visualNode.LineColor = Color.Black;
visualNode.FillColor = Color.Transparent;
}
}
symbolicExpressionTreeChart.RepaintNodes();
}
private void treeChart_HighlightSubtree(ISymbolicExpressionTreeNode subtree) {
foreach (var s in subtree.IterateNodesPrefix()) {
var visualNode = symbolicExpressionTreeChart.GetVisualSymbolicExpressionTreeNode(s);
visualNode.LineColor = Color.RoyalBlue;
visualNode.FillColor = Color.LightBlue;
foreach (var c in s.Subtrees) {
var visualArc = symbolicExpressionTreeChart.GetVisualSymbolicExpressionTreeNodeConnection(s, c);
visualArc.LineColor = Color.RoyalBlue;
}
}
symbolicExpressionTreeChart.RepaintNodes();
}
#region events for configuring the behavior of the genealogy chart (trace/match, simple lineages, etc)
private void trace_checkBox_CheckedChanged(object sender, System.EventArgs e) {
genealogyGraphChart.TraceFragments = trace_checkBox.Checked;
}
private void simpleLineages_checkBox_CheckedChanged(object sender, System.EventArgs e) {
genealogyGraphChart.SimpleLineages = simpleLineages_checkBox.Checked;
}
private void lockGraph_checkBox_CheckedChanged(object sender, System.EventArgs e) {
genealogyGraphChart.LockGenealogy = lockGraph_checkBox.Checked;
}
#endregion
#region fragment tracing
#region helper methods to shorten the code
#endregion
private FragmentGraph TraceSubtree(ISymbolicExpressionTreeNode subtree) {
var graphNode = (IGenealogyGraphNode)genealogyGraphChart.SelectedGraphNode;
var graph = new GenealogyGraph>();
Trace(graphNode, subtree, graph);
return graph;
}
private void Trace(IGenealogyGraphNode graphNode, ISymbolicExpressionTreeNode subtree, FragmentGraph fragmentGraph, FragmentNode parentNode = null) {
while (true) {
if (!graphNode.InArcs.Any()) return;
var parentVertices = graphNode.Parents.ToList();
// the subtree must belong to the currently displayed tree which in turn must belong to the currently selected graph node
var tree = graphNode.Content;
var subtreeIndex = tree.IndexOf(subtree);
var subtreeLength = subtree.GetLength();
var fragment = (IFragment)graphNode.InArcs.Last().Data;
if (fragment == null) return;
var fragmentLength = fragment.Root.GetLength();
FragmentNode node = new FragmentNode { Content = new Fragment { Root = subtree }, Rank = graphNode.Rank };
if (parentNode != null) {
AddChild(parentNode, node);
}
fragmentGraph.AddVertex(node);
// if the selected subtree is the actual fragment
if (fragment.Index == subtreeIndex) {
if (fragmentLength != subtreeLength) throw new Exception("Fragment and subtree lengths should be the same!");
graphNode = parentVertices.Last();
tree = graphNode.Content;
subtree = tree.NodeAt(fragment.OldIndex);
parentNode = node;
continue;
}
// if the fragment contains the selected subtree => track fragment, then track subtree
if (fragment.Index < subtreeIndex && subtreeIndex < fragment.Index + fragmentLength) {
if (subtreeLength >= fragmentLength) throw new Exception("Fragment contains subtree, so subtree length should be less than the fragment length.");
graphNode = parentVertices.Last();
tree = graphNode.Content;
var i = fragment.Root.IndexOf(subtree); // get the index of the selected subtree, relative to the fragment root
subtree = tree.NodeAt(fragment.OldIndex + i);
parentNode = node;
continue;
}
// if the selected subtree contains the fragment => track fragment and subtree
if (subtreeIndex < fragment.Index && fragment.Index < subtreeIndex + subtreeLength) {
if (fragmentLength >= subtreeLength) throw new Exception("Subtree contains fragment, so fragment length should be less than the subtree length.");
graphNode = parentVertices[0];
tree = graphNode.Content;
subtree = tree.NodeAt(subtreeIndex);
// track subtree
Trace(graphNode, subtree, fragmentGraph, node);
// track fragment
if (parentVertices.Count > 1) {
graphNode = parentVertices[1];
tree = graphNode.Content;
subtree = tree.NodeAt(fragment.OldIndex);
parentNode = node;
continue;
}
} else {
// fragment and subtree are completely distinct => we only track the subtree
graphNode = parentVertices[0];
tree = graphNode.Content;
subtree = tree.NodeAt(subtreeIndex);
parentNode = node;
continue;
}
break;
}
}
private void AddChild(FragmentNode parent, FragmentNode child) {
child.Rank = parent.Rank - 1;
parent.AddForwardArc(child);
child.AddReverseArc(parent);
}
#endregion
}
internal static class Util {
private static string ViewAsText(this ISymbolicExpressionTreeNode root) {
var writer = new StringWriter();
SymbolicExpressionTreeHierarchicalFormatter.RenderNode(writer, root, string.Empty);
return writer.ToString();
}
internal static ISymbolicExpressionTreeNode NodeAt(this ISymbolicExpressionTree tree, int position) {
return NodeAt(tree.Root, position);
}
internal static ISymbolicExpressionTreeNode NodeAt(this ISymbolicExpressionTreeNode root, int position) {
return root.IterateNodesPrefix().ElementAt(position);
}
internal static int IndexOf(this ISymbolicExpressionTree tree, ISymbolicExpressionTreeNode node) {
return IndexOf(tree.Root, node);
}
internal static int IndexOf(this ISymbolicExpressionTreeNode root, ISymbolicExpressionTreeNode node) {
return root.IterateNodesPrefix().ToList().IndexOf(node); // not too worried about efficiency here
}
}
}