Nemo  2.3.46
How to add a trait?

Adding a trait to the framework is in fact pretty straightforward. You will have to derive the TraitPrototype class to build your prototype and the TTrait interface to specify what the trait is actually doing.

Why having these two classes? The TraitPrototype class is a SimComponent and is thus used to implement the trait's parameters. It is then used by the Metapop class (or its IndFactory base) at the simulation setup time to generate the Individual prototype. This Individual prototype is then cloned each time a new individual is needed in the population. The TraitPrototype implements a method called TraitPrototype::hatch() that creates (instantiates) a trait with all its variables set to the input (user defined) parameter values when a new individual is created.

The TTrait class encapsulates the way the trait behaves, that is how it is inherited, how it mutates, and how its phenotype is given by its genotype. However, the interface does not provide any structure to implement its precise genetic architecture leaving it completely to the implementer. Futhermore, as it is also a StorableComponent, the implementer must thus define the StorableComponent::store_data and StorableComponent::retrieve_data routines.

The way the genetic architecture of a trait has been implemented so far is rather simple. The current traits usually declare a double array as a private member, which type varies from trait to trait. For instance, the neutral markers are coded on unsigned char while the deleterious mutations are coded on char (in version 2.0.1) or bitstring (i.e. strings of bits coded on arrays of long int as in version 2.0.2), and the dispersal genes are double precision variables with values between 0 and 1.

Here is an example of a basic TTrait and TraitPrototype implementation. The MyTrait derived class implements a trait coded on a double array of unsigned char (unsigned char *_sequence[2]) as this trait is supposed to be diploid. The number of locus present in each individual will be given by a parameter called "mytrait_loci" present in the implementation of the trait prototype. The same is true for the number of alleles per locus and the mutation rate (parameters "mytrait_alleles" and "mytrait_mutation_rate"). In order to allow the trait prototype to transmit the parameters values to the traits, the MyTrait class must define the accessors functions to its private members _nb_locus, _nb_allele, and _mutation_rate (see the example below). It must also define the copy constructor, operator =, and operator == to copy these values when cloning itself or when the assignment operator is called, and to compare two of its instances. The two later operators are used when loading individuals from binary files, in which case the trait settings from the binary source must be compared to the current simulation settings (c.f. Metapop::loadPopFromBinarySource() for more details).

The mytrait.h file looks like this:

#include "ttrait.h"
//-------------------------------------------------
// MyTrait class declaration
//-------------------------------------------------
class MyTrait : public TTrait {
private:
//Variable declarations
unsigned int _nb_locus;
unsigned int _nb_allele;
double _mutation_rate;
//This is the gene sequence (diploid), declared as an array of unsigned char
unsigned char *_sequence[2];
public:
//Constructor declaration
MyTrait ( );
//Copy Constructor, MUST be present!!
MyTrait ( const MyTrait& TT );
//add variable accessors:
void set_nb_locus ( unsigned int val ) { _nb_locus = val; }
void set_nb_allele ( unsigned int val ) { _nb_allele = val; }
void set_mutation_rate ( double val ) { _mutation_rate = val; }
//TTrait implementation
//Init: allocate the sequence memory
virtual void init ( );
//Init_sequence: sets the sequence value at first generation and allocate if not already done
virtual void init_sequence ( );
// [skip the rest for now, check the source for the traits already implemented]
};
//-------------------------------------------------
// MyTraitProto class declaration
//-------------------------------------------------
class MyTraitProto : public TraitPrototype {
private:
//Variable declarations
unsigned int _nb_locus;
unsigned int _nb_allele;
double _mutation_rate;
public:
//Constructor declaration
MyTraitProto ( );
//Copy Constructor, MUST be present!!
MyTraitProto ( const MyTraitProto& TP );
//SimComponent implementation
virtual void setParameters ( );
virtual void loadFileServices ( FileServices* loader ) {} //left empty, we declare no file output
virtual void loadStatServices ( StatServices* loader ) {} //left empty, we declare no stat recorders
//TraitPrototype implementation
//hatch: create a new trait and sets its state according the prototype state
virtual MyTrait* hatch ( );
//clone: return a copy of itself
virtual MyTraitProto* clone ( ) { return new MyTraitProto((*this)); }
//get_type: return the trait's identifier
virtual trait_t get_type ( ) const { return "mytrait"; }
//StorageComponent implementation
virtual void store_data (BinaryStorageBuffer* saver) {} //nothing to store from the proto
virtual bool retrieve_data (BinaryStorageBuffer* reader) {} //nothing to retrieve
};

The mytrait.cpp file looks like this:

#include "mytrait.h"
//-------------------------------------------------
// MyTrait implementation
//-------------------------------------------------
//Constructor
MyTrait ( ) : _nb_locus(0), _nb_allele(0), _mutation_rate(0)
{ /*nothing else to do here*/ }
//Copy constructor
MyTrait ( const MyTrait& TT )
: _nb_locus(TT._nb_locus), _nb_allele(TT._nb_allele), _mutation_rate(TT._mutation_rate)
{ /*nothing else to do!!*/ }
//Sequence memory allocation
void MyTrait::init ( )
{
if(sequence != NULL) fatal("MyTrait::init::sequence is not NULL !\n");
sequence[0] = new unsigned char[_nb_locus];
sequence[1] = new unsigned char[_nb_locus];
}
//Sequence memory allocation and initialization
void MyTrait::init_sequence ( )
{
if(sequence == NULL) {
sequence[0] = new unsigned char[_nb_locus];
sequence[1] = new unsigned char[_nb_locus];
}
//sequence inititalization:
for(int i = 0; i < _nb_locus; i++)
sequence[0][i] = sequence[1][i] = 0;
}
//-------------------------------------------------
// MyTraitPrototype implementation
//-------------------------------------------------
//Constructor definition
MyTraitProto ( ) : _nb_locus(0), _nb_allele(0), _mutation_rate(0)
{
//use the SimComponent interface to build the set of parameters
//first, set the ParamSet name, if it is a mandatory component, and its reference:
set_paramset("mytraitname", false, this);
//and add the parameters to the ParamSet
add_parameter("mytrait_loci",INT,true,false,0,0, 0); //last 0 says there is no ParamUpdater
add_parameter("mytrait_alleles",INT,true,true,1,256, 0);
add_parameter("mytrait_mutation_rate",DBL,true,true,0,1, 0);
//note: we do not add parameter updaters for the trait, the
// framework do not support them for the traits for now.
}
//Copy constructor definition
MyTraitProto ( const MyTraitProto& TP )
: _nb_locus(TP._nb_locus), _nb_allele(TP._nb_allele), _mutation_rate(TP._mutation_rate)
{
//use the ParamSet copy constructor as well:
_paramSet = new ParamSet( *(TP._paramSet) ) ;
//note: _paramSet is a protected member of the SimComponent base class.
}
//Variables initialization:
void MyTraitProto::setParameters ( )
{
_nb_loci = (int)get_parameter_value( "mytrait_loci" );
_nb_alleles = (int)get_parameter_value( "mytrait_alleles" );
_mutation_rate = get_parameter_value( "mytrait_mutation_rate" );
//note that we don't have to make extra checks on
//the parameter values as they have been checked during the parameters setup
}
//Create a trait with the parameter values:
MyTrait* MyTraitProto::hatch ( )
{
MyTrait* new_trait = new MyTrait();
new_trait->set_nb_locus( _nb_locus );
new_trait->set_nb_alleles( _nb_alleles );
new_trait->set_mutation_rate( _mutation_rate );
return new_trait;
}

<<prev | –t o p– | next>>


Generated for Nemo v2.3.0 by  doxygen 1.8.8 --
Catalogued on GSR