source: cpp/frams/genetics/f9/f9_oper.cpp @ 1333

Last change on this file since 1333 was 1330, checked in by Maciej Komosinski, 5 days ago

f9: less diversifying, more distance-preserving (DPX) crossover (children now have a similar length to parents). Additionally, avoids cutpoints that would cause a simple swap of complete parent genotypes.

  • Property svn:eol-style set to native
File size: 5.2 KB
Line 
1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
2// Copyright (C) 1999-2024  Maciej Komosinski and Szymon Ulatowski.
3// See LICENSE.txt for details.
4
5#include "f9_oper.h"
6#include "f9_conv.h"
7#include <common/nonstd.h> //rndUint, rndDouble
8#include <string.h>
9#include <assert.h>
10
11
12#define FIELDSTRUCT GenoOper_f9
13static ParamEntry geno_f9_paramtab[] =
14{
15        { "Genetics: f9", 1, 1, },
16        { "f9_mut", 0, 0, "Mutation probability", "f 0 1 0", FIELD(mut_prob), "How many genes should be mutated during a single mutation (1=all genes, 0.1=ten percent, 0=one gene)", },
17        { 0, },
18};
19#undef FIELDSTRUCT
20
21
22GenoOper_f9::GenoOper_f9()
23{
24        par.setParamTab(geno_f9_paramtab);
25        par.select(this);
26        par.setDefault();
27        supported_format = '9';
28}
29
30int GenoOper_f9::checkValidity(const char* gene, const char *genoname)
31{
32        if (!gene[0]) return 1; //empty is not valid
33        bool ok = true;
34        size_t i;
35        for (i = 0; i < strlen(gene); i++) if (!strchr(turtle_commands_f9, gene[i])) { ok = false; break; }
36        return ok ? GENOPER_OK : int(i) + 1;
37}
38
39///Remove all invalid letters from the genotype
40int GenoOper_f9::validate(char *&gene, const char *genoname)
41{
42        SString validated; //new genotype (everything except turtle_commands_f9 is skipped)
43        for (size_t i = 0; i < strlen(gene); i++)
44                if (strchr(turtle_commands_f9, gene[i])) validated += gene[i];  //validated contains only turtle_commands_f9
45        free(gene);
46        gene = strdup(validated.c_str()); //reallocate
47        return GENOPER_OK;
48}
49
50void GenoOper_f9::mutate_replace(char *gene, int i)
51{
52        char oldgene = gene[i];
53        const char *pos = strchr(turtle_commands_f9, oldgene);
54        if (pos == NULL) //gene not in the set of allowed commands
55                gene[i] = turtle_commands_f9[rndUint(turtle_commands_f9_count)];
56        else
57                gene[i] = turtle_commands_f9[((pos - turtle_commands_f9) + 1 + rndUint(turtle_commands_f9_count - 1)) % turtle_commands_f9_count]; //always change to a different command
58        //printf("%c %c\n", oldgene, gene[i]);
59        assert(gene[i] != oldgene);
60}
61
62void GenoOper_f9::mutate_add_or_del(char *& gene, int len, int add_0_del_1)
63{
64        string newgeno(gene);
65        if (add_0_del_1 == 0) //add
66        {
67                int p = rndUint(len + 1);  //random location
68                //printf("before add: %s\n",(const char*)newgeno);
69                newgeno = newgeno.substr(0, p) + string(turtle_commands_f9 + rndUint(turtle_commands_f9_count), 1) + newgeno.substr(p);
70                //printf("after add: %s\n",(const char*)newgeno);
71        }
72        else //delete
73        {
74                int p = rndUint(len);  //random location
75                //printf("before delete: %s\n",(const char*)newgeno);
76                newgeno = newgeno.substr(0, p) + newgeno.substr(p + 1);
77                //printf("after delete: %s\n",(const char*)newgeno);
78        }
79        free(gene);
80        gene = strdup(newgeno.c_str()); //reallocate
81}
82
83///A simple mutation
84int GenoOper_f9::mutate(char *&gene, float &chg, int &method)
85{
86        method = 0;
87        int changes = 0, len = (int)strlen(gene);
88
89        if (mut_prob > 0)
90                for (int i = 0; i < len; i++)
91                {
92                        if (rndDouble(1) < mut_prob) //normalize prob with the length of the genotype
93                        {
94                                mutate_replace(gene, i);
95                                changes++;
96                        }
97                }
98
99        if (rndDouble(1) < mut_prob) //add or delete a random char
100        {
101                int change_add_or_del = 1 + (len > 1); //either add a new symbol (1), or delete a random symbol (+(len>1))
102                int add_0_del_1 = rndUint(change_add_or_del);
103                mutate_add_or_del(gene, len, add_0_del_1);
104                changes++;
105        }
106
107        if (changes == 0) //if nothing changed so far, then fallback to minimal change: always changes one symbol
108        {
109                int change_what = len + 1 + (len > 1); //either replace an existing gene, or add a new symbol (+1), or delete a random symbol (+(len>1))
110                int change_selected = rndUint(change_what);
111                if (change_selected < len)
112                        mutate_replace(gene, change_selected);
113                else
114                        mutate_add_or_del(gene, len, change_selected - len);
115                changes++;
116        }
117
118        chg = (float)changes / len;
119        return changes > 0 ? GENOPER_OK : GENOPER_OPFAIL; //no changes? (should never happen) => OPFAIL so that GenMan will call mutate again
120}
121
122///A simple one-point crossover
123int GenoOper_f9::crossOver(char *&g1, char *&g2, float& chg1, float& chg2)
124{
125        // printf("%s  \t  %s\n", g1, g2);
126        int len1 = (int)strlen(g1), len2 = (int)strlen(g2);
127        double p = rndDouble(1); //random cut point
128        int p1 = 1 + int(p * (len1 - 1));  //...mapped to the first parent, avoiding cuts that would cause a simple swap of the entire genotype. Unavoidable for a single-letter parent
129        int p2 = 1 + int(p * (len2 - 1));  //...mapped to the second parent, as above
130        char *child1 = (char*)malloc(p1 + len2 - p2 + 1);
131        char *child2 = (char*)malloc(p2 + len1 - p1 + 1);
132        strncpy(child1, g1, p1);   strcpy(child1 + p1, g2 + p2);
133        strncpy(child2, g2, p2);   strcpy(child2 + p2, g1 + p1);
134        free(g1); g1 = child1;
135        free(g2); g2 = child2;
136        //children non-empty if parents non-empty (valid)
137        chg1 = (float)p1 / strlen(child1);
138        chg2 = (float)p2 / strlen(child2);
139        // printf("%s  \t  %s\n", child1, child2);
140        // printf("%g  \t  %g\n", chg1, chg2);
141        return GENOPER_OK;
142}
143
144///Applying some colors and font styles...
145uint32_t GenoOper_f9::style(const char *g, int pos)
146{
147        char ch = g[pos];
148        uint32_t style = GENSTYLE_CS(0, GENSTYLE_INVALID); //default, should be changed below
149        char *ptr = strchr((char*)turtle_commands_f9, ch);
150        if (ptr)
151        {
152                int pos = ptr - turtle_commands_f9;
153                int axis = pos / 2;
154                style = GENSTYLE_RGBS(axis == 0 ? 200 : 0, axis == 2 ? 200 : 0, axis == 1 ? 200 : 0, GENSTYLE_NONE);
155        }
156        return style;
157}
Note: See TracBrowser for help on using the repository browser.