graphshaper 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .rvmrc
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in graphshaper.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Lucas Dohmen
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # Graphshaper
2
+
3
+ Graphshaper can generate realistic, scale-free graphs of any size. The resulting graph can then be saved into different kinds of databases.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'graphshaper'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install graphshaper
18
+
19
+ ## Usage
20
+
21
+ The commandline tool expects one argument: The number of nodes you want your generated graph to have. For example:
22
+
23
+ graphshaper 50
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Make your changes – don't forget to spec them with RSpec
30
+ 4. Commit your changes (`git commit -am 'Added some feature'`)
31
+ 5. Push to the branch (`git push origin my-new-feature`)
32
+ 6. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require "rspec"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new("spec") do |spec|
7
+ spec.pattern = "spec/**/*_spec.rb"
8
+ end
9
+
10
+ task :default => :spec
data/bin/graphshaper ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'graphshaper'
4
+
5
+ if ARGV.length == 0 || ARGV[0] == "--help"
6
+ puts " Usage: graphshaper SIZE"
7
+ else
8
+ inner_nodes = 20
9
+ number_of_nodes = ARGV[0].to_i
10
+
11
+ graph = Graphshaper::UndirectedGraph.without_orphans_with_order_of 20, edge_creation_logger: STDOUT
12
+ (number_of_nodes - inner_nodes).times do
13
+ graph.add_vertex do |preferential_attachment|
14
+ preferential_attachment > rand
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/graphshaper/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Lucas Dohmen"]
6
+ gem.email = ["me@moonglum.net"]
7
+ gem.description = %q{Graphshaper generates graphs for databases}
8
+ gem.summary = %q{Graphshaper can generate realistic, scale-free graphs of any size. The resulting graph can then be saved into different kinds of databases.}
9
+ gem.homepage = "http://github.com/moonglum/graphshaper"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "graphshaper"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Graphshaper::VERSION
17
+
18
+ gem.add_development_dependency 'rspec'
19
+ end
@@ -0,0 +1,5 @@
1
+ require "graphshaper/version"
2
+ require "graphshaper/undirected_graph"
3
+
4
+ module Graphshaper
5
+ end
@@ -0,0 +1,111 @@
1
+ require "set"
2
+
3
+ module Graphshaper
4
+ class UndirectedGraph
5
+ # Create a graph with a given number of vertices
6
+ def initialize(number_of_vertices, options_hash = {})
7
+ @vertex_degrees = [0] * number_of_vertices
8
+ @edges = Set.new
9
+ @unconnected_vertices = Set.new (0...number_of_vertices).to_a
10
+
11
+ if options_hash.has_key? :edge_creation_logger
12
+ @edge_creation_logger = options_hash[:edge_creation_logger]
13
+ end
14
+ end
15
+
16
+ def UndirectedGraph.without_orphans_with_order_of(number_of_vertices, options_hash = {})
17
+ graph = self.new number_of_vertices, options_hash
18
+
19
+ while graph.number_of_orphans > 0
20
+ a = rand(graph.order)
21
+ while a == (b = rand(graph.order)); end
22
+
23
+ graph.add_edge a, b
24
+ end
25
+
26
+ return graph
27
+ end
28
+
29
+ # the number of vertices
30
+ def order
31
+ @vertex_degrees.length
32
+ end
33
+
34
+ # the number of edges
35
+ def size
36
+ @edges.length
37
+ end
38
+
39
+ def add_vertex(&block)
40
+ @vertex_degrees << 0
41
+
42
+ new_vertex_id = @vertex_degrees.length - 1
43
+ if block_given?
44
+ each_vertex_with_preferential_attachment do |vertex_id, preferential_attachment|
45
+ add_edge new_vertex_id, vertex_id if block.call(preferential_attachment)
46
+ end
47
+ end
48
+ end
49
+
50
+ def add_edge(first_vertex_id, second_vertex_id)
51
+ if first_vertex_id == second_vertex_id
52
+ raise "No Self-Referential Edge"
53
+ elsif first_vertex_id >= order || second_vertex_id >= order
54
+ raise "ID doesn't exist"
55
+ else
56
+ @unconnected_vertices.delete first_vertex_id
57
+ @vertex_degrees[first_vertex_id] += 1
58
+ @unconnected_vertices.delete second_vertex_id
59
+ @vertex_degrees[second_vertex_id] += 1
60
+ @edges << [first_vertex_id, second_vertex_id].sort
61
+
62
+ @edge_creation_logger << "#{first_vertex_id},#{second_vertex_id}\n" if @edge_creation_logger
63
+ end
64
+ end
65
+
66
+ def edge_between?(first_vertex_id, second_vertex_id)
67
+ @edges.include? [first_vertex_id, second_vertex_id]
68
+ end
69
+
70
+ # The number of vertices without edges
71
+ def number_of_orphans
72
+ @unconnected_vertices.length
73
+ end
74
+
75
+ def vertex_degree_for(vertex_id)
76
+ @vertex_degrees[vertex_id]
77
+ end
78
+
79
+ def degree_distribution
80
+ degree_distribution = []
81
+ @vertex_degrees.each do |vertex_degree|
82
+ if degree_distribution[vertex_degree]
83
+ degree_distribution[vertex_degree] += 1
84
+ else
85
+ degree_distribution[vertex_degree] = 1
86
+ end
87
+ end
88
+ degree_distribution
89
+ end
90
+
91
+ def calculate_vertex_degree_for(vertex_id)
92
+ @vertex_degrees[vertex_id]
93
+ end
94
+
95
+ def sum_of_all_degrees
96
+ @edges.length * 2
97
+ end
98
+
99
+ def each_vertex_with_preferential_attachment(&block)
100
+ if sum_of_all_degrees > 0
101
+ preferential_attachments = @vertex_degrees.map { |degree| degree.round(1) / sum_of_all_degrees }
102
+ vertex_id = 0
103
+ preferential_attachments.each do |preferential_attachment|
104
+ block.call vertex_id, preferential_attachment
105
+ vertex_id += 1
106
+ end
107
+ end
108
+ end
109
+
110
+ end
111
+ end
@@ -0,0 +1,3 @@
1
+ module Graphshaper
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,4 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
3
+
4
+ require 'graphshaper'
@@ -0,0 +1,162 @@
1
+ require "spec_helper"
2
+ require "stringio" # for testing the logger
3
+
4
+ describe Graphshaper::UndirectedGraph do
5
+ it "should create a graph with a given number of vertices and no edges" do
6
+ graph = Graphshaper::UndirectedGraph.new 5
7
+ graph.order.should ==(5)
8
+ graph.size.should ==(0)
9
+ end
10
+
11
+ it "should create a graph with a logger for edge creation" do
12
+ edge_creation_logger = StringIO.new
13
+ graph = Graphshaper::UndirectedGraph.new 5, edge_creation_logger: edge_creation_logger
14
+
15
+ graph.add_edge 1,3
16
+ graph.add_edge 2,3
17
+ edge_creation_logger.string.should ==("1,3\n2,3\n")
18
+ end
19
+
20
+ describe "initialized graph" do
21
+ before :each do
22
+ @graph = Graphshaper::UndirectedGraph.new 5
23
+ end
24
+
25
+ it "should be able to add new vertices" do
26
+ expect { @graph.add_vertex }.to change{ @graph.order }.by(1)
27
+ end
28
+
29
+ it "should add a vertex with two existing ids" do
30
+ expect { @graph.add_edge 0, 1 }.to change{ @graph.size }.by(1)
31
+ end
32
+
33
+ it "shouldn't add a vertex if one of the ids doesn't exist" do
34
+ expect { @graph.add_edge 0, 5}.to raise_error(RuntimeError, "ID doesn't exist")
35
+ end
36
+
37
+ it "should answer the question if there is an edge between two vertices with false if they are not" do
38
+ @graph.edge_between?(0,1).should be_false
39
+ end
40
+
41
+ it "should answer the question if there is an edge between two vertices with true if they are" do
42
+ @graph.add_edge 0,1
43
+ @graph.edge_between?(0,1).should be_true
44
+ end
45
+
46
+ it "shouldn't add an edge that has already been added" do
47
+ @graph.add_edge 0,1
48
+ expect { @graph.add_edge 0, 1 }.to change{ @graph.size }.by(0)
49
+ end
50
+
51
+ it "shouldn't add an edge that has already been added - independent of direction" do
52
+ @graph.add_edge 0,1
53
+ expect { @graph.add_edge 1,0 }.to change{ @graph.size }.by(0)
54
+ end
55
+
56
+ it "should not add an edge where the first and second vertex are the same" do
57
+ expect { @graph.add_edge 0, 0}.to raise_error(RuntimeError, "No Self-Referential Edge")
58
+ end
59
+
60
+ it "should return the graph's order for the number of orphans for a graph without vertices" do
61
+ @graph.number_of_orphans.should ==(@graph.order)
62
+ end
63
+
64
+ it "should return 0 for the number of orphans for a graph connected in a circle" do
65
+ circle_array = (0...5).to_a
66
+ circle_array.zip(circle_array.rotate).each do |vertex_a, vertex_b|
67
+ @graph.add_edge vertex_a, vertex_b
68
+ end
69
+ end
70
+
71
+ it "should calculate the vertex degree" do
72
+ expect { @graph.add_edge 0,1 }.to change { @graph.vertex_degree_for 1}.by(1)
73
+ end
74
+
75
+ it "should calculate the degree distribution" do
76
+ @graph.degree_distribution.should ==[5]
77
+ @graph.add_edge 0,1
78
+ @graph.add_edge 1,2
79
+ @graph.degree_distribution.should ==[2,2,1]
80
+ end
81
+ end
82
+
83
+ describe "random generated graph without orphans" do
84
+ before :each do
85
+ @graph = Graphshaper::UndirectedGraph.without_orphans_with_order_of 15
86
+ end
87
+
88
+ it "should have the correct order" do
89
+ @graph.order.should ==(15)
90
+ end
91
+
92
+ it "should have no orphans" do
93
+ @graph.number_of_orphans.should ==(0)
94
+ end
95
+ end
96
+
97
+ describe "calculating the vertex's degree and preferential attachment" do
98
+ before :each do
99
+ @graph = Graphshaper::UndirectedGraph.new 5
100
+ end
101
+
102
+ it "should calculate the degree of 0 for every vertex in a graph without edges" do
103
+ 5.times do |vertex_id|
104
+ @graph.calculate_vertex_degree_for(vertex_id).should ==0
105
+ end
106
+ end
107
+
108
+ it "should calculate the degree for a vertex with two edges" do
109
+ @graph.add_edge 0,1
110
+ @graph.add_edge 1,2
111
+ @graph.calculate_vertex_degree_for(1).should ==2
112
+ end
113
+
114
+ it "should calculate the sum of all degrees" do
115
+ @graph.add_edge 0,1
116
+ @graph.add_edge 1,2
117
+ @graph.sum_of_all_degrees.should ==4
118
+ end
119
+
120
+ it "should provide an iterator for preferential attachments that sums up to 0 for a graph without edges" do
121
+ sum = 0
122
+ @graph.each_vertex_with_preferential_attachment do |vertex_id, preferential_attachment|
123
+ sum += preferential_attachment
124
+ end
125
+ sum.should ==0
126
+ end
127
+
128
+ it "should calculate the preferential attachments in a way that their sum is always 1 when there is at least one edge" do
129
+ sum = 0
130
+ @graph.add_edge 0,1
131
+ @graph.each_vertex_with_preferential_attachment do |vertex_id, preferential_attachment|
132
+ sum += preferential_attachment
133
+ end
134
+ sum.should ==1
135
+ end
136
+
137
+ it "should add up the preferential attachments to one even if edges are added in the block" do
138
+ sum = 0
139
+ @graph.add_edge 0,1
140
+ @graph.each_vertex_with_preferential_attachment do |vertex_id, preferential_attachment|
141
+ @graph.add_edge 1,3 if @graph.size < 2
142
+ sum += preferential_attachment
143
+ end
144
+ sum.should ==1
145
+ end
146
+
147
+ it "should add a vertex to the graph with edges according to preferential attachment" do
148
+ @graph.add_edge 0,1
149
+
150
+ # Two nodes with preferential_attachment of 0.5, all others with 0
151
+ @graph.add_vertex do |preferential_attachment|
152
+ preferential_attachment > 0.4
153
+ end
154
+
155
+ # One more node
156
+ @graph.order.should ==(6)
157
+
158
+ # Two additional edges
159
+ @graph.size.should ==(3)
160
+ end
161
+ end
162
+ end
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: graphshaper
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Lucas Dohmen
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-03 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &70255506366760 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70255506366760
25
+ description: Graphshaper generates graphs for databases
26
+ email:
27
+ - me@moonglum.net
28
+ executables:
29
+ - graphshaper
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - .gitignore
34
+ - Gemfile
35
+ - LICENSE
36
+ - README.md
37
+ - Rakefile
38
+ - bin/graphshaper
39
+ - graphshaper.gemspec
40
+ - lib/graphshaper.rb
41
+ - lib/graphshaper/undirected_graph.rb
42
+ - lib/graphshaper/version.rb
43
+ - spec/spec_helper.rb
44
+ - spec/undirected_graph_spec.rb
45
+ homepage: http://github.com/moonglum/graphshaper
46
+ licenses: []
47
+ post_install_message:
48
+ rdoc_options: []
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ requirements: []
64
+ rubyforge_project:
65
+ rubygems_version: 1.8.6
66
+ signing_key:
67
+ specification_version: 3
68
+ summary: Graphshaper can generate realistic, scale-free graphs of any size. The resulting
69
+ graph can then be saved into different kinds of databases.
70
+ test_files:
71
+ - spec/spec_helper.rb
72
+ - spec/undirected_graph_spec.rb