Skip to content

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, from Element
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