Skip to contents

Motivating Scenario

We extend the single-endpoint RA trial to a bivariate design with two co-primary continuous endpoints:

  • Endpoint 1: change from baseline in DAS28 score
  • Endpoint 2: change from baseline in HAQ-DI (Health Assessment Questionnaire – Disability Index)

The trial enrolls nt=nc=20n_t = n_c = 20 patients per group (treatment vs. placebo) in a 1:1 randomised controlled design.

Clinically meaningful thresholds (posterior probability):

Endpoint 1 Endpoint 2
θTV,1\theta_{\mathrm{TV},1} 1.5 1.0
θMAV,1\theta_{\mathrm{MAV},1} 0.5 0.3

Null hypothesis thresholds (predictive probability): θNULL,1=0.5\theta_{\mathrm{NULL},1} = 0.5, θNULL,2=0.3\theta_{\mathrm{NULL},2} = 0.3.

Decision thresholds: γ1=0.80\gamma_1 = 0.80 (Go), γ2=0.20\gamma_2 = 0.20 (NoGo).

Observed data (used in Sections 2–4):

  • Treatment: 𝐲t=(3.5,2.1)\bar{\mathbf{y}}_t = (3.5, 2.1)^\top, St=(18.03.63.69.0)S_t = \begin{pmatrix}18.0 & 3.6\\3.6 & 9.0\end{pmatrix}
  • Control: 𝐲c=(1.8,1.0)\bar{\mathbf{y}}_c = (1.8, 1.0)^\top, Sc=(16.02.82.88.5)S_c = \begin{pmatrix}16.0 & 2.8\\2.8 & 8.5\end{pmatrix}

where Sj=i=1nj(𝐲ij𝐲j)(𝐲ij𝐲j)S_j = \sum_{i=1}^{n_j}(\mathbf{y}_{ij} - \bar{\mathbf{y}}_j) (\mathbf{y}_{ij} - \bar{\mathbf{y}}_j)^\top (j=t,cj = t, c) is the sum-of-squares matrix.


1. Bayesian Model: Normal-Inverse-Wishart Conjugate

1.1 Prior Distribution

For group jj (j=tj = t: treatment, j=cj = c: control), we model the bivariate outcomes for patient ii (i=1,,nji = 1, \ldots, n_j) as

𝐲ij𝛍j,Σjiid𝒩2(𝛍j,Σj).\mathbf{y}_{ij} \mid \boldsymbol{\mu}_j, \Sigma_j \;\overset{iid}{\sim}\; \mathcal{N}_2(\boldsymbol{\mu}_j,\, \Sigma_j).

The conjugate prior is the Normal-Inverse-Wishart (NIW) distribution:

(𝛍j,Σj)NIW(𝛍0j,κ0j,ν0j,Λ0j),(\boldsymbol{\mu}_j, \Sigma_j) \;\sim\; \mathrm{NIW}(\boldsymbol{\mu}_{0j},\, \kappa_{0j},\, \nu_{0j},\, \Lambda_{0j}),

where 𝛍0j2\boldsymbol{\mu}_{0j} \in \mathbb{R}^2 is the prior mean (argument mu0_t or mu0_c), κ0j>0\kappa_{0j} > 0 is the prior concentration (kappa0_t or kappa0_c), ν0j>3\nu_{0j} > 3 is the prior degrees of freedom (nu0_t or nu0_c), and Λ0j\Lambda_{0j} is a 2×22 \times 2 positive-definite prior scale matrix (Lambda0_t or Lambda0_c).

The vague (Jeffreys) prior is obtained by letting κ0j0\kappa_{0j} \to 0 and ν0j0\nu_{0j} \to 0 (prior = 'vague').

1.2 Posterior Distribution

Given njn_j observations with sample mean 𝐲j\bar{\mathbf{y}}_j (ybar_t or ybar_c) and sum-of-squares matrix SjS_j (S_t or S_c), the NIW posterior has updated parameters:

κnj=κ0j+nj,νnj=ν0j+nj,\kappa_{nj} = \kappa_{0j} + n_j, \qquad \nu_{nj} = \nu_{0j} + n_j,

𝛍nj=κ0j𝛍0j+nj𝐲jκnj,\boldsymbol{\mu}_{nj} = \frac{\kappa_{0j}\,\boldsymbol{\mu}_{0j} + n_j\,\bar{\mathbf{y}}_j} {\kappa_{nj}},

Λnj=Λ0j+Sj+κ0jnjκnj(𝐲j𝛍0j)(𝐲j𝛍0j).\Lambda_{nj} = \Lambda_{0j} + S_j + \frac{\kappa_{0j}\,n_j}{\kappa_{nj}} (\bar{\mathbf{y}}_j - \boldsymbol{\mu}_{0j}) (\bar{\mathbf{y}}_j - \boldsymbol{\mu}_{0j})^\top.

The marginal posterior of the group mean 𝛍j\boldsymbol{\mu}_j is a bivariate non-standardised tt-distribution:

𝛍j𝐘jtνnj1(𝛍nj,Λnjκnj(νnj1)).\boldsymbol{\mu}_j \mid \mathbf{Y}_j \;\sim\; t_{\nu_{nj} - 1}\!\!\left(\boldsymbol{\mu}_{nj},\; \frac{\Lambda_{nj}}{\kappa_{nj}(\nu_{nj} - 1)}\right).

Under the vague prior, this simplifies to

𝛍j𝐘jtnj2(𝐲j,Sjnj(nj2)).\boldsymbol{\mu}_j \mid \mathbf{Y}_j \;\sim\; t_{n_j - 2}\!\!\left(\bar{\mathbf{y}}_j,\; \frac{S_j}{n_j(n_j - 2)}\right).

1.3 Posterior of the Bivariate Treatment Effect

The bivariate treatment effect is 𝛉=𝛍t𝛍c\boldsymbol{\theta} = \boldsymbol{\mu}_t - \boldsymbol{\mu}_c. Since the two groups are independent, the posterior of 𝛉\boldsymbol{\theta} is the distribution of the difference of two independent bivariate tt-vectors:

Tjtνnj1(𝛍nj,Vj),T_j \;\sim\; t_{\nu_{nj}-1}(\boldsymbol{\mu}_{nj},\, V_j),

where the posterior scale matrix is Vj=Λnj/[κnj(νnj1)]V_j = \Lambda_{nj} / [\kappa_{nj}(\nu_{nj}-1)].

The posterior probability that 𝛉\boldsymbol{\theta} falls in a rectangular region RR defined by thresholds on each component is

P(𝛉R𝐘)=P(TtTcR),P(\boldsymbol{\theta} \in R \mid \mathbf{Y}) = P(T_t - T_c \in R),

computed by one of two methods (MC or MM) described in Section 3.

1.4 Nine-Region Grid (Posterior Probability)

The bivariate threshold grid for (θ1,θ2)(\theta_1, \theta_2) creates a 3×33 \times 3 partition using theta_TV1, theta_MAV1, theta_TV2, theta_MAV2:

Nine-region grid for two-endpoint posterior probability
Endpoint 1
θ1 > θTV1 θTV1 ≥ θ1 > θMAV1 θMAV1 ≥ θ1
Endpoint 2 θ2 > θTV2 R1 R4 R7
θTV2 ≥ θ2 > θMAV2 R2 R5 R8
θMAV2 ≥ θ2 R3 R6 R9

Region R1R_1 (both endpoints exceed θTV\theta_{\mathrm{TV}}) is typically designated as Go and R9R_9 (both endpoints below θMAV\theta_{\mathrm{MAV}}) as NoGo.

1.5 Four-Region Grid (Predictive Probability)

For the predictive probability, a 2×22 \times 2 partition is used, defined by theta_NULL1 and theta_NULL2:

Four-region grid for two-endpoint predictive probability
Endpoint 1
θ1 > θNULL1 θ1 ≤ θNULL1
Endpoint 2 θ2 > θNULL2 R1 R3
θ2 ≤ θNULL2 R2 R4

Region R1R_1 (both endpoints exceed θNULL\theta_{\mathrm{NULL}}) is typically designated as Go and R4R_4 (both endpoints at or below θNULL\theta_{\mathrm{NULL}}) as NoGo.


2. Posterior Predictive Distribution

The predictive distribution of the future sample mean 𝐲̃j\bar{\tilde{\mathbf{y}}}_j based on mjm_j future patients (m_t or m_c) is again a bivariate tt-distribution:

𝐲̃j𝐘jtνnj1(𝛍nj,Λnjκnj(νnj1)κnj+1κnj1mj)\bar{\tilde{\mathbf{y}}}_j \mid \mathbf{Y}_j \;\sim\; t_{\nu_{nj}-1}\!\!\left(\boldsymbol{\mu}_{nj},\; \frac{\Lambda_{nj}}{\kappa_{nj}(\nu_{nj}-1)} \cdot \frac{\kappa_{nj} + 1}{\kappa_{nj}} \cdot \frac{1}{m_j} \right)

under the NIW prior. Under the vague prior the scale inflates to Sj(nj+1)/[nj2(nj2)mj]S_j(n_j + 1) / [n_j^2(n_j - 2) m_j].


3. Two Computation Methods

The computation method is specified via the CalcMethod argument.

3.1 Monte Carlo Simulation (CalcMethod = 'MC')

For each of nMCn_\mathrm{MC} draws (nMC), independent samples are generated:

Tt(i)tνnt1(𝛍nt,Vt),Tc(i)tνnc1(𝛍nc,Vc),T_t^{(i)} \;\sim\; t_{\nu_{nt}-1}(\boldsymbol{\mu}_{nt}, V_t), \qquad T_c^{(i)} \;\sim\; t_{\nu_{nc}-1}(\boldsymbol{\mu}_{nc}, V_c),

and the probability of region RR_\ell is estimated as the empirical proportion:

P̂(R)=1nMCi=1nMC𝟏[Tt(i)Tc(i)R].\widehat{P}(R_\ell) = \frac{1}{n_\mathrm{MC}} \sum_{i=1}^{n_\mathrm{MC}} \mathbf{1}\!\left[T_t^{(i)} - T_c^{(i)} \in R_\ell\right].

When pbayespostpred2cont() is called in vectorised mode over nsimn_\mathrm{sim} replicates, all nMCn_\mathrm{MC} standard normal and chi-squared draws are pre-generated once and reused; only the Cholesky factor of the replicate-specific scale matrix is recomputed per observation.

3.2 Moment-Matching Approximation (CalcMethod = 'MM')

The difference 𝐃=TtTc\boldsymbol{D} = T_t - T_c is approximated by a bivariate non-standardised tt-distribution (Yamaguchi et al., 2025, Theorem 3). Let VjV_j be the scale matrix of TjT_j (j=t,cj = t, c), and define

A=(νtνt2Vt+νcνc2Vc)1,A = \left(\frac{\nu_t}{\nu_t - 2} V_t + \frac{\nu_c}{\nu_c - 2} V_c\right)^{\!-1},

Qm=1p(p+2)[νt2(νt2)(νt4){(tr(AVt))2+2tr(AVtAVt)}Q_m = \frac{1}{p(p+2)}\Biggl[ \frac{\nu_t^2}{(\nu_t-2)(\nu_t-4)} \left\{(\mathrm{tr}(AV_t))^2 + 2\,\mathrm{tr}(AV_t AV_t)\right\}

+νc2(νc2)(νc4){(tr(AVc))2+2tr(AVcAVc)}\quad + \frac{\nu_c^2}{(\nu_c-2)(\nu_c-4)} \left\{(\mathrm{tr}(AV_c))^2 + 2\,\mathrm{tr}(AV_c AV_c)\right\}

+2νtνc(νt2)(νc2){tr(AVt)tr(AVc)+2tr(AVtAVc)}]\quad + \frac{2\nu_t\nu_c}{(\nu_t-2)(\nu_c-2)} \left\{\mathrm{tr}(AV_t)\,\mathrm{tr}(AV_c) + 2\,\mathrm{tr}(AV_t AV_c)\right\} \Biggr]

with p=2p = 2. Then 𝐃Z*tν*(𝛍*,Σ*)\boldsymbol{D} \approx Z^* \sim t_{\nu^*}(\boldsymbol{\mu}^*, \Sigma^*), where

𝛍*=𝛍t𝛍c,\boldsymbol{\mu}^* = \boldsymbol{\mu}_t - \boldsymbol{\mu}_c,

ν*=24Qm1Qm,\nu^* = \frac{2 - 4Q_m}{1 - Q_m},

Σ*=(νtνt2Vt+νcνc2Vc)ν*2ν*.\Sigma^* = \left(\frac{\nu_t}{\nu_t - 2} V_t + \frac{\nu_c}{\nu_c - 2} V_c\right) \frac{\nu^* - 2}{\nu^*}.

The joint probability over each rectangular region is evaluated by a single call to mvtnorm::pmvt(), which avoids simulation entirely and is exact in the normal limit (νnj\nu_{nj} \to \infty).

The MM method requires νnj1>4\nu_{nj} - 1 > 4 for both groups (νj>4\nu_j > 4 in the notation above); if this condition is not met, the function falls back to MC automatically.


4. Study Designs

4.1 Controlled Design

Both groups are observed in the PoC trial. All NIW posterior parameters are updated from the current data.

Posterior probability – vague prior:

S_t <- matrix(c(18.0, 3.6, 3.6, 9.0), 2, 2)
S_c <- matrix(c(16.0, 2.8, 2.8, 8.5), 2, 2)

set.seed(42)
p_post_vague <- pbayespostpred2cont(
  prob = 'posterior', design = 'controlled', prior = 'vague',
  theta_TV1  = 1.5, theta_MAV1 = 0.5,
  theta_TV2  = 1.0, theta_MAV2 = 0.3,
  theta_NULL1 = NULL, theta_NULL2 = NULL,
  n_t = 20L, n_c = 20L,
  ybar_t = c(3.5, 2.1), S_t = S_t,
  ybar_c = c(1.8, 1.0), S_c = S_c,
  m_t = NULL, m_c = NULL,
  kappa0_t = NULL, nu0_t = NULL, mu0_t = NULL, Lambda0_t = NULL,
  kappa0_c = NULL, nu0_c = NULL, mu0_c = NULL, Lambda0_c = NULL,
  r = NULL,
  ne_t = NULL, ne_c = NULL, alpha0e_t = NULL, alpha0e_c = NULL,
  bar_ye_t = NULL, bar_ye_c = NULL, se_t = NULL, se_c = NULL,
  nMC = 5000L
)
print(round(p_post_vague, 4))
#>     R1     R2     R3     R4     R5     R6     R7     R8     R9 
#> 0.5056 0.2198 0.0000 0.1478 0.1264 0.0002 0.0000 0.0002 0.0000
cat(sprintf(
  "g_Go  = P(R1 | data) = %.4f\n", p_post_vague["R1"]
))
#> g_Go  = P(R1 | data) = 0.5056
cat(sprintf(
  "g_NoGo = P(R9 | data) = %.4f\n\n", p_post_vague["R9"]
))
#> g_NoGo = P(R9 | data) = 0.0000
cat(sprintf(
  "Go  criterion: g_Go  >= gamma1 (0.80)? %s\n",
  ifelse(p_post_vague["R1"] >= 0.80, "YES", "NO")
))
#> Go  criterion: g_Go  >= gamma1 (0.80)? NO
cat(sprintf(
  "NoGo criterion: g_NoGo >= gamma2 (0.20)? %s\n",
  ifelse(p_post_vague["R9"] >= 0.20, "YES", "NO")
))
#> NoGo criterion: g_NoGo >= gamma2 (0.20)? NO
cat(sprintf("Decision: %s\n",
  ifelse(p_post_vague["R1"] >= 0.80 & p_post_vague["R9"] <  0.20, "Go",
  ifelse(p_post_vague["R1"] <  0.80 & p_post_vague["R9"] >= 0.20, "NoGo",
  ifelse(p_post_vague["R1"] >= 0.80 & p_post_vague["R9"] >= 0.20, "Miss",
                                                                    "Gray")))
))
#> Decision: Gray

Posterior probability – NIW informative prior:

L0 <- matrix(c(8.0, 0.0, 0.0, 2.0), 2, 2)

set.seed(42)
p_post_niw <- pbayespostpred2cont(
  prob = 'posterior', design = 'controlled', prior = 'N-Inv-Wishart',
  theta_TV1  = 1.5, theta_MAV1 = 0.5,
  theta_TV2  = 1.0, theta_MAV2 = 0.3,
  theta_NULL1 = NULL, theta_NULL2 = NULL,
  n_t = 20L, n_c = 20L,
  ybar_t = c(3.5, 2.1), S_t = S_t,
  ybar_c = c(1.8, 1.0), S_c = S_c,
  m_t = NULL, m_c = NULL,
  kappa0_t = 2.0, nu0_t = 5.0, mu0_t = c(2.0, 1.0), Lambda0_t = L0,
  kappa0_c = 2.0, nu0_c = 5.0, mu0_c = c(0.0, 0.0), Lambda0_c = L0,
  r = NULL,
  ne_t = NULL, ne_c = NULL, alpha0e_t = NULL, alpha0e_c = NULL,
  bar_ye_t = NULL, bar_ye_c = NULL, se_t = NULL, se_c = NULL,
  nMC = 5000L
)
print(round(p_post_niw, 4))
#>     R1     R2     R3     R4     R5     R6     R7     R8     R9 
#> 0.5116 0.2228 0.0000 0.1320 0.1324 0.0010 0.0002 0.0000 0.0000

Posterior predictive probability (future Phase III: mt=mc=60m_t = m_c = 60):

set.seed(42)
p_pred <- pbayespostpred2cont(
  prob = 'predictive', design = 'controlled', prior = 'vague',
  theta_TV1 = NULL, theta_MAV1 = NULL,
  theta_TV2 = NULL, theta_MAV2 = NULL,
  theta_NULL1 = 0.5, theta_NULL2 = 0.3,
  n_t = 20L, n_c = 20L,
  ybar_t = c(3.5, 2.1), S_t = S_t,
  ybar_c = c(1.8, 1.0), S_c = S_c,
  m_t = 60L, m_c = 60L,
  kappa0_t = NULL, nu0_t = NULL, mu0_t = NULL, Lambda0_t = NULL,
  kappa0_c = NULL, nu0_c = NULL, mu0_c = NULL, Lambda0_c = NULL,
  r = NULL,
  ne_t = NULL, ne_c = NULL, alpha0e_t = NULL, alpha0e_c = NULL,
  bar_ye_t = NULL, bar_ye_c = NULL, se_t = NULL, se_c = NULL,
  nMC = 5000L
)
print(round(p_pred, 4))
#> R1 R2 R3 R4 
#>  1  0  0  0
cat(sprintf(
  "\nGo  region (R1): P = %.4f  >= gamma1 (0.80)? %s\n",
  p_pred["R1"], ifelse(p_pred["R1"] >= 0.80, "YES", "NO")
))
#> 
#> Go  region (R1): P = 1.0000  >= gamma1 (0.80)? YES
cat(sprintf(
  "NoGo region (R4): P = %.4f  >= gamma2 (0.20)? %s\n",
  p_pred["R4"], ifelse(p_pred["R4"] >= 0.20, "YES", "NO")
))
#> NoGo region (R4): P = 0.0000  >= gamma2 (0.20)? NO
cat(sprintf("Decision: %s\n",
  ifelse(p_pred["R1"] >= 0.80 & p_pred["R4"] <  0.20, "Go",
  ifelse(p_pred["R1"] <  0.80 & p_pred["R4"] >= 0.20, "NoGo",
  ifelse(p_pred["R1"] >= 0.80 & p_pred["R4"] >= 0.20, "Miss",
                                                        "Gray")))
))
#> Decision: Go

4.2 Uncontrolled Design

No concurrent control group is enrolled. Under the NIW prior, the hypothetical control distribution is

𝛍ctνnt1(𝛍0c,rΛntκnt(νnt1)),\boldsymbol{\mu}_c \;\sim\; t_{\nu_{nt}-1}\!\!\left(\boldsymbol{\mu}_{0c},\; r \cdot \frac{\Lambda_{nt}}{\kappa_{nt}(\nu_{nt}-1)}\right),

where 𝛍0c\boldsymbol{\mu}_{0c} (mu0_c) is the assumed control mean, r>0r > 0 (r) scales the covariance relative to the treatment group, and Λnt\Lambda_{nt} is the posterior scale matrix of the treatment group.

set.seed(1)
p_unctrl <- pbayespostpred2cont(
  prob = 'posterior', design = 'uncontrolled', prior = 'N-Inv-Wishart',
  theta_TV1  = 1.5, theta_MAV1 = 0.5,
  theta_TV2  = 1.0, theta_MAV2 = 0.3,
  theta_NULL1 = NULL, theta_NULL2 = NULL,
  n_t = 20L, n_c = NULL,
  ybar_t = c(3.5, 2.1), S_t = S_t,
  ybar_c = NULL,        S_c = NULL,
  m_t = NULL, m_c = NULL,
  kappa0_t = 2.0, nu0_t = 5.0, mu0_t = c(2.0, 1.0), Lambda0_t = L0,
  kappa0_c = NULL, nu0_c = NULL, mu0_c = c(0.0, 0.0), Lambda0_c = NULL,
  r = 1.0,
  ne_t = NULL, ne_c = NULL, alpha0e_t = NULL, alpha0e_c = NULL,
  bar_ye_t = NULL, bar_ye_c = NULL, se_t = NULL, se_c = NULL,
  nMC = 5000L
)
print(round(p_unctrl, 4))
#> R1 R2 R3 R4 R5 R6 R7 R8 R9 
#>  1  0  0  0  0  0  0  0  0

4.3 External Design (Power Prior)

External data for group jj (sample size ne,jn_{e,j}, sample mean 𝐲e,j\bar{\mathbf{y}}_{e,j}, sum-of-squares matrix Se,jS_{e,j}) are incorporated via a power prior with weight α0e,j(0,1]\alpha_{0e,j} \in (0,1]. Both prior = 'vague' and prior = 'N-Inv-Wishart' are supported.

Vague prior (prior = 'vague')

The posterior parameters after combining the vague power prior with the PoC data are given by Corollary 2 of Huang et al. (2025):

𝛍nj*=α0e,jne,j𝐲e,j+nj𝐲jκnj*,\boldsymbol{\mu}_{nj}^* = \frac{\alpha_{0e,j}\,n_{e,j}\,\bar{\mathbf{y}}_{e,j} + n_j\,\bar{\mathbf{y}}_j} {\kappa_{nj}^*},

κnj*=α0e,jne,j+nj,νnj*=α0e,jne,j+nj1,\kappa_{nj}^* = \alpha_{0e,j}\,n_{e,j} + n_j, \qquad \nu_{nj}^* = \alpha_{0e,j}\,n_{e,j} + n_j - 1,

Λnj*=α0e,jSe,j+Sj+α0e,jne,jnjκnj*(𝐲j𝐲e,j)(𝐲j𝐲e,j).\Lambda_{nj}^* = \alpha_{0e,j}\,S_{e,j} + S_j + \frac{\alpha_{0e,j}\,n_{e,j}\,n_j}{\kappa_{nj}^*} (\bar{\mathbf{y}}_j - \bar{\mathbf{y}}_{e,j}) (\bar{\mathbf{y}}_j - \bar{\mathbf{y}}_{e,j})^\top.

The marginal posterior of 𝛍j\boldsymbol{\mu}_j is then

𝛍j𝐘jtνnj*1(𝛍nj*,Λnj*κnj*(νnj*1)).\boldsymbol{\mu}_j \mid \mathbf{Y}_j \;\sim\; t_{\nu_{nj}^* - 1}\!\!\left(\boldsymbol{\mu}_{nj}^*,\; \frac{\Lambda_{nj}^*}{\kappa_{nj}^*(\nu_{nj}^* - 1)}\right).

S_t     <- matrix(c(18.0, 3.6, 3.6, 9.0), 2, 2)
S_c     <- matrix(c(16.0, 2.8, 2.8, 8.5), 2, 2)
Se2_ext <- matrix(c(15.0, 2.5, 2.5, 7.5), 2, 2)

set.seed(2)
p_ext_vague <- pbayespostpred2cont(
  prob = 'posterior', design = 'external', prior = 'vague',
  theta_TV1  = 1.5, theta_MAV1 = 0.5,
  theta_TV2  = 1.0, theta_MAV2 = 0.3,
  theta_NULL1 = NULL, theta_NULL2 = NULL,
  n_t = 20L, n_c = 20L,
  ybar_t = c(3.5, 2.1), S_t = S_t,
  ybar_c = c(1.8, 1.0), S_c = S_c,
  m_t = NULL, m_c = NULL,
  kappa0_t = NULL, nu0_t = NULL, mu0_t = NULL, Lambda0_t = NULL,
  kappa0_c = NULL, nu0_c = NULL, mu0_c = NULL, Lambda0_c = NULL,
  r = NULL,
  ne_t = NULL, ne_c = 10L, alpha0e_t = NULL, alpha0e_c = 0.5,
  bar_ye_t = NULL, bar_ye_c = c(1.5, 0.8), se_t = NULL, se_c = Se2_ext,
  nMC = 5000L
)
print(round(p_ext_vague, 4))
#>     R1     R2     R3     R4     R5     R6     R7     R8     R9 
#> 0.6164 0.1900 0.0002 0.1184 0.0750 0.0000 0.0000 0.0000 0.0000

N-Inv-Wishart prior (prior = 'N-Inv-Wishart')

The power prior first combines the initial NIW prior with the discounted external data (Theorem 5 of Huang et al., 2025):

𝛍e,j=α0e,jne,j𝐲e,j+κ0j𝛍0jκe,j,\boldsymbol{\mu}_{e,j} = \frac{\alpha_{0e,j}\,n_{e,j}\,\bar{\mathbf{y}}_{e,j} + \kappa_{0j}\,\boldsymbol{\mu}_{0j}} {\kappa_{e,j}},

κe,j=α0e,jne,j+κ0j,νe,j=α0e,jne,j+ν0j,\kappa_{e,j} = \alpha_{0e,j}\,n_{e,j} + \kappa_{0j}, \qquad \nu_{e,j} = \alpha_{0e,j}\,n_{e,j} + \nu_{0j},

Λe,j=α0e,jSe,j+Λ0j+κ0jα0e,jne,jκe,j(𝛍0j𝐲e,j)(𝛍0j𝐲e,j).\Lambda_{e,j} = \alpha_{0e,j}\,S_{e,j} + \Lambda_{0j} + \frac{\kappa_{0j}\,\alpha_{0e,j}\,n_{e,j}}{\kappa_{e,j}} (\boldsymbol{\mu}_{0j} - \bar{\mathbf{y}}_{e,j}) (\boldsymbol{\mu}_{0j} - \bar{\mathbf{y}}_{e,j})^\top.

This intermediate NIW result is then updated with the current PoC data by standard conjugate updating (Theorem 6 of Huang et al., 2025):

𝛍nj*=κe,j𝛍e,j+nj𝐲jκnj*,\boldsymbol{\mu}_{nj}^* = \frac{\kappa_{e,j}\,\boldsymbol{\mu}_{e,j} + n_j\,\bar{\mathbf{y}}_j} {\kappa_{nj}^*},

κnj*=κe,j+nj,νnj*=νe,j+nj,\kappa_{nj}^* = \kappa_{e,j} + n_j, \qquad \nu_{nj}^* = \nu_{e,j} + n_j,

Λnj*=Λe,j+Sj+κe,jnjκnj*(𝐲j𝛍e,j)(𝐲j𝛍e,j).\Lambda_{nj}^* = \Lambda_{e,j} + S_j + \frac{\kappa_{e,j}\,n_j}{\kappa_{nj}^*} (\bar{\mathbf{y}}_j - \boldsymbol{\mu}_{e,j}) (\bar{\mathbf{y}}_j - \boldsymbol{\mu}_{e,j})^\top.

The marginal posterior of 𝛍j\boldsymbol{\mu}_j is then

𝛍j𝐘jtνnj*1(𝛍nj*,Λnj*κnj*(νnj*1)).\boldsymbol{\mu}_j \mid \mathbf{Y}_j \;\sim\; t_{\nu_{nj}^* - 1}\!\!\left(\boldsymbol{\mu}_{nj}^*,\; \frac{\Lambda_{nj}^*}{\kappa_{nj}^*(\nu_{nj}^* - 1)}\right).

S_t     <- matrix(c(18.0, 3.6, 3.6, 9.0), 2, 2)
S_c     <- matrix(c(16.0, 2.8, 2.8, 8.5), 2, 2)
L0      <- matrix(c(8.0, 0.0, 0.0, 2.0), 2, 2)
Se2_ext <- matrix(c(15.0, 2.5, 2.5, 7.5), 2, 2)

set.seed(3)
p_ext <- pbayespostpred2cont(
  prob = 'posterior', design = 'external', prior = 'N-Inv-Wishart',
  theta_TV1  = 1.5, theta_MAV1 = 0.5,
  theta_TV2  = 1.0, theta_MAV2 = 0.3,
  theta_NULL1 = NULL, theta_NULL2 = NULL,
  n_t = 20L, n_c = 20L,
  ybar_t = c(3.5, 2.1), S_t = S_t,
  ybar_c = c(1.8, 1.0), S_c = S_c,
  m_t = NULL, m_c = NULL,
  kappa0_t = 2.0, nu0_t = 5.0, mu0_t = c(2.0, 1.0), Lambda0_t = L0,
  kappa0_c = 2.0, nu0_c = 5.0, mu0_c = c(0.0, 0.0), Lambda0_c = L0,
  r = NULL,
  ne_t = NULL, ne_c = 10L, alpha0e_t = NULL, alpha0e_c = 0.5,
  bar_ye_t = NULL, bar_ye_c = c(1.5, 0.8), se_t = NULL, se_c = Se2_ext,
  nMC = 5000L
)
print(round(p_ext, 4))
#>     R1     R2     R3     R4     R5     R6     R7     R8     R9 
#> 0.5674 0.2008 0.0000 0.1212 0.1100 0.0002 0.0000 0.0004 0.0000

5. Operating Characteristics

5.1 Definition

For given true parameters (𝛍t,Σt,𝛍c,Σc)(\boldsymbol{\mu}_t, \Sigma_t, \boldsymbol{\mu}_c, \Sigma_c) (mu_t, Sigma_t, mu_c, Sigma_c), the operating characteristics are estimated by Monte Carlo over nsimn_\mathrm{sim} replicates (nsim). Let ĝGo,i=P(GoRegions𝐘(i))\hat{g}_{\mathrm{Go},i} = P(\text{GoRegions} \mid \mathbf{Y}^{(i)}) and ĝNoGo,i=P(NoGoRegions𝐘(i))\hat{g}_{\mathrm{NoGo},i} = P(\text{NoGoRegions} \mid \mathbf{Y}^{(i)}) for the ii-th simulated dataset. Then:

Pr̂(Go)=1nsimi=1nsim𝟏[ĝGo,iγ1andĝNoGo,i<γ2],\widehat{\Pr}(\mathrm{Go}) = \frac{1}{n_\mathrm{sim}} \sum_{i=1}^{n_\mathrm{sim}} \mathbf{1}\!\left[\hat{g}_{\mathrm{Go},i} \ge \gamma_1 \;\text{and}\; \hat{g}_{\mathrm{NoGo},i} < \gamma_2\right],

Pr̂(NoGo)=1nsimi=1nsim𝟏[ĝNoGo,iγ2andĝGo,i<γ1],\widehat{\Pr}(\mathrm{NoGo}) = \frac{1}{n_\mathrm{sim}} \sum_{i=1}^{n_\mathrm{sim}} \mathbf{1}\!\left[\hat{g}_{\mathrm{NoGo},i} \ge \gamma_2 \;\text{and}\; \hat{g}_{\mathrm{Go},i} < \gamma_1\right],

Pr̂(Miss)=1nsimi=1nsim𝟏[ĝGo,iγ1andĝNoGo,iγ2],\widehat{\Pr}(\mathrm{Miss}) = \frac{1}{n_\mathrm{sim}} \sum_{i=1}^{n_\mathrm{sim}} \mathbf{1}\!\left[\hat{g}_{\mathrm{Go},i} \ge \gamma_1 \;\text{and}\; \hat{g}_{\mathrm{NoGo},i} \ge \gamma_2\right],

Pr̂(Gray)=1Pr̂(Go)Pr̂(NoGo)Pr̂(Miss).\widehat{\Pr}(\mathrm{Gray}) = 1 - \widehat{\Pr}(\mathrm{Go}) - \widehat{\Pr}(\mathrm{NoGo}) - \widehat{\Pr}(\mathrm{Miss}).

Here γ1\gamma_1 and γ2\gamma_2 correspond to arguments gamma_go and gamma_nogo, respectively. A Miss (ĝGoγ1\hat{g}_{\mathrm{Go}} \ge \gamma_1 and ĝNoGoγ2\hat{g}_{\mathrm{NoGo}} \ge \gamma_2 simultaneously) indicates an inconsistent threshold configuration and triggers an error by default (error_if_Miss = TRUE).

5.2 Example: Controlled Design, Posterior Probability

Treatment effect scenarios are defined over a two-dimensional grid of (μt1,μt2)(\mu_{t1}, \mu_{t2}) while fixing 𝛍c=(0,0)\boldsymbol{\mu}_c = (0, 0)^\top. The full factorial combination of unique μt1\mu_{t1} and μt2\mu_{t2} values triggers the tile plot in plot(), with colour intensity encoding Pr(Go)\Pr(\mathrm{Go}):

Sigma <- matrix(c(4.0, 0.8, 0.8, 1.0), 2, 2)

mu_t1_seq <- seq(0.0, 3.5, by = 0.5)
mu_t2_seq <- seq(0.0, 2.1, by = 0.3)
n_scen    <- length(mu_t1_seq) * length(mu_t2_seq)

oc_ctrl <- pbayesdecisionprob2cont(
  nsim = 100L, prob = 'posterior', design = 'controlled',
  prior = 'vague',
  GoRegions = 1L, NoGoRegions = 9L,
  gamma_go = 0.80, gamma_nogo = 0.20,
  theta_TV1  = 1.5, theta_MAV1 = 0.5,
  theta_TV2  = 1.0, theta_MAV2 = 0.3,
  theta_NULL1 = NULL, theta_NULL2 = NULL,
  n_t = 20L, n_c = 20L, m_t = NULL, m_c = NULL,
  mu_t    = cbind(rep(mu_t1_seq, times = length(mu_t2_seq)),
                  rep(mu_t2_seq, each  = length(mu_t1_seq))),
  Sigma_t = Sigma,
  mu_c    = matrix(0, nrow = n_scen, ncol = 2),
  Sigma_c = Sigma,
  kappa0_t = NULL, nu0_t = NULL, mu0_t = NULL, Lambda0_t = NULL,
  kappa0_c = NULL, nu0_c = NULL, mu0_c = NULL, Lambda0_c = NULL,
  r = NULL,
  ne_t = NULL, ne_c = NULL, alpha0e_t = NULL, alpha0e_c = NULL,
  bar_ye_t = NULL, bar_ye_c = NULL, se_t = NULL, se_c = NULL,
  nMC = 500L, CalcMethod = 'MC',
  error_if_Miss = TRUE, Gray_inc_Miss = FALSE, seed = 42L
)
print(oc_ctrl)
#> Go/NoGo/Gray Decision Probabilities (Two Continuous Endpoints) 
#> ---------------------------------------------------------------- 
#>   Probability type : posterior 
#>   Design           : controlled 
#>   Prior            : vague 
#>   Method           : NULL 
#>   Simulations      : nsim = 100 
#>   MC draws         : nMC = 500 
#>   Seed             : 42 
#>   Threshold(s)     : TV1 = 1.5, MAV1 = 0.5 
#>                      TV2 = 1, MAV2 = 0.3 
#>   Go  threshold    : gamma_go = 0.8 
#>   NoGo threshold   : gamma_nogo = 0.2 
#>   Go  regions      : {1} 
#>   NoGo regions     : {9} 
#>   Sample size      : n_t = 20, n_c = 20 
#>   True cov (treat.): Sigma_t = [4, 0.8; 0.8, 1] 
#>   True cov (cont.) : Sigma_c = [4, 0.8; 0.8, 1] 
#>   Miss handling    : error_if_Miss = TRUE, Gray_inc_Miss = FALSE 
#> ---------------------------------------------------------------- 
#>  mu_t1 mu_t2 mu_c1 mu_c2     Go   Gray   NoGo
#>    0.0   0.0     0     0 0.0000 0.0800 0.9200
#>    0.5   0.0     0     0 0.0000 0.2100 0.7900
#>    1.0   0.0     0     0 0.0000 0.5900 0.4100
#>    1.5   0.0     0     0 0.0000 0.8600 0.1400
#>    2.0   0.0     0     0 0.0000 0.9400 0.0600
#>    2.5   0.0     0     0 0.0000 0.9900 0.0100
#>    3.0   0.0     0     0 0.0000 1.0000 0.0000
#>    3.5   0.0     0     0 0.0000 1.0000 0.0000
#>    0.0   0.3     0     0 0.0000 0.2600 0.7400
#>    0.5   0.3     0     0 0.0000 0.4000 0.6000
#>    1.0   0.3     0     0 0.0000 0.6800 0.3200
#>    1.5   0.3     0     0 0.0000 0.8700 0.1300
#>    2.0   0.3     0     0 0.0000 0.9400 0.0600
#>    2.5   0.3     0     0 0.0000 0.9900 0.0100
#>    3.0   0.3     0     0 0.0000 1.0000 0.0000
#>    3.5   0.3     0     0 0.0000 1.0000 0.0000
#>    0.0   0.6     0     0 0.0000 0.6200 0.3800
#>    0.5   0.6     0     0 0.0000 0.6600 0.3400
#>    1.0   0.6     0     0 0.0000 0.7900 0.2100
#>    1.5   0.6     0     0 0.0000 0.9300 0.0700
#>    2.0   0.6     0     0 0.0200 0.9400 0.0400
#>    2.5   0.6     0     0 0.0300 0.9600 0.0100
#>    3.0   0.6     0     0 0.0300 0.9700 0.0000
#>    3.5   0.6     0     0 0.0300 0.9700 0.0000
#>    0.0   0.9     0     0 0.0000 0.8700 0.1300
#>    0.5   0.9     0     0 0.0000 0.9000 0.1000
#>    1.0   0.9     0     0 0.0100 0.9200 0.0700
#>    1.5   0.9     0     0 0.0400 0.9000 0.0600
#>    2.0   0.9     0     0 0.0400 0.9300 0.0300
#>    2.5   0.9     0     0 0.0600 0.9400 0.0000
#>    3.0   0.9     0     0 0.1100 0.8900 0.0000
#>    3.5   0.9     0     0 0.1400 0.8600 0.0000
#>    0.0   1.2     0     0 0.0000 0.9500 0.0500
#>    0.5   1.2     0     0 0.0000 0.9500 0.0500
#>    1.0   1.2     0     0 0.0100 0.9700 0.0200
#>    1.5   1.2     0     0 0.0500 0.9300 0.0200
#>    2.0   1.2     0     0 0.1200 0.8800 0.0000
#>    2.5   1.2     0     0 0.3000 0.7000 0.0000
#>    3.0   1.2     0     0 0.4000 0.6000 0.0000
#>    3.5   1.2     0     0 0.3900 0.6100 0.0000
#>    0.0   1.5     0     0 0.0000 1.0000 0.0000
#>    0.5   1.5     0     0 0.0000 1.0000 0.0000
#>    1.0   1.5     0     0 0.0300 0.9700 0.0000
#>    1.5   1.5     0     0 0.0800 0.9200 0.0000
#>    2.0   1.5     0     0 0.2700 0.7300 0.0000
#>    2.5   1.5     0     0 0.5900 0.4100 0.0000
#>    3.0   1.5     0     0 0.7400 0.2600 0.0000
#>    3.5   1.5     0     0 0.7700 0.2300 0.0000
#>    0.0   1.8     0     0 0.0000 1.0000 0.0000
#>    0.5   1.8     0     0 0.0000 1.0000 0.0000
#>    1.0   1.8     0     0 0.0300 0.9700 0.0000
#>    1.5   1.8     0     0 0.1000 0.9000 0.0000
#>    2.0   1.8     0     0 0.4100 0.5900 0.0000
#>    2.5   1.8     0     0 0.7600 0.2400 0.0000
#>    3.0   1.8     0     0 0.8800 0.1200 0.0000
#>    3.5   1.8     0     0 0.9300 0.0700 0.0000
#>    0.0   2.1     0     0 0.0000 1.0000 0.0000
#>    0.5   2.1     0     0 0.0000 1.0000 0.0000
#>    1.0   2.1     0     0 0.0300 0.9700 0.0000
#>    1.5   2.1     0     0 0.1000 0.9000 0.0000
#>    2.0   2.1     0     0 0.3600 0.6400 0.0000
#>    2.5   2.1     0     0 0.8000 0.2000 0.0000
#>    3.0   2.1     0     0 0.9200 0.0800 0.0000
#>    3.5   2.1     0     0 0.9700 0.0300 0.0000
#> ----------------------------------------------------------------
plot(oc_ctrl, base_size = 20)

The same function supports design = 'uncontrolled' (with hypothetical control mean mu0_c and scale factor r) and design = 'external' (with power prior arguments ne_c, bar_ye_c, se_c, alpha0e_c). The argument prob = 'predictive' switches to predictive probability (with future sample sizes m_t, m_c and null thresholds theta_NULL1, theta_NULL2). The function signature and output format are identical across all combinations.


6.1 Objective and Algorithm

Because there are two decision thresholds (γ1,γ2)(\gamma_1, \gamma_2), the optimal search is performed over a two-dimensional grid Γ1×Γ2\Gamma_1 \times \Gamma_2 (arguments gamma_go_grid and gamma_nogo_grid). The two thresholds are calibrated independently under separate scenarios:

  • γ1*\gamma_1^*: calibrated under the Go-calibration scenario (mu_t_go, Sigma_t_go, mu_c_go, Sigma_c_go; typically the null scenario 𝛍t=𝛍c\boldsymbol{\mu}_t = \boldsymbol{\mu}_c), so that Pr(Go)<𝚝𝚊𝚛𝚐𝚎𝚝_𝚐𝚘\Pr(\mathrm{Go}) < \texttt{target\_go}.
  • γ2*\gamma_2^*: calibrated under the NoGo-calibration scenario (mu_t_nogo, Sigma_t_nogo, mu_c_nogo, Sigma_c_nogo; typically the alternative scenario), so that Pr(NoGo)<𝚝𝚊𝚛𝚐𝚎𝚝_𝚗𝚘𝚐𝚘\Pr(\mathrm{NoGo}) < \texttt{target\_nogo}.

getgamma2cont() uses a three-stage strategy:

Stage 1 (Simulate and precompute): nsimn_\mathrm{sim} bivariate datasets are generated separately from each calibration scenario. For each replicate ii, pbayespostpred2cont() is called once in vectorised mode to return the full region probability vector. The marginal Go and NoGo probabilities per replicate are:

ĝGo,i=GoRegionsp̂i,ĝNoGo,i=NoGoRegionsp̂i.\hat{g}_{\mathrm{Go},i} = \sum_{\ell \in \text{GoRegions}} \hat{p}_{i\ell}, \qquad \hat{g}_{\mathrm{NoGo},i} = \sum_{\ell \in \text{NoGoRegions}} \hat{p}_{i\ell}.

Stage 2 (Gamma sweep): For each candidate γ1Γ1\gamma_1 \in \Gamma_1 (under the Go-calibration scenario) and each γ2Γ2\gamma_2 \in \Gamma_2 (under the NoGo-calibration scenario):

Pr(Goγ1)=1nsimi=1nsim𝟏[ĝGo,iγ1],\Pr(\mathrm{Go} \mid \gamma_1) = \frac{1}{n_\mathrm{sim}} \sum_{i=1}^{n_\mathrm{sim}} \mathbf{1}\!\left[\hat{g}_{\mathrm{Go},i} \ge \gamma_1\right],

Pr(NoGoγ2)=1nsimi=1nsim𝟏[ĝNoGo,iγ2].\Pr(\mathrm{NoGo} \mid \gamma_2) = \frac{1}{n_\mathrm{sim}} \sum_{i=1}^{n_\mathrm{sim}} \mathbf{1}\!\left[\hat{g}_{\mathrm{NoGo},i} \ge \gamma_2\right].

The results are stored as vectors PrGo_grid and PrNoGo_grid indexed by gamma_go_grid and gamma_nogo_grid, respectively.

Stage 3 (Optimal selection): γ1*\gamma_1^* is the smallest grid value satisfying Pr(Goγ1)<𝚝𝚊𝚛𝚐𝚎𝚝_𝚐𝚘\Pr(\mathrm{Go} \mid \gamma_1) < \texttt{target\_go}; γ2*\gamma_2^* is the smallest grid value satisfying Pr(NoGoγ2)<𝚝𝚊𝚛𝚐𝚎𝚝_𝚗𝚘𝚐𝚘\Pr(\mathrm{NoGo} \mid \gamma_2) < \texttt{target\_nogo}.

6.2 Example: Controlled Design, Posterior Probability

Go-calibration scenario: 𝛍t=(0.5,0.3)\boldsymbol{\mu}_t = (0.5, 0.3)^\top, 𝛍c=(0,0)\boldsymbol{\mu}_c = (0, 0)^\top (effect at MAV boundary – above which Go is not yet warranted, so Pr(Go)<𝚝𝚊𝚛𝚐𝚎𝚝_𝚐𝚘\Pr(\mathrm{Go}) < \texttt{target\_go} is required). NoGo-calibration scenario: 𝛍t=(1.0,0.6)\boldsymbol{\mu}_t = (1.0, 0.6)^\top, 𝛍c=(0,0)\boldsymbol{\mu}_c = (0, 0)^\top (effect between MAV and TV – above which NoGo should be unlikely).

res_gamma <- getgamma2cont(
  nsim = 500L, prob = 'posterior', design = 'controlled',
  prior = 'vague',
  GoRegions = 1L, NoGoRegions = 9L,
  mu_t_go    = c(0.5, 0.3), Sigma_t_go    = Sigma,
  mu_c_go    = c(0.0, 0.0), Sigma_c_go    = Sigma,
  mu_t_nogo  = c(1.0, 0.6), Sigma_t_nogo  = Sigma,
  mu_c_nogo  = c(0.0, 0.0), Sigma_c_nogo  = Sigma,
  target_go = 0.05, target_nogo = 0.20,
  n_t = 20L, n_c = 20L,
  theta_TV1  = 1.5, theta_MAV1 = 0.5,
  theta_TV2  = 1.0, theta_MAV2 = 0.3,
  theta_NULL1 = NULL, theta_NULL2 = NULL,
  m_t = NULL, m_c = NULL,
  kappa0_t = NULL, nu0_t = NULL, mu0_t = NULL, Lambda0_t = NULL,
  kappa0_c = NULL, nu0_c = NULL, mu0_c = NULL, Lambda0_c = NULL,
  r = NULL,
  ne_t = NULL, ne_c = NULL, alpha0e_t = NULL, alpha0e_c = NULL,
  bar_ye_t = NULL, bar_ye_c = NULL, se_t = NULL, se_c = NULL,
  nMC = 500L, CalcMethod = 'MC',
  gamma_go_grid   = seq(0.05, 0.95, by = 0.05),
  gamma_nogo_grid = seq(0.05, 0.95, by = 0.05),
  seed = 42L
)
plot(res_gamma, base_size = 20)

The same function supports design = 'uncontrolled' (with mu0_c and r) and design = 'external' (with power prior arguments ne_c, bar_ye_c, se_c, alpha0e_c). Setting prob = 'predictive' switches to predictive probability calibration (with theta_NULL1, theta_NULL2, m_t, m_c). The function signature is identical across all combinations.


7. Summary

This vignette covered two continuous endpoint analysis in BayesianQDM:

  1. Bayesian model: NIW conjugate posterior with explicit update formulae for κnj\kappa_{nj}, νnj\nu_{nj}, 𝛍nj\boldsymbol{\mu}_{nj}, and Λnj\Lambda_{nj}; bivariate non-standardised tt marginal posterior.
  2. Nine-region grid for posterior probability and four-region grid for predictive probability; typical choice Go = R1, NoGo = R9 (posterior) or R4 (predictive).
  3. Two computation methods: MC (vectorised pre-generated draws with Cholesky reuse) and MM (Mahalanobis-distance fourth-moment matching via Yamaguchi et al. (2025), Theorem 3, + mvtnorm::pmvt for joint probability; requires νj>4\nu_j > 4).
  4. Three study designs: controlled, uncontrolled (𝛍0c\boldsymbol{\mu}_{0c} and rr fixed), external design (vague power prior follows Corollary 2 of Huang et al. (2025); NIW power prior follows Theorems 5–6).
  5. Operating characteristics: Monte Carlo estimation with vectorised region probability computation.
  6. Optimal threshold search: three-stage strategy in getgamma2cont() calibrating γ1*\gamma_1^* and γ2*\gamma_2^* independently under separate Go- and NoGo-calibration scenarios, with calibration curves visualised via plot() and optimal thresholds marked at the target probability levels (Pr(Go)<0.05\Pr(\mathrm{Go}) < 0.05 under null, Pr(NoGo)<0.20\Pr(\mathrm{NoGo}) < 0.20 under alternative).

See vignette("two-binary") for the analogous two binary endpoint analysis.