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

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

fS: faster collision detection, depends on "geometry" algorithms

File size: 23.0 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                {
[1000]53                        logPrintf("GenoOper_fS", "checkValidity", LOG_WARN, "Invalid part size");
[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{
[1000]228        return "1.1: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();
[1006]299        char partType = SHAPE_TO_GENE.at(availablePartShapes[rndUint(availablePartShapes.size())]);
[958]300
301        Substring substring(&partType, 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        }
324        // Assign part size to default value
325        double volumeMultiplier = pow(node->getParam(SIZE) * node->state->s, 3);
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
[969]332        double newRadius = std::cbrt(relativeVolume / volumeMultipliers.at(newNode->partType));
[958]333        newNode->params[SIZE_X] = newRadius;
334        newNode->params[SIZE_Y] = newRadius;
335        newNode->params[SIZE_Z] = newRadius;
336        node->children.push_back(newNode);
337
338        if (mutateSize)
339        {
[1017]340                geno.getState(false);
[1006]341                mutateSizeParam(newNode, SIZE_X, true);
342                mutateSizeParam(newNode, SIZE_Y, true);
343                mutateSizeParam(newNode, SIZE_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
[1017]352        // It may be difficult to choose a eligible node, so the number of tries should be high
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);
382                if (availablePartShapes[index] == randomNode->partType)
383                        index = (index + 1 + rndUint(availShapesLen - 1)) % availShapesLen;
384                Part::Shape newType = availablePartShapes[index];
[958]385
386#ifdef _DEBUG
387                if(newType == randomNode->partType)
388                        throw fS_Exception("Internal error: invalid part type chosen in mutation.", 1);
389#endif
390
[1017]391                geno.getState(false);
[969]392                double sizeMultiplier = randomNode->getParam(SIZE) * randomNode->state->s;
393                double relativeVolume = randomNode->calculateVolume() / pow(sizeMultiplier, 3.0);
394
[1000]395                if (!ensureCircleSection || newType == Part::Shape::SHAPE_CUBOID || (randomNode->partType == Part::Shape::SHAPE_ELLIPSOID && newType == Part::Shape::SHAPE_CYLINDER))
[958]396                {
[969]397                        double radiusQuotient = std::cbrt(volumeMultipliers.at(randomNode->partType) / volumeMultipliers.at(newType));
398                        randomNode->params[SIZE_X] = randomNode->getParam(SIZE_X) * radiusQuotient;
399                        randomNode->params[SIZE_Y] = randomNode->getParam(SIZE_Y) * radiusQuotient;
400                        randomNode->params[SIZE_Z] = randomNode->getParam(SIZE_Z) * radiusQuotient;
[1000]401                } else if (randomNode->partType == Part::Shape::SHAPE_CUBOID && newType == Part::Shape::SHAPE_CYLINDER)
[969]402                {
403                        double newRadius = 0.5 * (randomNode->getParam(SIZE_X) + randomNode->getParam(SIZE_Y));
[1000]404                        randomNode->params[SIZE_X] = 0.5 * relativeVolume / (M_PI * newRadius * newRadius);
[969]405                        randomNode->params[SIZE_Y] = newRadius;
[1000]406                        randomNode->params[SIZE_Z] = newRadius;
407                } else if (newType == Part::Shape::SHAPE_ELLIPSOID)
[969]408                {
409                        double newRelativeRadius = cbrt(relativeVolume / volumeMultipliers.at(newType));
410                        randomNode->params[SIZE_X] = newRelativeRadius;
411                        randomNode->params[SIZE_Y] = newRelativeRadius;
412                        randomNode->params[SIZE_Z] = newRelativeRadius;
[1000]413                } else
[969]414                {
415                        throw fS_Exception("Invalid part type", 1);
416                }
[958]417                randomNode->partType = newType;
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
[1000]448        bool isRadiusOfBase = key == SIZE_Y || key == SIZE_Z;
449        bool isRadius = isRadiusOfBase || key == SIZE_X;
[958]450        if (ensureCircleSection && isRadius)
451        {
452                if (randomNode->partType == Part::Shape::SHAPE_ELLIPSOID)
453                        return false;
454                if (randomNode->partType == Part::Shape::SHAPE_CYLINDER && isRadiusOfBase)
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{
492        // Do not allow invalid changes in part size
493        if (std::find(SIZE_PARAMS.begin(), SIZE_PARAMS.end(), key) == SIZE_PARAMS.end())
494        {
495                node->params[key] = GenoOperators::mutateCreep('f', node->getParam(key), Node::minValues.at(key), Node::maxValues.at(key), true);
496                return true;
497        } else
498                return mutateSizeParam(node, key, ensureCircleSection);
499}
500
[958]501bool GenoOper_fS::changeParam(fS_Genotype &geno)
502{
[1017]503        geno.getState(false);
[958]504        for (int i = 0; i < mutationTries; i++)
505        {
506                Node *randomNode = geno.chooseNode();
507                int paramCount = randomNode->params.size();
508                if (paramCount >= 1)
509                {
510                        auto it = randomNode->params.begin();
511                        advance(it, rndUint(paramCount));
[1017]512                        return mutateParamValue(randomNode, it->first);
[958]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())];
[1017]522        int oldValue = randomNode->modifiers[randomModifier];
523
[958]524        randomNode->modifiers[randomModifier] += rndUint(2) == 0 ? 1 : -1;
525
526        bool isSizeMod = tolower(randomModifier) == SIZE_MODIFIER;
527        if (isSizeMod && geno.checkValidityOfPartSizes() != 0)
528        {
[1017]529                randomNode->modifiers[randomModifier] = oldValue;
[958]530                return false;
531        }
532        return true;
533}
534
535bool GenoOper_fS::addNeuro(fS_Genotype &geno)
536{
537        Node *randomNode = geno.chooseNode();
538        fS_Neuron *newNeuron;
[1000]539        NeuroClass *rndclass = GenoOperators::getRandomNeuroClass(Model::SHAPETYPE_SOLIDS);
540        if (rndclass->preflocation == NeuroClass::PREFER_JOINT && randomNode == geno.startNode)
[958]541                return false;
542
543        const char *name = rndclass->getName().c_str();
544        newNeuron = new fS_Neuron(name, randomNode->partDescription->start, strlen(name));
545        int effectiveInputCount = rndclass->prefinputs > -1 ? rndclass->prefinputs : 1;
546        if (effectiveInputCount > 0)
547        {
548                // Create as many connections for the neuron as possible (at most prefinputs)
[1000]549                vector < fS_Neuron * > allNeurons = geno.getAllNeurons();
[958]550                vector<int> neuronsWithOutput;
551                for (int i = 0; i < int(allNeurons.size()); i++)
552                {
553                        if (allNeurons[i]->getClass()->prefoutput > 0)
554                                neuronsWithOutput.push_back(i);
555                }
556                int size = neuronsWithOutput.size();
557                if (size > 0)
558                {
559                        for (int i = 0; i < effectiveInputCount; i++)
560                        {
561                                int selectedNeuron = neuronsWithOutput[rndUint(size)];
562                                newNeuron->inputs[selectedNeuron] = DEFAULT_NEURO_CONNECTION_WEIGHT;
563                        }
564                }
565        }
566
567        randomNode->neurons.push_back(newNeuron);
568
569        geno.rearrangeNeuronConnections(newNeuron, SHIFT::RIGHT);
570        return true;
571}
572
573bool GenoOper_fS::removeNeuro(fS_Genotype &geno)
574{
575        Node *randomNode = geno.chooseNode();
576        for (int i = 0; i < mutationTries; i++)
577        {
578                randomNode = geno.chooseNode();
579                if (!randomNode->neurons.empty())
580                {
581                        // Remove the selected neuron
582                        int size = randomNode->neurons.size();
583                        fS_Neuron *it = randomNode->neurons[rndUint(size)];
584                        geno.rearrangeNeuronConnections(it, SHIFT::LEFT);        // Important to rearrange the neurons before deleting
585                        swap(it, randomNode->neurons.back());
586                        randomNode->neurons.pop_back();
587                        randomNode->neurons.shrink_to_fit();
588                        delete it;
589                        return true;
590                }
591        }
592        return false;
593}
594
595bool GenoOper_fS::changeNeuroConnection(fS_Genotype &geno)
596{
[1000]597        vector < fS_Neuron * > neurons = geno.getAllNeurons();
[958]598        if (neurons.empty())
599                return false;
600
601        int size = neurons.size();
602        for (int i = 0; i < mutationTries; i++)
603        {
604                fS_Neuron *selectedNeuron = neurons[rndUint(size)];
605                if (!selectedNeuron->inputs.empty())
606                {
607                        int inputCount = selectedNeuron->inputs.size();
608                        auto it = selectedNeuron->inputs.begin();
609                        advance(it, rndUint(inputCount));
610
[969]611                        it->second = GenoOperators::getMutatedNeuronConnectionWeight(it->second);
[958]612                        return true;
613                }
614        }
615        return false;
616}
617
618bool GenoOper_fS::addNeuroConnection(fS_Genotype &geno)
619{
[1000]620        vector < fS_Neuron * > neurons = geno.getAllNeurons();
[958]621        if (neurons.empty())
622                return false;
623
624        int size = neurons.size();
625        fS_Neuron *selectedNeuron;
626        for (int i = 0; i < mutationTries; i++)
627        {
628                selectedNeuron = neurons[rndUint(size)];
629                if (selectedNeuron->acceptsInputs())
630                        break;
631        }
632        if (!selectedNeuron->acceptsInputs())
633                return false;
634
635        for (int i = 0; i < mutationTries; i++)
636        {
637                int index = rndUint(size);
638                if (selectedNeuron->inputs.count(index) == 0 && neurons[index]->getClass()->getPreferredOutput() > 0)
639                {
640
641                        selectedNeuron->inputs[index] = DEFAULT_NEURO_CONNECTION_WEIGHT;
642                        return true;
643                }
644        }
645        return false;
646}
647
648bool GenoOper_fS::removeNeuroConnection(fS_Genotype &geno)
649{
[1000]650        vector < fS_Neuron * > neurons = geno.getAllNeurons();
[958]651        if (neurons.empty())
652                return false;
653
654        int size = neurons.size();
655        for (int i = 0; i < mutationTries; i++)
656        {
657                fS_Neuron *selectedNeuron = neurons[rndUint(size)];
658                if (!selectedNeuron->inputs.empty())
659                {
660                        int inputCount = selectedNeuron->inputs.size();
661                        auto it = selectedNeuron->inputs.begin();
662                        advance(it, rndUint(inputCount));
663                        selectedNeuron->inputs.erase(it->first);
664                        return true;
665                }
666        }
667        return false;
668}
669
670bool GenoOper_fS::changeNeuroParam(fS_Genotype &geno)
671{
[1000]672        vector < fS_Neuron * > neurons = geno.getAllNeurons();
[958]673        if (neurons.empty())
674                return false;
675
676        fS_Neuron *neu = neurons[rndUint(neurons.size())];
[969]677        return GenoOperators::mutateRandomNeuroClassProperty(neu);
[1000]678}
679
[1006]680bool GenoOper_fS::mutateSizeParam(Node *node, string key, bool ensureCircleSection)
[1000]681{
[1006]682        double oldValue = node->getParam(key);
683        double volume = node->calculateVolume();
[1000]684        double valueAtMinVolume, valueAtMaxVolume;
685        if(key == SIZE)
686        {
687                valueAtMinVolume = oldValue * std::cbrt(Model::getMinPart().volume / volume);
688                valueAtMaxVolume = oldValue * std::cbrt(Model::getMaxPart().volume / volume);
689        }
690        else
691        {
692                valueAtMinVolume = oldValue * Model::getMinPart().volume / volume;
693                valueAtMaxVolume = oldValue * Model::getMaxPart().volume / volume;
694        }
695
[1017]696        double min = std::max(Node::minValues.at(key), valueAtMinVolume);
697        double max = std::min(Node::maxValues.at(key), valueAtMaxVolume);
[1000]698
[1006]699        node->params[key] = GenoOperators::mutateCreep('f', node->getParam(key), min, max, true);
[1000]700
[1006]701        if (!ensureCircleSection || node->isPartSizeValid())
[1000]702                return true;
703        else
704        {
[1006]705                node->params[key] = oldValue;
[1000]706                return false;
707        }
708}
Note: See TracBrowser for help on using the repository browser.