Skip to content

Commit

Permalink
Release 0.0.2: Generate and compile serial C++ code for SPNs;
Browse files Browse the repository at this point in the history
  • Loading branch information
sommerlukas committed Apr 5, 2019
2 parents 3d28062 + 38eddbb commit d3355f2
Show file tree
Hide file tree
Showing 53 changed files with 2,518 additions and 17 deletions.
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
## About `spnc` ##

`spnc` is a multi-target compiler for Sum-Product Networks,
a class of machine learning models.

`spnc` currently supports generation of serial C++ code for SPNs,
support for generating multi-threaded OpenMP code and CUDA code for
Nvidia GPUs is under way. Additionally, `spnc` allows to compute
statistics about an SPNs graph and output them to JSON.

### Prerequisites ###

`spnc` requires a recent version of Java to run and
at least one of `g++` or `clang++` to be installed on your machine.

### Installation ###

Either download one of the release zips from the github page or
compile `spnc` from source. To compile from source, simply run
`./gradlew installDist` in the root folder of `spnc` and use the executable
found at `build/install/spnc/bin/spnc`.

### Usage ###

`spnc` compiles SPNs from a textual representation
(see `src/main/resources/NIPS5.spn` for an example).

Running `spnc` with the desired input-file will generate an executable.
The executable contains an automatically generated `main`-method which will
read input-data from a plain-text file and, if a second file-name is given,
compare them to the reference data from the reference-file (also plain text).
The executable will also track the time spent to execute the SPN inference
for all examples in the input sample.

Run `spnc --help` to see additional options.

##### Limitations #####
`spnc` *currently does not yet support Poisson or Gaussian distributions.*
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ dependencies {
// https://github.com/scopt/scopt
compile group: 'com.github.scopt', name: 'scopt_2.12', version: '4.0.0-RC2'

// https://mvnrepository.com/artifact/org.wvlet.airframe/airframe-log
compile group: 'org.wvlet.airframe', name: 'airframe-log_2.12', version: '19.4.0'

}

apply plugin: 'application'
Expand Down
103 changes: 103 additions & 0 deletions src/main/resources/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <cmath>
#include <chrono>
#include <vector>
#include "spn.hpp"

#ifndef VALUES_PER_SAMPLE
#define VALUES_PER_SAMPLE 5
#endif

int* readInputSamples(char * inputfile, int * sample_count){
std::ifstream infile(inputfile);
std::string line;
std::vector<std::string> lines;
while(std::getline(infile, line)){
lines.push_back(line);
}

auto * input_data = (int*) malloc(VALUES_PER_SAMPLE * lines.size() * sizeof(int));
int sample_count_int = 0;
for(const std::string& s : lines){
std::istringstream stream(s);
std::string token;
int value_count = 0;
while(std::getline(stream, token, ';')){
std::istringstream value_string(token);
double value;
if(!(value_string >> value)){
std::cout << "ERROR: Could not parse double from " << token.c_str() << std::endl;
exit(-1);
}
input_data[sample_count_int*VALUES_PER_SAMPLE + value_count] = (int) value;
++value_count;
}
++sample_count_int;
}

std::cout << "Read " << sample_count_int << " input samples" << std::endl;
*sample_count = sample_count_int;
return input_data;
}

double* readReferenceValues(char * outputfile, int sample_count){
std::ifstream infile(outputfile);
std::string line;
auto * output_data = (double*) malloc(sample_count * sizeof(double));
int count = 0;
while(count < sample_count && std::getline(infile, line)){
std::istringstream value_string(line);
double value;
if(!(value_string >> value)){
std::cout << "ERROR: Could not parse double from " << line.c_str() << std::endl;
exit(-1);
}
output_data[count] = value;
++count;
}

std::cout << "Read " << count << " reference values" << std::endl;
return output_data;
}

int main(int argc, char ** argv) {
if(argc != 3){
std::cout << "Please provide input- and output-data file as first and second argument!" << std::endl;
exit(1);
}

int sample_count = 0;

void * input_data = readInputSamples(argv[1], &sample_count);
double result[sample_count];
for(int i=0; i<sample_count; ++i){
result[i] = 42.0;
}
double * reference_data = readReferenceValues(argv[2], sample_count);

auto begin = std::chrono::high_resolution_clock::now();

// TODO Kernel invocation
spn_toplevel(sample_count, (activation_t*) input_data, result);

auto end = std::chrono::high_resolution_clock::now();
int num_errors = 0;
std::cout << "Sample count: " << sample_count << std::endl;
for(int i=0; i<sample_count; ++i){
if(std::abs(std::log(result[i])-reference_data[i])>1e-6){
std::cout << "ERROR: Significant deviation @" << i << ": " << std::log(result[i]) << " (" << result[i] << ") " << " vs. " << reference_data[i] << std::endl;
++num_errors;
}
}
if(num_errors==0){
std::cout << "COMPUTATION OK" << std::endl;
}

std::cout << std::setprecision(15)<< "time per instance " << std::chrono::duration_cast<std::chrono::microseconds>(end-begin).count() / (double) sample_count << " us" << std::endl;
std::cout << std::setprecision(15) << "time per task " << std::chrono::duration_cast<std::chrono::microseconds>(end-begin).count() << " us" << std::endl;

return 0;
}
6 changes: 6 additions & 0 deletions src/main/resources/simple.spn
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
SumNode_0 SumNode(0.5*Histogram1, 0.5*Histogram2){
Histogram1 Histogram(var1|[0., 1., 5.];[0.1,0.2])
Histogram2 Histogram(var2|[0., 3., 7.];[0.3,0.4])
}

# var1;var2
Loading

0 comments on commit d3355f2

Please sign in to comment.