graphshaper 0.0.2 → 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - jruby-19mode
6
+ - rbx-19mode
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Graphshaper
2
2
 
3
- Graphshaper can generate realistic, scale-free graphs of any size.
3
+ [![Build Status](https://secure.travis-ci.org/moonglum/graphshaper.png?branch=master)](http://travis-ci.org/moonglum/graphshaper)
4
+
5
+ Graphshaper can generate realistic, scale-free graphs of any size. It is tested with MRI Ruby (1.9.2. and 1.9.3) and the 1.9 versions of jRuby and Rubinius.
4
6
 
5
7
  ## Installation
6
8
 
@@ -18,11 +20,11 @@ Or install it yourself as:
18
20
 
19
21
  ## Usage
20
22
 
21
- The commandline tool expects one argument: The number of nodes you want your generated graph to have. For example:
23
+ The commandline tool expects one argument: The number of vertices you want your generated graph to have. This will result in two CSV files containing your vertices and edges. For example:
22
24
 
23
25
  graphshaper 50
24
26
 
25
- You can also use the `Graphshaper::UndirectedGraph` class in your Rubycode. To find examples on how to do that please refer to the specs.
27
+ You can also use the library in your Ruby Code. You can find the documentation [here](http://rubydoc.info/github/moonglum/graphshaper).
26
28
 
27
29
  ## Contributing
28
30
 
data/bin/graphshaper CHANGED
@@ -4,14 +4,34 @@ require 'graphshaper'
4
4
 
5
5
  if ARGV.length == 0 || ARGV[0] == "--help"
6
6
  puts " Usage: graphshaper SIZE"
7
+ elsif ARGV[0].to_i < 21
8
+ puts "Please choose a size of at least 21"
7
9
  else
8
- inner_nodes = 20
9
- number_of_nodes = ARGV[0].to_i
10
+ number_of_vertices = ARGV[0].to_i
11
+ inner_vertices = 20
10
12
 
11
- graph = Graphshaper::UndirectedGraph.without_orphans_with_order_of 20, edge_creation_logger: STDOUT
12
- (number_of_nodes - inner_nodes).times do
13
+ edge_output_file = File.new "edges.csv", "w"
14
+ vertex_output_file = File.new "vertices.csv", "w"
15
+
16
+ start_time = Time.now
17
+ graph = Graphshaper::UndirectedGraph.without_orphans_with_order_of inner_vertices, edge_creation_logger: edge_output_file, vertex_creation_logger: vertex_output_file
18
+ (number_of_vertices - inner_vertices).times do
13
19
  graph.add_vertex do |preferential_attachment|
14
20
  preferential_attachment > rand
15
21
  end
16
22
  end
23
+ end_time = Time.now
24
+
25
+ ellapsed_time = end_time - start_time
26
+
27
+ puts "#{graph.order} vertices (saved to vertices.csv)"
28
+ puts "#{graph.size} edges (saved to edges.csv)"
29
+
30
+ if ellapsed_time < 2
31
+ puts "Generated in about one second"
32
+ elsif ellapsed_time < 60
33
+ puts "Generated in about #{ellapsed_time.round} seconds"
34
+ else
35
+ puts "Generated in about #{ellapsed_time.round / 60} minutes and #{ellapsed_time.round % 60} seconds"
36
+ end
17
37
  end
data/graphshaper.gemspec CHANGED
@@ -15,5 +15,7 @@ Gem::Specification.new do |gem|
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = Graphshaper::VERSION
17
17
 
18
- gem.add_development_dependency 'rspec', "~> 2.9.0"
18
+ gem.add_development_dependency "rake", "~> 0.9.2.2"
19
+ gem.add_development_dependency "rspec", "~> 2.9.0"
20
+ gem.add_development_dependency "yard", "~> 0.7.5"
19
21
  end
@@ -1,52 +1,91 @@
1
1
  require "set"
2
2
 
3
3
  module Graphshaper
4
+
5
+ # A Graph is undirected when its edges do not have a direction.
4
6
  class UndirectedGraph
5
- # Create a graph with a given number of vertices
7
+
8
+ # Create a graph with a given number of vertices and no edges.
9
+ #
10
+ # @param [Integer] number_of_vertices The number of vertices that the generated graph should have
11
+ # @param [Hash] options_hash The options to create an undirected graph
12
+ # @option options_hash [IO] :edge_creation_logger An IO object that should log every edge creation in the graph (default: no logging)
13
+ # @option options_hash [IO] ::vertex_creation_logger An IO object that should log every vertex creation in the graph (default: no logging)
6
14
  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
15
+ @edge_creation_logger = options_hash[:edge_creation_logger] if options_hash.has_key? :edge_creation_logger
16
+ @vertex_creation_logger = options_hash[:vertex_creation_logger] if options_hash.has_key? :vertex_creation_logger
10
17
 
11
- if options_hash.has_key? :edge_creation_logger
12
- @edge_creation_logger = options_hash[:edge_creation_logger]
13
- end
18
+ @vertex_degrees = []
19
+ @unconnected_vertices = Set.new
20
+
21
+ number_of_vertices.times { add_vertex }
22
+ @edges = Set.new
14
23
  end
15
24
 
25
+ # Create a graph with a given number of vertices. Then it adds random edges until the graph doesn't contain any orphans (vertices without edges).
26
+ #
27
+ # @param [Integer] number_of_vertices The number of vertices that the generated graph should have
28
+ # @param [Hash] options_hash The options to create an undirected graph
29
+ # @option options_hash [IO] :edge_creation_logger An IO object that should log every edge creation in the graph (default: no logging)
30
+ # @option options_hash [IO] ::vertex_creation_logger An IO object that should log every vertex creation in the graph (default: no logging)
31
+ # @return [UndirectedGraph] The generated graph.
16
32
  def UndirectedGraph.without_orphans_with_order_of(number_of_vertices, options_hash = {})
17
33
  graph = self.new number_of_vertices, options_hash
18
34
 
19
35
  while graph.number_of_orphans > 0
20
- a = rand(graph.order)
21
- while a == (b = rand(graph.order)); end
36
+ vertex_orphan = graph.orphans.shuffle.first
37
+ while vertex_orphan == (random_vertex = rand(graph.order)); end
22
38
 
23
- graph.add_edge a, b
39
+ graph.add_edge vertex_orphan, random_vertex
24
40
  end
25
41
 
26
42
  return graph
27
43
  end
28
44
 
29
- # the number of vertices
45
+ # The number of vertices.
46
+ #
47
+ # @return [Integer] Number of vertices.
30
48
  def order
31
49
  @vertex_degrees.length
32
50
  end
33
51
 
34
- # the number of edges
52
+ # The number of edges.
53
+ #
54
+ # @return [Integer] Number of edges.
35
55
  def size
36
56
  @edges.length
37
57
  end
38
58
 
59
+ # Add a new vertex to the graph.
60
+ # If you call it with a block {|preferential_attachment| ... }, the block will be called for every existing vertex: An edge from the new vertex to this vertex will be created if and only if the block returns true.
61
+ # @yield [preferential_attachment] The block that tests if the edge should be added.
62
+ #
63
+ # @yieldparam [Float] preferential_attachment The preferential attachment of the existing vertex
64
+ # @yieldreturn [Boolean] Should the edge be added?
65
+ #
66
+ # @return [Integer] ID of the newly created vertex.
39
67
  def add_vertex(&block)
68
+ new_vertex_id = @vertex_degrees.length
69
+
40
70
  @vertex_degrees << 0
71
+ @unconnected_vertices << new_vertex_id
72
+
73
+ @vertex_creation_logger << "#{new_vertex_id}\n" if @vertex_creation_logger
41
74
 
42
- new_vertex_id = @vertex_degrees.length - 1
43
75
  if block_given?
44
76
  each_vertex_with_preferential_attachment do |vertex_id, preferential_attachment|
45
77
  add_edge new_vertex_id, vertex_id if block.call(preferential_attachment)
46
78
  end
47
79
  end
80
+
81
+ new_vertex_id
48
82
  end
49
83
 
84
+ # Add a new edge to the graph between two existing vertices.
85
+ #
86
+ # @param [Integer] first_vertex_id
87
+ # @param [Integer] second_vertex_id
88
+ # @raise [RuntimeError] The method throws a RuntimeError if you try to add a self-referential edge or an edge with a non-existing vertex.
50
89
  def add_edge(first_vertex_id, second_vertex_id)
51
90
  if first_vertex_id == second_vertex_id
52
91
  raise "No Self-Referential Edge"
@@ -63,19 +102,40 @@ module Graphshaper
63
102
  end
64
103
  end
65
104
 
105
+ # Tests, if an edge between the two vertices exists.
106
+ #
107
+ # @param [Integer] first_vertex_id
108
+ # @param [Integer] second_vertex_id
109
+ # @return [Boolean] Does the vertex exist?
66
110
  def edge_between?(first_vertex_id, second_vertex_id)
67
111
  @edges.include? [first_vertex_id, second_vertex_id]
68
112
  end
69
113
 
70
- # The number of vertices without edges
114
+ # The number of vertices without edges.
115
+ #
116
+ # @return [Integer] Number of vertices without edges.
71
117
  def number_of_orphans
72
- @unconnected_vertices.length
118
+ @unconnected_vertices.to_a.length
73
119
  end
74
120
 
121
+ # The vertices without edges as an array.
122
+ #
123
+ # @return [Array<Integer>] IDs of the Vertices without edges.
124
+ def orphans
125
+ @unconnected_vertices.to_a
126
+ end
127
+
128
+ # Return the vertex degree for the vertex with the given ID.
129
+ #
130
+ # @param [Integer] vertex_id
131
+ # @return [Integer] Degree of the given vertex
75
132
  def vertex_degree_for(vertex_id)
76
133
  @vertex_degrees[vertex_id]
77
134
  end
78
135
 
136
+ # Calculates the distribution of degrees in the graph. The value at the n-th position of the returned array is the number of vertices with n edges.
137
+ #
138
+ # @return [Array<Integer>] The degree distribution as an array of Integers.
79
139
  def degree_distribution
80
140
  degree_distribution = []
81
141
  @vertex_degrees.each do |vertex_degree|
@@ -87,15 +147,19 @@ module Graphshaper
87
147
  end
88
148
  degree_distribution
89
149
  end
90
-
91
- def calculate_vertex_degree_for(vertex_id)
92
- @vertex_degrees[vertex_id]
93
- end
94
-
150
+
151
+ # Return the sum of all degrees.
152
+ #
153
+ # @return [Integer] Sum of all degrees
95
154
  def sum_of_all_degrees
96
155
  @edges.length * 2
97
156
  end
98
157
 
158
+ # Iterate over all vertices of the graph. Call it with a block {|vertex_id, preferential_attachment| ... }.
159
+ #
160
+ # @yield [vertex_id, preferential_attachment] The block that tests if the edge should be added.
161
+ # @yieldparam [Integer] vertex_id The preferential attachment of the existing vertex
162
+ # @yieldparam [Float] preferential_attachment The preferential attachment of the existing vertex
99
163
  def each_vertex_with_preferential_attachment(&block)
100
164
  if sum_of_all_degrees > 0
101
165
  preferential_attachments = @vertex_degrees.map { |degree| degree.round(1) / sum_of_all_degrees }
@@ -106,6 +170,5 @@ module Graphshaper
106
170
  end
107
171
  end
108
172
  end
109
-
110
173
  end
111
174
  end
@@ -1,3 +1,3 @@
1
1
  module Graphshaper
2
- VERSION = "0.0.2"
2
+ VERSION = "0.1"
3
3
  end
@@ -17,6 +17,14 @@ describe Graphshaper::UndirectedGraph do
17
17
  edge_creation_logger.string.should ==("1,3\n2,3\n")
18
18
  end
19
19
 
20
+ it "should create a graph with a logger for vertex creation" do
21
+ vertex_creation_logger = StringIO.new
22
+ graph = Graphshaper::UndirectedGraph.new 5, vertex_creation_logger: vertex_creation_logger
23
+
24
+ graph.add_vertex
25
+ vertex_creation_logger.string.should ==("0\n1\n2\n3\n4\n5\n")
26
+ end
27
+
20
28
  describe "initialized graph" do
21
29
  before :each do
22
30
  @graph = Graphshaper::UndirectedGraph.new 5
@@ -101,14 +109,14 @@ describe Graphshaper::UndirectedGraph do
101
109
 
102
110
  it "should calculate the degree of 0 for every vertex in a graph without edges" do
103
111
  5.times do |vertex_id|
104
- @graph.calculate_vertex_degree_for(vertex_id).should ==0
112
+ @graph.vertex_degree_for(vertex_id).should ==0
105
113
  end
106
114
  end
107
115
 
108
116
  it "should calculate the degree for a vertex with two edges" do
109
117
  @graph.add_edge 0,1
110
118
  @graph.add_edge 1,2
111
- @graph.calculate_vertex_degree_for(1).should ==2
119
+ @graph.vertex_degree_for(1).should ==2
112
120
  end
113
121
 
114
122
  it "should calculate the sum of all degrees" do
@@ -147,12 +155,12 @@ describe Graphshaper::UndirectedGraph do
147
155
  it "should add a vertex to the graph with edges according to preferential attachment" do
148
156
  @graph.add_edge 0,1
149
157
 
150
- # Two nodes with preferential_attachment of 0.5, all others with 0
158
+ # Two vertices with preferential_attachment of 0.5, all others with 0
151
159
  @graph.add_vertex do |preferential_attachment|
152
160
  preferential_attachment > 0.4
153
161
  end
154
162
 
155
- # One more node
163
+ # One more vertex
156
164
  @graph.order.should ==(6)
157
165
 
158
166
  # Two additional edges
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphshaper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: '0.1'
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-03 00:00:00.000000000Z
12
+ date: 2012-04-04 00:00:00.000000000Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: &70350123480940 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.9.2.2
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70350123480940
14
25
  - !ruby/object:Gem::Dependency
15
26
  name: rspec
16
- requirement: &70183947411400 !ruby/object:Gem::Requirement
27
+ requirement: &70350123480440 !ruby/object:Gem::Requirement
17
28
  none: false
18
29
  requirements:
19
30
  - - ~>
@@ -21,7 +32,18 @@ dependencies:
21
32
  version: 2.9.0
22
33
  type: :development
23
34
  prerelease: false
24
- version_requirements: *70183947411400
35
+ version_requirements: *70350123480440
36
+ - !ruby/object:Gem::Dependency
37
+ name: yard
38
+ requirement: &70350123479980 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 0.7.5
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70350123479980
25
47
  description: Generate realistic graphs
26
48
  email:
27
49
  - me@moonglum.net
@@ -31,6 +53,7 @@ extensions: []
31
53
  extra_rdoc_files: []
32
54
  files:
33
55
  - .gitignore
56
+ - .travis.yml
34
57
  - Gemfile
35
58
  - LICENSE
36
59
  - README.md
@@ -69,3 +92,4 @@ summary: Graphshaper can generate realistic, scale-free graphs of any size.
69
92
  test_files:
70
93
  - spec/spec_helper.rb
71
94
  - spec/undirected_graph_spec.rb
95
+ has_rdoc: