Commit d4b03d5f authored by Arthur Bit-Monnot's avatar Arthur Bit-Monnot

Optimization of topological sort and tarjan algorithms.

parent 174aa6ab
......@@ -2,6 +2,8 @@ package dahu.graphs
import dahu.utils.Graph
import scala.reflect.ClassTag
/** A Directed Acyclic Graph.
*
* @tparam F Parametric node: a node is of type F[A]. Note that one can use cats.Id if node is a recursive type.
......@@ -17,14 +19,14 @@ trait DAG[F[_], A] {
def descendantsAndSelf(a: A): Set[A] = children(algebra(a)).flatMap(descendantsAndSelf) + a
def topologicalOrder(nodes: Set[A]): Seq[A] = {
def topologicalOrder(nodes: Set[A])(implicit ct: ClassTag[A]): Seq[A] = {
val graph = nodes.map(n => (n, children(algebra(n)))).toMap
Graph.topologicalOrder(graph) match {
case Some(order) => order
case None => dahu.utils.errors.unexpected("This dag instance is not a DAG.")
}
}
def topologicalOrderFromRoot(a: A): Seq[A] = {
def topologicalOrderFromRoot(a: A)(implicit ct: ClassTag[A]): Seq[A] = {
topologicalOrder(descendantsAndSelf(a))
}
......
package dahu.utils
import scala.collection.{immutable, mutable}
import scala.reflect.ClassTag
object Graph {
def tarjan[V: ClassTag](graph: Map[V, Set[V]]): Array[debox.Set[V]] = {
tarjan(
debox.Map.fromIterable(graph.mapValues(s => debox.Set.fromIterable(s)))
)
}
/** Tarjan's algorithm for extracting strongly connected components.
*
* Given a directed graph, the algorithm outputs a sequence of strongly connected
* components sorted in topological order.
* */
def tarjan[V](graph: Map[V, Set[V]]): Seq[Set[V]] = {
def tarjan[@specialized(Int) V: ClassTag](
graph: debox.Map[V, debox.Set[V]]): Array[debox.Set[V]] = {
class Data(var index: Int, var lowLink: Int, var onStack: Boolean)
var index = 0
val stack = new mutable.ArrayBuffer[V]()
val data = mutable.Map[V, Data]()
val stack = debox.Buffer[V]()
val data = debox.Map[V, Data]()
@inline def last(stack: debox.Buffer[V]): V = {
stack(stack.length - 1)
}
@inline def pop(stack: debox.Buffer[V]): V = {
stack.remove(stack.length - 1)
}
var components: Seq[Set[V]] = immutable.Seq()
val components: debox.Buffer[debox.Set[V]] = debox.Buffer()
for(v <- graph.keys) {
for(v <- graph.keysSet) {
if(!data.contains(v))
strongConnect(v)
}
......@@ -27,7 +41,7 @@ object Graph {
data(v) = new Data(index, index, true)
index += 1
stack += v
for(w <- graph.getOrElse(v, Set())) {
for(w <- graph.getOrElse(v, debox.Set())) { // todo: creating an object that is not needed
if(!data.contains(w)) {
strongConnect(w)
data(v).lowLink = data(v).lowLink.min(data(w).lowLink)
......@@ -37,28 +51,34 @@ object Graph {
}
if(data(v).lowLink == data(v).index) {
var scc: Set[V] = Set()
var w = stack.last
val scc: debox.Set[V] = debox.Set()
var w = last(stack)
do {
w = stack.last
stack.remove(stack.size - 1)
w = pop(stack)
data(w).onStack = false
scc += w
} while(w != v)
components = components :+ scc
components += scc
}
}
components.reverse
components.reverse.toArray()
}
def topologicalOrder[V](graph: Map[V, Set[V]]): Option[Seq[V]] = {
def topologicalOrder[V: ClassTag](graph: Map[V, Set[V]]): Option[Seq[V]] = {
val sccs = tarjan(graph)
sccs.foldLeft(Option(Seq[V]())) {
case (opt, scc) if scc.size == 1 => opt.map(_ :+ scc.head)
case _ => None // non singleton strongly connected component
val order = debox.Buffer[V]()
var i = 0
while(i < sccs.length) {
if(sccs(i).size == 1)
sccs(i).foreach(e => order += e)
else
return None // the graph has a (non-singleton) strongly connected component
i += 1
}
Some(order.toArray())
}
}
......@@ -15,9 +15,13 @@ object UtilsTests extends TestSuite {
def tests = Tests {
"graph-utils" - {
"tarjan" - {
assert(tarjan(g1) == Seq(Set(1, 2), Set(3)))
assert(tarjan(dag1) == (1 to 4).map(Set(_)))
assert(tarjan(dag2) == (1 to 4).map(Set(_)))
// simple converter function to help the comparison.
def tj(g: Map[Int, Set[Int]]): Seq[Set[Int]] =
tarjan(g).toList.map(_.toScalaSet())
tj(g1) ==> Seq(Set(1, 2), Set(3))
assert(tj(dag1) == (1 to 4).map(Set(_)))
assert(tj(dag2) == (1 to 4).map(Set(_)))
}
"topological-order" - {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment