Skip to content

Commit cbee784

Browse files
committed
Better integrated the new multithreaded calculations into the other parts.
1 parent 2debd8d commit cbee784

6 files changed

Lines changed: 39 additions & 29 deletions

File tree

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Encoding: UTF-8
22
Type: Package
33
Package: fastTopics
4-
Version: 0.7-41
4+
Version: 0.7-42
55
Date: 2026-04-17
66
Title: Fast Algorithms for Fitting Topic Models and Non-Negative
77
Matrix Factorizations to Count Data

R/fit_poisson_nmf.R

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ fit_poisson_nmf <- function (X, k, fit0, numiter = 100,
455455
cat(sprintf("Running at most %d %s updates, %s extrapolation ",
456456
numiter,method.text,
457457
ifelse(control$extrapolate,"with","without")))
458-
cat("(fastTopics 0.7-41).\n")
458+
cat("(fastTopics 0.7-42).\n")
459459
}
460460

461461
# INITIALIZE ESTIMATES
@@ -554,7 +554,9 @@ fit_poisson_nmf_main_loop <- function (X, fit, numiter, update.factors,
554554
progress[i,"loglik.multinom"] <-
555555
loglik.const - fit$loss - sum(loglik_size_factors(X,fit$F,fit$L))
556556
progress[i,"dev"] <- dev.const + 2*fit$loss
557-
res <- with(poisson_nmf_kkt(X,fit$F,fit$L),
557+
res <- with(poisson_nmf_kkt(X,fit$F,fit$L,
558+
version = ifelse(control$nc == 1,
559+
"Rcpp","RcppParallel")),
558560
max(abs(rbind(F[update.factors,],
559561
L[update.loadings,]))))
560562
progress[i,"res"] <- res
@@ -647,7 +649,9 @@ update_poisson_nmf <- function (X, fit, update.factors, update.loadings,
647649

648650
# Compute the value of the objective ("loss") function at the updated
649651
# estimates.
650-
fit$loss <- sum(cost(X,fit$L,t(fit$F),control$eps))
652+
fit$loss <- sum(cost(X,fit$L,t(fit$F),control$eps,
653+
version = ifelse(control$nc == 1,
654+
"Rcpp","RcppParallel")))
651655
fit$loss.fnly <- fit$loss
652656

653657
# Output the updated "fit".
@@ -697,7 +701,9 @@ update_poisson_nmf_extrapolated <- function (X, fit, update.factors,
697701
# Compute the value of the objective (loss) function at the
698702
# extrapolated solution for the loadings (Ly) and the
699703
# non-extrapolated solution for the factors (Fn).
700-
fit$loss.fnly <- sum(cost(X,fit$Ly,t(Fn),control$eps))
704+
fit$loss.fnly <- sum(cost(X,fit$Ly,t(Fn),control$eps,
705+
version = ifelse(control$nc == 1,
706+
"Rcpp","RcppParallel")))
701707

702708
# Update the extrapolation parameters following Algorithm 3 of
703709
# Ang & Gillis (2019).

R/init_poisson_nmf.R

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,9 @@ init_poisson_nmf <-
142142
L <- pmax(L,control$minval)
143143

144144
# Compute the value of the objective ("loss") function at the
145-
# initial estimates of the factors and loading.
146-
loss <- sum(cost(X,L,t(F),control$eps))
145+
# initial estimates of the factors and loadings.
146+
loss <- sum(cost(X,L,t(F),control$eps,
147+
version = ifelse(control$nc == 1,"Rcpp","RcppParallel")))
147148

148149
# Restore the BLAS settings.
149150
blas_set_num_threads(ncb)

R/likelihood.R

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,13 @@ cost <- function (X, A, B, e = 1e-8, family = c("poisson","multinom"),
9595
B <- matrix(B,1,ncol(X))
9696

9797
# Check and process "model" and "version" input arguments.
98-
family <- match.arg(family)
98+
family <- match.arg(family)
9999
poisson <- family == "poisson"
100100
if (missing(version)) {
101101
if (is.matrix(X))
102102
version <- "R"
103103
else
104-
version <- "Rcpp_parallel"
104+
version <- "Rcpp"
105105
}
106106

107107
# Compute the terms in the log-likelihoods that depend on A or B.
@@ -184,6 +184,7 @@ deviance_poisson_const <- function (X) {
184184
# Compute the residuals of the first-order Karush-Kuhn-Tucker (KKT)
185185
# conditions for Poisson non-negative matrix factorization at solution
186186
# estimate (F,L).
187+
#
187188
#' @importFrom Matrix sparseMatrix
188189
poisson_nmf_kkt <- function (X, F, L, e = 1e-8,
189190
version = c("Rcpp_parallel","Rcpp")) {
@@ -198,10 +199,10 @@ poisson_nmf_kkt <- function (X, F, L, e = 1e-8,
198199
A <- sparseMatrix(i = d$i,j = d$j,x = y,dims = dim(X))
199200
return(list(F = F*(repmat(colSums(L),ncol(X)) - as.matrix(t(A) %*% L)),
200201
L = L*(repmat(colSums(F),nrow(X)) - as.matrix(A %*% F))))
201-
} else {
202-
result <- poisson_nmf_kkt_sparse_parallel_rcpp(X,L,F,e)
203-
return(list(F = F*(repmat(colSums(L),ncol(X)) - result$tAL),
204-
L = L*(repmat(colSums(F),nrow(X)) - result$AF)))
202+
} else if (version == "Rcpp_parallel") {
203+
res <- poisson_nmf_kkt_sparse_parallel_rcpp(X,L,F,e)
204+
return(list(F = F*(repmat(colSums(L),ncol(X)) - res$tAL),
205+
L = L*(repmat(colSums(F),nrow(X)) - res$AF)))
205206
}
206207
}
207208

tests/testthat/test_likelihood.R

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ test_that(paste("R, Rcpp and Rcpp_parallel versions of cost function return",
99

1010
# Generate a data set.
1111
out <- simulate_count_data(10,8,k)
12-
X <- out$X
13-
F <- out$F
14-
L <- out$L
15-
Y <- as(X,"CsparseMatrix")
12+
X <- out$X
13+
F <- out$F
14+
L <- out$L
15+
Y <- as(X,"CsparseMatrix")
1616

1717
# Compute the loss function.
1818
f1 <- cost(X,L,t(F),version = "R")
@@ -23,11 +23,11 @@ test_that(paste("R, Rcpp and Rcpp_parallel versions of cost function return",
2323
f6 <- cost(Y,L,t(F),version = "Rcpp_parallel")
2424

2525
# The cost function calculations should all give the same result.
26-
expect_equal(f1,f2)
27-
expect_equal(f1,f3)
28-
expect_equal(f1,f4)
29-
expect_equal(f1,f5)
30-
expect_equal(f1,f6)
26+
expect_equal(f1,f2,scale = 1,tolerance = 1e-10)
27+
expect_equal(f1,f3,scale = 1,tolerance = 1e-10)
28+
expect_equal(f1,f4,scale = 1,tolerance = 1e-10)
29+
expect_equal(f1,f5,scale = 1,tolerance = 1e-10)
30+
expect_equal(f1,f6,scale = 1,tolerance = 1e-10)
3131
}
3232
})
3333

@@ -40,19 +40,20 @@ test_that(paste("loglik_poisson_nmf gives correct result for sparse and",
4040

4141
# Generate a data set.
4242
out <- simulate_count_data(10,8,k)
43-
X <- out$X
43+
X <- out$X
44+
Y <- as(X,"CsparseMatrix")
4445
fit <- out[c("F","L")]
4546
class(fit) <- c("poisson_nmf_fit","list")
46-
47+
4748
# Compute the log-likelikhood.
4849
f1 <- loglik_poisson_nmf_with_dpois(X,fit)
4950
f2 <- loglik_poisson_nmf(X,fit,e = 0)
50-
f3 <- loglik_poisson_nmf(as(X,"CsparseMatrix"),fit,e = 0)
51+
f3 <- loglik_poisson_nmf(Y,fit,e = 0)
5152
names(f1) <- rownames(X)
5253

5354
# The likelihood calculations should all be the same.
54-
expect_equal(f1,f2)
55-
expect_equal(f1,f3)
55+
expect_equal(f1,f2,scale = 1,tolerance = 1e-10)
56+
expect_equal(f1,f3,scale = 1,tolerance = 1e-10)
5657
}
5758
})
5859

tests/testthat/test_predict.R

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ test_that("project_poisson_nmf leaves the F matrix unchanged",{
1717

1818
# Predict L in unseen (test) data points.
1919
capture.output(out <- project_poisson_nmf(test,F,numiter = 20))
20-
expect_equal(rep(1,3),unname(diag(cor(out$F,F))),tolerance = 1e-8)
20+
expect_equal(rep(1,3),unname(diag(cor(out$F,F))),scale=1,tolerance=1e-8)
2121
})
2222

2323
test_that(paste("Running project_poisson_nmf on the training data",
@@ -32,5 +32,6 @@ test_that(paste("Running project_poisson_nmf on the training data",
3232
capture.output(fit <- init_poisson_nmf(X,F = dat$F,init.method = "random"))
3333
capture.output(fit <- fit_poisson_nmf(X,fit0 = fit))
3434
capture.output(out <- project_poisson_nmf(X,fit$F,numiter = 20))
35-
expect_equal(rep(1,3),unname(diag(cor(out$L,fit$L))),tolerance = 1e-3)
35+
expect_equal(rep(1,3),unname(diag(cor(out$L,fit$L))),scale=1,
36+
tolerance=1e-3)
3637
})

0 commit comments

Comments
 (0)