1"""
2PySCeS - Python Simulator for Cellular Systems (http://pysces.sourceforge.net)
3
4Copyright (C) 2004-2020 B.G. Olivier, J.M. Rohwer, J.-H.S Hofmeyr all rights reserved,
5
6Brett G. Olivier (bgoli@users.sourceforge.net)
7Triple-J Group for Molecular Cell Physiology
8Stellenbosch University, South Africa.
9
10Permission to use, modify, and distribute this software is given under the
11terms of the PySceS (BSD style) license. See LICENSE.txt that came with
12this distribution for specifics.
13
14NO WARRANTY IS EXPRESSED OR IMPLIED.  USE AT YOUR OWN RISK.
15Brett G. Olivier
16"""
17from __future__ import division, print_function
18from __future__ import absolute_import
19from __future__ import unicode_literals
20
21from .version import __version__
22
23import os
24import math, operator
25import numpy
26
27CurrentDirectory = os.getcwd() # temporary thing
28
29# this is a PySCeS specific hack
30from pysces import output_dir as CurrentDirectory
31
32from .InfixParser import MyInfixParser
33InfixParser = MyInfixParser()
34InfixParser.buildlexer()
35InfixParser.buildparser(debug=0, debugfile='infix.dbg',
36	tabmodule='infix_tabmodule', outputdir=CurrentDirectory)
37InfixParser.setNameStr('self.', '()')
38
39#print 'CurrentDirectory', CurrentDirectory
40
41class MapList(list):
42    def __init__(self, *args):
43        list.__init__(self,*args)
44
45    def asSet(self):
46        return set(self.__getslice__(0, self.__len__()))
47
48
49class NewCoreBase(object):
50    __DEBUG__ = False
51    name = None
52    annotations = None
53
54    def getName(self):
55        return self.name
56
57    def setName(self,name):
58        self.name = name
59
60    def get(self, attr):
61        """Return an attribute whose name is str(attr)"""
62        return self.__getattribute__(attr)
63
64    def getAnnotation(self):
65        """Returns an annotation dictionary"""
66        if self.annotations == None:
67            self.annotations = {}
68        return self.annotations.copy()
69
70    def setAnnotation(self, key, value):
71        """Set an annotation as a key:value pair"""
72        if self.annotations == None:
73            self.annotations = {}
74        self.annotations.update({key : value})
75
76
77class NumberBase(NewCoreBase):
78    value = None
79    value_initial = None
80
81    def __call__(self):
82        return self.value
83
84    def getValue(self):
85        return self.value
86
87    def setValue(self, v):
88        self.value = v
89
90
91class Compartment(NewCoreBase):
92    size = None
93    dimensions = None
94    Compartment = None
95    reactions = None
96    species = None
97    area = None
98
99    def __init__(self, name, compartment=None):
100        self.name = name
101        self.Compartment = compartment
102        self.reactions = []
103        self.species = []
104
105    def __call__(self):
106        return self.size
107
108    def setSize(self, size, dim):
109        self.size = size
110        assert dim in [0,1,2,3], '\nOkeee! %s dimensions?' % dim
111        self.dimensions = dim
112
113    def setArea(self, area=None):
114        if area == None and self.dimensions == 2:
115            self.area = self.size
116            if self.__DEBUG__: print('Setting reactive area to size for 2D compartment %s' % self.name)
117        elif area == None and self.dimensions == 3:
118            self.area = (113.09733552923255*self.size**2.0)**(0.33333333333333331)
119            if self.__DEBUG__: print('Setting reactive area to surface area for 3D compartment %s (assuming a sphere geometry)' % self.name)
120        self.area = area
121
122    def hasReactions(self):
123        return MapList([r.name for r in self.reactions])
124
125    def hasSpecies(self):
126        return MapList([s.name for s in self.species])
127
128    def addReaction(self, reaction):
129        if reaction.name not in self.hasReactions():
130            self.reactions.append(reaction)
131            self.__setattr__(reaction.name, reaction)
132            if self.__DEBUG__: print('Adding reaction %s' % reaction.name)
133
134    def addSpecies(self, species):
135        if species.name not in self.hasSpecies():
136            self.species.append(species)
137            self.__setattr__(species.name, species)
138            if self.__DEBUG__: print('Adding species %s' % species.name)
139        else:
140            if self.__DEBUG__: print('Species %s already added' % species.name)
141
142    def getDimensions(self):
143        return self.dimensions
144
145    def getCompartment(self):
146        return self.Compartment
147
148    def hasCompartment(self):
149        if self.Compartment != None:
150            return True
151        else:
152            return False
153
154    def isVolume(self):
155        if self.dimensions == 3: return True
156        else: return False
157
158    def isArea(self):
159        if self.dimensions == 2: return True
160        else: return False
161
162    def isLength(self):
163        if self.dimensions == 1: return True
164        else: return False
165
166    def isPoint(self):
167        if self.dimensions == 0: return True
168        else: return False
169
170
171class BaseUnit(NewCoreBase):
172    '''Base Unit can be of type: time, substance, volume'''
173    _types = ('time', 'substance', 'volume','area','length')
174    value = 1.0
175    type = None
176
177    def __init__(self, name, type):
178        self.name = name
179        assert type in self._types, '\nType must be one of: %s' % str(self._types)
180        self.type = type
181
182    def __call__(self):
183        return self.value
184
185    def getType(self):
186        return self.type
187
188
189class SimpleUnit(NewCoreBase):
190    exponent = 1.0
191    scale = 0.0
192    multiplier = 1.0
193    baseunit = None
194    type = None
195
196    def __init__(self, baseunit, name, exp=1.0, scale=0.0, mult=1.0):
197        self.baseunit = baseunit
198        self.exponent = exp
199        self.scale = scale
200        self.multiplier = mult
201        self.name = name
202        self.type = baseunit.type
203
204    def __call__(self):
205        return (self.multiplier*self.baseunit()*10**self.scale)**self.exponent
206
207    def getType(self):
208        return self.type
209
210
211class CompoundUnit(NewCoreBase):
212    units = None
213    _HAS_USERNAME = False
214
215    def __init__(self, name=None):
216        self.units = []
217        if name != None:
218            self.name = name
219            self._HAS_USERNAME = True
220        else:
221            self.name = ''
222
223    def __call__(self):
224        U = 1.0
225        for u in self.units:
226            U *= u()
227        return U
228
229    def addUnit(self, unit):
230        self.units.append(unit)
231        if not self._HAS_USERNAME:
232            self.name = '%s%s' % (self.name, unit.getName())
233
234    def getUnits(self):
235        return self.units
236
237    def hasUnits(self):
238        return MapList([u.getName() for u in self.units])
239
240
241class Species(NumberBase):
242    subs = None
243    prods = None
244    mods = None
245    fixed = False
246    Compartment = None
247    __amount__ = False
248
249    def __init__(self, name, value):
250        self.setName(name)
251        self.value = value
252        self.value_initial = value
253        self.subs = []
254        self.prods = []
255        self.mods = []
256
257    def getCompartment(self):
258        return self.Compartment
259
260    def setCompartment(self, c):
261        self.Compartment = c
262
263    def hasCompartment(self):
264        if self.Compartment != None:
265            return True
266        else:
267            return False
268
269    def setSubstrate(self, reaction):
270        self.__setattr__(reaction.name, reaction)
271        self.subs.append(reaction)
272
273    def setProduct(self, reaction):
274        self.__setattr__(reaction.name, reaction)
275        self.prods.append(reaction)
276
277    def setModifier(self, reaction):
278        self.__setattr__(reaction.name, reaction)
279        self.mods.append(reaction)
280
281    def isSubstrateOf(self):
282        return MapList([r.name for r in self.subs])
283
284    def isProductOf(self):
285        return MapList([r.name for r in self.prods])
286
287    def isModifierOf(self):
288        return MapList([r.name for r in self.mods])
289
290    def isReagentOf(self):
291        return MapList(self.isSubstrateOf() + self.isProductOf())
292
293    def setAmount(self, b):
294        self.__amount__ = bool(b)
295
296    def isAmount(self):
297        return self.__amount__
298
299class SpeciesAssignmentRule(Species):
300    formula = None
301    code_string = None
302    _names = None
303    _functions = None
304    type = 'assignment'
305    _TIME_ = None
306
307    def __init__(self, name, value):
308        Species.__init__(self, name, value)
309
310    def __call__(self):
311        exec(self.xcode)
312        return self.value
313
314    def addFormula(self, formula):
315        formula = formula.replace('self.','')
316        self.formula = formula
317        InfixParser.setNameStr('self.', '()')
318        InfixParser.parse(formula)
319        self.code_string = 'self.value=%s' % InfixParser.output
320        self._names = InfixParser.names
321        self._functions = InfixParser.functions
322        self.xcode = compile(self.code_string, '<string>', 'exec')
323
324    def addModelAttr(self, obj):
325        self.__setattr__(obj.name, obj)
326
327class Function(NewCoreBase):
328    formula = None
329    code_string = None
330    xcode = None
331    value = None
332    _names = None
333    args = None
334    _TIME_ = None
335
336    def __init__(self, name):
337        self.setName(name)
338        self.args = []
339
340    def __call__(self, *args):
341        for ar in range(len(args)):
342            self.__setattr__(self.args[ar], args[ar])
343        exec(self.xcode)
344        return self.value
345
346    def setArg(self, var, value=None):
347        self.__setattr__(var, value)
348        self.args.append(var)
349
350    def addFormula(self, formula):
351        formula = formula.replace('self.','')
352        self.formula = formula
353        InfixParser.setNameStr('self.', '')
354        InfixParser.SymbolReplacements = {'_TIME_':'_TIME_()'}
355        InfixParser.parse(formula)
356        self._names = InfixParser.names
357        self.code_string = 'self.value=%s' % InfixParser.output
358        self.xcode = compile(self.code_string, '<string>', 'exec')
359
360class Reaction(NewCoreBase):
361    modifiers = None
362    substrates = None
363    products = None
364    stoichiometry = None
365    multistoich = None
366    multistoich_enabled = False
367    parameters = None
368    functions = None
369    reversible = True
370    formula = None
371    code_string = None
372    rate = None
373    xcode = None
374    _names = None
375    _functions = None
376    _TIME_ = None
377    Compartment = None
378
379    def __call__(self):
380        exec(self.xcode)
381        return self.rate
382
383    def __init__(self, name):
384        self.setName(name)
385        self.modifiers = []
386        self.substrates = []
387        self.products = []
388        self.stoichiometry = {}
389        self.parameters = []
390        self.functions = []
391        self.multistoich = []
392
393    def addSubstrate(self, species):
394        self.__setattr__(species.name, species)
395        self.substrates.append(species)
396
397    def addProduct(self, species):
398        self.__setattr__(species.name, species)
399        self.products.append(species)
400
401    def addModifier(self, species):
402        self.__setattr__(species.name, species)
403        self.modifiers.append(species)
404
405    def addFormula(self, formula):
406        formula = formula.replace('self.','')
407        self.formula = formula
408        InfixParser.setNameStr('self.', '()')
409        InfixParser.parse(formula)
410        self._names = InfixParser.names
411        self._functions = InfixParser.functions
412        self.code_string = 'self.rate=%s' % InfixParser.output
413        self.xcode = compile(self.code_string, '<string>', 'exec')
414
415    def addParameter(self, par):
416        self.__setattr__(par.name, par)
417        self.parameters.append(par)
418
419    def addFunction(self, func):
420        self.__setattr__(func.name, func)
421        self.functions.append(func)
422
423    def hasProducts(self, t=type):
424        return MapList([p.name for p in self.products])
425
426    def hasSubstrates(self):
427        return MapList([s.name for s in self.substrates])
428
429    def hasModifiers(self):
430        return MapList([m.name for m in self.modifiers])
431
432    def hasParameters(self):
433        return MapList([p.name for p in self.parameters])
434
435    def hasReagents(self):
436        return MapList(self.hasSubstrates() + self.hasProducts())
437
438    def setCompartment(self, compartment):
439        self.Compartment = compartment
440
441    def getCompartment(self):
442        return self.Compartment
443
444    def hasCompartment(self):
445        if self.Compartment != None:
446            return True
447        else:
448            return False
449
450
451class Parameter(NumberBase):
452    association = None
453
454    def __init__(self, name, value):
455        self.name = name
456        self.value = value
457        self.value_initial = value
458        self.association = []
459
460    def setAssociation(self, reac):
461        self.association.append(reac)
462        self.__setattr__(reac.name, reac)
463
464    def isParameterOf(self):
465        return MapList([a.name for a in self.association])
466
467class AssignmentRule(Parameter):
468    formula = None
469    code_string = None
470    _names = None
471    _functions = None
472    type = 'assignment'
473    _TIME_ = None
474    fixed = False # added so that assignment rules can modify fixed species
475
476    def __init__(self, name, value):
477        Parameter.__init__(self, name, value)
478
479    def __call__(self):
480        exec(self.xcode)
481        return self.value
482
483    def addFormula(self, formula):
484        formula = formula.replace('self.','')
485        self.formula = formula
486        InfixParser.setNameStr('self.', '()')
487        InfixParser.parse(formula)
488        self.code_string = 'self.value=%s' % InfixParser.output
489        self._names = InfixParser.names
490        self._functions = InfixParser.functions
491        self.xcode = compile(self.code_string, '<string>', 'exec')
492
493    def addModelAttr(self, obj):
494        self.__setattr__(obj.name, obj)
495
496
497class RateRule(NewCoreBase):
498    formula = None
499    rate = None
500    xcode = None
501    code_string = None
502    _names = None
503    _functions = None
504    compartment = None
505
506    def __init__(self, name, formula):
507        self.name = name
508        self.addFormula(formula)
509
510    def __call__(self):
511        exec(self.xcode)
512        return self.rate
513
514    def addFormula(self, formula):
515        formula = formula.replace('self.','')
516        self.formula = formula.replace('()','')
517        InfixParser.setNameStr('self.', '()')
518        InfixParser.parse(self.formula)
519        self.code_string = 'self.rate=%s' % InfixParser.output
520        self._names = InfixParser.names
521        self._functions = InfixParser.functions
522        self.xcode = compile(self.code_string, 'RateRule: %s' % self.name, 'exec')
523
524    def getFormula(self):
525        return self.formula
526
527    def addModelAttr(self, obj):
528        self.__setattr__(obj.name, obj)
529
530
531class ODE(NewCoreBase):
532    sdot = None
533    value = None
534    coefficients = None
535    reactions = None
536    independent = None
537    ode_terms = None
538    formula = ''
539    formula_alt = ''
540    code_string = 'self.value='
541    code_string_alt = 'sdot='
542
543    def __init__(self, species, independent=True):
544        self.sdot = species
545        self.name = 'ODE_'+species.name
546        self.reactions = []
547        self.coefficients = []
548        self.ode_terms = []
549        self.independent = independent
550
551    def __call__(self):
552        exec(self.code_string)
553        return self.value
554
555    def addReaction(self, reaction, coefficient):
556        self.reactions.append(reaction)
557        self.coefficients.append(coefficient)
558        if coefficient > 0.0:
559            if coefficient == 1.0:
560                term = '+self.%s() ' % (reaction.name)
561                aterm = '+(%s) ' % (reaction.code_string.replace('self.rate=',''))
562                fterm = '+%s' % (reaction.name)
563                afterm = '+ (%s) ' % (reaction.formula)
564            else:
565                term = '+%g*self.%s() ' % (abs(coefficient), reaction.name)
566                aterm = '+%g*(%s) ' % (abs(coefficient), reaction.code_string.replace('self.rate=',''))
567                fterm = '+%g*%s' % (abs(coefficient), reaction.name)
568                afterm = '+ %g*(%s) ' % (abs(coefficient), reaction.formula)
569        else:
570            if coefficient == -1.0:
571                term = '-self.%s() ' % (reaction.name)
572                aterm = '-(%s) ' % (reaction.code_string.replace('self.rate=',''))
573                fterm = '-%s' % (reaction.name)
574                afterm = '- (%s) ' % (reaction.formula)
575            else:
576                term = '-%g*self.%s() ' % (abs(coefficient), reaction.name)
577                aterm = '-%g*(%s) ' % (abs(coefficient), reaction.code_string.replace('self.rate=',''))
578                fterm = '-%g*%s' % (abs(coefficient), reaction.name)
579                afterm = '- %g*(%s) ' % (abs(coefficient), reaction.formula)
580        self.ode_terms.append(term)
581        self.code_string += term
582        self.code_string_alt += aterm
583        self.formula += fterm
584        self.formula_alt += afterm
585        self.__setattr__(reaction.name, reaction)
586
587    def hasReactions(self):
588        return MapList([r.name for r in self.reactions])
589
590    def getFormula(self):
591        return self.code_string
592
593    def getGlobalFormula(self):
594        return self.code_string_alt
595
596
597class StructMatrix(NewCoreBase):
598    """
599    This class is specifically designed to store structural matrix information
600    give it an array and row/col index permutations it can generate its own
601    row/col labels given the label src.
602    """
603
604    array = None
605    ridx = None
606    cidx = None
607    row = None
608    col = None
609
610    def __init__(self, array, ridx, cidx, row=None, col=None):
611        """
612        Instantiate with array and matching row/col index arrays, optional label arrays
613        """
614        self.array = array
615        self.ridx = ridx
616        self.cidx = cidx
617        self.row = row
618        self.col = col
619        self.shape = array.shape
620
621    def __call__(self):
622        return self.array
623
624    def getRowsByIdx(self, *args):
625        """Return the rows referenced by index (1,3,5)"""
626        return self.array.take(args, axis=0)
627
628    def getColsByIdx(self, *args):
629        """Return the columns referenced by index (1,3,5)"""
630        return self.array.take(args, axis=1)
631
632    def setRow(self, src):
633        """
634        Assuming that the row index array is a permutation (full/subset)
635        of a source label array by supplying that source to setRow it
636        maps the row labels to ridx and creates self.row (row label list)
637        """
638        self.row = [src[r] for r in self.ridx]
639
640    def setCol(self, src):
641        """
642        Assuming that the col index array is a permutation (full/subset)
643        of a source label array by supplying that src to setCol
644        maps the row labels to cidx and creates self.col (col label list)
645        """
646        self.col = [src[c] for c in self.cidx]
647
648    def getRowsByName(self, *args):
649        """Return the rows referenced by label ('s','x','d')"""
650        assert self.row != None, "\nI need row labels"
651        try:
652            return self.array.take([self.row.index(l) for l in args], axis=0)
653        except Exception as ex:
654            print(ex)
655            print("\nValid row labels are: %s" % self.row)
656            return None
657
658    def getColsByName(self, *args):
659        """Return the columns referenced by label ('s','x','d')"""
660        assert self.col != None, "\nI need column labels"
661        try:
662            return self.array.take([self.col.index(l) for l in args], axis=1)
663        except Exception as ex:
664            print(ex)
665            print("Valid column labels are: %s" % self.col)
666            return None
667
668    def getLabels(self, axis='all'):
669        """Return the matrix labels ([rows],[cols]) where axis='row'/'col'/'all'"""
670        if axis == 'row': return self.row
671        elif axis == 'col': return self.col
672        else: return self.row, self.col
673
674    def getIndexes(self, axis='all'):
675        """Return the matrix indexes ([rows],[cols]) where axis='row'/'col'/'all'"""
676        if axis == 'row': return self.ridx
677        elif axis == 'col': return self.cidx
678        else: return self.ridx, self.cidx
679
680    def getByIdx(self, row, col):
681        assert row in self.ridx, '\n%s is an invalid index' % row
682        assert col in self.cidx, '\n%s is an invalid index' % col
683        return self.array[row, col]
684
685    def getByName(self, row, col):
686        assert row in self.row, '\n%s is an invalid name' % row
687        assert col in self.col, '\n%s is an invalid name' % col
688        return self.array[self.row.index(row), self.col.index(col)]
689
690    def setByIdx(self, row, col, val):
691        assert row in self.ridx, '\n%s is an invalid index' % row
692        assert col in self.cidx, '\n%s is an invalid index' % col
693        self.array[row, col] = val
694
695    def setByName(self, row, col, val):
696        assert row in self.row, '\n%s is an invalid name' % row
697        assert col in self.col, '\n%s is an invalid name' % col
698        self.array[self.row.index(row), self.col.index(col)] = val
699
700    def shape(self):
701        return self.array.shape
702
703class EventAssignment(NumberBase):
704    variable = None
705    _names = None
706    formula = None
707    code_string = None
708    xcode = None
709
710    def __call__(self):
711        self.variable.value = self.value
712        if self.__DEBUG__: print('\tAssigning %s = %s' % (self.variable.name, self.value))
713        return True
714
715    def __init__(self, name='None'):
716        self.setName(name)
717
718    def setVariable(self, var):
719        self.variable = var
720
721    def setFormula(self, formula):
722        self.formula = formula
723        InfixParser.setNameStr('self.', '()')
724        ##  InfixParser.SymbolReplacements = {'_TIME_':'_TIME_()'}
725        InfixParser.parse(formula)
726        self._names = InfixParser.names
727        self.code_string = 'self.value=%s' % InfixParser.output
728        self.xcode = compile(self.code_string, '<string>', 'exec')
729        if self.__DEBUG__: '\t', self.name, self.code_string
730
731    def evaluateAssignment(self):
732        exec(self.xcode)
733
734
735class Event(NewCoreBase):
736    trigger = None
737    delay = 0.0
738
739    formula = None
740    code_string = None
741    xcode = None
742
743    state0 = False
744    state = False
745
746    assignments = None
747    _TIME_ = None
748    _ASS_TIME_ = 0.0
749    _need_action = False
750    _names = None
751    _time_symbol = None
752
753    def __init__(self, name):
754        self.setName(name)
755        self.assignments = []
756
757    def __call__(self, time):
758        self._TIME_.set(time)
759        exec(self.xcode)
760        if self.state0 and not self.state:
761            self.state0 = self.state
762        if not self.state0 and self.state:
763            for ass in self.assignments:
764                ass.evaluateAssignment()
765            self.state0 = self.state
766            self._need_action = True
767            self._ASS_TIME_ = self._TIME_() + self.delay
768            if self.__DEBUG__: print('event %s is evaluating at %s' % (self.name, time))
769        if self._need_action and self._TIME_() >= self._ASS_TIME_:
770            for ass in self.assignments:
771                ass()
772            if self.__DEBUG__: print('event %s is assigning at %s (delay=%s)' % (self.name, time, self.delay))
773            self._need_action = False
774
775    def setTrigger(self, formula, delay=0.0):
776        self.formula = formula
777        self.delay = delay
778        InfixParser.setNameStr('self.', '()')
779        ##  print self._time_symbol
780        if self._time_symbol != None:
781            InfixParser.SymbolReplacements = {self._time_symbol : '_TIME_'}
782            ##  self.formula = formula.replace(self._time_symbol, '_TIME_')
783        InfixParser.parse(formula)
784        self._names = InfixParser.names
785        self.code_string = 'self.state=%s' % InfixParser.output
786        if self._time_symbol != None:
787            InfixParser.setNameStr('', '')
788            InfixParser.SymbolReplacements = {self._time_symbol : '_TIME_'}
789            InfixParser.parse(formula)
790            self.formula = InfixParser.output
791        self.xcode = compile(self.code_string, '<string>', 'exec')
792        if self.__DEBUG__: self.name, self.code_string
793
794    def setTriggerAttributes(self, core):
795        # TODO: experimental
796        for n in self._names:
797            self.__setattr__(n, core.__getattribute__(n))
798
799    def setAssignment(self, var, formula):
800        ass = EventAssignment(var.name)
801        ass.setVariable(var)
802        ass.setFormula(formula)
803        self.assignments.append(ass)
804        self.__setattr__('_'+var.name, ass)
805
806class PieceWise(NewCoreBase):
807    """
808    Generic piecewise class written by me!
809
810    - *args* a dictionary of piecewise information generated by the InfixParser
811    """
812    name = None
813    value = None
814    formula = None
815    code_string = None
816    xcode = None
817    _names = None
818    _TIME_ = None
819
820    def __init__(self, pwd):
821        pwd = pwd.copy()
822        if pwd['other'] != None:
823            other = 'self.value = %s' % pwd.pop('other')
824        else:
825            other = 'pass'
826            pwd.pop('other')
827        InfixParser.setNameStr('self.', '')
828        InfixParser.SymbolReplacements = {'_TIME_':'_TIME_()'}
829        self._names = []
830        if len(list(pwd.keys())) == 1:
831            formula = pwd[0][0]
832            InfixParser.parse(formula)
833            for n in InfixParser.names:
834                if n not in self._names and n != '_TIME_()':
835                    self._names.append(n)
836            formula = InfixParser.output
837            self.code_string = 'if %s:\n    self.value = %s\nelse:\n    %s' %\
838            (formula, pwd[0][1], other)
839            self.formula = self.code_string.replace('self.','')
840        else:
841            formula = pwd[0][0]
842            InfixParser.parse(formula)
843            for n in InfixParser.names:
844                if n not in self._names and n != '_TIME_()':
845                    self._names.append(n)
846
847            formula = InfixParser.output
848            self.code_string = 'if %s:\n    self.value = %s\n' % (formula, pwd[0][1])
849            pwd.pop(0)
850            for p in pwd:
851                formula = pwd[p][0]
852                InfixParser.SymbolReplacements = {'_TIME_':'_TIME_()'}
853                InfixParser.parse(formula)
854                for n in InfixParser.names:
855                    if n not in self._names and n != '_TIME_()':
856                        self._names.append(n)
857                formula = InfixParser.output
858                self.code_string += 'elif %s:\n    self.value = %s\n' % (formula, pwd[p][1])
859            self.code_string += 'else:\n    %s' % other
860            self.formula = self.code_string.replace('self.','')
861        self.xcode = compile(self.code_string, 'PieceWise','exec')
862
863
864    def __call__(self):
865        exec(self.xcode)
866        return self.value
867
868
869class Time(object):
870    value = None
871    name = '__TIME__'
872    def __init__(self, t=0):
873        self.value = t
874
875    def __call__(self):
876        return self.value
877
878    def set(self, t):
879        self.value=t
880
881##  def delay(*args):
882    ##  print 'delay() ignored'
883    ##  return 1.0
884
885class NewCore(NewCoreBase):
886    __nDict__ = None
887    reactions = None
888    species = None
889    species_variable = None
890    __model__ = None
891    __InitDict__ = None
892    __not_inited__ = None
893    global_parameters = None
894    __parameter_store__ = None
895    forcing_functions = None
896    __rules__ = None
897    __events__ = None
898    # new
899    __compartments__ = None
900    compartments = None
901    rate_rules = None
902    description = "Pysces Core2"
903    __uDict__ = None
904    stoichiometric_matrix = None
905    struct = None
906    ODEs = None
907    functions = None
908    _TIME_ = None
909    events = None
910    __sDict__ = None
911    __KeyWords__ = None
912    __piecewises__ = None
913    piecewise_functions = None
914    netStoich = None
915
916    def __init__(self, model, iValues=True, netStoich=True):
917        # setup core dictionaries
918        self.__nDict__ = model.__nDict__
919        self.__sDict__ = model.__sDict__
920        self.__KeyWords__ = model.__KeyWords__
921        if self.__KeyWords__['Modelname'] != None:
922            self.setName(self.__KeyWords__['Modelname'])
923        else:
924            self.setName('PySCeSModel')
925        if self.__KeyWords__['Description'] != None:
926            self.setDescription(self.__KeyWords__['Description'])
927        else:
928            self.setDescription('PySCeSModel')
929
930        self.__model__ = model
931        self.__InitDict__ = model.__InitDict__
932        if not iValues:
933            if self.__DEBUG__: print(self.__InitDict__)
934            for k in list(self.__InitDict__.keys()):
935                self.__InitDict__[k] = getattr(self.__model__, k)
936            for c in model.__compartments__:
937                model.__compartments__[c]['size'] = getattr(self.__model__, c)
938        self.netStoich = netStoich
939
940        self.global_parameters = []
941        self.__parameter_store__ = []
942        self.__not_inited__ = []
943        self.forcing_functions = []
944        self.__rules__ = model.__rules__
945        self.__uDict__ = model.__uDict__
946        self.__piecewises__ = model.__piecewises__
947        InfixParser.__pwcntr__ = 0
948
949        # start building objects
950        self.__compartments__ = model.__compartments__
951        self.addCompartments()
952        self._TIME_ = Time()
953        self.addPieceWiseFunctions() # this adds any piecewise functions
954        self.addSpecies()
955
956        # the order is important from here as eg functions can occur in rate equations
957        try:
958            self.__functions__ = model.__functions__
959        except:
960            self.__functions__ = {}
961            if self.__DEBUG__: print('No functions')
962        self.functions = []
963        self.addFunctions()
964        self.addReactions()
965        self.generateMappings()
966        self.setAssignmentRules()
967        self.setRateRules()
968
969        # add event support
970        self.__events__ = self.__model__.__eDict__
971        self.events = []
972        self.addEvents()
973        self.addPieceWiseFunctions(update=True)  # this updates their attributes
974
975        ##  # get rid of _TIME_ in not intited
976        ##  if '_TIME_' in self.__not_inited__:
977            ##  self.__not_inited__.pop(self.__not_inited__.index('_TIME_'))
978        if len(self.__not_inited__) > 0:
979            print("\nWARNING: Uninitialised parameters: %s (value set to zero)" % self.__not_inited__)
980
981    def __cleanString__(self,s):
982        s = s.lstrip()
983        s = s.rstrip()
984        return s
985
986    ##  def parseForcingFunctions(self):
987        ##  self.__rules__ = {}
988        ##  try:
989            ##  ff = self.__function_forced_str__.split('\n')
990            ##  for f in ff:
991                ##  if f != '':
992                    ##  f =  f.split('=')
993                    ##  f[0] = f[0].replace('self.','')
994                    ##  f[1] = f[1].replace('self.','')
995                    ##  self.__rules__.setdefault(self.__cleanString__(f[0]), self.__cleanString__(f[1]))
996        ##  except Exception, ex:
997            ##  pass
998            # print 'No forcing functions (%s).' % ex
999
1000    def setDescription(self, txt):
1001        self.description = str(txt)
1002
1003    def getDescription(self):
1004        return str(self.description)
1005
1006    def setGlobalUnits(self, **kwargs):
1007        for un in list(kwargs.keys()):
1008            self.__uDict__[un] = (kwargs[un][0], kwargs[un][1])
1009            if self.__DEBUG__: print("Modified \"%s\" to be %i*%s*10**%i" % (un, kwargs[un][0], un, kwargs[un][1]))
1010
1011    def getGlobalUnits(self):
1012        return self.__uDict__
1013
1014    def addPieceWiseFunctions(self, update=False):
1015        if not update:
1016            self.piecewise_functions = []
1017            for pw in list(self.__piecewises__.keys()):
1018                if self.__DEBUG__: print('Info: adding piecewise function:%s' % pw)
1019                P = PieceWise(self.__piecewises__[pw])
1020                P.setName(pw)
1021                P.__setattr__('_TIME_', self.__getattribute__('_TIME_'))
1022                self.piecewise_functions.append(P)
1023                self.__setattr__(pw, P)
1024        else:
1025            for pw in self.piecewise_functions:
1026                for a in pw._names:
1027                    pw.__setattr__(a, self.__getattribute__(a))
1028
1029
1030
1031    def addOneCompartment(self, name, size, dimensions, compartment=None, area=None):
1032        C = Compartment(name, compartment)
1033        C.setSize(size, dimensions)
1034        ##  C.setArea(area)
1035        self.compartments.append(C)
1036        self.__setattr__(name, C)
1037
1038    def addCompartments(self):
1039        self.compartments = []
1040        for C in self.__compartments__:
1041            c2 = self.__compartments__[C]
1042            if self.__DEBUG__: print('Adding compartment %s' % c2['name'])
1043            self.addOneCompartment(c2['name'], c2['size'], c2['dimensions'],
1044                            compartment=c2['compartment'], area=None)
1045
1046    def addOneSpecies(self, species, value, fix=False, comp=None, amount=False, fullName=None):
1047        s = Species(species, value)
1048        ##  if comp != None:
1049        s.setCompartment(comp)
1050        s.setAmount(amount)
1051        s.setAnnotation('sbml_name', fullName)
1052        if fix: s.fixed = True
1053        self.__setattr__(species, s)
1054        self.species.append(s)
1055        if not fix: self.species_variable.append(s)
1056        if comp != None:
1057            comp.addSpecies(s)
1058
1059    def addSpecies(self):
1060        self.species = []
1061        self.species_variable = []
1062        for s in self.__sDict__:
1063            ##  print s
1064            ##  print self.__sDict__[s]
1065            name = self.__sDict__[s]['name']
1066            if s in self.__InitDict__:
1067                val = self.__InitDict__[s]
1068            else:
1069                val = 0.0
1070            ##  print val
1071            fix = self.__sDict__[s]['fixed']
1072            if self.__sDict__[s]['compartment'] != None:
1073                comp = self.__getattribute__(self.__sDict__[s]['compartment'])
1074            else:
1075                comp = None
1076            amount = self.__sDict__[s]['isamount']
1077            fullName = None
1078            if 'fullName' in self.__sDict__[s]:
1079                fullName = self.__sDict__[s]['fullName']
1080            self.addOneSpecies(name, val, fix=fix, comp=comp, amount=amount, fullName=fullName)
1081
1082    def addOneFunction(self, name, args, formula):
1083        func = Function(name)
1084        # TODO: make better
1085        setattr(func, '_TIME_', self._TIME_)
1086        for a in args:
1087            func.setArg(a)
1088        func.addFormula(formula)
1089        self.functions.append(func)
1090        self.__setattr__(name, func)
1091
1092
1093    def addFunctions(self):
1094        for f in list(self.__functions__.keys()):
1095            self.addOneFunction(f,\
1096                                self.__functions__[f]['args'],\
1097                                self.__functions__[f]['formula'])
1098
1099    def addOneReaction(self, rDict):
1100        r = Reaction(rDict['name'])
1101        if rDict['compartment'] != None:
1102            C = self.__getattribute__(rDict['compartment'])
1103            r.setCompartment(C)
1104            C.addReaction(r)
1105        fullName = None
1106        if 'fullName' in rDict:
1107            r.setAnnotation('sbml_name', rDict['fullName'])
1108
1109        # add dummy reaction kinetic law
1110        # TODO: make better
1111        setattr(r, '_TIME_', self._TIME_)
1112        if rDict['RateEq'] != None:
1113            r.addFormula(rDict['RateEq'].replace('self.',''))
1114        else:
1115            r.addFormula('J')
1116            self.addParameter('J')
1117        if rDict['Type'] == 'Irrev': r.reversible = False
1118        # now we can add formulas that occured in the rate equation
1119        if len(r._functions) > 0:
1120            for func in r._functions:
1121                try:
1122                    r.addFunction(self.__getattribute__(func))
1123                except Exception as ex:
1124                    print(ex)
1125                    print('\nHave you added the function objects yet (addFunctions())')
1126
1127        #fxnames = self.hasFixedSpecies()
1128        processed_parameter = []
1129
1130        # where parameters are defined `locally' per reaction
1131        for p in rDict['Params']:
1132            p = p.replace('self.','')
1133            if p not in self.hasGlobalParameters() and not (p in self.hasFixedSpecies() or p in self.__compartments__):
1134                if self.__DEBUG__: print("Adding parameter %s from networkdict" % p)
1135                self.addParameter(p)
1136                par = self.__getattribute__(p)
1137                par.setAssociation(r)
1138                r.addParameter(par)
1139                processed_parameter.append(p)
1140            elif not (p in self.hasFixedSpecies() or p in self.__compartments__):
1141                if self.__DEBUG__: print("Updating parameter %s from networkdict" % p)
1142                pidx = self.hasGlobalParameters().index(p)
1143                self.global_parameters[pidx].setAssociation(r)
1144                r.addParameter(self.global_parameters[pidx])
1145                processed_parameter.append(p)
1146
1147        #print self.hasGlobalParameters()
1148        # where parameters are not `locally' defined and are extracted from Req (ie from SBML)
1149        for p in r._names:
1150            p = p.replace('self.','')
1151            if p == '_TIME_':
1152                pass
1153            elif p in [pw.name for pw in self.piecewise_functions]:
1154                pass
1155            elif p in self.hasCompartments() and p not in processed_parameter:
1156                C = self.__getattribute__(p)
1157                C.addReaction(r)
1158                # TODO: this will work until isParameterOf is called on a compartment object
1159                r.addParameter(C)
1160                # dirty alternative
1161                #setattr(r, C.name, C)
1162                processed_parameter.append(p)
1163            elif p not in processed_parameter and p not in self.hasGlobalParameters() and p not in self.hasSpecies():
1164                if self.__DEBUG__: print("Adding parameter %s from global" % p)
1165                self.addParameter(p)
1166                par = self.__getattribute__(p)
1167                par.setAssociation(r)
1168                r.addParameter(par)
1169                processed_parameter.append(p)
1170
1171            elif p not in processed_parameter and p not in self.hasSpecies():
1172                if self.__DEBUG__: print("Updating parameter %s from global" % p)
1173                pidx = self.hasGlobalParameters().index(p)
1174                self.global_parameters[pidx].setAssociation(r)
1175                r.addParameter(self.global_parameters[pidx])
1176                processed_parameter.append(p)
1177
1178        self.__setattr__(rDict['name'], r)
1179        self.reactions.append(r)
1180
1181    def addParameter(self, name):
1182        if name not in self.__piecewises__:
1183            if name in self.__InitDict__:
1184                par = Parameter(name, self.__InitDict__[name])
1185            else:
1186                par = Parameter(name, 0.0)
1187                if name not in self.__not_inited__: self.__not_inited__.append(name)
1188            self.global_parameters.append(par)
1189            self.__setattr__(name, par)
1190
1191    def addReactions(self):
1192        self.reactions = []
1193        for r in self.__model__.reactions:
1194            self.addOneReaction(self.__nDict__[r])
1195        non_parameters = self.hasGlobalParameters()+self.hasSpecies()+self.hasFixedSpecies()
1196        for k in list(self.__InitDict__.keys()):
1197            if k not in non_parameters:
1198                if self.__DEBUG__: print('Adding new parameter:', k)
1199                self.addParameter(k)
1200
1201    def replaceParameterWithRule(self, ar):
1202        par = self.__getattribute__(ar.name)
1203        for r in par.association:
1204            ar.setAssociation(r)
1205            setattr(r, ar.name, ar)
1206            r.parameters[r.hasParameters().index(ar.name)] = ar
1207        self.global_parameters[self.hasGlobalParameters().index(ar.name)] = ar
1208        self.__setattr__(ar.name, ar)
1209
1210    def replaceFixedSpeciesWithRule(self, ar):
1211        fs = self.__getattribute__(ar.name)
1212        ar.fixed = fs.fixed
1213        for r in fs.subs:
1214            ar.setSubstrate(r)
1215            setattr(r, ar.name, ar)
1216            r.substrates[r.hasSubstrates().index(ar.name)] = ar
1217        for r in fs.prods:
1218            ar.setProduct(r)
1219            setattr(r, ar.name, ar)
1220            r.products[r.hasProducts().index(ar.name)] = ar
1221        for r in fs.mods:
1222            ar.setModifier(r)
1223            setattr(r, ar.name, ar)
1224            r.modifiers[r.hasModifiers().index(ar.name)] = ar
1225        self.species[self.hasSpecies().index(ar.name)] = ar
1226        self.__setattr__(ar.name, ar)
1227
1228    def replaceSpeciesWithRule(self, ar):
1229        fs = self.__getattribute__(ar.name)
1230        for r in fs.subs:
1231            ar.setSubstrate(r)
1232            setattr(r, ar.name, ar)
1233            r.substrates[r.hasSubstrates().index(ar.name)] = ar
1234        for r in fs.prods:
1235            ar.setProduct(r)
1236            setattr(r, ar.name, ar)
1237            r.products[r.hasProducts().index(ar.name)] = ar
1238        for r in fs.mods:
1239            ar.setModifier(r)
1240            setattr(r, ar.name, ar)
1241            r.modifiers[r.hasModifiers().index(ar.name)] = ar
1242        self.species[self.hasSpecies().index(ar.name)] = ar
1243        self.species_variable[self.hasVariableSpecies().index(ar.name)] = ar
1244        self.__setattr__(ar.name, ar)
1245
1246    def setAssignmentRules(self):
1247        aps = [self.__rules__[ar]['name'] for ar in self.__rules__ if self.__rules__[ar]['type'] == 'assignment']
1248        ##  for p in self.global_parameters + [self.get(fs) for fs in self.hasFixedSpecies()]:
1249        for p in self.global_parameters + self.species:
1250            #print p.name
1251            if p.name in aps:
1252                if self.__DEBUG__: print('Assigning: %s = %s' % (p.name, self.__rules__[p.name]['formula']))
1253                p2 = None
1254                # TODO: make better
1255                if p.name in self.hasGlobalParameters():
1256                    p2 = AssignmentRule(p.name, self.__InitDict__[p.name])
1257                    setattr(p2, '_TIME_', self._TIME_)
1258                    self.replaceParameterWithRule(p2)
1259                elif p.name in self.hasFixedSpecies():
1260                    p2 = SpeciesAssignmentRule(p.name, self.__InitDict__[p.name])
1261                    p2.setCompartment(p.getCompartment())
1262                    setattr(p2, '_TIME_', self._TIME_)
1263                    self.replaceFixedSpeciesWithRule(p2)
1264                elif p.name in self.hasVariableSpecies():
1265                    p2 = SpeciesAssignmentRule(p.name, self.__InitDict__[p.name])
1266                    p2.setCompartment(p.getCompartment())
1267                    setattr(p2, '_TIME_', self._TIME_)
1268                    self.replaceSpeciesWithRule(p2)
1269
1270                assert isinstance(p2, AssignmentRule) or isinstance(p2, SpeciesAssignmentRule), "\nHappy assertion error"
1271                #print type(p2)
1272                p2.addFormula(self.__rules__[p.name]['formula'])
1273                ##  print p2._names
1274                for n in p2._names+p2._functions:
1275                    p2.addModelAttr(self.__getattribute__(n))
1276                ##  # setup initial values
1277                ##  p2.value_initial = self.p2()
1278                if p2.name in self.__not_inited__:
1279                    self.__not_inited__.pop(self.__not_inited__.index(p.name))
1280        for p in self.global_parameters:
1281            if p.name in self.hasAssignmentRules():
1282                # TODO assignment rules need a list of properties
1283                for ar in p._names:
1284                    if ar in self.hasAssignmentRules():
1285                        setattr(p, ar, self.__getattribute__(ar))
1286        #TODO this is where things will go wrong if fs --> ar contains nested ar's
1287
1288    def setRateRules(self):
1289        # TODO mayvbe split into two methods for now read from self.__rules__
1290        # TODO add functions to rules
1291        ars = [self.__rules__[ar]['name'] for ar in self.__rules__ if self.__rules__[ar]['type'] == 'rate']
1292        self.rate_rules = []
1293        for rr in ars:
1294            rrobj = RateRule(self.__rules__[rr]['name'], self.__rules__[rr]['formula'])
1295            ##  print 'RR:', rrobj.name, rrobj._names, rrobj._functions
1296            for symb in  rrobj._names+rrobj._functions:
1297                rrobj.addModelAttr(self.__getattribute__(symb))
1298            self.rate_rules.append(rrobj)
1299            # TODO investgiate this as it is problematic, the rate rule
1300            # is not a model property as such more an ODE property
1301            ##  self.__setattr__(rrobj.name, rrobj)
1302            if self.__DEBUG__: print('Adding RateRule %s with formula: %s' % (rrobj.name, rrobj.formula))
1303
1304    def addOneEvent(self, e):
1305        """Add a single event using an event dictionary """
1306        # translate self.__events__[e] to e
1307
1308        ev = Event(e['name'])
1309        ev._time_symbol = e['tsymb']
1310        ev.setTrigger(e['trigger'], e['delay'])
1311        # associate model attributes with event
1312        # TODO: check that this still works
1313        ev.setTriggerAttributes(self)
1314        ##  for n in ev._names:
1315            ##  setattr(ev, n, self.__getattribute__(n))
1316        # for each assignment
1317        for ass in e['assignments']:
1318            ev.setAssignment(self.__getattribute__(ass), e['assignments'][ass])
1319            assref = getattr(ev, '_'+ass) # don\t like this at all :-(
1320            # associate model attributes with assignment
1321            for n in assref._names:
1322                setattr(assref, n, self.__getattribute__(n))
1323        self.events.append(ev)
1324        self.__setattr__(ev.name, ev)
1325        setattr(ev, '_TIME_', self._TIME_)
1326
1327    def addEvents(self):
1328        # TODO: check that you can change the trigger on the fly (might need a setAttr thing in event obj)
1329        self.events = []
1330        # for each event
1331        for e in self.__events__:
1332            self.addOneEvent(self.__events__[e])
1333
1334    def generateMappings(self):
1335        ##  self.netStoich = False
1336        for reac in self.reactions:
1337            if self.netStoich:
1338                for reag in self.__nDict__[reac.name]['Reagents']:
1339                    if self.__nDict__[reac.name]['Reagents'][reag] < 0.0:
1340                        reac.addSubstrate(self.__getattribute__(reag.replace('self.','')))
1341                        self.__getattribute__(reag.replace('self.','')).setSubstrate(self.__getattribute__(reac.name))
1342                    else:
1343                        reac.addProduct(self.__getattribute__(reag.replace('self.','')))
1344                        self.__getattribute__(reag.replace('self.','')).setProduct(self.__getattribute__(reac.name))
1345                    reac.stoichiometry.setdefault(reag.replace('self.',''), self.__nDict__[reac.name]['Reagents'][reag])
1346            else:
1347                for reag in self.__nDict__[reac.name]['AllReagents']:
1348                    if reag[1] < 0.0:
1349                        reac.addSubstrate(self.__getattribute__(reag[0].replace('self.','')))
1350                        self.__getattribute__(reag[0].replace('self.','')).setSubstrate(self.__getattribute__(reac.name))
1351                    else:
1352                        reac.addProduct(self.__getattribute__(reag[0].replace('self.','')))
1353                        self.__getattribute__(reag[0].replace('self.','')).setProduct(self.__getattribute__(reac.name))
1354                    reac.multistoich.append((reag[0].replace('self.',''), reag[1]))
1355                    if reag[0].replace('self.','') in reac.stoichiometry:
1356                        reac.multistoich_enabled = True
1357                    reac.stoichiometry.setdefault(reag[0].replace('self.',''), reag[1])
1358            for mod in self.__nDict__[reac.name]['Modifiers']:
1359                reac.addModifier(self.__getattribute__(mod.replace('self.','')))
1360                self.__getattribute__(mod.replace('self.','')).setModifier(self.__getattribute__(reac.name))
1361            ##  print 'I AM LEGEND'
1362            ##  print reac.stoichiometry
1363            ##  print reac.multistoich
1364            ##  print 'reac.multistoich_enabled', reac.multistoich_enabled
1365            ##  print self.__nDict__[reac.name]['Reagents']
1366            ##  print self.__nDict__[reac.name]['AllReagents']
1367
1368    def setStoichiometricMatrix(self):
1369        vspec = self.hasVariableSpecies()
1370        react = self.hasReactions()
1371        nm = numpy.zeros((len(vspec), len(react)),'d')
1372        for sp in vspec:
1373            for r in self.get(sp).isReagentOf():
1374                nm[vspec.index(sp)][react.index(r)] = self.get(r).stoichiometry[sp]
1375            # this is if absolute stoichiometry value is used
1376            ##  for r in self.get(sp).isSubstrateOf():
1377                ##  nm[vspec.index(sp)][react.index(r)] = abs(self.get(r).stoichiometry[sp])
1378            ##  for r in self.get(sp).isProductOf():
1379                ##  nm[vspec.index(sp)][react.index(r)] = -abs(self.get(r).stoichiometry[sp])
1380        self.stoichiometric_matrix = StructMatrix(nm, list(range(len(vspec))), list(range(len(react))))
1381        self.stoichiometric_matrix.setRow(vspec)
1382        self.stoichiometric_matrix.setCol(react)
1383
1384    def addODEs(self):
1385        self.ODEs = []
1386        for varspec in self.stoichiometric_matrix.row:
1387            if self.struct != None:
1388                if varspec not in self.struct.Nr.row:
1389                    if self.__DEBUG__: print('Creating dependent ODE_%s' % varspec)
1390                    ode = ODE(self.get(varspec), independent=False)
1391                else:
1392                    if self.__DEBUG__: print('Creating independent ODE_%s' % varspec)
1393                    ode = ODE(self.get(varspec), independent=True)
1394            else:
1395                if self.__DEBUG__: print('Creating independent* ODE_%s (*assumed - no structural information available)' % varspec)
1396                ode = ODE(self.get(varspec), independent=True)
1397            mrow = self.stoichiometric_matrix.getRowsByName(varspec)
1398            for e in range(len(mrow[0])):
1399                if mrow[0,e] != 0.0:
1400                    print('Adding term: %s*%s' % (mrow[0,e], self.stoichiometric_matrix.col[e]))
1401                    ode.addReaction(self.get(self.stoichiometric_matrix.col[e]), mrow[0,e])
1402            self.__setattr__(ode.name, ode)
1403            self.ODEs.append(ode)
1404            self.__setattr__('xcode_'+ode.name, compile(ode.getGlobalFormula(), '<string>', 'exec'))
1405
1406    def hasODEs(self):
1407        return MapList([o.name for o in self.ODEs])
1408
1409    def evalODEs(self, odes):
1410        return [v() for v in odes]
1411
1412    def evalXcode(self, ode):
1413        exec(self.__getattribute__('xcode_'+ode.name))
1414        return sdot
1415
1416    def hasFunctions(self):
1417        return MapList([f.name for f in self.functions])
1418
1419    def hasReactions(self):
1420        return MapList([r.name for r in self.reactions])
1421
1422    def hasSpecies(self):
1423        return MapList([s.name for s in self.species])
1424
1425    def hasFixedSpecies(self):
1426        return MapList([s.name for s in self.species if s.fixed])
1427
1428    def hasVariableSpecies(self):
1429        return MapList([s.name for s in self.species if not s.fixed])
1430
1431    def findReactionsThatIncludeAllSpecifiedReagents(self, *args):
1432        assert len(args) > 1, '\nNeed two or more species for this one!'
1433        setlist = [self.__getattribute__(s).isReagentOf().asSet() for s in args]
1434        isect = setlist[0]
1435        for s in setlist:
1436            isect.intersection_update(s)
1437        return MapList(isect)
1438
1439    def hasGlobalParameters(self):
1440        return MapList(p.name for p in self.global_parameters)
1441
1442    def hasAssignmentRules(self):
1443        return MapList([ar.name for ar in self.global_parameters+self.species if hasattr(ar, 'type')=='assignemnt'])
1444
1445    def hasAssignmentRules(self):
1446        return MapList([ar.name for ar in self.global_parameters+self.species if hasattr(ar, 'type')=='rate'])
1447
1448    def hasEvents(self):
1449        return MapList(e.name for e in self.events)
1450
1451    def hasCompartments(self):
1452        return MapList(c.name for c in self.compartments)
1453
1454##  if __psyco_active__:
1455    ##  psyco.bind(NewCoreBase)
1456    ##  psyco.bind(NumberBase)
1457    ##  psyco.bind(Species)
1458    ##  psyco.bind(Parameter)
1459    ##  psyco.bind(AssignmentRule)
1460    ##  psyco.bind(Reaction)
1461    ##  psyco.bind(ODE)
1462    ##  psyco.bind(NewCore)
1463
1464
1465
1466