Blackbox MCMC Testing

library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.1     ✔ tibble    3.2.1
✔ lubridate 1.9.4     ✔ tidyr     1.3.1
✔ purrr     1.0.4     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
source('blackbox.r')

Making sure initially that we can plot the mystery function:

xs = seq(-30,30, 0.01)
data = tibble(x = xs, y = mystery(xs))
ggplot(data=data, aes(x=x, y=y)) + geom_point()

Setting up our sampler:

mcmc_sampler <- function(func, start, step_size, num_steps) {
    samples = numeric(num_steps)
    samples[1] = start
    accepted = 0
    for (i in 2:num_steps) {
        current = samples[i-1]
        proposed = rnorm(1, current, step_size)
        ratio = func(proposed) / func(current)
        if (ratio > runif(1)) {
            samples[i] = proposed
            accepted = accepted + 1
        } else {
            samples[i] = current
        }
    }
    return(list(samples=samples, accept_rate=accepted / length(samples)))
}

Running the sampler

out = mcmc_sampler(mystery, -10, 4, 50000)
samples = out$samples
accept_rate = out$accept_rate
accept_rate
[1] 0.26828

Looking at the trace plot:

data = tibble(iter=1:length(samples), x=samples)
ggplot(data=data, aes(x=iter, y=x)) + geom_line()

Looking at the lag plot:

burned = data %>% filter(iter > 1000)
ggplot(data=burned, aes(x=x, y=lag(x))) + geom_point(alpha=0.1)
Warning: Removed 1 row containing missing values or values outside the scale range
(`geom_point()`).

And looking at the overall histogram:

ggplot(data=burned, aes(x=x)) + geom_histogram(bins=100)