source: cpp/frams/genetics/fH/fH_oper.cpp @ 972

Last change on this file since 972 was 968, checked in by Maciej Komosinski, 4 years ago

Introduced a separate function to mutate neuron connection weight

File size: 13.9 KB
Line 
1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
2// Copyright (C) 1999-2020  Maciej Komosinski and Szymon Ulatowski.
3// See LICENSE.txt for details.
4
5#include "fH_oper.h"
6
7#include <common/loggers/loggers.h>
8#include <frams/param/syntparam.h>
9#include <frams/param/paramobj.h>
10
11#define FIELDSTRUCT Geno_fH
12
13static ParamEntry GENOfHparam_tab[] =
14{
15        { "Genetics: fH", 1, FH_OPCOUNT + FH_ADD_OPCOUNT, },
16        { "fH_mut_addition", 0, 0, "Add element", "f 0 1 0.3", FIELD(operations[FH_ADD]), "Probability of adding new element to genotype", },
17        { "fH_mut_add_joint", 0, 0, " - add joint", "f 0 1 0.33", FIELD(addoperations[FH_ADD_STICK]), "Probability of adding new stick handle", },
18        { "fH_mut_add_neuron", 0, 0, " - add neuron", "f 0 1 0.33", FIELD(addoperations[FH_ADD_NEURO]), "Probability of adding new neuron handle", },
19        { "fH_mut_add_connection", 0, 0, " - add connection", "f 0 1 0.33", FIELD(addoperations[FH_ADD_CONN]), "Probability of adding new connection handle", },
20        { "fH_mut_deletion", 0, 0, "Delete element", "f 0 1 0.1", FIELD(operations[FH_DEL]), "Probability of removing element from genotype", },
21        { "fH_mut_handle", 0, 0, "Modify vectors of handles", "f 0 1 0.3", FIELD(operations[FH_HANDLE]), "Probability of changing values in vectors of handle", },
22        { "fH_mut_property", 0, 0, "Modify properties of handles", "f 0 1 0.3", FIELD(operations[FH_PROP]), "Probability of changing properties of handles", },
23        { 0, },
24};
25
26#undef FIELDSTRUCT
27
28Geno_fH::Geno_fH()
29{
30        par.setParamTab(GENOfHparam_tab);
31        par.select(this);
32        par.setDefault();
33        supported_format = 'H';
34}
35
36int Geno_fH::checkValidity(const char* geno, const char* genoname)
37{
38        LoggerToMemory eh(LoggerBase::Enable | LoggerToMemory::StoreAllMessages, LOG_WARN);
39        fH_Builder builder;
40        // during parsing method tries to approximate error position
41        int err = builder.parseGenotype(geno);
42        if (err != 0)
43        {
44                return err;
45        }
46        if (builder.sticks.size() == 0)
47        {
48                return 1;
49        }
50        int amount = builder.removeNeuronsWithInvalidClasses();
51        if (amount > 0)
52        {
53                return 1;
54        }
55        return 0;
56}
57
58int Geno_fH::validate(char *&geno, const char *genoname)
59{
60        // 'eh' variable is used for "hiding" error and warning messages generated during fH
61        // genotype parsing
62        LoggerToMemory eh(LoggerBase::Enable | LoggerToMemory::StoreAllMessages, LOG_WARN);
63        fH_Builder builder;
64        int err = builder.parseGenotype(geno);
65        // if parsing failed, then it is impossible to repair genotype
66        if (err != 0)
67        {
68                return GENOPER_OPFAIL;
69        }
70        // method removes definitions of neurons that have invalid genotype
71        int amount = builder.removeNeuronsWithInvalidClasses();
72        // if there were any warnings, then rewrite genotype
73        if (eh.getWarningCount() > 0 || amount > 0)
74        {
75                free(geno);
76                geno = strdup(builder.toString().c_str());
77        }
78        return GENOPER_OK;
79}
80
81int Geno_fH::crossOver(char *&g1, char *&g2, float& chg1, float& chg2)
82{
83        fH_Builder *parent1 = new fH_Builder();
84        fH_Builder *parent2 = new fH_Builder();
85
86        // first of all, both parents need to be parsed. If parents cannot be
87        // parsed or their dimensionality differs, then method returns GENOPER_OPFAIL
88        if (parent1->parseGenotype(g1) != 0 || parent2->parseGenotype(g2) != 0 ||
89                parent1->dimensions != parent2->dimensions)
90        {
91                return GENOPER_OPFAIL;
92        }
93
94        // Builders for children are defined
95        fH_Builder *child1 = new fH_Builder();
96        fH_Builder *child2 = new fH_Builder();
97
98        child1->dimensions = child2->dimensions = parent1->dimensions;
99
100        // Children Params are prepared for incoming handles
101        child1->prepareParams();
102        child2->prepareParams();
103
104        int child1count = 0;
105        int child2count = 0;
106
107        for (unsigned int i = 0; i < parent1->sticks.size(); i++)
108        {
109                if (rndUint(2) == 0)
110                {
111                        child1->sticks.push_back(parent1->sticks[i]);
112                        child1count++;
113                }
114                else
115                {
116                        child2->sticks.push_back(parent1->sticks[i]);
117                }
118        }
119
120        for (unsigned int i = 0; i < parent2->sticks.size(); i++)
121        {
122                if (rndUint(2) == 0)
123                {
124                        child1->sticks.push_back(parent2->sticks[i]);
125                }
126                else
127                {
128                        child2->sticks.push_back(parent2->sticks[i]);
129                        child2count++;
130                }
131        }
132
133        // if one of children does not have any sticks, then other child takes
134        // everything else
135        bool skip1 = false;
136        bool skip2 = false;
137        if (child1->sticks.size() == 0) skip1 = true;
138        if (child2->sticks.size() == 0) skip2 = true;
139
140        for (unsigned int i = 0; i < parent1->neurons.size(); i++)
141        {
142                if ((rndUint(2) == 0 || skip2) && !skip1)
143                {
144                        child1->neurons.push_back(parent1->neurons[i]);
145                        child1count++;
146                }
147                else
148                {
149                        child2->neurons.push_back(parent1->neurons[i]);
150                }
151        }
152
153        for (unsigned int i = 0; i < parent2->neurons.size(); i++)
154        {
155                if ((rndUint(2) == 0 || skip2) && !skip1)
156                {
157                        child1->neurons.push_back(parent2->neurons[i]);
158                }
159                else
160                {
161                        child2->neurons.push_back(parent2->neurons[i]);
162                        child2count++;
163                }
164        }
165
166        for (unsigned int i = 0; i < parent1->connections.size(); i++)
167        {
168                if ((rndUint(2) == 0 || skip2) && !skip1)
169                {
170                        child1->connections.push_back(parent1->connections[i]);
171                        child1count++;
172                }
173                else
174                {
175                        child2->connections.push_back(parent1->connections[i]);
176                }
177        }
178
179        for (unsigned int i = 0; i < parent2->connections.size(); i++)
180        {
181                if ((rndUint(2) == 0 || skip2) && !skip1)
182                {
183                        child1->connections.push_back(parent2->connections[i]);
184                }
185                else
186                {
187                        child2->connections.push_back(parent2->connections[i]);
188                        child2count++;
189                }
190        }
191
192        chg1 = (float)child1count / (parent1->sticks.size() + parent1->neurons.size() + parent1->connections.size());
193        chg2 = (float)child2count / (parent2->sticks.size() + parent2->neurons.size() + parent2->connections.size());
194
195        free(g1);
196        free(g2);
197        if (skip1 && !skip2)
198        {
199                g1 = strdup(child2->toString().c_str());
200                g2 = strdup("");
201        }
202        else if (!skip1 && skip2)
203        {
204                g1 = strdup(child1->toString().c_str());
205                g2 = strdup("");
206        }
207        else
208        {
209                g1 = strdup(child1->toString().c_str());
210                g2 = strdup(child2->toString().c_str());
211        }
212
213        child1->sticks.clear();
214        child1->neurons.clear();
215        child1->connections.clear();
216
217        child2->sticks.clear();
218        child2->neurons.clear();
219        child2->connections.clear();
220
221        delete parent1;
222        delete parent2;
223        delete child1;
224        delete child2;
225
226        return GENOPER_OK;
227}
228
229int Geno_fH::mutate(char *&geno, float& chg, int &method)
230{
231        // method only needs to parse genotype - it won't create Model
232        fH_Builder *creature = new fH_Builder();
233        if (creature->parseGenotype(geno) != 0)
234        {
235                return GENOPER_OPFAIL;
236        }
237
238        // used for computing chg
239        unsigned int sumgenes = creature->sticks.size() + creature->neurons.size() + creature->connections.size();
240
241        // if there is only one element in genotype (stick), then deletion would end
242        // up with wrong genotype. If this occurs, deletion is skipped (deletion is
243        // last possible operation listed in #defines, so fH_OPCOUNT - 1 will skip
244        // this mutation, and roulette method will normalize the rest of probabilities)
245        int skipdelete = 0;
246        if (creature->sticks.size() + creature->neurons.size() + creature->connections.size() == 1)
247        {
248                skipdelete = 1;
249        }
250
251        method = roulette(operations, FH_OPCOUNT - skipdelete);
252        switch (method) {
253        case FH_ADD:
254        {
255                fH_Handle *handle = NULL;
256                method = FH_OPCOUNT + roulette(addoperations, FH_ADD_OPCOUNT);
257                if (getActiveNeuroClassCount(Model::SHAPE_BALL_AND_STICK) == 0) method = FH_OPCOUNT + FH_ADD_STICK;
258                switch (method - FH_OPCOUNT)
259                {
260                case FH_ADD_STICK:
261                {
262                        handle = new fH_StickHandle(creature->dimensions, 0, 0);
263                        createHandleVectors(handle, creature->stickparamtab, creature->dimensions);
264                        break;
265                }
266                case FH_ADD_NEURO:
267                {
268                        handle = new fH_NeuronHandle(creature->dimensions, 0, 0);
269                        createHandleVectors(handle, creature->neuronparamtab, creature->dimensions);
270                        break;
271                }
272                case FH_ADD_CONN:
273                {
274                        handle = new fH_ConnectionHandle(creature->dimensions, 0, 0);
275                        createHandleVectors(handle, creature->connectionparamtab, creature->dimensions);
276                        break;
277                }
278                }
279                creature->addHandle(handle);
280                break;
281        }
282        case FH_HANDLE:
283        {
284                ParamEntry *tab = NULL;
285                fH_Handle *handle = NULL;
286                getRandomHandle(creature, handle, tab, true);
287                mutateHandleValues(handle, tab, creature->dimensions, true, false);
288                break;
289        }
290        case FH_PROP:
291        {
292                ParamEntry *tab = NULL;
293                fH_Handle *handle = NULL;
294                getRandomHandle(creature, handle, tab, true);
295                if (handle->type == fHBodyType::NEURON)
296                {
297                        mutateNeuronHandleProperties((fH_NeuronHandle *)handle, creature->neuronparamtab);
298                }
299                else
300                {
301                        mutateHandleValues(handle, tab, creature->dimensions, false, true);
302                }
303                break;
304        }
305        case FH_DEL:
306        {
307                ParamEntry *tab = NULL;
308                fH_Handle *handle = NULL;
309                int todelete = getRandomHandle(creature, handle, tab, true);
310                switch (handle->type)
311                {
312                case JOINT:
313                        creature->sticks.erase(creature->sticks.begin() + todelete);
314                        break;
315                case NEURON:
316                        creature->neurons.erase(creature->neurons.begin() + todelete);
317                        break;
318                case CONNECTION:
319                        creature->connections.erase(creature->connections.begin() + todelete);
320                        break;
321                }
322                delete handle;
323                break;
324        }
325        }
326        free(geno);
327        geno = strdup(creature->toString().c_str());
328        chg = (double)1.0 / sumgenes;
329        delete creature;
330        return GENOPER_OK;
331}
332
333void Geno_fH::mutateHandleValues(fH_Handle *handle, ParamEntry *tab,
334        int dimensions, bool changedimensions, bool changeproperties)
335{
336        Param par(tab, handle->obj);
337        handle->saveProperties(par);
338        if (changedimensions)
339        {
340                int i = rndUint(2 * dimensions);
341                changeDoubleProperty(i, par, handle->type);
342        }
343
344        if (changeproperties)
345        {
346                int i = 2 * dimensions + rndUint(par.getPropCount() - 2 * dimensions);
347                changeDoubleProperty(i, par, handle->type);
348        }
349        handle->loadProperties(par);
350        //ParamObject::freeObject(obj);
351}
352
353void Geno_fH::createHandleVectors(fH_Handle *handle, ParamEntry *tab, int dimensions)
354{
355        void *obj = ParamObject::makeObject(tab);
356        Param par(tab, obj);
357        par.setDefault();
358        double min, max, def;
359        par.getMinMaxDouble(0, min, max, def);
360        for (int i = 0; i < dimensions; i++)
361        {
362                par.setDouble(i, min + rndDouble(max - min));
363                par.setDouble(i + dimensions, min + rndDouble(max - min));
364        }
365        handle->loadProperties(par);
366        if (handle->type != fHBodyType::NEURON)
367        {
368                int i = 2 * dimensions + rndUint(par.getPropCount() - 2 * dimensions);
369                changeDoubleProperty(i, par, handle->type);
370        }
371        else
372        {
373                mutateNeuronHandleProperties((fH_NeuronHandle *)handle, tab, true);
374        }
375}
376
377void Geno_fH::changeDoubleProperty(int id, Param &par, fHBodyType type)
378{
379        double min, max, def;
380        if (*par.type(id) == 'f')
381        {
382                // need to check if property is not weight of connection
383                if (type != fHBodyType::CONNECTION || *par.id(id) != 'w')
384                {
385                        par.getMinMaxDouble(id, min, max, def);
386                        par.setDouble(id, mutateCreep('f', par.getDouble(id), min, max, true));
387                }
388                else
389                {
390                        // if it is weight, then method needs to use mutateNeuProperty
391                        double current = par.getDouble(id);
392                        par.setDouble(id, getMutatedNeuronConnectionWeight(current));
393                }
394        }
395        else
396        {
397                logMessage("Geno_fH", "changeDoubleProperty", LOG_WARN, "fH mutations are not prepared for non-double properties");
398        }
399}
400
401unsigned int Geno_fH::getRandomHandle(fH_Builder *creature, fH_Handle *&handle, ParamEntry *&tab, bool skipalonestick)
402{
403        unsigned int allhandlescount = creature->connections.size() + creature->neurons.size();
404        if (!skipalonestick || creature->sticks.size() > 1)
405        {
406                allhandlescount += creature->sticks.size();
407        }
408        unsigned int toselect = rndUint(allhandlescount);
409        if (toselect < creature->connections.size())
410        {
411                handle = creature->connections[toselect];
412                tab = creature->connectionparamtab;
413                return toselect;
414        }
415        else if (toselect - creature->connections.size() < creature->neurons.size())
416        {
417                toselect -= creature->connections.size();
418                handle = creature->neurons[toselect];
419                tab = creature->neuronparamtab;
420                return toselect;
421        }
422        toselect -= creature->connections.size() + creature->neurons.size();
423        handle = creature->sticks[toselect];
424        tab = creature->stickparamtab;
425        return toselect;
426}
427
428void Geno_fH::mutateNeuronProperties(SString &det) //used also by the "fB" and "fL" encodings
429{
430        Neuro neu;
431        neu.setDetails(det == "" ? "N" : det);
432        GenoOperators::mutateRandomNeuroClassProperty(&neu);
433        det = neu.getDetails();
434}
435
436void Geno_fH::mutateNeuronHandleProperties(fH_NeuronHandle *handle, ParamEntry *tab, bool userandomclass)
437{
438        Param hpar(tab, handle->obj);
439        SString det;
440        if (userandomclass)
441        {
442                NeuroClass *nc = getRandomNeuroClass(Model::SHAPE_BALL_AND_STICK); // checking that neuron classes are available should be done before
443                det = nc->getName();
444        }
445        else
446        {
447                det = hpar.getStringById("d");
448        }
449        mutateNeuronProperties(det);
450        hpar.setStringById("d", det);
451}
452
453//uint32_t Geno_fH::style(const char *geno, int pos)
454//{
455//      char ch = geno[pos];
456//      uint32_t style = GENSTYLE_CS(0, GENSTYLE_STRIKEOUT);
457//      if (pos == 0 || geno[pos - 1] == '\n' || ch == ':') // single-character handle type and all colons
458//      {
459//              style = GENSTYLE_CS(GENCOLOR_TEXT, GENSTYLE_BOLD);
460//      }
461//      else if (isalpha(ch)) // properties name
462//      {
463//              style = GENSTYLE_RGBS(0, 200, 0, GENSTYLE_BOLD);
464//      }
465//      else if (isdigit(ch) || strchr(",.=", ch)) // properties values
466//      {
467//              style = GENSTYLE_CS(GENCOLOR_TEXT, GENSTYLE_NONE);
468//      }
469//      else if (ch == '\"')
470//      {
471//              style = GENSTYLE_RGBS(200, 0, 0, GENSTYLE_BOLD);
472//      }
473//
474//      return style;
475//}
476
477uint32_t Geno_fH::style(const char *g, int pos)
478{
479        char ch = g[pos];
480        uint32_t style = GENSTYLE_CS(0, GENSTYLE_NONE); //default, should be changed below
481
482        int pp = pos; //detect comment line
483        while (pp > 1 && g[pp - 1] != '\n') pp--;
484        if (g[pp] == '#') return GENSTYLE_RGBS(0, 220, 0, GENSTYLE_NONE); //comment line
485
486        if (pos == 0 || g[pos - 1] == '\n' || ch == ':' || ch == ',') style = GENSTYLE_CS(0, GENSTYLE_BOLD); else
487                if (ch == '\"') style = GENSTYLE_RGBS(150, 0, 0, GENSTYLE_BOLD); else
488                {
489                        int cudz = 0, neuclass = 1; //ile cudz. do poczatku linii; czy w nazwie neuroklasy?
490                        while (pos > 0)
491                        {
492                                pos--;
493                                if (g[pos] == '\"') cudz++;
494                                if (cudz == 0 && (g[pos] == ':' || g[pos] == ',')) neuclass = 0;
495                                if (g[pos] == '\n') break;
496                        }
497                        if (cudz % 2)
498                        {
499                                if (neuclass) style = GENSTYLE_RGBS(150, 0, 150, GENSTYLE_BOLD); else //neuroclass
500                                        if (isalpha(ch)) style = GENSTYLE_RGBS(255, 140, 0, GENSTYLE_BOLD); else //property
501                                                style = GENSTYLE_RGBS(200, 0, 0, GENSTYLE_NONE);
502                        }
503                        else
504                                if (isalpha(ch)) style = GENSTYLE_RGBS(0, 0, 200, GENSTYLE_BOLD);
505                }
506        return style;
507}
Note: See TracBrowser for help on using the repository browser.