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

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

Used std::min(), std::max() explicitly to avoid compiler confusion. Used std::size() explicitly instead of the equivalent macro

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