louvian_ruby 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/lib/louvian/community.rb +23 -8
  2. data/lib/louvian.rb +47 -139
  3. metadata +1 -1
@@ -1,19 +1,32 @@
1
1
  class Louvian::Community
2
2
  attr_accessor :in, :tot, :nodes_ids, :id
3
3
  @@count = 0
4
- def initialize node_id
4
+ def initialize adj_list, level
5
+ #puts "Adj list is "
5
6
  @id = @@count
6
7
  @@count+=1
7
- @nodes_ids = [node_id]
8
- @in = 0 # sum of links weights inside the community
9
- @tot = Louvian.get_adj(node_id).count # sum of links weights incident to the community
8
+
9
+ # TODO NO NEED TO SORT
10
+ @nodes_ids = adj_list.keys.sort
11
+ @level = level
12
+
13
+ # sum of links weights inside the community
14
+ #@in = adj_list.select {|k,v| nodes_ids.include? k}.inject(0) {|r,(k,v)| r+v.count}
15
+
16
+ @in = 0
17
+ adj_list.each do |node, neighbors|
18
+ @in += neighbors.select {|node| @nodes_ids.include? node}.count
19
+ end
20
+
21
+ # sum of links weights incident to the community
22
+ @tot = adj_list.inject(0) {|r,(k,v)| r+v.count}
10
23
  end
11
24
 
12
25
  def self.reset
13
26
  @@count = 0
14
27
  end
15
28
 
16
- def insert node, links_to_comm
29
+ def insert node, node_adj
17
30
  #puts "\t\tinsert node #{node} to comm #{@id}"
18
31
  @nodes_ids << node
19
32
 
@@ -21,12 +34,13 @@ class Louvian::Community
21
34
  #@in += links_to_comm
22
35
  #@tot += (Louvian.get_adj(node).count - links_to_comm)
23
36
 
37
+ links_to_comm = node_adj.select {|n| nodes_ids.include? n}.count
24
38
  # Copied from the cpp code
25
39
  @in += 2*links_to_comm
26
- @tot += (Louvian.get_adj(node).count)
40
+ @tot += node_adj.count
27
41
  end
28
42
 
29
- def remove node, links_to_comm
43
+ def remove node, node_adj
30
44
  #puts "\t\tremove node #{node} to comm #{@id}"
31
45
  @nodes_ids.delete node
32
46
 
@@ -34,9 +48,10 @@ class Louvian::Community
34
48
  #@in -= links_to_comm
35
49
  #@tot -= (Louvian.get_adj(node).count - links_to_comm)
36
50
 
51
+ links_to_comm = node_adj.select {|n| nodes_ids.include? n}.count
37
52
  # Copied from the cpp code
38
53
  @in -= 2*links_to_comm
39
- @tot -= (Louvian.get_adj(node).count)
54
+ @tot -= node_adj.count
40
55
  end
41
56
 
42
57
  end
data/lib/louvian.rb CHANGED
@@ -1,179 +1,89 @@
1
- require 'set'
2
1
  module Louvian
3
2
  require 'louvian/community'
3
+ require 'louvian/graph'
4
4
 
5
5
  MIN_INCREASE = 0.000001
6
6
 
7
- # Node => community
8
- @@n2c = {}
9
7
 
10
- # Adjacency list
11
- @@adj={}
12
-
13
- # List of all communities (meta_nodes)
14
- @@communities = []
15
-
16
- # List of all nodes
17
- @@nodes = []
18
-
19
- # Sum of all links half edges (double the number of edges)
20
- @@total_weight = 0.0
21
-
22
- def self.communities
23
- @@communities
24
- end
25
-
26
- def self.get_adj node_id
27
- return @@adj[node_id]
8
+ def self.graph
9
+ @@graph
28
10
  end
29
11
 
30
- # This method outputs information about communities
31
- def self.display_communities
32
- @@communities.each do |m|
33
- puts "#{m.id} => #{m.nodes_ids} in=#{m.in} tot=#{m.tot}"
34
- end
12
+ def self.levels
13
+ @@levels
35
14
  end
36
-
37
15
  # This method sets up the whole environemnt for calculations
38
16
  #
39
17
  # @param string [String] in the form of src dest (one edge per line)
40
- def self.init_env string
41
- make_adj string
42
- @@nodes = @@adj.keys.sort
43
- @@nodes.each do |k,v|
44
- @@communities << Community.new(k)
45
- @@n2c[k] = @@communities.last.id
46
- end
47
- @@total_weight = @@adj.inject(0) {|r,(k,v)| r+v.count}
48
-
49
- end
50
-
51
- # This method reset the enviroment and all counters
52
- def self.reset
53
- @@n2c = {}
54
- @@adj={}
55
- @@communities = []
56
- @@nodes = []
57
- @@total_weight = 0.0
58
- Community.reset
18
+ def self.init_env string, directed=false
19
+ list = string.split("\n").map {|line| line.split.map{|n| n.to_i}}
20
+ @@graph = Graph.new list, directed, 0
21
+ @@levels = [] # List of Graphs
22
+ nil
59
23
  end
60
24
 
61
- # This method calcualtes the current modularity of the communities
62
- # @returns q [Float]
63
- def self.modularity
64
- q = 0.0
65
- m2 = @@total_weight
66
-
67
- @@communities.each do |m_node|
68
- q += m_node.in.to_f/m2 - (m_node.tot.to_f/m2 * m_node.tot.to_f/m2)
69
- end
70
- return q
25
+ def self.run
26
+ l = 0
27
+ mod = @@graph.modularity
28
+ begin
29
+ puts "Level #{l}: Comms #{@@graph.communities.size}"
30
+ @@levels << @@graph
31
+ @@graph = @@graph.build_graph_from_comms
32
+ l+=1
33
+ end while self.one_level
71
34
  end
72
35
 
73
36
  # This method iterates over the graph to optimze the modularity. Iterations
74
37
  # stops when there are no possible moves anymore.
75
- def self.iterate
76
- cur_mod = self.modularity
77
- new_mod = cur_mod
78
- improvement = 0.0
79
- min_improvement = 0.01
38
+ # @returns improvement [Boolean] indicates whether there was improvment or no
39
+ def self.one_level
40
+ improvement = false
80
41
  nb_passes = 0
81
- max_passes = 10
42
+ cur_mod = @@graph.modularity
43
+ new_mod = cur_mod
82
44
  begin
83
45
  #puts "Iterating"
84
- #puts "modularity is #{self.modularity}"
46
+ #puts "modularity is #{@@graph.modularity}"
85
47
  cur_mod = new_mod
86
48
  nb_moves = 0
87
49
  nb_passes += 1
88
- @@nodes.shuffle.each do |node|
50
+ @@graph.nodes.shuffle.each do |node|
51
+ #puts "\t#{@@graph.n2c}"
89
52
  #puts "\tconsidering node #{node}"
90
- orig_community_id = @@n2c[node]
91
- orig_community = @@communities.find {|i| i.id == orig_community_id}
92
- node_to_comms_links = self.get_node_to_comms_links node
93
- neighbour_communities = @@communities.find_all {|i| node_to_comms_links.include? i.id}
53
+ orig_community = @@graph.get_community node
94
54
 
95
- orig_community.remove node, node_to_comms_links[orig_community_id]
55
+ neighbour_communities = @@graph.get_neighbour_comms node
96
56
 
57
+ #puts "\tneihbours#{neighbour_communities.map {|i| i.id}}"
58
+ @@graph.remove_node node, orig_community
97
59
 
98
- best_community_id = orig_community_id
60
+
61
+ best_community = orig_community
99
62
  max_gain = 0.0
100
63
 
101
- neighbour_communities.each do |m_node|
102
- mod_gain = self.modularity_gain(node, m_node, node_to_comms_links[m_node.id])
103
- #puts "\t\tfor comm #{m_node.id} mod increase is #{mod_gain}"
64
+ neighbour_communities.each do |comm|
65
+ mod_gain = @@graph.modularity_gain node, comm
66
+ #puts "\t\tfor comm #{comm.id} mod increase is #{mod_gain}"
104
67
  if mod_gain > max_gain
105
68
  max_gain = mod_gain
106
- best_community_id = m_node.id
69
+ best_community = comm
107
70
  end
108
71
  end
109
- if best_community_id != orig_community_id
72
+ if best_community != orig_community
110
73
  nb_moves += 1
74
+ improvement = true
111
75
  end
112
76
 
113
- best_community = @@communities.find{|m| m.id == best_community_id}
114
- #puts "\t\tbest comm #{best_community.id}"
77
+ @@graph.insert_node node, best_community
115
78
 
116
- best_community.insert node, node_to_comms_links[best_community.id]
117
- @@n2c[node] = best_community_id
79
+ @@graph.garbage_collect orig_community
118
80
 
119
- if orig_community.nodes_ids.empty?
120
- @@communities.delete orig_community
121
- end
122
81
  end
123
- display_communities
124
- new_mod = self.modularity
125
- puts "modularity is #{self.modularity}"
82
+ #display_communities
83
+ new_mod = @@graph.modularity
84
+ #puts "modularity was #{cur_mod} and now #{new_mod}, moves #{nb_moves}"
126
85
  end while nb_moves > 0 and new_mod - cur_mod >= MIN_INCREASE
127
- end
128
-
129
- private
130
-
131
- # This method builds the adjacency list from +string+
132
- # @param string [String] in the form of src dest (edge per line)
133
- def self.make_adj string
134
- @@adj = {}
135
- lines = string.split("\n")
136
- lines.each do |l|
137
- @@adj[l.split[0].to_i] ||= Set.new
138
- @@adj[l.split[0].to_i] << l.split[1].to_i
139
- @@adj[l.split[1].to_i] ||= Set.new
140
- @@adj[l.split[1].to_i] << l.split[0].to_i
141
- end
142
- nil
143
- end
144
-
145
-
146
- # This method calcualtes the modularity gain for moving +node+ to +community+
147
- # @param +node+ this is the node to be moved
148
- # @param +community+ this is the destination community
149
- # @param +nb_links_to_comm+ is the number of links from +node+ to +community+
150
- # @returns delta_q (the gain of modularity)
151
- def self.modularity_gain node, community, nb_links_to_comm
152
- tot = community.tot
153
- deg = get_adj(node).count
154
- m2 = @@total_weight
155
-
156
- #puts "\t\t\tcomm #{community.id} #{[tot, deg, m2, nb_links_to_comm]}"
157
- # what makes sense
158
- #return (nb_links_to_comm.to_f/m2) - (tot * deg.to_f/m2**2/2)
159
-
160
- # copied from the cpp code
161
- return nb_links_to_comm.to_f - tot*deg.to_f/m2
162
- end
163
-
164
- # This method gets all neighbour communities and the number of links from +node+
165
- # to all neighbou communities
166
- # @param +node+ to be considered
167
- # @return +node_to_comms_links+ [Hash] {community => number_of_links from +node+
168
- # to +community+}
169
- def self.get_node_to_comms_links node
170
- neighbour_nodes = self.get_adj node
171
- node_to_comms_links = {}
172
- neighbour_nodes.each do |node|
173
- node_to_comms_links[@@n2c[node]] = (node_to_comms_links[@@n2c[node]] || 0) + 1
174
- end
175
- node_to_comms_links[@@n2c[node]] ||= 0
176
- return node_to_comms_links
86
+ return improvement
177
87
  end
178
88
 
179
89
 
@@ -190,10 +100,8 @@ module Louvian
190
100
  5 6
191
101
  5 7'
192
102
 
193
- Louvian.init_env s
194
- Louvian.iterate
195
- Louvian.display_communities
196
- Louvian.reset
103
+ Louvian.init_env s, false
104
+ Louvian.run
197
105
  nil
198
106
  end
199
107
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: louvian_ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors: