rgraphum 0.0.1.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. data/.gitignore +26 -0
  2. data/GLOSSARIES.md +108 -0
  3. data/GREMLIN.md +1398 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +136 -0
  7. data/Rakefile +16 -0
  8. data/bin/.irbrc +41 -0
  9. data/bin/rgraphum_console +61 -0
  10. data/bin/rgraphum_runner +57 -0
  11. data/examples/ba_model/make.rb +19 -0
  12. data/examples/ba_model/make_dummy_twitter_rt_data.rb +0 -0
  13. data/examples/basic/check_modularity.rb +27 -0
  14. data/examples/basic/make_graph.rb +12 -0
  15. data/examples/parser/dot.rb +28 -0
  16. data/examples/sis_model/lifegame.rb +161 -0
  17. data/graph_struct.jpg +0 -0
  18. data/lib/rgraphum/analyzer/linear_regression.rb +31 -0
  19. data/lib/rgraphum/analyzer/meme_tracker.rb +296 -0
  20. data/lib/rgraphum/analyzer/twitter/rt_at_mark.rb +45 -0
  21. data/lib/rgraphum/analyzer.rb +8 -0
  22. data/lib/rgraphum/cluster.rb +67 -0
  23. data/lib/rgraphum/communities.rb +65 -0
  24. data/lib/rgraphum/community.rb +86 -0
  25. data/lib/rgraphum/cosine_similarity_matrix.rb +40 -0
  26. data/lib/rgraphum/edge.rb +194 -0
  27. data/lib/rgraphum/edges.rb +161 -0
  28. data/lib/rgraphum/ext/cosine_similarity_matrix.rb +79 -0
  29. data/lib/rgraphum/ext/linear_regression.rb +22 -0
  30. data/lib/rgraphum/ext/tf_idf.rb +52 -0
  31. data/lib/rgraphum/graph/gremlin.rb +193 -0
  32. data/lib/rgraphum/graph/math/clustering_coefficient.rb +53 -0
  33. data/lib/rgraphum/graph/math/community_detection.rb +141 -0
  34. data/lib/rgraphum/graph/math/degree_distribution.rb +50 -0
  35. data/lib/rgraphum/graph/math/dijkstra.rb +331 -0
  36. data/lib/rgraphum/graph/math.rb +45 -0
  37. data/lib/rgraphum/graph.rb +267 -0
  38. data/lib/rgraphum/importer.rb +97 -0
  39. data/lib/rgraphum/marshal.rb +26 -0
  40. data/lib/rgraphum/motifs.rb +8 -0
  41. data/lib/rgraphum/parsers/flare.rb +42 -0
  42. data/lib/rgraphum/parsers/gephi.rb +193 -0
  43. data/lib/rgraphum/parsers/graphviz.rb +78 -0
  44. data/lib/rgraphum/parsers/miserables.rb +54 -0
  45. data/lib/rgraphum/parsers.rb +32 -0
  46. data/lib/rgraphum/path.rb +37 -0
  47. data/lib/rgraphum/query.rb +130 -0
  48. data/lib/rgraphum/rgraphum_array.rb +159 -0
  49. data/lib/rgraphum/rgraphum_array_dividers.rb +43 -0
  50. data/lib/rgraphum/rgraphum_random.rb +5 -0
  51. data/lib/rgraphum/simulator/ba_model.rb +140 -0
  52. data/lib/rgraphum/simulator/sir_model.rb +178 -0
  53. data/lib/rgraphum/simulator/sis_model.rb +158 -0
  54. data/lib/rgraphum/simulator.rb +29 -0
  55. data/lib/rgraphum/statistic/power_law.rb +9 -0
  56. data/lib/rgraphum/t.rb +12 -0
  57. data/lib/rgraphum/tf_idf.rb +27 -0
  58. data/lib/rgraphum/version.rb +3 -0
  59. data/lib/rgraphum/vertex.rb +354 -0
  60. data/lib/rgraphum/vertices.rb +97 -0
  61. data/lib/rgraphum.rb +38 -0
  62. data/performance/add-vertices-edges.rb +20 -0
  63. data/performance/add-vertices.rb +12 -0
  64. data/performance/build-graph.rb +19 -0
  65. data/performance/delete-graph.rb +24 -0
  66. data/performance/delete-vertices.rb +25 -0
  67. data/performance/refer-graph.rb +23 -0
  68. data/rgraphum.gemspec +30 -0
  69. data/test/lib/rgraphum/analyzer/linear_regression_test.rb +20 -0
  70. data/test/lib/rgraphum/analyzer/meme_tracker_test.rb +383 -0
  71. data/test/lib/rgraphum/analyzer/twitter/rt_at_mark_test.rb +120 -0
  72. data/test/lib/rgraphum/array_test.rb +95 -0
  73. data/test/lib/rgraphum/bubble_test.rb +7 -0
  74. data/test/lib/rgraphum/communities_test.rb +53 -0
  75. data/test/lib/rgraphum/cosine_similarity_test.rb +18 -0
  76. data/test/lib/rgraphum/edge_test.rb +89 -0
  77. data/test/lib/rgraphum/edges_test.rb +178 -0
  78. data/test/lib/rgraphum/graph_builder_test.rb +64 -0
  79. data/test/lib/rgraphum/graph_dup_test.rb +199 -0
  80. data/test/lib/rgraphum/graph_plus_test.rb +80 -0
  81. data/test/lib/rgraphum/graph_test.rb +512 -0
  82. data/test/lib/rgraphum/gremlin_test.rb +145 -0
  83. data/test/lib/rgraphum/importers/idg_json_edges.json +20 -0
  84. data/test/lib/rgraphum/importers/idg_json_test.rb +207 -0
  85. data/test/lib/rgraphum/importers/idg_json_vertices.json +46 -0
  86. data/test/lib/rgraphum/math/average_distance_matrix_test.rb +142 -0
  87. data/test/lib/rgraphum/math/clustering_coefficient_test.rb +219 -0
  88. data/test/lib/rgraphum/math/community_test.rb +78 -0
  89. data/test/lib/rgraphum/math/degree_distribution_test.rb +40 -0
  90. data/test/lib/rgraphum/math/dijkstra_test.rb +146 -0
  91. data/test/lib/rgraphum/math/modularity_test.rb +154 -0
  92. data/test/lib/rgraphum/math/quick_average_distance_matrix_test.rb +84 -0
  93. data/test/lib/rgraphum/path_test.rb +44 -0
  94. data/test/lib/rgraphum/query/enumerable_test.rb +42 -0
  95. data/test/lib/rgraphum/query/where_operators_test.rb +75 -0
  96. data/test/lib/rgraphum/query/where_test.rb +59 -0
  97. data/test/lib/rgraphum/simulator/ba_model_test.rb +75 -0
  98. data/test/lib/rgraphum/simulator/sir_model_test.rb +513 -0
  99. data/test/lib/rgraphum/simulator/sis_model_test.rb +478 -0
  100. data/test/lib/rgraphum/simulator_test.rb +22 -0
  101. data/test/lib/rgraphum/tf_idf_test.rb +30 -0
  102. data/test/lib/rgraphum/vertex_test.rb +50 -0
  103. data/test/lib/rgraphum/vertices_test.rb +180 -0
  104. data/test/test_helper.rb +98 -0
  105. data/tmp/.gitkeep +0 -0
  106. metadata +254 -0
@@ -0,0 +1,267 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # FIXME some 'edge.source's are edge.source.id
4
+ # FIXME some 'edge.target's are edge.target.id
5
+
6
+ class Rgraphum::Graph
7
+ RGRAPHUM = Rgraphum
8
+
9
+ include Rgraphum::Graph::Math
10
+ include Rgraphum::Graph::Gremlin
11
+ include Rgraphum::Marshal
12
+ include Rgraphum::Simulator
13
+ include Rgraphum::Importer
14
+ include Rgraphum::Parsers
15
+
16
+ attr_accessor :aspect
17
+ attr_accessor :label
18
+
19
+ class << self
20
+
21
+ def build_from_adjacency_matrix( matrix, vertex_labels = [], options={} )
22
+ options = { loop: false, limit: 0.65 }.merge(options)
23
+
24
+ graph = new
25
+ if vertex_labels.size == matrix.size
26
+ vertex_labels.each do |label|
27
+ v = graph.vertices.build(label:label)
28
+ end
29
+ else
30
+ matrix.size.times do
31
+ graph.vertices.build
32
+ end
33
+ end
34
+
35
+ matrix.each_with_index do | row,row_index|
36
+ row.each_with_index do |weight,col_index|
37
+ next if col_index == row_index and !options[:loop]
38
+
39
+ if weight and weight >= options[:limit]
40
+ graph.edges.build( {source:graph.vertices[row_index], target:graph.vertices[col_index],weight:weight} )
41
+ end
42
+ end
43
+ end
44
+ graph
45
+ end
46
+
47
+ end
48
+
49
+ # @param [Hash] options
50
+ # @option options [Rgraphum::Vertices] :vertices
51
+ # @option options [Rgraphum::Edges] :edges
52
+ #
53
+ def initialize(options={})
54
+ @vertices = Rgraphum::Vertices.new
55
+ if options[:vertices]
56
+ self.vertices = options[:vertices]
57
+ else
58
+ @vertices.graph = self
59
+ end
60
+
61
+ @edges = Rgraphum::Edges.new
62
+ if options[:edges]
63
+ @edges.graph = self
64
+ self.edges = options[:edges]
65
+ else
66
+ @edges.graph = self
67
+ end
68
+
69
+ @aspect = "real"
70
+ end
71
+
72
+
73
+ # 再計算が必要な物にnilをセットする。
74
+ def clear_cache
75
+ @m = nil
76
+ @m_with_weight = nil
77
+ end
78
+
79
+ # m is size of edges
80
+ def m
81
+ @m ||= @edges.size
82
+ end
83
+
84
+ # degree
85
+ def degree
86
+ self.m * 2
87
+ end
88
+
89
+ # m with weight
90
+ def m_with_weight
91
+ @m_with_weight ||= @edges.inject(0.0) { |sum, edge| sum + edge.weight }
92
+ end
93
+
94
+ # average degree
95
+ def average_degree
96
+ @average_degree ||= (2 * @m / @vertices.size)
97
+ end
98
+
99
+ def average_path_length
100
+ raise NotImplementedError
101
+ end
102
+
103
+ # basic method
104
+ def vertices
105
+ @vertices
106
+ end
107
+
108
+ def vertices=(vertex_array)
109
+ @vertices = @vertices.substitute(vertex_array) do |vertex|
110
+ Rgraphum::Vertex(vertex)
111
+ end
112
+ end
113
+
114
+ def edges
115
+ @edges
116
+ end
117
+
118
+ def edges=(edge_array)
119
+ @edges = @edges.substitute(edge_array) do |edge|
120
+ Rgraphum::Edge(edge)
121
+ end
122
+ end
123
+
124
+ # to_real mean edge has vertex pointer
125
+ def real_aspect!
126
+ return self if @aspect == "real"
127
+
128
+ edges.each do |edge|
129
+ edge.source = @vertices.find_by_id(edge.source) # FIXME
130
+ edge.target = @vertices.find_by_id(edge.target) # FIXME
131
+ end
132
+
133
+ @aspect = "real"
134
+ self
135
+ end
136
+
137
+ # to_id mean edge has vertex id
138
+ def id_aspect!
139
+ return self if @aspect == "id"
140
+
141
+ edges.each do |edge|
142
+ edge.source = edge.source.id
143
+ edge.target = edge.target.id
144
+ end
145
+
146
+ @aspect = "id"
147
+ self
148
+ end
149
+
150
+ # pickup start vertices
151
+ # it mean pick vertices having no in degree
152
+ def start_root_vertices
153
+ vertices.select do |vertex|
154
+ vertex.inE.empty?
155
+ end
156
+ end
157
+
158
+ # pickup end vertices
159
+ # it mean pick vertices having no out degree
160
+ def end_root_vertices
161
+ vertices.select do |vertex|
162
+ vertex.outE.empty?
163
+ end
164
+ end
165
+
166
+ def dup
167
+ new_graph = Rgraphum::Graph.new
168
+
169
+ @vertices.each do |vertex|
170
+ new_vertex = vertex.dup
171
+ new_vertex.edges = Rgraphum::Edges.new
172
+ new_graph.vertices << new_vertex
173
+ end
174
+
175
+ @edges.each do |edge|
176
+ new_graph.edges << edge.dup
177
+ end
178
+
179
+ new_graph
180
+ end
181
+
182
+ def +(other)
183
+ new_graph = Rgraphum::Graph.new
184
+ start_vertex_id = @vertices.id.max
185
+ start_edge_id = @edges.id.max
186
+
187
+ other_dup = other.dup
188
+ other_dup.vertices.each do | vertex |
189
+ vertex.id = vertex.id + start_vertex_id
190
+ end
191
+ other_dup.edges.each do |edge|
192
+ edge.id = edge.id + start_edge_id
193
+ end
194
+
195
+ new_graph.vertices = self.dup.vertices + other_dup.vertices
196
+ new_graph.edges = self.dup.edges + other_dup.edges
197
+
198
+ new_graph
199
+ end
200
+
201
+ def compact_with_label(options={})
202
+ compact_with(:label, self, options)
203
+ end
204
+
205
+ def marge_with_label(target)
206
+ new_graph = self + target
207
+ new_graph.compact_with_label
208
+ end
209
+
210
+ def divide_by_time(interval=20)
211
+ @vertices.divide_by_time(interval)
212
+ @edges.divide_by_time(interval)
213
+
214
+ new_edges = Rgraphum::Edges.new
215
+ new_edges.graph = self
216
+ @edges.each do |edge|
217
+ conditions = { source: edge.source, target: edge.target, start: edge.start }
218
+ same_edge = new_edges.where(conditions).first
219
+ if same_edge
220
+ same_edge.weight += edge.weight
221
+ else
222
+ new_edges << edge
223
+ end
224
+ end
225
+ self.edges = new_edges
226
+ end
227
+
228
+ def compact_with(method_name, graph=self, options ={})
229
+ new_vertices = Rgraphum::Vertices.new
230
+ new_vertices.graph = graph
231
+ graph.vertices.each do |vertex|
232
+ same_vertex = new_vertices.find{ |v| v.send(method_name) == vertex.send(method_name) }
233
+ unless same_vertex
234
+ new_vertex = vertex.dup
235
+ new_vertex.edges = Rgraphum::Edges.new
236
+ new_vertices << new_vertex
237
+ end
238
+ end
239
+
240
+ new_edges = Rgraphum::Edges.new
241
+ graph.edges.each do |edge|
242
+ source_label = edge.source.send(method_name)
243
+ target_label = edge.target.send(method_name)
244
+ edge.source = new_vertices.find{ |vertex| vertex.send(method_name) == source_label }
245
+ edge.target = new_vertices.find{ |vertex| vertex.send(method_name) == target_label }
246
+
247
+ same_edge = new_edges.find{ |e| e.source.equal?(edge.source) and e.source.equal?(edge.source) }
248
+ if same_edge
249
+ same_edge.weight += edge.weight
250
+ else
251
+ new_edges << edge
252
+ end
253
+ end
254
+
255
+ graph.vertices = new_vertices
256
+ graph.edges = new_edges
257
+ graph
258
+ end
259
+
260
+ def ==(other)
261
+ return false unless aspect == other.aspect
262
+ return false unless label == other.label
263
+ return false unless vertices == other.vertices
264
+ return false unless edges == other.edges
265
+ true
266
+ end
267
+ end
@@ -0,0 +1,97 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Rgraphum
4
+ module Importer
5
+ def self.included(base)
6
+ base.extend ClassMethods
7
+ end
8
+
9
+ module ClassMethods
10
+ # Load graph from files
11
+ #
12
+ #
13
+ def load(options={})
14
+ parse_options = options.dup
15
+ parse_options[:vertices] &&= open(parse_options[:vertices])
16
+ parse_options[:edges] &&= open(parse_options[:edges])
17
+ parse_options[:path] &&= open(parse_options[:path])
18
+ parse_options[:options] = options[:options]
19
+ parse parse_options
20
+ end
21
+
22
+ # Parse str and load graph
23
+ #
24
+ #
25
+ def parse(options={})
26
+ graph = Rgraphum::Graph.new
27
+
28
+ case options[:format]
29
+ when :idg_json
30
+ build_graph_from_idg_json graph, options[:vertices], options[:edges], (options[:options] || {})
31
+ when :dump
32
+ graph = load_from(options[:path])
33
+ else
34
+ raise ArgumentError, "Rgraphum::Importer::ClassMethods.parse: Unknown format: '#{options[:format]}'"
35
+ end
36
+
37
+ graph
38
+ end
39
+
40
+ private
41
+
42
+ def build_graph_from_idg_json(graph, vertices_json_str_or_stream, edges_json_str_or_stream, options={})
43
+ vertex_id_hash = {}
44
+ community_hash = {}
45
+ verbose = options[:verbose]
46
+
47
+ puts "Loading vertices ... #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}" if verbose
48
+ $stdout.flush if verbose
49
+ if vertices_json_str_or_stream
50
+ puts "JSON.load start ... #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}" if verbose
51
+ json = JSON.load(vertices_json_str_or_stream)
52
+ puts "JSON.load end ... #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}" if verbose
53
+ json["result"].each_with_index do |vertex_hash, i|
54
+ vertex_id_hash[vertex_hash["rid"]] ||= vertex_id_hash.size
55
+ community_hash[vertex_hash["c_id"]] ||= graph.communities.build
56
+ params = {
57
+ id: vertex_id_hash[vertex_hash["rid"]],
58
+ label: vertex_hash["screen_name"],
59
+ community_id: community_hash[vertex_hash["c_id"]].id,
60
+ }
61
+ graph.vertices.build(params)
62
+ if verbose
63
+ puts ".................... #{Time.now.strftime('%Y-%m-%d %H:%M:%S')} #{i}" if (i % 10000) == 0
64
+ end
65
+ end
66
+ end
67
+
68
+ puts "Loading edges ...... #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}" if verbose
69
+ $stdout.flush if verbose
70
+ start_edge_number = (options[:start_edge_number].to_i rescue 1) || 1
71
+ end_edge_number = (options[:end_edge_number].to_i rescue nil)
72
+ end_edge_number = nil if end_edge_number == 0
73
+ if edges_json_str_or_stream
74
+ json = JSON.load(edges_json_str_or_stream)
75
+ json["result"].each_with_index do |edge_hash, i|
76
+ next if i < start_edge_number
77
+ break if end_edge_number && end_edge_number < i
78
+ params = {
79
+ weight: edge_hash["weight"].to_f,
80
+ source: vertex_id_hash[edge_hash["in"]],
81
+ target: vertex_id_hash[edge_hash["out"]],
82
+ }
83
+ if graph.class::RGRAPHUM::Edge.has_field?(:created_at)
84
+ params[:created_at] = Time.at(edge_hash["created_at"].to_i)
85
+ end
86
+ graph.edges.build(params)
87
+ end
88
+ end
89
+
90
+ puts "Loaded! #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}" if verbose
91
+ $stdout.flush if verbose
92
+
93
+ graph
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,26 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Rgraphum::Marshal
4
+ def self.included(base)
5
+ base.__send__ :include, InstanceMethods
6
+ base.__send__ :extend, ClassMethods
7
+ end
8
+
9
+ module InstanceMethods
10
+ def dump_to(path)
11
+ data = Marshal.dump(self)
12
+ File.write(path, data)
13
+ end
14
+ end
15
+
16
+ module ClassMethods
17
+ def load_from(path)
18
+ data = File.read(path)
19
+ graph = Marshal.load(data)
20
+ unless graph.is_a?(self)
21
+ raise TypeError, "No #{self} instance in: #{path} (#{graph.class})"
22
+ end
23
+ graph
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,8 @@
1
+
2
+
3
+ class Rgraphum::Motifs
4
+
5
+ # def
6
+ # end
7
+
8
+ end
@@ -0,0 +1,42 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'json'
4
+
5
+ module Rgraphum::Parsers
6
+ class FlareParser
7
+ class << self
8
+ #
9
+ def builder(graph)
10
+ stream = "var flare = { \"#{graph.label}\" : "
11
+
12
+ vertices_hash = {}
13
+ graph.vertices.each do |vertex|
14
+ vertices_hash[vertex.label] = vertex.amount
15
+ end
16
+ stream += vertices_hash.to_json
17
+ stream += "};"
18
+ stream
19
+ end
20
+ end
21
+
22
+ # Options:
23
+ #
24
+ def initialize(options={})
25
+ default_options = {
26
+ }
27
+ @options = default_options.merge(options)
28
+ builder(@options[:graph]) if @options.key?(:graph)
29
+ end
30
+
31
+ def builder(graph)
32
+ @stream = self.class.builder(graph)
33
+ end
34
+
35
+ def to_s
36
+ unless @stream
37
+ raise ArgumentError, "Didn't build stream with builder(graph)"
38
+ end
39
+ @stream
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,193 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'builder/xmlmarkup'
4
+
5
+ module Rgraphum::Parsers
6
+ class GephiParser
7
+ class << self
8
+ def builder(graph)
9
+ new.builder(graph)
10
+ end
11
+ end
12
+
13
+ # Options:
14
+ #
15
+ def initialize(options={})
16
+ default_options = {
17
+ }
18
+ @options = default_options.merge(options)
19
+ builder(@options[:graph]) if @options.key?(:graph)
20
+ end
21
+
22
+ def builder(graph)
23
+ xml = ""
24
+ @xmlobj = Builder::XmlMarkup.new(target: xml , indent: 2)
25
+ set_header
26
+
27
+ options = {
28
+ "xmlns" => "http://www.gexf.net/1.2draft",
29
+ "xmlns:viz" => "http://www.gexf.net/1.1draft/viz",
30
+ "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
31
+ "xsi:schemaLocation" => "http://www.gexf.net/1.2draft http://www.gexf.net/1.2draft/gexf.xsd",
32
+ "version" => "1.2",
33
+ }
34
+ @xmlobj.gexf(options) do
35
+ @xmlobj.meta(lastmodifieddate: Time.now.strftime("%Y-%m-%d")) do
36
+ @xmlobj.creator("Gephi 0.8")
37
+ end
38
+ opts = {
39
+ defaultedgetype: "directed",
40
+ mode: "dynamic",
41
+ idtype: "string",
42
+ timeformat: "dateTime",
43
+ }
44
+ @xmlobj.graph(opts) do
45
+ vertices_to_xml(graph.vertices)
46
+ edges_to_xml(graph.edges)
47
+ end
48
+ end
49
+
50
+ @xml = xml
51
+ @xml
52
+ end
53
+
54
+ def to_s
55
+ unless @xml
56
+ raise ArgumentError, "Didn't build xml with builder(graph)"
57
+ end
58
+ @xml
59
+ end
60
+
61
+ private
62
+
63
+ def vertices_to_xml(vertices)
64
+ # Vertexの登録
65
+ @xmlobj.nodes() do
66
+ vertices.each do |vertex|
67
+ node_to_xml(vertex)
68
+ end
69
+ end
70
+ end
71
+ alias :nodes_to_xml :vertices_to_xml
72
+
73
+ def node_to_xml(vertex)
74
+ new_vertex = vertex.dup
75
+ new_vertex.start = time_format(vertex.start) if vertex.start
76
+ new_vertex.end = time_format(vertex.end) if vertex.end
77
+
78
+ @xmlobj.node(id: vertex.id, label: vertex.label) do
79
+ # if vertex.attvalues and vertex.attvalues !=[]
80
+ # xmlobj.spells(){
81
+ # vertex.attvalues.each do |attvalue|
82
+ # opts = {
83
+ # start: time_format(attvalue.start),
84
+ # endopen: time_format(attvalue.end),
85
+ # }
86
+ # @xmlobj.spell(opts)
87
+ # end
88
+ # }
89
+ # end
90
+ end
91
+ end
92
+ alias :vertex_to_xml :node_to_xml
93
+
94
+ def edges_to_xml(edges)
95
+ # @xmlobj.attributes(class: "edge", mode: "dynamic") do
96
+ # @xmlobj.attribute(id: "weight", title: "Weight", type: "float")
97
+ # end
98
+ @xmlobj.edges() do
99
+ edges.each do |edge|
100
+ edge_to_xml(edge.dup)
101
+ end
102
+ end
103
+ end
104
+
105
+ def edge_to_xml(edge)
106
+ edge_opts = {
107
+ id: edge.id,
108
+ source: edge.source.id,
109
+ target: edge.target.id,
110
+ label: edge.label,
111
+ weight: (edge.weight || 0.0),
112
+ }
113
+ @xmlobj.edge(edge_opts) do
114
+ if edge.attvalues and !edge.attvalues.empty?
115
+ mlobj.spells() do
116
+ edge.attvalues.each do |attvalue|
117
+ opts = {
118
+ start: time_format(attvalue.start),
119
+ endopen: time_format(attvalue.end),
120
+ }
121
+ xmlobj.spell(opts)
122
+ end
123
+ end
124
+
125
+ xmlobj.attvalues() do
126
+ edge.attvalues.each do |attvalue|
127
+ attrs = {
128
+ :for => "weight",
129
+ :value => attvalue.weight.to_f,
130
+ :start => time_format(attvalue.start),
131
+ :end => time_format(attvalue.end),
132
+ }
133
+ xmlobj.attvalue(attrs)
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ def make_spells_compact_with(field_name, graph, options={})
141
+ graph.divide_by_time(60 * 8)
142
+
143
+ new_vertices = Rgraphum::Vertices.new
144
+ new_vertices.graph = graph
145
+
146
+ graph.vertices.each do |vertex|
147
+ same_vertex = new_vertices.find { |v|
148
+ v.send(field_name) == vertex.send(field_name)
149
+ }
150
+ unless same_vertex
151
+ new_vertex = vertex.dup
152
+ new_vertex.edges = []
153
+ new_vertices << new_vertex
154
+ end
155
+ end
156
+
157
+ new_edges = Rgraphum::Edges.new
158
+
159
+ graph.edges.each do |edge|
160
+ source_label = edge.source.send(field_name)
161
+ target_label = edge.target.send(field_name)
162
+
163
+ edge.source = new_vertices.find { |vertex| vertex.send(field_name) == source_label }
164
+ edge.target = new_vertices.find { |vertex| vertex.send(field_name) == target_label }
165
+
166
+ new_edge = new_edges.find { |e|
167
+ e.source == edge.source and e.source == edge.source
168
+ }
169
+ if new_edge
170
+ new_edge.weight += edge.weight
171
+ else
172
+ new_edges << edge
173
+ end
174
+ end
175
+
176
+ graph.vertices = new_vertices
177
+ graph.edges = new_edges
178
+ graph
179
+ end
180
+
181
+ def set_header
182
+ @xmlobj.instruct! :xml, encoding: 'UTF-8'
183
+ end
184
+
185
+ def add_spells(vertex_a, vertex_b)
186
+ raise NotImplementedError
187
+ end
188
+
189
+ def time_format(t)
190
+ t.strftime("%Y-%m-%dT%H:%M:%S")
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,78 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require "open3"
4
+
5
+ module Rgraphum::Parsers
6
+ # Graphviz is Graph drowing application.
7
+ # Using dot formatted file, it drow picture.
8
+ # This class make dot formatted file from graph and write file
9
+ # if you want to know details, please see graphviz manual
10
+ class GraphvizParser
11
+ class << self
12
+ # build graphviz file(.dot) content as string
13
+ # @param [Rgraphum::Graph] graph target graph .
14
+ # @param [String] layout type of layout, "dot", "neato", "fdp", "sfdp", "twopi", "circo". please see graphviz manual.
15
+ def builder(graph, layout="dot")
16
+ dot = "digraph #{graph.label || "sample"} { \n"
17
+ dot += " graph [ layout = \"#{layout}\", overlap = false ] \n\n"
18
+
19
+ graph.edges.each do |edge|
20
+ s = edge[:source][:label] || edge[:source][:id]
21
+ t = edge[:target][:label] || edge[:target][:id]
22
+ label = "[label = \"#{edge[:label]}\"]" if edge[:label]
23
+ dot += " \"#{s}\" -> \"#{t}\" #{label}; \n"
24
+ end
25
+
26
+ dot += "}"
27
+
28
+ dot
29
+ end
30
+
31
+ # save graph as .dot and image files
32
+ #
33
+ # @param [Rgraphum::Graph] graph
34
+ # @param [String] path_prefix Will create path_prefix.{dot,*} files
35
+ # @param [String] type picture type, "jpeg", "jpg", "png" and etc..
36
+ #
37
+ def export(graph, path_prefix, type)
38
+ dot = builder(graph)
39
+ open("#{path_prefix}.dot", "w") do |file|
40
+ file.write(dot)
41
+ end
42
+ cmd = "dot -T#{type} #{path_prefix}.dot -o #{path_prefix}.#{type}"
43
+ o, e, s = Open3.capture3(cmd)
44
+ end
45
+ end
46
+
47
+ def initialize(options={})
48
+ default_options = {
49
+ layout: "dot",
50
+ }
51
+ @options = default_options.merge(options)
52
+ builder(@options[:graph]) if @options.key?(:graph)
53
+ end
54
+
55
+ # dot output builder
56
+ # it call only class.method
57
+ # @see GraphvizParser::builder
58
+ def builder(graph)
59
+ @dot = self.class.builder(graph, @options[:layout])
60
+ end
61
+
62
+ # save dot file and image
63
+ # @param [String] path_prefix Will create path_prefix.{dot,*} files
64
+ # @param [String] type picture type, "jpeg", "jpg", "png" and etc..
65
+ def export(path_prefix, type=nil)
66
+ graph = @options[:graph]
67
+ builder(graph) unless @dot
68
+ self.class.export(graph, path_prefix, type)
69
+ end
70
+
71
+ def to_s
72
+ unless @dot
73
+ raise ArgumentError, "Didn't build dot with builder(graph)"
74
+ end
75
+ @dot
76
+ end
77
+ end
78
+ end