Treat as a prescribed input protein concentration, and as the downstream gene-product concentrations. Define the Hill activation functionA minimal model with no basal expression isHere are maximal production rates, are Hill constants with the same units as , and are dimensionless Hill coefficients. The negative terms describe a first-order reaction for loss of each gene product, with both rate constants set to one in the chosen time units. Thus have units of concentration per time and the unit loss rates have units of inverse time.
The equations model regulation of gene expression at the level of its effective protein output, without separate messenger RNA dynamics. The independent branches mean that neither nor regulates the other in this initial model. Additive basal production could be included, but is not required by the question and is set to zero throughout this example.
For a constant , set both ordinary differential equation derivatives to zero:These equilibrium concentrations are unique for each input. For a loss rate other than one, divide the corresponding production term by .
Choose , , and , , . The following Python code plots the requested equilibrium concentrations and defines the functions used in part (c):
import numpy as np
import matplotlib.pyplot as plt
def hill(u, K, n):
u = np.asarray(u)
return u**n / (K**n + u**n)
X_grid = np.linspace(0, 5, 501)
Y_eq = 2 * hill(X_grid, 1, 2)
Z_eq = 3 * hill(X_grid, 2, 4)
plt.figure(figsize=(6, 4))
plt.plot(X_grid, Y_eq, label="Y equilibrium: K=1, n=2")
plt.plot(X_grid, Z_eq, label="Z equilibrium: K=2, n=4")
plt.xlabel("X concentration")
plt.ylabel("Equilibrium concentration")
plt.legend()
plt.tight_layout()
plt.show()The Hill constant sets the half-maximum input: and . Increasing that constant shifts activation toward larger . The Hill coefficient changes steepness rather than the half-maximum position; the normalized slope at is . Because the two examples also differ in and maximal production, compare normalized responses when isolating the effect of alone. Both curves vanish at zero and saturate toward their respective maxima.
Independent Hill activation and convergence from two initial states
. The left panel shows different half-activation inputs and Hill coefficients. The right panel integrates the two independent output equations at fixed X=2; dashed curves start above equilibrium and solid curves start at zero. Details.At a fixed input the production terms are constant, so both linear ordinary differential equations can also be solved exactly:Each displacement from its equilibrium concentration decays with time constant one. The Jacobian matrix is , with two eigenvalues equal to , so the fixed point is globally asymptotically stable for the nonnegative state space.
Here is a numerical solver using the classical fourth-order Runge-Kutta method; it uses only the repository's existing NumPy and matplotlib dependencies. Continue the definitions from part (b):
def integrate(rhs, initial, times):
out = np.empty((len(times), len(initial)))
out[0] = initial
for j, dt in enumerate(np.diff(times)):
state = out[j]
k1 = rhs(state)
k2 = rhs(state + dt*k1/2)
k3 = rhs(state + dt*k2/2)
k4 = rhs(state + dt*k3)
out[j+1] = state + dt*(k1 + 2*k2 + 2*k3 + k4)/6
return out
def direct_rhs(state, X):
Y, Z = state
return np.array([2*hill(X, 1, 2) - Y,
3*hill(X, 2, 4) - Z])
times = np.linspace(0, 8, 801)
plt.figure(figsize=(7, 4))
for initial, style in [([0, 0], "-"), ([3, 4], "--")]:
trajectory = integrate(lambda s: direct_rhs(s, 2), initial, times)
exact = np.array([1.6, 1.5]) + (np.array(initial) - [1.6, 1.5]) \
* np.exp(-times[:, None])
assert np.max(np.abs(trajectory - exact)) < 1e-8
plt.plot(times, trajectory[:, 0], style, label=f"Y, initial {initial}")
plt.plot(times, trajectory[:, 1], style, label=f"Z, initial {initial}")
plt.xlabel("Time")
plt.ylabel("Concentration")
plt.legend()
plt.tight_layout()
plt.show()For , . The initial state approaches those values from below, while approaches from above. Over eight time units the initial displacement is reduced by , explaining why both trajectories nearly coincide with the equilibria from part (b). The two-panel figure above displays these simulations.
Add the activating link from to and multiply the two Hill activation functions for the target:The extra Hill constant has units of concentration and is its Hill coefficient. Both inputs are required for substantial target production: if either is zero, the production term for vanishes. This all-activating circuit is a coherent feed-forward loop. A product of graded input functions implements the specified AND rule; it is a phenomenological rule rather than a complete molecular promoter model.
Retain the parameter values of parts (b) and (c), and set , . Continue the same Python script with the altered right-hand side:
def and_rhs(state, X):
Y, Z = state
return np.array([2*hill(X, 1, 2) - Y,
3*hill(X, 2, 4)*hill(Y, 1, 4) - Z])
times = np.linspace(0, 10, 1001)
plt.figure(figsize=(7, 4))
for X in [1, 3]:
trajectory = integrate(lambda s: and_rhs(s, X), [0, 0], times)
plt.plot(times, trajectory[:, 0], "--", label=f"Y, X={X}")
plt.plot(times, trajectory[:, 1], label=f"Z, X={X}")
plt.xlabel("Time")
plt.ylabel("Concentration")
plt.legend()
plt.tight_layout()
plt.show()At , and . At , and . In both simulations first accumulates and opens the additional gate; therefore initially grows more slowly than under direct activation alone. The equilibrium concentration of is unchanged by the added outgoing interaction.
AND feed-forward dynamics and the effect of activation thresholds
. The left panel compares fixed inputs X=1 and X=3 from the zero state. The right panel compares the full feed-forward equilibrium response with direct activation alone and with individual Hill constants increased. Details.The quasi-steady-state approximation for sets . Substituting into the equation givesIts equilibrium concentration is thereforeFor constant input this is also the exact equilibrium of the full two-variable model: the fast- assumption is needed to replace the transient dynamics, not to calculate a fixed point.
Relative to the direct-activation result, . The extra activation gate suppresses the response at low input and can sharpen its rise. The high-input limit is , which need not reach because saturates at .
For the parameters in part (d), the following Python continuation plots the expression and changes each Hill constant separately:
def z_equilibrium(X, Kxy=1, Kxz=2, Kyz=1):
Yeq = 2 * hill(X, Kxy, 2)
return 3 * hill(X, Kxz, 4) * hill(Yeq, Kyz, 4)
X_grid = np.linspace(0, 5, 501)
plt.figure(figsize=(7, 4))
plt.plot(X_grid, 3*hill(X_grid, 2, 4), ":", label="Direct activation")
for parameters, label in [((1, 2, 1), "Baseline"),
((2, 2, 1), "Kxy=2"),
((1, 3, 1), "Kxz=3"),
((1, 2, 2), "Kyz=2")]:
plt.plot(X_grid, z_equilibrium(X_grid, *parameters), label=label)
plt.xlabel("X concentration")
plt.ylabel("Equilibrium Z")
plt.legend()
plt.tight_layout()
plt.show()Increasing lowers at fixed finite , delaying opening of the indirect gate. Increasing delays opening of the direct gate. Neither changes the limiting response as , provided the constants remain finite. Increasing makes a given less effective and also lowers the high-input plateau. These changes are visible in the preceding figure. At small , the response is proportional to , while both gates are far below saturation, explaining the increased low-input suppression.
For a persistence detector, retain finite dynamics and use, for example,with unit loss rates, , , and initially . On switching the input on, andThe time to reach the half-activation threshold of the indirect gate isAn isolated input pulse of duration ends well before this threshold; a duration-four pulse persists long enough to open the gate. The large but finite Hill coefficient makes this a graded filter, not a perfectly sharp cutoff. Once vanishes, its direct gate closes immediately and stops production of , which then decays with time constant one. The protein output itself does not drop discontinuously. This delayed-on, prompt-production-off mechanism is the characteristic persistence detector interpretation of an AND-type coherent feed-forward loop.[ref]
Structure and function of the feed-forward loop network motif by Shmoolik Mangan and Uri Alon. p. 11981, Equations for Gene Regulation Reactions; pp. 11983–11985, coherent FFL kinetics and Coherent FFLs as Persistence Detectors
This final Python continuation simulates both pulse durations. Each discontinuity is placed on the time grid and the input is held constant within each integration step:
def persistence_rhs(state, X):
Y, Z = state
return np.array([hill(X, 1, 4) - Y,
hill(X, 1, 4)*hill(Y, 0.7, 8) - Z])
def pulse_trajectory(duration, dt=0.005):
times = np.arange(round(8/dt) + 1) * dt
out = np.zeros((len(times), 2))
for j in range(len(times)-1):
X = 2.0 if 1 <= times[j] + dt/2 < 1 + duration else 0.0
# Each step is integrated with its constant input.
out[j+1] = integrate(lambda s: persistence_rhs(s, X),
out[j], [0, dt])[-1]
return times, out
fig, axes = plt.subplots(1, 2, figsize=(10, 4))
for duration in [0.25, 4]:
times, out = pulse_trajectory(duration)
axes[0].plot(times, out[:, 0], label=f"Y, duration {duration}")
axes[1].plot(times, out[:, 1], label=f"Z, duration {duration}")
print(duration, "maximum output", out[:, 1].max())
axes[0].axhline(0.7, color="black", linestyle=":")
for ax in axes:
ax.set_xlabel("Time")
ax.set_ylabel("Concentration")
ax.legend()
plt.tight_layout()
plt.show()The chosen example produces peak of about for the short pulse and for the long pulse. These are original illustrative simulations, not fitted experimental measurements.
Finally, the strict instantaneous- reduction used for the equilibrium calculation has no separate intermediate accumulation delay. Its remaining one-variable dynamics can still attenuate short pulses through ordinary output relaxation, but it cannot demonstrate the additional delay caused by finite buildup. The pulse test therefore uses the full two-variable model.
Articles by others on the same topic
There are currently no matching articles.


