The prozone effect#
The prozone effect, also called the hook effect, refers to when the abundance of a molecular complex first increases then decreases as the relative concentration of a constituent molecule E
increases. In this example, we will say that a complex is composed of an L
molecule, an M
molecule, and an E
molecule, which connects the former two together.
[2]:
from kappybara.system import System
system = System.from_ka(
"""
%init: 100 L(e[.])
%init: 100 M(e[.])
%obs: 'E' |E()|
%obs: 'Incomplete complexes' |E(l[_], m[.])| + |E(l[.], m[_])|
%obs: 'Complete complexes' |E(l[_], m[_])|
E(l[.]), L(e[.]) <-> E(l[1]), L(e[1]) @ 1, 1 // Bind L to E
E(m[.]), M(e[.]) <-> E(m[1]), M(e[1]) @ 1, 1 // Bind M to E
. -> E(l[.], m[.]) @ 1 // Inflow of E
"""
)
We’ve set up the system so that there’s no E
to start but that it flows in at a constant stochastic rate. Let’s now simulate while tracking the corresponding number of complexes:
[3]:
while system.time < 5 * 10 ** 2:
system.update()
system.monitor.plot();
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[3], line 2
1 while system.time < 5 * 10 ** 2:
----> 2 system.update()
3 system.monitor.plot();
File /opt/hostedtoolcache/Python/3.12.11/x64/lib/python3.12/site-packages/kappybara/system.py:475, in System.update(self)
473 def update(self) -> None:
474 """Perform one simulation step."""
--> 475 self.wait()
476 if (rule := self.choose_rule()) is not None:
477 self.apply_rule(rule)
File /opt/hostedtoolcache/Python/3.12.11/x64/lib/python3.12/site-packages/kappybara/system.py:439, in System.wait(self)
433 """Advance simulation time according to exponential distribution.
434
435 Raises:
436 RuntimeWarning: If system has no reactivity (infinite wait time).
437 """
438 try:
--> 439 self.time += random.expovariate(self.reactivity)
440 except ZeroDivisionError:
441 warnings.warn(
442 "system has no reactivity: infinite wait time", RuntimeWarning
443 )
File /opt/hostedtoolcache/Python/3.12.11/x64/lib/python3.12/site-packages/kappybara/system.py:430, in System.reactivity(self)
423 @property
424 def reactivity(self) -> float:
425 """The total reactivity of the system.
426
427 Returns:
428 Sum of all rule reactivities.
429 """
--> 430 return sum(self.rule_reactivities)
File /opt/hostedtoolcache/Python/3.12.11/x64/lib/python3.12/functools.py:998, in cached_property.__get__(self, instance, owner)
996 val = cache.get(self.attrname, _NOT_FOUND)
997 if val is _NOT_FOUND:
--> 998 val = self.func(instance)
999 try:
1000 cache[self.attrname] = val
File /opt/hostedtoolcache/Python/3.12.11/x64/lib/python3.12/site-packages/kappybara/system.py:421, in System.rule_reactivities(self)
414 @cached_property
415 def rule_reactivities(self) -> list[float]:
416 """The reactivity of each rule in the system.
417
418 Returns:
419 List of reactivities corresponding to system rules.
420 """
--> 421 return [rule.reactivity(self) for rule in self.rules.values()]
File /opt/hostedtoolcache/Python/3.12.11/x64/lib/python3.12/site-packages/kappybara/rule.py:186, in KappaRule.reactivity(self, system)
175 def reactivity(self, system: "System") -> float:
176 """Calculate the total reactivity of this rule in the given system.
177
178 Args:
(...) 183 for rule symmetry.
184 """
185 return (
--> 186 self.n_embeddings(system.mixture) // self.n_symmetries * self.rate(system)
187 )
File /opt/hostedtoolcache/Python/3.12.11/x64/lib/python3.12/functools.py:998, in cached_property.__get__(self, instance, owner)
996 val = cache.get(self.attrname, _NOT_FOUND)
997 if val is _NOT_FOUND:
--> 998 val = self.func(instance)
999 try:
1000 cache[self.attrname] = val
File /opt/hostedtoolcache/Python/3.12.11/x64/lib/python3.12/site-packages/kappybara/rule.py:218, in KappaRule.n_symmetries(self)
215 r_site.agent = r
216 r_site.partner = l_site
--> 218 l.interface["__temp__"] = l_site
219 r.interface["__temp__"] = r_site
221 pattern = Pattern(left_agents + right_agents)
AttributeError: 'NoneType' object has no attribute 'interface'
As we introduce more E
, at first more complexes can be formed but eventually there are so many E
that the other components of the complex become more likely to bind separate E
molecules. The result is that the full complex is less likely to form.