rgl 0.5.10 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dc32ab84139b87296c8e71d490843c6d90c83257e7876f42f25b23d0ae852cd8
4
- data.tar.gz: 44177136283480e8ba77c431343d7a6361d3ac79e58b1d5864d8a74556ca4bd6
3
+ metadata.gz: fe8f8fb223d89b38255ea2b695204a5fc6f686773c5f127945929d75013ac2bb
4
+ data.tar.gz: 8df8df91c990d1c9bf564069681f9ee1176636ff9507f470db5260032af768b1
5
5
  SHA512:
6
- metadata.gz: 51c96d180f5d570bfcaeaf5653749bf432fbcb3a82e75336f7883c0e45bc48de0d20e2ce6b26238fc9678bc0449e8564d7c1902f1dbd7101f6680d4481847850
7
- data.tar.gz: eb9dcbd3b6ee1d870c2a2afeaab045bc29107ef5b9fc1b9fa2dc356ca5961a87c0bcf8d7d32dca2fb5add0e466598bc167128a4fbebc6f4428970439adfcd02d
6
+ metadata.gz: 7f4caa6a12dc76b702c4f7d0219614d2617861fd331614446731bf2270fa71dd768d2f78d44b1a786c9fb5f9934ec61f1030b77ec16a2922c92f4051b5c9403f
7
+ data.tar.gz: 4343592e3a7bd1bbdfd45ab839a16b38f3dec3d336a0febc1f44876c889b058db052e3dff85323a5f4a10a87293e7ca4f9f05d6b5735a23ce75048c418dff3f8
data/README.md CHANGED
@@ -1,4 +1,8 @@
1
- # Ruby Graph Library (RGL)[![Test](https://github.com/monora/rgl/actions/workflows/test.yml/badge.svg)](https://github.com/monora/rgl/actions/workflows/test.yml) [![Doc](https://github.com/monora/rgl/actions/workflows/doc.yml/badge.svg)](https://github.com/monora/rgl/actions/workflows/doc.yml) [<img src="https://codeclimate.com/github/monora/rgl/badges/coverage.svg" />](https://codeclimate.com/github/monora/rgl/coverage) [<img src="https://badge.fury.io/rb/rgl.svg" alt="Version"/>](https://badge.fury.io/rb/rgl) [![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/monora/rgl)
1
+ # Ruby Graph Library (RGL)
2
+ [![Test](https://github.com/monora/rgl/actions/workflows/test.yml/badge.svg)](https://github.com/monora/rgl/actions/workflows/test.yml) [![Doc](https://github.com/monora/rgl/actions/workflows/doc.yml/badge.svg)](https://github.com/monora/rgl/actions/workflows/doc.yml)
3
+ [![Version](https://badge.fury.io/rb/rgl.svg)](https://badge.fury.io/rb/rgl)
4
+ [![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/monora/rgl)
5
+ [![Code Climate](https://img.shields.io/codeclimate/maintainability/monora/rgl)](https://codeclimate.com/github/monora/rgl)
2
6
 
3
7
  RGL is a framework for graph data structures and algorithms.
4
8
 
data/lib/rgl/adjacency.rb CHANGED
@@ -161,6 +161,7 @@ module RGL
161
161
 
162
162
  end # class AdjacencyGraph
163
163
 
164
+
164
165
  module Graph
165
166
 
166
167
  # Convert a general graph to an AdjacencyGraph. If the graph is directed,
data/lib/rgl/base.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # base.rb
2
2
 
3
3
  # version information
4
- RGL_VERSION = "0.5.10"
4
+ RGL_VERSION = "0.6.0"
5
5
 
6
6
  # Module {RGL} defines the namespace for all modules and classes of the graph
7
7
  # library. The main module is {Graph} which defines the abstract behavior of
@@ -192,6 +192,15 @@ module RGL
192
192
  include?(v) # inherited from enumerable
193
193
  end
194
194
 
195
+ # Returns true if +(u, v)+ is an edge of the graph.
196
+ # @param (see #each_edge)
197
+ def has_edge?(u, v)
198
+ each_adjacent(u) do |w|
199
+ return true if v == w
200
+ end
201
+ return false
202
+ end
203
+
195
204
  # Returns true if the graph has no vertices, i.e. num_vertices == 0.
196
205
  #
197
206
  def empty?
@@ -27,6 +27,7 @@ module RGL
27
27
 
28
28
  end
29
29
 
30
+ # This class implements {Graph#bellman_ford_shortest_paths}.
30
31
  class BellmanFordAlgorithm
31
32
 
32
33
  # Initializes Bellman-Ford algorithm for a _graph_ with provided edges weights map.
@@ -89,6 +90,7 @@ module RGL
89
90
 
90
91
  end # class BellmanFordAlgorithm
91
92
 
93
+
92
94
  module Graph
93
95
 
94
96
  # Finds the shortest paths from the _source_ to each vertex of the graph.
@@ -26,6 +26,20 @@ module RGL
26
26
  yield u
27
27
  end
28
28
 
29
+ alias :each_out_neighbor :each_adjacent
30
+
31
+ def has_in_edge?(u, v)
32
+ raise NotImplementedError
33
+ end
34
+
35
+ alias :has_out_edge? :has_edge?
36
+
37
+ def in_neighbors(v)
38
+ raise NotImplementedError
39
+ end
40
+
41
+ alias :out_neighbors :adjacent_vertices
42
+
29
43
  # Returns the number of in-edges (for directed graphs) or the number of
30
44
  # incident edges (for undirected graphs) of vertex _v_.
31
45
  # @return [int]
@@ -0,0 +1,74 @@
1
+ # bidirectional_adjacency.rb
2
+ #
3
+ require 'rgl/adjacency'
4
+ require 'rgl/bidirectional'
5
+
6
+ module RGL
7
+
8
+ # This implementation of {BidirectionalGraph} creates an internal
9
+ # {DirectedAdjacencyGraph} to store the in-edges and overrides methods
10
+ # to ensure that the out and in graphs remain synchronized.
11
+ #
12
+ class BidirectionalAdjacencyGraph < DirectedAdjacencyGraph
13
+
14
+ include BidirectionalGraph
15
+
16
+ # @see DirectedAdjacencyGraph#initialize
17
+ #
18
+ # In super method the in edges are also added since {add_edge} of this class
19
+ # also inserts edges in `@reverse`.
20
+ def initialize(edgelist_class = Set, *other_graphs)
21
+ @reverse = DirectedAdjacencyGraph.new(edgelist_class)
22
+ super(edgelist_class, *other_graphs)
23
+ end
24
+
25
+ # We don't need to override add_vertex() because the reverse graph doesn't need to
26
+ # contain any unconnected vertices. Vertices will be added by add_edge() as
27
+ # required.
28
+
29
+ # @see MutableGraph#add_edge.
30
+ def add_edge(u, v)
31
+ super(u, v)
32
+ @reverse.add_edge(v, u)
33
+ end
34
+
35
+ # @see MutableGraph#remove_vertex.
36
+ def remove_vertex(v)
37
+ super(v)
38
+ @reverse.remove_vertex(v)
39
+ end
40
+
41
+ # @see MutableGraph::remove_edge.
42
+ def remove_edge(u, v)
43
+ super(u, v)
44
+ @reverse.remove_edge(v, u)
45
+ end
46
+
47
+ # @see Graph#has_edge?
48
+ def has_in_edge?(u, v)
49
+ @reverse.has_edge?(u, v)
50
+ end
51
+
52
+ alias :has_out_edge? :has_edge?
53
+
54
+ # @see BidirectionalGraph#each_in_neighbor
55
+ def each_in_neighbor(v)
56
+ @reverse.each_adjacent(v)
57
+ end
58
+
59
+ alias :each_out_neighbor :each_adjacent
60
+
61
+ def in_neighbors(v)
62
+ @reverse.adjacent_vertices(v)
63
+ end
64
+
65
+ # Returns the number of in-edges (for directed graphs) or the number of
66
+ # incident edges (for undirected graphs) of vertex _v_.
67
+ # @return [int]
68
+ def in_degree(v)
69
+ @reverse.out_degree(v)
70
+ end
71
+
72
+ end
73
+
74
+ end
data/lib/rgl/bipartite.rb CHANGED
@@ -41,6 +41,7 @@ module RGL
41
41
 
42
42
  end # module Graph
43
43
 
44
+
44
45
  class BipartiteBFSIterator < BFSIterator
45
46
 
46
47
  attr_reader :bipartite_sets_map, :found_odd_cycle
data/lib/rgl/dijkstra.rb CHANGED
@@ -6,6 +6,7 @@ require 'pairing_heap'
6
6
 
7
7
  module RGL
8
8
 
9
+ # This class implements {Graph#dijkstra_shortest_path} and {Graph#dijkstra_shortest_paths}
9
10
  class DijkstraAlgorithm
10
11
 
11
12
  # Distance combinator is a lambda that accepts the distance (usually from the source) to vertex _u_ and the weight
@@ -103,6 +104,7 @@ module RGL
103
104
 
104
105
  end # class DijkstraAlgorithm
105
106
 
107
+
106
108
  module Graph
107
109
 
108
110
  # Finds the shortest path from the _source_ to the _target_ in the graph.
@@ -37,6 +37,7 @@ module RGL
37
37
 
38
38
  end # EdgePropertiesMap
39
39
 
40
+
40
41
  class NonNegativeEdgePropertiesMap < EdgePropertiesMap
41
42
 
42
43
  private
@@ -52,4 +53,4 @@ module RGL
52
53
 
53
54
  end
54
55
 
55
- end
56
+ end
@@ -116,6 +116,7 @@ module RGL
116
116
 
117
117
  end # class EdmondsKarpAlgorithm
118
118
 
119
+
119
120
  module Graph
120
121
 
121
122
  # Finds the maximum flow from the _source_ to the _sink_ in the graph.
data/lib/rgl/implicit.rb CHANGED
@@ -97,6 +97,7 @@ module RGL
97
97
 
98
98
  end # class ImplicitGraph
99
99
 
100
+
100
101
  module Graph
101
102
 
102
103
  # Returns a new {ImplicitGraph} which has as vertices all vertices of the
@@ -1,6 +1,6 @@
1
1
  module RGL
2
2
 
3
- class PathBuilder
3
+ class PathBuilder
4
4
 
5
5
  def initialize(source, parents_map)
6
6
  @source = source
data/lib/rgl/prim.rb CHANGED
@@ -34,6 +34,7 @@ module RGL
34
34
 
35
35
  end # class PrimAlgorithm
36
36
 
37
+
37
38
  module Graph
38
39
 
39
40
  # Finds the minimum spanning tree of the graph.
data/lib/rgl/topsort.rb CHANGED
@@ -8,10 +8,10 @@ module RGL
8
8
  #
9
9
  # The topological sort algorithm creates a linear ordering of the vertices
10
10
  # such that if edge (u,v) appears in the graph, then u comes before v in
11
- # the ordering. The graph must be a directed acyclic graph (DAG).
11
+ # the ordering. The graph must be a directed acyclic graph.
12
12
  #
13
13
  # The iterator can also be applied to an undirected graph or to a directed graph
14
- # which contains a cycle. In this case, the Iterator does not reach all
14
+ # which contains a cycle. In this case, the iterator does not reach all
15
15
  # vertices. The implementation of {Graph#acyclic?} uses this fact.
16
16
  #
17
17
  # @see Graph#topsort_iterator
@@ -60,6 +60,7 @@ module RGL
60
60
 
61
61
  end # class TopsortIterator
62
62
 
63
+
63
64
  module Graph
64
65
 
65
66
  # @return [TopsortIterator] for the graph.
data/lib/rgl/traversal.rb CHANGED
@@ -104,6 +104,7 @@ module RGL
104
104
 
105
105
  end # class BFSIterator
106
106
 
107
+
107
108
  module Graph
108
109
 
109
110
  # @return [BFSIterator] starting at vertex _v_.
@@ -161,6 +162,7 @@ module RGL
161
162
 
162
163
  end # class DFSVisitor
163
164
 
165
+
164
166
  module Graph
165
167
 
166
168
  # @return [DFSIterator] staring at vertex _v_.
@@ -0,0 +1,169 @@
1
+ require 'test_helper'
2
+
3
+ require 'rgl/bidirectional_adjacency'
4
+ require 'directed_graph_test'
5
+
6
+ include RGL
7
+ include RGL::Edge
8
+
9
+ class TestBidirectionalAdjacencyGraph < Test::Unit::TestCase
10
+ def setup
11
+ @edges = [[1, 2], [1, 3], [2, 3], [2, 4], [2, 5], [2, 6], [3, 2], [3, 7], [3, 8],
12
+ [5, 10], [6, 9], [7, 9], [7, 10], [8, 10]]
13
+ @out_neighbors = Hash.new { |h, k| h[k] = Set.new }
14
+ @in_neighbors = Hash.new { |h, k| h[k] = Set.new }
15
+ @edges.each do |e|
16
+ @out_neighbors[e[0]] << e[1]
17
+ @in_neighbors[e[1]] << e[0]
18
+ end
19
+ @dg = BidirectionalAdjacencyGraph.new
20
+ @edges.each do |(src, target)|
21
+ @dg.add_edge(src, target)
22
+ end
23
+ @eg = BidirectionalAdjacencyGraph.new
24
+ @gfa = BidirectionalAdjacencyGraph[1, 2, 3, 4]
25
+ end
26
+
27
+ def test_empty_graph
28
+ assert @eg.empty?
29
+ assert @eg.directed?
30
+ assert(!@eg.has_edge?(2, 1))
31
+ assert(!@eg.has_out_edge?(2, 1))
32
+ assert(!@eg.has_in_edge?(1, 2))
33
+ assert(!@eg.has_vertex?(3))
34
+ # Non existent vertex result in a Name Error because each_key is
35
+ # called for nil
36
+ assert_raises(NoVertexError) { @eg.out_degree(3) }
37
+ assert_raises(NoVertexError) { @eg.in_degree(3) }
38
+ assert_equal([], @eg.vertices)
39
+ assert_equal(0, @eg.size)
40
+ assert_equal(0, @eg.num_vertices)
41
+ assert_equal(0, @eg.num_edges)
42
+ assert_equal(DirectedEdge, @eg.edge_class)
43
+ assert_empty(@eg.edges)
44
+ end
45
+
46
+ def test_add
47
+ @eg.add_edge(1, 2)
48
+ assert(!@eg.empty?)
49
+ assert(@eg.has_edge?(1, 2))
50
+ assert(@eg.has_out_edge?(1, 2))
51
+ assert(@eg.has_in_edge?(2, 1))
52
+ assert(!@eg.has_edge?(2, 1))
53
+ assert(!@eg.has_out_edge?(2, 1))
54
+ assert(!@eg.has_in_edge?(1, 2))
55
+ assert(@eg.has_vertex?(1) && @eg.has_vertex?(2))
56
+ assert(!@eg.has_vertex?(3))
57
+
58
+ assert_equal([1, 2], @eg.vertices.sort)
59
+ assert([DirectedEdge.new(1, 2)].eql?(@eg.edges))
60
+ assert_equal("(1-2)", @eg.edges.join)
61
+
62
+ assert_equal([2], @eg.adjacent_vertices(1))
63
+ assert_equal([2], @eg.out_neighbors(1))
64
+ assert_equal([], @eg.in_neighbors(1))
65
+ assert_equal([], @eg.adjacent_vertices(2))
66
+ assert_equal([], @eg.out_neighbors(2))
67
+ assert_equal([1], @eg.in_neighbors(2))
68
+
69
+ assert_equal(1, @eg.out_degree(1))
70
+ assert_equal(0, @eg.in_degree(1))
71
+ assert_equal(0, @eg.out_degree(2))
72
+ assert_equal(1, @eg.in_degree(2))
73
+ end
74
+
75
+ def test_edges
76
+ assert_equal(14, @dg.edges.length)
77
+ assert_equal(@edges.map { |e| e[0] }.to_set, @dg.edges.map { |l| l.source }.to_set)
78
+ assert_equal(@edges.map { |e| e[1] }.to_set, @dg.edges.map { |l| l.target }.to_set)
79
+ assert_equal("(1-2)(1-3)(2-3)(2-4)(2-5)(2-6)(3-2)(3-7)(3-8)(5-10)(6-9)(7-10)(7-9)(8-10)", @dg.edges.map { |l| l.to_s }.sort.join)
80
+ end
81
+
82
+ def test_vertices
83
+ assert_equal(@edges.flatten.to_set, @dg.vertices.to_set)
84
+ end
85
+
86
+ def test_edges_from_to?
87
+ @edges.each do |u, v|
88
+ assert @dg.has_edge?(u, v)
89
+ assert @dg.has_out_edge?(u, v)
90
+ assert @dg.has_in_edge?(v, u)
91
+ end
92
+ end
93
+
94
+ def test_remove_edges
95
+ @dg.remove_edge 1, 2
96
+ assert !@dg.has_edge?(1, 2)
97
+ assert !@dg.has_out_edge?(1, 2)
98
+ assert !@dg.has_in_edge?(2, 1)
99
+ @dg.remove_edge 1, 2
100
+ assert !@dg.has_edge?(1, 2)
101
+ assert !@dg.has_out_edge?(1, 2)
102
+ assert !@dg.has_in_edge?(2, 1)
103
+ @dg.remove_vertex 3
104
+ assert !@dg.has_vertex?(3)
105
+ assert !@dg.has_edge?(2, 3)
106
+ assert !@dg.has_out_edge?(2, 3)
107
+ assert !@dg.has_in_edge?(3, 2)
108
+ assert_equal('(2-4)(2-5)(2-6)(5-10)(6-9)(7-9)(7-10)(8-10)', @dg.edges.join)
109
+ end
110
+
111
+ def test_add_vertices
112
+ @eg.add_vertices 1, 3, 2, 4
113
+ assert_equal @eg.vertices.sort, [1, 2, 3, 4]
114
+
115
+ @eg.remove_vertices 1, 3
116
+ assert_equal @eg.vertices.sort, [2, 4]
117
+ end
118
+
119
+ def test_creating_from_array
120
+ assert_equal([1, 2, 3, 4], @gfa.vertices.sort)
121
+ assert_equal('(1-2)(3-4)', @gfa.edges.join)
122
+ end
123
+
124
+ def test_creating_from_graphs
125
+ dg2 = BidirectionalAdjacencyGraph.new(Set, @dg, @gfa)
126
+ assert_equal(dg2.vertices.to_set, (@dg.vertices + @gfa.vertices).to_set)
127
+ assert_equal(dg2.edges.to_set, (@dg.edges + @gfa.edges).to_set)
128
+ end
129
+
130
+ def test_reverse
131
+ # Add isolated vertex
132
+ @dg.add_vertex(42)
133
+ reverted = @dg.reverse
134
+
135
+ @dg.each_edge do |u, v|
136
+ assert(reverted.has_edge?(v, u))
137
+ end
138
+
139
+ assert(reverted.has_vertex?(42), 'Reverted graph should contain isolated Vertex 42')
140
+ end
141
+
142
+ def test_to_undirected
143
+ undirected = @dg.to_undirected
144
+ assert_equal '(1=2)(1=3)(2=3)(2=4)(2=5)(2=6)(3=7)(3=8)(5=10)(6=9)(7=9)(7=10)(8=10)', undirected.edges.sort.join
145
+ end
146
+
147
+ def test_neighbors
148
+ @edges.flatten.to_set.each do |v|
149
+ assert_equal @out_neighbors[v], @dg.out_neighbors(v).to_set
150
+ assert_equal @in_neighbors[v], @dg.in_neighbors(v).to_set
151
+ end
152
+ end
153
+
154
+ def test_each_neighbor
155
+ @edges.flatten.to_set.each do |v|
156
+ assert_equal @out_neighbors[v], @dg.each_out_neighbor(v).inject(Set.new) { |s, v| s << v }
157
+ assert_equal @in_neighbors[v], @dg.each_in_neighbor(v).inject(Set.new) { |s, v| s << v }
158
+ end
159
+ end
160
+
161
+ def test_degrees
162
+ @edges.flatten.to_set.each do |v|
163
+ assert_equal @out_neighbors[v].size, @dg.out_degree(v)
164
+ assert_equal @in_neighbors[v].size, @dg.in_degree(v)
165
+ assert_equal @out_neighbors[v].size + @in_neighbors[v].size, @dg.degree(v)
166
+ end
167
+ end
168
+
169
+ end
@@ -11,6 +11,7 @@ class TestDirectedGraph < Test::Unit::TestCase
11
11
  [[1, 2], [2, 3], [3, 2], [2, 4]].each do |(src, target)|
12
12
  @dg.add_edge(src, target)
13
13
  end
14
+ @gfa = DirectedAdjacencyGraph[1, 2, 3, 4]
14
15
  end
15
16
 
16
17
  def test_empty_graph
@@ -86,17 +87,23 @@ class TestDirectedGraph < Test::Unit::TestCase
86
87
 
87
88
  def test_add_vertices
88
89
  dg = DirectedAdjacencyGraph.new
89
- dg.add_vertices 1, 3, 2, 4
90
- assert_equal dg.vertices.sort, [1, 2, 3, 4]
90
+ dg.add_vertices(1, 3, 2, 4)
91
+ assert_equal(dg.vertices.sort, [1, 2, 3, 4])
91
92
 
92
- dg.remove_vertices 1, 3
93
- assert_equal dg.vertices.sort, [2, 4]
93
+ dg.remove_vertices(1, 3)
94
+ assert_equal(dg.vertices.sort, [2, 4])
94
95
  end
95
96
 
96
97
  def test_creating_from_array
97
- dg = DirectedAdjacencyGraph[1, 2, 3, 4]
98
- assert_equal([1, 2, 3, 4], dg.vertices.sort)
99
- assert_equal('(1-2)(3-4)', dg.edges.join)
98
+ assert_equal([1, 2, 3, 4], @gfa.vertices.sort)
99
+ assert_equal('(1-2)(3-4)', @gfa.edges.join)
100
+ end
101
+
102
+ def test_creating_from_graphs
103
+ @gfa.each_edge { |e| @dg.add_edge(e[0], e[1])}
104
+ dg = DirectedAdjacencyGraph.new(Set, @dg, @gfa)
105
+ assert_equal(dg.vertices.to_set, (@dg.vertices + @gfa.vertices).to_set)
106
+ assert_equal(dg.edges.to_set, (@dg.edges + @gfa.edges).to_set)
100
107
  end
101
108
 
102
109
  def test_reverse
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rgl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.10
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Horst Duchene
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire: rgl/base
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2023-01-02 00:00:00.000000000 Z
12
+ date: 2023-02-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: stream
@@ -191,6 +191,7 @@ files:
191
191
  - lib/rgl/base.rb
192
192
  - lib/rgl/bellman_ford.rb
193
193
  - lib/rgl/bidirectional.rb
194
+ - lib/rgl/bidirectional_adjacency.rb
194
195
  - lib/rgl/bipartite.rb
195
196
  - lib/rgl/condensation.rb
196
197
  - lib/rgl/connected_components.rb
@@ -215,6 +216,7 @@ files:
215
216
  - lib/rgl/traversal.rb
216
217
  - rakelib/dep_graph.rake
217
218
  - test/bellman_ford_test.rb
219
+ - test/bidirectional_graph_test.rb
218
220
  - test/bipartite_test.rb
219
221
  - test/components_test.rb
220
222
  - test/cycles_test.rb