A tractable, scalable, expression for the Kullback Leibler divergence between two multivariate t distributions

The \(d\)-variate \(t\) probability density function with \(\nu>0\) degrees of freedom (see and for an extensive review of this model) is given by \[ f_d({\bf x}\vert {\mu},{\Sigma},\nu) = \dfrac{\Gamma\left(\dfrac{\nu+d}{2}\right)}{\Gamma\left(\dfrac{\nu}{2}\right)\sqrt{(\pi\nu)^d\vert{\Sigma}\vert}} \left(1+\dfrac{({\bf x}-{\mu})^{T}{\Sigma}^{-1}({\bf x}-{\mu})}{\nu}\right)^{-\frac{\nu+d}{2}}, \]

The Kullback Leibler divergence between two \(d\)-variate \(t\) densities, \(f_{d,\nu}\) and \(f_{d,\nu^{\prime}}\), satisfies \[ D_{KL}(f_{d}(\cdot\vert {\mu},{\Sigma},\nu)\vert\vert f_{d,}(\cdot\vert {\mu},{\Sigma},\nu^{\prime})) = D_{KL}(f_{d}(\cdot\vert {\bf 0},{\bf I},\nu)\vert\vert f_{d}(\cdot\vert {\bf 0},{\bf I},\nu^{\prime})) =\int_{{\mathbb R}^n} f_{d}({\bf x}\vert {\bf 0},{\bf I},\nu) \log \dfrac{f_{d}({\bf x}\vert {\bf 0},{\bf I},\nu)}{f_{d}({\bf x}\vert {\bf 0},{\bf I},\nu^{\prime})} \,d{\bf x} \\ \] [1] found that this Kullback Leibler divergence can be written as \(D_{KL}(f_{d}(\cdot\vert {\mu},{\Sigma},\nu)\vert\vert f_{d,}(\cdot\vert {\mu},{\Sigma},\nu^{\prime})) =\) \[ \log \dfrac{K(d,\nu)}{K(d,\nu^{\prime})} -\dfrac{\nu+d}{2}\left[ \Psi\left(\dfrac{\nu+d}{2}\right) - \Psi\left(\dfrac{\nu}{2}\right) \right] + \dfrac{\nu^{\prime}+d}{2}K(d,\nu) \dfrac{\pi^{\frac{d}{2}}}{\Gamma\left(\frac{d}{2}\right)} \int_0^{\infty} \left(1+\dfrac{t}{\nu}\right)^{-\frac{\nu+d}{2}} t^{\frac{d}{2}-1}\log\left(1+\dfrac{t}{\nu^{\prime}}\right) dt, \] where \[K(d,\nu)=\dfrac{\Gamma\left(\dfrac{\nu+d}{2}\right)}{\Gamma\left(\dfrac{\nu}{2}\right)\sqrt{(\pi\nu)^d}}.\] Thus, this expression only involves a 1-dimensional integral, making the calculation of the Kullback Leibler divergence scalable and tractable for any dimension \(d=1,2,3,\dots\). The following R code shows the implementation of this divergence for several dimensions.

References

  1. Objective priors for the number of degrees of freedom of a multivariate t distribution and the t-copula
rm(list=ls())
# Package required for a nice format of the tables 
library(knitr) 
## Warning: package 'knitr' was built under R version 3.3.3
# Normalising constant
K <- function(d,nu) (gamma(0.5*(nu+d))/( gamma(0.5*nu)*sqrt((pi*nu)^d) ))

# Kullback Liebler divergence
DKL <- function(nu,nup){
val1 <- log(K(d,nu)/K(d,nup)) -0.5*(nu+d)*(digamma(0.5*(nu+d))-digamma(0.5*nu))  
tempf <- Vectorize(function(t) (1+t/nu)^(-0.5*(nu+d))*t^(0.5*d-1)*log(1+t/nup))
  int<- integrate(tempf,0,Inf,rel.tol = 1e-9)$value
 val2 <- (K(d,nu)*pi^(0.5*d)*0.5*(nup+d)/gamma(0.5*d))*int
 return(val1+val2)
}

# Kullback Leibler in one dimension
d=1 # dimension

D <- matrix(0,ncol=3,nrow=29)
D[1,] <- c(1,0,DKL(1,2))
for(i in 2:29) D[i,] = c(i,DKL(i,i-1),DKL(i,i+1))
colnames(D) <- c("nu","DKL(nu,nu-1)","DKL(nu,nu+1)")
rownames(D) <- 1:29
print(kable(D,digits=12))
## 
## 
##  nu   DKL(nu,nu-1)   DKL(nu,nu+1)
## ---  -------------  -------------
##   1   0.000000e+00   1.130965e-01
##   2   6.209980e-02   1.916666e-02
##   3   1.363916e-02   5.897187e-03
##   4   4.700441e-03   2.411720e-03
##   5   2.047079e-03   1.169834e-03
##   6   1.032711e-03   6.364048e-04
##   7   5.768258e-04   3.760650e-04
##   8   3.472837e-04   2.365812e-04
##   9   2.214986e-04   1.563224e-04
##  10   1.478908e-04   1.074607e-04
##  11   1.024925e-04   7.631950e-05
##  12   7.326122e-05   5.570451e-05
##  13   5.375104e-05   4.161431e-05
##  14   4.032643e-05   3.171703e-05
##  15   3.084435e-05   2.459879e-05
##  16   2.399309e-05   1.937253e-05
##  17   1.894314e-05   1.546500e-05
##  18   1.515480e-05   1.249589e-05
##  19   1.226799e-05   1.020705e-05
##  20   1.003705e-05   8.419554e-06
##  21   8.290989e-06   7.007113e-06
##  22   6.908659e-06   5.879059e-06
##  23   5.802797e-06   4.969316e-06
##  24   4.909621e-06   4.229056e-06
##  25   4.181876e-06   3.621740e-06
##  26   3.584116e-06   3.119700e-06
##  27   3.089448e-06   2.701765e-06
##  28   2.677253e-06   2.351571e-06
##  29   2.331567e-06   2.056351e-06
# Kullback Leibler in two dimensions
rm(d)
d=2 # dimension

D <- matrix(0,ncol=3,nrow=29)
D[1,] <- c(1,0,DKL(1,2))
for(i in 2:29) D[i,] = c(i,DKL(i,i-1),DKL(i,i+1))
colnames(D) <- c("nu","DKL(nu,nu-1)","DKL(nu,nu+1)")
rownames(D) <- 1:29
print(kable(D,digits=12))
## 
## 
##  nu   DKL(nu,nu-1)   DKL(nu,nu+1)
## ---  -------------  -------------
##   1   0.000000e+00   1.415927e-01
##   2   7.944154e-02   2.732554e-02
##   3   1.956127e-02   9.139055e-03
##   4   7.282898e-03   3.961126e-03
##   5   3.352862e-03   2.005447e-03
##   6   1.763603e-03   1.127459e-03
##   7   1.017649e-03   6.838292e-04
##   8   6.288552e-04   4.393997e-04
##   9   4.097277e-04   2.954756e-04
##  10   2.784711e-04   2.061445e-04
##  11   1.959103e-04   1.482667e-04
##  12   1.418505e-04   1.094059e-04
##  13   1.052417e-04   8.251520e-05
##  14   7.973102e-05   6.342075e-05
##  15   6.151054e-05   4.955533e-05
##  16   4.821469e-05   3.928773e-05
##  17   3.832778e-05   3.155168e-05
##  18   3.085192e-05   2.563259e-05
##  19   2.511425e-05   2.104082e-05
##  20   2.065128e-05   1.743421e-05
##  21   1.713759e-05   1.456933e-05
##  22   1.434074e-05   1.227021e-05
##  23   1.209212e-05   1.040777e-05
##  24   1.026762e-05   8.886050e-06
##  25   8.774727e-06   7.632841e-06
##  26   7.543654e-06   6.593187e-06
##  27   6.521166e-06   5.724832e-06
##  28   5.666240e-06   4.994967e-06
##  29   4.946970e-06   4.377892e-06
# Kullback Leibler in three dimensions
rm(d)
d=3 # dimension

D <- matrix(0,ncol=3,nrow=29)
D[1,] <- c(1,0,DKL(1,2))
for(i in 2:29) D[i,] = c(i,DKL(i,i-1),DKL(i,i+1))
colnames(D) <- c("nu","DKL(nu,nu-1)","DKL(nu,nu+1)")
rownames(D) <- 1:29
print(kable(D,digits=12))
## 
## 
##  nu   DKL(nu,nu-1)   DKL(nu,nu+1)
## ---  -------------  -------------
##   1   0.000000e+00   1.551520e-01
##   2   8.850926e-02   3.208384e-02
##   3   2.313346e-02   1.129171e-02
##   4   9.021193e-03   5.087129e-03
##   5   4.306589e-03   2.654500e-03
##   6   2.331819e-03   1.528908e-03
##   7   1.377596e-03   9.458675e-04
##   8   8.680423e-04   6.178895e-04
##   9   5.749090e-04   4.213451e-04
##  10   3.962234e-04   2.974995e-04
##  11   2.821191e-04   2.162038e-04
##  12   2.064156e-04   1.609913e-04
##  13   1.545540e-04   1.223979e-04
##  14   1.180430e-04   9.474667e-05
##  15   9.172706e-05   7.450584e-05
##  16   7.236639e-05   5.940842e-05
##  17   5.786327e-05   4.795876e-05
##  18   4.682359e-05   3.914604e-05
##  19   3.829915e-05   3.227232e-05
##  20   3.163169e-05   2.684642e-05
##  21   2.635566e-05   2.251663e-05
##  22   2.213633e-05   1.902718e-05
##  23   1.872936e-05   1.618941e-05
##  24   1.595391e-05   1.386235e-05
##  25   1.367446e-05   1.193942e-05
##  26   1.178826e-05   1.033911e-05
##  27   1.021657e-05   8.998528e-06
##  28   8.898470e-06   7.868622e-06
##  29   7.786376e-06   6.910837e-06
# Kullback Leibler in four dimensions
rm(d)
d=4 # dimension

D <- matrix(0,ncol=3,nrow=29)
D[1,] <- c(1,0,DKL(1,2))
for(i in 2:29) D[i,] = c(i,DKL(i,i-1),DKL(i,i+1))
colnames(D) <- c("nu","DKL(nu,nu-1)","DKL(nu,nu+1)")
rownames(D) <- 1:29
print(kable(D,digits=12))
## 
## 
##  nu   DKL(nu,nu-1)   DKL(nu,nu+1)
## ---  -------------  -------------
##   1   0.000000e+00   1.635764e-01
##   2   9.453489e-02   3.534458e-02
##   3   2.566131e-02   1.288059e-02
##   4   1.032586e-02   5.966897e-03
##   5   5.058505e-03   3.184816e-03
##   6   2.798317e-03   1.868943e-03
##   7   1.683192e-03   1.174484e-03
##   8   1.076901e-03   7.775137e-04
##   9   7.226231e-04   5.362981e-04
##  10   5.036958e-04   3.824496e-04
##  11   3.622049e-04   2.803748e-04
##  12   2.673282e-04   2.103906e-04
##  13   2.017137e-04   1.610568e-04
##  14   1.551273e-04   1.254411e-04
##  15   1.212923e-04   9.919120e-05
##  16   9.622739e-05   7.948973e-05
##  17   7.733310e-05   6.446368e-05
##  18   6.286835e-05   5.283823e-05
##  19   5.164053e-05   4.372748e-05
##  20   4.281619e-05   3.650418e-05
##  21   3.580232e-05   3.071672e-05
##  22   3.017012e-05   2.603498e-05
##  23   2.560495e-05   2.221429e-05
##  24   2.187277e-05   1.907101e-05
##  25   1.879743e-05   1.646569e-05
##  26   1.624477e-05   1.429133e-05
##  27   1.411160e-05   1.246499e-05
##  28   1.231775e-05   1.092180e-05
##  29   1.080038e-05   9.610588e-06
# Kullback Leibler in fifty dimensions
rm(d)
d=50 # dimension

D <- matrix(0,ncol=3,nrow=29)
D[1,] <- c(1,0,DKL(1,2))
for(i in 2:29) D[i,] = c(i,DKL(i,i-1),DKL(i,i+1))
colnames(D) <- c("nu","DKL(nu,nu-1)","DKL(nu,nu+1)")
rownames(D) <- 1:29
print(kable(D,digits=12))
## 
## 
##  nu   DKL(nu,nu-1)   DKL(nu,nu+1)
## ---  -------------  -------------
##   1   0.0000000000   0.2074545331
##   2   0.1255888908   0.0552570704
##   3   0.0414442942   0.0243147651
##   4   0.0199175133   0.0133356785
##   5   0.0114607688   0.0082832698
##   6   0.0073359426   0.0055738121
##   7   0.0050388786   0.0039660586
##   8   0.0036393210   0.0029408104
##   9   0.0027292267   0.0022508204
##  10   0.0021074666   0.0017666052
##  11   0.0016659117   0.0014152297
##  12   0.0013423871   0.0011531719
##  13   0.0010991686   0.0009532154
##  14   0.0009123396   0.0007976672
##  15   0.0007661720   0.0006746422
##  16   0.0006499973   0.0005759313
##  17   0.0005563838   0.0004957252
##  18   0.0004800343   0.0004298258
##  19   0.0004170955   0.0003751422
##  20   0.0003647146   0.0003293608
##  21   0.0003207454   0.0002907246
##  22   0.0002835504   0.0002578810
##  23   0.0002518640   0.0002297764
##  24   0.0002246969   0.0002055813
##  25   0.0002012673   0.0001846359
##  26   0.0001809516   0.0001664106
##  27   0.0001632478   0.0001504769
##  28   0.0001477488   0.0001364853
##  29   0.0001341216   0.0001241487