plexus 0.5.4 → 0.5.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/Gemfile +3 -0
  2. data/LICENSE +37 -0
  3. data/README.md +208 -0
  4. data/Rakefile +25 -0
  5. data/lib/plexus.rb +90 -0
  6. data/lib/plexus/adjacency_graph.rb +225 -0
  7. data/lib/plexus/arc.rb +60 -0
  8. data/lib/plexus/arc_number.rb +50 -0
  9. data/lib/plexus/biconnected.rb +84 -0
  10. data/lib/plexus/chinese_postman.rb +91 -0
  11. data/lib/plexus/classes/graph_classes.rb +28 -0
  12. data/lib/plexus/common.rb +63 -0
  13. data/lib/plexus/comparability.rb +63 -0
  14. data/lib/plexus/directed_graph.rb +78 -0
  15. data/lib/plexus/directed_graph/algorithms.rb +95 -0
  16. data/lib/plexus/directed_graph/distance.rb +167 -0
  17. data/lib/plexus/dot.rb +94 -0
  18. data/lib/plexus/edge.rb +38 -0
  19. data/lib/plexus/ext.rb +79 -0
  20. data/lib/plexus/graph.rb +628 -0
  21. data/lib/plexus/graph_api.rb +35 -0
  22. data/lib/plexus/labels.rb +112 -0
  23. data/lib/plexus/maximum_flow.rb +77 -0
  24. data/lib/plexus/ruby_compatibility.rb +17 -0
  25. data/lib/plexus/search.rb +510 -0
  26. data/lib/plexus/strong_components.rb +93 -0
  27. data/lib/plexus/support/support.rb +9 -0
  28. data/lib/plexus/undirected_graph.rb +56 -0
  29. data/lib/plexus/undirected_graph/algorithms.rb +90 -0
  30. data/lib/plexus/version.rb +6 -0
  31. data/spec/biconnected_spec.rb +27 -0
  32. data/spec/chinese_postman_spec.rb +27 -0
  33. data/spec/community_spec.rb +44 -0
  34. data/spec/complement_spec.rb +27 -0
  35. data/spec/digraph_distance_spec.rb +121 -0
  36. data/spec/digraph_spec.rb +339 -0
  37. data/spec/dot_spec.rb +48 -0
  38. data/spec/edge_spec.rb +158 -0
  39. data/spec/inspection_spec.rb +38 -0
  40. data/spec/multi_edge_spec.rb +32 -0
  41. data/spec/neighborhood_spec.rb +36 -0
  42. data/spec/properties_spec.rb +146 -0
  43. data/spec/search_spec.rb +227 -0
  44. data/spec/spec.opts +4 -0
  45. data/spec/spec_helper.rb +59 -0
  46. data/spec/strong_components_spec.rb +61 -0
  47. data/spec/triangulated_spec.rb +125 -0
  48. data/spec/undirected_graph_spec.rb +220 -0
  49. data/vendor/priority-queue/CHANGELOG +33 -0
  50. data/vendor/priority-queue/Makefile +140 -0
  51. data/vendor/priority-queue/README +133 -0
  52. data/vendor/priority-queue/benchmark/dijkstra.rb +171 -0
  53. data/vendor/priority-queue/compare_comments.rb +49 -0
  54. data/vendor/priority-queue/doc/c-vs-rb.png +0 -0
  55. data/vendor/priority-queue/doc/compare_big.gp +14 -0
  56. data/vendor/priority-queue/doc/compare_big.png +0 -0
  57. data/vendor/priority-queue/doc/compare_small.gp +15 -0
  58. data/vendor/priority-queue/doc/compare_small.png +0 -0
  59. data/vendor/priority-queue/doc/results.csv +37 -0
  60. data/vendor/priority-queue/ext/priority_queue/CPriorityQueue/extconf.rb +2 -0
  61. data/vendor/priority-queue/ext/priority_queue/CPriorityQueue/priority_queue.c +947 -0
  62. data/vendor/priority-queue/lib/priority_queue.rb +14 -0
  63. data/vendor/priority-queue/lib/priority_queue/c_priority_queue.rb +1 -0
  64. data/vendor/priority-queue/lib/priority_queue/poor_priority_queue.rb +46 -0
  65. data/vendor/priority-queue/lib/priority_queue/ruby_priority_queue.rb +526 -0
  66. data/vendor/priority-queue/priority_queue.so +0 -0
  67. data/vendor/priority-queue/setup.rb +1551 -0
  68. data/vendor/priority-queue/test/priority_queue_test.rb +371 -0
  69. data/vendor/rdot.rb +360 -0
  70. metadata +100 -10
@@ -0,0 +1,628 @@
1
+ module Plexus
2
+ # Using the methods required by the {GraphAPI}, it implements all the
3
+ # *basic* functions of a {Graph} using *only* functions
4
+ # requested in {GraphAPI}. The process is under the control of the pattern
5
+ # {AdjacencyGraphBuilder}, unless a specific implementation is specified
6
+ # during initialization.
7
+ #
8
+ # An actual, complete implementation still needs to be done using this cheap result,
9
+ # hence {Digraph}, {UndirectedGraph} and their roomates.
10
+ module GraphBuilder
11
+ include Enumerable
12
+ include Labels
13
+ include Dot
14
+
15
+ #def self.[](*a)
16
+ #puts self
17
+ #self.new.from_array(*a)
18
+ #end
19
+ # after the class->module transition, has been moved at implementation level,
20
+ # using a helper (extends_host)
21
+ #
22
+ extends_host
23
+ module ClassMethods
24
+ def [](*a)
25
+ self.new.from_array(*a)
26
+ end
27
+ end
28
+
29
+ # Creates a generic graph.
30
+ #
31
+ # @param [Hash(Plexus::Graph, Array)] *params initialization parameters.
32
+ # See {AdjacencyGraphBuilder#implementation_initialize} for more details.
33
+ # @return [Graph]
34
+ #
35
+ def initialize(*params)
36
+ raise ArgumentError if params.any? do |p|
37
+ # FIXME: checking wether it's a GraphBuilder (module) is not sufficient
38
+ # and the is_a? redefinition trick (instance_evaling) should be
39
+ # completed by a clever way to check the actual class of p.
40
+ # Maybe using ObjectSpace to get the available Graph classes?
41
+ !(p.is_a? Plexus::GraphBuilder or p.is_a? Array or p.is_a? Hash)
42
+ end
43
+
44
+ args = params.last || {}
45
+
46
+ class << self
47
+ self
48
+ end.module_eval do
49
+ # These inclusions trigger some validations checks by the way.
50
+ include(args[:implementation] ? args[:implementation] : Plexus::AdjacencyGraphBuilder)
51
+ include(args[:algorithmic_category] ? args[:algorithmic_category] : Plexus::DigraphBuilder )
52
+ include Plexus::GraphAPI
53
+ end
54
+
55
+ implementation_initialize(*params)
56
+ end
57
+
58
+ # Shortcut for creating a Graph.
59
+ #
60
+ # Using an arry of implicit {Arc}, specifying the vertices:
61
+ #
62
+ # Plexus::Graph[1,2, 2,3, 2,4, 4,5].edges.to_a.to_s
63
+ # # => "(1-2)(2-3)(2-4)(4-5)"
64
+ #
65
+ # Using a Hash for specifying labels along the way:
66
+ #
67
+ # Plexus::Graph[ [:a,:b] => 3, [:b,:c] => 4 ] (note: do not use for Multi or Pseudo graphs)
68
+ #
69
+ # @param [Array, Hash] *a
70
+ # @return [Graph]
71
+ def from_array(*a)
72
+ if a.size == 1 and a[0].is_a? Hash
73
+ # Convert to edge class
74
+ a[0].each do |k,v|
75
+ #FIXME, edge class shouldn't be assume here!!!
76
+ if edge_class.include? Plexus::ArcNumber
77
+ add_edge!(edge_class[k[0],k[1],nil,v])
78
+ else
79
+ add_edge!(edge_class[k[0],k[1],v])
80
+ end
81
+ end
82
+ #FIXME, edge class shouldn't be assume here!!!
83
+ elsif a[0].is_a? Plexus::Arc
84
+ a.each{ |e| add_edge!(e); self[e] = e.label}
85
+ elsif a.size % 2 == 0
86
+ 0.step(a.size-1, 2) {|i| add_edge!(a[i], a[i+1])}
87
+ else
88
+ raise ArgumentError
89
+ end
90
+ self
91
+ end
92
+
93
+ # Non destructive version of {AdjacencyGraphBuilder#add_vertex!} (works on a copy of the graph).
94
+ #
95
+ # @param [vertex] v
96
+ # @param [Label] l
97
+ # @return [Graph] a new graph with the supplementary vertex
98
+ def add_vertex(v, l = nil)
99
+ x = self.class.new(self)
100
+ x.add_vertex!(v, l)
101
+ end
102
+
103
+ # Non destructive version {AdjacencyGraphBuilder#add_edge!} (works on a copy of the graph).
104
+ #
105
+ # @param [vertex] u
106
+ # @param [vertex] v
107
+ # @param [Label] l
108
+ # @return [Graph] a new graph with the supplementary edge
109
+ def add_edge(u, v = nil, l = nil)
110
+ x = self.class.new(self)
111
+ x.add_edge!(u, v, l)
112
+ end
113
+ alias add_arc add_edge
114
+
115
+ # Non destructive version of {AdjacencyGraphBuilder#remove_vertex!} (works on a copy of the graph).
116
+ #
117
+ # @param [vertex] v
118
+ # @return [Graph] a new graph without the specified vertex
119
+ def remove_vertex(v)
120
+ x = self.class.new(self)
121
+ x.remove_vertex!(v)
122
+ end
123
+
124
+ # Non destructive version {AdjacencyGraphBuilder#remove_edge!} (works on a copy of the graph).
125
+ #
126
+ # @param [vertex] u
127
+ # @param [vertex] v
128
+ # @return [Graph] a new graph without the specified edge
129
+ def remove_edge(u, v = nil)
130
+ x = self.class.new(self)
131
+ x.remove_edge!(u, v)
132
+ end
133
+ alias remove_arc remove_edge
134
+
135
+ # Computes the adjacent portions of the Graph.
136
+ #
137
+ # The options specify the parameters about the adjacency search.
138
+ # Note: it is probably more efficently done in the implementation class.
139
+ #
140
+ # @param [vertex, Edge] x can either be a vertex an edge
141
+ # @option options [Symbol] :type (:vertices) can be either `:edges` or `:vertices`
142
+ # @option options [Symbol] :direction (:all) can be `:in`, `:out` or `:all`
143
+ # @return [Array] an array of the adjacent portions
144
+ # @fixme
145
+ def adjacent(x, options = {})
146
+ d = directed? ? (options[:direction] || :out) : :all
147
+
148
+ # Discharge the easy ones first.
149
+ return [x.source] if x.is_a? Arc and options[:type] == :vertices and d == :in
150
+ return [x.target] if x.is_a? Arc and options[:type] == :vertices and d == :out
151
+ return [x.source, x.target] if x.is_a? Arc and options[:type] != :edges and d == :all
152
+
153
+ (options[:type] == :edges ? edges : to_a).select { |u| adjacent?(x,u,d) }
154
+ end
155
+ #FIXME: This is a hack around a serious problem
156
+ alias graph_adjacent adjacent
157
+
158
+ # Adds all specified vertices to the vertex set.
159
+ #
160
+ # @param [#each] *a an Enumerable vertices set
161
+ # @return [Graph] `self`
162
+ def add_vertices!(*a)
163
+ a.each { |v| add_vertex! v }
164
+ self
165
+ end
166
+
167
+ # Same as {GraphBuilder#add_vertices! add_vertices!} but works on copy of the receiver.
168
+ #
169
+ # @param [#each] *a
170
+ # @return [Graph] a modified copy of `self`
171
+ def add_vertices(*a)
172
+ x = self.class.new(self)
173
+ x.add_vertices!(*a)
174
+ self
175
+ end
176
+
177
+ # Adds all edges mentionned in the specified Enumerable to the edge set.
178
+ #
179
+ # Elements of the Enumerable can be either two-element arrays or instances of
180
+ # {Edge} or {Arc}.
181
+ #
182
+ # @param [#each] *a an Enumerable edges set
183
+ # @return [Graph] `self`
184
+ def add_edges!(*a)
185
+ a.each { |edge| add_edge!(edge) }
186
+ self
187
+ end
188
+ alias add_arcs! add_edges!
189
+
190
+ # Same as {GraphBuilder#add_egdes! add_edges!} but works on a copy of the receiver.
191
+ #
192
+ # @param [#each] *a an Enumerable edges set
193
+ # @return [Graph] a modified copy of `self`
194
+ def add_edges(*a)
195
+ x = self.class.new(self)
196
+ x.add_edges!(*a)
197
+ self
198
+ end
199
+ alias add_arcs add_edges
200
+
201
+ # Removes all vertices mentionned in the specified Enumerable from the graph.
202
+ #
203
+ # The process relies on {GraphBuilder#remove_vertex! remove_vertex!}.
204
+ #
205
+ # @param [#each] *a an Enumerable vertices set
206
+ # @return [Graph] `self`
207
+ def remove_vertices!(*a)
208
+ a.each { |v| remove_vertex! v }
209
+ end
210
+ alias delete_vertices! remove_vertices!
211
+
212
+ # Same as {GraphBuilder#remove_vertices! remove_vertices!} but works on a copy of the receiver.
213
+ #
214
+ # @param [#each] *a a vertex Enumerable set
215
+ # @return [Graph] a modified copy of `self`
216
+ def remove_vertices(*a)
217
+ x = self.class.new(self)
218
+ x.remove_vertices(*a)
219
+ end
220
+ alias delete_vertices remove_vertices
221
+
222
+ # Removes all edges mentionned in the specified Enumerable from the graph.
223
+ #
224
+ # The process relies on {GraphBuilder#remove_edges! remove_edges!}.
225
+ #
226
+ # @param [#each] *a an Enumerable edges set
227
+ # @return [Graph] `self`
228
+ def remove_edges!(*a)
229
+ a.each { |e| remove_edge! e }
230
+ end
231
+ alias remove_arcs! remove_edges!
232
+ alias delete_edges! remove_edges!
233
+ alias delete_arcs! remove_edges!
234
+
235
+ # Same as {GraphBuilder#remove_edges! remove_edges!} but works on a copy of the receiver.
236
+ #
237
+ # @param [#each] *a an Enumerable edges set
238
+ # @return [Graph] a modified copy of `self`
239
+ def remove_edges(*a)
240
+ x = self.class.new(self)
241
+ x.remove_edges!(*a)
242
+ end
243
+ alias remove_arcs remove_edges
244
+ alias delete_edges remove_edges
245
+ alias delete_arcs remove_edges
246
+
247
+ # Executes the given block for each vertex. It allows for mixing Enumerable in.
248
+ def each(&block)
249
+ vertices.each(&block)
250
+ end
251
+
252
+ # Returns true if the specified vertex belongs to the graph.
253
+ #
254
+ # This is a default implementation that is of O(n) average complexity.
255
+ # If a subclass uses a hash to store vertices, then this can be
256
+ # made into an O(1) average complexity operation.
257
+ #
258
+ # @param [vertex] v
259
+ # @return [Boolean]
260
+ def vertex?(v)
261
+ vertices.include?(v)
262
+ end
263
+ alias has_vertex? vertex?
264
+ # TODO: (has_)vertices?
265
+
266
+ # Returns true if u or (u,v) is an {Edge edge} of the graph.
267
+ #
268
+ # @overload edge?(a)
269
+ # @param [Arc, Edge] a
270
+ # @overload edge?(u, v)
271
+ # @param [vertex] u
272
+ # @param [vertex] v
273
+ # @return [Boolean]
274
+ def edge?(*args)
275
+ edges.include?(edge_convert(*args))
276
+ end
277
+ alias arc? edge?
278
+ alias has_edge? edge?
279
+ alias has_arc? edge?
280
+
281
+ # Tests two objects to see if they are adjacent.
282
+ #
283
+ # Note that in this method, one is primarily concerned with finding
284
+ # all adjacent objects in a graph to a given object. The concern is primarily on seeing
285
+ # if two objects touch. For two vertexes, any edge between the two will usually do, but
286
+ # the direction can be specified if needed.
287
+ #
288
+ # @param [vertex] source
289
+ # @param [vertex] target
290
+ # @param [Symbol] direction (:all) constraint on the direction of adjacency; may be either `:in`, `:out` or `:all`
291
+ def adjacent?(source, target, direction = :all)
292
+ if source.is_a? Plexus::Arc
293
+ raise NoArcError unless edge? source
294
+ if target.is_a? Plexus::Arc
295
+ raise NoArcError unless edge? target
296
+ (direction != :out and source.source == target.target) or (direction != :in and source.target == target.source)
297
+ else
298
+ raise NoVertexError unless vertex? target
299
+ (direction != :out and source.source == target) or (direction != :in and source.target == target)
300
+ end
301
+ else
302
+ raise NoVertexError unless vertex? source
303
+ if target.is_a? Plexus::Arc
304
+ raise NoArcError unless edge? target
305
+ (direction != :out and source == target.target) or (direction != :in and source == target.source)
306
+ else
307
+ raise NoVertexError unless vertex? target
308
+ (direction != :out and edge?(target,source)) or (direction != :in and edge?(source,target))
309
+ end
310
+ end
311
+ end
312
+
313
+ # Is the graph connected?
314
+ #
315
+ # A graph is called connected if every pair of distinct vertices in the graph
316
+ # can be connected through some path. The exact definition depends on whether
317
+ # the graph is directed or not, hence this method should overriden in specific
318
+ # implementations.
319
+ #
320
+ # This methods implements a lazy routine using the internal vertices hash.
321
+ # If you ever want to check connectivity state using a bfs/dfs algorithm, use
322
+ # the `:algo => :bfs` or `:dfs` option.
323
+ #
324
+ # @return [Boolean] `true` if the graph is connected, `false` otherwise
325
+ def connected?(options = {})
326
+ options = options.reverse_merge! :algo => :bfs
327
+ if options[:algo] == (:bfs || :dfs)
328
+ num_nodes = 0
329
+ send(options[:algo]) { |n| num_nodes += 1 }
330
+ return num_nodes == @vertex_dict.size
331
+ else
332
+ !@vertex_dict.collect { |v| degree(v) > 0 }.any? { |check| check == false }
333
+ end
334
+ end
335
+ # TODO: do it!
336
+ # TODO: for directed graphs, add weakly_connected? and strongly_connected? (aliased as strong?)
337
+ # TODO: in the context of vertices/Arc, add connected_vertices? and disconnected_vertices?
338
+ # TODO: maybe implement some routine which would compute cuts and connectivity? tricky though,
339
+ # but would be useful (k_connected?(k))
340
+
341
+ # Returns true if the graph has no vertex.
342
+ #
343
+ # @return [Boolean]
344
+ def empty?
345
+ vertices.size.zero?
346
+ end
347
+
348
+ # Returns true if the given object is a vertex or an {Arc arc} of the graph.
349
+ #
350
+ # @param [vertex, Arc] x
351
+ def include?(x)
352
+ x.is_a?(Plexus::Arc) ? edge?(x) : vertex?(x)
353
+ end
354
+ alias has? include?
355
+
356
+ # Returns the neighborhood of the given vertex or {Arc arc}.
357
+ #
358
+ # This is equivalent to {GraphBuilder#adjacent adjacent}, but the type is based on the
359
+ # type of the specified object.
360
+ #
361
+ # @param [vertex, Arc] x
362
+ # @param [Symbol] direction (:all) can be either `:all`, `:in` or `:out`
363
+ def neighborhood(x, direction = :all)
364
+ adjacent(x, :direction => direction, :type => ((x.is_a? Plexus::Arc) ? :edges : :vertices ))
365
+ end
366
+
367
+ # Union of all neighborhoods of vertices (or edges) in the Enumerable x minus the contents of x.
368
+ #
369
+ # Definition taken from: Jorgen Bang-Jensen, Gregory Gutin, *Digraphs: Theory, Algorithms and Applications*, pg. 4
370
+ #
371
+ # @param [vertex] x
372
+ # @param [Symbol] direction can be either `:all`, `:in` or `:out`
373
+ def set_neighborhood(x, direction = :all)
374
+ x.inject(Set.new) { |a,v| a.merge(neighborhood(v, direction))}.reject { |v2| x.include?(v2) }
375
+ end
376
+
377
+ # Union of all {GraphBuilder#set_neighborhood set_neighborhoods} reachable
378
+ # among the specified edges.
379
+ #
380
+ # Definition taken from Jorgen Bang-Jensen, Gregory Gutin, *Digraphs:
381
+ # Theory, Algorithms and Applications*, pg. 46
382
+ #
383
+ # @param [vertex] w
384
+ # @param [Edges] p
385
+ # @param [Symbol] direction can be `:all`, `:in`, or `:out`
386
+ def closed_pth_neighborhood(w, p, direction = :all)
387
+ if p <= 0
388
+ w
389
+ elsif p == 1
390
+ (w + set_neighborhood(w, direction)).uniq
391
+ else
392
+ n = set_neighborhood(w, direction)
393
+ (w + n + closed_pth_neighborhood(n, p-1, direction)).uniq
394
+ end
395
+ end
396
+
397
+ # Returns the neighboorhoods reachable in a certain amount of steps from
398
+ # every vertex (or edge) in the specified Enumerable.
399
+ #
400
+ # Definition taken from Jorgen Bang-Jensen, Gregory Gutin, _Digraphs:
401
+ # Theory, Algorithms and Applications_, pg. 46
402
+ #
403
+ # @param [Enumerable] x
404
+ # @param [Integer] p number of steps to perform
405
+ # @param [Symbol] direction can be `:all`, `:in`, or `:out`
406
+ def open_pth_neighborhood(x, p, direction = :all)
407
+ if p <= 0
408
+ x
409
+ elsif p == 1
410
+ set_neighborhood(x,direction)
411
+ else
412
+ set_neighborhood(open_pth_neighborhood(x, p-1, direction), direction) -
413
+ closed_pth_neighborhood(x, p-1, direction)
414
+ end
415
+ end
416
+
417
+ # Returns the number of out-edges (for directed graphs) or the number of
418
+ # incident edges (for undirected graphs) of the specified vertex.
419
+ #
420
+ # @param [vertex] v
421
+ # @return [Integer] number of matching edges
422
+ def out_degree(v)
423
+ adjacent(v, :direction => :out).size
424
+ end
425
+
426
+ # Returns the number of in-edges (for directed graphs) or the number of
427
+ # incident edges (for undirected graphs) of the specified vertex
428
+ #
429
+ # @param [vertex] v
430
+ # @return [Integer] number of matching edges
431
+ def in_degree(v)
432
+ adjacent(v, :direction => :in).size
433
+ end
434
+
435
+ # Returns the sum of the number in and out edges for the specified vertex.
436
+ #
437
+ # @param [vertex] v
438
+ # @return [Integer] degree
439
+ def degree(v)
440
+ in_degree(v) + out_degree(v)
441
+ end
442
+
443
+ # Minimum in-degree of the graph.
444
+ #
445
+ # @return [Integer, nil] returns `nil` if the graph is empty
446
+ def min_in_degree
447
+ return nil if to_a.empty?
448
+ to_a.map { |v| in_degree(v) }.min
449
+ end
450
+
451
+ # Minimum out-degree of the graph.
452
+ #
453
+ # @return [Integer, nil] returns `nil` if the graph is empty
454
+ def min_out_degree
455
+ return nil if to_a.empty?
456
+ to_a.map {|v| out_degree(v)}.min
457
+ end
458
+
459
+ # Minimum degree of all vertexes of the graph.
460
+ #
461
+ # @return [Integer] `min` between {GraphBuilder#min_in_degree min_in_degree}
462
+ # and {GraphBuilder#min_out_degree max_out_degree}
463
+ def min_degree
464
+ [min_in_degree, min_out_degree].min
465
+ end
466
+
467
+ # Maximum in-degree of the graph.
468
+ #
469
+ # @return [Integer, nil] returns `nil` if the graph is empty
470
+ def max_in_degree
471
+ return nil if to_a.empty?
472
+ vertices.map { |v| in_degree(v)}.max
473
+ end
474
+
475
+ # Maximum out-degree of the graph.
476
+ #
477
+ # @return [Integer, nil] returns nil if the graph is empty
478
+ def max_out_degree
479
+ return nil if to_a.empty?
480
+ vertices.map { |v| out_degree(v)}.max
481
+ end
482
+
483
+ # Maximum degree of all vertexes of the graph.
484
+ #
485
+ # @return [Integer] `max` between {GraphBuilder#max_in_degree max_in_degree}
486
+ # and {GraphBuilder#max_out_degree max_out_degree}
487
+ def max_degree
488
+ [max_in_degree, max_out_degree].max
489
+ end
490
+
491
+ # Is the graph regular, that is are its min degree and max degree equal?
492
+ #
493
+ # @return [Boolean]
494
+ def regular?
495
+ min_degree == max_degree
496
+ end
497
+
498
+ # Number of vertices.
499
+ #
500
+ # @return [Integer]
501
+ def size
502
+ vertices.size
503
+ end
504
+ alias num_vertices size
505
+ alias number_of_vertices size
506
+
507
+ # Number of vertices.
508
+ #
509
+ # @return [Integer]
510
+ def num_vertices
511
+ vertices.size
512
+ end
513
+ alias number_of_vertices num_vertices
514
+
515
+ # Number of edges.
516
+ #
517
+ # @return [Integer]
518
+ def num_edges
519
+ edges.size
520
+ end
521
+ alias number_of_edges num_edges
522
+
523
+ # Utility method to show a string representation of the edges of the graph.
524
+ #def to_s
525
+ #edges.to_s
526
+ #end
527
+
528
+ # Equality is defined to be same set of edges and directed?
529
+ def eql?(g)
530
+ return false unless g.is_a? Plexus::Graph
531
+
532
+ (directed? == g.directed?) and
533
+ (vertices.sort == g.vertices.sort) and
534
+ (edges.sort == g.edges.sort)
535
+ end
536
+ alias == eql?
537
+
538
+ # Merges another graph into the receiver.
539
+ #
540
+ # @param [Graph] other the graph to merge in
541
+ # @return [Graph] `self`
542
+ def merge(other)
543
+ other.vertices.each { |v| add_vertex!(v) }
544
+ other.edges.each { |e| add_edge!(e) }
545
+ other.edges.each { |e| add_edge!(e.reverse) } if directed? and !other.directed?
546
+ self
547
+ end
548
+
549
+ # A synonym for {GraphBuilder#merge merge}, but doesn't modify the current graph.
550
+ #
551
+ # @param [Graph, Arc] other
552
+ # @return [Graph] a new graph
553
+ def +(other)
554
+ result = self.class.new(self)
555
+ case other
556
+ when Plexus::Graph
557
+ result.merge(other)
558
+ when Plexus::Arc
559
+ result.add_edge!(other)
560
+ else
561
+ result.add_vertex!(other)
562
+ end
563
+ end
564
+
565
+ # Removes all vertices in the specified graph.
566
+ #
567
+ # @param [Graph, Arc] other
568
+ # @return [Graph]
569
+ def -(other)
570
+ case other
571
+ when Plexus::Graph
572
+ induced_subgraph(vertices - other.vertices)
573
+ when Plexus::Arc
574
+ self.class.new(self).remove_edge!(other)
575
+ else
576
+ self.class.new(self).remove_vertex!(other)
577
+ end
578
+ end
579
+
580
+ # A synonym for {AdjacencyGraphBuilder#add_edge! add_edge!}.
581
+ def <<(edge)
582
+ add_edge!(edge)
583
+ end
584
+
585
+ # Computes the complement of the current graph.
586
+ #
587
+ # @return [Graph]
588
+ def complement
589
+ vertices.inject(self.class.new) do |a,v|
590
+ a.add_vertex!(v)
591
+ vertices.each { |v2| a.add_edge!(v, v2) unless edge?(v, v2) }; a
592
+ end
593
+ end
594
+
595
+ # Given an array of vertices, computes the induced subgraph.
596
+ #
597
+ # @param [Array(vertex)] v
598
+ # @return [Graph]
599
+ def induced_subgraph(v)
600
+ edges.inject(self.class.new) do |a,e|
601
+ (v.include?(e.source) and v.include?(e.target)) ? (a << e) : a
602
+ end
603
+ end
604
+
605
+ def inspect
606
+ ## FIXME: broken, it's not updated. The issue's not with inspect, but it's worth mentionning here.
607
+ ## Example:
608
+ ## dg = Digraph[1,2, 2,3, 2,4, 4,5, 6,4, 1,6]
609
+ ## dg.add_vertices! 1, 5, "yosh"
610
+ ## # => Plexus::Digraph[Plexus::Arc[1,2,nil], Plexus::Arc[1,6,nil], Plexus::Arc[2,3,nil], Plexus::Arc[2,4,nil], Plexus::Arc[4,5,nil], Plexus::Arc[6,4,nil]]
611
+ ## dg.vertex?("yosh")
612
+ ## # => true
613
+ ## dg
614
+ ## # =>Plexus::Digraph[Plexus::Arc[1,2,nil], Plexus::Arc[1,6,nil], Plexus::Arc[2,3,nil], Plexus::Arc[2,4,nil], Plexus::Arc[4,5,nil], Plexus::Arc[6,4,nil]]
615
+ ## the new vertex doesn't show up.
616
+ ## Actually this version of inspect is far too verbose IMO :)
617
+ l = vertices.select { |v| self[v]}.map { |u| "vertex_label_set(#{u.inspect}, #{self[u].inspect})"}.join('.')
618
+ self.class.to_s + '[' + edges.map {|e| e.inspect}.join(', ') + ']' + (l && l != '' ? '.'+l : '')
619
+ end
620
+
621
+ private
622
+
623
+ # ?
624
+ def edge_convert(*args)
625
+ args[0].is_a?(Plexus::Arc) ? args[0] : edge_class[*args]
626
+ end
627
+ end
628
+ end