Acquired and induced resistance with phenotype switching
Sensitive cells and cells resistant by mutation or by a reversible phenotype switch under a dose.
- Name:
drug_resistance_switching - Field:
biology/oncology - Formalism: ordinary differential equations
- References: Padmanabhan, Meskin, and Al Moustafa 2021; Greene, Sanchez-Tapia, and Sontag 2018; Greene, Gevertz, and Sontag 2019
Factory
@register_model(
field='biology/oncology',
tags=('chemotherapy', 'ode', 'drug resistance', 'phenotype switching'),
references=(BOOK, 'Greene, Sanchez-Tapia, and Sontag 2018', 'Greene, Gevertz, and Sontag 2019'),
defaults={'until': 400.0, 'dt': 1.0},
)
def drug_resistance_switching(
S: float = 0.01,
*,
u: float = 1.0,
m1: float = 1e-6,
m2: float = 0.0,
alpha_m: float = 0.01,
alpha_p: float = 0.0,
m5: float = 0.0,
r2: float = 0.2,
r3: float = 0.2,
) -> System:
"""Return sensitive cells and cells resistant by mutation or by a reversible phenotype switch under a dose.
The drug kills the sensitive cells, so the resistant cells, which grow slower, take the tumour over. Resistance is
acquired spontaneously at the rates `m1` and `m2` and induced by the drug at the rates `alpha_m` and `alpha_p` per
unit of dose, and switched cells return to sensitivity at the rate `m5`. Time and the cells are dimensionless, the
carrying capacity being 1. The book gives values only for the two-population model of Greene, Sanchez-Tapia, and
Sontag, its Table 3.5, which is this model without switched cells. The defaults are that model, with the
induced rate `alpha_m = 0.01` of the table and an effective dose of 1, so switching is off until `m2`, `alpha_p`,
or `m5` is set.
Args:
S:
The initial sensitive cells, the resistant cells starting at 0.
u:
The effective dose.
m1:
The rate of spontaneous resistance by mutation.
m2:
The rate of spontaneous resistance by phenotype switching.
alpha_m:
The rate per unit of dose of resistance induced by mutation.
alpha_p:
The rate per unit of dose of resistance induced by phenotype switching.
m5:
The rate at which switched cells become sensitive again.
r2:
The growth rate of mutated cells relative to sensitive cells.
r3:
The growth rate of switched cells relative to sensitive cells.
Returns:
system:
The model, with the cells `tumour.S`, `tumour.Rm`, and `tumour.Rp`.
Examples:
>>> from skcomplex.models.biology.oncology import drug_resistance_switching
>>> drug_resistance_switching(alpha_m=0.0).elements[0].alpha_m
0.0
"""
tumour = ResistanceSwitchingElement(
'tumour',
S=S,
u=u,
m1=m1,
m2=m2,
alpha_m=alpha_m,
alpha_p=alpha_p,
m5=m5,
r2=r2,
r3=r3,
)
return System(elements=[tumour])
Components
class ResistanceSwitchingElement(Element):
"""Sensitive cells and cells resistant by mutation or by a reversible phenotype switch, under a constant dose.
With the total `V = S + Rm + Rp`, the sensitive cells follow
`dS/dt = r1 (1 - b V) S - (m1 + m2) S - (alpha_m + alpha_p) u S - a u S + m5 Rp`, the mutated cells
`dRm/dt = r2 (1 - b V) Rm + m1 S + alpha_m u S - am u Rm`, and the switched cells
`dRp/dt = r3 (1 - b V) Rp + m2 S + alpha_p u S - m5 Rp - ap u Rp`. The rates `m1` and `m2` are the spontaneous
(acquired) resistance, `alpha_m` and `alpha_p` the resistance the drug induces, and `m5` the resensitization of
switched cells.
Args:
label:
The label of the tumour.
visual:
How the element is drawn, or `None` for `default_visual_`.
S:
The initial sensitive cells.
Rm:
The initial cells resistant by mutation.
Rp:
The initial cells resistant by phenotype switching.
r1:
The growth rate of sensitive cells.
r2:
The growth rate of mutated cells.
r3:
The growth rate of switched cells.
b:
The reciprocal carrying capacity of the tumour.
m1:
The rate of spontaneous resistance by mutation.
m2:
The rate of spontaneous resistance by phenotype switching.
alpha_m:
The rate per unit of dose of resistance induced by mutation.
alpha_p:
The rate per unit of dose of resistance induced by phenotype switching.
m5:
The rate at which switched cells become sensitive again.
a:
The kill rate of sensitive cells per unit of dose.
am:
The kill rate of mutated cells per unit of dose.
ap:
The kill rate of switched cells per unit of dose.
u:
The effective dose.
Examples:
>>> from skcomplex.models.biology.oncology import ResistanceSwitchingElement
>>> ResistanceSwitchingElement('tumour').alpha_m
0.01
"""
def __init__(
self,
label: str,
visual: VisualSpec | None = None,
*,
S: Annotated[float, StateSpec(domain=RealSet.closed_open(0, oo))] = 0.01,
Rm: Annotated[float, StateSpec(domain=RealSet.closed_open(0, oo))] = 0.0,
Rp: Annotated[float, StateSpec(domain=RealSet.closed_open(0, oo))] = 0.0,
r1: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 1.0,
r2: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.2,
r3: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.2,
b: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 1.0,
m1: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 1e-06,
m2: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.0,
alpha_m: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.01,
alpha_p: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.0,
m5: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.0,
a: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 1.0,
am: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.0,
ap: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.0,
u: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 1.0,
) -> None:
super().__init__(label, visual=visual)
self.S = S
self.Rm = Rm
self.Rp = Rp
self.r1 = r1
self.r2 = r2
self.r3 = r3
self.b = b
self.m1 = m1
self.m2 = m2
self.alpha_m = alpha_m
self.alpha_p = alpha_p
self.m5 = m5
self.a = a
self.am = am
self.ap = ap
self.u = u
def _get_laws(self, s: SimpleNamespace) -> list[BaseLaw]:
"""Return the growth, transitions, and kill of the sensitive, mutated, and switched cells.
Args:
s:
The namespace of the symbols the laws can use.
Returns:
laws:
The laws of the component.
"""
S, Rm, Rp, u = s.S, s.Rm, s.Rp, s.u
room = 1 - s.b * (S + Rm + Rp)
return [
RateLaw(
S,
s.r1 * room * S - (s.m1 + s.m2) * S - (s.alpha_m + s.alpha_p) * u * S - s.a * u * S + s.m5 * Rp,
),
RateLaw(Rm, s.r2 * room * Rm + s.m1 * S + s.alpha_m * u * S - s.am * u * Rm),
RateLaw(Rp, s.r3 * room * Rp + s.m2 * S + s.alpha_p * u * S - s.m5 * Rp - s.ap * u * Rp),
]
Equations
tumour.S: diff(S_tumour(t), t) == -(b_tumour*(Rm_tumour(t) + Rp_tumour(t) + S_tumour(t)) - 1)*r1_tumour*S_tumour(t) - a_tumour*u_tumour*S_tumour(t) - (alpha_m_tumour + alpha_p_tumour)*u_tumour*S_tumour(t) + m5_tumour*Rp_tumour(t) - (m1_tumour + m2_tumour)*S_tumour(t)
tumour.Rm: diff(Rm_tumour(t), t) == -(b_tumour*(Rm_tumour(t) + Rp_tumour(t) + S_tumour(t)) - 1)*r2_tumour*Rm_tumour(t) - am_tumour*u_tumour*Rm_tumour(t) + alpha_m_tumour*u_tumour*S_tumour(t) + m1_tumour*S_tumour(t)
tumour.Rp: diff(Rp_tumour(t), t) == -(b_tumour*(Rm_tumour(t) + Rp_tumour(t) + S_tumour(t)) - 1)*r3_tumour*Rp_tumour(t) - ap_tumour*u_tumour*Rp_tumour(t) + alpha_p_tumour*u_tumour*S_tumour(t) - m5_tumour*Rp_tumour(t) + m2_tumour*S_tumour(t)