def simulate_with_bioscrape_via_sbml(self,
                                         timepoints,
                                         filename=None,
                                         initial_condition_dict=None,
                                         return_dataframe=True,
                                         stochastic=False,
                                         safe=False,
                                         return_model=False,
                                         check_validity=True,
                                         **kwargs):
        """Simulate CRN model with bioscrape via writing a SBML file temporarily.
        [Bioscrape on GitHub](https://github.com/biocircuits/bioscrape).

        Returns the data for all species as Pandas dataframe.
        """
        result = None
        m = None
        try:
            from bioscrape.simulator import py_simulate_model
            from bioscrape.types import Model

            if filename is None:
                self.write_sbml_file(file_name="temp_sbml_file.xml",
                                     stochastic_model=stochastic,
                                     for_bioscrape=True,
                                     check_validity=check_validity)
                file_name = "temp_sbml_file.xml"
            elif isinstance(filename, str):
                file_name = filename
            else:
                raise ValueError(
                    f"filename must be None or a string. Recievied: {filename}"
                )

            if 'sbml_warnings' in kwargs:
                sbml_warnings = kwargs.get('sbml_warnings')
            else:
                sbml_warnings = False
            m = Model(sbml_filename=file_name, sbml_warnings=sbml_warnings)
            # m.write_bioscrape_xml('temp_bs'+ file_name + '.xml') # Uncomment if you want a bioscrape XML written as well.
            if initial_condition_dict is not None:
                processed = process_initial_concentration_dict(
                    initial_condition_dict)
                m.set_species(processed)
            result = py_simulate_model(timepoints,
                                       Model=m,
                                       stochastic=stochastic,
                                       safe=safe,
                                       return_dataframe=return_dataframe)
        except ModuleNotFoundError:
            warnings.warn('bioscrape was not found, please install bioscrape')

        if return_model:
            return result, m
        else:
            return result
    plt.subplot(3, 2, i + 1)

    CRN = CRNs[i]
    CRN.write_sbml_file(file_name=fname)
    print("Saved")

    from bioscrape.types import Model
    from bioscrape.simulator import py_simulate_model
    from bioscrape.sbmlutil import *
    M = Model(sbml_filename=fname)

    A_list = [0, 1, 2, 5, 10]
    for ind in range(len(A_list)):
        x0[repr(A)] = A_list[ind]

        M.set_species(x0)

        R = py_simulate_model(timepoints, Model=M)

        plt.plot(timepoints,
                 R["protein_X"],
                 label="A=" + str(A_list[ind]),
                 color='blue',
                 alpha=(ind + 1) / len(A_list))

    txt = ""
    for rxn in CRN.reactions:
        txt += repr(rxn) + "\n"
    plt.title(txt[:-1], fontsize=8)
    plt.legend()
Exemple #3
0
def import_sbml(sbml_file, bioscrape_model=None, input_printout=False):
    """
    Convert SBML document to bioscrape Model object. Note that events, compartments, non-standard function definitions,
    and some kinds of rules will be ignored. 
    Adds mass action kinetics based reactions with the appropriate mass action propensity in bioscrape. 
    Propensities with the correct annotation are added as compiled propensity types.
    All other propensities are added as general propensity.
    Local parameters are renamed if there is a conflict since bioscrape does not have a local environment.
    """
    # Attempt to import libsbml and read the SBML model.
    try:
        import libsbml
    except:
        raise ImportError(
            "libsbml not found. See sbml.org for installation help!\n" +
            'If you are using anaconda you can run the following:\n' +
            'conda install -c SBMLTeam python-libsbml\n\n\n')

    reader = libsbml.SBMLReader()
    doc = reader.readSBML(sbml_file)

    if doc.getNumErrors() > 1:
        raise SyntaxError(
            "SBML File {0} cannot be read without errors".format(sbml_file))

    model = doc.getModel()

    # Parse through species and parameters and keep a set of both along with their values.
    allspecies = {}
    allparams = {}
    allreactions = []
    for s in model.getListOfSpecies():
        sid = s.getId()
        if sid == "volume" or sid == "t":
            warnings.warn(
                "You have defined a species called '" + sid +
                ". This species is being ignored and treated as a keyword.")
            continue
        allspecies[sid] = 0.0
        if np.isfinite(s.getInitialAmount()):
            allspecies[sid] = s.getInitialAmount()
        if np.isfinite(s.getInitialConcentration()) and allspecies[sid] == 0:
            allspecies[sid] = s.getInitialConcentration()

    for p in model.getListOfParameters():
        pid = p.getId()
        allparams[pid] = 0.0
        if np.isfinite(p.getValue()):
            allparams[pid] = p.getValue()

    # Now go through reactions one at a time to get stoich and rates, then append to reaction_list.
    for reaction in model.getListOfReactions():
        # get the propensity
        kl = reaction.getKineticLaw()
        # capture any local parameters
        for p in kl.getListOfParameters():
            pid = p.getId()
            if pid in allparams:
                # If local parameter ID already exists in allparams due to another local/global parameter with same ID
                oldid = pid
                newid = oldid + '_' + reaction.getId()
                # Rename the ID everywhere it's used (such as in the Kinetic Law)
                kl.renameSIdRefs(oldid, newid)
                p.setId(newid)
                # Rename its usages
                for element in reaction.getListOfAllElements():
                    element.renameSIdRefs(oldid, newid)
                pid = newid

            allparams[pid] = 0.0
            if np.isfinite(p.getValue()):
                allparams[pid] = p.getValue()

        # get the formula as a string and then add
        # a leading _ to parameter names
        kl_formula = libsbml.formulaToL3String(kl.getMath())
        rate_string = _add_underscore_to_parameters(kl_formula, allparams)

        if reaction.getReversible():
            warnings.warn(
                'SBML model contains reversible reaction!\n' +
                'Please check rate expressions and ensure they are non-negative before doing '
                + 'stochastic simulations.')

        #Get Reactants and Products
        reactant_list = []
        product_list = []
        for reactant in reaction.getListOfReactants():
            reactantspecies = model.getSpecies(reactant.getSpecies())
            reactantspecies_id = reactantspecies.getId()
            if reactantspecies_id in allspecies:
                reactant_list.append(reactantspecies_id)
            else:
                warnings.warn(
                    'Reactant in reaction {0} not found in the list of species in the SBML model.'
                    + ' The species will be added with zero initial amount'.
                    format(reaction.getId()))
                allspecies[reactantspecies_id] = 0.0
                reactant_list.append(reactantspecies_id)

        for product in reaction.getListOfProducts():
            productspecies = model.getSpecies(product.getSpecies())
            productspecies_id = productspecies.getId()
            if productspecies_id in allspecies:
                product_list.append(productspecies_id)
            else:
                warnings.warn(
                    'Reactant in reaction {0} not found in the list of species in the SBML model.'
                    + ' The species will be added with zero initial amount'.
                    format(reaction.getId()))
                allspecies[productspecies_id] = 0.0
                product_list.append(productspecies_id)

        #Identify propensities based upon annotations
        annotation_string = reaction.getAnnotationString()
        if "PropensityType" in annotation_string:
            ind0 = annotation_string.find("<PropensityType>")
            ind1 = annotation_string.find("</PropensityType>")
            # propensity_definition = {}
            annotation_list = annotation_string[ind0:ind1].split(" ")
            key_vals = [(i.split("=")[0], i.split("=")[1])
                        for i in annotation_list if "=" in i]
            propensity_params = {}
            for (k, v) in key_vals:
                try:
                    propensity_params[k] = float(v)
                except ValueError:
                    propensity_params[k] = v
            if input_printout:
                print("Reaction found:", reactant_list, "->", product_list)
                print("Annotated propensity found with params:",
                      propensity_params)
            rxn = (reactant_list, product_list, propensity_params['type'],
                   propensity_params)

        else:  #No annotation found
            propensity_type = 'general'
            general_kl_formula = {}
            general_kl_formula['rate'] = rate_string
            rxn = (reactant_list, product_list, propensity_type,
                   general_kl_formula)
            if input_printout:
                print("Reaction found:", reactant_list, "->", product_list)
                print("Unnotated propensity found with ratestring:",
                      rate_string)
        allreactions.append(rxn)

    # Go through rules one at a time
    allrules = []
    #"Rules must be a tuple: (rule_type (string), rule_attributes (dict), rule_frequency (optional))")
    for rule in model.getListOfRules():
        rule_formula = libsbml.formulaToL3String(rule.getMath())
        rulevariable = rule.getVariable()
        if rulevariable in allspecies:
            rule_string = rulevariable + '=' + _add_underscore_to_parameters(
                rule_formula, allparams)
        elif rulevariable in allparams:
            rule_string = '_' + rulevariable + '=' + _add_underscore_to_parameters(
                rule_formula, allparams)
        else:
            warnings.warn(
                'SBML: Attempting to assign something that is not a parameter or species %s'
                % rulevariable)
            continue
        if rule.getElementName() == 'algebraicRule':
            warnings.warn('Unsupported rule type: %s' % rule.getElementName())
            continue
        elif rule.getElementName() == 'assignmentRule':
            rule_type = 'assignment'
        elif rule.getElementName() == 'rateRule':
            rate_rule_formula = _add_underscore_to_parameters(
                rule_formula, allparams)
            rule_rxn = ([''], [rulevariable], 'general', rate_rule_formula
                        )  # Create --> X type reaction to model rate rules.
            allreactions.append(rule_rxn)
            continue
        else:
            raise ValueError('Invalid SBML Rule type.')
        rule_dict = {}
        rule_dict['equation'] = rule_string
        rule_frequency = 'repeated'
        rule_tuple = (rule_type, rule_dict, rule_frequency)
        allrules.append(rule_tuple)

    #print('allparams = {0}'.format(allparams))
    #print('allspecies = {0}'.format(allspecies))
    #print('allreactions = {0}'.format(allreactions))
    #print(allrules)

    # Check and warn if there are any unrecognized components (function definitions, packages, etc.)
    if len(model.getListOfCompartments()) > 0 or len(
            model.getListOfUnitDefinitions()) > 0 or len(
                model.getListOfEvents()) > 0:
        warnings.warn(
            'Compartments, UnitDefintions, Events, and some other SBML model components are not recognized by bioscrape. '
            + 'Refer to the bioscrape wiki for more information.')

    #If no Model is passed into the function, a Model is returned
    if bioscrape_model == None:
        bioscrape_model = Model()
    #If a Model is passed into the function, that Model is modified
    if isinstance(bioscrape_model, Model):
        for species in allspecies.keys():
            bioscrape_model._add_species(species)

        for (param, val) in allparams.items():
            bioscrape_model._add_param(param)
            bioscrape_model.set_parameter(param, val)
            if input_printout:
                print("Adding Parameter:", param, "=", val)

        for rxn in allreactions:
            if len(rxn) == 4:
                reactants, products, propensity_type, propensity_param_dict = rxn
                delay_type, delay_reactants, delay_products, delay_param_dict = None, None, None, None
            elif len(rxn) == 8:
                reactants, products, propensity_type, propensity_param_dict, delay_type, delay_reactants, delay_products, delay_param_dict = rxn
            bioscrape_model.create_reaction(reactants,
                                            products,
                                            propensity_type,
                                            propensity_param_dict,
                                            delay_type,
                                            delay_reactants,
                                            delay_products,
                                            delay_param_dict,
                                            input_printout=input_printout)

        for rule in allrules:
            if len(rule) == 2:
                rule_type, rule_attributes = rule
                bioscrape_model.create_rule(rule_type,
                                            rule_attributes,
                                            input_printout=input_printout)
            elif len(rule) == 3:
                rule_type, rule_attributes, rule_frequency = rule
                bioscrape_model.create_rule(rule_type,
                                            rule_attributes,
                                            rule_frequency=rule_frequency,
                                            input_printout=input_printout)
        bioscrape_model.set_species(allspecies)
        bioscrape_model.py_initialize()
        return bioscrape_model
    else:
        raise ValueError(
            "bioscrape_model keyword must be a Bioscrape Model object or None (in which case a Model object is returned)."
        )
class Bioscrape(Process):
    '''
    This process provides a wrapper around a bioscrape model, interface, and simulator.
    It allows for stochastic or determinstic simulation, variable volume, and generates ports
    to access all bioscrape species and rate parameters.
    '''

    # give the process a name, so that it can register in the process_repository
    name = NAME

    # declare default parameters as class variables
    defaults = {
        'internal_dt': 0.01,
        'stochastic': False,
        'initial_volume': 1.0,
        'safe_mode': False,
        'lineage': False
    }

    def __init__(self, parameters=None):
        super(Bioscrape, self).__init__(parameters)

        # get the parameters out of self.parameters if available, or use defaults
        if 'sbml_file' not in self.parameters and 'bioscrape_model' not in self.parameters:
            raise ValueError(
                "Bioscrape Process requires either an sbml_file or bioscrape_model parameter."
            )
        elif 'sbml_file' not in self.parameters and isinstance(
                self.parameters['bioscrape_model'], Model):
            # load the sbml file to create the model
            self.sbml_file = None
            self.model = self.parameters['bioscrape_model']
        elif isinstance(self.parameters['sbml_file'],
                        str) and 'bioscrape_model' not in self.parameters:
            self.sbml_file = self.parameters['sbml_file']
            if self.parameters["lineage"]:
                self.model = LineageModel(sbml_filename=self.sbml_file, )
            else:
                self.model = Model(sbml_filename=self.sbml_file,
                                   sbml_warnings=False)
        elif isinstance(self.parameters['sbml_file'], str) and isinstance(
                self.parameters['bioscrape_model'], Model):
            raise ValueError(
                "Bioscrape recieved an sbml_file and a bioscrape_model. Please use one or the other."
            )
        else:
            raise ValueError(
                f"Bioscrape did not recieve a valid bioscrape_model "
                f"(recieved: {self.parameters['bioscrape_model']} or a "
                f"valid sbml_file (recieved: {self.parameters['sbml_file']}).")

        self.internal_dt = self.parameters['internal_dt']
        self.stochastic = self.parameters['stochastic']

        #Toggle using Lineage Model
        if self.parameters["lineage"]:
            if not self.stochastic:
                raise ValueError(
                    "Bioscrape lineage only available with stochastic = True")
            self.simulator = LineageSSASimulator()
            self.interface = LineageCSimInterface(self.model)
            self.volume = LineageVolumeCellState(
            )  #Create an internal bioscrape Volume
        #Otherwise use normal bioscrape models
        else:
            # create the interface
            if self.parameters["safe_mode"]:
                self.interface = SafeModelCSimInterface(
                    self.model, max_species_count=10**8)
            else:
                self.interface = ModelCSimInterface(self.model)

            #Stochastic
            if self.stochastic:
                self.simulator = VolumeSSASimulator()
            #Not Stochastic
            elif not self.stochastic:
                self.interface.py_prep_deterministic_simulation()
                # create a Simulator
                self.simulator = DeterministicSimulator()
            self.volume = Volume()  #Create an internal bioscrape Volume

        #Set dt
        self.interface.py_set_dt(self.internal_dt)
        #Set the volume
        self.volume.py_set_volume(self.parameters['initial_volume'])

    def get_species_names(self):
        #Gets the names of teh species in a bioscrape model
        model_species = self.model.get_species_dictionary()
        return list(model_species.keys())

    def get_state(self, array):
        #Gets the state of a bioscrape simulation
        mapping = self.model.get_species2index()

        return {species: array[index] for species, index in mapping.items()}

    def initial_state(self, config=None):
        #gets the current (or initial) state of a bioscrape simulation.
        if config is None:
            config = {}
        self.model.set_species(config)
        state = self.model.get_species_array()
        return {'species': self.get_state(state)}

    def ports_schema(self):
        '''
        ports_schema returns a dictionary that declares how each state will behave.
        Each key can be assigned settings for the schema_keys declared in Store:

        * `_default`
        * `_updater`
        * `_divider`
        * `_value`
        * `_properties`
        * `_emit`
        * `_serializer`
        '''

        #Different divide settings between stochastic and determinsitic CRNs
        if self.stochastic:
            divider = "binomial"
        else:
            divider = "set"  #division does not change concentrations

        return {
            'species': {
                species: {
                    '_default': 0.0,
                    '_updater': 'accumulate',
                    '_emit': True,
                    '_divider': divider
                }
                for species in self.model.get_species()
            },
            'delta_species': {
                species: {
                    '_default': 0.0,
                    '_updater': 'set',
                    '_emit': False
                }
                for species in self.model.get_species()
            },
            'rates': {
                p: {
                    '_default': self.model.get_parameter_dictionary()[p],
                    '_updater': 'set',
                }
                for p in self.model.get_param_list()
            },
            'globals': {
                'volume': {
                    '_default': self.parameters['initial_volume'],
                    '_updater': 'accumulate',
                    '_emit': True
                }
            }
        }

    def next_update(self, timestep, states):
        if 'species' in states:
            self.model.set_species(states['species'])
            self.interface.py_set_initial_state(self.model.get_species_array())
        if 'rates' in states:
            self.model.set_params(states['rates'])

        #Set Volume if needed
        if 'volume' in states:
            self.volume.py_set_volume(states['globals']['volume'])

        # create the interface

        timepoints = np.arange(0, timestep, self.internal_dt)
        if self.parameters["lineage"]:
            output = self.simulator.py_SimulateSingleCell(
                timepoints,
                Model=self.model,
                interface=self.interface,
                v=self.volume)
        elif self.stochastic:
            output = self.simulator.py_volume_simulate(self.interface,
                                                       self.volume, timepoints)
        else:
            output = self.simulator.py_simulate(self.interface, timepoints)

        result = output.py_get_result()[-1]
        result_state = self.get_state(result)
        delta = get_delta(states['species'], result_state)
        rates = self.model.get_parameter_dictionary()

        #If the simulation is a volume simulation, return the change in volume
        if getattr(output, "py_get_volume", None) is not None:
            Vi = output.py_get_volume()[0]
            Vf = output.py_get_volume()[-1]
            deltaV = Vf - Vi
        else:
            deltaV = 0

        return {
            'species': delta,
            'delta_species': delta,
            'rates': rates,
            'globals': {
                'volume': deltaV
            }
        }

    def get_model(self):
        return self.model

    def get_model_species(self):
        return self.model.get_species_dictionary()

    def get_model_species_ids(self):
        return list(self.model.get_species_dictionary().keys())

    def get_volume(self):
        return self.volume.py_get_volume()