DAG
Simple directed acyclic graphs for Ruby.
History
This ruby gem started out as a fork of kevinrutherford's dag implementation. If you want to migrate from his implementation to this one, have a look at the breaking changes. Have a look at performance improvements to see why you might want to migrate.
Installation
Install the gem
gem install simple_dag
Or add it to your Gemfile and run bundle.
gem 'simple_dag'Usage
require 'simple_dag'
dag = DAG.new
v1 = dag.add_vertex
v2 = dag.add_vertex
v3 = dag.add_vertex
dag.add_edge from: v1, to: v2
dag.add_edge from: v2, to: v3
v1.path_to?(v3) # => true
v3.path_to?(v1) # => false
dag.add_edge from: v3, to: v1 # => ArgumentError: A DAG must not have cycles
dag.add_edge from: v1, to: v2 # => ArgumentError: Edge already exists
dag.add_edge from: v1, to: v3
v1.successors # => [v2, v3]See the specs for more detailed usage scenarios.
Compatibility
Tested with Ruby 2.2, 2.3, 2.4, 2.5, JRuby, Rubinius. Builds with Ruby 2.5 and JRuby are currently failing. See this issue for details.
Differences to dag
Breaking changes
-
The function
DAG::Vertex#has_path_to?aliased asDAG::Vertex#has_descendant?andDAG::Vertex#has_descendent?has been renamed toDAG::Vertex#path_to?. The aliases have been removed. -
The function
DAG::Vertex#has_ancestor?aliased asDAG::Vertex#is_reachable_from?has been renamed toDAG::Vertex#reachable_from?. The aliases have been removed. -
The array of edges returned by
DAG#edgesis no longer sorted by insertion order of the edges. -
DAG::Vertex#path_to?andDAG::Vertex#reachable_from?no longer raise errors if the vertex passed as an argument is not a vertex in the sameDAG. Instead, they just returnfalse. -
Parallel edges are no longer allowed in the dag. Instead,
DAG#add_edgeraises anArgumentErrorif you try to add an edge between two adjacent vertices. If you want to model a multigraph, you can add a weight payload to the edges that contains a natural number.
New functions
-
DAG#topological_sortreturns a topological sort of the vertices in the dag in a theoretically optimal computational time complexity. -
DAG#enumerated_edgesreturns anEnumeratorof the edges in the dag.
Performance improvements
-
The computational complexity of
DAG::Vertex#outgoing_edgeshas improved to a constant because the edges are no longer stored in one array in theDAG. Instead, the edges are now stored in their respective sourceVertex. -
The performance of
DAG::Vertex#successorshas improved because firstly, it depends onDAG::Vertex#outgoing_edgesand secondly the call toArray#uniqis no longer necessary since parallel edges are prohibited. -
The computational complexities of
DAG::Vertex#descendants,DAG::Vertex#path_to?andDAG::Vertex#reachable_from?have improved because the functions depend onDAG::Vertex#successors -
I optimized
DAG::Vertex#path_to?further in commit 5d8f8e5 -
I optimized
DAG::Vertex#descendantsfurther in commit 58f8233 -
The computational complexity of
DAG::Vertex#incoming_edgesis unchanged: Linear in the number of all edges in theDAG. -
The performance of
DAG::Vertex#predecessorshas improved because the call toArray#uniqis no longer necessary since parallel edges are prohibited. -
The performance of
DAG::Vertex#ancestorshas improved because the function depends onDAG::Vertex#predecessorsand I optimizedDAG::Vertex#ancestorsfurther in commit 58f8233 -
The computational complexity of
DAG::add_edgehas improved because the cycle check in the function depends onDAG::Vertex#path_to?. -
The performance of
DAG#subgraphhas improved because the function depends onDAG::Vertex#descendants,DAG::Vertex#ancestorsandDAG::add_edge. And I optimizedDAG#subgraphfurther in commits eda52e4 and 6cd84dd -
The computational complexity of
DAG::edgeshas worsened from a constant complexity to a linear complexity. This is irrelevant if you want to iterate over all the edges in the graph. You should consider usingDAG#enumerated_edgesfor a better space utilization.