source: cpp/frams/genetics/fS/fS_oper.cpp @ 967

Last change on this file since 967 was 967, checked in by Maciej Komosinski, 6 weeks ago

Improved names of functions that mutate neuron properties and improved docs

File size: 21.8 KB
Line 
1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
2// Copyright (C) 2019-2020  Maciej Komosinski and Szymon Ulatowski.
3// See LICENSE.txt for details.
4
5#include <float.h>
6#include <assert.h>
7#include "fS_oper.h"
8#include "frams/util/rndutil.h"
9
10#define FIELDSTRUCT GenoOper_fS
11static ParamEntry GENOfSparam_tab[] =
12                {
13                                {"Genetics: fS",            1, FS_OPCOUNT + 5,},
14                                {"fS_mut_add_part",         0, 0, "Add part",                 "f 0 100 10", FIELD(prob[FS_ADD_PART]),             "mutation: probability of adding a part",},
15                                {"fS_mut_rem_part",         0, 0, "Remove part",              "f 0 100 10", FIELD(prob[FS_REM_PART]),             "mutation: probability of deleting a part",},
16                                {"fS_mut_mod_part",         0, 0, "Modify part",              "f 0 100 10", FIELD(prob[FS_MOD_PART]),             "mutation: probability of changing the part type",},
17                                {"fS_mut_add_joint",        0, 0, "Add joint",                "f 0 100 10", FIELD(prob[FS_ADD_JOINT]),            "mutation: probability of adding a joint",},
18                                {"fS_mut_rem_joint",        0, 0, "Remove joint",             "f 0 100 10", FIELD(prob[FS_REM_JOINT]),            "mutation: probability of removing a joint",},
19                                {"fS_mut_add_param",        0, 0, "Add param",                "f 0 100 10", FIELD(prob[FS_ADD_PARAM]),            "mutation: probability of adding a parameter",},
20                                {"fS_mut_rem_param",        0, 0, "Remove param",             "f 0 100 10", FIELD(prob[FS_REM_PARAM]),            "mutation: probability of removing a parameter",},
21                                {"fS_mut_mod_param",        0, 0, "Modify param",             "f 0 100 10", FIELD(prob[FS_MOD_PARAM]),            "mutation: probability of modifying a parameter",},
22                                {"fS_mut_mod_mod",          0, 0, "Modify modifier",           "f 0 100 10", FIELD(prob[FS_MOD_MOD]),              "mutation: probability of modifying a modifier",},
23                                {"fS_mut_add_neuro",        0, 0, "Add neuron",               "f 0 100 10", FIELD(prob[FS_ADD_NEURO]),            "mutation: probability of adding a neuron",},
24                                {"fS_mut_rem_neuro",        0, 0, "Remove neuron",            "f 0 100 10", FIELD(prob[FS_REM_NEURO]),            "mutation: probability of removing a neuron",},
25                                {"fS_mut_mod_neuro_conn",        0, 0, "Modify neuron connection",            "f 0 100 10", FIELD(prob[FS_MOD_NEURO_CONNECTION]), "mutation: probability of changing a neuron connection",},
26                                {"fS_mut_add_neuro_conn",   0, 0, "Add neuron connection",    "f 0 100 10", FIELD(prob[FS_ADD_NEURO_CONNECTION]), "mutation: probability of adding a neuron connection",},
27                                {"fS_mut_rem neuro_conn",   0, 0, "Remove neuron connection", "f 0 100 10", FIELD(prob[FS_REM_NEURO_CONNECTION]), "mutation: probability of removing a neuron connection",},
28                                {"fS_mut_mod_neuro_params", 0, 0, "Modify neuron params",     "f 0 100 10", FIELD(prob[FS_MOD_NEURO_PARAMS]),     "mutation: probability of changing a neuron param",},
29                                {"fS_circle_section",       0, 0, "Ensure circle section",    "d 0 1 1",    FIELD(ensureCircleSection),           "Ensure that ellipsoids and cylinders have circle cross-section"},
30                                {"fS_use_elli",       0, 0, "Use ellipsoids in mutations",    "d 0 1 1",    FIELD(useElli),           "Use ellipsoids in mutations"},
31                                {"fS_use_cub",       0, 0, "Use cuboids in mutations",    "d 0 1 1",    FIELD(useCub),           "Use cuboids in mutations"},
32                                {"fS_use_cyl",       0, 0, "Use cylinders in mutations",    "d 0 1 1",    FIELD(useCyl),           "Use cylinders in mutations"},
33                                {"fS_mut_add_part_strong",       0, 0, "Strong add part mutation",    "d 0 1 1",    FIELD(strongAddPart),           "Add part mutation will produce more parametrized parts"},
34                };
35
36#undef FIELDSTRUCT
37
38GenoOper_fS::GenoOper_fS()
39{
40        par.setParamTab(GENOfSparam_tab);
41        par.select(this);
42        par.setDefault();
43        supported_format = "S";
44}
45
46int GenoOper_fS::checkValidity(const char *geno, const char *genoname)
47{
48        try
49        {
50                fS_Genotype genotype(geno);
51                int errorPosition = genotype.checkValidityOfPartSizes();
52                if(errorPosition != 0)
53                {
54                        logPrintf("GenoOper_fS", "checkValidity", LOG_ERROR, "Invalid part size");
55                        return 1 + errorPosition;
56                }
57        }
58        catch (fS_Exception &e)
59        {
60                logPrintf("GenoOper_fS", "checkValidity", LOG_ERROR, e.what());
61                return 1 + e.errorPosition;
62        }
63        return 0;
64}
65
66
67int GenoOper_fS::mutate(char *&geno, float &chg, int &method)
68{
69        fS_Genotype genotype(geno);
70
71        // Calculate available part types
72        string availableTypes;
73        if(useElli)
74                availableTypes += ELLIPSOID;
75        if(useCub)
76                availableTypes += CUBOID;
77        if(useCyl)
78                availableTypes += CYLINDER;
79
80        // Select a mutation
81        bool result = false;
82        method = GenoOperators::roulette(prob, FS_OPCOUNT);
83        switch (method)
84        {
85                case FS_ADD_PART:
86                        result = addPart(genotype, availableTypes);
87                        break;
88                case FS_REM_PART:
89                        result = removePart(genotype);
90                        break;
91                case FS_MOD_PART:
92                        result = changePartType(genotype, availableTypes);
93                        break;
94                case FS_ADD_JOINT:
95                        result = addJoint(genotype);
96                        break;
97                case FS_REM_JOINT:
98                        result = removeJoint(genotype);
99                        break;
100                case FS_ADD_PARAM:
101                        result = addParam(genotype);
102                        break;
103                case FS_REM_PARAM:
104                        result = removeParam(genotype);
105                        break;
106                case FS_MOD_PARAM:
107                        result = changeParam(genotype);
108                        break;
109                case FS_MOD_MOD:
110                        result = changeModifier(genotype);
111                        break;
112                case FS_ADD_NEURO:
113                        result = addNeuro(genotype);
114                        break;
115                case FS_REM_NEURO:
116                        result = removeNeuro(genotype);
117                        break;
118                case FS_MOD_NEURO_CONNECTION:
119                        result = changeNeuroConnection(genotype);
120                        break;
121                case FS_ADD_NEURO_CONNECTION:
122                        result = addNeuroConnection(genotype);
123                        break;
124                case FS_REM_NEURO_CONNECTION:
125                        result = removeNeuroConnection(genotype);
126                        break;
127                case FS_MOD_NEURO_PARAMS:
128                        result = changeNeuroParam(genotype);
129                        break;
130        }
131
132        if (result)
133        {
134                free(geno);
135                geno = strdup(genotype.getGeno().c_str());
136                return GENOPER_OK;
137        }
138        return GENOPER_OPFAIL;
139}
140
141int GenoOper_fS::crossOver(char *&g0, char *&g1, float &chg0, float &chg1)
142{
143        assert(PARENT_COUNT == 2); // Cross over works only for 2 parents
144        fS_Genotype *parents[PARENT_COUNT] = {new fS_Genotype(g0), new fS_Genotype(g1)};
145
146        // Choose random subtrees that have similar size
147        Node *selected[PARENT_COUNT];
148        vector<Node*> allNodes0 = parents[0]->getAllNodes();
149        vector<Node*> allNodes1 = parents[1]->getAllNodes();
150
151        double bestQuotient = DBL_MAX;
152        for (int i = 0; i < crossOverTries; i++)
153        {
154                Node *tmp0 = allNodes0[rndUint(allNodes0.size())];
155                Node *tmp1 = allNodes1[rndUint(allNodes1.size())];
156                // Choose this pair if it is the most similar
157                double quotient = double(tmp0->getNodeCount()) / double(tmp1->getNodeCount());
158                if(quotient < 1.0)
159                        quotient = 1.0 / quotient;
160                if (quotient < bestQuotient)
161                {
162                        bestQuotient = quotient;
163                        selected[0] = tmp0;
164                        selected[1] = tmp1;
165                }
166                if (bestQuotient == 1.0)
167                        break;
168        }
169
170        // Compute gene percentages in children
171        double subtreeSizes[PARENT_COUNT], restSizes[PARENT_COUNT];
172        for (int i = 0; i < PARENT_COUNT; i++)
173        {
174
175                subtreeSizes[i] = selected[i]->getNodeCount();
176                restSizes[i] = parents[i]->getNodeCount() - subtreeSizes[i];
177        }
178        chg0 = restSizes[0] / (restSizes[0] + subtreeSizes[1]);
179        chg1 = restSizes[1] / (restSizes[1] + subtreeSizes[0]);
180
181        // Rearrange neurons before crossover
182        int subOldStart[PARENT_COUNT] {-1, -1};
183        rearrangeConnectionsBeforeCrossover(parents[0], selected[0], subOldStart[0]);
184        rearrangeConnectionsBeforeCrossover(parents[1], selected[1], subOldStart[1]);
185
186        // Swap the subtress
187        for(int i=0; i<PARENT_COUNT; i++)
188        {
189                Node *other = selected[1 - i];
190                Node *p = selected[i]->parent;
191                if (p != nullptr)
192                {
193                        size_t index = std::distance(p->children.begin(), std::find(p->children.begin(), p->children.end(), selected[i]));
194                        p->children[index] = other;
195                } else
196                        parents[i]->startNode = other;
197        }
198
199        // Rearrange neurons after crossover
200        rearrangeConnectionsAfterCrossover(parents[0], selected[1], subOldStart[0]);
201        rearrangeConnectionsAfterCrossover(parents[1], selected[0], subOldStart[1]);
202
203        // Clenup, assign children to result strings
204        free(g0);
205        free(g1);
206        g0 = strdup(parents[0]->getGeno().c_str());
207        g1 = strdup(parents[1]->getGeno().c_str());
208
209        delete parents[0];
210        delete parents[1];
211        return GENOPER_OK;
212}
213
214const char* GenoOper_fS::getSimplest()
215{
216        return "S:C{x=0.80599;y=0.80599;z=0.80599}";
217}
218
219uint32_t GenoOper_fS::style(const char *geno, int pos)
220{
221        char ch = geno[pos];
222        uint32_t style = GENSTYLE_CS(0, GENSTYLE_NONE);
223        if (ch == ELLIPSOID || ch == CUBOID || ch == CYLINDER) // part type
224        {
225                style = GENSTYLE_RGBS(0, 0, 200, GENSTYLE_BOLD);
226        }
227        else if(JOINTS.find(ch) != string::npos)        // Joint type
228        {
229                style = GENSTYLE_RGBS(0, 200, 200, GENSTYLE_BOLD);
230        }
231        else if(MODIFIERS.find(ch) != string::npos) // Modifier
232        {
233                style = GENSTYLE_RGBS(0, 200, 0, GENSTYLE_NONE);
234        }
235        else if (isdigit(ch) || strchr(".=", ch)) // Numerical value
236        {
237                style = GENSTYLE_RGBS(200, 0, 0, GENSTYLE_NONE);
238        }
239        else if(strchr("()_;[],", ch))
240        {
241                style = GENSTYLE_CS(0, GENSTYLE_BOLD); // Important char
242        }
243
244        return style;
245}
246
247void GenoOper_fS::rearrangeConnectionsBeforeCrossover(fS_Genotype *geno, Node *sub, int &subStart)
248{
249        vector<fS_Neuron*> genoNeurons = geno->getAllNeurons();
250        vector<fS_Neuron*> subNeurons = fS_Genotype::extractNeurons(sub);
251
252        if (!subNeurons.empty())
253        {
254                subStart = fS_Genotype::getNeuronIndex(genoNeurons, subNeurons[0]);
255                fS_Genotype::shiftNeuroConnections(genoNeurons, subStart, subStart + subNeurons.size() - 1, SHIFT::LEFT);
256        }
257}
258
259void GenoOper_fS::rearrangeConnectionsAfterCrossover(fS_Genotype *geno, Node *sub, int subOldStart)
260{
261        vector<fS_Neuron*> genoNeurons = geno->getAllNeurons();
262        vector<fS_Neuron*> subNeurons = fS_Genotype::extractNeurons(sub);
263
264        // Shift the inputs right
265        if (!subNeurons.empty())
266        {
267                int subStart = fS_Genotype::getNeuronIndex(genoNeurons, subNeurons[0]);
268                int subCount = subNeurons.size();
269                int subEnd = subStart + subCount - 1;
270                for (int i = 0; i < subCount; i++)
271                {
272                        auto inputs = subNeurons[i]->inputs;
273                        std::map<int, double> newInputs;
274                        // TODO figure out how to keep internal connections in subtree
275//                      for (auto it = inputs.begin(); it != inputs.end(); ++it)
276//                      {
277//                              int newIndex = it->first + subStart;
278//                              if(subEnd > newIndex && newIndex > subStart)
279//                                      newInputs[newIndex] = it->second;
280//                      }
281                        subNeurons[i]->inputs = newInputs;
282                }
283                fS_Genotype::shiftNeuroConnections(genoNeurons, subStart, subEnd, SHIFT::RIGHT);
284        }
285}
286
287bool GenoOper_fS::addPart(fS_Genotype &geno, string availableTypes, bool mutateSize)
288{
289        geno.getState();
290        Node *node = geno.chooseNode();
291        char partType = availableTypes[rndUint(availableTypes.length())];
292
293        Substring substring(&partType, 0, 1);
294        Node *newNode = new Node(substring, node->modifierMode, node->paramMode, node->cycleMode, node);
295        // Add random rotation
296        string rotationParams[]{ROT_X, ROT_Y, ROT_Z};
297        if(strongAddPart)
298        {
299                for(int i=0; i < 3; i++)
300                        newNode->params[rotationParams[i]] = RndGen.Uni(-90, 90);
301        }
302        else
303        {
304                string selectedParam = rotationParams[rndUint(3)];
305                newNode->params[selectedParam] = RndGen.Uni(-90, 90);
306        }
307        string rParams[]{RX, RY, RZ};
308        if(strongAddPart)
309        {
310                for(int i=0; i < 3; i++)
311                        newNode->params[rParams[i]] = RndGen.Uni(-90, 90);
312        }
313        else
314        {
315                string selectedParam = rParams[rndUint(3)];
316                newNode->params[selectedParam] = RndGen.Uni(-90, 90);
317        }
318        // Assign part size to default value
319        double volumeMultiplier = pow(node->getParam(SIZE) * node->state->s, 3);
320        double minVolume = Model::getMinPart().volume;
321        double defVolume = Model::getDefPart().volume * volumeMultiplier;    // Default value after applying modifiers
322        double maxVolume = Model::getMaxPart().volume;
323        double volume = std::min(maxVolume, std::max(minVolume, defVolume));
324        double relativeVolume = volume / volumeMultiplier;    // Volume without applying modifiers
325
326        double newRadius = Node::calculateRadiusFromVolume(newNode->partType, relativeVolume);
327        newNode->params[SIZE_X] = newRadius;
328        newNode->params[SIZE_Y] = newRadius;
329        newNode->params[SIZE_Z] = newRadius;
330        node->children.push_back(newNode);
331
332        if (mutateSize)
333        {
334                geno.getState();
335                newNode->changeSizeParam(SIZE_X, fS_Genotype::randomParamMultiplier(), true);
336                newNode->changeSizeParam(SIZE_Y, fS_Genotype::randomParamMultiplier(), true);
337                newNode->changeSizeParam(SIZE_Z, fS_Genotype::randomParamMultiplier(), true);
338        }
339        return true;
340}
341
342bool GenoOper_fS::removePart(fS_Genotype &geno)
343{
344        Node *randomNode, *selectedChild;
345        // Choose a parent with children
346        for (int i = 0; i < mutationTries; i++)
347        {
348                randomNode = geno.chooseNode();
349                int childCount = randomNode->children.size();
350                if (childCount > 0)
351                {
352                        int selectedIndex = rndUint(childCount);
353                        selectedChild = randomNode->children[selectedIndex];
354                        if (selectedChild->children.empty() && selectedChild->neurons.empty())
355                        {
356                                // Remove the selected child
357                                swap(randomNode->children[selectedIndex], randomNode->children[childCount - 1]);
358                                randomNode->children.pop_back();
359                                randomNode->children.shrink_to_fit();
360                                delete selectedChild;
361                                return true;
362                        }
363                }
364        }
365        return false;
366}
367
368bool GenoOper_fS::changePartType(fS_Genotype &geno, string availTypes)
369{
370        int availTypesLength = availTypes.length();
371        for (int i = 0; i < mutationTries; i++)
372        {
373                Node *randomNode = geno.chooseNode();
374                int index = rndUint(availTypesLength);
375                if (availTypes[index] == SHAPETYPE_TO_GENE.at(randomNode->partType))
376                        index = (index + 1 + rndUint(availTypesLength)) % availTypesLength;
377                char newTypeChr = availTypes[index];
378
379                auto itr = GENE_TO_SHAPETYPE.find(newTypeChr);
380                Part::Shape newType = itr->second;
381
382#ifdef _DEBUG
383                if(newType == randomNode->partType)
384                        throw fS_Exception("Internal error: invalid part type chosen in mutation.", 1);
385#endif
386
387                if (ensureCircleSection)
388                {
389                        geno.getState();
390                        if (randomNode->partType == Part::Shape::SHAPE_CUBOID
391                                || (randomNode->partType == Part::Shape::SHAPE_CYLINDER && newType == Part::Shape::SHAPE_ELLIPSOID))
392                        {
393                                double sizeMultiplier = randomNode->getParam(SIZE) * randomNode->state->s;
394                                double relativeVolume = randomNode->calculateVolume() / pow(sizeMultiplier, 3.0);
395                                double newRelativeRadius = Node::calculateRadiusFromVolume(newType, relativeVolume);
396                                randomNode->params[SIZE_X] = newRelativeRadius;
397                                randomNode->params[SIZE_Y] = newRelativeRadius;
398                                randomNode->params[SIZE_Z] = newRelativeRadius;
399                        }
400                }
401                randomNode->partType = newType;
402                return true;
403        }
404        return false;
405}
406
407bool GenoOper_fS::addJoint(fS_Genotype &geno)
408{
409        if (geno.startNode->children.empty())
410                return false;
411
412        Node *randomNode;
413        for (int i = 0; i < mutationTries; i++)
414        {
415                char randomJoint = JOINTS[rndUint(JOINT_COUNT)];
416                randomNode = geno.chooseNode(1);        // First part does not have joints
417                if (randomNode->joint == DEFAULT_JOINT)
418                {
419                        randomNode->joint = randomJoint;
420                        return true;
421                }
422        }
423        return false;
424}
425
426
427bool GenoOper_fS::removeJoint(fS_Genotype &geno)
428{
429        // This operator may can lower success rate that others, as it does not work when there is only one node
430        if (geno.startNode->children.size() < 1) // Only one node; there are no joints
431                return false;
432
433        // Choose a node with joints
434        for (int i = 0; i < mutationTries; i++)
435        {
436                Node *randomNode = geno.chooseNode(1);    // First part does not have joints
437                if (randomNode->joint != DEFAULT_JOINT)
438                {
439                        randomNode->joint = DEFAULT_JOINT;
440                        return true;
441                }
442        }
443        return false;
444}
445
446bool GenoOper_fS::addParam(fS_Genotype &geno)
447{
448        Node *randomNode = geno.chooseNode();
449        int paramCount = randomNode->params.size();
450        if (paramCount == int(PARAMS.size()))
451                return false;
452        string selectedParam = PARAMS[rndUint(PARAMS.size())];
453        // Not allow 'j' parameter when the cycle mode is not on
454        if (selectedParam == JOINT_DISTANCE && !geno.startNode->cycleMode)
455                return false;
456        if (randomNode->params.count(selectedParam) > 0)
457                return false;
458        // Do not allow invalid changes in part size
459        bool isRadiusOfBase = selectedParam == SIZE_X || selectedParam == SIZE_Y;
460        bool isRadius = isRadiusOfBase || selectedParam == SIZE_Z;
461        if (ensureCircleSection && isRadius)
462        {
463                if (randomNode->partType == Part::Shape::SHAPE_ELLIPSOID)
464                        return false;
465                if (randomNode->partType == Part::Shape::SHAPE_CYLINDER && isRadiusOfBase)
466                        return false;
467        }
468        // Add modified default value for param
469        randomNode->params[selectedParam] = defaultParamValues.at(selectedParam);
470        return true;
471}
472
473bool GenoOper_fS::removeParam(fS_Genotype &geno)
474{
475        // Choose a node with params
476        for (int i = 0; i < mutationTries; i++)
477        {
478                Node *randomNode = geno.chooseNode();
479                int paramCount = randomNode->params.size();
480                if (paramCount >= 1)
481                {
482                        auto it = randomNode->params.begin();
483                        advance(it, rndUint(paramCount));
484                        randomNode->params.erase(it->first);
485                        return true;
486                }
487        }
488        return false;
489}
490
491bool GenoOper_fS::changeParam(fS_Genotype &geno)
492{
493        geno.getState();
494        for (int i = 0; i < mutationTries; i++)
495        {
496                Node *randomNode = geno.chooseNode();
497                int paramCount = randomNode->params.size();
498                if (paramCount >= 1)
499                {
500                        auto it = randomNode->params.begin();
501                        advance(it, rndUint(paramCount));
502
503                        double multiplier = fS_Genotype::randomParamMultiplier();
504
505
506                        // Do not allow invalid changes in part size
507                        if (it->first != SIZE_X && it->first != SIZE_Y && it->first != SIZE_Z)
508                        {
509                                it->second *= multiplier;
510                                return true;
511                        } else
512                                return randomNode->changeSizeParam(it->first, multiplier, ensureCircleSection);
513                }
514        }
515        return false;
516}
517
518bool GenoOper_fS::changeModifier(fS_Genotype &geno)
519{
520        Node *randomNode = geno.chooseNode();
521        char randomModifier = MODIFIERS[rndUint(MODIFIERS.length())];
522        randomNode->modifiers[randomModifier] += rndUint(2) == 0 ? 1 : -1;
523
524        bool isSizeMod = tolower(randomModifier) == SIZE_MODIFIER;
525        if (isSizeMod && geno.checkValidityOfPartSizes() != 0)
526        {
527                randomNode->modifiers[randomModifier]++;
528                return false;
529        }
530        return true;
531}
532
533bool GenoOper_fS::addNeuro(fS_Genotype &geno)
534{
535        Node *randomNode = geno.chooseNode();
536        fS_Neuron *newNeuron;
537        NeuroClass *rndclass = GenoOperators::getRandomNeuroClass(Model::SHAPE_SOLIDS);
538        if(rndclass->preflocation == 2 && randomNode == geno.startNode)
539                return false;
540
541        const char *name = rndclass->getName().c_str();
542        newNeuron = new fS_Neuron(name, randomNode->partDescription->start, strlen(name));
543        int effectiveInputCount = rndclass->prefinputs > -1 ? rndclass->prefinputs : 1;
544        if (effectiveInputCount > 0)
545        {
546                // Create as many connections for the neuron as possible (at most prefinputs)
547                vector<fS_Neuron*> allNeurons = geno.getAllNeurons();
548                vector<int> neuronsWithOutput;
549                for (int i = 0; i < int(allNeurons.size()); i++)
550                {
551                        if (allNeurons[i]->getClass()->prefoutput > 0)
552                                neuronsWithOutput.push_back(i);
553                }
554                int size = neuronsWithOutput.size();
555                if (size > 0)
556                {
557                        for (int i = 0; i < effectiveInputCount; i++)
558                        {
559                                int selectedNeuron = neuronsWithOutput[rndUint(size)];
560                                newNeuron->inputs[selectedNeuron] = DEFAULT_NEURO_CONNECTION_WEIGHT;
561                        }
562                }
563        }
564
565        randomNode->neurons.push_back(newNeuron);
566
567        geno.rearrangeNeuronConnections(newNeuron, SHIFT::RIGHT);
568        return true;
569}
570
571bool GenoOper_fS::removeNeuro(fS_Genotype &geno)
572{
573        Node *randomNode = geno.chooseNode();
574        for (int i = 0; i < mutationTries; i++)
575        {
576                randomNode = geno.chooseNode();
577                if (!randomNode->neurons.empty())
578                {
579                        // Remove the selected neuron
580                        int size = randomNode->neurons.size();
581                        fS_Neuron *it = randomNode->neurons[rndUint(size)];
582                        geno.rearrangeNeuronConnections(it, SHIFT::LEFT);        // Important to rearrange the neurons before deleting
583                        swap(it, randomNode->neurons.back());
584                        randomNode->neurons.pop_back();
585                        randomNode->neurons.shrink_to_fit();
586                        delete it;
587                        return true;
588                }
589        }
590        return false;
591}
592
593bool GenoOper_fS::changeNeuroConnection(fS_Genotype &geno)
594{
595        vector<fS_Neuron*> neurons = geno.getAllNeurons();
596        if (neurons.empty())
597                return false;
598
599        int size = neurons.size();
600        for (int i = 0; i < mutationTries; i++)
601        {
602                fS_Neuron *selectedNeuron = neurons[rndUint(size)];
603                if (!selectedNeuron->inputs.empty())
604                {
605                        int inputCount = selectedNeuron->inputs.size();
606                        auto it = selectedNeuron->inputs.begin();
607                        advance(it, rndUint(inputCount));
608
609                        it->second = GenoOperators::getMutatedNeuroClassProperty(it->second, selectedNeuron, -1);
610                        return true;
611                }
612        }
613        return false;
614}
615
616bool GenoOper_fS::addNeuroConnection(fS_Genotype &geno)
617{
618        vector<fS_Neuron*> neurons = geno.getAllNeurons();
619        if (neurons.empty())
620                return false;
621
622        int size = neurons.size();
623        fS_Neuron *selectedNeuron;
624        for (int i = 0; i < mutationTries; i++)
625        {
626                selectedNeuron = neurons[rndUint(size)];
627                if (selectedNeuron->acceptsInputs())
628                        break;
629        }
630        if (!selectedNeuron->acceptsInputs())
631                return false;
632
633        for (int i = 0; i < mutationTries; i++)
634        {
635                int index = rndUint(size);
636                if (selectedNeuron->inputs.count(index) == 0 && neurons[index]->getClass()->getPreferredOutput() > 0)
637                {
638
639                        selectedNeuron->inputs[index] = DEFAULT_NEURO_CONNECTION_WEIGHT;
640                        return true;
641                }
642        }
643        return false;
644}
645
646bool GenoOper_fS::removeNeuroConnection(fS_Genotype &geno)
647{
648        vector<fS_Neuron*> neurons = geno.getAllNeurons();
649        if (neurons.empty())
650                return false;
651
652        int size = neurons.size();
653        for (int i = 0; i < mutationTries; i++)
654        {
655                fS_Neuron *selectedNeuron = neurons[rndUint(size)];
656                if (!selectedNeuron->inputs.empty())
657                {
658                        int inputCount = selectedNeuron->inputs.size();
659                        auto it = selectedNeuron->inputs.begin();
660                        advance(it, rndUint(inputCount));
661                        selectedNeuron->inputs.erase(it->first);
662                        return true;
663                }
664        }
665        return false;
666}
667
668bool GenoOper_fS::changeNeuroParam(fS_Genotype &geno)
669{
670        vector<fS_Neuron*> neurons = geno.getAllNeurons();
671        if (neurons.empty())
672                return false;
673
674        fS_Neuron *neu = neurons[rndUint(neurons.size())];
675        SyntParam par = neu->classProperties();
676
677        if (par.getPropCount() > 0)
678        {
679                int i = rndUint(par.getPropCount());
680                if (*par.type(i) == 'f')
681                {
682                        double change = GenoOperators::getMutatedNeuroClassProperty(par.getDouble(i), neu, GenoOperators::NEUROCLASS_PROP_OFFSET + i);
683                        par.setDouble(i, change);
684                }
685                SString line;
686                int tmp = 0;
687                par.update(&line);
688                SString props;
689                line.getNextToken(tmp, props, '\n'); // removal of newline character
690                if (props != "")
691                {
692                        SString det = neu->getClass()->name + ": " + props;
693                        neu->setDetails(det);
694                        return true;
695                }
696        }
697
698        return false;
699}
Note: See TracBrowser for help on using the repository browser.