Last updated: 2018-07-20
workflowr checks: (Click a bullet for more information) ✔ R Markdown file: up-to-date
Great! Since the R Markdown file has been committed to the Git repository, you know the exact version of the code that produced these results.
✔ Environment: empty
Great job! The global environment was empty. Objects defined in the global environment can affect the analysis in your R Markdown file in unknown ways. For reproduciblity it’s best to always run the code in an empty environment.
✔ Seed:
set.seed(20180714)
The command set.seed(20180714) was run prior to running the code in the R Markdown file. Setting a seed ensures that any results that rely on randomness, e.g. subsampling or permutations, are reproducible.
✔ Session information: recorded
Great job! Recording the operating system, R version, and package versions is critical for reproducibility.
✔ Repository version: c23f41c
wflow_publish or wflow_git_commit). workflowr only checks the R Markdown file, but you know if there are other scripts or data files that it depends on. Below is the status of the Git repository when the results were generated:
Ignored files:
Ignored: .DS_Store
Ignored: .Rhistory
Ignored: .Rproj.user/
Ignored: docs/.DS_Store
Ignored: docs/figure/.DS_Store
Note that any generated files, e.g. HTML, png, CSS, etc., are not included in this status report because it is ok for generated content to have uncommitted changes.
Here I implement the algorithm described in a previous note and compare results with FLASH.
Click “Code” to view the implementation.
# INITIALIZATION FUNCTIONS ------------------------------------------
add_new_altfl <- function(data, fl, seed=1) {
set.seed(seed)
altfl <- list()
altfl$tau <- fl$tau
altfl$Rk <- flashr:::flash_get_R(data, fl)
n <- nrow(fl$tau)
p <- ncol(fl$tau)
altfl$wl <- rep(0.1, n)
altfl$wf <- rep(0.1, p)
altfl$mul <- rnorm(n)
altfl$muf <- rnorm(p)
altfl$s2l <- rep(1, n)
altfl$s2f <- rep(1, p)
altfl$al <- altfl$af <- 1
altfl$pi0l <- altfl$pi0f <- 0.9
altfl$KL <- sum(unlist(fl$KL_l) + unlist(fl$KL_f))
return(altfl)
}
fl_to_altfl <- function(data, fl, k) {
altfl <- list()
altfl$tau <- fl$tau
altfl$Rk <- flashr:::flash_get_R(data, fl)
altfl$al <- fl$gl[[k]]$a
altfl$pi0l <- fl$gl[[k]]$pi0
altfl$af <- fl$gf[[k]]$a
altfl$pi0f <- fl$gf[[k]]$pi0
s2 = 1/(fl$EF2[, k] %*% t(fl$tau))
s = sqrt(s2)
Rk = flashr:::flash_get_Rk(data, fl, k)
x = fl$EF[, k] %*% t(Rk * fl$tau) * s2
w = 1 - fl$gl[[k]]$pi0
a = fl$gl[[k]]$a
altfl$wl <- ebnm:::wpost_normal(x, s, w, a)
altfl$mul <- ebnm:::pmean_cond_normal(x, s, a)
altfl$s2l <- ebnm:::pvar_cond_normal(s, a)
s2 = 1/(fl$EL2[, k] %*% fl$tau)
s = sqrt(s2)
Rk = flashr:::flash_get_Rk(data, fl, k)
x = fl$EL[, k] %*% (Rk * fl$tau) * s2
w = 1 - fl$gf[[k]]$pi0
a = fl$gf[[k]]$a
altfl$wf <- ebnm:::wpost_normal(x, s, w, a)
altfl$muf <- ebnm:::pmean_cond_normal(x, s, a)
altfl$s2f <- ebnm:::pvar_cond_normal(s, a)
altfl$KL <- sum(unlist(fl$KL_l)[-k] + unlist(fl$KL_f)[-k])
return(altfl)
}
altfl_to_fl <- function(altfl, fl, k) {
fl$EL[, k] <- compute_EX(altfl$wl, altfl$mul)
fl$EL2[, k] <- compute_EX2(altfl$wl, altfl$mul, altfl$s2l)
fl$EF[, k] <- compute_EX(altfl$wf, altfl$muf)
fl$EF2[, k] <- compute_EX2(altfl$wf, altfl$muf, altfl$s2f)
fl$gl[[k]] <- list(pi0 = altfl$pi0l, a = altfl$al)
fl$gf[[k]] <- list(pi0 = altfl$pi0f, a = altfl$af)
fl$ebnm_fn_l <- fl$ebnm_fn_f <- "alt"
fl$ebnm_param_l <- fl$ebnm_param_f <- list()
fl$tau <- altfl$tau
return(fl)
}
# OBJECTIVE FUNCTION ------------------------------------------------
compute_obj <- function(altfl) {
with(altfl, {
EL <- compute_EX(wl, mul)
EL2 <- compute_EX2(wl, mul, s2l)
EF <- compute_EX(wf, muf)
EF2 <- compute_EX2(wf, muf, s2f)
obj <- rep(0, 8)
obj[1] <- sum(0.5 * log(tau / (2 * pi)))
obj[2] <- sum(-0.5 * (tau * (Rk^2 - 2 * Rk * outer(EL, EF) + outer(EL2, EF2))))
tmp <- (1 - wl) * (log(pi0l) - log(1 - wl))
obj[3] <- sum(tmp[!is.nan(tmp)])
tmp <- wl * (log(1 - pi0l) - log(wl))
obj[4] <- sum(tmp[!is.nan(tmp)])
obj[5] <- sum(0.5 * wl * (log(al) + log(s2l) + 1 - al * (mul^2 + s2l)))
tmp <- (1 - wf) * (log(pi0f) - log(1 - wf))
obj[6] <- sum(tmp[!is.nan(tmp)])
tmp <- wf * (log(1 - pi0f) - log(wf))
obj[7] <- sum(tmp[!is.nan(tmp)])
obj[8] <- sum(0.5 * wf * (log(af) + log(s2f) + 1 - af * (muf^2 + s2f)))
return(sum(obj) + KL)
})
}
compute_EX <- function(w, mu) {
return(as.vector(w * mu))
}
compute_EX2 <- function(w, mu, sigma2) {
return(as.vector(w * (mu^2 + sigma2)))
}
# UPDATE FUNCTIONS --------------------------------------------------
update_a <- function(w, EX2) {
return(sum(w) / sum(EX2))
}
update_pi0 <- function(w) {
return(sum(1 - w) / length(w))
}
update_mul <- function(a, tau, Rk, EF, EF2) {
n <- nrow(tau)
p <- ncol(tau)
numer <- rowSums(tau * Rk * matrix(EF, nrow=n, ncol=p, byrow=TRUE))
denom <- a + rowSums(tau * matrix(EF2, nrow=n, ncol=p, byrow=TRUE))
return(numer / denom)
}
update_muf <- function(a, tau, Rk, EL, EL2) {
n <- nrow(tau)
p <- ncol(tau)
numer <- colSums(tau * Rk * matrix(EL, nrow=n, ncol=p, byrow=FALSE))
denom <- a + colSums(tau * matrix(EL2, nrow=n, ncol=p, byrow=FALSE))
return(numer / denom)
}
update_s2l <- function(a, tau, EF2) {
n <- nrow(tau)
p <- ncol(tau)
return(1 / (a + rowSums(tau * matrix(EF2, nrow=n, ncol=p, byrow=TRUE))))
}
update_s2f <- function(a, tau, EL2) {
n <- nrow(tau)
p <- ncol(tau)
return(1 / (a + colSums(tau * matrix(EL2, nrow=n, ncol=p, byrow=FALSE))))
}
update_wl <- function(a, pi0, mu, sigma2, tau, Rk, EF, EF2) {
C1 <- log(1 - pi0) - log(pi0)
C2 <- 0.5 * (log(a) + log(sigma2) - a * (mu^2 + sigma2) + 1)
C3 <- rowSums(tau * (Rk * outer(mu, EF) - 0.5 * outer(mu^2 + sigma2, EF2)))
C <- C1 + C2 + C3
return(1 / (1 + exp(-C)))
}
update_wf <- function(a, pi0, mu, sigma2, tau, Rk, EL, EL2) {
C1 <- log(1 - pi0) - log(pi0)
C2 <- 0.5 * (log(a) + log(sigma2) - a * (mu^2 + sigma2) + 1)
C3 <- colSums(tau * (Rk * outer(EL, mu) - 0.5 * outer(EL2, mu^2 + sigma2)))
C <- C1 + C2 + C3
return(1 / (1 + exp(-C)))
}
# ALGORITHM ---------------------------------------------------------
update_tau <- function(altfl) {
within(altfl, {
EL <- compute_EX(wl, mul)
EL2 <- compute_EX2(wl, mul, s2l)
EF <- compute_EX(wf, muf)
EF2 <- compute_EX2(wf, muf, s2f)
R2 <- Rk^2 - 2 * Rk * outer(EL, EF) + outer(EL2, EF2)
tau <- matrix(1 / colMeans(R2), nrow=nrow(tau), ncol=ncol(tau),
byrow=TRUE)
})
}
update_loadings_post <- function(altfl) {
within(altfl, {
EF <- compute_EX(wf, muf)
EF2 <- compute_EX2(wf, muf, s2f)
mul <- update_mul(al, tau, Rk, EF, EF2)
s2l <- update_s2l(al, tau, EF2)
wl <- update_wl(al, pi0l, mul, s2l, tau, Rk, EF, EF2)
})
}
update_loadings_prior <- function(altfl) {
within(altfl, {
EL2 <- compute_EX2(wl, mul, s2l)
al <- update_a(wl, EL2)
pi0l <- update_pi0(wl)
})
}
update_factor_post <- function(altfl) {
within(altfl, {
EL <- compute_EX(wl, mul)
EL2 <- compute_EX2(wl, mul, s2l)
muf <- update_muf(af, tau, Rk, EL, EL2)
s2f <- update_s2f(af, tau, EL2)
wf <- update_wf(af, pi0f, muf, s2f, tau, Rk, EL, EL2)
})
}
update_factor_prior <- function(altfl) {
within(altfl, {
EF2 <- compute_EX2(wf, muf, s2f)
af <- update_a(wf, EF2)
pi0f <- update_pi0(wf)
})
}
do_one_update <- function(altfl) {
obj <- rep(0, 5)
altfl <- update_tau(altfl)
obj[1] <- compute_obj(altfl)
altfl <- update_loadings_post(altfl)
obj[2] <- compute_obj(altfl)
altfl <- update_loadings_prior(altfl)
obj[3] <- compute_obj(altfl)
altfl <- update_factor_post(altfl)
obj[4] <- compute_obj(altfl)
altfl <- update_factor_prior(altfl)
obj[5] <- compute_obj(altfl)
return(list(altfl = altfl, obj = obj))
}
optimize_alt_fl <- function(altfl, tol = .01, verbose = FALSE) {
obj <- compute_obj(altfl)
diff <- Inf
while (diff > tol) {
tmp <- do_one_update(altfl)
new_obj <- tmp$obj[length(tmp$obj)]
diff <- new_obj - obj
obj <- new_obj
if (verbose) {
message(paste("Objective:", obj))
}
altfl <- tmp$altfl
}
return(altfl)
}
Using the same dataset as in previous investigations, I fit a FLASH object with four factors (recall that it’s the fourth factor that has been causing problems during loadings updates):
load("./data/before_bad.Rdata")
# devtools::install_github("stephenslab/flashr")
devtools::load_all("/Users/willwerscheid/GitHub/flashr")
Loading flashr
# devtools::install_github("stephenslab/flashr")
devtools::load_all("/Users/willwerscheid/GitHub/ebnm")
Loading ebnm
fl <- flash_add_greedy(data, Kmax=4, verbose=FALSE)
fitting factor/loading 1
fitting factor/loading 2
fitting factor/loading 3
fitting factor/loading 4
The objective as computed by FLASH is:
flash_get_objective(data, fl)
[1] -1297135
I now convert the fourth factor to an “altfl” object. The objective as computed by the alternate method is:
altfl <- fl_to_altfl(data, fl, 4)
compute_obj(altfl)
[1] -1287925
Next, I optimize the altfl object:
altfl <- optimize_alt_fl(altfl, verbose=TRUE)
Objective: -1266527.52893897
Objective: -1259591.41880171
Objective: -1256717.73280192
Objective: -1256092.73281445
Objective: -1255909.73574933
Objective: -1255818.46915015
Objective: -1255766.3647692
Objective: -1255735.3950312
Objective: -1255716.97331457
Objective: -1255705.43249546
Objective: -1255697.71996832
Objective: -1255692.31493727
Objective: -1255688.43367143
Objective: -1255685.61158832
Objective: -1255683.53045841
Objective: -1255681.97109968
Objective: -1255680.78688195
Objective: -1255679.87854253
Objective: -1255679.17674322
Objective: -1255678.63159121
Objective: -1255678.20636055
Objective: -1255677.87357066
Objective: -1255677.61241567
Objective: -1255677.40700628
Objective: -1255677.24512755
Objective: -1255677.11733974
Objective: -1255677.01631629
Objective: -1255676.93635016
Objective: -1255676.87298197
Objective: -1255676.82271789
Objective: -1255676.78281419
Objective: -1255676.75111177
Objective: -1255676.72590854
Objective: -1255676.7058606
Objective: -1255676.68990533
Objective: -1255676.67720158
Objective: -1255676.66708273
Objective: -1255676.65902003
Finally, I put the altfl object back into the fourth factor of the flash object.
fl2 <- altfl_to_fl(altfl, fl, 4)
The fits are very different. For priors on both factors and loadings, the altfl fit favors less sparsity (smaller spikes, i.e., smaller pi0) and more shrinkage (narrower slabs, i.e., greater a).
list(loadings = fl$gl[[4]], alt_loadings = fl2$gl[[4]])
$loadings
$loadings$pi0
[1] 0.72317
$loadings$a
[1] 0.04110932
$alt_loadings
$alt_loadings$pi0
[1] 0.6009072
$alt_loadings$a
[1] 0.09951342
list(factors = fl$gf[[4]], alt_factors = fl2$gf[[4]])
$factors
$factors$pi0
[1] 0.3616584
$factors$a
[1] 29.84806
$alt_factors
$alt_factors$pi0
[1] 0.1264111
$alt_factors$a
[1] 46.48745
A scatterplot comparing the fitted fourth factor/loading appears as follows:
fitted <- flash_get_fitted_values(fl)
fitted2 <- flash_get_fitted_values(fl2)
minval <- min(c(fitted, fitted2))
maxval <- max(c(fitted, fitted2))
plot(fitted, fitted2, pch='.',
xlab="FLASH fit", ylab="Alternate fit",
xlim=c(minval, maxval), ylim=c(minval, maxval),
main="Fitted values")

| Version | Author | Date |
|---|---|---|
| 2cbe7dd | Jason Willwerscheid | 2018-07-19 |
To see what’s going on, I fit the estimated loadings against the estimated prior on the loadings. For the FLASH fit:
plot(density(fl$EL[, 4]), xlim=c(-15, 15), ylim=c(0, 0.1),
main="FLASH loadings")
grid <- seq(-15, 15, by=.05)
y <- (1 - fl$gl[[4]]$pi0) * dnorm(grid, 0, 1/sqrt(fl$gl[[4]]$a))
lines(grid, y, lty=2)
legend("topright", legend = c("fitted", "prior"), lty = c(1, 2))

| Version | Author | Date |
|---|---|---|
| 2cbe7dd | Jason Willwerscheid | 2018-07-19 |
For the alternate approach:
plot(density(fl2$EL[, 4]), xlim=c(-15, 15), ylim=c(0, 0.1),
main="Alternate approach")
grid <- seq(-15, 15, by=.05)
y <- (1 - fl2$gl[[4]]$pi0) * dnorm(grid, 0, 1/sqrt(fl2$gl[[4]]$a))
lines(grid, y, lty=2)
legend("topright", legend = c("fitted", "prior"), lty = c(1, 2))

| Version | Author | Date |
|---|---|---|
| 2cbe7dd | Jason Willwerscheid | 2018-07-19 |
This doesn’t seem right!
sessionInfo()
R version 3.4.3 (2017-11-30)
Platform: x86_64-apple-darwin15.6.0 (64-bit)
Running under: macOS Sierra 10.12.6
Matrix products: default
BLAS: /Library/Frameworks/R.framework/Versions/3.4/Resources/lib/libRblas.0.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/3.4/Resources/lib/libRlapack.dylib
locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] ebnm_0.1-13 flashr_0.5-12
loaded via a namespace (and not attached):
[1] Rcpp_0.12.17 pillar_1.2.1 plyr_1.8.4
[4] compiler_3.4.3 git2r_0.21.0 workflowr_1.0.1
[7] R.methodsS3_1.7.1 R.utils_2.6.0 iterators_1.0.9
[10] tools_3.4.3 testthat_2.0.0 digest_0.6.15
[13] tibble_1.4.2 evaluate_0.10.1 memoise_1.1.0
[16] gtable_0.2.0 lattice_0.20-35 rlang_0.2.0
[19] Matrix_1.2-12 foreach_1.4.4 commonmark_1.4
[22] yaml_2.1.17 parallel_3.4.3 withr_2.1.1.9000
[25] stringr_1.3.0 roxygen2_6.0.1.9000 xml2_1.2.0
[28] knitr_1.20 devtools_1.13.4 rprojroot_1.3-2
[31] grid_3.4.3 R6_2.2.2 rmarkdown_1.8
[34] ggplot2_2.2.1 ashr_2.2-10 magrittr_1.5
[37] whisker_0.3-2 backports_1.1.2 scales_0.5.0
[40] codetools_0.2-15 htmltools_0.3.6 MASS_7.3-48
[43] assertthat_0.2.0 softImpute_1.4 colorspace_1.3-2
[46] stringi_1.1.6 lazyeval_0.2.1 munsell_0.4.3
[49] doParallel_1.0.11 pscl_1.5.2 truncnorm_1.0-8
[52] SQUAREM_2017.10-1 R.oo_1.21.0
This reproducible R Markdown analysis was created with workflowr 1.0.1