Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
*~

*.o
*.swp
10 changes: 10 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
CMAKE_MINIMUM_REQUIRED( VERSION 3.3.1 )
PROJECT( MLGkernel CXX )

SET( CMAKE_CXX_STANDARD 11 )
SET( CMAKE_CXX_STANDARD_REQUIRED ON )

FIND_PACKAGE( Eigen3 REQUIRED )
FIND_PACKAGE( Threads REQUIRED )

ADD_SUBDIRECTORY( MLGkernel )
19 changes: 19 additions & 0 deletions MLGkernel/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
FILE(GLOB SOURCES
*.cpp
../utility/*.cpp
../matrices/matrices.cpp
)

INCLUDE_DIRECTORIES(
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/matrices
${CMAKE_SOURCE_DIR}/utility
${CMAKE_SOURCE_DIR}/utility/filetypes
)

ADD_EXECUTABLE( runMLG
${SOURCES}
)

TARGET_INCLUDE_DIRECTORIES( runMLG SYSTEM PUBLIC ${EIGEN3_INCLUDE_DIR} )
TARGET_LINK_LIBRARIES( runMLG Threads::Threads )
12 changes: 10 additions & 2 deletions MLGkernel/FLGkernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,18 @@ double FLGkernel::operator()(const FLGinstance& x1, const FLGinstance& x2) const

Cvector lambda=(x1.Sinv+x2.Sinv).eigenvalues();
//double detS=1; for(int i=0; i<lambda.n; i++) detS*=lambda(i); detS=1.0/detS;
double log_detS=0; for(int i=0; i<lambda.n; i++) log_detS-=log(lambda(i));
double log_detS=0;
for(int i=0; i<lambda.n; i++)
{
if( isfinite( lambda(i) ) )
log_detS-=log(lambda(i));
}

auto det_x1 = isfinite( x1.log_detS ) ? x1.log_detS : 0.0;
auto det_x2 = isfinite( x2.log_detS ) ? x2.log_detS : 0.0;

//double r=sqrt(detS/sqrt(x1.detS*x2.detS));
double logr=(log_detS-0.5*(x1.log_detS+x2.log_detS))/2;
double logr=(log_detS-0.5*(det_x1 + det_x2))/2;
double r=0;
if(logr<-30){cout<<"Underflow!"<<endl;}
else r=exp(logr);
Expand Down
78 changes: 67 additions & 11 deletions MLGkernel/MLGdataset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@
#include "FLGkernel.hpp"
#include "MatrixOF_ASCII.hpp"
#include "params.hpp"

#include <string>
#include <unordered_map>
#include <set>

void MLGdataset::condense(const int nlevels, const int leaf_radius){
assert(nlevels>0);
Expand Down Expand Up @@ -100,9 +103,16 @@ void MLGdataset::loadGraphs(std::string filename){
int i=0;
int n;
ifs >> numGraphs;
while(ifs.good()){

graphs.reserve( numGraphs );

while( ifs ){
ifs>>n;
if(!ifs.good()) break;

// done with reading; skip!
if( !ifs )
break;

//cout<<"Reading graph "<<++i<<" (n="<<n<<")"<<endl;
Cmatrix A(n,n);
for(int j=0; j<n; j++){
Expand All @@ -114,27 +124,73 @@ void MLGdataset::loadGraphs(std::string filename){
assert(numGraphs == graphs.size());
}

void MLGdataset::loadDiscreteFeatures(std::string filename, int numFeatures){
void MLGdataset::loadDiscreteFeatures( std::string filename ) {
ifstream ifs(filename);
if(ifs.fail()){

if( !ifs ) {
cout << "Failed to open " << filename << "." << endl;
exit(0);
exit( -1 );
}

int numVertices = 0;
int graphIndex = 0;
int label;
int numGraphs;
ifs >> numGraphs;
while(ifs.good()){
ifs>>numVertices;
if(!ifs.good()) break;
for(int i=0; i<numVertices; i++){

// Store the position of the stream so that we can roll it back later
// on *after* having counted the number of features.
auto position = ifs.tellg();

// Stores the labels that are encountered. Since this is a set, the
// label count is guaranteed to be unique.
set<int> labels;

while( ifs ) {
ifs >> numVertices; // ignore this for now as we do not need it for counting

for( int i = 0; i < numVertices; i++ ) {
ifs >> label;
labels.insert( label );
}
}

int numFeatures = static_cast<int>( labels.size() );
cout << "Number of features: " << numFeatures << endl;

// Create a mapping of labels to indices. This makes it possible to
// load and handle graphs that have non-contiguous label sequences.

unordered_map<int, int> label_to_index;

{
int index = 0;
for( auto&& label : labels )
label_to_index[label] = index++;
}

// Reset the stream and convert the labels now into their graph
// representation.
ifs.clear();
ifs.seekg( position );

int graphIndex = 0;

while( ifs ) {
ifs >> numVertices;

if( !ifs )
break;

for( int i = 0; i < numVertices; i++ ) {
ifs >> label;

graphs[graphIndex]->labels[i] = Cvector::Zero(numFeatures+1);
graphs[graphIndex]->labels[i](label) = 1;
graphs[graphIndex]->labels[i](label_to_index[label]) = 1;
}

graphIndex++;
}

assert(graphs.size() == numGraphs);
assert(graphIndex == graphs.size());
}
Expand Down
2 changes: 1 addition & 1 deletion MLGkernel/MLGdataset.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class MLGdataset{
public:

void loadGraphs(std::string filename);
void loadDiscreteFeatures(std::string filename, int numFeatures);
void loadDiscreteFeatures(std::string filename);
void loadFeatures(std::string filename);
void saveGram(std::string filename);
void fillGram(double *npmatrix, int rows, int cols);
Expand Down
19 changes: 2 additions & 17 deletions MLGkernel/runMLG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,19 +61,6 @@ string genSaveName(string data, double eta, double gamma, int radius, int levels
return ss.str();
}

// These are the number of discrete node labels for each of the benchmark datasets.
int get_num_features(string features){
if(features.find("MUTAG") != string::npos) return 7;
if(features.find("PTC") != string::npos) return 22;
if(features.find("PROTEINS") != string::npos) return 3;
if(features.find("NCI109") != string::npos) return 38;
if(features.find("NCI1") != string::npos) return 37;

cout << "Supplied dataset is not one of the sample datasets! You can manually change this code to use the correct number of discrete features of your dataset." << endl;
exit(0);
return 0;
}

void runMLG(Params& p) {
threadManager.maxthreads = p.num_threads;
MLGdataset dataset(p.data_path, p.eta, p.gamma, p.grow_or_double);
Expand All @@ -83,10 +70,8 @@ void runMLG(Params& p) {
cout << "Computing degree features" << endl;
for(auto g: dataset.graphs) g->computeDegreeFeatures(20); // all sample datasets have max degree < 20
} else {
cout << "Computing discrete features" << endl;
int num_features = get_num_features(p.features_path);
cout << "num features: " << num_features << endl;
dataset.loadDiscreteFeatures(p.features_path, num_features);
cout << "Loading discrete features" << endl;
dataset.loadDiscreteFeatures(p.features_path);
}

dataset.computeGram(p.levels, p.radius);
Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,30 @@ R. Kondor, H. Pan, [The Multiscale Graph Laplacian](https://arxiv.org/abs/1603.0
* [Eigen](http://eigen.tuxfamily.org/index.php)

## Installation/Setup

### Automated installation using `CMake`

The project is configured to use `CMake` to provide a streamlined installation
experience. After installing `CMake` and `eigen3` using your favourite package
manager, the following commands are sufficient to compile the
executable:

```bash
$ mkdir build
$ cd build
$ cmake ../
$ make -j4
```

When using Mac OS X, we recommend the [Homebrew](https://brew.sh)
package manager for installing the dependencies:

```bash
$ brew install cmake eigen3
```

### Manual installation using `make`

Change the EIGENDIR variable Makefile.options to the path to your installation of
the Eigen library. Run the following command to create the runMLG executable in the MLGkernel directory.
```bash
Expand Down
4 changes: 2 additions & 2 deletions matrices/Cvector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,8 @@ class Cvector: public DenseVector, public Serializable{

public: // element access

FIELD& operator()(const int i){return array[i];}
FIELD operator()(const int i) const {return array[i];}
FIELD& operator()(const int i){ assert( i < n ); return array[i];}
FIELD operator()(const int i) const { assert( i < n ); return array[i];}

void (foreach)(std::function<void(const INDEX, FIELD&)> lambda) {for(int i=0; i<n; i++) lambda(i,array[i]);}
void (foreach)(std::function<void(const INDEX, const FIELD)> lambda) const {for(int i=0; i<n; i++) lambda(i,array[i]);}
Expand Down