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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.DS_Store +0 -0
  3. data/.gitignore +11 -0
  4. data/.rspec +3 -0
  5. data/.travis.yml +7 -0
  6. data/CODE_OF_CONDUCT.md +74 -0
  7. data/Gemfile +6 -0
  8. data/Gemfile.lock +37 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +338 -0
  11. data/Rakefile +6 -0
  12. data/bin/console +14 -0
  13. data/bin/setup +8 -0
  14. data/examples/ApproxAlgs/TSP/tsp.rb +45 -0
  15. data/examples/ApproxAlgs/VertexCover/main.rb +34 -0
  16. data/examples/ApproxAlgs/VertexCover/vertex_cover.rb +31 -0
  17. data/examples/Apts/artc_points.rb +59 -0
  18. data/examples/Apts/main.rb +51 -0
  19. data/examples/BackEdges/backedges.rb +37 -0
  20. data/examples/BackEdges/main.rb +41 -0
  21. data/examples/BipartMatching/bipart_matching.rb +34 -0
  22. data/examples/BipartMatching/main.rb +58 -0
  23. data/examples/EdgeConnectivity/econ.rb +31 -0
  24. data/examples/EdgeConnectivity/main.rb +34 -0
  25. data/examples/Euler/euler_tour.rb +36 -0
  26. data/examples/Euler/main.rb +19 -0
  27. data/examples/FundCircuits/fund_circuits.rb +49 -0
  28. data/examples/FundCircuits/main.rb +34 -0
  29. data/examples/FundCutsets/fund_cutsets.rb +50 -0
  30. data/examples/FundCutsets/main.rb +26 -0
  31. data/examples/MaxFlow/edmonds_karp.rb +91 -0
  32. data/examples/MaxFlow/main.rb +79 -0
  33. data/examples/PrimMST/main.rb +46 -0
  34. data/examples/PrimMST/prim.rb +82 -0
  35. data/examples/ProbAlgs/LargeCustset/large_cutset.rb +59 -0
  36. data/examples/ProbAlgs/LargeCustset/main.rb +45 -0
  37. data/graphsrb.gemspec +39 -0
  38. data/lib/graphsrb.rb +11 -0
  39. data/lib/graphsrb/adjacency_list.rb +78 -0
  40. data/lib/graphsrb/base_graph.rb +167 -0
  41. data/lib/graphsrb/diedge.rb +6 -0
  42. data/lib/graphsrb/digraph.rb +93 -0
  43. data/lib/graphsrb/edge.rb +34 -0
  44. data/lib/graphsrb/exceptions.rb +5 -0
  45. data/lib/graphsrb/graph.rb +46 -0
  46. data/lib/graphsrb/node.rb +21 -0
  47. data/lib/graphsrb/version.rb +3 -0
  48. data/lib/graphsrb/vertex.rb +32 -0
  49. 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