---
title: "Computational Cognitive Science 2021-2022"
output:
  pdf_document: default
  word_document: default
  html_document: default
---
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
require(DirichletReg) # We'll be using Dirichlet distributions. gtools includes rdirichlet and ddirichlet but not with log densities.
```

# Tutorial 5: Active learning

This tutorial is designed to provide some hands-on exposure to concepts from the active learning lectures.

Imagine you come across a curiosity shop that has four devices for sale at £100 each. Each device can automatically create a delicious cake when one pulls its lever, but the devices vary in their reliability.

The shopkeeper says that 

* one device never works, 
* one device works exactly 20 percent of the time,
* one works 30 percent of the time, and 
* one works 75 percent of the time.

Critically, we don't know which device is which, but the shopkeeper will let us use the devices a few times to choose one.

Let's take an information-theoretic approach to this active learning problem.

Here are some helper functions.

```{r}
plogp <- function(p) {
  # bits, not nats
  if(p==0) 0 else p*log2(p) # Consider why we can't just use p*log2(p).
}

cat_entropy <- function(catv) {
  -sum(sapply(catv,plogp))
}
```

Let's define our hypothesis space as all of the different cake probabilities the machines before us might have.

**Question 1**: How does the code below define that hypothesis space?

```{r}
library(combinat) # From googling "all list permutations in R"
probs <- c(0,.2,.3,.75)
hyps <- permn(probs)
```

**Solution**: *Our hypotheses capture which machine is assigned which cake probability by exhaustively permuting a single probability list, yielding 4!=24 hypotheses.*

**Question 2**: What is our entropy?

**Solution**: 

```{r}
cat_entropy(rep(1/24,24))
```

**Question 3**: Suppose we turn the lever on the first machine and get nothing. What probability should we now assign our different hypotheses? Edit the code below.

```{r}
update_probs <- function(prior,choice,outcome) {
  # x, below, is a list of probabilities under a specific hypothesis
  likes <- sapply(hyps,function(x) {1.0}) # Replace the 1.0 with an actual likelihood
  unnp <- likes*prior
  z <- sum(unnp)
  unnp/z
}

post <- update_probs(runif(24),1,0) # Replace the "runif(24)" with the actual prior
print(post)
```


**Solution**:

```{r}
update_probs <- function(prior,choice,outcome) {
  # x, below, is a list of probabilities under a specific hypothesis
  likes <- sapply(hyps,function(x) {dbinom(outcome,1,x[choice])})
  unnp <- likes*prior
  z <- sum(unnp)
  unnp/z
}

post <- update_probs(rep(1/24,24),1,0)
print(post)
```

**Question 4**: What is our new entropy? Did we learning much? Why or why not?

**Solution**:

```{r}
cat_entropy(post)
```

*We didn't learn very much! It makes sense, because 3/4 of our machines are unlikely to produce a cake, so most hypotheses are quite consistent with what we've seen.*

**Question 5**: What is the expected probability getting a cake if we choose machine 2 next?

**Solution**:

```{r}
# First we want the probability of each outcome in each hyp
# Then we take the weighted sum over hypothesis probabilities
expected_outcome <- function(i,ph) {
  sum(sapply(hyps,function(x) {x[i]})*ph)
}
print(expected_outcome(2,post))
```

**Question 6**: What is the expected entropy if we choose machine 2 next?

**Solution**: *We take the expectation over possible outcomes*.

```{r}
expected_entropy <- function(choice,prior) {
  pwin <- expected_outcome(choice,prior)
  if_win <- update_probs(prior,choice,1)
  if_lose <- update_probs(prior,choice,0)
  cat_entropy(if_win)*pwin+cat_entropy(if_lose)*(1-pwin)
}
print(expected_entropy(2,post))
```

**Question 7**: Suppose we do choose machine 2 and it yields a cake. What is our current entropy and what are the expected entropies after each of our next possible choices?

**Solution**:

```{r}
post2 <- update_probs(post,2,1)
print(cat_entropy(post2))
sapply(c(1,2,3,4),function(x) {expected_entropy(x,post2)})
```

**Question 8**: What does this imply we should do next?

**Solution**: *It implies we should try a new arm -- either 3 or 4.*

**Question 9**: Is this at all counter-intuitive? What might we do differently if we are only interested in knowing which is the most reliable machine?

**Solution**: 

*It seems odd to learn about a machine that is probably bad, if we want to learn which machine is good -- our learning objective isn't matched to our goals. We can instead focus on the entropy of the random variable that matters: The identity of the machine with the highest probability.*

```{r}
is_75_ent <- function(probs) {
  oh_75 <- sapply(hyps,function(x) {sapply(x,function(y) {if(y==.75) 1.0 else 0})})
  pr <- matrix(rep(probs,4),ncol=4)
  p75<-colSums(t(oh_75) * pr)
  cat_entropy(p75)
}

ee_75 <- function(choice,prior) {
  pwin <- expected_outcome(choice,prior)
  if_win <- update_probs(prior,choice,1)
  if_lose <- update_probs(prior,choice,0)
  is_75_ent(if_win)*pwin+is_75_ent(if_lose)*(1-pwin)
}

ents_75 <- sapply(c(1,2,3,4),function(x) {ee_75(x,post2)})
ents_75


```
