Steerable receptor-effector negative feedback loop
A receptor-effector loop that holds a product at its set point until a steering signal shifts it.
- Name:
steerable_feedback_loop - Field:
biology/physiology - Formalism: ordinary differential equations
- References: Wach et al. 2015; Konieczny et al. 2023
Select this system
from skcomplex.systems import create_simulation, create_system, describe
system = create_system('steerable_feedback_loop')
entry = describe('steerable_feedback_loop')
trajectory = create_simulation('steerable_feedback_loop').prepare().run().trajectory_
Factory
@register_system(
field='biology/physiology',
tags=('negative feedback', 'homeostasis', 'set point', 'fever', 'delay'),
references=('Wach et al. 2015', 'Konieczny et al. 2023'),
defaults={'until': 60.0, 'dt': 0.1},
)
def steerable_feedback_loop(
*,
v: float = 2.0,
d: float = 1.0,
K: float = 1.0,
n: float = 4.0,
T: float = 1.0,
tau: float = 0.5,
steering: float = 0.5,
start: float = 20.0,
duration: float = 20.0,
) -> System:
"""Return a receptor-effector loop that holds a product at its set point until a steering signal shifts it.
The loop of the negative feedback system of Konieczny et al. (2023) settles at the set point where synthesis
`v / (1 + (x / K)^n)` balances consumption `d x`. A steering signal, such as a bacterial toxin acting on the
temperature receptors in fever, raises the sensitivity of the receptor by the factor `1 + steering`, so the loop
climbs to a higher set point while it acts and returns once it ends. A slow effector or a long transmission delay
turns the regulation into sustained oscillations around the set point.
Args:
v:
The largest rate of synthesis.
d:
The consumption rate of the product.
K:
The sensitivity of the receptor.
n:
The steepness of the receptor.
T:
The response time of the effector.
tau:
The transmission delay between the receptor and the effector.
steering:
The relative shift of the sensitivity by the steering signal.
start:
The time the steering signal starts.
duration:
The duration of the steering signal.
Returns:
system:
The system, with the product `loop.x` and the effector activity `loop.e`.
Examples:
>>> from skcomplex.systems.biology.physiology import steerable_feedback_loop
>>> steerable_feedback_loop(tau=3.0).elements[0].tau
3.0
"""
loop = FeedbackLoopElement(
'loop',
v=v,
d=d,
K=K,
n=n,
T=T,
tau=tau,
steering=steering,
start=start,
duration=duration,
)
return System(elements=[loop])
Components
Elements:
FeedbackLoopElement, fromElement
class FeedbackLoopElement(Element):
"""Receptor-effector negative feedback loop that holds a product at a set point an outside signal steers.
The product `x` is made by the effector at the rate `v e` and consumed at the rate `d x`. The receptor reads the
product after the transmission delay `tau` and inhibits the effector with the Hill function `1 / (1 + (x / K)^n)`,
where the sensitivity `K (1 + steering)` rises while a steering signal acts, from `start` for `duration`. The
effector follows the receptor with the response time `T`, `de/dt = (1 / (1 + (x(t - tau) / K)^n) - e) / T`.
Args:
label:
The label of the loop.
visual:
How the element is drawn, or `None` for `default_visual_`.
x:
The initial product.
e:
The initial effector activity.
v:
The largest rate of synthesis.
d:
The consumption rate of the product.
K:
The sensitivity of the receptor, the product at which it inhibits the effector by half.
n:
The steepness of the receptor.
T:
The response time of the effector.
tau:
The transmission delay between the receptor and the effector.
steering:
The relative shift of the sensitivity by the steering signal.
start:
The time the steering signal starts.
duration:
The duration of the steering signal.
Examples:
>>> from skcomplex.systems.biology.physiology import FeedbackLoopElement
>>> FeedbackLoopElement('loop', tau=2.0).tau
2.0
"""
def __init__(
self,
label: str,
visual: VisualSpec | None = None,
*,
x: Annotated[float, StateSpec(domain=RealSet.closed_open(0, oo))] = 0.0,
e: Annotated[float, StateSpec(domain=RealSet.closed(0, 1))] = 0.0,
v: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 2.0,
d: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 1.0,
K: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 1.0,
n: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 4.0,
T: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 1.0,
tau: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 0.5,
steering: Annotated[float, ParameterSpec()] = 0.0,
start: float = 0.0,
duration: float = 0.0,
) -> None:
super().__init__(label, visual=visual)
self.x = x
self.e = e
self.v = v
self.d = d
self.K = K
self.n = n
self.T = T
self.tau = tau
self.steering = steering
self.start = start
self.duration = duration
def _get_laws(self, s: SimpleNamespace) -> list[BaseLaw]:
"""Return the synthesis and consumption of the product and the delayed response of the effector.
Args:
s:
The namespace of the symbols the laws can use.
Returns:
laws:
The laws of the component.
"""
sensitivity = s.K * (1 + s.steering * pulse(s.t, self.start, self.duration))
receptor = 1 / (1 + (delayed(s.x, s.tau) / sensitivity) ** s.n)
return [RateLaw(s.x, s.v * s.e - s.d * s.x), RateLaw(s.e, (receptor - s.e) / s.T)]
Equations
loop.x: diff(x_loop(t), t) == v_loop*e_loop(t) - d_loop*x_loop(t)
loop.e: diff(e_loop(t), t) == (1/((x_loop(t - tau_loop)/((steering_loop*where(both(t >= 20.0, t < 40.0), 1.0, 0) + 1)*K_loop))^n_loop + 1) - e_loop(t))/T_loop