graphs 0.1.4-x86-linux → 0.1.5-x86-linux

Sign up to get free protection for your applications and to get access to all the features.
data/lib/graph.rb CHANGED
@@ -8,7 +8,8 @@ require 'yaml'
8
8
  # @!attribute [rw] edges
9
9
  # @return [EdgeArray] array of current Graph's edges
10
10
  # @!attribute [rw] attrs
11
- # @return [Hash] attributes of the current Graph (e.g. author, description, …)
11
+ # @return [Hash] attributes of the current Graph (e.g. author, description, …).
12
+ # By default, the graph is directed, i.e. the :directed attribute is set to `true`.
12
13
  class Graph
13
14
 
14
15
  # Return a new Graph which is the intersection of every given graph.
@@ -46,11 +47,60 @@ class Graph
46
47
  perform_graphs_group_op(*graphs, &:^)
47
48
  end
48
49
 
49
- # An array of nodes, each node is an hash of label/value paires
50
+ # A node. This class is just a wrapper around a hash of
51
+ # attributes since in version <= 0.1.5 nodes were simple hashes
52
+ class Node
53
+
54
+ attr_accessor :attrs
55
+
56
+ def initialize(attrs=nil)
57
+ @attrs = attrs || {}
58
+ end
59
+
60
+ # compare two nodes
61
+ # @param other [Node]
62
+ def ==(other)
63
+ return false if !other.is_a?(Node)
64
+
65
+ @attrs == other.attrs
66
+ end
67
+
68
+ def method_missing(method, *args, &block)
69
+ @attrs.send(method, *args, &block)
70
+ end
71
+
72
+ end
73
+
74
+ # An edge. This class is just a wrapper around a hash of
75
+ # attributes since in version <= 0.1.5 edges were simple hashes
76
+ class Edge
77
+
78
+ attr_accessor :attrs
79
+
80
+ def initialize(attrs=nil)
81
+ @attrs = attrs || {}
82
+ end
83
+
84
+ # compare two edges
85
+ # @param other [Edge]
86
+ def ==(other)
87
+ return false if !other.is_a?(Edge)
88
+
89
+ @attrs == other.attrs
90
+ end
91
+
92
+ def method_missing(method, *args, &block)
93
+ @attrs.send(method, *args, &block)
94
+ end
95
+
96
+ end
97
+
98
+ # An array of Node objects
50
99
  class NodeArray < Array
51
100
 
52
- def initialize(*args)
53
- super(*args)
101
+ def initialize(li)
102
+ nodes = li.map { |n| n.is_a?(Node) ? n : Node.new(n) }
103
+ super(nodes)
54
104
  @defaults = {}
55
105
  end
56
106
 
@@ -64,19 +114,43 @@ class Graph
64
114
  end
65
115
 
66
116
  # Add the given node at the end of the list
67
- # @param o [Node]
68
- def push(o)
69
- if (!o.is_a?(Hash))
70
- raise TypeError.new "#{o.inspect} is not an Hash!"
117
+ # @param n [Node]
118
+ def push(n)
119
+ if (!n.is_a?(Hash) && !n.is_a?(Node))
120
+ raise TypeError.new "#{n.inspect} is not an Hash or a Node!"
71
121
  end
72
- o2 = o.clone
73
- o2.update(@defaults)
74
- super(o2)
122
+
123
+ super(n.clone.update(@defaults))
75
124
  end
76
125
  end
77
126
 
78
- # An array of edges, each edge is an hash of label/value paires
79
- class EdgeArray < NodeArray
127
+ # An array of Edge objects
128
+ class EdgeArray < Array
129
+ def initialize(li)
130
+ edges = li.map { |n| n.is_a?(Edge) ? n : Edge.new(n) }
131
+ super(edges)
132
+ @defaults = {}
133
+ end
134
+
135
+ # Set some default values for current elements.
136
+ # @note This method can be called multiple times.
137
+ # @example Set all edges's 'created-at' value to '2012-05-03'
138
+ # myEdgeList.set_default({'created-at'=>'2012-05-03'})
139
+ # @param dict [Hash]
140
+ def set_default(dict)
141
+ @defaults.update(dict)
142
+ self.map! { |e| e.update(@defaults) }
143
+ end
144
+
145
+ # Add the given edge at the end of the list
146
+ # @param e [Edge]
147
+ def push(e)
148
+ if (!e.is_a?(Hash) && !e.is_a?(Edge))
149
+ raise TypeError.new "#{e.inspect} is not an Hash or an Edge!"
150
+ end
151
+
152
+ super(e.clone.update(@defaults))
153
+ end
80
154
  end
81
155
 
82
156
  attr_accessor :nodes, :edges, :attrs
@@ -86,7 +160,7 @@ class Graph
86
160
  def initialize(nodes=nil, edges=nil)
87
161
  @nodes = NodeArray.new(nodes || [])
88
162
  @edges = EdgeArray.new(edges || [])
89
- @attrs = {}
163
+ @attrs = { :directed => true }
90
164
  end
91
165
 
92
166
  # Test if current graph has same nodes and edges as the other
@@ -148,6 +222,7 @@ class Graph
148
222
  # Perform an OR operation on the current Graph and the given one. Returns a
149
223
  # new graph which every node is in the current Graph and/or the other
150
224
  # (idem for edges).
225
+ # @param other [Graph]
151
226
  def |(other)
152
227
  if (!other.is_a?(Graph))
153
228
  return nil
@@ -178,6 +253,12 @@ class Graph
178
253
  self - other
179
254
  end
180
255
 
256
+ # Return true if the Graph is directed.
257
+ # @see Graph.attrs
258
+ def directed?()
259
+ self.attrs[:directed]
260
+ end
261
+
181
262
  # Clone the current graph. All nodes and edges are also cloned. A new Graph
182
263
  # is returned.
183
264
  def clone()
@@ -220,6 +301,111 @@ class Graph
220
301
 
221
302
  end
222
303
 
304
+ # Return the degree of the node n in the current graph, i.e. the number
305
+ # of edges which are connected to this node. Note that this is useful
306
+ # only for a undirected graph, for a directed one, you should use
307
+ # Graph#in_degree_of and/or Graph#out_degree_of.
308
+ #
309
+ # Edges must have the 'node1' and 'node2' attributes, which must contain
310
+ # the 'label' attributes of nodes.
311
+ #
312
+ # @param n [Node,String] A node or a label of one
313
+ # @see Graph#in_degree_of
314
+ # @see Graph#out_degree_of
315
+ def degree_of(n)
316
+ label = Graph::get_label_from_node(n)
317
+
318
+ degree = 0
319
+
320
+ # This is more efficient than in_degree_of(n)+out_degree_of(n)
321
+ # since it goes only once through the edges array
322
+ self.edges.each do |e|
323
+ degree += 1 if (e['node1'] || e[:node1]).to_s == label
324
+ degree += 1 if (e['node2'] || e[:node2]).to_s == label
325
+ end
326
+
327
+ degree
328
+ end
329
+
330
+ # Return the “in degree” of the node n in the current graph, i.e. the number
331
+ # of edges which are directed to this node. Note that the graph must be oriented.
332
+ #
333
+ # Edges must have the 'node1' and 'node2' attributes, which must contain
334
+ # the 'label' attributes of nodes.
335
+ #
336
+ # @param n [Node,String] A node or a label of one
337
+ # @see Graph#degree_of
338
+ # @see Graph#out_degree_of
339
+ def in_degree_of(n)
340
+ label = Graph::get_label_from_node(n)
341
+
342
+ degree = 0
343
+
344
+ self.edges.each do |e|
345
+ degree += 1 if (e['node2'] || e[:node2]).to_s == label
346
+ end
347
+
348
+ degree
349
+ end
350
+
351
+ # Return the “out degree” of the node n in the current graph, i.e. the number
352
+ # of edges which are directed from this node. Note that the graph must be oriented.
353
+ #
354
+ # Edges must have the 'node1' and 'node2' attributes, which must contain
355
+ # the 'label' attributes of nodes.
356
+ #
357
+ # @param n [Node,String] A node or a label of one
358
+ # @see Graph#degree_of
359
+ # @see Graph#out_degree_of
360
+ def out_degree_of(n)
361
+ label = Graph::get_label_from_node(n)
362
+
363
+ degree = 0
364
+
365
+ self.edges.each do |e|
366
+ degree += 1 if (e['node1'] || e[:node1]).to_s == label
367
+ end
368
+
369
+ degree
370
+ end
371
+
372
+ # Return the “in degree” of the node n in the current graph, i.e. the number
373
+ # of edges which are directed to this node. Note that the graph must be oriented.
374
+ #
375
+ # Edges must have the 'node1' and 'node2' attributes, which must contain
376
+ # the 'label' attributes of nodes.
377
+ #
378
+ # @param n [Node,String] A node or a label of one
379
+ # @see Graph#degree_of
380
+ # @see Graph#out_degree_of
381
+ def in_degree_of(n)
382
+ label = Graph::get_label_from_node(n)
383
+
384
+ degree = 0
385
+
386
+ # This is more efficient than in_degree_of(n)+out_degree_of(n)
387
+ # since it goes only once through the edges array
388
+ self.edges.each do |e|
389
+ degree += 1 if (e['node2'] || e[:node2]).to_s == label
390
+ end
391
+
392
+ degree
393
+ end
394
+
395
+
396
+ # return the label of a node. Raise a TypeError exception if the argument
397
+ # is not a Node nor a String object.
398
+ # @param n [Node,String] A node with a 'label' or :label attribute, or a string
399
+ def Graph::get_label_from_node(n)
400
+ label = n.is_a?(Node) \
401
+ ? (n['label'] || n[:label]).to_s \
402
+ : n.is_a?(String) ? n : nil
403
+
404
+ raise TypeError.new("#{n.inspect} must be a Node or String object.") if label.nil?
405
+
406
+ label
407
+ end
408
+
223
409
  # return the provided set of graphs, from which every node/edge label which
224
410
  # is not in all graphs has been removed. So every returned graph has same
225
411
  # node/edge labels than each other
@@ -275,16 +461,17 @@ class Graph
275
461
  # from the arguments
276
462
  if graphs[-1].is_a?(Hash)
277
463
  return nil if graphs.length == 1
278
- opts.update(graphs.pop)
464
+
465
+ opts = graphs.pop
279
466
  end
280
467
 
281
468
  # return nil if one argument is not a graph
282
- graphs.each {|g|
283
- return nil if (!g.is_a?(Graph))
284
- }
469
+ graphs.each do |g|
470
+ return nil if !g.is_a?(Graph)
471
+ end
285
472
 
286
473
  # if :same_fields option is set, call `keep_only_same_fields` function
287
- *graphs = keep_only_same_fields(*graphs) if opts[:same_fields]
474
+ graphs = keep_only_same_fields(*graphs) if opts[:same_fields]
288
475
 
289
476
  # perform an and operation on all graph list
290
477
  graphs.inject(&block)
@@ -1,8 +1,6 @@
1
1
  #! /usr/bin/ruby1.9.1
2
2
 
3
- require 'graph'
4
- require 'graphs/gdf'
5
- require 'test/unit'
3
+ require_relative '../lib/graphs/gdf'
6
4
 
7
5
  module Utils
8
6
  def self.get_sample_graph
@@ -1,5 +1,4 @@
1
1
  #! /usr/bin/ruby1.9.1
2
2
  # -*- coding: UTF-8 -*-
3
3
 
4
- require 'test/unit'
5
4
  # require 'graphs/gexf'
@@ -3,7 +3,7 @@
3
3
 
4
4
  require 'test/unit'
5
5
  require 'yaml'
6
- require 'graph'
6
+ require_relative '../lib/graph'
7
7
 
8
8
  class Graph_test < Test::Unit::TestCase
9
9
 
@@ -40,9 +40,10 @@ class Graph_test < Test::Unit::TestCase
40
40
 
41
41
  # == Graph#attrs == #
42
42
 
43
- def test_graph_attrs
43
+ def test_empty_graph_attrs
44
44
  g = @@empty
45
- assert_equal({}, g.attrs)
45
+ assert_equal({:directed => true}, g.attrs)
46
+ assert_equal(true, g.directed?)
46
47
 
47
48
  g.attrs['mode'] = 'static'
48
49
  g.attrs['defaultedgetype'] = 'directed'
@@ -181,6 +182,9 @@ class Graph_test < Test::Unit::TestCase
181
182
 
182
183
  assert_equal(21, g.nodes[0]['age'])
183
184
  assert_equal(21, g.nodes[1]['age'])
185
+
186
+ assert_equal(21, g.nodes[0]['age'])
187
+ assert_equal(21, g.nodes[1]['age'])
184
188
  end
185
189
 
186
190
  def test_nodearray_set_default_existing_property
@@ -467,8 +471,8 @@ class Graph_test < Test::Unit::TestCase
467
471
  def test_union_sample_graph_and_itself
468
472
  g = @@sample_graph
469
473
 
470
- assert_equal(g, Graph::intersection(g, g))
471
- assert_equal(g, Graph::intersection(g, g, g, g))
474
+ assert_equal(g, Graph::union(g, g))
475
+ assert_equal(g, Graph::union(g, g, g, g))
472
476
  end
473
477
 
474
478
  def test_union_sample_graph_and_other_sample_graph
@@ -0,0 +1,84 @@
1
+ #! /usr/bin/ruby1.9.1
2
+ # -*- coding: UTF-8 -*-
3
+
4
+ class Node_test < Test::Unit::TestCase
5
+
6
+ def setup
7
+ @@empty = Graph::Node.new
8
+ @@alice = Graph::Node.new('label' => 'Alice')
9
+
10
+ # Alice ----> Bob
11
+ # ↑ ↑
12
+ # | |
13
+ # Oscar -------'
14
+ @@sample_graph = Graph.new(
15
+ [
16
+ { 'label' => 'Alice' },
17
+ { 'label' => 'Bob' },
18
+ { 'label' => 'Oscar' }
19
+ ],
20
+ [
21
+ { 'node1' => 'Alice', 'node2' => 'Bob' },
22
+ { 'node1' => 'Oscar', 'node2' => 'Alice'},
23
+ { 'node1' => 'Oscar', 'node2' => 'Bob'}
24
+ ]
25
+ )
26
+
27
+ end
28
+
29
+ def test_create_graph_with_node_objects
30
+ g1 = Graph.new([
31
+ Graph::Node.new('label' => 'Foo')
32
+ ])
33
+
34
+ g2 = Graph.new([
35
+ { 'label' => 'Foo' }
36
+ ])
37
+
38
+ assert_equal(g2, g1)
39
+ end
40
+
41
+ def test_node_attrs
42
+ a = @@alice
43
+
44
+ a['foo'] = 'bar'
45
+
46
+ assert_equal('Alice', @@alice['label'])
47
+ assert_equal('bar', @@alice['foo'])
48
+ assert_equal(nil, @@alice['fooo'])
49
+ end
50
+
51
+ def test_node_degree_by_label
52
+ assert_equal(2, @@sample_graph.degree_of('Alice'))
53
+ assert_equal(2, @@sample_graph.degree_of('Oscar'))
54
+ assert_equal(2, @@sample_graph.degree_of('Bob'))
55
+ assert_equal(0, @@sample_graph.degree_of('not found'))
56
+ end
57
+
58
+ def test_node_degree_by_object
59
+ assert_equal(2, @@sample_graph.degree_of(@@alice))
60
+ end
61
+
62
+ def test_node_in_degree_by_label
63
+ assert_equal(1, @@sample_graph.in_degree_of('Alice'))
64
+ assert_equal(2, @@sample_graph.in_degree_of('Bob'))
65
+ assert_equal(0, @@sample_graph.in_degree_of('Oscar'))
66
+ assert_equal(0, @@sample_graph.in_degree_of('not found'))
67
+ end
68
+
69
+ def test_node_in_degree_by_object
70
+ assert_equal(1, @@sample_graph.in_degree_of(@@alice))
71
+ end
72
+
73
+ def test_node_out_degree_by_label
74
+ assert_equal(1, @@sample_graph.out_degree_of('Alice'))
75
+ assert_equal(0, @@sample_graph.out_degree_of('Bob'))
76
+ assert_equal(2, @@sample_graph.out_degree_of('Oscar'))
77
+ assert_equal(0, @@sample_graph.out_degree_of('not found'))
78
+ end
79
+
80
+ def test_node_out_degree_by_object
81
+ assert_equal(1, @@sample_graph.out_degree_of(@@alice))
82
+ end
83
+
84
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  prerelease:
6
6
  platform: x86-linux
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-05-06 00:00:00.000000000 Z
12
+ date: 2012-10-23 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Provide functions to (un)parse GDF files and generate graphs
15
15
  email: batifon@yahoo.fr
@@ -19,9 +19,10 @@ extra_rdoc_files: []
19
19
  files:
20
20
  - lib/graph.rb
21
21
  - lib/graphs/gdf.rb
22
- - tests/tests_gexf.rb
23
- - tests/tests_graph.rb
24
- - tests/tests_gdf.rb
22
+ - tests/graph_tests.rb
23
+ - tests/gdf_tests.rb
24
+ - tests/node_tests.rb
25
+ - tests/gexf_tests.rb
25
26
  homepage: https://github.com/bfontaine/Graphs.rb
26
27
  licenses:
27
28
  - MIT
@@ -48,6 +49,7 @@ signing_key:
48
49
  specification_version: 3
49
50
  summary: Utilities to manipulate graph files
50
51
  test_files:
51
- - tests/tests_gexf.rb
52
- - tests/tests_graph.rb
53
- - tests/tests_gdf.rb
52
+ - tests/graph_tests.rb
53
+ - tests/gdf_tests.rb
54
+ - tests/node_tests.rb
55
+ - tests/gexf_tests.rb