Exemple #1
0
 def __init__(self, model, dialect='kasim', _warn_no_ic=True):
     if model and model.compartments:
         raise CompartmentsNotSupported()
     self.model = model
     self.__content = None
     self.dialect = dialect
     self._warn_no_ic = _warn_no_ic
     self._renamed_states = collections.defaultdict(dict)
     self._log = pysb.logging.get_logger(__name__)
Exemple #2
0
 def __init__(self, model, dialect='kasim', _warn_no_ic=True,
              _exclude_ic_param=False):
     if model:
         if model.compartments:
             raise CompartmentsNotSupported()
         if model.tags:
             raise LocalFunctionsNotSupported()
     self.model = model
     self.__content = None
     self.dialect = dialect
     self._warn_no_ic = _warn_no_ic
     self._exclude_ic_param = _exclude_ic_param
     self._renamed_states = collections.defaultdict(dict)
     self._log = pysb.logging.get_logger(__name__)
Exemple #3
0
    def export(self):
        """Export the SBML for the PySB model associated with the exporter.

        Returns
        -------
        string
            String containing the SBML output.
        """
        if self.model.expressions:
            raise ExpressionsNotSupported()
        if self.model.compartments:
            raise CompartmentsNotSupported()

        output = StringIO()
        pysb.bng.generate_equations(self.model)

        output.write("""<?xml version="1.0" encoding="UTF-8"?>
    <sbml xmlns="http://www.sbml.org/sbml/level2" level="2" version="1">
        <model name="%s">""" % self.model.name)

        if self.docstring:
            notes_str = """
            <notes>
                <body xmlns="http://www.w3.org/1999/xhtml">
                    <p>%s</p>
                </body>
            </notes>""" % self.docstring.replace("\n", "<br />\n" + " " * 20)
            output.write(notes_str)

        output.write("""
            <listOfCompartments>
                <compartment id="default" name="default" spatialDimensions="3" size="1"/>
            </listOfCompartments>
    """)

        # complexpattern, initial value
        ics = [[s, 0] for s in self.model.species]
        for cp, ic_param in self.model.initial_conditions:
            ics[self.model.get_species_index(cp)][1] = ic_param.value
        output.write("        <listOfSpecies>\n")
        for i, (cp, value) in enumerate(ics):
            id = '__s%d' % i
            metaid = 'metaid_%d' % i
            name = str(cp).replace(
                '% ',
                '._br_')  # CellDesigner does something weird with % in names
            output.write(
                '            <species id="%s" metaid="%s" name="%s" compartment="default" initialAmount="%.17g">\n'
                % (id, metaid, name, value))
            output.write(get_species_annotation(metaid, cp))
            output.write('            </species>\n')
        output.write("        </listOfSpecies>\n")

        output.write("        <listOfParameters>\n")
        for i, param in enumerate(self.model.parameters_rules()):
            output.write(
                '            <parameter id="%s" metaid="metaid_%s" name="%s" value="%.17g"/>\n'
                % (param.name, param.name, param.name, param.value))
        output.write("        </listOfParameters>\n")

        output.write("        <listOfReactions>\n")
        for i, reaction in enumerate(self.model.reactions_bidirectional):
            reversible = str(reaction['reversible']).lower()
            output.write(
                '            <reaction id="r%d" metaid="metaid_r%d" name="r%d" reversible="%s">\n'
                % (i, i, i, reversible))
            output.write('                <listOfReactants>\n')
            for species in reaction['reactants']:
                output.write(
                    '                    <speciesReference species="__s%d"/>\n'
                    % species)
            output.write('                </listOfReactants>\n')
            output.write('                <listOfProducts>\n')
            for species in reaction['products']:
                output.write(
                    '                    <speciesReference species="__s%d"/>\n'
                    % species)
            output.write('                </listOfProducts>\n')
            mathml = '<math xmlns="http://www.w3.org/1998/Math/MathML">' \
                + print_mathml(reaction['rate']) + '</math>'
            output.write('                <kineticLaw>' + mathml +
                         '</kineticLaw>\n')
            output.write('            </reaction>\n')
        output.write("        </listOfReactions>\n")

        output.write("    </model>\n</sbml>\n")
        return output.getvalue()
Exemple #4
0
    def export(self):
        """Export Python code for simulation of a model without PySB.

        Returns
        -------
        string
            String containing the standalone Python code.
        """
        if self.model.expressions:
            raise ExpressionsNotSupported()
        if self.model.compartments:
            raise CompartmentsNotSupported()

        output = StringIO()
        pysb.bng.generate_equations(self.model)

        # Note: This has a lot of duplication from pysb.integrate.
        # Can that be helped?

        code_eqs = '\n'.join([
            'ydot[%d] = %s;' % (i, sympy.ccode(self.model.odes[i]))
            for i in range(len(self.model.odes))
        ])
        code_eqs = re.sub(r'__s(\d+)', lambda m: 'y[%s]' % (int(m.group(1))),
                          code_eqs)
        for i, p in enumerate(self.model.parameters):
            code_eqs = re.sub(r'\b(%s)\b' % p.name, 'p[%d]' % i, code_eqs)

        if self.docstring:
            output.write('"""')
            output.write(self.docstring)
            output.write('"""\n\n')
        output.write("# exported from PySB model '%s'\n" % self.model.name)
        output.write(
            pad(r"""
            import numpy
            import scipy.integrate
            import collections
            import itertools
            import distutils.errors
            """))
        output.write(
            pad(r"""
            _use_cython = False
            # try to inline a C statement to see if Cython is functional
            try:
                import Cython
            except ImportError:
                Cython = None
            if Cython:
                from Cython.Compiler.Errors import CompileError
                try:
                    Cython.inline('x = 1', force=True, quiet=True)
                    _use_cython = True
                except (CompileError,
                        distutils.errors.CompileError,
                        ValueError):
                    pass

            Parameter = collections.namedtuple('Parameter', 'name value')
            Observable = collections.namedtuple('Observable', 'name species coefficients')
            Initial = collections.namedtuple('Initial', 'param_index species_index')
            """))
        output.write("\n")

        output.write("class Model(object):\n")
        init_data = {
            'num_species': len(self.model.species),
            'num_params': len(self.model.parameters),
            'num_observables': len(self.model.observables),
            'num_ics': len(self.model.initials),
        }
        output.write(
            pad(
                r"""
            def __init__(self):
                self.y = None
                self.yobs = None
                self.y0 = numpy.empty(%(num_species)d)
                self.ydot = numpy.empty(%(num_species)d)
                self.sim_param_values = numpy.empty(%(num_params)d)
                self.parameters = [None] * %(num_params)d
                self.observables = [None] * %(num_observables)d
                self.initials = [None] * %(num_ics)d
            """, 4) % init_data)
        for i, p in enumerate(self.model.parameters):
            p_data = (i, repr(p.name), p.value)
            output.write(" " * 8)
            output.write("self.parameters[%d] = Parameter(%s, %.17g)\n" %
                         p_data)
        output.write("\n")
        for i, obs in enumerate(self.model.observables):
            obs_data = (i, repr(obs.name), repr(obs.species),
                        repr(obs.coefficients))
            output.write(" " * 8)
            output.write("self.observables[%d] = Observable(%s, %s, %s)\n" %
                         obs_data)
        output.write("\n")
        for i, ic in enumerate(self.model.initials):
            ic_data = (i, self.model.parameters.index(ic.value),
                       self.model.get_species_index(ic.pattern))
            output.write(" " * 8)
            output.write("self.initials[%d] = Initial(%d, %d)\n" % ic_data)
        output.write("\n")

        output.write(" " * 8)
        if 'math.' in code_eqs:
            code_eqs = 'import math\n' + code_eqs
        output.write('code_eqs = \'\'\'\n%s\n\'\'\'\n' %
                     code_eqs.replace(';', ''))

        output.write(" " * 8)
        output.write("if _use_cython:\n")
        output.write(
            pad(
                r"""
            def ode_rhs(t, y, p):
                ydot = self.ydot
                Cython.inline(code_eqs, quiet=True)
                return ydot
            """, 12))
        output.write("        else:\n")
        output.write(
            pad(
                r"""
            def ode_rhs(t, y, p):
                ydot = self.ydot
                exec(code_eqs)
                return ydot
            """, 12))
        output.write(" " * 8)
        output.write('self.integrator = scipy.integrate.ode(ode_rhs)\n')
        output.write(" " * 8)
        output.write("self.integrator.set_integrator('vode', method='bdf', "
                     "with_jacobian=True)\n")

        # note the simulate method is fixed, i.e. it doesn't require any templating
        output.write(
            pad(
                r"""
            def simulate(self, tspan, param_values=None, view=False):
                if param_values is not None:
                    # accept vector of parameter values as an argument
                    if len(param_values) != len(self.parameters):
                        raise Exception("param_values must have length %d" %
                                        len(self.parameters))
                    self.sim_param_values[:] = param_values
                else:
                    # create parameter vector from the values in the model
                    self.sim_param_values[:] = [p.value for p in self.parameters]
                self.y0.fill(0)
                for ic in self.initials:
                    self.y0[ic.species_index] = self.sim_param_values[ic.param_index]
                if self.y is None or len(tspan) != len(self.y):
                    self.y = numpy.empty((len(tspan), len(self.y0)))
                    if len(self.observables):
                        self.yobs = numpy.ndarray(len(tspan),
                                        list(zip((obs.name for obs in self.observables),
                                            itertools.repeat(float))))
                    else:
                        self.yobs = numpy.ndarray((len(tspan), 0))
                    self.yobs_view = self.yobs.view(float).reshape(len(self.yobs),
                                                                   -1)
                # perform the actual integration
                self.integrator.set_initial_value(self.y0, tspan[0])
                self.integrator.set_f_params(self.sim_param_values)
                self.y[0] = self.y0
                t = 1
                while self.integrator.successful() and self.integrator.t < tspan[-1]:
                    self.y[t] = self.integrator.integrate(tspan[t])
                    t += 1
                for i, obs in enumerate(self.observables):
                    self.yobs_view[:, i] = \
                        (self.y[:, obs.species] * obs.coefficients).sum(1)
                if view:
                    y_out = self.y.view()
                    yobs_out = self.yobs.view()
                    for a in y_out, yobs_out:
                        a.flags.writeable = False
                else:
                    y_out = self.y.copy()
                    yobs_out = self.yobs.copy()
                return (y_out, yobs_out)
            """, 4))

        return output.getvalue()
Exemple #5
0
    def export(self):
        """Generate the corresponding Mathematica ODEs for the PySB model
        associated with the exporter.

        Returns
        -------
        string
            String containing the Mathematica code for the model's ODEs.
        """
        if self.model.expressions:
            raise ExpressionsNotSupported()
        if self.model.compartments:
            raise CompartmentsNotSupported()

        output = StringIO()
        pysb.bng.generate_equations(self.model)

        # Add docstring if there is one
        if self.docstring:
            output.write('(*\n' + self.docstring + '\n')
        else:
            output.write("(*\n")

        # Header comment
        output.write("Mathematica model definition file for ")
        output.write("model " + self.model.name + ".\n")
        output.write("Generated by " \
                     "pysb.export.mathematica.MathematicaExporter.\n")
        output.write("\n")
        output.write("Run with (for example):\n")
        output.write("tmax = 10\n")
        output.write("soln = NDSolve[Join[odes, initconds], slist, " \
                     "{t, 0, tmax}]\n")
        output.write("Plot[s0[t] /. soln, {t, 0, tmax}, PlotRange -> All]\n")
        output.write("*)\n\n")

        # PARAMETERS
        # Note that in Mathematica, underscores are not allowed in variable
        # names, so we simply strip them out here
        params_str = ''
        for i, p in enumerate(self.model.parameters):
            # Remove underscores
            pname = p.name.replace('_', '')

            # Convert parameter values to scientific notation
            # If the parameter is 0, don't take the log!
            if p.value == 0:
                params_str += '%s = %g;\n' % (pname, p.value)
            # Otherwise, take the log (base 10) and format accordingly
            else:
                val_str = '%.17g' % p.value
                if 'e' in val_str:
                    (mantissa, exponent) = val_str.split('e')
                    params_str += '%s = %s * 10^%s;\n' % \
                            (pname, mantissa, exponent)
                else:
                    params_str += '%s = %s;\n' % (pname, val_str)

        ## ODEs ###
        odes_str = 'odes = {\n'
        # Concatenate the equations
        odes_str += ',\n'.join([
            's%d == %s' % (i, sympy.ccode(self.model.odes[i]))
            for i in range(len(self.model.odes))
        ])
        # Replace, e.g., s0 with s[0]
        odes_str = re.sub(r's(\d+)', lambda m: 's%s[t]' % (int(m.group(1))),
                          odes_str)
        # Add the derivative symbol ' to the left hand sides
        odes_str = re.sub(r's(\d+)\[t\] ==', r"s\1'[t] ==", odes_str)
        # Correct the exponentiation syntax
        odes_str = re.sub(r'pow\(([^,]+), ([^)]+)\)', r'\1^\2', odes_str)
        odes_str += '\n}'
        #c_code = odes_str
        # Eliminate underscores from parameter names in equations
        for i, p in enumerate(self.model.parameters):
            odes_str = re.sub(r'\b(%s)\b' % p.name, p.name.replace('_', ''),
                              odes_str)

        ## INITIAL CONDITIONS
        ic_values = ['0'] * len(self.model.odes)
        for i, ic in enumerate(self.model.initials):
            idx = self.model.get_species_index(ic.pattern)
            ic_values[idx] = ic.value.name.replace('_', '')

        init_conds_str = 'initconds = {\n'
        init_conds_str += ',\n'.join(
            ['s%s[0] == %s' % (i, val) for i, val in enumerate(ic_values)])
        init_conds_str += '\n}'

        ## SOLVE LIST
        solvelist_str = 'solvelist = {\n'
        solvelist_str += ',\n'.join(
            ['s%s[t]' % (i) for i in range(0, len(self.model.odes))])
        solvelist_str += '\n}'

        ## OBSERVABLES
        observables_str = ''
        for obs in self.model.observables:
            # Remove underscores
            observables_str += obs.name.replace('_', '') + ' = '
            #groups = self.model.observable_groups[obs_name]
            observables_str += ' + '.join([
                '(s%s[t] * %d)' % (s, c)
                for s, c in zip(obs.species, obs.coefficients)
            ])
            observables_str += ' /. soln\n'

        # Add comments identifying the species
        species_str = '\n'.join([
            '(* s%d[t] = %s *)' % (i, s)
            for i, s in enumerate(self.model.species)
        ])

        output.write('(* Parameters *)\n')
        output.write(params_str + "\n")
        output.write('(* List of Species *)\n')
        output.write(species_str + "\n\n")
        output.write('(* ODEs *)\n')
        output.write(odes_str + "\n\n")
        output.write('(* Initial Conditions *)\n')
        output.write(init_conds_str + "\n\n")
        output.write('(* List of Variables (e.g., as an argument to NDSolve) ' \
                     '*)\n')
        output.write(solvelist_str + '\n\n')
        output.write('(* Run the simulation -- example *)\n')
        output.write('tmax = 100\n')
        output.write('soln = NDSolve[Join[odes, initconds], ' \
                     'solvelist, {t, 0, tmax}]\n\n')
        output.write('(* Observables *)\n')
        output.write(observables_str + '\n')

        return output.getvalue()
Exemple #6
0
    def export(self):
        """Generate the PottersWheel code for the ODEs of the PySB model
        associated with the exporter.

        Returns
        -------
        string
            String containing the PottersWheel code for the ODEs.
        """
        if self.model.expressions:
            raise ExpressionsNotSupported()
        if self.model.compartments:
            raise CompartmentsNotSupported()

        output = StringIO()
        pysb.bng.generate_equations(self.model)

        model_name = self.model.name.replace('.', '_')

        ic_values = [0] * len(self.model.odes)
        for cp, ic_param in self.model.initial_conditions:
            ic_values[self.model.get_species_index(cp)] = ic_param.value

        # list of "dynamic variables"
        pw_x = [
            "m = pwAddX(m, 's%d', %e);" % (i, ic_values[i])
            for i in range(len(self.model.odes))
        ]

        # parameters
        pw_k = [
            "m = pwAddK(m, '%s', %e);" % (p.name, p.value)
            for p in self.model.parameters
        ]

        # equations (one for each dynamic variable)
        # Note that we just generate C code, which for basic math expressions
        # is identical to matlab.  We just have to change 'pow' to 'power'.
        # Ideally there would be a matlab formatter for sympy.
        pw_ode = [
            "m = pwAddODE(m, 's%d', '%s');" %
            (i, sympy.ccode(self.model.odes[i]))
            for i in range(len(self.model.odes))
        ]
        pw_ode = [re.sub(r'pow(?=\()', 'power', s) for s in pw_ode]

        # observables
        pw_y = [
            "m = pwAddY(m, '%s', '%s');" %
            (obs.name, ' + '.join('%f * s%s' % t
                                  for t in zip(obs.coefficients, obs.species)))
            for obs in self.model.observables
        ]

        # Add docstring, if present
        if self.docstring:
            output.write('% ' + self.docstring.replace('\n', '\n% '))
            output.write('\n')

        output.write('% PottersWheel model definition file\n')
        output.write('%% save as %s.m\n' % model_name)
        output.write('function m = %s()\n' % model_name)
        output.write('\n')
        output.write('m = pwGetEmptyModel();\n')
        output.write('\n')
        output.write('% meta information\n')
        output.write("m.ID          = '%s';\n" % model_name)
        output.write("m.name        = '%s';\n" % model_name)
        output.write("m.description = '';\n")
        output.write("m.authors     = {''};\n")
        output.write("m.dates       = {''};\n")
        output.write("m.type        = 'PW-1-5';\n")
        output.write('\n')
        output.write('% dynamic variables\n')
        for x in pw_x:
            output.write(x)
            output.write('\n')
        output.write('\n')
        output.write('% dynamic parameters\n')
        for k in pw_k:
            output.write(k)
            output.write('\n')
        output.write('\n')
        output.write('% ODEs\n')
        for ode in pw_ode:
            output.write(ode)
            output.write('\n')
        output.write('\n')
        output.write('% observables\n')
        for y in pw_y:
            output.write(y)
            output.write('\n')
        output.write('\n')
        output.write('%% end of PottersWheel model %s\n' % model_name)
        return output.getvalue()
Exemple #7
0
    def export(self):
        """Generate a MATLAB class definition containing the ODEs for the PySB
        model associated with the exporter.

        Returns
        -------
        string
            String containing the MATLAB code for an implementation of the
            model's ODEs.
        """
        if self.model.expressions:
            raise ExpressionsNotSupported()
        if self.model.compartments:
            raise CompartmentsNotSupported()

        output = StringIO()
        pysb.bng.generate_equations(self.model)

        docstring = ''
        if self.docstring:
            docstring += self.docstring.replace('\n', '\n    % ')

        # Substitute underscores for any dots in the model name
        model_name = self.model.name.replace('.', '_')

        # -- Parameters and Initial conditions -------
        # Declare the list of parameters as a struct
        params_str = 'self.parameters = struct( ...\n' + ' ' * 16
        params_str_list = []
        for i, p in enumerate(self.model.parameters):
            # Add parameter to struct along with nominal value
            cur_p_str = "'%s', %.17g" % (_fix_underscores(p.name), p.value)
            # Decide whether to continue or terminate the struct declaration:
            if i == len(self.model.parameters) - 1:
                cur_p_str += ');'  # terminate
            else:
                cur_p_str += ', ...'  # continue

            params_str_list.append(cur_p_str)

        # Format and indent the params struct declaration
        params_str += ('\n' + ' ' * 16).join(params_str_list)

        # Fill in an array of the initial conditions based on the named
        # parameter values
        initial_values_str = ('initial_values = zeros(1,%d);\n'+' '*12) % \
                             len(self.model.species)
        initial_values_str += ('\n' + ' ' * 12).join([
            'initial_values(%d) = self.parameters.%s; %% %s' %
            (i + 1, _fix_underscores(ic.value.name), ic.pattern)
            for i, ic in enumerate(self.model.initials)
        ])

        # -- Build observables declaration --
        observables_str = 'self.observables = struct( ...\n' + ' ' * 16
        observables_str_list = []
        for i, obs in enumerate(self.model.observables):
            # Associate species and coefficient lists with observable names,
            # changing from zero- to one-based indexing
            cur_obs_str = "'%s', [%s; %s]" % \
                          (_fix_underscores(obs.name),
                           ' '.join([str(sp+1) for sp in obs.species]),
                           ' '.join([str(c) for c in obs.coefficients]))
            # Decide whether to continue or terminate the struct declaration:
            if i == len(self.model.observables) - 1:
                cur_obs_str += ');'  # terminate
            else:
                cur_obs_str += ', ...'  # continue

            observables_str_list.append(cur_obs_str)
        # Format and indent the observables struct declaration
        observables_str += ('\n' + ' ' * 16).join(observables_str_list)

        # -- Build ODEs -------
        # Build a stringified list of species
        species_list = ['%% %s;' % s for i, s in enumerate(self.model.species)]
        # Build the ODEs as strings from the model.odes array
        odes_list = [
            'y(%d,1) = %s;' % (i + 1, sympy.ccode(self.model.odes[i]))
            for i in range(len(self.model.odes))
        ]
        # Zip the ODEs and species string lists and then flatten them
        # (results in the interleaving of the two lists)
        odes_species_list = [
            item for sublist in zip(species_list, odes_list)
            for item in sublist
        ]
        # Flatten to a string and add correct indentation
        odes_str = ('\n' + ' ' * 12).join(odes_species_list)

        # Change species names from, e.g., '__s(0)' to 'y0(1)' (note change
        # from zero-based indexing to 1-based indexing)
        odes_str = re.sub(r'__s(\d+)', \
                          lambda m: 'y0(%s)' % (int(m.group(1))+1), odes_str)
        # Change C code 'pow' function to MATLAB 'power' function
        odes_str = re.sub(r'pow\(', 'power(', odes_str)
        # Prepend 'p.' to named parameters and fix any underscores
        for i, p in enumerate(self.model.parameters):
            odes_str = re.sub(r'\b(%s)\b' % p.name,
                              'p.%s' % _fix_underscores(p.name), odes_str)

        # -- Build final output --
        output.write(
            pad(
                r"""
            classdef %(model_name)s
                %% %(docstring)s
                %% A class implementing the ordinary differential equations
                %% for the %(model_name)s model.
                %%
                %% Save as %(model_name)s.m.
                %%
                %% Generated by pysb.export.matlab.MatlabExporter.
                %%
                %% Properties
                %% ----------
                %% observables : struct
                %%     A struct containing the names of the observables from the
                %%     PySB model as field names. Each field in the struct
                %%     maps the observable name to a matrix with two rows:
                %%     the first row specifies the indices of the species
                %%     associated with the observable, and the second row
                %%     specifies the coefficients associated with the species.
                %%     For any given timecourse of model species resulting from
                %%     integration, the timecourse for an observable can be
                %%     retrieved using the get_observable method, described
                %%     below.
                %%
                %% parameters : struct
                %%     A struct containing the names of the parameters from the
                %%     PySB model as field names. The nominal values are set by
                %%     the constructor and their values can be overriden
                %%     explicitly once an instance has been created.
                %%
                %% Methods
                %% -------
                %% %(model_name)s.odes(tspan, y0)
                %%     The right-hand side function for the ODEs of the model,
                %%     for use with MATLAB ODE solvers (see Examples).
                %%
                %% %(model_name)s.get_initial_values()
                %%     Returns a vector of initial values for all species,
                %%     specified in the order that they occur in the original
                %%     PySB model (i.e., in the order found in model.species).
                %%     Non-zero initial conditions are specified using the
                %%     named parameters included as properties of the instance.
                %%     Hence initial conditions other than the defaults can be
                %%     used by assigning a value to the named parameter and then
                %%     calling this method. The vector returned by the method
                %%     is used for integration by passing it to the MATLAB
                %%     solver as the y0 argument.
                %%
                %% %(model_name)s.get_observables(y)
                %%     Given a matrix of timecourses for all model species
                %%     (i.e., resulting from an integration of the model),
                %%     get the trajectories corresponding to the observables.
                %%     Timecourses are returned as a struct which can be
                %%     indexed by observable name.
                %%
                %% Examples
                %% --------
                %% Example integration using default initial and parameter
                %% values:
                %%
                %% >> m = %(model_name)s();
                %% >> tspan = [0 100];
                %% >> [t y] = ode15s(@m.odes, tspan, m.get_initial_values());
                %%
                %% Retrieving the observables:
                %%
                %% >> y_obs = m.get_observables(y)
                %%
                properties
                    observables
                    parameters
                end

                methods
                    function self = %(model_name)s()
                        %% Assign default parameter values
                        %(params_str)s

                        %% Define species indices (first row) and coefficients
                        %% (second row) of named observables
                        %(observables_str)s
                    end

                    function initial_values = get_initial_values(self)
                        %% Return the vector of initial conditions for all
                        %% species based on the values of the parameters
                        %% as currently defined in the instance.

                        %(initial_values_str)s
                    end

                    function y = odes(self, tspan, y0)
                        %% Right hand side function for the ODEs

                        %% Shorthand for the struct of model parameters
                        p = self.parameters;

                        %(odes_str)s
                    end

                    function y_obs = get_observables(self, y)
                        %% Retrieve the trajectories for the model observables
                        %% from a matrix of the trajectories of all model
                        %% species.

                        %% Initialize the struct of observable timecourses
                        %% that we will return
                        y_obs = struct();

                        %% Iterate over the observables;
                        observable_names = fieldnames(self.observables);
                        for i = 1:numel(observable_names)
                            obs_matrix = self.observables.(observable_names{i});
                            if isempty(obs_matrix)
                                y_obs.(observable_names{i}) = zeros(size(y, 1), 1);
                                continue
                            end
                            species = obs_matrix(1, :);
                            coefficients = obs_matrix(2, :);
                            y_obs.(observable_names{i}) = ...
                                            y(:, species) * coefficients';
                        end
                    end
                end
            end
            """, 0) % {
                    'docstring': docstring,
                    'model_name': model_name,
                    'params_str': params_str,
                    'initial_values_str': initial_values_str,
                    'observables_str': observables_str,
                    'params_str': params_str,
                    'odes_str': odes_str
                })

        return output.getvalue()
Exemple #8
0
    def export(self, initials=None, param_values=None):
        """Generate the corresponding StochKit2 XML for a PySB model

        Parameters
        ----------
        initials : list of numbers
            List of initial species concentrations overrides
            (must be same length as model.species). If None,
            the concentrations from the model are used.
        param_values : list
            List of parameter value overrides (must be same length as
            model.parameters). If None, the parameter values from the model
            are used.

        Returns
        -------
        string
            The model in StochKit2 XML format
        """
        if self.model.compartments:
            raise CompartmentsNotSupported()

        generate_equations(self.model)
        document = etree.Element("Model")

        d = etree.Element('Description')

        d.text = 'Exported from PySB Model: %s' % self.model.name
        document.append(d)

        # Number of Reactions
        nr = etree.Element('NumberOfReactions')
        nr.text = str(len(self.model.reactions))
        document.append(nr)

        # Number of Species
        ns = etree.Element('NumberOfSpecies')
        ns.text = str(len(self.model.species))
        document.append(ns)

        if param_values is None:
            # Get parameter values from model if not supplied
            param_values = [p.value for p in self.model.parameters]
        else:
            # Validate length
            if len(param_values) != len(self.model.parameters):
                raise Exception('param_values must be a list of numeric '
                                'parameter values the same length as '
                                'model.parameters')

        # Get initial species concentrations from model if not supplied
        if initials is None:
            initials = np.zeros((len(self.model.species), ))
            subs = dict((p, param_values[i])
                        for i, p in enumerate(self.model.parameters))

            for cp, value_obj in self.model.initial_conditions:
                cp = as_complex_pattern(cp)
                si = self.model.get_species_index(cp)
                if si is None:
                    raise IndexError("Species not found in model: %s" %
                                     repr(cp))
                if isinstance(value_obj, (int, float)):
                    value = value_obj
                elif value_obj in self.model.parameters:
                    pi = self.model.parameters.index(value_obj)
                    value = param_values[pi]
                elif value_obj in self.model.expressions:
                    value = value_obj.expand_expr().evalf(subs=subs)
                else:
                    raise ValueError("Unexpected initial condition value type")
                initials[si] = value
        else:
            # Validate length
            if len(initials) != len(self.model.species):
                raise Exception('initials must be a list of numeric initial '
                                'concentrations the same length as '
                                'model.species')

        # Species
        spec = etree.Element('SpeciesList')
        for s_id in range(len(self.model.species)):
            spec.append(
                self._species_to_element('__s%d' % s_id, initials[s_id]))
        document.append(spec)

        # Parameters
        params = etree.Element('ParametersList')
        for p_id, param in enumerate(self.model.parameters):
            p_name = param.name
            if p_name == 'vol':
                p_name = '__vol'
            p_value = param.value if param_values is None else \
                param_values[p_id]
            params.append(self._parameter_to_element(p_name, p_value))
        # Default volume parameter value
        params.append(self._parameter_to_element('vol', 1.0))

        document.append(params)

        # Expressions and observables
        expr_strings = {
            e.name:
            '(%s)' % sympy.ccode(e.expand_expr(expand_observables=True))
            for e in self.model.expressions
        }

        # Reactions
        reacs = etree.Element('ReactionsList')
        pattern = re.compile("(__s\d+)\*\*(\d+)")
        for rxn_id, rxn in enumerate(self.model.reactions):
            rxn_name = 'Rxn%d' % rxn_id
            rxn_desc = 'Rules: %s' % str(rxn["rule"])

            reactants = defaultdict(int)
            products = defaultdict(int)
            # reactants
            for r in rxn["reactants"]:
                reactants["__s%d" % r] += 1
            # products
            for p in rxn["products"]:
                products["__s%d" % p] += 1
            # replace terms like __s**2 with __s*(__s-1)
            rate = str(rxn["rate"])

            matches = pattern.findall(rate)
            for m in matches:
                repl = m[0]
                for i in range(1, int(m[1])):
                    repl += "*(%s-%d)" % (m[0], i)
                rate = re.sub(pattern, repl, rate, count=1)

            # expand only expressions used in the rate eqn
            for e in {
                    sym
                    for sym in rxn["rate"].atoms()
                    if isinstance(sym, Expression)
            }:
                rate = re.sub(r'\b%s\b' % e.name, expr_strings[e.name], rate)

            total_reactants = sum(reactants.values())
            rxn_params = rxn["rate"].atoms(Parameter)
            rate = None
            if total_reactants <= 2 and len(rxn_params) == 1:
                # Try to parse as mass action to avoid compiling custom
                # propensity functions in StochKit (slow for big models)
                rxn_param = rxn_params.pop()
                putative_rate = sympy.Mul(*[
                    sympy.symbols(r)**r_stoich
                    for r, r_stoich in reactants.items()
                ]) * rxn_param

                rxn_floats = rxn["rate"].atoms(sympy.Float)
                rate_mul = 1.0
                if len(rxn_floats) == 1:
                    rate_mul = next(iter(rxn_floats))
                    putative_rate *= rate_mul

                if putative_rate == rxn["rate"]:
                    # Reaction is mass-action, set rate to a Parameter or float
                    if len(rxn_floats) == 0:
                        rate = rxn_param
                    elif len(rxn_floats) == 1:
                        rate = rxn_param.value * float(rate_mul)

                    if rate is not None and len(reactants) == 1 and \
                            max(reactants.values()) == 2:
                        # Need rate * 2 in addition to any rate factor
                        rate = (rate.value
                                if isinstance(rate, Parameter) else rate) * 2.0

            if rate is None:
                # Custom propensity function needed

                rxn_atoms = rxn["rate"].atoms()

                # replace terms like __s**2 with __s*(__s-1)
                rate = str(rxn["rate"])

                matches = pattern.findall(rate)
                for m in matches:
                    repl = m[0]
                    for i in range(1, int(m[1])):
                        repl += "*(%s-%d)" % (m[0], i)
                    rate = re.sub(pattern, repl, rate, count=1)

                # expand only expressions used in the rate eqn
                for e in {
                        sym
                        for sym in rxn_atoms if isinstance(sym, Expression)
                }:
                    rate = re.sub(r'\b%s\b' % e.name, expr_strings[e.name],
                                  rate)

            reacs.append(
                self._reaction_to_element(rxn_name, rxn_desc, rate, reactants,
                                          products))
        document.append(reacs)

        if pretty_print:
            return etree.tostring(document, pretty_print=True)
        else:
            # Hack to print pretty xml without pretty-print
            # (requires the lxml module).
            doc = etree.tostring(document)
            xmldoc = xml.dom.minidom.parseString(doc)
            uglyXml = xmldoc.toprettyxml(indent='  ')
            text_re = re.compile(">\n\s+([^<>\s].*?)\n\s+</", re.DOTALL)
            prettyXml = text_re.sub(">\g<1></", uglyXml)
            return prettyXml