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
db6c581
Renamed functions and parameters, documentation fixes
ericbair-sciome Jan 3, 2025
ff0e307
Pull request #67: Renamed functions and parameters, documentation fixes
ericbair-sciome Jan 6, 2025
291bb10
Merge branch 'main-sciome'
sciome-bot Jan 7, 2025
98ce34b
Merge branch 'main'
sciome-bot Jan 7, 2025
c405919
Adaptive lasso
ericbair-sciome Jan 7, 2025
8ddb266
Pull request #69: Adaptive lasso
ericbair-sciome Jan 9, 2025
6ee742f
Relaxed lasso
ericbair-sciome Jan 14, 2025
4b5f559
Pull request #71: Relaxed lasso
ericbair-sciome Jan 27, 2025
1cab9af
Faster imputation
ericbair-sciome Jan 29, 2025
d5c2427
Faster imputation
ericbair-sciome Jan 29, 2025
5ecff19
Pull request #72: Faster imputation
ericbair-sciome Jan 29, 2025
7332bc0
Merge remote-tracking branch 'sciome-git/main'
sciome-bot Jan 29, 2025
ee47067
Merge branch 'main-sciome'
sciome-bot Jan 29, 2025
9f7d678
New penalties, bug fixes
ericbair-sciome Mar 7, 2025
f431661
Fixed some lint issues
ericbair-sciome Mar 11, 2025
af4c269
Pull request #74: New penalties, bug fixes
ericbair-sciome Mar 12, 2025
70d678a
Merge branch 'main-sciome'
sciome-bot Mar 12, 2025
9491a4b
Merge branch 'main-sciome'
sciome-bot Mar 12, 2025
d836bc8
Lower LOD, imputation parameters
ericbair-sciome Apr 24, 2025
36deb68
Fixed a bug in prestogp_predict
ericbair-sciome Apr 28, 2025
c83f160
Pull request #76: Lower LOD, imputation parameters
ericbair-sciome Apr 28, 2025
245d08f
Fixed a bug for lod.upper/lod.lower
ericbair-sciome May 3, 2025
83fd5ee
Pull request #78: Fixed a bug for lod.upper/lod.lower
ericbair-sciome May 6, 2025
f494045
Merge branch 'main' of sciome-bot-git:NIEHS/PrestoGP
sciome-bot May 6, 2025
17fd6b8
Merge branch 'main-sciome' of sciome-bot-git:NIEHS/PrestoGP
sciome-bot May 6, 2025
2438326
Fixed another bug caused by numerically singular U matrices
ericbair-sciome May 13, 2025
4a9ebfe
Merge branch 'master' of http://192.168.167.103:7990/bitbucket/scm/st…
ericbair-sciome May 13, 2025
40f83ef
Pull request #81: Fixed another bug caused by numerically singular U …
ericbair-sciome May 14, 2025
11e92ea
Merge branch 'main-sciome' of sciome-bot-git:NIEHS/PrestoGP
sciome-bot May 14, 2025
6484043
Imputation bug fixes
ericbair-sciome Nov 13, 2025
dd33a47
Imputation bug fixes
ericbair-sciome Nov 13, 2025
5de4c36
Imputation bug fixes
ericbair-sciome Nov 13, 2025
c190541
Pull request #83: Eb dev
ericbair-sciome Nov 24, 2025
d186837
Added omp.cores parameter to prestogp_fit
ericbair-sciome Jan 13, 2026
0eb1dc1
Fixed issues caused by numerically singular V matrices
ericbair-sciome Jan 20, 2026
848f796
Modifed the NEWS.md file
ericbair-sciome Jan 20, 2026
d4d98c9
Fixed a bug where then length check on beta.hat was invalid
ericbair-sciome May 26, 2026
93c3ca6
Fix prestogp_fit stability and beta.hat validation
ericbair-sciome Jun 2, 2026
fd45394
Merge branch 'main-niehs'
sciome-bot Jun 2, 2026
be7b22d
Pull request #88: Fix prestogp_fit stability and beta.hat validation
sciome-bot Jun 2, 2026
fda72c8
Pull request #88: Fix prestogp_fit stability and beta.hat validation
sciome-bot Jun 2, 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
5 changes: 3 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: PrestoGP
Type: Package
Title: Penalized Regression for Spatio-Temporal Outcomes via Gaussian Processes
Version: 0.2.0.9050
Version: 0.2.0.9053
Authors@R: c(
person(given = "Eric",
family = "Bair",
Expand All @@ -21,7 +21,7 @@ Authors@R: c(
role = "aut"))
Description: Simultaneous variable seletion and estimation of LUR models with spatiotemporally correlated errors that is scalable for big data.
Depends:
R (>= 3.5.0)
R (>= 4.1.0)
LinkingTo:
Rcpp, RcppArmadillo, BH
Imports:
Expand Down Expand Up @@ -67,3 +67,4 @@ Suggests:
testthat (>= 3.0.0)
Config/testthat/edition: 3
URL: https://niehs.github.io/PrestoGP/, https://github.com/NIEHS/PrestoGP
Config/roxygen2/version: 8.0.0
25 changes: 25 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
# PrestoGP 0.2.0.9053 (2026-5-26)

## BUG FIXES

* Fixed a bug where specifying the beta.hat parameter to `prestogp_fit`
caused an error due to a faulty length check.

# PrestoGP 0.2.0.9052 (2026-1-20)

## BUG FIXES

* Created local versions of several GPvecchia utility functions (e.g.,
`vecchia_likelihood`, `vecchia_prediction`) that are robust to numerically
singular U/V matrices.

* Modified `vecchia_prediction` and `vecchia_Mprediction` to avoid crashes
when the estimated V matrix is numerically singular.

# PrestoGP 0.2.0.9051 (2026-1-14)

## NEW FEATURES

* Added a parameter `omp.cores` to `prestogp_fit` to specify the number of
cores used by OMP when computing the U matrix.

# PrestoGP 0.2.0.9050 (2025-11-13)

## BUG FIXES
Expand Down
38 changes: 38 additions & 0 deletions R/Log_Likelihood.R

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should this be negative or positive infinite?

Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,20 @@ negloglik.full <- function(logparams, d, y, param.seq) {
-1 * mvtnorm::dmvnorm(y, rep(0, N), cov.mat, log = TRUE)
}

vecchia_likelihood <- function(z, vecchia.approx, covparms, nuggets,
covmodel = "matern") {
if (vecchia.approx$cond.yz == "zy")
warning("cond.yz='zy' will produce a poor likelihood approximation. Use 'SGV' instead.")
removeNAs <- getFromNamespace("removeNAs", "GPvecchia")
removeNAs()
U.obj <- createU(vecchia.approx, covparms, nuggets, covmodel)
res <- try(junk <- vecchia_likelihood_U(z, U.obj), silent = TRUE)
if (inherits(res, "try-error")) {
junk <- -1 * Inf
}
junk
}

#' Evaluation of the multivariate Vecchia likelihood
#'
#' This function is used to evaluate the multivariate Vecchia likelihood.
Expand Down Expand Up @@ -118,6 +132,30 @@ vecchia_Mlikelihood <- function(z, vecchia.approx, covparams) {
}


vecchia_likelihood_U <- function(z, U.obj) {
U <- U.obj$U
latent <- U.obj$latent
zord <- z[U.obj$ord.z]
const <- sum(!latent) * log(2 * pi)
z1 <- Matrix::crossprod(U[!latent, ], zord)
quadform.num <- sum(z1^2)
logdet.num <- -2 * sum(log(Matrix::diag(U)))
if (sum(latent) == 0) {
logdet.denom <- quadform.denom <- 0
} else {
U.y <- U[latent, ]
z2 <- as.numeric(U.y %*% z1)
V.ord <- U2V(U.obj)
z3 <- Matrix::solve(V.ord, rev(z2), system = "L")
quadform.denom <- sum(z3^2)
logdet.denom <- -2 * sum(log(Matrix::diag(V.ord)))
}
neg2loglik <- logdet.num - logdet.denom + quadform.num - quadform.denom +
const
loglik <- -neg2loglik / 2
loglik
}

##############################################################################
### Flexible Multivariate Matern Negative Loglikelihood Function ###########

Expand Down
7 changes: 4 additions & 3 deletions R/PrestoGP_CreateU_Multivariate.R
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ create_param_sequence <- function(P, ns = 1) {
}

param.sequence.begin <- c(1, P + 1, seq(P * (ns + 1) + 1, length = 3, by = P))
param.sequence.end <- c(P, ns * P, P, P, nk) %>% cumsum()
param.sequence.end <- c(P, ns * P, P, P, nk) |> cumsum()
param.sequence <- cbind(param.sequence.begin, param.sequence.end)

param.sequence
Expand Down Expand Up @@ -470,7 +470,7 @@ vecchia_Mspecify <- function(locs.list, m, locs.list.pred = NULL,
ord.pred = ordering.pred, cond.yz = "SGV", conditioning = "NN",
P = P, ondx = ondx, dist.func = dist.func,
dist.func.code = dist.func.code, q.list = q.list,
n.neighbors = m
n.neighbors = m, n.cores = -1
)
}

Expand Down Expand Up @@ -609,7 +609,8 @@ createUMultivariate <- function(vec.approx, params, cov_func = NULL) {
cur.qys <- do.call(cbind, q.list$q.y)
cur.qzs <- do.call(cbind, q.list$q.z)
# browser()
U <- createU_helper_mat(olocs, ondx, cur.qys, cur.qzs, vijs, aijs, full_const, nugget, sig2, uvec)
U <- createU_helper_mat(olocs, ondx, cur.qys, cur.qzs, vijs, aijs,
full_const, nugget, sig2, uvec, vec.approx$n.cores)
# U <- sparseMatrix(i=U1[1,], j=U1[2,], x = U1[3,], triangular = TRUE)
}

Expand Down
28 changes: 23 additions & 5 deletions R/PrestoGP_Model.R
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
#' @slot nscale The number of scale parameters in the model.
#' @slot common_scale Do all columns of locs have the same scale parameter?
#' @slot param_sequence Records the indices of the various Matern parameters.
#' @slot omp_cores Number of cores used by OMP to compute the U matrix.
#' @slot logparams Transformed version of the Matern parameters.
#' See \code{\link{create_param_sequence}}.
#'
#' @seealso \code{\link{VecchiaModel-class}}, \code{\link{FullModel-class}},
Expand Down Expand Up @@ -97,6 +99,7 @@ PrestoGPModel <- setClass("PrestoGPModel",
nscale = "numeric", # the number of scale parameters
common_scale = "logical", # is there a common scale parameter?
param_sequence = "matrix", # maps the indices of the various Matern parameters
omp_cores = "numeric", # Number of cores used by OMP to compute the U matrix
logparams = "numeric" # transformed version of the Matern parameters
)
)
Expand Down Expand Up @@ -171,7 +174,7 @@ setGeneric(
optim.control = list(trace = 0, reltol = 1e-3, maxit = 5000),
penalty = c("lasso", "relaxed", "MCP", "SCAD"), alpha = 1,
family = c("gaussian", "binomial"), nfolds = 10, foldid = NULL,
parallel = FALSE, cluster = NULL, adaptive = FALSE) {
omp.cores = -1, parallel = FALSE, cluster = NULL, adaptive = FALSE) {
standardGeneric("prestogp_fit")
}
)
Expand Down Expand Up @@ -827,13 +830,19 @@ setMethod(
#' what fold each observation should be assigned to in the cv.glmnet
#' cross-validation. See \code{\link[glmnet]{cv.glmnet}} and
#' \code{\link[ncvreg]{cv.ncvreg}}.
#' @param omp.cores Number of parallel cores to be used by OMP when computing
#' the U matrix in the Vecchia approximation. Defaults to -1 (which causes
#' all available cores to be used).
#' @param parallel Should parallel "foreach" be used to speed up the model
#' fitting procedure where possible? Defaults to FALSE. Specifically,
#' parallelization will be used for imputation and fitting the cv.glmnet
#' object. See \code{\link[glmnet]{cv.glmnet}}. Note that this
#' only applies to glmnet models (where penalty="lasso" or
#' penalty="relaxed"). Models using ncvreg (where penalty="MCP" or
#' penalty="SCAD") require a cluster argument for parallelization (see below).
#' Note that parallelization will be used to compute the Vecchia
#' approximation even if this parameter is set to FALSE (assuming that OMP is
#' available on the system). To disable this, set omp.cores=1.
#' @param cluster A cluster for running cv.ncvreg in parallel. See
#' \code{\link[ncvreg]{cv.ncvreg}} and \code{\link[parallel]{makeCluster}}.
#' This must be specified to run cv.ncvreg in parallel. It is ignored for
Expand Down Expand Up @@ -947,9 +956,19 @@ setMethod(
quiet = FALSE, verbose = FALSE, optim.method = "Nelder-Mead",
optim.control = list(trace = 0, reltol = 1e-3, maxit = 5000),
penalty = c("lasso", "relaxed", "MCP", "SCAD"), alpha = 1,
family = c("gaussian", "binomial"),
nfolds = 10, foldid = NULL, parallel = FALSE, cluster = NULL,
family = c("gaussian", "binomial"), nfolds = 10, foldid = NULL,
omp.cores = -1, parallel = FALSE, cluster = NULL,
adaptive = FALSE) {
if (!is.numeric(omp.cores)) {
stop("omp.cores must be a positive integer >= 1")
}
if (length(omp.cores) != 1) {
stop("omp.cores must be a positive integer >= 1")
}
if (omp.cores < -1 | omp.cores == 0 | omp.cores %% 1 != 0) {
stop("omp.cores must be a positive integer >= 1")
}
model@omp_cores <- omp.cores
penalty <- match.arg(penalty)
model@penalty <- penalty
family <- match.arg(family)
Expand Down Expand Up @@ -994,8 +1013,7 @@ setMethod(
if (!is.vector(beta.hat) | !is.numeric(beta.hat)) {
stop("beta.hat parameter must be a numeric vector")
}
if (length(beta.hat) != (ncol(model@X_train) +
length(model@locs_train))) {
if (length(beta.hat) != (ncol(model@X_train) + 1)) {
stop("Length of beta.hat must match the number of predictors")
}
beta.hat <- as.matrix(beta.hat)
Expand Down
1 change: 1 addition & 0 deletions R/PrestoGP_Multivariate_Vecchia.R
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,7 @@ setMethod("specify", "MultivariateVecchiaModel", function(model) {
locs <- model@locs_train
locs.scaled <- scale_locs(model, locs)
model@vecchia_approx <- vecchia_Mspecify(locs.scaled, model@n_neighbors)
model@vecchia_approx$n.cores <- model@omp_cores
if (!model@common_scale) {
olocs.scaled <- model@vecchia_approx$locsord
for (i in seq_along(locs)) {
Expand Down
50 changes: 49 additions & 1 deletion R/PrestoGP_Util_Functions.R
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,44 @@ revMat <- function(mat) {
mat.out
}

vecchia_prediction <- function(z, vecchia.approx, covparms, nuggets,
var.exact, covmodel = "matern", return.values = "all") {
removeNAs <- getFromNamespace("removeNAs", "GPvecchia")
removeNAs()
U.obj <- createU(vecchia.approx, covparms, nuggets, covmodel)
V.ord <- U2V(U.obj)
if (length(U.obj$zero.nugg) > 0)
warning("Rows/cols of V have been removed for data with zero noise")
vecchia_mean <- getFromNamespace("vecchia_mean", "GPvecchia")
V.singular <- FALSE
res <- try(vecchia.mean <- vecchia_mean(z, U.obj, V.ord), silent = TRUE)
if (inherits(res, "try-error")) {
warning("V is numerically singular. Predicted means are unreliable.")
V.ord.pd <- Matrix::nearPD(V.ord)$mat
V.singular <- TRUE
vecchia.mean <- vecchia_mean(z, U.obj, V.ord.pd)
}
return.list <- list(mu.pred = vecchia.mean$mu.pred,
mu.obs = vecchia.mean$mu.obs, var.pred = NULL, var.obs = NULL,
V.ord = NULL, U.obj = NULL)
if (return.values == "meanmat" || return.values == "all") {
return.list$V.ord <- V.ord
return.list$U.obj <- U.obj
}
if (return.values == "meanvar" || return.values == "all") {
if (V.singular) {
stop("V is numerically singular. Prediction variance cannot be computed.")
}
if (missing(var.exact))
var.exact <- (sum(!vecchia.approx$obs) < 4 * 10000)
vecchia_var <- getFromNamespace("vecchia_var", "GPvecchia")
vars.vecchia <- vecchia_var(U.obj, V.ord, exact = var.exact)
return.list$var.pred <- vars.vecchia$vars.pred
return.list$var.obs <- vars.vecchia$vars.obs
}
return.list
}

#' Multivariate Vecchia prediction
#'
#' This function is used to make predictions based on multivariate Vecchia
Expand Down Expand Up @@ -296,7 +334,14 @@ vecchia_Mprediction <- function(z, vecchia.approx, covparms, var.exact = NULL, r
# if (length(U.obj$zero.nugg) > 0)
# warning("Rows/cols of V have been removed for data with zero noise")
vecchia_mean <- getFromNamespace("vecchia_mean", "GPvecchia")
vecchia.mean <- vecchia_mean(z, U.obj, V.ord)
V.singular <- FALSE
res <- try(vecchia.mean <- vecchia_mean(z, U.obj, V.ord), silent = TRUE)
if (inherits(res, "try-error")) {
warning("V is numerically singular. Predicted means are unreliable.")
V.ord.pd <- Matrix::nearPD(V.ord)$mat
V.singular <- TRUE
vecchia.mean <- vecchia_mean(z, U.obj, V.ord.pd)
}
return.list <- list(
mu.pred = vecchia.mean$mu.pred, mu.obs = vecchia.mean$mu.obs,
var.pred = NULL, var.obs = NULL, V.ord = NULL, U.obj = NULL
Expand All @@ -306,6 +351,9 @@ vecchia_Mprediction <- function(z, vecchia.approx, covparms, var.exact = NULL, r
return.list$U.obj <- U.obj
}
if (return.values == "meanvar" || return.values == "all") {
if (V.singular) {
stop("V is numerically singular. Prediction variance cannot be computed.")
}
if (is.null(var.exact)) {
var.exact <- (sum(!vecchia.approx$obs) < 4 * 10000)
}
Expand Down
3 changes: 3 additions & 0 deletions R/PrestoGP_Vecchia.R
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,9 @@ setMethod("impute_y_lod", "VecchiaModel", function(model, lodu, lodl, n.mi = 10,
setMethod("specify", "VecchiaModel", function(model) {
locs.scaled <- scale_locs(model, model@locs_train)
model@vecchia_approx <- vecchia_specify(locs.scaled[[1]], model@n_neighbors)
if (model@omp_cores > -1) {
model@vecchia_approx$U.prep$n.cores <- model@omp_cores
}
if (!model@common_scale) {
olocs.scaled <- model@vecchia_approx$locsord
for (j in 1:model@nscale) {
Expand Down
4 changes: 2 additions & 2 deletions R/RcppExports.R
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ na_omit_c <- function(x) {
.Call('_PrestoGP_na_omit_c', PACKAGE = 'PrestoGP', x)
}

createU_helper_mat <- function(olocs, ondx, curqys, curqzs, vijs, aijs, full_const, nugget, sig2, U_beginning) {
.Call('_PrestoGP_createU_helper_mat', PACKAGE = 'PrestoGP', olocs, ondx, curqys, curqzs, vijs, aijs, full_const, nugget, sig2, U_beginning)
createU_helper_mat <- function(olocs, ondx, curqys, curqzs, vijs, aijs, full_const, nugget, sig2, U_beginning, n_cores) {
.Call('_PrestoGP_createU_helper_mat', PACKAGE = 'PrestoGP', olocs, ondx, curqys, curqzs, vijs, aijs, full_const, nugget, sig2, U_beginning, n_cores)
}

1 change: 1 addition & 0 deletions man/PrestoGP-package.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion man/PrestoGPModel-class.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion man/prestogp_fit-PrestoGPModel-method.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions src/RcppExports.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ BEGIN_RCPP
END_RCPP
}
// createU_helper_mat
arma::sp_mat createU_helper_mat(const arma::mat& olocs, const arma::vec& ondx, const arma::mat& curqys, const arma::mat& curqzs, const arma::mat& vijs, const arma::mat& aijs, const arma::mat& full_const, const arma::vec& nugget, const arma::vec& sig2, const arma::vec& U_beginning);
RcppExport SEXP _PrestoGP_createU_helper_mat(SEXP olocsSEXP, SEXP ondxSEXP, SEXP curqysSEXP, SEXP curqzsSEXP, SEXP vijsSEXP, SEXP aijsSEXP, SEXP full_constSEXP, SEXP nuggetSEXP, SEXP sig2SEXP, SEXP U_beginningSEXP) {
arma::sp_mat createU_helper_mat(const arma::mat& olocs, const arma::vec& ondx, const arma::mat& curqys, const arma::mat& curqzs, const arma::mat& vijs, const arma::mat& aijs, const arma::mat& full_const, const arma::vec& nugget, const arma::vec& sig2, const arma::vec& U_beginning, const int n_cores);
RcppExport SEXP _PrestoGP_createU_helper_mat(SEXP olocsSEXP, SEXP ondxSEXP, SEXP curqysSEXP, SEXP curqzsSEXP, SEXP vijsSEXP, SEXP aijsSEXP, SEXP full_constSEXP, SEXP nuggetSEXP, SEXP sig2SEXP, SEXP U_beginningSEXP, SEXP n_coresSEXP) {
BEGIN_RCPP
Rcpp::RObject rcpp_result_gen;
Rcpp::RNGScope rcpp_rngScope_gen;
Expand All @@ -38,14 +38,15 @@ BEGIN_RCPP
Rcpp::traits::input_parameter< const arma::vec& >::type nugget(nuggetSEXP);
Rcpp::traits::input_parameter< const arma::vec& >::type sig2(sig2SEXP);
Rcpp::traits::input_parameter< const arma::vec& >::type U_beginning(U_beginningSEXP);
rcpp_result_gen = Rcpp::wrap(createU_helper_mat(olocs, ondx, curqys, curqzs, vijs, aijs, full_const, nugget, sig2, U_beginning));
Rcpp::traits::input_parameter< const int >::type n_cores(n_coresSEXP);
rcpp_result_gen = Rcpp::wrap(createU_helper_mat(olocs, ondx, curqys, curqzs, vijs, aijs, full_const, nugget, sig2, U_beginning, n_cores));
return rcpp_result_gen;
END_RCPP
}

static const R_CallMethodDef CallEntries[] = {
{"_PrestoGP_na_omit_c", (DL_FUNC) &_PrestoGP_na_omit_c, 1},
{"_PrestoGP_createU_helper_mat", (DL_FUNC) &_PrestoGP_createU_helper_mat, 10},
{"_PrestoGP_createU_helper_mat", (DL_FUNC) &_PrestoGP_createU_helper_mat, 11},
{NULL, NULL, 0}
};

Expand Down
Loading
Loading