Advertisement
David_Fenger

evsim.c version 0.1

Feb 17th, 2019
161
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 7.85 KB | None | 0 0
  1. // evsim.c
  2. // Simple simulation of genetic drift
  3. // Arguments: m n g l s d
  4. // m = mutation rate (capped at 1.0) per birth
  5. // n = population size [default: 1000]
  6. // g = generations (needs to be at least 4*n to get close to steady state) [default: 40*n]
  7. // l = fraction of mutatinos that are lethal (0.01 = 1%) [default: 0.01]
  8. // s = fraction of mutations that are strongly negative (10% lower survival chance) [default: 0.02]
  9. // d = fraction of mutations that are mildly negative (1% lower survival chance) [default: 0.03]
  10. //
  11. // If desired, consider a 'p' that is fraction of mutations that increase survival by 1%
  12. //
  13. // Version 0.1 - David Fenger
  14.  
  15. #include <stdlib.h>
  16. #include <stdio.h>
  17. #include <math.h>
  18. #include <time.h>
  19.  
  20. #define GENE_BYTES 10000
  21.  
  22. float m_rate;
  23. long pop_size = 1000;
  24. long generations, cur_generation;
  25. long cur_pop;
  26. long min_pop;
  27. float neg_l=0.01,neg_s=0.02,neg_m=0.03;
  28. long neg_lb,neg_sb,neg_mb;
  29. unsigned char *p_genes, *p_offspring;
  30. unsigned char fixed_genes[GENE_BYTES];
  31. long c_fixed = 0;
  32. long first_fix = 0;
  33. long first_half_fixed = -1;
  34. float inv_rmax = 1.0f / (float)RAND_MAX;
  35. char bit_count[256];
  36.  
  37. // TODO: Find a good rand(), don't forget to seed it.
  38. // TODO: Print seed value used, for future reference.  And add as another argument...  [default = clock]
  39.  
  40. float frand() {
  41.     return (float)rand() * inv_rmax;
  42. }
  43.  
  44. long lrand(long n) {
  45.     return rand() % n;
  46. }
  47.  
  48. // Note: this is not as random as I'd like, as even a 1% mutation rate will occasionally
  49. // cause a double mutation (1 time in 10,000).  But it's close enough for the purpose, and fast.
  50. void copy_with_mutate(int icur, int ioff) {
  51.     long i,loc;
  52.    
  53.     memcpy(p_offspring+ioff*GENE_BYTES, p_genes+icur*GENE_BYTES, GENE_BYTES);
  54.    
  55.     // Roll for mutation
  56.     if(frand() <= m_rate) {
  57.         loc = lrand(GENE_BYTES*8);
  58.         p_offspring[ioff*GENE_BYTES+loc/8] |= 1<<(loc%8);
  59.     }
  60. }
  61.  
  62. void copy_to_pop(long icur, long ioff)
  63. {
  64.     memcpy(p_genes+icur*GENE_BYTES, p_offspring+ioff*GENE_BYTES, GENE_BYTES);
  65. }
  66.  
  67. void do_breed() {
  68.     // Fill offspring array
  69.     long i;
  70.     // To be fair, everyone gets at least one offspring
  71.     for(i=0;i<cur_pop;i++) {
  72.         copy_with_mutate(i,i);
  73.     }
  74.     // Need 2xpop_size offspring to cull from
  75.     for(;i<2*pop_size;i++) {
  76.         copy_with_mutate(lrand(cur_pop), i);
  77.     }
  78. }
  79.  
  80. float calc_viability(long ioff)
  81. {
  82.     float v = 0.5f; // Baseline survivability
  83.     long i;
  84.     char *p = p_offspring+ioff*GENE_BYTES;
  85.     // Lethality - return 0 for any lethal mutations
  86.     for(i=0;i<neg_lb;i++) {
  87.         if(p[i] > 0) return 0.0f;
  88.     }
  89.  
  90.     // Strongly negative mutations - each reduces v by 0.1;
  91.     for(;i<neg_lb+neg_sb;i++) {
  92.         if(p[i] > 0) v -= bit_count[p[i]] * 0.1;
  93.     }
  94.     if(v <= 0.0f) return 0.0f;
  95.  
  96.     // Mildly negative mutations - each reduces v by 0.01;
  97.     for(;i<neg_lb+neg_sb+neg_mb;i++) {
  98.         if(p[i] > 0) v -= bit_count[p[i]] * 0.01;
  99.     }
  100.     if(v <= 0.0f) return 0.0f;
  101.     return v;
  102. }
  103.  
  104. void do_cull() {
  105.     // Kill off children, copying survivors into original population
  106.     long ipop = 0;
  107.     long ioff = 0;
  108.     float v,vt;
  109.     vt = 0;
  110.     for(ioff = 0; ioff < 2*pop_size; ioff++) {
  111.         // Compute viability of offspring
  112.         v = calc_viability(ioff);
  113.         vt+=v;
  114.         if(frand() < v) {
  115.             copy_to_pop(ipop, ioff);
  116.             ipop++;
  117.         }
  118.     }
  119.     cur_pop = ipop;
  120.     if(cur_pop < min_pop) min_pop = cur_pop;
  121.     if(cur_pop <= 0) {
  122.         printf("WHOOPS - population is dead.\n");
  123.         return(-5);
  124.     }
  125. }
  126.  
  127. void do_evaluate(long cur_generation) {
  128.     // Count # of fixed genes
  129.     long ipop, igene, c;
  130.    
  131.     memcpy(fixed_genes, p_genes, GENE_BYTES); // Seed with first organism
  132.    
  133.     for(ipop = 1; ipop < cur_pop; ipop++) {
  134.         for(igene=0;igene<GENE_BYTES;igene++) {
  135.             fixed_genes[igene] &= p_genes[ipop*GENE_BYTES+igene];
  136.         }
  137.     }
  138.    
  139.     // Count bits set in fixed_genes - these are mutations possessed by entire population
  140.     c = 0;
  141.     for(igene=0;igene<GENE_BYTES;igene++) {
  142.         c += bit_count[fixed_genes[igene]];
  143.     }
  144.     if(c > c_fixed) {
  145.         if(c_fixed == 0) {
  146.             first_fix = cur_generation;
  147.             printf("ff gen %d c %d\n",cur_generation,c);
  148.         }
  149.         if(cur_generation > generations / 2 && first_half_fixed < 0) {
  150.             first_half_fixed = c_fixed;
  151.         }
  152.         c_fixed = c;
  153.     }
  154. }
  155.  
  156. void show_status(long cur_generation)
  157. {
  158.     printf("Gen %d: pop %d min %d fixed %d\n",cur_generation,cur_pop,min_pop,c_fixed);
  159. }
  160.  
  161. void show_final()
  162. {
  163.     printf("After %d generations: population = %d (min %d)\n",generations, cur_pop, min_pop);
  164.     printf("Total fixed genes: %d\n",c_fixed);
  165.     printf(" first fix: generation %d\n",first_fix);
  166.     printf(" fixed in first half %d\n",first_half_fixed);
  167.     if(c_fixed > 1) {
  168.         printf(" rate (post first): %g per generation\n", (c_fixed-1)/(float)(generations-first_fix));
  169.         printf(" rate (last half of run): %g\n",(c_fixed-first_half_fixed)/(float)(generations*0.5f));
  170.     }
  171.     // For fun
  172.     long icur,igene;
  173.     long long c_mut=0;
  174.     for(icur=0;icur<cur_pop;icur++) {
  175.         for(igene=0;igene<GENE_BYTES;igene++) {
  176.             c_mut += bit_count[p_genes[icur*GENE_BYTES+igene]];
  177.         }
  178.     }
  179.     printf("Total mutations across population: %lld, mutations per organism %lld\n",
  180.         c_mut, c_mut / cur_pop);
  181. }
  182.  
  183. int main(int argc, char **argv)
  184. {
  185.     // Parse arguments
  186.     if(argc<2) {
  187.         // Print usage and exit
  188.         printf("Usage: %s m [n [g [l [s [d]]]]]\n",argv[0]);
  189.         printf("  m = mutation rate (capped at 1.0) per birth\n");
  190.         printf("  n = population size [default: 1000]\n");
  191.         printf("  g = generations (needs to be at least 4*n to get close to steady state) [default: 40*n]\n");
  192.         printf("  l = fraction of mutations that are lethal (0.01 = 1%) [default: 0.01]\n");
  193.         printf("  s = fraction of mutations that are strongly negative (10% lower survival chance) [default: 0.02]\n");
  194.         printf("  d = fraction of mutations that are mildly negative (1% lower survival chance) [default: 0.03]\n");
  195.         return(-1);
  196.     }
  197.     printf("argc %d\n",argc);
  198.     m_rate = atof(argv[1]);
  199.     pop_size = 1000;
  200.     neg_l = 0.01;
  201.     neg_s = 0.02;
  202.     neg_m = 0.03;
  203.     if(m_rate > 1.0f) m_rate = 1.0f;
  204.     if(argc > 2) pop_size = atol(argv[2]);
  205.     if(argc > 3) generations = atol(argv[3]);
  206.     else generations = 40*pop_size;
  207.     if(argc > 4) neg_l = atof(argv[4]);
  208.     if(argc > 5) neg_s = atof(argv[5]);
  209.     if(argc > 6) neg_m = atof(argv[6]);
  210.    
  211.     // Check negatives
  212.     if(neg_l < 0 || neg_s < 0 || neg_m < 0) {
  213.         printf("Negative mutation probabilities cannot be less than 0. (%f %f %f)\n",
  214.             neg_l,neg_s,neg_m);
  215.         return(-2);
  216.     }
  217.     if(neg_l + neg_s + neg_m > 1.0f) {
  218.         printf("Negative mutation probabilities cannot sum to more than 1.0: (%f %f %f)\n",
  219.             neg_l,neg_s,neg_m);
  220.         return(-3);
  221.     }
  222.     neg_lb = neg_l * GENE_BYTES;
  223.     neg_sb = neg_s * GENE_BYTES;
  224.     neg_mb = neg_m * GENE_BYTES;
  225.     printf("neg_bytes %d %d %d\n",neg_lb,neg_sb,neg_mb);
  226.     // Allocate memory for organisms
  227.     p_genes = calloc(pop_size*2, GENE_BYTES);
  228.     p_offspring = calloc(pop_size*2, GENE_BYTES);
  229.     if(p_genes == NULL || p_offspring == NULL) {
  230.         printf("Insufficient memory to host %d organisms and 2 x %d offspring, please try a lower population size.\n",
  231.             pop_size);
  232.         if(p_genes != NULL) free(p_genes);
  233.         return(-4);
  234.     }
  235.    
  236.     // Print our values before we start
  237.     printf("Starting run with mutation rate = %f, population size = %d, running %d generations.\n",
  238.         m_rate, pop_size, generations);
  239.     printf(" Negative mutation rates: %f lethal, %f strongly negative, %f mildly negative\n",
  240.         neg_l, neg_s, neg_m);
  241.        
  242.     // Seed RNG
  243.     long tval = (long)time(NULL);
  244.     printf(" srand(%d)\n",tval);
  245.     srand(tval);
  246.    
  247.    
  248.     // Set up the optimized bit counter array
  249.     for(int i=0;i<=255;i++) {
  250.         int bits = 0;
  251.         int tmp = i;
  252.         while(tmp != 0) {
  253.             tmp &= (tmp-1);
  254.             bits++;
  255.         }
  256.         bit_count[i] = bits;
  257.     }
  258.    
  259.     // Initialize - p_genes already 0 from calloc
  260.     cur_pop = pop_size;
  261.     min_pop = pop_size;
  262.     // Loop and evaluate
  263.     for(long i=0;i<generations;i++) {
  264.         do_breed();
  265.         do_cull();
  266.         if(i % 10 == 0) {
  267.             do_evaluate(i);
  268.         }
  269.         if(i % 1000 == 0) {
  270.             show_status(i);
  271.         }
  272.     }
  273.     show_final();
  274.     free(p_genes);
  275.     free(p_offspring);
  276.    
  277.     // TODO: Wrap the above in a "# of experiments" loop later
  278. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement