Create a new interaction

φ-evo allows you to add a custom interaction that is not available in the default list.
To do so you need to write an interaction file.

To make make the explanation clearer, we will explain how to build a new interaction on a real example of a methylation interaction.

The methylation adds a methyl group to a species \(S\). The methylated species is denoted with a \({}^{*}\) symbol:

\[S \leftrightarrow S^{*}\]

We choose the simplest kinetics for this reaction:

\[\frac{d S^{*}}{d t} = -\frac{d S}{d t} = k_f S - k_b S^{*}\]

Let us start by creating the Methyl.py in a project directory.

Imports

Every interaction depends on the following φ-evo modules:

  • classes_eds2: for the core structure of the intaction
  • mutation: to handle mutation
  • deriv2: to explain how to generate the C code associated to the new mutation
# In Methyl.py

from phievo import __silent__,__verbose__
if __verbose__:
    print("Execute Methyl (Interaction Template)")

from phievo.Networks import mutation
from phievo.Networks import deriv2
from phievo.Networks import classes_eds2
import copy

Define a new type of species

Only methylable species can be methylated. For now φ-evo does not know how to create a methylable species and what are its characteristics. There should be a few line telling how to do it:

# In Methyl.py
mutation.species_types["Methylable"] = lambda random_generator:[
    ["Methylable"],
    ['Diffusible',mutation.sample_dictionary_ranges('Species.diffusion',random_generator)]
]
classes_eds2.Species.Tags_Species["Methylable"] = []

In the above lines, we tell φ-evo that a Methylable species has two characteristics:

  • Methylable: obviously
  • Diffusable: An extra characteristic is added to show how one would add a characteristics that comes with a parameter. A lambda function allows the program to generate new parameters when a new species is created.

Note: You can use the mutation.sample_dictionary_ranges to sample a random variable whose range has been define in dictionary_ranges in the init file. ### Set the default ranges for the parameters

# In Methyl.py

### Define the default dictionary_range
mutation.dictionary_ranges['Methyl.methyl'] = 0.0/(mutation.C*mutation.T)
mutation.dictionary_ranges['Methyl.demethyl'] = 0.0/mutation.T

Define the Methyl class

Every interaction in φ-evo inherits from the classes_eds2.Interaction:

# In Methyl.py
class Methyl(classes_eds2.Interaction):
    """
    Methylation interaction

    Args:
        Methyl.methyl(float): binding rate of a methyl group
        Methyl.demethyl(float): unbinding rate of a methyl group
        label(str): Methylation
        input(list): Type of the inputs
        output(list): Type of the outputs
    """
    def __init__(self,methyl=0,demethyl=0):
        classes_eds2.Node.__init__(self)
        self.methyl=methyl
        self.demethyl=demethyl
        self.label='Methylation'
        self.input=['Methylable']
        self.output=['Species']

    def __str__(self):
        """
        Used by the print function to display the interaction.
        """
        return "{0.id} Methylation: methyl. = {0.methyl:.2f}, demethyl = {0.demethyl:.2f}".format(self)

    def outputs_to_delete(self,net):
        """
        Returns the methylated form of the species to delete when the reaction is deleted.
        """
        return net.graph.successors(self)

The interaction’s methods are the following:

  • __init__: Creates the interaction object
  • __str__: Produces the string used by the print function
  • outputs_to_delete: Function that tells what are the species that were added to the network when the interaction was built and that need to be deleted when the interaction is removed.

Handling the mutation

The program needs five functions to tell φ-evo how to add the mutation via a mutation

number_Methyl

Evaluate the number of possible interactions of type Methyl that can be added to the network. This number is used to verify that the actual number of possible mutation found in random_Methyl is consistant with our intuition.

# In Methyl.py

def number_Methyl(self):
    """
    Returns the number of possible methylation in the current network.
    Note: this function is optional, it is used to check the consistency of
    the random_Methyl function.
    """
    n = self.number_nodes('Methylable')
    n_Methyl = self.number_nodes('Methyl')
    return n-n_Methyl

new_Methyl

This is the function that adds the Methyl interaction to the Network. It creates both a Methyl interaction and a methylated species.

# In Methyl.py
def new_Methyl(self,S,methyl,demethyl,parameters):
    """
    Creates a new :class:`Networks.Methyl.Methyl` and the species methylated for in the the network.

    Args:
        S: species to methylate
        methyl(float): binding rate of a methyl group
        demethyl(float): unbinding rate of a methyl group
        parameters(list): Parameters of the methylated species
    Returns:
        [methyl_inter,S_methyl]: returns a Methyl interaction and a methylated species.
    """

    S_methyl = classes_eds2.Species(parameters)
    meth_inter = Methyl(methyl,demethyl)
    assert meth_inter.check_grammar([S],[S_methyl]),"Error in grammar, new Methylation"
    self.add_Node(S_methyl)
    self.add_Node(meth_inter)
    self.graph.add_edge(S,meth_inter)
    self.graph.add_edge(meth_inter,S_methyl)
    return [meth_inter,S_methyl]

Note: Then function needs a list of characteristics for the methylated species created. It is provide via parameters.

new_random_Methyl

Wrapping of the new_Methyl function. It generates randomly the rate of the methylation and the parameters of the methylated species created.

# In Methyl.py

def new_random_Methyl(self,S):
    """
    Creates a methylation with random parameters.

    Args:
        S: Species to methylate
    Returns:
        [methyl_inter,S_methyl]:returns a Methyl interaction and a methylated species.
    """
    parameters = {}
    if S.isinstance("TF"):
        parameters['TF'] = self.Random.random()*2
    for tt in S.types:
        if tt not in ["TF","Methylable","Input","Output"]:
            parameters[tt] = [mutation.sample_dictionary_ranges('Species.{}'.format(attr),self.Random) for attr in S.Tags_Species[tt]]

    # Transform to fit phievo list structure
    parameters = [[kk]+val if val else [kk] for kk,val in parameters.items()]
    methyl = mutation.sample_dictionary_ranges('Methyl.methyl',self.Random)
    demethyl = mutation.sample_dictionary_ranges('Methyl.demethyl',self.Random)
    return self.new_Methyl(S,methyl,demethyl,parameters)

random_Methyl

Function called by the φ-evo to add a new Methylation interaction to the network during the evolution. It chooses a methylable species randomly and calls new_random_Methyl to add a methylation to this species.

# In Methyl.py

def random_Methyl(self):
    """
    Evaluates the species that can be phosphorilated, picks one an create a random
    methylation. The random mutation is made using :func:`new_random_Methyl <phievo.Networks.classes_eds2.new_random_Methyl>`.

    Returns:
        [methyl_inter,S_methyl]: returns a Methyl interaction and a methylated species.
    """
    try:
        list_methylable=self.dict_types["Methylable"]
    except KeyError:
        print("\tThe network contain no Methylacble species.")
        raise
    list_possible_methylable = []
    for S in list_methylable:
        if not self.check_existing_binary([S],"Methyl"):
            list_possible_methylable.append(S)
    n_possible = len(list_possible_methylable)
    assert n_possible==self.number_Methyl(),"The number of possible new methylation does not match its theoretical value."
    if n_possible==0:
        if __verbose__:
            print("No more possible methylation.")
        return None
    else:
        S = list_possible_methylable[int(self.Random.random()*n_possible)]
        return self.new_random_Methyl(S)

Methyl_deriv_inC

Function that generates the C code string of the interaction kinetics.

# In Methyl.py

def Methyl_deriv_inC(net):
    """
    Function called to generate the string corresponding to in a methylation in C.
    """
    func_str = "\n/************** Methylations *****************/\n"
    methylations = net.dict_types.get("Methyl",[])
    for methyl_inter in methylations:
        S = net.graph.predecessors(methyl_inter)[0]
        S_meth = net.graph.successors(methyl_inter)[0]
        f_rate = "{M.methyl}*{S.id}".format(M=methyl_inter,S=S)
        b_rate = "{M.demethyl}*{S_m.id}".format(M=methyl_inter,S_m=S_meth)

        func_str += deriv2.compute_leap([S.id],[S_meth.id],f_rate)
        func_str += deriv2.compute_leap([S_meth.id],[S.id],b_rate)
    return func_str

Bind the code to φ-evo

The last step is to add all the functions written previously to the default Mutable_Network.

# In Methyl.py
setattr(classes_eds2.Network,"number_Methyl",number_Methyl)
setattr(classes_eds2.Network,"new_Methyl",new_Methyl)
setattr(classes_eds2.Network,"new_random_Methyl",new_random_Methyl)
setattr(classes_eds2.Network,"random_Methyl",random_Methyl)
deriv2.interactions_deriv_inC["Methyl"] = Methyl_deriv_inC

You can download Methyl.py from φ-evo’s examples ### Edit the init file to load Methyl

The top of the init file should now be able to load the Methyl module with an import if the two files are in the same directory:

# In initialization.py
import Methyl

Now the new mutation settings are made similarly to any of the default interaction:

# In initialization.py

mutation.dictionary_ranges['Methyl.methyl'] = [0.1,1]
mutation.dictionary_ranges['Methyl.demethyl'] = [0.1,0.5]

dictionary_mutation['random_gene(\'Methylable\')'] = 0.1
dictionary_mutation['random_Interaction(\'Methyl\')']=0.1
dictionary_mutation['remove_Interaction(\'Methyl\')']=0.01
....