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

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

Cosmetic

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