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,82 @@
1
+ require 'graphsrb'
2
+ include Graphsrb
3
+
4
+ #Assumption: graph is nonempty connected undirected
5
+ module PrimMST
6
+ INF = (2**(0.size * 8 -2) -1)
7
+
8
+ def self.run(graph)
9
+ init(graph)
10
+
11
+ start = graph.vertices.first
12
+ include_vertex(start)
13
+
14
+ adj_vertices = graph.adjacent_vertices(start)
15
+ adj_vertices.each do |v|
16
+ @dist[v.id] = graph.edge(start, v).weight
17
+ @parent[v.id] = start
18
+ end
19
+
20
+ while not all_vertices_included?
21
+ v = min_dist_vertex
22
+ include_vertex(v)
23
+ update_distances(v)
24
+ end
25
+
26
+ edges = []
27
+ (0...@parent.size).each do |i|
28
+ edges << graph.edge(@parent[i], Vertex.new(i)) if @parent[i]
29
+ end
30
+ edges
31
+ end
32
+
33
+
34
+ def self.init(g)
35
+ @graph = g
36
+ @vertex_count = g.vertex_count
37
+ @included_vertex_count = 0
38
+ @included = []
39
+ @dist = []
40
+ @parent = []
41
+ @graph.vertices.each do |v|
42
+ @included[v.id] = false
43
+ @dist[v.id] = INF
44
+ end
45
+ end
46
+
47
+ def self.included?(v)
48
+ @included[v.id]
49
+ end
50
+
51
+ def self.include_vertex(v)
52
+ @included[v.id] = true
53
+ @included_vertex_count += 1
54
+ end
55
+
56
+ def self.update_distances(v)
57
+ vertices = @graph.adjacent_vertices(v)
58
+ vertices.each do |u|
59
+ edge = @graph.edge(v,u)
60
+ if !included?(u) && (@dist[u.id] > edge.weight)
61
+ @dist[u.id] = edge.weight
62
+ @parent[u.id] = v
63
+ end
64
+ end
65
+ end
66
+
67
+ def self.all_vertices_included?
68
+ @included_vertex_count == @vertex_count
69
+ end
70
+
71
+ def self.min_dist_vertex
72
+ min = INF
73
+ vertex = nil
74
+ @graph.vertices.each do |v|
75
+ if @dist[v.id] < min && !included?(v)
76
+ min = @dist[v.id]
77
+ vertex = v
78
+ end
79
+ end
80
+ vertex
81
+ end
82
+ end
@@ -0,0 +1,59 @@
1
+ require 'graphsrb'
2
+ include Graphsrb
3
+
4
+ # The problem is finding a large cutset in
5
+ # a simple undirected graph where each edge
6
+ # has weigh equal to 1.
7
+ # This problem is NP-hard.
8
+ module LargeCutset
9
+
10
+ def self.run(graph)
11
+ init(graph)
12
+ compute_cut
13
+ end
14
+
15
+ def self.init(graph)
16
+ @graph = graph
17
+ @pA, @pB = [], []
18
+ end
19
+
20
+ #Monte Carlo algorithm
21
+ def self.compute_cut
22
+ prng = Random.new(Time.now.to_i)
23
+ @graph.vertices.each do |v|
24
+ if prng.rand(100) < 50
25
+ @pA << v
26
+ else
27
+ @pB << v
28
+ end
29
+ end
30
+ cut_weight
31
+ end
32
+
33
+ def self.cut_weight
34
+ total_weight = 0
35
+ @pA.each do |v|
36
+ @graph.adjacent_vertices(v).each do |u|
37
+ total_weight += 1 if @pB.include?(u)
38
+ end
39
+ end
40
+ total_weight
41
+ end
42
+
43
+ def self.print_cutset
44
+ puts "Cutset:"
45
+ @pA.each do |v|
46
+ @graph.adjacent_vertices(v).each do |u|
47
+ puts @graph.edge(v,u) if @pB.include?(u)
48
+ end
49
+ end
50
+ end
51
+
52
+ def self.print_partitions
53
+ print "Partition A: "
54
+ @pA.each{|v| print "#{v} "}
55
+ print "\nPartition B: "
56
+ @pB.each{|v| print "#{v} "}
57
+ print "\n"
58
+ end
59
+ end
@@ -0,0 +1,45 @@
1
+ require_relative 'large_cutset'
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 "Cutset size #{LargeCutset.run(graph)}"
7
+ LargeCutset.print_partitions
8
+ LargeCutset.print_cutset
9
+ end
10
+
11
+
12
+ def main
13
+ edges = [
14
+ [1,2], [1,3], [1,5],
15
+ [2,4], [2,5], [2,6],
16
+ [3,4], [3,6],
17
+ [4,5], [4,6],
18
+ [5,6]
19
+ ]
20
+ run_example('Example 1', Graph.new(edges:edges))
21
+
22
+ edges = [
23
+ [1,2], [1,3],
24
+ [2,4],
25
+ [3,4],
26
+ [4,5],
27
+ [5,6], [5,6],
28
+ [6,7]
29
+ ]
30
+ run_example('Example 2', Graph.new(edges:edges))
31
+
32
+ edges = [[1,2],[2,3],[3,4],[4,1],[1,3],[4,2]]
33
+ run_example('Example 3', Graph.new(edges:edges))
34
+
35
+ edges = [
36
+ [1,2], [1,3], [1,4], [1,5],
37
+ [2,3], [2,4], [2,5],
38
+ [3,4], [3,5],
39
+ [4,5]
40
+ ]
41
+ run_example('Example 4', Graph.new(edges:edges))
42
+
43
+ end
44
+
45
+ main
@@ -0,0 +1,39 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "graphsrb/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "graphsrb"
8
+ spec.version = Graphsrb::VERSION
9
+ spec.authors = ["Bayram Kuliyev"]
10
+ spec.email = ["bkuliyev@gmail.com"]
11
+
12
+ spec.summary = %q{This gem allows to create simple directed and undirected graphs. Basic operations allows easily implement graph algorithms}
13
+ spec.homepage = "https://github.com/fade2black/graphsrb"
14
+ spec.license = "MIT"
15
+
16
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
18
+ if spec.respond_to?(:metadata)
19
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
20
+ else
21
+ raise "RubyGems 2.0 or newer is required to protect against " \
22
+ "public gem pushes."
23
+ end
24
+
25
+ # Specify which files should be added to the gem when it is released.
26
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
27
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
28
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
29
+ end
30
+ spec.bindir = "exe"
31
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
32
+ spec.require_paths = ["lib"]
33
+
34
+ spec.add_development_dependency "bundler", "~> 1.16"
35
+ spec.add_development_dependency "rake", "~> 10.0"
36
+ spec.add_development_dependency "rspec", "~> 3.0"
37
+
38
+ spec.add_dependency "json", "~> 1.8"
39
+ end
@@ -0,0 +1,11 @@
1
+ module Graphsrb; end
2
+ require "graphsrb/version"
3
+ require "graphsrb/exceptions"
4
+ require "graphsrb/vertex"
5
+ require "graphsrb/node"
6
+ require "graphsrb/adjacency_list"
7
+ require "graphsrb/edge"
8
+ require "graphsrb/base_graph"
9
+ require "graphsrb/diedge"
10
+ require "graphsrb/digraph"
11
+ require "graphsrb/graph"
@@ -0,0 +1,78 @@
1
+
2
+ class Graphsrb::AdjacencyList
3
+ include Enumerable
4
+
5
+ def initialize
6
+ @adj_list = []
7
+ end
8
+
9
+ #Adds a node to the adjacency list
10
+ def add(node)
11
+ adj_list << node.clone
12
+ end
13
+
14
+ #Adds a node to the adjacency list
15
+ def <<(node)
16
+ add(node)
17
+ end
18
+
19
+ #Updates weight
20
+ def update_weight(node, w)
21
+ node = find(node)
22
+ node.update_weight(w) unless node.nil?
23
+ end
24
+
25
+ #Increses weight by +dw+
26
+ def increase_weight(node, dw)
27
+ node = find(node)
28
+ node.update_weight(node.weight + dw) unless node.nil?
29
+ end
30
+
31
+ def each(&block)
32
+ @adj_list.each(&block)
33
+ end
34
+
35
+ #Returns the number of nodes in the adjacency list
36
+ def size
37
+ adj_list.size
38
+ end
39
+
40
+ #Removes a node from the adjacency list
41
+ def delete(node)
42
+ adj_list.delete(node)
43
+ end
44
+
45
+ #Searches for a node in the adjacency list
46
+ #Returns nil if not found
47
+ def find(node)
48
+ index = adj_list.index node
49
+ if index.nil?
50
+ return nil
51
+ else
52
+ adj_list[index]
53
+ end
54
+ end
55
+
56
+ #Returns all nodes
57
+ def nodes
58
+ adj_list.clone
59
+ end
60
+
61
+ #Returns true if the adjacency list contains the node, false otherwise
62
+ def has_node?(node)
63
+ not find(node).nil?
64
+ end
65
+
66
+ #Remove all nodes from the list
67
+ def clear
68
+ adj_list.clear
69
+ end
70
+
71
+ #Creates and returns the created node
72
+ def self.create_node(vertex_id, args={})
73
+ Graphsrb::Node.new(vertex_id, args)
74
+ end
75
+
76
+ private
77
+ attr_reader :adj_list
78
+ end
@@ -0,0 +1,167 @@
1
+
2
+ #This class is the base clase for undirected and directed graphs.
3
+ class Graphsrb::BaseGraph
4
+
5
+ def initialize(args={})
6
+ @adj_table = {}
7
+ args.fetch(:vertices,[]).each{|vertex_id| adj_table[vertex_id] = _create_adjacency_list }
8
+ args.fetch(:edges,[]).each do |e|
9
+ unless has_edge?(e[0], e[1])
10
+ vertices_must_be_different!(e[0], e[1])
11
+ add_edge(e[0], e[1], weight: e[2] || 1)
12
+ end
13
+ end
14
+ end
15
+
16
+ #Returns vertices of the graph
17
+ def vertices
18
+ vertex_array = []
19
+ adj_table.keys.each do |id|
20
+ vertex_array << _create_vertex(id)
21
+ end
22
+ vertex_array
23
+ end
24
+
25
+ #Returns an edge
26
+ def edge(v, u)
27
+ id1, id2 = v.id, u.id
28
+ if has_vertex?(id1)
29
+ node = adj_table[id1].find(_create_node(id2))
30
+ return _create_edge(id1, id2, weight:node.weight) if node
31
+ end
32
+
33
+ if has_vertex?(id2)
34
+ node = adj_table[id2].find(_create_node(id1))
35
+ return _create_edge(id1, id2, weight:node.weight) if node
36
+ end
37
+ end
38
+
39
+ #Updates edge weight
40
+ def update_weight(v, u, w)
41
+ id1, id2 = v.id, u.id
42
+ adj_table[id1].update_weight(_create_node(id2), w) if has_vertex?(id1)
43
+ adj_table[id2].update_weight(_create_node(id1), w) if has_vertex?(id2)
44
+ end
45
+
46
+ #Increases edge weight by +dw+
47
+ def increase_weight(v, u, dw)
48
+ id1, id2 = v.id, u.id
49
+ adj_table[id1].increase_weight(_create_node(id2), dw) if has_vertex?(id1)
50
+ adj_table[id2].increase_weight(_create_node(id1), dw) if has_vertex?(id2)
51
+ end
52
+
53
+ #Returns edges of the graph
54
+ def edges
55
+ edges_array = []
56
+ vertices.each do |vertex|
57
+ adj_table[vertex.id].each do |node|
58
+ edges_array << _create_edge(vertex.id, node.vertex.id, weight: node.weight)
59
+ end
60
+ end
61
+ edges_array
62
+ end
63
+
64
+ #Removes all vertices and edges.
65
+ def clear
66
+ adj_table.each_value{|list| list.clear}
67
+ @adj_table = {}
68
+ true
69
+ end
70
+
71
+ ##Creates a copy of the graph
72
+ def copy
73
+ Marshal::load(Marshal.dump(self))
74
+ end
75
+
76
+ #Returns the number of vertices
77
+ def vertex_count
78
+ vertices.size
79
+ end
80
+
81
+ #Returns the number of edges in the graph
82
+ def edge_count
83
+ sum = 0
84
+ adj_table.each_value{|list| sum += list.size}
85
+ sum
86
+ end
87
+
88
+ #Checks whether the graph has a vertex
89
+ def has_vertex?(id)
90
+ not adj_table[id].nil?
91
+ end
92
+
93
+ alias vertex? has_vertex?
94
+
95
+ #Checks whether the graph has an edge
96
+ def has_edge?(id1, id2)
97
+ has_vertex?(id1) && adj_table[id1].has_node?(_create_node(id2)) ||
98
+ has_vertex?(id2) && adj_table[id2].has_node?(_create_node(id1))
99
+ end
100
+
101
+ alias edge? has_edge?
102
+
103
+ #Adds a new vertex
104
+ def add_vertex(id)
105
+ return nil if has_vertex?(id)
106
+ adj_table[id] = _create_adjacency_list
107
+ true
108
+ end
109
+
110
+ #Adds a new edge
111
+ def add_edge(id1, id2, args={})
112
+ vertices_must_be_different!(id1, id2)
113
+ return nil if has_edge?(id1, id2)
114
+ add_vertex(id1) unless has_vertex?(id1)
115
+ add_vertex(id2) unless has_vertex?(id2)
116
+ adj_table[id1] << _create_node(id2, args)
117
+ true
118
+ end
119
+
120
+ #Remove an edge from the graph
121
+ def remove_edge(v1, v2)
122
+ id1, id2 = v1.id, v2.id
123
+ adj_table[id1].delete(_create_node(id2)) if has_vertex?(id1)
124
+ adj_table[id2].delete(_create_node(id1)) if has_vertex?(id2)
125
+ true
126
+ end
127
+
128
+ #Remove a vertex from the graph
129
+ def remove_vertex(v)
130
+ id = v.id
131
+ return nil unless has_vertex?(id)
132
+
133
+ adj_table[id].clear
134
+ adj_table.delete(id)
135
+
136
+ node = _create_node(id)
137
+ adj_table.each_value{|list| list.delete(node)}
138
+ return v
139
+ end
140
+
141
+
142
+ protected
143
+ attr_reader :adj_table
144
+
145
+ def _create_node(vertex_id, args={})
146
+ Graphsrb::Node.new(vertex_id, args)
147
+ end
148
+
149
+ def _create_adjacency_list
150
+ Graphsrb::AdjacencyList.new
151
+ end
152
+
153
+ def _create_edge(id1, id2, args={})
154
+ Graphsrb::Edge.new(id1, id2, args)
155
+ end
156
+
157
+ def _create_vertex(id)
158
+ Graphsrb::Vertex.new(id)
159
+ end
160
+
161
+ def vertices_must_be_different!(id1, id2)
162
+ if id1 == id2
163
+ raise Graphsrb::EdgeInitializationError, "Vertex id's must be different from each other"
164
+ end
165
+ end
166
+
167
+ end