From 82b97e0e73cba01b1acf7857591457540d2bb0d1 Mon Sep 17 00:00:00 2001 From: Lingfei Wang Date: Mon, 5 Feb 2018 13:10:54 +0000 Subject: [PATCH] 1.0.6: Corrected a bug that may produce biased output in pij_gassist, pij_gassist_trad, and pijs_gassist when nodiag is set or when memlimit is small so computation is split into chunks. Now setting histogram bounds based on the maximum of all LLRs (as opposed to the maximum of the chunk when memlimit is small) in pij_gassist, pij_gassist_trad, and pijs_gassist. This ensures the output is independent of memlimit (related to question from sritchie73@github). Added sanity checks for agreement between input data and nodiag flag for pij functions excluding _pv (suggested by sritchie73@github). Lots of internal function renaming and code restructuring. Removed some unneeded files and functions. Now support pkg-config setup. --- COPYRIGHT | 2 +- Makefile | 44 ++-- UPDATES | 11 +- base/config.h | 2 +- base/const.h | 2 +- base/data_process.c | 68 +++++- base/data_process.h | 27 ++- base/data_struct.h | 2 +- base/data_struct_heap.c | 2 +- base/data_struct_heap.h | 2 +- base/data_struct_ll.c | 2 +- base/data_struct_ll.h | 2 +- base/general_alg.c | 102 --------- base/general_alg.h | 64 +----- base/gsl/blas.h | 2 +- base/gsl/cdf.h | 2 +- base/gsl/errno.h | 2 +- base/gsl/histogram.h | 2 +- base/gsl/math.h | 2 +- base/gsl/matrix.h | 2 +- base/gsl/permutation.h | 2 +- base/gsl/randist.h | 2 +- base/gsl/rng.h | 2 +- base/gsl/sf.h | 2 +- base/gsl/sort.h | 2 +- base/gsl/statistics.h | 2 +- base/gsl/vector.h | 2 +- base/histogram.c | 2 +- base/histogram.h | 20 +- base/lib.c | 2 +- base/lib.h | 2 +- base/logger.c | 2 +- base/logger.h | 2 +- base/macros.h | 2 +- base/math.c | 2 +- base/math.h | 2 +- base/os.h | 2 +- base/random.c | 2 +- base/random.h | 2 +- base/supernormalize.c | 2 +- base/supernormalize.h | 2 +- base/threading.h | 2 +- base/types.h | 2 +- cycle/cycle.h | 2 +- cycle/vg.c | 2 +- cycle/vg.h | 2 +- doc.pdf | Bin 223594 -> 236876 bytes external/R.c | 2 +- netr/one.c | 2 +- netr/one.h | 2 +- pij/cassist/cassist.c | 40 ++-- pij/cassist/cassist.h | 4 +- pij/cassist/llr.c | 5 +- pij/cassist/llr.h | 2 +- pij/cassist/llrtopij.c | 91 +++++++- pij/cassist/llrtopij.h | 14 +- pij/cassist/llrtopij_a.c | 116 ---------- pij/cassist/llrtopij_a.h | 82 ------- pij/cassist/llrtopv.h | 8 +- pij/gassist/gassist.c | 90 ++++---- pij/gassist/gassist.h | 8 +- pij/gassist/llr.c | 34 +-- pij/gassist/llr.h | 116 +--------- pij/gassist/llrtopij.c | 196 ++++++++++++++++- pij/gassist/llrtopij.h | 13 +- pij/gassist/llrtopij_a.c | 430 ------------------------------------- pij/gassist/llrtopij_a.h | 107 --------- pij/gassist/llrtopij_tot.c | 184 ---------------- pij/gassist/llrtopij_tot.h | 103 --------- pij/gassist/llrtopv.c | 6 +- pij/gassist/llrtopv.h | 2 +- pij/gassist/nulldist.c | 310 -------------------------- pij/gassist/nulldist.h | 115 ---------- pij/gassist/nullhist.c | 63 ++---- pij/gassist/nullhist.h | 33 ++- pij/llrtopij.c | 291 ++++++++++++++++++++++++- pij/llrtopij.h | 30 ++- pij/llrtopij_a.c | 411 ----------------------------------- pij/llrtopij_a.h | 82 ------- pij/llrtopij_tot.c | 121 ----------- pij/llrtopij_tot.h | 93 -------- pij/llrtopv.c | 2 +- pij/llrtopv.h | 2 +- pij/nulldist.c | 2 +- pij/nulldist.h | 2 +- pij/nullhist.c | 225 +++++++------------ pij/nullhist.h | 147 ++++--------- pij/nullmodeler.c | 220 ------------------- pij/nullmodeler.h | 143 ------------ pij/nullsampler.h | 73 ------- pij/rank.c | 45 ++-- pij/rank.h | 16 +- 92 files changed, 1040 insertions(+), 3457 deletions(-) delete mode 100644 base/general_alg.c delete mode 100644 pij/cassist/llrtopij_a.c delete mode 100644 pij/cassist/llrtopij_a.h delete mode 100644 pij/gassist/llrtopij_a.c delete mode 100644 pij/gassist/llrtopij_a.h delete mode 100644 pij/gassist/llrtopij_tot.c delete mode 100644 pij/gassist/llrtopij_tot.h delete mode 100644 pij/gassist/nulldist.c delete mode 100644 pij/gassist/nulldist.h delete mode 100644 pij/llrtopij_a.c delete mode 100644 pij/llrtopij_a.h delete mode 100644 pij/llrtopij_tot.c delete mode 100644 pij/llrtopij_tot.h delete mode 100644 pij/nullmodeler.c delete mode 100644 pij/nullmodeler.h delete mode 100644 pij/nullsampler.h diff --git a/COPYRIGHT b/COPYRIGHT index 9fe6926..500f8ee 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -1 +1 @@ -Copyright 2016, 2017 Lingfei Wang +Copyright 2016-2018 Lingfei Wang diff --git a/Makefile b/Makefile index fea521b..8d26b02 100644 --- a/Makefile +++ b/Makefile @@ -15,11 +15,10 @@ URL_BIN_REL="$(URL_BIN)/releases" URL_R_REL="$(URL_R)/releases" VERSION1=1 VERSION2=0 -VERSION3=5 +VERSION3=6 LICENSE=AGPL-3 LICENSE_FULL="GNU Affero General Public License, Version 3" LICENSE_URL="https://www.gnu.org/licenses/agpl-3.0" -#UNAME=$$(uname) ifdef INCLUDE_MAKEFILE_BEFORE #Input package info here include $(INCLUDE_MAKEFILE_BEFORE) @@ -39,7 +38,8 @@ DIR_SRC=. endif DIR_INSTALL_PREFIX=$(PREFIX) DIR_INSTALL_LIB=$(DIR_INSTALL_PREFIX)/lib -DIR_INSTALL_INC=$(DIR_INSTALL_PREFIX)/include/$(LIB_NAME) +DIR_INSTALL_INC0=$(DIR_INSTALL_PREFIX)/include +DIR_INSTALL_INC=$(DIR_INSTALL_INC0)/$(LIB_NAME) CC=gcc CFLAGSI=$(addprefix -I ,. $(R_INCLUDE_DIR) $(PREFIX)/include /usr/local/include) @@ -66,10 +66,24 @@ INC_INSTALL_FILES=$(LIB_H) INC_INSTALL_DIRS=$(dir $(LIB_H)) LIB_UNINSTALL=$(addprefix $(DIR_INSTALL_LIB)/,$(notdir $(LIB_DPRODUCT))) INC_UNINSTALL=$(DIR_INSTALL_INC) +PKGCONFIG=$(LIB_NAME).pc +PKGCONFIG_UNINSTALL=$(DIR_INSTALL_LIB)/pkgconfig/$(LIB_NAME).pc .PHONY: all clean distclean install-lib install-inc install uninstall -all: $(LIB_DPRODUCT) +all: $(LIB_DPRODUCT) $(PKGCONFIG) + +$(PKGCONFIG): + @echo "prefix=$(DIR_INSTALL_PREFIX)" > $@ + @echo "exec_prefix=$(DIR_INSTALL_PREFIX)" >> $@ + @echo "libdir=$(DIR_INSTALL_LIB)" >> $@ + @echo "includedir=$(DIR_INSTALL_INC0)" >> $@ + @echo >> $@ + @echo "Name: $(LIB_NAME)" >> $@ + @echo "Description: Fast Inference of Networks from Directed Regulations" >> $@ + @echo "Version: $(VERSION1).$(VERSION2).$(VERSION3)" >> $@ + @echo "Libs: -L$(DIR_INSTALL_LIB) -l$(LIB_NAME) -lgsl" >> $@ + @echo "Cflags: -I$(DIR_INSTALL_INC0)" >> $@ $(LIB_CONFIG): @echo "#ifndef _HEADER_LIB_CONFIG_AUTO_H_" > $@ @@ -100,23 +114,14 @@ clean: $(RM) $(LIB_PRODUCT) distclean: clean - $(RM) $(LIB_DPRODUCT) - $(RM) $(LIB_CONFIG) - $(RM) Makefile.flags $(TMP_FILE) + $(RM) $(LIB_DPRODUCT) $(PKGCONFIG) $(LIB_CONFIG) Makefile.flags $(TMP_FILE) install-lib: SHELL:=/bin/bash install-lib: all umask 0022 && mkdir -p $(DIR_INSTALL_LIB) && \ cp $(LIB_DPRODUCT) $(DIR_INSTALL_LIB)/ && \ chmod 0755 $(DIR_INSTALL_LIB)/$(notdir $(LIB_DPRODUCT)) && \ - if [ "$$(uname)" == "Linux" ]; then \ - ldconfig $(DIR_INSTALL_LIB) || true; \ - fi; \ - tcyg=$$(uname | grep -io "cygwin"); \ - if [ -n "$$tcyg" ]; then \ - ln -s $(DIR_INSTALL_LIB)/$(notdir $(LIB_DPRODUCT)) $(basename $(DIR_INSTALL_LIB)/$(notdir $(LIB_DPRODUCT))).dll; \ - fi - + ldconfig $(DIR_INSTALL_LIB) || true install-inc: SHELL:=/bin/bash install-inc: $(LIB_CONFIG) @@ -130,10 +135,15 @@ install-inc: $(LIB_CONFIG) chmod 0644 $(DIR_INSTALL_INC)/$$fname || exit 1; \ done -install: install-lib install-inc +install-pkgconfig: $(PKGCONFIG) + umask 0022 && mkdir -p $(DIR_INSTALL_LIB)/pkgconfig && \ + cp $< $(DIR_INSTALL_LIB)/pkgconfig/ + chmod 0644 $(DIR_INSTALL_LIB)/pkgconfig/$(notdir $<) + +install: install-lib install-inc install-pkgconfig uninstall: - $(RM) -R $(LIB_UNINSTALL) $(INC_UNINSTALL) + $(RM) -R $(LIB_UNINSTALL) $(INC_UNINSTALL) $(PKGCONFIG_UNINSTALL) TMP_FILE=.tmp Makefile.flags: diff --git a/UPDATES b/UPDATES index 74bb24d..d6dd91f 100644 --- a/UPDATES +++ b/UPDATES @@ -1,8 +1,15 @@ +1.0.6: + Corrected a bug that may produce biased output in pij_gassist, pij_gassist_trad, and pijs_gassist when nodiag is set or when memlimit is small so computation is split into chunks. + Now setting histogram bounds based on the maximum of all LLRs (as opposed to the maximum of the chunk when memlimit is small) in pij_gassist, pij_gassist_trad, and pijs_gassist. This ensures the output is independent of memlimit (related to question from sritchie73@github). + Added sanity checks for agreement between input data and nodiag flag for pij functions excluding _pv (suggested by sritchie73@github). + Lots of internal function renaming and code restructuring. + Removed some unneeded files and functions. + Now support pkg-config setup. 1.0.5: - Updated Makefiles to account for different make environments. + Updated Makefiles to account for different make environments (reported by sritchie73@github). 1.0.1: Bug correction: - Updated LDFLAGS for R interface. + Updated LDFLAGS for R interface (reported by audreyqyfu@github). 1.0.0: New functions: Included P-value computation for 4 tests in pijs_gassist_pv and correlation test in pij_rank_pv. diff --git a/base/config.h b/base/config.h index 0f7f6ca..5b5781f 100644 --- a/base/config.h +++ b/base/config.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/base/const.h b/base/const.h index c251a70..1fea54a 100644 --- a/base/const.h +++ b/base/const.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/base/data_process.c b/base/data_process.c index f085e35..a8f6027 100644 --- a/base/data_process.c +++ b/base/data_process.c @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * @@ -18,6 +18,7 @@ #include "config.h" #include #include +#include #include #include #include "types.h" @@ -435,7 +436,72 @@ void MATRIXFF(minmax_nodiag)(const MATRIXF* d,FTYPE* restrict dmin,FTYPE* restri } } +/* Quick hash of VECTORF using bitwise operation + * Return: hash of VECTORF as FTYPE + */ +static inline FTYPE VECTORFF(hash)(const VECTORF* v) +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-aliasing" +#define TFUTYPE CONCATENATE3(uint,FTYPEBITS,_t) + TFUTYPE h; + size_t i; + + h=*(const TFUTYPE*)VECTORFF(const_ptr)(v,0); + for(i=1;isize;i++) + { + h=(h<<1)|(h>>(FTYPEBITS-1)); + h^=*(const TFUTYPE*)VECTORFF(const_ptr)(v,i); + } + return *(FTYPE*)(&h); +} + +int MATRIXFF(cmprow)(const MATRIXF* m1,const MATRIXF* m2,VECTORF* buff1,VECTORF* buff2,char nodiag,char warn) +{ + size_t i,j; + size_t ng,nt; + union u + { + FTYPE f; + TFUTYPE u; + } t; + FTYPE fmin,fmax; + int test; + ng=m1->size1; + nt=m2->size1; + assert((m2->size2==m1->size2)&&(buff->size1==ng)&&(buff1->size==ng)&&(buff2->size==m1->size2)); + + for(i=0;isize;i++) + if(VECTORFF(get)(v,i)==vold) + VECTORFF(set)(v,i,vnew); +} static inline void MATRIXFF(cov1)(const MATRIXF* m,MATRIXF* cov) { diff --git a/base/data_struct.h b/base/data_struct.h index 2bb5d7b..0071203 100644 --- a/base/data_struct.h +++ b/base/data_struct.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/base/data_struct_heap.c b/base/data_struct_heap.c index 254581c..ff46c6b 100644 --- a/base/data_struct_heap.c +++ b/base/data_struct_heap.c @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/base/data_struct_heap.h b/base/data_struct_heap.h index 6eb777d..04fd8f7 100644 --- a/base/data_struct_heap.h +++ b/base/data_struct_heap.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/base/data_struct_ll.c b/base/data_struct_ll.c index 6d85ce4..45de3da 100644 --- a/base/data_struct_ll.c +++ b/base/data_struct_ll.c @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/base/data_struct_ll.h b/base/data_struct_ll.h index c098c26..946e38b 100644 --- a/base/data_struct_ll.h +++ b/base/data_struct_ll.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/base/general_alg.c b/base/general_alg.c deleted file mode 100644 index 61d7436..0000000 --- a/base/general_alg.c +++ /dev/null @@ -1,102 +0,0 @@ -/* Copyright 2016, 2017 Lingfei Wang - * - * This file is part of Findr. - * - * Findr is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Findr is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Findr. If not, see . - */ -#include "config.h" -#include -#include -#include "logger.h" -#include "macros.h" -#include "general_alg.h" - -size_t break_string_into_substrings(char** s,const char* restrict sep,size_t nmax) -{ - size_t i,l,n; - - l=strlen(s[0]); - for(i=0,n=1;i=l) - break; - if(n>=nmax) - return nmax+1; - s[0][i++]=0; - i+=strspn(s[0]+i,sep); - if(i>=l) - break; - s[n++]=s[0]+i; - } - return n; -} - -long count_instance_fromfile(FILE* f,size_t size,size_t num,int (*func)(const void*)) -{ -#define CLEANUP AUTOFREE(dat) - size_t i,n; - AUTOALLOC(char,dat,size,10000) - size_t sret; - - if(!dat) - { - LOG(3,"Can't allocate memory of size "PRINTFSIZET".",size) - return -1; - } - for(i=0,n=0;i nmax, the last string s[nmax-1] contains - * separators because insufficient breaking. - */ -size_t break_string_into_substrings(char** s,const char* restrict sep,size_t nmax); - -/* Counts the number of instances that gives the criterion function a return value of true. - * Parameters: - * inst: the pointer to the first item of the instance array - * size: size of one instance - * num: the number of instances to be tested - * func: the criterion function to test the instances. - * Return: the number of instances that gives true for the function func, for a total of num - * instances, starting from inst, each of which has size bytes. - */ -static inline size_t count_instance(const void* inst,size_t size,size_t num,int (*func)(const void*)); - -/* Similar with above (count_instance), but processes a file handle. - * Return: the number of instances satisfying the criterion function func if succeed, - * or -1 if fail. - */ -long count_instance_fromfile(FILE* f,size_t size,size_t num,int (*func)(const void*)); - -/* Search for left insert position of item in ascending array. - * x: item to search for position - * a: ascending array - * n: size of a - * Return: left insert position of x (=0,...,n) - */ -static inline size_t bsearch_d(double x,const double* restrict a,size_t n); - /* Categorize data according to categorical information into separate arrays. * s: Source of data. * c: Categorical information. Each element contains a category of the corresponding element of s. @@ -96,31 +59,6 @@ static inline size_t remove_sorted_duplicates(double* restrict a,size_t n); -static inline size_t count_instance(const void* inst,size_t size,size_t num,int (*func)(const void*)) -{ - size_t i,n; - for(i=0,n=0;i=x) - end=mid; - else if(a[mid]=n if value outside range - */ -static inline size_t histogram_c2d(double val,const double* restrict range,size_t n); - /* Construct finer histogram ranges from existing sparse one. * Each bin is evenly split into n bins. * hnew[i*n+j]=(hold[i]*(n-j)+hold[i+1]*j)/n @@ -234,12 +226,10 @@ static inline void histogram_equalbins_fromnullcdf(size_t n,double left,double r math_cdf_quantile(n,left,right,func,param,eps,ans); } -static inline size_t histogram_c2d(double val,const double* restrict range,size_t n) -{ - if((valrange[n])) - return n; - return bsearch_d(val,(double*)range+1,n-1); -} + + + + diff --git a/base/lib.c b/base/lib.c index 3634d08..6feaa8a 100644 --- a/base/lib.c +++ b/base/lib.c @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/base/lib.h b/base/lib.h index 00d0088..244b531 100644 --- a/base/lib.h +++ b/base/lib.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/base/logger.c b/base/logger.c index 48e57ca..5c1c03f 100644 --- a/base/logger.c +++ b/base/logger.c @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/base/logger.h b/base/logger.h index 079a039..e4d6437 100644 --- a/base/logger.h +++ b/base/logger.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/base/macros.h b/base/macros.h index 712e447..35e6279 100644 --- a/base/macros.h +++ b/base/macros.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/base/math.c b/base/math.c index 7314fd1..ffe9eca 100644 --- a/base/math.c +++ b/base/math.c @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/base/math.h b/base/math.h index ba3e349..a421967 100644 --- a/base/math.h +++ b/base/math.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/base/os.h b/base/os.h index 3fb921c..23c2d7a 100644 --- a/base/os.h +++ b/base/os.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/base/random.c b/base/random.c index bb612f6..e918742 100644 --- a/base/random.c +++ b/base/random.c @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/base/random.h b/base/random.h index 3c4f4f9..78cf7fc 100644 --- a/base/random.h +++ b/base/random.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/base/supernormalize.c b/base/supernormalize.c index e8a9e27..0cd1698 100644 --- a/base/supernormalize.c +++ b/base/supernormalize.c @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/base/supernormalize.h b/base/supernormalize.h index 99b1cf7..5662e93 100644 --- a/base/supernormalize.h +++ b/base/supernormalize.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/base/threading.h b/base/threading.h index 6679587..bee398a 100644 --- a/base/threading.h +++ b/base/threading.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/base/types.h b/base/types.h index 4211dbf..d06b706 100644 --- a/base/types.h +++ b/base/types.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/cycle/cycle.h b/cycle/cycle.h index f7ae596..5b7f4c6 100644 --- a/cycle/cycle.h +++ b/cycle/cycle.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/cycle/vg.c b/cycle/vg.c index 3b7c30a..d0bab2b 100644 --- a/cycle/vg.c +++ b/cycle/vg.c @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/cycle/vg.h b/cycle/vg.h index 1127e1e..e3c1e51 100644 --- a/cycle/vg.h +++ b/cycle/vg.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/doc.pdf b/doc.pdf index 9eadc5df959cea2f0224e63adf0f518f2d165673..22c7dfae2b1d927f3b0b835499abf1a10fce2905 100644 GIT binary patch delta 47420 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 bXN%P&W{TQ?6C$v4^02bOQBq1OOTqmYD`4Yh delta 34134 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 diff --git a/external/R.c b/external/R.c index 6ae9265..2c82e61 100644 --- a/external/R.c +++ b/external/R.c @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/netr/one.c b/netr/one.c index c2c5c74..0165897 100644 --- a/netr/one.c +++ b/netr/one.c @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/netr/one.h b/netr/one.h index 2a74782..1866b56 100644 --- a/netr/one.h +++ b/netr/one.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/pij/cassist/cassist.c b/pij/cassist/cassist.c index dfd33a5..c9026b6 100644 --- a/pij/cassist/cassist.c +++ b/pij/cassist/cassist.c @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * @@ -24,6 +24,7 @@ #include "../../base/const.h" #include "../../base/supernormalize.h" #include "../../base/threading.h" +#include "../../base/data_process.h" #include "llr.h" #include "llrtopij.h" #include "llrtopv.h" @@ -96,7 +97,7 @@ int pijs_cassist_pv(const MATRIXF* g,const MATRIXF* t,const MATRIXF* t2,VECTORF* #undef CLEANUP } -int pijs_cassist_a(const MATRIXF* g,const MATRIXF* t,const MATRIXF* t2,VECTORF* p1,MATRIXF* p2,MATRIXF* p3,MATRIXF* p4,MATRIXF* p5,char nodiag,size_t memlimit) +int pijs_cassist(const MATRIXF* g,const MATRIXF* t,const MATRIXF* t2,VECTORF* p1,MATRIXF* p2,MATRIXF* p3,MATRIXF* p4,MATRIXF* p5,char nodiag,size_t memlimit) { #define CLEANUP CLEANMATF(gnew)CLEANMATF(tnew)CLEANMATF(tnew2) MATRIXF *gnew; //(ng,ns) Supernormalized transcript matrix @@ -140,6 +141,13 @@ int pijs_cassist_a(const MATRIXF* g,const MATRIXF* t,const MATRIXF* t2,VECTORF* if(!(gnew&&tnew&&tnew2)) ERRRET("Not enough memory.") + //Check for identical rows in input data + { + VECTORFF(view) vbuff1=MATRIXFF(column)(tnew,0); + VECTORFF(view) vbuff2=MATRIXFF(row)(tnew2,0); + MATRIXFF(cmprow)(t,t2,&vbuff1.vector,&vbuff2.vector,nodiag,1); + } + //Step 1: Supernormalization LOG(9,"Supernormalizing...") MATRIXFF(memcpy)(gnew,g); @@ -155,7 +163,7 @@ int pijs_cassist_a(const MATRIXF* g,const MATRIXF* t,const MATRIXF* t2,VECTORF* LOG(9,"Calculating real log likelihood ratios...") pij_cassist_llr(gnew,tnew,tnew2,p1,p2,p3,p4,p5); //Step 3: Convert log likelihood ratios to probabilities - if((ret=pij_cassist_llrtopijs_a(p1,p2,p3,p4,p5,ns,nodiag))) + if((ret=pij_cassist_llrtopijs(p1,p2,p3,p4,p5,ns,nodiag))) LOG(4,"Failed to convert all log likelihood ratios to probabilities.") if(nodiag) { @@ -175,17 +183,7 @@ int pijs_cassist_a(const MATRIXF* g,const MATRIXF* t,const MATRIXF* t2,VECTORF* #undef CLEANUP } -int pijs_cassist(const MATRIXF* g,const MATRIXF* t,const MATRIXF* t2,VECTORF* p1,MATRIXF* p2,MATRIXF* p3,MATRIXF* p4,MATRIXF* p5,char nodiag,size_t memlimit) -{ - return pijs_cassist_a(g,t,t2,p1,p2,p3,p4,p5,nodiag,memlimit); -} - -/* Estimates the probability p(E->A->B) from genotype and expression data. Combines results - * from any pij_cassist_pijs. For more information, see pij_cassist_pijs_ab. - * ans: (ng,nt) Output matrix for probabilities. ans[A,B] is p(E->A->B). - * pijs: Function to calculate pijs. - */ -static int pij_cassist_any(const MATRIXF* g,const MATRIXF* t,const MATRIXF* t2,MATRIXF* ans,char nodiag,int (*pijs)(const MATRIXF*,const MATRIXF*,const MATRIXF*,VECTORF*,MATRIXF*,MATRIXF*,MATRIXF*,MATRIXF*,char,size_t),size_t memlimit) +int pij_cassist(const MATRIXF* g,const MATRIXF* t,const MATRIXF* t2,MATRIXF* ans,char nodiag,size_t memlimit) { #define CLEANUP CLEANVECF(p1)CLEANMATF(p2)CLEANMATF(p3)CLEANMATF(p4) VECTORF *p1; @@ -202,7 +200,7 @@ static int pij_cassist_any(const MATRIXF* g,const MATRIXF* t,const MATRIXF* t2,M p4=MATRIXFF(alloc)(ng,nt); if(!(p1&&p2&&p3&&p4)) ERRRET("Not enough memory.") - if(pijs(g,t,t2,p1,p2,p3,p4,ans,nodiag,memlimit)) + if(pijs_cassist(g,t,t2,p1,p2,p3,p4,ans,nodiag,memlimit)) ERRRET("pij_cassist_pijs failed.") //Combine tests @@ -227,16 +225,6 @@ static int pij_cassist_any(const MATRIXF* g,const MATRIXF* t,const MATRIXF* t2,M #undef CLEANUP } -int pij_cassist_a(const MATRIXF* g,const MATRIXF* t,const MATRIXF* t2,MATRIXF* ans,char nodiag,size_t memlimit) -{ - return pij_cassist_any(g,t,t2,ans,nodiag,pijs_cassist_a,memlimit); -} - -int pij_cassist(const MATRIXF* g,const MATRIXF* t,const MATRIXF* t2,MATRIXF* ans,char nodiag,size_t memlimit) -{ - return pij_cassist_a(g,t,t2,ans,nodiag,memlimit); -} - int pij_cassist_trad(const MATRIXF* g,const MATRIXF* t,const MATRIXF* t2,MATRIXF* ans,char nodiag,size_t memlimit) { #define CLEANUP CLEANVECF(p1)CLEANMATF(p2)CLEANMATF(p4)CLEANMATF(p5) @@ -254,7 +242,7 @@ int pij_cassist_trad(const MATRIXF* g,const MATRIXF* t,const MATRIXF* t2,MATRIXF p5=MATRIXFF(alloc)(ng,nt); if(!(p1&&p2&&p5&&p4)) ERRRET("Not enough memory.") - if(pijs_cassist_a(g,t,t2,p1,p2,ans,p4,p5,nodiag,memlimit)) + if(pijs_cassist(g,t,t2,p1,p2,ans,p4,p5,nodiag,memlimit)) ERRRET("pij_cassist_pijs failed.") //Combine tests diff --git a/pij/cassist/cassist.h b/pij/cassist/cassist.h index 858c915..238a49f 100644 --- a/pij/cassist/cassist.h +++ b/pij/cassist/cassist.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * @@ -65,14 +65,12 @@ int pijs_cassist_pv(const MATRIXF* g,const MATRIXF* t,const MATRIXF* t2,VECTORF* * nt: Number of genes with expression data for B * ns: Number of samples. */ -int pijs_cassist_a(const MATRIXF* g,const MATRIXF* t,const MATRIXF* t2,VECTORF* p1,MATRIXF* p2,MATRIXF* p3,MATRIXF* p4,MATRIXF* p5,char nodiag,size_t memlimit); int pijs_cassist(const MATRIXF* g,const MATRIXF* t,const MATRIXF* t2,VECTORF* p1,MATRIXF* p2,MATRIXF* p3,MATRIXF* p4,MATRIXF* p5,char nodiag,size_t memlimit); /* Estimates the probability of A->B from genotype and expression data with defaults combination of tests. Uses results from pijs_gassist_tot or pijs_gassist_a. Variables have the same definitions except: * ans: (ng,nt) Predicted probability of A->B based on default combination of 5 tests. The default combination is (p2*p5+p4)/2. Note: this combination does not include p1. * Return: 0 on sucess */ -int pij_cassist_a(const MATRIXF* g,const MATRIXF* t,const MATRIXF* t2,MATRIXF* ans,char nodiag,size_t memlimit); int pij_cassist(const MATRIXF* g,const MATRIXF* t,const MATRIXF* t2,MATRIXF* ans,char nodiag,size_t memlimit); /* Estimates the probability of A->B from genotype and expression data with traditional causal inference method. diff --git a/pij/cassist/llr.c b/pij/cassist/llr.c index e3ae584..f92878d 100644 --- a/pij/cassist/llr.c +++ b/pij/cassist/llr.c @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * @@ -102,7 +102,6 @@ static void pij_cassist_llr_block(const MATRIXF* g,const MATRIXF* t,const MATRIX MATRIXFF(set)(llr5,i,j,(FTYPE)log(MATRIXFF(get)(llr5,i,j))); } } - //llr4=llr4 before scaling -0.5 for(i=0;i #include +#include #include "../../base/logger.h" #include "llrtopij.h" +#include "../llrtopij.h" -int pij_cassist_llrtopij1_1(VECTORF* p1) + + + + + +/* Always return probability of step 1 is 1. This is useful when best eQTL are already selected in advance. + */ +static inline int pij_cassist_llrtopij1_1(VECTORF* p1) { LOG(9,"Converting LLR to probabilities for step 1. Filling with 1.") VECTORFF(set_all)(p1,1); return 0; } +/* Functions to convert LLR of specific steps into probabilities. + * Uses pij_llrtopij_convert with different settings of n1d and n2d. + * Function name suffices indicate which LLR to convert. + */ +static inline int pij_cassist_llrtopij1(VECTORF* d) +{ + LOG(9,"Converting LLR to probabilities for step 1 on per A basis.") + return pij_cassist_llrtopij1_1(d); +} + +static inline int pij_cassist_llrtopij2(MATRIXF* d,size_t ns,char nodiag) +{ + LOG(9,"Converting LLR to probabilities for step 2 on per A basis.") + assert(ns>2); + return pij_llrtopij_convert_single_self(d,1,ns-2,nodiag,0); +} + +static inline int pij_cassist_llrtopij3(MATRIXF* d,size_t ns,char nodiag) +{ + LOG(9,"Converting LLR to probabilities for step 3 on per A basis.") + assert(ns>3); + if(pij_llrtopij_convert_single_self(d,1,ns-3,nodiag,0)) + return 1; + MATRIXFF(scale)(d,-1); + MATRIXFF(add_constant)(d,1); + return 0; +} + +static inline int pij_cassist_llrtopij4(MATRIXF* d,size_t ns,char nodiag) +{ + LOG(9,"Converting LLR to probabilities for step 4 on per A basis.") + assert(ns>3); + return pij_llrtopij_convert_single_self(d,2,ns-3,nodiag,0); +} + +static inline int pij_cassist_llrtopij5(MATRIXF* d,size_t ns,char nodiag) +{ + LOG(9,"Converting LLR to probabilities for step 5 on per A basis.") + assert(ns>3); + return pij_llrtopij_convert_single_self(d,1,ns-3,nodiag,0); +} + + +int pij_cassist_llrtopijs(VECTORF* p1,MATRIXF* p2,MATRIXF* p3,MATRIXF* p4,MATRIXF* p5,size_t ns,char nodiag) +{ + int ret=0,ret2=0; + + if(ns<4) + { + LOG(0,"Cannot convert log likelihood ratios to probabilities. Needs at least 4 samples.") + return 1; + } + ret=ret||(ret2=pij_cassist_llrtopij2(p2,ns,nodiag)); + if(ret2) + LOG(1,"Failed to convert log likelihood ratios to probabilities in step 2.") + //For p1, if nodiag, copy p2 data, otherwise set all to 1. + if(nodiag) + { + VECTORFF(view) vv; + vv=MATRIXFF(diagonal)(p2); + ret=ret||(ret2=VECTORFF(memcpy)(p1,&vv.vector)); + } + else + ret=ret||(ret2=pij_cassist_llrtopij1_1(p1)); + if(ret2) + LOG(1,"Failed to convert log likelihood ratios to probabilities in step 1.") + ret=ret||(ret2=pij_cassist_llrtopij3(p3,ns,nodiag)); + if(ret2) + LOG(1,"Failed to convert log likelihood ratios to probabilities in step 3.") + ret=ret||(ret2=pij_cassist_llrtopij4(p4,ns,nodiag)); + if(ret2) + LOG(1,"Failed to convert log likelihood ratios to probabilities in step 4.") + ret=ret||(ret2=pij_cassist_llrtopij5(p5,ns,nodiag)); + if(ret2) + LOG(1,"Failed to convert log likelihood ratios to probabilities in step 5.") + return ret; +} + diff --git a/pij/cassist/llrtopij.h b/pij/cassist/llrtopij.h index b7ec39b..3401786 100644 --- a/pij/cassist/llrtopij.h +++ b/pij/cassist/llrtopij.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * @@ -23,16 +23,20 @@ #define _HEADER_LIB_PIJ_CASSIST_LLRTOPIJ_H_ #include "../../base/config.h" #include "../../base/types.h" -#include "llrtopij_a.h" #ifdef __cplusplus extern "C" { #endif - -/* Always return probability of step 1 is 1. This is useful when best eQTL are already selected in advance. +/* Converts four LLRs into probabilities together. + * Uses pij_cassit_llrtopij1_a to pij_cassit_llrtopij5_a. + * See above functions for parameter definitions. + * Return: 0 if all functions are successful. */ -int pij_cassist_llrtopij1_1(VECTORF* p1); +int pij_cassist_llrtopijs(VECTORF* p1,MATRIXF* p2,MATRIXF* p3,MATRIXF* p4,MATRIXF* p5,size_t ns,char nodiag); + + + diff --git a/pij/cassist/llrtopij_a.c b/pij/cassist/llrtopij_a.c deleted file mode 100644 index 7a4f286..0000000 --- a/pij/cassist/llrtopij_a.c +++ /dev/null @@ -1,116 +0,0 @@ -/* Copyright 2016, 2017 Lingfei Wang - * - * This file is part of Findr. - * - * Findr is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Findr is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Findr. If not, see . - */ -#include "../../base/config.h" -#include -#include -#include -#include -#include "../../base/logger.h" -#include "../llrtopij.h" -#include "llrtopij_a.h" -#include "llrtopij.h" -#pragma GCC diagnostic ignored "-Wunused-parameter" - -/* Functions to convert LLR of specific steps into probabilities. - * Uses pij_llrtopij_a_convert with different settings of n1d and n2d. - * Function name suffices indicate which LLR to convert. - */ -static inline int pij_cassist_llrtopij1_a(VECTORF* d) -{ - LOG(9,"Converting LLR to probabilities for step 1 on per A basis.") - return pij_cassist_llrtopij1_1(d); -} - -static inline int pij_cassist_llrtopij2_a(MATRIXF* d,size_t ns,char nodiag) -{ - LOG(9,"Converting LLR to probabilities for step 2 on per A basis.") - assert(ns>2); - return pij_llrtopij_a_convert_single_self(d,1,ns-2,nodiag,0); -} - -static inline int pij_cassist_llrtopij3_a(MATRIXF* d,size_t ns,char nodiag) -{ - LOG(9,"Converting LLR to probabilities for step 3 on per A basis.") - assert(ns>3); - if(pij_llrtopij_a_convert_single_self(d,1,ns-3,nodiag,0)) - return 1; - MATRIXFF(scale)(d,-1); - MATRIXFF(add_constant)(d,1); - return 0; -} - -static inline int pij_cassist_llrtopij4_a(MATRIXF* d,size_t ns,char nodiag) -{ - LOG(9,"Converting LLR to probabilities for step 4 on per A basis.") - assert(ns>3); - return pij_llrtopij_a_convert_single_self(d,2,ns-3,nodiag,0); -} - -static inline int pij_cassist_llrtopij5_a(MATRIXF* d,size_t ns,char nodiag) -{ - LOG(9,"Converting LLR to probabilities for step 5 on per A basis.") - assert(ns>3); - return pij_llrtopij_a_convert_single_self(d,1,ns-3,nodiag,0); -} - - -int pij_cassist_llrtopijs_a(VECTORF* p1,MATRIXF* p2,MATRIXF* p3,MATRIXF* p4,MATRIXF* p5,size_t ns,char nodiag) -{ - int ret=0,ret2=0; - - if(ns<4) - { - LOG(0,"Cannot convert log likelihood ratios to probabilities. Needs at least 4 samples.") - return 1; - } - ret=ret||(ret2=pij_cassist_llrtopij2_a(p2,ns,nodiag)); - if(ret2) - LOG(1,"Failed to convert log likelihood ratios to probabilities in step 2.") - //For p1, if nodiag, copy p2 data, otherwise set all to 1. - if(nodiag) - { - VECTORFF(view) vv; - vv=MATRIXFF(diagonal)(p2); - ret=ret||(ret2=VECTORFF(memcpy)(p1,&vv.vector)); - } - else - ret=ret||(ret2=pij_cassist_llrtopij1_1(p1)); - if(ret2) - LOG(1,"Failed to convert log likelihood ratios to probabilities in step 1.") - ret=ret||(ret2=pij_cassist_llrtopij3_a(p3,ns,nodiag)); - if(ret2) - LOG(1,"Failed to convert log likelihood ratios to probabilities in step 3.") - ret=ret||(ret2=pij_cassist_llrtopij4_a(p4,ns,nodiag)); - if(ret2) - LOG(1,"Failed to convert log likelihood ratios to probabilities in step 4.") - ret=ret||(ret2=pij_cassist_llrtopij5_a(p5,ns,nodiag)); - if(ret2) - LOG(1,"Failed to convert log likelihood ratios to probabilities in step 5.") - return ret; -} - - - - - - - - - - - diff --git a/pij/cassist/llrtopij_a.h b/pij/cassist/llrtopij_a.h deleted file mode 100644 index 5401e53..0000000 --- a/pij/cassist/llrtopij_a.h +++ /dev/null @@ -1,82 +0,0 @@ -/* Copyright 2016, 2017 Lingfei Wang - * - * This file is part of Findr. - * - * Findr is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Findr is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Findr. If not, see . - */ -/* This file contains the conversion from log likelihood ratio to probabilities - * - */ - -#ifndef _HEADER_LIB_PIJ_CASSIST_LLRTOPIJ_A_H_ -#define _HEADER_LIB_PIJ_CASSIST_LLRTOPIJ_A_H_ -#include "../../base/config.h" -#include "../../base/types.h" -#include "../llrtopij_a.h" -#ifdef __cplusplus -extern "C" -{ -#endif - -/* Converts four LLRs into probabilities together. - * Uses pij_cassit_llrtopij1_a to pij_cassit_llrtopij5_a. - * See above functions for parameter definitions. - * Return: 0 if all functions are successful. - */ -int pij_cassist_llrtopijs_a(VECTORF* p1,MATRIXF* p2,MATRIXF* p3,MATRIXF* p4,MATRIXF* p5,size_t ns,char nodiag); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#ifdef __cplusplus -} -#endif -#endif diff --git a/pij/cassist/llrtopv.h b/pij/cassist/llrtopv.h index 8519299..24179ae 100644 --- a/pij/cassist/llrtopv.h +++ b/pij/cassist/llrtopv.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * @@ -29,21 +29,19 @@ extern "C" { #endif - - /* Converts log likelihood ratios into p-values for continuous assisted causal inference test for each test separately. * d: MATRIXF of any size, as input of LLRs and also output of corresponding p-values * ns: Number of samples, to be used to calculate the null distribution */ static inline void pij_cassist_llrtopv1(VECTORF* d,size_t ns) { - assert(ns>2); + assert(ns>3); pij_llrtopv_block(d,1,ns-2); } static inline void pij_cassist_llrtopv2(MATRIXF* d,size_t ns) { - assert(ns>2); + assert(ns>3); pij_llrtopvm(d,1,ns-2); } diff --git a/pij/gassist/gassist.c b/pij/gassist/gassist.c index 0d33ef9..081f531 100644 --- a/pij/gassist/gassist.c +++ b/pij/gassist/gassist.c @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * @@ -24,12 +24,14 @@ #include "../../base/const.h" #include "../../base/supernormalize.h" #include "../../base/threading.h" +#include "../../base/data_process.h" +#include "../llrtopij.h" #include "llr.h" #include "llrtopv.h" #include "llrtopij.h" +#include "nullhist.h" #include "gassist.h" - int pijs_gassist_pv(const MATRIXG* g,const MATRIXF* t,const MATRIXF* t2,VECTORF* p1,MATRIXF* p2,MATRIXF* p3,MATRIXF* p4,MATRIXF* p5,size_t nv,size_t memlimit) { #define CLEANUP CLEANMATF(tnew)CLEANMATF(tnew2) @@ -113,20 +115,17 @@ int pijs_gassist_pv(const MATRIXG* g,const MATRIXF* t,const MATRIXF* t2,VECTORF* #undef CLEANUP } - -int pijs_gassist_a(const MATRIXG* g,const MATRIXF* t,const MATRIXF* t2,VECTORF* p1,MATRIXF* p2,MATRIXF* p3,MATRIXF* p4,MATRIXF* p5,size_t nv,char nodiag,size_t memlimit) +int pijs_gassist(const MATRIXG* g,const MATRIXF* t,const MATRIXF* t2,VECTORF* p1,MATRIXF* p2,MATRIXF* p3,MATRIXF* p4,MATRIXF* p5,size_t nv,char nodiag,size_t memlimit) { -#define CLEANUP CLEANMATF(tnew)CLEANMATF(tnew2) - MATRIXF *tnew,*tnew2; //(nt,ns) Supernormalized transcript matrix +#define CLEANUP CLEANMATF(tnew)CLEANMATF(tnew2)for(i=0;i<4;i++){if(hnull[i])for(j=0;jsize1; -#endif ng=g->size1; ns=g->size2; @@ -152,7 +151,7 @@ int pijs_gassist_a(const MATRIXG* g,const MATRIXF* t,const MATRIXF* t2,VECTORF* ERRRET("Memory limit lower than minimum memory needed. Try increasing your memory usage limit.") LOG(10,"Memory limit: %lu bytes.",memlimit) nsplit=(size_t)ceil((float)ng/ceil((float)ng/(float)nsplit)); - //if(nsplitsize2); mvp4=MATRIXFF(submatrix)(p4,i,0,ngnow,p4->size2); mvp5=MATRIXFF(submatrix)(p5,i,0,ngnow,p5->size2); - //Step 2: Log likelihood ratios from nonpermuted data - LOG(9,"Calculating real log likelihood ratios...") if(pij_gassist_llr(&mvg.matrix,&mvt.matrix,tnew2,&vvp1.vector,&mvp2.matrix,&mvp3.matrix,&mvp4.matrix,&mvp5.matrix,nv)) ERRRET("pij_gassist_llr failed.") - //Step 3: Convert log likelihood ratios to probabilities - if((ret=pij_gassist_llrtopijs_a(&mvg.matrix,&vvp1.vector,&mvp2.matrix,&mvp3.matrix,&mvp4.matrix,&mvp5.matrix,nv,nodiag,(long)i))) + } + + //Step 3: Obtain null histograms + { + FTYPE dmax[4]; + dmax[0]=pij_llrtopij_llrmatmax(p2,nodiag); + dmax[1]=pij_llrtopij_llrmatmax(p3,nodiag); + dmax[2]=pij_llrtopij_llrmatmax(p4,nodiag); + dmax[3]=pij_llrtopij_llrmatmax(p5,nodiag); + if(!(dmax[0]&&dmax[1]&&dmax[2]&&dmax[3])) + ERRRET("Negative or NAN found in LLR.") + if(pij_gassist_nullhists(hnull,nt,ns,nv,dmax)) + ERRRET("Failed to construct null histograms.") + } + + //Step 4: Convert log likelihood ratios to probabilities + for(i=0;isize2); + mvt=MATRIXFF(submatrix)(tnew,i,0,ngnow,tnew->size2); + vvp1=VECTORFF(subvector)(p1,i,ngnow); + mvp2=MATRIXFF(submatrix)(p2,i,0,ngnow,p2->size2); + mvp3=MATRIXFF(submatrix)(p3,i,0,ngnow,p3->size2); + mvp4=MATRIXFF(submatrix)(p4,i,0,ngnow,p4->size2); + mvp5=MATRIXFF(submatrix)(p5,i,0,ngnow,p5->size2); + if((ret=pij_gassist_llrtopijs(&mvg.matrix,&vvp1.vector,&mvp2.matrix,&mvp3.matrix,&mvp4.matrix,&mvp5.matrix,nv,(const gsl_histogram* const **)hnull,nodiag,(long)i))) LOG(4,"Failed to convert all log likelihood ratios to probabilities.") if(nodiag) { @@ -207,17 +239,7 @@ int pijs_gassist_a(const MATRIXG* g,const MATRIXF* t,const MATRIXF* t2,VECTORF* #undef CLEANUP } -int pijs_gassist(const MATRIXG* g,const MATRIXF* t,const MATRIXF* t2,VECTORF* p1,MATRIXF* p2,MATRIXF* p3,MATRIXF* p4,MATRIXF* p5,size_t nv,char nodiag,size_t memlimit) -{ - return pijs_gassist_a(g,t,t2,p1,p2,p3,p4,p5,nv,nodiag,memlimit); -} - -/* Estimates the probability p(E->A->B) from genotype and expression data. Combines results - * from any pij_gassist_pijs. For more information, see pij_gassist_pijs_ab. - * ans: (ng,nt) Output matrix for probabilities. ans[A,B] is p(E->A->B). - * pijs: Function to calculate pijs. - */ -static int pij_gassist_any(const MATRIXG* g,const MATRIXF* t,const MATRIXF* t2,MATRIXF* ans,size_t nv,char nodiag,int (*pijs)(const MATRIXG*,const MATRIXF*,const MATRIXF*,VECTORF*,MATRIXF*,MATRIXF*,MATRIXF*,MATRIXF*,size_t,char,size_t),size_t memlimit) +int pij_gassist(const MATRIXG* g,const MATRIXF* t,const MATRIXF* t2,MATRIXF* ans,size_t nv,char nodiag,size_t memlimit) { #define CLEANUP CLEANVECF(p1)CLEANMATF(p2)CLEANMATF(p3)CLEANMATF(p4) VECTORF *p1; @@ -234,7 +256,7 @@ static int pij_gassist_any(const MATRIXG* g,const MATRIXF* t,const MATRIXF* t2,M p4=MATRIXFF(alloc)(ng,nt); if(!(p1&&p2&&p3&&p4)) ERRRET("Not enough memory.") - if(pijs(g,t,t2,p1,p2,p3,p4,ans,nv,nodiag,memlimit)) + if(pijs_gassist(g,t,t2,p1,p2,p3,p4,ans,nv,nodiag,memlimit)) ERRRET("pij_gassist_pijs failed.") //Combine tests @@ -259,16 +281,6 @@ static int pij_gassist_any(const MATRIXG* g,const MATRIXF* t,const MATRIXF* t2,M #undef CLEANUP } -int pij_gassist_a(const MATRIXG* g,const MATRIXF* t,const MATRIXF* t2,MATRIXF* ans,size_t nv,char nodiag,size_t memlimit) -{ - return pij_gassist_any(g,t,t2,ans,nv,nodiag,pijs_gassist_a,memlimit); -} - -int pij_gassist(const MATRIXG* g,const MATRIXF* t,const MATRIXF* t2,MATRIXF* ans,size_t nv,char nodiag,size_t memlimit) -{ - return pij_gassist_a(g,t,t2,ans,nv,nodiag,memlimit); -} - int pij_gassist_trad(const MATRIXG* g,const MATRIXF* t,const MATRIXF* t2,MATRIXF* ans,size_t nv,char nodiag,size_t memlimit) { #define CLEANUP CLEANVECF(p1)CLEANMATF(p2)CLEANMATF(p4)CLEANMATF(p5) @@ -286,7 +298,7 @@ int pij_gassist_trad(const MATRIXG* g,const MATRIXF* t,const MATRIXF* t2,MATRIXF p5=MATRIXFF(alloc)(ng,nt); if(!(p1&&p2&&p5&&p4)) ERRRET("Not enough memory.") - if(pijs_gassist_a(g,t,t2,p1,p2,ans,p4,p5,nv,nodiag,memlimit)) + if(pijs_gassist(g,t,t2,p1,p2,ans,p4,p5,nv,nodiag,memlimit)) ERRRET("pij_gassist_pijs failed.") //Combine tests diff --git a/pij/gassist/gassist.h b/pij/gassist/gassist.h index 66fd126..9cda131 100644 --- a/pij/gassist/gassist.h +++ b/pij/gassist/gassist.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * @@ -66,19 +66,17 @@ int pijs_gassist_pv(const MATRIXG* g,const MATRIXF* t,const MATRIXF* t2,VECTORF* * nt: Number of genes with expression data for B * ns: Number of samples. */ -int pijs_gassist_a(const MATRIXG* g,const MATRIXF* t,const MATRIXF* t2,VECTORF* p1,MATRIXF* p2,MATRIXF* p3,MATRIXF* p4,MATRIXF* p5,size_t nv,char nodiag,size_t memlimit); int pijs_gassist(const MATRIXG* g,const MATRIXF* t,const MATRIXF* t2,VECTORF* p1,MATRIXF* p2,MATRIXF* p3,MATRIXF* p4,MATRIXF* p5,size_t nv,char nodiag,size_t memlimit); -/* Estimates the probability of A->B from genotype and expression data with defaults combination of tests. Uses results from pijs_gassist_tot or pijs_gassist_a. Variables have the same definitions except: +/* Estimates the probability of A->B from genotype and expression data with defaults combination of tests. Uses results from pijs_gassist. Variables have the same definitions except: * ans: (ng,nt) Predicted probability of A->B based on default combination of 5 tests. The default combination is (p2*p5+p4)/2. Note: this combination does not include p1. * Return: 0 on sucess */ -int pij_gassist_a(const MATRIXG* g,const MATRIXF* t,const MATRIXF* t2,MATRIXF* ans,size_t nv,char nodiag,size_t memlimit); int pij_gassist(const MATRIXG* g,const MATRIXF* t,const MATRIXF* t2,MATRIXF* ans,size_t nv,char nodiag,size_t memlimit); /* Estimates the probability of A->B from genotype and expression data with traditional causal inference method. * NOTE: This is not and is not intended as a loyal reimplementation of the Trigger R package. Instead, it aims at reusing methods and tests of Findr to produce inferences that mimicks the three tests performed by Trigger. Many implementational details are different between this function and Trigger, althrough a significant (but not full) overlap has been observed in existing studies. This method does not include p1. - * Inputs and ouputs are the same as function pij_gassist_a. + * Inputs and ouputs are the same as function pij_gassist. */ int pij_gassist_trad(const MATRIXG* g,const MATRIXF* t,const MATRIXF* t2,MATRIXF* ans,size_t nv,char nodiag,size_t memlimit); diff --git a/pij/gassist/llr.c b/pij/gassist/llr.c index 0b200b1..56904de 100644 --- a/pij/gassist/llr.c +++ b/pij/gassist/llr.c @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * @@ -57,38 +57,6 @@ struct pij_gassist_llr_block_buffed_params{ MATRIXF** mb1; }; -void pij_gassist_llr_ratioandmean_1v1(const MATRIXG* g,const MATRIXF* t1,const MATRIXF* t2,MATRIXF* mratio,MATRIXF* mmean1,MATRIXF* mmean2,size_t nv) -{ - size_t ng=g->size1; - size_t ns=g->size2; - size_t i,j; - size_t val; - FTYPE f1; - - MATRIXFF(set_zero)(mratio); - MATRIXFF(set_zero)(mmean1); - MATRIXFF(set_zero)(mmean2); - - //Calculating sums - for(i=0;iB with A--B v.s. E->A<-B. A and B can have different sets of transcripts here. - * This is a more conservative version. - * g: MATRIXF (ng,ns) of genotype data - * t: MATRIXF (ng,ns) of transcript data for A - * t2: MATRIXF (nt,ns) of transcript data for B - * nv: number of possible values of g. - * llr2: MATRIXF (ng,nt) of output - * mratio: MATRIXF (nv,ng) of the ratio of samples for each SNP type. For buffer purpose. - * mmean: MATRIXF (nv,ng) of the means of each transcript among the samples of a specific SNP type. For buffer purpose. - * mmean2: MATRIXF[nv] (ng,nt) of the means of each transcript among the samples of a specific SNP type. For buffer purpose. - * mmb1: MATRIXF[nv] (ng,ns) Buffer matrix - * vb1: const VECTORF (ns). Must be set to 1 for all elements. - */ -//void pij_gassist_llr2c_buffed(const MATRIXG* g,const MATRIXF* t,const MATRIXF* tp,size_t nv,MATRIXF* llr2,MATRIXF* mratio,MATRIXF* mmean1,MATRIXF** mmean2,MATRIXF** mmb1,const VECTORF* vb1); - -//int pij_gassist_llr2c(const MATRIXG* g,const MATRIXF* t,const MATRIXF* t2,size_t nv,MATRIXF* llr2); - -/* Calculates the log likelihood ratio of step 2 when the transcripts of those with eQTLs are different with those - * to be tested against: A<-E->B with A--B v.s. E->A. A and B can have different sets of transcripts here. - * This is a less conservative version because it does not exclude B->A. - * g: MATRIXF (ng,ns) of genotype data - * t: MATRIXF (ng,ns) of transcript data for A - * t2: MATRIXF (nt,ns) of transcript data for B - * nv: number of possible values of g. - * llr2: MATRIXF (ng,nt) of output - * mratio: MATRIXF (nv,ng) of the ratio of samples for each SNP type. For buffer purpose. - * mmean: MATRIXF (nv,ng) of the means of each transcript among the samples of a specific SNP type. For buffer purpose. - * mmean2: MATRIXF[nv] (ng,nt) of the means of each transcript among the samples of a specific SNP type. For buffer purpose. - * mmb1: MATRIXF[nv] (ng,ns) Buffer matrix - * mmb2: MATRIXF[nv] (ng,nt) Buffer matrix - * mb1: MATRIXF (ng,nt). Buffer matrix - * mb2: MATRIXF (ng,nt). Buffer matrix - * mb3: MATRIXF (ng,nt). Buffer matrix - * vb1: const VECTORF (ns). Must be set to 1 for all elements. - * vb2: VECTORF (ng). Buffer vector - */ -//void pij_gassist_llr2b_buffed(const MATRIXG* g,const MATRIXF* t,const MATRIXF* tp,size_t nv,MATRIXF* llr2,MATRIXF* mratio,MATRIXF* mmean1,MATRIXF** mmean2,MATRIXF** mmb1,MATRIXF** mmb2,MATRIXF* mb1,MATRIXF* mb2,MATRIXF* mb3,const VECTORF* vb1,VECTORF* vb2); - -/* Calculates the log likelihood ratio of step 2 when the transcripts of those with eQTLs are different with those - * to be tested against: A<-E->B with A--B v.s. E->A<-B. A and B can have different sets of transcripts here. - * g: MATRIXF (ng,ns) of genotype data - * t: MATRIXF (ng,ns) of transcript data for A - * t2: MATRIXF (nt,ns) of transcript data for B - * nv: number of possible values of g. - * llr2: MATRIXF (ng,nt) of output - * Return: 0 on success - */ -//int pij_gassist_llr2b(const MATRIXG* g,const MATRIXF* t,const MATRIXF* t2,size_t nv,MATRIXF* llr2); - -//#define pij_gassist_llr2 pij_gassist_llr2_bold -//#define pij_gassist_llr2_buffed pij_gassist_llr2_bold_buffed - -/* Calculate log likelihood ratio of step 3 for single (E,A,B): - * log likelihood(E->A->B)-log likelihood(A<-E->B with A--B) - * fcov: covariance (A,B) for each B - * vratio: (nv) categorical ratio of each value of g - * vmean1: (nv) categorical mean of A - * vmean2: (nv) categorical mean of B - * vb1: (nv) buffer vector f_alpha mu_{alpha 1} - * Return: log likelihood ratio - */ -//FTYPE pij_gassist_llr3_one_from_stats_buffed(FTYPE fcov,const VECTORF* vratio,const VECTORF* vmean1,const VECTORF* vmean2,VECTORF* vb1); - -/* Calculate log likelihood ratio of step 3 for single (E,A) but multiple B: - * log likelihood(E->A->B)-log likelihood(A<-E->B with A--B) - * g: (ns) genotype data (E), each=0,1,...,nv-1 - * t1: (ns) Supernormalizedtranscript data for A - * t2: (nt,ns) Supernormalized transcript data for Bs - * nv: Number of values each entry of g can take. - * llr3: (nt) output buffer for calculated llr - * vb1: (ns) constant buffer, must be set to all 1 initially - * vratio: (nv) categorical ratio of each value of g - * vmean1: (nv) categorical mean of A - * mmean: (nt,nv) categorical mean of each B for each value of g - * vcov: (nt) covariance (A,B) for each B - * vb2: (nv) buffer vector f_alpha mu_{alpha 1} - * vb3: (nt) buffer matrix for f_alpha mu_{alpha 2} - * mb1: (nv,ns) buffer matrix of expanded representation of sample category - */ -//void pij_gassist_llr3_E1_buffed(const VECTORG* g,const VECTORF* t1,const MATRIXF* t2,size_t nv,VECTORF* llr3,const VECTORF* vb1,VECTORF *vratio,VECTORF* vmean1,MATRIXF *mmean,VECTORF *vcov,VECTORF *vb2,VECTORF *vb3,MATRIXF *mb1); - -/* Calculates log likelihood ratio for block of step 3 with buffer provided - * g: MATRIXF (ng,ns) Full genotype data matrix - * t: MATRIXF (ng,ns) Supernormalized transcript data matrix for A - * t2: MATRIXF (nt,ns) Supernormalized transcript data matrix for B - * nv: Number of possible values for each genotype - * llr3: MATRIXF (ng,nt). Log likelihood ratios for test 3. - * vb1: VECTORF (ns). Constant buffer vector, must be set to 1 initially. - * mratio: MATRIXF (nv,ng). Buffer matrix for categorical ratio - * mmean1: MATRIXF (nv,ng). Buffer matrix set for categorical mean - * mmean2: MATRIXF[nv] (ng,nt). Buffer matrix set for categorical mean - * mcov: MATRIXF (ng,nt). Buffer covariance matrix - * mmb1: MATRIXF[nv] (ng,nt). Buffer matrix - * mmb2: MATRIXF[nv] (ng,ns). Buffer matrix - */ -//void pij_gassist_llr3_buffed(const MATRIXG* g,const MATRIXF* t,const MATRIXF* t2,size_t nv,MATRIXF* llr3,const VECTORF* vb1,MATRIXF *mratio,MATRIXF *mmean1,MATRIXF **mmean2,MATRIXF *mcov,MATRIXF **mmb1,MATRIXF **mmb2); - /* Multithread calculation of log likelihood ratios for 5 tests. * g: MATRIXF (ng,ns) Full genotype data matrix * t: MATRIXF (ng,ns) Supernormalized transcript data matrix of A @@ -172,6 +62,10 @@ int pij_gassist_llr(const MATRIXG* g,const MATRIXF* t,const MATRIXF* t2,VECTORF* + + + + diff --git a/pij/gassist/llrtopij.c b/pij/gassist/llrtopij.c index c81bf49..4a826a7 100644 --- a/pij/gassist/llrtopij.c +++ b/pij/gassist/llrtopij.c @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * @@ -18,22 +18,210 @@ #include "../../base/config.h" #include #include -#include -#include #include +#include "../../base/gsl/math.h" +#include "../../base/gsl/histogram.h" +#include "../../base/gsl/blas.h" #include "../../base/logger.h" +#include "../../base/threading.h" #include "../../base/macros.h" #include "../../base/data_process.h" +#include "../llrtopij.h" #include "llrtopij.h" -int pij_gassist_llrtopij1_1(VECTORF* p1) + +/* Always return probability of step 1 is 1. This is useful when best eQTL are already selected in advance. + */ +static inline int pij_gassist_llrtopij1_1(VECTORF* p1) { LOG(9,"Converting LLR to probabilities for step 1. Filling with 1.") VECTORFF(set_all)(p1,1); return 0; } +/* Convert real log likelihood ratios into probability functions. + * This function converts every A in hypothesis (E->A->B) separately. + * Suppose there are ng (E,A) pairs and nt Bs, this function converts ng times, + * each for one (E,A) pair but all Bs. + * d: (ng,nt) Input log likelihood ratios for construction of + * histograms and calculation of probability of true hypothesis. + * g: (ng,ns) Original genotype matrix, used for analytical calculation + * of null distribution. Every element=0,1,...,nv-1. + * h: [nv-1]. Null histogram of the specific test. + * Output of pij_nullhist. + * nv: Maximum number of values each g may take. + * nodiag: If diagonal elements of d should be removed in construction of real + * histogram. This should be set to true (!=0) when t is identical with + * the top rows of t2 (in calculation of llr). + * Return: 0 if success. + */ +static int pij_gassist_llrtopij_convert_self(MATRIXF* d,const MATRIXG* g,const gsl_histogram * const * h, size_t nv,char nodiag,long nodiagshift) +{ +#define CLEANUP CLEANVECG(vcount)CLEANAMHIST(hreal,nth)CLEANAMHIST(hc,nth)\ + CLEANMATD(mb1)CLEANMATD(mb2)CLEANMATD(mnull)CLEANMATF(mb3)CLEANVECD(vwidth) + + VECTORG *vcount; + size_t ng=g->size1; + size_t i,nbin; + //gsl_histogram **hreal,**hc; + MATRIXD *mb1,*mb2,*mnull; + MATRIXF *mb3; + VECTORD *vwidth; + VECTORDF(view) vv1; + size_t nth; + + mb1=mb2=mnull=0; + mb3=0; + vwidth=0; + vcount=0; + //Validity checks + { + int nth0=omp_get_max_threads(); + assert(nth0>0); + nth=(size_t)nth0; + } + + + AUTOCALLOC(gsl_histogram*,hreal,nth,64) + AUTOCALLOC(gsl_histogram*,hc,nth,64) + if(!(hreal&&hc)) + ERRRET("Not enough memory."); + + //Construct null density histograms + nbin=h[0]->n; + //Memory allocation + { + size_t n1,n2; + pij_llrtopij_convert_histograms_get_buff_sizes(nbin,&n1,&n2); + mb1=MATRIXDF(alloc)(nth,n1); + mb2=MATRIXDF(alloc)(nth,n2); + mnull=MATRIXDF(alloc)(nth,nbin); + mb3=MATRIXFF(alloc)(nth,d->size2); + vwidth=VECTORDF(alloc)(nbin); + if(!(mb1&&mb2&&mnull&&mb3&&vwidth)) + ERRRET("Not enough memory.") + } + + //Prepare for real histogram + { + int ret; + for(i=0,ret=1;in+2); + ret=ret&&hreal[i]&&hc[i]; + } + vcount=VECTORGF(alloc)(ng); + if(!(ret&&vcount)) + ERRRET("Not enough memory."); + } + + { + VECTORUC *vb4=VECTORUCF(alloc)(nv); + if(!vb4) + ERRRET("Not enough memory."); + MATRIXGF(countv_byrow_buffed)(g,vcount,vb4); + CLEANVECUC(vb4) + } + + //Conversion + for(i=2;i<=nv;i++) + { + vv1=VECTORDF(view_array)(h[i-2]->range+1,nbin); + VECTORDF(memcpy)(vwidth,&vv1.vector); + vv1=VECTORDF(view_array)(h[i-2]->range,nbin); + VECTORDF(sub)(vwidth,&vv1.vector); + vv1=VECTORDF(view_array)(h[i-2]->bin,nbin); + #pragma omp parallel + { + size_t ng1,ng2,id; + size_t j; + long k; + VECTORDF(view) vvreal,vvnull,vvb1,vvb2; + VECTORFF(view) vvb3,vva; + + id=(size_t)omp_get_thread_num(); + vvreal=VECTORDF(view_array)(hreal[id]->bin,nbin); + vvnull=MATRIXDF(row)(mnull,id); + vvb1=MATRIXDF(row)(mb1,id); + vvb2=MATRIXDF(row)(mb2,id); + vvb3=MATRIXFF(row)(mb3,id); + threading_get_startend(ng,&ng1,&ng2); + + for(j=ng1;jrange,h[i-2]->range,(nbin+1)*sizeof(*hreal[id]->range)); + memset(hreal[id]->bin,0,nbin*sizeof(*hreal[id]->bin)); + //Construct real histogram + if(nodiag&&((long)j+nodiagshift>=0)&&((long)j+nodiagshift<(long)d->size2)) + { + for(k=(long)j+nodiagshift-1;k>=0;k--) + gsl_histogram_increment(hreal[id],MATRIXFF(get)(d,j,(size_t)k)); + for(k=(long)j+nodiagshift+1;k<(long)d->size2;k++) + gsl_histogram_increment(hreal[id],MATRIXFF(get)(d,j,(size_t)k)); + VECTORDF(scale)(&vvreal.vector,1./(double)(d->size2-1)); + } + else + { + for(k=0;k<(long)d->size2;k++) + gsl_histogram_increment(hreal[id],MATRIXFF(get)(d,j,(size_t)k)); + VECTORDF(scale)(&vvreal.vector,1./(double)(d->size2)); + } + + //Convert to density histogram + VECTORDF(div)(&vvreal.vector,vwidth); + //Convert to probability central histogram + pij_llrtopij_convert_histograms_buffed(hreal[id],&vvnull.vector,hc[id],&vvb1.vector,&vvb2.vector); + //Convert likelihoods to probabilities + vva=MATRIXFF(row)(d,j); + pij_llrtopij_histogram_interpolate_linear(hc[id],&vvb3.vector,&vva.vector); + } + } + } + CLEANUP + return 0; +#undef CLEANUP +} + +int pij_gassist_llrtopijs(const MATRIXG* g,VECTORF* p1,MATRIXF* p2,MATRIXF* p3,MATRIXF* p4,MATRIXF* p5,size_t nv,const gsl_histogram * const * h[4],char nodiag,long nodiagshift) +{ + int ret=0,ret2=0; + if(g->size2<=3) + { + LOG(0,"Needs at least 4 samples to compute probabilities.") + return 1; + } + ret=ret||(ret2=pij_gassist_llrtopij_convert_self(p2,g,h[0],nv,nodiag,nodiagshift)); + if(ret2) + LOG(1,"Failed to log likelihood ratios to probabilities in step 2.") + //For p1, if nodiag, copy p2 data, otherwise set all to 1. + if(nodiag) + { + VECTORFF(view) vv; + vv=MATRIXFF(superdiagonal)(p2,(size_t)nodiagshift); + ret=(ret2=VECTORFF(memcpy)(p1,&vv.vector)); + } + else + ret=(ret2=pij_gassist_llrtopij1_1(p1)); + if(ret2) + LOG(1,"Failed to log likelihood ratios to probabilities in step 1.") + ret=ret||(ret2=pij_gassist_llrtopij_convert_self(p3,g,h[1],nv,nodiag,nodiagshift)); + if(ret2) + LOG(1,"Failed to log likelihood ratios to probabilities in step 3.") + MATRIXFF(scale)(p3,-1); + MATRIXFF(add_constant)(p3,1); + ret=ret||(ret2=pij_gassist_llrtopij_convert_self(p4,g,h[2],nv,nodiag,nodiagshift)); + if(ret2) + LOG(1,"Failed to log likelihood ratios to probabilities in step 4.") + ret=ret||(ret2=pij_gassist_llrtopij_convert_self(p5,g,h[3],nv,nodiag,nodiagshift)); + if(ret2) + LOG(1,"Failed to log likelihood ratios to probabilities in step 5.") + return ret; +} diff --git a/pij/gassist/llrtopij.h b/pij/gassist/llrtopij.h index a1a8a91..53b51b9 100644 --- a/pij/gassist/llrtopij.h +++ b/pij/gassist/llrtopij.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * @@ -24,17 +24,20 @@ #include "../../base/config.h" #include "../../base/gsl/histogram.h" #include "../../base/types.h" -#include "llrtopij_tot.h" -#include "llrtopij_a.h" #ifdef __cplusplus extern "C" { #endif -/* Always return probability of step 1 is 1. This is useful when best eQTL are already selected in advance. + +/* Converts four LLRs into probabilities together. + * Uses pij_gassist_llrtopij1 to pij_gassist_llrtopij5. + * See above functions for parameter definitions. + * h: Null histograms. 0 to 3 for tests 2 to 5. + * Return: 0 if all functions are successful. */ -int pij_gassist_llrtopij1_1(VECTORF* p1); +int pij_gassist_llrtopijs(const MATRIXG* g,VECTORF* p1,MATRIXF* p2,MATRIXF* p3,MATRIXF* p4,MATRIXF* p5,size_t nv,const gsl_histogram * const * h[4],char nodiag,long nodiagshift); diff --git a/pij/gassist/llrtopij_a.c b/pij/gassist/llrtopij_a.c deleted file mode 100644 index 1dccc25..0000000 --- a/pij/gassist/llrtopij_a.c +++ /dev/null @@ -1,430 +0,0 @@ -/* Copyright 2016, 2017 Lingfei Wang - * - * This file is part of Findr. - * - * Findr is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Findr is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Findr. If not, see . - */ -#include "../../base/config.h" -#include -#include -#include -#include "../../base/gsl/math.h" -#include "../../base/gsl/histogram.h" -#include "../../base/gsl/blas.h" -#include "../../base/logger.h" -#include "../../base/threading.h" -#include "../../base/macros.h" -#include "../../base/data_process.h" -#include "../../base/histogram.h" -#include "../nulldist.h" -#include "../llrtopij.h" -#include "llrtopij_a.h" -#include "llrtopij.h" -#pragma GCC diagnostic ignored "-Wunused-parameter" - -int pij_gassist_llrtopij_a_convert(const MATRIXF* d,const MATRIXF* dconv,MATRIXF* ans,const MATRIXG* g,size_t nv,long n1c,size_t n1d,long n2c,size_t n2d,char nodiag,long nodiagshift) -{ -#define CLEANUP CLEANVECG(vcount)CLEANAMHIST(hreal,nth)CLEANAMHIST(hc,nth)\ - CLEANMHIST(h,(nv-1))CLEANMATD(mb1)CLEANMATD(mb2)CLEANMATD(mnull)CLEANVECD(vwidth) - - VECTORG *vcount; - size_t ng=g->size1; - size_t i,nbin; - gsl_histogram **h; - //gsl_histogram **hreal,**hc; - MATRIXD *mb1,*mb2,*mnull; - VECTORD *vwidth; - VECTORDF(view) vv1; - size_t nth; - - h=0; - mb1=mb2=mnull=0; - vwidth=0; - vcount=0; - //Validity checks - assert((dconv->size1==ng)&&(ans->size1==ng)&&(d->size1==ng)&&(dconv->size2==ans->size2)); - - { - int nth0=omp_get_max_threads(); - assert(nth0>0); - nth=(size_t)nth0; - } - - - AUTOCALLOC(gsl_histogram*,hreal,nth,64) - AUTOCALLOC(gsl_histogram*,hc,nth,64) - if(!(hreal&&hc)) - ERRRET("Not enough memory."); - - //Construct null density histograms - { - FTYPE dmin,dmax; - MATRIXFF(minmax)(d,&dmin,&dmax); - if((!(dmin>=0))||gsl_isnan(dmax)||gsl_isinf(dmax)) - ERRRET("Negative or NAN found in input data. It may invalidate follow up analysis. This may be due to incorrect previous steps.") - h=pij_llrtopij_a_nullhist((double)dmax,nv,d->size2,n1c,n1d,n2c,n2d); - if(!h) - ERRRET("pij_llrtopij_a_nullhist failed.") - nbin=h[0]->n; - } - //Memory allocation - { - size_t n1,n2; - pij_llrtopij_convert_histograms_get_buff_sizes(nbin,&n1,&n2); - mb1=MATRIXDF(alloc)(nth,n1); - mb2=MATRIXDF(alloc)(nth,n2); - mnull=MATRIXDF(alloc)(nth,nbin); - vwidth=VECTORDF(alloc)(nbin); - if(!(mb1&&mb2&&mnull&&vwidth)) - ERRRET("Not enough memory.") - } - - //Prepare for real histogram - { - int ret; - for(i=0,ret=1;in+2); - ret=ret&&hreal[i]&&hc[i]; - } - vcount=VECTORGF(alloc)(ng); - if(!(ret&&vcount)) - ERRRET("Not enough memory."); - } - - { - VECTORUC *vb4=VECTORUCF(alloc)(nv); - if(!vb4) - ERRRET("Not enough memory."); - MATRIXGF(countv_byrow_buffed)(g,vcount,vb4); - CLEANVECUC(vb4) - } - - - //Conversion - vv1=VECTORDF(view_array)(h[0]->range+1,nbin); - VECTORDF(memcpy)(vwidth,&vv1.vector); - vv1=VECTORDF(view_array)(h[0]->range,nbin); - VECTORDF(sub)(vwidth,&vv1.vector); - - - //Conversion - for(i=2;i<=nv;i++) - { - vv1=VECTORDF(view_array)(h[i-2]->range+1,nbin); - VECTORDF(memcpy)(vwidth,&vv1.vector); - vv1=VECTORDF(view_array)(h[i-2]->range,nbin); - VECTORDF(sub)(vwidth,&vv1.vector); - vv1=VECTORDF(view_array)(h[i-2]->bin,nbin); - #pragma omp parallel - { - size_t ng1,ng2,id; - size_t j; - long k; - VECTORDF(view) vvreal,vvnull,vvb1,vvb2; - VECTORFF(view) vva; - - id=(size_t)omp_get_thread_num(); - vvreal=VECTORDF(view_array)(hreal[id]->bin,nbin); - vvnull=MATRIXDF(row)(mnull,id); - vvb1=MATRIXDF(row)(mb1,id); - vvb2=MATRIXDF(row)(mb2,id); - threading_get_startend(ng,&ng1,&ng2); - - for(j=ng1;jrange,h[i-2]->range,(nbin+1)*sizeof(*hreal[id]->range)); - memset(hreal[id]->bin,0,nbin*sizeof(*hreal[id]->bin)); - //Construct real histogram - if(nodiag&&((long)j+nodiagshift>=0)&&((long)j+nodiagshift<(long)d->size2)) - { - for(k=(long)j+nodiagshift-1;k>=0;k--) - gsl_histogram_increment(hreal[id],MATRIXFF(get)(d,j,(size_t)k)); - for(k=(long)j+nodiagshift+1;k<(long)d->size2;k++) - gsl_histogram_increment(hreal[id],MATRIXFF(get)(d,j,(size_t)k)); - VECTORDF(scale)(&vvreal.vector,1./(double)(d->size2-1)); - } - else - { - for(k=0;k<(long)d->size2;k++) - gsl_histogram_increment(hreal[id],MATRIXFF(get)(d,j,(size_t)k)); - VECTORDF(scale)(&vvreal.vector,1./(double)(d->size2)); - } - - //Convert to density histogram - VECTORDF(div)(&vvreal.vector,vwidth); - //Convert to probability central histogram - pij_llrtopij_convert_histograms_buffed(hreal[id],&vvnull.vector,hc[id],&vvb1.vector,&vvb2.vector); - //Convert likelihoods to probabilities - vva=MATRIXFF(row)(ans,j); - pij_llrtopij_histogram_interpolate_linear(hc[id],&vvd.vector,&vva.vector); - } - } - } - CLEANUP - return 0; -#undef CLEANUP -} - -int pij_gassist_llrtopij_a_convert_self(MATRIXF* d,const MATRIXG* g,size_t nv,long n1c,size_t n1d,long n2c,size_t n2d,char nodiag,long nodiagshift) -{ -#define CLEANUP CLEANVECG(vcount)CLEANAMHIST(hreal,nth)CLEANAMHIST(hc,nth)\ - CLEANMHIST(h,(nv-1))CLEANMATD(mb1)CLEANMATD(mb2)CLEANMATD(mnull)CLEANMATF(mb3)CLEANVECD(vwidth) - - VECTORG *vcount; - size_t ng=g->size1; - size_t i,nbin; - gsl_histogram **h; - //gsl_histogram **hreal,**hc; - MATRIXD *mb1,*mb2,*mnull; - MATRIXF *mb3; - VECTORD *vwidth; - VECTORDF(view) vv1; - size_t nth; - - h=0; - mb1=mb2=mnull=0; - mb3=0; - vwidth=0; - vcount=0; - //Validity checks - { - int nth0=omp_get_max_threads(); - assert(nth0>0); - nth=(size_t)nth0; - } - - - AUTOCALLOC(gsl_histogram*,hreal,nth,64) - AUTOCALLOC(gsl_histogram*,hc,nth,64) - if(!(hreal&&hc)) - ERRRET("Not enough memory."); - - //Construct null density histograms - { - FTYPE dmin,dmax; - if(nodiag) - MATRIXFF(minmax_nodiag)(d,&dmin,&dmax,nodiagshift); - else - MATRIXFF(minmax)(d,&dmin,&dmax); - if((!(dmin>=0))||gsl_isnan(dmax)) - ERRRET("Negative or NAN found in input data. It may invalidate follow up analysis. This may be due to incorrect previous steps.") - if(gsl_isinf(dmax)) - { - LOG(5,"INF found in input data. It may invalidate follow up analysis. This may be due to incorrect previous steps or duplicate rows (by Spearman correlation).") - MATRIXFF(set_inf)(d,-1); - if(nodiag) - MATRIXFF(minmax_nodiag)(d,&dmin,&dmax,nodiagshift); - else - MATRIXFF(minmax)(d,&dmin,&dmax); - MATRIXFF(set_value)(d,-1,dmax); - } - h=pij_llrtopij_a_nullhist((double)dmax,nv,d->size2,n1c,n1d,n2c,n2d); - if(!h) - ERRRET("pij_llrtopij_a_nullhist failed.") - nbin=h[0]->n; - } - //Memory allocation - { - size_t n1,n2; - pij_llrtopij_convert_histograms_get_buff_sizes(nbin,&n1,&n2); - mb1=MATRIXDF(alloc)(nth,n1); - mb2=MATRIXDF(alloc)(nth,n2); - mnull=MATRIXDF(alloc)(nth,nbin); - mb3=MATRIXFF(alloc)(nth,d->size2); - vwidth=VECTORDF(alloc)(nbin); - if(!(mb1&&mb2&&mnull&&mb3&&vwidth)) - ERRRET("Not enough memory.") - } - - //Prepare for real histogram - { - int ret; - for(i=0,ret=1;in+2); - ret=ret&&hreal[i]&&hc[i]; - } - vcount=VECTORGF(alloc)(ng); - if(!(ret&&vcount)) - ERRRET("Not enough memory."); - } - - { - VECTORUC *vb4=VECTORUCF(alloc)(nv); - if(!vb4) - ERRRET("Not enough memory."); - MATRIXGF(countv_byrow_buffed)(g,vcount,vb4); - CLEANVECUC(vb4) - } - - //Conversion - for(i=2;i<=nv;i++) - { - vv1=VECTORDF(view_array)(h[i-2]->range+1,nbin); - VECTORDF(memcpy)(vwidth,&vv1.vector); - vv1=VECTORDF(view_array)(h[i-2]->range,nbin); - VECTORDF(sub)(vwidth,&vv1.vector); - vv1=VECTORDF(view_array)(h[i-2]->bin,nbin); - #pragma omp parallel - { - size_t ng1,ng2,id; - size_t j; - long k; - VECTORDF(view) vvreal,vvnull,vvb1,vvb2; - VECTORFF(view) vvb3,vva; - - id=(size_t)omp_get_thread_num(); - vvreal=VECTORDF(view_array)(hreal[id]->bin,nbin); - vvnull=MATRIXDF(row)(mnull,id); - vvb1=MATRIXDF(row)(mb1,id); - vvb2=MATRIXDF(row)(mb2,id); - vvb3=MATRIXFF(row)(mb3,id); - threading_get_startend(ng,&ng1,&ng2); - - for(j=ng1;jrange,h[i-2]->range,(nbin+1)*sizeof(*hreal[id]->range)); - memset(hreal[id]->bin,0,nbin*sizeof(*hreal[id]->bin)); - //Construct real histogram - if(nodiag&&((long)j+nodiagshift>=0)&&((long)j+nodiagshift<(long)d->size2)) - { - for(k=(long)j+nodiagshift-1;k>=0;k--) - gsl_histogram_increment(hreal[id],MATRIXFF(get)(d,j,(size_t)k)); - for(k=(long)j+nodiagshift+1;k<(long)d->size2;k++) - gsl_histogram_increment(hreal[id],MATRIXFF(get)(d,j,(size_t)k)); - VECTORDF(scale)(&vvreal.vector,1./(double)(d->size2-1)); - } - else - { - for(k=0;k<(long)d->size2;k++) - gsl_histogram_increment(hreal[id],MATRIXFF(get)(d,j,(size_t)k)); - VECTORDF(scale)(&vvreal.vector,1./(double)(d->size2)); - } - - //Convert to density histogram - VECTORDF(div)(&vvreal.vector,vwidth); - //Convert to probability central histogram - pij_llrtopij_convert_histograms_buffed(hreal[id],&vvnull.vector,hc[id],&vvb1.vector,&vvb2.vector); - //Convert likelihoods to probabilities - vva=MATRIXFF(row)(d,j); - pij_llrtopij_histogram_interpolate_linear(hc[id],&vvb3.vector,&vva.vector); - } - } - } - CLEANUP - return 0; -#undef CLEANUP -} - -/* Functions to convert LLR of specific steps into probabilities. - * Uses pij_llrtopij_a_convert with different settings of n1d and n2d. - * Function name suffices indicate which LLR to convert. - */ -static inline int pij_gassist_llrtopij1_a(MATRIXF* d,const MATRIXG* g,size_t nv,char nodiag,long nodiagshift) -{ - LOG(9,"Converting LLR to probabilities for step 1 on per A basis.") - assert(g->size2>2); - return pij_gassist_llrtopij_a_convert_self(d,g,nv,1,1,1,g->size2-2,nodiag,nodiagshift); -} - -static inline int pij_gassist_llrtopij2_a(MATRIXF* d,const MATRIXG* g,size_t nv,char nodiag,long nodiagshift) -{ - LOG(9,"Converting LLR to probabilities for step 2 on per A basis.") - assert(g->size2>2); - return pij_gassist_llrtopij_a_convert_self(d,g,nv,1,1,1,g->size2-2,nodiag,nodiagshift); -} - -static inline int pij_gassist_llrtopij3_a(MATRIXF* d,const MATRIXG* g,size_t nv,char nodiag,long nodiagshift) -{ - LOG(9,"Converting LLR to probabilities for step 3 on per A basis.") - assert(g->size2>3); - if(pij_gassist_llrtopij_a_convert_self(d,g,nv,1,1,1,g->size2-3,nodiag,nodiagshift)) - return 1; - MATRIXFF(scale)(d,-1); - MATRIXFF(add_constant)(d,1); - return 0; -} - -static inline int pij_gassist_llrtopij4_a(MATRIXF* d,const MATRIXG* g,size_t nv,char nodiag,long nodiagshift) -{ - LOG(9,"Converting LLR to probabilities for step 4 on per A basis.") - assert(g->size2>3); - return pij_gassist_llrtopij_a_convert_self(d,g,nv,1,2,1,g->size2-3,nodiag,nodiagshift); -} - -static inline int pij_gassist_llrtopij5_a(MATRIXF* d,const MATRIXG* g,size_t nv,char nodiag,long nodiagshift) -{ - LOG(9,"Converting LLR to probabilities for step 5 on per A basis.") - assert(g->size2>3); - if(pij_gassist_llrtopij_a_convert_self(d,g,nv,0,1,1,g->size2-3,nodiag,nodiagshift)) - return 1; - return 0; -} - - -int pij_gassist_llrtopijs_a(const MATRIXG* g,VECTORF* p1,MATRIXF* p2,MATRIXF* p3,MATRIXF* p4,MATRIXF* p5,size_t nv,char nodiag,long nodiagshift) -{ - int ret=0,ret2=0; - if(g->size2<=3) - { - LOG(0,"Needs at least 4 samples to compute probabilities.") - return 1; - } - ret=ret||(ret2=pij_gassist_llrtopij2_a(p2,g,nv,0,0)); - if(ret2) - LOG(1,"Failed to log likelihood ratios to probabilities in step 2.") - //For p1, if nodiag, copy p2 data, otherwise set all to 1. - if(nodiag) - { - VECTORFF(view) vv; - vv=MATRIXFF(superdiagonal)(p2,(size_t)nodiagshift); - ret=(ret2=VECTORFF(memcpy)(p1,&vv.vector)); - } - else - ret=(ret2=pij_gassist_llrtopij1_1(p1)); - if(ret2) - LOG(1,"Failed to log likelihood ratios to probabilities in step 1.") - ret=ret||(ret2=pij_gassist_llrtopij3_a(p3,g,nv,nodiag,nodiagshift)); - if(ret2) - LOG(1,"Failed to log likelihood ratios to probabilities in step 3.") - ret=ret||(ret2=pij_gassist_llrtopij4_a(p4,g,nv,nodiag,nodiagshift)); - if(ret2) - LOG(1,"Failed to log likelihood ratios to probabilities in step 4.") - ret=ret||(ret2=pij_gassist_llrtopij5_a(p5,g,nv,nodiag,nodiagshift)); - if(ret2) - LOG(1,"Failed to log likelihood ratios to probabilities in step 5.") - return ret; -} - - - - - - - - - - - diff --git a/pij/gassist/llrtopij_a.h b/pij/gassist/llrtopij_a.h deleted file mode 100644 index 0c75c77..0000000 --- a/pij/gassist/llrtopij_a.h +++ /dev/null @@ -1,107 +0,0 @@ -/* Copyright 2016, 2017 Lingfei Wang - * - * This file is part of Findr. - * - * Findr is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Findr is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Findr. If not, see . - */ -/* This file contains the conversion from log likelihood ratio to probabilities - * - */ - -#ifndef _HEADER_LIB_PIJ_GASSIST_LLRTOPIJ_A_H_ -#define _HEADER_LIB_PIJ_GASSIST_LLRTOPIJ_A_H_ -#include "../../base/config.h" -#include "../../base/types.h" -#include "../llrtopij_a.h" -#ifdef __cplusplus -extern "C" -{ -#endif - -/* Convert real log likelihood ratios into probability functions. - * This function converts every A in hypothesis (E->A->B) separately. - * Suppose there are ng (E,A) pairs and nt Bs, this function converts ng times, - * each for one (E,A) pair but all Bs. - * d: (ng,nt) Input log likelihood ratios for construction of - * histograms and calculation of probability of true hypothesis. - * dconv: (ng,nx) Actual log likelihood ratios to be converted to - * probabilities using histogram constructed base on d. - * ans: (ng,nx) Output matrix for probabilities. - * g: (ng,ns) Original genotype matrix, used for analytical calculation - * of null distribution. Every element=0,1,...,nv-1. - * nv: Maximum number of values each g may take. - * n1c, - * n1d, - * n2c, - * n2d: Parameters to specify null distribution. See pij_llrtopij_a_nullhist - * nodiag: If diagonal elements of d should be removed in construction of real - * histogram. This should be set to true (!=0) when t is identical with - * the top rows of t2 (in calculation of llr). - * Return: 0 if success. - */ -int pij_gassist_llrtopij_a_convert(const MATRIXF* d,const MATRIXF* dconv,MATRIXF* ans,const MATRIXG* g,size_t nv,long n1c,size_t n1d,long n2c,size_t n2d,char nodiag,long nodiagshift); - -int pij_gassist_llrtopij_a_convert_self(MATRIXF* d,const MATRIXG* g,size_t nv,long n1c,size_t n1d,long n2c,size_t n2d,char nodiag,long nodiagshift); - -/* Converts four LLRs into probabilities together. - * Uses pij_gassist_llrtopij1_a to pij_gassist_llrtopij5_a. - * See above functions for parameter definitions. - * Return: 0 if all functions are successful. - */ -int pij_gassist_llrtopijs_a(const MATRIXG* g,VECTORF* p1,MATRIXF* p2,MATRIXF* p3,MATRIXF* p4,MATRIXF* p5,size_t nv,char nodiag,long nodiagshift); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#ifdef __cplusplus -} -#endif -#endif diff --git a/pij/gassist/llrtopij_tot.c b/pij/gassist/llrtopij_tot.c deleted file mode 100644 index 2a65e40..0000000 --- a/pij/gassist/llrtopij_tot.c +++ /dev/null @@ -1,184 +0,0 @@ -/* Copyright 2016, 2017 Lingfei Wang - * - * This file is part of Findr. - * - * Findr is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Findr is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Findr. If not, see . - */ -#include "../../base/config.h" -#include -#include -#include -#include -#include -#include "../../base/gsl/math.h" -#include "../../base/gsl/histogram.h" -#include "../../base/gsl/blas.h" -#include "../../base/logger.h" -#include "../../base/macros.h" -#include "../../base/data_process.h" -#include "../../base/threading.h" -#include "../../base/histogram.h" -#include "../nullhist.h" -#include "nullhist.h" -#include "llrtopij_tot.h" -#include "llrtopij.h" -#pragma GCC diagnostic ignored "-Wunused-parameter" - - -int pij_gassist_llrtopij1_tot(const MATRIXG* g,const VECTORF* llr,VECTORF* p,size_t nv,double* nratio) -{ - const struct histogram_unequalbins_exp_param ph={g->size2-nv,1}; - const struct pij_gassist_nullhist_analytical_pdf_param pnh={g,nv,5}; - return pij_llrtopij_tot_convert(llr,llr,p,histogram_unequalbins_exp,pij_gassist_nullhist_analytical1_pdf,&ph,&pnh,nratio); -} - -int pij_gassist_llrtopij2_tot(const MATRIXG* g,const VECTORF* llr,VECTORF* p,size_t nv,double* nratio) -{ - const struct histogram_unequalbins_exp_param ph={g->size2-nv,1}; - const struct pij_gassist_nullhist_analytical_pdf_param pnh={g,nv,5}; - return pij_llrtopij_tot_convert(llr,llr,p,histogram_unequalbins_exp,pij_gassist_nullhist_analytical2_pdf,&ph,&pnh,nratio); -} - -int pij_gassist_llrtopij3_tot(const MATRIXG* g,const VECTORF* llr,VECTORF* p,size_t nv,double* nratio) -{ -#define CLEANUP - const struct histogram_unequalbins_exp_param ph={g->size2-nv,1}; - const struct pij_gassist_nullhist_analytical_pdf_param pnh={g,nv,5}; - if(pij_llrtopij_tot_convert(llr,llr,p,histogram_unequalbins_exp,pij_gassist_nullhist_analytical3_pdf,&ph,&pnh,nratio)) - ERRRET("Conversion failed.") - VECTORFF(scale)(p,-1); - VECTORFF(add_constant)(p,1); - return 0; -#undef CLEANUP -} - -int pij_gassist_llrtopij4_tot(const MATRIXG* g,const VECTORF* llr,VECTORF* p,size_t nv,double* nratio) -{ - const struct histogram_unequalbins_exp_param ph={g->size2-nv,1}; - const struct pij_gassist_nullhist_analytical_pdf_param pnh={g,nv,5}; - return pij_llrtopij_tot_convert(llr,llr,p,histogram_unequalbins_exp,pij_gassist_nullhist_analytical4_pdf,&ph,&pnh,nratio); -} - -int pij_gassist_llrtopij5_tot(const MATRIXG* g,const VECTORF* llr,VECTORF* p,size_t nv,double* nratio) -{ - const struct histogram_unequalbins_exp_param ph={g->size2-nv,1}; - const struct pij_gassist_nullhist_analytical_pdf_param pnh={g,nv,5}; - return pij_llrtopij_tot_convert(llr,llr,p,histogram_unequalbins_exp,pij_gassist_nullhist_analytical5_pdf,&ph,&pnh,nratio); -} - -int pij_gassist_llrtopijs_tot(const MATRIXG* g,const MATRIXF* t,const MATRIXF* t2,const VECTORF* llr1,const MATRIXF* llr2,const MATRIXF* llr3,const MATRIXF* llr4,const MATRIXF* llr5,VECTORF* p1,MATRIXF* p2,MATRIXF* p3,MATRIXF* p4,MATRIXF* p5,size_t nv,char nodiag) -{ -#define CLEANUP CLEANVECF(vp)CLEANVECF(tp) - int ret; - size_t ng,n; - double nratio; //Ratio of null distribution - VECTORF *vp,*tp; - - //Initialize - vp=tp=0; - ng=t->size1; - n=ng*t2->size1; - if(nodiag) - n-=GSL_MIN(ng,t2->size1); - vp=VECTORFF(alloc)(n); - tp=VECTORFF(alloc)(n); - if(!(vp&&tp)) - ERRRET("Not enough memory.") - - if(VECTORFF(first_nan)(llr1)>=0) - LOG(4,"Infinity found for log likelihood ratio, possibly because data contains fully correlated/anticorrelated columns. This may affect downstream analyses.") - ret=pij_gassist_llrtopij1_1(p1); - if(ret) - ERRRET("Failed to calculate probabilities in step 1.") - - if(nodiag) - MATRIXFF(flatten_nodiag)(llr2,tp); - else - MATRIXFF(flatten)(llr2,tp); - if(VECTORFF(first_nan)(tp)>=0) - LOG(4,"Infinity found for log likelihood ratio, possibly because data contains fully correlated/anticorrelated columns. This may affect downstream analyses.") - ret=pij_gassist_llrtopij2_tot(g,tp,vp,nv,&nratio); - if(ret) - ERRRET("Failed to calculate probabilities in step 2.") - if(nodiag) - VECTORFF(wrap_nodiag)(vp,p2); - else - VECTORFF(wrap)(vp,p2); - - if(nodiag) - MATRIXFF(flatten_nodiag)(llr3,tp); - else - MATRIXFF(flatten)(llr3,tp); - if(VECTORFF(first_nan)(tp)>=0) - { - LOG(4,"Infinity found for log likelihood ratio, possibly because data contains fully correlated/anticorrelated columns. This may affect downstream analyses.") - VECTORFF(set_nan)(tp,0); - } - ret=pij_gassist_llrtopij3_tot(g,tp,vp,nv,&nratio); - if(ret) - ERRRET("Failed to calculate probabilities in step 3.") - if(nodiag) - VECTORFF(wrap_nodiag)(vp,p3); - else - VECTORFF(wrap)(vp,p3); - - if(nodiag) - MATRIXFF(flatten_nodiag)(llr4,tp); - else - MATRIXFF(flatten)(llr4,tp); - if(VECTORFF(first_nan)(tp)>=0) - LOG(4,"Infinity found for log likelihood ratio, possibly because data contains fully correlated/anticorrelated columns. This may affect downstream analyses.") - ret=pij_gassist_llrtopij4_tot(g,tp,vp,nv,&nratio); - if(ret) - ERRRET("Failed to calculate probabilities in step 4.") - if(nodiag) - VECTORFF(wrap_nodiag)(vp,p4); - else - VECTORFF(wrap)(vp,p4); - - if(nodiag) - MATRIXFF(flatten_nodiag)(llr4,tp); - else - MATRIXFF(flatten)(llr4,tp); - if(VECTORFF(first_nan)(tp)>=0) - LOG(4,"Infinity found for log likelihood ratio, possibly because data contains fully correlated/anticorrelated columns. This may affect downstream analyses.") - ret=pij_gassist_llrtopij5_tot(g,tp,vp,nv,&nratio); - if(ret) - ERRRET("Failed to calculate probabilities in step 5.") - if(nodiag) - VECTORFF(wrap_nodiag)(vp,p5); - else - VECTORFF(wrap)(vp,p5); - - //Free memory - CLEANUP - return 0; -#undef CLEANUP -} - - - - - - - - - - - - - - - - diff --git a/pij/gassist/llrtopij_tot.h b/pij/gassist/llrtopij_tot.h deleted file mode 100644 index e87c811..0000000 --- a/pij/gassist/llrtopij_tot.h +++ /dev/null @@ -1,103 +0,0 @@ -/* Copyright 2016, 2017 Lingfei Wang - * - * This file is part of Findr. - * - * Findr is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Findr is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Findr. If not, see . - */ -/* This file contains the conversion from log likelihood ratio to probabilities - */ - -#ifndef _HEADER_LIB_PIJ_GASSIST_LLRTOPIJ_TOT_H_ -#define _HEADER_LIB_PIJ_GASSIST_LLRTOPIJ_TOT_H_ -#include "../../base/config.h" -#include "../../base/gsl/histogram.h" -#include "../../base/types.h" -#include "../llrtopij_tot.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - -/* The recommended wrapper conversion methods of step 1-5 data. Uses analytical generation of pdfs of null distribution. - * g: (ng,ns) Genotype data. - * llr: Log likelihood ratios of real data - * p: Output matrix of converted probabilities - * nv: number of possible genotype values for each SNP - * nratio: if not NULL, return the ratio of null distribution in real data. - * Return: 0 on success - */ -int pij_gassist_llrtopij1_tot(const MATRIXG* g,const VECTORF* llr,VECTORF* p,size_t nv,double* nratio); -int pij_gassist_llrtopij2_tot(const MATRIXG* g,const VECTORF* llr,VECTORF* p,size_t nv,double* nratio); -int pij_gassist_llrtopij3_tot(const MATRIXG* g,const VECTORF* llr,VECTORF* p,size_t nv,double* nratio); -int pij_gassist_llrtopij4_tot(const MATRIXG* g,const VECTORF* llr,VECTORF* p,size_t nv,double* nratio); -int pij_gassist_llrtopij5_tot(const MATRIXG* g,const VECTORF* llr,VECTORF* p,size_t nv,double* nratio); - - -/* Calculates null log likelihood ratios from permuted data, and then convert log likelihood ratios of real data - * into probabilities. For step 3, permute A and B. - * g: (ng,ns) Full genotype data - * t: (ng,ns) Full transcript data of A. Each rows best eQTL must be the same row of g - * t2: (nt,ns) Full transcript data of B. - * llr1: (ng) Log likelihood ratios of real data for step 1 - * llr2: (ng,nt) Log likelihood ratios of real data for step 2 - * llr3: (ng,nt) Log likelihood ratios of real data for step 3 - * llr4: (ng,nt) Log likelihood ratios of real data for step 4 - * llr5: (ng,nt) Log likelihood ratios of real data for step 5 - * p1: (ng) Output for converted probabilities of step 1 - * p2: (ng,nt) Output for converted probabilities of step 2 - * p3: (ng,nt) Output for converted probabilities of step 3 - * p4: (ng,nt) Output for converted probabilities of step 4 - * p5: (ng,nt) Output for converted probabilities of step 5 - * ans: (ng,nt) Output matrix of converted probabilities - * nv: number of possible genotype values for each SNP - * nodiag: When the top ng rows of t2 is exactly t, diagonals of p2 and p3 are meaningless. - * In this case, set nodiag to 1 to avoid inclusion of NANs. For nodiag=0, t and t2 - * should not have any identical genes. - * Return: 0 on success - */ -int pij_gassist_llrtopijs_tot(const MATRIXG* g,const MATRIXF* t,const MATRIXF* t2,const VECTORF* llr1,const MATRIXF* llr2,const MATRIXF* llr3,const MATRIXF* llr4,const MATRIXF* llr5,VECTORF* p1,MATRIXF* p2,MATRIXF* p3,MATRIXF* p4,MATRIXF* p5,size_t nv,char nodiag); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#ifdef __cplusplus -} -#endif -#endif diff --git a/pij/gassist/llrtopv.c b/pij/gassist/llrtopv.c index 29ef7f3..ad3b708 100644 --- a/pij/gassist/llrtopv.c +++ b/pij/gassist/llrtopv.c @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * @@ -34,7 +34,7 @@ * n1c, * n1d, * n2c, - * n2d: Parameters to specify null distribution. See pij_llrtopij_a_nullhist + * n2d: Parameters to specify null distribution. See pij_nullhist * Return: 0 if success. */ static int pij_gassist_llrtopv_block(MATRIXF* d,const MATRIXG* g,size_t nv,long n1c,size_t n1d,long n2c,size_t n2d) @@ -81,7 +81,7 @@ static int pij_gassist_llrtopv_block(MATRIXF* d,const MATRIXG* g,size_t nv,long * n1c, * n1d, * n2c, - * n2d: Parameters to specify null distribution. See pij_llrtopij_a_nullhist + * n2d: Parameters to specify null distribution. See pij_nullhist * Return: 0 if success. */ static inline int pij_gassist_llrtopv_vec_block(VECTORF* d,const MATRIXG* g,size_t nv,long n1c,size_t n1d,long n2c,size_t n2d) diff --git a/pij/gassist/llrtopv.h b/pij/gassist/llrtopv.h index 2a650b3..bff394e 100644 --- a/pij/gassist/llrtopv.h +++ b/pij/gassist/llrtopv.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/pij/gassist/nulldist.c b/pij/gassist/nulldist.c deleted file mode 100644 index e5e0ac1..0000000 --- a/pij/gassist/nulldist.c +++ /dev/null @@ -1,310 +0,0 @@ -/* Copyright 2016, 2017 Lingfei Wang - * - * This file is part of Findr. - * - * Findr is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Findr is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Findr. If not, see . - */ -#include "../../base/config.h" -#include -#include -#include -#include -#include -#include "../../base/gsl/blas.h" -#include "../../base/gsl/math.h" -#include "../../base/gsl/sf.h" -#include "../../base/logger.h" -#include "../../base/macros.h" -#include "../../base/histogram.h" -#include "../../base/math.h" -#include "../../base/data_process.h" -#include "../nulldist.h" -#include "nulldist.h" -#pragma GCC diagnostic ignored "-Wunused-parameter" - - - -/************************************************************* - * Generic functions for any step - * - * Mixture distributions of LLR for any step at specific locations. - * The distribution is a mixture one because different genotype locations - * can have different number - * of values observed, yielding to different analytical distributions of LLR. - *************************************************************/ - -/* Calculate log total mixture pdf of LLR for any step at specific locations with buffer provided. - * The distribution is a mixture one because different genotype locations can have different number - * of values observed, yielding to different analytical distributions of LLR. - * Specify function for different steps. - * g: (ng,ns) Genotype data - * nv: Number of values each genotype can take - * loc: (nd) Locations of LLR of current step, where log pdf will be calculated - * ans: (nd) Output for log pdf calculated. - * vb2: (nd) Buffer - * vb3: (ng) Buffer - * vb4: (nv+1) Buffer - * mb1: (nv-1,nd) Saves pdf before summing up w.r.t nv. - * nd: loc->size - * func:Function to calculate pdf buffed (e.g. pij_nulldist1_calcpdf_buffed or pij_nulldist3_calcpdf_buffed) - */ -static void pij_gassist_nulldist_mixed_pdf_buffed(const MATRIXG* g,size_t nv,const VECTORD* loc,VECTORD* ans,VECTORD* vb2,VECTORG* vb3,VECTORD* vb4,VECTORUC* vb5,MATRIXD* mb1,void (*func)(size_t,size_t,const VECTORD*,MATRIXD*,VECTORD*)) -{ - size_t ns=g->size2; - - assert(loc->size&&(ans->size==loc->size)&&(vb2->size==loc->size)&&(mb1->size2==loc->size)); - assert((nv>1)&&(mb1->size1==(nv-1))); - - //First calculate separate log pdfs - func(ns,nv,loc,mb1,vb2); - - //Count alleles for each genotype - MATRIXGF(countv_byrow_buffed)(g,vb3,vb5); - VECTORGF(count_ratio_d)(vb3,vb4); - - //Calculate averaged pdf w.r.t genotype - if(VECTORDF(get)(vb4,1)!=0) - LOG(1,"Found genotype that has single value across all samples. Ignored.") - - VECTORDF(const_view) vvc=VECTORDF(const_subvector)(vb4,2,(size_t)nv-1); - gsl_blas_dgemv(CblasTrans,1/(1-VECTORDF(get)(vb4,1)),mb1,&vvc.vector,0,ans); -} - -/* Calculate log pdf of LLR for any step at specific locations. - * g: (ng,ns) Genotype data - * nv: Number of values each genotype can take - * loc: (nd) Locations of LLR of current step, where log pdf will be calculated - * ans: (nd) Output for log pdf calculated. - * nd: loc->size - * func:Function to calculate pdf buffed (e.g. pij_nulldist1_calcpdf_buffed or pij_nulldist3_calcpdf_buffed) - * Return: 0 on success. - */ -static int pij_gassist_nulldist_mixed_pdf(const MATRIXG* g,size_t nv,const VECTORD* loc,VECTORD* ans,void (*func)(size_t,size_t,const VECTORD*,MATRIXD*,VECTORD*)) -{ -#define CLEANUP AUTOFREEVEC(vb2)AUTOFREEVEC(vb4)AUTOFREEVEC(vb4) - size_t ng=g->size1,nd=loc->size; - - VECTORG *vb3=VECTORGF(alloc)(ng); - MATRIXD *mb1=MATRIXDF(alloc)(nv-1,nd); - AUTOALLOCVECD(vb2,nd,10000) - AUTOALLOCVECD(vb4,nv+1,1000) - AUTOALLOCVECUC(vb5,nv,1000) - - if(!(vb2&&vb3&&vb4&&vb5&&mb1)) - ERRRET("Not enough memory.") - - pij_gassist_nulldist_mixed_pdf_buffed(g,nv,loc,ans,vb2,vb3,vb4,vb5,mb1,func); - CLEANUP - return 0; -#undef CLEANUP -} - -/* Calculates density histogram of null distribution of log likelihood ratio. - * Is a pij_allrtopij_nullhist_method (see pij_allrtopij.h). - * Method is to use central pdf value as the density. - * g: (ng,ns) Genotype data - * nv: Number of values each genotype can take - * range: (nbin+1) Bin boundary values - * nbin: Number of bins - * hist: (nbin) Output array for density histogram. - * param: Redundant, must be 0. - * func: Function to calculate pdf buffed (e.g. pij_nulldist1_calcpdf_buffed or pij_nulldist3_calcpdf_buffed) - * Return: 0 on success. - */ -static int pij_gassist_nulldist_nullhist_mixed_pdf0(const MATRIXG* g,size_t nv,const double* restrict range,size_t nbin,double* restrict hist,void* param,void (*func)(size_t,size_t,const VECTORD*,MATRIXD*,VECTORD*)) -{ -#define CLEANUP AUTOFREEVEC(vloc) - int ret; - VECTORDF(view) vvh=VECTORDF(view_array)(hist,nbin); - AUTOALLOCVECD(vloc,nbin,1000) - - assert(!param); - if(!vloc) - ERRRET("Not enough memory.") - //Obtain bin central values - { - VECTORDF(const_view) vv1=VECTORDF(const_view_array)(range+1,nbin); - VECTORDF(memcpy)(vloc,&vv1.vector); - } - { - VECTORDF(const_view) vv1=VECTORDF(const_view_array)(range,nbin); - VECTORDF(add)(vloc,&vv1.vector); - } - VECTORDF(scale)(vloc,0.5); - ret=pij_gassist_nulldist_mixed_pdf(g,nv,vloc,&vvh.vector,func); - CLEANUP - return ret; -#undef CLEANUP -} - -/* Calculates density histogram of null distribution of log likelihood ratio. - * Method is to use central pdf value as the density. - * g: (ng,ns) Genotype data - * nv: Number of values each genotype can take - * range: (nbin+1) Bin boundary values - * nbin: Number of bins - * hist: (nbin) Output array for density histogram. - * param: Redundant, must be 0. - * func: Function to calculate pdf buffed (e.g. pij_nulldist1_calcpdf_buffed or pij_nulldist3_calcpdf_buffed) - * Return: 0 on success. - */ -static int pij_gassist_nulldist_nullhist_mixed_pdf(const MATRIXG* g,size_t nv,const double* restrict range,size_t nbin,double* restrict hist,void* param,void (*func)(size_t,size_t,const VECTORD*,MATRIXD*,VECTORD*)) -{ -#define CLEANUP CLEANVECD(loc)CLEANVECD(val) - VECTORD *loc,*val; - VECTORDF(view) vvh=VECTORDF(view_array)(hist,nbin); - size_t *n=param; - size_t i; - size_t nsp; - - assert(n&&(*n<10)); - if(!*n) - return pij_gassist_nulldist_nullhist_mixed_pdf0(g,nv,range,nbin,hist,param,func); - nsp=(size_t)1<<(*n-1); - loc=VECTORDF(alloc)(nbin*nsp); - val=VECTORDF(alloc)(nbin*nsp); - if(!(loc&&val)) - ERRRET("Not enough memory.") - - //Construct bin ranges - { - VECTORDF(const_view) vvc=VECTORDF(const_view_array)(range,nbin+1); - histogram_finer_central(&vvc.vector,loc,nsp); - } - - //Calculate bin values - if(pij_gassist_nulldist_mixed_pdf(g,nv,loc,val,func)) - { - CLEANUP - return 1; - } - - //Shrink to output - VECTORDF(set_zero)(&vvh.vector); - for(i=0;ig,p->nv,loc,ans,pij_gassist_nulldist1_calcpdf_buffed); -} - -int pij_gassist_nulldist2_mixed_pdf(const VECTORD* loc,VECTORD* ans,const void* param) -{ - const struct pij_gassist_nulldist_mixed_pdf_data *p=param; - return pij_gassist_nulldist_mixed_pdf(p->g,p->nv,loc,ans,pij_gassist_nulldist2_calcpdf_buffed); -} - -int pij_gassist_nulldist3_mixed_pdf(const VECTORD* loc,VECTORD* ans,const void* param) -{ - const struct pij_gassist_nulldist_mixed_pdf_data *p=param; - return pij_gassist_nulldist_mixed_pdf(p->g,p->nv,loc,ans,pij_gassist_nulldist3_calcpdf_buffed); -} - -int pij_gassist_nulldist4_mixed_pdf(const VECTORD* loc,VECTORD* ans,const void* param) -{ - const struct pij_gassist_nulldist_mixed_pdf_data *p=param; - return pij_gassist_nulldist_mixed_pdf(p->g,p->nv,loc,ans,pij_gassist_nulldist4_calcpdf_buffed); -} - -int pij_gassist_nulldist5_mixed_pdf(const VECTORD* loc,VECTORD* ans,const void* param) -{ - const struct pij_gassist_nulldist_mixed_pdf_data *p=param; - return pij_gassist_nulldist_mixed_pdf(p->g,p->nv,loc,ans,pij_gassist_nulldist5_calcpdf_buffed); -} - -int pij_gassist_nulldist_nullhist1_pdf(const MATRIXG* g,size_t nv,const double* restrict range,size_t nbin,double* restrict hist,void* param) -{ - return pij_gassist_nulldist_nullhist_mixed_pdf(g,nv,range,nbin,hist,param,pij_gassist_nulldist1_calcpdf_buffed); -} - -int pij_gassist_nulldist_nullhist2_mixed_pdf(const MATRIXG* g,size_t nv,const double* restrict range,size_t nbin,double* restrict hist,void* param) -{ - return pij_gassist_nulldist_nullhist_mixed_pdf(g,nv,range,nbin,hist,param,pij_gassist_nulldist2_calcpdf_buffed); -} - -int pij_gassist_nulldist_nullhist3_mixed_pdf(const MATRIXG* g,size_t nv,const double* restrict range,size_t nbin,double* restrict hist,void* param) -{ - return pij_gassist_nulldist_nullhist_mixed_pdf(g,nv,range,nbin,hist,param,pij_gassist_nulldist3_calcpdf_buffed); -} - -int pij_gassist_nulldist_nullhist4_mixed_pdf(const MATRIXG* g,size_t nv,const double* restrict range,size_t nbin,double* restrict hist,void* param) -{ - return pij_gassist_nulldist_nullhist_mixed_pdf(g,nv,range,nbin,hist,param,pij_gassist_nulldist4_calcpdf_buffed); -} - -int pij_gassist_nulldist_nullhist5_mixed_pdf(const MATRIXG* g,size_t nv,const double* restrict range,size_t nbin,double* restrict hist,void* param) -{ - return pij_gassist_nulldist_nullhist_mixed_pdf(g,nv,range,nbin,hist,param,pij_gassist_nulldist5_calcpdf_buffed); -} - - - - - - - - - - - - - - - - - - - - diff --git a/pij/gassist/nulldist.h b/pij/gassist/nulldist.h deleted file mode 100644 index 22b8529..0000000 --- a/pij/gassist/nulldist.h +++ /dev/null @@ -1,115 +0,0 @@ -/* Copyright 2016, 2017 Lingfei Wang - * - * This file is part of Findr. - * - * Findr is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Findr is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Findr. If not, see . - */ -/* This part contains analytical calculation of the distribution of log likelihood ratio from null hypothesis. - * Each function is applicable to one or more stages, which are stated in the function name as pij_nulldistX_..., where X is the applicable stage. - * For each stage, different methods to calculate histogram can coexist. The method is declared in the function name as suffix: - * _cdf: Calculate histogram as the difference of cdf. - * This is applicable when distribution is single-variable integrable. - * _pdf: Calculate histogram as the pdf mean of points evenly split within the bin. This is applicable when distribution is single-variable non-integrable. - * _sim: Construct histogram by sampling. This is applicable when distribution is multi-variable non-integrable. - */ - -#ifndef _HEADER_LIB_PIJ_GASSIST_NULLDIST_H_ -#define _HEADER_LIB_PIJ_GASSIST_NULLDIST_H_ -#include "../../base/config.h" -#include "../../base/types.h" -#ifdef __cplusplus -extern "C" -{ -#endif - - -/************************************************************* - * Specific functions for each step - *************************************************************/ - -/* Calculate log pdf of LLR for each step at specific locations with buffer provided. - * Uses pij_nulldist_calcpdf_buffed. - * ns: Number of samples - * nv: Number of values each genotype can take. Calculates for value counts=2,...,nv. - * loc: (nd) Locations of LLR of each step, where log pdf will be calculated - * ans: (nv-1,nd) Output matrix. ans[i,j]=p(loc[j],ns,i+2). - * vb2: (nd) Buffer. - */ -void pij_gassist_nulldist1_calcpdf_buffed(size_t ns,size_t nv,const VECTORD* loc,MATRIXD* ans,VECTORD* vb2); -void pij_gassist_nulldist2_calcpdf_buffed(size_t ns,size_t nv,const VECTORD* loc,MATRIXD* ans,VECTORD* vb2); -void pij_gassist_nulldist3_calcpdf_buffed(size_t ns,size_t nv,const VECTORD* loc,MATRIXD* ans,VECTORD* vb2); -void pij_gassist_nulldist4_calcpdf_buffed(size_t ns,size_t nv,const VECTORD* loc,MATRIXD* ans,VECTORD* vb2); -void pij_gassist_nulldist5_calcpdf_buffed(size_t ns,size_t nv,const VECTORD* loc,MATRIXD* ans,VECTORD* vb2); - - -struct pij_gassist_nulldist_mixed_pdf_data -{ - const MATRIXG* g; - size_t nv; -}; - -/* Calculate log pdf of LLR for each step at specific locations using pij_nulldist_mixed_pdf. - * Steps 1 and 2_conserv are identical. - * g: (ng,ns) Genotype data - * nv: Number of values each genotype can take - * loc: (nd) Locations of LLR of current step, where log pdf will be calculated - * ans: (nd) Output for log pdf calculated. - * Return: 0 on success. - */ -int pij_gassist_nulldist1_mixed_pdf(const VECTORD* loc,VECTORD* ans,const void* param); -int pij_gassist_nulldist2_mixed_pdf(const VECTORD* loc,VECTORD* ans,const void* param); -int pij_gassist_nulldist3_mixed_pdf(const VECTORD* loc,VECTORD* ans,const void* param); -int pij_gassist_nulldist4_mixed_pdf(const VECTORD* loc,VECTORD* ans,const void* param); -int pij_gassist_nulldist5_mixed_pdf(const VECTORD* loc,VECTORD* ans,const void* param); - -// #define pij_nulldist2_nullhist_pdf0 pij_nulldist1_nullhist_pdf0 -/* Calculates density histogram of null distribution of log likelihood ratio. - * Steps 1 and 2_conserv are identical. - * Uses pij_nulldist_nullhist_pdf. - * Method is to use central pdf value as the density. - * g: (ng,ns) Genotype data - * nv: Number of values each genotype can take - * range: (nbin+1) Bin boundary values - * nbin: Number of bins - * hist: (nbin) Output array for density histogram. - * param: Redundant, must be 0. - * Return: 0 on success. - */ -int pij_gassist_nulldist_nullhist1_mixed_pdf(const MATRIXG* g,size_t nv,const double* restrict range,size_t nbin,double* restrict hist,void* param); -int pij_gassist_nulldist_nullhist2_mixed_pdf(const MATRIXG* g,size_t nv,const double* restrict range,size_t nbin,double* restrict hist,void* param); -int pij_gassist_nulldist_nullhist3_mixed_pdf(const MATRIXG* g,size_t nv,const double* restrict range,size_t nbin,double* restrict hist,void* param); -int pij_gassist_nulldist_nullhist4_mixed_pdf(const MATRIXG* g,size_t nv,const double* restrict range,size_t nbin,double* restrict hist,void* param); -int pij_gassist_nulldist_nullhist5_mixed_pdf(const MATRIXG* g,size_t nv,const double* restrict range,size_t nbin,double* restrict hist,void* param); - - - - - - - - - - - - - - - - - - -#ifdef __cplusplus -} -#endif -#endif diff --git a/pij/gassist/nullhist.c b/pij/gassist/nullhist.c index 3c9e326..280b4df 100644 --- a/pij/gassist/nullhist.c +++ b/pij/gassist/nullhist.c @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * @@ -19,62 +19,25 @@ #include #include #include -#include -#include "../../base/gsl/blas.h" #include "../../base/logger.h" -#include "../../base/macros.h" -#include "../../base/histogram.h" +#include "../../base/gsl/histogram.h" #include "../nullhist.h" #include "nullhist.h" -#include "nulldist.h" - -int pij_gassist_nullhist_analytical1_pdf(const void* param,gsl_histogram* h) -{ - const struct pij_gassist_nullhist_analytical_pdf_param *p=param; - const struct pij_gassist_nulldist_mixed_pdf_data p2={p->g,p->nv}; - const struct pij_nullhist_analytical_pdf_param p3={p->nsplit,pij_gassist_nulldist1_mixed_pdf,&p2}; - - return pij_nullhist_analytical_pdf(&p3,h); -} - -int pij_gassist_nullhist_analytical2_pdf(const void* param,gsl_histogram* h) -{ - const struct pij_gassist_nullhist_analytical_pdf_param *p=param; - const struct pij_gassist_nulldist_mixed_pdf_data p2={p->g,p->nv}; - const struct pij_nullhist_analytical_pdf_param p3={p->nsplit,pij_gassist_nulldist2_mixed_pdf,&p2}; - - return pij_nullhist_analytical_pdf(&p3,h); -} - -int pij_gassist_nullhist_analytical3_pdf(const void* param,gsl_histogram* h) +int pij_gassist_nullhists(gsl_histogram** h[4],size_t nt,size_t ns,size_t nv,const FTYPE dmax[4]) { - const struct pij_gassist_nullhist_analytical_pdf_param *p=param; - const struct pij_gassist_nulldist_mixed_pdf_data p2={p->g,p->nv}; - const struct pij_nullhist_analytical_pdf_param p3={p->nsplit,pij_gassist_nulldist3_mixed_pdf,&p2}; - - return pij_nullhist_analytical_pdf(&p3,h); + //Construct null density histograms + h[0]=pij_nullhist((double)dmax[0],nv,nt,1,1,1,ns-2); + h[1]=pij_nullhist((double)dmax[1],nv,nt,1,1,1,ns-3); + h[2]=pij_nullhist((double)dmax[2],nv,nt,1,2,1,ns-3); + h[3]=pij_nullhist((double)dmax[3],nv,nt,0,1,1,ns-3); + if(h[0]&&h[1]&&h[2]&&h[3]) + return 0; + + LOG(1,"pij_nullhist failed.") + return 1; } -int pij_gassist_nullhist_analytical4_pdf(const void* param,gsl_histogram* h) -{ - const struct pij_gassist_nullhist_analytical_pdf_param *p=param; - const struct pij_gassist_nulldist_mixed_pdf_data p2={p->g,p->nv}; - const struct pij_nullhist_analytical_pdf_param p3={p->nsplit,pij_gassist_nulldist4_mixed_pdf,&p2}; - - return pij_nullhist_analytical_pdf(&p3,h); -} - -int pij_gassist_nullhist_analytical5_pdf(const void* param,gsl_histogram* h) -{ - const struct pij_gassist_nullhist_analytical_pdf_param *p=param; - const struct pij_gassist_nulldist_mixed_pdf_data p2={p->g,p->nv}; - const struct pij_nullhist_analytical_pdf_param p3={p->nsplit,pij_gassist_nulldist5_mixed_pdf,&p2}; - - return pij_nullhist_analytical_pdf(&p3,h); -} - - diff --git a/pij/gassist/nullhist.h b/pij/gassist/nullhist.h index 05fde9d..d9cc65a 100644 --- a/pij/gassist/nullhist.h +++ b/pij/gassist/nullhist.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * @@ -29,26 +29,17 @@ extern "C" { #endif -struct pij_gassist_nullhist_analytical_pdf_param -{ - //(ng,ns) Genotype data - const MATRIXG* g; - //Maximum number of possible values each genotype can take. - //=Number of alleles + 1 - size_t nv; - /* Number of split within each bin. 2^nsplit central points are - * taken and the mean is calculated as the average pdf within the bin. - */ - size_t nsplit; -}; - - -//Specific interface function for different stages -int pij_gassist_nullhist_analytical1_pdf(const void* param,gsl_histogram* h); -int pij_gassist_nullhist_analytical2_pdf(const void* param,gsl_histogram* h); -int pij_gassist_nullhist_analytical3_pdf(const void* param,gsl_histogram* h); -int pij_gassist_nullhist_analytical4_pdf(const void* param,gsl_histogram* h); -int pij_gassist_nullhist_analytical5_pdf(const void* param,gsl_histogram* h); +/* Produce null histograms for all tests (2 to 5). + * h: Output location of null histograms. 0 to 3 for tests 2 to 5. + * nt: Number of targets + * ns: Number of samples + * nv: Number of values, = number of alleles + 1 + * dmax: Maximum value of all LLRs, for histogram construction. + * It can be larger than the maximum of d, if memlimit is not infinite. + * 0 to 4 for tests 1 to 5. + * Return: 0 on success and 1 otherwise + */ +int pij_gassist_nullhists(gsl_histogram** h[4],size_t nt,size_t ns,size_t nv,const FTYPE dmax[4]); diff --git a/pij/llrtopij.c b/pij/llrtopij.c index 1a21fa0..d3e6962 100644 --- a/pij/llrtopij.c +++ b/pij/llrtopij.c @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * @@ -28,11 +28,11 @@ #include "../base/macros.h" #include "../base/data_process.h" #include "../base/histogram.h" -#include "nullmodeler.h" -#include "nullsampler.h" +#include "../base/threading.h" #include "nullhist.h" #include "llrtopij.h" -#pragma GCC diagnostic ignored "-Wunused-parameter" + +// #pragma GCC diagnostic ignored "-Wunused-parameter" /* Smoothen true ratio histogram to make it monotonically increasing * Method: 1. Construct increasing upper bound histogram (hup) @@ -289,6 +289,289 @@ int pij_llrtopij_convert_histograms(gsl_histogram* hreal,VECTORD* vnull,gsl_hist #undef CLEANUP } +FTYPE pij_llrtopij_llrmatmax(MATRIXF* d,char nodiag) +{ + FTYPE dmin,dmax; + if(nodiag) + MATRIXFF(minmax_nodiag)(d,&dmin,&dmax,0); + else + MATRIXFF(minmax)(d,&dmin,&dmax); + if((!(dmin>=0))||gsl_isnan(dmax)) + { + LOG(1,"Negative or NAN found in input data. It may invalidate follow up analysis. This may be due to incorrect previous steps.") + return 0; + } + if(gsl_isinf(dmax)) + { + LOG(5,"INF found in input data. It may invalidate follow up analysis. This may be due to incorrect previous steps or duplicate rows. Now regard INFs as largest non-INF value.") + MATRIXFF(set_inf)(d,-1); + if(nodiag) + MATRIXFF(minmax_nodiag)(d,&dmin,&dmax,0); + else + dmax=MATRIXFF(max)(d); + MATRIXFF(set_value)(d,-1,dmax); + } + return dmax; +} + +FTYPE pij_llrtopij_llrvecmax(VECTORF* d) +{ + FTYPE dmin,dmax; + VECTORFF(minmax)(d,&dmin,&dmax); + if((!(dmin>=0))||gsl_isnan(dmax)) + { + LOG(1,"Negative or NAN found in input data. It may invalidate follow up analysis. This may be due to incorrect previous steps.") + return 0; + } + if(gsl_isinf(dmax)) + { + LOG(5,"INF found in input data. It may invalidate follow up analysis. This may be due to incorrect previous steps or duplicate rows. Now regard INFs as largest non-INF value.") + VECTORFF(set_inf)(d,-1); + dmax=VECTORFF(max)(d); + VECTORFF(set_value)(d,-1,dmax); + } + return dmax; +} + + +int pij_llrtopij_convert_single(const MATRIXF* d,const MATRIXF* dconv,MATRIXF* ans,size_t n1,size_t n2,char nodiag,long nodiagshift) +{ +#define CLEANUP CLEANHIST(hreal)CLEANHIST(hc)\ + CLEANHIST(h)CLEANVECD(vb1)CLEANVECD(vb2) + size_t ng=d->size1; + long k; + size_t j,nbin; + gsl_histogram *h,*hreal,*hc; + VECTORD *vb1,*vb2; + + h=0; + hreal=hc=0; + vb1=0; + vb2=0; + //Validity checks + assert((dconv->size1==ng)&&(ans->size1==ng)&&(ans->size2==dconv->size2)); + + //Construct null density histograms + { + FTYPE dmin,dmax; + MATRIXFF(minmax)(d,&dmin,&dmax); + if((!(dmin>=0))||gsl_isnan(dmax)||gsl_isinf(dmax)) + ERRRET("Negative or NAN found in input data. It may invalidate follow up analysis. This may be due to incorrect previous steps.") + h=pij_nullhist_single((double)dmax,d->size2,n1,n2); + if(!h) + ERRRET("pij_nullhist_single failed.") + nbin=h->n; + } + + //Prepare for real histogram + hreal=gsl_histogram_clone(h); + hc=gsl_histogram_alloc(hreal->n+2); + if(!(hreal&&hc)) + ERRRET("Not enough memory."); + if(pij_llrtopij_convert_histograms_make_buffs(nbin,&vb1,&vb2)) + ERRRET("pij_llrtopij_convert_histograms_make_buffs failed.") + + //Conversion + { + VECTORDF(view) vv1,vvreal; + VECTORFF(view) vva; + VECTORD *vwidth,*vnull; + vwidth=VECTORDF(alloc)(nbin); + vnull=VECTORDF(alloc)(nbin); + if(!(vwidth&&vnull)) + { + CLEANVECD(vwidth)CLEANVECD(vnull) + ERRRET("Not enough memory.") + } + vv1=VECTORDF(view_array)(h->range+1,nbin); + VECTORDF(memcpy)(vwidth,&vv1.vector); + vv1=VECTORDF(view_array)(h->range,nbin); + VECTORDF(sub)(vwidth,&vv1.vector); + vvreal=VECTORDF(view_array)(hreal->bin,nbin); + vv1=VECTORDF(view_array)(h->bin,nbin); + for(j=0;jrange,h->range,(nbin+1)*sizeof(*hreal->range)); + memset(hreal->bin,0,nbin*sizeof(*hreal->bin)); + //Construct real histogram + if(nodiag&&((long)j+nodiagshift>=0)&&((long)j+nodiagshift<(long)d->size2)) + { + for(k=(long)j+nodiagshift-1;k>=0;k--) + gsl_histogram_increment(hreal,MATRIXFF(get)(d,j,(size_t)k)); + for(k=(long)j+nodiagshift+1;k<(long)d->size2;k++) + gsl_histogram_increment(hreal,MATRIXFF(get)(d,j,(size_t)k)); + VECTORDF(scale)(&vvreal.vector,1./(double)(d->size2-1)); + } + else + { + for(k=0;k<(long)d->size2;k++) + gsl_histogram_increment(hreal,MATRIXFF(get)(d,j,(size_t)k)); + VECTORDF(scale)(&vvreal.vector,1./(double)(d->size2)); + } + //Convert to density histogram + VECTORDF(div)(&vvreal.vector,vwidth); + //Convert to probability central histogram + pij_llrtopij_convert_histograms_buffed(hreal,vnull,hc,vb1,vb2); + //Convert likelihoods to probabilities + vva=MATRIXFF(row)(ans,j); + pij_llrtopij_histogram_interpolate_linear(hc,&vvd.vector,&vva.vector); + } + CLEANVECD(vwidth)CLEANVECD(vnull) + } + CLEANUP + return 0; +#undef CLEANUP +} + +int pij_llrtopij_convert_single_self(MATRIXF* d,size_t n1,size_t n2,char nodiag,long nodiagshift) +{ +#define CLEANUP CLEANAMHIST(hreal,nth)CLEANAMHIST(hc,nth)\ + CLEANHIST(h)CLEANMATD(mb1)CLEANMATD(mb2)CLEANMATD(mnull)CLEANMATF(mb3)CLEANVECD(vwidth) + + size_t ng=d->size1; + size_t i,nbin; + gsl_histogram *h; + MATRIXD *mb1,*mb2,*mnull; + MATRIXF *mb3; + VECTORD *vwidth; + VECTORDF(view) vv1; + size_t nth; + + h=0; + mb1=mb2=mnull=0; + mb3=0; + vwidth=0; + //Validity checks + { + int nth0=omp_get_max_threads(); + assert(nth0>0); + nth=(size_t)nth0; + } + + + AUTOCALLOC(gsl_histogram*,hreal,nth,64) + AUTOCALLOC(gsl_histogram*,hc,nth,64) + if(!(hreal&&hc)) + ERRRET("Not enough memory."); + + //Construct null density histograms + { + FTYPE dmin,dmax; + if(nodiag) + MATRIXFF(minmax_nodiag)(d,&dmin,&dmax,nodiagshift); + else + MATRIXFF(minmax)(d,&dmin,&dmax); + if((!(dmin>=0))||gsl_isnan(dmax)) + ERRRET("Negative or NAN found in input data. It may invalidate follow up analysis. This may be due to incorrect previous steps.") + if(gsl_isinf(dmax)) + { + LOG(5,"INF found in input data. It may invalidate follow up analysis. This may be due to incorrect previous steps or duplicate rows (by Spearman correlation).") + MATRIXFF(set_inf)(d,-1); + if(nodiag) + MATRIXFF(minmax_nodiag)(d,&dmin,&dmax,nodiagshift); + else + MATRIXFF(minmax)(d,&dmin,&dmax); + MATRIXFF(set_value)(d,-1,dmax); + } + h=pij_nullhist_single((double)dmax,d->size2,n1,n2); + if(!h) + ERRRET("pij_nullhist_single failed.") + nbin=h->n; + } + //Memory allocation + { + size_t n1,n2; + pij_llrtopij_convert_histograms_get_buff_sizes(nbin,&n1,&n2); + mb1=MATRIXDF(alloc)(nth,n1); + mb2=MATRIXDF(alloc)(nth,n2); + mnull=MATRIXDF(alloc)(nth,nbin); + mb3=MATRIXFF(alloc)(nth,d->size2); + vwidth=VECTORDF(alloc)(nbin); + if(!(mb1&&mb2&&mnull&&mb3&&vwidth)) + ERRRET("Not enough memory.") + } + + //Prepare for real histogram + { + int ret; + for(i=0,ret=1;in+2); + ret=ret&&hreal[i]&&hc[i]; + } + if(!ret) + ERRRET("Not enough memory."); + } + + //Conversion + vv1=VECTORDF(view_array)(h->range+1,nbin); + VECTORDF(memcpy)(vwidth,&vv1.vector); + vv1=VECTORDF(view_array)(h->range,nbin); + VECTORDF(sub)(vwidth,&vv1.vector); + vv1=VECTORDF(view_array)(h->bin,nbin); + #pragma omp parallel + { + size_t ng1,ng2,id; + size_t j; + long k; + VECTORDF(view) vvreal,vvnull,vvb1,vvb2; + VECTORFF(view) vvb3,vva; + + id=(size_t)omp_get_thread_num(); + vvreal=VECTORDF(view_array)(hreal[id]->bin,nbin); + vvnull=MATRIXDF(row)(mnull,id); + vvb1=MATRIXDF(row)(mb1,id); + vvb2=MATRIXDF(row)(mb2,id); + vvb3=MATRIXFF(row)(mb3,id); + + threading_get_startend(ng,&ng1,&ng2); + + for(j=ng1;jrange,h->range,(nbin+1)*sizeof(*hreal[id]->range)); + memset(hreal[id]->bin,0,nbin*sizeof(*hreal[id]->bin)); + //Construct real histogram + if(nodiag&&((long)j+nodiagshift>=0)&&((long)j+nodiagshift<(long)d->size2)) + { + for(k=(long)j+nodiagshift-1;k>=0;k--) + gsl_histogram_increment(hreal[id],MATRIXFF(get)(d,j,(size_t)k)); + for(k=(long)j+nodiagshift+1;k<(long)d->size2;k++) + gsl_histogram_increment(hreal[id],MATRIXFF(get)(d,j,(size_t)k)); + VECTORDF(scale)(&vvreal.vector,1./(double)(d->size2-1)); + } + else + { + for(k=0;k<(long)d->size2;k++) + gsl_histogram_increment(hreal[id],MATRIXFF(get)(d,j,(size_t)k)); + VECTORDF(scale)(&vvreal.vector,1./(double)(d->size2)); + } + + //Convert to density histogram + VECTORDF(div)(&vvreal.vector,vwidth); + //Convert to probability central histogram + pij_llrtopij_convert_histograms_buffed(hreal[id],&vvnull.vector,hc[id],&vvb1.vector,&vvb2.vector); + //Convert likelihoods to probabilities + vva=MATRIXFF(row)(d,j); + pij_llrtopij_histogram_interpolate_linear(hc[id],&vvb3.vector,&vva.vector); + } + } + + CLEANUP + return 0; +#undef CLEANUP +} + + + + + + + diff --git a/pij/llrtopij.h b/pij/llrtopij.h index 8b45b61..05e1755 100644 --- a/pij/llrtopij.h +++ b/pij/llrtopij.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * @@ -75,9 +75,37 @@ void pij_llrtopij_convert_histograms_buffed(gsl_histogram* hreal,VECTORD* vnull, int pij_llrtopij_convert_histograms(gsl_histogram* hreal,VECTORD* vnull,gsl_histogram* hc); +/* Obtains the maximum of matrix, possibly ignoring diagonal elements. + * Fails in the presence of NAN, and warns and updates at INFs. + * d: Matrix/Vector to get maximum, and update any INFs + * nodiag: Whether to ignore diagonal values when searching for maximum. + * Return: 0 if NAN is found, or the non-INF maximum otherwise. + */ +FTYPE pij_llrtopij_llrmatmax(MATRIXF* d,char nodiag); +FTYPE pij_llrtopij_llrvecmax(VECTORF* d); +/* Convert LLR of real data to probabilities, when the distribution + * of LLR of null distribution can be calculated analytically to follow + * x=-0.5*log(1-z1/(z1+z2)), where z1~chi2(n1),z2~chi2(n2). + * The conversion is performed for each gene A, i.e. per row of d and dconv. + * This function is older than pij_llrtopij_convert_single so it is not parallel. + * Make it parallel before using. + * d: [nrow,nx] The data to use for calculation of conversion rule from LLR to pij. + * dconv: [nrow,nd] The data of LLR to actually convert to pij. Can be same with d. + * ans: [nrow,nd] The output location of converted pij from dconv. + * n1, + * n2: Parameters of null distribution. + * nodiag: Whether diagonal elements of d should be ignored when converting + * to probabilities. + * nodiagshift: Diangonal column shift for nodiag==1. + * For nodiagshift>0/<0, use upper/lower diagonal. + * Return: 0 on success. + */ +int pij_llrtopij_convert_single(const MATRIXF* d,const MATRIXF* dconv,MATRIXF* ans,size_t n1,size_t n2,char nodiag,long nodiagshift); +// Same with pij_llrtopij_convert_single, for d=dconv=ans. Saves memory. +int pij_llrtopij_convert_single_self(MATRIXF* d,size_t n1,size_t n2,char nodiag,long nodiagshift); diff --git a/pij/llrtopij_a.c b/pij/llrtopij_a.c deleted file mode 100644 index 9a7a698..0000000 --- a/pij/llrtopij_a.c +++ /dev/null @@ -1,411 +0,0 @@ -/* Copyright 2016, 2017 Lingfei Wang - * - * This file is part of Findr. - * - * Findr is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Findr is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Findr. If not, see . - */ -#include "../base/config.h" -#include -#include -#include -#include "../base/threading.h" -#include "../base/gsl/math.h" -#include "../base/gsl/histogram.h" -#include "../base/gsl/blas.h" -#include "../base/logger.h" -#include "../base/macros.h" -#include "../base/data_process.h" -#include "../base/histogram.h" -#include "nulldist.h" -#include "llrtopij_a.h" -#include "llrtopij.h" - -/* Construct one null histogram for a specific genotype value count. - * The function calculates the null density histogram for random variable: - * x=-0.5*log(1-z1/(z1+z2)), where z1~chi2(n1),z2~chi2(n2), - * Histogram bin count and width are automatically determined - * from real data count (nd). - * For bin range settings, see histogram_unequalbins_fromnullcdf. - * For null density histogram from pdf, see pij_nulldist_hist_pdf. - * dmax: Specifies the histogram bound as [0,dmax). - * nd: Count of real data to form real histograms. This is used to - * automatically decide number of bins and widths. - * n1, - * n2: Parameters of null distribution. - * Return: Constructed null distribution histograms with preset - * bin ranges and values as density. - */ -static gsl_histogram* pij_llrtopij_a_nullhist_single(double dmax,size_t nd,size_t n1,size_t n2) -{ -#define CLEANUP CLEANHIST(h) - struct pij_nulldist_pdfs_param param={n1,n2}; - size_t nbin; - gsl_histogram *h=0; - - assert(n1&&n2); - dmax*=(1+1E-6); - nbin=histogram_unequalbins_param_count(nd); - if(nbin<5) - ERRRETV(0,"Determined "PRINTFSIZET" bins constructed. Bin count too small.",nbin) - else if(nbin<10) - LOG(5,"Determined "PRINTFSIZET" bins, smaller than recommended minimum bin count (10).",nbin) - else - LOG(10,"Determined "PRINTFSIZET" bins.",nbin) - h=gsl_histogram_alloc(nbin); - if(!h) - ERRRETV(0,"Not enough memory.") - //Null density histogram - gsl_histogram_set_ranges_uniform(h,0,dmax); - //Set null histogram ranges - if(histogram_unequalbins_fromnullpdfs(nbin,h->range,pij_nulldist_pdfs,¶m)) - ERRRETV(0,"histogram_unequalbins_fromnullpdfs failed.") - //Calculate null density histogram - if(pij_nulldist_hist_pdf(h->range,nbin,h->bin,param.n1,param.n2,5)) - ERRRETV(0,"pij_nulldist_hist_pdf failed.") - return h; -#undef CLEANUP -} - -gsl_histogram** pij_llrtopij_a_nullhist(double dmax,size_t nv,size_t nd,long n1c,size_t n1d,long n2c,size_t n2d) -{ -#define CLEANUP if(h){for(i=0;i=2); - dmax*=(1+1E-6); - CALLOCSIZE(h,nv-1); - if(!h) - ERRRETV(0,"Not enough memory.") - nbin=histogram_unequalbins_param_count(nd); - if(nbin<5) - ERRRETV(0,"Determined "PRINTFSIZET" bins constructed. Bin count too small.",nbin) - else if(nbin<10) - LOG(5,"Determined "PRINTFSIZET" bins, smaller than recommended minimum bin count (10).",nbin) - else - LOG(10,"Determined "PRINTFSIZET" bins.",nbin) - ret=1; - for(i=0;irange,pij_nulldist_pdfs,¶m)) - ERRRETV(0,"histogram_unequalbins_fromnullpdfs failed.") - //Calculate null density histogram - if(pij_nulldist_hist_pdf(h[i]->range,nbin,h[i]->bin,param.n1,param.n2,5)) - ERRRETV(0,"pij_nulldist_hist_pdf failed.") - } - return h; -#undef CLEANUP -} - -int pij_llrtopij_a_convert_single(const MATRIXF* d,const MATRIXF* dconv,MATRIXF* ans,size_t n1,size_t n2,char nodiag,long nodiagshift) -{ -#define CLEANUP CLEANHIST(hreal)CLEANHIST(hc)\ - CLEANHIST(h)CLEANVECD(vb1)CLEANVECD(vb2) - size_t ng=d->size1; - long k; - size_t j,nbin; - gsl_histogram *h,*hreal,*hc; - VECTORD *vb1,*vb2; - - h=0; - hreal=hc=0; - vb1=0; - vb2=0; - //Validity checks - assert((dconv->size1==ng)&&(ans->size1==ng)&&(ans->size2==dconv->size2)); - - //Construct null density histograms - { - FTYPE dmin,dmax; - MATRIXFF(minmax)(d,&dmin,&dmax); - if((!(dmin>=0))||gsl_isnan(dmax)||gsl_isinf(dmax)) - ERRRET("Negative or NAN found in input data. It may invalidate follow up analysis. This may be due to incorrect previous steps.") - h=pij_llrtopij_a_nullhist_single((double)dmax,d->size2,n1,n2); - if(!h) - ERRRET("pij_llrtopij_a_nullhist_single failed.") - nbin=h->n; - } - - //Prepare for real histogram - hreal=gsl_histogram_clone(h); - hc=gsl_histogram_alloc(hreal->n+2); - if(!(hreal&&hc)) - ERRRET("Not enough memory."); - if(pij_llrtopij_convert_histograms_make_buffs(nbin,&vb1,&vb2)) - ERRRET("pij_llrtopij_convert_histograms_make_buffs failed.") - - //Conversion - { - VECTORDF(view) vv1,vvreal; - VECTORFF(view) vva; - VECTORD *vwidth,*vnull; - vwidth=VECTORDF(alloc)(nbin); - vnull=VECTORDF(alloc)(nbin); - if(!(vwidth&&vnull)) - { - CLEANVECD(vwidth)CLEANVECD(vnull) - ERRRET("Not enough memory.") - } - vv1=VECTORDF(view_array)(h->range+1,nbin); - VECTORDF(memcpy)(vwidth,&vv1.vector); - vv1=VECTORDF(view_array)(h->range,nbin); - VECTORDF(sub)(vwidth,&vv1.vector); - vvreal=VECTORDF(view_array)(hreal->bin,nbin); - vv1=VECTORDF(view_array)(h->bin,nbin); - for(j=0;jrange,h->range,(nbin+1)*sizeof(*hreal->range)); - memset(hreal->bin,0,nbin*sizeof(*hreal->bin)); - //Construct real histogram - if(nodiag&&((long)j+nodiagshift>=0)&&((long)j+nodiagshift<(long)d->size2)) - { - for(k=(long)j+nodiagshift-1;k>=0;k--) - gsl_histogram_increment(hreal,MATRIXFF(get)(d,j,(size_t)k)); - for(k=(long)j+nodiagshift+1;k<(long)d->size2;k++) - gsl_histogram_increment(hreal,MATRIXFF(get)(d,j,(size_t)k)); - VECTORDF(scale)(&vvreal.vector,1./(double)(d->size2-1)); - } - else - { - for(k=0;k<(long)d->size2;k++) - gsl_histogram_increment(hreal,MATRIXFF(get)(d,j,(size_t)k)); - VECTORDF(scale)(&vvreal.vector,1./(double)(d->size2)); - } - //Convert to density histogram - VECTORDF(div)(&vvreal.vector,vwidth); - //Convert to probability central histogram - pij_llrtopij_convert_histograms_buffed(hreal,vnull,hc,vb1,vb2); - //Convert likelihoods to probabilities - vva=MATRIXFF(row)(ans,j); - pij_llrtopij_histogram_interpolate_linear(hc,&vvd.vector,&vva.vector); - } - CLEANVECD(vwidth)CLEANVECD(vnull) - } - CLEANUP - return 0; -#undef CLEANUP -} - -int pij_llrtopij_a_convert_single_self(MATRIXF* d,size_t n1,size_t n2,char nodiag,long nodiagshift) -{ -#define CLEANUP CLEANAMHIST(hreal,nth)CLEANAMHIST(hc,nth)\ - CLEANHIST(h)CLEANMATD(mb1)CLEANMATD(mb2)CLEANMATD(mnull)CLEANMATF(mb3)CLEANVECD(vwidth) - - size_t ng=d->size1; - size_t i,nbin; - gsl_histogram *h; - MATRIXD *mb1,*mb2,*mnull; - MATRIXF *mb3; - VECTORD *vwidth; - VECTORDF(view) vv1; - size_t nth; - - h=0; - mb1=mb2=mnull=0; - mb3=0; - vwidth=0; - //Validity checks - { - int nth0=omp_get_max_threads(); - assert(nth0>0); - nth=(size_t)nth0; - } - - - AUTOCALLOC(gsl_histogram*,hreal,nth,64) - AUTOCALLOC(gsl_histogram*,hc,nth,64) - if(!(hreal&&hc)) - ERRRET("Not enough memory."); - - //Construct null density histograms - { - FTYPE dmin,dmax; - if(nodiag) - MATRIXFF(minmax_nodiag)(d,&dmin,&dmax,nodiagshift); - else - MATRIXFF(minmax)(d,&dmin,&dmax); - if((!(dmin>=0))||gsl_isnan(dmax)) - ERRRET("Negative or NAN found in input data. It may invalidate follow up analysis. This may be due to incorrect previous steps.") - if(gsl_isinf(dmax)) - { - LOG(5,"INF found in input data. It may invalidate follow up analysis. This may be due to incorrect previous steps or duplicate rows (by Spearman correlation).") - MATRIXFF(set_inf)(d,-1); - if(nodiag) - MATRIXFF(minmax_nodiag)(d,&dmin,&dmax,nodiagshift); - else - MATRIXFF(minmax)(d,&dmin,&dmax); - MATRIXFF(set_value)(d,-1,dmax); - } - h=pij_llrtopij_a_nullhist_single((double)dmax,d->size2,n1,n2); - if(!h) - ERRRET("pij_llrtopij_a_nullhist_single failed.") - nbin=h->n; - } - //Memory allocation - { - size_t n1,n2; - pij_llrtopij_convert_histograms_get_buff_sizes(nbin,&n1,&n2); - mb1=MATRIXDF(alloc)(nth,n1); - mb2=MATRIXDF(alloc)(nth,n2); - mnull=MATRIXDF(alloc)(nth,nbin); - mb3=MATRIXFF(alloc)(nth,d->size2); - vwidth=VECTORDF(alloc)(nbin); - if(!(mb1&&mb2&&mnull&&mb3&&vwidth)) - ERRRET("Not enough memory.") - } - - //Prepare for real histogram - { - int ret; - for(i=0,ret=1;in+2); - ret=ret&&hreal[i]&&hc[i]; - } - if(!ret) - ERRRET("Not enough memory."); - } - - //Conversion - vv1=VECTORDF(view_array)(h->range+1,nbin); - VECTORDF(memcpy)(vwidth,&vv1.vector); - vv1=VECTORDF(view_array)(h->range,nbin); - VECTORDF(sub)(vwidth,&vv1.vector); - vv1=VECTORDF(view_array)(h->bin,nbin); - #pragma omp parallel - { - size_t ng1,ng2,id; - size_t j; - long k; - VECTORDF(view) vvreal,vvnull,vvb1,vvb2; - VECTORFF(view) vvb3,vva; - - id=(size_t)omp_get_thread_num(); - vvreal=VECTORDF(view_array)(hreal[id]->bin,nbin); - vvnull=MATRIXDF(row)(mnull,id); - vvb1=MATRIXDF(row)(mb1,id); - vvb2=MATRIXDF(row)(mb2,id); - vvb3=MATRIXFF(row)(mb3,id); - - threading_get_startend(ng,&ng1,&ng2); - - for(j=ng1;jrange,h->range,(nbin+1)*sizeof(*hreal[id]->range)); - memset(hreal[id]->bin,0,nbin*sizeof(*hreal[id]->bin)); - //Construct real histogram - if(nodiag&&((long)j+nodiagshift>=0)&&((long)j+nodiagshift<(long)d->size2)) - { - for(k=(long)j+nodiagshift-1;k>=0;k--) - gsl_histogram_increment(hreal[id],MATRIXFF(get)(d,j,(size_t)k)); - for(k=(long)j+nodiagshift+1;k<(long)d->size2;k++) - gsl_histogram_increment(hreal[id],MATRIXFF(get)(d,j,(size_t)k)); - VECTORDF(scale)(&vvreal.vector,1./(double)(d->size2-1)); - } - else - { - for(k=0;k<(long)d->size2;k++) - gsl_histogram_increment(hreal[id],MATRIXFF(get)(d,j,(size_t)k)); - VECTORDF(scale)(&vvreal.vector,1./(double)(d->size2)); - } - - //Convert to density histogram - VECTORDF(div)(&vvreal.vector,vwidth); - //Convert to probability central histogram - pij_llrtopij_convert_histograms_buffed(hreal[id],&vvnull.vector,hc[id],&vvb1.vector,&vvb2.vector); - //Convert likelihoods to probabilities - vva=MATRIXFF(row)(d,j); - pij_llrtopij_histogram_interpolate_linear(hc[id],&vvb3.vector,&vva.vector); - } - } - - CLEANUP - return 0; -#undef CLEANUP -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/pij/llrtopij_a.h b/pij/llrtopij_a.h deleted file mode 100644 index 9de373c..0000000 --- a/pij/llrtopij_a.h +++ /dev/null @@ -1,82 +0,0 @@ -/* Copyright 2016, 2017 Lingfei Wang - * - * This file is part of Findr. - * - * Findr is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Findr is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Findr. If not, see . - */ -/* This file contains the conversion from log likelihood ratio to probabilities, - * when gene pairs of the same A are converted together. - * - */ - -#ifndef _HEADER_LIB_PIJ_LLRTOPIJ_A_H_ -#define _HEADER_LIB_PIJ_LLRTOPIJ_A_H_ -#include "../base/config.h" -#include "../base/gsl/histogram.h" -#include "../base/types.h" -#ifdef __cplusplus -extern "C" -{ -#endif - -/* Construct multiple null histograms for different genotype value counts. - * The function calculates the null density histogram for random variable: - * x=-0.5*log(1-z1/(z1+z2)), where z1~chi2(i*n1c+n1d),z2~chi2(-i*n2c+n2d), - * i=0,...,nv-2. Histogram bin count and width are automatically determined - * from real data count (nd). - * For bin range settings, see histogram_unequalbins_fromnullcdf. - * For null density histogram from pdf, see pij_nulldist_hist_pdf. - * dmax: Specifies the histogram bound as [0,dmax). - * nv: Maximum number of values each genotype can type. Must be nv>=2. - * This limits the possible values of kv in distribution, and - * also output histogram count. - * nd: Count of real data to form real histograms. This is used to - * automatically decide number of bins and widths. - * n1c, - * n1d, - * n2c - * n2d: Parameters of null distribution. - * Return: [nv-1]. Constructed null distribution histograms with preset - * bin ranges and values as density. Genotypes with i values have - * histogram stored in Return[i-2]. - */ -gsl_histogram** pij_llrtopij_a_nullhist(double dmax,size_t nv,size_t nd,long n1c,size_t n1d,long n2c,size_t n2d); - -/* Convert LLR of real data to probabilities, when the distribution - * of LLR of null distribution can be calculated analytically to follow - * x=-0.5*log(1-z1/(z1+z2)), where z1~chi2(n1),z2~chi2(n2). - * The conversion is performed for each gene A, i.e. per row of d and dconv. - * This function is older than pij_llrtopij_a_convert_single so it is not parallel. - * Make it parallel before using. - * d: [nrow,nx] The data to use for calculation of conversion rule from LLR to pij. - * dconv: [nrow,nd] The data of LLR to actually convert to pij. Can be same with d. - * ans: [nrow,nd] The output location of converted pij from dconv. - * n1, - * n2: Parameters of null distribution. - * nodiag: Whether diagonal elements of d should be ignored when converting - * to probabilities. - * nodiagshift: Diangonal column shift for nodiag==1. - * For nodiagshift>0/<0, use upper/lower diagonal. - * Return: 0 on success. - */ -int pij_llrtopij_a_convert_single(const MATRIXF* d,const MATRIXF* dconv,MATRIXF* ans,size_t n1,size_t n2,char nodiag,long nodiagshift); - -// Same with pij_llrtopij_a_convert_single, for d=dconv=ans. Saves memory. -int pij_llrtopij_a_convert_single_self(MATRIXF* d,size_t n1,size_t n2,char nodiag,long nodiagshift); - - -#ifdef __cplusplus -} -#endif -#endif diff --git a/pij/llrtopij_tot.c b/pij/llrtopij_tot.c deleted file mode 100644 index 1364ca9..0000000 --- a/pij/llrtopij_tot.c +++ /dev/null @@ -1,121 +0,0 @@ -/* Copyright 2016, 2017 Lingfei Wang - * - * This file is part of Findr. - * - * Findr is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Findr is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Findr. If not, see . - */ -#include "../base/config.h" -#include -#include -#include -#include -#include -#include "../base/gsl/math.h" -#include "../base/gsl/histogram.h" -#include "../base/gsl/blas.h" -#include "../base/logger.h" -#include "../base/macros.h" -#include "../base/data_process.h" -#include "../base/threading.h" -#include "../base/histogram.h" -#include "nullhist.h" -#include "llrtopij_tot.h" -#include "llrtopij.h" -#pragma GCC diagnostic ignored "-Wunused-parameter" - -int pij_llrtopij_tot_convert(const VECTORF* d,const VECTORF* dconv,VECTORF* ans,gsl_histogram* (*fh)(const VECTORF*,const void*),int (*fnh)(const void*,gsl_histogram*),const void* ph,const void* pnh,double* nullratio) -{ -#define CLEANUP CLEANHIST(hist)CLEANHIST(histc)AUTOFREE(histn)AUTOFREEVEC(vbuff) - - gsl_histogram *hist,*histc; -// double* histn; Normal histogram of null density - double* tp; - VECTORDF(view) vv; - size_t i; - int ret; - - hist=fh(d,ph); - if(!hist) - { - LOG(1,"Histogram bin range construction failed.") - return 1; - } - AUTOALLOC(double,histn,hist->n,1000) - histc=gsl_histogram_alloc(hist->n+2); - AUTOALLOCVECD(vbuff,hist->n,1000) - if(!(histn&&histc&&vbuff)) - ERRRET("Not enough memory.") - //Real histogram - for(i=0;isize;i++) - gsl_histogram_increment(hist,(double)VECTORFF(get)(d,i)); - - //Null histogram - tp=histn; - histn=hist->bin; - hist->bin=tp; - ret=fnh(pnh,hist); - tp=histn; - histn=hist->bin; - hist->bin=tp; - if(ret) - ERRRET("Custom null histogram function failed.") - - //Calculate ratio of true distribution - { - VECTORDF(view) vvnull=VECTORDF(view_array)(histn,hist->n); - //convert to density histogram - vv=VECTORDF(view_array)(hist->range+1,hist->n); - VECTORDF(memcpy)(vbuff,&vv.vector); - vv=VECTORDF(view_array)(hist->range,hist->n); - VECTORDF(sub)(vbuff,&vv.vector); - VECTORDF(div)(&vvnull.vector,vbuff); - vv=VECTORDF(view_array)(hist->bin,hist->n); - VECTORDF(scale)(&vv.vector,1./(double)d->size); - VECTORDF(div)(&vv.vector,vbuff); - if(pij_llrtopij_convert_histograms(hist,&vvnull.vector,histc)) - ERRRET("Failed in pij_llrtopij_convert_histograms.") - } - //Convert likelihoods to probabilities - pij_llrtopij_histogram_interpolate_linear(histc,dconv,ans); - - if(nullratio) - { - *nullratio=1-BLASF(asum)(ans)/(FTYPE)ans->size; - LOG(9,"Null distribution ratio: %G",*nullratio) - } - CLEANUP - return 0; -#undef CLEANUP -} - - - - - - - - - - - - - - - - - - - - - diff --git a/pij/llrtopij_tot.h b/pij/llrtopij_tot.h deleted file mode 100644 index 5c32cf0..0000000 --- a/pij/llrtopij_tot.h +++ /dev/null @@ -1,93 +0,0 @@ -/* Copyright 2016, 2017 Lingfei Wang - * - * This file is part of Findr. - * - * Findr is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Findr is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Findr. If not, see . - */ -/* This file contains the conversion from log likelihood ratio to probabilities, - * when all gene pairs are considered together. - * - */ - -#ifndef _HEADER_LIB_PIJ_LLRTOPIJ_TOT_H_ -#define _HEADER_LIB_PIJ_LLRTOPIJ_TOT_H_ -#include "../base/config.h" -#include "../base/gsl/histogram.h" -#include "../base/types.h" -#ifdef __cplusplus -extern "C" -{ -#endif - -/* Converts log likelihood ratio data into probabilities based on the reference null log likelihood ratios. - * The approach assumes the real data is the outcome of a mixture of alternative and null hypotheses. - * The approach first simulates null log likelihood ratios from permuted the real data, and then - * draw histograms of log likelihood ratios for both real and only null situations. - * Assuming on one tail of the histogram, the real data should contain mostly (if not only) null hypothesis. - * The null fraction can be known at the tail, and therefore at every bin based on the simulated - * null histogram shape. The final probability is defined to be the ratio of alternative hypothesis versus - * total, only on that bin. - * NOTE: Real and simulated data should both be non-negative. Real data should reach true probability->0 - * at the limit of log likelihood ratio ->0+. - * d: Real log likelihood ratios, to be compared against permuted (null) data. Real data is assumed to contain - * both null and alternative hypotheses. - * dconv: Real log likelihood ratios to be converted to probabilities, based on the comparison between d and null distribution. - * Same with d in most cases. - * ans: Output vector for the probabilities. Have same size with dconv, and each provides the estimated probability - * of the corresponding entry of dconv. - * fh: Function to obtain real histogram from data d. It should automatically determine bin widths. Takes parameters: - * Param 1: Data points. (i.e. d). - * Param 2: Parameter of the function to be specified in a later parameter, and passed on by pij_llrtopij_convert. - * Return: Constructed gsl_histogram if success, or 0 if fail. - * fnh: Function to obtain null histogram. Can be simulation based or analytical. They are defined in pij_gassist/nullhist.h. - * Param 1: Parameter to pass on to the function and defined later in the parameters. - * Param 2: Existing empty histogram to fill for the function. - * Param 3: Number of parallel threads. - * Return: 0 if success. - * ph: Parameter (2) to be passed onto fh. - * pnh: Parameter (1) to be passed onto fnh. - * nullratio: if not NULL, return the ratio of null data in real data into nullratio. - * Return: 0 on success. - */ -int pij_llrtopij_tot_convert(const VECTORF* d,const VECTORF* dconv,VECTORF* ans,gsl_histogram* (*fh)(const VECTORF*,const void*),int (*fnh)(const void*,gsl_histogram*),const void* ph,const void* pnh,double* nullratio); - - - - - - - - - - - - - - - - - - - - - - - - - - -#ifdef __cplusplus -} -#endif -#endif diff --git a/pij/llrtopv.c b/pij/llrtopv.c index 4c1ce77..af8fa09 100644 --- a/pij/llrtopv.c +++ b/pij/llrtopv.c @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/pij/llrtopv.h b/pij/llrtopv.h index 931c183..b91df3d 100644 --- a/pij/llrtopv.h +++ b/pij/llrtopv.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/pij/nulldist.c b/pij/nulldist.c index 3209f99..92dc3e4 100644 --- a/pij/nulldist.c +++ b/pij/nulldist.c @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/pij/nulldist.h b/pij/nulldist.h index 5147bca..6a57a2b 100644 --- a/pij/nulldist.h +++ b/pij/nulldist.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * diff --git a/pij/nullhist.c b/pij/nullhist.c index a73fc86..796dc8e 100644 --- a/pij/nullhist.c +++ b/pij/nullhist.c @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * @@ -18,177 +18,98 @@ #include "../base/config.h" #include #include -#include -#include -#include "../base/gsl/blas.h" +#include +#include "../base/gsl/histogram.h" #include "../base/logger.h" #include "../base/macros.h" #include "../base/histogram.h" -#include "../base/threading.h" -#include "nullhist.h" #include "nulldist.h" +#include "nullhist.h" -/* Null histogram generation of sample-model method on single thread. - * g,t,t2,sampler,modeler,ps: See pij_nullhist_sample_model_param. - * pmc: Model container object. See definition in nullmodel.h. - * Return: 0 if success. - */ -static int pij_nullhist_sample_model_single(const void* data,const struct pij_nullsampler* sampler,const struct pij_nullmodeler* modeler,const void* ps,void* pmc) + + +gsl_histogram* pij_nullhist_single(double dmax,size_t nd,size_t n1,size_t n2) { -#define CLEANUP if(ptrs){sampler->close(ptrs);ptrs=0;}\ - if(pm){modeler->close(pm);pm=0;} - - struct timeval tv; - void *ptrs,*pm; - size_t n1,n2; - size_t i; +#define CLEANUP CLEANHIST(h) + struct pij_nulldist_pdfs_param param={n1,n2}; + size_t nbin; + gsl_histogram *h=0; - assert(sampler&&modeler); - ptrs=pm=0; - //Initialize sampler with timely random seed. - gettimeofday(&tv,NULL); - ptrs=sampler->init(data,ps,(long unsigned int)tv.tv_usec); - if(!ptrs) - ERRRET("Sampler construction failed.") - //Obtain dimensions of random samples. - sampler->dimensions(ptrs,&n1,&n2); assert(n1&&n2); - - AUTOALLOCVECF(vd,n1,30000) - if(!vd) - ERRRET("Not enough memory.") - //Initialize modeler object given model container object. - pm=modeler->init(pmc,data,n1,n2); - if(!pm) - { - AUTOFREEVEC(vd) - ERRRET("Modeler construction failed.") - } - //Sample - for(i=0;isample(ptrs,vd); - modeler->input(pm,vd); - } - //Synced updated of model container. - #pragma omp critical - modeler->merge(pm,pmc); - CLEANUP - AUTOFREEVEC(vd) - return 0; + dmax*=(1+1E-6); + nbin=histogram_unequalbins_param_count(nd); + if(nbin<5) + ERRRETV(0,"Determined "PRINTFSIZET" bins constructed. Bin count too small.",nbin) + else if(nbin<10) + LOG(5,"Determined "PRINTFSIZET" bins, smaller than recommended minimum bin count (10).",nbin) + else + LOG(10,"Determined "PRINTFSIZET" bins.",nbin) + h=gsl_histogram_alloc(nbin); + if(!h) + ERRRETV(0,"Not enough memory.") + //Null density histogram + gsl_histogram_set_ranges_uniform(h,0,dmax); + //Set null histogram ranges + if(histogram_unequalbins_fromnullpdfs(nbin,h->range,pij_nulldist_pdfs,¶m)) + ERRRETV(0,"histogram_unequalbins_fromnullpdfs failed.") + //Calculate null density histogram + if(pij_nulldist_hist_pdf(h->range,nbin,h->bin,param.n1,param.n2,5)) + ERRRETV(0,"pij_nulldist_hist_pdf failed.") + return h; #undef CLEANUP } -int pij_nullhist_sample_model(const void* param,gsl_histogram* h) +gsl_histogram** pij_nullhist(double dmax,size_t nv,size_t nd,long n1c,size_t n1d,long n2c,size_t n2d) { -#define CLEANUP if(container){\ - p->modeler->close_container(container);\ - container=0;} - const struct pij_nullhist_sample_model_param *p=param; +#define CLEANUP if(h){for(i=0;imodeler->init_container(p->d,p->pm,h); - if(!container) - ERRRET("Modeler container construction failed.") - - #pragma omp parallel shared(ret) + assert(nv>=2); + dmax*=(1+1E-6); + CALLOCSIZE(h,nv-1); + if(!h) + ERRRETV(0,"Not enough memory.") + nbin=histogram_unequalbins_param_count(nd); + if(nbin<5) + ERRRETV(0,"Determined "PRINTFSIZET" bins constructed. Bin count too small.",nbin) + else if(nbin<10) + LOG(5,"Determined "PRINTFSIZET" bins, smaller than recommended minimum bin count (10).",nbin) + else + LOG(10,"Determined "PRINTFSIZET" bins.",nbin) + ret=1; + for(i=0;idsize]; - //Initializing block selection - if(p->partition(p->d,d,(size_t)omp_get_thread_num(),(size_t)omp_get_num_threads())) - { - int retth; - //Calculate null histograms - retth=pij_nullhist_sample_model_single(d,p->sampler,p->modeler,p->ps,container); - #pragma omp atomic - ret+=retth; - } + gsl_histogram_set_ranges_uniform(h[i],0,dmax); + //Set null histogram ranges + param.n1=(size_t)((long)i*n1c+(long)n1d); + param.n2=(size_t)(-(long)i*n2c+(long)n2d); + if(histogram_unequalbins_fromnullpdfs(nbin,h[i]->range,pij_nulldist_pdfs,¶m)) + ERRRETV(0,"histogram_unequalbins_fromnullpdfs failed.") + //Calculate null density histogram + if(pij_nulldist_hist_pdf(h[i]->range,nbin,h[i]->bin,param.n1,param.n2,5)) + ERRRETV(0,"pij_nulldist_hist_pdf failed.") } - if(ret) - ERRRET("Failed to construct histogram.") - - //Output from model container - ret=p->modeler->output(container,h); - if(ret) - ERRRET("Failed to output histogram.") - CLEANUP - return 0; + return h; #undef CLEANUP } -int pij_nullhist_analytical_pdf(const void* param,gsl_histogram* h) -{ -#define CLEANUP CLEANVECD(loc)CLEANVECD(val) - const struct pij_nullhist_analytical_pdf_param *p=param; - - VECTORD *loc,*val; - VECTORDF(view) vvh=VECTORDF(view_array)(h->bin,h->n); - size_t i; - size_t nsp; - double v; - - assert((p->nsplit<10)); - nsp=(size_t)1<<(p->nsplit); - loc=VECTORDF(alloc)(h->n*nsp); - val=VECTORDF(alloc)(h->n*nsp); - if(!(loc&&val)) - ERRRET("Not enough memory.") - - //Construct bin ranges - if(nsp==1) - { - VECTORDF(const_view) vv1=VECTORDF(const_view_array)(h->range,h->n); - VECTORDF(const_view) vv2=VECTORDF(const_view_array)(h->range+1,h->n); - VECTORDF(memcpy)(loc,&vv1.vector); - VECTORDF(add)(loc,&vv2.vector); - VECTORDF(scale)(loc,0.5); - } - else - { - VECTORDF(const_view) vvc=VECTORDF(const_view_array)(h->range,h->n+1); - histogram_finer_central(&vvc.vector,loc,nsp); - } - //Calculate bin densities - if(p->func(loc,val,p->param)) - { - CLEANUP - return 1; - } - - //Shrink to output - if(nsp==1) - { - VECTORDF(memcpy)(&vvh.vector,val); - } - else - { - VECTORDF(set_zero)(&vvh.vector); - for(i=0;in); - VECTORDF(add)(&vvh.vector,&vvc.vector); - } - } - //Convert to bin values from densities - { - VECTORDF(const_view) vv1=VECTORDF(const_view_array)(h->range,h->n); - VECTORDF(const_view) vv2=VECTORDF(const_view_array)(h->range+1,h->n); - VECTORDF(view) vv3=VECTORDF(subvector)(loc,0,h->n); - VECTORDF(memcpy)(&vv3.vector,&vv2.vector); - VECTORDF(sub)(&vv3.vector,&vv1.vector); - VECTORDF(mul)(&vvh.vector,&vv3.vector); - } - //Scale to unity. - v=gsl_blas_dasum(&vvh.vector); - VECTORDF(scale)(&vvh.vector,1/v); - CLEANUP - return 0; -#undef CLEANUP -} + + + + + + + + diff --git a/pij/nullhist.h b/pij/nullhist.h index 509aab4..ba18906 100644 --- a/pij/nullhist.h +++ b/pij/nullhist.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * @@ -15,122 +15,59 @@ * You should have received a copy of the GNU Affero General Public License * along with Findr. If not, see . */ -/* This file contains the methods to construct null histogram. - * Each method contains two parts: an interface function to produce - * the null histogram and a struct for the parameters needed by the - * function. - * Currently two methods are implemented. - * 1: Sample-model method utilizes sampler functions - * to randomly sample null data and modeler functions to model the - * null histogram based on sampled null data. This is a generic - * method and can be tailored for specific needs. - * 2: Analytical method specifically for pij model, where the distribution - * of LLR of null hypothesis is exactly calculable. This is - * the actually used method for faster and more precise performance. - * - * Regardless of method, the interface function should conform with - * the following definition: - * (*int)(const void* p,gsl_histogram* h); - * p: Parameter of the method. - * h: Existing histogram with range specified. The function should - * fill this histogram in the bins. - * Return: 0 on success. +/* This part produces the histogram of parametric null distributions. */ #ifndef _HEADER_LIB_PIJ_NULLHIST_H_ #define _HEADER_LIB_PIJ_NULLHIST_H_ #include "../base/config.h" -#include "../base/gsl/histogram.h" #include "../base/types.h" -#include "nullsampler.h" -#include "nullmodeler.h" +#include "../base/gsl/histogram.h" #ifdef __cplusplus extern "C" { #endif -/**************************************************************** - * The sample-model method for generation of null histogram - * Uses a sampler function to randomly sample null data and a modeler - * to model the histogram of null data. The samplers are defined - * in pij/nullsampler.h. The modelers are defined in - * pij/nullmodeler.h. The sample-model method is generic - * but for the specific pij model, analytical method (see below) - * perform more precise and faster. - ***************************************************************/ - -struct pij_nullhist_sample_model_param -{ - //Input data - const void *d; - /* Data struct size (for d). - * Must be small and use pointers, for memory saving and easier partitioning. - */ - size_t dsize; - /* Partition function to partition mission/data into smaller chunks. - * Look threading_threading_get_startend for help - * src: Larger data to be partitioned, having same type as d - * dest: Smaller data after partitioning, same type as d - * id: Id of the data chuck (out of total) to obtain from partition - * total: Total number of chucks for partitioning. - * Return: 'Size' of dest after partition. Must be !=0 for nonzero data size - * which needs further process, or 0 if no calculation is required - * due to zero size in the partition. - */ - size_t (*partition)(const void* src,void* dest,size_t id,size_t total); - //Sampler function struct - const struct pij_nullsampler* sampler; - //Modeler function struct - const struct pij_nullmodeler* modeler; - //Parameter of sampler function - const void* ps; - //Parameter of modeler function - const void* pm; -}; - -// Interface function of sample-model method, multithreaded. -int pij_nullhist_sample_model(const void* p,gsl_histogram* h); - - -/**************************************************************** - * The analytical methods to calculate null histograms for pij - * is faster and more precise. This method calculates pdf values at - * evenly spreaded points within each bin for better accuracy. - * The three steps of pij share the same analytical formula of - * null histograms. - ***************************************************************/ - - -struct pij_nullhist_analytical_pdf_param -{ - /* Number of split within each bin. 2^nsplit central points are - * taken and the mean is calculated as the average pdf within the bin. - */ - size_t nsplit; - /* Null pdf function to draw null histogram from - * Param 1: Coordinates to calculate pdfs - * Param 2: Output buffer for pdfs as return - * Param 3: Parameter accepted by function - * Return: 0 on success. - */ - int (*func)(const VECTORD*,VECTORD*,const void*); - //Parameter accepted by pdf function (Param 3) - const void* param; -}; - -/* Generic interface function of analytical method. - * Same as interface function except with one extra parameter in the end. - * func: to specifiy which function to use to calculate null pdf. - */ -int pij_nullhist_analytical_pdf(const void* param,gsl_histogram* h); - - - - - - - +/* Construct one null histogram for a specific genotype value count. + * The function calculates the null density histogram for random variable: + * x=-0.5*log(1-z1/(z1+z2)), where z1~chi2(n1),z2~chi2(n2), + * Histogram bin count and width are automatically determined + * from real data count (nd). + * For bin range settings, see histogram_unequalbins_fromnullcdf. + * For null density histogram from pdf, see pij_nulldist_hist_pdf. + * dmax: Specifies the histogram bound as [0,dmax). + * nd: Count of real data to form real histograms. This is used to + * automatically decide number of bins and widths. + * n1, + * n2: Parameters of null distribution. + * Return: Constructed null distribution histograms with preset + * bin ranges and values as density. + */ +gsl_histogram* pij_nullhist_single(double dmax,size_t nd,size_t n1,size_t n2); + +/* Construct multiple null histograms for different genotype value counts. + * The function calculates the null density histogram for random variable: + * x=-0.5*log(1-z1/(z1+z2)), where z1~chi2(i*n1c+n1d),z2~chi2(-i*n2c+n2d), + * i=0,...,nv-2. Histogram bin count and width are automatically determined + * from real data count (nd). + * For bin range settings, see histogram_unequalbins_fromnullcdf. + * For null density histogram from pdf, see pij_nulldist_hist_pdf. + * dmax: Specifies the histogram bound as [0,dmax). + * nv: Maximum number of values each genotype can type. Must be nv>=2. + * This limits the possible values of kv in distribution, and + * also output histogram count. + * nd: Count of real data to form real histograms. This is used to + * automatically decide number of bins and widths. + * n1c, + * n1d, + * n2c + * n2d: Parameters of null distribution. + * Return: [nv-1]. Constructed null distribution histograms with preset + * bin ranges and values as density. Genotypes with i values have + * histogram stored in Return[i-2]. + */ +gsl_histogram** pij_nullhist(double dmax,size_t nv,size_t nd,long n1c,size_t n1d,long n2c,size_t n2d); diff --git a/pij/nullmodeler.c b/pij/nullmodeler.c deleted file mode 100644 index 987457f..0000000 --- a/pij/nullmodeler.c +++ /dev/null @@ -1,220 +0,0 @@ -/* Copyright 2016, 2017 Lingfei Wang - * - * This file is part of Findr. - * - * Findr is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Findr is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Findr. If not, see . - */ -#include "../base/config.h" -#include -#include -#include -#include -#include -#include "../base/gsl/blas.h" -#include "../base/logger.h" -#include "../base/macros.h" -#include "nullmodeler.h" -#pragma GCC diagnostic ignored "-Wunused-parameter" - -static void* pij_nullmodeler_exp_init(const void* c,const void* d,size_t n1,size_t n2); - -static void pij_nullmodeler_exp_close(void* m); - -static void* pij_nullmodeler_exp_init_container(const void* d,const void* pm,const gsl_histogram* h) -{ - //Model container same with modeler - return pij_nullmodeler_exp_init(0,d,0,0); -} - -static int pij_nullmodeler_exp_output(const void* c,gsl_histogram* h) -{ -#define CLEANUP AUTOFREEVEC(vwidth) - const struct pij_nullmodeler_exp_state* p=c; - double v; - size_t i; - AUTOALLOCVECD(vwidth,h->n,1000) - VECTORDF(view) vv1,vv2; - - if(!vwidth) - ERRRET("Not enough memory.") - - //Calculate central exponential values as density. - vv1=VECTORDF(view_array)(h->range,h->n); - vv2=VECTORDF(view_array)(h->bin,h->n); - VECTORDF(memcpy)(&vv2.vector,&vv1.vector); - vv1=VECTORDF(view_array)(h->range+1,h->n); - VECTORDF(add)(&vv2.vector,&vv1.vector); - VECTORDF(scale)(&vv2.vector,-(double)(p->n)/(2*p->v)); - for(i=0;in;i++) - h->bin[i]=exp(h->bin[i]); - //Bin=density*width - VECTORDF(memcpy)(vwidth,&vv1.vector); - vv1=VECTORDF(view_array)(h->range,h->n); - VECTORDF(sub)(vwidth,&vv1.vector); - VECTORDF(mul)(&vv2.vector,vwidth); - //Normalize to total 1. - v=gsl_blas_dasum(&vv2.vector); - VECTORDF(scale)(&vv2.vector,1/v); - CLEANUP - return 0; -#undef CLEANUP -} - -static void pij_nullmodeler_exp_close_container(void* c) -{ - pij_nullmodeler_exp_close(c); -} - -static void* pij_nullmodeler_exp_init(const void* c,const void* d,size_t n1,size_t n2) -{ - struct pij_nullmodeler_exp_state* p; - MALLOCSIZE(p,1); - if(!p) - return 0; - p->n=0; - p->v=0; - return p; -} -static void pij_nullmodeler_exp_input(void* m,const VECTORF* data) -{ - struct pij_nullmodeler_exp_state* p=m; - p->v+=BLASF(asum)(data); - p->n+=data->size; -} - -static void pij_nullmodeler_exp_merge(const void* m,void* c) -{ - const struct pij_nullmodeler_exp_state* pm=m; - struct pij_nullmodeler_exp_state* pc=c; - pc->n+=pm->n; - pc->v+=pm->v; -} - -static void pij_nullmodeler_exp_close(void* m) -{ - if(m) - free(m); -} - -const struct pij_nullmodeler pij_nullmodeler_exp={ - pij_nullmodeler_exp_init_container, - pij_nullmodeler_exp_output, - pij_nullmodeler_exp_close_container, - pij_nullmodeler_exp_init, - pij_nullmodeler_exp_input, - pij_nullmodeler_exp_merge, - pij_nullmodeler_exp_close -}; - - - - - - - -static void* pij_nullmodeler_naive_init_container(const void* d,const void* pm,const gsl_histogram* h) -{ -#define CLEANUP if(p){CLEANHIST(p->h)free(p);p=0;} - struct pij_nullmodeler_naive_state *p; - CALLOCSIZE(p,1); - if(!p) - ERRRETV(0,"Not enough memory.") - p->h=gsl_histogram_alloc(h->n); - if(!p->h) - ERRRETV(0,"Not enough memory.") - //Initialize histogram with same ranges. - gsl_histogram_set_ranges(p->h,h->range,h->n+1); - return p; -#undef CLEANUP -} - -static int pij_nullmodeler_naive_output(const void* c,gsl_histogram* h) -{ - const struct pij_nullmodeler_naive_state* p=c; - VECTORDF(view) vv=VECTORDF(view_array)(h->bin,h->n); - - memcpy(h->bin,p->h->bin,h->n*sizeof(*h->bin)); - //Scale to unit total probability. - VECTORDF(scale)(&vv.vector,1/gsl_blas_dasum(&vv.vector)); - return 0; -} - -static void pij_nullmodeler_naive_close_container(void* c) -{ - struct pij_nullmodeler_naive_state* p=c; - CLEANHIST(p->h) - free(p); -} - -static void* pij_nullmodeler_naive_init(const void* c,const void* d,size_t n1,size_t n2) -{ - const struct pij_nullmodeler_naive_state* p=c; - return pij_nullmodeler_naive_init_container(d,0,p->h); -} - -static void pij_nullmodeler_naive_input(void* m,const VECTORF* data) -{ - struct pij_nullmodeler_naive_state* p=m; - size_t i; - for(i=0;isize;i++) - gsl_histogram_increment(p->h,VECTORFF(get)(data,i)); -} - -static void pij_nullmodeler_naive_merge(const void* m,void* c) -{ - const struct pij_nullmodeler_naive_state* pm=m; - struct pij_nullmodeler_naive_state* pc=c; - gsl_histogram_add(pc->h,pm->h); -} - -static void pij_nullmodeler_naive_close(void* m) -{ - pij_nullmodeler_naive_close_container(m); -} - -const struct pij_nullmodeler pij_nullmodeler_naive={ - pij_nullmodeler_naive_init_container, - pij_nullmodeler_naive_output, - pij_nullmodeler_naive_close_container, - pij_nullmodeler_naive_init, - pij_nullmodeler_naive_input, - pij_nullmodeler_naive_merge, - pij_nullmodeler_naive_close -}; - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/pij/nullmodeler.h b/pij/nullmodeler.h deleted file mode 100644 index 0fd04e2..0000000 --- a/pij/nullmodeler.h +++ /dev/null @@ -1,143 +0,0 @@ -/* Copyright 2016, 2017 Lingfei Wang - * - * This file is part of Findr. - * - * Findr is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Findr is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Findr. If not, see . - */ -/* This file contains the modelers of LLR histograms of null hypothesis. - * Modelers are used by sample-model method to produce null histogram. - * (See pij_nullhist_sample_model.) - * - * Each null modeler is consisted of seven functions and a parameter struct. - * For function formats, see struct pij_nullmodeler. - * The nullhist function will first invoke 'init_container' method to obtain - * the unique model container object. Then for each thread, one modeler object - * is constructed as a child of the container, via 'init' method. Each thread - * holds a separate modeler object and use 'input' method to feed in data. - * Upon finishing, every thread merges modeler into the model container, with - * 'merge'. Merge takes place for one thread at a time, guaranteed by nullhist. - * The thread will 'close' modeler before exit. When all child threads completes, - * the master thread will apply 'output' method on the model container to obtain - * null histogram, and then use 'close_container' method to free up container. - * - * Two modelers are implemented: - * 1: Exponential modeler assumes LLR satisfies an exponential distribution. - * 2: Naive modeler assumes the true LLR histogram is in exact agreement with - * the sampled histogram. - */ - -#ifndef _HEADER_LIB_PIJ_NULLMODELER_H_ -#define _HEADER_LIB_PIJ_NULLMODELER_H_ -#include "../base/config.h" -#include "../base/gsl/histogram.h" -#include "../base/types.h" -#ifdef __cplusplus -extern "C" -{ -#endif - - -struct pij_nullmodeler -{ - /* Container initialization function. Should be invoked before - * any other operation. - * Function should perform parameter storage and memory allocations here. - * Somer modelers may use only part of the parameters. - * Param 1: Input data - * Param 2: Modeler parameter - * Param 3: Histogram as output. - * Return: Modeler container parameter struct pointer, or 0 if failed. - */ - void* (*init_container)(const void*,const void*,const gsl_histogram*); - /* Output modeler container to histogram. - * Histogram should sum to 1. - * Param 1: Modeler container parameter struct. - * Param 2: Output histogram. Bin ranges must be identical with the one - * referenced in init. - */ - int (*output)(const void*,gsl_histogram*); - /* Closes modeler container and frees memory. - * Param 1: Modeler container parameter struct. - */ - void (*close_container)(void*); - /* Initialization function. Should be invoked after container but - * before any other operation. - * Function should perform parameter storage and memory allocations here. - * Somer modelers may use only part of the parameters. - * Param 1: Output model container for reference. - * Param 2: Input data - * Param 3: n1. Sample size to be provided each time. - * Param 4: n2. Number of times samples will be provided. - * So n1*n2 is the total sample count. - * Return: Modeler parameter struct pointer, or 0 if failed. - */ - void* (*init)(const void*,const void*,size_t,size_t); - /* Input samples into modeler. Each sample should count 1. - * Param 1: Modeler parameter struct. - * Param 2: Existing samples. - */ - void (*input)(void*,const VECTORF*); - /* Perform final calculations and merge modeler into modeler container. - * Function does not need to guarantee threadsafe. - * Param 1: Modeler struct. - * Param 2: Modeler container. - */ - void (*merge)(const void*,void*); - /* Close modeler and frees memory. - * Param 1: Modeler struct. - */ - void (*close)(void*); - -}; - -/************************************************************************* - * Exponential modeler believes null data yields exponential distribution. - * This requries all data to be non-negative. - * The modeler seeks the best exponential coefficient via maximization of - * likelihood. - ************************************************************************/ - -//Modeler state and also model container state -struct pij_nullmodeler_exp_state -{ - //Number of samples - size_t n; - //Sum of samples - FTYPE v; -}; - -extern const struct pij_nullmodeler pij_nullmodeler_exp; - - -/************************************************************************* - * Naive modeler assumes the pdf of falling into each bin is exactly proportional - * to the number of instances sampled to be in that bin. - * Therefore a simple accumulation of samples in existing bins would suffice. - ************************************************************************/ -struct pij_nullmodeler_naive_state -{ -//Histogram of sampled null data. - gsl_histogram* h; -}; - -extern const struct pij_nullmodeler pij_nullmodeler_naive; - - - - - -#ifdef __cplusplus -} -#endif -#endif diff --git a/pij/nullsampler.h b/pij/nullsampler.h deleted file mode 100644 index c1ad98e..0000000 --- a/pij/nullsampler.h +++ /dev/null @@ -1,73 +0,0 @@ -/* Copyright 2016, 2017 Lingfei Wang - * - * This file is part of Findr. - * - * Findr is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Findr is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Findr. If not, see . - */ -/* This file contains the LLR sampler definition of null hypotheses. - * Samplers are used in combination with modelers for the estimation - * of histograms for the distribution of LLR of null hypothesis, as - * applied in sample-model method. (See pij_nullhist_sample_model.) - * - * Each null sampler is consisted of four functions and a parameter struct. - * For function formats, see struct pij_nullsampler. - * - * For multithreading, each thread holds a separate and independent sampler. - */ - -#ifndef _HEADER_LIB_PIJ_NULLSAMPLER_H_ -#define _HEADER_LIB_PIJ_NULLSAMPLER_H_ -#include "../base/config.h" -#include "../base/gsl/histogram.h" -#include "../base/types.h" -#include "../base/random.h" -#ifdef __cplusplus -extern "C" -{ -#endif - - -struct pij_nullsampler -{ - /* Initialization function. Should be invoked before any other operation. - * Function should perform parameter storage and memory allocations here. - * Somer samplers may use only part of the parameters. - * Param 1: Input data - * Param 2: Sampler parameters - * Param 3: Initial random seed. - * Return: Sampler parameter struct pointer, or 0 if failed. - */ - void* (*init)(const void*,const void*,unsigned long); - /* Obtain dimensionality of samples. - * Param 1: Sampler parameter struct. - * Param 2: Return the return size of vector each time sampling function - * is invoked. - * Param 3: Return the number of cycles sampling function should be invoked. - */ - void (*dimensions)(const void*,size_t*,size_t*); - /* Produce samples by random with the stated amount by function dimensions. - * Param 1: Sampler parameter struct. - * Param 2: Existing buffer to write samples into. - */ - void (*sample)(void*,VECTORF*); - /* Close sampler and frees memory. - * Param 1: Sampler parameter struct. - */ - void (*close)(void*); -}; - -#ifdef __cplusplus -} -#endif -#endif diff --git a/pij/rank.c b/pij/rank.c index bf3865b..3892272 100644 --- a/pij/rank.c +++ b/pij/rank.c @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * @@ -28,7 +28,7 @@ #include "../base/data_process.h" #include "../base/supernormalize.h" #include "../base/threading.h" -#include "llrtopij_a.h" +#include "llrtopij.h" #include "llrtopv.h" #include "rank.h" @@ -145,7 +145,7 @@ int pij_rank_pv(const MATRIXF* t,const MATRIXF* t2,MATRIXF* p,size_t memlimit) #undef CLEANUP } -/* Convert LLR into probabilities per A. Uses pij_llrtopij_a_convert. +/* Convert LLR into probabilities per A. Uses pij_llrtopij_convert. * ans: (ng,nt) Source real LLRs to compare with null LLRs, * also output location of converted probabilities. * ns: Number of samples, used for calculation of null distribution @@ -155,7 +155,7 @@ int pij_rank_pv(const MATRIXF* t,const MATRIXF* t2,MATRIXF* p,size_t memlimit) * For nodiagshift>0/<0, use upper/lower diagonals of corresponding id. * Return: 0 if succeed. */ -static int pij_rank_llrtopij_a(MATRIXF* ans,size_t ns,char nodiag,long nodiagshift) +static int pij_rank_llrtopij(MATRIXF* ans,size_t ns,char nodiag,long nodiagshift) { LOG(9,"Converting LLR to probabilities on per A basis.") if(ns<=2) @@ -163,22 +163,10 @@ static int pij_rank_llrtopij_a(MATRIXF* ans,size_t ns,char nodiag,long nodiagshi LOG(0,"Needs at least 3 samples to compute probabilities.") return 1; } - return pij_llrtopij_a_convert_single_self(ans,1,ns-2,nodiag,nodiagshift); + return pij_llrtopij_convert_single_self(ans,1,ns-2,nodiag,nodiagshift); } -/* Calculate probabilities of A--B based on LLR distributions of real data - * and null hypothesis. - * t: (ng,ns) Expression data for A - * t2: (nt,ns) Expression data for B - * p: (ng,nt) Output for probabilities A--B is true - * nodiag: When the top ng rows of t2 is exactly t, diagonals of pij are meaningless. - * In this case, set nodiag to 1 to avoid inclusion of NANs. For nodiag=0, t and t2 - * should not have any identical genes. - * pij: Function to convert LLR to probabilities, such as pij_rank_llrtopij_a - * memlimit:Specifies approximate memory usage. Function can fail if memlimit is too small. For large dataset, memory usage will be reduced by spliting t into smaller chunks and infer separately. For unlimited memory, set memlimit=-1. - * Return: 0 if succeed. - */ -static int pij_rank_any(const MATRIXF* t,const MATRIXF* t2,MATRIXF* p,char nodiag,int (*pij)(MATRIXF*,size_t,char,long),size_t memlimit) +int pij_rank(const MATRIXF* t,const MATRIXF* t2,MATRIXF* p,char nodiag,size_t memlimit) { #define CLEANUP CLEANMATF(tnew)CLEANMATF(tnew2) MATRIXF *tnew,*tnew2; //(nt,ns) Supernormalized transcript matrix @@ -210,6 +198,13 @@ static int pij_rank_any(const MATRIXF* t,const MATRIXF* t2,MATRIXF* p,char nodia if(!(tnew&&tnew2)) ERRRET("Not enough memory.") + //Check for identical rows in input data + { + VECTORFF(view) vbuff1=MATRIXFF(column)(tnew,0); + VECTORFF(view) vbuff2=MATRIXFF(row)(tnew2,0); + MATRIXFF(cmprow)(t,t2,&vbuff1.vector,&vbuff2.vector,nodiag,1); + } + //Step 1: Supernormalization LOG(9,"Supernormalizing...") MATRIXFF(memcpy)(tnew,t); @@ -228,7 +223,7 @@ static int pij_rank_any(const MATRIXF* t,const MATRIXF* t2,MATRIXF* p,char nodia VECTORFF(set_zero)(&vv.vector); } //Step 3: Convert log likelihood ratios to probabilities - if((ret=pij(p,ns,nodiag,0))) + if((ret=pij_rank_llrtopij(p,ns,nodiag,0))) LOG(1,"Failed to convert log likelihood ratios to probabilities.") //Cleanup @@ -237,18 +232,6 @@ static int pij_rank_any(const MATRIXF* t,const MATRIXF* t2,MATRIXF* p,char nodia #undef CLEANUP } -int pij_rank_a(const MATRIXF* t,const MATRIXF* t2,MATRIXF* p,char nodiag,size_t memlimit) -{ - return pij_rank_any(t,t2,p,nodiag,pij_rank_llrtopij_a,memlimit); -} - -int pij_rank(const MATRIXF* t,const MATRIXF* t2,MATRIXF* p,char nodiag,size_t memlimit) -{ - return pij_rank_a(t,t2,p,nodiag,memlimit); -} - - - diff --git a/pij/rank.h b/pij/rank.h index 2dd7368..d9716be 100644 --- a/pij/rank.h +++ b/pij/rank.h @@ -1,4 +1,4 @@ -/* Copyright 2016, 2017 Lingfei Wang +/* Copyright 2016-2018 Lingfei Wang * * This file is part of Findr. * @@ -45,11 +45,16 @@ extern "C" int pij_rank_pv(const MATRIXF* t,const MATRIXF* t2,MATRIXF* p,size_t memlimit); /* Calculate probabilities of A--B based on LLR distributions of real data - * and null hypothesis. Conversion from LLR to probability is per A. - * See pij_rank_pij_any. + * and null hypothesis. + * t: (ng,ns) Expression data for A + * t2: (nt,ns) Expression data for B + * p: (ng,nt) Output for probabilities A--B is true + * nodiag: When the top ng rows of t2 is exactly t, diagonals of pij are meaningless. + * In this case, set nodiag to 1 to avoid inclusion of NANs. For nodiag=0, t and t2 + * should not have any identical genes. + * memlimit:Specifies approximate memory usage. Function can fail if memlimit is too small. For large dataset, memory usage will be reduced by spliting t into smaller chunks and infer separately. For unlimited memory, set memlimit=-1. + * Return: 0 if succeed. */ -int pij_rank_a(const MATRIXF* t,const MATRIXF* t2,MATRIXF* p,char nodiag,size_t memlimit); -//Duplicate with a different name int pij_rank(const MATRIXF* t,const MATRIXF* t2,MATRIXF* p,char nodiag,size_t memlimit); @@ -66,7 +71,6 @@ int pij_rank(const MATRIXF* t,const MATRIXF* t2,MATRIXF* p,char nodiag,size_t me - #ifdef __cplusplus } #endif