Rejection sampling example

ggplot2 is used for plotting, tidyr for manipulating data frames

library(ggplot2)
theme_set(theme_minimal())
library(tidyr)

Fake interesting distribution

x <- seq(-4, 4, length.out = 200)
r <- c(1.1, 1.3, -0.1, -0.7, 0.2, -0.4, 0.06, -1.7,
      1.7, 0.3, 0.7, 1.6, -2.06, -0.74, 0.2, 0.5)

Compute unnormalized target density (named q, to emphesize that it does not need to be normalized).

q <- density(r, bw = 0.5, n = 200, from = -4, to = 4)$y

Gaussian proposal distribution

g_mean <- 0
g_sd <- 1.1
g <- dnorm(x, g_mean, g_sd)

M is computed by discrete approximation

M <- max(q/g)
g <- g*M

One draw at -0.8

r1 = -0.8
zi = which.min(abs(x - r1)) # find the closest grid point
r21 = 0.3 * g[zi]
r22 = 0.8 * g[zi]

Visualize one accepted and one rejected draw:

df1 <- data.frame(x, q, g) %>% 
  pivot_longer(cols = !x, names_to = "grp", values_to = "p")
# subset with only target distribution
dfq <- subset(df1 , grp == "q")
# labels 
labs1 <- c('Mg(theta)','q(theta|y)')
ggplot() +
  geom_line(data = df1, aes(x, p, color = grp, linetype = grp)) +
  geom_area(data = dfq, aes(x, p), fill = 'lightblue', alpha = 0.3) +
  geom_point(aes(x[zi], r21), col = 'forestgreen', size = 2) +
  geom_point(aes(x[zi], r22), col = 'red', size = 2) +
  geom_segment(aes(x = x[zi], xend = x[zi], y = 0, yend = q[zi])) +
  geom_segment(aes(x = x[zi], xend = x[zi], y = q[zi], yend = g[zi]),
               linetype = 'dashed') +
  scale_y_continuous(breaks = NULL) +
  labs(y = '') +
  theme(legend.position = 'bottom', legend.title = element_blank()) +
  scale_linetype_manual(values = c('dashed', 'solid'), labels = labs1) +
  scale_color_discrete(labels = labs1) +
  annotate('text', x = x[zi] + 0.1, y = c(r21, r22),
           label = c('accepted', 'rejected'), hjust = 0)

200 draws from the proposal distribution

nsamp <- 200
r1s <- rnorm(nsamp, g_mean, g_sd)
zis <- sapply(r1s, function(r) which.min(abs(x - r)))
r2s <- runif(nsamp) * g[zis]
acc <- ifelse(r2s < q[zis], 'a', 'r')

Visualize 200 draws, only some of which are accepted

df2 <- data.frame(r1s, r2s, acc)
# labels
labs2 <- c('Accepted', 'Rejected', 'Mg(theta)', 'q(theta|y)')
ggplot() +
  geom_line(data = df1, aes(x, p, color = grp, linetype = grp)) +
  geom_area(data = dfq, aes(x, p), fill = 'lightblue', alpha = 0.3) +
  geom_point(data = df2, aes(r1s, r2s, color = acc), size = 2) +
  geom_rug(data = subset(df2, acc== 'a'), aes(x = r1s, r2s),
           col = 'forestgreen', sides = 'b') +
  labs(y = '') +
  scale_y_continuous(breaks = NULL) +
  scale_linetype_manual(values = c(2, 1, 0, 0), labels = labs2) +
  scale_color_manual(values=c('forestgreen','red','#00BFC4','red'), labels = labs2) +
  guides(color=guide_legend(override.aes=list(
    shape = c(16, 16, NA, NA), linetype = c(0, 0, 2, 1),
    color=c('forestgreen', 'red', 'red', '#00BFC4'), labels = labs2)),
    linetype="none") +
  theme(legend.position = 'bottom', legend.title = element_blank())

Rejection sampling for truncated distribution

q <- g
q[x < -1.5] <- 0
df1 <- data.frame(x, q, g) %>%
  pivot_longer(cols = !x, names_to = "grp", values_to = "p")
acc <- ifelse(r1s > -1.5, 'a', 'r')
df2 <- data.frame(r1s, r2s, acc)
dfq <- subset(df1 , grp == "q")
# labels
labs2 <- c('Accepted', 'Rejected', 'Mg(theta)', 'q(theta|y)')
ggplot() +
  geom_line(data = df1, aes(x, p, color = grp, linetype = grp)) +
  geom_area(data = dfq, aes(x, p), fill = 'lightblue', alpha = 0.3) +
  geom_point(data = df2, aes(r1s, r2s, color = acc), size = 2) +
  geom_rug(data = subset(df2, acc== 'a'), aes(x = r1s, r2s),
           col = 'forestgreen', sides = 'b') +
  labs(y = '') +
  scale_y_continuous(breaks = NULL) +
  scale_linetype_manual(values = c(2, 1, 0, 0), labels = labs2) +
  scale_color_manual(values=c('forestgreen','red','#00BFC4','red'), labels = labs2) +
  guides(color=guide_legend(override.aes=list(
      shape = c(16, 16, NA, NA), linetype = c(0, 0, 2, 1),
      color=c('forestgreen', 'red', 'red', '#00BFC4'), labels = labs2)),
    linetype="none") +
  theme(legend.position = 'bottom', legend.title = element_blank())

LS0tCnRpdGxlOiAiQmF5ZXNpYW4gZGF0YSBhbmFseXNpcyBkZW1vIDEwLjEiCmF1dGhvcjogIkFraSBWZWh0YXJpLCBNYXJrdXMgUGFhc2luaWVtaSIKZGF0ZTogImByIGZvcm1hdChTeXMuRGF0ZSgpKWAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdGhlbWU6IHJlYWRhYmxlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQojIyBSZWplY3Rpb24gc2FtcGxpbmcgZXhhbXBsZQoKZ2dwbG90MiBpcyB1c2VkIGZvciBwbG90dGluZywgdGlkeXIgZm9yIG1hbmlwdWxhdGluZyBkYXRhIGZyYW1lcwoKYGBge3Igc2V0dXAsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGdncGxvdDIpCnRoZW1lX3NldCh0aGVtZV9taW5pbWFsKCkpCmxpYnJhcnkodGlkeXIpCmBgYAoKRmFrZSBpbnRlcmVzdGluZyBkaXN0cmlidXRpb24KCmBgYHtyIH0KeCA8LSBzZXEoLTQsIDQsIGxlbmd0aC5vdXQgPSAyMDApCnIgPC0gYygxLjEsIDEuMywgLTAuMSwgLTAuNywgMC4yLCAtMC40LCAwLjA2LCAtMS43LAogICAgICAxLjcsIDAuMywgMC43LCAxLjYsIC0yLjA2LCAtMC43NCwgMC4yLCAwLjUpCmBgYAoKQ29tcHV0ZSB1bm5vcm1hbGl6ZWQgdGFyZ2V0IGRlbnNpdHkgKG5hbWVkIHEsIHRvIGVtcGhlc2l6ZSB0aGF0IGl0CmRvZXMgbm90IG5lZWQgdG8gYmUgbm9ybWFsaXplZCkuCgpgYGB7ciB9CnEgPC0gZGVuc2l0eShyLCBidyA9IDAuNSwgbiA9IDIwMCwgZnJvbSA9IC00LCB0byA9IDQpJHkKYGBgCgpHYXVzc2lhbiBwcm9wb3NhbCBkaXN0cmlidXRpb24KCmBgYHtyIH0KZ19tZWFuIDwtIDAKZ19zZCA8LSAxLjEKZyA8LSBkbm9ybSh4LCBnX21lYW4sIGdfc2QpCmBgYAoKTSBpcyBjb21wdXRlZCBieSBkaXNjcmV0ZSBhcHByb3hpbWF0aW9uCgpgYGB7ciB9Ck0gPC0gbWF4KHEvZykKZyA8LSBnKk0KYGBgCgpPbmUgZHJhdyBhdCAtMC44CgpgYGB7ciB9CnIxID0gLTAuOAp6aSA9IHdoaWNoLm1pbihhYnMoeCAtIHIxKSkgIyBmaW5kIHRoZSBjbG9zZXN0IGdyaWQgcG9pbnQKcjIxID0gMC4zICogZ1t6aV0KcjIyID0gMC44ICogZ1t6aV0KYGBgCgpWaXN1YWxpemUgb25lIGFjY2VwdGVkIGFuZCBvbmUgcmVqZWN0ZWQgZHJhdzoKCmBgYHtyIH0KZGYxIDwtIGRhdGEuZnJhbWUoeCwgcSwgZykgJT4lIAogIHBpdm90X2xvbmdlcihjb2xzID0gIXgsIG5hbWVzX3RvID0gImdycCIsIHZhbHVlc190byA9ICJwIikKIyBzdWJzZXQgd2l0aCBvbmx5IHRhcmdldCBkaXN0cmlidXRpb24KZGZxIDwtIHN1YnNldChkZjEgLCBncnAgPT0gInEiKQojIGxhYmVscyAKbGFiczEgPC0gYygnTWcodGhldGEpJywncSh0aGV0YXx5KScpCmdncGxvdCgpICsKICBnZW9tX2xpbmUoZGF0YSA9IGRmMSwgYWVzKHgsIHAsIGNvbG9yID0gZ3JwLCBsaW5ldHlwZSA9IGdycCkpICsKICBnZW9tX2FyZWEoZGF0YSA9IGRmcSwgYWVzKHgsIHApLCBmaWxsID0gJ2xpZ2h0Ymx1ZScsIGFscGhhID0gMC4zKSArCiAgZ2VvbV9wb2ludChhZXMoeFt6aV0sIHIyMSksIGNvbCA9ICdmb3Jlc3RncmVlbicsIHNpemUgPSAyKSArCiAgZ2VvbV9wb2ludChhZXMoeFt6aV0sIHIyMiksIGNvbCA9ICdyZWQnLCBzaXplID0gMikgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IHhbemldLCB4ZW5kID0geFt6aV0sIHkgPSAwLCB5ZW5kID0gcVt6aV0pKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0geFt6aV0sIHhlbmQgPSB4W3ppXSwgeSA9IHFbemldLCB5ZW5kID0gZ1t6aV0pLAogICAgICAgICAgICAgICBsaW5ldHlwZSA9ICdkYXNoZWQnKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IE5VTEwpICsKICBsYWJzKHkgPSAnJykgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsKICBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzID0gYygnZGFzaGVkJywgJ3NvbGlkJyksIGxhYmVscyA9IGxhYnMxKSArCiAgc2NhbGVfY29sb3JfZGlzY3JldGUobGFiZWxzID0gbGFiczEpICsKICBhbm5vdGF0ZSgndGV4dCcsIHggPSB4W3ppXSArIDAuMSwgeSA9IGMocjIxLCByMjIpLAogICAgICAgICAgIGxhYmVsID0gYygnYWNjZXB0ZWQnLCAncmVqZWN0ZWQnKSwgaGp1c3QgPSAwKQpgYGAKCjIwMCBkcmF3cyBmcm9tIHRoZSBwcm9wb3NhbCBkaXN0cmlidXRpb24KCmBgYHtyIH0KbnNhbXAgPC0gMjAwCnIxcyA8LSBybm9ybShuc2FtcCwgZ19tZWFuLCBnX3NkKQp6aXMgPC0gc2FwcGx5KHIxcywgZnVuY3Rpb24ocikgd2hpY2gubWluKGFicyh4IC0gcikpKQpyMnMgPC0gcnVuaWYobnNhbXApICogZ1t6aXNdCmFjYyA8LSBpZmVsc2UocjJzIDwgcVt6aXNdLCAnYScsICdyJykKYGBgCgpWaXN1YWxpemUgMjAwIGRyYXdzLCBvbmx5IHNvbWUgb2Ygd2hpY2ggYXJlIGFjY2VwdGVkCgpgYGB7ciB9CmRmMiA8LSBkYXRhLmZyYW1lKHIxcywgcjJzLCBhY2MpCiMgbGFiZWxzCmxhYnMyIDwtIGMoJ0FjY2VwdGVkJywgJ1JlamVjdGVkJywgJ01nKHRoZXRhKScsICdxKHRoZXRhfHkpJykKZ2dwbG90KCkgKwogIGdlb21fbGluZShkYXRhID0gZGYxLCBhZXMoeCwgcCwgY29sb3IgPSBncnAsIGxpbmV0eXBlID0gZ3JwKSkgKwogIGdlb21fYXJlYShkYXRhID0gZGZxLCBhZXMoeCwgcCksIGZpbGwgPSAnbGlnaHRibHVlJywgYWxwaGEgPSAwLjMpICsKICBnZW9tX3BvaW50KGRhdGEgPSBkZjIsIGFlcyhyMXMsIHIycywgY29sb3IgPSBhY2MpLCBzaXplID0gMikgKwogIGdlb21fcnVnKGRhdGEgPSBzdWJzZXQoZGYyLCBhY2M9PSAnYScpLCBhZXMoeCA9IHIxcywgcjJzKSwKICAgICAgICAgICBjb2wgPSAnZm9yZXN0Z3JlZW4nLCBzaWRlcyA9ICdiJykgKwogIGxhYnMoeSA9ICcnKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IE5VTEwpICsKICBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzID0gYygyLCAxLCAwLCAwKSwgbGFiZWxzID0gbGFiczIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoJ2ZvcmVzdGdyZWVuJywncmVkJywnIzAwQkZDNCcsJ3JlZCcpLCBsYWJlbHMgPSBsYWJzMikgKwogIGd1aWRlcyhjb2xvcj1ndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzPWxpc3QoCiAgICBzaGFwZSA9IGMoMTYsIDE2LCBOQSwgTkEpLCBsaW5ldHlwZSA9IGMoMCwgMCwgMiwgMSksCiAgICBjb2xvcj1jKCdmb3Jlc3RncmVlbicsICdyZWQnLCAncmVkJywgJyMwMEJGQzQnKSwgbGFiZWxzID0gbGFiczIpKSwKICAgIGxpbmV0eXBlPSJub25lIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKUmVqZWN0aW9uIHNhbXBsaW5nIGZvciB0cnVuY2F0ZWQgZGlzdHJpYnV0aW9uCgpgYGB7ciB9CnEgPC0gZwpxW3ggPCAtMS41XSA8LSAwCmRmMSA8LSBkYXRhLmZyYW1lKHgsIHEsIGcpICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gIXgsIG5hbWVzX3RvID0gImdycCIsIHZhbHVlc190byA9ICJwIikKYWNjIDwtIGlmZWxzZShyMXMgPiAtMS41LCAnYScsICdyJykKZGYyIDwtIGRhdGEuZnJhbWUocjFzLCByMnMsIGFjYykKZGZxIDwtIHN1YnNldChkZjEgLCBncnAgPT0gInEiKQojIGxhYmVscwpsYWJzMiA8LSBjKCdBY2NlcHRlZCcsICdSZWplY3RlZCcsICdNZyh0aGV0YSknLCAncSh0aGV0YXx5KScpCmdncGxvdCgpICsKICBnZW9tX2xpbmUoZGF0YSA9IGRmMSwgYWVzKHgsIHAsIGNvbG9yID0gZ3JwLCBsaW5ldHlwZSA9IGdycCkpICsKICBnZW9tX2FyZWEoZGF0YSA9IGRmcSwgYWVzKHgsIHApLCBmaWxsID0gJ2xpZ2h0Ymx1ZScsIGFscGhhID0gMC4zKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZGYyLCBhZXMocjFzLCByMnMsIGNvbG9yID0gYWNjKSwgc2l6ZSA9IDIpICsKICBnZW9tX3J1ZyhkYXRhID0gc3Vic2V0KGRmMiwgYWNjPT0gJ2EnKSwgYWVzKHggPSByMXMsIHIycyksCiAgICAgICAgICAgY29sID0gJ2ZvcmVzdGdyZWVuJywgc2lkZXMgPSAnYicpICsKICBsYWJzKHkgPSAnJykgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBOVUxMKSArCiAgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcyA9IGMoMiwgMSwgMCwgMCksIGxhYmVscyA9IGxhYnMyKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCdmb3Jlc3RncmVlbicsJ3JlZCcsJyMwMEJGQzQnLCdyZWQnKSwgbGFiZWxzID0gbGFiczIpICsKICBndWlkZXMoY29sb3I9Z3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcz1saXN0KAogICAgICBzaGFwZSA9IGMoMTYsIDE2LCBOQSwgTkEpLCBsaW5ldHlwZSA9IGMoMCwgMCwgMiwgMSksCiAgICAgIGNvbG9yPWMoJ2ZvcmVzdGdyZWVuJywgJ3JlZCcsICdyZWQnLCAnIzAwQkZDNCcpLCBsYWJlbHMgPSBsYWJzMikpLAogICAgbGluZXR5cGU9Im5vbmUiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ2JvdHRvbScsIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgo=