diff --git a/CMakeLists.txt b/CMakeLists.txt index e79de44..808d7d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,18 +1,40 @@ -cmake_minimum_required(VERSION 2.8.12) -project(ProPCA) +cmake_minimum_required(VERSION 3.15) +project(ProPCA VERSION 1.0.0 LANGUAGES CXX) +include(GNUInstallDirs) +set(default_build_type "Release") +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG=1") -include_directories(include/) +cmake_policy(SET CMP0069 NEW) +include(CheckIPOSupported) +check_ipo_supported(RESULT iporesult) -set(CMAKE_CXX_FLAGS "-std=c++0x -O3 -lrt") +set(CMAKE_CXX_STANDARD 11) +find_package(Threads REQUIRED) +set(ARCH "haswell" CACHE STRING "Architecture to tell gcc to optimize for (-march)") +set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -march=${ARCH} -mtune=${ARCH}") +option(PROPCA_ASAN "Enable (debug) address sanitizer build" OFF) +option(PROPCA_TSAN "Enable (debug) Thread sanitizer build" OFF) +add_executable(propca src/propca.cpp src/genotype.cpp src/storage.cpp) +target_include_directories(propca PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(propca Threads::Threads) +if(iporesult) + message (STATUS "IPO supported for project ${PROJECT_NAME}") + set_property(TARGET propca PROPERTY CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -flto") +endif() -IF (NOT DEFINED SSE_SUPPORT) - SET(SSE_SUPPORT 0) -ENDIF() -ADD_DEFINITIONS(-DSSE_SUPPORT=${SSE_SUPPORT}) -IF (NOT DEFINED DEBUG) - SET(DEBUG 0) -ENDIF() -ADD_DEFINITIONS(-DDEBUG=${DEBUG}) -add_executable(propca src/propca.cpp src/genotype.cpp src/storage.cpp) +if(PROPCA_ASAN) + set(CMAKE_BUILD_TYPE "Debug") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") +endif() + + +if(PROPCA_TSAN) + set(CMAKE_BUILD_TYPE "RelWithDebInfo") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=thread") +endif() + + +install(TARGETS propca) diff --git a/README.md b/README.md index 9f85475..11610f6 100644 --- a/README.md +++ b/README.md @@ -26,32 +26,25 @@ make ### Installing -Installing ProPCA is fairly simple. Just issue the following commands on a linux machine +Installing ProPCA is fairly simple. Just issue the following commands on a Unix-alike machine ``` git clone https://github.com/sriramlab/ProPCA.git cd ProPCA -mkdir build -cd build +cmake -S . -B build +cmake --build build ``` -By default, the release version is built, if you wish to build the debug version, build with cmake flag `-D DEBUG=1` as shown below. - -ProPCA supports, [SSE](https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions) instructions. - -If your architecure is Intel and supports SSE instructions, build with the cmake flag `-D SSE_SUPPORT=1` for an faster improved version as shown below. - +By default, the release version is built, if you wish to build the debug version, build with cmake flag `-DCMAKE_BUILD_TYPE=Debug` as shown below. ``` -cmake -D SSE_SUPPORT=1 -D DEBUG=1 .. -make +cmake -S . -B Debug -DCMAKE_BUILD_TYPE=Debug +cmake --build Debug ``` -Else just issue these commands below: +ProPCA supports, [SSE](https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions) instructions. + +If your architecure is Intel and supports SSE instructions, the C++ compiler will automatically detect this for a faster version of the algorithm. -``` -cmake .. -make -``` diff --git a/examples/par.txt b/examples/par.txt index 1b3fa0d..093885a 100644 --- a/examples/par.txt +++ b/examples/par.txt @@ -9,4 +9,5 @@ output_path = example_ accelerated_em = 1 var_normalize = true memory_efficient = false +text_version = true fast_mode = false diff --git a/include/arguments.h b/include/arguments.h index ff3dfb7..d03381f 100644 --- a/include/arguments.h +++ b/include/arguments.h @@ -6,7 +6,7 @@ #include #include #include -#include + using namespace std; @@ -47,7 +47,7 @@ struct is_same { extern options command_line_opts; -void exitWithError(const std::string &error) { +inline void exitWithError(const std::string &error) { std::cout << error; std::cin.ignore(); std::cin.get(); @@ -191,7 +191,7 @@ class ConfigFile{ } }; -void parse_args(int argc, char const *argv[]){ +inline void parse_args(int argc, char const *argv[]){ // Setting Default Values command_line_opts.num_of_evec=5; diff --git a/include/genotype.h b/include/genotype.h index 828f1d1..021c55d 100644 --- a/include/genotype.h +++ b/include/genotype.h @@ -1,64 +1,65 @@ #ifndef GENOTYPE_H #define GENOTYPE_H -#include + #include "storage.h" -#include -#include -#include -#include +#include +#include "Eigen/Dense" +#include "Eigen/Core" +#include "Eigen/LU" +#include "Eigen/SVD" class genotype { - std::vector< std::vector > msb; - std::vector< std::vector > lsb; - std::vector columnsum; - std::vector columnmeans; + std::vector< std::vector > msb; + std::vector< std::vector > lsb; + std::vector columnsum; + std::vector columnmeans; - public: +public: - unsigned char mask; - int wordsize; - unsigned int unitsperword; - int unitsize; - int nrow, ncol; - unsigned char *gtype; + unsigned char mask; + int wordsize; + unsigned int unitsperword; + int unitsize; + int nrow, ncol; + unsigned char *gtype; - int Nsnp, Nindv, Nsegments_hori, segment_size_hori, segment_size_ver, Nsegments_ver; - int Nbits_hori, Nbits_ver; - int Nelements_hori, Nelements_ver; - std::vector< std::vector > p; + int Nsnp, Nindv, Nsegments_hori, segment_size_hori, segment_size_ver, Nsegments_ver; + int Nbits_hori, Nbits_ver; + int Nelements_hori, Nelements_ver; + std::vector< std::vector > p; - // std::vector< std::vector > p_eff; - // std::vector< std::vector > q_eff; + // std::vector< std::vector > p_eff; + // std::vector< std::vector > q_eff; - std::vector< std::vector > not_O_j; - std::vector< std::vector > not_O_i; + std::vector< std::vector > not_O_j; + std::vector< std::vector > not_O_i; - void init_means(bool is_missing); + void init_means(bool is_missing); - float get_observed_pj(const std::string &line); - float get_observed_pj(const unsigned char* line); + float get_observed_pj(const std::string &line); + float get_observed_pj(const unsigned char* line); - void read_txt_naive(std::string filename,bool allow_missing); + void read_txt_naive(std::string filename,bool allow_missing); - void read_txt_mailman (std::string filename,bool allow_missing); + void read_txt_mailman (std::string filename,bool allow_missing); - void read_plink (std::string filenameprefix, bool allow_missing,bool mailman_mode); - void read_bed (std::string filename, bool allow_missing, bool mailman_mode ) ; - void read_bed_mailman (std::string filename,bool allow_missing ) ; - void read_bed_naive (std::string filename ,bool allow_missing) ; + void read_plink (std::string filenameprefix, bool allow_missing,bool mailman_mode); + void read_bed (std::string filename, bool allow_missing, bool mailman_mode ) ; + void read_bed_mailman (std::string filename,bool allow_missing ) ; + void read_bed_naive (std::string filename ,bool allow_missing) ; - int countlines(std::string filename); - void set_metadata () ; + int countlines(std::string filename); + void set_metadata () ; - double get_geno(int snpindex,int indvindex,bool var_normalize); - double get_col_mean(int snpindex); - double get_col_sum(int snpindex); - double get_col_std(int snpindex); - void update_col_mean(int snpindex,double value); + double get_geno(int snpindex,int indvindex,bool var_normalize); + double get_col_mean(int snpindex); + double get_col_sum(int snpindex); + double get_col_std(int snpindex); + void update_col_mean(int snpindex,double value); - void generate_eigen_geno(Eigen::Matrix &geno_matrix,bool var_normalize); + void generate_eigen_geno(Eigen::Matrix &geno_matrix,bool var_normalize); }; diff --git a/include/helper.h b/include/helper.h index f9c7926..1e6e091 100644 --- a/include/helper.h +++ b/include/helper.h @@ -1,15 +1,17 @@ #ifndef HELPER_H #define HELPER_H -#include + #include "time.h" +#include +#include using namespace std; extern struct timespec t0; -struct timespec elapsed (){ +inline struct timespec elapsed (){ struct timespec ts; clock_gettime (CLOCK_REALTIME, &ts); if (ts.tv_nsec < t0.tv_nsec){ @@ -20,12 +22,12 @@ struct timespec elapsed (){ return (ts); } -int timelog (const char* message){ +inline int timelog (const char* message){ struct timespec ts = elapsed (); return (printf ("[%06ld.%09ld] %s\n", ts.tv_sec, ts.tv_nsec, message)); } -void * malloc_double_align(size_t n, unsigned int a /*alignment*/, double *& output){ +inline void * malloc_double_align(size_t n, unsigned int a /*alignment*/, double *& output){ void *adres=NULL; void *adres2=NULL; adres=malloc(n*sizeof(double)+a); @@ -36,17 +38,17 @@ void * malloc_double_align(size_t n, unsigned int a /*alignment*/, double *& out return adres; // pointer to be used in free() } -void print_timenl () { +inline void print_timenl () { clock_t c = clock(); double t = double(c) / CLOCKS_PER_SEC; cout << "Time = " << t << endl ; } -void print_time () { +inline void print_time () { clock_t c = clock(); double t = double(c) / CLOCKS_PER_SEC; cout << "Time = " << t << " : "; } -#endif \ No newline at end of file +#endif diff --git a/include/mailman.h b/include/mailman.h index bdb84b1..1438006 100644 --- a/include/mailman.h +++ b/include/mailman.h @@ -1,15 +1,14 @@ #ifndef MAILMAN_H #define MAILMAN_H -#include -#include -#include -#include -#include +#include "Eigen/Dense" +#include "Eigen/Core" +#include "Eigen/LU" +#include "Eigen/SVD" #include "storage.h" #include #include - +#include namespace mailman { @@ -23,7 +22,7 @@ namespace mailman { * c : intermediate computation * y : result */ - void fastmultiply_normal(int m, int n , int k, std::vector &p, Eigen::Matrix &x, double *yint, double *c, double **y){ +inline void fastmultiply_normal(int m, int n , int k, std::vector &p, Eigen::Matrix &x, double *yint, double *c, double **y){ for (int i = 0 ; i < n; i++) { int l = p[i] ; for (int j = 0 ; j < k ; j ++) @@ -68,7 +67,7 @@ namespace mailman { * c : intermediate computation * y : result. also contains Y_0 that is updated. */ - void fastmultiply_pre_normal(int m, int n , int k, int start, std::vector &p, Eigen::Matrix &x, double *yint, double *c, double **y){ +inline void fastmultiply_pre_normal(int m, int n , int k, int start, std::vector &p, Eigen::Matrix &x, double *yint, double *c, double **y){ int size1 = pow(3.,m); memset (yint, 0, size1* sizeof(double)); @@ -106,7 +105,7 @@ namespace mailman { * c : intermediate computation * y : result */ - #if SSE_SUPPORT==1 + #ifdef __SSE__ // k must be a multiple of 10 void fastmultiply_sse (int m, int n , int k, std::vector &p, Eigen::Matrix &x, double *yint, double *c, double **y){ __m128d x0, x2, x4, x6, x8; diff --git a/include/storage.h b/include/storage.h index 396f89c..2a9e20f 100644 --- a/include/storage.h +++ b/include/storage.h @@ -1,10 +1,10 @@ #ifndef STORAGE_H #define STORAGE_H -#include +#include void add_to_arr(int x, int j, int beta,std::vector &arr); int extract_from_arr(int j,int beta,std::vector &arr); std::vector get_orig_arr(int beta,std::vector &arr,int Nelements); -#endif \ No newline at end of file +#endif diff --git a/src/genotype.cpp b/src/genotype.cpp index 4af9d1f..6985d60 100644 --- a/src/genotype.cpp +++ b/src/genotype.cpp @@ -1,6 +1,8 @@ -#include + #include "genotype.h" #include "storage.h" +#include +#include using namespace std; @@ -50,7 +52,7 @@ float genotype::get_observed_pj(const unsigned char* line){ } for ( int l = 0 ; l < lmax; l++){ int j = j0 + l ; - int ver_seg_no = j/segment_size_ver ; + //int ver_seg_no = j/segment_size_ver ; // Extract PLINK coded genotype and convert into 0/1/2 // PLINK coding: // 00->0 @@ -278,6 +280,8 @@ void genotype::read_bed_mailman (string filename,bool allow_missing) { binary_read(ifs,magic); + + segment_size_hori = floor(log(Nindv)/log(3)) - 2 ; Nsegments_hori = ceil(Nsnp*1.0/(segment_size_hori*1.0)); p.resize(Nsegments_hori,std::vector(Nindv)); @@ -315,7 +319,7 @@ void genotype::read_bed_mailman (string filename,bool allow_missing) { } for ( int l = 0 ; l < lmax; l++){ int j = j0 + l ; - int ver_seg_no = j/segment_size_ver ; + //int ver_seg_no = j/segment_size_ver ; // Extract PLINK coded genotype and convert into 0/1/2 // PLINK coding: // 00->0 @@ -514,128 +518,3 @@ void genotype::update_col_mean(int snpindex,double value){ columnmeans[snpindex] = value; } - - -/* Redundant Function -void genotype::read_genotype_eff (std::string filename,bool allow_missing){ - FILE* fp; - fp= fopen(filename.c_str(),"r"); - int j=0; - int i=0; - char ch; - // Calculating the sizes and other stuff for genotype matrix - int rd = fscanf(fp,"%d %d\n",&Nsnp,&Nindv); - segment_size_hori = ceil(log(Nindv)/log(3)); - segment_size_ver = ceil(log(Nsnp)/log(3)); - Nsegments_hori = ceil(Nsnp*1.0/(segment_size_hori*1.0)); - Nsegments_ver = ceil(Nindv*1.0/(segment_size_ver*1.0)); - Nbits_hori = ceil(log2(pow(3,segment_size_hori))); - Nbits_ver = ceil(log2(pow(3,segment_size_ver))); - Nelements_hori = floor( (Nindv * Nbits_hori *1.0) / 32) + 1; - Nelements_ver = floor( (Nsnp * Nbits_ver*1.0) / 32) + 1; - cout<(Nelements_hori)); - q_eff.resize(Nsegments_ver,std::vector(Nelements_ver)); - int sum=0; - if(allow_missing){ - not_O_i.resize(Nsnp); - not_O_j.resize(Nindv); - } - - do{ - int rd = fscanf(fp,"%c",&ch); - if(ch=='\n'){ - i++; - columnsum.push_back(sum); - sum=0; - j=0; - } - else{ - int val = int(ch-'0'); - int horiz_seg_no = i/segment_size_hori ; - int ver_seg_no = j/segment_size_ver ; - if(val==0){ - int temp = 3* extract_from_arr(j,Nbits_hori,p_eff[horiz_seg_no]); - add_to_arr(temp,j,Nbits_hori,p_eff[horiz_seg_no]); - add_to_arr(3*extract_from_arr(i,Nbits_ver,q_eff[ver_seg_no]),i,Nbits_ver,q_eff[ver_seg_no]); - } - else if(val==1){ - sum+=1; - int temp = 3* extract_from_arr(j,Nbits_hori,p_eff[horiz_seg_no]) + 1; - add_to_arr(temp,j,Nbits_hori,p_eff[horiz_seg_no]); - add_to_arr(3*extract_from_arr(i,Nbits_ver,q_eff[ver_seg_no]) + 1,i,Nbits_ver,q_eff[ver_seg_no]); - } - else if(val==2){ - sum+=2; - int temp = 3* extract_from_arr(j,Nbits_hori,p_eff[horiz_seg_no]) + 2; - add_to_arr(temp,j,Nbits_hori,p_eff[horiz_seg_no]); - add_to_arr(3*extract_from_arr(i,Nbits_ver,q_eff[ver_seg_no]) + 2,i,Nbits_ver,q_eff[ver_seg_no]); - } - else if(val==9 && allow_missing){ - int temp = 3* extract_from_arr(j,Nbits_hori,p_eff[horiz_seg_no]); - add_to_arr(temp,j,Nbits_hori,p_eff[horiz_seg_no]); - add_to_arr(3*extract_from_arr(i,Nbits_ver,q_eff[ver_seg_no]),i,Nbits_ver,q_eff[ver_seg_no]); - not_O_i[i].push_back(j); - not_O_j[j].push_back(i); - } - else{ - cout<<"Invalid entry in Genotype Matrix"< +// #include #include #include #include @@ -11,6 +11,7 @@ #include #include "time.h" #include +#include #include #include "genotype.h" @@ -19,12 +20,14 @@ #include "helper.h" #include "storage.h" -#if SSE_SUPPORT==1 - #define fastmultiply fastmultiply_sse - #define fastmultiply_pre fastmultiply_pre_sse +#ifdef __SSE__ +#define SSE_SUPPORT 1 +#define fastmultiply fastmultiply_sse +#define fastmultiply_pre fastmultiply_pre_sse #else - #define fastmultiply fastmultiply_normal - #define fastmultiply_pre fastmultiply_pre_normal +#define SSE_SUPPORT 0 +#define fastmultiply fastmultiply_normal +#define fastmultiply_pre fastmultiply_pre_normal #endif using namespace Eigen; @@ -232,7 +235,10 @@ void multiply_y_post_fast(MatrixXdr &op_orig, int Nrows_op, MatrixXdr &res,bool std::thread th [nthreads]; int perthread = g.Nsegments_hori/nthreads; -// cout << "post: " << g.segment_size_hori << "\t" << g.Nsegments_hori << "\t" << nthreads << "\t" << perthread << endl; + + #if DEBUG + // cout << "post: " << g.segment_size_hori << "\t" << g.Nsegments_hori << "\t" << nthreads << "\t" << perthread << endl; + #endif int t = 0; for (; t < nthreads - 1; t++) { // cout << "Launching " << t << endl; diff --git a/src/storage.cpp b/src/storage.cpp index feda67f..a700712 100644 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -1,5 +1,5 @@ #include "storage.h" -#include +#include void add_to_arr(int x, int j, int beta,std::vector &arr){