source: cpp/frams/genetics/fB/fB_oper.cpp @ 1130

Last change on this file since 1130 was 1130, checked in by Maciej Komosinski, 20 months ago

Used std::min(), std::max() explicitly to avoid compiler confusion. Used std::size() explicitly instead of the equivalent macro

File size: 15.0 KB
Line 
1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
2// Copyright (C) 1999-2021  Maciej Komosinski and Szymon Ulatowski.
3// See LICENSE.txt for details.
4
5#include <frams/util/sstring.h>
6#include <vector>
7#include <algorithm>
8#include <frams/param/param.h>
9#include "fB_conv.h"
10#include "fB_general.h"
11#include "fB_oper.h"
12#include "../fH/fH_oper.h"
13
14#define FIELDSTRUCT Geno_fB
15
16static ParamEntry geno_fB_paramtab[] =
17{
18        { "Genetics: fB", 3, FB_MUT_COUNT + FB_XOVER_COUNT, },
19        { "Genetics: fB: Mutation", },
20        { "Genetics: fB: Crossover", },
21        { "fB_mut_substitution", 1, 0, "Substitution", "f 0 1 0.6", FIELD(mutationprobs[FB_SUBSTITUTION]), "Probability of mutation by changing single random letter in genotype", },
22        { "fB_mut_insertion", 1, 0, "Insertion", "f 0 1 0.095", FIELD(mutationprobs[FB_INSERTION]), "Probability of mutation by inserting characters in random place of genotype", },
23        { "fB_mut_nclassins", 1, 0, "Insertion of neuron class definition", "f 0 1 0.005", FIELD(mutationprobs[FB_NCLASSINS]), "Probability of mutation by inserting neuron class definition in random place of genotype", },
24        { "fB_mut_deletion", 1, 0, "Deletion", "f 0 1 0.1", FIELD(mutationprobs[FB_DELETION]), "Probability of mutation by deleting random characters in genotype", },
25        { "fB_mut_duplication", 1, 0, "Duplication", "f 0 1 0.0", FIELD(mutationprobs[FB_DUPLICATION]), "Probability of mutation by copying single *gene* of genotype and appending it to the beginning of this genotype", },
26        { "fB_mut_translocation", 1, 0, "Translocation", "f 0 1 0.15", FIELD(mutationprobs[FB_TRANSLOCATION]), "Probability of mutation by replacing two substrings in genotype", },
27        { "fB_cross_gene_transfer", 2, 0, "Horizontal gene transfer", "f 0 1 0.0", FIELD(crossoverprobs[FB_GENE_TRANSFER]), "Probability of crossing over by transferring single genes from both parents to beginning of each other", },
28        { "fB_cross_crossover", 2, 0, "Crossing over", "f 0 1 1.0", FIELD(crossoverprobs[FB_CROSSING_OVER]), "Probability of crossing over by random distribution of genes from both parents to both children", },
29        { 0, },
30};
31
32#undef FIELDSTRUCT
33
34Geno_fB::Geno_fB()
35{
36        par.setParamTab(geno_fB_paramtab);
37        par.select(this);
38        par.setDefault();
39        supported_format = 'B';
40}
41
42bool Geno_fB::hasStick(const SString &genotype)
43{
44        for (int i = 0; i < fB_GenoHelpers::geneCount(genotype); i++)
45        {
46                int start, end;
47                SString gene = fB_GenoHelpers::getGene(i, genotype, start, end);
48                int endoffset = 0;
49                if (gene.indexOf("zz", 0) != -1) endoffset = 2;
50                if (gene.length() - endoffset < 3)
51                {
52                        return true; // genes with length < 3 are always sticks
53                }
54                else if (gene[2] >= 'a' && gene[2] <= 'i')
55                {
56                        return true; // gene within this range is stick
57                }
58        }
59        return false;
60}
61
62int Geno_fB::checkValidity(const char *geno, const char *genoname)
63{
64        // load genotype
65        SString genotype(geno);
66        SString line;
67        int pos = 0;
68        // if there is no genotype to load, then return error
69        if (!genotype.getNextToken(pos, line, '\n'))
70        {
71                return pos + 1;
72        }
73        // extract dimensions
74        int dims = 0;
75        if (!ExtValue::parseInt(line.c_str(), dims, true, false))
76        {
77                return 1;
78        }
79        // extract next token in order to check if next line starts with "aa"
80        int genstart = genotype.indexOf("aa", 0);
81        if (genstart != pos)
82        {
83                return pos + 1;
84        }
85        // check if rest of characters are lowercase
86        for (int i = genstart; i < genotype.length(); i++)
87        {
88                if (!islower(genotype[i]))
89                {
90                        if (genotype[i] == '"')
91                        {
92                                SString neuclassdef;
93                                int nextid = i + 1;
94                                if (!genotype.getNextToken(nextid, neuclassdef, '"'))
95                                {
96                                        return i + 1;
97                                }
98                                Neuro *neu = new Neuro();
99                                neu->setDetails(neuclassdef);
100
101                                bool isclass = neu->getClass() ? true : false;
102                                delete neu;
103                                if (!isclass)
104                                {
105                                        return i + 1;
106                                }
107                                i = nextid;
108                        }
109                        else
110                        {
111                                return i + 1;
112                        }
113                }
114        }
115        if (!hasStick(genotype))
116        {
117                return 1;
118        }
119        return GENOPER_OK;
120}
121
122int Geno_fB::validate(char *&geno, const char *genoname)
123{
124        // load genotype
125        SString genotype(geno);
126        SString strdims;
127        int pos = 0;
128        if (!genotype.getNextToken(pos, strdims, '\n'))
129        {
130                return GENOPER_OPFAIL;
131        }
132        // parse dimension
133        int dims = 0;
134        if (!ExtValue::parseInt(strdims.c_str(), dims, true, false))
135        {
136                return GENOPER_OPFAIL;
137        }
138        SString line;
139        bool fix = false;
140        int genstart = genotype.indexOf("aa", 0);
141        // if there is no "aa" codon in the beginning of a genotype, then add it
142        if (genstart != pos)
143        {
144                genotype = strdims + "\naa" + genotype.substr(pos);
145                fix = true;
146        }
147        for (int i = pos; i < genotype.length(); i++)
148        {
149                // if character is not alphabetic - error
150                if (!isalpha(genotype[i]))
151                {
152                        if (genotype[i] == '"')
153                        {
154                                SString neuclassdef;
155                                int nextid = i + 1;
156                                if (!genotype.getNextToken(nextid, neuclassdef, '"'))
157                                {
158                                        return i + 1;
159                                }
160                                Neuro *neu = new Neuro();
161                                neu->setDetails(neuclassdef);
162
163                                bool isclass = neu->getClass() ? true : false;
164                                delete neu;
165                                if (!isclass)
166                                {
167                                        return i + 1;
168                                }
169                                i = nextid;
170                        }
171                        else
172                        {
173                                return GENOPER_OPFAIL;
174                        }
175                }
176                // if character is uppercase, then convert it to lowercase
177                else if (isupper(genotype[i]))
178                {
179                        genotype.directWrite()[i] = tolower(genotype[i]);
180                        fix = true;
181                }
182        }
183        // if the genotype does not contain any stick - add it
184        if (!hasStick(genotype))
185        {
186                genotype = SString("aaazz") + genotype;
187        }
188        // if there were any changes - save them
189        if (fix)
190        {
191                free(geno);
192                geno = strdup(genotype.c_str());
193        }
194        return GENOPER_OK;
195}
196
197SString Geno_fB::detokenizeSequence(std::list<SString> *tokenlist)
198{
199        SString res = "";
200        for (std::list<SString>::iterator it = tokenlist->begin(); it != tokenlist->end(); it++)
201        {
202                res += (*it);
203        }
204        return res;
205}
206
207std::list<SString> Geno_fB::tokenizeSequence(const SString &genotype)
208{
209        std::list<SString> res;
210        int i = 0;
211        while (i < genotype.length())
212        {
213                // if character is not alphabetic - error
214                if (isalpha(genotype[i]))
215                {
216                        SString el = "";
217                        el += genotype[i];
218                        res.push_back(el);
219                        i++;
220                }
221                else
222                {
223                        SString neuclassdef;
224                        i++;
225                        genotype.getNextToken(i, neuclassdef, '"');
226                        SString ndef = "\"";
227                        ndef += neuclassdef;
228                        ndef += "\"";
229                        res.push_back(ndef);
230                }
231        }
232        return res;
233}
234
235int Geno_fB::mutate(char *&geno, float &chg, int &method)
236{
237        SString genotype(geno);
238        SString strdims;
239        int pos = 0;
240        genotype.getNextToken(pos, strdims, '\n');
241        SString line;
242        genotype.getNextToken(pos, line, '\n');
243        method = roulette(mutationprobs, FB_MUT_COUNT);
244        switch (method)
245        {
246        case FB_SUBSTITUTION:
247        {
248                std::list<SString> tokenized = tokenizeSequence(line);
249                int rndid = rndUint(tokenized.size()); // select random letter from genotype
250                // increment/decrement character - when overflow happens, this method
251                // uses the "reflect" approach
252                std::list<SString>::iterator it = tokenized.begin();
253                std::advance(it, rndid);
254                SString t = (*it);
255                if ((*it).length() == 1)
256                {
257                        if (rndUint(2) == 0)
258                        {
259                                if ((*it)[0] == 'a') (*it).directWrite()[0] = 'b';
260                                else (*it).directWrite()[0] = (*it)[0] - 1;
261                        }
262                        else
263                        {
264                                if ((*it)[0] == 'z') (*it).directWrite()[0] = 'y';
265                                else (*it).directWrite()[0] = (*it)[0] + 1;
266                        }
267                        chg = 1.0 / line.length();
268                }
269                else
270                {
271                        // first method needs to extract quotes
272                        SString def = (*it);
273                        def = def.substr(1, def.length() - 2);
274                        Geno_fH::mutateNeuronProperties(def);
275                        SString res = "\"";
276                        res += def;
277                        res += "\"";
278                        (*it) = res;
279                        chg = (double)def.length() / line.length();
280                }
281                line = detokenizeSequence(&tokenized);
282                break;
283        }
284        case FB_NCLASSINS:
285        {
286                std::list<SString> tokenized = tokenizeSequence(line);
287                std::list<SString>::iterator it = tokenized.begin();
288                int rndid = rndUint(tokenized.size()); // select random insertion point
289                std::advance(it, rndid);
290                NeuroClass *cls = getRandomNeuroClass(Model::SHAPETYPE_BALL_AND_STICK);
291                if (cls)
292                {
293                        SString classdef = cls->getName();
294                        Geno_fH::mutateNeuronProperties(classdef);
295                        SString res = "\"";
296                        res += classdef;
297                        res += "\"";
298                        tokenized.insert(it, res);
299                        chg = (double)classdef.length() / line.length();
300                        line = detokenizeSequence(&tokenized);
301                        break;
302                }
303        }
304        [[fallthrough]];
305        case FB_INSERTION:
306        {
307                chg = 1.0 / line.length();
308                std::list<SString> tokenized = tokenizeSequence(line);
309                int rndid = rndUint(tokenized.size()); // select random insertion point
310                std::list<SString>::iterator it = tokenized.begin();
311                std::advance(it, rndid);
312                SString letter = "a";
313                letter.directWrite()[0] = 'a' + rndUint(26);
314                tokenized.insert(it, letter);
315                line = detokenizeSequence(&tokenized);
316                break;
317        }
318        case FB_DELETION:
319        {
320                chg = 1.0 / line.length();
321                std::list<SString> tokenized = tokenizeSequence(line);
322                std::list<SString>::iterator it = tokenized.begin();
323                int rndid = rndUint(tokenized.size()); // select random deletion point
324                std::advance(it, rndid);
325                tokenized.erase(it);
326                line = detokenizeSequence(&tokenized);
327                break;
328        }
329        case FB_DUPLICATION:
330        {
331                int rndgene = rndUint(fB_GenoHelpers::geneCount(line));
332                int start, end;
333                SString gene = fB_GenoHelpers::getGene(rndgene, line, start, end);
334                if (gene.indexOf("zz", 0) == -1) gene += "zz";
335                chg = (float)gene.length() / line.length();
336                line = gene + line;
337                break;
338        }
339        case FB_TRANSLOCATION:
340        {
341                std::list<SString> tokenized = tokenizeSequence(line);
342                std::vector<unsigned int> cuts(4);
343                for (int i = 0; i < 4; i++)
344                {
345                        cuts[i] = rndUint(tokenized.size());
346                }
347                std::sort(cuts.begin(), cuts.end());
348                std::vector<std::list<SString>::iterator> iters(4);
349                for (int i = 0; i < 4; i++)
350                {
351                        iters[i] = tokenized.begin();
352                        std::advance(iters[i], cuts[i]);
353                }
354
355                std::list<SString> res;
356                res.insert(res.end(), tokenized.begin(), iters[0]);
357                res.insert(res.end(), iters[2], iters[3]);
358                res.insert(res.end(), iters[1], iters[2]);
359                res.insert(res.end(), iters[0], iters[1]);
360                res.insert(res.end(), iters[3], tokenized.end());
361
362                //              SString first = line.substr(cuts[0], cuts[1] - cuts[0]);
363                //              SString second = line.substr(cuts[2], cuts[3] - cuts[2]);
364                //              SString result = line.substr(0, cuts[0]) + second +
365                //                      line.substr(cuts[1], cuts[2] - cuts[1]) + first + line.substr(cuts[3]);
366                line = detokenizeSequence(&res);
367                chg = (float)(cuts[3] - cuts[2] + cuts[1] - cuts[0]) / line.length();
368                break;
369        }
370        }
371        SString result = strdims + "\n" + line;
372        free(geno);
373        geno = strdup(result.c_str());
374        return GENOPER_OK;
375}
376
377int Geno_fB::crossOver(char *&g1, char *&g2, float& chg1, float& chg2)
378{
379        SString p1(g1);
380        SString p2(g2);
381
382        int dims1 = 0, dims2 = 0;
383        int pos = 0;
384        SString strdims;
385        p1.getNextToken(pos, strdims, '\n');
386        ExtValue::parseInt(strdims.c_str(), dims1, true, false);
387        SString parent1;
388        p1.getNextToken(pos, parent1, '\n');
389
390        pos = 0;
391        p2.getNextToken(pos, strdims, '\n');
392        ExtValue::parseInt(strdims.c_str(), dims2, true, false);
393
394        if (dims1 != dims2)
395        {
396                return GENOPER_OPFAIL;
397        }
398
399        SString parent2;
400        p2.getNextToken(pos, parent2, '\n');
401
402        SString child1 = "";
403        SString child2 = "";
404
405        switch (roulette(crossoverprobs, FB_XOVER_COUNT))
406        {
407        case FB_GENE_TRANSFER:
408        {
409                // get random gene from first parent
410                int choice = rndUint(fB_GenoHelpers::geneCount(parent1));
411                int start, end;
412                SString gene = fB_GenoHelpers::getGene(choice, parent1, start, end);
413                // add this gene to the beginning of the second parent genotype
414                child2 = gene + parent2;
415                chg2 = (float)parent2.length() / (float)child2.length();
416                // do the same for second parent
417                choice = rndUint(fB_GenoHelpers::geneCount(parent2));
418                gene = fB_GenoHelpers::getGene(choice, parent2, start, end);
419                child1 = gene + parent1;
420                chg1 = (float)parent1.length() / (float)child1.length();
421                break;
422        }
423        //      case FB_CROSSING_OVER:
424        //      {
425        //              // iterate through all genes of the first parent and assign them
426        //              // randomly to children
427        //              for (int i = 0; i < fB_GenoHelpers::geneCount(parent1); i++)
428        //              {
429        //                      int start, end;
430        //                      SString gene = fB_GenoHelpers::getGene(i, parent1, start, end);
431        //                      if (rndUint(2) == 0)
432        //                      {
433        //                              child1 += gene;
434        //                              chg1 += 1.0f;
435        //                      }
436        //                      else
437        //                      {
438        //                              child2 += gene;
439        //                      }
440        //              }
441        //              chg1 /= fB_GenoHelpers::geneCount(parent1);
442        //
443        //              // do the same with second parent
444        //              for (int i = 0; i < fB_GenoHelpers::geneCount(parent2); i++)
445        //              {
446        //                      int start, end;
447        //                      SString gene = fB_GenoHelpers::getGene(i, parent2, start, end);
448        //                      if (rndUint(2) == 0)
449        //                      {
450        //                              child1 += gene;
451        //                      }
452        //                      else
453        //                      {
454        //                              child2 += gene;
455        //                              chg2 += 1.0f;
456        //                      }
457        //              }
458        //              chg2 /= fB_GenoHelpers::geneCount(parent2);
459        //              break;
460        //      }
461        case FB_CROSSING_OVER:
462        {
463                // get maximal count of genes from both parents
464                int maxgenecount = std::max(fB_GenoHelpers::geneCountNoNested(parent1),
465                        fB_GenoHelpers::geneCountNoNested(parent2));
466
467                // while there are genes in at least one genotype
468                for (int i = 0; i < maxgenecount; i++)
469                {
470                        SString to1 = "", to2 = "";
471                        int start = 0, end = 0;
472
473                        // if both parents have genes available, then distribute them
474                        if (i < fB_GenoHelpers::geneCountNoNested(parent1) &&
475                                i < fB_GenoHelpers::geneCountNoNested(parent2))
476                        {
477                                if (rndUint(2) == 0)
478                                {
479                                        to1 = fB_GenoHelpers::getNonNestedGene(i, parent1, start, end);
480                                        to2 = fB_GenoHelpers::getNonNestedGene(i, parent2, start, end);
481                                        chg1 += 1.0f;
482                                        chg2 += 1.0f;
483                                }
484                                else
485                                {
486                                        to1 = fB_GenoHelpers::getNonNestedGene(i, parent2, start, end);
487                                        to2 = fB_GenoHelpers::getNonNestedGene(i, parent1, start, end);
488                                }
489                        }
490                        else if (i < fB_GenoHelpers::geneCountNoNested(parent1))
491                        {
492                                if (rndUint(2) == 0)
493                                {
494                                        to1 = fB_GenoHelpers::getNonNestedGene(i, parent1, start, end);
495                                        chg1 += 1.0f;
496                                }
497                                else
498                                {
499                                        to2 = fB_GenoHelpers::getNonNestedGene(i, parent1, start, end);
500                                }
501                        }
502                        else // if (i < fB_GenoHelpers::geneCountNoNested(parent2))
503                        {
504                                if (rndUint(2) == 0)
505                                {
506                                        to1 = fB_GenoHelpers::getNonNestedGene(i, parent2, start, end);
507                                }
508                                else
509                                {
510                                        to2 = fB_GenoHelpers::getNonNestedGene(i, parent2, start, end);
511                                        chg2 += 1.0f;
512                                }
513                        }
514                        child1 += to1;
515                        child2 += to2;
516                }
517
518                chg1 /= fB_GenoHelpers::geneCountNoNested(parent1);
519                chg2 /= fB_GenoHelpers::geneCountNoNested(parent2);
520                break;
521        }
522        }
523
524        free(g1);
525        free(g2);
526        if (child1.length() > 0 && child2.length() == 0)
527        {
528                child1 = strdims + "\n" + child1;
529                g1 = strdup(child1.c_str());
530                g2 = strdup("");
531        }
532        else if (child2.length() > 0 && child1.length() == 0)
533        {
534                child2 = strdims + "\n" + child2;
535                g1 = strdup(child2.c_str());
536                g2 = strdup("");
537        }
538        else
539        {
540                child1 = strdims + "\n" + child1;
541                child2 = strdims + "\n" + child2;
542                g1 = strdup(child1.c_str());
543                g2 = strdup(child2.c_str());
544        }
545        return GENOPER_OK;
546}
547
548uint32_t Geno_fB::style(const char *geno, int pos)
549{
550        char ch = geno[pos];
551        if (isdigit(ch))
552        {
553                while (pos > 0)
554                {
555                        pos--;
556                        if (isdigit(geno[pos]) == 0)
557                        {
558                                return GENSTYLE_CS(0, GENSTYLE_INVALID);
559                        }
560                }
561                return GENSTYLE_RGBS(0, 0, 200, GENSTYLE_BOLD);
562        }
563        if (islower(ch) == 0)
564        {
565                return GENSTYLE_CS(0, GENSTYLE_INVALID);
566        }
567        uint32_t style = GENSTYLE_CS(GENCOLOR_TEXT, GENSTYLE_NONE);
568        if (ch == 'a' && pos > 0 && (geno[pos - 1] == 'a' || geno[pos - 1] == '\n'))
569        {
570                style = GENSTYLE_RGBS(0, 200, 0, GENSTYLE_BOLD);
571        }
572        else if (ch == 'z' && pos > 0 && geno[pos - 1] == 'z')
573        {
574                style = GENSTYLE_RGBS(200, 0, 0, GENSTYLE_BOLD);
575        }
576        return style;
577}
Note: See TracBrowser for help on using the repository browser.