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,53 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
module Rgraphum::Graph::Math::ClusteringCoefficient
|
|
4
|
+
def self.included(base)
|
|
5
|
+
base.extend ClassMethods
|
|
6
|
+
base::RGRAPHUM::Vertex.send :include, VertexMethods
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Global clustering coefficient
|
|
10
|
+
#
|
|
11
|
+
# Returns clustering coefficient in Rational
|
|
12
|
+
#
|
|
13
|
+
def clustering_coefficient
|
|
14
|
+
n = vertices.size
|
|
15
|
+
# vertices.inject(0) { |cc, vertex|
|
|
16
|
+
# cc + (vertex.clustering_coefficient / n)
|
|
17
|
+
# }
|
|
18
|
+
sum_of_local_clustering_coefficient = vertices.inject(0) { |cc, vertex|
|
|
19
|
+
cc + (vertex.clustering_coefficient)
|
|
20
|
+
}
|
|
21
|
+
sum_of_local_clustering_coefficient / n
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
module ClassMethods
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
module VertexMethods
|
|
28
|
+
# Local clustering coefficient
|
|
29
|
+
#
|
|
30
|
+
# <pre>
|
|
31
|
+
# open triplet closed triplet
|
|
32
|
+
# V1 V2 V1 - V2
|
|
33
|
+
# \ / \ /
|
|
34
|
+
# V3 V3
|
|
35
|
+
# </pre>
|
|
36
|
+
#
|
|
37
|
+
def clustering_coefficient
|
|
38
|
+
ajacency_vertices = both
|
|
39
|
+
return Rational(0) if ajacency_vertices.size < 2
|
|
40
|
+
num_open_triplets, num_close_triplets = 0, 0
|
|
41
|
+
ajacency_vertices.combination(2) do |(v1, v2)|
|
|
42
|
+
va, vb = v1, v2
|
|
43
|
+
va, vb = v2, v1 if v1.edges.size > v2.edges.size
|
|
44
|
+
if va.edges.any? { |e| e.bothV.include?(vb) }
|
|
45
|
+
num_close_triplets += 1
|
|
46
|
+
else
|
|
47
|
+
num_open_triplets += 1
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
Rational(num_close_triplets, num_open_triplets + num_close_triplets)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
module Rgraphum::Graph::Math
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
# Community Detection by Girvan?Newman algorithm
|
|
7
|
+
# 1st Set all vertex on each uniq communiy
|
|
8
|
+
# 2nd find edges within communities and make communities pair
|
|
9
|
+
# 3rd calc delta_q if marge communities pair
|
|
10
|
+
# 4th max delta_q communities pair is marged
|
|
11
|
+
# 5th update delta_q and do 4th
|
|
12
|
+
# 6th if dalta_q <= limit, stop community detection
|
|
13
|
+
class Rgraphum::Graph::Math::CommunityDetection
|
|
14
|
+
|
|
15
|
+
def initialize(graph,limit = 0)
|
|
16
|
+
@graph = graph
|
|
17
|
+
@m = graph.m_with_weight
|
|
18
|
+
@limit = 0
|
|
19
|
+
|
|
20
|
+
@modularity = 0
|
|
21
|
+
@id_community_hash = {}
|
|
22
|
+
initial_modularity_and_communities
|
|
23
|
+
@delta_q_hash = {}
|
|
24
|
+
initial_delta_q_hash
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# modularity
|
|
29
|
+
# main loop of community_detection and cale modularity with delta_q( = delta_modularity )
|
|
30
|
+
#
|
|
31
|
+
def modularity
|
|
32
|
+
|
|
33
|
+
loop do
|
|
34
|
+
(community_id_a, community_id_b), delta_q = @delta_q_hash.max { |a, b| a[1] <=> b[1] }
|
|
35
|
+
|
|
36
|
+
break unless community_id_a && community_id_b && delta_q
|
|
37
|
+
break if delta_q <= @limit
|
|
38
|
+
|
|
39
|
+
@modularity += delta_q / (2.0 * @m ** 2)
|
|
40
|
+
|
|
41
|
+
community_a = @id_community_hash[community_id_a]
|
|
42
|
+
community_b = @id_community_hash[community_id_b]
|
|
43
|
+
|
|
44
|
+
community_a.merge(community_b)
|
|
45
|
+
@delta_q_hash = update_delta_q_hash(community_id_a, community_id_b, @delta_q_hash)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
@modularity
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def initial_modularity_and_communities
|
|
53
|
+
|
|
54
|
+
@graph.vertices.each_with_index do |vertex, i|
|
|
55
|
+
vertex.community_id = i
|
|
56
|
+
unless vertex.edges == []
|
|
57
|
+
community = Rgraphum::Community.new(id: vertex.community_id, graph: self, vertices: [vertex])
|
|
58
|
+
@id_community_hash[i] = community
|
|
59
|
+
@modularity -= vertex.degree_weight.to_f ** 2 / (2.0 * @m ) ** 2
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# delta_q_hash: Hash
|
|
66
|
+
# key: [community_a.id, community_b.id] # Array of Integer
|
|
67
|
+
# value: delta_q_seed # Float
|
|
68
|
+
def initial_delta_q_hash( limit=0 )
|
|
69
|
+
@graph.edges.each do |edge|
|
|
70
|
+
s_c_id = edge[:source].community_id
|
|
71
|
+
t_c_id = edge[:target].community_id
|
|
72
|
+
|
|
73
|
+
# don't use loop
|
|
74
|
+
next if s_c_id == t_c_id
|
|
75
|
+
|
|
76
|
+
key = [ s_c_id, t_c_id ].sort!
|
|
77
|
+
|
|
78
|
+
delta_q_seed = delta_q_seed( @id_community_hash[ key[0] ] , @id_community_hash[ key[1] ] )
|
|
79
|
+
next if delta_q_seed <= limit
|
|
80
|
+
@delta_q_hash[key] = delta_q_seed
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# ΔQ = Qa - Qb
|
|
86
|
+
def delta_q(community_a, community_b)
|
|
87
|
+
seed = delta_q_seed(community_a, community_b)
|
|
88
|
+
seed / (2.0 * @m ** 2)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
def update_delta_q_hash(community_id_a, community_id_b, delta_q_hash)
|
|
94
|
+
a_id = community_id_a
|
|
95
|
+
b_id = community_id_b
|
|
96
|
+
|
|
97
|
+
new_keys = []
|
|
98
|
+
used_keys = []
|
|
99
|
+
|
|
100
|
+
# b_id -> a_id
|
|
101
|
+
@delta_q_hash.delete_if do |key,value|
|
|
102
|
+
next true if key == [a_id,b_id]
|
|
103
|
+
next false unless index = key.index(b_id)
|
|
104
|
+
|
|
105
|
+
key_dup = key.dup
|
|
106
|
+
key_dup[index] = a_id
|
|
107
|
+
|
|
108
|
+
key_dup.sort!
|
|
109
|
+
new_keys << key_dup
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
new_keys.each do |key|
|
|
113
|
+
@delta_q_hash[key] = 0.0
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# update delta_q_value
|
|
117
|
+
community_a = @id_community_hash[a_id]
|
|
118
|
+
@delta_q_hash.each do |key, value|
|
|
119
|
+
if a_key_index = key.index(a_id)
|
|
120
|
+
o_key_index = a_key_index - 1
|
|
121
|
+
o_id = key[o_key_index]
|
|
122
|
+
other_community = @id_community_hash[o_id]
|
|
123
|
+
|
|
124
|
+
delta_q_seed = delta_q_seed(community_a, other_community)
|
|
125
|
+
next if delta_q_seed <= @limit
|
|
126
|
+
@delta_q_hash[key] = delta_q_seed
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
@delta_q_hash
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def delta_q_seed(community_a, community_b)
|
|
134
|
+
tot_i = community_a.degree_weight
|
|
135
|
+
tot_j = community_b.degree_weight
|
|
136
|
+
|
|
137
|
+
ki_in = community_a.edges_from(community_b).inject(0) { |sum, edge| sum + edge.weight }
|
|
138
|
+
|
|
139
|
+
2.0 * @m * ki_in - tot_i * tot_j
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
module Rgraphum::Graph::Math
|
|
4
|
+
|
|
5
|
+
class DegreeDistribution
|
|
6
|
+
attr_reader :degree_distribution
|
|
7
|
+
|
|
8
|
+
def initialize(graph)
|
|
9
|
+
degree_distribution = {}
|
|
10
|
+
|
|
11
|
+
graph.vertices.each do |vertex|
|
|
12
|
+
degree_distribution[vertex.degree] ||= 0
|
|
13
|
+
degree_distribution[vertex.degree] += 1
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
@degree_distribution = {}
|
|
17
|
+
degree_distribution.each do |key, value|
|
|
18
|
+
@degree_distribution[key] = value.to_f / graph.vertices.size
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
self
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def exponent
|
|
25
|
+
loged_degree_distribution = to_log
|
|
26
|
+
ral = Rgraphum::Analyzer::LinearRegression.new
|
|
27
|
+
ral.analyze(loged_degree_distribution.keys, loged_degree_distribution.values).first
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def to_log
|
|
31
|
+
output ={}
|
|
32
|
+
@degree_distribution.each do |key,value|
|
|
33
|
+
output[Math.log(key, 10)] = Math.log(value, 10)
|
|
34
|
+
end
|
|
35
|
+
output
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def degree_distribution(force=false)
|
|
40
|
+
@degree_distribution = nil if force
|
|
41
|
+
@degree_distribution ||= DegreeDistribution.new(self)
|
|
42
|
+
@degree_distribution.degree_distribution
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def degree_distribution_exponent(force=false)
|
|
46
|
+
@degree_distribution = nil if force
|
|
47
|
+
@degree_distribution ||= DegreeDistribution.new(self)
|
|
48
|
+
@degree_distribution.exponent
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
# A --(1)-- B --(4)-- G
|
|
4
|
+
# | \ | /
|
|
5
|
+
#(2) (4) (2) (1)
|
|
6
|
+
# | \ | /
|
|
7
|
+
# C --(3)-- D
|
|
8
|
+
#
|
|
9
|
+
# A -> B -> D -> G
|
|
10
|
+
#
|
|
11
|
+
#
|
|
12
|
+
# \ A B C D G
|
|
13
|
+
# A - 1 2 4 -
|
|
14
|
+
# B 1 - - 2 4
|
|
15
|
+
# C 2 - - 3 -
|
|
16
|
+
# D 4 2 3 - 1
|
|
17
|
+
# G - 4 - 1 -
|
|
18
|
+
#
|
|
19
|
+
# \ A B C D G
|
|
20
|
+
# A - 1 2 4 -
|
|
21
|
+
# B - - - 2 4
|
|
22
|
+
# C - - - 3 -
|
|
23
|
+
# D - - - - 1
|
|
24
|
+
# G - - - - -
|
|
25
|
+
#
|
|
26
|
+
# \ A B C D G
|
|
27
|
+
# A - 1 2 4 -
|
|
28
|
+
# B - - - 2 4
|
|
29
|
+
# C - - - 3 -
|
|
30
|
+
# D - - - - 1
|
|
31
|
+
# G - - - - -
|
|
32
|
+
#
|
|
33
|
+
# A-B 1
|
|
34
|
+
# A-C 2
|
|
35
|
+
# A-D 4
|
|
36
|
+
#
|
|
37
|
+
# A-B-D 1+2=3
|
|
38
|
+
# A-B-G 1+4=5
|
|
39
|
+
# A-C 1
|
|
40
|
+
# A-D 4
|
|
41
|
+
#
|
|
42
|
+
# A-B-D 1+2=3
|
|
43
|
+
# A-B-G 1+4=5
|
|
44
|
+
# A-C-D 1+3=4
|
|
45
|
+
# A-D 4
|
|
46
|
+
#
|
|
47
|
+
# A-B-D 1+2=3
|
|
48
|
+
# A-B-G 1+4=5
|
|
49
|
+
# A-C-D 1+3=4 x
|
|
50
|
+
# A-D 4
|
|
51
|
+
#
|
|
52
|
+
# A-B-D-G 1+2+1=4
|
|
53
|
+
# A-B-G 1+4 =5 x
|
|
54
|
+
# A-C-D 1+3 =4 x
|
|
55
|
+
# A-D 4 x
|
|
56
|
+
#
|
|
57
|
+
module Rgraphum::Graph::Math::Dijkstra
|
|
58
|
+
def self.included(base)
|
|
59
|
+
base.extend ClassMethods
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Find shortest route from start to end
|
|
63
|
+
#
|
|
64
|
+
# Returns array of vertices
|
|
65
|
+
#
|
|
66
|
+
def dijkstra(start_vertex, end_vertex)
|
|
67
|
+
self.class.dijkstra(self, start_vertex, end_vertex)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def adjacency_matrix
|
|
71
|
+
self.class.adjacency_matrix(self)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def average_distance
|
|
75
|
+
self.class.average_distance(self)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def quick_average_distance
|
|
79
|
+
self.class.quick_average_distance(self)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def minimum_distance_matrix
|
|
83
|
+
self.class.minimum_distance_matrix(self)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
module ClassMethods
|
|
87
|
+
# class Route
|
|
88
|
+
# attr_reader :id
|
|
89
|
+
# attr_accessor :vertices, :total_weight, :ended
|
|
90
|
+
#
|
|
91
|
+
# def initialize(vertex, options={})
|
|
92
|
+
# weight = options[:weight] || 1
|
|
93
|
+
# end_vertex = options[:end_vertex]
|
|
94
|
+
# end_id = end_vertex ? end_vertex.id : options[:end_id]
|
|
95
|
+
# if options.key?(:start_vertex)
|
|
96
|
+
# start_vertex = options[:start_vertex]
|
|
97
|
+
# @id = vertex.id
|
|
98
|
+
# @vertices = [start_vertex, vertex]
|
|
99
|
+
# @total_weight = weight
|
|
100
|
+
# @ended = (id == end_id)
|
|
101
|
+
# elsif options.key?(:route)
|
|
102
|
+
# route = options[:route]
|
|
103
|
+
# @id = vertex.id
|
|
104
|
+
# @vertices = route[:vertices] + [vertex]
|
|
105
|
+
# @total_weight = route[:total_weight] + weight
|
|
106
|
+
# @ended = (id == end_id)
|
|
107
|
+
# else
|
|
108
|
+
# raise ArgumentError
|
|
109
|
+
# end
|
|
110
|
+
# end
|
|
111
|
+
# end
|
|
112
|
+
|
|
113
|
+
# Find shortest route from start to end
|
|
114
|
+
#
|
|
115
|
+
# Returns array of vertices
|
|
116
|
+
#
|
|
117
|
+
def dijkstra(graph, start_vertex, end_vertex)
|
|
118
|
+
details = dijkstra_details(graph, start_vertex, end_vertex)
|
|
119
|
+
routes = details[:routes]
|
|
120
|
+
|
|
121
|
+
return [] if routes.nil? || routes.empty?
|
|
122
|
+
routes[0][:vertices]
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Find shortest routes from start to end
|
|
126
|
+
#
|
|
127
|
+
# Returns routes
|
|
128
|
+
#
|
|
129
|
+
def dijkstra_details(graph, start_vertex, end_vertex)
|
|
130
|
+
return {} if start_vertex.id == end_vertex.id
|
|
131
|
+
|
|
132
|
+
end_id = end_vertex.id
|
|
133
|
+
size = graph.vertices.size
|
|
134
|
+
|
|
135
|
+
shortest_map = Array.new(size, Float::INFINITY)
|
|
136
|
+
adjacency_matrix = adjacency_matrix(graph)
|
|
137
|
+
|
|
138
|
+
routes = []
|
|
139
|
+
|
|
140
|
+
start_index = graph.vertices.index(start_vertex)
|
|
141
|
+
weights = adjacency_matrix[start_index]
|
|
142
|
+
weights.each_with_index do |weight, i|
|
|
143
|
+
next unless weight
|
|
144
|
+
shortest_map[i] = weight
|
|
145
|
+
|
|
146
|
+
vertex = graph.vertices[i]
|
|
147
|
+
routes << {
|
|
148
|
+
id: vertex.id,
|
|
149
|
+
index: i,
|
|
150
|
+
vertices: [start_vertex, vertex],
|
|
151
|
+
weights: [weight],
|
|
152
|
+
total_weight: weight,
|
|
153
|
+
ended: vertex.id == end_id,
|
|
154
|
+
}
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
loop do
|
|
158
|
+
shortest_route = routes.min_by { |route|
|
|
159
|
+
route[:ended] ? Float::INFINITY : route[:total_weight]
|
|
160
|
+
}
|
|
161
|
+
break if !shortest_route || shortest_route[:ended]
|
|
162
|
+
|
|
163
|
+
routes.delete shortest_route
|
|
164
|
+
weights = adjacency_matrix[shortest_route[:index]]
|
|
165
|
+
weights.each_with_index do |weight, i|
|
|
166
|
+
next unless weight
|
|
167
|
+
vertex = graph.vertices[i]
|
|
168
|
+
next if shortest_route[:vertices].include?(vertex)
|
|
169
|
+
|
|
170
|
+
total_weight = shortest_route[:total_weight] + weight
|
|
171
|
+
next if shortest_map[i] < total_weight
|
|
172
|
+
shortest_map[i] = total_weight
|
|
173
|
+
|
|
174
|
+
routes << {
|
|
175
|
+
id: vertex.id,
|
|
176
|
+
index: i,
|
|
177
|
+
vertices: shortest_route[:vertices] + [vertex],
|
|
178
|
+
weights: shortest_route[:weights] + [weight],
|
|
179
|
+
total_weight: shortest_route[:total_weight] + weight,
|
|
180
|
+
ended: vertex.id == end_id,
|
|
181
|
+
}
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
remove_long_routes routes
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
{
|
|
188
|
+
graph: graph,
|
|
189
|
+
start_vertex: start_vertex,
|
|
190
|
+
end_vertex: end_vertex,
|
|
191
|
+
routes: routes,
|
|
192
|
+
shortest_map: shortest_map,
|
|
193
|
+
adjacency_matrix: adjacency_matrix,
|
|
194
|
+
}
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# 隣接行列 Adjacency matrix
|
|
198
|
+
def adjacency_matrix(graph)
|
|
199
|
+
ids = graph.vertices.map(&:id)
|
|
200
|
+
id_index_map = Hash[*ids.map.with_index { |id, i| [id, i] }.flatten]
|
|
201
|
+
size = graph.vertices.size
|
|
202
|
+
adjacency_matrix = (0...size).map { Array.new(size) }
|
|
203
|
+
|
|
204
|
+
graph.edges.each do |e|
|
|
205
|
+
i = id_index_map[e.source.id]
|
|
206
|
+
j = id_index_map[e.target.id]
|
|
207
|
+
adjacency_matrix[i][j] = e.weight
|
|
208
|
+
adjacency_matrix[j][i] = e.weight # FIXME
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
adjacency_matrix
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def average_distance_with_minimum_distance_matrix(graph, &block)
|
|
215
|
+
minimum_distance_matrix = yield
|
|
216
|
+
n = minimum_distance_matrix.size
|
|
217
|
+
total_minimum_distane = 0
|
|
218
|
+
(0...n).each do |i|
|
|
219
|
+
((i+1)...n).each do |j|
|
|
220
|
+
total_minimum_distane += minimum_distance_matrix[i][j]
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
Rational(total_minimum_distane, Rational(n * (n-1), 2))
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def average_distance(graph)
|
|
227
|
+
average_distance_with_minimum_distance_matrix(graph) {
|
|
228
|
+
minimum_distance_matrix(graph)
|
|
229
|
+
}
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def quick_average_distance(graph)
|
|
233
|
+
average_distance_with_minimum_distance_matrix(graph) {
|
|
234
|
+
quick_minimum_distance_matrix(graph)
|
|
235
|
+
}
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def minimum_distance_matrix(graph)
|
|
239
|
+
size = graph.vertices.size
|
|
240
|
+
distance_matrix = (0...size).map { Array.new(size) }
|
|
241
|
+
|
|
242
|
+
(0...size).each do |i|
|
|
243
|
+
(i...size).each do |j|
|
|
244
|
+
if i == j
|
|
245
|
+
distance = 0
|
|
246
|
+
else
|
|
247
|
+
v1, v2 = graph.vertices[i], graph.vertices[j]
|
|
248
|
+
details = dijkstra_details(graph, v1, v2)
|
|
249
|
+
weights = details[:routes][0][:weights] rescue []
|
|
250
|
+
if weights.empty?
|
|
251
|
+
distance = nil
|
|
252
|
+
else
|
|
253
|
+
distance = weights.inject(0, &:+)
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
distance_matrix[i][j] = distance
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
(0...(size-1)).each do |i|
|
|
261
|
+
((i+1)...size).each do |j|
|
|
262
|
+
distance_matrix[j][i] = distance_matrix[i][j]
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
distance_matrix
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def quick_minimum_distance_matrix(graph)
|
|
270
|
+
size = graph.vertices.size
|
|
271
|
+
distance_matrix = (0...size).map { Array.new(size) }
|
|
272
|
+
|
|
273
|
+
remaining_paths = {}
|
|
274
|
+
(0...(size-1)).each do |i|
|
|
275
|
+
((i+1)...size).each do |j|
|
|
276
|
+
remaining_paths[[i, j]] = true
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
until remaining_paths.empty?
|
|
281
|
+
(i, j), _ = remaining_paths.shift
|
|
282
|
+
v1, v2 = graph.vertices[i], graph.vertices[j]
|
|
283
|
+
|
|
284
|
+
details = dijkstra_details(graph, v1, v2)
|
|
285
|
+
weights = details[:routes][0][:weights] rescue []
|
|
286
|
+
if weights.empty?
|
|
287
|
+
distance_matrix[i][j] = nil
|
|
288
|
+
else
|
|
289
|
+
route_vertices = details[:routes][0][:vertices]
|
|
290
|
+
route_vertices_size = route_vertices.size
|
|
291
|
+
|
|
292
|
+
(0...(route_vertices_size-1)).each do |k|
|
|
293
|
+
((k+1)...route_vertices_size).each do |l|
|
|
294
|
+
m = graph.vertices.index(route_vertices[k])
|
|
295
|
+
n = graph.vertices.index(route_vertices[l])
|
|
296
|
+
m, n = n, m if m > n
|
|
297
|
+
unless distance_matrix[m][n]
|
|
298
|
+
distance_matrix[m][n] = weights[k..l].inject(0, &:+)
|
|
299
|
+
remaining_paths.delete [m, n]
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
(0...size).each do |i|
|
|
307
|
+
distance_matrix[i][i] = 0
|
|
308
|
+
end
|
|
309
|
+
(0...(size-1)).each do |i|
|
|
310
|
+
((i+1)...size).each do |j|
|
|
311
|
+
distance_matrix[j][i] = distance_matrix[i][j]
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
distance_matrix
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def remove_long_routes(routes)
|
|
319
|
+
ended_routes = routes.select { |route| route[:ended] }
|
|
320
|
+
shortest_ended_route = ended_routes.min_by { |route| route[:total_weight] }
|
|
321
|
+
|
|
322
|
+
if shortest_ended_route
|
|
323
|
+
routes.reject! do |route|
|
|
324
|
+
shortest_ended_route[:total_weight] < route[:total_weight]
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
private
|
|
331
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
class Rgraphum::Graph
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
module Rgraphum::Graph::Math
|
|
6
|
+
require_relative 'math/dijkstra'
|
|
7
|
+
require_relative 'math/clustering_coefficient'
|
|
8
|
+
require_relative 'math/degree_distribution'
|
|
9
|
+
require_relative 'math/community_detection'
|
|
10
|
+
|
|
11
|
+
def self.included(base)
|
|
12
|
+
# base.extend ClassMethods
|
|
13
|
+
base.send :include, Rgraphum::Graph::Math::Dijkstra
|
|
14
|
+
base.send :include, Rgraphum::Graph::Math::ClusteringCoefficient
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def modularity
|
|
18
|
+
CommunityDetection.new(self).modularity
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def communities
|
|
22
|
+
existing_community_ids = {}
|
|
23
|
+
communities = []
|
|
24
|
+
@vertices.each do |vertex|
|
|
25
|
+
next if existing_community_ids.key?(vertex.community_id)
|
|
26
|
+
community = community_by_id(vertex.community_id)
|
|
27
|
+
communities << community
|
|
28
|
+
existing_community_ids[community.id] = true
|
|
29
|
+
end
|
|
30
|
+
Rgraphum::Communities(communities)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def power_low_rand(max,min,exponent)
|
|
34
|
+
( (max^exponent-min^exponent)*rand() + min^exponent )^( 1.0/exponent )
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def community_by_id(community_id)
|
|
40
|
+
community_id = community_id.id if community_id.is_a?(Rgraphum::Community)
|
|
41
|
+
vertices = @vertices.find_all { |vertex| vertex.community_id == community_id }
|
|
42
|
+
Rgraphum::Community.new(id: community_id, graph: self, vertices: vertices)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
end
|