rgraphum 0.0.1.alpha
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +26 -0
- data/GLOSSARIES.md +108 -0
- data/GREMLIN.md +1398 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +136 -0
- data/Rakefile +16 -0
- data/bin/.irbrc +41 -0
- data/bin/rgraphum_console +61 -0
- data/bin/rgraphum_runner +57 -0
- data/examples/ba_model/make.rb +19 -0
- data/examples/ba_model/make_dummy_twitter_rt_data.rb +0 -0
- data/examples/basic/check_modularity.rb +27 -0
- data/examples/basic/make_graph.rb +12 -0
- data/examples/parser/dot.rb +28 -0
- data/examples/sis_model/lifegame.rb +161 -0
- data/graph_struct.jpg +0 -0
- data/lib/rgraphum/analyzer/linear_regression.rb +31 -0
- data/lib/rgraphum/analyzer/meme_tracker.rb +296 -0
- data/lib/rgraphum/analyzer/twitter/rt_at_mark.rb +45 -0
- data/lib/rgraphum/analyzer.rb +8 -0
- data/lib/rgraphum/cluster.rb +67 -0
- data/lib/rgraphum/communities.rb +65 -0
- data/lib/rgraphum/community.rb +86 -0
- data/lib/rgraphum/cosine_similarity_matrix.rb +40 -0
- data/lib/rgraphum/edge.rb +194 -0
- data/lib/rgraphum/edges.rb +161 -0
- data/lib/rgraphum/ext/cosine_similarity_matrix.rb +79 -0
- data/lib/rgraphum/ext/linear_regression.rb +22 -0
- data/lib/rgraphum/ext/tf_idf.rb +52 -0
- data/lib/rgraphum/graph/gremlin.rb +193 -0
- data/lib/rgraphum/graph/math/clustering_coefficient.rb +53 -0
- data/lib/rgraphum/graph/math/community_detection.rb +141 -0
- data/lib/rgraphum/graph/math/degree_distribution.rb +50 -0
- data/lib/rgraphum/graph/math/dijkstra.rb +331 -0
- data/lib/rgraphum/graph/math.rb +45 -0
- data/lib/rgraphum/graph.rb +267 -0
- data/lib/rgraphum/importer.rb +97 -0
- data/lib/rgraphum/marshal.rb +26 -0
- data/lib/rgraphum/motifs.rb +8 -0
- data/lib/rgraphum/parsers/flare.rb +42 -0
- data/lib/rgraphum/parsers/gephi.rb +193 -0
- data/lib/rgraphum/parsers/graphviz.rb +78 -0
- data/lib/rgraphum/parsers/miserables.rb +54 -0
- data/lib/rgraphum/parsers.rb +32 -0
- data/lib/rgraphum/path.rb +37 -0
- data/lib/rgraphum/query.rb +130 -0
- data/lib/rgraphum/rgraphum_array.rb +159 -0
- data/lib/rgraphum/rgraphum_array_dividers.rb +43 -0
- data/lib/rgraphum/rgraphum_random.rb +5 -0
- data/lib/rgraphum/simulator/ba_model.rb +140 -0
- data/lib/rgraphum/simulator/sir_model.rb +178 -0
- data/lib/rgraphum/simulator/sis_model.rb +158 -0
- data/lib/rgraphum/simulator.rb +29 -0
- data/lib/rgraphum/statistic/power_law.rb +9 -0
- data/lib/rgraphum/t.rb +12 -0
- data/lib/rgraphum/tf_idf.rb +27 -0
- data/lib/rgraphum/version.rb +3 -0
- data/lib/rgraphum/vertex.rb +354 -0
- data/lib/rgraphum/vertices.rb +97 -0
- data/lib/rgraphum.rb +38 -0
- data/performance/add-vertices-edges.rb +20 -0
- data/performance/add-vertices.rb +12 -0
- data/performance/build-graph.rb +19 -0
- data/performance/delete-graph.rb +24 -0
- data/performance/delete-vertices.rb +25 -0
- data/performance/refer-graph.rb +23 -0
- data/rgraphum.gemspec +30 -0
- data/test/lib/rgraphum/analyzer/linear_regression_test.rb +20 -0
- data/test/lib/rgraphum/analyzer/meme_tracker_test.rb +383 -0
- data/test/lib/rgraphum/analyzer/twitter/rt_at_mark_test.rb +120 -0
- data/test/lib/rgraphum/array_test.rb +95 -0
- data/test/lib/rgraphum/bubble_test.rb +7 -0
- data/test/lib/rgraphum/communities_test.rb +53 -0
- data/test/lib/rgraphum/cosine_similarity_test.rb +18 -0
- data/test/lib/rgraphum/edge_test.rb +89 -0
- data/test/lib/rgraphum/edges_test.rb +178 -0
- data/test/lib/rgraphum/graph_builder_test.rb +64 -0
- data/test/lib/rgraphum/graph_dup_test.rb +199 -0
- data/test/lib/rgraphum/graph_plus_test.rb +80 -0
- data/test/lib/rgraphum/graph_test.rb +512 -0
- data/test/lib/rgraphum/gremlin_test.rb +145 -0
- data/test/lib/rgraphum/importers/idg_json_edges.json +20 -0
- data/test/lib/rgraphum/importers/idg_json_test.rb +207 -0
- data/test/lib/rgraphum/importers/idg_json_vertices.json +46 -0
- data/test/lib/rgraphum/math/average_distance_matrix_test.rb +142 -0
- data/test/lib/rgraphum/math/clustering_coefficient_test.rb +219 -0
- data/test/lib/rgraphum/math/community_test.rb +78 -0
- data/test/lib/rgraphum/math/degree_distribution_test.rb +40 -0
- data/test/lib/rgraphum/math/dijkstra_test.rb +146 -0
- data/test/lib/rgraphum/math/modularity_test.rb +154 -0
- data/test/lib/rgraphum/math/quick_average_distance_matrix_test.rb +84 -0
- data/test/lib/rgraphum/path_test.rb +44 -0
- data/test/lib/rgraphum/query/enumerable_test.rb +42 -0
- data/test/lib/rgraphum/query/where_operators_test.rb +75 -0
- data/test/lib/rgraphum/query/where_test.rb +59 -0
- data/test/lib/rgraphum/simulator/ba_model_test.rb +75 -0
- data/test/lib/rgraphum/simulator/sir_model_test.rb +513 -0
- data/test/lib/rgraphum/simulator/sis_model_test.rb +478 -0
- data/test/lib/rgraphum/simulator_test.rb +22 -0
- data/test/lib/rgraphum/tf_idf_test.rb +30 -0
- data/test/lib/rgraphum/vertex_test.rb +50 -0
- data/test/lib/rgraphum/vertices_test.rb +180 -0
- data/test/test_helper.rb +98 -0
- data/tmp/.gitkeep +0 -0
- metadata +254 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Rgraphum::Parsers
|
6
|
+
class MiserablesParser
|
7
|
+
class << self
|
8
|
+
def builder(graph)
|
9
|
+
stream = "var miserables = { vertices: ["
|
10
|
+
|
11
|
+
graph.vertices.each_with_index do |vertex, n|
|
12
|
+
vertex.class_eval { attr_accessor :no } # FIXME
|
13
|
+
vertex.no = n
|
14
|
+
# stream += "\n{vertexName: \"#{vertex.label}\", group: #{vertex[:community_id]}},"
|
15
|
+
stream += "\n" + { vertexName: vertex.label, group: vertex.community_id }.to_json + ","
|
16
|
+
end
|
17
|
+
stream.chop!
|
18
|
+
stream += "],\n"
|
19
|
+
|
20
|
+
stream += "links: ["
|
21
|
+
graph.edges.each do |edge|
|
22
|
+
stream += "\n{source: #{edge.source.no},"
|
23
|
+
stream += " target: #{edge.target.no},"
|
24
|
+
stream += " value: #{edge.weight}},"
|
25
|
+
end
|
26
|
+
stream.chop!
|
27
|
+
stream += "]\n"
|
28
|
+
stream += "};"
|
29
|
+
|
30
|
+
stream
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Options:
|
35
|
+
#
|
36
|
+
def initialize(options={})
|
37
|
+
default_options = {
|
38
|
+
}
|
39
|
+
@options = default_options.merge(options)
|
40
|
+
builder(@options[:graph]) if @options.key?(:graph)
|
41
|
+
end
|
42
|
+
|
43
|
+
def builder(graph)
|
44
|
+
@stream = self.class.builder(graph)
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_s
|
48
|
+
unless @stream
|
49
|
+
raise ArgumentError, "Didn't build stream with builder(graph)"
|
50
|
+
end
|
51
|
+
@stream
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require_relative 'parsers/gephi'
|
4
|
+
require_relative 'parsers/miserables'
|
5
|
+
require_relative 'parsers/graphviz'
|
6
|
+
require_relative 'parsers/flare'
|
7
|
+
|
8
|
+
module Rgraphum::Parsers
|
9
|
+
# parsers
|
10
|
+
def to_gephi
|
11
|
+
self.real_aspect! if @aspect == "id"
|
12
|
+
gephi_xml = Rgraphum::Parsers::GephiParser.new(graph: self)
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_miserables
|
16
|
+
self.real_aspect! if @aspect == "id"
|
17
|
+
stream = Rgraphum::Parsers::MiserablesParser.new(graph: self)
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_graphviz( options={} )
|
21
|
+
self.real_aspect! if @aspect == "id"
|
22
|
+
default_options = { graph:self, layout: "dot" }
|
23
|
+
options = default_options.merge(options)
|
24
|
+
dot = Rgraphum::Parsers::GraphvizParser.new( options )
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_flare
|
28
|
+
self.real_aspect! if @aspect == "id"
|
29
|
+
stream = Rgraphum::Parsers::MiserablesParser.new(graph: self)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
class Rgraphum::Path
|
4
|
+
attr_reader :end_vertex
|
5
|
+
attr_reader :vertices
|
6
|
+
|
7
|
+
# vertices: Array of Rgraphum::Vertex
|
8
|
+
#
|
9
|
+
def initialize(end_vertex=nil, vertices=[])
|
10
|
+
@end_vertex = end_vertex
|
11
|
+
@vertices = vertices
|
12
|
+
end
|
13
|
+
|
14
|
+
def include?(vertex)
|
15
|
+
vertices.include?(vertex)
|
16
|
+
end
|
17
|
+
|
18
|
+
def edges
|
19
|
+
# FIXME
|
20
|
+
last_vertex = vertices.first
|
21
|
+
graph = last_vertex.graph
|
22
|
+
new_edges = []
|
23
|
+
vertices.each do |vertex|
|
24
|
+
if last_vertex != vertex
|
25
|
+
edge = graph.edges.where(source: last_vertex, target: vertex).first
|
26
|
+
edge ||= graph.edges.where(source: vertex, target: last_vertex).first
|
27
|
+
new_edges << edge
|
28
|
+
end
|
29
|
+
last_vertex = vertex
|
30
|
+
end
|
31
|
+
new_edges
|
32
|
+
end
|
33
|
+
|
34
|
+
def total_weight
|
35
|
+
edges.inject(0) { |sum, edge| sum + edge.weight }
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
class Rgraphum::Query
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
def initialize(array, *conditions)
|
7
|
+
@array = array
|
8
|
+
@conditions = []
|
9
|
+
@id_conditions = nil
|
10
|
+
where(*conditions)
|
11
|
+
end
|
12
|
+
|
13
|
+
def each(&block)
|
14
|
+
if block_given?
|
15
|
+
all.each do |item|
|
16
|
+
yield item
|
17
|
+
end
|
18
|
+
else
|
19
|
+
to_enum
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def empty?
|
24
|
+
all.empty?
|
25
|
+
end
|
26
|
+
|
27
|
+
# where(id: 3).first
|
28
|
+
# where(label: "abc").all
|
29
|
+
# where(id: 1, label: "abc").all
|
30
|
+
# where(:label, :eq, "abc").all
|
31
|
+
# where(:label, :ne, "abc").all
|
32
|
+
def where(*conditions)
|
33
|
+
if conditions.size == 1 && conditions[0].is_a?(Hash)
|
34
|
+
conditions = conditions[0]
|
35
|
+
end
|
36
|
+
return self if conditions.empty?
|
37
|
+
|
38
|
+
case conditions
|
39
|
+
when Hash
|
40
|
+
conditions.each do |fieldname, rvalue|
|
41
|
+
condition = [fieldname, :eq, rvalue]
|
42
|
+
@conditions << condition
|
43
|
+
if fieldname == :id
|
44
|
+
@id_conditions ||= []
|
45
|
+
@id_conditions << condition
|
46
|
+
end
|
47
|
+
end
|
48
|
+
when Array
|
49
|
+
fieldname, operator, rvalue = conditions
|
50
|
+
condition = [fieldname, operator, rvalue]
|
51
|
+
@conditions << condition
|
52
|
+
if fieldname == :id && (operator == :eq || operator == :==)
|
53
|
+
@id_conditions ||= []
|
54
|
+
@id_conditions << condition
|
55
|
+
end
|
56
|
+
else
|
57
|
+
raise NotImplementedError
|
58
|
+
end
|
59
|
+
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
def all
|
64
|
+
if @id_conditions && @array.respond_to?(:find_by_id)
|
65
|
+
item = @array.find_by_id(@id_conditions.first[2])
|
66
|
+
if try_conditions(item)
|
67
|
+
[item]
|
68
|
+
else
|
69
|
+
[]
|
70
|
+
end
|
71
|
+
else
|
72
|
+
@array.class.new( @array.select { |item| try_conditions(item) } )
|
73
|
+
end
|
74
|
+
end
|
75
|
+
alias :to_a :all
|
76
|
+
|
77
|
+
def first
|
78
|
+
if @id_conditions && @array.respond_to?(:find_by_id)
|
79
|
+
item = @array.find_by_id(@id_conditions.first[2])
|
80
|
+
return item if item && try_conditions(item)
|
81
|
+
else
|
82
|
+
@array.each do |item|
|
83
|
+
return item if try_conditions(item)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
nil
|
87
|
+
end
|
88
|
+
|
89
|
+
def last
|
90
|
+
if @id_conditions && @array.respond_to?(:find_by_id)
|
91
|
+
item = @array.find_by_id(@id_conditions.first[2])
|
92
|
+
return item if item && try_conditions(item)
|
93
|
+
else
|
94
|
+
@array.reverse_each do |item|
|
95
|
+
return item if try_conditions(item)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
nil
|
99
|
+
end
|
100
|
+
|
101
|
+
def method_missing(method_name, *args)
|
102
|
+
if @array.first.respond_to?(method_name)
|
103
|
+
all.map { |item| item.send(method_name) }
|
104
|
+
else
|
105
|
+
super
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def try_conditions(item)
|
112
|
+
@conditions.each do |(fieldname, operator, rvalue)|
|
113
|
+
lvalue = item.send(fieldname)
|
114
|
+
case operator
|
115
|
+
when :eq, :==; return false unless lvalue == rvalue
|
116
|
+
when :ne, :!=; return false unless lvalue != rvalue
|
117
|
+
when :gt, :>; return false unless lvalue > rvalue
|
118
|
+
when :gte, :>=; return false unless lvalue >= rvalue
|
119
|
+
when :lt, :<; return false unless lvalue < rvalue
|
120
|
+
when :lte, :<=; return false unless lvalue <= rvalue
|
121
|
+
when :match, :=~; return false unless lvalue =~ rvalue
|
122
|
+
when :not_match, :!~; return false unless lvalue !~ rvalue
|
123
|
+
else
|
124
|
+
raise ArgumentError, "Unknown operator #{operator}"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
true
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require_relative 'query'
|
4
|
+
|
5
|
+
class Rgraphum::RgraphumArray < Array
|
6
|
+
attr_accessor :graph
|
7
|
+
|
8
|
+
# def ids
|
9
|
+
# map { |obj| obj.id }
|
10
|
+
# end
|
11
|
+
|
12
|
+
# FIXME use initialize_copy instead
|
13
|
+
def dup
|
14
|
+
array = self.class.new
|
15
|
+
each do |item|
|
16
|
+
array << item.dup
|
17
|
+
end
|
18
|
+
array
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.reset_id
|
22
|
+
remove_instance_variable :@new_id
|
23
|
+
end
|
24
|
+
|
25
|
+
# FIXME maybe better id to start from 1
|
26
|
+
# def self.new_id
|
27
|
+
# @new_id ||= -1
|
28
|
+
# @new_id += 1
|
29
|
+
# end
|
30
|
+
|
31
|
+
def new_id(id=nil)
|
32
|
+
# self.class.new_id
|
33
|
+
@new_id ||= -1
|
34
|
+
if id
|
35
|
+
@new_id = id if @new_id < id
|
36
|
+
id
|
37
|
+
else
|
38
|
+
@new_id += 1
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def substitute(array, &block)
|
43
|
+
return unless array.is_a?(Array)
|
44
|
+
new_array = self.class.new
|
45
|
+
new_array.graph = @graph
|
46
|
+
array.each do |item|
|
47
|
+
if block_given?
|
48
|
+
new_array << (yield item)
|
49
|
+
else
|
50
|
+
new_array << item
|
51
|
+
end
|
52
|
+
end
|
53
|
+
new_array
|
54
|
+
end
|
55
|
+
|
56
|
+
alias :original_delete_if :delete_if
|
57
|
+
alias :original_reject! :reject!
|
58
|
+
def delete_if
|
59
|
+
if block_given?
|
60
|
+
i = 0
|
61
|
+
size = self.size
|
62
|
+
while i < size
|
63
|
+
item = self[i]
|
64
|
+
if yield(item)
|
65
|
+
delete(item)
|
66
|
+
size -= 1
|
67
|
+
else
|
68
|
+
i += 1
|
69
|
+
end
|
70
|
+
end
|
71
|
+
self
|
72
|
+
else
|
73
|
+
to_enum
|
74
|
+
end
|
75
|
+
end
|
76
|
+
alias :reject! :delete_if
|
77
|
+
|
78
|
+
alias :original_reject :reject
|
79
|
+
def reject(&block)
|
80
|
+
dup.reject! &block
|
81
|
+
end
|
82
|
+
|
83
|
+
def where(*conditions)
|
84
|
+
Rgraphum::Query.new(self, *conditions)
|
85
|
+
end
|
86
|
+
|
87
|
+
# gremlin methods
|
88
|
+
|
89
|
+
# has
|
90
|
+
# Allows an element if it has a particular property. Utilizes several options for comparisons through T:
|
91
|
+
# T.gt - greater than
|
92
|
+
# T.gte - greater than or equal to
|
93
|
+
# T.eq - equal to
|
94
|
+
# T.neq - not equal to
|
95
|
+
# T.lte - less than or equal to
|
96
|
+
# T.lt - less than
|
97
|
+
# It is worth noting that the syntax of has is similar to g.V("name", "marko"), which has the difference of being a key index lookup and as such will perform faster. In contrast, this line, g.V.has("name", "marko"), will iterate over all vertices checking the name property of each vertex for a match and will be significantly slower than the key index approach.
|
98
|
+
# gremlin> g.V.has("name", "marko").name
|
99
|
+
# ==>marko
|
100
|
+
# gremlin> g.v(1).outE.has("weight", T.gte, 0.5f).weight
|
101
|
+
# ==>0.5
|
102
|
+
# ==>1.0
|
103
|
+
# gremlin> g.V.has("age", null).name
|
104
|
+
# ==>lop
|
105
|
+
# ==>ripple
|
106
|
+
def has(*conditions)
|
107
|
+
if conditions.size == 1
|
108
|
+
self.class.new( where( conditions[0], :!=, nil ) )
|
109
|
+
elsif conditions.size == 2
|
110
|
+
self.class.new( where( { conditions[0] => conditions[1] } ) )
|
111
|
+
elsif conditions.size == 3
|
112
|
+
self.class.new( where( *conditions ) )
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# hasNot
|
117
|
+
# Allows an element if it does not have a particular property. Utilizes several options for comparisons on through T:
|
118
|
+
# T.gt - greater than
|
119
|
+
# T.gte - greater than or equal to
|
120
|
+
# T.eq - equal to
|
121
|
+
# T.neq - not equal to
|
122
|
+
# T.lte - less than or equal to
|
123
|
+
# T.lt - less than
|
124
|
+
# gremlin> g.v(1).outE.hasNot("weight", T.eq, 0.5f).weight
|
125
|
+
# ==>1.0
|
126
|
+
# ==>0.4
|
127
|
+
# gremlin> g.V.hasNot("age", null).name
|
128
|
+
# ==>vadas
|
129
|
+
# ==>marko
|
130
|
+
# ==>peter
|
131
|
+
# ==>josh
|
132
|
+
def hasNot(*conditions)
|
133
|
+
nor_hash = {
|
134
|
+
:== => :!=,
|
135
|
+
:!= => :==,
|
136
|
+
:< => :>=,
|
137
|
+
:<= => :> ,
|
138
|
+
:>= => :<,
|
139
|
+
:> => :<=,
|
140
|
+
}
|
141
|
+
|
142
|
+
if conditions.size == 1
|
143
|
+
self.class.new( where( conditions[0], :==, nil ) )
|
144
|
+
elsif conditions.size == 2
|
145
|
+
where( conditions[0], :!=, conditions[1] )
|
146
|
+
elsif conditions.size == 3
|
147
|
+
where( conditions[0], nor_hash[conditions[1]], conditions[2] )
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def method_missing(name, *args)
|
152
|
+
if first.class.has_field?(name)
|
153
|
+
map{|item| item.send(name)}
|
154
|
+
else
|
155
|
+
super(name,*args)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module Rgraphum::RgraphumArrayDividers
|
4
|
+
def divide_by_time(interval=20)
|
5
|
+
# interval is min
|
6
|
+
|
7
|
+
base_sec = interval * 60
|
8
|
+
|
9
|
+
each do |item|
|
10
|
+
if item.start
|
11
|
+
item.start = time_rounddown(item.start, base_sec)
|
12
|
+
|
13
|
+
if item.end
|
14
|
+
item.end = time_roundup(item.end, base_sec)
|
15
|
+
else
|
16
|
+
item.end = time_roundup(item.start, base_sec)
|
17
|
+
end
|
18
|
+
|
19
|
+
if item.end > (item.start + base_sec)
|
20
|
+
(item.start.to_i + base_sec).step(item.end.to_i, base_sec) do |t|
|
21
|
+
new_item = item.dup
|
22
|
+
new_item.id = nil
|
23
|
+
new_item.start = Time.at(t)
|
24
|
+
new_item.end = Time.at(t + base_sec - 1)
|
25
|
+
|
26
|
+
self << new_item
|
27
|
+
end
|
28
|
+
item.end = Time.at(item.start.to_i + base_sec - 1)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def time_rounddown(time, sec)
|
37
|
+
Time.at((time.to_i / sec) * sec)
|
38
|
+
end
|
39
|
+
|
40
|
+
def time_roundup(time, sec)
|
41
|
+
Time.at((time.to_i / sec) * sec + sec - 1)
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
# About BA Model
|
4
|
+
# The BA Model is a mathematical model of the network with "growth" and "preferential attachment".
|
5
|
+
# Growth means that the network is added to vertices with a fixed number of edges one at a time.
|
6
|
+
# Preferential attachment shows that the more connected a vertex is, the more likely to receive new edge.
|
7
|
+
# An importance of the model is to generate scale-free networks.
|
8
|
+
# The consruction of the model is as follows;
|
9
|
+
# 1st: Select vertices with the probability of each degree / graph's total degree,
|
10
|
+
# 2nd: Add every selected vertices to new edge.
|
11
|
+
# 3rd: Repeat the 1st step and the 2nd step until the number of vertices attain the target one.
|
12
|
+
class Rgraphum::Simulator::BAModel
|
13
|
+
def initialize(options={})
|
14
|
+
if options[:graph]
|
15
|
+
@graph = options[:graph].dup
|
16
|
+
else
|
17
|
+
@graph = Rgraphum::Graph.new
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Simurate BAmodel
|
22
|
+
# @param [Hash] options BAmodel's options
|
23
|
+
# @option options [Graph] :graph (graph) start(base) origin graph.
|
24
|
+
# default is a graph which used on newly constructing BAModel
|
25
|
+
# @option options [Integer] :round (10,000) simurate rounds, this means added vertex size
|
26
|
+
# @option options [Integer] :edge_size (1) edge size on once added a vertex
|
27
|
+
# @option options [Integer] :interval (1) plus time(min) on new vertex and its edges if vertex or edge has 'cteated_at' value.
|
28
|
+
# @option options [Float] :new_vertex_rate (1.0) Probability of adding new vertex.
|
29
|
+
# If you need to add no new vertex but add edge, use this rate(0.0 - 1.0).
|
30
|
+
# default is 1.0. it means always add new vertex
|
31
|
+
# @return [Graph] a graph after simurate, not same origin graph.
|
32
|
+
#
|
33
|
+
def simulate(options={})
|
34
|
+
default_options = {
|
35
|
+
graph: @graph,
|
36
|
+
round: 10_000,
|
37
|
+
edge_size: 1,
|
38
|
+
interval: 1,
|
39
|
+
new_vertex_rate: 1.0,
|
40
|
+
random_seed: 10,
|
41
|
+
}
|
42
|
+
options = default_options.merge(options)
|
43
|
+
|
44
|
+
srand options[:random_seed]
|
45
|
+
graph = options[:graph]
|
46
|
+
|
47
|
+
base_vertices_size = graph.vertices.size
|
48
|
+
|
49
|
+
while graph.vertices.size - base_vertices_size < options[:round]
|
50
|
+
t_v = target_vertex(graph, options[:new_vertex_rate])
|
51
|
+
options[:edge_size].times do
|
52
|
+
edge = graph.edges.build( { source: source_vertex(graph), target: t_v } )
|
53
|
+
end
|
54
|
+
end
|
55
|
+
graph
|
56
|
+
end
|
57
|
+
|
58
|
+
# selecting source vertex method
|
59
|
+
# Propability of BAModel's vertex selection divide
|
60
|
+
# into probability of edges selection and probalility of source or target selection on edge.
|
61
|
+
# And select source vertex
|
62
|
+
def source_vertex(graph=@graph)
|
63
|
+
return graph.vertices.build({label: new_dummy_label}) if graph.edges.size == 0
|
64
|
+
|
65
|
+
edge_index = rand( graph.edges.size )
|
66
|
+
if rand(2) == 0
|
67
|
+
source_vertex = graph.edges[edge_index][:source]
|
68
|
+
else
|
69
|
+
source_vertex = graph.edges[edge_index][:target]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# selecting target vertex method
|
74
|
+
# with new_vertex_rate, select new vertex or existing vertex.
|
75
|
+
# if selectiong new vertex, make new vertex and add it on graph
|
76
|
+
# @param [Graph] graph (@graph)
|
77
|
+
# @param [Float] new_vertex_rate (1.0)
|
78
|
+
# @return [Vertex]
|
79
|
+
def target_vertex(graph=@graph, new_vertex_rate=1.0)
|
80
|
+
vertices = graph.vertices
|
81
|
+
if new_vertex_rate < rand and vertices.size > 0
|
82
|
+
target_vertex = vertices[rand(vertices.size)]
|
83
|
+
else
|
84
|
+
graph.vertices.build(label: new_dummy_label)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# new dummy label method
|
89
|
+
def new_dummy_label
|
90
|
+
@dummy_label_index ||= 0
|
91
|
+
@dummy_labels ||= ("a".."aaaaaa").to_a
|
92
|
+
dummy_label = @dummy_labels[@dummy_label_index]
|
93
|
+
@dummy_label_index += 1
|
94
|
+
dummy_label
|
95
|
+
end
|
96
|
+
|
97
|
+
######################################################################
|
98
|
+
# @private
|
99
|
+
def vertices_per_min(graph=@graph)
|
100
|
+
return nil if graph.vertices.empty?
|
101
|
+
first_vertex = graph.vertices.min_by { |vertex| vertex.start }
|
102
|
+
last_vertex = graph.vertices.max_by { |vertex| vertex.start }
|
103
|
+
graph.vertices.size / ((last_vertex.start - first_vertex.start) / 60.0)
|
104
|
+
end
|
105
|
+
|
106
|
+
def edges_per_min(graph=@graph)
|
107
|
+
return nil if graph.edges.empty?
|
108
|
+
first_vertex = graph.edges.min_by { |edge| edge.start }
|
109
|
+
last_vertex = graph.edges.max_by { |edge| edge.start }
|
110
|
+
graph.edges.size / ((last_vertex.start - first_vertex.start) / 60.0)
|
111
|
+
end
|
112
|
+
|
113
|
+
def edges_size_array_per_interval(graph=@graph, interval=1)
|
114
|
+
sorted_edges = graph.edges.sort{ |a, b| a.start <=> b.start}
|
115
|
+
step = interval * 60
|
116
|
+
|
117
|
+
start_time = sorted_edges[0].start.to_i / step * 60
|
118
|
+
end_time = sorted_edges[-1].start.to_i / step * 60 + 1
|
119
|
+
|
120
|
+
time_array = []
|
121
|
+
time = start_time + step
|
122
|
+
size = 0
|
123
|
+
starts = sorted_edges.map { |edge| edge.start.to_i }
|
124
|
+
|
125
|
+
time, time_array = next_time(starts, time, step, time_array, size)
|
126
|
+
|
127
|
+
time_array
|
128
|
+
end
|
129
|
+
|
130
|
+
def next_time(sources, time, step, time_array, size)
|
131
|
+
return [time, time_array + [size]] if sources.empty?
|
132
|
+
if sources[0] >= time
|
133
|
+
time_array << size
|
134
|
+
time, time_array = next_time(sources, time+step, step, time_array, 0)
|
135
|
+
else
|
136
|
+
sources.shift
|
137
|
+
time, time_array = next_time(sources, time, step, time_array, size + 1)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|