Mechanisms with regulators

A regulator is a species that binds the enzyme and changes its rate without being consumed by the reaction. The derivation treats regulators no differently from substrates and products: it walks whatever enzyme-form graph the steps define and reads the rate law off it. A regulator's kind — competitive inhibitor, essential activator, general modifier — is therefore not a setting you choose but an emergent consequence of where it binds and how the kinetics of regulator-bound enzyme species change.

This makes the derivation more general than the automatic search. When identify_rate_equation enumerates mechanisms it adds only competitive (dead-end) inhibitors and allosteric regulators. A hand-written @enzyme_mechanism, though, can express the full range — competitive, non-competitive, and uncompetitive inhibition, essential and non-essential activation — simply by choosing which enzyme forms the regulator binds. Declare the regulator with regulators: <name> and write its binding steps.

A competitive inhibitor

A competitive inhibitor binds the free enzyme only, competing with substrate. The inhibitor-bound form has no catalytic exit, so it appears only in the denominator — it ties up enzyme but produces no flux:

using EnzymeRates
competitive = @enzyme_mechanism begin
    substrates: S
    products:   P
    regulators: I
    steps: begin
        E + S ⇌ E(S)
        E(S) <--> E(P)
        E(P) ⇌ E + P
        E + I ⇌ E(I)
    end
end
print(rate_equation_string(competitive))
(; K_Iinh_E, K_P_E, K_S_E, k_ES_to_EP, Keq, E_total) = params
(; S, P, I) = concs
# Haldane constraints:
k_EP_to_ES = (1 / Keq) * K_P_E * (1 / K_S_E) * k_ES_to_EP
v = E_total * (k_ES_to_EP * S / K_S_E - k_EP_to_ES * P / K_P_E) / (1 + I / K_Iinh_E + P / K_P_E + S / K_S_E)

The inhibitor adds a single I / K_Iinh_E term to the denominator and leaves the numerator untouched — the textbook competitive form. (I renders with an inh marker, K_Iinh_E, because it occupies a regulator binding site.)

An essential activator

An essential activator must be bound for catalysis to occur at all: there is no catalytic cycle on the activator-free enzyme. The activator R binds the free enzyme, and every catalytic form carries it:

essential = @enzyme_mechanism begin
    substrates: S
    products:   P
    regulators: R
    steps: begin
        E + R ⇌ E(R)
        E(R) + S ⇌ E(S, R)
        E(S, R) <--> E(P, R)
        E(R) + P ⇌ E(P, R)
    end
end
print(rate_equation_string(essential))
(; K_P_ERinh, K_Rinh_E, K_S_ERinh, k_ERinhS_to_EPRinh, Keq, E_total) = params
(; S, P, R) = concs
# Haldane constraints:
k_EPRinh_to_ERinhS = (1 / Keq) * K_P_ERinh * (1 / K_S_ERinh) * k_ERinhS_to_EPRinh
v = E_total * (k_ERinhS_to_EPRinh * R * S / (K_Rinh_E * K_S_ERinh) - k_EPRinh_to_ERinhS * P * R / (K_P_ERinh * K_Rinh_E)) / (1 + R / K_Rinh_E + P * R / (K_P_ERinh * K_Rinh_E) + R * S / (K_Rinh_E * K_S_ERinh))

Now the activator concentration R appears in every numerator term, so the rate vanishes as R → 0 — the defining behaviour of an essential activator, opposite to the competitive inhibitor whose regulator stayed in the denominator. Nothing in the DSL marks R as an activator; its role follows entirely from binding before the substrate and riding along through catalysis.

See also: @enzyme_mechanism, rate_equation_string.