Skip to content

Commit

Permalink
Fix repr() of single-monomer complexes w/ compartments (pysb#550)
Browse files Browse the repository at this point in the history
* Fix repr() of single-monomer complexes w/ compartments

In BNG, a compartment applied to a complex is distinct from one
applied to a monomer, by use of prefix and postfix notation. In
PySB, one can use parentheses to add a compartment to a complex,
but this fails for a complex of length 1. One can use
`as_complex_pattern()` to explicitly upgrade to a complex from
a monomer, but the string representation and PySB flat exporter
currently fail to respect this.

    A() ** C  # Monomer compartment
    as_complex_pattern(A()) ** C  # Complex compartment

This PR adds `as_complex_pattern()` around complexes with a
single monomer pattern that also have a compartment, to make
sure their string representation and PySB flat export works
as expected.

Co-authored-by: Jeremy Muhlich <jmuhlich@bitflood.org>
  • Loading branch information
alubbock and jmuhlich committed Aug 29, 2022
1 parent 4344b2d commit 8819eaa
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 2 deletions.
2 changes: 2 additions & 0 deletions pysb/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1117,6 +1117,8 @@ def __repr__(self):
if self.compartment:
if len(self.monomer_patterns) > 1:
ret = '(%s)' % ret
else:
ret = 'as_complex_pattern(%s)' % ret
ret += ' ** %s' % self.compartment.name
if self.match_once:
ret = 'MatchOnce(%s)' % ret
Expand Down
3 changes: 2 additions & 1 deletion pysb/export/pysb_flat.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ def write_cset(cset):
output.write("\n")
output.write("from pysb import Model, Monomer, Parameter, Expression, "
"Compartment, Rule, Observable, Initial, MatchOnce, "
"EnergyPattern, Annotation, MultiState, Tag, ANY, WILD\n")
"EnergyPattern, Annotation, MultiState, Tag, ANY, WILD, "
"as_complex_pattern\n")
if sympy_functions:
output.write(
"from sympy import " + ", ".join(sympy_functions) + "\n"
Expand Down
19 changes: 18 additions & 1 deletion pysb/tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,23 @@ def test_complex_pattern_equivalence_compartments():
_check_pattern_equivalence((cp0, cp1))


@with_model
def test_complex_pattern_single_monomer_complex_compartment():
Monomer('A')
Compartment('C')
Parameter('A_0', 10)
cp_source = as_complex_pattern(A()) ** C

# Check executing the string representation of the above ComplexPattern
# leads to the correct application of compartment, i.e. compartment applies
# to complex, not to monomer
exec(f"Initial({str(cp_source)}, A_0)")

cp_in_model = model.initials[0].pattern
assert cp_in_model.compartment == C
assert cp_in_model.monomer_patterns[0].compartment is None


@with_model
def test_reaction_pattern_match_complex_pattern_ordering():
"""Ensure CP equivalence is insensitive to MP order."""
Expand Down Expand Up @@ -487,7 +504,7 @@ def test_tags():

# Test tag with compartment
Compartment('c')
assert repr((A().__matmul__(x)) ** c) == 'A() ** c @ x'
assert repr((A().__matmul__(x)) ** c) == 'as_complex_pattern(A()) ** c @ x'
assert repr((A() ** c).__matmul__(x)) == 'A() ** c @ x'


Expand Down

0 comments on commit 8819eaa

Please sign in to comment.