graphunk 0.3.0 → 0.4.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 +4 -4
- data/README.md +40 -6
- data/lib/graphunk/directed_graph.rb +5 -5
- data/lib/graphunk/graph.rb +17 -5
- data/lib/graphunk/undirected_graph.rb +2 -2
- data/lib/graphunk/weighted_graph.rb +4 -4
- data/lib/graphunk/weighted_undirected_graph.rb +9 -5
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5c8b58585a2175ae4116953c6f823d0bd3887efc
|
4
|
+
data.tar.gz: af62b1d472c236cabcb03b7e1dc1ab44967e89b8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b40e4673e48bc00dcf9b39570da7318ceed67f0fa2308ba9d038820b4155e511144f083afe5ea01714c1d5138b7c4cafe810a69ffdcbfdfb3aa6ba034752d4e9
|
7
|
+
data.tar.gz: 15f2947ffea732b89b6f7f53e6963ef565f93bb99a4cd63b93fe2390dcbe988a47f1733b430410ce8a0316a95e2f142986b2f2658ebb63bd1827dfbd30158852
|
data/README.md
CHANGED
@@ -4,18 +4,18 @@ Graphunk defines simple and fully-tested graph classes in Ruby which you can use
|
|
4
4
|
|
5
5
|
## Defining a Graph
|
6
6
|
|
7
|
-
|
7
|
+
### Unweighted Graphs
|
8
8
|
|
9
|
-
Graphs
|
9
|
+
Graphs are internally represented as a hash, so you can define a graph similarly to how you would define a Hash:
|
10
10
|
|
11
11
|
```
|
12
|
-
UndirectedGraph
|
12
|
+
UndirectedGraph.new({
|
13
13
|
'a' => ['b','c'],
|
14
14
|
'b' => ['c', 'd', 'e'],
|
15
15
|
'c' => ['d'],
|
16
16
|
'd' => ['e'],
|
17
17
|
'e' => []
|
18
|
-
|
18
|
+
})
|
19
19
|
```
|
20
20
|
|
21
21
|
Each key is a string representing a vertex, and the value is a list
|
@@ -29,12 +29,12 @@ In a directed graph, the order in an edge matters. A construction of a directed
|
|
29
29
|
might look like this:
|
30
30
|
|
31
31
|
```
|
32
|
-
DirectedGraph
|
32
|
+
DirectedGraph.new({
|
33
33
|
'a' => ['b','c'],
|
34
34
|
'b' => ['a'],
|
35
35
|
'c' => ['d'],
|
36
36
|
'd' => []
|
37
|
-
|
37
|
+
})
|
38
38
|
```
|
39
39
|
|
40
40
|
Graphs can also be built by individually adding edges and vertices.
|
@@ -46,6 +46,40 @@ graph.add_vertex('b')
|
|
46
46
|
graph.add_edge('a','b')
|
47
47
|
```
|
48
48
|
|
49
|
+
### Weighted Graphs
|
50
|
+
|
51
|
+
Weighted graphs have an additional property: each edge must specify a numerical weight.
|
52
|
+
|
53
|
+
To construct a weighted graph, you must pass in the vertex and edge information as well as the weights:
|
54
|
+
|
55
|
+
```
|
56
|
+
WeightedUndirectedGraph.new({
|
57
|
+
'a' => ['b','c'],
|
58
|
+
'b' => ['c', 'd', 'e'],
|
59
|
+
'c' => ['d'],
|
60
|
+
'd' => ['e'],
|
61
|
+
'e' => []
|
62
|
+
},
|
63
|
+
{
|
64
|
+
['a','b'] => 2,
|
65
|
+
['a','c'] => 4,
|
66
|
+
['b','c'] => 1,
|
67
|
+
['b','d'] => 4,
|
68
|
+
['b','e'] => 7,
|
69
|
+
['c','d'] => 4,
|
70
|
+
['d','e'] => 3
|
71
|
+
})
|
72
|
+
```
|
73
|
+
|
74
|
+
You can also build them by adding vertices and edges.
|
75
|
+
```
|
76
|
+
graph = WeightedUndirectedGraph.new
|
77
|
+
graph.add_vertex('a')
|
78
|
+
graph.add_vertex('b')
|
79
|
+
graph.add_edge('a','b',3)
|
80
|
+
```
|
81
|
+
Now the edge 'a-b' will have a weight of 3.
|
82
|
+
|
49
83
|
## Testing
|
50
84
|
|
51
85
|
To run the test suite simply execute:
|
@@ -3,7 +3,7 @@ class DirectedGraph < Graph
|
|
3
3
|
if edge_exists?(first_vertex, second_vertex)
|
4
4
|
raise ArgumentError, "This edge already exists"
|
5
5
|
elsif vertex_exists?(first_vertex) && vertex_exists?(second_vertex)
|
6
|
-
|
6
|
+
@representation[first_vertex] << second_vertex
|
7
7
|
else
|
8
8
|
raise ArgumentError, "One of the vertices referenced does not exist in the graph"
|
9
9
|
end
|
@@ -11,7 +11,7 @@ class DirectedGraph < Graph
|
|
11
11
|
|
12
12
|
def remove_edge(first_vertex, second_vertex)
|
13
13
|
if edge_exists?(first_vertex, second_vertex)
|
14
|
-
|
14
|
+
@representation[first_vertex].delete(second_vertex)
|
15
15
|
else
|
16
16
|
raise ArgumentError, "That edge does not exist in the graph"
|
17
17
|
end
|
@@ -19,7 +19,7 @@ class DirectedGraph < Graph
|
|
19
19
|
|
20
20
|
def neighbors_of_vertex(name)
|
21
21
|
if vertex_exists?(name)
|
22
|
-
|
22
|
+
@representation[name]
|
23
23
|
else
|
24
24
|
raise ArgumentError, "That vertex does not exist in the graph"
|
25
25
|
end
|
@@ -53,9 +53,9 @@ class DirectedGraph < Graph
|
|
53
53
|
|
54
54
|
def reachable_by_two_path(start)
|
55
55
|
if vertex_exists?(start)
|
56
|
-
reached_vertices =
|
56
|
+
reached_vertices = @representation[start]
|
57
57
|
reached_vertices.each do |vertex|
|
58
|
-
reached_vertices +=
|
58
|
+
reached_vertices += @representation[vertex]
|
59
59
|
end
|
60
60
|
reached_vertices.uniq
|
61
61
|
else
|
data/lib/graphunk/graph.rb
CHANGED
@@ -1,13 +1,17 @@
|
|
1
1
|
# this class should not be invoked directly
|
2
|
-
class Graph
|
2
|
+
class Graph
|
3
|
+
def initialize(hash = {})
|
4
|
+
@representation = hash
|
5
|
+
end
|
6
|
+
|
3
7
|
def vertices
|
4
|
-
keys
|
8
|
+
@representation.keys
|
5
9
|
end
|
6
10
|
|
7
11
|
def edges
|
8
12
|
[].tap do |edge_constructor|
|
9
13
|
vertices.each do |vertex|
|
10
|
-
|
14
|
+
@representation[vertex].each do |neighbor|
|
11
15
|
edge_constructor << [vertex, neighbor]
|
12
16
|
end
|
13
17
|
end
|
@@ -16,18 +20,26 @@ class Graph < Hash
|
|
16
20
|
|
17
21
|
def add_vertex(name)
|
18
22
|
unless vertex_exists?(name)
|
19
|
-
|
23
|
+
@representation[name] = []
|
20
24
|
else
|
21
25
|
raise ArgumentError, "Vertex already exists"
|
22
26
|
end
|
23
27
|
end
|
24
28
|
|
29
|
+
def add_vertices(*names)
|
30
|
+
if (names & vertices).count == 0
|
31
|
+
names.each { |name| add_vertex(name) }
|
32
|
+
else
|
33
|
+
raise ArgumentError, "One or more of the given vertices already exists"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
25
37
|
def remove_vertex(name)
|
26
38
|
if vertex_exists?(name)
|
27
39
|
edges.each do |edge|
|
28
40
|
remove_edge(edge.first, edge.last) if edge.include?(name)
|
29
41
|
end
|
30
|
-
|
42
|
+
@representation.delete(name)
|
31
43
|
else
|
32
44
|
raise ArgumentError, "That vertex does not exist in the graph"
|
33
45
|
end
|
@@ -6,7 +6,7 @@ class UndirectedGraph < Graph
|
|
6
6
|
raise ArgumentError, "This edge already exists"
|
7
7
|
elsif vertex_exists?(first_vertex) && vertex_exists?(second_vertex)
|
8
8
|
ordered_vertices = order_vertices(first_vertex, second_vertex)
|
9
|
-
|
9
|
+
@representation[ordered_vertices.first] << ordered_vertices.last
|
10
10
|
else
|
11
11
|
raise ArgumentError, "One of the vertices referenced does not exist in the graph"
|
12
12
|
end
|
@@ -15,7 +15,7 @@ class UndirectedGraph < Graph
|
|
15
15
|
def remove_edge(first_vertex, second_vertex)
|
16
16
|
if edge_exists?(first_vertex, second_vertex)
|
17
17
|
ordered_vertices = order_vertices(first_vertex, second_vertex)
|
18
|
-
|
18
|
+
@representation[ordered_vertices.first].delete(ordered_vertices.last)
|
19
19
|
else
|
20
20
|
raise ArgumentError, "That edge does not exist in the graph"
|
21
21
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
#this class should not be invoked directly
|
2
2
|
class WeightedGraph < Graph
|
3
3
|
|
4
|
-
|
4
|
+
attr_reader :weights
|
5
5
|
|
6
|
-
def initialize
|
7
|
-
|
8
|
-
@weights =
|
6
|
+
def initialize(hash = {}, weights = {})
|
7
|
+
@representation = hash
|
8
|
+
@weights = weights
|
9
9
|
end
|
10
10
|
end
|
@@ -5,8 +5,8 @@ class WeightedUndirectedGraph < WeightedGraph
|
|
5
5
|
raise ArgumentError, "This edge already exists"
|
6
6
|
elsif vertex_exists?(v) && vertex_exists?(u)
|
7
7
|
ordered_vertices = order_vertices(v, u)
|
8
|
-
|
9
|
-
weights[ordered_vertices] = w
|
8
|
+
@representation[ordered_vertices.first] << ordered_vertices.last
|
9
|
+
@weights[ordered_vertices] = w
|
10
10
|
else
|
11
11
|
raise ArgumentError, "One of the vertices referenced does not exist in the graph"
|
12
12
|
end
|
@@ -15,16 +15,20 @@ class WeightedUndirectedGraph < WeightedGraph
|
|
15
15
|
def remove_edge(v, u)
|
16
16
|
if edge_exists?(v, u)
|
17
17
|
ordered_vertices = order_vertices(v, u)
|
18
|
-
|
18
|
+
@representation[ordered_vertices.first].delete(ordered_vertices.last)
|
19
19
|
remove_weight(v, u)
|
20
20
|
else
|
21
21
|
raise ArgumentError, "That edge does not exist in the graph"
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
+
def edge_weight(edge)
|
26
|
+
@weights[edge]
|
27
|
+
end
|
28
|
+
|
25
29
|
def adjust_weight(v, u, w)
|
26
30
|
if edge_exists?(v, u)
|
27
|
-
weights[[v,u]] = w
|
31
|
+
@weights[[v,u]] = w
|
28
32
|
else
|
29
33
|
raise ArgumentError, "That edge does not exist in the graph"
|
30
34
|
end
|
@@ -54,6 +58,6 @@ class WeightedUndirectedGraph < WeightedGraph
|
|
54
58
|
end
|
55
59
|
|
56
60
|
def remove_weight(v, u)
|
57
|
-
weights.delete(order_vertices(v, u))
|
61
|
+
@weights.delete(order_vertices(v, u))
|
58
62
|
end
|
59
63
|
end
|