graphsrb 0.1.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 +7 -0
- data/.DS_Store +0 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +37 -0
- data/LICENSE.txt +21 -0
- data/README.md +338 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/examples/ApproxAlgs/TSP/tsp.rb +45 -0
- data/examples/ApproxAlgs/VertexCover/main.rb +34 -0
- data/examples/ApproxAlgs/VertexCover/vertex_cover.rb +31 -0
- data/examples/Apts/artc_points.rb +59 -0
- data/examples/Apts/main.rb +51 -0
- data/examples/BackEdges/backedges.rb +37 -0
- data/examples/BackEdges/main.rb +41 -0
- data/examples/BipartMatching/bipart_matching.rb +34 -0
- data/examples/BipartMatching/main.rb +58 -0
- data/examples/EdgeConnectivity/econ.rb +31 -0
- data/examples/EdgeConnectivity/main.rb +34 -0
- data/examples/Euler/euler_tour.rb +36 -0
- data/examples/Euler/main.rb +19 -0
- data/examples/FundCircuits/fund_circuits.rb +49 -0
- data/examples/FundCircuits/main.rb +34 -0
- data/examples/FundCutsets/fund_cutsets.rb +50 -0
- data/examples/FundCutsets/main.rb +26 -0
- data/examples/MaxFlow/edmonds_karp.rb +91 -0
- data/examples/MaxFlow/main.rb +79 -0
- data/examples/PrimMST/main.rb +46 -0
- data/examples/PrimMST/prim.rb +82 -0
- data/examples/ProbAlgs/LargeCustset/large_cutset.rb +59 -0
- data/examples/ProbAlgs/LargeCustset/main.rb +45 -0
- data/graphsrb.gemspec +39 -0
- data/lib/graphsrb.rb +11 -0
- data/lib/graphsrb/adjacency_list.rb +78 -0
- data/lib/graphsrb/base_graph.rb +167 -0
- data/lib/graphsrb/diedge.rb +6 -0
- data/lib/graphsrb/digraph.rb +93 -0
- data/lib/graphsrb/edge.rb +34 -0
- data/lib/graphsrb/exceptions.rb +5 -0
- data/lib/graphsrb/graph.rb +46 -0
- data/lib/graphsrb/node.rb +21 -0
- data/lib/graphsrb/version.rb +3 -0
- data/lib/graphsrb/vertex.rb +32 -0
- metadata +149 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
require_relative 'econ'
|
2
|
+
|
3
|
+
def run_example(example_title, graph)
|
4
|
+
puts "------- #{example_title} ------ "
|
5
|
+
puts "The graph has #{graph.vertex_count} vertices and #{graph.edge_count} edges"
|
6
|
+
puts "Edge connectivity is #{EdgeConnectivity.run(graph)}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def main
|
10
|
+
edges = [
|
11
|
+
[1,2], [1,3], [1,5],
|
12
|
+
[2,4], [2,5], [2,6],
|
13
|
+
[3,4], [3,6],
|
14
|
+
[4,5], [4,6],
|
15
|
+
[5,6]
|
16
|
+
]
|
17
|
+
run_example('Example 1', Graph.new(edges:edges))
|
18
|
+
|
19
|
+
edges = [
|
20
|
+
[1,2], [1,3],
|
21
|
+
[2,4],
|
22
|
+
[3,4],
|
23
|
+
[4,5],
|
24
|
+
[5,6], [5,6],
|
25
|
+
[6,7]
|
26
|
+
]
|
27
|
+
run_example('Example 2', Graph.new(edges:edges))
|
28
|
+
|
29
|
+
edges = [[1,2],[2,3],[3,4],[4,1],[1,3],[4,2]]
|
30
|
+
run_example('Example 3', Graph.new(edges:edges))
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
main
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'graphsrb'
|
2
|
+
include Graphsrb
|
3
|
+
|
4
|
+
#Assumption: graph is nonempty connected undirected and all vertices have even degree
|
5
|
+
module EulerTour
|
6
|
+
def self.run(graph)
|
7
|
+
init(graph)
|
8
|
+
dfs(graph.vertices.first)
|
9
|
+
return @circuit.reverse
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.init(g)
|
13
|
+
@graph = g
|
14
|
+
@circuit = []
|
15
|
+
@stack = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.dfs(v)
|
19
|
+
vertices = @graph.adjacent_vertices(v)
|
20
|
+
#If current vertex has no neighbors then
|
21
|
+
#add it to circuit, remove the last vertex from the stack
|
22
|
+
#and move to this vertex
|
23
|
+
if vertices.size == 0
|
24
|
+
@circuit << v
|
25
|
+
dfs(@stack.pop) unless @stack.empty?
|
26
|
+
else
|
27
|
+
#Add the vertex to the stack,
|
28
|
+
#take any of its neighbors u, remove the edge (v,u),
|
29
|
+
# and move to the vertex u.
|
30
|
+
@stack.push(v)
|
31
|
+
u = vertices.first
|
32
|
+
@graph.remove_edge(v, u)
|
33
|
+
dfs(u)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative 'euler_tour'
|
2
|
+
|
3
|
+
def run_example(example_title, graph)
|
4
|
+
puts "------- #{example_title} ------ "
|
5
|
+
puts "The graph has #{graph.vertex_count} vertices and #{graph.edge_count} edges"
|
6
|
+
tour = EulerTour.run(graph)
|
7
|
+
puts "Euler tour:"
|
8
|
+
tour.each{|v| puts v}
|
9
|
+
end
|
10
|
+
|
11
|
+
def main
|
12
|
+
edges = [[1,2],[2,3],[3,4],[4,1]]
|
13
|
+
run_example('Example 1', Graph.new(edges:edges))
|
14
|
+
|
15
|
+
edges = [[1,2],[2,3],[3,4],[4,1],[2,7],[2,5],[7,6],[5,6]]
|
16
|
+
run_example('Example 2', Graph.new(edges:edges))
|
17
|
+
end
|
18
|
+
|
19
|
+
main
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'graphsrb'
|
2
|
+
require_relative '../PrimMST/prim'
|
3
|
+
include Graphsrb
|
4
|
+
|
5
|
+
# Consider a graph G and a spanning tree T of the graph G.
|
6
|
+
# +Fundamental circuit+ is a circuit created by adding and edge to T.
|
7
|
+
# The fundamental curcuits form a basis for all circuits in the graph.
|
8
|
+
|
9
|
+
# Computes the set of fundamental circuits with
|
10
|
+
# respect to a spanning tree
|
11
|
+
module FundCircuits
|
12
|
+
def self.run(graph)
|
13
|
+
edges = PrimMST.run(graph)
|
14
|
+
#Create a spanning tree
|
15
|
+
@spanning_tree = Graph.new
|
16
|
+
edges.each{|e| @spanning_tree.add_edge(e.initial_vertex.id, e.terminal_vertex.id)}
|
17
|
+
cotree = graph.edges - edges
|
18
|
+
|
19
|
+
puts "Spanning tree:"
|
20
|
+
puts @spanning_tree.edges
|
21
|
+
puts "-"*25
|
22
|
+
|
23
|
+
fcs = []
|
24
|
+
#Computes fundamental circuits
|
25
|
+
#as sequences of vertices
|
26
|
+
cotree.each{|e| fcs << compute_path(e.initial_vertex, e.terminal_vertex)}
|
27
|
+
#Returns array of arrays of vertices
|
28
|
+
return fcs
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.compute_path(from, to)
|
32
|
+
@target = to
|
33
|
+
@stack = [from]
|
34
|
+
@visited = []
|
35
|
+
dfs(from)
|
36
|
+
return @stack
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.dfs(v)
|
40
|
+
@visited[v.id] = true
|
41
|
+
@spanning_tree.adjacent_vertices(v).each do |v|
|
42
|
+
@stack.push(v)
|
43
|
+
return true if v == @target
|
44
|
+
return true if !@visited[v.id] && dfs(v)
|
45
|
+
@stack.pop
|
46
|
+
end
|
47
|
+
false
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require_relative 'fund_circuits'
|
2
|
+
|
3
|
+
def run_example(example_title, graph)
|
4
|
+
puts "------- #{example_title} ------ "
|
5
|
+
puts "The graph has #{graph.vertex_count} vertices and #{graph.edge_count} edges"
|
6
|
+
FundCircuits.run(graph)
|
7
|
+
end
|
8
|
+
|
9
|
+
def main
|
10
|
+
edges = [
|
11
|
+
[1,2],[1,3],[1,4],
|
12
|
+
[2,3],[2,4],[2,5],
|
13
|
+
[3,4],[3,5]
|
14
|
+
]
|
15
|
+
fcs = run_example('Example 1', Graph.new(edges:edges))
|
16
|
+
fcs.each{|c| puts c; puts ''}
|
17
|
+
|
18
|
+
edges = [
|
19
|
+
[1,2],[1,5],[1,4],
|
20
|
+
[2,3],[2,5],[2,6],
|
21
|
+
[3,5],[3,6],
|
22
|
+
[4,5],
|
23
|
+
[5,6]
|
24
|
+
]
|
25
|
+
fcs = run_example('Example 2', Graph.new(edges:edges))
|
26
|
+
fcs.each{|c| puts c; puts ''}
|
27
|
+
|
28
|
+
edges = [
|
29
|
+
[1,2],[3,2],[3,4],[4,1]]
|
30
|
+
fcs = run_example('Example 3', Graph.new(edges:edges))
|
31
|
+
fcs.each{|c| puts c; puts ''}
|
32
|
+
end
|
33
|
+
|
34
|
+
main
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'graphsrb'
|
2
|
+
require_relative '../PrimMST/prim'
|
3
|
+
include Graphsrb
|
4
|
+
|
5
|
+
module FundCutset
|
6
|
+
def self.run(graph)
|
7
|
+
edges = PrimMST.run(graph)
|
8
|
+
#Create a spanning tree
|
9
|
+
@spanning_tree = Graph.new
|
10
|
+
edges.each{|e| @spanning_tree.add_edge(e.initial_vertex.id, e.terminal_vertex.id)}
|
11
|
+
|
12
|
+
puts "Spanning tree:"
|
13
|
+
puts @spanning_tree.edges
|
14
|
+
puts "-"*25
|
15
|
+
|
16
|
+
# Compute fundamental cut-sets
|
17
|
+
fcs = []
|
18
|
+
edges.each do |e|
|
19
|
+
@spanning_tree.remove_edge(e.initial_vertex, e.terminal_vertex)
|
20
|
+
block1 = compute_block(e.initial_vertex)
|
21
|
+
block2 = compute_block(e.terminal_vertex)
|
22
|
+
|
23
|
+
cut_set = []
|
24
|
+
block1.each do |v|
|
25
|
+
block2.each do |u|
|
26
|
+
edge = graph.edge(v,u)
|
27
|
+
cut_set << edge if edge
|
28
|
+
end
|
29
|
+
end
|
30
|
+
@spanning_tree.add_edge(e.initial_vertex.id, e.terminal_vertex.id)
|
31
|
+
fcs << cut_set
|
32
|
+
end
|
33
|
+
return fcs
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.compute_block(v)
|
37
|
+
@block = []
|
38
|
+
@visited = []
|
39
|
+
dfs(v)
|
40
|
+
return @block
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.dfs(v)
|
44
|
+
@visited[v.id] = true
|
45
|
+
@block.push(v)
|
46
|
+
@spanning_tree.adjacent_vertices(v).each do |v|
|
47
|
+
dfs(v) unless @visited[v.id]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative 'fund_cutsets'
|
2
|
+
|
3
|
+
def run_example(example_title, graph)
|
4
|
+
puts "------- #{example_title} ------ "
|
5
|
+
puts "The graph has #{graph.vertex_count} vertices and #{graph.edge_count} edges"
|
6
|
+
FundCutset.run(graph)
|
7
|
+
end
|
8
|
+
|
9
|
+
def main
|
10
|
+
edges = [
|
11
|
+
[1,2],[1,3],[1,5],
|
12
|
+
[2,4],[2,5],
|
13
|
+
[3,4],[3,5],
|
14
|
+
[4,5]
|
15
|
+
]
|
16
|
+
fcs = run_example('Example 1', Graph.new(edges:edges))
|
17
|
+
puts "The fundamental cut set:"
|
18
|
+
fcs.each{|c| puts c; puts ''}
|
19
|
+
|
20
|
+
edges = [[1,2],[2,3],[3,4],[4,1]]
|
21
|
+
fcs = run_example('Example 2', Graph.new(edges:edges))
|
22
|
+
puts "The fundamental cut set:"
|
23
|
+
fcs.each{|c| puts c; puts ''}
|
24
|
+
end
|
25
|
+
|
26
|
+
main
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'graphsrb'
|
2
|
+
include Graphsrb
|
3
|
+
|
4
|
+
module EdmondsKarp
|
5
|
+
MAX_INT = (2**(0.size * 8 -2) -1)
|
6
|
+
|
7
|
+
# Given undirected graph, source, and target
|
8
|
+
# computes maximum flow value between the source and target
|
9
|
+
def self.run(graph, s, t)
|
10
|
+
init(graph)
|
11
|
+
max_flow = 0
|
12
|
+
while true
|
13
|
+
min_val, vertices = find_path(s,t)
|
14
|
+
break if min_val.nil?
|
15
|
+
update_flows(vertices, min_val)
|
16
|
+
max_flow += min_val
|
17
|
+
end
|
18
|
+
max_flow
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.init(graph)
|
22
|
+
@graph = graph
|
23
|
+
|
24
|
+
@flow = []
|
25
|
+
@graph.vertices.each{|v| @flow[v.id] = []}
|
26
|
+
|
27
|
+
@graph.vertices.each do |v|
|
28
|
+
@graph.adjacent_vertices(v).each do |u|
|
29
|
+
@flow[v.id][u.id] = 0
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.find_path(s,t)
|
35
|
+
parent = []
|
36
|
+
queue = [s]
|
37
|
+
visited = []
|
38
|
+
|
39
|
+
parent[s.id] = nil
|
40
|
+
visited[s.id] = true
|
41
|
+
|
42
|
+
max_id = @graph.vertices.map{|v| v.id}.max
|
43
|
+
pushed_amount = Array.new(max_id, MAX_INT)
|
44
|
+
|
45
|
+
while !queue.empty?
|
46
|
+
v = queue.shift
|
47
|
+
break if v == t
|
48
|
+
vertices = @graph.adjacent_vertices(v)
|
49
|
+
|
50
|
+
vertices.each do |u|
|
51
|
+
cap = @graph.edge(v,u).weight
|
52
|
+
flow = @flow[v.id][u.id]
|
53
|
+
if !visited[u.id] && (cap - flow > 0)
|
54
|
+
queue.push(u)
|
55
|
+
visited[u.id] = true
|
56
|
+
parent[u.id] = v
|
57
|
+
pushed_amount[u.id] = [pushed_amount[v.id], cap - flow].min
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
return [nil, nil] if v != t
|
63
|
+
|
64
|
+
path = [t]
|
65
|
+
v = parent[t.id]
|
66
|
+
while true
|
67
|
+
path << v
|
68
|
+
break if v == s
|
69
|
+
v = parent[v.id]
|
70
|
+
end
|
71
|
+
|
72
|
+
[pushed_amount[t.id], path.reverse]
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.update_flows(vertices, min_val)
|
76
|
+
(0...vertices.size-1).each do |i|
|
77
|
+
u,v = vertices[i], vertices[i+1]
|
78
|
+
@flow[u.id][v.id] += min_val
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.print_flow
|
83
|
+
@graph.vertices.each do |v|
|
84
|
+
@graph.adjacent_vertices(v).each do |u|
|
85
|
+
cap = @graph.edge(v,u).weight
|
86
|
+
puts "(#{v.id})==#{@flow[v.id][u.id]}/#{cap}==>(#{u.id})"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
puts "-"*45
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require_relative 'edmonds_karp'
|
2
|
+
|
3
|
+
def run_example(example_title, graph, s, t)
|
4
|
+
puts "------- #{example_title} ------ "
|
5
|
+
puts "Max flow: #{EdmondsKarp.run(graph, s, t)}"
|
6
|
+
puts "Flow graph:"
|
7
|
+
EdmondsKarp.print_flow
|
8
|
+
end
|
9
|
+
|
10
|
+
def main
|
11
|
+
edges = [
|
12
|
+
[1,2,16], [1,3,13],
|
13
|
+
[2,4,12],
|
14
|
+
[3,2,4], [3,5,14],
|
15
|
+
[4,3,9], [4,6,20],
|
16
|
+
[5,4,7], [5,6,4]
|
17
|
+
]
|
18
|
+
run_example("Example 1", Digraph.new(edges:edges), Vertex.new(1), Vertex.new(6))
|
19
|
+
|
20
|
+
edges = [
|
21
|
+
[1,2,3],[1,3,2],
|
22
|
+
[2,3,2],[2,4,4],
|
23
|
+
[3,6,6],
|
24
|
+
[4,5,3],[4,3,2],
|
25
|
+
[5,6,1]
|
26
|
+
]
|
27
|
+
run_example("Example 2", Digraph.new(edges:edges), Vertex.new(1), Vertex.new(6))
|
28
|
+
|
29
|
+
edges = [
|
30
|
+
[1,2,10], [1,3,8],
|
31
|
+
[2,3,2], [2,4,5],
|
32
|
+
[3,5,10],
|
33
|
+
[4,6,7],
|
34
|
+
[5,4,8], [5,6,10]
|
35
|
+
]
|
36
|
+
run_example("Example 3", Digraph.new(edges:edges), Vertex.new(1), Vertex.new(6))
|
37
|
+
|
38
|
+
edges = [
|
39
|
+
[1,2,10], [1,3,8],
|
40
|
+
[2,3,2], [2,4,8],
|
41
|
+
[3,4,6],[3,5,7],
|
42
|
+
[4,6,10],
|
43
|
+
[5,6,10]
|
44
|
+
]
|
45
|
+
run_example("Example 4", Digraph.new(edges:edges), Vertex.new(1), Vertex.new(6))
|
46
|
+
|
47
|
+
edges = [
|
48
|
+
[1,2,3], [1,3,3], [1,4,4],
|
49
|
+
[2,5,2],
|
50
|
+
[3,2,10], [3,5,1],
|
51
|
+
[4,6,5],
|
52
|
+
[5,6,1], [5,7,2],
|
53
|
+
[6,7,5]
|
54
|
+
]
|
55
|
+
run_example("Example 5", Digraph.new(edges:edges), Vertex.new(1), Vertex.new(7))
|
56
|
+
|
57
|
+
edges = [
|
58
|
+
[1,2,38], [1,3,1], [1,4,2],
|
59
|
+
[2,3,8], [2,5,13],[2,6,10],
|
60
|
+
[3,6,26],
|
61
|
+
[4,8,27],
|
62
|
+
[5,3,2], [5,7,1], [5,8,7],
|
63
|
+
[6,4,24], [6,7,8], [6,8,1],
|
64
|
+
[7,8,7]
|
65
|
+
]
|
66
|
+
run_example("Example 6", Digraph.new(edges:edges), Vertex.new(1), Vertex.new(8))
|
67
|
+
|
68
|
+
edges = [
|
69
|
+
[1,2],
|
70
|
+
[2,9],
|
71
|
+
[3,2],
|
72
|
+
[5,2],
|
73
|
+
[7,2],
|
74
|
+
[8,1],[8,3],[8,5],[8,7]
|
75
|
+
]
|
76
|
+
run_example("Example 7", Digraph.new(edges:edges), Vertex.new(8), Vertex.new(9))
|
77
|
+
end
|
78
|
+
|
79
|
+
main
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require_relative 'prim'
|
2
|
+
|
3
|
+
def run_example(example_title, graph)
|
4
|
+
puts "------- #{example_title} ------ "
|
5
|
+
puts "The graph has #{graph.vertex_count} vertices and #{graph.edge_count} edges"
|
6
|
+
edges = PrimMST.run(graph)
|
7
|
+
puts "MST:"
|
8
|
+
edges.each{|e| puts e}
|
9
|
+
puts "MST value: #{edges.inject(0){|s, e| s + e.weight}}"
|
10
|
+
end
|
11
|
+
|
12
|
+
def main
|
13
|
+
edges = [
|
14
|
+
[1,2,19],[1,3,17],[1,5,5],
|
15
|
+
[2,4,13],[2,5,2], [2,6,7],
|
16
|
+
[3,4,3], [3,6,17],
|
17
|
+
[4,5,10],[4,6,14],
|
18
|
+
[5,6,9]
|
19
|
+
]
|
20
|
+
run_example('Example 1', Graph.new(edges:edges))
|
21
|
+
|
22
|
+
edges = [
|
23
|
+
[1,2,1], [1,3,3], [1,4,4],
|
24
|
+
[2,3,2],
|
25
|
+
[3,4,5]
|
26
|
+
]
|
27
|
+
run_example('Example 2', Graph.new(edges:edges))
|
28
|
+
|
29
|
+
edges = [
|
30
|
+
[1,2,1], [1,3,7],
|
31
|
+
[2,3,5], [2,4,3], [2,5,4],
|
32
|
+
[3,4,6],
|
33
|
+
[4,5,2]
|
34
|
+
]
|
35
|
+
run_example('Example 3', Graph.new(edges:edges))
|
36
|
+
|
37
|
+
edges = [
|
38
|
+
[1,2,7], [1,4,6],
|
39
|
+
[2,3,6],
|
40
|
+
[4,2,9], [4,3,8]
|
41
|
+
]
|
42
|
+
run_example('Example 4', Graph.new(edges:edges))
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
main
|