rgraphum 0.0.1.alpha

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