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

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

Added sources for genetic encodings fB, fH, fL

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