require(robustlmm) require(robustbase) setClass("psi_func_cached", contains = c("psi_func")) psiFuncCached <- function(rho,psi,wgt,Dwgt,Dpsi,name=NULL, ...) { lent <- length(dotsargs <- list(...)) ## '...' must contain all tuning parameters and their defaults: stopifnot(length(nt <- names(dotsargs)) == lent, all(nchar(nt)) >= 1) if(lent >= 1) { ## rho, psi,... checking: must have argument names argn <- c("x", nt) for(fnam in list("rho", "psi", "wgt", "Dwgt", "Dpsi")) { f <- get(fnam, inherits = FALSE) ef <- environment(f) nf <- names(ff <- formals(f)) # "x" and "k" for Huber's if(!identical(nf, argn)) stop("arguments of function '",fnam,"' are (", paste(nf, collapse=","),") but should be (", paste(argn,collapse=","),").") formals(f)[-1] <- dotsargs environment(f) <- ef assign(fnam, f, inherits = FALSE) } } Erho.val <- integrate(function(x) rho(x)*dnorm(x),-Inf, Inf, rel.tol = .Machine$double.eps^0.5)$value Epsi2.val <- integrate(function(x) psi(x)^2*dnorm(x),-Inf, Inf, rel.tol = .Machine$double.eps^0.5)$value EDpsi.val <- integrate(function(x) Dpsi(x)*dnorm(x),-Inf, Inf, rel.tol = .Machine$double.eps^0.5)$value new("psi_func_cached", rho = new("functionX", rho), psi = new("functionX", psi), wgt = new("functionX", wgt), Dpsi= new("functionX", Dpsi), Dwgt= new("functionX", Dwgt), ## tNams = if(lent) nt else character(0), tDefs = if(lent) unlist(dotsargs) else numeric(0), Erho= Erho <- new("functionXal", function(arg=1) rep(Erho.val, length(arg))), Epsi2= Epsi2 <- new("functionXal", function(arg=1) rep(Epsi2.val, length(arg))), EDpsi= EDpsi <- new("functionXal", function(arg=1) rep(EDpsi.val, length(arg))), name= name ) } F0 <- function(x=1, .) rep.int(0, length(x)) F1 <- function(x=1, .) rep.int(1, length(x)) cPsi2 <- psiFuncCached(rho = function(x, .) x^2 / 2, psi = function(x, .) x, wgt = F1, Dwgt = F0, Dpsi = F1, name = "classic (x^2/2)", . = Inf ## dummy, need at least one parameter ) stopifnot(all.equal(cPsi@Erho(), cPsi2@Erho()), all.equal(cPsi@Epsi2(), cPsi2@Epsi2()), all.equal(cPsi@EDpsi(), cPsi2@EDpsi())) smoothPsiOld <- psiFuncCached(rho = function(x, k, s) { a <- s^(1/(s+1)) c <- k - a^(-s) d <- c - a ax <- abs(x) ifelse(ax <= c, x^2/2, c^2/2 + k*(ax-c) - ((ax-d)^(1-s) - a^(1-s))/(1-s)) }, psi = function(x, k, s) { a <- s^(1/(s+1)) c <- k - a^(-s) d <- c - a ax <- abs(x) ifelse(ax <= c, x, sign(x)*(k - (ax-d)^(-s))) }, Dpsi = function(x, k, s) { a <- s^(1/(s+1)) c <- k - a^(-s) d <- c - a ax <- abs(x) ifelse(ax <= c, 1, s*(ax-d)^(-s-1)) }, wgt = function(x, k, s) { a <- s^(1/(s+1)) c <- k - a^(-s) d <- c - a ax <- abs(x) ifelse(ax <= c, 1, (k - (ax-d)^(-s))/ax) }, Dwgt = function(x, k, s) { a <- s^(1/(s+1)) c <- k - a^(-s) d <- c - a ax <- abs(x) ifelse(ax <= c, 0, (ax - d)^(-s-1)*s/x - (k - (ax-d)^(-s))/(x*ax)) }, k = 1.345, s = 10, name = "smoothed Huber") chgDefaults.old <- function(object, ...) { ##cat("~~~~ chgDefaults of psi_func_cached ~~~~~\n") lent <- length(dotsargs <- list(...)) ## '...' must contain all tuning parameters and their defaults: stopifnot(length(nt <- names(dotsargs)) == lent, all(nchar(nt)) >= 1) if(lent >= 1) { ## rho "..." must conform to rho, etc: nf <- names(ff <- formals(object@rho)) if(!identical(nf[-1], nt)) stop("invalid tuning parameter names: ", paste(nt, collapse=",")," instead of ", paste(nf[-1],collapse=","),".") for(fnam in list("rho", "psi", "wgt", "Dwgt", "Dpsi")) { f <- slot(object, fnam) ef <- environment(f) formals(f)[-1] <- dotsargs environment(f) <- ef ## lowlevel {faster than}: slot(..) <- new("functionX", f) slot(object, fnam)@.Data <- f } object@tDefs <- unlist(dotsargs) } Erho.val <- integrate(function(x) object@rho(x)*dnorm(x),-Inf, Inf, rel.tol = .Machine$double.eps^0.5)$value Epsi2.val <- integrate(function(x) object@psi(x)^2*dnorm(x),-Inf, Inf, rel.tol = .Machine$double.eps^0.5)$value EDpsi.val <- integrate(function(x) object@Dpsi(x)*dnorm(x),-Inf, Inf, rel.tol = .Machine$double.eps^0.5)$value object@Erho <- new("functionXal", function(arg=1) rep(Erho.val, length(arg))) object@Epsi2 <- new("functionXal", function(arg=1) rep(Epsi2.val, length(arg))) object@EDpsi <- new("functionXal", function(arg=1) rep(EDpsi.val, length(arg))) object } x <- seq(-10, 10, length.out = 1001) stopifnot(all.equal(smoothPsi@wgt(x), smoothPsiOld@wgt(x)), all.equal(smoothPsi@Dwgt(x), smoothPsiOld@Dwgt(x)), all.equal(smoothPsi@psi(x), smoothPsiOld@psi(x)), all.equal(smoothPsi@Dpsi(x), smoothPsiOld@Dpsi(x)), all.equal(smoothPsi@rho(x), smoothPsiOld@rho(x)), all.equal(smoothPsi@Erho(), smoothPsiOld@Erho()), all.equal(smoothPsi@Epsi2(), smoothPsiOld@Epsi2()), all.equal(smoothPsi@EDpsi(), smoothPsiOld@EDpsi())) sP <- chgDefaults(smoothPsi, k = 2.0, s = 9.0) sPOld <- chgDefaults.old(smoothPsiOld, k = 2.0, s = 9.0) stopifnot(all.equal(sP@wgt(x), sPOld@wgt(x)), all.equal(sP@Dwgt(x), sPOld@Dwgt(x)), all.equal(sP@psi(x), sPOld@psi(x)), all.equal(sP@Dpsi(x), sPOld@Dpsi(x)), all.equal(sP@rho(x), sPOld@rho(x)), all.equal(sP@Erho(), sPOld@Erho()), all.equal(sP@Epsi2(), sPOld@Epsi2()), all.equal(sP@EDpsi(), sPOld@EDpsi())) stopifnot(all.equal(huberPsiRcpp@wgt(x), robustbase::huberPsi@wgt(x)), all.equal(huberPsiRcpp@Dwgt(x), robustbase::huberPsi@Dwgt(x)), all.equal(huberPsiRcpp@psi(x), robustbase::huberPsi@psi(x)), all.equal(huberPsiRcpp@Dpsi(x), robustbase::huberPsi@Dpsi(x) + 0), all.equal(huberPsiRcpp@rho(x), robustbase::huberPsi@rho(x)), all.equal(huberPsiRcpp@Erho(), robustbase::huberPsi@Erho()), all.equal(huberPsiRcpp@Epsi2(), robustbase::huberPsi@Epsi2()), all.equal(huberPsiRcpp@EDpsi(), robustbase::huberPsi@EDpsi())) .psi2propII <- function(object, ...) { ## do not do anything for cPsi if (identical(object, cPsi)) return(object) ## Convert a regular psi-function into a Proposal 2 psi function ## (with squared weights) f <- formals(object@psi) nf <- names(f) args <- paste(nf, collapse=",") x <- nf[1] ## wgt fun <- paste("function(",args,") object@wgt(", args, ")^2") wgt <- eval(parse(text=fun)) formals(wgt) <- f ## Dwgt fun <- paste("function(",args,") 2*object@wgt(", args, ")*object@Dwgt(",args,")") Dwgt <- eval(parse(text=fun)) formals(Dwgt) <- f ## psi fun <- paste("function(",args,") object@wgt(", args, ")*object@psi(",args,")") psi <- eval(parse(text=fun)) formals(psi) <- f ## Dpsi fun <- paste("function(",args,") object@wgt(", args, ")*object@Dpsi(",args, ") + object@Dwgt(", args, ")*object@psi(",args,")") Dpsi <- eval(parse(text=fun)) formals(Dpsi) <- f ## rho intRho <- function(psi, x, ...) { ret <- x for (i in seq_along(x)) { if (is.infinite(x[i])) next ret[i] <- integrate(psi, 0, x[i], ..., rel.tol = .Machine$double.eps^0.5)$value } ret } fun <- paste("function(",args,") intRho(psi,",args,")") rho <- eval(parse(text=fun)) formals(rho) <- f ret <- do.call(psiFuncCached, c(list(wgt=wgt, Dwgt=Dwgt, psi=psi, Dpsi=Dpsi, rho=rho), f[-1], name=paste(object@name, ", Proposal 2", sep=""))) ## if ... is given: pass it to chgDefaults chgArgs <- list(...) if (length(chgArgs) > 0) { if (is.null(names(chgArgs))) stop("Extra arguments in ... need to be named") ## extend list, all arguments need to be passed to chgDefaults for (name in names(ret@tDefs)) if (is.null(chgArgs[[name]])) chgArgs[[name]] <- ret@tDefs[[name]] ret <- do.call("chgDefaults", c(list(ret), chgArgs)) } return(ret) } sP2 <- psi2propII(smoothPsi) sPOld2 <- .psi2propII(smoothPsiOld) stopifnot(all.equal(sP2@wgt(x), sPOld2@wgt(x)), all.equal(sP2@Dwgt(x), sPOld2@Dwgt(x)), all.equal(sP2@psi(x), sPOld2@psi(x)), all.equal(sP2@Dpsi(x), sPOld2@Dpsi(x)), all.equal(sP2@rho(x), sPOld2@rho(x)), all.equal(sP2@Erho(), sPOld2@Erho()), all.equal(sP2@Epsi2(), sPOld2@Epsi2()), all.equal(sP2@EDpsi(), sPOld2@EDpsi())) sP <- chgDefaults(smoothPsi, k = 2.0, s = 9.0) sPOld <- chgDefaults.old(smoothPsiOld, k = 2.0, s = 9.0) sP2 <- psi2propII(sP) sPOld2 <- .psi2propII(sPOld) stopifnot(all.equal(sP2@wgt(x), sPOld2@wgt(x)), all.equal(sP2@Dwgt(x), sPOld2@Dwgt(x)), all.equal(sP2@psi(x), sPOld2@psi(x)), all.equal(sP2@Dpsi(x), sPOld2@Dpsi(x)), all.equal(sP2@rho(x), sPOld2@rho(x)), all.equal(sP2@Erho(), sPOld2@Erho()), all.equal(sP2@Epsi2(), sPOld2@Epsi2()), all.equal(sP2@EDpsi(), sPOld2@EDpsi())) test1 <- psi2propII(smoothPsi, k = 2.2) test2 <- psi2propII(chgDefaults(smoothPsi, k = 2.2)) test3 <- chgDefaults(psi2propII(smoothPsi), k = 2.2) stopifnot(all.equal(test1@psi(x), test2@psi(x)), all.equal(test2@psi(x), test3@psi(x))) ## test changing of constants is not kept for next call stopifnot(all.equal(sP@wgt(x, k = 1.0), sPOld@wgt(x, k = 1.0)), all.equal(sP2@wgt(x, k = 1.0), sPOld2@wgt(x, k = 1.0)), all.equal(huberPsiRcpp@psi(x, k = 1.0), robustbase::huberPsi@psi(x, k = 1.0))) stopifnot(all.equal(sP@wgt(x), sPOld@wgt(x)), all.equal(sP2@wgt(x), sPOld2@wgt(x)), all.equal(huberPsiRcpp@psi(x), robustbase::huberPsi@psi(x))) # test chgDefaults with different order of arguments stopifnot(all.equal(sP@psi(x, s = 8., k = 1.2), sP@psi(x, k = 1.2, s = 8.)), all.equal(sP2@psi(x, s = 8., k = 1.2), sP2@psi(x, k = 1.2, s = 8.))) sPc <- chgDefaults(sP, s = 8., k = 1.2) sP2c <- chgDefaults(sP2, s = 8., k = 1.2) stopifnot(all.equal(sPc@psi(x), sP@psi(x, k = 1.2, s = 8.)), all.equal(sP2c@psi(x), sP2@psi(x, k = 1.2, s = 8.))) # test handling of missing arguments stopifnot(all.equal(sPc@psi(x, s = 9.), sPc@psi(x, k = 1.2, s = 9.)), all.equal(sP2c@psi(x, s = 9.), sP2c@psi(x, k = 1.2, s = 9.))) sPc <- chgDefaults(sP, k = 1.2) sP2c <- chgDefaults(sP2, k = 1.2) stopifnot(all.equal(sPc@psi(x), sP@psi(x, k = 1.2, s = 9.)), all.equal(sP2c@psi(x), sP2@psi(x, k = 1.2, s = 9.))) ## test chgDefaults croaks on additional arguments, too many arguments stopifnot(inherits(try(chgDefaults(sP, k = 1.2, h = 1.3, s = 10.), silent = TRUE), "try-error"), inherits(try(chgDefaults(sP, 1.2, 1.3, 10.), silent = TRUE), "try-error"), inherits(try(chgDefaults(sP, 1.2, s = 10.), silent = TRUE), "try-error"), inherits(try(chgDefaults(sP2, k = 1.2, h = 1.3, s = 10.), silent = TRUE), "try-error")) ## test unnamed arguments work as expected sPc <- chgDefaults(sP, 1.2, 8.) sPcc <- chgDefaults(sPc, 1.3) stopifnot(all.equal(sPc@psi(x), sP@psi(x, k = 1.2, s = 8.)), all.equal(sPcc@psi(x), sP@psi(x, k = 1.3, s = 8.))) ## getInstanceWithOriginalDefaults gets the right defaults stopifnot(all.equal(smoothPsi@getInstanceWithOriginalDefaults()$tDefs(), smoothPsi@tDefs), all.equal(sP2@getInstanceWithOriginalDefaults()$tDefs(), sP2@tDefs), all.equal(sPc@getInstanceWithOriginalDefaults()$tDefs(), sPc@tDefs), all.equal(sP2c@getInstanceWithOriginalDefaults()$tDefs(), sP2c@tDefs)) ## test psi2propII only works once stopifnot(inherits(try(psi2propII(sP2c), silent = TRUE), "try-error")) ## test saving and loading expected <- chgDefaults(smoothPsi, k = 1.0, s = 12) actual <- chgDefaults(smoothPsi, k = 1.0, s = 12) tfile <- tempfile() save(actual, file = tfile) load(tfile) stopifnot(all.equal(expected@EDpsi(), actual@EDpsi())) unlink(tfile)