I’m trying the perform a numerical integration over 4-dimensions. Here is a self-contained example:
import HCubature
using StaticArrays: SMatrix
using StaticArrays: SVector
using LinearAlgebra: Symmetric
using LinearAlgebra: issymmetric
using LogExpFunctions: logcosh
@kwdef struct QuadOpts
rtol::Float64 = sqrt(eps())
atol::Float64 = 0.0
maxevals::Int = typemax(Int)
initdiv::Int = 1
end
function mvnormal_expect(logf::Function, g::Function, Σ::AbstractMatrix, quad::QuadOpts = QuadOpts())
μ = zeros(eltype(Σ), size(Σ, 1))
return mvnormal_expect(logf, g, μ, Σ, quad)
end
function mvnormal_expect(logf::Function, g::Function, μ::AbstractVector, Σ::AbstractMatrix, quad::QuadOpts = QuadOpts())
@assert size(Σ, 1) == size(Σ, 2) == length(μ)
@assert issymmetric(Σ)
N = length(μ)
U = oftype(Σ, sqrt(Symmetric(Matrix(Σ))))
t_lb = fill(-1, N)
t_ub = fill(+1, N)
log_normalization = N/2 * log(2π) # Includes Jacobian of z = Σ * x
cubature_result = HCubature.hcubature(t_lb, t_ub; quad.rtol, quad.atol, quad.maxevals, quad.initdiv) do t
t::Union{Real, AbstractVector}
# change of variables from t ∈ (-1,1) to x ∈ (-Inf, +Inf)
z = t ./ (1 .- t.^2)
jacobian = prod(@. (1 + t^2) / (1 - t^2)^2)
# log density of the multivariate normal
log_pdf = -z' * z / 2 - log_normalization
# Evaluate target functions
x = U' * z + μ
g_result = g(x)::Union{Real,AbstractArray}
log_f_result = logf(x)::Real
return g_result * exp(log_f_result + log_pdf) * jacobian
end
return first(cubature_result)
end
function integrand_logf(z::AbstractVector)
η, ζ = z[SVector(1,2)], z[SVector(3,4)]
return 10 * (sum(logcosh, η) - sum(logcosh, ζ))
end
function integrand_g(z::AbstractVector)
t = tanh.(z)
return t * t'
end
Σ = SMatrix{4,4}([0.0625 0.03125 0.04340661301312787 0.022453469438932686; 0.03125 0.0625 0.02081381050160219 0.043421385231874735; 0.04340661301312787 0.02081381050160219 0.05663074530536689 0.028551532538083665; 0.022453469438932686 0.043421385231874735 0.028551532538083665 0.055918260634255944]);
@time result_h = mvnormal_expect(integrand_logf, integrand_g, Symmetric(Σ))
However this calculation is very slow. On my machine it takes about 1 minute to complete. I need to compute such an integration many times, so it would be better if it were faster.
Any suggestions on how I can speed things up? Perhaps HCubature is not the best algorithm to try here.