Пример #1
0
    def __init__(self, parameter_values=None):
        Model.__init__(self, name="lotka_volterra")
        self.volume = 1

        ## Parameters
        self.add_parameter(Parameter(name="k1", expression=1.0))
        self.add_parameter(Parameter(name="k2", expression=0.005))
        self.add_parameter(Parameter(name="k3", expression=0.6))

        ## Species
        self.add_species(Species(name='prey', initial_value = 100, mode = 'discrete'))
        self.add_species(Species(name='predator', initial_value = 100, mode = 'discrete'))

        ## Reactions
        self.add_reaction(Reaction(name="r1",
                                   reactants = {'prey' : 1},
                                   products = {'prey' : 2},
                                   rate = self.listOfParameters['k1']))
        self.add_reaction(Reaction(name="r2",
                                   reactants = {'predator': 1, 'prey' : 1},
                                   products = {'predator' : 2},
                                   rate = self.listOfParameters['k2']))
        self.add_reaction(Reaction(name="r3",
                                   reactants = {'predator' : 1},
                                   products = {},
                                   rate = self.listOfParameters['k3']))

        # Timespan
        self.timespan(np.linspace(0, 50, 51))
Пример #2
0
    def __init__(self, parameter_values=None):
        Model.__init__(self, name="chapter2")
        self.volume = 1

        # Parameters
        self.add_parameter(Parameter(name="birth_A", expression="1"))
        self.add_parameter(Parameter(name="birth_B", expression="0.1"))
        self.add_parameter(Parameter(name="death_B", expression="0.05"))

        # Variables
        self.add_species(Species(name="A", initial_value=100, mode="discrete"))
        self.add_species(Species(name="B", initial_value=0, mode="discrete"))

        # Reactions
        self.add_reaction(
            Reaction(name="birthA",
                     reactants={},
                     products={'A': 1},
                     rate=self.listOfParameters["birth_A"]))
        self.add_reaction(
            Reaction(name="birthB",
                     reactants={'B': 1},
                     products={},
                     rate=self.listOfParameters["death_B"]))
        self.add_reaction(
            Reaction(name="birthC",
                     reactants={'A': 1},
                     products={'B': 1},
                     rate=self.listOfParameters["birth_B"]))

        # Timespan
        self.timespan(np.arange(0, 100, 1.0))
Пример #3
0
 def setUp(self):
     self.valid_ma_reaction = Reaction(name="test_reaction",
                                       reactants={"A": 1},
                                       products={"B": 1},
                                       rate="k1")
     self.valid_cp_reaction = Reaction(name="test_reaction",
                                       reactants={"A": 1},
                                       products={"B": 1},
                                       propensity_function="k1 * A")
Пример #4
0
 def test_constructor__invalid_products(self):
     """ Test the Reaction constructor with non-dict products. """
     with self.assertRaises(ReactionError):
         Reaction(name="test_reaction",
                  reactants={"A": 1},
                  products=[["B", 1]],
                  rate="k2")
Пример #5
0
 def test_constructor__products_invalid_values(self):
     """ Test the Reaction constructor with products containing invalid stoichiometry. """
     with self.assertRaises(ReactionError):
         Reaction(name="test_reaction",
                  reactants={"A": 1},
                  products={"B": "1"},
                  rate="k2")
Пример #6
0
 def test_constructor__products_invalid_keys(self):
     """ Test the Reaction constructor with products keyed with invalid keys. """
     with self.assertRaises(ReactionError):
         Reaction(name="test_reaction",
                  reactants={"A": 1},
                  products={1: 1},
                  rate="k2")
Пример #7
0
 def test_constructor__products_is_none(self):
     """ Test the Reaction constructor with None as products. """
     reaction = Reaction(name="test_reaction",
                         reactants={"A": 1},
                         products=None,
                         rate="k2")
     self.assertEqual(reaction.products, {})
Пример #8
0
 def test_from_json__massaction_json_changed(self):
     """ Test Reaction.from_json with a mass-action reaction and modified json. Ensure propensities are updated."""
     test_json = {
         'name': 'test_reaction',
         'reactants': [{
             'key': 'B',
             'value': 1
         }],
         'products': [{
             'key': 'A',
             'value': 1
         }],
         'marate': 'k1',
         'annotation': None,
         'propensity_function': '(k1*A)',
         'ode_propensity_function': '(k1*A)',
         'massaction': True,
         'type': 'mass-action'
     }
     reaction = Reaction.from_json(test_json)
     self.assertEqual(reaction.name, 'test_reaction')
     self.assertEqual(reaction.reactants, {'B': 1})
     self.assertEqual(reaction.products, {'A': 1})
     self.assertEqual(reaction.propensity_function, '(k1*B)')
     self.assertEqual(reaction.ode_propensity_function, '(k1*B)')
     self.assertEqual(reaction.marate, 'k1')
     self.assertIsNone(reaction.annotation)
     self.assertTrue(reaction.massaction)
     self.assertEqual(reaction.type, 'mass-action')
Пример #9
0
 def test_from_json__customized_json_changed(self):
     """ Test Reaction.from_json with a customized reaction and modified json. Ensure propensities don't change. """
     test_json = {
         'name': 'test_reaction',
         'reactants': [{
             'key': 'B',
             'value': 1
         }],
         'products': [{
             'key': 'A',
             'value': 1
         }],
         'marate': None,
         'annotation': None,
         'propensity_function': '(k1*A)',
         'ode_propensity_function': '(k1*A)',
         'massaction': False,
         'type': 'customized'
     }
     reaction = Reaction.from_json(test_json)
     self.assertEqual(reaction.name, 'test_reaction')
     self.assertEqual(reaction.reactants, {'B': 1})
     self.assertEqual(reaction.products, {'A': 1})
     self.assertEqual(reaction.propensity_function, '(k1*A)')
     self.assertEqual(reaction.ode_propensity_function, '(k1*A)')
     self.assertIsNone(reaction.marate)
     self.assertIsNone(reaction.annotation)
     self.assertFalse(reaction.massaction)
     self.assertEqual(reaction.type, 'customized')
Пример #10
0
    def build_reaction(self, args, parameters):
        '''
        Build a GillesPy2 reaction.

        Attributes
        ----------
        args : dict
            A json representation of a reaction.
        parameters : list
            List of GillesPy2 parameters.
        '''
        if args['reactionType'] not in 'custom-propensity':
            rate = list(
                filter(lambda p: p.name == args['rate']['name'],
                       parameters))[0]
            propensity = None
        else:
            rate = None
            propensity = args['propensity']
        R = Reaction(name=args['name'],
                     reactants=self.build_stoich_species_dict(
                         args['reactants']),
                     products=self.build_stoich_species_dict(args['products']),
                     rate=rate,
                     propensity_function=propensity)
        return R
Пример #11
0
 def test_constructor__products_keyed_by_species_obj(self):
     """ Test the Reaction constructor with products keyed by species objs. """
     test_species = Species(name="B", initial_value=0)
     reaction = Reaction(name="test_reaction",
                         reactants={"A": 1},
                         products={test_species: 1},
                         rate="k2")
     self.assertEqual(reaction.products, {"B": 1})
Пример #12
0
 def test_model_add__reaction(self):
     from gillespy2 import Reaction
     r4 = Reaction(name="r4",
                   reactants={"s1": 1},
                   products={"s2": 1},
                   rate="k1")
     self.model.add(r4)
     self.assertIn("r4", self.model.listOfReactions)
Пример #13
0
 def test_constructor__float_rate(self):
     """ Test the Reaction constructor with a float rate. """
     reaction = Reaction(name="test_reaction",
                         reactants={"A": 1},
                         products={"B": 1},
                         rate=0.5)
     self.assertIsInstance(reaction.marate, str)
     self.assertEqual(reaction.marate, "0.5")
Пример #14
0
 def test_constructor__rate_and_ode_propensity_function_are_not_none(self):
     """ Test the Reaction constructor with rate and propensity function set. """
     with self.assertRaises(ReactionError):
         Reaction(name="test_reaction",
                  reactants={"A": 1},
                  products={"B": 1},
                  rate="k1",
                  ode_propensity_function="A**2 + B**2")
Пример #15
0
 def test_constructor__parameter_rate(self):
     """ Test the Reaction constructor with an parameter object as rate. """
     k1 = Parameter(name="k1", expression="20")
     reaction = Reaction(name="test_reaction",
                         reactants={"A": 1},
                         products={"B": 1},
                         rate=k1)
     self.assertIsInstance(reaction.marate, str)
     self.assertEqual(reaction.marate, "k1")
Пример #16
0
 def test_constructor__invalid_ode_propensity_function(self):
     """ Test the Reaction constructor with a ode propensity function that is not of the proper type. """
     test_opfs = ["", ["k1 * A * B"]]
     for test_opf in test_opfs:
         with self.subTest(ode_propensity_function=test_opf):
             with self.assertRaises(ReactionError):
                 Reaction(name="test_reaction",
                          reactants={"A": 1},
                          products={"B": 1},
                          ode_propensity_function=test_opf)
Пример #17
0
 def test_constructor__float_ode_propensity_function(self):
     """ Test the Reaction constructor with a float ode propensity function. """
     reaction = Reaction(name="test_reaction",
                         reactants={"A": 1},
                         products={"B": 1},
                         ode_propensity_function=0.5)
     self.assertIsInstance(reaction.propensity_function, str)
     self.assertEqual(reaction.propensity_function, "0.5")
     self.assertIsInstance(reaction.ode_propensity_function, str)
     self.assertEqual(reaction.ode_propensity_function, "0.5")
Пример #18
0
 def test_constructor__int_propensity_function(self):
     """ Test the Reaction constructor with an int propensity function. """
     reaction = Reaction(name="test_reaction",
                         reactants={"A": 1},
                         products={"B": 1},
                         propensity_function=20)
     self.assertIsInstance(reaction.propensity_function, str)
     self.assertEqual(reaction.propensity_function, "20")
     self.assertIsInstance(reaction.ode_propensity_function, str)
     self.assertEqual(reaction.ode_propensity_function, "20")
Пример #19
0
 def test_constructor__rate_not_accepted_type(self):
     """ Test the Reaction constructor with a rate that is an invalid type. """
     test_rates = ["", ["k1"]]
     for test_rate in test_rates:
         with self.subTest(rate=test_rate):
             with self.assertRaises(ReactionError):
                 reaction = Reaction(name="test_reaction",
                                     reactants={"A": 1},
                                     products={"B": 1},
                                     rate=test_rate)
Пример #20
0
 def test_constructor__name_is_none_or_empty(self):
     """ Test the Reaction constructor with None or empty string as name. """
     test_names = [None, ""]
     for test_name in test_names:
         with self.subTest(name=test_name):
             reaction = Reaction(name=test_name,
                                 reactants={"A": 1},
                                 products={"B": 1},
                                 propensity_function="k1 * A * B")
             self.assertIsNotNone(re.search("rxn.*", reaction.name))
Пример #21
0
 def test_constructor__custom_ode_propensity(self):
     """ Test the Reaction constructor for a custom ode propensity reaction. """
     reaction = Reaction(name="test_reaction",
                         reactants={"A": 1},
                         products={"B": 1},
                         ode_propensity_function="k1 * A * B")
     self.assertEqual(reaction.name, "test_reaction")
     self.assertEqual(reaction.reactants, {"A": 1})
     self.assertEqual(reaction.products, {"B": 1})
     self.assertEqual(reaction.propensity_function, "((k1*A)*B)")
     self.assertEqual(reaction.ode_propensity_function, "((k1*A)*B)")
Пример #22
0
 def test_constructor__annotation_invalid_type(self):
     """ Test the Reaction construct with an annotation of invalid type. """
     test_annotations = [5, 0.1, ["g"]]
     for test_annotation in test_annotations:
         with self.subTest(annotation=test_annotation):
             with self.assertRaises(ReactionError):
                 reaction = Reaction(name="test_reaction",
                                     reactants={"A": 1},
                                     products={"B": 1},
                                     rate="k1",
                                     annotation=test_annotation)
Пример #23
0
 def test_constructor__mass_action(self):
     """ Test the Reaction constructor for a mass action reaction. """
     reaction = Reaction(name="test_reaction",
                         reactants={"A": 1},
                         products={"B": 1},
                         rate="k1")
     self.assertEqual(reaction.name, "test_reaction")
     self.assertEqual(reaction.reactants, {"A": 1})
     self.assertEqual(reaction.products, {"B": 1})
     self.assertEqual(reaction.marate, "k1")
     self.assertEqual(reaction.propensity_function, "(k1*A)")
     self.assertEqual(reaction.ode_propensity_function, "(k1*A)")
Пример #24
0
 def test_constructor__different_custom_propensity(self):
     """ Test the Reaction constructor for a custom propensity reaction with different propensities. """
     reaction = Reaction(name="test_reaction",
                         reactants={"A": 1},
                         products={"B": 1},
                         propensity_function="k1 * A * B",
                         ode_propensity_function="k1 * A * B / vol")
     self.assertEqual(reaction.name, "test_reaction")
     self.assertEqual(reaction.reactants, {"A": 1})
     self.assertEqual(reaction.products, {"B": 1})
     self.assertEqual(reaction.propensity_function, "((k1*A)*B)")
     self.assertEqual(reaction.ode_propensity_function, "(((k1*A)*B)/vol)")
Пример #25
0
 def test_constructor__invalid_reactants_and_products(self):
     """ Test the Reaction constructor with reactants and products both set to None or empty. """
     test_reacs = [None, {}]
     test_prods = [None, {}]
     for test_reac in test_reacs:
         for test_prod in test_prods:
             with self.subTest(reactants=test_reac, products=test_prod):
                 with self.assertRaises(ReactionError):
                     Reaction(name="test_reaction",
                              reactants=test_reac,
                              products=test_prod,
                              rate="k1")
Пример #26
0
 def test_from_json__invalid_json(self):
     """ Test Reaction.from_json with a reaction that fails validation. """
     test_json = {
         'name': 'test_reaction',
         'reactants': [{
             'key': 'A',
             'value': 1
         }],
         'products': [{
             'key': 'B',
             'value': 1
         }],
         'marate': None,
         'annotation': None,
         'propensity_function': '(k1*A)',
         'ode_propensity_function': '(k1*A)',
         'massaction': True,
         'type': 'mass-action'
     }
     with self.assertRaises(ReactionError):
         reaction = Reaction.from_json(test_json)
Пример #27
0
 def test_constructor__no_name(self):
     """ Test the Reaction constructor with no name provided. """
     reaction = Reaction(reactants={"A": 1},
                         products={"B": 1},
                         propensity_function="k1 * A * B")
     self.assertIsNotNone(re.search("rxn.*", reaction.name))
Пример #28
0
 def test_constructor__no_reactants_or_products(self):
     """ Test the Reaction constructor with reactants and products not set. """
     with self.assertRaises(ReactionError):
         Reaction(name="test_reaction", rate="k1")
Пример #29
0
class TestReaction(unittest.TestCase):
    '''
    ################################################################################################
    Unit tests for gillespy2.Reaction.
    ################################################################################################
    '''
    def setUp(self):
        self.valid_ma_reaction = Reaction(name="test_reaction",
                                          reactants={"A": 1},
                                          products={"B": 1},
                                          rate="k1")
        self.valid_cp_reaction = Reaction(name="test_reaction",
                                          reactants={"A": 1},
                                          products={"B": 1},
                                          propensity_function="k1 * A")

    def test_constructor__mass_action(self):
        """ Test the Reaction constructor for a mass action reaction. """
        reaction = Reaction(name="test_reaction",
                            reactants={"A": 1},
                            products={"B": 1},
                            rate="k1")
        self.assertEqual(reaction.name, "test_reaction")
        self.assertEqual(reaction.reactants, {"A": 1})
        self.assertEqual(reaction.products, {"B": 1})
        self.assertEqual(reaction.marate, "k1")
        self.assertEqual(reaction.propensity_function, "(k1*A)")
        self.assertEqual(reaction.ode_propensity_function, "(k1*A)")

    def test_constructor__custom_propensity(self):
        """ Test the Reaction constructor for a custom propensity reaction. """
        reaction = Reaction(name="test_reaction",
                            reactants={"A": 1},
                            products={"B": 1},
                            propensity_function="k1 * A * B")
        self.assertEqual(reaction.name, "test_reaction")
        self.assertEqual(reaction.reactants, {"A": 1})
        self.assertEqual(reaction.products, {"B": 1})
        self.assertEqual(reaction.propensity_function, "((k1*A)*B)")
        self.assertEqual(reaction.ode_propensity_function, "((k1*A)*B)")

    def test_constructor__custom_ode_propensity(self):
        """ Test the Reaction constructor for a custom ode propensity reaction. """
        reaction = Reaction(name="test_reaction",
                            reactants={"A": 1},
                            products={"B": 1},
                            ode_propensity_function="k1 * A * B")
        self.assertEqual(reaction.name, "test_reaction")
        self.assertEqual(reaction.reactants, {"A": 1})
        self.assertEqual(reaction.products, {"B": 1})
        self.assertEqual(reaction.propensity_function, "((k1*A)*B)")
        self.assertEqual(reaction.ode_propensity_function, "((k1*A)*B)")

    def test_constructor__different_custom_propensity(self):
        """ Test the Reaction constructor for a custom propensity reaction with different propensities. """
        reaction = Reaction(name="test_reaction",
                            reactants={"A": 1},
                            products={"B": 1},
                            propensity_function="k1 * A * B",
                            ode_propensity_function="k1 * A * B / vol")
        self.assertEqual(reaction.name, "test_reaction")
        self.assertEqual(reaction.reactants, {"A": 1})
        self.assertEqual(reaction.products, {"B": 1})
        self.assertEqual(reaction.propensity_function, "((k1*A)*B)")
        self.assertEqual(reaction.ode_propensity_function, "(((k1*A)*B)/vol)")

    def test_constructor__no_name(self):
        """ Test the Reaction constructor with no name provided. """
        reaction = Reaction(reactants={"A": 1},
                            products={"B": 1},
                            propensity_function="k1 * A * B")
        self.assertIsNotNone(re.search("rxn.*", reaction.name))

    def test_constructor__name_is_none_or_empty(self):
        """ Test the Reaction constructor with None or empty string as name. """
        test_names = [None, ""]
        for test_name in test_names:
            with self.subTest(name=test_name):
                reaction = Reaction(name=test_name,
                                    reactants={"A": 1},
                                    products={"B": 1},
                                    propensity_function="k1 * A * B")
                self.assertIsNotNone(re.search("rxn.*", reaction.name))

    def test_constructor__invalid_name(self):
        """ Test the Reaction constructor with non-str name. """
        with self.assertRaises(ReactionError):
            Reaction(name=0, reactants={"A": 1}, products={"B": 1}, rate="k1")

    def test_constructor__no_reactants_or_products(self):
        """ Test the Reaction constructor with reactants and products not set. """
        with self.assertRaises(ReactionError):
            Reaction(name="test_reaction", rate="k1")

    def test_constructor__invalid_reactants_and_products(self):
        """ Test the Reaction constructor with reactants and products both set to None or empty. """
        test_reacs = [None, {}]
        test_prods = [None, {}]
        for test_reac in test_reacs:
            for test_prod in test_prods:
                with self.subTest(reactants=test_reac, products=test_prod):
                    with self.assertRaises(ReactionError):
                        Reaction(name="test_reaction",
                                 reactants=test_reac,
                                 products=test_prod,
                                 rate="k1")

    def test_constructor__reactants_is_none(self):
        """ Test the Reaction constructor with None as reactants. """
        reaction = Reaction(name="test_reaction",
                            reactants=None,
                            products={"B": 1},
                            rate="k2")
        self.assertEqual(reaction.reactants, {})

    def test_constructor__reactants_keyed_by_species_obj(self):
        """ Test the Reaction constructor with reactants keyed by species objs. """
        test_species = Species(name="A", initial_value=0)
        reaction = Reaction(name="test_reaction",
                            reactants={test_species: 1},
                            products={"B": 1},
                            rate="k2")
        self.assertEqual(reaction.reactants, {"A": 1})

    def test_constructor__reactants_invalid_keys(self):
        """ Test the Reaction constructor with reactants keyed with invalid keys. """
        with self.assertRaises(ReactionError):
            Reaction(name="test_reaction",
                     reactants={1: 1},
                     products={"B": 1},
                     rate="k2")

    def test_constructor__reactants_invalid_values(self):
        """ Test the Reaction constructor with reactants containing invalid stoichiometry. """
        with self.assertRaises(ReactionError):
            Reaction(name="test_reaction",
                     reactants={"A": "1"},
                     products={"B": 1},
                     rate="k2")

    def test_constructor__invalid_reactants(self):
        """ Test the Reaction constructor with non-dict reactants. """
        with self.assertRaises(ReactionError):
            Reaction(name="test_reaction",
                     reactants=[["A", 1]],
                     products={"B": 1},
                     rate="k2")

    def test_constructor__products_is_none(self):
        """ Test the Reaction constructor with None as products. """
        reaction = Reaction(name="test_reaction",
                            reactants={"A": 1},
                            products=None,
                            rate="k2")
        self.assertEqual(reaction.products, {})

    def test_constructor__products_keyed_by_species_obj(self):
        """ Test the Reaction constructor with products keyed by species objs. """
        test_species = Species(name="B", initial_value=0)
        reaction = Reaction(name="test_reaction",
                            reactants={"A": 1},
                            products={test_species: 1},
                            rate="k2")
        self.assertEqual(reaction.products, {"B": 1})

    def test_constructor__products_invalid_keys(self):
        """ Test the Reaction constructor with products keyed with invalid keys. """
        with self.assertRaises(ReactionError):
            Reaction(name="test_reaction",
                     reactants={"A": 1},
                     products={1: 1},
                     rate="k2")

    def test_constructor__products_invalid_values(self):
        """ Test the Reaction constructor with products containing invalid stoichiometry. """
        with self.assertRaises(ReactionError):
            Reaction(name="test_reaction",
                     reactants={"A": 1},
                     products={"B": "1"},
                     rate="k2")

    def test_constructor__invalid_products(self):
        """ Test the Reaction constructor with non-dict products. """
        with self.assertRaises(ReactionError):
            Reaction(name="test_reaction",
                     reactants={"A": 1},
                     products=[["B", 1]],
                     rate="k2")

    def test_constructor__rate_and_propensity_functions_are_none(self):
        """ Test the Reaction constructor with rate and propensity functions set to None. """
        with self.assertRaises(ReactionError):
            Reaction(name="test_reaction",
                     reactants={"A": 1},
                     products={"B": 1})

    def test_constructor__rate_and_propensity_function_are_not_none(self):
        """ Test the Reaction constructor with rate and propensity function set. """
        with self.assertRaises(ReactionError):
            Reaction(name="test_reaction",
                     reactants={"A": 1},
                     products={"B": 1},
                     rate="k1",
                     propensity_function="A**2 + B**2")

    def test_constructor__int_propensity_function(self):
        """ Test the Reaction constructor with an int propensity function. """
        reaction = Reaction(name="test_reaction",
                            reactants={"A": 1},
                            products={"B": 1},
                            propensity_function=20)
        self.assertIsInstance(reaction.propensity_function, str)
        self.assertEqual(reaction.propensity_function, "20")
        self.assertIsInstance(reaction.ode_propensity_function, str)
        self.assertEqual(reaction.ode_propensity_function, "20")

    def test_constructor__float_propensity_function(self):
        """ Test the Reaction constructor with a float propensity function. """
        reaction = Reaction(name="test_reaction",
                            reactants={"A": 1},
                            products={"B": 1},
                            propensity_function=0.5)
        self.assertIsInstance(reaction.propensity_function, str)
        self.assertEqual(reaction.propensity_function, "0.5")
        self.assertIsInstance(reaction.ode_propensity_function, str)
        self.assertEqual(reaction.ode_propensity_function, "0.5")

    def test_constructor__invalid_propensity_function(self):
        """ Test the Reaction constructor with a propensity function that is not of the proper type. """
        test_pfs = ["", ["k1 * A * B"]]
        for test_pf in test_pfs:
            with self.subTest(propensity_function=test_pf):
                with self.assertRaises(ReactionError):
                    Reaction(name="test_reaction",
                             reactants={"A": 1},
                             products={"B": 1},
                             propensity_function=test_pf)

    def test_constructor__rate_and_ode_propensity_function_are_not_none(self):
        """ Test the Reaction constructor with rate and propensity function set. """
        with self.assertRaises(ReactionError):
            Reaction(name="test_reaction",
                     reactants={"A": 1},
                     products={"B": 1},
                     rate="k1",
                     ode_propensity_function="A**2 + B**2")

    def test_constructor__int_ode_propensity_function(self):
        """ Test the Reaction constructor with an int ode propensity function. """
        reaction = Reaction(name="test_reaction",
                            reactants={"A": 1},
                            products={"B": 1},
                            ode_propensity_function=20)
        self.assertIsInstance(reaction.propensity_function, str)
        self.assertEqual(reaction.propensity_function, "20")
        self.assertIsInstance(reaction.ode_propensity_function, str)
        self.assertEqual(reaction.ode_propensity_function, "20")

    def test_constructor__float_ode_propensity_function(self):
        """ Test the Reaction constructor with a float ode propensity function. """
        reaction = Reaction(name="test_reaction",
                            reactants={"A": 1},
                            products={"B": 1},
                            ode_propensity_function=0.5)
        self.assertIsInstance(reaction.propensity_function, str)
        self.assertEqual(reaction.propensity_function, "0.5")
        self.assertIsInstance(reaction.ode_propensity_function, str)
        self.assertEqual(reaction.ode_propensity_function, "0.5")

    def test_constructor__invalid_ode_propensity_function(self):
        """ Test the Reaction constructor with a ode propensity function that is not of the proper type. """
        test_opfs = ["", ["k1 * A * B"]]
        for test_opf in test_opfs:
            with self.subTest(ode_propensity_function=test_opf):
                with self.assertRaises(ReactionError):
                    Reaction(name="test_reaction",
                             reactants={"A": 1},
                             products={"B": 1},
                             ode_propensity_function=test_opf)

    def test_constructor__parameter_rate(self):
        """ Test the Reaction constructor with an parameter object as rate. """
        k1 = Parameter(name="k1", expression="20")
        reaction = Reaction(name="test_reaction",
                            reactants={"A": 1},
                            products={"B": 1},
                            rate=k1)
        self.assertIsInstance(reaction.marate, str)
        self.assertEqual(reaction.marate, "k1")

    def test_constructor__int_rate(self):
        """ Test the Reaction constructor with an int rate. """
        reaction = Reaction(name="test_reaction",
                            reactants={"A": 1},
                            products={"B": 1},
                            rate=20)
        self.assertIsInstance(reaction.marate, str)
        self.assertEqual(reaction.marate, "20")

    def test_constructor__float_rate(self):
        """ Test the Reaction constructor with a float rate. """
        reaction = Reaction(name="test_reaction",
                            reactants={"A": 1},
                            products={"B": 1},
                            rate=0.5)
        self.assertIsInstance(reaction.marate, str)
        self.assertEqual(reaction.marate, "0.5")

    def test_constructor__rate_not_accepted_type(self):
        """ Test the Reaction constructor with a rate that is an invalid type. """
        test_rates = ["", ["k1"]]
        for test_rate in test_rates:
            with self.subTest(rate=test_rate):
                with self.assertRaises(ReactionError):
                    reaction = Reaction(name="test_reaction",
                                        reactants={"A": 1},
                                        products={"B": 1},
                                        rate=test_rate)

    def test_constructor__annotation_invalid_type(self):
        """ Test the Reaction construct with an annotation of invalid type. """
        test_annotations = [5, 0.1, ["g"]]
        for test_annotation in test_annotations:
            with self.subTest(annotation=test_annotation):
                with self.assertRaises(ReactionError):
                    reaction = Reaction(name="test_reaction",
                                        reactants={"A": 1},
                                        products={"B": 1},
                                        rate="k1",
                                        annotation=test_annotation)

    def test___str__(self):
        """ Test Reaction.__str__ method. """
        self.assertIsInstance(str(self.valid_ma_reaction), str)

    def test__create_mass_action__total_stoch_3(self):
        """ Test Reaction._create_mass_action total stochiometry > 2. """
        self.valid_ma_reaction.reactants = {"A": 1, "B": 2}
        self.valid_ma_reaction.products = {"C": 1}
        with self.assertRaises(ReactionError):
            self.valid_ma_reaction._create_mass_action()

    def test__create_mass_action__marate_type_as_string(self):
        """ Test Reaction._create_mass_action marate as string. """
        self.valid_ma_reaction.reactants = {}
        self.valid_ma_reaction.products = {"C": 1}
        self.valid_ma_reaction._create_mass_action()
        self.assertEqual(self.valid_ma_reaction.propensity_function,
                         "(k1*vol)")
        self.assertEqual(self.valid_ma_reaction.ode_propensity_function, "k1")

    def test__create_mass_action__marate_type_as_int(self):
        """ Test Reaction._create_mass_action marate as int. """
        self.valid_ma_reaction.reactants = {}
        self.valid_ma_reaction.products = {"C": 1}
        self.valid_ma_reaction.marate = 1
        self.valid_ma_reaction._create_mass_action()
        self.assertEqual(self.valid_ma_reaction.propensity_function, "(1*vol)")
        self.assertEqual(self.valid_ma_reaction.ode_propensity_function, "1")

    def test__create_mass_action__marate_type_as_float(self):
        """ Test Reaction._create_mass_action marate as float. """
        self.valid_ma_reaction.reactants = {}
        self.valid_ma_reaction.products = {"C": 1}
        self.valid_ma_reaction.marate = 0.5
        self.valid_ma_reaction._create_mass_action()
        self.assertEqual(self.valid_ma_reaction.propensity_function,
                         "(0.5*vol)")
        self.assertEqual(self.valid_ma_reaction.ode_propensity_function, "0.5")

    def test__create_mass_action__marate_type_as_parameter(self):
        """ Test Reaction._create_mass_action marate as parameter. """
        test_parameter = Parameter("k1", expression=0.1)
        self.valid_ma_reaction.reactants = {}
        self.valid_ma_reaction.products = {"C": 1}
        self.valid_ma_reaction.marate = test_parameter
        self.valid_ma_reaction._create_mass_action()
        self.assertEqual(self.valid_ma_reaction.propensity_function,
                         "(k1*vol)")
        self.assertEqual(self.valid_ma_reaction.ode_propensity_function, "k1")

    def test__create_mass_action__X_to_Y(self):
        """ Test Reaction._create_mass_action X -> Y. """
        self.valid_ma_reaction.reactants = {"X": 1}
        self.valid_ma_reaction.products = {"Y": 1}
        self.valid_ma_reaction._create_mass_action()
        self.assertEqual(self.valid_ma_reaction.propensity_function, "(k1*X)")
        self.assertEqual(self.valid_ma_reaction.ode_propensity_function,
                         "(k1*X)")

    def test__create_mass_action__X_plus_Y_to_Z(self):
        """ Test Reaction._create_mass_action X + Y -> Z. """
        self.valid_ma_reaction.reactants = {"X": 1, "Y": 1}
        self.valid_ma_reaction.products = {"Z": 1}
        self.valid_ma_reaction._create_mass_action()
        self.assertEqual(self.valid_ma_reaction.propensity_function,
                         "(((k1*X)*Y)/vol)")
        self.assertEqual(self.valid_ma_reaction.ode_propensity_function,
                         "((k1*X)*Y)")

    def test__create_mass_action__2X_to_Y(self):
        """ Test Reaction._create_mass_action 2X -> Y. """
        self.valid_ma_reaction.reactants = {"X": 2}
        self.valid_ma_reaction.products = {"Y": 1}
        self.valid_ma_reaction._create_mass_action()
        self.assertEqual(self.valid_ma_reaction.propensity_function,
                         "((((0.5*k1)*X)*(X-1))/vol)")
        self.assertEqual(self.valid_ma_reaction.ode_propensity_function,
                         "((k1*X)*X)")

    def test__create_mass_action__species_obj_reactant(self):
        """ Test Reaction._create_mass_action when reactants is keyed by species object. """
        test_species = Species(name="A", initial_value=1)
        self.valid_ma_reaction.reactants = {test_species: 1}
        self.valid_ma_reaction._create_mass_action()
        self.assertEqual(self.valid_ma_reaction.propensity_function, "(k1*A)")
        self.assertEqual(self.valid_ma_reaction.ode_propensity_function,
                         "(k1*A)")

    def test__create_custom_propensity__exponent(self):
        """ Test Reaction._create_custom_propensity with a propensity containing exponents. """
        test_propensities = ["1/A^2", "1/A**2"]
        for test_propensity in test_propensities:
            with self.subTest(propensity_function=test_propensity):
                expr = self.valid_cp_reaction._create_custom_propensity(
                    test_propensity)
                self.assertEqual(expr, "(1/pow(A,2))")

    def test__create_custom_propensity__ln(self):
        """ Test Reaction._create_custom_propensity with a propensity containing ln. """
        expr = self.valid_cp_reaction._create_custom_propensity("ln(A)")
        self.assertEqual(expr, "log(A)")

    def test__create_custom_propensity__e(self):
        """ Test Reaction._create_custom_propensity with a propensity containing e. """
        expr = self.valid_cp_reaction._create_custom_propensity("A*e")
        self.assertEqual(expr, "(A*2.718281828459045)")

    def test__create_custom_propensity__fake_e(self):
        """ Test Reaction._create_custom_propensity with a propensity containing e. """
        expr = self.valid_cp_reaction._create_custom_propensity("A*ex")
        self.assertEqual(expr, "(A*ex)")

    def test__create_custom_propensity__propensity_parsing(self):
        """ Test Reaction._create_custom_propensity for parsing accuracy. """
        test_propensities = [
            "5*x^2+e*b+6", "5*x**2+e*b+6", "1*alpha/2+5^beta",
            "1*alpha/2+5**beta", "2.78*x+3^(4*x)", "2.78*x+3**(4*x)",
            "-5*-x^2", "-5*-x**2", "(alpha/beta + delta**gamma)/(atlas-zeta)",
            "(alpha/beta + delta^gamma)/(atlas-zeta)"
        ]
        expected_results = [
            "(((5*pow(x,2))+(2.718281828459045*b))+6)",
            "(((5*pow(x,2))+(2.718281828459045*b))+6)",
            "(((1*alpha)/2)+pow(5,beta))", "(((1*alpha)/2)+pow(5,beta))",
            "((2.78*x)+pow(3,(4*x)))", "((2.78*x)+pow(3,(4*x)))",
            "((-5)*(-pow(x,2)))", "((-5)*(-pow(x,2)))",
            "(((alpha/beta)+pow(delta,gamma))/(atlas-zeta))",
            "(((alpha/beta)+pow(delta,gamma))/(atlas-zeta))"
        ]
        for i, test_propensity in enumerate(test_propensities):
            expected_result = expected_results[i]
            with self.subTest(propensity_function=test_propensity,
                              expected_propensity_function=expected_result):
                expr = self.valid_cp_reaction._create_custom_propensity(
                    test_propensity)
                self.assertEqual(expr, expected_result)

    def test_add_product__species_string(self):
        """ Test Reaction.add_product when species is string. """
        self.valid_ma_reaction.add_product("X", 1)
        self.assertIn("X", self.valid_ma_reaction.products)
        self.assertEqual(self.valid_ma_reaction.products["X"], 1)

    def test_add_product__species_object(self):
        """ Test Reaction.add_product when species is GillesPy2.Species. """
        test_species = Species(name="X", initial_value=1)
        self.valid_ma_reaction.add_product(test_species, 1)
        self.assertIn(test_species.name, self.valid_ma_reaction.products)
        self.assertEqual(self.valid_ma_reaction.products[test_species.name], 1)

    def test_add_product__invalid_species(self):
        """ Test Reaction.add_product with an invalid species. """
        test_species = [None, "", 5, 0.5, ["A"]]
        for test_spec in test_species:
            with self.subTest(species=test_spec):
                with self.assertRaises(ReactionError):
                    self.valid_ma_reaction.add_product(test_spec, 1)

    def test_add_product__invalid_stochiometry(self):
        """ Test Reaction.add_product with an invalid stochiometry. """
        test_stoichs = [None, "1", -5, 0, 0.5, [1]]
        for test_stoich in test_stoichs:
            with self.subTest(stoichiometry=test_stoich):
                with self.assertRaises(ReactionError):
                    self.valid_ma_reaction.add_product("X", test_stoich)

    def test_add_reactant__massaction_reaction_species_string(self):
        """ Test Reaction.add_reactant when reaction is mass-action and species is string. """
        self.valid_ma_reaction.add_reactant("X", 1)
        self.assertIn("X", self.valid_ma_reaction.reactants)
        self.assertEqual(self.valid_ma_reaction.reactants["X"], 1)
        self.assertEqual(self.valid_ma_reaction.propensity_function,
                         "(((k1*A)*X)/vol)")
        self.assertEqual(self.valid_ma_reaction.ode_propensity_function,
                         "((k1*A)*X)")

    def test_add_reactant__customized_reaction_species_string(self):
        """ Test Reaction.add_reactant when reaction is customized and species is string. """
        self.valid_cp_reaction.add_reactant("X", 1)
        self.assertIn("X", self.valid_cp_reaction.reactants)
        self.assertEqual(self.valid_cp_reaction.reactants["X"], 1)
        self.assertEqual(self.valid_cp_reaction.propensity_function, "(k1*A)")
        self.assertEqual(self.valid_cp_reaction.ode_propensity_function,
                         "(k1*A)")

    def test_add_reactant__massaction_reaction_species_object(self):
        """ Test Reaction.add_reactant when reaction is mass-action and species is GillesPy2.Species. """
        test_species = Species(name="X", initial_value=1)
        self.valid_ma_reaction.add_reactant(test_species, 1)
        self.assertIn(test_species.name, self.valid_ma_reaction.reactants)
        self.assertEqual(self.valid_ma_reaction.reactants[test_species.name],
                         1)
        self.assertEqual(self.valid_ma_reaction.propensity_function,
                         f"(((k1*A)*{test_species.name})/vol)")
        self.assertEqual(self.valid_ma_reaction.ode_propensity_function,
                         f"((k1*A)*{test_species.name})")

    def test_add_reactant__customized_reaction_species_object(self):
        """ Test Reaction.add_reactant when reaction is customized and species is GillesPy2.Species. """
        test_species = Species(name="X", initial_value=1)
        self.valid_cp_reaction.add_reactant(test_species, 1)
        self.assertIn(test_species.name, self.valid_cp_reaction.reactants)
        self.assertEqual(self.valid_cp_reaction.reactants[test_species.name],
                         1)
        self.assertEqual(self.valid_cp_reaction.propensity_function, "(k1*A)")
        self.assertEqual(self.valid_cp_reaction.ode_propensity_function,
                         "(k1*A)")

    def test_add_reactant__massaction_reaction_stoich_0_to_1(self):
        """ Test Reaction.add_reactant when reaction is mass-action and stoichiometry goes from 0 to 1. """
        self.valid_ma_reaction.reactants = {}
        self.valid_ma_reaction.add_reactant("X", 1)
        self.assertIn("X", self.valid_ma_reaction.reactants)
        self.assertEqual(self.valid_ma_reaction.reactants["X"], 1)
        self.assertEqual(self.valid_ma_reaction.propensity_function, "(k1*X)")
        self.assertEqual(self.valid_ma_reaction.ode_propensity_function,
                         "(k1*X)")

    def test_add_reactant__massaction_reaction_stoich_0_to_2(self):
        """ Test Reaction.add_reactant when reaction is mass-action and stoichiometry goes from 0 to 2. """
        self.valid_ma_reaction.reactants = {}
        self.valid_ma_reaction.add_reactant("X", 2)
        self.assertIn("X", self.valid_ma_reaction.reactants)
        self.assertEqual(self.valid_ma_reaction.reactants["X"], 2)
        self.assertEqual(self.valid_ma_reaction.propensity_function,
                         "((((0.5*k1)*X)*(X-1))/vol)")
        self.assertEqual(self.valid_ma_reaction.ode_propensity_function,
                         "((k1*X)*X)")

    def test_add_reactant__invalid_species(self):
        """ Test Reaction.add_reactant with an invalid species. """
        test_species = [None, "", 5, 0.5, {}]
        for test_spec in test_species:
            with self.subTest(species=test_spec):
                with self.assertRaises(ReactionError):
                    self.valid_ma_reaction.add_reactant(test_spec, 1)

    def test_add_reactant__invalid_stochiometry(self):
        """ Test Reaction.add_reactant with an invalid stochiometry. """
        test_stoichs = [None, "1", -5, 0, 0.5, [1]]
        for test_stoich in test_stoichs:
            with self.subTest(stoichiometry=test_stoich):
                with self.assertRaises(ReactionError):
                    self.valid_ma_reaction.add_reactant("X", test_stoich)

    def test_from_json__massaction(self):
        """ Test Reaction.from_json with a mass-action reaction. """
        test_json = {
            'name': 'test_reaction',
            'reactants': [{
                'key': 'A',
                'value': 1
            }],
            'products': [{
                'key': 'B',
                'value': 1
            }],
            'marate': 'k1',
            'annotation': None,
            'propensity_function': '(k1*A)',
            'ode_propensity_function': '(k1*A)',
            'massaction': True,
            'type': 'mass-action'
        }
        reaction = Reaction.from_json(test_json)
        self.assertIsInstance(reaction, Reaction)
        self.assertEqual(reaction.name, 'test_reaction')
        self.assertEqual(reaction.reactants, {'A': 1})
        self.assertEqual(reaction.products, {'B': 1})
        self.assertEqual(reaction.propensity_function, '(k1*A)')
        self.assertEqual(reaction.ode_propensity_function, '(k1*A)')
        self.assertEqual(reaction.marate, 'k1')
        self.assertIsNone(reaction.annotation)
        self.assertTrue(reaction.massaction)
        self.assertEqual(reaction.type, 'mass-action')

    def test_from_json__customized(self):
        """ Test Reaction.from_json with a customized reaction. """
        test_json = {
            'name': 'test_reaction',
            'reactants': [{
                'key': 'A',
                'value': 1
            }],
            'products': [{
                'key': 'B',
                'value': 1
            }],
            'marate': None,
            'annotation': None,
            'propensity_function': '(k1*A)',
            'ode_propensity_function': '(k1*A)',
            'massaction': False,
            'type': 'customized'
        }
        reaction = Reaction.from_json(test_json)
        self.assertIsInstance(reaction, Reaction)
        self.assertEqual(reaction.name, 'test_reaction')
        self.assertEqual(reaction.reactants, {'A': 1})
        self.assertEqual(reaction.products, {'B': 1})
        self.assertEqual(reaction.propensity_function, '(k1*A)')
        self.assertEqual(reaction.ode_propensity_function, '(k1*A)')
        self.assertIsNone(reaction.marate)
        self.assertIsNone(reaction.annotation)
        self.assertFalse(reaction.massaction)
        self.assertEqual(reaction.type, 'customized')

    def test_from_json__massaction_json_changed(self):
        """ Test Reaction.from_json with a mass-action reaction and modified json. Ensure propensities are updated."""
        test_json = {
            'name': 'test_reaction',
            'reactants': [{
                'key': 'B',
                'value': 1
            }],
            'products': [{
                'key': 'A',
                'value': 1
            }],
            'marate': 'k1',
            'annotation': None,
            'propensity_function': '(k1*A)',
            'ode_propensity_function': '(k1*A)',
            'massaction': True,
            'type': 'mass-action'
        }
        reaction = Reaction.from_json(test_json)
        self.assertEqual(reaction.name, 'test_reaction')
        self.assertEqual(reaction.reactants, {'B': 1})
        self.assertEqual(reaction.products, {'A': 1})
        self.assertEqual(reaction.propensity_function, '(k1*B)')
        self.assertEqual(reaction.ode_propensity_function, '(k1*B)')
        self.assertEqual(reaction.marate, 'k1')
        self.assertIsNone(reaction.annotation)
        self.assertTrue(reaction.massaction)
        self.assertEqual(reaction.type, 'mass-action')

    def test_from_json__customized_json_changed(self):
        """ Test Reaction.from_json with a customized reaction and modified json. Ensure propensities don't change. """
        test_json = {
            'name': 'test_reaction',
            'reactants': [{
                'key': 'B',
                'value': 1
            }],
            'products': [{
                'key': 'A',
                'value': 1
            }],
            'marate': None,
            'annotation': None,
            'propensity_function': '(k1*A)',
            'ode_propensity_function': '(k1*A)',
            'massaction': False,
            'type': 'customized'
        }
        reaction = Reaction.from_json(test_json)
        self.assertEqual(reaction.name, 'test_reaction')
        self.assertEqual(reaction.reactants, {'B': 1})
        self.assertEqual(reaction.products, {'A': 1})
        self.assertEqual(reaction.propensity_function, '(k1*A)')
        self.assertEqual(reaction.ode_propensity_function, '(k1*A)')
        self.assertIsNone(reaction.marate)
        self.assertIsNone(reaction.annotation)
        self.assertFalse(reaction.massaction)
        self.assertEqual(reaction.type, 'customized')

    def test_from_json__invalid_json(self):
        """ Test Reaction.from_json with a reaction that fails validation. """
        test_json = {
            'name': 'test_reaction',
            'reactants': [{
                'key': 'A',
                'value': 1
            }],
            'products': [{
                'key': 'B',
                'value': 1
            }],
            'marate': None,
            'annotation': None,
            'propensity_function': '(k1*A)',
            'ode_propensity_function': '(k1*A)',
            'massaction': True,
            'type': 'mass-action'
        }
        with self.assertRaises(ReactionError):
            reaction = Reaction.from_json(test_json)

    def test_set_annotation__invalid_annotation(self):
        """ Test Reaction.set_annotation with an invalid annotation. """
        test_annotations = [None, 5, 0.1, ["g"]]
        for test_annotation in test_annotations:
            with self.subTest(annotation=test_annotation):
                with self.assertRaises(ReactionError):
                    self.valid_ma_reaction.set_annotation(test_annotation)

    def test_set_propensities__propensity_function_only(self):
        """ Test Reaction.set_propensities with propensity_function only. """
        self.valid_ma_reaction.set_propensities(propensity_function="k1*A/2")
        expected_result = "((k1*A)/2)"
        self.assertEqual(self.valid_ma_reaction.propensity_function,
                         expected_result)
        self.assertEqual(self.valid_ma_reaction.ode_propensity_function,
                         expected_result)
        self.assertIsNone(self.valid_ma_reaction.marate)
        self.assertFalse(self.valid_ma_reaction.massaction)
        self.assertEqual(self.valid_ma_reaction.type, "customized")

    def test_set_propensities__ode_propensity_function_only(self):
        """ Test Reaction.set_propensities with ode_propensity_function only. """
        self.valid_ma_reaction.set_propensities(
            ode_propensity_function="k1*A/2")
        expected_result = "((k1*A)/2)"
        self.assertEqual(self.valid_ma_reaction.propensity_function,
                         expected_result)
        self.assertEqual(self.valid_ma_reaction.ode_propensity_function,
                         expected_result)
        self.assertIsNone(self.valid_ma_reaction.marate)
        self.assertFalse(self.valid_ma_reaction.massaction)
        self.assertEqual(self.valid_ma_reaction.type, "customized")

    def test_set_propensities__different_propensities(self):
        """ Test Reaction.set_propensities with different propensity functions. """
        self.valid_ma_reaction.set_propensities(
            propensity_function="k1*A/2", ode_propensity_function="k1*A/3")
        self.assertEqual(self.valid_ma_reaction.propensity_function,
                         "((k1*A)/2)")
        self.assertEqual(self.valid_ma_reaction.ode_propensity_function,
                         "((k1*A)/3)")
        self.assertIsNone(self.valid_ma_reaction.marate)
        self.assertFalse(self.valid_ma_reaction.massaction)
        self.assertEqual(self.valid_ma_reaction.type, "customized")

    def test_set_propensities__int_propensity_function(self):
        """ Test Reaction.set_propensities with propensity_function of type int. """
        self.valid_ma_reaction.set_propensities(propensity_function=5)
        self.assertEqual(self.valid_ma_reaction.propensity_function, "5")
        self.assertEqual(self.valid_ma_reaction.ode_propensity_function, "5")

    def test_set_propensities__float_propensity_function(self):
        """ Test Reaction.set_propensities with propensity_function of type float. """
        self.valid_ma_reaction.set_propensities(propensity_function=0.5)
        self.assertEqual(self.valid_ma_reaction.propensity_function, "0.5")
        self.assertEqual(self.valid_ma_reaction.ode_propensity_function, "0.5")

    def test_set_propensities__int_ode_propensity_function(self):
        """ Test Reaction.set_propensities with ode_propensity_function of type int. """
        self.valid_ma_reaction.set_propensities(ode_propensity_function=5)
        self.assertEqual(self.valid_ma_reaction.propensity_function, "5")
        self.assertEqual(self.valid_ma_reaction.ode_propensity_function, "5")

    def test_set_propensities__float_ode_propensity_function(self):
        """ Test Reaction.set_propensities with ode_propensity_function of type float. """
        self.valid_ma_reaction.set_propensities(propensity_function=0.5)
        self.assertEqual(self.valid_ma_reaction.propensity_function, "0.5")
        self.assertEqual(self.valid_ma_reaction.ode_propensity_function, "0.5")

    def test_set_propensities__invalid_propensity_function(self):
        """ Test Reaction.set_propensities with an invalid propensity_function. """
        test_pfs = ["", ["k1 * A * B"]]
        for test_pf in test_pfs:
            with self.subTest(propensity_function=test_pf):
                with self.assertRaises(ReactionError):
                    self.valid_ma_reaction.set_propensities(
                        propensity_function=test_pf)

    def test_set_propensities__invalid_ode_propensity_function(self):
        """ Test Reaction.set_propensities with an invalid ode_propensity_function. """
        test_pfs = ["", ["k1 * A * B"]]
        for test_pf in test_pfs:
            with self.subTest(ode_propensity_function=test_pf):
                with self.assertRaises(ReactionError):
                    self.valid_ma_reaction.set_propensities(
                        ode_propensity_function=test_pf)

    def test_set_rate(self):
        """ Test Reaction.set_rate. """
        self.valid_cp_reaction.set_rate("k2")
        self.assertEqual(self.valid_cp_reaction.propensity_function, "(k2*A)")
        self.assertEqual(self.valid_cp_reaction.ode_propensity_function,
                         "(k2*A)")
        self.assertEqual(self.valid_cp_reaction.marate, "k2")
        self.assertTrue(self.valid_cp_reaction.massaction)
        self.assertEqual(self.valid_cp_reaction.type, "mass-action")

    def test_set_rate__parameter_rate(self):
        """ Test Reaction.set_rate with rate of type GillesPy2.Parameter. """
        test_parameter = Parameter(name="k2", expression="0.75")
        self.valid_cp_reaction.set_rate(test_parameter)
        self.assertEqual(self.valid_cp_reaction.marate, "k2")

    def test_set_rate__int_rate(self):
        """ Test Reaction.set_rate with rate of type int. """
        self.valid_cp_reaction.set_rate(5)
        self.assertEqual(self.valid_cp_reaction.marate, "5")

    def test_set_rate__float_rate(self):
        """ Test Reaction.set_rate with rate of type float. """
        self.valid_cp_reaction.set_rate(0.5)
        self.assertEqual(self.valid_cp_reaction.marate, "0.5")

    def test_set_rate__invalid_rate(self):
        """ Test Reaction.set_rate with an invalid rate. """
        test_rates = [None, "", ["k1"]]
        for test_rate in test_rates:
            with self.subTest(rate=test_rate):
                with self.assertRaises(ReactionError):
                    self.valid_cp_reaction.set_rate(test_rate)

    def test_to_dict__massaction(self):
        """ Test Reaction.to_dict with a mass-action reaction. """
        test_json = {
            'name': 'test_reaction',
            'reactants': [{
                'key': 'A',
                'value': 1
            }],
            'products': [{
                'key': 'B',
                'value': 1
            }],
            'marate': 'k1',
            'annotation': None,
            'propensity_function': '(k1*A)',
            'ode_propensity_function': '(k1*A)',
            'massaction': True,
            'type': 'mass-action'
        }
        test_dict = self.valid_ma_reaction.to_dict()
        self.assertIsInstance(test_dict, dict)
        self.assertEqual(test_dict, test_json)

    def test_to_dict__customized(self):
        """ Test Reaction.to_dict with a customized reaction. """
        test_json = {
            'name': 'test_reaction',
            'reactants': [{
                'key': 'A',
                'value': 1
            }],
            'products': [{
                'key': 'B',
                'value': 1
            }],
            'marate': None,
            'annotation': None,
            'propensity_function': '(k1*A)',
            'ode_propensity_function': '(k1*A)',
            'massaction': False,
            'type': 'customized'
        }
        test_dict = self.valid_cp_reaction.to_dict()
        self.assertIsInstance(test_dict, dict)
        self.assertEqual(test_dict, test_json)

    def test_validate__mass_action(self):
        """ Test Reaction.validate for mass-action reactions. """
        test_types = ["mass-action", "customized"]
        test_massactions = [True, False]
        for test_type in test_types:
            for test_massaction in test_massactions:
                with self.subTest(reaction_type=test_type,
                                  massaction=test_massaction):
                    if test_type == "mass-action" and test_massaction:
                        continue
                    with self.assertRaises(ReactionError):
                        self.valid_ma_reaction.type = test_type
                        self.valid_ma_reaction.massaction = test_massaction
                        self.valid_ma_reaction.validate(coverage="all")

    def test_validate__customized(self):
        """ Test Reaction.validate for customized reactions. """
        test_types = ["mass-action", "customized"]
        test_massactions = [True, False]
        for test_type in test_types:
            for test_massaction in test_massactions:
                with self.subTest(reaction_type=test_type,
                                  massaction=test_massaction):
                    if test_type == "customized" and not test_massaction:
                        continue
                    with self.assertRaises(ReactionError):
                        self.valid_cp_reaction.type = test_type
                        self.valid_cp_reaction.massaction = test_massaction
                        self.valid_cp_reaction.validate(coverage="all")

    def test_validate__invalid_name(self):
        """ Test Reaction.validate with an invalid name. """
        test_names = [None, "", 0]
        for test_name in test_names:
            with self.subTest(name=test_name):
                with self.assertRaises(ReactionError):
                    self.valid_ma_reaction.name = test_name
                    self.valid_ma_reaction.validate(coverage="all")

    def test_valdiate__invalid_reactants_and_products(self):
        """ Test Reaction.validate with reactants and products both set to None or empty. """
        test_reacs = [None, {}]
        test_prods = [None, {}]
        for test_reac in test_reacs:
            for test_prod in test_prods:
                with self.subTest(reactants=test_reac, products=test_prod):
                    with self.assertRaises(ReactionError):
                        self.valid_ma_reaction.reactants = test_reac
                        self.valid_ma_reaction.products = test_prod
                        self.valid_ma_reaction.validate(coverage="all")

    def test_validate__reactants_invalid_keys(self):
        """ Test Reaction.validate with reactants keyed with invalid keys. """
        with self.assertRaises(ReactionError):
            self.valid_ma_reaction.reactants = {1: 1}
            self.valid_ma_reaction.validate(coverage="all")

    def test_validate__reactants_invalid_values(self):
        """ Test Reaction.validate with reactants containing invalid stoichiometry. """
        with self.assertRaises(ReactionError):
            self.valid_ma_reaction.reactants = {"A": "1"}
            self.valid_ma_reaction.validate(coverage="all")

    def test_validate__invalid_reactants(self):
        """ Test Reaction.validate with non-dict reactants. """
        test_reactants = [None, ["A", 1]]
        for test_reactant in test_reactants:
            with self.subTest(reactants=test_reactant):
                with self.assertRaises(ReactionError):
                    self.valid_ma_reaction.reactants = test_reactant
                    self.valid_ma_reaction.validate(coverage="all")

    def test_validate__prodects_invalid_keys(self):
        """ Test Reaction.validate with products keyed with invalid keys. """
        with self.assertRaises(ReactionError):
            self.valid_ma_reaction.products = {1: 1}
            self.valid_ma_reaction.validate(coverage="all")

    def test_validate__products_invalid_values(self):
        """ Test Reaction.validate with products containing invalid stoichiometry. """
        with self.assertRaises(ReactionError):
            self.valid_ma_reaction.products = {"B": "1"}
            self.valid_ma_reaction.validate(coverage="all")

    def test_validate__invalid_products(self):
        """ Test Reaction.validate with non-dict products. """
        test_products = [None, ["B", 1]]
        for test_product in test_products:
            with self.subTest(products=test_product):
                with self.assertRaises(ReactionError):
                    self.valid_ma_reaction.products = test_product
                    self.valid_ma_reaction.validate(coverage="all")

    def test_validate__invalid_propensity_function(self):
        """ Test Reaction.validate with an invalid propensity function. """
        test_pfs = [None, 20, 0.5, "", ["k1 * A * B"]]
        for test_pf in test_pfs:
            with self.subTest(propensity_function=test_pf):
                with self.assertRaises(ReactionError):
                    self.valid_ma_reaction.propensity_function = test_pf
                    self.valid_ma_reaction.validate(coverage="all")

    def test_validate__invalid_ode_propensity_function(self):
        """ Test Reaction.validate with an invalid ode propensity function. """
        test_opfs = [None, 20, 0.5, "", ["k1 * A * B"]]
        for test_opf in test_opfs:
            with self.subTest(ode_propensity_function=test_opf):
                with self.assertRaises(ReactionError):
                    self.valid_ma_reaction.ode_propensity_function = test_opf
                    self.valid_ma_reaction.validate(coverage="all")

    def test_validate__invalid_rate(self):
        """ Test Reaction.validate with a rate that is an invalid type. """
        test_rates = [20, 0.5, "", ["k1"]]
        for test_rate in test_rates:
            with self.subTest(rate=test_rate):
                with self.assertRaises(ReactionError):
                    self.valid_ma_reaction.marate = test_rate
                    self.valid_ma_reaction.validate(coverage="all")

    def test_validate__annotation_invalid_type(self):
        """ Test Reaction.validate with an annotation of invalid type. """
        test_annotations = [5, 0.1, ["g"]]
        for test_annotation in test_annotations:
            with self.subTest(annotation=test_annotation):
                with self.assertRaises(ReactionError):
                    self.valid_ma_reaction.annotation = test_annotation
                    self.valid_ma_reaction.validate(coverage="all")

    def test_comp_time_of_validate(self):
        """ Check the computation time of validate. """
        import time
        from datetime import datetime
        start = time.time()
        self.valid_ma_reaction.validate(coverage="all")
        tic = datetime.utcfromtimestamp(time.time() - start)
        print(
            f"Total time to run validate on a mass-action reaction: {tic.strftime('%M mins %S secs %f msecs')}"
        )
        start = time.time()
        self.valid_cp_reaction.validate(coverage="all")
        tic = datetime.utcfromtimestamp(time.time() - start)
        print(
            f"Total time to run validate on a customized reaction: {tic.strftime('%M mins %S secs %f msecs')}"
        )
Пример #30
0
 def test_constructor__rate_and_propensity_functions_are_none(self):
     """ Test the Reaction constructor with rate and propensity functions set to None. """
     with self.assertRaises(ReactionError):
         Reaction(name="test_reaction",
                  reactants={"A": 1},
                  products={"B": 1})