The AHA Model  Revision: 12809
Reference implementation 04 (HEDG02_04)
m_genome.f90
Go to the documentation of this file.
1 !> @file m_genome.f90
2 !! The Genome objects of the AHA Model.
3 !! @author Sergey Budaev <sergey.budaev@uib.no>
4 !! @author Jarl Giske <jarl.giske@uib.no>
5 !! @date 2016-2017
6 
7 !-------------------------------------------------------------------------------
8 ! $Id: m_genome.f90 6335 2017-11-15 14:37:02Z sbu062 $
9 !-------------------------------------------------------------------------------
10 
11 !-------------------------------------------------------------------------------
12 !> @brief Definition the genetic architecture of the agent
13 !> @section the_genome_module THE_GENOME module
14 !> This module defines the genetic architecture objects of the agent. See
15 !! @ref aha_buildblocks_genome "The genome structure" for an overview.
16 module the_genome
17 
18  use commondata !> @note we need global data in every module!
19 
20  !> @note We don't need all environmental objects definitions here!
21  !! But the individual genome `INDIVIDUAL_GENOME` is an extension
22  !! of `SPATIAL_MOVING`.
24 
25  implicit none
26 
27  character (len=*), parameter, private :: modname = "(THE_GENOME)"
28 
29  !-----------------------------------------------------------------------------
30  !> @brief This describes an individual gene object. See
31  !! @ref aha_buildblocks_genome "the genome structure" for as general
32  !! description and @ref aha_buildblocks_genome_gene "gene" for
33  !! details.
34  type, public :: gene
35  !> sets a descriptive label of the allele, e.g its role and purpose
36  character(len=LABEL_LENGTH) :: allele_label
37  !> @brief Sets the value of the allele that is stored and evolved.
38  !! @note In the new version allele values are *INTEGER*
39  !! rather than *REAL*. Integer genome is not affected by the CPU
40  !! precision and does not suffer from FPU rounding errors. This is
41  !! what is expected from the genome: genes should be atomic, fixed,
42  !! and never subject to any uncontrollable fluctuations and drift.
43  !! Otherwise no "inheritance" is guaranteed. Only controlled
44  !! mutations are allowed. Integer calculations will also have higher
45  !! calculation speed and may hopefully avoid IEEE float point errors
46  !! (overflow/underflow). Also, we may in future use more realistic
47  !! limited-range allele functions to mimic real DNA structure.
48  !! If we have sufficiently large range of possible allele values,
49  !! e.g. 1:10000 and integer-to-real conversion function for
50  !! converting these true integer allele values to real values
51  !! within 0.:1. in the gamma neural response function, this would
52  !! not have a much different effect compared with the old
53  !! real-value gene implementation.
54  integer, dimension(ADDITIVE_COMPS) :: allele_value
55  !> sets if the allele is dominant
56  logical :: dominant
57  !> sets the multiplicative dominance weight
58  real(srp) :: dominance_weight
59  !> rank_id of the gene, needed for sorting alleles within the chromosome
60  integer :: rank_id
61  contains
62  !> init alleles with random values, labels not set here,
63  !! use this function for startup initialisations of random agents
64  !! See `the_genome::allele_init_random()`
65  procedure, public :: init_allele => allele_init_random
66  !> create empty zero allele object, should be used for offspring inits
67  !! as we do not need to init them with random values, they will get
68  !! them from the parents using inherit function set
69  !! See `the_genome::allele_create_zero()`
70  procedure, public :: create_allele => allele_create_zero
71  !> init label alleles random
72  !! See `the_genome::allele_label_init_random()`
73  procedure, public :: label_random => allele_label_init_random
74  !> set labels for the allele
75  !! See `the_genome::allele_label_set()`
76  procedure, public :: labels => allele_label_set
77  !> get the allele label
78  !! See `the_genome::allele_label_get()`
79  procedure, public :: label_get => allele_label_get
80  !> set individual value of allele
81  !! See `the_genome::allele_value_set()`
82  procedure, public :: set => allele_value_set
83  !> set the vector of additive allele components
84  !! See `the_genome::alleles_value_vector_set()`
85  procedure, public :: set_vector => alleles_value_vector_set
86  !> get the value of the allele
87  !! See `the_genome::allele_value_get()`
88  procedure, public :: get => allele_value_get
89  !> get the vector of additive allele components
90  !! See `the_genome::allele_values_vector_get()`
91  procedure, public :: get_vector => allele_values_vector_get
92  !> set rank_id for the allele
93  !! See `the_genome::allele_rank_id_set()`
94  procedure, public :: rank => allele_rank_id_set
95  !> Introduce a random point mutation to one (random) of the alleles
96  !! See `the_genome::allele_mutate_random()`
97  procedure, public :: mutate_point => allele_mutate_random
98  !> Introduce random mutations to the whole allele components set
99  !! See `the_genome::allele_mutate_random_batch()`
100  procedure, public :: mutate_set => allele_mutate_random_batch
101  end type gene
102 
103  !-----------------------------------------------------------------------------
104  !> This type describes the chromosome object.
105  !! Chromosome consists of an array of alleles and a descriptive
106  !! string label. See
107  !! @ref aha_buildblocks_genome "\"the genome structure\"" for as general
108  !! description and @ref aha_buildblocks_genome_chromosome
109  !! "\"chromosome\"" for details.
110  type, public :: chromosome
111  !> chromosome label
112  character(len=LABEL_LENGTH) :: chromosome_label
113  !> chromosome length, i.e. N of alleles here
114  integer :: clength
115  !> array of alleles of the size `clength`
116  type(gene), allocatable, dimension(:) :: allele
117  contains
118  !> This subroutine initialises the chromosome with, and allocates, random
119  !! alleles, sets one of them randomly dominant and optionally defines the
120  !! chromosome label.
121  !! See `the_genome::chromosome_init_allocate_random()`
122  procedure, public :: init_chromosome => chromosome_init_allocate_random
123  !> Init a new chromosome, zero, non-random.
124  !! See `the_genome::chromosome_create_allocate_zero()`
125  procedure, public :: create_chromosome => chromosome_create_allocate_zero
126  !> This subroutine recalculates rank_id indices for consecutive gene objects
127  !! within the chromosome. This may be necessary after reordering by random
128  !! relocation mutation.
129  !! See `the_genome::chromosome_recalculate_rank_ids()`
130  procedure, public :: recalc_rank_id => chromosome_recalculate_rank_ids
131  !> mutate within the same chromosome, relocate a gene (unit of alleles) to a
132  !! different random position within the same chromosome, the misplaced gene
133  !! moves to the relocated gene position, so they are just swap.
134  !! See `the_genome::chromosome_mutate_relocate_swap_random()`
135  procedure, public :: mutate_swap => chromosome_mutate_relocate_swap_random
136  !> Mutate within the same chromosome, relocate a gene (unit of alleles) to a
137  !! different random position within the same chromosome, shifting all other
138  !! genes within the chromosome down one position. This works as
139  !! follows: first, we randomly determine the gene to relocate, assign it
140  !! a new random rank_id. Then re-sort the chromosome according to the new
141  !! ranks with `qsort` with the `qs_partition_rank_id` backend.
142  !! See `the_genome::chromosome_mutate_relocate_shift_random()`
143  procedure, public :: mutate_shift => chromosome_mutate_relocate_shift_random
144  !> Sort GENE objects within the CHROMOSOME by their rank_id
145  !! The two subroutines below are a variant of the recursive quick-sort
146  !! algorithm adapted for sorting integer components of the the `CHROMOSOME`
147  !! object.
148  !! See `the_genome::chromosome_sort_rank_id()`
149  procedure, public :: sort_rank_id => chromosome_sort_rank_id
150  end type chromosome
151 
152  !-----------------------------------------------------------------------------
153  !> This type describes parameters of the individual agent's genome
154  !! The genome is an array of allocatable the_genome::chromosome objects,
155  !! different kinds of agents may have different genomes with
156  !! different number of chromosomes. See
157  !! @ref aha_buildblocks_genome "\"the genome structure\"" for as general
158  !! description and @ref aha_buildblocks_genome_genome "\"genome\"" for
159  !! details.
160  type, public, extends(spatial_moving) :: individual_genome
161  !> label for the genome
162  character(len=LABEL_LENGTH) :: genome_label
163  !> the size of the genome, i.e. N of chromosomes = N_CHROMOSOMES
164  !! in this version it is constant, can implement variable genomes later.
165  integer :: genome_size = n_chromosomes
166  !> array of chromosome objects, the two dimensions refer to
167  !! (1) chromosome number in the genome and (2) the number of homologs
168  !! (1:2 for diploid), so chromosome is a 2D array.
169  type(chromosome), allocatable, dimension(:,:) :: chromosome
170  !> The sex of the individual: is male = TRUE or female = FALSE
171  !! this is the main sex identifier. The sex_label defined below
172  !! should only be used for outputs and similar purposes.
173  logical :: sex_is_male
174  !> Verbal label for sex ("male" or "female").
175  character(len=LABEL_LENGTH) :: sex_label
176  !> Flag the agent is alive (TRUE) or dead (False).
177  logical :: alive
178  contains
179  !> Initialise the genome at random, and set sex as determined by the sex
180  !! determination locus.
181  !! See `the_genome::genome_init_random()`
182  procedure, public :: init_genome => genome_init_random
183  !> Create a new empty genome, and set sex as determined by the sex
184  !! determination locus. Genome values are from parents using inherit
185  !! functions.
186  !! See `the_genome::genome_create_zero()`
187  procedure, public :: create_genome => genome_create_zero
188  !> Label genome. If label is not provided, make a random string.
189  !! @note TMP NOTE: label setting removed from the create function
190  !! as it will now override create for `SPATIAL_MOVING`.
191  !! See `the_genome::genome_label_set()`
192  procedure, public :: label => genome_label_set
193  !> Accessor function to get the genome label. The label is a kind of a
194  !! (random) text string name of the genome and the individual agent.
195  !! @note We especially need this accessor function because the genome (and
196  !! individual) name is used in other modules for file names ids etc.
197  !! See `the_genome::genome_label_get()`
198  procedure, public :: individ_label => genome_label_get
199 
200  !> Sex has a separate status from all other genetically determined traits.
201  !! It is initialised here, at the genotype level of the class hierarchy.
202  !! See `the_genome::genome_sex_determine_init()`
203  procedure, public :: sex_init => genome_sex_determine_init ! init sex
204  !> Get the logical sex ID of the genome object component.
205  !! See `the_genome::genome_get_sex_is_male()`
206  procedure, public :: is_male => genome_get_sex_is_male
207  !> Get the logical sex ID of the genome object component.
208  !! See `the_genome::genome_get_sex_is_female()`
209  procedure, public :: is_female => genome_get_sex_is_female
210  !> Get the descriptive sex label: male or female.
211  !! See `the_genome::genome_get_sex_label()`
212  procedure, public :: label_sex => genome_get_sex_label
213 
214  !> Init a trait from the genotype, trait can be any object in any of
215  !! the up level class hierarchy that is determined from the boolean
216  !! genotype x phenotype matrix.
217  !! See `the_genome::trait_init_genotype_gamma2gene()`
218  procedure, public :: trait_init => trait_init_genotype_gamma2gene
219  !> Set an **individual trait** of the agent that depends on the
220  !! genotype. This can be any trait upwards in the class hierarchy.
221  !! See `the_genome::trait_set_genotype_gamma2gene()`
222  procedure, public :: trait_set => trait_set_genotype_gamma2gene
223  !> Generic interface to the neuronal response function.
224  !! See `the_genome::trait_init_genotype_gamma2gene()` and
225  !! `the_genome::trait_set_genotype_gamma2gene()`.
226  generic, public :: neuro_resp => trait_init, trait_set
227 
228  !> Init a trait from the genotype, trait can be any object in any of
229  !! the up level class hierarchy that is determined from the boolean
230  !! genotype x phenotype matrix. Note that this method is based on
231  !! simple linear rescale rather than neuronal response.
232  !! See `the_genome::trait_init_linear_sum_additive_comps_2genes_r()`
233  procedure, public :: trait_init_linear => &
235  !> Set an **individual trait** of the agent that depends on the
236  !! genotype. This can be any trait upwards in the class hierarchy.
237  !! Note that this method is based on simple linear rescale rather than
238  !! neuronal response.
239  !! See `the_genome::trait_set_linear_sum_additive_comps_2genes_r()`
240  procedure, public :: trait_set_linear => &
242  !> Generic interface to the simple linear genotype to phenotype
243  !! transformation functions. See
244  !! `the_genome::trait_init_linear_sum_additive_comps_2genes_r()` and
245  !! `the_genome::trait_set_linear_sum_additive_comps_2genes_r()`.
246  generic, public :: linear_g2p => trait_init_linear, trait_set_linear
247 
248  !> Set the individual to be alive, normally this function is used after
249  !! init or birth.
250  !! See `the_genome::genome_individual_set_alive()`
251  procedure, public :: lives => genome_individual_set_alive
252  !> Set the individual to be **dead**. Note that this function does not
253  !! deallocate the individual agent object, this may be a separate
254  !! destructor function.
255  !! The `dies` method is implemented at the following levels
256  !! of the agent object hierarchy (upper overrides the lower level):
257  !! - the_genome::individual_genome::dies();
258  !! - the_neurobio::appraisal::dies();
259  !! - the_neurobio::gos_global::dies();
260  !! - the_individual::individual_agent::dies().
261  !! .
262  !! See `the_genome::genome_individual_set_dead()`
263  procedure, public :: dies => genome_individual_set_dead
264  !> Set the individual to be **dead**. Note that in this class this method
265  !! implementation points to the same procedure as
266  !! `the_genome::individual_genome::dies(). However the `dies` method is
267  !! overriden upwards in the class hierarchy to also nullify
268  !! neurobiological and behavioural objects. So this method should be
269  !! only called in procedures that specifically implemented override of
270  !! the `dies` method:
271  !! - the_neurobio::gos_global::dies()
272  !! - the_individual::individual_agent::dies()
273  !! .
274  !! See `the_genome::genome_individual_set_dead()`
275  procedure, public :: set_dead => genome_individual_set_dead
276  !> Check if the individual is alive.
277  !! See `the_genome::genome_individual_check_alive()`
278  procedure, public :: is_alive => genome_individual_check_alive
279  !> Check if the individual is dead (the opposite of `is_alive`).
280  !! See `the_genome::genome_individual_check_dead()`
281  procedure, public :: is_dead => genome_individual_check_dead
282 
283  !> Internal **genetic recombination backend**, exchange individual alleles
284  !! between homologous chromosomes in mother and father genomes to form
285  !! the `this` (offspring) genome. Fully random recombination. See
286  !! `the_genome::genome_individual_recombine_homol_full_rand_alleles()`.
287  procedure, public :: recombine_random => &
289  !> Internal genetic recombination backend, exchange individual alleles
290  !! between homologous chromosomes in mother and father genomes to form
291  !! the `this` (offspring) genome. Partially random recombination. See
292  !! `the_genome::genome_individual_recombine_homol_part_rand_alleles()`.
293  procedure, public :: recombine_partial => &
295  !> Internal **fixed genetic crossover** backend, exchange blocks of
296  !! alleles between homologous chromosomes in mother and father genomes
297  !! to form the `this` (offspring) genome.
298  !! See `the_genome::genome_individual_crossover_homol_fix()`.
299  procedure, public :: crossover => &
301 
302  !> Perform a probabilistic random mutation(s) on the individual genome.
303  !! This is a high level wrapper to build mutations from various
304  !! components. See `the_genome::genome_mutate_wrapper()`.
305  procedure, public :: mutate => genome_mutate_wrapper
306  end type individual_genome
307 
308  ! qsort and partition are only for sorting genetic objects within this
309  ! module, so they are private. We also don't use real procedure names, refer
310  ! them by their intarface names (left of =>).
311  ! private :: genome_init_random
312 
313 contains ! ........ implementation of procedures for this level ................
314 
315  !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
316  ! General sorting functions for THE_GENOME module objects
317  !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
318 
319  !> Sort GENE objects within the CHROMOSOME by their `rank_id`.
320  !! The two subroutines `qsort` and `qs_partition_rank_id` are a variant of
321  !! the recursive quick-sort algorithm adapted for sorting integer components
322  !! of the the `CHROMOSOME` object.
323  elemental subroutine chromosome_sort_rank_id(this)
324  class(chromosome), intent(inout) :: this
325 
326  call qsort(this%allele) ! This is the array component we sort.
327 
328  contains
329 
330  !...........................................................................
331  !> `qsort` and `qs_partition_` are the two parts of the recursive sort
332  !! algorithm `qsort` is the recursive frontend. Sorts genes within the
333  !! chromosome by components of the array of `allele`s.
334  recursive pure subroutine qsort(A)
335 
336  !> @param[inout] input array to be sorted. It has the same type as
337  !! the individual component objects of the array-object
338  !! that we are going to sort.
339  type(gene), intent(in out), dimension(:) :: a
340  integer :: iq
341 
342  if(size(a) > 1) then
343  call qs_partition_rank_id(a, iq)
344  call qsort(a(:iq-1))
345  call qsort(a(iq:))
346  endif
347 
348  end subroutine qsort
349 
350  !...........................................................................
351  !> `qsort` and `qs_partition_` are the two parts of the recursive sort
352  !! algorithm `qs_partition_rank_id` is a pivot backend, here it sorts
353  !! genes within the chromosome object by integer `rank_id` components of
354  !! the genes.
355  pure subroutine qs_partition_rank_id(A, marker)
356 
357  !> @param[inout] input array to be sorted.
358  type(gene), intent(in out), dimension(:) :: a
359  !> @param[out] internal pivot marker.
360  integer, intent(out) :: marker
361 
362  integer :: i, j
363  type(gene) :: temp
364  !> @note Pivot point `x`, has the same type **as
365  !! the sorted object component**.
366  integer :: x
367 
368  ! We sort GENE objects within the CHROMOSOME by their `rank_id`
369  ! components (hardwired).
370  x = a(1)%rank_id
371  i= 0
372  j= size(a) + 1
373 
374  do
375  j = j-1
376  do
377  if (a(j)%rank_id <= x) exit
378  j = j-1
379  end do
380  i = i+1
381  do
382  if (a(i)%rank_id >= x) exit
383  i = i+1
384  end do
385  if (i < j) then
386  ! exchange A(i) and A(j)
387  temp = a(i)
388  a(i) = a(j)
389  a(j) = temp
390  elseif (i == j) then
391  marker = i+1
392  return
393  else
394  marker = i
395  return
396  endif
397  end do
398 
399  end subroutine qs_partition_rank_id
400 
401  end subroutine chromosome_sort_rank_id
402 
403  !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
404  ! Functions linked with the object GENE
405  !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
406 
407  !-----------------------------------------------------------------------------
408  !> Initialises allele with a random integer. Note that we do **not** set
409  !! the labels for the alleles here during the random initialisation.
410  subroutine allele_init_random(this)
411  class(gene), intent(inout) :: this
412 
413  !> The allele components are initialised by a random integers within
414  !! the range `ALLELERANGE_MIN` and `ALLELERANGE_MAX` parameetr values.
415  call rand_array(this%allele_value, allelerange_min, allelerange_max)
416 
417  ! create random labels for the allele initially (disabled)
418  ! call this%label_random()
419 
420  end subroutine allele_init_random
421 
422  !-----------------------------------------------------------------------------
423  !> Create allele with zero value. We don't set labels for alleles here
424  elemental subroutine allele_create_zero(this)
425  class(gene), intent(inout) :: this
426 
427  !> @note Note that there is no need to allocate `allele_value` array
428  !! as it has fixed shape.
429  this%allele_value = 0
430 
431  end subroutine allele_create_zero
432 
433  !-----------------------------------------------------------------------------
434  !> The (pair of) alleles here are assigned random string labels
435  !! Not sure if that is necessary for any application though
436  subroutine allele_label_init_random(this)
437  class(gene), intent(inout) :: this
438 
439  this%allele_label = rand_string(label_length,label_cst,label_cen)
440 
441  end subroutine allele_label_init_random
442 
443  !-----------------------------------------------------------------------------
444  !> Set labels for the alleles. The subroutine parameter is array of labels
445  elemental subroutine allele_label_set(this, label)
446  class(gene), intent(inout) :: this
447  !> @param[in] label, provides an array of labels to set for the allele.
448  character(len=*), intent(in) :: label
449 
450  this%allele_label = label
451 
452  end subroutine allele_label_set
453 
454  !-----------------------------------------------------------------------------
455  !> Get the i-th allele label.
456  elemental function allele_label_get(this) result(label)
457  class(gene), intent(in) :: this
458  !> @returns Returns the label of the allele.
459  character(len=LABEL_LENGTH) :: label
460 
461  label = this%allele_label
462 
463  end function allele_label_get
464 
465  !-----------------------------------------------------------------------------
466  !> Set a single value of the allele additive component.
467  elemental subroutine allele_value_set(this, set_value, nr)
468  class(gene), intent(inout) :: this
469  !> @param[in] value, provides the value to set for the allele and the
470  !! allele number.
471  integer, intent(in) :: set_value
472 
473  !> @param[in] number, provides the number of the allele component to set.
474  integer, intent(in) :: nr
475 
476  this%allele_value(nr) = set_value
477 
478  end subroutine allele_value_set
479 
480  !-----------------------------------------------------------------------------
481  !> Set values of the alleles as a vector, i.e. sets the whole gene values.
482  pure subroutine alleles_value_vector_set(this, values)
483  class(gene), intent(inout) :: this
484  !> @param[in] values, provides vector of values to set for the alleles.
485  integer, dimension(ADDITIVE_COMPS), intent(in) :: values
486 
487  this%allele_value = values
488 
489  end subroutine alleles_value_vector_set
490 
491  !-----------------------------------------------------------------------------
492  !> Get the value of the allele
493  elemental function allele_value_get(this, nr) result(avalue)
494  class(gene), intent(in) :: this
495 
496  !> @param[in] number, provides the number of the allele component to set.
497  integer, intent(in) :: nr
498 
499  !> @returns Returns the value of the `nr`'s allele.
500  integer :: avalue
501 
502  avalue = this%allele_value(nr)
503 
504  end function allele_value_get
505 
506  !-----------------------------------------------------------------------------
507  !> Get the vector of all values of the alleles, i.e. gets the gene values.
508  pure subroutine allele_values_vector_get(this, values)
509  class(gene), intent(in) :: this
510  !> @param[out] values, Gets the vector of the values for the alleles.
511  integer, dimension(ADDITIVE_COMPS), intent(out) :: values
512 
513  values = this%allele_value
514 
515  end subroutine allele_values_vector_get
516 
517  !-----------------------------------------------------------------------------
518  elemental subroutine allele_rank_id_set(this, value_id)
519  class(gene), intent(inout) :: this
520 
521  !> @param rank_id, set this value to the allele `rank_id`.
522  integer, intent(in) :: value_id
523 
524  this%rank_id = value_id
525 
526  end subroutine allele_rank_id_set
527 
528  !-----------------------------------------------------------------------------
529  !> Introduce a random point mutation to a random allele component.
530  subroutine allele_mutate_random(this, prob)
531  class(gene), intent(inout) :: this
532  !> @param[in] prob optional probability of mutation, if absent, the default
533  !! value commondata::mutationrate_point is used.
534  real(SRP), optional, intent(in) :: prob
535 
536  ! Local copies of optionals.
537  real(SRP) :: prob_mut
538 
539  integer :: this_allele_mutates
540 
541  ! Check optional probability of mutation.
542  if (present(prob)) then
543  prob_mut = prob
544  else
545  prob_mut = mutationrate_point
546  end if
547 
548  !> ### Implementation details ###
549  !> Do mutate if a random value is smaller than the commondata::mutationrate
550  !! parameter constant.
551  if (rand_r4() < prob_mut) then
552  !> First, determine which of the alleles components gets mutation.
553  this_allele_mutates = rand_i(1, additive_comps)
554  !> Second, change the value of this allele component to an random integer.
555  call this%set(rand_i(allelerange_min,allelerange_max),this_allele_mutates)
556  end if
557 
558  end subroutine allele_mutate_random
559 
560  !-----------------------------------------------------------------------------
561  !> Introduce a random mutation of the whole set of additive allele components.
562  subroutine allele_mutate_random_batch(this, prob)
563  class(gene), intent(inout) :: this
564  !> @param[in] prob optional probability of mutation, if absent, the default
565  !! value commondata::mutationrate_batch is used.
566  real(SRP), optional, intent(in) :: prob
567 
568  ! Local copies of optionals.
569  real(SRP) :: prob_mut
570 
571  ! Check optional probability of mutation.
572  if (present(prob)) then
573  prob_mut = prob
574  else
575  prob_mut = mutationrate_batch
576  end if
577 
578  if (rand_r4() < prob_mut) then
579  !> ### Implementation details ###
580  !> This mutation just re-init the whole allele set as random.
581  call this%init_allele()
582  end if
583 
584  end subroutine allele_mutate_random_batch
585 
586  !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
587  ! Functions linked with the object CHROMOSOME
588  !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
589 
590  !-----------------------------------------------------------------------------
591  !> This subroutine initialises the chromosome with, and allocates, random
592  !! alleles, sets one of them randomly dominant and optionally defines the
593  !! chromosome label.
594  !! @param[in] length, sets the length of the chromosome object, N of alleles.
595  !! @param[in] label, sets the label for the chromosome object, optional.
596  subroutine chromosome_init_allocate_random(this, length, label)
597  class(chromosome), intent(inout) :: this
598  ! @param[in] length, sets the length of the chromosome object, N of alleles.
599  integer, intent(in) :: length
600  ! @param[in] label, sets the label for the chromosome object, optional.
601  character(len=*), optional, intent(in) :: label
602 
603  ! local cycle counters
604  integer :: i
605 
606  !> ### Implementation details ###
607  !> We set the chromosome label if such a parameter is provided, or
608  !! a random string if not.
609  if (present(label)) then
610  this%chromosome_label = label
611  else
612  ! label chromosome with a random string here
613  this%chromosome_label = rand_string(label_length,label_cst,label_cen)
614  end if
615 
616  !> First, set the chromosome length using the procedure parameter `length`.
617  this%clength = length
618 
619  !> Then, allocate the array of the allele objects with this length.
620  if (.not. allocated(this%allele)) allocate(this%allele(length))
621 
622  !> Initialise all the alleles within this chromosome.
623  do i=1, length
624  !> Specifically, initialise the allele.
625  call this%allele(i)%init_allele()
626  !> Set initial rank_id ID of the allele.
627  call this%allele(i)%rank(i)
628  !> Finally, set the label for the alleles within this chromosome.
629  !! Labels can be set random using this function (disabled):
630  !! @code
631  !! call this%allele(i)%label_random()
632  !! @endcode
633  !! But in this implementation we construct the label for the allele from
634  !! the *chromosome label* and the *allele number*;
635  call this%allele(i)%labels( label( 1:len_trim(label)- &
636  max(0, len_trim(label)+ &
637  len(tostr(length))-label_length)-1 &
638  ) // "_" // tostr(i,length) &
639  )
640  !> Long chromosome labels are trimmed at right to fit the allele number.
641  end do
642 
643  end subroutine chromosome_init_allocate_random
644 
645  !-----------------------------------------------------------------------------
646  !> Init a new chromosome, zero, non-random.
647  subroutine chromosome_create_allocate_zero(this, length, label)
648  class(chromosome), intent(inout) :: this
649  ! @param[in] length, sets the length of the chromosome object,N of alleles.
650  integer, intent(in) :: length
651  ! @param[in] label, sets the label for the chromosome object, optional.
652  character(len=*), optional, intent(in) :: label
653 
654  ! local cycle counters
655  integer :: i
656 
657  !> ### Implementation details ###
658  !> Set the chromosome label if provided as parameter, or random string
659  !! if not.
660  if (present(label)) then
661  this%chromosome_label = label
662  else
663  ! label chromosome with a random string here
664  this%chromosome_label = rand_string(label_length,label_cst,label_cen)
665  end if
666 
667  !> First, set the chromosome length using the procedure parameter `length`.
668  this%clength = length
669 
670  !> Then, we allocate the array of the allele objects with this length.
671  if (.not. allocated(this%allele)) allocate(this%allele(length))
672 
673  !> Initialise all the alleles within this chromosome.
674  !! @note Parallel `do concurrent` construct is used here.
675  ! @warning The `do concurrent` construct is F2008 and can not (yet) be
676  ! implemented in all compilers. Use normal `do` in such a case.
677  do concurrent( i=1:length )
678  ! initialise the allele with zeros
679  call this%allele(i)%create_allele()
680  ! set initial rank_id ID of the allele
681  this%allele(i)%rank_id = i
682  ! make labels for the alleles within this chromosome.
683  ! 1. we can set random labels for the alleles using this
684  ! function below:
685  !call this%allele(i)%label_random()
686  ! 2. or construct the vector of labels for the alleles from the chromosome
687  ! label and allele number:
688  call this%allele(i)%labels( trim(label) // tostr(i,length) )
689  end do
690 
691  end subroutine chromosome_create_allocate_zero
692 
693  !-----------------------------------------------------------------------------
694  !> This subroutine recalculates rank_id indices for consecutive gene objects
695  !! within the chromosome. This may be necessary after reordering by random
696  !! relocation mutation.
697  elemental subroutine chromosome_recalculate_rank_ids(this)
698  class(chromosome), intent(inout) :: this
699  integer :: i
700 
701  ! @warning The `do concurrent` construct is F2008 and can not (yet) be
702  ! implemented in all compilers. Use normal `do` in such a case.
703  do concurrent( i = 1:this%clength )
704  ! Reset rank_id ID of each.
705  this%allele(i)%rank_id = i
706  end do
707 
708  end subroutine chromosome_recalculate_rank_ids
709 
710  !-----------------------------------------------------------------------------
711  !> Mutate within the same chromosome, relocate a gene (unit of alleles) to a
712  !! different random position within the same chromosome, the misplaced gene
713  !! moves to the relocated gene position, so they are just **swap**.
715  class(chromosome), intent(inout) :: this
716  !> @param[in] prob optional probability of mutation, if absent, the default
717  !! value commondata::relocation_swap_rate is used.
718  real(SRP), optional, intent(in) :: prob
719  ! Local copies of optionals.
720  real(SRP) :: prob_mut
721 
722  type(gene) :: temp_shift
723  integer :: gene_move, gene_swap
724 
725  ! Check optional probability of mutation.
726  if (present(prob)) then
727  prob_mut = prob
728  else
729  prob_mut = relocation_swap_rate
730  end if
731 
732  !> ### Implementation details ###
733  !> Do mutate if a random value is smaller than the
734  !! commondata::relocation_swap_rate parameter constant value.
735  if (rand_r4() < prob_mut) then
736 
737  !> If yes, randomly determine the gene (`gene_move`) that initiates
738  !! the mutation move within the chromosome.
739  gene_move = rand_i(1,this%clength)
740 
741  !> Randomly determine the gene that will be swapped with the `gene_move`.
742  gene_swap = rand_i(1,this%clength)
743 
744  !> Then, cycle through the alleles and select new random allele if
745  !! it happens to coincide with `gene_move`.
746  do while (gene_swap == gene_move)
747  gene_swap = rand_i(1,this%clength)
748  end do
749 
750  !> After this, do the gene swap, and gene rank_id ID swap.
751  temp_shift = this%allele(gene_move)
752 
753  ! swap objects
754  this%allele(gene_move) = this%allele(gene_swap)
755  this%allele(gene_swap) = temp_shift
756 
757  ! and swap their rank_id's
758  this%allele(gene_move)%rank_id = gene_move
759  this%allele(gene_swap)%rank_id = gene_swap
760 
761  end if
762 
764 
765  !-----------------------------------------------------------------------------
766  !> Mutate within the same chromosome, relocate a gene (unit of alleles) to a
767  !! different random position within the same chromosome, **shifting** all
768  !! other genes within the chromosome down one position. This works as
769  !! follows: first, we randomly determine the gene to relocate, assign it
770  !! a new random rank_id. Then re-sort the chromosome according to the new
771  !! ranks with `qsort` with the `qs_partition_rank_id` backend.
773  class(chromosome), intent(inout) :: this
774  !> @param[in] prob optional probability of mutation, if absent, the default
775  !! value commondata::relocation_shift_rate is used.
776  real(SRP), optional, intent(in) :: prob
777  ! Local copies of optionals.
778  real(SRP) :: prob_mut
779 
780  integer :: gene_move, gene_moveto
781 
782  ! Check optional probability of mutation.
783  if (present(prob)) then
784  prob_mut = prob
785  else
786  prob_mut = relocation_shift_rate
787  end if
788 
789  !> ### Implementation details ###
790  !> Do mutate if a random value is smaller than the
791  !! commondata::relocation_shift_rate parameter constant value.
792  if (rand_r4() < prob_mut) then
793 
794  !> If yes, randomly determine the gene that initiates move within
795  !! the chromosome.
796  gene_move = rand_i(1,this%clength)
797 
798  !> Randomly determine the new position of this gene.
799  gene_moveto = rand_i(1,this%clength)
800 
801  !> Then, cycle through alleles and select new random allele if
802  !! it happens to coincide with `gene_move`.
803  do while (gene_moveto == gene_move)
804  gene_moveto = rand_i(1,this%clength)
805  end do
806 
807  !> After the cycle, adjust rank_id's of the alleles.
808  this%allele(gene_move)%rank_id = gene_moveto
809 
810  !> Then re-sort the allele objects by their updated rank_id's.
811  call this%sort_rank_id()
812 
813  !> Finally, recalculate rank_id's so they are again ordered.
814  call this%recalc_rank_id()
815 
816  end if
817 
819 
820  !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
821  ! Functions linked with the object INDIVIDUAL_GENOME
822  !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
823 
824  !-----------------------------------------------------------------------------
825  !> Initialise the genome at random, and set sex as determined by the sex
826  !! determination locus.
827  subroutine genome_init_random(this, label)
828  class(individual_genome), intent(inout) :: this
829  !! @param[in] label, set an optional label of the genome, if unset,
830  !! generate random string.
831  character(len=*), optional, intent(in) :: label
832 
833  ! Local variables
834  integer :: i,j
835 
836  !> ### Implementation details ###
837  !> First, create spatial moving object component of the individual genome.
838  !! But we do not yet position the genome object.
839  call this%create()
840 
841  !> Allocate the genome object, it must have `genome_size` chromosomes and
842  !! `CHROMOSOME_PLOIDY` homologs.
843  if (.not. allocated(this%chromosome)) &
844  allocate(this%chromosome(this%genome_size,chromosome_ploidy))
845 
846  !> Now cycle over all the chromosomes and homologs and initialise each
847  !! of them.
848  do j=1, chromosome_ploidy
849  do i=1, this%genome_size
850  call this%chromosome(i,j)%init_chromosome( len_chromosomes(i), &
851  lab_chromosomes(i) )
852  end do
853  end do
854 
855  !> On exit from the cycle, set the genome label if provided, or random
856  !! string if not.
857  if (present(label)) then
858  call this%label( label )
859  else
860  ! label the genome with a random string here
861  call this%label ()
862  end if
863 
864  !> Then, determine the sex of the genome by the genome sex
865  !! determination locus taking into account the sex ratio.
866  call this%sex_init()
867 
868  end subroutine genome_init_random
869 
870  !-----------------------------------------------------------------------------
871  !> Create a new empty genome, and set sex as determined by the sex
872  !! determination locus. Genome values are from parents using inherit
873  !! functions.
874  subroutine genome_create_zero(this)
875  class(individual_genome), intent(inout) :: this
876 
877  ! Local variables
878  integer :: i,j
879 
880  !> ### Implementation details ###
881  !> First of all, Create spatial moving object component of the
882  !! individual genome.
883  call this%create()
884 
885  !> Allocate the genome object, it must have `genome_size` chromosomes and
886  !! `CHROMOSOME_PLOIDY` homologs
887  if (.not. allocated(this%chromosome)) &
888  allocate(this%chromosome(this%genome_size,chromosome_ploidy))
889 
890  !> Now cycle over all the chromosomes and homologs and initialise each
891  !! of them with empty genes (zero).
892  do j=1, chromosome_ploidy
893  do i=1, this%genome_size
894  call this%chromosome(i,j)%create_chromosome( len_chromosomes(i), &
895  lab_chromosomes(i) )
896  end do
897  end do
898 
899  !> Initialise the label the genome with a random string.
900  call this%label()
901 
902  !> Determine the sex of the genome by the genome sex determination locus
903  !! taking into account the sex ratio.
904  call this%sex_init()
905 
906  end subroutine genome_create_zero
907 
908  !-----------------------------------------------------------------------------
909  !> Label genome. If label is not provided, make a random string.
910  ! @note TMP NOTE: label setting removed from the create function
911  ! as it will now override create for `SPATIAL_MOVING`.
912  subroutine genome_label_set(this, label)
913  class(individual_genome), intent(inout) :: this
914  !! @param[in] label, set an optional label of the genome, if unset,
915  !! generate random string.
916  character(len=*), optional, intent(in) :: label
917 
918  !> ### Implementation details ###
919  !> Set the genome label if provided, or random string if not.
920  if (present(label)) then
921  this%genome_label = label
922  else
923  ! label the genome with a random string here
924  this%genome_label = rand_string(label_length,label_cst,label_cen)
925  end if
926 
927  end subroutine genome_label_set
928 
929  !-----------------------------------------------------------------------------
930  !> Accessor function to get the genome label. The label is a kind of a
931  !! (random) text string name of the genome and the individual agent.
932  !! @note We especially need this accessor function because the genome (and
933  !! individual) name is used in other modules for file names ids etc.
934  !! @returns String label.
935  elemental function genome_label_get(this) result (label_str)
936  class(individual_genome), intent(in) :: this
937  ! @warning Intel Fortran 17 does not allow setting allocatable attribute
938  ! for an elemental function. GNU gfortran allows.
939  ! @warning Use intrinsic `trim` function to avoid blanks, especially when
940  ! building file names. For example:
941  ! `MMDD // "_a_"// trim(this%individ_label()) `
942  character(len=LABEL_LENGTH) :: label_str
943 
944  label_str = this%genome_label
945 
946  end function genome_label_get
947 
948  !-----------------------------------------------------------------------------
949  !> @brief Sex determination initialisation subroutine.
950  !! @details Determine the genome's sex, sex is set by a logical identifier,
951  !! `sex_is_male` TRUE is **male**. Sex is calculated from the genome
952  !! and based on the average values of the sex determination alleles
953  !! in homologous chromosomes, rescaled to 0:1. This rescaled
954  !! value is then compared with the sex ratio parameter.
955  subroutine genome_sex_determine_init(this)
956  class(individual_genome), intent(inout) :: this
957 
958  integer :: i, j, k
959  integer :: sex_locus_sum ! average_sex_locus
960  integer :: sex_locus_num ! counter
961  integer, dimension(ADDITIVE_COMPS) :: values_from_allele
962 
963  !---------------------------------------------------------------------------
964  !> ### Implementation details ###
965  !> The implementation is based on **genotype** x **phenotype** matrix
966  !! (logical type): commondata::sex_genotype_phenotype.
967  !!
968  !> First, initialise the average sex locus sum across the homologous
969  !! chromosomes.
970  sex_locus_sum = 0; sex_locus_num = 0
971  !> #### Loops ####
972  !> Then loop across **homologs**, **chromosomes** and **alleles** until
973  !! the value of `SEX_GENOTYPE_PHENOTYPE` gets TRUE. This means it is the
974  !! *sex locus*.
975  chomologs: do k=1,chromosome_ploidy
976  cchromos: do i=1,this%genome_size
977  callales: do j=1, this%chromosome(i,k)%clength
978  if ( sex_genotype_phenotype(j,i) ) then
979  !> - If this condition is met, set label to the sex locus allele
980  !! ("SEX_LOCUS").
981  call this%chromosome(i,k)%allele(j)%labels(sexlocus_label)
982  !> - Sex is determined by an average of the sex loci of the
983  !! homologous chromosomes.
984  !! Therefore, first get the vector of additive allele components.
985  call this%chromosome(i,k)%allele(j)%get_vector(values_from_allele)
986  !> - And sum it up to finally get the total sum for all chromosomes.
987  sex_locus_sum = sex_locus_sum + sum(values_from_allele)
988  !> - Finally, also update the counter of the totals.
989  !! .
990  sex_locus_num = sex_locus_num + additive_comps
991  end if
992  end do callales
993  end do cchromos
994  end do chomologs
995  !! -------------------------------------------------------------------------
996 
997  !> Upon exit from the loop, check if the average sex locus across all
998  !! homologous chromosomes and additive allele components, scaled to 0:1
999  !! is less than the `SEX_RATIO`, the subject becomes the **male** genotype.
1000  if ( ((real(sex_locus_sum,srp)/real(sex_locus_num,srp)) / &
1002  ! set the main sex id of the object
1003  this%sex_is_male = .true.
1004  ! set the sex label for the object
1005  this%sex_label = male
1006  !> Otherwise, the subject becomes the **female**.
1007  else
1008  this%sex_is_male = .false.
1009  this%sex_label = female
1010  end if
1011 
1012  end subroutine genome_sex_determine_init
1013 
1014  !-----------------------------------------------------------------------------
1015  !> Get the logical sex ID of the genome object component.
1016  elemental function genome_get_sex_is_male(this) result (male)
1017  class(individual_genome), intent(in) :: this
1018 
1019  !> @return Returns logical value for sex: is it **male**?
1020  logical :: male
1021 
1022  male = this%sex_is_male
1023 
1024  end function genome_get_sex_is_male
1025 
1026  !-----------------------------------------------------------------------------
1027  !> Get the logical sex ID of the genome object component.
1028  elemental function genome_get_sex_is_female(this) result (female)
1029  class(individual_genome), intent(in) :: this
1030 
1031  !> @return Returns logical value for sex: is it a **female**?
1032  logical :: female
1033 
1034  if (this%sex_is_male) then
1035  female = .false.
1036  else
1037  female = .true.
1038  end if
1039 
1040  end function genome_get_sex_is_female
1041 
1042  !-----------------------------------------------------------------------------
1043  !> Get the descriptive sex label: male or female.
1044  elemental function genome_get_sex_label(this) result (sex_label)
1045  class(individual_genome), intent(in) :: this
1046 
1047  !> @return Returns the the descriptive sex label
1048  character(len=LABEL_LENGTH) :: sex_label
1049 
1050  sex_label = this%sex_label
1051 
1052  end function genome_get_sex_label
1053 
1054  !-----------------------------------------------------------------------------
1055  !> Set the individual to be **alive**, normally this function is used after
1056  !! init or birth.
1057  elemental subroutine genome_individual_set_alive(this)
1058  class(individual_genome), intent(inout) :: this
1059 
1060  this%alive = .true.
1061 
1062  end subroutine genome_individual_set_alive
1063 
1064  !-----------------------------------------------------------------------------
1065  !> Set the individual to be **dead**. Note that this function does not
1066  !! deallocate the individual agent object, this may be a separate destructor
1067  !! function.
1068  !!
1069  !! The `dies` method is implemented at the following levels
1070  !! of the agent object hierarchy (upper overrides the lower level):
1071  !! - the_genome::individual_genome::dies();
1072  !! - the_neurobio::appraisal::dies();
1073  !! - the_neurobio::gos_global::dies();
1074  !! - the_individual::individual_agent::dies().
1075  !! .
1076  elemental subroutine genome_individual_set_dead(this)
1077  class(individual_genome), intent(inout) :: this
1078 
1079  this%alive = .false.
1080 
1081  end subroutine genome_individual_set_dead
1082 
1083  !-----------------------------------------------------------------------------
1084  !> Check if the individual is **alive**.
1085  elemental function genome_individual_check_alive(this) result(is_alive_now)
1086  class(individual_genome), intent(in) :: this
1087  logical :: is_alive_now !> @return Logical flag for **alive**.
1088 
1089  is_alive_now = this%alive
1090 
1091  end function genome_individual_check_alive
1092 
1093  !-----------------------------------------------------------------------------
1094  !> Check if the individual is **dead** (the opposite of `is_alive`).
1095  elemental function genome_individual_check_dead(this) result(is_dead_now)
1096  class(individual_genome), intent(in) :: this
1097  logical :: is_dead_now !> @return Logical flag for **dead**.
1098 
1099  is_dead_now = .true.
1100  if (this%alive) is_dead_now = .false.
1101 
1102  end function genome_individual_check_dead
1103 
1104  !-----------------------------------------------------------------------------
1105  !> Internal genetic recombination backend, exchange individual alleles
1106  !! between homologous chromosomes in mother and father genomes to form
1107  !! the `this` (offspring) genome. Fully random recombination.
1108  !! @image html aha_genome_recombine_full.svg
1109  !! @image latex aha_genome_recombine_full.eps "Scheme of random genetic recombination" width=14cm
1110  !> @note Note that in this procedure, each of the individual alleles is
1111  !! copied from the mother or from the farther **independently** and
1112  !! **randomly**. This means that genetic distances are equal
1113  !! and there is no linkage disequilibrium.
1114  !> @note Note also that recombinations across the homologs of the same
1115  !! chromosome are also randomised, i.e. different in all homologs.
1117  mother, father, exchange_ratio)
1118  class(individual_genome), intent(inout) :: this
1119  !> @param[in] mother The **mother** object the_genome::individual_genome
1120  !! class.
1121  class(individual_genome), intent(in) :: mother
1122  !> @param[in] father The **father** object the_genome::individual_genome
1123  !! class.
1124  class(individual_genome), intent(in) :: father
1125  real(SRP), optional, intent(in) :: exchange_ratio
1126 
1127  ! Local copies of optionals
1128  real(SRP) :: exchange_ratio_here
1129 
1130  ! Local counters
1131  integer :: i, j, k
1132 
1133  !> ### Notable local variables ###
1134  !> - **n_alleles** is the number of alleles for each of the chromosomes,
1135  !! dynamically updated within the loops.
1136  integer :: n_alleles
1137 
1138  !> - **acomp_vect_mother** and **acomp_vect_father** are the vectors of
1139  !! additive allele components that are obtained from the mother and
1140  !! the father.
1141  !! .
1142  integer, dimension(ADDITIVE_COMPS) :: acomp_vect_mother, acomp_vect_father
1143 
1144  !> ### Implementation details ###
1145  !> Check optional `exchange_ratio` parameter that defines the ratio of the
1146  !! alleles that are inherited from the mother. If absent, get the default
1147  !! value from the commondata::genome_recombination_ratio_mother parameter.
1148  if (present(exchange_ratio)) then
1149  exchange_ratio_here = exchange_ratio
1150  else
1151  exchange_ratio_here = genome_recombination_ratio_mother
1152  end if
1153 
1154  !> #### Nested loops: HOMOLOGS, CHROMOSOMES, ALLELES ####
1155  !> Loop through the chromosomes (`CHROMOSOMES` block over `i`) and their
1156  !! homologues (`HOMOLOGS` block over `j`) and set the genetic make up of
1157  !! the `this` object from the genes of the `mother` and the `father`
1158  !! objects.
1159  !!
1160  !! The outline of the main loop is:
1161  !! - homologues
1162  !! - chromosomes
1163  !! - alleles
1164  !! .
1165  !! .
1166  !! .
1167  !!
1168  !! @warning Note that it is not possible to use parallel `do concurrent`
1169  !! loops here due to non-pure random call of the `RAND_R4()`
1170  !! function in the innermost loop (`ALLELES` block over `k`).
1171  homologs: do j=1, chromosome_ploidy
1172  chromosomes: do i=1, this%genome_size
1173  !> First, get the number of alleles for each of the chromosomes:
1174  !! `n_alleles`.
1175  n_alleles = this%chromosome(i,j)%clength
1176  !> Then, loop over the `n_alleles` alleles (`ALLELES` block over `k`):
1177  alleles: do k=1, n_alleles
1178  !> Randomly select if this specific allele (k) is copied from the
1179  !! **mother** or is a subject to (random) recombination and gets
1180  !! the values from the **father**. This is determined stochastically
1181  !! using the `exchange_ratio` ratio dummy argument or the
1182  !! `commondata::genome_recombination_ratio_mother` parameter as the
1183  !! probability to get the allele from the mother.
1184  if ( rand_r4() < exchange_ratio_here ) then
1185  !> - Get the vectors of additive allele components for the
1186  !! **mother**.
1187  ! @note This step is necessary because the
1188  ! `the_genome::gene::get_vector()` is a subroutine rather
1189  ! than function. Therefore, it is not possible to place
1190  ! the values just inline as a function into the next call.
1191  call mother%chromosome(i,j)%allele(k)%get_vector(acomp_vect_mother)
1192  !> Finally, **set** the vector of additive allele components of
1193  !! the `this` agent from the mother's vector.
1194  call this%chromosome(i,j)%allele(k)%set_vector(acomp_vect_mother)
1195  else
1196  !> - Get the vectors of additive allele components for the
1197  !! **father**.
1198  ! @note This step is necessary because the
1199  ! `the_genome::gene::get_vector()` is a subroutine rather
1200  ! than function. Therefore, it is not possible to place
1201  ! the values just inline as a function into the next call.
1202  call father%chromosome(i,j)%allele(k)%get_vector(acomp_vect_father)
1203  !> Finally, **set** the vector of additive allele components of
1204  !! the `this` agent from the father's vector.
1205  !! .
1206  call this%chromosome(i,j)%allele(k)%set_vector(acomp_vect_father)
1207  end if
1208  end do alleles
1209  end do chromosomes
1210  end do homologs
1211 
1213 
1214  !-----------------------------------------------------------------------------
1215  !> Internal genetic recombination backend, exchange individual alleles
1216  !! between homologous chromosomes in mother and father genomes to form
1217  !! the `this` (offspring) genome. Partially random recombination, identical
1218  !! across the homologous chromosomes.
1219  !! @image html aha_genome_recombine_part.svg
1220  !! @image latex aha_genome_recombine_part.eps "Scheme of partially random genetic recombination" width=14cm
1221  !> @note Note that in this procedure, each of the individual alleles is
1222  !! copied from the mother or from the farther **independently** and
1223  !! **partially randomly**. This means that genetic distances are equal
1224  !! and there is no linkage disequilibrium.
1225  !> @note Note also that recombination is identical across all homologs of
1226  !! the same chromosome.
1228  mother, father, exchange_ratio)
1229  class(individual_genome), intent(inout) :: this
1230  !> @param[in] mother The **mother** object the_genome::individual_genome
1231  !! class.
1232  class(individual_genome), intent(in) :: mother
1233  !> @param[in] father The **father** object the_genome::individual_genome
1234  !! class.
1235  class(individual_genome), intent(in) :: father
1236  real(SRP), optional, intent(in) :: exchange_ratio
1237 
1238  ! Local copies of optionals
1239  real(SRP) :: exchange_ratio_here
1240 
1241  ! Local counters
1242  integer :: i, j, k
1243 
1244  !> ### Notable local variables ###
1245  !> - **n_alleles** is the number of alleles for each of the chromosomes,
1246  !! dynamically updated within the loops.
1247  integer :: n_alleles
1248 
1249  !> - **acomp_vect_mother** and **acomp_vect_father** are the vectors of
1250  !! additive allele components that are obtained from the mother and
1251  !! the father.
1252  !! .
1253  integer, dimension(ADDITIVE_COMPS) :: acomp_vect_mother, acomp_vect_father
1254 
1255  !> ### Implementation details ###
1256  !> Check optional `exchange_ratio` parameter that defines the ratio of the
1257  !! alleles that are inherited from the mother. If absent, get the default
1258  !! value from the commondata::genome_recombination_ratio_mother parameter.
1259  if (present(exchange_ratio)) then
1260  exchange_ratio_here = exchange_ratio
1261  else
1262  exchange_ratio_here = genome_recombination_ratio_mother
1263  end if
1264 
1265  !> #### Nested loops: CHROMOSOMES, ALLELES, HOMOLOGS ####
1266  !> Loop through the chromosomes (`CHROMOSOMES` block over `i`) and their
1267  !! and set the genetic make up of
1268  !! the `this` object from the genes of the `mother` and the `father`
1269  !! objects.
1270  !!
1271  !! The outline of the main loop is:
1272  !! - chromosomes
1273  !! - alleles
1274  !! - homologues (within if block)
1275  !! .
1276  !! .
1277  !! .
1278  !!
1279  !! @warning Note that it is not possible to use parallel `do concurrent`
1280  !! loops here due to non-pure random call of the `RAND_R4()`
1281  !! function in the innermost loop (`ALLELES` block over `k`).
1282  chromosomes: do i=1, this%genome_size
1283  !> First, get the number of alleles for each of the chromosomes:
1284  !! `n_alleles`.
1285  n_alleles = this%chromosome(i,j)%clength
1286  !> Then, loop over the `n_alleles` alleles (`ALLELES` block over `k`):
1287  alleles: do k=1, n_alleles
1288  !> Randomly select if this specific allele (k) is copied from the
1289  !! **mother** or is a subject to (random) recombination and gets
1290  !! the values from the **father**. This is determined stochastically
1291  !! using the `exchange_ratio` ratio dummy argument or the
1292  !! `commondata::genome_recombination_ratio_mother` parameter as the
1293  !! probability to get the allele from the mother.
1294  if ( rand_r4() < exchange_ratio_here ) then
1295  !> Finally, loop through the homologues of the `i`th chromosome
1296  !! (`HOMOLOGS` block over `j`) and set the same allele across all
1297  !! homologues the same way (transferred from mother or the father).
1298  !! Thus, all homologues have identical non-random recombination
1299  !! pattern, either from the mother or from the father.
1300  homologs_mo: do j=1, chromosome_ploidy
1301  !> - Get the vectors of additive allele components for the
1302  !! **mother**.
1303  ! @note This step is necessary because the
1304  ! `the_genome::gene::get_vector()` is a subroutine rather
1305  ! than function. Therefore, it is not possible to place
1306  ! the values just inline as a function into the next call.
1307  call mother%chromosome(i,j)%allele(k)%get_vector(acomp_vect_mother)
1308  !> Finally, **set** the vector of additive allele components of
1309  !! the `this` agent from the mother's vector.
1310  call this%chromosome(i,j)%allele(k)%set_vector(acomp_vect_mother)
1311  end do homologs_mo
1312  else
1313  homologs_fa: do j=1, chromosome_ploidy
1314  !> - Get the vectors of additive allele components for the
1315  !! **father**.
1316  ! @note This step is necessary because the
1317  ! `the_genome::gene::get_vector()` is a subroutine rather
1318  ! than function. Therefore, it is not possible to place
1319  ! the values just inline as a function into the next call.
1320  call father%chromosome(i,j)%allele(k)%get_vector(acomp_vect_father)
1321  !> Finally, **set** the vector of additive allele components of
1322  !! the `this` agent from the father's vector.
1323  !! .
1324  call this%chromosome(i,j)%allele(k)%set_vector(acomp_vect_father)
1325  end do homologs_fa
1326  end if
1327  end do alleles
1328  end do chromosomes
1329 
1331 
1332  !-----------------------------------------------------------------------------
1333  !> Internal fixed genetic crossover backend, exchange blocks of alleles
1334  !! between homologous chromosomes in mother and father genomes to form
1335  !! the `this` (offspring) genome.
1336  !! @image html aha_genome_crossover.svg
1337  !! @image latex aha_genome_crossover.eps "Scheme of genetic crossover" width=14cm
1338  !> @note Note that in this procedure, blocks of alleles (i.e. sections of
1339  !! the chromosomes) are copied from the mother or from the farther.
1340  !! The blocks are fixed and defined by the boolean parameter matrix
1341  !! commondata::genome_crossover_fixed_mother. This means that genetic
1342  !! recombination is non-random, genetic distances are small within the
1343  !! blocks that are inherited from the same parent and relatively large
1344  !! between the alleles that belong to different blocks (i.e. the
1345  !! genetic distances depend on the proximity of the alleles). This
1346  !! makes linkage disequilibrium possible.
1348  mother, father, pattern_matrix)
1349  class(individual_genome), intent(inout) :: this
1350  !> @param[in] mother The **mother** object the_genome::individual_genome
1351  !! class.
1352  class(individual_genome), intent(in) :: mother
1353  !> @param[in] father The **father** object the_genome::individual_genome
1354  !! class.
1355  class(individual_genome), intent(in) :: father
1356  !> @param[in] pattern_matrix is an optional boolean pattern matrix that
1357  !! determines the pattern of fixed chromosome crossover. For
1358  !! each chromosome, the alleles that are marked with the
1359  !! TRUE (YES) values are inherited from the **mother** whereas
1360  !! those marked FALSE (NO) are inherited from the **father**.
1361  !! see commondata::genome_crossover_fixed_mother.
1362  logical, dimension(MAX_NALLELES,N_CHROMOSOMES), &
1363  optional, intent(in) :: pattern_matrix
1364 
1365  ! Local copies of optionals
1366  logical, dimension(MAX_NALLELES,N_CHROMOSOMES) :: pattern_matrix_loc
1367 
1368  ! Local counters
1369  integer :: i, j, k
1370 
1371  !> ### Notable local variables ###
1372  !> - **n_alleles** is the number of alleles for each of the chromosomes,
1373  !! dynamically updated within the loops.
1374  integer :: n_alleles
1375 
1376  !> - **acomp_vect_mother** and **acomp_vect_father** are the vectors of
1377  !! additive allele components that are obtained from the mother and
1378  !! the father.
1379  !! .
1380  integer, dimension(ADDITIVE_COMPS) :: acomp_vect_mother, acomp_vect_father
1381 
1382  !> ### Implementation details ###
1383  !> Check optional `pattern_matrix` parameter matrix. If it is not provided,
1384  !! use the default commondata::genome_crossover_fixed_mother.
1385  if (present(pattern_matrix)) then
1386  pattern_matrix_loc = pattern_matrix
1387  else
1388  pattern_matrix_loc = genome_crossover_fixed_mother
1389  end if
1390 
1391  !> #### Nested parallel loops: HOMOLOGS, CHROMOSOMES, ALLELES ####
1392  !> Loop through the chromosomes (`CHROMOSOMES` block over `i`) and their
1393  !! homologues (`HOMOLOGS`block over `j`) and set the genetic make up of
1394  !! the `this` object from the genes of the `mother` and the `father`
1395  !! objects.
1396  homologs: do concurrent(j=1:chromosome_ploidy)
1397  chromosomes: do concurrent(i=1:this%genome_size)
1398  !> First, get the number of alleles for each of the chromosomes:
1399  !! `n_alleles`.
1400  n_alleles = this%chromosome(i,j)%clength
1401  !> Then, loop over the `n_alleles` alleles (`ALLELES` block over `k`):
1402  alleles: do concurrent(k=1:n_alleles)
1403  !> Check the boolean pattern matrix `pattern_matrix_loc`value,
1404  !! if it is TRUE, the allele value is copied from the **mother**,
1405  !! otherwise from the **father**.
1406  !! ##### If block check #####
1407  if ( pattern_matrix_loc(k,i) ) then
1408  !> - Get the vectors of additive allele components for the
1409  !! **mother**.
1410  ! @note This step is necessary because the
1411  ! `the_genome::gene::get_vector()` is a subroutine rather
1412  ! than function. Therefore, it is not possible to place
1413  ! the values just inline as a function into the next call.
1414  call mother%chromosome(i,j)%allele(k)%get_vector(acomp_vect_mother)
1415  !> Finally, **set** the vector of additive allele components of
1416  !! the `this` agent from the mother's vector.
1417  call this%chromosome(i,j)%allele(k)%set_vector(acomp_vect_mother)
1418  else
1419  !> - Get the vectors of additive allele components for the
1420  !! **father**.
1421  ! @note This step is necessary because the
1422  ! `the_genome::gene::get_vector()` is a subroutine rather
1423  ! than function. Therefore, it is not possible to place
1424  ! the values just inline as a function into the next call.
1425  call father%chromosome(i,j)%allele(k)%get_vector(acomp_vect_father)
1426  !> Finally, **set** the vector of additive allele components of
1427  !! the `this` agent from the father's vector.
1428  !! .
1429  call this%chromosome(i,j)%allele(k)%set_vector(acomp_vect_father)
1430  end if
1431  end do alleles
1432  end do chromosomes
1433  end do homologs
1434 
1436 
1437  !-----------------------------------------------------------------------------
1438  !> @name Neuronal response functions
1439  !! There are two separate functions that produce a trait value from
1440  !! the genotype. The procedure `trait_init_genotype_gamma2gene` does
1441  !! modify the agent (`this`, intent[inout]) as it sets the label.
1442  !! On the other hand, a similar procedure
1443  !! `the_genome::trait_set_genotype_gamma2gene()` does *not* affect the
1444  !! agent, it has the intent [in].
1445  !! @{
1446 
1447  !> @brief Initialise an **individual trait** of the agent that depends on
1448  !! the genotype. This can be any trait upwards in the class
1449  !! hierarchy.
1450  !! @details Create a new **trait** object from the genotype according to the
1451  !! boolean genotype x phenotype matrix `g_p_matrix` by
1452  !! `commondata::gamma2gene()` function and the `init_val` baseline
1453  !! value. It also introduces an initial Gaussian variance with the
1454  !! coefficient of variation=`gerror_cv`. Normally, `g_p_matrix`,
1455  !! `init_val` and `gerror_cv` are parameters set in `commondata` for
1456  !! each specific trait. For example, for *thyroid* hormone
1457  !! (`the_hormones::hormones` class level up) they are:
1458  !! - `commondata::thyroid_genotype_phenotype`,
1459  !! - `commondata::thyroid_init` and
1460  !! - `commondata::thyroid_gerror_cv`.
1461  !! .
1462  !!
1463  !! @note This procedure has the **intent [inout]** for `this`, the agent
1464  !! as it modifies the label. Therefore, it should be mainly used
1465  !! for initialisation of agent traits.
1466  !! @warning This code does not (yet) work with *haploid* genotype
1467  !! `commondata::chromosome_ploidy = 1`, in such a case there is no
1468  !! need to select random homologous chromosome and it is impossible
1469  !! to set the two parameters of the `commondata::gamma2gene`
1470  !! function (there is only a single chromosome).
1471  subroutine trait_init_genotype_gamma2gene(this, this_trait, g_p_matrix, &
1472  init_val, gerror_cv, label)
1473  class(individual_genome), intent(inout) :: this
1474 
1475  !> @param[out] this_trait the component of the individual agent object
1476  !! hierarchy that we are initialising in.
1477  real(SRP), intent(out) :: this_trait
1478  !> @param[in] g_p_matrix matrix genotype x phenotype, the matrix setting
1479  !! the correspondence between the genome and the phenotype.
1480  logical, dimension(:,:), intent(in) :: g_p_matrix
1481  !> @param[in] init_val start value, trait initialisation baseline value
1482  !! parameter, e.g. for *thyroid* it is `commondata::thyroid_init`.
1483  real(SRP), intent(in) :: init_val
1484  !> @param[in] gerror_cv error value, trait Gaussian error value, e.g.
1485  !! for *thyroid* it is `commondata::thyroid_gerror_cv`, if absent,
1486  !! we don't introduce random error and the initial trait values are
1487  !! deterministic by the genome.
1488  real(SRP), optional, intent(in) :: gerror_cv
1489  !> @param[in] label a label for the allele locus that sets the
1490  !! phenotypic object.
1491  character(len=*), intent(in) :: label
1492 
1493  ! Local variables
1494  integer :: i, j, k1, k2
1495  integer, dimension(ADDITIVE_COMPS) :: additive_vals_1, additive_vals_2
1496 
1497  !> ### Implementation details ###
1498  !> Set **trait** values from the genome using the `g_p_matrix` matrix.
1499  !> We first need to select two chromosomes from the available set (normally
1500  !! two, but possibly more) for input into commondata::gamma2gene() function.
1501  !! We do it random.
1502  ! - Assign the first chromosome.
1503  k1 = rand_i(1,chromosome_ploidy)
1504  ! - Also assign the second chromosome.
1505  k2 = rand_i(1,chromosome_ploidy)
1506  !> Then, cycle through and select a different chromosomes (i.e. cycle
1507  !! if happens to coincide with the first).
1508  !! (Use `do while chromosome1 = chromosome2` construct.)
1509  do while (k1 == k2)
1510  k2 = rand_i(1,chromosome_ploidy)
1511  end do
1512  !> As a result there are two distinct chromosomes `k1` and `k2`.
1513  !! This unique chromosomes selection part of the code precludes
1514  !! the use of haploid genome.
1515  !> #### Loop over chromosomes and alleles ####
1516  cchromos: do i=1,this%genome_size
1517  calleles: do j=1, this%chromosome(i,k1)%clength
1518  if ( g_p_matrix(j,i) ) then
1519  !> - set label to the **trait** locus allele
1520  call this%chromosome(i,k1)%allele(j)%labels(label)
1521  call this%chromosome(i,k2)%allele(j)%labels(label)
1522  !> - The initial trait value is determined using the
1523  !! commondata::gamma2gene() function using additive allele
1524  !! components. So, we first get the two vectors of additive
1525  !! allele components.
1526  call this%chromosome(i,k1)%allele(j)%get_vector(additive_vals_1)
1527  call this%chromosome(i,k2)%allele(j)%get_vector(additive_vals_2)
1528  !> - And get the value of the trait from commondata::gamma2gene().
1529  !! @note commondata::gamma2gene() now accepts vectors of
1530  !! additive allele components.
1531  if (present(gerror_cv)) then
1532  this_trait = gamma2gene( additive_vals_1, additive_vals_2, &
1533  init_val, gerror_cv )
1534  else
1535  this_trait = gamma2gene( additive_vals_1, additive_vals_2, &
1536  init_val)
1537  end if
1538  end if
1539  end do calleles
1540  end do cchromos
1541 
1542  end subroutine trait_init_genotype_gamma2gene
1543 
1544  !-----------------------------------------------------------------------------
1545  !> @brief Set an **individual trait** of the agent that depends on
1546  !! the genotype. This can be any trait upwards in the class hierarchy.
1547  !! @details This is almost the same as
1548  !! `the_genome::trait_init_genotype_gamma2gene()`, but does
1549  !! *not* modify the `this` object (it has **intent [in]**).
1550  !! Therefore it should be used for setting such traits as
1551  !! behavioural expectancies.
1552  !!
1553  !! @note This procedure has the intent [in] for `this`, the agent as it
1554  !! does not modify the object (e.g. does not set label). Therefore,
1555  !! it can be used in assessing the subjective expectancies.
1556  !! @warning This code does not (yet) work with *haploid* genotype
1557  !! `commondata::chromosome_ploidy = 1`, in such a case there is no
1558  !! need to select random homologous chromosome and it is impossible
1559  !! to set the two parameters of the `commondata::gamma2gene()`
1560  !! function (there is only a single chromosome).
1561  subroutine trait_set_genotype_gamma2gene(this, this_trait, g_p_matrix, &
1562  init_val, gerror_cv)
1563  class(individual_genome), intent(in) :: this
1564 
1565  !> @param[out] this_trait the component of the individual agent
1566  !! object hierarchy that we are initialising.
1567  real(SRP), intent(out) :: this_trait
1568  !> @param[in] matrix genotype x phenotype, the matrix setting the
1569  !! correspondence between the genome and the phenotype.
1570  logical, dimension(:,:), intent(in) :: g_p_matrix
1571  !> @param[in] init_val start value, trait initialisation baseline value
1572  !! parameter, e.g. for *thyroid* it is `commondata::thyroid_init`.
1573  real(SRP), intent(in) :: init_val
1574  !> @param[in] gerror_cv error value, trait Gaussian error value, e.g. for
1575  !! *thyroid* it is `commondata::thyroid_gerror_cv`, if absent,
1576  !! we don't introduce random error and the initial trait values are
1577  !! deterministic by the genome.
1578  real(SRP), optional, intent(in) :: gerror_cv
1579 
1580  ! Local variables
1581  integer :: i, j, k1, k2
1582  integer, dimension(ADDITIVE_COMPS) :: additive_vals_1, additive_vals_2
1583 
1584  !> ### Implementation details ###
1585  !> Set **trait** values from the genome using the `g_p_matrix` matrix.
1586  !> We first need to select two chromosomes from the available set (normally
1587  !! two, but possibly more) for input into commondata::gamma2gene() function.
1588  !! We do it random.
1589  ! - Assign the first chromosome.
1590  k1 = rand_i(1,chromosome_ploidy)
1591  ! - Also assign the second chromosome.
1592  k2 = rand_i(1,chromosome_ploidy)
1593  !> Then, we cycle through and select a different chromosomes (i.e. cycle
1594  !! if happens to coincide with the first.
1595  do while (k1 == k2)
1596  k2 = rand_i(1,chromosome_ploidy)
1597  end do
1598  !> As a result there are two distinct chromosomes `k1` and `k2`.
1599  !! This unique chromosomes selection part of the code precludes
1600  !! the use of haploid genome.
1601  !> #### Loop over chromosomes and alleles ####
1602  cchromos: do i=1,this%genome_size
1603  calleles: do j=1, this%chromosome(i,k1)%clength
1604  if ( g_p_matrix(j,i) ) then
1605  !> - The initial trait value is determined using the
1606  !! commondata::gamma2gene() function using additive allele
1607  !! components. So, we first get the two vectors of additive
1608  !! allele components.
1609  call this%chromosome(i,k1)%allele(j)%get_vector(additive_vals_1)
1610  call this%chromosome(i,k2)%allele(j)%get_vector(additive_vals_2)
1611  !> - And get the value of the trait from `commondata::gamma2gene()`.
1612  !! @note commondata::gamma2gene() now accepts vectors of additive
1613  !! allele components.
1614  if (present(gerror_cv)) then
1615  this_trait = gamma2gene( additive_vals_1, additive_vals_2, &
1616  init_val, gerror_cv )
1617  else
1618  this_trait = gamma2gene( additive_vals_1, additive_vals_2, &
1619  init_val )
1620  end if
1621  end if
1622  end do calleles
1623  end do cchromos
1624 
1625  end subroutine trait_set_genotype_gamma2gene
1626 
1627  !> @}
1628 
1629  !-----------------------------------------------------------------------------
1630  !> @name The genotype to phenotype functions based on fixed linear
1631  !! transformation.
1632  !! @{
1633 
1634  !-----------------------------------------------------------------------------
1635  !> @brief Initialise an **individual trait** of the agent that depends on
1636  !! the genotype. This can be any trait upwards in the class
1637  !! hierarchy.
1638  !! @details Create a new **trait** object from the genotype according to the
1639  !! boolean genotype x phenotype matrix `g_p_matrix` by a simple
1640  !! linear scaling transformation function. Normally, `g_p_matrix`,
1641  !! is a parameters set in @ref commondata for each specific trait.
1642  !! @note This version uses only two chromosomes for compatibility with
1643  !! the counterpart the_genome::trait_init_genotype_gamma2gene()
1644  !! procedure; the code logic is almost the same as in that based on
1645  !! the neuronal response function.
1647  this_trait, g_p_matrix, &
1648  phenotype_min, phenotype_max, label)
1649  class(individual_genome), intent(inout) :: this
1650 
1651  !> @param[out] this_trait the component of the individual agent object
1652  !! hierarchy that we are initialising in.
1653  real(SRP), intent(out) :: this_trait
1654  !> @param[in] g_p_matrix matrix genotype x phenotype, the matrix setting
1655  !! the correspondence between the genome and the phenotype.
1656  logical, dimension(:,:), intent(in) :: g_p_matrix
1657  !> @param[in] init_val start value, trait initialisation baseline value
1658  !! parameter, e.g. for *thyroid* it is `commondata::thyroid_init`.
1659  real(SRP), intent(in) :: phenotype_min
1660  real(SRP), intent(in) :: phenotype_max
1661  !> @param[in] label a label for the allele locus that sets the
1662  !! phenotypic object.
1663  character(len=*), intent(in) :: label
1664 
1665  ! Local variables
1666  integer :: i, j, k1, k2
1667  integer, dimension(ADDITIVE_COMPS) :: additive_vals_1, additive_vals_2
1668 
1669  ! The Minimum possible value of the genome, assuming each of the
1670  ! commondata::additive_comps additive components of two homologous
1671  ! (genes) has the minimum value commondata::allelerange_min.
1672  integer, parameter :: SCALE_GENOME_MIN = allelerange_min*additive_comps * 2
1673 
1674  ! The Maximum possible value of the genome, assuming each of the
1675  ! commondata::additive_comps additive components of two homologous
1676  ! (genes) has the maximum value commondata::allelerange_max.
1677  integer, parameter :: SCALE_GENOME_MAX = allelerange_max*additive_comps * 2
1678 
1679  !> ### Implementation details ###
1680  !> Set **trait** values from the genome using the `g_p_matrix` matrix.
1681  !> We first need to select two chromosomes from the available set (normally
1682  !! two, but possibly more) for input into `gamma2gene`. We do it random.
1683  !> Assign the first chromosome.
1684  k1 = rand_i(1,chromosome_ploidy)
1685  !> Also assign the second chromosome.
1686  k2 = rand_i(1,chromosome_ploidy)
1687  !> Then, cycle through and select a different chromosomes (i.e. cycle
1688  !! if happens to coincide with the first).
1689  !! (Use do while chromosome1 = chromosome2` construct.)
1690  do while (k1 == k2)
1691  k2 = rand_i(1,chromosome_ploidy)
1692  end do
1693  !> As a result there are two distinct chromosomes `k1` and `k2`.
1694  !! This unique chromosomes selection part of the code precludes
1695  !! the use of haploid genome.
1696  !> #### Loop over chromosomes and alleles ####
1697  cchromos: do i=1,this%genome_size
1698  calleles: do j=1, this%chromosome(i,k1)%clength
1699  if ( g_p_matrix(j,i) ) then
1700  !> - set label to the **trait** locus allele
1701  call this%chromosome(i,k1)%allele(j)%labels(label)
1702  call this%chromosome(i,k2)%allele(j)%labels(label)
1703  !> - The initial trait value is determined using the gamma2gene
1704  !! function using additive allele components.
1705  !! So, we first get the two vectors of additive allele components.
1706  call this%chromosome(i,k1)%allele(j)%get_vector(additive_vals_1)
1707  call this%chromosome(i,k2)%allele(j)%get_vector(additive_vals_2)
1708  !> - Unlike ::trait_init_genotype_gamma2gene, the output phenotypic
1709  !! value is obtained by simple linear commondata::rescale from the
1710  !! minimum genotype range to the phenotypic range set by the
1711  !! `phenotype_min` and `phenotype_max` parameters.
1712  !! .
1713  this_trait = rescale( real(sum(additive_vals_1) + &
1714  sum(additive_vals_2), srp), &
1715  real(SCALE_GENOME_MIN, SRP), &
1716  real(SCALE_GENOME_MAX, SRP), &
1717  phenotype_min, &
1718  phenotype_max )
1719  end if
1720  end do calleles
1721  end do cchromos
1722 
1724 
1725  !-----------------------------------------------------------------------------
1726  !> @brief Set an **individual trait** of the agent that depends on
1727  !! the genotype. This can be any trait upwards in the class
1728  !! hierarchy.
1729  !! @details This is almost the same as
1730  !! the_genome::trait_init_linear_sum_additive_comps_2genes_r(), but
1731  !! does *not* modify the `this` object (it has **intent [in]**).
1732  !! Therefore it should be used for setting such traits as
1733  !! behavioural expectancies.
1734  !! Create a new **trait** object from the genotype according to the
1735  !! boolean genotype x phenotype matrix `g_p_matrix` by a simple
1736  !! linear scaling transformation function. Normally, `g_p_matrix`,
1737  !! is a parameters set in @ref commondata for each specific trait.
1738  !! @note This version uses only two chromosomes for compatibility with
1739  !! the counterpart the_genome::trait_init_genotype_gamma2gene()
1740  !! procedure; the code logic is almost the same as in that based on
1741  !! the neuronal response function.
1743  this_trait, g_p_matrix, &
1744  phenotype_min, phenotype_max )
1745  class(individual_genome), intent(in) :: this
1746 
1747  !> @param[out] this_trait the component of the individual agent object
1748  !! hierarchy that we are initialising in.
1749  real(SRP), intent(out) :: this_trait
1750  !> @param[in] g_p_matrix matrix genotype x phenotype, the matrix setting
1751  !! the correspondence between the genome and the phenotype.
1752  logical, dimension(:,:), intent(in) :: g_p_matrix
1753  !> @param[in] init_val start value, trait initialisation baseline value
1754  !! parameter, e.g. for *thyroid* it is `commondata::thyroid_init`.
1755  real(SRP), intent(in) :: phenotype_min
1756  real(SRP), intent(in) :: phenotype_max
1757 
1758  ! Local variables
1759  integer :: i, j, k1, k2
1760  integer, dimension(ADDITIVE_COMPS) :: additive_vals_1, additive_vals_2
1761 
1762  ! The Minimum possible value of the genome, assuming each of the
1763  ! commondata::additive_comps additive components of two homologous
1764  ! (genes) has the minimum value commondata::allelerange_min.
1765  integer, parameter :: SCALE_GENOME_MIN = allelerange_min*additive_comps * 2
1766 
1767  ! The Maximum possible value of the genome, assuming each of the
1768  ! commondata::additive_comps additive components of two homologous
1769  ! (genes) has the maximum value commondata::allelerange_max.
1770  integer, parameter :: SCALE_GENOME_MAX = allelerange_max*additive_comps * 2
1771 
1772  !> ### Implementation details ###
1773  !> Set **trait** values from the genome using the `g_p_matrix` matrix.
1774  !> We first need to select two chromosomes from the available set (normally
1775  !! two, but possibly more) for input into `gamma2gene`. We do it random.
1776  !> Assign the first chromosome.
1777  k1 = rand_i(1,chromosome_ploidy)
1778  !> Also assign the second chromosome.
1779  k2 = rand_i(1,chromosome_ploidy)
1780  !> Then, cycle through and select a different chromosomes (i.e. cycle
1781  !! if happens to coincide with the first).
1782  !! (Use do while chromosome1 = chromosome2` construct.)
1783  do while (k1 == k2)
1784  k2 = rand_i(1,chromosome_ploidy)
1785  end do
1786  !> As a result there are two distinct chromosomes `k1` and `k2`.
1787  !! This unique chromosomes selection part of the code precludes
1788  !! the use of haploid genome.
1789  !> #### Loop over chromosomes and alleles ####
1790  cchromos: do i=1,this%genome_size
1791  calleles: do j=1, this%chromosome(i,k1)%clength
1792  if ( g_p_matrix(j,i) ) then
1793  !> - The initial trait value is determined using the gamma2gene
1794  !! function using additive allele components.
1795  !! So, we first get the two vectors of additive allele components.
1796  call this%chromosome(i,k1)%allele(j)%get_vector(additive_vals_1)
1797  call this%chromosome(i,k2)%allele(j)%get_vector(additive_vals_2)
1798  !> - Unlike ::trait_init_genotype_gamma2gene, the output phenotypic
1799  !! value is obtained by simple linear commondata::rescale from the
1800  !! minimum genotype range to the phenotypic range set by the
1801  !! `phenotype_min` and `phenotype_max` parameters.
1802  !! .
1803  this_trait = rescale( real(sum(additive_vals_1) + &
1804  sum(additive_vals_2), srp), &
1805  real(SCALE_GENOME_MIN, SRP), &
1806  real(SCALE_GENOME_MAX, SRP), &
1807  phenotype_min, &
1808  phenotype_max )
1809  end if
1810  end do calleles
1811  end do cchromos
1812 
1814 
1815  !> @}
1816 
1817  !-----------------------------------------------------------------------------
1818  !> Perform a probabilistic random mutation(s) on the individual genome.
1819  !! This is a high level wrapper to build mutations from various components.
1820  subroutine genome_mutate_wrapper(this, p_point, p_set, p_swap, p_shift)
1821  class(individual_genome), intent(inout) :: this
1822  !> @param[in] p_point optional probability of mutation, if absent, the
1823  !! default value commondata::mutationrate_point is used.
1824  real(SRP), optional, intent(in) :: p_point
1825  !> @param[in] p_set optional probability of mutation, if absent, the
1826  !! default value commondata::mutationrate_batch is used.
1827  real(SRP), optional, intent(in) :: p_set
1828  !> @param[in] p_swap optional probability of mutation, if absent, the
1829  !! default value commondata::mutationrate_batch is used.
1830  real(SRP), optional, intent(in) :: p_swap
1831  !> @param[in] p_shift optional probability of mutation, if absent, the
1832  !! default value commondata::mutationrate_batch is used.
1833  real(SRP), optional, intent(in) :: p_shift
1834 
1835  ! Local variables that randomly set the location for the mutation.
1836  integer :: mutate_chromosome, mutate_homolog, mutate_allele
1837 
1838  !> ### Implementation notes ###
1839  !> Each of the mutations below is a random process that occurs with a
1840  !! specific probability that is set by its respective mutation rate
1841  !! parameter.
1842  !> - Call for a point mutation on a single random allele component of a
1843  !! randomly chosen chromosome using the the_genome::gene::mutate_point()
1844  !! method.
1845  mutate_chromosome = rand_i(1, this%genome_size)
1846  mutate_homolog = rand_i(1, chromosome_ploidy)
1847  mutate_allele = rand_i(1, this%chromosome( &
1848  mutate_chromosome,mutate_homolog)%clength)
1849  if (present(p_point)) then
1850  call this%chromosome(mutate_chromosome,mutate_homolog)%allele( &
1851  mutate_allele)%mutate_point(p_point)
1852  else
1853  call this%chromosome(mutate_chromosome,mutate_homolog)%allele( &
1854  mutate_allele)%mutate_point()
1855  end if
1856 
1857  !> - Call for a point mutation on a whole random allele (batch of allele
1858  !! components) of a randomly chosen chromosome using the
1859  !! the_genome::gene::mutate_set() method.
1860  mutate_chromosome = rand_i(1, this%genome_size)
1861  mutate_homolog = rand_i(1, chromosome_ploidy)
1862  mutate_allele = rand_i(1, this%chromosome( &
1863  mutate_chromosome,mutate_homolog)%clength)
1864  if (present(p_set)) then
1865  call this%chromosome(mutate_chromosome,mutate_homolog)%allele( &
1866  mutate_allele)%mutate_set(p_set)
1867  else
1868  call this%chromosome(mutate_chromosome,mutate_homolog)%allele( &
1869  mutate_allele)%mutate_set()
1870  end if
1871 
1872  ! > - Call for a relocation swap mutation; this calls the
1873  ! ! the_genome::chromosome::mutate_swap() method.
1874  ! mutate_chromosome = RAND_I(1, this%genome_size)
1875  ! mutate_homolog = RAND_I(1, CHROMOSOME_PLOIDY)
1876  ! if (present(p_swap)) then
1877  ! call this%chromosome(mutate_chromosome, mutate_homolog)% &
1878  ! mutate_swap(p_swap)
1879  ! else
1880  ! call this%chromosome(mutate_chromosome, mutate_homolog)%mutate_swap()
1881  ! end if
1882 
1883  ! > - Call for a relocation shift mutation: this calls the
1884  ! ! the_genome::chromosome::mutate_shift() method.
1885  ! ! .
1886  ! mutate_chromosome = RAND_I(1, this%genome_size)
1887  ! mutate_homolog = RAND_I(1, CHROMOSOME_PLOIDY)
1888  ! if (present(p_shift)) then
1889  ! call this%chromosome(mutate_chromosome, mutate_homolog)% &
1890  ! mutate_shift(p_shift)
1891  ! else
1892  ! call this%chromosome(mutate_chromosome, mutate_homolog)%mutate_shift()
1893  ! end if
1894 
1895  end subroutine genome_mutate_wrapper
1896 
1897 end module the_genome
Sigmoidal relationship between environmental factor and the organism response, as affected by the gen...
Definition: m_common.f90:5269
Arbitrary rescales value(s) from one range (A:B) to another (A1:B1).
Definition: m_common.f90:5341
recursive pure subroutine qsort(A)
qsort and qs_partition_ are the two parts of the recursive sort algorithm qsort is the recursive fron...
Definition: m_env.f90:7014
pure subroutine qs_partition_rank_id(A, marker)
qsort and qs_partition_ are the two parts of the recursive sort algorithm qs_partition_rank_id is a p...
Definition: m_genome.f90:356
COMMONDATA – definitions of global constants and procedures.
Definition: m_common.f90:1497
character(len= *), parameter, private modname
MODNAME always refers to the name of the current module for use by the LOGGER function LOG_DBG....
Definition: m_common.f90:1591
real(srp), parameter, public genome_recombination_ratio_mother
The ratio of the genome that inherited from the mother. The other part is inherited from the father....
Definition: m_common.f90:2673
integer, parameter, public n_chromosomes
The number of chromosomes for the agents.
Definition: m_common.f90:2622
integer, parameter, public srp
Definition of the standard real type precision (SRP).
Definition: m_common.f90:1551
logical, dimension(max_nalleles, n_chromosomes), parameter, public sex_genotype_phenotype
Sex definition can be implemented differently from all other traits. Here is an example of the phenot...
Definition: m_common.f90:2753
integer, parameter, public label_length
The length of standard character string labels. We use labels for various objects,...
Definition: m_common.f90:1736
logical, dimension(max_nalleles, n_chromosomes), parameter, public genome_crossover_fixed_mother
Boolean 2D matrix that determines the pattern of fixed chromosome crossover. For each chromosome,...
Definition: m_common.f90:2684
integer, parameter, public chromosome_ploidy
The ploidy of the chromosome set. Can theoretically be haploid (=1), diploid (=2) or,...
Definition: m_common.f90:2667
integer, parameter, public additive_comps
Number of additive allele components.
Definition: m_common.f90:2590
integer, parameter, public allelerange_min
The minimum possible value of alleles (allele range minimum) See implementation notes on the_genome::...
Definition: m_common.f90:2576
integer, dimension(n_chromosomes), parameter, public len_chromosomes
The number of alleles in each of the chromosomes. NOTE: This must be an array (vector) of the size co...
Definition: m_common.f90:2629
integer, parameter, public allelerange_max
The maximum possible value of alleles (allele range maximum) See implementation notes on the_genome::...
Definition: m_common.f90:2582
real(srp), parameter, public mutationrate_point
Mutation rate for point allele mutations.
Definition: m_common.f90:2595
logical, parameter, public true
Safety parameter avoid errors in logical values, so we can now refer to standard Fortran ....
Definition: m_common.f90:1632
real(srp), parameter, public sex_ratio
Sex ratio for initialising genomes.
Definition: m_common.f90:2711
integer, parameter, public label_cen
Definition: m_common.f90:1743
real(srp), parameter, public relocation_swap_rate
Mutation rate for chromosome relocation, i.e. probability of a gene moving to a different position on...
Definition: m_common.f90:2614
real(srp), parameter, public mutationrate_batch
Mutation rate for point allele mutations, a whole batch of allele components.
Definition: m_common.f90:2603
character(len= *), dimension(n_chromosomes), parameter, public lab_chromosomes
Set the labels of the chromosomes. NOTE, must be an array(vector) ) of the size commondata::n_chromos...
Definition: m_common.f90:2653
integer, parameter, public label_cst
This parameter defines the range of characters that is used for generating random labels,...
Definition: m_common.f90:1743
real(srp), parameter, public relocation_shift_rate
Definition: m_common.f90:2615
character(len= *), parameter, public male
Set names of the sexes – the allele labels.
Definition: m_common.f90:2718
character(len= *), parameter, public female
Definition: m_common.f90:2718
character(len=label_length), parameter sexlocus_label
Labels for the sex locus alleles (gene) - vector as we don't need to label individual alleles....
Definition: m_common.f90:2715
logical, parameter, public false
Definition: m_common.f90:1632
Definition of environmental objects.
Definition: m_env.f90:19
Definition the genetic architecture of the agent.
Definition: m_genome.f90:16
pure subroutine allele_values_vector_get(this, values)
Get the vector of all values of the alleles, i.e. gets the gene values.
Definition: m_genome.f90:509
subroutine genome_mutate_wrapper(this, p_point, p_set, p_swap, p_shift)
Perform a probabilistic random mutation(s) on the individual genome. This is a high level wrapper to ...
Definition: m_genome.f90:1821
subroutine genome_sex_determine_init(this)
Sex determination initialisation subroutine.
Definition: m_genome.f90:956
subroutine genome_individual_recombine_homol_full_rand_alleles(this, mother, father, exchange_ratio)
Internal genetic recombination backend, exchange individual alleles between homologous chromosomes in...
Definition: m_genome.f90:1118
subroutine allele_label_init_random(this)
The (pair of) alleles here are assigned random string labels Not sure if that is necessary for any ap...
Definition: m_genome.f90:437
elemental logical function genome_individual_check_alive(this)
Check if the individual is alive.
Definition: m_genome.f90:1086
elemental integer function allele_value_get(this, nr)
Get the value of the allele.
Definition: m_genome.f90:494
elemental logical function genome_individual_check_dead(this)
Check if the individual is dead (the opposite of is_alive).
Definition: m_genome.f90:1096
elemental subroutine chromosome_recalculate_rank_ids(this)
This subroutine recalculates rank_id indices for consecutive gene objects within the chromosome....
Definition: m_genome.f90:698
elemental subroutine genome_individual_set_alive(this)
Set the individual to be alive, normally this function is used after init or birth.
Definition: m_genome.f90:1058
elemental subroutine allele_value_set(this, set_value, nr)
Set a single value of the allele additive component.
Definition: m_genome.f90:468
subroutine genome_create_zero(this)
Create a new empty genome, and set sex as determined by the sex determination locus....
Definition: m_genome.f90:875
subroutine chromosome_init_allocate_random(this, length, label)
This subroutine initialises the chromosome with, and allocates, random alleles, sets one of them rand...
Definition: m_genome.f90:597
elemental character(len=label_length) function allele_label_get(this)
Get the i-th allele label.
Definition: m_genome.f90:457
subroutine genome_label_set(this, label)
Label genome. If label is not provided, make a random string.
Definition: m_genome.f90:913
subroutine genome_individual_crossover_homol_fix(this, mother, father, pattern_matrix)
Internal fixed genetic crossover backend, exchange blocks of alleles between homologous chromosomes i...
Definition: m_genome.f90:1349
elemental character(len=label_length) function genome_get_sex_label(this)
Get the descriptive sex label: male or female.
Definition: m_genome.f90:1045
subroutine allele_mutate_random(this, prob)
Introduce a random point mutation to a random allele component.
Definition: m_genome.f90:531
subroutine chromosome_mutate_relocate_swap_random(this, prob)
Mutate within the same chromosome, relocate a gene (unit of alleles) to a different random position w...
Definition: m_genome.f90:715
subroutine allele_init_random(this)
Initialises allele with a random integer. Note that we do not set the labels for the alleles here dur...
Definition: m_genome.f90:411
subroutine trait_init_linear_sum_additive_comps_2genes_r(this, this_trait, g_p_matrix, phenotype_min, phenotype_max, label)
Initialise an individual trait of the agent that depends on the genotype. This can be any trait upwar...
Definition: m_genome.f90:1649
elemental subroutine allele_label_set(this, label)
Set labels for the alleles. The subroutine parameter is array of labels.
Definition: m_genome.f90:446
subroutine chromosome_create_allocate_zero(this, length, label)
Init a new chromosome, zero, non-random.
Definition: m_genome.f90:648
subroutine genome_individual_recombine_homol_part_rand_alleles(this, mother, father, exchange_ratio)
Internal genetic recombination backend, exchange individual alleles between homologous chromosomes in...
Definition: m_genome.f90:1229
subroutine trait_init_genotype_gamma2gene(this, this_trait, g_p_matrix, init_val, gerror_cv, label)
Initialise an individual trait of the agent that depends on the genotype. This can be any trait upwar...
Definition: m_genome.f90:1473
elemental logical function genome_get_sex_is_male(this)
Get the logical sex ID of the genome object component.
Definition: m_genome.f90:1017
subroutine trait_set_linear_sum_additive_comps_2genes_r(this, this_trait, g_p_matrix, phenotype_min, phenotype_max)
Set an individual trait of the agent that depends on the genotype. This can be any trait upwards in t...
Definition: m_genome.f90:1745
elemental subroutine genome_individual_set_dead(this)
Set the individual to be dead. Note that this function does not deallocate the individual agent objec...
Definition: m_genome.f90:1077
elemental subroutine allele_create_zero(this)
Create allele with zero value. We don't set labels for alleles here.
Definition: m_genome.f90:425
pure subroutine alleles_value_vector_set(this, values)
Set values of the alleles as a vector, i.e. sets the whole gene values.
Definition: m_genome.f90:483
elemental logical function genome_get_sex_is_female(this)
Get the logical sex ID of the genome object component.
Definition: m_genome.f90:1029
subroutine genome_init_random(this, label)
Initialise the genome at random, and set sex as determined by the sex determination locus.
Definition: m_genome.f90:828
elemental subroutine allele_rank_id_set(this, value_id)
Definition: m_genome.f90:519
subroutine trait_set_genotype_gamma2gene(this, this_trait, g_p_matrix, init_val, gerror_cv)
Set an individual trait of the agent that depends on the genotype. This can be any trait upwards in t...
Definition: m_genome.f90:1563
subroutine allele_mutate_random_batch(this, prob)
Introduce a random mutation of the whole set of additive allele components.
Definition: m_genome.f90:563
elemental subroutine chromosome_sort_rank_id(this)
Sort GENE objects within the CHROMOSOME by their rank_id. The two subroutines qsort and qs_partition_...
Definition: m_genome.f90:324
subroutine chromosome_mutate_relocate_shift_random(this, prob)
Mutate within the same chromosome, relocate a gene (unit of alleles) to a different random position w...
Definition: m_genome.f90:773
elemental character(len=label_length) function genome_label_get(this)
Accessor function to get the genome label. The label is a kind of a (random) text string name of the ...
Definition: m_genome.f90:936
Definition of a movable spatial object. It extends the the_environment::spatial object,...
Definition: m_env.f90:168
This type describes the chromosome object. Chromosome consists of an array of alleles and a descripti...
Definition: m_genome.f90:110
This describes an individual gene object. See the genome structure for as general description and gen...
Definition: m_genome.f90:34
This type describes parameters of the individual agent's genome The genome is an array of allocatable...
Definition: m_genome.f90:160