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