graphunk 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|