Skip to content

Commit

Permalink
Fix inheritance from sympy.Symbol (#474)
Browse files Browse the repository at this point in the history
This corrects the way PySB symbol-like classes inherit from sympy.Symbol.

* Fix usage of `super` in multiple inheritance
* Replace sympy.Symbol by sympy.Dummy
* Fix doctests (symbols are now commutative!)
* Relax tolerances for importer test (commutativity impedes roundtripping to BNG)
  • Loading branch information
FFroehlich committed Jan 30, 2020
1 parent 9c2cf61 commit b4a5d86
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 36 deletions.
44 changes: 18 additions & 26 deletions pysb/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,16 @@ def rename(obj, new_name):
"name '%s'" % obj.name)


class Symbol(sympy.Dummy):
def __new__(cls, name):
return super(Symbol, cls).__new__(cls, name)

def _lambdacode(self, printer, **kwargs):
""" custom printer method that ensures that the dummyid is not
appended when printing code """
return self.name


class Component(object):

"""
Expand Down Expand Up @@ -1243,7 +1253,7 @@ def build_rule_expression(reactant, product, is_reversible):
return RuleExpression(reactant, product, is_reversible)


class Parameter(Component, sympy.Symbol):
class Parameter(Component, Symbol):

"""
Model component representing a named constant floating point number.
Expand All @@ -1265,7 +1275,7 @@ class Parameter(Component, sympy.Symbol):
"""

def __new__(cls, name, value=0.0, _export=True):
return super(sympy.Symbol, cls).__new__(cls, name)
return super(Parameter, cls).__new__(cls, name)

def __getnewargs__(self):
return (self.name, self.value, False)
Expand All @@ -1284,12 +1294,6 @@ def value(self, new_value):

def get_value(self):
return self.value

# This is needed to make sympy's evalf machinery treat this class like a
# Symbol.
@property
def func(self):
return sympy.Symbol

def __repr__(self):
return '%s(%s, %s)' % (self.__class__.__name__, repr(self.name), repr(self.value))
Expand Down Expand Up @@ -1505,7 +1509,7 @@ def validate_const_expr(obj, description):



class Observable(Component, sympy.Symbol):
class Observable(Component, Symbol):

"""
Model component representing a linear combination of species.
Expand Down Expand Up @@ -1545,7 +1549,7 @@ class Observable(Component, sympy.Symbol):
"""

def __new__(cls, name, reaction_pattern, match='molecules', _export=True):
return super(sympy.Symbol, cls).__new__(cls, name)
return super(Observable, cls).__new__(cls, name)

def __getnewargs__(self):
return (self.name, self.reaction_pattern, self.match, False)
Expand All @@ -1563,12 +1567,6 @@ def __init__(self, name, reaction_pattern, match='molecules', _export=True):
self.species = []
self.coefficients = []

# This is needed to make sympy's evalf machinery treat this class like a
# Symbol.
@property
def func(self):
return sympy.Symbol

def expand_obs(self):
""" Expand observables in terms of species and coefficients """
return sympy.Add(*[a * b for a, b in zip(
Expand All @@ -1595,7 +1593,7 @@ def __call__(self, tag):
return sympy.Function(self.name)(tag)


class Expression(Component, sympy.Symbol):
class Expression(Component, Symbol):

"""
Model component representing a symbolic expression of other variables.
Expand All @@ -1613,7 +1611,7 @@ class Expression(Component, sympy.Symbol):
"""

def __new__(cls, name, expr, _export=True):
return super(sympy.Symbol, cls).__new__(cls, name)
return super(Expression, cls).__new__(cls, name)

def __getnewargs__(self):
return (self.name, self.expr, False)
Expand Down Expand Up @@ -1653,12 +1651,6 @@ def get_value(self):
subs[a] = a.get_value()
return self.expr.xreplace(subs)

# This is needed to make sympy's evalf machinery treat this class like a
# Symbol.
@property
def func(self):
return sympy.Symbol

@property
def is_local(self):
return len(self.expr.atoms(Tag)) > 0
Expand All @@ -1682,10 +1674,10 @@ def __call__(self, tag):
return sympy.Function(self.name)(tag)


class Tag(Component, sympy.Symbol):
class Tag(Component, Symbol):
"""Tag for labelling MonomerPatterns and ComplexPatterns"""
def __new__(cls, name, _export=True):
return super(sympy.Symbol, cls).__new__(cls, name)
return super(Tag, cls).__new__(cls, name)

def __getnewargs__(self):
return self.name, False
Expand Down
3 changes: 3 additions & 0 deletions pysb/generator/bng.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,9 @@ def _print_Piecewise(self, expr):

return if_stmt

def _print_Dummy(self, expr):
return expr.name

def _print_Pow(self, expr, rational=False):
return super(BngPrinter, self)._print_Pow(expr, rational)\
.replace('**', '^')
Expand Down
4 changes: 2 additions & 2 deletions pysb/macros.py
Original file line number Diff line number Diff line change
Expand Up @@ -953,8 +953,8 @@ def drug_binding(drug, d_site, substrate, s_site, t_action, klist):
Monomer('__t'),
Parameter('__k_t', 1.0),
Observable('t', __t()),
Expression('kf_expr_drug_substrate', (t > 10)*kf_drug_substrate),
Expression('kr_expr_drug_substrate', (t > 10)*kr_drug_substrate),
Expression('kf_expr_drug_substrate', kf_drug_substrate*(t > 10)),
Expression('kr_expr_drug_substrate', kr_drug_substrate*(t > 10)),
])
"""
Expand Down
14 changes: 7 additions & 7 deletions pysb/pattern.py
Original file line number Diff line number Diff line change
Expand Up @@ -928,7 +928,7 @@ class ReactionPatternMatcher(object):
[Rxn (reversible):
Reactants: {'__s15': mSmac(b=None), '__s45': AMito(b=None)}
Products: {'__s47': AMito(b=1) % mSmac(b=1)}
Rate: __s15*__s45*kf21 - __s47*kr21
Rate: kf21*__s15*__s45 - kr21*__s47
Rules: [Rule('bind_mSmac_AMito', AMito(b=None) + mSmac(b=None) |
AMito(b=1) % mSmac(b=1), kf21, kr21)]]
Expand All @@ -938,21 +938,21 @@ class ReactionPatternMatcher(object):
[Rxn (one-way):
Reactants: {'__s46': AMito(b=1) % mCytoC(b=1)}
Products: {'__s45': AMito(b=None), '__s48': ACytoC(b=None)}
Rate: __s46*kc20
Rate: kc20*__s46
Rules: [Rule('produce_ACytoC_via_AMito', AMito(b=1) % mCytoC(b=1) >>
AMito(b=None) + ACytoC(b=None), kc20)],
Rxn (one-way):
Reactants: {'__s47': AMito(b=1) % mSmac(b=1)}
Products: {'__s45': AMito(b=None), '__s49': ASmac(b=None)}
Rate: __s47*kc21
Rate: kc21*__s47
Rules: [Rule('produce_ASmac_via_AMito', AMito(b=1) % mSmac(b=1) >>
AMito(b=None) + ASmac(b=None), kc21)]]
>>> rpm.match_products(cSmac(b=ANY)) # doctest:+NORMALIZE_WHITESPACE
[Rxn (reversible):
Reactants: {'__s7': XIAP(b=None), '__s51': cSmac(b=None)}
Products: {'__s53': XIAP(b=1) % cSmac(b=1)}
Rate: __s51*__s7*kf28 - __s53*kr28
Rate: kf28*__s51*__s7 - kr28*__s53
Rules: [Rule('inhibit_cSmac_by_XIAP', cSmac(b=None) + XIAP(b=None) |
cSmac(b=1) % XIAP(b=1), kf28, kr28)]]
Expand All @@ -962,7 +962,7 @@ class ReactionPatternMatcher(object):
[Rxn (one-way):
Reactants: {'__s47': AMito(b=1) % mSmac(b=1)}
Products: {'__s45': AMito(b=None), '__s49': ASmac(b=None)}
Rate: __s47*kc21
Rate: kc21*__s47
Rules: [Rule('produce_ASmac_via_AMito', AMito(b=1) % mSmac(b=1) >>
AMito(b=None) + ASmac(b=None), kc21)]]
Expand All @@ -971,13 +971,13 @@ class ReactionPatternMatcher(object):
[Rxn (reversible):
Reactants: {'__s14': mCytoC(b=None), '__s45': AMito(b=None)}
Products: {'__s46': AMito(b=1) % mCytoC(b=1)}
Rate: __s14*__s45*kf20 - __s46*kr20
Rate: kf20*__s14*__s45 - kr20*__s46
Rules: [Rule('bind_mCytoC_AMito', AMito(b=None) + mCytoC(b=None) |
AMito(b=1) % mCytoC(b=1), kf20, kr20)],
Rxn (one-way):
Reactants: {'__s46': AMito(b=1) % mCytoC(b=1)}
Products: {'__s45': AMito(b=None), '__s48': ACytoC(b=None)}
Rate: __s46*kc20
Rate: kc20*__s46
Rules: [Rule('produce_ACytoC_via_AMito', AMito(b=1) % mCytoC(b=1) >>
AMito(b=None) + ACytoC(b=None), kc20)]]
"""
Expand Down
3 changes: 2 additions & 1 deletion pysb/tests/test_importers.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
'motor': 1e-8,
'fceri_ji': 1e-4,
'test_paramname': 1e-4,
'tlmr': 1e-4
'tlmr': 1e-4,
'Repressilator': 1e-11,
}

logger = get_logger(__name__)
Expand Down

0 comments on commit b4a5d86

Please sign in to comment.