# Introduction

The Bayesian Optimal Interval (BOIN) design was introduced by Liu and Yuan (2015). It is one of a series of dose-finding trial designs that works by partitioning the probability of toxicity into a set of intervals. These designs make dose-selection decisions that are determined by the interval in which the probability of toxicity for the current dose is believed to reside.

# Summary of the BOIN Design

BOIN seeks a dose with probability of toxicity close to some pre-specified target level, $$p_T$$. Let the probability of toxicity at dose $$i$$ be $$p_i$$. The entire range of possible values for $$p_i$$ can be broken up into the following intervals:

• The underdosing interval (UI), defined as $$(0, \lambda_{1i})$$;
• The equivalence interval (EI), defined as $$(\lambda_{1i}, \lambda_{2i})$$;
• The overdosing interval (OI), defined as $$(\lambda_{2i}, 1)$$;

Let $$\pi_{UI,i}, \pi_{EI,i}$$ and $$\pi_{OI,i}$$ be the a-priori probabilities that the rate of toxicity associated with dose $$i$$ belongs to the intervals UI, EI and OI. By definition, $$\pi_{UI,i} + \pi_{EI,i} + \pi_{OI,i} = 1$$. The authors advocate $$\pi_{UI,i} = \pi_{EI,i} = \pi_{OI,i} = \frac{1}{3}$$.

Let $$n_i$$ be the number of patients that have been treated at dose $$i$$, yielding $$x_i$$ toxicity events. The so-called local BOIN variant (i.e. that advocated by Liu and Yuan (2015)) defines:

$\lambda_{1i} = \frac{\log{\left( \frac{1 - \phi_1}{1 - p_T} \right)} + \frac{1}{n_i} \log{\left( \frac{\pi_{UI,i}}{\pi_{EI,i}} \right)} }{ \log{\left( \frac{p_T (1 - \phi_1)}{\phi_1 (1 - p_T)} \right)} }$

$\lambda_{2i} = \frac{\log{\left( \frac{1 - p_T}{1 - \phi_2} \right)} + \frac{1}{n_i} \log{\left( \frac{\pi_{EI,i}}{\pi_{OI,i}} \right)} }{ \log{\left( \frac{ \phi_2(1 - p_T)}{p_T (1 - \phi_2)} \right)} }$

where $$\phi_1$$ and $$\phi_2$$ are model parameters. The authors advocate $$\phi_1 \in \left[ 0.5pT, 0.7pT \right]$$ and $$\phi_2 \in \left[ 1.3pT, 1.5pT \right]$$. As defaults, they recommend $$\phi_1 = 0.6p_T$$ and $$\phi_2 = 1.4p_T$$.

Having observed toxicity rate $$\hat{p}_i = x_i / n_i$$ at the current dose $$i$$, the logical action depends on the interval in which $$\hat{p}_i$$ resides. If $$\hat{p}_i < \lambda_{1i}$$, then the current dose is likely an underdose, so our desire should be to escalate dose to $$i+1$$. In contrast, if $$\hat{p}_i > \lambda_{2i}$$, then the current dose is likely an overdose and we will want to de-escalate dose to $$i-1$$ for the next patient. If $$\lambda_{1i} < \hat{p}_i < \lambda_{2i}$$, then the current dose is deemed sufficiently close to $$p_T$$ and we will want to stay at dose-level $$i$$.

The authors advocate a stopping rule to protect against repeated administration of a dose that is evidently excessively toxic. The proposed rule is similar to that used in TPI and mTPI. Using a $$Beta(1, 1)$$ prior, dose $$i$$ is deemed inadmissible for being excessively toxic if

$Pr(p_{i} > p_{T} | x_i, n_i) > \xi,$

for a certainty threshold, $$\xi$$, with $$\xi = 0.95$$ being suggested. If a dose is excluded by this rule, it should not be recommended by the model. Irrespective the values of $$\lambda_{1i}$$ and $$\lambda_{1i}$$, the design will recommend to stay at dose $$i$$ rather than escalate to a dose previously identified as being inadmissible. Furthermore, the design will advocate stopping if the lowest dose is inferred to be inadmissible.

See Liu and Yuan (2015) and Yuan and Liu (2018) for full details.

# Implementation in escalation

To demonstrate the method, let us reproduce the dose selection sequence in a trial of five doses targeting $$p_T = 0.3$$, described in Yuan and Liu (2018).

Opting to take the defaults, $$\phi_1 = 0.6 p_T = 0.18$$ and $$\phi_2 = 1.4 p_T = 0.42$$, we create an object to fit the model using:

library(escalation)

model <- get_boin(num_doses = 5, target = 0.3)

This is short-hand for

model <- get_boin(num_doses = 5, target = 0.3, p.saf = 0.18, p.tox = 0.42)

The text in the paper describes that outcomes ‘1NNN’ were observed in the first cohort:

fit <- model %>% fit('1NNN')

fit %>% recommended_dose()
#>  2

fit <- model %>% fit('1NNN 2NNN')
fit %>% recommended_dose()
#>  3

fit <- model %>% fit('1NNN 2NNN 3NTT')
fit %>% recommended_dose()
#>  2

The relatively low sample size at dose 3 means that the dose has not yet been rendered inadmissible:

fit %>% dose_admissible()
#>  TRUE TRUE TRUE TRUE TRUE

## Dose paths

We can reproduce some of the advice above using dose-paths to exhaustively calculate all possible future model advice. For example, after observing 1NNN in the first cohort, we can reproduce the advice to escalate further after seeing 2NNN and then de-escalate after seeing 3NTT using:

cohort_sizes <- c(3, 3)
paths <- model %>% get_dose_paths(cohort_sizes = cohort_sizes,
previous_outcomes = '1NNN', next_dose = 2)
graph_paths(paths)

Thus, we can trace the path 1NNN 2NNN 3NNT, along with every other possible path in two cohorts of three after having observed 1NNN.

For more information on working with dose-paths, refer to the dose-paths vignette.

## Simulation

Liu and Yuan (2015) present in their Table 4 some simulated operating characteristics. We can use the simulate_trials function to reproduce the findings.

Their example concerns a clinical trial of six doses that targets 25% toxicity. We specify the model object to reflect this. They also elect to limit the trial to a sample size of $$n=36$$:

model <- get_boin(num_doses = 6, target = 0.25) %>%
stop_at_n(n = 36)

Their scenario 1 investigates the true probability vector:

true_prob_tox <- c(0.25, 0.35, 0.5, 0.6, 0.7, 0.8)

For the sake of speed, we will run just fifty iterations:

num_sims <- 50

In real life, however, we would naturally run many thousands of iterations.

Running the simulation:

sims <- model %>%
simulate_trials(num_sims = num_sims, true_prob_tox = true_prob_tox)

we see that from this small sample size, the probability that each dose is recommended is similar to that reported:

prob_recommend(sims)
#> NoDose      1      2      3      4      5      6
#>   0.22   0.54   0.18   0.06   0.00   0.00   0.00

and so is the expected number of patients treated at each dose level:

colMeans(n_at_dose(sims))
#>     1     2     3     4     5     6
#> 21.00  8.22  1.68  0.00  0.00  0.00

For more information on running dose-finding simulations, refer to the simulation vignette.