MoonTools.Graph/Graph/DirectedGraph.cs

561 lines
19 KiB
C#
Raw Normal View History

2019-10-22 01:48:27 +00:00
using System;
using System.Collections.Generic;
using System.Linq;
2019-10-23 23:46:04 +00:00
using Collections.Pooled;
2019-10-22 01:48:27 +00:00
namespace MoonTools.Core.Graph
{
public enum SearchSymbol
{
2019-10-23 02:39:57 +00:00
Start,
Finish
2019-10-22 01:48:27 +00:00
}
2019-10-23 00:46:44 +00:00
public class DirectedGraph<TNode, TEdgeData> : IGraph<TNode, TEdgeData> where TNode : IEquatable<TNode>
2019-10-22 01:48:27 +00:00
{
2019-10-22 07:28:29 +00:00
protected HashSet<TNode> nodes = new HashSet<TNode>();
protected HashSet<(TNode, TNode)> edges = new HashSet<(TNode, TNode)>();
protected Dictionary<(TNode, TNode), TEdgeData> edgesToEdgeData = new Dictionary<(TNode, TNode), TEdgeData>();
protected Dictionary<TNode, HashSet<TNode>> neighbors = new Dictionary<TNode, HashSet<TNode>>();
private class SimpleCycleComparer : IEqualityComparer<IEnumerable<TNode>>
2019-10-22 01:48:27 +00:00
{
2019-10-22 07:28:29 +00:00
public bool Equals(IEnumerable<TNode> x, IEnumerable<TNode> y)
2019-10-22 01:48:27 +00:00
{
return x.SequenceEqual(y);
}
2019-10-22 07:28:29 +00:00
public int GetHashCode(IEnumerable<TNode> obj)
2019-10-22 01:48:27 +00:00
{
return obj.Aggregate(0, (current, next) => current.GetHashCode() ^ next.GetHashCode());
}
}
public IEnumerable<TNode> Nodes => nodes;
public IEnumerable<(TNode, TNode)> Edges => edges;
2019-10-22 01:48:27 +00:00
2019-10-23 21:15:17 +00:00
public int Order => nodes.Count;
public int Size => edges.Count;
2019-10-22 07:28:29 +00:00
public void AddNode(TNode node)
2019-10-22 01:48:27 +00:00
{
2019-10-22 07:28:29 +00:00
if (!Exists(node))
2019-10-22 01:48:27 +00:00
{
2019-10-22 07:28:29 +00:00
nodes.Add(node);
neighbors.Add(node, new HashSet<TNode>());
2019-10-22 01:48:27 +00:00
}
}
2019-10-22 07:28:29 +00:00
public void AddNodes(params TNode[] nodes)
2019-10-22 01:48:27 +00:00
{
2019-10-22 07:28:29 +00:00
foreach (var node in nodes)
{
AddNode(node);
}
2019-10-22 01:48:27 +00:00
}
public bool Exists(TNode node)
{
return nodes.Contains(node);
}
public bool Exists(TNode v, TNode u)
{
return edges.Contains((v, u));
}
private void CheckNodes(params TNode[] givenNodes)
{
foreach (var node in givenNodes)
{
if (!Exists(node))
{
throw new ArgumentException($"Vertex {node} does not exist in the graph");
}
}
}
2019-10-23 21:15:17 +00:00
public int Degree(TNode node)
{
CheckNodes(node);
return neighbors[node].Count;
}
2019-10-22 07:28:29 +00:00
public void RemoveNode(TNode node)
2019-10-22 01:48:27 +00:00
{
CheckNodes(node);
2019-10-23 23:46:04 +00:00
var edgesToRemove = new PooledList<(TNode, TNode)>(ClearMode.Always);
2019-10-22 01:48:27 +00:00
foreach (var entry in neighbors)
2019-10-22 01:48:27 +00:00
{
if (entry.Value.Contains(node))
2019-10-22 01:48:27 +00:00
{
edgesToRemove.Add((entry.Key, node));
2019-10-22 01:48:27 +00:00
}
}
2019-10-22 01:48:27 +00:00
foreach (var edge in edgesToRemove)
{
RemoveEdge(edge.Item1, edge.Item2);
2019-10-22 01:48:27 +00:00
}
2019-10-23 23:46:04 +00:00
edgesToRemove.Dispose();
nodes.Remove(node);
neighbors.Remove(node);
2019-10-22 01:48:27 +00:00
}
2019-10-23 21:15:17 +00:00
public virtual void AddEdge(TNode v, TNode u, TEdgeData edgeData)
2019-10-22 01:48:27 +00:00
{
CheckNodes(v, u);
2019-10-24 00:42:48 +00:00
if (v.Equals(u)) { throw new ArgumentException("Self-edges are not allowed in a simple graph. Use a multigraph instead"); }
neighbors[v].Add(u);
edges.Add((v, u));
edgesToEdgeData.Add((v, u), edgeData);
2019-10-22 01:48:27 +00:00
}
2019-10-23 21:15:17 +00:00
public virtual void AddEdges(params (TNode, TNode, TEdgeData)[] edges)
2019-10-22 01:48:27 +00:00
{
foreach (var edge in edges)
{
2019-10-22 07:28:29 +00:00
AddEdge(edge.Item1, edge.Item2, edge.Item3);
2019-10-22 01:48:27 +00:00
}
}
private void CheckEdge(TNode v, TNode u)
{
CheckNodes(v, u);
if (!Exists(v, u)) { throw new ArgumentException($"Edge between vertex {v} and vertex {u} does not exist in the graph"); }
}
2019-10-23 21:15:17 +00:00
public virtual void RemoveEdge(TNode v, TNode u)
2019-10-22 01:48:27 +00:00
{
CheckEdge(v, u);
2019-10-22 07:28:29 +00:00
neighbors[v].Remove(u);
2019-10-23 21:15:17 +00:00
edges.Remove((v, u));
edgesToEdgeData.Remove((v, u));
2019-10-22 01:48:27 +00:00
}
public TEdgeData EdgeData(TNode v, TNode u)
2019-10-22 01:48:27 +00:00
{
CheckEdge(v, u);
return edgesToEdgeData[(v, u)];
2019-10-22 07:28:29 +00:00
}
public IEnumerable<TNode> Neighbors(TNode node)
{
CheckNodes(node);
return neighbors[node];
2019-10-22 01:48:27 +00:00
}
2019-10-23 21:15:17 +00:00
public IEnumerable<TNode> PreorderNodeDFS()
2019-10-22 01:48:27 +00:00
{
2019-10-23 23:46:04 +00:00
var dfsStack = new PooledStack<TNode>(ClearMode.Always);
var dfsDiscovered = new PooledSet<TNode>(ClearMode.Always);
2019-10-23 21:15:17 +00:00
foreach (var node in Nodes)
{
if (!dfsDiscovered.Contains(node))
{
dfsStack.Push(node);
while (dfsStack.Count > 0)
{
var current = dfsStack.Pop();
if (!dfsDiscovered.Contains(current))
{
dfsDiscovered.Add(current);
yield return current;
foreach (var neighbor in Neighbors(current))
{
dfsStack.Push(neighbor);
}
}
}
}
}
2019-10-23 23:46:04 +00:00
dfsStack.Dispose();
dfsDiscovered.Dispose();
}
2019-10-23 21:15:17 +00:00
2019-10-23 23:46:04 +00:00
private IEnumerable<TNode> PostorderNodeDFSHelper(PooledSet<TNode> discovered, TNode v)
2019-10-23 21:15:17 +00:00
{
2019-10-23 23:46:04 +00:00
discovered.Add(v);
2019-10-22 01:48:27 +00:00
2019-10-23 23:46:04 +00:00
foreach (var neighbor in Neighbors(v))
2019-10-22 01:48:27 +00:00
{
2019-10-23 23:46:04 +00:00
if (!discovered.Contains(neighbor))
2019-10-22 01:48:27 +00:00
{
2019-10-23 23:46:04 +00:00
foreach (var node in PostorderNodeDFSHelper(discovered, neighbor))
2019-10-22 01:48:27 +00:00
{
2019-10-23 23:46:04 +00:00
yield return node;
2019-10-22 01:48:27 +00:00
}
}
}
2019-10-23 23:46:04 +00:00
yield return v;
}
public IEnumerable<TNode> PostorderNodeDFS()
{
var dfsDiscovered = new PooledSet<TNode>(ClearMode.Always);
2019-10-22 07:28:29 +00:00
foreach (var node in Nodes)
2019-10-22 01:48:27 +00:00
{
2019-10-23 21:15:17 +00:00
if (!dfsDiscovered.Contains(node))
2019-10-22 01:48:27 +00:00
{
2019-10-23 23:46:04 +00:00
foreach (var thing in PostorderNodeDFSHelper(dfsDiscovered, node))
{
yield return thing;
}
2019-10-22 01:48:27 +00:00
}
}
2019-10-23 21:15:17 +00:00
}
public IEnumerable<TNode> NodeBFS()
{
2019-10-23 23:46:04 +00:00
var bfsQueue = new PooledQueue<TNode>(ClearMode.Always);
var bfsDiscovered = new PooledSet<TNode>(ClearMode.Always);
2019-10-23 21:15:17 +00:00
foreach (var node in Nodes)
{
if (!bfsDiscovered.Contains(node))
{
bfsQueue.Enqueue(node);
while (bfsQueue.Count > 0)
{
var current = bfsQueue.Dequeue();
foreach (var neighbor in Neighbors(current))
{
if (!bfsDiscovered.Contains(neighbor))
{
bfsDiscovered.Add(neighbor);
bfsQueue.Enqueue(neighbor);
yield return neighbor;
}
}
}
}
}
2019-10-23 23:46:04 +00:00
bfsQueue.Dispose();
bfsDiscovered.Dispose();
2019-10-23 21:15:17 +00:00
}
2019-10-23 23:46:04 +00:00
List<PooledSet<TNode>> lexicographicSets = new List<PooledSet<TNode>>();
HashSet<PooledSet<TNode>> replacedSets = new HashSet<PooledSet<TNode>>();
2019-10-23 21:15:17 +00:00
public IEnumerable<TNode> LexicographicBFS()
{
2019-10-23 23:46:04 +00:00
lexicographicSets.Add(Nodes.ToPooledSet());
2019-10-23 21:15:17 +00:00
2019-10-23 23:46:04 +00:00
while (lexicographicSets.Count > 0)
2019-10-23 21:15:17 +00:00
{
2019-10-23 23:46:04 +00:00
var firstSet = lexicographicSets[0];
var node = firstSet.First();
firstSet.Remove(node);
if (firstSet.Count == 0) { lexicographicSets.RemoveAt(0); }
2019-10-23 21:15:17 +00:00
yield return node;
foreach (var neighbor in Neighbors(node))
{
2019-10-23 23:46:04 +00:00
if (lexicographicSets.Any(set => set.Contains(neighbor)))
2019-10-23 21:15:17 +00:00
{
2019-10-23 23:46:04 +00:00
var s = lexicographicSets.Find(set => set.Contains(neighbor));
var sIndex = lexicographicSets.IndexOf(s);
PooledSet<TNode> t;
if (replacedSets.Contains(s) && sIndex > 0)
2019-10-23 21:15:17 +00:00
{
2019-10-23 23:46:04 +00:00
t = lexicographicSets[sIndex - 1];
2019-10-23 21:15:17 +00:00
}
else
{
2019-10-23 23:46:04 +00:00
t = new PooledSet<TNode>(ClearMode.Always);
lexicographicSets.Insert(sIndex, t);
replacedSets.Add(s);
2019-10-23 21:15:17 +00:00
}
s.Remove(neighbor);
t.Add(neighbor);
2019-10-24 00:31:22 +00:00
if (s.Count == 0) { lexicographicSets.Remove(s); replacedSets.Remove(s); s.Dispose(); }
2019-10-23 21:15:17 +00:00
}
}
}
2019-10-23 23:46:04 +00:00
lexicographicSets.Clear();
replacedSets.Clear();
2019-10-22 01:48:27 +00:00
}
public bool Cyclic()
{
return StronglyConnectedComponents().Any((scc) => scc.Count() > 1);
}
2019-10-22 07:28:29 +00:00
public IEnumerable<TNode> TopologicalSort()
2019-10-22 01:48:27 +00:00
{
2019-10-23 21:15:17 +00:00
return PostorderNodeDFS().Reverse();
2019-10-22 01:48:27 +00:00
}
2019-10-24 00:31:22 +00:00
List<PooledList<TNode>> sccs = new List<PooledList<TNode>>();
2019-10-22 07:28:29 +00:00
public IEnumerable<IEnumerable<TNode>> StronglyConnectedComponents()
2019-10-22 01:48:27 +00:00
{
2019-10-24 00:31:22 +00:00
foreach (var scc in sccs)
{
scc.Dispose();
}
sccs.Clear();
var preorder = new PooledDictionary<TNode, uint>();
var lowlink = new PooledDictionary<TNode, uint>();
var sccFound = new PooledDictionary<TNode, bool>();
var sccQueue = new PooledStack<TNode>();
2019-10-22 01:48:27 +00:00
uint preorderCounter = 0;
2019-10-22 07:28:29 +00:00
foreach (var source in Nodes)
2019-10-22 01:48:27 +00:00
{
if (!sccFound.ContainsKey(source))
{
2019-10-22 07:28:29 +00:00
var queue = new Stack<TNode>();
2019-10-22 01:48:27 +00:00
queue.Push(source);
while (queue.Count > 0)
{
var v = queue.Peek();
if (!preorder.ContainsKey(v))
{
preorderCounter++;
preorder[v] = preorderCounter;
}
var done = true;
var vNeighbors = Neighbors(v);
foreach (var w in vNeighbors)
{
if (!preorder.ContainsKey(w))
{
queue.Push(w);
done = false;
break;
}
}
if (done)
{
lowlink[v] = preorder[v];
foreach (var w in vNeighbors)
{
if (!sccFound.ContainsKey(w))
{
if (preorder[w] > preorder[v])
{
lowlink[v] = Math.Min(lowlink[v], lowlink[w]);
}
else
{
lowlink[v] = Math.Min(lowlink[v], preorder[w]);
}
}
}
queue.Pop();
if (lowlink[v] == preorder[v])
{
sccFound[v] = true;
2019-10-24 00:31:22 +00:00
var scc = new PooledList<TNode>
2019-10-22 01:48:27 +00:00
{
v
};
while (sccQueue.Count > 0 && preorder[sccQueue.Peek()] > preorder[v])
{
var k = sccQueue.Pop();
sccFound[k] = true;
scc.Add(k);
}
2019-10-24 00:31:22 +00:00
sccs.Add(scc);
yield return scc;
2019-10-22 01:48:27 +00:00
}
else
{
sccQueue.Push(v);
}
}
}
}
}
}
2019-10-22 07:28:29 +00:00
public IEnumerable<IEnumerable<TNode>> SimpleCycles()
2019-10-22 01:48:27 +00:00
{
void unblock(TNode thisnode, HashSet<TNode> blocked, Dictionary<TNode, HashSet<TNode>> B) //refactor to remove closure
2019-10-22 01:48:27 +00:00
{
2019-10-22 07:28:29 +00:00
var stack = new Stack<TNode>();
2019-10-22 01:48:27 +00:00
stack.Push(thisnode);
while (stack.Count > 0)
{
var node = stack.Pop();
if (blocked.Contains(thisnode))
{
blocked.Remove(thisnode);
if (B.ContainsKey(node))
{
foreach (var n in B[node])
{
if (!stack.Contains(n))
{
stack.Push(n);
}
}
B[node].Clear();
}
}
}
}
2019-10-22 07:28:29 +00:00
List<List<TNode>> result = new List<List<TNode>>();
2019-10-22 01:48:27 +00:00
var subGraph = Clone();
2019-10-22 07:28:29 +00:00
var sccs = new Stack<IEnumerable<TNode>>();
2019-10-22 01:48:27 +00:00
foreach (var scc in StronglyConnectedComponents())
{
sccs.Push(scc);
}
while (sccs.Count > 0)
{
2019-10-22 07:28:29 +00:00
var scc = new Stack<TNode>(sccs.Pop());
2019-10-22 01:48:27 +00:00
var startNode = scc.Pop();
2019-10-22 07:28:29 +00:00
var path = new Stack<TNode>();
2019-10-22 01:48:27 +00:00
path.Push(startNode);
2019-10-22 07:28:29 +00:00
var blocked = new HashSet<TNode>
2019-10-22 01:48:27 +00:00
{
startNode
};
2019-10-22 07:28:29 +00:00
var closed = new HashSet<TNode>();
var B = new Dictionary<TNode, HashSet<TNode>>();
var stack = new Stack<Tuple<TNode, Stack<TNode>>>();
stack.Push(Tuple.Create(startNode, new Stack<TNode>(subGraph.Neighbors(startNode))));
2019-10-22 01:48:27 +00:00
while (stack.Count > 0)
{
var entry = stack.Peek();
var thisnode = entry.Item1;
var neighbors = entry.Item2;
if (neighbors.Count > 0)
{
var nextNode = neighbors.Pop();
if (nextNode.Equals(startNode))
{
2019-10-22 07:28:29 +00:00
var resultPath = new List<TNode>();
2019-10-22 01:48:27 +00:00
foreach (var v in path)
{
resultPath.Add(v);
}
result.Add(resultPath);
foreach (var v in path)
{
closed.Add(v);
}
}
else if (!blocked.Contains(nextNode))
{
path.Push(nextNode);
2019-10-22 07:28:29 +00:00
stack.Push(Tuple.Create(nextNode, new Stack<TNode>(subGraph.Neighbors(nextNode))));
2019-10-22 01:48:27 +00:00
closed.Remove(nextNode);
blocked.Add(nextNode);
continue;
}
}
if (neighbors.Count == 0)
{
if (closed.Contains(thisnode))
{
unblock(thisnode, blocked, B);
}
else
{
foreach (var neighbor in subGraph.Neighbors(thisnode))
{
if (!B.ContainsKey(neighbor))
{
2019-10-22 07:28:29 +00:00
B[neighbor] = new HashSet<TNode>();
2019-10-22 01:48:27 +00:00
}
B[neighbor].Add(thisnode);
}
}
stack.Pop();
path.Pop();
}
}
2019-10-22 07:28:29 +00:00
subGraph.RemoveNode(startNode);
2019-10-22 01:48:27 +00:00
var H = subGraph.SubGraph(scc.ToArray());
var HSccs = H.StronglyConnectedComponents();
foreach (var HScc in HSccs)
{
sccs.Push(HScc);
}
}
return result.Distinct(new SimpleCycleComparer());
}
2019-10-22 07:28:29 +00:00
public DirectedGraph<TNode, TEdgeData> Clone()
2019-10-22 01:48:27 +00:00
{
2019-10-22 07:28:29 +00:00
var clone = new DirectedGraph<TNode, TEdgeData>();
clone.AddNodes(Nodes.ToArray());
2019-10-22 01:48:27 +00:00
2019-10-22 07:28:29 +00:00
foreach (var v in Nodes)
2019-10-22 01:48:27 +00:00
{
foreach (var n in Neighbors(v))
{
clone.AddEdge(v, n, EdgeData(v, n));
2019-10-22 01:48:27 +00:00
}
}
return clone;
}
2019-10-22 07:28:29 +00:00
public DirectedGraph<TNode, TEdgeData> SubGraph(params TNode[] subVertices)
2019-10-22 01:48:27 +00:00
{
2019-10-22 07:28:29 +00:00
var subGraph = new DirectedGraph<TNode, TEdgeData>();
subGraph.AddNodes(subVertices.ToArray());
2019-10-22 01:48:27 +00:00
2019-10-22 07:28:29 +00:00
foreach (var n in Nodes)
2019-10-22 01:48:27 +00:00
{
2019-10-22 07:28:29 +00:00
if (Nodes.Contains(n))
2019-10-22 01:48:27 +00:00
{
2019-10-22 07:28:29 +00:00
var neighbors = Neighbors(n);
2019-10-22 01:48:27 +00:00
foreach (var u in neighbors)
{
if (subVertices.Contains(u))
{
subGraph.AddEdge(n, u, EdgeData(n, u));
2019-10-22 01:48:27 +00:00
}
}
}
}
return subGraph;
}
2019-10-22 07:28:29 +00:00
public void Clear()
{
nodes.Clear();
neighbors.Clear();
edges.Clear();
edgesToEdgeData.Clear();
2019-10-22 07:28:29 +00:00
}
2019-10-22 01:48:27 +00:00
}
}