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