## Copyright (C) 2010 Marius Hofert and Martin Maechler ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ## FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . source(system.file("Rsource", "utils.R", package="nacopula")) ##--> tryCatch.W.E(), canGet() stopifnot(require(Runuran), require(optimx)) ## ==== GIG generator and related functions ==================================== ## generator (parameters theta_1 in IR, theta_2 in (0,Inf)) ## note: theta_1!=0 and theta_2=0 reduces to copClayton@psi(t, 1/theta_1) psi.GIG <- function(t, theta) (1+t)^(-theta[1]/2)*besselK(theta[2]*sqrt(1+t),nu=theta[1])/ besselK(theta[2],nu=theta[1]) ## generator inverse ## note: the initial interval is only valid for theta_1 > -1/2 ## it can be large, but it's finite; it is based on an inequality about ## modified Bessel functions of the third kind (besselK) given in ## Paris (1984) ("An inequality for the Bessel function J_v(vx)", ## SIAM J. Math. Anal. 15, 203--205) psiInv.GIG <- function(t, theta, upper=NULL, ...){ if(is.null(upper)){ if(theta[1] > -0.5) upper <- function(x) (1-log(x)/theta[2])^2-1 else stop("initial interval for psiInv.GIG fails") } res <- numeric(length(t)) is0 <- t==0 is1 <- t==1 n01 <- !(is0 | is1) res[is0] <- Inf res[is1] <- 0 t. <- t[n01] up <- upper(t.) res[n01] <- unlist(lapply(seq_along(t.), function(i){ uniroot(function(t..) psi.GIG(t.., theta)-t.[i], interval=c(0, up[i]), ...)$root })) if(is.matrix(t)) matrix(res, ncol=ncol(t)) else res } ## generator derivatives psiDabs.GIG <- function(t, theta, degree=1, n.MC=0, log=FALSE){ res <- numeric(length(t)) iInf <- is.infinite(t) res[iInf] <- -Inf # log(0) if(any(!iInf)){ t. <- t[!iInf] if(n.MC>0){ V <- V0.GIG(n.MC, theta) res[!iInf] <- nacopula:::lsum(-V %*% t(t.) + degree*log(V) - log(n.MC)) }else{ res[!iInf] <- degree*log(theta[2]/2)-((theta[1]+degree)/2)*log1p(t.) + log(besselK(theta[2]*sqrt(1+t.), nu=theta[1]+degree, expon.scaled=TRUE)) - log(besselK(theta[2], nu=theta[1], expon.scaled=TRUE)) - (sqrt(1+t.)-1)*theta[2] } } r <- if(log) res else exp(res) if(is.matrix(t)) matrix(r, ncol=ncol(t)) else r } ## derivative of the generator inverse psiInvD1abs.GIG <- function(t, theta, log=FALSE){ res <- -psiDabs.GIG(psiInv.GIG(t, theta=theta), theta=theta, log=TRUE) if(log) res else exp(res) } ## density of the GIG copula dacopula.GIG <- function(u, theta, n.MC=0, log=FALSE){ if(!is.matrix(u)) u <- rbind(u) if((d <- ncol(u)) < 2) stop("u should be at least bivariate") # check that d >= 2 ## f() := NaN outside and on the boundary of the unit hypercube res <- rep.int(NaN, n <- nrow(u)) n01 <- apply(u,1,function(x) all(0 < x, x < 1)) # indices for which density has to be evaluated if(!any(n01)) return(res) ## auxiliary results u. <- u[n01,, drop=FALSE] psiI <- psiInv.GIG(u., theta=theta) # this is costly if d is large psiI.sum <- rowSums(psiI) ## main part if(n.MC > 0){ # Monte Carlo res[n01] <- psiDabs.GIG(psiI.sum, theta=theta, degree=d, n.MC=n.MC, log=TRUE) - rowSums(psiDabs.GIG(psiI, theta=theta, log=TRUE)) }else{ # explicit ## auxiliary function for density evaluation h <- function(t, k, theta, log=FALSE){ s1pt <- sqrt(1+t) B <- besselK(theta[2]*s1pt, nu=theta[1]+k) if(log) log(B)-(theta[1]+k)*log(s1pt) else B/s1pt^(theta[1]+k) } ## result res[n01] <- h(psiI.sum, k=d, theta=theta, log=TRUE) + (d-1)* log(besselK(theta[2], nu=theta[1])) - rowSums(h(psiI, k=1, theta=theta, log=TRUE)) } if(log) res else exp(res) } ## V0 random number generator V0.GIG <- function(n, theta){ dens <- udgig(theta[1], 1, theta[2]^2) # the three args lambda, psi, chi for the GIG density as on page 497 in McNeil, Frey, Embrechts (2005) gen <- pinvd.new(dens) # works fast, via approximation of the quantile function by piecewise polynomials ur(gen, n)/2 } ## density of V0 dV0.GIG <- function(x, theta, log=FALSE){ dens <- udgig(theta[1], 1, theta[2]^2) if(log) log(2*ud(dens, 2*x)) else 2*ud(dens, 2*x) } ## Kendall's tau for fixed theta_1 as a function in theta_2 ## quiet==TRUE means that neither warnings nor errors are returned, just NAs tau.GIG. <- function(theta, upper=c(200,200), quiet=TRUE, ...){ if(theta[1]<0) stop("tau.GIG.: only implemented for nu >= 0") stopifnot(theta[1]<=upper[1], 0= 0") tau.max <- 1/(1+2*theta[1]) ## make sure we are in the range of attainable Kendall's tau if(tau>=tau.max) stop(paste("tauInv.GIG: the supremum of attainable taus is", round(tau.max,8))) # this assumes tau to be falling in theta (numerical experiments show this behavior) interval <- c(1e-100, upper[2]) } replace(theta, i, uniroot(function(th) do.call(tau.GIG, c(list(replace(theta, i, th), upper=upper, quiet=quiet), iargs))-tau, interval=interval, ...)$root) } ## generate vectors of random variates from a GIG copula rnacopula.GIG <- function(n, d, theta) psi.GIG(matrix(rexp(n*d), nrow=n, ncol=d)/V0.GIG(n, theta), theta)