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

Last change on this file since 829 was 803, checked in by Maciej Komosinski, 6 years ago

Performance improvements, including avoiding unnecessary passing of objects by value

File size: 14.3 KB
Line 
1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
2// Copyright (C) 1999-2018  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 (randomN(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 (randomN(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 ((randomN(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 ((randomN(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 ((randomN(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 ((randomN(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() == 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 = randomN(2 * dimensions);
341                changeDoubleProperty(i, par, handle->type);
342        }
343
344        if (changeproperties)
345        {
346                int i = 2 * dimensions + randomN(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 + rnd0N(max - min));
363                par.setDouble(i + dimensions, min + rnd0N(max - min));
364        }
365        handle->loadProperties(par);
366        if (handle->type != fHBodyType::NEURON)
367        {
368                int i = 2 * dimensions + randomN(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, mutateNeuProperty(current, NULL, -1));
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 = randomN(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)
429{
430        Neuro neu;
431        det = det == "" ? "N" : det;
432        neu.setDetails(det == "" ? "N" : det);
433
434        SyntParam par = neu.classProperties();
435
436        if (par.getPropCount() > 0)
437        {
438                int i = randomN(par.getPropCount());
439                if (*par.type(i) == 'f')
440                {
441                        double change = mutateNeuProperty(par.getDouble(i), &neu, 100 + i);
442                        par.setDouble(i, change);
443                }
444                SString line;
445                int tmp = 0;
446                par.update(&line);
447                SString props;
448                line.getNextToken(tmp, props, '\n'); // removal of newline character
449                if (props != "")
450                {
451                        det = neu.getClass()->name;
452                        det += ": ";
453                        det += props;
454                }
455        }
456}
457
458void Geno_fH::mutateNeuronHandleProperties(fH_NeuronHandle *handle, ParamEntry *tab, bool userandomclass)
459{
460        Neuro neu;
461        Param hpar(tab, handle->obj);
462        SString det = hpar.getStringById("d");
463        neu.setDetails(det == "" ? "N" : det);
464        NeuroClass *nc = neu.getClass();
465
466        if (userandomclass)
467        {
468                nc = getRandomNeuroClass();
469                // checking of neuron class availability should be checked before
470        }
471
472        det = nc->getName();
473
474        mutateNeuronProperties(det);
475
476        hpar.setStringById("d", det);
477}
478
479//uint32_t Geno_fH::style(const char *geno, int pos)
480//{
481//      char ch = geno[pos];
482//      uint32_t style = GENSTYLE_CS(0, GENSTYLE_STRIKEOUT);
483//      if (pos == 0 || geno[pos - 1] == '\n' || ch == ':') // single-character handle type and all colons
484//      {
485//              style = GENSTYLE_CS(GENCOLOR_TEXT, GENSTYLE_BOLD);
486//      }
487//      else if (isalpha(ch)) // properties name
488//      {
489//              style = GENSTYLE_RGBS(0, 200, 0, GENSTYLE_BOLD);
490//      }
491//      else if (isdigit(ch) || strchr(",.=", ch)) // properties values
492//      {
493//              style = GENSTYLE_CS(GENCOLOR_TEXT, GENSTYLE_NONE);
494//      }
495//      else if (ch == '\"')
496//      {
497//              style = GENSTYLE_RGBS(200, 0, 0, GENSTYLE_BOLD);
498//      }
499//
500//      return style;
501//}
502
503uint32_t Geno_fH::style(const char *g, int pos)
504{
505   char ch=g[pos];
506   uint32_t style=GENSTYLE_CS(0,GENSTYLE_NONE); //default, should be changed below
507
508   int pp=pos; //detect comment line
509   while (pp>1 && g[pp-1]!='\n') pp--;
510   if (g[pp]=='#') return GENSTYLE_RGBS(0,220,0,GENSTYLE_NONE); //comment line
511
512   if (pos==0 || g[pos-1]=='\n' || ch==':' || ch==',') style=GENSTYLE_CS(0,GENSTYLE_BOLD); else
513     if (ch=='\"') style=GENSTYLE_RGBS(150,0,0,GENSTYLE_BOLD); else
514     {
515      int cudz=0,neuclass=1; //ile cudz. do poczatku linii; czy w nazwie neuroklasy?
516      while (pos>0)
517      {
518         pos--;
519         if (g[pos]=='\"') cudz++;
520         if (cudz==0 && (g[pos]==':' || g[pos]==',')) neuclass=0;
521         if (g[pos]=='\n') break;
522      }
523      if (cudz%2)
524      {
525         if (neuclass) style=GENSTYLE_RGBS(150,0,150,GENSTYLE_BOLD); else //neuroclass
526           if (isalpha(ch)) style=GENSTYLE_RGBS(255,140,0,GENSTYLE_BOLD); else //property
527             style=GENSTYLE_RGBS(200,0,0,GENSTYLE_NONE);
528      } else
529        if (isalpha(ch)) style=GENSTYLE_RGBS(0,0,200,GENSTYLE_BOLD);
530     }
531   return style;
532}
Note: See TracBrowser for help on using the repository browser.