louvian_ruby 0.0.3 → 0.0.4

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/graph.rb +159 -0
  2. data/lib/louvian.rb +1 -1
  3. metadata +2 -1
@@ -0,0 +1,159 @@
1
+ class Louvian::Graph
2
+
3
+ attr_accessor :adj_list, :nodes, :communities, :directed, :n2c, :total_weight
4
+ def initialize edges_list, directed, level
5
+ # Adjacency list
6
+ @adj_list = Louvian::Graph.make_adj edges_list, directed
7
+
8
+ # List of all nodes
9
+ # TODO remove sort
10
+ @nodes = @adj_list.keys.sort
11
+
12
+ # List of all communities (meta_nodes)
13
+ @communities = []
14
+
15
+ # whether the graph is diercted or not
16
+ @directed = false
17
+
18
+ # node_id => community_id
19
+ @n2c = {}
20
+
21
+ @level = level
22
+ # TODO remove sort
23
+ @adj_list.sort.each do |k, v|
24
+ @communities << Louvian::Community.new({k => v}, @level)
25
+ @n2c[k] = @communities.last.id
26
+ end
27
+
28
+ # Sum of all links half edges (double the number of edges)
29
+ @total_weight = @adj_list.inject(0) {|r,(k,v)| r+v.count}
30
+ end
31
+
32
+ # This method builds the adjacency list from list of edges
33
+ # @param list [Array] in the form of [src dest] (edge per cell)
34
+ # @param directed, whether the edge list is directed or not
35
+ def self.make_adj edges_list, directed
36
+ adj = {}
37
+ edges_list.each do |edge|
38
+ adj[edge[0]] ||= []
39
+ adj[edge[1]] ||= []
40
+
41
+ adj[edge[0]] << edge[1]
42
+ if not directed
43
+ adj[edge[1]] << edge[0]
44
+ end
45
+ end
46
+ adj
47
+ end
48
+
49
+ def get_neighbour_nodes node
50
+ neighbours = adj_list[node]
51
+ end
52
+
53
+ def get_neighbour_comms node
54
+ node_to_comms_links = get_node_to_comms_links node
55
+
56
+ neighbour_communities = @communities.find_all {|comm| node_to_comms_links.include? comm.id}
57
+ end
58
+
59
+ def get_community node
60
+ @communities.find {|comm| comm.id == @n2c[node]}
61
+ end
62
+
63
+ # This method gets all neighbour communities and the number of links from node
64
+ # to all neighbou communities
65
+ # @param node to be considered
66
+ # @return node_to_comms_links [Hash] {community => number_of_links from node
67
+ # to community}
68
+ def get_node_to_comms_links node
69
+ neighbour_nodes = get_neighbour_nodes node
70
+ node_to_comms_links = {}
71
+ neighbour_nodes.each do |n|
72
+ node_to_comms_links[@n2c[n]] = (node_to_comms_links[@n2c[n]] || 0) + 1
73
+ end
74
+ node_to_comms_links[@n2c[node]] ||= 0
75
+ return node_to_comms_links
76
+ end
77
+
78
+ # OPTIMIZE
79
+ def get_number_of_links from_node, to_comm
80
+ get_node_to_comms_links(from_node)[to_comm.id]
81
+ end
82
+
83
+ # This method calcualtes the current modularity of the communities
84
+ # @returns q [Float] which is the modularity
85
+ def modularity
86
+ q = 0.0
87
+ m2 = @total_weight
88
+
89
+ @communities.each do |m_node|
90
+ q += m_node.in.to_f/m2 - (m_node.tot.to_f/m2 * m_node.tot.to_f/m2)
91
+ end
92
+ q
93
+ end
94
+
95
+ # This method calcualtes the modularity gain for moving +node+ to community
96
+ # @param node this is the node to be moved
97
+ # @param community this is the destination community
98
+ # @param nb_links_to_comm is the number of links from +node+ to community
99
+ # @returns delta_q (the gain of modularity)
100
+ def modularity_gain node, community
101
+ nb_links_to_comm = get_number_of_links node, community
102
+ tot = community.tot
103
+ deg = @adj_list[node].count
104
+ m2 = @total_weight
105
+
106
+ #puts "\t\t\tcomm #{community.id} #{[tot, deg, m2, nb_links_to_comm]}"
107
+ # what makes sense
108
+ #return (nb_links_to_comm.to_f/m2) - (tot * deg.to_f/m2**2/2)
109
+
110
+ # copied from the cpp code
111
+ return nb_links_to_comm.to_f - tot*deg.to_f/m2
112
+ end
113
+
114
+ # This method outputs information about communities
115
+ def display_communities
116
+ @communities.each do |m|
117
+ puts "#{m.id} => #{m.nodes_ids} in=#{m.in} tot=#{m.tot}"
118
+ end
119
+ nil
120
+ end
121
+
122
+ def insert_node node, comm
123
+ comm.insert node, @adj_list[node]
124
+ @n2c[node] = comm.id
125
+ end
126
+
127
+ def remove_node node, comm
128
+ comm.remove node, @adj_list[node]
129
+ @n2c[node] = -1
130
+ end
131
+
132
+ def garbage_collect community
133
+ if community.nodes_ids.empty?
134
+ @communities.delete community
135
+ end
136
+ end
137
+
138
+ def build_graph_from_comms
139
+
140
+ comm_edges = []
141
+
142
+ count = @communities.count
143
+ if not directed # iterate only on one half of communities
144
+ count % 2 == 0 ? count : count + 1
145
+ count /=2
146
+ end
147
+
148
+ @communities[0,count].each do |comm|
149
+ comm.nodes_ids.each do |node|
150
+ @adj_list[node].each do |linked_node|
151
+ if not comm.nodes_ids.include? linked_node
152
+ comm_edges << [comm.id, @n2c[linked_node]]
153
+ end
154
+ end
155
+ end
156
+ end
157
+ return Louvian::Graph.new comm_edges, directed, @level+1
158
+ end
159
+ end
data/lib/louvian.rb CHANGED
@@ -25,7 +25,7 @@ module Louvian
25
25
  def self.run
26
26
  l = 0
27
27
  mod = @@graph.modularity
28
- begin
28
+ begin
29
29
  puts "Level #{l}: Comms #{@@graph.communities.size}"
30
30
  @@levels << @@graph
31
31
  @@graph = @@graph.build_graph_from_comms
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.3
4
+ version: 0.0.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -19,6 +19,7 @@ extra_rdoc_files: []
19
19
  files:
20
20
  - lib/louvian.rb
21
21
  - lib/louvian/community.rb
22
+ - lib/louvian/graph.rb
22
23
  homepage: http://rubygems.org/gems/louvian_ruby
23
24
  licenses: []
24
25
  post_install_message: