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

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

A more complete implementation of fB, fH, fL

File size: 14.2 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                switch (method - FH_OPCOUNT)
258                {
259                case FH_ADD_STICK:
260                {
261                        handle = new fH_StickHandle(creature->dimensions, 0, 0);
262                        createHandleVectors(handle, creature->stickparamtab, creature->dimensions);
263                        break;
264                }
265                case FH_ADD_NEURO:
266                {
267                        handle = new fH_NeuronHandle(creature->dimensions, 0, 0);
268                        createHandleVectors(handle, creature->neuronparamtab, creature->dimensions);
269                        break;
270                }
271                case FH_ADD_CONN:
272                {
273                        handle = new fH_ConnectionHandle(creature->dimensions, 0, 0);
274                        createHandleVectors(handle, creature->connectionparamtab, creature->dimensions);
275                        break;
276                }
277                }
278                creature->addHandle(handle);
279                break;
280        }
281        case FH_HANDLE:
282        {
283                ParamEntry *tab = NULL;
284                fH_Handle *handle = NULL;
285                getRandomHandle(creature, handle, tab, true);
286                mutateHandleValues(handle, tab, creature->dimensions, true, false);
287                break;
288        }
289        case FH_PROP:
290        {
291                ParamEntry *tab = NULL;
292                fH_Handle *handle = NULL;
293                getRandomHandle(creature, handle, tab, true);
294                if (handle->type == fHBodyType::NEURON)
295                {
296                        mutateNeuronHandleProperties((fH_NeuronHandle *)handle, creature->neuronparamtab);
297                }
298                else
299                {
300                        mutateHandleValues(handle, tab, creature->dimensions, false, true);
301                }
302                break;
303        }
304        case FH_DEL:
305        {
306                ParamEntry *tab = NULL;
307                fH_Handle *handle = NULL;
308                int todelete = getRandomHandle(creature, handle, tab, true);
309                switch (handle->type)
310                {
311                case JOINT:
312                        creature->sticks.erase(creature->sticks.begin() + todelete);
313                        break;
314                case NEURON:
315                        creature->neurons.erase(creature->neurons.begin() + todelete);
316                        break;
317                case CONNECTION:
318                        creature->connections.erase(creature->connections.begin() + todelete);
319                        break;
320                }
321                delete handle;
322                break;
323        }
324        }
325        free(geno);
326        geno = strdup(creature->toString().c_str());
327        chg = (double)1.0 / sumgenes;
328        delete creature;
329        return GENOPER_OK;
330}
331
332void Geno_fH::mutateHandleValues(fH_Handle *handle, ParamEntry *tab,
333        int dimensions, bool changedimensions, bool changeproperties)
334{
335        Param par(tab, handle->obj);
336        handle->saveProperties(par);
337        if (changedimensions)
338        {
339                int i = randomN(2 * dimensions);
340                changeDoubleProperty(i, par, handle->type);
341        }
342
343        if (changeproperties)
344        {
345                int i = 2 * dimensions + randomN(par.getPropCount() - 2 * dimensions);
346                changeDoubleProperty(i, par, handle->type);
347        }
348        handle->loadProperties(par);
349        //ParamObject::freeObject(obj);
350}
351
352void Geno_fH::createHandleVectors(fH_Handle *handle, ParamEntry *tab, int dimensions)
353{
354        void *obj = ParamObject::makeObject(tab);
355        Param par(tab, obj);
356        par.setDefault();
357        double min, max, def;
358        par.getMinMaxDouble(0, min, max, def);
359        for (int i = 0; i < dimensions; i++)
360        {
361                par.setDouble(i, min + rnd0N(max - min));
362                par.setDouble(i + dimensions, min + rnd0N(max - min));
363        }
364        handle->loadProperties(par);
365        if (handle->type != fHBodyType::NEURON)
366        {
367                int i = 2 * dimensions + randomN(par.getPropCount() - 2 * dimensions);
368                changeDoubleProperty(i, par, handle->type);
369        }
370        else
371        {
372                mutateNeuronHandleProperties((fH_NeuronHandle *)handle, tab, true);
373        }
374}
375
376void Geno_fH::changeDoubleProperty(int id, Param &par, fHBodyType type)
377{
378        double min, max, def;
379        if (*par.type(id) == 'f')
380        {
381                // need to check if property is not weight of connection
382                if (type != fHBodyType::CONNECTION || *par.id(id) != 'w')
383                {
384                        par.getMinMaxDouble(id, min, max, def);
385                        par.setDouble(id, mutateCreep('f', par.getDouble(id), min, max, true));
386                }
387                else
388                {
389                        // if it is weight, then method needs to use mutateNeuProperty
390                        double current = par.getDouble(id);
391                        par.setDouble(id, mutateNeuProperty(current, NULL, -1));
392                }
393        }
394        else
395        {
396                logMessage("Geno_fH", "changeDoubleProperty", LOG_WARN, "fH mutations are not prepared for non-double properties");
397        }
398}
399
400unsigned int Geno_fH::getRandomHandle(fH_Builder *creature, fH_Handle *&handle, ParamEntry *&tab, bool skipalonestick)
401{
402        unsigned int allhandlescount = creature->connections.size() + creature->neurons.size();
403        if (!skipalonestick || creature->sticks.size() > 1)
404        {
405                allhandlescount += creature->sticks.size();
406        }
407        unsigned int toselect = randomN(allhandlescount);
408        if (toselect < creature->connections.size())
409        {
410                handle = creature->connections[toselect];
411                tab = creature->connectionparamtab;
412                return toselect;
413        }
414        else if (toselect - creature->connections.size() < creature->neurons.size())
415        {
416                toselect -= creature->connections.size();
417                handle = creature->neurons[toselect];
418                tab = creature->neuronparamtab;
419                return toselect;
420        }
421        toselect -= creature->connections.size() + creature->neurons.size();
422        handle = creature->sticks[toselect];
423        tab = creature->stickparamtab;
424        return toselect;
425}
426
427void Geno_fH::mutateNeuronProperties(SString &det)
428{
429        Neuro neu;
430        det = det == "" ? "N" : det;
431        neu.setDetails(det == "" ? "N" : det);
432
433        SyntParam par = neu.classProperties();
434
435        if (par.getPropCount() > 0)
436        {
437                int i = randomN(par.getPropCount());
438                if (*par.type(i) == 'f')
439                {
440                        double change = mutateNeuProperty(par.getDouble(i), &neu, 100 + i);
441                        par.setDouble(i, change);
442                }
443                SString line;
444                int tmp = 0;
445                par.update(&line);
446                SString props;
447                line.getNextToken(tmp, props, '\n'); // removal of newline character
448                if (props != "")
449                {
450                        det = neu.getClass()->name;
451                        det += ": ";
452                        det += props;
453                }
454        }
455}
456
457void Geno_fH::mutateNeuronHandleProperties(fH_NeuronHandle *handle, ParamEntry *tab, bool userandomclass)
458{
459        Neuro neu;
460        Param hpar(tab, handle->obj);
461        SString det = hpar.getStringById("d");
462        neu.setDetails(det == "" ? "N" : det);
463        NeuroClass *nc = neu.getClass();
464
465        if (userandomclass)
466        {
467                nc = getRandomNeuroClass();
468                if (!nc) nc = Neuro::getClass("N");
469        }
470
471        det = nc->getName();
472
473        mutateNeuronProperties(det);
474
475        hpar.setStringById("d", det);
476}
477
478//uint32_t Geno_fH::style(const char *geno, int pos)
479//{
480//      char ch = geno[pos];
481//      uint32_t style = GENSTYLE_CS(0, GENSTYLE_STRIKEOUT);
482//      if (pos == 0 || geno[pos - 1] == '\n' || ch == ':') // single-character handle type and all colons
483//      {
484//              style = GENSTYLE_CS(GENCOLOR_TEXT, GENSTYLE_BOLD);
485//      }
486//      else if (isalpha(ch)) // properties name
487//      {
488//              style = GENSTYLE_RGBS(0, 200, 0, GENSTYLE_BOLD);
489//      }
490//      else if (isdigit(ch) || strchr(",.=", ch)) // properties values
491//      {
492//              style = GENSTYLE_CS(GENCOLOR_TEXT, GENSTYLE_NONE);
493//      }
494//      else if (ch == '\"')
495//      {
496//              style = GENSTYLE_RGBS(200, 0, 0, GENSTYLE_BOLD);
497//      }
498//
499//      return style;
500//}
501
502uint32_t Geno_fH::style(const char *g, int pos)
503{
504   char ch=g[pos];
505   uint32_t style=GENSTYLE_CS(0,GENSTYLE_NONE); //default, should be changed below
506
507   int pp=pos; //detect comment line
508   while (pp>1 && g[pp-1]!='\n') pp--;
509   if (g[pp]=='#') return GENSTYLE_RGBS(0,220,0,GENSTYLE_NONE); //comment line
510
511   if (pos==0 || g[pos-1]=='\n' || ch==':' || ch==',') style=GENSTYLE_CS(0,GENSTYLE_BOLD); else
512     if (ch=='\"') style=GENSTYLE_RGBS(150,0,0,GENSTYLE_BOLD); else
513     {
514      int cudz=0,neuclass=1; //ile cudz. do poczatku linii; czy w nazwie neuroklasy?
515      while (pos>0)
516      {
517         pos--;
518         if (g[pos]=='\"') cudz++;
519         if (cudz==0 && (g[pos]==':' || g[pos]==',')) neuclass=0;
520         if (g[pos]=='\n') break;
521      }
522      if (cudz%2)
523      {
524         if (neuclass) style=GENSTYLE_RGBS(150,0,150,GENSTYLE_BOLD); else //neuroclass
525           if (isalpha(ch)) style=GENSTYLE_RGBS(255,140,0,GENSTYLE_BOLD); else //property
526             style=GENSTYLE_RGBS(200,0,0,GENSTYLE_NONE);
527      } else
528        if (isalpha(ch)) style=GENSTYLE_RGBS(0,0,200,GENSTYLE_BOLD);
529     }
530   return style;
531}
Note: See TracBrowser for help on using the repository browser.