Skip to content

Commit

Permalink
Simpler rate expressions + some reorganization
Browse files Browse the repository at this point in the history
  • Loading branch information
johnsekar committed Apr 26, 2017
1 parent ef250c5 commit a0ea693
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 100 deletions.
32 changes: 23 additions & 9 deletions tests/test_wc_rules.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from wc_rules import bioschema as bio
from wc_rules import ratelaw as rl
from wc_rules import utils
import unittest

class TestBioschema(unittest.TestCase):
Expand All @@ -11,7 +13,7 @@ class A(bio.Site):pass
a.set_id('1')
self.assertEqual(a.id,'1')

with self.assertRaises(bio.AddObjectError): a.add(object())
with self.assertRaises(utils.AddObjectError): a.add(object())

def test_bond(self):
class A(bio.Site):pass
Expand All @@ -34,7 +36,7 @@ class B(bio.Site):pass
bnd.set_id('1')
self.assertEqual(bnd.id,'1')

with self.assertRaises(bio.AddObjectError): bnd.add(object())
with self.assertRaises(utils.AddObjectError): bnd.add(object())

a = bnd.sites.get(label='A')
self.assertEqual(a.label,'A')
Expand All @@ -56,7 +58,7 @@ class C(bio.Site):pass
exc.set_id('1')
self.assertEqual(exc.id,'1')

with self.assertRaises(bio.AddObjectError): exc.add(object())
with self.assertRaises(utils.AddObjectError): exc.add(object())

a = exc.sites.get(label='A')
self.assertEqual(a.label,'A')
Expand Down Expand Up @@ -97,7 +99,7 @@ class C(bio.Site):pass
self.assertEqual([x.label for x in a.sites],['B','C'])
self.assertEqual([x.label for x in a.exclusions[0].sites],['B','C'])

with self.assertRaises(bio.AddObjectError): a.add(object())
with self.assertRaises(utils.AddObjectError): a.add(object())

b = a.sites.get(label='B')
self.assertEqual(b.label,'B')
Expand All @@ -123,7 +125,7 @@ class B(bio.Site):pass
cplx.set_id('1')
self.assertEqual(cplx.id,'1')

with self.assertRaises(bio.AddObjectError): cplx.add(object())
with self.assertRaises(utils.AddObjectError): cplx.add(object())

a = cplx.molecules.get(label='A',id='1')
self.assertEqual([a.label,a.id],['A','1'])
Expand All @@ -137,16 +139,22 @@ class A(bio.Site):pass
bnd_op = bio.BondOperation().set_target([a1,a2])
self.assertEqual([x.label for x in bnd_op.target],['A','A'])

with self.assertRaises(bio.AddObjectError): bnd_op.set_target(object())
with self.assertRaises(utils.AddObjectError): bnd_op.set_target(object())

def test_state_op(self):
class P(bio.BooleanStateVariable):pass
p = P()
state_op = bio.BooleanStateOperation().set_target(p)
self.assertEqual(state_op.target.label,'P')

with self.assertRaises(bio.AddObjectError): state_op.set_target(object())
with self.assertRaises(bio.AddObjectError): state_op.set_target([object(),])
with self.assertRaises(utils.AddObjectError): state_op.set_target(object())
with self.assertRaises(utils.AddObjectError): state_op.set_target([object(),])

def test_rate_expression(self):

k1 = rl.Parameter(symbol='k1',value=1000.0)
expr1 = rl.RateExpression(expr='k1*k2',parameters=[k1])
self.assertEqual([expr1.expr,expr1.parameters.get().symbol,expr1.parameters.get().value],['k1*k2','k1',1000.0])

def test_rule(self):
class A(bio.Molecule): pass
Expand All @@ -164,10 +172,16 @@ class P(bio.BooleanStateVariable):pass
reac2 = bio.Complex().add(B1)
op1 = bio.AddBond().set_target([a1,b1])
op2 = bio.Phosphorylate().set_target(b1.boolvars.get())
kf = rl.RateExpression(expr='kf')
kr = rl.RateExpression(expr='kr')

rule1 = bio.Rule(reversible=True).add([reac1,reac2,op1,op2])
rule1 = bio.Rule(reversible=True,forward=kf,reverse=kr).add([reac1,reac2,op1,op2])

self.assertEqual([x.label for y in rule1.reactants for x in y.molecules],['A','B'])
self.assertEqual([y.label for y in rule1.operations[0].target],['a','b'])
self.assertEqual(rule1.operations[1].target.label,'P')
self.assertEqual([rule1.forward.expr,rule1.reverse.expr],['kf','kr'])




50 changes: 12 additions & 38 deletions wc_rules/bioschema.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,9 @@
"""

from obj_model import core
import wc_rules.utils as utils
import wc_rules.ratelaw as rl

###### Factory ######
class Factory(object):
def build(self,obj_type,names,instances=True):
types = dict()
vec = []
for name in names:
if name not in moltypes:
types[name] = type(name,(obj_type,),{})
if instances==False:
vec.append( types[name] )
if instances==True:
vec.append( types[name]() )
return vec

###### Structures ######
class Complex(core.Model):
id = core.StringAttribute(primary=True,unique=True)
Expand All @@ -37,7 +25,7 @@ def add(self,v):
if type(v) is list: [self.add(w) for w in v]
elif isinstance(v,Molecule): self.molecules.append(v)
elif isinstance(v,Bond): self.bonds.append(v)
else: raise AddObjectError(self,v,['Molecule','Bond'])
else: raise utils.AddObjectError(self,v,['Molecule','Bond'])
return self

class Molecule(core.Model):
Expand All @@ -57,7 +45,7 @@ def add(self,v):
if type(v) is list: [self.add(w) for w in v]
elif isinstance(v,Site): self.sites.append(v)
elif isinstance(v,Exclusion): self.exclusions.append(v)
else: raise AddObjectError(self,v,['Molecule','Bond'])
else: raise utils.AddObjectError(self,v,['Molecule','Bond'])
return self

class Site(core.Model):
Expand Down Expand Up @@ -90,15 +78,14 @@ def set_id(self,id):
def add(self,v):
if type(v) is list: [self.add(w) for w in v]
elif isinstance(v,BooleanStateVariable): self.boolvars.append(v)
else: raise AddObjectError(self,v,['BooleanStateVariable'])
else: raise utils.AddObjectError(self,v,['BooleanStateVariable'])
return self
def get_boolvar(self,label,**kwargs):
if label is not None:
return self.boolvars.get(label=label,**kwargs)
else:
return self.boolvars.get(**kwargs)


class Bond(core.Model):
id = core.StringAttribute(primary=True,unique=True)
complex = core.ManyToOneAttribute(Complex,related_name='bonds')
Expand All @@ -109,7 +96,7 @@ def set_id(self,id):
def add(self,v):
if type(v) is list: [self.add(w) for w in v]
elif isinstance(v,Site): self.sites.append(v)
else: raise AddObjectError(self,v,['Site'])
else: raise utils.AddObjectError(self,v,['Site'])
return self

class Exclusion(core.Model):
Expand All @@ -122,7 +109,7 @@ def set_id(self,id):
def add(self,v):
if type(v) is list: [self.add(w) for w in v]
elif isinstance(v,Site): self.sites.append(v)
else: raise AddObjectError(self,v,['Site'])
else: raise utils.AddObjectError(self,v,['Site'])
return self

###### Variables ######
Expand Down Expand Up @@ -167,7 +154,7 @@ def target(self,arr):
def set_target(self,v):
if type(v) is list: [self.set_target(w) for w in v]
elif isinstance(v,Site): self.sites.append(v)
else: raise AddObjectError(self,v,['Site'],'set_target()')
else: raise utils.AddObjectError(self,v,['Site'],'set_target()')
return self

class AddBond(BondOperation):pass
Expand All @@ -182,7 +169,7 @@ def target(self,boolvar):
self.boolvar = boolvar
def set_target(self,v):
if isinstance(v,BooleanStateVariable): self.boolvar = v
else: raise AddObjectError(self,v,['BooleanStateVariable'],'set_target()')
else: raise utils.AddObjectError(self,v,['BooleanStateVariable'],'set_target()')
return self

class SetTrue(BooleanStateOperation):pass
Expand All @@ -195,31 +182,18 @@ class Rule(core.Model):
reactants = core.OneToManyAttribute(Complex,related_name='rule')
reversible = core.BooleanAttribute(default=False)
operations = core.OneToManyAttribute(Operation,related_name='rule')
forward = core.OneToOneAttribute(rl.RateExpression,related_name='rule_forward')
reverse = core.OneToOneAttribute(rl.RateExpression,related_name='rule_reverse')
def set_id(self,id):
self.id = id
return self
def add(self,v):
if type(v) is list: [self.add(w) for w in v]
elif isinstance(v,Complex): self.reactants.append(v)
elif isinstance(v,Operation): self.operations.append(v)
else: raise AddObjectError(self,v,['Complex','Operation'])
else: raise utils.AddObjectError(self,v,['Complex','Operation'])
return self

###### Error ######
class AddObjectError(Exception):
def __init__(self,parentobject,currentobject,allowedobjects,methodname='add()'):
msg = '\nObject of ' + self.to_str(currentobject)+ ' cannot be added to ' + self.to_str(parentobject) + ' using ' + methodname + '. '
if (len(allowedobjects)==1 or isinstance(allowedobjects,str)):
msg = msg + 'Only objects of type' + str(allowedobjects) + 'are allowed.'
else:
msg = msg + 'Only objects of type ' + ', '.join(allowedobjects) + ' are allowed.'
super(AddObjectError, self).__init__(msg)
@staticmethod
def to_str(obj):
msg = str(type(obj))
msg = ''.join([ch for ch in msg if ch not in "<>"])
return msg

###### Structure Improvements ######
class Protein(Molecule): pass
class ProteinSite(Site): pass
Expand Down
58 changes: 5 additions & 53 deletions wc_rules/ratelaw.py
Original file line number Diff line number Diff line change
@@ -1,64 +1,16 @@
from obj_model import core

import sympy
from six import integer_types, string_types, with_metaclass
#from sympy import S
#from sympy import core.all_classes as sympy_all_classes

class SymbolicExpressionAttribute(core.Attribute):
""" Symbolic Expression to be used in RateLaws """
def __init__(self,none=True,default=None,verbose_name='',help='Enter a Sympify-able Expression',primary=False,unique=False):
if default is not None:
try:
default = sympy.S(default)
except:
raise ValueError('SymbolicExpressionAttribute must be either None or Sympify-able Expression')
super(SymbolicExpressionAttribute, self).__init__(default=default,verbose_name=verbose_name,help=help,primary=primary,unique=unique)
self.none = none
def clean(self,value):
if value is None:
return (value,None)
if value is '':
return (value,None)
if isinstance(value, tuple(sympy.core.all_classes)):
return (value,None)
if isinstance(value, string_types+integer_types+(float,)):
return (sympy.S(value),None)

class SymbolAttribute(core.Attribute):
def __init__(self,none=True,default=None,verbose_name='',help='Enter a Sympy Symbol',primary=True,unique=True):
if default is not None:
try:
default = sympy.Symbol(default)
except:
raise ValueError('SymbolAttribute must be a Sympy Symbol')
super(SymbolAttribute, self).__init__(default=default,verbose_name=verbose_name,help=help,primary=primary,unique=unique)
self.none = none
def clean(self,value):
if value is None:
return (value,None)
if value is '':
return (value,None)
if isinstance(value, tuple(sympy.core.all_classes)):
return (value,None)
if isinstance(value, string_types):
return (sympy.Symbol(value),None)

class Parameter(core.Model):
symbol = SymbolAttribute(primary=True)
symbol = core.StringAttribute(primary=True)
value = core.FloatAttribute()

class SymbolicExpression(core.Model):
class RateExpression(core.Model):
id = core.StringAttribute(primary=True)
expr = SymbolicExpressionAttribute()
params = core.ManyToManyAttribute(Parameter)

expr = core.StringAttribute()
parameters = core.ManyToManyAttribute(Parameter,related_name='expressions')

def main():
k1 = Parameter(symbol=sympy.Symbol('k1'),value=1000)
print( k1, type(k1), k1.symbol, type(k1.symbol), )


return

if __name__ == '__main__':
main()
28 changes: 28 additions & 0 deletions wc_rules/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
###### Factory ######
class Factory(object):
def build(self,obj_type,names,instances=True):
types = dict()
vec = []
for name in names:
if name not in moltypes:
types[name] = type(name,(obj_type,),{})
if instances==False:
vec.append( types[name] )
if instances==True:
vec.append( types[name]() )
return vec

###### Error ######
class AddObjectError(Exception):
def __init__(self,parentobject,currentobject,allowedobjects,methodname='add()'):
msg = '\nObject of ' + self.to_str(currentobject)+ ' cannot be added to ' + self.to_str(parentobject) + ' using ' + methodname + '. '
if (len(allowedobjects)==1 or isinstance(allowedobjects,str)):
msg = msg + 'Only objects of type' + str(allowedobjects) + 'are allowed.'
else:
msg = msg + 'Only objects of type ' + ', '.join(allowedobjects) + ' are allowed.'
super(AddObjectError, self).__init__(msg)
@staticmethod
def to_str(obj):
msg = str(type(obj))
msg = ''.join([ch for ch in msg if ch not in "<>"])
return msg

0 comments on commit a0ea693

Please sign in to comment.