Skip to content

Commit

Permalink
Merge pull request #6 from smu-cs-3353/Louvain
Browse files Browse the repository at this point in the history
Louvain
  • Loading branch information
Cry1is authored Apr 13, 2022
2 parents f8596ad + c347a51 commit 4195f33
Show file tree
Hide file tree
Showing 3 changed files with 202 additions and 5 deletions.
145 changes: 145 additions & 0 deletions src/Graph_helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,151 @@ void Graph_helper::girvan_newman_helper() {
}
}

void Graph_helper::louvain() {
// keeps track of total initial edges and nodes for later
numNodes = (int)boost::num_vertices(graph);
numEdges = (int)boost::num_edges(graph);

// keeps track of the original degree of each node for modularity calculation
auto vpair = boost::vertices(graph);
for(auto iter = vpair.first; iter != vpair.second; iter++) {
graph[*iter].origDegree = (int)boost::degree(*iter, graph);
}

best_mod = 0;
curr_mod = 0;

// copy the graph over to a temp variable to keep track of edges
Graph temp = graph;

// remove the edges from the class variable
auto epair = boost::edges(graph);
auto e = *(epair.first);
for(auto iter = epair.first; iter != epair.second;) {
e = *iter;
iter++;
boost::remove_edge(e.m_source, e.m_target, graph);
}

//put each node of G in its own community.
num_communities = boost::connected_components(graph, boost::make_assoc_property_map(max_comp));

// loop until the modularity no longer increases
while(true) {
// loop through all vertices and place each vertex in the neighboring
// community including its own which maximizes the modularity gain
louvain_helper(temp);

// check if the current modularity is greater than the previous best modularity
double mod = get_modularity();
if (mod > best_mod) {
best_mod = mod;
}
else
break;
}

// gets communities and prepares community report
std::vector<std::string> report(num_communities);
std::cout << "Best Modularity: " << best_mod << std::endl;
for(auto& c : max_comp) {
// std::cout << c.first << " in community " << c.second << std::endl;
report[c.second] += std::to_string(c.first);
report[c.second] += "-";
report[c.second] += std::to_string(graph[c.first].community);
report[c.second] += ", ";
}
print_report(report);
}

void Graph_helper::louvain_helper(Graph& temp) {
bool nodesMoved;
// loop while nodes are moved
do {
nodesMoved = false;
// grab all vertices from the graph
auto vpair = boost::vertices(graph);

// for all node n of G
for(vertexIt iter = vpair.first; iter != vpair.second; iter++) {
// get the current community of the current node
int this_comm = max_comp.find(*iter)->second;

// get the neighboring community which maximizes
// the modularity gain
int comm = join_nodes(iter, temp);

// if the community is found
if (comm != -1) {
// set nodesMoved to true
nodesMoved = true;

// refactor the node
clear_node(iter);
fill_node(iter, comm, temp);
}
else {
// otherwise refactor the node back to its previous community
clear_node(iter);
fill_node(iter, this_comm, temp);
}

num_communities = boost::connected_components(graph, boost::make_assoc_property_map(max_comp));
}
} while(nodesMoved);
}

int Graph_helper::join_nodes(vertexIt iter, Graph& temp) {
// get the neighbors of the current node
auto neighbors = boost::adjacent_vertices(*iter, temp);

// set the initial value of the new community (default -1)
int new_comm = -1;

curr_mod = get_modularity();

// loop through all of the neighbors
for (auto it = neighbors.first; it != neighbors.second; it++) {
// the current neighbor's community
int comm = max_comp.find(*it)->second;

// clear the previous node's community
clear_node(iter);

// add the node to the new community
fill_node(iter, comm, temp);

// get the modularity of the current state of the graph
double mod = get_modularity();

// if the new modularity is greater than the previous
// greatest, set curr_mod to the new modularity, and update
// the new community
if (mod > curr_mod) {
curr_mod = mod;
new_comm = comm;
}
}
return new_comm;
}

void Graph_helper::clear_node(vertexIt iter) {
auto neighbors = boost::adjacent_vertices(*iter, graph);
for (auto it = neighbors.first; it != neighbors.second;) {
// neighbor node
vd n = *it;
it++;
boost::remove_edge(*iter, n, graph);
}
}

void Graph_helper::fill_node(vertexIt iter, int community, Graph& temp) {
for (auto c = max_comp.begin(); c != max_comp.end(); c++) {
if (c->second == community && boost::edge(*iter, c->first, temp).second)
boost::add_edge(*iter, c->first, graph);
}
}

double Graph_helper::get_modularity () {
set_degree(); // to set current degrees for each vertex

Expand Down
35 changes: 34 additions & 1 deletion src/Graph_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <cxxabi.h>
#include <fstream>
#include <queue>
#include <float.h>



Expand All @@ -42,6 +43,7 @@ struct component {
typedef boost::adjacency_list<boost::setS, boost::vecS, boost::undirectedS, VertexProperty, EdgeProperty> Graph;
typedef boost::range_detail::integer_iterator<unsigned long> vertexIt;
typedef boost::graph_traits<Graph>::vertex_descriptor vd;
typedef boost::graph_traits<Graph>::edge_descriptor ed;


class Graph_helper {
Expand All @@ -50,7 +52,7 @@ class Graph_helper {
int numNodes;
int numEdges;
std::map<vd, int> max_comp;
double best_mod;
double best_mod, curr_mod;
int num_communities;

public:
Expand All @@ -70,6 +72,8 @@ class Graph_helper {
*/
void set_degree();

double comp_modularity(int, std::map<vd, int>);

/**
* Calculates the modularity of the current graph and components
* @return the modularity
Expand All @@ -90,6 +94,35 @@ class Graph_helper {
*/
void girvan_newman_helper();

/**
* The master function for the Louvain community detection algorithm. Uses change in
* modularity to determine which community each node belongs to.
*/
void louvain();

/**
* Loop for Louvain algorithm. Joins communities together based on modularity, leading to new
* partitions.
*/
void louvain_helper(Graph&);


/**
* Join communities that create a higher modularity.
*/
int join_nodes(vertexIt, Graph&);

/**
* clear a node's edges
*/
void clear_node(vertexIt);

/**
* fill a node with the edges for a given community, given
* a template graph
*/
void fill_node(vertexIt, int, Graph&);

/**
* Implementation of Breadth First Search in order to visit the graph by visiting each adjacent node
* before moving further. Keeps track of each previously visited node for every node.
Expand Down
27 changes: 23 additions & 4 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,32 @@
// Created by Zachary on 3/25/2022.
//
#include "Graph_helper.h"
#include <chrono>

int main(int argc, char** argv) {
using namespace std;

int main(int argc, char** argv) {
if(argc == 2) {
Graph_helper g;
g.read_graphml(argv[1]);
g.girvan_newman();
char* file = argv[1];
Graph_helper g1;
g1.read_graphml(file);
std::cout << "Girvan Newman: " << std::endl;
chrono::high_resolution_clock::time_point t1 = chrono::high_resolution_clock::now();
g1.girvan_newman();
chrono::high_resolution_clock::time_point t2 = chrono::high_resolution_clock::now();
chrono::duration<double> time = chrono::duration_cast<chrono::duration<double>>(t2 - t1);
std::cout << "Time: " << time.count() << " seconds" << std::endl;

std::cout << std::endl;

Graph_helper g2;
g2.read_graphml(file);
std::cout << "Louvian: " << std::endl;
t1 = chrono::high_resolution_clock::now();
g2.louvain();
t2 = chrono::high_resolution_clock::now();
time = chrono::duration_cast<chrono::duration<double>>(t2 - t1);
std::cout << "Time: " << time.count() << " seconds" << std::endl;
} else {
std::cout << "Please enter the correct amount of program arguments (2)" << std::endl;
}
Expand Down

0 comments on commit 4195f33

Please sign in to comment.