Skip to content

Commit

Permalink
Improve the implementation of the Graphviz algorithm to better work w…
Browse files Browse the repository at this point in the history
…ith delegate graph implementations.

Fix KeRNeLith#41
  • Loading branch information
KeRNeLith committed Nov 1, 2021
1 parent 36702b7 commit 53620e9
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 44 deletions.
117 changes: 73 additions & 44 deletions src/QuikGraph.Graphviz/GraphvizAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
#if SUPPORTS_AGGRESSIVE_INLINING
using System.Runtime.CompilerServices;
#endif
using JetBrains.Annotations;
using QuikGraph.Graphviz.Dot;

Expand Down Expand Up @@ -175,11 +179,14 @@ public string Generate()
Output = new StringWriter();
// Build vertex id map
int i = 0;
var vertices = new HashSet<TVertex>(VisitedGraph.Vertices);
foreach (TVertex vertex in VisitedGraph.Vertices)
{
_verticesIds.Add(vertex, i++);
}

var edges = new HashSet<TEdge>(VisitedGraph.Edges);

Output.Write(VisitedGraph.IsDirected ? "digraph " : "graph ");
Output.Write(GraphFormat.Name);
Output.WriteLine(" {");
Expand All @@ -200,26 +207,13 @@ public string Generate()
Output.WriteLine($"edge [{edgeFormat}];");
}

// Initialize vertices map
var verticesColors = new Dictionary<TVertex, GraphColor>();
foreach (TVertex vertex in VisitedGraph.Vertices)
{
verticesColors[vertex] = GraphColor.White;
}

var edgeColors = new Dictionary<TEdge, GraphColor>();
foreach (TEdge edge in VisitedGraph.Edges)
{
edgeColors[edge] = GraphColor.White;
}

if (VisitedGraph is IClusteredGraph clusteredGraph)
{
WriteClusters(verticesColors, edgeColors, clusteredGraph);
WriteClusters(vertices, edges, clusteredGraph);
}

WriteVertices(verticesColors, VisitedGraph.Vertices);
WriteEdges(edgeColors, VisitedGraph.Edges);
WriteVertices(vertices);
WriteEdges(edges);

Output.Write("}");
return Output.ToString();
Expand All @@ -244,12 +238,12 @@ public string Generate([NotNull] IDotEngine dot, [NotNull] string outputFilePath
}

private void WriteClusters(
[NotNull] IDictionary<TVertex, GraphColor> verticesColors,
[NotNull] IDictionary<TEdge, GraphColor> edgeColors,
[NotNull, ItemNotNull] ICollection<TVertex> remainingVertices,
[NotNull, ItemNotNull] ICollection<TEdge> remainingEdges,
[NotNull] IClusteredGraph parent)
{
Debug.Assert(verticesColors != null);
Debug.Assert(edgeColors != null);
Debug.Assert(remainingVertices != null);
Debug.Assert(remainingEdges != null);
Debug.Assert(parent != null);

++ClusterCount;
Expand All @@ -260,65 +254,100 @@ private void WriteClusters(
OnFormatCluster(subGraph);
if (subGraph is IClusteredGraph clusteredGraph)
{
WriteClusters(verticesColors, edgeColors, clusteredGraph);
WriteClusters(remainingVertices, remainingEdges, clusteredGraph);
}

if (parent.Collapsed)
{
foreach (TVertex vertex in subGraph.Vertices)
{
verticesColors[vertex] = GraphColor.Black;
remainingVertices.Remove(vertex);
}

foreach (TEdge edge in subGraph.Edges)
{
edgeColors[edge] = GraphColor.Black;
remainingEdges.Remove(edge);
}
}
else
{
WriteVertices(verticesColors, subGraph.Vertices);
WriteEdges(edgeColors, subGraph.Edges);
WriteVertices(remainingVertices, subGraph.Vertices);
WriteEdges(remainingEdges, subGraph.Edges);
}
Output.WriteLine("}");
}
}

private void WriteVertices(
[NotNull] IDictionary<TVertex, GraphColor> verticesColors,
[NotNull, ItemNotNull] IEnumerable<TVertex> vertices)
#if SUPPORTS_AGGRESSIVE_INLINING
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
private void WriteVertex([NotNull] TVertex vertex)
{
Debug.Assert(vertex != null);

OnFormatVertex(vertex);
}

private void WriteVertices([NotNull, ItemNotNull] IEnumerable<TVertex> vertices)
{
Debug.Assert(verticesColors != null);
Debug.Assert(vertices != null);

foreach (TVertex vertex in vertices)
{
if (verticesColors[vertex] != GraphColor.White)
continue;
WriteVertex(vertex);
}
}

OnFormatVertex(vertex);
verticesColors[vertex] = GraphColor.Black;
private void WriteVertices(
[NotNull, ItemNotNull] ICollection<TVertex> remainingVertices,
[NotNull, ItemNotNull] IEnumerable<TVertex> vertices)
{
Debug.Assert(remainingVertices != null);
Debug.Assert(vertices != null);

foreach (TVertex vertex in vertices.Where(remainingVertices.Contains))
{
WriteVertex(vertex);
remainingVertices.Remove(vertex);
}
}

private void WriteEdges(
[NotNull] IDictionary<TEdge, GraphColor> edgesColors,
[NotNull, ItemNotNull] IEnumerable<TEdge> edges)
#if SUPPORTS_AGGRESSIVE_INLINING
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
private void WriteEdge([NotNull] TEdge edge)
{
Debug.Assert(edge != null);

Output.Write(VisitedGraph.IsDirected
? $"{_verticesIds[edge.Source]} -> {_verticesIds[edge.Target]}"
: $"{_verticesIds[edge.Source]} -- {_verticesIds[edge.Target]}");

OnFormatEdge(edge);
}

private void WriteEdges([NotNull, ItemNotNull] IEnumerable<TEdge> edges)
{
Debug.Assert(edgesColors != null);
Debug.Assert(edges != null);

foreach (TEdge edge in edges)
{
if (edgesColors[edge] != GraphColor.White)
continue;
WriteEdge(edge);
}
}

Output.Write(VisitedGraph.IsDirected
? $"{_verticesIds[edge.Source]} -> {_verticesIds[edge.Target]}"
: $"{_verticesIds[edge.Source]} -- {_verticesIds[edge.Target]}");

OnFormatEdge(edge);
edgesColors[edge] = GraphColor.Black;
private void WriteEdges(
[NotNull, ItemNotNull] ICollection<TEdge> remainingEdges,
[NotNull, ItemNotNull] IEnumerable<TEdge> edges)
{
Debug.Assert(remainingEdges != null);
Debug.Assert(edges != null);

foreach (TEdge edge in edges.Where(remainingEdges.Contains))
{
WriteEdge(edge);
remainingEdges.Remove(edge);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
Expand Down Expand Up @@ -41,6 +42,96 @@ public void ToGraphviz()
Assert.AreEqual(expectedDot, dotGraph);
}

[Test]
public void ToGraphviz_DelegateGraph()
{
int[] vertices = { 1, 2, 3, 4, 5 };
var graph = new DelegateVertexAndEdgeListGraph<int, Edge<int>>(
vertices,
(int vertex, out IEnumerable<Edge<int>> outEdges) =>
{
if (vertex == 1)
{
outEdges = new[] { new Edge<int>(1, 2), new Edge<int>(1, 3) };
return true;
}

if (vertex == 2)
{
outEdges = new[] { new Edge<int>(2, 4) };
return true;
}

if (vertex is 3 or 4 or 5)
{
outEdges = new Edge<int>[] { };
return true;
}

outEdges = null;
return false;
});

string expectedDot =
@"digraph G {" + Environment.NewLine
+ @"0;" + Environment.NewLine
+ @"1;" + Environment.NewLine
+ @"2;" + Environment.NewLine
+ @"3;" + Environment.NewLine
+ @"4;" + Environment.NewLine
+ @"0 -> 1;" + Environment.NewLine
+ @"0 -> 2;" + Environment.NewLine
+ @"1 -> 3;" + Environment.NewLine
+ @"}";
string dotGraph = graph.ToGraphviz();
Assert.AreEqual(expectedDot, dotGraph);
}

[Test]
public void ToGraphviz_EquatableEdgeDelegateGraph()
{
int[] vertices = { 1, 2, 3, 4, 5 };
var graph = new DelegateVertexAndEdgeListGraph<int, EquatableEdge<int>>(
vertices,
(int vertex, out IEnumerable<EquatableEdge<int>> outEdges) =>
{
if (vertex == 1)
{
outEdges = new[] { new EquatableEdge<int>(1, 2), new EquatableEdge<int>(1, 3) };
return true;
}

if (vertex == 2)
{
outEdges = new[] { new EquatableEdge<int>(2, 4) };
return true;
}

if (vertex is 3 or 4 or 5)
{
outEdges = new EquatableEdge<int>[] { };
return true;
}

outEdges = null;
return false;
});

string expectedDot =
@"digraph G {" + Environment.NewLine
+ @"0;" + Environment.NewLine
+ @"1;" + Environment.NewLine
+ @"2;" + Environment.NewLine
+ @"3;" + Environment.NewLine
+ @"4;" + Environment.NewLine
+ @"0 -> 1;" + Environment.NewLine
+ @"0 -> 2;" + Environment.NewLine
+ @"1 -> 3;" + Environment.NewLine
+ @"}";
string dotGraph = graph.ToGraphviz();
Assert.AreEqual(expectedDot, dotGraph);
}

[Test]
public void ToGraphvizWithEmptyInit()
{
Expand Down

0 comments on commit 53620e9

Please sign in to comment.