library(ggplot2)
library(ggquiver)
library(mvtnorm)
set.seed(12345)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Funciones auxiliares
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<- function(x, y, rho) {
dlogpdx - (x - rho * y) / (1 - rho ^ 2)
}
<- function(x, y, rho) {
dlogpdy - (y - rho * x) / (1 - rho ^ 2)
}
<- function(q, rho) {
dlogp <- q[1]
x <- q[2]
y c(dlogpdx(x, y, rho), dlogpdy(x, y, rho))
}
<- function(rho) {
make_neg_dlogp function(q) -dlogp(q, rho)
}
<- function(rho) {
make_neg_logp <- rep(0, 2)
Mu <- diag(2)
Sigma == 0] <- rho
Sigma[Sigma function(q) -dmvnorm(q, Mu, Sigma, log = TRUE)
}
<- function(p, q, neg_dlogp, path_length, step_size) {
leapfrog <- list()
leap_q <- list()
leap_p <- p - step_size * neg_dlogp(q) / 2
p for (i in seq_len(round(path_length / step_size) - 1)) {
<- q + step_size * p
q <- p - step_size * neg_dlogp(q)
p <- q
leap_q[[i]] <- p
leap_p[[i]]
}<- q + step_size * p
q <- p - step_size * neg_dlogp(q) / 2
p
# Flip del momentum
return(list(q = q, p = -p, leap_q = leap_q, leap_p = leap_p))
}
<- function(l, object) {
lappend length(l) + 1]] <- object
l[[
l
}
<- function(l) {
ltail length(l)]]
l[[
}
<- function(
hmc
neg_logp,
neg_dlogp,
n_samples,
initial_position, path_length = 1,
step_size = 0.01
) {
<- list()
leap_p <- list()
leap_q <- list(initial_position)
samples <- length(initial_position)
n_dimensions
# Se generan momentums a partir de una MVN(0, 1)
# Es de dimension (n_samples, n_dimensions)
<- rmvnorm(n_samples, rep(0, n_dimensions), diag(n_dimensions))
momentum
for (i in seq_len(n_samples)) {
# Obtener posicion y momentum
<- momentum[i, ]
p_current <- ltail(samples)
q_current
# Integrar para obtener una nueva posición y momentum
<- leapfrog(
integration
p_current, q_current, neg_dlogp, path_length, step_size
)
<- integration$p
p_new <- integration$q
q_new <- lappend(leap_p, integration$leap_p)
leap_p <- lappend(leap_q, integration$leap_q)
leap_q
# Criterio de aceptacion de Metropolis
<- neg_logp(q_current) - dmvnorm(p_current, log = TRUE)
current_logp <- neg_logp(q_new) - dmvnorm(p_new, log = TRUE)
new_logp
<- if(log(runif(1)) < current_logp - new_logp) {
sample_new
q_newelse {
}
sample_current
}<- lappend(samples, sample_new)
samples
}<- as.data.frame(do.call(rbind, samples))
samples <- as.data.frame(do.call(rbind, lapply(leap_p, function(x) do.call(rbind, x))))
leap_p <- as.data.frame(do.call(rbind, lapply(leap_q, function(x) do.call(rbind, x))))
leap_q
colnames(samples) <- c("x", "y")
colnames(leap_p) <- c("p0", "p1")
colnames(leap_q) <- c("q0", "q1")
list(samples = samples, leap_p = leap_p, leap_q = leap_q)
}
13 - Hamiltonian Monte Carlo para normal bivariada
Esta sección muestra una implementación de Hamiltonian Monte Carlo para obtener muestras de una distribución \(\mathcal{N}(\mathbf{0}, \pmb{\Sigma})\) con: \[ \pmb{\Sigma} = \begin{bmatrix} 1 & \rho \\ \rho & 1 \end{bmatrix} \]
Funciones auxiliares
Ejemplo 1: Se grafican trayectorias
<- make_neg_logp(0)
neg_logp <- make_neg_dlogp(0)
neg_dlogp
<- c(1.6, -0.6)
initial_position <- hmc(neg_logp, neg_dlogp, 10, initial_position, 1.5, 0.01)
hmc_output
<- hmc_output$samples
df_samples <- hmc_output$leap_p
df_leap_p <- hmc_output$leap_q
df_leap_q <- cbind(df_leap_p, df_leap_q)
df_leap
<- tidyr::crossing(x1 = seq(-3, 3, 0.1), x2 = seq(-3, 3, 0.1))
df_basis <- df_basis |>
plt ::mutate(f = mvtnorm::dmvnorm(df_basis, c(0, 0), diag(2))) |>
dplyrggplot() +
geom_raster(aes(x = x1, y = x2, fill = f)) +
stat_contour(aes(x = x1, y = x2, z = f), col = "white", bins = 5) +
::scale_fill_viridis() +
viridistheme(
legend.position = "none",
plot.title = element_text(hjust = 0.5, size = 18)
)
+
plt geom_path(aes(x = q0, y = q1), linewidth = 1, data = df_leap) +
geom_quiver(
aes(x = q0, y = q1, u = p0, v = p1),
linewidth = 1,
vecsize = 600,
data = df_leap[seq(1, nrow(df_leap), by = 30), ]
+
) geom_point(
aes(x = x, y = y),
color = "red",
size = 2,
data = df_samples
+
) labs(x = "x", y = "y") +
scale_x_continuous(expand = c(0, 0)) +
scale_y_continuous(expand = c(0, 0))
Ejemplo 2: Se obtienen muestras
<- make_neg_logp(0)
neg_logp <- make_neg_dlogp(0)
neg_dlogp
<- c(1.6, -0.6)
initial_position <- hmc(neg_logp, neg_dlogp, 1000, initial_position, 1.5, 0.01)
hmc_output
<- hmc_output$samples
df_samples <- hmc_output$leap_p
df_leap_p <- hmc_output$leap_q
df_leap_q
<- tidyr::crossing(x1 = seq(-3.5, 3.5, 0.1), x2 = seq(-3.5, 3.5, 0.1))
df_basis <- df_basis |>
plt ::mutate(f = mvtnorm::dmvnorm(df_basis, c(0, 0), diag(2))) |>
dplyrggplot() +
geom_raster(aes(x = x1, y = x2, fill = f)) +
stat_contour(aes(x = x1, y = x2, z = f), col = "white", bins = 5) +
::scale_fill_viridis() +
viridistheme(
legend.position = "none",
plot.title = element_text(hjust = 0.5, size = 18)
)
+
plt geom_point(
aes(x = x, y = y),
size = 2,
alpha = 0.7,
color = "red",
data = df_samples
+
) labs(x = "x", y = "y") +
scale_x_continuous(expand = c(0, 0)) +
scale_y_continuous(expand = c(0, 0))
Ejemplo 3: Se obtienen muestras con \(\rho = 0.6\)
<- make_neg_logp(0.7)
neg_logp <- make_neg_dlogp(0.7)
neg_dlogp
<- c(1.6, -0.6)
initial_position <- hmc(neg_logp, neg_dlogp, 1000, initial_position, 1.5, 0.01)
hmc_output
<- hmc_output$samples
df_samples <- hmc_output$leap_p
df_leap_p <- hmc_output$leap_q
df_leap_q
<- matrix(c(1, 0.7, 0.7, 1), ncol = 2)
Sigma
<- tidyr::crossing(x1 = seq(-3.5, 3.5, 0.1), x2 = seq(-3.5, 3.5, 0.1))
df_basis <- df_basis |>
plt ::mutate(f = mvtnorm::dmvnorm(df_basis, c(0, 0), Sigma)) |>
dplyrggplot() +
geom_raster(aes(x = x1, y = x2, fill = f)) +
stat_contour(aes(x = x1, y = x2, z = f), col = "white", bins = 5) +
::scale_fill_viridis() +
viridistheme(
legend.position = "none",
plot.title = element_text(hjust = 0.5, size = 18)
)
+
plt geom_point(
aes(x = x, y = y),
size = 2,
alpha = 0.7,
color = "red",
data = df_samples
+
) labs(x = "x", y = "y") +
scale_x_continuous(expand = c(0, 0)) +
scale_y_continuous(expand = c(0, 0))
cor(df_samples$x, df_samples$y)
[1] 0.6584974