Ignore:
Timestamp:
04/27/23 04:04:06 (2 years ago)
Author:
Maciej Komosinski
Message:

Improvements in f4:

  • fixed a bug where newly created cells in a given development step were not counted as in-active-development (overlooked), and if they were the only in-active-development cells, the development of an organism would stop
  • added one extra development step (#ifdef EXTRA_STEP_CELL_DEVELOPMENT) so that cells that became not in-active-development ("halted" or yielding, usually due to waiting for neurons to develop in other cells) would get a chance to continue development (important when we don't want to ignore invalid neuron connections, #ifdef TREAT_BAD_CONNECTIONS_AS_INVALID_GENO)
  • ensured that all connections in a cell are processed (earlier they could be skipped if the development of the cell was "halted" and all cells became not in-active-development)
  • got rid of neuron connection syntax [sensor:weight], now all neuron classes are handled in a uniform way and their [connections] too; the only allowed syntax is [input_index:weight]
  • unified handling of all neuroclasses during parsing, conversion and mutation
  • more correct syntax coloring
  • got rid of general-purpose fields (i1, i2, f1, s1) in class f4_node - now separate fields serve their individual purpose
  • rewritten creating and modifying neuron connections - it is more deliberate to satisfy neuron input/output preferences
  • some invalid neuron connections make a genotype invalid (previously invalid neuron connections were ignored and the genotype was considered valid)
  • added (surprisingly missing) simple debug printout functions to see the f4_Node tree structure and the developing f4_Cells
  • more informative variable and constant names
  • improved performance
File:
1 edited

Legend:

Unmodified
Added
Removed
  • cpp/frams/genetics/f4/f4_oper.cpp

    r1108 r1227  
    11// This file is a part of Framsticks SDK.  http://www.framsticks.com/
    2 // Copyright (C) 1999-2020  Maciej Komosinski and Szymon Ulatowski.
     2// Copyright (C) 1999-2023  Maciej Komosinski and Szymon Ulatowski.
    33// See LICENSE.txt for details.
    44
     
    66// Copyright (C) since 2001 Maciej Komosinski
    77// 2018, Grzegorz Latosinski, added support for new API for neuron types and their properties
     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 (having valid genotypes with a lot of "junk code") in this representation looks like a good idea.
     14//
     15// Note: symbols after the last > are ignored, for example /*4*/<X>N:N>N:N[2:-0.5]XXXwhatever but since they are not parsed into the f4_Node tree, they will be lost after any mutation.
     16//
     17// TODO the behavior of neuron input indexes during mutation seems badly implemented (see also TREAT_BAD_CONNECTIONS_AS_ERRORS). 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 '['.
     18// TODO add simplifying sequences of modifiers (so capital and small letter cancel out, like in f1) - but seems like each single modifier is a separate f4_Node? and perhaps we don't want to use the repair mechanism for this... maybe mutations, when they add/modify/remove a modifier node, should be "cleaning" the tree by removing nodes when they encounter contradictory modifiers on the same subpath?
     19// TODO add support for properties of (any class of) neurons - not just sigmoid/force/intertia (':' syntax) for N
     20// TODO add mapping genotype character ranges for neural [connections]
     21
    822
    923#include "f4_oper.h"
     
    2034
    2135// codes that can be changed (apart from being added/deleted)
    22 #define MUT_CHAN_CODES "<[#"
    23 #define REP_MAXCOUNT 19
     36#define F4_MUT_CHANGE_CODES "<[#"
    2437
    2538#define FIELDSTRUCT Geno_f4
     
    2740static ParamEntry geno_f4_paramtab[] =
    2841{
    29         { "Genetics: f4", 1, F4_COUNT + F4_ADD_COUNT + 1, },
    30         { "f4_mut_add", 0, 0, "Add node", "f 0 100 50", FIELD(prob[F4_ADD]), "mutation: probability of adding a node", },
    31         { "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", },
    32         { "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", },
    33         { "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", },
    34         { "f4_mut_add_rep", 0, 0, "- add repetition", "f 0 100 10", FIELD(probadd[F4_ADD_REP]), "add node mutation: probability of adding a repetition", },
    35         { "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", },
    36         { "f4_mut_del", 0, 0, "Delete node", "f 0 100 20", FIELD(prob[F4_DEL]), "mutation: probability of deleting a node", },
    37         { "f4_mut_mod", 0, 0, "Modify node", "f 0 100 30", FIELD(prob[F4_MOD]), "mutation: probability of changing a node", },
     42        { "Genetics: f4", 1, F4_COUNT + F4_ADD_COUNT + F4_MODNEU_COUNT + 2, },
     43        { "f4_mut_add", 0, 0, "Add node", "f 0 100 50", FIELD(prob[F4_ADD]), "Mutation: probability of adding a node", },
     44        { "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", },
     45        { "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", },
     46        { "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", },
     47        { "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", },
     48        { "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", },
     49
     50        { "f4_mut_del", 0, 0, "Delete node", "f 0 100 20", FIELD(prob[F4_DEL]), "Mutation: probability of deleting a node", },
     51
     52        { "f4_mut_mod", 0, 0, "Modify node", "f 0 100 30", FIELD(prob[F4_MOD]), "Mutation: probability of changing a node", },
     53        { "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", },
     54        { "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", },
     55
     56        { "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", },
    3857        { "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 ")", },
    3958        { 0, },
     
    5978        mutation_method_names[index++] = "deleted a node";
    6079        mutation_method_names[index++] = "modified a node";
    61         if (index != F4_COUNT + F4_ADD_COUNT - 1) logMessage("Geno_f4", "Constructor", 3, "Mutation names init error");
     80        if (index != F4_COUNT + F4_ADD_COUNT - 1) logMessage("Geno_f4", "Constructor", LOG_CRITICAL, "Mutation names init error");
    6281}
    6382
     
    6786}
    6887
    69 int Geno_f4::ValidateRec(f4_node *geno, int retrycount) const
     88int Geno_f4::ValidateRec(f4_Node *geno, int retrycount) const
    7089{
    7190        // ! the genotype is geno->child (not geno) !
     
    7695
    7796        // errors not fixed:
    78         if (GENOPER_OPFAIL == cells.geterror())
    79         {
    80                 if (cells.geterrorpos() >= 0) return 1 + cells.geterrorpos();
     97        if (cells.getErrorCode() == GENOPER_OPFAIL)
     98        {
     99                if (cells.getErrorPos() >= 0) return 1 + cells.getErrorPos();
    81100                return GENOPER_OPFAIL;
    82101        }
    83102        // errors can be fixed
    84         if (GENOPER_REPAIR == cells.geterror())
     103        if (cells.getErrorCode() == GENOPER_REPAIR)
    85104        {
    86105                cells.repairGeno(geno, 1);
     
    102121{
    103122        // convert geno to tree, then try to validate 20 times
    104         f4_node root;
     123        f4_Node root;
    105124        if (f4_processrec(geno, 0, &root) || root.childCount() != 1) return GENOPER_OK; // cannot repair
    106125        if (ValidateRec(&root, 20) == GENOPER_REPAIR) // if repaired, make it back to string
     
    115134int Geno_f4::checkValidity(const char* geno, const char *genoname)
    116135{
    117         f4_node root;
     136        f4_Node root;
    118137        int res = f4_processrec(geno, 0, &root);
    119138        if (res) return res;  // errorpos, >0
     
    121140        f4_Cells cells(root.child, 0);
    122141        cells.simulate();
    123         if (cells.geterror() == GENOPER_OPFAIL || cells.geterror() == GENOPER_REPAIR)
    124         {
    125                 if (cells.geterrorpos() >= 0) return 1 + cells.geterrorpos();
     142        if (cells.getErrorCode() == GENOPER_OPFAIL || cells.getErrorCode() == GENOPER_REPAIR)
     143        {
     144                if (cells.getErrorPos() >= 0) return 1 + cells.getErrorPos();
    126145                else return 1; //earlier: GENOPER_OPFAIL;
    127146        }
     
    130149
    131150
    132 int Geno_f4::MutateOne(f4_node *& g, int &method) const
     151int Geno_f4::MutateOne(f4_Node *& g, int &method) const
    133152{
    134153        // ! the genotype is g->child (not g) !
     
    136155        // do the mutation
    137156        // pick a random node
    138         f4_node *n1 = g->child->randomNode();
    139         vector<NeuroClass*> neulist;
    140         //DB( printf("%c\n", n1->name); )
    141         int neuronid = -1;
     157        f4_Node *node_mutated = g->child->randomNode();
     158        //DB( printf("%c\n", node_mutated->name); )
    142159
    143160        switch (roulette(prob, F4_COUNT))
     
    151168                {
    152169                        // add division ('<')
    153                         f4_node *n3 = n1->parent;
    154                         n3->removeChild(n1);
    155                         f4_node *n2 = new f4_node('<', n3, n3->pos);
    156                         n2->addChild(n1);
     170                        f4_Node *node_mutated_parent = node_mutated->parent;
     171                        node_mutated_parent->removeChild(node_mutated);
     172                        f4_Node *node_new_div = new f4_Node('<', node_mutated_parent, node_mutated_parent->pos);
     173                        node_new_div->addChild(node_mutated);
    157174                        // new cell is stick or neuron
    158175                        // "X>" or "N>"
    159                         f4_node *n5 = NULL;
    160                         double pr = rndDouble(1);
    161                         pr -= 0.5;
    162                         if (pr < 0) n5 = new f4_node('X', n2, n2->pos);
     176                        constexpr double STICK_OR_NEURON = 0.5; // hardcoded probability... could be parametrized, but in a general case (unknown fitness goal) 0.5 makes sense?
     177                        f4_Node *node_new = NULL; //stick or neuron or neural connection
     178                        if (rndDouble(1) < STICK_OR_NEURON)
     179                                node_new = new f4_Node('X', node_new_div, node_new_div->pos);
    163180                        else
    164181                        {
    165182                                // make neuron
    166183                                NeuroClass *rndclass = GenoOperators::getRandomNeuroClass(Model::SHAPETYPE_BALL_AND_STICK);
    167                                 if (rndclass == NULL)
     184                                if (rndclass == NULL) //no active neurons?
    168185                                {
    169                                         n5 = new f4_node('X', n2, n2->pos);
     186                                        node_new = new f4_Node('X', node_new_div, node_new_div->pos);
    170187                                }
    171188                                else
    172189                                {
    173                                         f4_node *n4 = new f4_node(rndclass->getName().c_str(), n2, n2->pos);
    174                                         if (rndclass->getPreferredInputs() != 0)
     190                                        f4_Node *node_new_neuron = new f4_Node(rndclass->getName().c_str(), node_new_div, node_new_div->pos);
     191                                        node_new_neuron->neuclass = rndclass;
     192                                        node_new = node_new_neuron; //can be changed below if all goes well and we add a new connection too
     193                                        if (probadd[F4_ADD_CONN] > 0) //user wants to add connections
    175194                                        {
    176                                                 neuronid = -1;
    177                                                 for (int i = 0; i < g->count(); i++)
     195                                                if (rndclass->getPreferredInputs() != 0) //neuron also wants connections?
    178196                                                {
    179                                                         f4_node *gcur = g->ordNode(i);
    180                                                         char* temp = (char*)gcur->name.c_str();
    181                                                         NeuroClass *neuclass = GenoOperators::parseNeuroClass(temp);
    182                                                         if (neuclass != NULL)
     197                                                        int node_new_neuron_index, other_neuron_index;
     198                                                        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
     199                                                        if (ok) //we can create a new connection
    183200                                                        {
    184                                                                 neulist.push_back(neuclass);
    185                                                         }
    186                                                         if (g->ordNode(i) == n3)
    187                                                         {
    188                                                                 neuronid = int(neulist.size()) - 1;
     201                                                                node_new = new f4_Node('[', node_new_neuron, node_new_div->pos);
     202                                                                connectionNodeChangeRandom(node_new, node_new_neuron_index, other_neuron_index);
    189203                                                        }
    190204                                                }
    191                                                 if (neuronid == -1)
     205                                                else if (rndclass->getPreferredOutput() > 0) //neuron also wants connections?
    192206                                                {
    193                                                         return GENOPER_OPFAIL;
     207                                                        // 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).
     208                                                        // The "false" argument in findConnectionNeuronIndexes() below is not suffient, because we also need to access (find) the f4_Node of the other neuron.
     209                                                        // A similar logic is implemented in F4_ADD_CONN below, but let's not complicate this F4_ADD_DIV mutation anymore.
     210                                                        // A 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.).
     211                                                        //bool ok = findConnectionNeuronIndexes(g, ... , false, ..., ...);
    194212                                                }
    195                                                 n5 = new f4_node('[', n4, n2->pos);
    196                                                 linkNodeMakeRandom(n5, neuronid, neulist);
    197                                         }
    198                                         else {
    199                                                 n5 = n4;
    200213                                        }
    201214                                }
    202215                        }
    203                         new f4_node('>', n5, n5->pos);
    204                         n1->parent = n2;
    205                         // now with 50% chance swap children
     216                        new f4_Node('>', node_new, node_new->pos); //adds to node_new
     217                        node_mutated->parent = node_new_div;
     218                        // now, swap children with 50% chance
    206219                        if (rndUint(2) == 0)
    207220                        {
    208                                 n3 = n2->child;
    209                                 n2->child = n2->child2;
    210                                 n2->child2 = n3;
    211                         }
    212                 }
    213                         break;
     221                                node_mutated_parent = node_new_div->child;
     222                                node_new_div->child = node_new_div->child2;
     223                                node_new_div->child2 = node_mutated_parent;
     224                        }
     225                }
     226                break;
    214227                case F4_ADD_CONN:
    215228                {
    216                         // add link
    217                         f4_node *par = n1->parent;
    218                         char* temp = (char*)par->name.c_str();
    219                         NeuroClass *neuclass = GenoOperators::parseNeuroClass(temp);
    220                         if (neuclass != NULL)
    221                         {
    222                                 n1->parent->removeChild(n1);
    223                                 f4_node *n2 = new f4_node('[', n1->parent, n1->parent->pos);
    224                                 n2->addChild(n1);
    225                                 n1->parent = n2;
    226                                 neuronid = -1;
    227                                 for (int i = 0; i < g->count(); i++)
    228                                 {
    229                                         f4_node *gcur = g->ordNode(i);
    230                                         temp = (char*)gcur->name.c_str();
    231                                         NeuroClass *neuclass = GenoOperators::parseNeuroClass(temp);
    232                                         if (neuclass != NULL)
    233                                         {
    234                                                 neulist.push_back(neuclass);
    235                                         }
    236                                         if (gcur == par)
    237                                         {
    238                                                 neuronid = int(neulist.size()) - 1;
    239                                         }
    240                                 }
    241                                 if (neuronid == -1)
    242                                 {
    243                                         return GENOPER_OPFAIL;
    244                                 }
    245                                 linkNodeMakeRandom(n2, neuronid, neulist);
    246                         }
    247                 }
    248                         break;
     229                        // add connection
     230
     231                        // the probability that a randomly selected node will be a neuron and additionally this neuron will accept inputs is low,
     232                        // so we disregard randomly picked node_mutated and build a list of all valid candidate nodes here, then select a random one from them.
     233
     234                        vector<f4_Node*> candidate_nodes; //neurons that accept input(s)
     235                        for (int i = 0; i < g->count(); i++)
     236                        {
     237                                f4_Node *node = g->ordNode(i);
     238                                f4_Node *node_parent = node->parent;
     239                                if (node_parent == NULL || node_parent->neuclass == NULL) continue;
     240                                int prefinputs = node_parent->neuclass->getPreferredInputs();
     241                                if (prefinputs == -1 ||
     242                                        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
     243                                        candidate_nodes.push_back(node);
     244                        }
     245
     246                        if (candidate_nodes.size() == 0)
     247                                return GENOPER_OPFAIL;
     248
     249                        node_mutated = candidate_nodes[rndUint((unsigned int)candidate_nodes.size())];
     250                        f4_Node *node_mutated_parent = node_mutated->parent;
     251
     252                        int node_mutated_parent_index, other_neuron_index;
     253                        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
     254                        if (!ok)
     255                                return GENOPER_OPFAIL;
     256
     257                        node_mutated->parent->removeChild(node_mutated); //this subtree will be reconnected below, as a child to node_new_conn
     258                        f4_Node *node_new_conn = new f4_Node('[', node_mutated->parent, node_mutated->parent->pos);
     259                        node_new_conn->addChild(node_mutated);
     260                        node_mutated->parent = node_new_conn; // node_mutated_parent is the neuron, node_mutated->parent is '['
     261                        connectionNodeChangeRandom(node_new_conn, node_mutated_parent_index, other_neuron_index);
     262                }
     263                break;
    249264                case F4_ADD_NEUPAR:
    250265                {
    251266                        // add neuron modifier
    252                         n1->parent->removeChild(n1);
    253                         f4_node *n2 = new f4_node(':', n1->parent, n1->parent->pos);
     267                        node_mutated->parent->removeChild(node_mutated);
     268                        f4_Node *n2 = new f4_Node(':', node_mutated->parent, node_mutated->parent->pos);
    254269                        nparNodeMakeRandom(n2);
    255                         n2->addChild(n1);
    256                         n1->parent = n2;
    257                 }
    258                         break;
     270                        n2->addChild(node_mutated);
     271                        node_mutated->parent = n2;
     272                }
     273                break;
    259274                case F4_ADD_REP:
    260275                {
    261276                        // add repetition ('#')
    262                         // repeated code (left child) is the original, right child is empty, count is 2
    263                         f4_node *n3 = n1->parent;
    264                         n3->removeChild(n1);
    265                         f4_node *n2 = new f4_node('#', n3, n3->pos);
    266                         n2->i1 = 2;
    267                         n2->addChild(n1);
    268                         new f4_node('>', n2, n2->pos);
    269                         n1->parent = n2;
    270                 }
    271                         break;
     277                        // repeated code (left child) is the original, right child is empty, count is set to 2
     278                        f4_Node *n3 = node_mutated->parent;
     279                        n3->removeChild(node_mutated);
     280                        f4_Node *n2 = new f4_Node('#', n3, n3->pos);
     281                        n2->reps = 2;
     282                        n2->addChild(node_mutated);
     283                        new f4_Node('>', n2, n2->pos);
     284                        node_mutated->parent = n2;
     285                }
     286                break;
    272287                case F4_ADD_SIMP:
    273288                {
    274289                        // add simple node
    275290                        // choose a simple node from ADD_SIMPLE_CODES
    276                         n1->parent->removeChild(n1);
    277                         //f4_node *n2 = new f4_node(ADD_SIMPLE_CODES[rndUint(strlen(ADD_SIMPLE_CODES))], n1->parent, n1->parent->pos);
     291                        node_mutated->parent->removeChild(node_mutated);
     292                        //f4_Node *n2 = new f4_Node(ADD_SIMPLE_CODES[rndUint(strlen(ADD_SIMPLE_CODES))], n1->parent, n1->parent->pos);
    278293                        int modifierid = GenoOperators::getRandomChar(all_modifiers, excluded_modifiers.c_str());
    279                         f4_node *n2 = new f4_node(all_modifiers[modifierid], n1->parent, n1->parent->pos);
    280                         n2->addChild(n1);
    281                         n1->parent = n2;
    282                 }
    283                         break;
    284                 }
    285         }
     294                        f4_Node *n2 = new f4_Node(all_modifiers[modifierid], node_mutated->parent, node_mutated->parent->pos);
     295                        n2->addChild(node_mutated);
     296                        node_mutated->parent = n2;
     297                }
    286298                break;
     299                }
     300        }
     301        break;
    287302
    288303        case F4_DEL:
     
    294309                for (int i = 0; i < 10; i++)
    295310                {
    296                         if ((NULL != n1->parent) && (g != n1->parent))
    297                                 if (NULL != n1->child)
     311                        if ((node_mutated->parent != NULL) && (g != node_mutated->parent))
     312                                if (node_mutated->child != NULL)
    298313                                        break;
    299314                        // try a new one
    300                         n1 = g->child->randomNode();
    301                 }
    302                 if ((NULL != n1->parent) && (g != n1->parent))
    303                 {
    304                         switch (n1->childCount())
     315                        node_mutated = g->child->randomNode();
     316                }
     317                if ((node_mutated->parent != NULL) && (g != node_mutated->parent))
     318                {
     319                        switch (node_mutated->childCount())
    305320                        {
    306321                        case 0: break;
    307322                        case 1:  // one child
    308323                        {
    309                                 f4_node *n2 = n1->parent;
    310                                 n2->removeChild(n1);
    311                                 if (NULL != n1->child)
     324                                f4_Node *node_mutated_parent = node_mutated->parent;
     325                                node_mutated_parent->removeChild(node_mutated);
     326                                if (node_mutated->child != NULL)
    312327                                {
    313                                         n1->child->parent = n2;
    314                                         n2->addChild(n1->child);
    315                                         n1->child = NULL;
     328                                        node_mutated->child->parent = node_mutated_parent;
     329                                        node_mutated_parent->addChild(node_mutated->child);
     330                                        node_mutated->child = NULL;
    316331                                }
    317                                 if (NULL != n1->child2)
     332                                if (node_mutated->child2 != NULL)
    318333                                {
    319                                         n1->child2->parent = n2;
    320                                         n2->addChild(n1->child2);
    321                                         n1->child2 = NULL;
     334                                        node_mutated->child2->parent = node_mutated_parent;
     335                                        node_mutated_parent->addChild(node_mutated->child2);
     336                                        node_mutated->child2 = NULL;
    322337                                }
    323338                                // destroy n1
    324                                 n1->parent = NULL;
    325                                 delete n1;
    326                         }
    327                                 break;
     339                                node_mutated->parent = NULL;
     340                                delete node_mutated;
     341                        }
     342                        break;
    328343
    329344                        case 2:  // two children
    330345                        {
    331346                                // two children
    332                                 f4_node *n2 = n1->parent;
    333                                 n2->removeChild(n1);
     347                                f4_Node *n2 = node_mutated->parent;
     348                                n2->removeChild(node_mutated);
    334349                                // n1 has two children. pick one randomly 50-50, destroy other
    335350                                if (rndUint(2) == 0)
    336351                                {
    337                                         n1->child->parent = n2;
    338                                         n2->addChild(n1->child);
    339                                         n1->child = NULL;
    340                                         n1->child2->parent = NULL;
     352                                        node_mutated->child->parent = n2;
     353                                        n2->addChild(node_mutated->child);
     354                                        node_mutated->child = NULL;
     355                                        node_mutated->child2->parent = NULL;
    341356                                }
    342357                                else
    343358                                {
    344                                         n1->child2->parent = n2;
    345                                         n2->addChild(n1->child2);
    346                                         n1->child2 = NULL;
    347                                         n1->child->parent = NULL;
     359                                        node_mutated->child2->parent = n2;
     360                                        n2->addChild(node_mutated->child2);
     361                                        node_mutated->child2 = NULL;
     362                                        node_mutated->child->parent = NULL;
    348363                                }
    349364                                // destroy n1
    350                                 n1->parent = NULL;
    351                                 delete n1;
    352                         }
    353                                 break;
     365                                node_mutated->parent = NULL;
     366                                delete node_mutated;
     367                        }
     368                        break;
    354369                        }
    355370                }
    356371                else return GENOPER_OPFAIL;
    357372        }
    358                 break;
     373        break;
    359374        case F4_MOD:
    360375        {
    361376                method = F4_ADD_COUNT - 1 + F4_MOD;
    362377                // change a node
    363                 // the only nodes that are modifiable are MUT_CHAN_CODES
     378                // the only nodes that are modifiable are F4_MUT_CHANGE_CODES
    364379                // try to get a modifiable node
    365380                // already picked a node, but repeat may be needed
     
    367382                while (1)
    368383                {
    369                         if (strchr(MUT_CHAN_CODES, n1->name[0])) break;
     384                        if (strchr(F4_MUT_CHANGE_CODES, node_mutated->name[0])) break;
    370385                        // try a new one
    371                         n1 = g->child->randomNode();
     386                        node_mutated = g->child->randomNode();
    372387                        i++;
    373388                        if (i >= 20) return GENOPER_OPFAIL;
    374389                }
    375                 switch (n1->name[0])
     390                switch (node_mutated->name[0])
    376391                {
    377392                case '<':
    378393                {
    379394                        // swap children
    380                         f4_node *n2 = n1->child; n1->child = n1->child2; n1->child2 = n2;
    381                 }
    382                         break;
     395                        f4_Node *n2 = node_mutated->child;
     396                        node_mutated->child = node_mutated->child2;
     397                        node_mutated->child2 = n2;
     398                }
     399                break;
    383400                case '[':
    384401                {
    385                         neuronid = -1;
    386                         for (int i = 0; i < g->count(); i++)
    387                         {
    388                                 f4_node *gcur = g->ordNode(i);
    389                                 char *temp = (char*)gcur->name.c_str();
    390                                 NeuroClass *neuclass = GenoOperators::parseNeuroClass(temp);
    391                                 if (neuclass != NULL)
    392                                 {
    393                                         neulist.push_back(neuclass);
    394                                 }
    395                                 if (gcur == n1)
    396                                 {
    397                                         neuronid = int(neulist.size()) - 1;
    398                                 }
    399                         }
    400                         if (neuronid == -1)
    401                         {
    402                                 return GENOPER_OPFAIL;
    403                         }
    404                         linkNodeChangeRandom(n1, neuronid, neulist);
    405                 }
    406                         break;
     402                        switch (roulette(probmodneu, F4_MODNEU_COUNT))
     403                        {
     404                        case F4_MODNEU_CONN:
     405                        {
     406                                f4_Node *neuron = node_mutated; //we start in '[' node and follow up parents until we find the neuron with these connections
     407                                while (neuron != NULL && neuron->neuclass == NULL) neuron = neuron->parent;
     408                                if (neuron == NULL)
     409                                        return GENOPER_OPFAIL; //did not find a neuron on the way up tree
     410
     411
     412                                int neuron_index, other_neuron_index;
     413                                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
     414                                if (!ok)
     415                                        return GENOPER_OPFAIL;
     416
     417                                connectionNodeChangeRandom(node_mutated, neuron_index, other_neuron_index);
     418                                break;
     419                        }
     420                        case F4_MODNEU_WEIGHT:
     421                                node_mutated->conn_weight = GenoOperators::getMutatedNeuronConnectionWeight(node_mutated->conn_weight);
     422                                break;
     423                        }
     424                }
     425                break;
    407426
    408427                case '#':
    409428                {
    410                         repeatNodeChangeRandom(n1);
    411                 }
    412                         break;
    413                 }
    414         }
     429                        repeatNodeChangeRandom(node_mutated);
     430                }
    415431                break;
     432                }
     433        }
     434        break;
    416435
    417436        default: //no mutations allowed?
    418437                return GENOPER_OPFAIL;
    419438        }
    420 
    421439        return GENOPER_OK;
    422440}
    423441
    424 // make a random [ node
    425 void Geno_f4::linkNodeMakeRandom(f4_node *nn, int neuid, vector<NeuroClass*> neulist) const
    426 {
    427         float prob1;
    428         NeuroClass *nc = NULL;
    429 
    430         // 35% chance one of *GTS
    431         prob1 = rndDouble(1);
    432         prob1 -= 0.35f;
    433         if (prob1 < 0)
    434         {
    435                 // '*', 'G', 'T', or 'S', 1/4 chance each
    436                 nc = GenoOperators::getRandomNeuroClassWithOutputAndNoInputs(Model::SHAPETYPE_BALL_AND_STICK);
    437         }
    438         if (nc != NULL)
    439         {
    440                 nn->i1 = 1;
    441                 nn->s1 = nc->getName().c_str();
    442                 nn->l1 = 0;
    443         }
    444         else
    445         {
    446                 // relative input link
    447                 int id = GenoOperators::getRandomNeuroClassWithOutput(neulist);
    448                 int relid = neuid - id;
    449                 nn->l1 = relid;
    450                 //nn->l1 = (int)(4.0f * (rndDouble(1) - 0.5f));
    451         }
    452         // weight
    453         nn->f1 = GenoOperators::getMutatedNeuronConnectionWeight(nn->f1);
    454         //nn->f1 = 10.0f * (rndDouble(1) - 0.5f);
     442// find all neurons and the needle
     443vector<NeuroClass*> Geno_f4::findAllNeuronsAndNode(f4_Node * const & g, f4_Node* const &needle_neuron, int &found_index)
     444{
     445        found_index = -1; // not found (for example, needle_neuron is not a neuroclass node or not added to the "g" tree)
     446        vector<NeuroClass*> neulist;
     447        for (int i = 0; i < g->count(); i++)
     448        {
     449                f4_Node *node = g->ordNode(i);
     450                if (node->neuclass != NULL)
     451                {
     452                        neulist.push_back(node->neuclass);
     453                        if (node == needle_neuron)
     454                                found_index = int(neulist.size()) - 1;
     455                }
     456        }
     457        return neulist;
     458}
     459
     460bool Geno_f4::findConnectionNeuronIndexes(f4_Node * const &g, f4_Node *neuron, bool other_has_output, int &neuron_index, int &other_neuron_index)
     461{
     462        vector<NeuroClass*> neulist = findAllNeuronsAndNode(g, neuron, neuron_index);
     463        if (neuron_index == -1)
     464                return false;
     465
     466        other_neuron_index = other_has_output ?
     467                GenoOperators::getRandomNeuroClassWithOutput(neulist) //find an existing neuron that provides an output
     468                :
     469                GenoOperators::getRandomNeuroClassWithInput(neulist); //find an existing neuron that accepts input(s)
     470        return other_neuron_index >= 0;
    455471}
    456472
    457473// change a [ node
    458 void Geno_f4::linkNodeChangeRandom(f4_node *nn, int neuid, std::vector<NeuroClass*> neulist) const      //rewritten by M.K. - should work as before (not tested)
    459 {
    460         double probs[3] = { 0.1, 0.3, 0.6 };
    461         NeuroClass *cl;
    462         // 10% change type
    463         // 30% change link
    464         // 60% change weight
    465 
    466         switch (roulette(probs, 3))
    467         {
    468         case 0: // change type
    469                 // 80% for link, 20% for random sensor
    470                 if (rndDouble(1) < 0.2f)
    471                 {
    472                         cl = GenoOperators::getRandomNeuroClassWithOutputAndNoInputs(Model::SHAPETYPE_BALL_AND_STICK);
    473                         if (cl != NULL)
    474                         {
    475                                 nn->i1 = 1;
    476                                 nn->s1 = cl->name.c_str();
    477                                 nn->l1 = 0;
    478                         }
    479                 }
    480                 break;
    481         case 1: // change link
    482                 if (0 == nn->i1) // relative input link
    483                 {
    484                         int id = GenoOperators::getRandomNeuroClassWithOutput(neulist);
    485                         nn->l1 = neuid - id;
    486                 }
    487                 //nn->l1 += (int)(2.0f * (rndDouble(1) - 0.5f));
    488                 break;
    489         case 2: // change weight
    490                 nn->f1 = GenoOperators::getMutatedNeuronConnectionWeight(nn->f1);
    491                 //nn->f1 += 1.0f * (rndDouble(1) - 0.5f);
    492                 break;
    493         }
    494 }
     474void Geno_f4::connectionNodeChangeRandom(f4_Node *nn, int nn_index, int other_index) const
     475{
     476        // relative input connection to some existing neuron
     477        nn->conn_from = nn_index - other_index;
     478        //nn->conn_from = (int)(4.0f * (rndDouble(1) - 0.5f)); //in very old times - did not care about neuron input/output preferences
     479
     480        nn->conn_weight = GenoOperators::getMutatedNeuronConnectionWeight(nn->conn_weight);
     481}
     482
    495483
    496484// make a random : node
    497 void Geno_f4::nparNodeMakeRandom(f4_node *nn) const
    498 {
    499         int sign = (int)rndDouble(2);
    500         int param = (int)rndDouble(3);
    501         if (param > 2) param = 2;
    502         nn->l1 = sign;
    503         nn->i1 = "!=/"[param];
     485void Geno_f4::nparNodeMakeRandom(f4_Node *nn) const
     486{
     487        unsigned int prop = rndUint(3); //random neuron property
     488        nn->prop_symbol = "!=/"[prop];
     489        nn->prop_increase = rndUint(2) == 1;
    504490}
    505491
    506492// change a repeat # node
    507 void Geno_f4::repeatNodeChangeRandom(f4_node *nn) const
    508 {
    509         int count;
    510         float prob1;
    511 
    512         // change count
    513         count = nn->i1;
    514         prob1 = rndDouble(1);
    515         if (prob1 < 0.5f) count++;
    516         else count--;
    517         if (count < 1) count = 1;
    518         if (count > REP_MAXCOUNT) count = REP_MAXCOUNT;
    519         nn->i1 = count;
    520 }
    521 
    522 
    523 int Geno_f4::MutateOneValid(f4_node *& g, int &method) const
     493void Geno_f4::repeatNodeChangeRandom(f4_Node *nn) const
     494{
     495        if (rndDouble(1) < 0.5f) nn->reps++; else nn->reps--; // change count
     496        if (nn->reps < 1) nn->reps = 1;
     497        if (nn->reps > mut_max_rep) nn->reps = mut_max_rep;
     498}
     499
     500
     501int Geno_f4::MutateOneValid(f4_Node *& g, int &method) const
    524502// mutate one, until a valid genotype is obtained
    525503{
    526504        // ! the genotype is g->child (not g) !
    527505        int i, res;
    528         f4_node *gcopy = NULL;
    529         // try this max 20 times:
    530         //   copy, mutate, then validate
    531 
    532         for (i = 0; i < 20; i++)
     506        f4_Node *gcopy = NULL;
     507        const int TRY_MUTATE = 20;
     508        // try this at most TRY_MUTATE times: copy, mutate, then validate
     509        for (i = 0; i < TRY_MUTATE; i++)
    533510        {
    534511                gcopy = g->duplicate();
     
    569546int Geno_f4::mutate(char *& g, float & chg, int &method)
    570547{
    571         f4_node *root = new f4_node;
     548        f4_Node *root = new f4_Node;
    572549        if (f4_processrec(g, 0, root) || root->childCount() != 1)
    573550        {
     
    600577
    601578// convert to tree
    602 f4_node *root;
    603 root = new f4_node();
     579f4_Node *root;
     580root = new f4_Node();
    604581res = f4_processrec(g, 0, root);
    605582if (res) {
     
    654631
    655632
    656 int Geno_f4::CrossOverOne(f4_node *g1, f4_node *g2, float chg) const
     633int Geno_f4::CrossOverOne(f4_Node *g1, f4_Node *g2, float chg) const
    657634{
    658635        // ! the genotypes are g1->child and g2->child (not g1 g2) !
     
    660637        int smin, smax;
    661638        float size;
    662         f4_node *n1, *n2, *n1p, *n2p;
     639        f4_Node *n1, *n2, *n1p, *n2p;
    663640
    664641        // determine desired size
    665642        size = (1 - chg) * (float)g1->count();
    666         smin = (int)(size*0.9f - 1);
    667         smax = (int)(size*1.1f + 1);
     643        smin = (int)(size * 0.9f - 1);
     644        smax = (int)(size * 1.1f + 1);
    668645        // get a random node with desired size
    669646        n1 = g1->child->randomNodeWithSize(smin, smax);
     
    671648        // determine desired size
    672649        size = (1 - chg) * (float)g2->count();
    673         smin = (int)(size*0.9f - 1);
    674         smax = (int)(size*1.1f + 1);
     650        smin = (int)(size * 0.9f - 1);
     651        smax = (int)(size * 1.1f + 1);
    675652        // get a random node with desired size
    676653        n2 = g2->child->randomNodeWithSize(smin, smax);
     
    691668int Geno_f4::crossOver(char *&g1, char *&g2, float &chg1, float &chg2)
    692669{
    693         f4_node root1, root2, *copy1, *copy2;
     670        f4_Node root1, root2, *copy1, *copy2;
    694671
    695672        // convert genotype strings into tree structures
     
    720697        // style categories
    721698#define STYL4CAT_MODIFIC F14_MODIFIERS ","
    722 #define STYL4CAT_NEUMOD "[]:+-/!="
     699#define STYL4CAT_NEUMOD "/!="
    723700#define STYL4CAT_NEUSPECIAL "|@*"
    724 #define STYL4CAT_DIGIT "0123456789."
    725 #define STYL4CAT_REST "XN<># "
     701#define STYL4CAT_DIGIT "+-0123456789.[]" //'+' is only for adjusting old-style properties "/!="
     702#define STYL4CAT_REST ":XN<># "
    726703
    727704        if (!isalpha(ch) && !strchr(STYL4CAT_MODIFIC STYL4CAT_NEUMOD STYL4CAT_NEUSPECIAL STYL4CAT_DIGIT STYL4CAT_REST "\t", ch))
     
    730707        }
    731708        uint32_t style = GENSTYLE_CS(0, GENSTYLE_STRIKEOUT); //default, should be changed below
    732         if (strchr("X ", ch))                    style = GENSTYLE_CS(0, GENSTYLE_NONE);
    733         else if (strchr("N", ch))                style = GENSTYLE_RGBS(0, 200, 0, GENSTYLE_NONE);
     709        if (strchr("X", ch))                     style = GENSTYLE_CS(0, GENSTYLE_BOLD);
     710        else if (strchr(":", ch))                style = GENSTYLE_CS(0, GENSTYLE_NONE);
     711        else if (strchr("#", ch))                style = GENSTYLE_RGBS(220, 0, 0, GENSTYLE_BOLD);
     712        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
     713        else if (strchr("N@|*", ch))             style = GENSTYLE_RGBS(150, 0, 150, GENSTYLE_BOLD); //neuroclass
    734714        else if (strchr("<", ch))                style = GENSTYLE_RGBS(0, 0, 200, GENSTYLE_BOLD);
    735715        else if (strchr(">", ch))                style = GENSTYLE_RGBS(0, 0, 100, GENSTYLE_NONE);
    736         else if (strchr(STYL4CAT_DIGIT, ch))     style = GENSTYLE_RGBS(100, 100, 100, GENSTYLE_NONE);
     716        else if (strchr(STYL4CAT_DIGIT, ch))     style = GENSTYLE_CS(GENCOLOR_NUMBER, GENSTYLE_NONE);
    737717        else if (strchr(STYL4CAT_MODIFIC, ch))   style = GENSTYLE_RGBS(100, 100, 100, GENSTYLE_NONE);
    738718        else if (strchr(STYL4CAT_NEUMOD, ch))    style = GENSTYLE_RGBS(0, 150, 0, GENSTYLE_NONE);
    739         if (isalpha(ch) || strchr(STYL4CAT_NEUSPECIAL, ch))
    740         {
     719        if (isalpha(ch))
     720        {
     721                // allowed neuron formats:
     722                //   N:CLASSNAME
     723                //   N:@
     724                //   N:|
     725                // old syntax still supported in coloring, but no longer valid:
     726                //   [SENSOR, WEIGHT]
     727                //   N@
     728                //   N|
     729                // ...so must have N: or [ before neuroclass name (or just N, but this is handled above - for N@|* only)
     730
    741731                while (pos > 0)
    742732                {
    743733                        pos--;
    744                         if (!(isalpha(g[pos]) || strchr(STYL4CAT_NEUSPECIAL, ch)))
    745                         {
    746                                 if (isupper(g[pos + 1]) && (g[pos] == ':' || g[pos] == '[')) // name of neuron class
    747                                         style = GENSTYLE_RGBS(150, 0, 150, GENSTYLE_ITALIC);
    748                                 else // property
    749                                         style = GENSTYLE_RGBS(100, 100, 100, GENSTYLE_NONE);
     734                        if (!isalpha(g[pos]))
     735                        {
     736                                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 ":"
     737                                        style = GENSTYLE_RGBS(150, 0, 150, GENSTYLE_BOLD); // neuroclass
     738                                //(...) else (...)
     739                                //      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 +! -! += -= +/ -/
     740                                break;
    750741                        }
    751742                }
Note: See TracChangeset for help on using the changeset viewer.