graphshaper 0.0.2 → 0.1

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.
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: