From ac0155d4cb9a7d610548278396a0e21a677493c4 Mon Sep 17 00:00:00 2001 From: Yining97 Date: Tue, 28 Jul 2026 16:38:21 -0400 Subject: [PATCH 1/3] susieR 0.16.6 compat: drop defunct R_mismatch_method/check_prior forwarding These moved into susie_rss_control() in susieR 0.16.6; .fmFitSusieRss no longer forwards them (control defaults apply). R_mismatch still passes through, incl. the new eb_mix. Pin susieR (>= 0.16.6). Behavior-preserving for default/eb runs. --- DESCRIPTION | 2 +- R/fineMappingPipeline.R | 21 ++++++--------------- R/fineMappingWrappers.R | 18 ++++++------------ man/fineMappingPipeline.Rd | 17 ++++------------- tests/testthat/test_fineMappingPipeline.R | 20 ++++++++++++++------ 5 files changed, 31 insertions(+), 47 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 86506d96..906109c3 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -35,7 +35,7 @@ Imports: readr, rlang, stringr, - susieR (>= 0.16.2), + susieR (>= 0.16.6), tibble, tictoc, tidyr, diff --git a/R/fineMappingPipeline.R b/R/fineMappingPipeline.R index 50592e0b..6db18c8b 100644 --- a/R/fineMappingPipeline.R +++ b/R/fineMappingPipeline.R @@ -282,13 +282,10 @@ #' case it defaults to the LD-panel sample size \code{getNSamples(ldSketch)}. #' @param rMismatch \code{QtlSumStats} / \code{GwasSumStats} only. LD-mismatch #' mode forwarded to \code{susieR::susie_rss()} as \code{R_mismatch} (e.g. -#' \code{"eb"} for empirical Bayes). Default \code{"none"} (susieR's default). -#' @param rMismatchMethod \code{QtlSumStats} / \code{GwasSumStats} only. Optional -#' \code{R_mismatch_method} forwarded to \code{susieR::susie_rss()} when -#' non-\code{NULL}. -#' @param checkPrior \code{QtlSumStats} / \code{GwasSumStats} only. Optional -#' \code{check_prior} forwarded to \code{susieR::susie_rss()} when -#' non-\code{NULL}. +#' \code{"eb"} for empirical Bayes, \code{"eb_mix"} for residual-mixture EB in +#' susieR >= 0.16.6). Default \code{"none"} (susieR's default). (\code{R_mismatch_method} +#' and \code{check_prior} are susie_rss_control() settings in susieR >= 0.16.6 and +#' are left at their defaults.) #' @param keepFullFit \code{QtlSumStats} / \code{GwasSumStats} only. Controls #' retention of the pre-fallback multi-effect SuSiE-RSS fit when #' \code{serFallback=TRUE}: \code{"fallback"} (default) keeps it only for @@ -1751,8 +1748,6 @@ setMethod("fineMappingPipeline", "QtlSumStats", serFallback = FALSE, rFinite = NULL, rMismatch = "none", - rMismatchMethod = NULL, - checkPrior = NULL, keepFullFit = "fallback", ...) { .fmAssertQcd(data) @@ -1872,8 +1867,7 @@ setMethod("fineMappingPipeline", "QtlSumStats", af = afByVar, fullFit = fullFit, fullFitAlphaOnly = fullFitAlphaOnly, includeAllCs = includeAllCs, serFallback = serFallback, rFinite = rFiniteResolved, - rMismatch = rMismatch, rMismatchMethod = rMismatchMethod, - checkPrior = checkPrior, keepFullFit = keepFullFit) + rMismatch = rMismatch, keepFullFit = keepFullFit) # The method column carries the bare token, independent of the # postprocess class. for (tk in names(ents)) pushRow(st, ctx, tr, tk, ents[[tk]]) @@ -1953,8 +1947,6 @@ setMethod("fineMappingPipeline", "GwasSumStats", serFallback = FALSE, rFinite = NULL, rMismatch = "none", - rMismatchMethod = NULL, - checkPrior = NULL, keepFullFit = "fallback", ...) { .fmAssertQcd(data) @@ -2038,8 +2030,7 @@ setMethod("fineMappingPipeline", "GwasSumStats", af = afByVar, fullFit = fullFit, fullFitAlphaOnly = fullFitAlphaOnly, includeAllCs = includeAllCs, serFallback = serFallback, rFinite = rFiniteResolved, - rMismatch = rMismatch, rMismatchMethod = rMismatchMethod, - checkPrior = checkPrior, keepFullFit = keepFullFit) + rMismatch = rMismatch, keepFullFit = keepFullFit) for (tk in names(ents)) pushRow(st, tk, region_id, ents[[tk]]) } diff --git a/R/fineMappingWrappers.R b/R/fineMappingWrappers.R index c66772c9..ba24e5b9 100644 --- a/R/fineMappingWrappers.R +++ b/R/fineMappingWrappers.R @@ -1991,8 +1991,7 @@ mergeSusieCs <- function(fineMappingResult, coverage = 0.95) { # @noRd .fmFitSusieRss <- function(z, R, n, token, chainFromInf = NULL, coverage = 0.95, userArgs = NULL, - rFinite = NULL, rMismatch = "none", - rMismatchMethod = NULL, checkPrior = NULL) { + rFinite = NULL, rMismatch = "none") { info <- .fineMappingMethodCapabilities[[token]] if (is.null(info) || identical(info$unmappableEffects, NA_character_)) { stop(".fmFitSusieRss: token '", token, "' is not a SuSiE-family method.") @@ -2006,8 +2005,8 @@ mergeSusieCs <- function(fineMappingResult, coverage = 0.95) { # methodArgs (folded in after) still override them. baseArgs$R_finite <- rFinite baseArgs$R_mismatch <- rMismatch - if (!is.null(rMismatchMethod)) baseArgs$R_mismatch_method <- rMismatchMethod - if (!is.null(checkPrior)) baseArgs$check_prior <- checkPrior + # R_mismatch_method / check_prior are susie_rss_control() settings in susieR + # >= 0.16.6 (not top-level susie_rss args); left at their control defaults. if (!is.null(chainFromInf) && token != "susieInf") { # SuSiE-RSS(-ash) initialised from a SuSiE-inf fit; userArgs folded into the # arg prep so L_greedy is clamped rather than passed through raw. @@ -2115,8 +2114,7 @@ mergeSusieCs <- function(fineMappingResult, coverage = 0.95) { fullFit = FALSE, fullFitAlphaOnly = TRUE, includeAllCs = FALSE, serFallback = FALSE, rFinite = NULL, - rMismatch = "none", rMismatchMethod = NULL, - checkPrior = NULL, keepFullFit = "fallback") { + rMismatch = "none", keepFullFit = "fallback") { chainLocal <- .fmResolveSusieChain(toRun, addSusieInf) infFit <- NULL if (chainLocal$runInf) { @@ -2124,9 +2122,7 @@ mergeSusieCs <- function(fineMappingResult, coverage = 0.95) { message(sprintf("Fitting susieInf (RSS) for %s ...", label)) infFit <- .fmFitSusieRss(z, R, n, "susieInf", coverage = coverage, userArgs = methodArgs[["susieInf"]], - rFinite = rFinite, rMismatch = rMismatch, - rMismatchMethod = rMismatchMethod, - checkPrior = checkPrior) + rFinite = rFinite, rMismatch = rMismatch) } out <- list() for (tk in toRun) { @@ -2155,9 +2151,7 @@ mergeSusieCs <- function(fineMappingResult, coverage = 0.95) { message(sprintf("Fitting %s (RSS) for %s ...", tk, label)) fit <- .fmFitSusieRss(z, R, n, tk, chainFromInf = chainFrom, coverage = coverage, userArgs = methodArgs[[tk]], - rFinite = rFinite, rMismatch = rMismatch, - rMismatchMethod = rMismatchMethod, - checkPrior = checkPrior) + rFinite = rFinite, rMismatch = rMismatch) rfd <- fit$R_finite_diagnostics flag <- if (!is.null(rfd) && !is.null(rfd$R_reliability_flag)) isTRUE(rfd$R_reliability_flag) else NA diff --git a/man/fineMappingPipeline.Rd b/man/fineMappingPipeline.Rd index 4508561a..0ec5a2ec 100644 --- a/man/fineMappingPipeline.Rd +++ b/man/fineMappingPipeline.Rd @@ -117,8 +117,6 @@ fineMappingPipeline(data, ...) serFallback = FALSE, rFinite = NULL, rMismatch = "none", - rMismatchMethod = NULL, - checkPrior = NULL, keepFullFit = "fallback", ... ) @@ -143,8 +141,6 @@ fineMappingPipeline(data, ...) serFallback = FALSE, rFinite = NULL, rMismatch = "none", - rMismatchMethod = NULL, - checkPrior = NULL, keepFullFit = "fallback", ... ) @@ -356,15 +352,10 @@ case it defaults to the LD-panel sample size \code{getNSamples(ldSketch)}.} \item{rMismatch}{\code{QtlSumStats} / \code{GwasSumStats} only. LD-mismatch mode forwarded to \code{susieR::susie_rss()} as \code{R_mismatch} (e.g. -\code{"eb"} for empirical Bayes). Default \code{"none"} (susieR's default).} - -\item{rMismatchMethod}{\code{QtlSumStats} / \code{GwasSumStats} only. Optional -\code{R_mismatch_method} forwarded to \code{susieR::susie_rss()} when -non-\code{NULL}.} - -\item{checkPrior}{\code{QtlSumStats} / \code{GwasSumStats} only. Optional -\code{check_prior} forwarded to \code{susieR::susie_rss()} when -non-\code{NULL}.} +\code{"eb"} for empirical Bayes, \code{"eb_mix"} for residual-mixture EB in +susieR >= 0.16.6). Default \code{"none"} (susieR's default). (\code{R_mismatch_method} +and \code{check_prior} are susie_rss_control() settings in susieR >= 0.16.6 and are +left at their defaults.)} \item{keepFullFit}{\code{QtlSumStats} / \code{GwasSumStats} only. Controls retention of the pre-fallback multi-effect SuSiE-RSS fit when diff --git a/tests/testthat/test_fineMappingPipeline.R b/tests/testthat/test_fineMappingPipeline.R index ea011b3c..1593448a 100644 --- a/tests/testthat/test_fineMappingPipeline.R +++ b/tests/testthat/test_fineMappingPipeline.R @@ -130,8 +130,7 @@ context("fineMappingPipeline") .fmp_mockFitRss <- function() { function(z, R, n, token, chainFromInf = NULL, coverage = 0.95, - userArgs = NULL, rFinite = NULL, rMismatch = "none", - rMismatchMethod = NULL, checkPrior = NULL) { + userArgs = NULL, rFinite = NULL, rMismatch = "none") { list(token = token, n_variants = length(z)) } } @@ -142,13 +141,10 @@ context("fineMappingPipeline") # args the pipeline forwarded so tests can assert on them. .fmp_mockFitRssDiag <- function(flag = FALSE, capture = NULL) { function(z, R, n, token, chainFromInf = NULL, coverage = 0.95, - userArgs = NULL, rFinite = NULL, rMismatch = "none", - rMismatchMethod = NULL, checkPrior = NULL) { + userArgs = NULL, rFinite = NULL, rMismatch = "none") { if (!is.null(capture)) { capture$rFinite <- rFinite capture$rMismatch <- rMismatch - capture$rMismatchMethod <- rMismatchMethod - capture$checkPrior <- checkPrior } fit <- list(token = token, n_variants = length(z), multiTag = "multi") fit$R_finite_diagnostics <- list( @@ -1602,6 +1598,18 @@ test_that("fineMappingPipeline(GwasSumStats): rFinite/rMismatch forwarded; rFini fineMappingPipeline(gss, methods = "susie", addSusieInf = FALSE, serFallback = TRUE, rFinite = 12345, rMismatch = "eb")) expect_equal(cap2$rFinite, 12345) + + # eb_mix (susieR >= 0.16.6) is passed through to R_mismatch unrestricted. + cap3 <- new.env(parent = emptyenv()) + local_mocked_bindings( + extractBlockGenotypes = .fmp_mockExtractor(), + .fmFitSusieRss = .fmp_mockFitRssDiag(flag = FALSE, capture = cap3), + .fmPostprocessOne = .fmp_mockPostprocess(), + .package = "pecotmr") + suppressMessages( + fineMappingPipeline(gss, methods = "susie", addSusieInf = FALSE, + serFallback = TRUE, rMismatch = "eb_mix")) + expect_equal(cap3$rMismatch, "eb_mix") }) test_that("fineMappingPipeline(GwasSumStats): keepFullFit='all' retains fit on non-fallback region", { From 3679e68d1771f29bd1484e31e8b9d971bb2a9f0a Mon Sep 17 00:00:00 2001 From: Yining97 Date: Tue, 28 Jul 2026 17:20:24 -0400 Subject: [PATCH 2/3] Relax susieR pin to >= 0.16.4; clarify rMismatch docs susie_rss() is no longer passed R_mismatch_method/check_prior (they moved into susie_rss_control() in 0.16.6), so the code runs on susieR 0.16.4+. The >= 0.16.6 pin couldn't be satisfied by the CI conda channel (max 0.16.4) and failed every CI job. Docs now note eb_mix needs 0.16.6 while the package itself works on 0.16.4+. --- DESCRIPTION | 2 +- R/fineMappingPipeline.R | 10 ++++++---- man/fineMappingPipeline.Rd | 10 ++++++---- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 906109c3..d5da4071 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -35,7 +35,7 @@ Imports: readr, rlang, stringr, - susieR (>= 0.16.6), + susieR (>= 0.16.4), tibble, tictoc, tidyr, diff --git a/R/fineMappingPipeline.R b/R/fineMappingPipeline.R index 6db18c8b..bcd707b5 100644 --- a/R/fineMappingPipeline.R +++ b/R/fineMappingPipeline.R @@ -282,10 +282,12 @@ #' case it defaults to the LD-panel sample size \code{getNSamples(ldSketch)}. #' @param rMismatch \code{QtlSumStats} / \code{GwasSumStats} only. LD-mismatch #' mode forwarded to \code{susieR::susie_rss()} as \code{R_mismatch} (e.g. -#' \code{"eb"} for empirical Bayes, \code{"eb_mix"} for residual-mixture EB in -#' susieR >= 0.16.6). Default \code{"none"} (susieR's default). (\code{R_mismatch_method} -#' and \code{check_prior} are susie_rss_control() settings in susieR >= 0.16.6 and -#' are left at their defaults.) +#' \code{"eb"} for empirical Bayes, \code{"eb_mix"} for residual-mixture EB; +#' \code{"eb_mix"} requires susieR >= 0.16.6). Default \code{"none"} (susieR's +#' default). \code{R_mismatch_method} and \code{check_prior} are no longer set +#' here: they became \code{susie_rss_control()} settings in susieR >= 0.16.6 +#' and are left at their control defaults, so this package works with +#' susieR 0.16.4+. #' @param keepFullFit \code{QtlSumStats} / \code{GwasSumStats} only. Controls #' retention of the pre-fallback multi-effect SuSiE-RSS fit when #' \code{serFallback=TRUE}: \code{"fallback"} (default) keeps it only for diff --git a/man/fineMappingPipeline.Rd b/man/fineMappingPipeline.Rd index 0ec5a2ec..bf5e7f66 100644 --- a/man/fineMappingPipeline.Rd +++ b/man/fineMappingPipeline.Rd @@ -352,10 +352,12 @@ case it defaults to the LD-panel sample size \code{getNSamples(ldSketch)}.} \item{rMismatch}{\code{QtlSumStats} / \code{GwasSumStats} only. LD-mismatch mode forwarded to \code{susieR::susie_rss()} as \code{R_mismatch} (e.g. -\code{"eb"} for empirical Bayes, \code{"eb_mix"} for residual-mixture EB in -susieR >= 0.16.6). Default \code{"none"} (susieR's default). (\code{R_mismatch_method} -and \code{check_prior} are susie_rss_control() settings in susieR >= 0.16.6 and are -left at their defaults.)} +\code{"eb"} for empirical Bayes, \code{"eb_mix"} for residual-mixture EB; +\code{"eb_mix"} requires susieR >= 0.16.6). Default \code{"none"} (susieR's +default). \code{R_mismatch_method} and \code{check_prior} are no longer set +here: they became \code{susie_rss_control()} settings in susieR >= 0.16.6 +and are left at their control defaults, so this package works with +susieR 0.16.4+.} \item{keepFullFit}{\code{QtlSumStats} / \code{GwasSumStats} only. Controls retention of the pre-fallback multi-effect SuSiE-RSS fit when From 14907f29a47068be6e4c3a252fd5f5d01b573e33 Mon Sep 17 00:00:00 2001 From: Yining97 Date: Thu, 30 Jul 2026 11:48:44 -0400 Subject: [PATCH 3/3] Add rssControl passthrough; clean up rMismatch docs (PR #557 review) Address review: drop the susieR 0.16.4/0.16.6 backwards-compat narrative from the rMismatch roxygen/.Rd and the fineMappingWrappers comment, and expose susie_rss_control() via a new rssControl named-list arg on fineMappingPipeline (forwarded as susie_rss(control=...)); NULL leaves the control defaults, a non-named-list errors. Threaded through .fmFitRssBlock and .fmFitSusieRss, with unit + real-data tests. --- R/fineMappingPipeline.R | 23 ++++++++++------- R/fineMappingWrappers.R | 24 +++++++++++++----- man/fineMappingPipeline.Rd | 18 ++++++++----- tests/testthat/test_fineMappingPipeline.R | 31 +++++++++++++++++++++-- tests/testthat/test_fineMappingWrappers.R | 30 ++++++++++++++++++++++ 5 files changed, 102 insertions(+), 24 deletions(-) diff --git a/R/fineMappingPipeline.R b/R/fineMappingPipeline.R index bcd707b5..9d37cfba 100644 --- a/R/fineMappingPipeline.R +++ b/R/fineMappingPipeline.R @@ -281,13 +281,14 @@ #' \code{rMismatch != "none"}) and \code{rFinite} is \code{NULL}, in which #' case it defaults to the LD-panel sample size \code{getNSamples(ldSketch)}. #' @param rMismatch \code{QtlSumStats} / \code{GwasSumStats} only. LD-mismatch -#' mode forwarded to \code{susieR::susie_rss()} as \code{R_mismatch} (e.g. -#' \code{"eb"} for empirical Bayes, \code{"eb_mix"} for residual-mixture EB; -#' \code{"eb_mix"} requires susieR >= 0.16.6). Default \code{"none"} (susieR's -#' default). \code{R_mismatch_method} and \code{check_prior} are no longer set -#' here: they became \code{susie_rss_control()} settings in susieR >= 0.16.6 -#' and are left at their control defaults, so this package works with -#' susieR 0.16.4+. +#' correction mode forwarded to \code{susieR::susie_rss()} as +#' \code{R_mismatch}: \code{"none"} (default), \code{"eb"} (empirical Bayes), +#' or \code{"eb_mix"} (residual-mixture EB). +#' @param rssControl \code{QtlSumStats} / \code{GwasSumStats} only. Optional +#' named list of \code{susieR::susie_rss_control()} settings (e.g. +#' \code{check_prior}, \code{mismatch_estimator}), forwarded as +#' \code{susie_rss()}'s \code{control} argument. Default \code{NULL} leaves +#' the \code{susie_rss_control()} defaults in place. #' @param keepFullFit \code{QtlSumStats} / \code{GwasSumStats} only. Controls #' retention of the pre-fallback multi-effect SuSiE-RSS fit when #' \code{serFallback=TRUE}: \code{"fallback"} (default) keeps it only for @@ -1750,6 +1751,7 @@ setMethod("fineMappingPipeline", "QtlSumStats", serFallback = FALSE, rFinite = NULL, rMismatch = "none", + rssControl = NULL, keepFullFit = "fallback", ...) { .fmAssertQcd(data) @@ -1869,7 +1871,8 @@ setMethod("fineMappingPipeline", "QtlSumStats", af = afByVar, fullFit = fullFit, fullFitAlphaOnly = fullFitAlphaOnly, includeAllCs = includeAllCs, serFallback = serFallback, rFinite = rFiniteResolved, - rMismatch = rMismatch, keepFullFit = keepFullFit) + rMismatch = rMismatch, rssControl = rssControl, + keepFullFit = keepFullFit) # The method column carries the bare token, independent of the # postprocess class. for (tk in names(ents)) pushRow(st, ctx, tr, tk, ents[[tk]]) @@ -1949,6 +1952,7 @@ setMethod("fineMappingPipeline", "GwasSumStats", serFallback = FALSE, rFinite = NULL, rMismatch = "none", + rssControl = NULL, keepFullFit = "fallback", ...) { .fmAssertQcd(data) @@ -2032,7 +2036,8 @@ setMethod("fineMappingPipeline", "GwasSumStats", af = afByVar, fullFit = fullFit, fullFitAlphaOnly = fullFitAlphaOnly, includeAllCs = includeAllCs, serFallback = serFallback, rFinite = rFiniteResolved, - rMismatch = rMismatch, keepFullFit = keepFullFit) + rMismatch = rMismatch, rssControl = rssControl, + keepFullFit = keepFullFit) for (tk in names(ents)) pushRow(st, tk, region_id, ents[[tk]]) } diff --git a/R/fineMappingWrappers.R b/R/fineMappingWrappers.R index ba24e5b9..c663fe79 100644 --- a/R/fineMappingWrappers.R +++ b/R/fineMappingWrappers.R @@ -1991,7 +1991,8 @@ mergeSusieCs <- function(fineMappingResult, coverage = 0.95) { # @noRd .fmFitSusieRss <- function(z, R, n, token, chainFromInf = NULL, coverage = 0.95, userArgs = NULL, - rFinite = NULL, rMismatch = "none") { + rFinite = NULL, rMismatch = "none", + rssControl = NULL) { info <- .fineMappingMethodCapabilities[[token]] if (is.null(info) || identical(info$unmappableEffects, NA_character_)) { stop(".fmFitSusieRss: token '", token, "' is not a SuSiE-family method.") @@ -2005,8 +2006,16 @@ mergeSusieCs <- function(fineMappingResult, coverage = 0.95) { # methodArgs (folded in after) still override them. baseArgs$R_finite <- rFinite baseArgs$R_mismatch <- rMismatch - # R_mismatch_method / check_prior are susie_rss_control() settings in susieR - # >= 0.16.6 (not top-level susie_rss args); left at their control defaults. + # Optional susie_rss_control() settings (e.g. check_prior, mismatch_estimator), + # supplied as a named list and forwarded as susie_rss()'s `control` argument. + if (!is.null(rssControl)) { + if (!is.list(rssControl) || is.null(names(rssControl)) || + any(!nzchar(names(rssControl)))) { + stop(".fmFitSusieRss: `rssControl` must be a named list of ", + "susieR::susie_rss_control() settings.") + } + baseArgs$control <- do.call(susieR::susie_rss_control, rssControl) + } if (!is.null(chainFromInf) && token != "susieInf") { # SuSiE-RSS(-ash) initialised from a SuSiE-inf fit; userArgs folded into the # arg prep so L_greedy is clamped rather than passed through raw. @@ -2114,7 +2123,8 @@ mergeSusieCs <- function(fineMappingResult, coverage = 0.95) { fullFit = FALSE, fullFitAlphaOnly = TRUE, includeAllCs = FALSE, serFallback = FALSE, rFinite = NULL, - rMismatch = "none", keepFullFit = "fallback") { + rMismatch = "none", rssControl = NULL, + keepFullFit = "fallback") { chainLocal <- .fmResolveSusieChain(toRun, addSusieInf) infFit <- NULL if (chainLocal$runInf) { @@ -2122,7 +2132,8 @@ mergeSusieCs <- function(fineMappingResult, coverage = 0.95) { message(sprintf("Fitting susieInf (RSS) for %s ...", label)) infFit <- .fmFitSusieRss(z, R, n, "susieInf", coverage = coverage, userArgs = methodArgs[["susieInf"]], - rFinite = rFinite, rMismatch = rMismatch) + rFinite = rFinite, rMismatch = rMismatch, + rssControl = rssControl) } out <- list() for (tk in toRun) { @@ -2151,7 +2162,8 @@ mergeSusieCs <- function(fineMappingResult, coverage = 0.95) { message(sprintf("Fitting %s (RSS) for %s ...", tk, label)) fit <- .fmFitSusieRss(z, R, n, tk, chainFromInf = chainFrom, coverage = coverage, userArgs = methodArgs[[tk]], - rFinite = rFinite, rMismatch = rMismatch) + rFinite = rFinite, rMismatch = rMismatch, + rssControl = rssControl) rfd <- fit$R_finite_diagnostics flag <- if (!is.null(rfd) && !is.null(rfd$R_reliability_flag)) isTRUE(rfd$R_reliability_flag) else NA diff --git a/man/fineMappingPipeline.Rd b/man/fineMappingPipeline.Rd index bf5e7f66..362c0d0b 100644 --- a/man/fineMappingPipeline.Rd +++ b/man/fineMappingPipeline.Rd @@ -117,6 +117,7 @@ fineMappingPipeline(data, ...) serFallback = FALSE, rFinite = NULL, rMismatch = "none", + rssControl = NULL, keepFullFit = "fallback", ... ) @@ -141,6 +142,7 @@ fineMappingPipeline(data, ...) serFallback = FALSE, rFinite = NULL, rMismatch = "none", + rssControl = NULL, keepFullFit = "fallback", ... ) @@ -351,13 +353,15 @@ except when a finite/EB mode is active (\code{serFallback=TRUE} or case it defaults to the LD-panel sample size \code{getNSamples(ldSketch)}.} \item{rMismatch}{\code{QtlSumStats} / \code{GwasSumStats} only. LD-mismatch -mode forwarded to \code{susieR::susie_rss()} as \code{R_mismatch} (e.g. -\code{"eb"} for empirical Bayes, \code{"eb_mix"} for residual-mixture EB; -\code{"eb_mix"} requires susieR >= 0.16.6). Default \code{"none"} (susieR's -default). \code{R_mismatch_method} and \code{check_prior} are no longer set -here: they became \code{susie_rss_control()} settings in susieR >= 0.16.6 -and are left at their control defaults, so this package works with -susieR 0.16.4+.} +correction mode forwarded to \code{susieR::susie_rss()} as +\code{R_mismatch}: \code{"none"} (default), \code{"eb"} (empirical Bayes), +or \code{"eb_mix"} (residual-mixture EB).} + +\item{rssControl}{\code{QtlSumStats} / \code{GwasSumStats} only. Optional +named list of \code{susieR::susie_rss_control()} settings (e.g. +\code{check_prior}, \code{mismatch_estimator}), forwarded as +\code{susie_rss()}'s \code{control} argument. Default \code{NULL} leaves +the \code{susie_rss_control()} defaults in place.} \item{keepFullFit}{\code{QtlSumStats} / \code{GwasSumStats} only. Controls retention of the pre-fallback multi-effect SuSiE-RSS fit when diff --git a/tests/testthat/test_fineMappingPipeline.R b/tests/testthat/test_fineMappingPipeline.R index 1593448a..857cef68 100644 --- a/tests/testthat/test_fineMappingPipeline.R +++ b/tests/testthat/test_fineMappingPipeline.R @@ -130,7 +130,8 @@ context("fineMappingPipeline") .fmp_mockFitRss <- function() { function(z, R, n, token, chainFromInf = NULL, coverage = 0.95, - userArgs = NULL, rFinite = NULL, rMismatch = "none") { + userArgs = NULL, rFinite = NULL, rMismatch = "none", + rssControl = NULL) { list(token = token, n_variants = length(z)) } } @@ -141,10 +142,12 @@ context("fineMappingPipeline") # args the pipeline forwarded so tests can assert on them. .fmp_mockFitRssDiag <- function(flag = FALSE, capture = NULL) { function(z, R, n, token, chainFromInf = NULL, coverage = 0.95, - userArgs = NULL, rFinite = NULL, rMismatch = "none") { + userArgs = NULL, rFinite = NULL, rMismatch = "none", + rssControl = NULL) { if (!is.null(capture)) { capture$rFinite <- rFinite capture$rMismatch <- rMismatch + capture$rssControl <- rssControl } fit <- list(token = token, n_variants = length(z), multiTag = "multi") fit$R_finite_diagnostics <- list( @@ -1610,6 +1613,30 @@ test_that("fineMappingPipeline(GwasSumStats): rFinite/rMismatch forwarded; rFini fineMappingPipeline(gss, methods = "susie", addSusieInf = FALSE, serFallback = TRUE, rMismatch = "eb_mix")) expect_equal(cap3$rMismatch, "eb_mix") + + # rssControl (susie_rss_control() settings) forwarded through to the fitter. + cap4 <- new.env(parent = emptyenv()) + local_mocked_bindings( + extractBlockGenotypes = .fmp_mockExtractor(), + .fmFitSusieRss = .fmp_mockFitRssDiag(flag = FALSE, capture = cap4), + .fmPostprocessOne = .fmp_mockPostprocess(), + .package = "pecotmr") + suppressMessages( + fineMappingPipeline(gss, methods = "susie", addSusieInf = FALSE, + rssControl = list(check_prior = TRUE, + mismatch_estimator = "map"))) + expect_equal(cap4$rssControl, list(check_prior = TRUE, + mismatch_estimator = "map")) + # default: no rssControl -> fitter sees NULL + cap5 <- new.env(parent = emptyenv()) + local_mocked_bindings( + extractBlockGenotypes = .fmp_mockExtractor(), + .fmFitSusieRss = .fmp_mockFitRssDiag(flag = FALSE, capture = cap5), + .fmPostprocessOne = .fmp_mockPostprocess(), + .package = "pecotmr") + suppressMessages( + fineMappingPipeline(gss, methods = "susie", addSusieInf = FALSE)) + expect_null(cap5$rssControl) }) test_that("fineMappingPipeline(GwasSumStats): keepFullFit='all' retains fit on non-fallback region", { diff --git a/tests/testthat/test_fineMappingWrappers.R b/tests/testthat/test_fineMappingWrappers.R index 6431fec6..278e1200 100644 --- a/tests/testthat/test_fineMappingWrappers.R +++ b/tests/testthat/test_fineMappingWrappers.R @@ -1768,6 +1768,36 @@ test_that("fitMvsusie forwards arguments to mvsusieR::mvsusie", { expect_equal(r$coverage, 0.9) }) +test_that(".fmFitSusieRss rejects a non-named-list rssControl", { + z <- c(0.5, -1.2, 2.0); R <- diag(3) + expect_error( + pecotmr:::.fmFitSusieRss(z, R, n = 1000, token = "susie", rssControl = "nope"), + "named list", fixed = TRUE) + expect_error( + pecotmr:::.fmFitSusieRss(z, R, n = 1000, token = "susie", rssControl = list(1, 2)), + "named list", fixed = TRUE) + expect_error( # partially named + pecotmr:::.fmFitSusieRss(z, R, n = 1000, token = "susie", + rssControl = list(check_prior = TRUE, 2)), + "named list", fixed = TRUE) +}) + +test_that(".fmFitSusieRss forwards rssControl to susie_rss as control", { + cap <- new.env(parent = emptyenv()) + local_mocked_bindings( + susie_rss_control = function(...) list(.tag = "ctrl", ...), + susie_rss = function(...) { args <- list(...); cap$control <- args$control + list(sets = list(), pip = numeric()) }, + .package = "susieR") + z <- c(0.5, -1.2, 2.0); R <- diag(3) + pecotmr:::.fmFitSusieRss(z, R, n = 1000, token = "susie", + rssControl = list(check_prior = TRUE, + mismatch_estimator = "map")) + expect_equal(cap$control$.tag, "ctrl") + expect_true(cap$control$check_prior) + expect_equal(cap$control$mismatch_estimator, "map") +}) + test_that("fitMvsusieRss forwards arguments to mvsusieR::mvsusie_rss", { skip_if_not_installed("mvsusieR") local_mocked_bindings(