louvian_ruby 0.0.2 → 0.0.3

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