Skip to content

Commit

Permalink
Add random betweenness modules
Browse files Browse the repository at this point in the history
  • Loading branch information
AlainKadar committed Sep 26, 2024
1 parent cac3027 commit 7afb007
Show file tree
Hide file tree
Showing 6 changed files with 319 additions and 0 deletions.
66 changes: 66 additions & 0 deletions BoundaryBetweennessCast.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#include <igraph.h>
#include <stdio.h>
#include <stdlib.h>
#include "BoundaryBetweennessCast.h"
#include "Util.h"

#include <chrono>

#if IGRAPH_INTEGER_SIZE==64
typedef int64_t IG_LONG;
#elif IGRAPH_INTEGER_SIZE==32
typedef int32_t IG_LONG;
#endif

namespace interface {

// Default constructor
BoundaryBetweennessCast::BoundaryBetweennessCast () {}

BoundaryBetweennessCast::~BoundaryBetweennessCast () {}

void BoundaryBetweennessCast::boundary_betweenness_compute () {
//printf("Begin\n");
igraph_t* g = (igraph_t*)this->G_ptr;

num_edges = igraph_ecount(g);

igraph_vector_int_t sources_vec, targets_vec;
igraph_vector_t res, weights_vec;
igraph_vector_init(&res, num_edges);
igraph_vs_t ig_sources, ig_targets;

igraph_integer_t* sources_arr = (IG_LONG*)sources_ptr;
igraph_integer_t* targets_arr = (IG_LONG*)targets_ptr;
igraph_vector_int_init_array(&sources_vec, sources_arr, sources_len);
igraph_vector_int_init_array(&targets_vec, targets_arr, targets_len);
igraph_vs_vector(&ig_sources, &sources_vec);
igraph_vs_vector(&ig_targets, &targets_vec);

igraph_real_t* weights_arr = (double *)weights_ptr;
igraph_vector_init_array(&weights_vec, weights_arr, num_edges);

//printf("Running\n");
std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
igraph_edge_betweenness_subset(g,
&res, /*igraph_vector_t *res*/
igraph_ess_all(IGRAPH_EDGEORDER_ID), /*igraph_es_t eids*/
false, /*igraph_bool_t directed*/
ig_sources, /*igraph_vs_t sources*/
ig_targets, /*igraph_vs_t targets*/
&weights_vec); /*igraph_vector_t *weights*/
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
// printf("Complete in %lld\n", std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count());

betweennesses <<= res;

igraph_vector_int_destroy(&sources_vec);
igraph_vector_int_destroy(&targets_vec);
igraph_vs_destroy(&ig_sources);
igraph_vs_destroy(&ig_targets);
igraph_destroy(g);
}



}
25 changes: 25 additions & 0 deletions BoundaryBetweennessCast.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include <vector>

#ifndef BOUNDARYBETWEENNESSCAST_H
#define BOUNDARYBETWEENNESSCAST_H

namespace interface {
class BoundaryBetweennessCast {
public:
void* G_ptr;
long long* sources_ptr;
long long* targets_ptr;
//std::vector<long long> sources;
//std::vector<long long> targets;
double* weights_ptr;
int sources_len;
int targets_len;
BoundaryBetweennessCast();
~BoundaryBetweennessCast();
void boundary_betweenness_compute();
int num_edges;
std::vector<float> betweennesses;
};
}

#endif
21 changes: 21 additions & 0 deletions BoundaryBetweennessCast.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from libcpp.vector cimport vector

cdef extern from "BoundaryBetweennessCast.cpp":
pass

# Declare the class with cdef
cdef extern from "BoundaryBetweennessCast.h" namespace "interface":
cdef cppclass BoundaryBetweennessCast:
void* G_ptr
long long* sources_ptr
long long* targets_ptr
#vector[long long] sources
#vector[long long] targets
double* weights_ptr
int sources_len
int targets_len
vector[float] betweennesses
int num_edges
BoundaryBetweennessCast() except +
void boundary_betweenness_compute() except +

161 changes: 161 additions & 0 deletions RandomBoundaryBetweennessCast.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
#include <igraph.h>
#include <stdio.h>
#include <stdlib.h>
#include "Util.h"
#include <Eigen/Dense>
#include "RandomBoundaryBetweennessCast.h"

#include <iostream>
namespace interface {

// Default constructor
RandomBoundaryBetweennessCast::RandomBoundaryBetweennessCast () {}

RandomBoundaryBetweennessCast::~RandomBoundaryBetweennessCast () {}


void RandomBoundaryBetweennessCast::random_boundary_betweenness_compute () {
igraph_t* g = (igraph_t*)this->G_ptr;
int num_verts = igraph_vcount(g);
num_edges = igraph_ecount(g);

igraph_vector_t weights_vec;
igraph_real_t* weights_arr = (double *)weights_ptr;
igraph_vector_init_array(&weights_vec, weights_arr, num_edges);

/*Prepare Laplacian as Eigen Array*/
igraph_sparsemat_t A, compA;
igraph_sparsemat_t L, compL;
igraph_sparsemat_init(&L, num_verts, num_verts, num_verts*2*6);
igraph_sparsemat_init(&A, num_verts, num_verts, num_verts*2*6);
igraph_get_laplacian_sparse(g, &L, IGRAPH_ALL,
IGRAPH_LAPLACIAN_UNNORMALIZED, &weights_vec);
igraph_get_adjacency_sparse(g, &A, IGRAPH_GET_ADJACENCY_BOTH,
&weights_vec, IGRAPH_NO_LOOPS);
igraph_sparsemat_compress(&L, &compL);
igraph_sparsemat_compress(&A, &compA);
igraph_sparsemat_iterator_t mit;
Eigen::MatrixXf eigL = Eigen::MatrixXf::Zero(num_verts, num_verts);

int row, col;
float val;
igraph_sparsemat_iterator_init(&mit, &compL);
for (int i=0; i<(num_edges*2+num_verts); i++) {

row = igraph_sparsemat_iterator_row(&mit);
col = igraph_sparsemat_iterator_col(&mit);
val = igraph_sparsemat_iterator_get(&mit);

eigL(row, col) = val;
igraph_sparsemat_iterator_next(&mit);
}

/*Invert Laplacian*/
std::chrono::steady_clock::time_point begin, end;
begin = std::chrono::steady_clock::now();
Eigen::MatrixXf pinv(num_verts, num_verts);
pinv = eigL.completeOrthogonalDecomposition().pseudoInverse();
end = std::chrono::steady_clock::now();
printf("Inversion complete in %lld\n", std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count());
//std::cout << pinv << "\n";

/*LINEAR RANDOM BOUNDARY BETWEENNESS*/
begin = std::chrono::steady_clock::now();
std::vector<float> V(num_verts, 0);
linear_betweennesses.resize(num_edges);
igraph_integer_t from, to;
//Old method
/*
for (int s=0; s<sources_len; s++) {
for (int t=0; t<targets_len; t++) {
for (int i=0; i< num_verts; i++) {
V[i] = pinv(i, sources[s]) - pinv(i, targets[t]);
}
for (int i=0; i<num_edges; i++) {
igraph_edge(g, i, &from, &to);
linear_betweennesses[i] += abs(V[int(from)] - V[int(to)])*igraph_sparsemat_get(&A, from, to);
}
}
}
end = std::chrono::steady_clock::now();
printf("Linear Complete in %lld\n", std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count());
*/
//New method
float V_from, V_to;
for (int e=0; e<num_edges; e++) {
igraph_edge(g, e, &from, &to);
for (int s=0; s<sources_len; s++) {
for (int t=0; t<targets_len; t++) {
V_from = pinv(from, sources[s]) - pinv(from, targets[t]);
V_to = pinv(to, sources[s]) - pinv(to, targets[t]);
linear_betweennesses[e] += abs(V_from-V_to)*igraph_sparsemat_get(&A, from, to);
}
}
}

/*NONLINEAR RANDOM BOUNDARY BETWEENNESS*/
/*Generate voltage vector*/
/*(Faster than matrix vector multiplication because we know the
* location of non-zero elements a priori)
* Note that, for nonlinear random betweenness, the target vertex has
* changed to the ghost vertex*/
begin = std::chrono::steady_clock::now();
float sum = 0;
std::fill(V.begin(), V.end(), 0);
for (int i=0; i<num_verts; i++) {
for (int s=0; s<sources_len; s++) {
V[i] += pinv(i, sources[s])*incoming[s];
sum += incoming[s];
}
V[i] -= pinv(i, num_verts-1)*sum;
}
/*Here, from/to refer to the edge endpoints; not the source/targets used
* to calculate the betweenness subset.
*/
nonlinear_betweennesses.resize(num_edges);
bool skip = false;
for (int i=0; i<num_edges; i++) {
igraph_edge(g, i, &from, &to);
//Skip edges connecting sources
/*
for (int s1=0; s1<sources_len; s1++) {
for (int s2=0; s2<sources_len; s2++) {
//printf("%i,%i,%i,%i\n",s1,s2,int(from),int(to));
if (((int(from) == sources[s1] && int(to) == sources[s2]) ||
(int(from) == sources[s2] && int(to) == sources[s1])) &&
!skip) {
skip = true;
break;
}
}
}
//Skip edges connecting targets
for (int t1=0; t1<targets_len; t1++) {
for (int t2=0; t2<targets_len; t2++) {
//printf("%i,%i,%i,%i\n",s1,s2,int(from),int(to));
if (((int(from) == targets[t1] && int(to) == targets[t2]) ||
(int(from) == targets[t2] && int(to) == targets[t1])) &&
!skip) {
skip = true;
break;
}
}
}
//Reset the skip flag
if (skip) {
skip = false;
continue;
}
*/
/*So if edge neither connects two sources nor two targets, do this...*/
nonlinear_betweennesses[i] = abs(V[int(from)] - V[int(to)])*igraph_sparsemat_get(
&A, from, to);
}
end = std::chrono::steady_clock::now();
printf("NL Complete in %lld\n", std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count());
igraph_destroy(g);
}



}
25 changes: 25 additions & 0 deletions RandomBoundaryBetweennessCast.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include <vector>

#ifndef RANDOMBOUNDARYBETWEENNESSCAST_H
#define RANDOMBOUNDARYBETWEENNESSCAST_H

namespace interface {
class RandomBoundaryBetweennessCast {
public:
void* G_ptr;
double* weights_ptr;
std::vector<int> sources;
std::vector<int> targets;
std::vector<float> incoming;
int sources_len;
int targets_len;
RandomBoundaryBetweennessCast();
~RandomBoundaryBetweennessCast();
void random_boundary_betweenness_compute();
int num_edges;
std::vector<float> linear_betweennesses;
std::vector<float> nonlinear_betweennesses;
};
}

#endif
21 changes: 21 additions & 0 deletions RandomBoundaryBetweennessCast.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from libcpp.vector cimport vector

cdef extern from "RandomBoundaryBetweennessCast.cpp":
pass

# Declare the class with cdef
cdef extern from "RandomBoundaryBetweennessCast.h" namespace "interface":
cdef cppclass RandomBoundaryBetweennessCast:
void* G_ptr
double* weights_ptr
vector[int] sources
vector[int] targets
vector[float] incoming
int sources_len
int targets_len
vector[float] linear_betweennesses
vector[float] nonlinear_betweennesses
int num_edges
RandomBoundaryBetweennessCast() except +
void random_boundary_betweenness_compute() except +

0 comments on commit 7afb007

Please sign in to comment.