#region License Information /* HeuristicLab * Copyright (C) 2002-2016 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.Linq; using System.Windows.Forms; using HeuristicLab.Common; using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding; using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views; using HeuristicLab.EvolutionTracking; using HeuristicLab.EvolutionTracking.Views; using HeuristicLab.MainForm; using HeuristicLab.Problems.DataAnalysis.Symbolic; namespace HeuristicLab.Problems.DataAnalysis.Symbolic.Views { [View("SymbolicDataAnalysisGenealogyGraphView")] [Content(typeof(IGenealogyGraph), IsDefaultView = true)] public partial class SymbolicDataAnalysisGenealogyGraphView : SymbolicDataAnalysisGenealogyGraphViewDesignable { private readonly ISymbolicExpressionTreeNodeEqualityComparer comparer; private TraceData lastTd; private ISymbolicExpressionTreeNode clonedSubtree; private Dictionary clonedMap; private ISymbolicExpressionTreeNode selectedSubtree; private ISymbolicExpressionTreeNode SelectedSubtree { get { return selectedSubtree; } set { if (value == null) return; selectedSubtree = value; // the code below enables a "shadow" copy of the selected subtree which is used for matching against the graph clonedSubtree = (ISymbolicExpressionTreeNode)selectedSubtree.Clone(); clonedMap = new Dictionary(); var e1 = selectedSubtree.IterateNodesPrefix().GetEnumerator(); var e2 = clonedSubtree.IterateNodesPrefix().GetEnumerator(); while (e1.MoveNext() && e2.MoveNext()) { clonedMap.Add(e1.Current, e2.Current); } } } // we use the symbolic expression tree chart to call the drawing routines // and highlight relevant tree parts public SymbolicExpressionTreeChart SymbolicExpressionTreeChart { get { var view = (GraphicalSymbolicExpressionTreeView)base.viewHost.ActiveView; return view == null ? null : view.SymbolicExpressionTreeChart; } } public SymbolicDataAnalysisGenealogyGraphView() { InitializeComponent(); comparer = new SymbolicExpressionTreeNodeEqualityComparer(); viewHost.ViewType = typeof(GraphicalSymbolicExpressionTreeView); } #region event handlers protected override void OnContentChanged() { base.OnContentChanged(); if (Content != null && Content != genealogyGraphChart.GenealogyGraph) { genealogyGraphChart.GenealogyGraph = Content; } } protected override void RegisterContentEvents() { base.RegisterContentEvents(); if (SymbolicExpressionTreeChart != null) SymbolicExpressionTreeChart.SymbolicExpressionTreeNodeClicked += treeChart_SymbolicExpressionTreeNodeClicked; } protected override void DeregisterContentEvents() { if (SymbolicExpressionTreeChart != null) SymbolicExpressionTreeChart.SymbolicExpressionTreeNodeClicked -= treeChart_SymbolicExpressionTreeNodeClicked; base.DeregisterContentEvents(); } public override void graphChart_GenealogyGraphNodeClicked(object sender, MouseEventArgs args) { base.graphChart_GenealogyGraphNodeClicked(sender, args); var visualNode = (VisualGenealogyGraphNode)sender; var graphNode = (IGenealogyGraphNode)visualNode.Data; lastTd = null; selectedNodeStatusStrip.Text = string.Format("Quality: {0:0.00}, Rank: {1:0.0}, Degree: {2}/{3}, Weight: {4:0.00}", graphNode.Quality, graphNode.Rank, graphNode.InDegree, graphNode.OutDegree, graphNode.Weight); // update the righthand side tree view when another genealogy graph node is selected UpdateTreeChart(graphNode); if (!graphNode.InArcs.Any()) return; // node has no ancestors, nothing to do if (createNewViewCheckBox.Checked) { // get the ancestors into a new view var cloner = new Cloner(); // clone arcs and vertices and use them to create a new graph // that will include just the individual and its ancestors var graph = new GenealogyGraph(); var ancestors = new[] { graphNode }.Concat(graphNode.Ancestors).ToList(); graph.AddVertices(ancestors.Select(cloner.Clone)); graph.AddArcs(ancestors.SelectMany(x => x.InArcs).Select(cloner.Clone)); MainFormManager.MainForm.ShowContent(graph); } } private void UpdateTreeChart(IGenealogyGraphNode graphNode) { SymbolicExpressionTreeChart.Tree = graphNode.Data; var nodes = graphNode.Data.IterateNodesPrefix().ToList(); // calculate max only in the current generation var max = Content.Vertices.Max(x => x.Data.IterateNodesPrefix().Max(n => n.NodeWeight)); if (max.IsAlmost(0)) max = 1; // we skip the program root node because it's useless for display and it just takes space in the chart foreach (var n in nodes.Skip(1)) { var visualSymbolicExpressionTreeNode = SymbolicExpressionTreeChart.GetVisualSymbolicExpressionTreeNode(n); visualSymbolicExpressionTreeNode.ToolTip += Environment.NewLine + string.Format("Weight: {0:0}", n.NodeWeight); var i = (int)Math.Round(n.NodeWeight / max * 255); var fillColor = Color.FromArgb(i, Color.Red); treeChart_HighlightSubtree(n, null, fillColor); } if (!graphNode.InArcs.Any()) return; var data = graphNode.InArcs.Last().Data; var fragment = data as IFragment; var td = lastTd; if (fragment != null) { // highlight the fragment with a blue line color foreach (var n in graphNode.Data.NodeAt(fragment.Index1).IterateNodesPrefix()) { treeChart_HighlightSubtree(n, Color.CornflowerBlue); } } else if (td != null) { // highlight traced subtree and fragment if (td.SubtreeIndex == td.FragmentIndex) { treeChart_HighlightSubtree(nodes[td.SubtreeIndex], Color.Purple); } else if (td.SubtreeIndex < td.FragmentIndex) { treeChart_HighlightSubtree(nodes[td.SubtreeIndex], Color.Orange); treeChart_HighlightSubtree(nodes[td.FragmentIndex], Color.CornflowerBlue); } else { treeChart_HighlightSubtree(nodes[td.FragmentIndex], Color.CornflowerBlue); treeChart_HighlightSubtree(nodes[td.SubtreeIndex], Color.Orange); } } } #region specific symbolic expression tree node click actions private void OnTreeNodeLeftClicked(ISymbolicExpressionTreeNode node) { SelectedSubtree = node; bool trace = traceCheckBox.Checked; // check whether we are in 'trace' or 'match' mode if (trace) { // perform fragment tracing var graphNode = (IGenealogyGraphNode)genealogyGraphChart.SelectedGraphNode; var subtreeIndex = graphNode.Data.IterateNodesPrefix().ToList().IndexOf(node); var traceGraph = TraceCalculator.TraceSubtree(graphNode, subtreeIndex, updateVertexWeights: false, updateSubtreeWeights: false, cacheTraceNodes: true); if (!traceGraph.Vertices.Any()) return; genealogyGraphChart.SuspendRendering(); genealogyGraphChart.ClearPrimitives(); // clear everything bool newView = createNewViewCheckBox.Checked; if (newView) { MainFormManager.MainForm.ShowContent(traceGraph); } else { genealogyGraphChart.SuspendRendering(); genealogyGraphChart.HighlightNodes(traceGraph.Vertices); genealogyGraphChart.ResumeRendering(); } } else { // perform matching like it was done before // currently there is no possibility to specify the subtree matching criteria var trees = Content.Vertices.Select(x => x.Data); //comparer.MatchVariableWeights = matchingModeButton.Checked; //comparer.MatchConstantValues = matchingModeButton.Checked; var matchingTrees = trees.Where(x => x.Root.ContainsSubtree(node, comparer)); var matchingVertices = matchingTrees.Select(x => Content.GetByContent(x)); graphChart_HighlightMatchingVertices(matchingVertices); } } private void OnTreeNodeMiddleClicked(ISymbolicExpressionTreeNode node) { ISymbolicExpressionTreeNode clone; if (!clonedMap.TryGetValue(node, out clone)) return; if (clone.Parent == null) return; var cloneParent = clone.Parent; var cloneIndex = cloneParent.IndexOfSubtree(clone); cloneParent.RemoveSubtree(cloneIndex); cloneParent.InsertSubtree(cloneIndex, new AnySubtreeSymbol().CreateTreeNode()); var trees = Content.Vertices.Select(x => x.Data); //comparer.MatchVariableWeights = comparer.MatchConstantValues = matchingModeButton.Checked; var matchingTrees = trees.Where(x => x.Root.ContainsSubtree(clonedSubtree, comparer)); var matchingVertices = matchingTrees.Select(x => Content.GetByContent(x)); treeChart_HighlightSubtree(node, Color.Black, Color.White); graphChart_HighlightMatchingVertices(matchingVertices); } #endregion public void treeChart_SymbolicExpressionTreeNodeClicked(object sender, MouseEventArgs args) { var visualNode = (VisualTreeNode)sender; var subtree = visualNode.Content; switch (args.Button) { case MouseButtons.Left: { // highlight the selected subtree inside the displayed tree on the right hand side treeChart_ClearColors(); treeChart_HighlightSubtree(subtree, Color.Black, Color.RoyalBlue); OnTreeNodeLeftClicked(subtree); break; } case MouseButtons.Middle: { OnTreeNodeMiddleClicked(subtree); break; } } } #endregion private void graphChart_HighlightMatchingVertices(IEnumerable vertices) { genealogyGraphChart.SuspendRendering(); genealogyGraphChart.ClearPrimitives(); genealogyGraphChart.HighlightNodes(vertices); genealogyGraphChart.ResumeRendering(); } #region treechart methods 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, Color? lineColor = null, Color? fillColor = null) { if (lineColor == null && fillColor == null) return; foreach (var s in subtree.IterateNodesPrefix()) { var visualNode = SymbolicExpressionTreeChart.GetVisualSymbolicExpressionTreeNode(s); if (lineColor != null) { visualNode.LineColor = (Color)lineColor; foreach (var c in s.Subtrees) { var visualArc = SymbolicExpressionTreeChart.GetVisualSymbolicExpressionTreeNodeConnection(s, c); visualArc.LineColor = (Color)lineColor; } } if (fillColor != null) visualNode.FillColor = (Color)fillColor; } SymbolicExpressionTreeChart.RepaintNodes(); } #endregion #region navigate the genealogy / trace graph private void navigateLeftButton_Click(object sender, EventArgs e) { var graphNode = (IGenealogyGraphNode)genealogyGraphChart.SelectedGraphNode; if (!graphNode.InArcs.Any()) return; // check if the arc contains fragment data var arc = graphNode.InArcs.First(); var fragment = arc.Data as IFragment; if (fragment != null) { genealogyGraphChart.SelectedGraphNode = arc.Source; UpdateTreeChart((IGenealogyGraphNode)arc.Source); return; } var comp = new SymbolicExpressionTreeNodeEqualityComparer(); if (lastTd != null && graphNode.InDegree > 1) { var descendant = graphNode.Data; // going left (in the direction of the traced subtree): // the ancestor shares the same structure with the descendant, with the exception of the fragment // thus we identify the right ancestor by comparing the remaining nodes //var descendantNodes = descendant.Root.IterateNodesPrefix(lastTd.FragmentIndex); // calculate the relative fragment preorder index. it will always be > 0 due to the way the trace graph is constructed var relativeFragmentIndex = lastTd.FragmentIndex - lastTd.SubtreeIndex; var descendantNodes = descendant.NodeAt(lastTd.SubtreeIndex).IterateNodesPrefix(relativeFragmentIndex); var filteredArcs = graphNode.InArcs.Where(a => { var td = a.Data as TraceData; if (td == null) return false; if (!(td.LastSubtreeIndex == lastTd.SubtreeIndex && td.LastFragmentIndex == lastTd.FragmentIndex)) return false; return true; }).ToList(); if (filteredArcs.Count == 0) return; if (filteredArcs.Count == 1) { arc = filteredArcs[0]; } else { arc = filteredArcs.FirstOrDefault( a => { var ancestor = (ISymbolicExpressionTree)a.Source.Data; var td = a.Data as TraceData; if (td == null) return false; var ancestorNodes = ancestor.NodeAt(td.SubtreeIndex).IterateNodesPrefix(skipIndex: relativeFragmentIndex); return descendantNodes.SequenceEqual(ancestorNodes, comp); }); } } if (arc == null) return; lastTd = arc.Data as TraceData; genealogyGraphChart.SelectedGraphNode = arc.Source; graphNode = (IGenealogyGraphNode)arc.Source; UpdateTreeChart(graphNode); var inDegreeDistinct = graphNode.InArcs.Select(x => x.Source).Distinct().Count(); var outDegreeDistinct = graphNode.OutArcs.Select(x => x.Target).Distinct().Count(); selectedNodeStatusStrip.Text = string.Format("Quality: {0:0.00}, Rank: {1:0.0}, Degree: {2}/{3}, Weight: {4:0.00}", graphNode.Quality, graphNode.Rank, inDegreeDistinct, outDegreeDistinct, graphNode.Weight); } private void navigateRightButton_Click(object sender, EventArgs e) { var graphNode = (IGenealogyGraphNode)genealogyGraphChart.SelectedGraphNode; if (!graphNode.InArcs.Any()) return; // check if the arc contains fragment data var arc = graphNode.InArcs.Last(); var fragment = arc.Data as IFragment; if (fragment != null) { genealogyGraphChart.SelectedGraphNode = arc.Source; UpdateTreeChart((IGenealogyGraphNode)arc.Source); return; } if (lastTd != null && graphNode.InDegree > 1) { var descendant = graphNode.Data; // going right (in the direction of the fragment): // 1) fragment was swapped by crossover // 2) fragment was introduced by mutation // in some cases mutation changes things completely so we do not know which way to go var f = descendant.NodeAt(lastTd.FragmentIndex); arc = graphNode.InArcs.FirstOrDefault(a => { var td = a.Data as TraceData; if (td == null) return false; var ancestor = (ISymbolicExpressionTree)a.Source.Data; return f.Difference(ancestor.NodeAt(td.SubtreeIndex)) == null; }); } if (arc == null) return; lastTd = arc.Data as TraceData; genealogyGraphChart.SelectedGraphNode = arc.Source; graphNode = (IGenealogyGraphNode)arc.Source; UpdateTreeChart(graphNode); var inDegreeDistinct = graphNode.InArcs.Select(x => x.Source).Distinct().Count(); var outDegreeDistinct = graphNode.OutArcs.Select(x => x.Target).Distinct().Count(); selectedNodeStatusStrip.Text = string.Format("Quality: {0:0.00}, Rank: {1:0.0}, Degree: {2}/{3}, Weight: {4:0.00}", graphNode.Quality, graphNode.Rank, inDegreeDistinct, outDegreeDistinct, graphNode.Weight); } private void nodeColorsComboBox_SelectedIndexChanged(object sender, EventArgs e) { switch (nodeColorsComboBox.SelectedIndex) { case 0: // color by quality genealogyGraphChart.ColorSelector = node => ColorGradient.Colors[(int)Math.Round(node.Quality * 255)]; genealogyGraphChart.SuspendRendering(); genealogyGraphChart.HighlightAll(); genealogyGraphChart.ResumeRendering(); break; case 1: // color by weight var weights = Content.Vertices.ToDictionary(v => (IGenealogyGraphNode)v, v => v.Data.IterateNodesPrefix().Average(x => x.NodeWeight)); var max = weights.Values.Max(); genealogyGraphChart.ColorSelector = node => ColorGradient.GrayscaledColors[(int)Math.Round(weights[node] / max * 255)]; genealogyGraphChart.SuspendRendering(); genealogyGraphChart.HighlightAll(); genealogyGraphChart.ResumeRendering(); break; default: break; } } private void lockCheckBox_CheckedChanged(object sender, EventArgs e) { genealogyGraphChart.LockGenealogy = lockCheckBox.Checked; } } #endregion } // bogus class needed in order to be able to "design" the view public class SymbolicDataAnalysisGenealogyGraphViewDesignable : GenealogyGraphView { } #region helper methods internal static class Util { 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 bool IsIsomorphicWith(this ISymbolicExpressionTreeNode a, ISymbolicExpressionTreeNode b) { return a.Difference(b) == null; } internal static IEnumerable IterateNodesPrefix(this ISymbolicExpressionTreeNode node, int skipIndex) { var e = node.IterateNodesPrefix().GetEnumerator(); var i = 0; while (e.MoveNext()) { var n = e.Current; if (i == skipIndex) { var len = n.GetLength(); while (--len >= 0 && e.MoveNext()) ; n = e.Current; } if (n != null) yield return n; ++i; } } internal static IEnumerable IterateNodesPrefix(this ISymbolicExpressionTreeNode node, ISymbolicExpressionTreeNode skipNode) { var e = node.IterateNodesPrefix().GetEnumerator(); while (e.MoveNext()) { var n = e.Current; if (n == skipNode) { int len = n.GetLength(); while (--len >= 0 && e.MoveNext()) ; n = e.Current; } if (n != null) yield return n; } } } #endregion