source: cpp/frams/genetics/f4/f4_oper.cpp @ 1247

Last change on this file since 1247 was 1247, checked in by Maciej Komosinski, 10 months ago

Cosmetic

  • Property svn:eol-style set to native
File size: 27.8 KB
Line 
1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
2// Copyright (C) 1999-2023  Maciej Komosinski and Szymon Ulatowski.
3// See LICENSE.txt for details.
4
5// Copyright (C) 1999,2000  Adam Rotaru-Varga (adam_rotaru@yahoo.com), GNU LGPL
6// Copyright (C) since 2001 Maciej Komosinski
7// 2018, Grzegorz Latosinski, added development checkpoints and support for new API for neuron types
8
9
10// This representation has a tendency to bloat - adding a small penalty to fitness such as "this.velocity - 0.000000001*String.len(this.genotype);"
11// may help, but it would be better to improve the source code to make genetic operators neutral in terms of genotype length. Adding such a penalty
12// removes "work in progress" changes in genotypes thus promoting immediate, straightforward improvements while hindering slower, multifaceted progress.
13// TODO getting rid of redundancy (valid genotypes with a lot of "junk code") in this representation looks like a good idea; many improvements to this end have already been done in April & May 2023, so maybe it is not a big problem now?
14//
15//
16// TODO the behavior of neuron input indexes during mutation seems badly implemented (see also TREAT_BAD_CONNECTIONS_AS_INVALID_GENO). Are they kept properly maintained when nodes are added and removed? This could be done well because during mutation we operate on the tree structure with cross-references between nodes (so they should not be affected by local changes in the tree), and then convert the tree back to string. Yet, the f4_Node.conn_from is an integer and these fields in nodes do not seem to be maintained on tree node adding/removal... change these integer offsets to references to node objects? But actually, do the offsets that constitute relative connection references concern the f4_Node tree structure (and all these sophisticated calculations of offsets during mutation are useful) or rather they concern the f4_Cells development? verify all situations in f4_Cell::oneStep(), case '['.
17// TODO in mutation, adding the '#' gene does not seem to be effective. The gene is added and genotypes are valid, but hardly ever #n is effective, i.e., it hardly ever multiplicates body or brain parts... investigate! And maybe, during repair or mutations, simplify/remove ineffective #N genes, if there is no chance/hardly any chance that they will be turned effective after future mutation (or crossover)
18// TODO add support for properties of (any class of) neurons - not just sigmoid/force/intertia (':' syntax) for N
19// TODO add mapping genotype character ranges for neural [connections]
20// TODO The f0 genotypes for /*4*/<<RX>X>X> and RX(X,X) are identical, but if you replace R with Q or C, there are small differences (they were present both before and after the Q,C change in f1 in 2023-05) - check why and perhaps unify?
21// TODO F4_SIMPLIFY_MODIFIERS in f4_general.cpp: currently it works while parsing (which is a bit "cheating": we get a phenotype that is a processed version of the genotype, thus some changes in modifiers in the genotype have no effect on its phenotype). Another (likely better) option, instead of simplifying while parsing, would be during mutations (like it is done in f1): when mutations add/modify/remove a modifier node, they could "clean" the tree by simplifying modifiers on the same subpath just as GenoOperators::simplifiedModifiers() does. This way, simplifying would be only performed when we actually modify a part of a genotype, not each time we interpret it, and there would be no hidden mechanism: all visible genes would have an expected effect on the phenotype.
22
23
24
25#include "f4_oper.h"
26#include <frams/util/sstring.h>
27#include <common/log.h>
28
29#include <stdio.h>
30#include <stdlib.h>
31#include "common/nonstd_math.h"
32#include <string.h>
33
34
35const char *Geno_f4::all_modifiers = F14_MODIFIERS ","; //comma in f4 is handled the same way (simple node, F4_ADD_SIMP) as modifiers. See also all_modifiers_no_comma in f4_general.cpp.
36
37// codes that can be changed (apart from being added/deleted)
38#define F4_MUT_CHANGE_CODES "<[#"
39
40#define FIELDSTRUCT Geno_f4
41
42static ParamEntry geno_f4_paramtab[] =
43{
44        { "Genetics: f4", 1, F4_COUNT + F4_ADD_COUNT + F4_MODNEU_COUNT + 2, },
45        { "f4_mut_add", 0, 0, "Add node", "f 0 100 50", FIELD(prob[F4_ADD]), "Mutation: probability of adding a node", },
46        { "f4_mut_add_div", 0, 0, "- add division", "f 0 100 20", FIELD(probadd[F4_ADD_DIV]), "Add node mutation: probability of adding a division", },
47        { "f4_mut_add_conn", 0, 0, "- add connection", "f 0 100 15", FIELD(probadd[F4_ADD_CONN]), "Add node mutation: probability of adding a neural connection", },
48        { "f4_mut_add_neupar", 0, 0, "- add neuron property", "f 0 100 5", FIELD(probadd[F4_ADD_NEUPAR]), "Add node mutation: probability of adding a neuron property/modifier", },
49        { "f4_mut_add_rep", 0, 0, "- add repetition '#'", "f 0 100 10", FIELD(probadd[F4_ADD_REP]), "Add node mutation: probability of adding the '#' repetition gene", },
50        { "f4_mut_add_simp", 0, 0, "- add simple node", "f 0 100 50", FIELD(probadd[F4_ADD_SIMP]), "Add node mutation: probability of adding a random, simple gene", },
51
52        { "f4_mut_del", 0, 0, "Delete node", "f 0 100 20", FIELD(prob[F4_DEL]), "Mutation: probability of deleting a node", },
53
54        { "f4_mut_mod", 0, 0, "Modify node", "f 0 100 30", FIELD(prob[F4_MOD]), "Mutation: probability of changing a node", },
55        { "f4_mut_modneu_conn", 0, 0, "- neuron input: modify source", "f 0 100 60", FIELD(probmodneu[F4_MODNEU_CONN]), "Neuron input mutation: probability of changing its source neuron", },
56        { "f4_mut_modneu_weight", 0, 0, "- neuron input: modify weight", "f 0 100 40", FIELD(probmodneu[F4_MODNEU_WEIGHT]), "Neuron input mutation: probability of changing its weight", },
57
58        { "f4_mut_max_rep", 1, 0, "Maximum number for '#' repetitions", "d 2 20 6", FIELD(mut_max_rep), "Maximum allowed number of repetitions for the '#' repetition gene", },
59        { "f4_mut_exmod", 1, 0, "Excluded modifiers", "s 0 30", FIELD(excluded_modifiers), "Modifiers that will not be added nor deleted during mutation\n(all: " F14_MODIFIERS ")", },
60        { 0, },
61};
62
63#undef FIELDSTRUCT
64
65
66Geno_f4::Geno_f4()
67{
68        supported_format = '4';
69        par.setParamTab(geno_f4_paramtab);
70        par.select(this);
71        par.setDefault();
72
73        mutation_method_names = new const char*[F4_COUNT + F4_ADD_COUNT - 1];
74        int index = 0;
75        mutation_method_names[index++] = "added division";
76        mutation_method_names[index++] = "added neural connection";
77        mutation_method_names[index++] = "added neuron property";
78        mutation_method_names[index++] = "added repetition gene";
79        mutation_method_names[index++] = "added a simple node";
80        mutation_method_names[index++] = "deleted a node";
81        mutation_method_names[index++] = "modified a node";
82        if (index != F4_COUNT + F4_ADD_COUNT - 1) logMessage("Geno_f4", "Constructor", LOG_CRITICAL, "Mutation names init error");
83}
84
85void Geno_f4::setDefaults()
86{
87        excluded_modifiers = F14_MODIFIERS_RARE F14_MODIFIERS_VISUAL;
88}
89
90int Geno_f4::ValidateRecur(f4_Node *geno, int retrycount) const
91{
92        // ! the genotype is geno->child (not geno) !
93        // build from it with repair on
94
95        f4_Cells cells(geno->child, true);
96        cells.simulate();  //we should simulate?!
97
98        // errors not fixed:
99        if (cells.getErrorCode() == GENOPER_OPFAIL)
100        {
101                if (cells.getErrorPos() >= 0) return 1 + cells.getErrorPos();
102                return GENOPER_OPFAIL;
103        }
104        // errors can be fixed
105        if (cells.getErrorCode() == GENOPER_REPAIR)
106        {
107                cells.repairGeno(geno, 1);
108                // note: geno might have been fixed
109                // check again
110                int res2 = GENOPER_OK;
111                if (retrycount > 0)
112                        res2 = ValidateRecur(geno, retrycount - 1);
113
114                if (res2 == GENOPER_OK) return GENOPER_REPAIR;
115                return res2;
116        }
117        // no errors:
118        return GENOPER_OK;
119}
120
121
122int Geno_f4::validate(char *& geno, const char *genoname)
123{
124        // convert geno to a tree, then try to validate
125        f4_Node root;
126        int res = f4_process(geno, &root);
127        if (root.childCount() != 1) return GENOPER_OK; // the resulting tree will not be repairable (fatal flaw; root must have exactly one child) - do not even attempt repair
128
129        // here we have a genotype with root.childCount()==1 (meaning some part was successfully parsed into a tree) and either res==0 (syntax was correct, semantics we don't know) or res>0 (for sure has some error)
130        const int VALIDATE_TRIALS = 20;
131        res = ValidateRecur(&root, VALIDATE_TRIALS);
132        if (res != GENOPER_OPFAIL) // if repaired (GENOPER_REPAIR) or had no errors (GENOPER_OK, e.g. the genotype had some errors that were ignored during tree creation or had junk genes appended at the end, so the tree was OK but the genotype was not),
133        {
134                geno[0] = 0;
135                root.child->sprintAdj(geno); //make it back to string
136        }
137        return GENOPER_OK;
138}
139
140
141int Geno_f4::checkValidity(const char* geno, const char *genoname)
142{
143        f4_Node root;
144        int res = f4_process(geno, &root);
145        if (res) return res;  // errorpos, >0
146        if (root.childCount() != 1) return 1; // fatal flaw; root must have exactly one child
147        f4_Cells cells(root.child, false);
148        cells.simulate();
149        if (cells.getErrorCode() == GENOPER_OPFAIL || cells.getErrorCode() == GENOPER_REPAIR)
150        {
151                if (cells.getErrorPos() >= 0) return 1 + cells.getErrorPos();
152                else return 1; //error, no known position
153        }
154        else return GENOPER_OK;
155}
156
157
158int Geno_f4::MutateOne(f4_Node *& g, int &method) const
159{
160        // ! the genotype is g->child (not g) !
161
162        // do the mutation
163        // pick a random node
164        f4_Node *node_mutated = g->child->randomNode();
165        //DB( printf("%c\n", node_mutated->name); )
166
167        switch (roulette(prob, F4_COUNT))
168        {
169        case F4_ADD:
170        {
171                // add a node
172                switch (method = roulette(probadd, F4_ADD_COUNT))
173                {
174                case F4_ADD_DIV:
175                {
176                        // add division ('<')
177                        f4_Node *node_mutated_parent = node_mutated->parent;
178                        node_mutated_parent->removeChild(node_mutated);
179                        f4_Node *node_new_div = new f4_Node('<', node_mutated_parent, node_mutated_parent->pos);
180                        node_new_div->addChild(node_mutated);
181                        // new cell is stick or neuron
182                        // "X>" or "N>"
183                        constexpr double STICK_OR_NEURON = 0.5; // hardcoded probability... could be parametrized, but in a general case (unknown fitness goal) 0.5 makes sense?
184                        f4_Node *node_new = NULL; //stick or neuron or neural connection
185                        if (rndDouble(1) < STICK_OR_NEURON)
186                                node_new = new f4_Node('X', node_new_div, node_new_div->pos);
187                        else
188                        {
189                                // make neuron
190                                NeuroClass *rndclass = GenoOperators::getRandomNeuroClass(Model::SHAPETYPE_BALL_AND_STICK);
191                                if (rndclass == NULL) //no active neurons?
192                                {
193                                        node_new = new f4_Node('X', node_new_div, node_new_div->pos);
194                                }
195                                else
196                                {
197                                        f4_Node *node_new_neuron = new f4_Node(rndclass->getName().c_str(), node_new_div, node_new_div->pos);
198                                        node_new_neuron->neuclass = rndclass;
199                                        node_new = node_new_neuron; //can be changed below if all goes well and we add a new connection too
200                                        if (probadd[F4_ADD_CONN] > 0) //user wants to add connections
201                                        {
202                                                if (rndclass->getPreferredInputs() != 0) //neuron also wants connections?
203                                                {
204                                                        int node_new_neuron_index, other_neuron_index;
205                                                        bool ok = findConnectionNeuronIndexes(g, node_new_neuron, true, node_new_neuron_index, other_neuron_index); //node_new_neuron_index==-1 should never happen, we just added node_new_neuron we are looking for
206                                                        if (ok) //we can create a new connection
207                                                        {
208                                                                node_new = new f4_Node('[', node_new_neuron, node_new_div->pos);
209                                                                connectionNodeChangeRandom(node_new, node_new_neuron_index, other_neuron_index);
210                                                        }
211                                                }
212                                                else if (rndclass->getPreferredOutput() > 0) //neuron also wants connections?
213                                                {
214                                                        // Not so easy: we would need to add a '[' node as a child not of node_new_neuron, but of other neuron that would get an input from node_new_neuron (and need to properly calculate relative connection reference).
215                                                        // The "false" argument in findConnectionNeuronIndexes() below is not suffient, because we also need to access (find) the f4_Node of the other neuron.
216                                                        // A similar logic is implemented in F4_ADD_CONN below, but let's not complicate this F4_ADD_DIV mutation anymore.
217                                                        // The disadvantage is that the node_new_neuron added here which is a neuron that provides output (e.g., a receptor, N, etc.) will not get connected immediately here even when there are already existing neurons wanting inputs (e.g., muscles, N, etc.).
218                                                        //bool ok = findConnectionNeuronIndexes(g, ... , false, ..., ...);
219                                                }
220                                        }
221                                }
222                        }
223                        new f4_Node('>', node_new, node_new->pos); //adds to node_new
224                        node_mutated->parent = node_new_div;
225                        // now, swap children with 50% chance
226                        if (rndUint(2) == 0)
227                        {
228                                node_mutated_parent = node_new_div->child;
229                                node_new_div->child = node_new_div->child2;
230                                node_new_div->child2 = node_mutated_parent;
231                        }
232                }
233                break;
234                case F4_ADD_CONN:
235                {
236                        // add connection
237
238                        // the probability that a randomly selected node will be a neuron and additionally this neuron will accept inputs is low,
239                        // so we disregard randomly picked node_mutated and build a list of all valid candidate nodes here, then randomly select one from them.
240
241                        vector<f4_Node*> candidate_nodes; //neurons that accept input(s)
242                        for (int i = 0; i < g->count(); i++)
243                        {
244                                f4_Node *node = g->ordNode(i);
245                                f4_Node *node_parent = node->parent;
246                                if (node_parent == NULL || node_parent->neuclass == NULL) continue;
247                                int prefinputs = node_parent->neuclass->getPreferredInputs();
248                                if (prefinputs == -1 ||
249                                        prefinputs > 0) //would be nice if we could easily and quickly check if the parent already has its preferred inputs used, so that we do not produce an invalid mutation here... it is possible through the f4_Cell.n_conns field, but only during organism development
250                                        candidate_nodes.push_back(node);
251                        }
252
253                        if (candidate_nodes.size() == 0)
254                                return GENOPER_OPFAIL;
255
256                        node_mutated = candidate_nodes[rndUint((unsigned int)candidate_nodes.size())];
257                        f4_Node *node_mutated_parent = node_mutated->parent;
258
259                        int node_mutated_parent_index, other_neuron_index;
260                        bool ok = findConnectionNeuronIndexes(g, node_mutated_parent, true, node_mutated_parent_index, other_neuron_index); //node_mutated_parent_index==-1 should never happen, we earlier selected the neuron we are now looking for
261                        if (!ok)
262                                return GENOPER_OPFAIL;
263
264                        node_mutated->parent->removeChild(node_mutated); //this subtree will be reconnected below, as a child to node_new_conn
265                        f4_Node *node_new_conn = new f4_Node('[', node_mutated->parent, node_mutated->parent->pos);
266                        node_new_conn->addChild(node_mutated);
267                        node_mutated->parent = node_new_conn; // node_mutated_parent is the neuron, node_mutated->parent is '['
268                        connectionNodeChangeRandom(node_new_conn, node_mutated_parent_index, other_neuron_index);
269                }
270                break;
271                case F4_ADD_NEUPAR:
272                {
273                        // add neuron modifier
274                        node_mutated->parent->removeChild(node_mutated);
275                        f4_Node *n2 = new f4_Node(':', node_mutated->parent, node_mutated->parent->pos);
276                        nparNodeMakeRandom(n2);
277                        n2->addChild(node_mutated);
278                        node_mutated->parent = n2;
279                }
280                break;
281                case F4_ADD_REP:
282                {
283                        // add repetition ('#')
284                        // repeated code (left child) is the original, right child is empty, count is set to 2
285                        f4_Node *n3 = node_mutated->parent;
286                        n3->removeChild(node_mutated);
287                        f4_Node *n2 = new f4_Node('#', n3, n3->pos);
288                        n2->reps = 2;
289                        n2->addChild(node_mutated);
290                        new f4_Node('>', n2, n2->pos);
291                        node_mutated->parent = n2;
292                }
293                break;
294                case F4_ADD_SIMP:
295                {
296                        // add simple node
297                        int modifier_index = GenoOperators::getRandomChar(all_modifiers, excluded_modifiers.c_str());
298                        if (modifier_index < 0)
299                                return GENOPER_OPFAIL;
300                        node_mutated->parent->removeChild(node_mutated);
301                        // old source: choose a simple node from ADD_SIMPLE_CODES
302                        //f4_Node *n2 = new f4_Node(ADD_SIMPLE_CODES[rndUint(strlen(ADD_SIMPLE_CODES))], node_mutated->parent, node_mutated->parent->pos);
303                        f4_Node *n2 = new f4_Node(all_modifiers[modifier_index], node_mutated->parent, node_mutated->parent->pos);
304                        n2->addChild(node_mutated);
305                        node_mutated->parent = n2;
306                }
307                break;
308                }
309        }
310        break;
311
312        case F4_DEL:
313        {
314                method = F4_ADD_COUNT - 1 + F4_DEL;
315                // delete a node
316                // must pick a node with parent, and at least one child
317                // already picked a node, but repeat may be needed
318                for (int i = 0; i < 10; i++)
319                {
320                        if ((node_mutated->parent != NULL) && (g != node_mutated->parent))
321                                if (node_mutated->child != NULL)
322                                        break;
323                        // try a new one
324                        node_mutated = g->child->randomNode();
325                }
326                if ((node_mutated->parent != NULL) && (g != node_mutated->parent))
327                {
328                        switch (node_mutated->childCount())
329                        {
330                        case 0: break;
331                        case 1:  // one child
332                        {
333                                f4_Node *node_mutated_parent = node_mutated->parent;
334                                node_mutated_parent->removeChild(node_mutated);
335                                if (node_mutated->child != NULL)
336                                {
337                                        node_mutated->child->parent = node_mutated_parent;
338                                        node_mutated_parent->addChild(node_mutated->child);
339                                        node_mutated->child = NULL;
340                                }
341                                if (node_mutated->child2 != NULL)
342                                {
343                                        node_mutated->child2->parent = node_mutated_parent;
344                                        node_mutated_parent->addChild(node_mutated->child2);
345                                        node_mutated->child2 = NULL;
346                                }
347                                // destroy n1
348                                node_mutated->parent = NULL;
349                                delete node_mutated;
350                        }
351                        break;
352
353                        case 2:  // two children
354                        {
355                                // two children
356                                f4_Node *n2 = node_mutated->parent;
357                                n2->removeChild(node_mutated);
358                                // n1 has two children. pick one randomly 50-50, destroy other
359                                if (rndUint(2) == 0)
360                                {
361                                        node_mutated->child->parent = n2;
362                                        n2->addChild(node_mutated->child);
363                                        node_mutated->child = NULL;
364                                        node_mutated->child2->parent = NULL;
365                                }
366                                else
367                                {
368                                        node_mutated->child2->parent = n2;
369                                        n2->addChild(node_mutated->child2);
370                                        node_mutated->child2 = NULL;
371                                        node_mutated->child->parent = NULL;
372                                }
373                                // destroy n1
374                                node_mutated->parent = NULL;
375                                delete node_mutated;
376                        }
377                        break;
378                        }
379                }
380                else return GENOPER_OPFAIL;
381        }
382        break;
383        case F4_MOD:
384        {
385                method = F4_ADD_COUNT - 1 + F4_MOD;
386                // change a node
387                // the only nodes that are modifiable are F4_MUT_CHANGE_CODES
388                // try to get a modifiable node
389                // already picked a node, but repeat may be needed
390                int i = 0;
391                while (1)
392                {
393                        if (strchr(F4_MUT_CHANGE_CODES, node_mutated->name[0])) break;
394                        // try a new one
395                        node_mutated = g->child->randomNode();
396                        i++;
397                        if (i >= 20) return GENOPER_OPFAIL;
398                }
399                switch (node_mutated->name[0])
400                {
401                case '<':
402                {
403                        // swap children
404                        f4_Node *n2 = node_mutated->child;
405                        node_mutated->child = node_mutated->child2;
406                        node_mutated->child2 = n2;
407                }
408                break;
409                case '[':
410                {
411                        switch (roulette(probmodneu, F4_MODNEU_COUNT))
412                        {
413                        case F4_MODNEU_CONN:
414                        {
415                                f4_Node *neuron = node_mutated; //we start in '[' node and follow up parents until we find the neuron with these connections
416                                while (neuron != NULL && neuron->neuclass == NULL) neuron = neuron->parent;
417                                if (neuron == NULL)
418                                        return GENOPER_OPFAIL; //did not find a neuron on the way up tree
419
420
421                                int neuron_index, other_neuron_index;
422                                bool ok = findConnectionNeuronIndexes(g, neuron, true, neuron_index, other_neuron_index); //neuron_index==-1 should never happen, we know the neuron is in the tree
423                                if (!ok)
424                                        return GENOPER_OPFAIL;
425
426                                connectionNodeChangeRandom(node_mutated, neuron_index, other_neuron_index);
427                                break;
428                        }
429                        case F4_MODNEU_WEIGHT:
430                                node_mutated->conn_weight = GenoOperators::getMutatedNeuronConnectionWeight(node_mutated->conn_weight);
431                                break;
432                        }
433                }
434                break;
435
436                case '#':
437                {
438                        repeatNodeChangeRandom(node_mutated);
439                }
440                break;
441                }
442        }
443        break;
444
445        default: //no mutations allowed?
446                return GENOPER_OPFAIL;
447        }
448        return GENOPER_OK;
449}
450
451// find all neurons and the needle
452vector<NeuroClass*> Geno_f4::findAllNeuronsAndNode(f4_Node * const & g, f4_Node* const &needle_neuron, int &found_index)
453{
454        found_index = -1; // not found (for example, needle_neuron is not a neuroclass node or not added to the "g" tree)
455        vector<NeuroClass*> neulist;
456        for (int i = 0; i < g->count(); i++)
457        {
458                f4_Node *node = g->ordNode(i);
459                if (node->neuclass != NULL)
460                {
461                        neulist.push_back(node->neuclass);
462                        if (node == needle_neuron)
463                                found_index = int(neulist.size()) - 1;
464                }
465        }
466        return neulist;
467}
468
469bool Geno_f4::findConnectionNeuronIndexes(f4_Node * const &g, f4_Node *neuron, bool other_has_output, int &neuron_index, int &other_neuron_index)
470{
471        vector<NeuroClass*> neulist = findAllNeuronsAndNode(g, neuron, neuron_index);
472        if (neuron_index == -1)
473                return false;
474
475        other_neuron_index = other_has_output ?
476                GenoOperators::getRandomNeuroClassWithOutput(neulist) //find an existing neuron that provides an output
477                :
478                GenoOperators::getRandomNeuroClassWithInput(neulist); //find an existing neuron that accepts input(s)
479        return other_neuron_index >= 0;
480}
481
482// change a [ node
483void Geno_f4::connectionNodeChangeRandom(f4_Node *nn, int nn_index, int other_index) const
484{
485        // relative input connection to some existing neuron
486        nn->conn_from = nn_index - other_index;
487        //nn->conn_from = (int)(4.0f * (rndDouble(1) - 0.5)); //in very old times - did not care about neuron input/output preferences
488
489        nn->conn_weight = GenoOperators::getMutatedNeuronConnectionWeight(nn->conn_weight);
490}
491
492
493// make a random : node
494void Geno_f4::nparNodeMakeRandom(f4_Node *nn) const
495{
496        unsigned int prop = rndUint(3); //random neuron property
497        nn->prop_symbol = "!=/"[prop];
498        nn->prop_increase = rndUint(2) == 1;
499}
500
501// change a repeat # node
502void Geno_f4::repeatNodeChangeRandom(f4_Node *nn) const
503{
504        if (rndDouble(1) < 0.5) nn->reps++; else nn->reps--; // change count
505        if (nn->reps < 1) nn->reps = 1;
506        if (nn->reps > mut_max_rep) nn->reps = mut_max_rep;
507}
508
509
510int Geno_f4::MutateOneValid(f4_Node *& g, int &method) const
511// mutate one, until a valid genotype is obtained
512{
513        // ! the genotype is g->child (not g) !
514        int i, res;
515        f4_Node *gcopy = NULL;
516        const int TRY_MUTATE = 20;
517        // try this at most TRY_MUTATE times: copy, mutate, then validate
518        for (i = 0; i < TRY_MUTATE; i++)
519        {
520                gcopy = g->duplicate();
521
522                res = MutateOne(gcopy, method);
523
524                if (GENOPER_OK != res)
525                {
526                        // mutation failed, try again
527                        delete gcopy;
528                        continue;  // for
529                }
530                // try to validate it
531                res = ValidateRecur(gcopy, 10);
532                // accept if it is OK, or was repaired
533                if (GENOPER_OK == res)
534                        //(GENOPER_REPAIR == res)
535                {
536                        // destroy the original one
537                        g->destroy();
538                        // make it the new one
539                        *g = *gcopy;
540                        gcopy->child = NULL;
541                        gcopy->child2 = NULL;
542                        delete gcopy;
543                        res = GENOPER_OK;
544                        goto retm1v;
545                }
546                delete gcopy;
547        }
548        // attempts failed
549        res = GENOPER_OPFAIL;
550retm1v:
551        return res;
552}
553
554
555int Geno_f4::mutate(char *& g, float & chg, int &method)
556{
557        f4_Node *root = new f4_Node;
558        if (f4_process(g, root) || root->childCount() != 1)
559        {
560                delete root;
561                return GENOPER_OPFAIL;
562        } // could not convert or bad: fail
563        // mutate one node, set chg as this percent
564        chg = 1.0 / float(root->child->count());
565        if (MutateOneValid(root, method) != GENOPER_OK)
566        {
567                delete root;
568                return GENOPER_OPFAIL;
569        }
570        // OK, convert back to string
571        g[0] = 0;
572        root->child->sprintAdj(g);
573        delete root;
574        return GENOPER_OK;
575}
576
577
578/*
579int Geno_f4::MutateMany(char *& g, float & chg)
580// check if original is valid, then
581// make a number of mutations
582{
583int res, n, i;
584int totNodes = 0;
585int maxToMut = 0;
586
587// convert to tree
588f4_Node *root;
589root = new f4_Node();
590res = f4_processrec(g, 0, root);
591if (res) {
592// could not convert, fail
593goto retm;
594}
595if (1 != root->childCount()) {
596res = GENOPER_OPFAIL;
597goto retm;
598}
599
600// check if original is valid
601res = ValidateRec( root, 20 );
602// might have been repaired!
603if (GENOPER_REPAIR==res) {
604res = GENOPER_OK;
605}
606if (GENOPER_OK != res) {
607goto retm;
608}
609
610// decide number of nodes to mutate
611// decide maximum number of nodes to mutate: 0.25*nodes, min 2
612totNodes = root->child->count();
613maxToMut = (int)( 0.25f * totNodes);
614if (maxToMut<2) maxToMut=2;
615if (maxToMut>totNodes) maxToMut=totNodes;
616
617// decide number of nodes to mutate
618n = (int)( 0.5 + rndDouble(1) * maxToMut );
619if (n<1) n=1;
620if (n>totNodes) n=totNodes;
621// set chg as this percent
622chg = ((float)n) / ((float)totNodes);
623for (i=0; i<n; i++)
624{
625res = MutateOneValid(root);
626if (GENOPER_OK != res)
627{
628res = GENOPER_OPFAIL;
629goto retm;
630}
631}
632// OK, convert back to string
633g[0]=0;
634root->child->sprintAdj(g);
635retm:
636delete root;
637return res;
638}
639*/
640
641
642int Geno_f4::CrossOverOne(f4_Node *g1, f4_Node *g2, float chg) const
643{
644        // ! the genotypes are g1->child and g2->child (not g1 g2) !
645        // single offspring in g1
646        int smin, smax;
647        float size;
648        f4_Node *n1, *n2, *n1p, *n2p;
649
650        // determine desired size
651        size = (1 - chg) * (float)g1->count();
652        smin = (int)(size * 0.9f - 1);
653        smax = (int)(size * 1.1f + 1);
654        // get a random node with desired size
655        n1 = g1->child->randomNodeWithSize(smin, smax);
656
657        // determine desired size
658        size = (1 - chg) * (float)g2->count();
659        smin = (int)(size * 0.9f - 1);
660        smax = (int)(size * 1.1f + 1);
661        // get a random node with desired size
662        n2 = g2->child->randomNodeWithSize(smin, smax);
663
664        // exchange the two nodes:
665        n1p = n1->parent;
666        n2p = n2->parent;
667        n1p->removeChild(n1);
668        n1p->addChild(n2);
669        n2p->removeChild(n2);
670        n2p->addChild(n1);
671        n1->parent = n2p;
672        n2->parent = n1p;
673
674        return GENOPER_OK;
675}
676
677int Geno_f4::crossOver(char *&g1, char *&g2, float &chg1, float &chg2)
678{
679        f4_Node root1, root2, *copy1, *copy2;
680
681        // convert genotype strings into tree structures
682        if (f4_process(g1, &root1) || (root1.childCount() != 1)) return GENOPER_OPFAIL;
683        if (f4_process(g2, &root2) || (root2.childCount() != 1)) return GENOPER_OPFAIL;
684
685        // decide amounts of crossover, 0.1-0.9
686        chg1 = 0.1 + rndDouble(0.8);
687        chg2 = 0.1 + rndDouble(0.8);
688
689        copy1 = root1.duplicate();
690        if (CrossOverOne(copy1, &root2, chg1) != GENOPER_OK) { delete copy1; copy1 = NULL; }
691        copy2 = root2.duplicate();
692        if (CrossOverOne(copy2, &root1, chg2) != GENOPER_OK) { delete copy2; copy2 = NULL; }
693
694        g1[0] = 0;
695        g2[0] = 0;
696        if (copy1) { copy1->child->sprintAdj(g1); delete copy1; }
697        if (copy2) { copy2->child->sprintAdj(g2); delete copy2; }
698        if (g1[0] || g2[0]) return GENOPER_OK; else return GENOPER_OPFAIL;
699}
700
701uint32_t Geno_f4::style(const char *g, int pos)
702{
703        char ch = g[pos];
704
705        // style categories
706#define STYL4CAT_MODIFIC F14_MODIFIERS ","
707#define STYL4CAT_NEUMOD "/!="
708#define STYL4CAT_NEUSPECIAL "|@*"
709#define STYL4CAT_DIGIT "+-0123456789.[]" //'+' is only for adjusting old-style properties "/!="
710#define STYL4CAT_REST ":XN<># "
711
712        if (!isalpha(ch) && !strchr(STYL4CAT_MODIFIC STYL4CAT_NEUMOD STYL4CAT_NEUSPECIAL STYL4CAT_DIGIT STYL4CAT_REST "\t", ch))
713        {
714                return GENSTYLE_CS(0, GENSTYLE_INVALID);
715        }
716        uint32_t style = GENSTYLE_CS(0, GENSTYLE_STRIKEOUT); //default, should be changed below
717        if (strchr("X", ch))                     style = GENSTYLE_CS(0, GENSTYLE_BOLD);
718        else if (strchr(":", ch))                style = GENSTYLE_CS(0, GENSTYLE_NONE);
719        else if (strchr("#", ch))                style = GENSTYLE_RGBS(220, 0, 0, GENSTYLE_BOLD);
720        else if (strchr("/=!", ch))              style = GENSTYLE_RGBS(255, 140, 0, GENSTYLE_BOLD); //property... for now, f4 does not supoprt properties in general for any neuron class, like f1 does
721        else if (strchr("N@|*", ch))             style = GENSTYLE_RGBS(150, 0, 150, GENSTYLE_BOLD); //neuroclass
722        else if (strchr("<", ch))                style = GENSTYLE_RGBS(0, 0, 200, GENSTYLE_BOLD);
723        else if (strchr(">", ch))                style = GENSTYLE_RGBS(0, 0, 100, GENSTYLE_NONE);
724        else if (strchr(STYL4CAT_DIGIT, ch))     style = GENSTYLE_CS(GENCOLOR_NUMBER, GENSTYLE_NONE);
725        else if (strchr(STYL4CAT_MODIFIC, ch))   style = GENSTYLE_RGBS(100, 100, 100, GENSTYLE_NONE);
726        else if (strchr(STYL4CAT_NEUMOD, ch))    style = GENSTYLE_RGBS(0, 150, 0, GENSTYLE_NONE);
727        if (isalpha(ch))
728        {
729                // allowed neuron formats:
730                //   N:CLASSNAME
731                //   N:@
732                //   N:|
733                // old syntax still supported in coloring, but no longer valid:
734                //   [SENSOR, WEIGHT]
735                //   N@
736                //   N|
737                // ...so must have N: or [ before neuroclass name (or just N, but this is handled above - for N@|* only)
738
739                while (pos > 0)
740                {
741                        pos--;
742                        if (!isalpha(g[pos]))
743                        {
744                                if (isupper(g[pos + 1]) && (g[pos] == '[') || (g[pos] == ':' && pos > 0 && g[pos - 1] == 'N')) //we may have sequences like :-/:I (even though they are not valid) - in this example "I" should not be treated as neuron name, hence there must also be a "N" before ":"
745                                        style = GENSTYLE_RGBS(150, 0, 150, GENSTYLE_BOLD); // neuroclass
746                                //(...) else (...)
747                                //      style = GENSTYLE_RGBS(255, 140, 0, GENSTYLE_BOLD); // property - current f4 does not support neuron properties in a general case, only those old-style "/=!" as +! -! += -= +/ -/
748                                break;
749                        }
750                }
751        }
752        return style;
753}
Note: See TracBrowser for help on using the repository browser.