random_graph 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c8138e4f808a95fa20e072ed897f60c1ccf271d4
4
+ data.tar.gz: 7e2028969794c5fa2678be5e677d10611b10a407
5
+ SHA512:
6
+ metadata.gz: ff0f7677effadc9e48d2a241bed2abc72be56a381ac7479e0af7bf3a42bf1475219af92a3f080b5a3210561a285c6bc0faff4e74ed062e041e88054ec8ad31b5
7
+ data.tar.gz: e16b25a0847b425df5b7248bd555dcadc1a60277779213670727acbe82949402a8e4762e4d74273aa350d23f963a507284e9ec68c4a26042436cf9185db9089c
data/ .png ADDED
Binary file
data/ .png ADDED
Binary file
Binary file
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.gem
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.1
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in random_graph.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 fselame
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,48 @@
1
+ # RandomGraph
2
+
3
+ ## Installation
4
+
5
+ Add this line to your application's Gemfile:
6
+
7
+ ```ruby
8
+ gem 'random_graph'
9
+ ```
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install random_graph
18
+
19
+ ## Usage
20
+
21
+ Install graphviz software before running visualize!
22
+
23
+ Sample usage:
24
+
25
+ ```ruby
26
+ require 'random_graph'
27
+ include RandomGraph
28
+
29
+ # Create a Erdos Renyi GNM graph
30
+ g = Graph.erdos_renyi_gnm(10, 10)
31
+
32
+ # Generate output image
33
+ g.visualize
34
+ ```
35
+
36
+ ## Development
37
+
38
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
39
+
40
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
41
+
42
+ ## Contributing
43
+
44
+ 1. Fork it ( https://github.com/[my-github-username]/random_graph/fork )
45
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
46
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
47
+ 4. Push to the branch (`git push origin my-new-feature`)
48
+ 5. Create a new Pull Request
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
Binary file
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'random_graph'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
Binary file
Binary file
Binary file
@@ -0,0 +1,7 @@
1
+ require 'random_graph/version'
2
+ require 'random_graph/graph'
3
+ require 'random_graph/random_generators'
4
+ require 'random_graph/graph_algorithms'
5
+ # opens random graph module
6
+ module RandomGraph
7
+ end
@@ -0,0 +1,98 @@
1
+ require 'random_graph/random_generators'
2
+ require 'random_graph/graph_algorithms'
3
+ require 'graphviz'
4
+
5
+ module RandomGraph
6
+ # Graph class implements basic graph object and relevant instance methods
7
+ class Graph
8
+ include Enumerable
9
+ extend RandomGenerators
10
+ include GraphAlgorithms
11
+
12
+ attr_reader :size
13
+ attr_accessor :nodes
14
+ attr_accessor :viz_counter
15
+
16
+ # We will used the adjacency list representation of a graph
17
+ # default to size 0
18
+ def initialize(size = 0)
19
+ @viz_counter = 0
20
+ @size = size
21
+ @nodes = Array.new(size) { [] }
22
+ end
23
+
24
+ def visualize
25
+ g = GraphViz.new(:G, type: :graph)
26
+ real_nodes = Array.new(@size)
27
+ (0...@size).each { |i| real_nodes[i] = g.add_nodes("#{i}") }
28
+ (0...@size).each do |i|
29
+ @nodes[i].each do |n|
30
+ g.add_edges(real_nodes[i], real_nodes[n]) if i < n
31
+ end
32
+ end
33
+ g.output(png: "graph_#{@viz_counter}.png")
34
+ @viz_counter += 1
35
+ end
36
+
37
+ def add_node
38
+ new_node = []
39
+ @nodes << new_node
40
+ @size += 1
41
+ end
42
+
43
+ def each
44
+ @nodes.each { |v| yield v }
45
+ end
46
+
47
+ # add edge between two nodes
48
+ def add_edge(from, to)
49
+ fail ArgumentError.new('invalid node') if from >= @size || from < 0
50
+ fail ArgumentError.new('invalid node') if to >= @size || to < 0
51
+ unless to == from
52
+ @nodes[from] << to unless @nodes[from].include?(to)
53
+ @nodes[to] << from unless @nodes[to].include?(from)
54
+ end
55
+ end
56
+
57
+ def remove_edge(from, to)
58
+ @nodes[from].delete(to) if @nodes[from].include?(to)
59
+ @nodes[to].delete(from) if @nodes[to].include?(from)
60
+ end
61
+
62
+ def degree(node)
63
+ @nodes[node].size
64
+ end
65
+
66
+ def max_degree
67
+ max_deg = 0
68
+ @nodes.each do |v|
69
+ max_deg = v.size if max_deg < v.size
70
+ end
71
+ max_deg
72
+ end
73
+
74
+ def adjacent_nodes(v)
75
+ @nodes[v]
76
+ end
77
+
78
+ # is there an edge between two nodes?
79
+ def edge?(from, to)
80
+ # check if nodes are valid?
81
+ invalid_nodes = from >= @size || from < 0 || to >= @size || to < 0
82
+ @nodes[from].include?(to) if !invalid_nodes
83
+ end
84
+
85
+ def edge_number
86
+ count = 0
87
+ @nodes.each do |n|
88
+ count += n.size
89
+ end
90
+ count / 2
91
+ end
92
+
93
+ def average_degree
94
+ 2.0 * edge_number / size
95
+ end
96
+
97
+ end
98
+ end
@@ -0,0 +1,103 @@
1
+ # implements useful graph algorithms
2
+ module RandomGraph
3
+ module GraphAlgorithms
4
+
5
+
6
+ def BFS_distance(root)
7
+ distance = Array.new(@size)
8
+ distance.fill(Float::INFINITY)
9
+
10
+ queue = []
11
+
12
+ distance[root] = 0
13
+ queue.unshift(root)
14
+
15
+ while !queue.empty? do
16
+
17
+ current = queue.pop
18
+ adj = adjacent_nodes(current)
19
+ adj.each do |a|
20
+ if distance[a] == Float::INFINITY
21
+ distance[a] = distance[current] + 1
22
+ queue.unshift(a)
23
+ end
24
+ end
25
+ end
26
+ distance
27
+ end
28
+
29
+ def connected_component(root)
30
+ distances = BFS_distance(root)
31
+ adjacent = []
32
+ (0...distances.size).each do |i|
33
+ adjacent << i unless distances[i] == Float::INFINITY
34
+ end
35
+ adjacent
36
+ end
37
+
38
+ def number_of_components
39
+ visited = Array.new(size)
40
+ visited.fill(false)
41
+ counter = 0
42
+
43
+ (0...size).each do |i|
44
+ next if visited[i] == true
45
+ counter += 1
46
+ comp = connected_component(i)
47
+ comp.each do |reachable|
48
+ visited[reachable] = true
49
+ end
50
+ end
51
+ counter
52
+ end
53
+
54
+ def worst_case_diameter
55
+ real_bad = number_of_components > 1 ? Float::INFINITY : 0
56
+ worst = 0
57
+ if real_bad == 0
58
+ (0...size).each do |i|
59
+ distances = BFS_distance(i)
60
+ worst = [distances.max, worst].max
61
+ end
62
+ end
63
+ [worst, real_bad].max
64
+ end
65
+
66
+ def average_case_diameter
67
+ real_bad = number_of_components > 1 ? Float::INFINITY : 0
68
+ total = 0
69
+ if real_bad == 0
70
+ (0...size).each do |i|
71
+ distances = BFS_distance(i)
72
+ average = distances.inject(:+).to_f / (size - 1)
73
+ total += average
74
+ end
75
+ end
76
+ total / size
77
+ end
78
+
79
+ def clustering_coefficient
80
+ total_cluster = 0
81
+ (0...size).each do |i|
82
+ neighbors = adjacent_nodes(i)
83
+ k = neighbors.size
84
+ possible_connections = k.to_f * (k - 1) / 2
85
+ actual_connections = 0
86
+ (0...k).each do |l|
87
+ (i+1...k).each do |j|
88
+ actual_connections += 1 if edge?(neighbors[l], neighbors[j])
89
+ end
90
+ end
91
+ if possible_connections == 0 then clustering = 0
92
+ else clustering = actual_connections.to_f / possible_connections
93
+ end
94
+ total_cluster += clustering
95
+ end
96
+ total_cluster / size
97
+ end
98
+
99
+
100
+
101
+
102
+ end
103
+ end
@@ -0,0 +1,158 @@
1
+ module RandomGraph
2
+ module RandomGenerators
3
+ # Select a graph randomly, where all graphs with n nodes and m edges
4
+ # are equally likely to be selected.
5
+ # O(n^2) expected worst-case rumtime, assuming random numbers generated
6
+ # in constant time. Number of random edges probed before adding edge k
7
+ # is (n^2 - n)/(n^2 - n - 2k). Total expected number of probes can be
8
+ # express as a summation, which is upper bounded by O(n^2)
9
+
10
+ def erdos_renyi_gnm(n, m)
11
+ fail ArgumentError.new('must have positive number of nodes') if n < 0
12
+ fail ArgumentError.new('must have positive number of edges') if m < 0
13
+
14
+ possible_edges = n * (n - 1) / 2
15
+
16
+ fail ArgumentError.new('too many edges') if m > possible_edges
17
+
18
+ # create empty graph
19
+ g = Graph.new(n)
20
+ added_edges = {}
21
+
22
+ # for every edge
23
+ (0...m).each do |i|
24
+ valid_edge = false
25
+ from = 0
26
+ to = 0
27
+ begin
28
+ # select a random number, representing on of the possible edges
29
+ r = rand(0...n**2)
30
+ # mapping from random number to edges, such that all edges
31
+ # can be selected with equal probabilities
32
+ from = r / n
33
+ to = r % n
34
+
35
+ # Keeps adjacency matrix upper triangular by mapping equivalent
36
+ # r values
37
+ if from < to
38
+ temp = from
39
+ from = to
40
+ to = temp
41
+ r = (from * n) + to
42
+ end
43
+ # make sure edge has not been slescted, and that it is not a
44
+ # self loop
45
+ valid_edge = !added_edges.key?(r) && (from != to)
46
+ end until valid_edge == true
47
+ added_edges[r] = true
48
+ g.add_edge(from, to)
49
+ end
50
+ g
51
+ end
52
+
53
+ # Create a graph with n nodes, where all edges have a probability p
54
+ # of existing
55
+ def erdos_renyi_gnp(n, p)
56
+ fail ArgumentError.new('must have positive number of nodes') if n < 0
57
+ fail ArgumentError.new('need positive probability') if p < 0
58
+ fail ArgumentError.new('probability cannot be greater than 1!') if p > 1
59
+
60
+ # create empty graph
61
+ g = Graph.new(n)
62
+ (0...n**2).each do |i|
63
+ from = i / n
64
+ to = i % n
65
+ g.add_edge(from, to) if rand <= p && to > from
66
+ end
67
+ g
68
+ end
69
+
70
+ # creates a Watts–Strogatz random graph. n represents the number of nodes,
71
+ # k is the mean degree, and b is the beta parameter
72
+ def watts_strogatz(n, k, b)
73
+ fail ArgumentError.new('must have positive number of nodes') if n < 0
74
+ fail ArgumentError.new('invalid beta parameter') if b < 0 || b > 1
75
+ fail ArgumentError.new('invalid mean degree if') if k > n || k < Math.log10(n)
76
+
77
+ # construction of regular ring lattice
78
+ g = Graph.new(n)
79
+ (0...n).each do |i|
80
+ (0...n).each do |j|
81
+ ab = (i - j).abs
82
+ modded = n - 1 - (k / 2)
83
+ checker = ab % modded
84
+ g.add_edge(i, j) if 0 < checker && checker <= (k / 2)
85
+ end
86
+ end
87
+
88
+ # rewiring under beta parameter
89
+ (0...n).each do |i|
90
+ (i...n).each do |j|
91
+ # skip non-existent edges
92
+ next unless g.edge?(i, j)
93
+ # rewire from uniform distribution
94
+ if rand < b && g.degree(i) != n - 1
95
+ # skip is node is completely wired
96
+ target = 0
97
+ loop do
98
+ target = rand(0...n)
99
+ break if target != i && !g.edge?(i, target)
100
+ end
101
+ g.remove_edge(i, j)
102
+ g.add_edge(i, target)
103
+ end
104
+ end
105
+ end
106
+ g
107
+ end
108
+
109
+ def preferential_attachment(size)
110
+ fail ArgumentError.new('invalid number of nodes') if size < 2
111
+ g = Graph.new(2)
112
+ g.add_edge(0, 1)
113
+ total_degree = 2
114
+
115
+ (2...size).each do |i|
116
+ g.add_node
117
+ r = rand(1..total_degree)
118
+ j = 0
119
+
120
+ loop do
121
+ r -= g.degree(j)
122
+ break if r <= 0
123
+ j += 1
124
+ end
125
+ g.add_edge(i, j)
126
+ total_degree += 2
127
+ end
128
+ g
129
+ end
130
+
131
+ def random_grid(n, p)
132
+ fail ArgumentError.new('invalid probability') if p > 1 || p < 0
133
+ g = Graph.new(n**2)
134
+ (0...n**2).each do |i|
135
+ bottom = (i + n) % n**2
136
+ right = (i + 1)
137
+ right -= n if right % n == 0
138
+ g.add_edge(i, bottom)
139
+ g.add_edge(i, right)
140
+ end
141
+ (0...n**2).each do |i|
142
+ next if rand > p
143
+ top = i - n
144
+ top += n**2 if top < 0
145
+ bottom = (i + n) % n**2
146
+ right = i + 1
147
+ right -= n if right % n == 0
148
+ left = i - 1
149
+ left += n if left == -1 || left % n == n - 1
150
+ g.remove_edge(i, top)
151
+ g.remove_edge(i, bottom)
152
+ g.remove_edge(i, left)
153
+ g.remove_edge(i, right)
154
+ end
155
+ g
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,3 @@
1
+ module RandomGraph
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'random_graph/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'random_graph'
8
+ spec.version = RandomGraph::VERSION
9
+ spec.authors = ['fselame']
10
+ spec.email = ['fsel@sas.upenn.edu']
11
+
12
+ spec.summary = 'Gem for generating random graphs'
13
+ spec.description = 'This gem implements the following random
14
+ network generation models: Erdos-Renyi GNM and GNP, Watts-Strogatz,
15
+ and preferential attachement. It also implements graph algorithms to
16
+ study the randomly generated graphs.'
17
+ spec.homepage = 'https://github.com/fselame/random_graph'
18
+ spec.license = 'MIT'
19
+
20
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
21
+ spec.bindir = 'exe'
22
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ spec.require_paths = ['lib']
24
+
25
+ spec.add_development_dependency 'bundler', '~> 1.9'
26
+ spec.add_development_dependency 'rake', '~> 10.0'
27
+ spec.add_development_dependency 'rspec', '~> 3.4'
28
+ spec.add_development_dependency 'simplecov', '~> 0.10'
29
+ spec.add_runtime_dependency 'ruby-graphviz', '~> 1.2'
30
+ end
metadata ADDED
@@ -0,0 +1,139 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: random_graph
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - fselame
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-12-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.9'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.9'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.4'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.4'
55
+ - !ruby/object:Gem::Dependency
56
+ name: simplecov
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.10'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.10'
69
+ - !ruby/object:Gem::Dependency
70
+ name: ruby-graphviz
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.2'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.2'
83
+ description: "This gem implements the following random \n network generation models:
84
+ Erdos-Renyi GNM and GNP, Watts-Strogatz,\n and preferential attachement. It also
85
+ implements graph algorithms to\n study the randomly generated graphs."
86
+ email:
87
+ - fsel@sas.upenn.edu
88
+ executables: []
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - " .png"
93
+ - " .png"
94
+ - ".DS_Store"
95
+ - ".gitignore"
96
+ - ".rspec"
97
+ - ".travis.yml"
98
+ - Gemfile
99
+ - LICENSE.txt
100
+ - README.md
101
+ - Rakefile
102
+ - bin/.DS_Store
103
+ - bin/console
104
+ - bin/setup
105
+ - graph_0.png
106
+ - graph_1.png
107
+ - lib/.DS_Store
108
+ - lib/random_graph.rb
109
+ - lib/random_graph/.DS_Store
110
+ - lib/random_graph/graph.rb
111
+ - lib/random_graph/graph_algorithms.rb
112
+ - lib/random_graph/random_generators.rb
113
+ - lib/random_graph/version.rb
114
+ - random_graph.gemspec
115
+ homepage: https://github.com/fselame/random_graph
116
+ licenses:
117
+ - MIT
118
+ metadata: {}
119
+ post_install_message:
120
+ rdoc_options: []
121
+ require_paths:
122
+ - lib
123
+ required_ruby_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ required_rubygems_version: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ requirements: []
134
+ rubyforge_project:
135
+ rubygems_version: 2.4.6
136
+ signing_key:
137
+ specification_version: 4
138
+ summary: Gem for generating random graphs
139
+ test_files: []