## ----setup, include = FALSE---------------------------------------------------
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  eval = FALSE
)

## -----------------------------------------------------------------------------
# library(tensorflow)
# # assume it's version 1.14, with eager not yet being the default
# tf$compat$v1$enable_v2_behavior()
# 
# library(tfprobability)
# library(keras)
# 
# library(dplyr)
# library(tidyr)
# library(ggplot2)
# 
# # generate the data
# x_min <- -40
# x_max <- 60
# n <- 150
# w0 <- 0.125
# b0 <- 5
# 
# normalize <- function(x) (x - x_min) / (x_max - x_min)
# 
# # training data; predictor
# x <- x_min + (x_max - x_min) * runif(n) %>% as.matrix()
# 
# # training data; target
# eps <- rnorm(n) * (3 * (0.25 + (normalize(x)) ^ 2))
# y <- (w0 * x * (1 + sin(x)) + b0) + eps
# 
# # test data (predictor)
# x_test <- seq(x_min, x_max, length.out = n) %>% as.matrix()

## -----------------------------------------------------------------------------
# ggplot(data.frame(x = x, y = y), aes(x, y)) + geom_point()

## ----eval=TRUE, echo=FALSE, layout="l-body-outset", fig.cap = "Simulated data"----
knitr::include_graphics("images/uncertainty_data.png")

## -----------------------------------------------------------------------------
# prior_trainable <-
#   function(kernel_size,
#            bias_size = 0,
#            dtype = NULL) {
#     n <- kernel_size + bias_size
#     keras_model_sequential() %>%
#       layer_variable(n, dtype = dtype, trainable = TRUE) %>%
#       layer_distribution_lambda(function(t) {
#         tfd_independent(tfd_normal(loc = t, scale = 1),
#                         reinterpreted_batch_ndims = 1)
#       })
#   }
# 

## -----------------------------------------------------------------------------
# posterior_mean_field <-
#   function(kernel_size,
#            bias_size = 0,
#            dtype = NULL) {
#     n <- kernel_size + bias_size
#     c <- log(expm1(1))
#     keras_model_sequential(list(
#       layer_variable(shape = 2 * n, dtype = dtype),
#       layer_distribution_lambda(
#         make_distribution_fn = function(t) {
#           tfd_independent(tfd_normal(
#             loc = t[1:n],
#             scale = 1e-5 + tf$nn$softplus(c + t[(n + 1):(2 * n)])
#             ), reinterpreted_batch_ndims = 1)
#         }
#       )
#     ))
#   }

## -----------------------------------------------------------------------------
# model <- keras_model_sequential() %>%
#   layer_dense_variational(
#     units = 2,
#     make_posterior_fn = posterior_mean_field,
#     make_prior_fn = prior_trainable,
#     # scale by the size of the dataset
#     kl_weight = 1 / n
#   ) %>%
#   layer_distribution_lambda(function(x)
#     tfd_normal(loc = x[, 1, drop = FALSE],
#                scale = 1e-3 + tf$math$softplus(0.01 * x[, 2, drop = FALSE])
#                )
#     )
# 

## -----------------------------------------------------------------------------
# negloglik <- function(y, model) - (model %>% tfd_log_prob(y))
# model %>% compile(optimizer = optimizer_adam(0.01), loss = negloglik)
# model %>% fit(x, y, epochs = 1000)

## -----------------------------------------------------------------------------
# # each time we ask the model to predict, we get a different line
# yhats <- purrr::map(1:100, function(x) model(tf$constant(x_test)))
# means <-
#   purrr::map(yhats, purrr::compose(as.matrix, tfd_mean)) %>% abind::abind()
# sds <-
#   purrr::map(yhats, purrr::compose(as.matrix, tfd_stddev)) %>% abind::abind()
# 
# means_gathered <- data.frame(cbind(x_test, means)) %>%
#   gather(key = run, value = mean_val,-X1)
# sds_gathered <- data.frame(cbind(x_test, sds)) %>%
#   gather(key = run, value = sd_val,-X1)
# 
# lines <-
#   means_gathered %>% inner_join(sds_gathered, by = c("X1", "run"))
# mean <- apply(means, 1, mean)
# 
# ggplot(data.frame(x = x, y = y, mean = as.numeric(mean)), aes(x, y)) +
#   geom_point() +
#   theme(legend.position = "none") +
#   geom_line(aes(x = x_test, y = mean), color = "violet", size = 1.5) +
#   geom_line(
#     data = lines,
#     aes(x = X1, y = mean_val, color = run),
#     alpha = 0.6,
#     size = 0.5
#   ) +
#   geom_ribbon(
#     data = lines,
#     aes(
#       x = X1,
#       ymin = mean_val - 2 * sd_val,
#       ymax = mean_val + 2 * sd_val,
#       group = run
#     ),
#     alpha = 0.05,
#     fill = "grey",
#     inherit.aes = FALSE
#   )

## ----eval=TRUE, echo=FALSE, layout="l-body-outset", fig.cap = "Displaying both epistemic and aleatoric uncertainty on the simulated dataset."----
knitr::include_graphics("images/uncertainty.png")

