#### Testing 1) dbinom_raw(), dnbinomR() and dnbinom.mu() #### 2) log1pmx(), logcf() etc require(DPQ) source(system.file(package="DPQ", "test-tools.R", mustWork=TRUE))# ../inst/test-tools.R ## => showProc.time(), ... list_() , loadList() , readRDS_() , save2RDS() (doExtras <- DPQ:::doExtras() && !grepl("valgrind", R.home())) if(!dev.interactive(orNone=TRUE)) pdf("dnbinom-log1pmx.pdf") ### 1. Testing dbinom_raw(), dnbinomR() and dnbinom.mu() >>> ../R/dbinom-nbinom.R <<< ### ---------- ../man/dbinom_raw.Rd & ../man/dnbinomR.Rd ## "FIXME:" use sfsmisc :: relErrV() already here ### dbinom() vs dbinom.raw() : for(n in 1:20) { cat("n=",n," ") for(x in 0:n) cat(".") for(p in c(0, .1, .5, .8, 1)) { stopifnot(all.equal(dbinom_raw(x, n, p, q=1-p, log=FALSE), dbinom (x, n, p, log=FALSE)), all.equal(dbinom_raw(x, n, p, q=1-p, log =TRUE), dbinom (x, n, p, log =TRUE))) } cat("\n") } showProc.time() ### dnbinom*() : stopifnot(exprs = { dnbinomR(0, 1, 1) == 1 }) ### exploring 'eps' == "true" tests must be done with Rmpfr !! ### 2. Testing log1pmx(), logcf() etc ### ---------- ### 2a: logcf() ## == ======= x <- c((-20:3)/4, (25:31)/32) # close (but not too close) to upper bound 1 (lC <- logcf (x, i=2, d=3, eps=1e-9)) lCt <- logcf (x, i=2, d=3, eps=1e-9, trace=TRUE) ; stopifnot(identical(lCt, lC)) (lR <- logcfR(x, i=2, d=3, eps=1e-9)) all.equal(lC, lR, tol = 0) # to see if .. stopifnot(all.equal(lC, lR, tol = 4e-15)) # 1.08295e-15 (Apple clang 14.0.3) lRt <- logcfR(x, i=2, d=3, eps=1e-9, trace=TRUE) ; stopifnot(identical(lRt, lR)) lRt2 <- logcfR(x, i=2, d=3, eps=1e-9, trace= 2) ; stopifnot(identical(lRt2,lR)) lR. <- logcfR.(x, i=2, d=3, eps=1e-9) lR.t <- logcfR.(x, i=2, d=3, eps=1e-9, trace=TRUE) ; stopifnot(identical(lR.t, lR.)) all.equal(lC, lR., tol = 0) # TRUE !! (every where ?) all.equal(lR, lR., tol = 0) # TRUE !! " " stopifnot(all.equal(lC, lR., tol = 1e-14)) ## (even though they used eps=1e-9 .. i.e., are not *so* accurate) showProc.time() ##--- now with improved logcfR.() {<< will become the new logcfR() at least for MPFR !}: ##require(Rmpfr) may be not, see if NS loading (via "::") is sufficient: requireNamespace("Rmpfr") || quit("no") ## ----- ---------- xM <- Rmpfr::mpfr(x, 512) (ct.14 <- system.time(lR.14 <- logcfR.(xM, i=2, d=3, eps=1e-20, trace=TRUE))) # 0.55 sec (ct14 <- system.time(lR14 <- logcfR (xM, i=2, d=3, eps=1e-20, trace=TRUE))) # 4 sec all.equal(lR.14, lR14, tol=0) # TRUE identical(lR.14, lR14) # TRUE !! (not sure if on all platforms!) SS <- function(ch, digits=7) sub(paste0("([0-9]{1,",digits,"})[0-9]*e"), "\\1e", ch) ## double prec <--> MPFR: vvvv (same eps) lR.9 <- logcfR.(xM, 2,3, eps=1e-9) ## show: SS(Rmpfr::all.equal(Rmpfr::roundMpfr(lR.9, 64), lR, tol=0))# .. 5.1138e-16 stopifnot(Rmpfr::all.equal(lR.9, lR , tol=1e-15)) SS(Rmpfr::all.equal(lR.14, lR.9, tol=0))# .. 3.701..e-10 stopifnot(Rmpfr::all.equal(lR.14, lR.9, tol=1e-9)) showProc.time() ### 2b: log1pmx() ## == ========= ## The first part is from original ../man/log1pmx.Rd e <- if(doExtras) 2^-12 else 2^-8; by.p <- 1/(if(doExtras) 256 else 64) xd <- sort(c(seq(-1+e, 0+100*e, by=e), seq(by.p, 5, by=by.p))) # length 676 or 5476 if do.X. plot(xd, log1pmx(xd), type="l", col=2, main = "log1pmx(x)") abline(h=0, v=-1:0, lty=3) xM <- Rmpfr::mpfr(xd, 512) asNumeric <- Rmpfr::asNumeric ## for MPFR numbers, really need more than tol_logcf = eps = 1e-14 (default) if(doExtras) { print( system.time( # the logcfR() completely unvectorized is *slow* lg1pM <- log1pmx(xM, tol_logcf = 1e-25, eps2 = 1e-4, trace=TRUE) )) # 2 sec [new logcfR.()] if(doExtras), otherwise 0.43s, but ??s (!) on winbuilder! lines(xd, asNumeric(lg1pM), col=adjustcolor(4, 1/4), lwd=3) } lg1pdR <- log1pmx(xd, trace=TRUE, logCF=logcfR) lines(xd, lg1pdR, col=adjustcolor(5, 1/4), lwd=5) ## lg1pM. <- log1p(xM) - xM xM2k <- Rmpfr::mpfr(xd, 2048) # even more bits lg1pM2k <- log1p(xM2k) - xM2k relErrV <- sfsmisc::relErrV reE00 <- asNumeric( relErrV(lg1pM2k, lg1pM.) ) print(signif(range(reE00)), 2) # [-1.5e-151, 4.8e-151] ==> 512 bits is sufficient plot(reE00 ~ xd, type="l") plot(abs(reE00) ~ xd, type="l", log="y") plot(reE00 ~ xd, type="l", subset = abs(xd) <= 1/64) plot(abs(reE00) ~ xd, type="l", log="y", subset = abs(xd) <= 1) if(doExtras) { ## the error of the log1pmx() "algorithm" even for "perfect" mpfr-accuracy: rE.log1pm <- asNumeric(relErrV(lg1pM2k, lg1pM)) print(summary( rE.log1pm ) ) # -1.1e-26 7.9e-28 {"matching" tol_logcf = 1e-25 above} } showProc.time() reR <- asNumeric(relErrV(lg1pM., log1pmx(xd))) re <- asNumeric(relErrV(lg1pM., log1pmx(xd))) ## MM: From around here, "move" to vignette <<<<<<<<<<<<<<<<<<<<<<<<< ../vignettes/log1pmx-etc.Rnw <<<<<< plot(xd, re, type="b", cex=1/4, xlab=quote(x), main="relErrV(log1p(xM) - xM, log1pmx(x)), xM <- mpfr(x, 2048)") abline(h = (-2:2)*2^-52, lty=2, col=adjustcolor("gray20", 1/2)) xl <- -0.84; xr <- -0.4 ## the "relevant x-range": colR <- adjustcolor("tomato", 1/2) abline(v= c(xl,xr), col=colR, lty=2, lwd=2) axis(1,at=c(xl,xr), labels=NULL, col.axis="tomato", col=colR, lwd=2, cex.axis=3/4) text(xl, adj=c(0,-1/2), par("usr")[3], "zoom in", col=colR) showProc.time() # only "relevant" x-range: values inside (xl, xr) iN <- xl < xd & xd < xr plot(xd[iN], re[iN], type="b", cex=1/4, main="rel.Error of log1pmx(x)", xlab=quote(x), ylab=quote(rE(x)), sub = "(in relevant x-range)") abline(h = (-2:2)*2^-52, lty=2, col=adjustcolor("gray30", 1/2)) abline(v = c(xl,xr), col=colR, lty=2, lwd=2) plot(xd[iN], abs(re[iN]), type="b", cex=1/2, log="y", main = "| relErr( log1pmx(x) ) | {via 'Rmpfr'}", ylab=quote(abs(rE(x))), xlab=quote(x), ylim = c(4e-17, max(abs(asNumeric(re)[iN])))) abline(h = c(1:2,4)*2^-52, lty=2, col=adjustcolor("gray20", 1/2)) mL1 <- eval(formals(log1pmx)$minL1) abline(v = mL1, lwd=3, col=adjustcolor(2, 1/2)) axis(3, at=mL1, "minL1", col.axis=2, col=2) mL1 <- -0.7; re2 <- asNumeric(relErrV(lg1pM.[iN], log1pmx(xd[iN], minL1 = mL1))) lines(xd[iN], abs(re2), col=adjustcolor(4, 1/2), lwd=2) abline(v = mL1, lwd=3, col=adjustcolor(4, 2/3), lty=3) axis(3, line=-1, at=mL1, sprintf("mL1 = %g",mL1), col.axis=4, col=4) R <- xd[iN] >= mL1; stopifnot(re2[R] == re[iN][R]) mL1 <- -0.66; re3 <- asNumeric(relErrV(lg1pM.[iN], log1pmx(xd[iN], minL1 = mL1))) re3.2 <- asNumeric(relErrV(lg1pM.[iN], log1pmx(xd[iN], minL1 = mL1, eps2 = .02))) print(max(abs(re3))) == # 4.818203e-16 max(abs(re3.2)) # TRUE ! of course, eps2 does not matter as long as it is far from our x-range !! xd[iN][which.max(abs(re3))] # -0.5710449 lines(xd[iN], abs(re3), col=adjustcolor(6, 1/3), lwd=2) abline(v = mL1, lwd=3, col=adjustcolor(6, 2/3), lty=3) axis(3, line=-2, at=mL1, sprintf("mL1 = %g",mL1), col.axis=6, col=6) lines(lowess(xd[iN], abs(re[iN]), f=1/50), col=adjustcolor("gray", 1/2), lwd=6) lines(lowess(xd[iN], abs(re2), f=1/50), col=adjustcolor(4, 1/2), lwd=6) lines(lowess(xd[iN], abs(re3), f=1/50), col=adjustcolor(6, 1/2), lwd=6) R <- xd[iN] > mL1; stopifnot(re3[R] == re2[R]) ## MM: for vignette, stop here? ## ---> since max(|re4|) {below} == max(|re3|) mL1 <- -0.64; re4 <- asNumeric(relErrV(lg1pM.[iN], log1pmx(xd[iN], minL1 = mL1))) max(abs(re4)) == max(abs(re3)) # TRUE ! lines(xd[iN], abs(re4), col=adjustcolor(7, 1/3), lwd=2) abline(v = mL1, lwd=3, col=adjustcolor(7, 2/3), lty=3) axis(3, line=-2, at=mL1, sprintf("mL1 = %g",mL1), col.axis=7, col=7) lines(lowess(xd[iN], abs(re[iN]), f=1/50), col=adjustcolor("gray", 1/2), lwd=6) lines(lowess(xd[iN], abs(re2), f=1/50), col=adjustcolor(4, 1/2), lwd=6) lines(lowess(xd[iN], abs(re3), f=1/50), col=adjustcolor(6, 1/2), lwd=6) lines(lowess(xd[iN], abs(re4), f=1/50), col=adjustcolor(7, 1/2), lwd=6) R <- xd[iN] > mL1; stopifnot(re4[R] == re3[R]) re5 <- asNumeric(relErrV(lg1pM.[iN], log1pmx(xd[iN], minL1 = -0.6))) max(abs(re5)) == max(abs(re3)) # TRUE !.. still not better lines(xd[iN], abs(re5), col=adjustcolor(8, 1/3), lwd=2) abline(v = -0.6, lwd=3, col=adjustcolor(8, 2/3), lty=3) axis(3, line=-1, at=-0.6, sprintf("mL1 = %g",mL1), col.axis=8, col=8) lines(lowess(xd[iN], abs(re[iN]), f=1/50), col=adjustcolor("gray", 1/2), lwd=6) lines(lowess(xd[iN], abs(re2), f=1/50), col=adjustcolor(4, 1/2), lwd=6) lines(lowess(xd[iN], abs(re3), f=1/50), col=adjustcolor(6, 1/2), lwd=6) lines(lowess(xd[iN], abs(re4), f=1/50), col=adjustcolor(7, 1/2), lwd=6) lines(lowess(xd[iN], abs(re5), f=1/50), col=adjustcolor(8, 1/2), lwd=6) R <- xd[iN] > -0.6; stopifnot(re5[R] == re4[R]) ## -0.6 now is clearly too large, -0.7 was better {MM: why the showProc.time() ##--- separate: is eps2 = 0.01 "optimal" / "good enough" ?? k. <- if(doExtras) 3 else 0 str(xS <- unique(sort(c(seq(-1/32, 1/32, by=2^-(12+k.)), seq(-1,1, by=2^-(7+k.))/2^(4+k.))))) ## 257 or 3841 values plot(xS, log1pmx(xS), type="l", col=2, main = "log1pmx(x)") abline(h=0, v = c(-.01, .01), lwd=3, col=adjustcolor(7, 2/3), lty=3) ## nothing visible at +- .01 xSm <- Rmpfr::mpfr(xS, 1024) xSM <- Rmpfr::mpfr(xS, 8192) lg1pM. <- log1p(xSm) - xSm ## the direct formally using many bits ==> not much cancellation summary(abs(asNumeric(relErrV(log1p(xSM) - xSM, lg1pM.))))# <= 4.1e-304 : 1024 is enough! reS <- asNumeric(relErrV(lg1pM., log1pmx(xS))) summary(reS) ## Lnx 64b gcc 11.2.1, 20210728: ## Min. 1st Qu. Median Mean 3rd Qu. Max. ## -2.625e-16 -5.246e-17 1.973e-18 1.211e-18 5.286e-17 2.688e-16 plot(xS, abs(reS), type="b", cex=1/2, log="y", main = "| relErr( log1pmx() ) | {via 'Rmpfr'}" , ylim = c(4e-17, max(abs(asNumeric(reS)))) , panel.last= abline(h=0, v = c(-.01, .01), lwd=3, col=adjustcolor(7, 2/3), lty=3)) ## no problem visible at +/- eps2 = +/- 0.01 stopifnot(abs(reS) < 1e-15, mean(abs(reS)) < 6e-16) # seen 2.688e-16, 6.35e-17 showProc.time() ## in spite of the above; ... what's the approximation error of accurate log1pmx() ? p.Err <- function(eps2 = .01, tol_logcf = 1e-17, xM=xSm) { stopifnot(eps2 > 0, tol_logcf > 0, inherits(xM, "mpfr"), (prec <- Rmpfr::getPrec(xM)) >= 64) lg1pM <- log1p(xM) - xM # direct formula suffering from cancellation lg1pM.<- log1pmx(xM, tol_logcf=tol_logcf, eps2=eps2) sys.call() reM <- asNumeric(relErrV(lg1pM., lg1pM)) plot(asNumeric(xM), abs(reM), type="b", cex=1/2, log="y", xlab = quote(x), ylim = quantile(abs(reM), c(1/20,1), names=FALSE), sub = paste("min(getPrec(x)) =", min(prec)), main = sprintf("relative error of R log1pmx(eps2=%g, tol_logcf=%g)", eps2, tol_logcf)) abline(h=0, v = eps2*c(-1,1), lwd=3, col=adjustcolor(7, 2/3), lty=3) invisible(reM) } ### --- for tol_logcf = 1e-17 --------------------------------------------- reSM <- p.Err() # aha! Here, eps2 = .01 was definitely too large! reSM001 <- p.Err(.001) # still too large xM2 <- Rmpfr::mpfr(seq(-1,1, length=1+1024)/2^8, 1024) reSM3_4 <- p.Err(3e-4, xM=xM2)# still too large reSM294 <- p.Err(2.9e-4, xM=xM2)# still reSM2854<- p.Err(2.85e-4, xM=xM2)# still (and practically identical to 2.9e-4) ## cbind(reSM294, reSM2854)[reSM294 != reSM2854 , ] ## reSM294 reSM2854 ## [1,] 2.572818e-36 2.988004e-46 ## [2,] -2.565617e-36 -2.977915e-46 reSM284 <- p.Err(2.8e-4, xM=xM2)# now too small -- bad! reSM274 <- p.Err(2.7e-4, xM=xM2)# (ditto) too small showProc.time() ### --- for tol_logcf = 1e-14 == R's default! reSM <- p.Err( tol = 1e-14) ## eps2=.01 is *still* too large reSM002 <- p.Err(.002, tol = 1e-14) # still too large reSM0017 <- p.Err(.0017, tol = 1e-14) # still .. reSM00165<- p.Err(.00165,tol = 1e-14) # still.. reSM00163<- p.Err(.00163,tol = 1e-14) # still.. <<<<<<<<<<<<< SHOULD WE CHANGE R TO USE THIS? reSM00162<- p.Err(.00162,tol = 1e-14) # already (very barely!) too small reSM0016 <- p.Err(.0016, tol = 1e-14) # already (barely!) too small showProc.time() summary(warnings())