Пример #1
0
 def setUp(self):
     #self.net = randomnet.Uniform(2, 2, (1,2), (1,2))
     self.rates = {
         (OrderedFrozenBag(["A", "B"]), OrderedFrozenBag(["B", "C"])): 0.1
     }
     self.net = achemkit.ReactionNetwork(self.rates)
     self.mols = [mol for mol in self.net.seen * 10]
     self.achem = achemkit.AChemReactionNetwork(self.net)
Пример #2
0
    def __init__(self, time, reactants, products, rateconstant=None):
        reactants = OrderedFrozenBag(reactants)
        products = OrderedFrozenBag(products)

        #have to do it this way to avoid immutability issues
        super(Event, self).__setattr__('time', time)
        super(Event, self).__setattr__('reactants', reactants)
        super(Event, self).__setattr__('products', products)
        super(Event, self).__setattr__('rateconstant', rateconstant)
Пример #3
0
 def test_reaction_to_string(self):
     """
     Check that it convert a reaction to a string correctly
     """
     target = """A + B\t-2.0>\tB + C"""
     self.assertEqual(
         self.net.reaction_to_string(
             (OrderedFrozenBag(["A", "B"]), OrderedFrozenBag(["B", "C"])),
             2.0), target)
Пример #4
0
 def test_reactions(self):
     """
     Makes sure the reactions that were specified are in reactions.
     Also checks that they are in sorted order
     TODO check sorted order between reactions
     """
     self.assertEqual(
         self.net.reactions,
         ((OrderedFrozenBag(["A", "B"]), OrderedFrozenBag(["B", "C"])), ))
Пример #5
0
    def setUp(self):
        self.rates = {
            (OrderedFrozenBag(["A", "B"]), OrderedFrozenBag(["B", "C"])): 2.0
        }
        self.net = ReactionNetwork(self.rates)

        self.othernet = ReactionNetwork(self.rates)

        self.wrongrates = {
            (OrderedFrozenBag(["A", "B"]), OrderedFrozenBag(["B", "C"])): 3.0
        }
        self.wrongnet = ReactionNetwork(self.wrongrates)
Пример #6
0
 def __init__(self, rates):
     for rate in list(rates.values()):
         assert rate > 0.0
     self._rates = {}
     for reaction in rates:
         reactants, products = reaction
         if not isinstance(reactants, OrderedFrozenBag):
             reactants = OrderedFrozenBag(reactants)
         if not isinstance(products, OrderedFrozenBag):
             products = OrderedFrozenBag(products)
         if reactants != products:
             self._rates[reactants, products] = rates[reaction]
     #self._rates = FrozenDict(self._rates)
     self.rates = dict(self._rates)
Пример #7
0
    def do(self, time):
        """
        Assign all molecules to a collection of reactants. Use the combined
        collections of products as the new molecules.
                
        Uses :py:mod:`umpf` for parallelism.
        
        :param float time: Time to simulate, at a rate of one step per unit.
        :rtype: yields :py:class:`achemkit.Event` objects.
        """
        self.maxtime += time
        while self.time < self.maxtime:
            self.mols = tuple(self.rng.sample(self.mols, len(self.mols)))
            newmols = ()
            allreactants = []
            while len(self.mols) > 0:
                noreactants = get_sample(self.achem.noreactants)
                if noreactants <= len(self.mols):
                    reactants = self.mols[:noreactants]
                    self.mols = self.mols[noreactants:]
                    allreactants.append(OrderedFrozenBag(reactants))
                else:
                    break

            allproducts = umpf.map(self.achem.react, allreactants)
            results = zip(allreactants, allproducts)
            for reactants, products in results:
                e = Event(self.time, reactants, products)
                newmols += tuple(products)
                yield e

            self.mols = newmols
            self.time += 1.0
Пример #8
0
    def all_reactions(self, reactants):
        #need to convert to a bag so that it maps to the reactionnetwork properly
        reactants = OrderedFrozenBag(reactants)

        possibleproducts = {}
        for netreactants, netproducts in self.reactionnetwork.reactions:
            if netreactants == reactants:
                possibleproducts[netreactants,
                                 netproducts] = self.reactionnetwork.rate(
                                     netreactants, netproducts)

        return possibleproducts
Пример #9
0
    def react(self, reactants):
        #need to convert to a bag so that it maps to the reactionnetwork properly
        if not isinstance(reactants, OrderedFrozenBag):
            reactants = OrderedFrozenBag(reactants)

        possibleproducts = []
        for netreactants, netproducts in self.reactionnetwork.reactions:
            if netreactants == reactants:
                possibleproducts.append(netproducts)

        if len(possibleproducts) == 0:
            return reactants
        else:
            return random.choice(possibleproducts)
Пример #10
0
 def add_mol(self, mol):
     """
     Internal utility function used to ensure molecular species have the
     relevant reaction combinations added.
     
     :param mol: Molecular species to add to reactor.
     """
     if mol not in self.mols:
         self.mols.append(mol)
         noreactants = self.achem.noreactants
         try:
             noreactants = set(noreactants)
         except TypeError:
             #not an itterable, single number
             noreactants = set([noreactants])
         for i in noreactants:
             for others in itertools.combinations(self.mols, i - 1):
                 reactants = (mol, ) + others
                 reactants = OrderedFrozenBag(reactants)
                 assert reactants not in self.tested
                 self.untested.append(reactants)
Пример #11
0
    def do(self, time):
        """
        Repeatedly determine a random collection of reactants and replace them 
        with the products generated by :py:meth:`achemkit.achem.AChem.react`.
                
        Uses :py:mod:`umpf` for parallelism.
        
        :param float time: Time to simulate, at a rate of one reaction per unit.
        :rtype: yields :py:class:`achemkit.Event` objects.
        """
        self.maxtime += time
        while self.time < self.maxtime:
            self.mols = tuple(self.rng.sample(self.mols, len(self.mols)))

            noreactants = 2
            reactants = OrderedFrozenBag(self.mols[:noreactants])
            self.mols = self.mols[noreactants:]

            products = self.achem.react(reactants)
            self.mols += tuple(products)

            e = Event(self.time, reactants, products)
            yield e
            self.time += 1.0
Пример #12
0
 def test_rate(self):
     self.assertEqual(
         self.net.rate(OrderedFrozenBag(["A", "B"]),
                       OrderedFrozenBag(["B", "C"])), 2.0)
Пример #13
0
 def setUp(self):
     self.rates = {
         (OrderedFrozenBag(["A", "B"]), OrderedFrozenBag(["B", "C"])): 2.0
     }
     self.net = achemkit.ReactionNetwork(self.rates)
     self.achem = achemkit.AChemReactionNetwork(self.net)
Пример #14
0
 def test(self):
     self.assertEqual(self.achem.react(("A", "B")),
                      OrderedFrozenBag(("B", "C")))
Пример #15
0
    def from_file(cls, infile):
        """
        Alternative constructor that accepts a :py:func:`file` object (or equivalent).

        Source must be formatted as a .chem file, see :ref:`chem_file_format`.
        """
        rates = {}
        linecount = 0
        repattern = r'-([0-9]*\.?[0-9]*)>'
        for line in infile:
            #keep a copy of the line for printout in case of problem
            rawline = line
            line = line.strip()
            #record which line of the file we are on for
            #printout in case of problem
            linecount += 1
            #ignore comments and blanks
            if len(line) == 0 or line[0] == "#":
                pass
            elif re.search(repattern, line) != None:
                #A -> B
                #A + B -> C
                #A+ B -> C
                #A -2> B
                # A -2.0> B
                splitline = re.split(repattern, line)

                if len(splitline) != 3:
                    raise ValueError("Invalid reaction at line %d : %s" %
                                     (linecount, rawline))

                inputstring, rate, outputstring = splitline

                if len(rate):
                    rate = float(rate)
                else:
                    rate = 1.0

                inputs = []
                for molspecies in inputstring.split('+'):
                    molspecies = molspecies.strip()
                    if len(molspecies):
                        inputs.append(molspecies)

                outputs = []
                for molspecies in outputstring.split('+'):
                    molspecies = molspecies.strip()
                    if len(molspecies):
                        outputs.append(molspecies)

                inputs.sort()
                inputs = OrderedFrozenBag(inputs)

                outputs.sort()
                outputs = OrderedFrozenBag(outputs)

                if (inputs, outputs) in rates:
                    raise ValueError("Duplicate reaction at line %d : %s" %
                                     (linecount, rawline))
                    break

                if inputs == outputs:
                    raise ValueError(
                        "Invalid reaction at line %d : %s (%s -> %s)" %
                        (linecount, rawline, str(inputs), str(outputs)))
                    break

                rates[(inputs, outputs)] = rate

            else:
                raise ValueError("Invalid reaction at line %d : %s" %
                                 (linecount, rawline))
        return cls(rates)
Пример #16
0
def Uniform(nmols, nreactions, nreactants, nproducts, rates = 1.0, cls = ReactionNetwork, rng = None):
    """
    Generates a random :py:class:`~achemkit.reactionnet.ReactionNetwork` by assigning reaction randomly between all molecular species.

    Arguments:

    nmols
        Number of molecules in the reaction network.

    .. note::

        :py:class:`achemkit.reactionnet.ReactionNetwork` tracks molecules by their reactions, so if a molecule is not part of any reaction
        it will not appear at all e.g. in :py:attr:`~achemkit.reactionnet.ReactionNetwork.seen`. This could lead to differences from `nmols`.

    nreactions
        Number of reaction in the reaction network. 

    .. note::

        The value of `nreactions` is the number of times a reaction will be added to the :py:class:`~achemkit.reactionnet.ReactionNetwork`. If it
        is already in the :py:class:`~achemkit.reactionnet.ReactionNetwork`, it will be replaced. This can lead to :class:`achemkit.reactionnet.ReactionNetwork` with less
        than `nreactions` reactions.

    nreactants
        Number of reactants for each reaction in the reaction network. Can be a single value or a tuple/list which will be uniformly sampled from (duplicates can be used to give a non-uniform distribution).

        .. note::
            If this is a tuple/list it will be sampled for each reaction.

    nproducts
        Number of products for each reaction in the reaction network. Can be a single value or a tuple/list which will be uniformly sampled from (duplicates can be used to give a non-uniform distribution).

        If this is None, then `nreactants` must be a list/tuple of tuples of (`nreactants`, `nproducts`) pairs that will be uniformly sampled from. Or `nreactants` must be a dictionary with keys of (`nreactants`, `nproducts`) and values of weightings, which will be sampled from.

        .. note::
            If this is a tuple/list it will be sampled for each reaction.

    rates
        Rate of each reaction in the reaction network. Can be a single value or a tuple/list which will be uniformly sampled from (duplicates can be used to give a non-uniform distribution).

        .. note::
            If this is a tuple/list it will be sampled for each reaction.

    cls
        Alternative class to use for constructing the return rather than :py:class:`achemkit.reactionnet.ReactionNetwork`.

    rng
        Random number generator to use. If not specifed, one will be generated at random.


    These arguments can be a single value, a tuple/list which will be uniformly sampled from, or a dictionary of value/weighting which will be sampled from

    For example:

    ``Uniform(5,3,2,1)`` will generate 5 molecules with 3 reactions between them where each reaction has two reactants and one product.

    ``Uniform(5,3,(1,2), (1,2))`` will generate 5 molecules with 3 reactions between them where each reaction has one or two reactants and one or two products.

    ``Uniform(5,3,((2,1),(1,2)), None)`` will generate 5 molecules with 3 reactions between them where each reaction has either two reactants and one product or one reactant and two products.

    """

    if rng is None:
        rng = random.Random(random.random())

    if isinstance(nmols, int):
        nmols = ["M%d"%i for i in range(nmols)]

    nreactions = get_sample(nreactions, rng)
    

    if nproducts is None:
        #its a tuple not a generator because its the same variable name
        newnreactants = []
        newnproducts = []
        for nreactant, nproduct in [get_sample(nreactants, rng) for i in range(nreactions)]:
            newnreactants.append(nreactant)
            newnproducts.append(nproduct)
        nproducts = newnreactants
        nreactants = newnproducts
    else:
        #its a tuple not a generator because its the same variable name
        nreactants = [get_sample(nreactants, rng) for i in range(nreactions)]
        nproducts = [get_sample(nproducts, rng) for i in range(nreactions)]

    rates = [get_sample(rates, rng) for i in range(nreactions)]

    outrates = {}
    for thisnreactants, thisnproducts, thisrate in zip(nreactants, nproducts, rates):

        reactants = [get_sample(nmols, rng)  for j in range(thisnreactants)]
        products = [get_sample(nmols, rng)  for j in range(thisnproducts)]
        reactants = OrderedFrozenBag(reactants)
        products = OrderedFrozenBag(products)

        outrates[(reactants, products)] = thisrate

    return cls(outrates)
Пример #17
0
def Linear(natoms, maxlength, pform, pbreak, directed = True, rates = 1.0, cls = ReactionNetwork, rng = None):
    """
    Generates a random :py:class:`~achemkit.reactionnet.ReactionNetwork` from molecules that are strings of atoms and can join together or break apart.

    Based on the paper Autocatalytic sets of proteins. 1986. Journal of Theoretical Biology 119:1-24 by Kauffman, Stuart A.  but without the explicit catalytic activity.


    Arguments:

    natoms
        Number of atoms to use. Can be a single value or a tuple/list which will be uniformly sampled from (duplicates can be used to give a non-uniform distribution), or a dict of value:weight which will be sampled from.

    .. note::

        :py:class:`achemkit.reactionnet.ReactionNetwork` tracks molecules by their reactions, so if a molecule is not part of any reaction
        it will not appear at all e.g. in :py:attr:`~achemkit.reactionnet.ReactionNetwork.seen`.

    maxlength
        Maximum number of atoms in a molecule. If this is None, then they are unbounded; this might cause problems with a computational explosion. Can be a single value or a tuple/list which will be uniformly sampled from (duplicates can be used to give a non-uniform distribution), or a dict of value:weight which will be sampled from.

    pform
        Probability that a pair of molecules will join together per orientation. Must be between 0 and 1. Can be a single value or a tuple/list which will be uniformly sampled from (duplicates can be used to give a non-uniform distribution), or a dict of value:weight which will be sampled from.

    pbreak
        Probability that any pair of atoms will break. Must be between 0 and 1.Can be a single value or a tuple/list which will be uniformly sampled from (duplicates can be used to give a non-uniform distribution), or a dict of value:weight which will be sampled from.

    directed
        If false, molecules have no intrinsic direction so AlphBeta is equivlanet to BetaAlpha.

    rates
        Rate of each reaction in the reaction network. Can be a single value, or a tuple/list which will be uniformly sampled from (duplicates can be used to give a non-uniform distribution), or a dict of value:weight which will be sampled from.

    cls
        Alternative class to use for constructing the return rather than :py:class:`achemkit.reactionnet.ReactionNetwork`.

    rng
        Random number generator to use. If not specifed, one will be generated at random.

    """
    
    if rng is None:
        rng = random.Random(random.random())


    natoms = get_sample(natoms, rng)
    maxlength = get_sample(maxlength, rng)
    pform = get_sample(pform, rng)
    pbreak = get_sample(pbreak, rng)
    assert pform >= 0.0
    assert pform <= 1.0
    assert pbreak >= 0.0
    assert pbreak <= 1.0

    #these are lists not sets because sets have machine-dependant ordering, which prevents reproducibility for the same random seed.
    molecules = []
    new = []

    #create some inital atoms
    #of the form Abc where first letter is capitalized
    alpha = "abcdefghijklmnopqrstuvwxyz"
    assert natoms > 0 
    for i in range(natoms):
        name = alpha[i%len(alpha)]
        while i >= len(alpha):
            i /= len(alpha)
            name = alpha[(i%len(alpha))-1] + name
        name = name.strip().capitalize()
        new.append(name)

    outrates = {}
    
    assert maxlength > 0 

    def mol_to_atoms(mol, cache = {}):
        if mol not in cache:
            cache[mol] = tuple([x for x in re.split(r"([A-Z][a-z]*)", mol) if len(x) > 0])
        return cache[mol] 

    def mol_len(mol):
        #return len(mol)
        return len(mol_to_atoms(mol))

    def mol_reverse(mol):
        rmol = ""
        for atom in mol_to_atoms(mol):
            rmol = atom + rmol
        return rmol

    def mol_order(mol):
        rmol = mol_reverse(mol)
        if mol < rmol:
            return mol
        else:
            return rmol

    def atoms_to_mol(atoms):
        mol = ""
        for atom in atoms:
            mol = mol + atom
        return mol


    while len(new) > 0:
        oldnew = new
        new = []
        #decomposition
        for z in oldnew:
            for i in range(1, mol_len(z)):
                if rng.random() < pbreak:
                    a = atoms_to_mol(mol_to_atoms(z)[:i])
                    b = atoms_to_mol(mol_to_atoms(z)[i:])

                    if not directed:
                        a = mol_order(a)
                        b = mol_order(b)

                    reaction = (OrderedFrozenBag((z,)), OrderedFrozenBag(sorted((a,b))))

                    if reaction not in outrates:
                        outrates[reaction] = get_sample(rates, rng)

                    if a not in molecules and a not in new:
                        new.append(a)
                    if b not in molecules and b not in new:
                        new.append(b)

        for a, b in itertools.chain(itertools.product(oldnew, molecules), combinations_with_replacement(oldnew, 2)):
            if maxlength is None or mol_len(a) + mol_len(b) <= maxlength:

                combinations = ((a,b), (b,a))
                if not directed:
                    combinations += ((mol_reverse(a), b), (a, mol_reverse(b)))

                for x,y in combinations:
                    if rng.random() < pform:
                        z = x + y

                        if not directed:
                            z = mol_order(z)

                        #because we may have revereed them, we have to order them again
                        if not directed:
                            reactants = OrderedFrozenBag(sorted((mol_order(x), mol_order(y))))
                        else:
                            reactants = OrderedFrozenBag(sorted((x, y)))
                        products = OrderedFrozenBag((z,))

                        reaction = (reactants, products)

                        if reaction not in outrates:
                            outrates[reaction] = get_sample(rates, rng)

                        if z not in molecules and z not in new:
                            new.append(z)
        for z in oldnew:
            molecules.append(z)

    return cls(outrates)