Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
5cb9fd5
refactor: remove unnecessary condition for Z_buffer size in glm_c fun…
zankrut20 Mar 17, 2026
09d2274
refactor: optimize glm_c function for parallel processing with OpenMP
zankrut20 Mar 17, 2026
7a1d997
refactor: improve parameter passing and optimize loop constructs in g…
zankrut20 Mar 18, 2026
a73969b
refactor: remove unnecessary Z_buffer resizing in mlm_c function
zankrut20 Mar 18, 2026
37812b1
refactor: replace dynamic buffer allocation with std::vector in read_…
zankrut20 Mar 18, 2026
07bf996
refactor: optimize conjugate_gradient function by using noalias for p…
zankrut20 Mar 18, 2026
0c657e6
refactor: optimize parallel processing in impute_marker and hasNA fun…
zankrut20 Mar 18, 2026
c738d7c
refactor: change step parameter type from size_t to int in kin_cal fu…
zankrut20 Mar 18, 2026
4b7b154
refactor: remove unnecessary declaration of omp_setup function in mvp…
zankrut20 Mar 18, 2026
3c49f01
refactor: fix header guard definition and clean up commented code in …
zankrut20 Mar 18, 2026
0906ce1
refactor: replace assignment operators with the preferred syntax in M…
zankrut20 Mar 18, 2026
af4a1d2
refactor: update assignment syntax and clean up commented code in MVP…
zankrut20 Mar 18, 2026
0d758fd
refactor: add new EMMA delta function and improve NaN handling in MVP…
zankrut20 Mar 18, 2026
c149e92
refactor: remove commented debug print statements and optimize log-li…
zankrut20 Mar 18, 2026
7ac3432
refactor: optimize log-likelihood calculation and vectorize sigma com…
zankrut20 Mar 18, 2026
57ddb0a
refactor: streamline matrix operations in MVP.GLM function
zankrut20 Mar 18, 2026
a4b583e
refactor: declare CXX_STD = CXX17 explicitly in Makevars and Makevars…
zankrut20 May 14, 2026
1d4d2d2
refactor: unify duplicated FaSTLMM implementation into .fastlmm_core
zankrut20 May 14, 2026
0d584fa
refactor: vectorize FaSTLMM beta and LL accumulation loops in .fastlm…
zankrut20 May 14, 2026
edff97d
refactor: extract fill_geno_buffer template to eliminate 8-branch Big…
zankrut20 May 14, 2026
c0fb39b
refactor: remove gc() from hot paths and unjustified call sites
zankrut20 May 14, 2026
b332d4b
refactor: add .safe_solve() helper and replace try/solve/inherits pat…
zankrut20 May 14, 2026
76b2925
refactor: rename internal FarmCPU helpers to .farmcpu_* dot-prefix co…
zankrut20 May 14, 2026
c02bd7d
refactor: add input validation and replace magic number in MVP.BRENT.…
zankrut20 May 14, 2026
9673c0c
refactor: remove dead LogRL_dev1 and fix double-subtraction in Standa…
zankrut20 May 14, 2026
19149f0
refactor: replace raw FILE* with RAII std::ifstream/ofstream in bfile…
zankrut20 May 14, 2026
5cbafc6
perf: eliminate per-token heap allocations in VCF/HAPMAP inner-loop p…
zankrut20 May 14, 2026
24ae95f
refactor: add DISPATCH_MATRIX_TYPE macro and apply to five bigmatrix …
zankrut20 May 14, 2026
986214c
chore: remove dead type2 progress bar block and add refactor CHANGELO…
zankrut20 May 14, 2026
3f615bd
docs: add Sprint 0.1 regression tests, Sprint 1.1 constants, fix EMMA…
zankrut20 May 15, 2026
7c806d5
fix: wrap print_info example in \dontrun{} to fix R CMD check ERROR
zankrut20 May 15, 2026
f7867fc
docs: regenerate man/print_info.Rd via devtools::document()
zankrut20 May 15, 2026
0d5e52b
fix: remove @examples from print_info to fix R CMD check ERROR
zankrut20 May 15, 2026
fac897e
fix: remove stale @param and examples from print_info to clear R CMD …
zankrut20 May 15, 2026
487c911
fix: replace ##__VA_ARGS__ with __VA_ARGS__ in DISPATCH_MATRIX_TYPE m…
zankrut20 May 16, 2026
5927c67
fix: address review feedback — typo, docs, dead constant, NA note
hyacz May 16, 2026
b1bee8e
fix: address review feedback — string_view guard and .safe_solve migr…
hyacz May 16, 2026
f3db64d
test: replace .rds fixture approach with hardcoded per-SNP values
zankrut20 May 21, 2026
084eb0e
fix: wire .EMMA_* constants into MVP.EMMA.Vg.Ve body
zankrut20 May 21, 2026
de3041c
docs: list all four method options in .farmcpu_bin @param
zankrut20 May 21, 2026
668edbe
docs: fix strwrap typo in R source and regenerate affected Rd files
zankrut20 May 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,6 @@ vignettes/*.pdf
*.knit.md
.Rproj.user
packages

# R Files for comparisions
compare_perf.R
21 changes: 21 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
## refactor-branch (2026-05-14)
+ Bug fixes:
+ Fixed 2*3.14 → 2*pi in FaSTLMM log-likelihood computation (~0.0028/obs error in LL values)
+ Fixed double-subtraction of mean^2 in MVP.HE.Vg.Ve StandardizeVector (numerically benign but mathematically wrong)
+ Code quality:
+ Named constants extracted to R/MVP.Constants.R (eliminated magic numbers in FaSTLMM, EMMA, BRENT, HE, FarmCPU)
+ FaSTLMM inner-loop vectorized: scalar for-loops replaced with sweep/crossprod operations
+ Duplicate FaSTLMM implementation unified via .fastlmm_core() shared by MVP.FaSTLMM.LL and FarmCPU
+ FarmCPU internal helpers renamed to .farmcpu_* convention
+ .safe_solve() helper eliminates repeated try/inherits(solve(...), 'try-error') patterns
+ MVP.BRENT.Vg.Ve gains stopifnot() input validation
+ Dead LogRL_dev1 function removed from MVP.HE.Vg.Ve
+ gc() calls removed from hot loops (kept only after explicit rm() of large objects)
+ C++ improvements:
+ C++ standard set to CXX17 in Makevars/Makevars.win
+ ProgressBar base class extracted to eliminate ~100 lines of duplicated C++
+ fill_geno_buffer<T> template eliminates 8-branch fill blocks in assoc.cpp and kinship.cpp
+ DISPATCH_MATRIX_TYPE macro eliminates repeated switch/case dispatch boilerplate
+ FILE* replaced with RAII std::ifstream/ofstream in write_bfile and read_bfile
+ split_line_view() using std::string_view eliminates per-token heap allocations in VCF/HAPMAP parsers

## v1.1.0beta (2018/09/12)
+ Unified installation package for Windows platform and Linux platform.
+ The 3D PCA map has been temporarily disabled, and we will improve this feature in subsequent versions.
Expand Down
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Depends: R (>= 3.3)
LinkingTo: Rcpp, RcppArmadillo, RcppEigen, RcppProgress, BH, bigmemory
NeedsCompilation: yes
Suggests: knitr, testthat, rmarkdown
RoxygenNote: 7.3.2
RoxygenNote: 7.3.3
Maintainer: Xiaolei Liu <xll198708@gmail.com>
Author: Lilin Yin [aut],
Haohao Zhang [aut],
Expand Down
17 changes: 11 additions & 6 deletions R/MVP.BRENT.Vg.Ve.R
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,19 @@
#' }
#'
MVP.BRENT.Vg.Ve <- function(y, X, eigenK, verbose = FALSE) {
p = 0
stopifnot(
!anyNA(eigenK$values),
length(eigenK$values) == nrow(X),
length(y) == nrow(X)
)
p <- 0
Sigma <- eigenK$values
w <- which(Sigma < 1e-6)
Sigma[w] <- 1e-6
w <- which(Sigma < .BRENT_MIN_EIGENVALUE)
Sigma[w] <- .BRENT_MIN_EIGENVALUE
U <- eigenK$vectors
min_h2 = 0
max_h2 = 1
tol = .Machine$double.eps^0.25
min_h2 <- 0
max_h2 <- 1
tol <- .Machine$double.eps^0.25
reml <- fit_diago_brent(y, X, p, Sigma, U, min_h2, max_h2, tol, verbose = verbose)
vg <- reml[[2]]
ve <- reml[[1]]
Expand Down
25 changes: 25 additions & 0 deletions R/MVP.Constants.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Package-level constants for rMVP statistical methods.
# Naming convention: .<METHOD>_<DESCRIPTION> (dot-prefix keeps them non-exported)

# FaSTLMM log-likelihood optimization
.FASTLMM_SVD_THRESHOLD <- 1e-8 # singular value cutoff in SVD
.FASTLMM_DELTA_EXP_START <- -5 # log-delta grid lower bound
.FASTLMM_DELTA_EXP_END <- 5 # log-delta grid upper bound
.FASTLMM_DELTA_EXP_STEP <- 0.1 # log-delta grid step size
.FASTLMM_DELTA_EXP_DEGENERATE <- 100 # sentinel: collapse grid to single point when SNP pool has a constant column
.FASTLMM_2PI <- 2 * pi # NOTE: original code had 2 * 3.14 (bug); corrected to 2 * pi

# EMMA variance component estimation (defaults match original function signature)
.EMMA_NGRIDS <- 100

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These constants are defined here but MVP.EMMA.Vg.Ve.r still uses hardcoded defaults (ngrids=100, llim=-10, ulim=10, esp=1e-10). Is this intentional (deferred to a follow-up PR)? Just want to confirm.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not intentional — this was an oversight. Fixed in 14eed67.

The function signature keeps literal defaults (ngrids=100, llim=-10, ulim=10, esp=1e-10) because R CMD check's codoc requires exported function formals to be evaluable as literals; using .EMMA_NGRIDS directly in the signature would break that. A comment at the top of the body now explicitly links each default to its canonical constant in MVP.Constants.R.

Additionally, the two bare 2 * pi literals inside the inner REML log-likelihood helpers have been replaced with .FASTLMM_2PI, matching the same substitution already made in MVP.FaSTLMM.LL.r. Both methods share the same LL formula structure, so this makes them consistent.

.EMMA_LLIM <- -10
.EMMA_ULIM <- 10
.EMMA_ESP <- 1e-10

# BRENT variance component estimation
.BRENT_MIN_EIGENVALUE <- 1e-6 # eigenvalue floor to avoid division by near-zero

# HE regression
.HE_DEFAULT_LOG_SIGMA2 <- log(0.1) # log-sigma2 fallback when CalcVChe returns non-positive value

# FarmCPU
.FARMCPU_LD_THRESHOLD <- 0.7 # LD threshold for pseudo-QTN deduplication
39 changes: 7 additions & 32 deletions R/MVP.Data.r
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ MVP.Data <- function(fileMVP = NULL, fileVCF = NULL, fileHMP = NULL, fileBed = N
# phenotype
if (!is.null(filePhe)) {
MVP.Data.Pheno(
pheno_file = filePhe,
pheno_file <- filePhe,
out = out,
header = TRUE,
cols = pheno_cols,
Expand Down Expand Up @@ -438,36 +438,12 @@ MVP.Data.Numeric2MVP <- function(num_file, map_file, out='mvp', maxLine=1e4, row
)
logging.log(paste0("Loading genotype at a step of ", maxLine, '...\n'), verbose = verbose)

# convert to bigmat - speed
# if (priority == "speed") {
# opts <- options(bigmemory.typecast.warning = FALSE)
# on.exit(options(opts))

# # detecte sep
# con <- file(num_file, open = 'r')
# line <- readLines(con, 1)
# close(con)
# sep <- substr(line, 2, 2)

# # load geno
# suppressWarnings(
# geno <- read.big.matrix(num_file, header = FALSE, sep = sep)
# )
# if (transposed) {
# bigmat[, ] <- t(geno[, ])
# } else {
# bigmat[, ] <- geno[, ]
# }
# rm("geno")
# }

# convert to bigmat - memory
# if (priority == "memory") {
i <- 0
con <- file(num_file, open = 'r')
# convert to bigmat - memory priority: read in chunks to reduce memory footprint
i <- 0
con <- file(num_file, open = 'r')
if (col_names) { readLines(con, n = 1) }
while (TRUE) {
line = readLines(con, n = maxLine)
line <- readLines(con, n = maxLine)

len <- length(line)
if (len == 0) { break }
Expand All @@ -488,7 +464,6 @@ MVP.Data.Numeric2MVP <- function(num_file, map_file, out='mvp', maxLine=1e4, row
}
logging.log("\n", verbose = verbose)
close(con)
# }

file.copy(map_file, paste0(out, ".geno.map"))

Expand Down Expand Up @@ -639,10 +614,10 @@ MVP.Data.Pheno <- function(pheno_file, out='mvp', cols=NULL, header=TRUE, sep='\

# drop empty traits
pheno[pheno %in% missing] <- NA
drop = c()
drop <- c()
for (i in 2:ncol(pheno)) {
if (all(is.na(pheno[, i]))) {
drop = c(drop, i)
drop <- c(drop, i)
}
}
if (length(drop) > 0) {
Expand Down
43 changes: 18 additions & 25 deletions R/MVP.EMMA.Vg.Ve.r
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
#'
MVP.EMMA.Vg.Ve <-
function(y, X, K, ngrids=100, llim=-10, ulim=10, esp=1e-10) {
# Defaults above match .EMMA_NGRIDS / .EMMA_LLIM / .EMMA_ULIM / .EMMA_ESP
# (kept as literals in the signature for R CMD check codoc compatibility).
# NA in phenotype
idx <- !is.na(y)
y <- y[idx]
Expand All @@ -61,18 +63,21 @@ function(y, X, K, ngrids=100, llim=-10, ulim=10, esp=1e-10) {
{
nq <- length(etas)
delta <- exp(logdelta)
return( 0.5 * (nq * (log(nq/(2 * pi))-1-log(sum(etas * etas/(lambda + delta))))-sum(log(lambda + delta))) )
return( 0.5 * (nq * (log(nq/.FASTLMM_2PI)-1-log(sum(etas * etas/(lambda + delta))))-sum(log(lambda + delta))) )
}
emma.delta.REML.dLL.wo.Z <- function(logdelta, lambda, etas) {
nq <- length(etas)
delta <- exp(logdelta)
etasq <- etas * etas
ldelta <- lambda + delta
return( 0.5 * (nq * sum(etasq/(ldelta * ldelta))/sum(etasq/ldelta)-sum(1/ldelta)) )
}
emma.eigen.R.wo.Z=function(K, X) {
n <- nrow(X)
q <- ncol(X)

XX <- crossprod(X)
iXX <- try(solve(XX), silent = TRUE)
if(inherits(iXX, "try-error")){
#library(MASS)
iXX <- ginv(XX)
}
iXX <- .safe_solve(XX)

SS1 <- X %*% iXX
SS2 <- tcrossprod(SS1, X)
Expand All @@ -98,7 +103,7 @@ function(y, X, K, ngrids=100, llim=-10, ulim=10, esp=1e-10) {
delta <- exp(logdelta)
Lambdas <- matrix(eig.R$values, n-q, m) + matrix(delta, n-q, m, byrow=TRUE)
Etasq <- matrix(etas * etas, n-q, m)
LL <- 0.5 * ((n-q) * (log((n-q)/(2 * pi))-1-log(colSums(Etasq/Lambdas)))-colSums(log(Lambdas)))
LL <- 0.5 * ((n-q) * (log((n-q)/.FASTLMM_2PI)-1-log(colSums(Etasq/Lambdas)))-colSums(log(Lambdas)))
dLL <- 0.5 * delta * ((n-q) * colSums(Etasq/(Lambdas * Lambdas))/colSums(Etasq/Lambdas)-colSums(1/Lambdas))
optlogdelta <- vector(length=0)
optLL <- vector(length=0)
Expand All @@ -112,30 +117,18 @@ function(y, X, K, ngrids=100, llim=-10, ulim=10, esp=1e-10) {
}
for(i in 1:(m-1) ){
if( ( dLL[i] * dLL[i + 1] < 0 ) && ( dLL[i] > 0 ) && ( dLL[i + 1] < 0 ) ) {
emma.delta.REML.dLL.wo.Z <- function(logdelta, lambda, etas) {
nq <- length(etas)
delta <- exp(logdelta)
etasq <- etas * etas
ldelta <- lambda + delta
return( 0.5 * (nq * sum(etasq/(ldelta * ldelta))/sum(etasq/ldelta)-sum(1/ldelta)) )
}
r <- uniroot(emma.delta.REML.dLL.wo.Z, lower=logdelta[i], upper=logdelta[i + 1], lambda=eig.R$values, etas=etas)
optlogdelta <- append(optlogdelta, r$root)
emma.delta.REML.LL.wo.Z <- function(logdelta, lambda, etas) {
nq <- length(etas)
delta <- exp(logdelta)
return( 0.5 * (nq * (log(nq/(2 * pi))-1-log(sum(etas * etas/(lambda + delta))))-sum(log(lambda + delta))) )
}
optLL <- append(optLL, emma.delta.REML.LL.wo.Z(r$root, eig.R$values, etas))
}
}
maxdelta <- exp(optlogdelta[which.max(optLL)])
#handler of grids with NaN log
replaceNaN<-function(LL) {
index=(LL == "NaN")
if(length(index)>0) theMin=min(LL[!index])
if(length(index)<1) theMin="NaN"
LL[index]=theMin
# Handle grids with NaN log values
replaceNaN <- function(LL) {
index <- (LL == "NaN")
if(length(index)>0) theMin <- min(LL[!index])
if(length(index)<1) theMin <- "NaN"
LL[index] <- theMin
return(LL)
}
optLL=replaceNaN(optLL)
Expand Down
Loading
Loading