Derive: MuSyC Model -- Synergy Analysis
derive_MuSyC_model.Rmd
MuSyC Epistasis Model
When two different treatments are made in an assay, their combined
effect may be stronger or weaker than what would be expected with an
additive model, the treatments are said to be epistatic For sigmoidal
dose-response models, one treatment may effect of the other in two
different ways; by either shifting the maximal response (efficacy) or by
shifting the dose needed to cause the response (potency). A range of
statistical models have been proposed that capture different aspects of
synergy, notably Bliss independence(Bliss
1956) and Loewe additivity(Loewe
1926) models can be used to test for significant efficacy or
potency interactions, respectively. The SynergyFinder
R
package(Ianevski, Giri, and Aittokallio
2022) and the synergy
python package(Wooten and Albert 2021) can be used to
visualize treatment interactions, compute a range of synergy scores, and
test if the interactions are significant.
Recently Meyer et al.Wooten et al.
(2021) derived an integrated functional synergistic sigmoidal
dose-response called the Multi-dimensional Synergy of Combinations
(MuSyC) method, which has the Loewe and Bliss models as special cases.
They implemented a Bayesian model-fitting strategy in
Matlab
, and a maximum likelihood model-fitting into the
synergy python package. To make the model more accessible to the
pharmacology community, in this section, we briefly review the
MuSyC
functional form, describe a Bayesian implementation
in Stan
/BRMS
, and illustrate using the model
to re-analyze how drugs and voltage may interact to modulate the current
through a potassium channel.
MuSyC Functional Form: The functional form for the MuSyC model gives an equation for the response $\color{brown}{E_d}$ at doses of $\color{teal}{d_1}$ and $\color{teal}{d_2}$ of the two treatments and has free parameters $\theta = \left({\color{purple}{C_1}}, {\color{purple}{C_2}}, {\color{brown}{E_0}}, {\color{brown}{E_1}}, {\color{brown}{E_2}}, {\color{brown}{E_3}}, {\color{purple}{h_1}}, {\color{purple}{h_2}}, {\color{purple}{\alpha}}\right)$
$$\begin{align} {\color{brown}{E_d}} = \mbox{MuSyC}({\color{teal}{d_1}}, {\color{teal}{d_2}}; \theta) &= \frac{ {\color{purple}{C_1}}^{\color{purple}{h_1}}{\color{purple}{C_2}}^{\color{purple}{h_2}}{\color{brown}{E_0}} + {\color{teal}{d_1}}^{\color{purple}{h_1}}{\color{purple}{C_2}}^{\color{purple}{h_2}}{\color{brown}{E_1}} + {\color{purple}{C_1}}^{\color{purple}{h_1}}{\color{teal}{d_2}}^{\color{purple}{h_2}}{\color{brown}{E_2}} + {\color{teal}{d_1}}^{\color{purple}{h_1}}{\color{teal}{d_2}}^{\color{purple}{h_2}}{\color{purple}{\alpha}} {\color{brown}{E_3}} }{ {\color{purple}{C_1}}^{\color{purple}{h_1}}{\color{purple}{C_2}}^{\color{purple}{h_2}} + {\color{teal}{d_1}}^{\color{purple}{h_1}}{\color{purple}{C_2}}^{\color{purple}{h_2}} + {\color{purple}{C_1}}^{\color{purple}{h_1}}{\color{teal}{d_2}}^{\color{purple}{h_2}} + {\color{teal}{d_1}}^{\color{purple}{h_1}}{\color{teal}{d_2}}^{\color{purple}{h_2}}{\color{purple}{\alpha}}} \end{align}$$ The parameters $\color{brown}{E_0}$, $\color{brown}{E_3}$, give response values for the extreme values for the doses: $\mbox{MuSyC}({\color{teal}{0}}, {\color{teal}{0}}; \theta) = \color{brown}{E_0}$ and $\mbox{MuSyC}({\color{teal}{\infty}}, {\color{teal}{\infty}}; \theta) = \color{brown}{E_3}$. Setting one of the doses to zero e.g., ${\color{teal}{d_2}} = 0$, the MuSyC functional form reduces to a sigmoid function of the other, where $\mbox{MuSyC}({\color{teal}{d_1}}, {\color{teal}{0}}; \theta) = Sigmoid({\color{teal}{d_1}}; \phi)$, where the half maximal activity is $\mbox{AC}_{50} = \color{purple}{C_1}$ and the slope at the half maximal activity is ${\color{purple}{\mbox{hill}}} = {\color{purple}{h_1}}$ and if , then ${\color{brown}{\mbox{top}}} = {\color{brown}{E_1}}$ and ${\color{brown}{\mbox{bottom}}} = {\color{brown}{E_0}}$, otherwise the assignment is reversed. See Appendix XXX for a derivation.
To interpret these parameters if we set $\color{teal}{d_2}=0$, then $$\begin{align} \color{brown}{E_d} &= \frac{ {\color{purple}{C_1}}^{\color{purple}{h_1}}{\color{brown}{E_0}} + {\color{teal}{d_1}}^{\color{purple}{h_1}}{\color{brown}{E_1}} }{ {\color{purple}{C_1}}^{\color{purple}{h_1}} + {\color{teal}{d_1}}^{\color{purple}{h_1}}} \end{align}$$ which is the Hill equation, which we modeled above . If we then additionally set $\color{teal}{d_1}=0$ then $\color{brown}{E_d}=\color{brown}{E_0}$, in the limit as ${\color{teal}{d_1}}\rightarrow \infty$ then ${\color{brown}{E_d}}\rightarrow {\color{brown}{E_1}}$, and if ${\color{teal}{d_1}}=\color{purple}{C_1}$ then ${\color{brown}{E_d}} = ({\color{brown}{E_0}} + {\color{brown}{E_2}})/2$, which is the half maximal response (either the $\color{brown}{\mbox{IC}_{50}}$ if treatment is an inhibitor or $\color{brown}{\mbox{EC}_{50}}$ if treatment is agonist). The slope at ${\color{teal}{d_1}}={\color{purple}{C_1}}$ is $$\begin{align*} \frac{\mathrm{d}\;\color{brown}{E_d}}{\mathrm{d}\color{teal}{d_1}} &= {\color{purple}{C_1}}^{v}{\color{brown}{E_0}} \frac{\mathrm{d}}{\mathrm{d}\color{teal}{d_1}} \frac{1}{{\color{purple}{C_1}}^{\color{purple}{h_1}} + {\color{teal}{d_1}}^{\color{purple}{h_1}}} + {\color{brown}{E_1}} \frac{\mathrm{d}}{\mathrm{d}\color{teal}{d_1}} \frac{{\color{teal}{d_1}}^{h_1}}{{\color{purple}{C_1}}^{\color{purple}{h_1}} + {\color{teal}{d_1}}^{\color{purple}{h_1}}}\\ &= {\color{purple}{C_1}}^{h_1}{\color{brown}{E_0}} \frac{ h_1{\color{teal}{d_1}}^{{\color{purple}{h_1}}-1}}{\left({\color{purple}{C_1}}^{\color{purple}{h_1}} + {\color{teal}{d_1}}^{\color{purple}{h_1}}\right)^2} + {\color{brown}{E_1}} \frac{{\color{purple}{C_1}}^{\color{purple}{h_1}}h_1{\color{teal}{d_1}}^{{\color{purple}{h_1}}-1}}{\left({\color{purple}{C_1}}^{\color{purple}{h_1}} + {\color{teal}{d_1}}^{\color{purple}{h_1}}\right)^2}\\ &= ({\color{brown}{E_0}} + {\color{brown}{E_1}}) \end{align*}$$
The evaluation of the functional form for ${\color{brown}{E_d}}$ is numerically unstable due to the exponentiation. To transform using the trick, let
$$\begin{align*} \texttt{numerator\_parts} = [\\ &{\color{purple}{h_1}}\log({\color{purple}{C_1}}) + {\color{purple}{h_2}}\log({\color{purple}{C_2}}) + \log({\color{brown}{E_0}}),\\ &{\color{purple}{h_1}}\log({\color{teal}{d_1}}) + {\color{purple}{h_2}}\log({\color{purple}{C_2}}) + \log({\color{brown}{E_1}}),\\ &{\color{purple}{h_1}}\log({\color{purple}{C_1}}) + {\color{purple}{h_2}}\log({\color{teal}{d_2}}) + \log({\color{brown}{E_2}}),\\ &{\color{purple}{h_1}}\log({\color{teal}{d_1}}) + {\color{purple}{h_2}}\log({\color{teal}{d_2}}) + \log({\color{brown}{E_3}}) + \log({\color{purple}{\alpha}}) ]\\ \texttt{denominator\_parts} = [\\ &{\color{purple}{h_1}}\log({\color{purple}{C_1}}) + {\color{purple}{h_2}}\log({\color{purple}{C_2}}),\\ &{\color{purple}{h_1}}\log({\color{teal}{d_1}}) + {\color{purple}{h_2}}\log({\color{purple}{C_2}}),\\ &{\color{purple}{h_1}}\log({\color{purple}{C_1}}) + {\color{purple}{h_2}}\log({\color{teal}{d_2}}),\\ &{\color{purple}{h_1}}\log({\color{teal}{d_1}}) + {\color{purple}{h_2}}\log({\color{teal}{d_2}})]\\ \end{align*}$$ Then for a vector , let . Then
$$ {\color{brown}{E_d}} = \mbox{exp}\!\left(\texttt{log-sum-exp}(\texttt{numerator\_parts}) - \texttt{log-sum-exp}(\texttt{denominator\_parts})\right). $$ To implement the model in Stan we use the following parameterization.
# the brms MuSyC formula with given covariates
synergy_formula <- MuSyC_formula(predictors = covariates)
# will generate a formula like this
synergy_formula_alt <- brms::brmsformula(
# The Stan MuSyC function is defined in BayesPharma::MuSyC_stanvar()
response ~ MuSyC(
logd1 - logd1scale,
logd2 - logd2scale,
logE0,
logC1, logE1, h1,
logC2, logE2, h2,
logE3, logalpha),
nl = TRUE) +
# The free parameters are regressed against the given covariates
brms::lf(logE0 ~ covariates) +
brms::lf(logC1 + logE1 + h1 ~ covariates) +
brms::lf(logC2 + logE2 + h2 ~ covariates) +
brms::lf(logE3 + logalpha ~ covariates)
Note that if the logd1scale
and logd1scale
values are not provided in the the data, when the model is run, they are
automatically computed as the mean value of the doses and is used to
make the model easier to fit.
MuSyC model BayesPharma
#β Drug Synergy #β MuSyC Drug Synergy model #β #β Assume that the response metric decreases with more effective drugs #β Let E3 be the effect at the maximum concentration of both drugs #β #β #β Special cases: #β * dose additive model: alpha1 = alpha2 = 0 #β * loewe: h1 = h2 = 1 #β * CI: E0 = 1, E1 = E2 = E3 = 0 #β the drug effect is equated with percent inhibition #β * bliss drug independence model: #β E0 = 1, E1 = E2 = E3 = 0, alpha1 = alpha2 = 1 #β (param?) d1 Dose of drug 1 #β (param?) d2 Dose of drug 2 #β #β (param?) E0 effect with no drug treatment #β #β # params for drug 1 by it self #β (param?) s1 drug 1 hill slope #β (param?) C1 drug 1 EC50 #β (param?) E1 drug 1 maximum effect #β #β # params for drug 2 by it self #β (param?) s2 drug 2 hill slope #β (param?) C2 drug 2 EC50 #β (param?) E2 drug 2 maximum effect #β #β (param?) beta synergistic efficacy #β percent increase in a drug combinationβs effect #β beyond the most efficacious single drug. #β #β beta > 0 => synergistic efficacy #β the effect at the maximum concentration of both drugs (E3) exceeds the #β maximum effect of either drug alone (E1 or E2) #β #β beta < 0 => antagonistic efficacy #β at least one or both drugs are more efficacious as #β single agents than in combination #β #β (param?) alpha1 synergistic potency #β how the effective dose of drug 1 #β is altered by the presence of drug 2 #β (param?) alpha2 synergistic potency #β how the effective dose of drug 2 #β is altered by the presence of drug 1 #β #β alpha > 1 => synergistic potency #β the EC50 decreases because of the addition of the other drug, #β corresponding to an increase in potency #β #β 0 <= alpha < 1 => antagonistic potency #β the EC50 of the drug increases as a result of the other drug, #β corresponding to a decrease in potency #β #β alpha1 == alpha2 if detailed balance #β (export?) generate_MuSyC_effects <- function( d1, d2, E0, s1, C1, E1, s2, C2, E2, alpha, E3) { h1 <- MuSyC_si_to_hi(s1, C1, E0, E1) h2 <- MuSyC_si_to_hi(s2, C2, E0, E2) numerator <- C1^h1 * C2^h2 * E0 + d1^h1 * C2^h2 * E1 + C1^h1 * d2^h2 * E2 + d1^h1 * d2^h2 * E3 * alpha denominator <- C1^h1 * C2^h2 + d1^h1 * C2^h2 + C1^h1 * d2^h2 + d1^h1 * d2^h2 * alpha numerator / denominator }
#β Create a formula for the MuSyC synergy model #β #β (description?) setup a
defaulMuSyC synergy model formula to predict #β the E0
,
C1
, E1
, s1
, C2
,
E2
, s2
, log10alpha
, and
E3alpha
#β parameters. #β #β (param?) predictors Additional
formula objects to specify predictors of #β non-linear parameters.
i.e.Β what perturbations/experimental differences #β should be modeled
separately? (Default: 1) should a random effect be taken #β into
consideration? i.e.Β cell number, plate number, etc. #β (return?) brmsformula #β #β
(examples?) #β #β #β
(export?) MuSyC_formula
<- function( predictors = 1, β¦) {
predictor_eq <- rlang::new_formula(
lhs = quote(E0 + C1 + E1 + s1 + C2 + E2 + s2 + log10alpha + E3alpha),
rhs = rlang::enexpr(predictors))
brms::brmsformula(
response ~ (C1^h1 * C2^h2 * E0 +
d1^h1 * C2^h2 * E1 +
C1^h1 * d2^h2 * E2 +
d1^h1 * d2^h2 * E3alpha
) / (
C1^h1 * C2^h2 +
d1^h1 * C2^h2 +
C1^h1 * d2^h2 +
d1^h1 * d2^h2 * 10^log10alpha),
brms::nlf(d1 ~ dose1 / d1_scale_factor),
brms::nlf(d2 ~ dose2 / d2_scale_factor),
brms::nlf(h1 ~ s1 * (4 * C1) / (E0 + E1)),
brms::nlf(h2 ~ s2 * (4 * C2) / (E0 + E2)),
predictors_eq,
nl = TRUE,
...)
}
#β Fit the MuSyC synergy model by dose #β #β (param?) data data.frame of
experimental data #β with columns: dose1, dose2, n_positive, count,
[
if (is.data.frame(well_scores)) { grouped_data <- well_scores |> dplyr::group_by(!!!group_vars) |> dplyr::mutate( d1_scale_factor = max(dose1), d2_scale_factor = max(dose2)) |> tidyr::nest() |> dplyr::ungroup() }
if (verbose) { cat(βFitting MuSyC modelβ) }
model <- brms::brm_multiple( formula = formula, data = grouped_data$data, family = binomial(βidentityβ), prior = prior, init = init, # stanvars = c( # brms::stanvar( # scode = β real d1_scale_factor = max(dose1));β, # block =βtdataβ, # position = βendβ), # brms::stanvar( # scode = β real d2_scale_factor = max(dose2));β, # block =βtdataβ, # position = βendβ), # brms::stanvar( # scode = β real drug1_IC50 = b_C1 * d1_scale_factor);β, # block =βgenquantβ, # position = βendβ), # brms::stanvar( # scode = β real drug2_IC50 = b_C2 * d2_scale_factor;β, # block =βgenquantβ, # position = βendβ)), combine = FALSE, data2 = NULL, iter = iter, cores = cores, stan_model_args = stan_model_args, control = control, β¦)
if (!is.null(model_evaluation_criteria)) { # evaluate fits model <- model |> purrr::imap(function(model, i) { group_index <- grouped_data[i, ] |> dplyr::select(-data) group_index_label <- paste0( names(group_index), β:β, group_index, collapse = β,β) cat(βEvaluating model fit forβ, group_index_label, ββ¦β, sep = ββ) model <- model |> brms::add_criterion( criterion = model_evaluation_criteria, model_name = paste0(βMuSyC:β, group_index_label), reloo = TRUE) model }) } grouped_data |> dplyr::mutate( model = model) }