Changeset 1227 for cpp/frams/genetics/f4/f4_oper.cpp
- Timestamp:
- 04/27/23 04:04:06 (2 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
cpp/frams/genetics/f4/f4_oper.cpp
r1108 r1227 1 1 // This file is a part of Framsticks SDK. http://www.framsticks.com/ 2 // Copyright (C) 1999-202 0Maciej Komosinski and Szymon Ulatowski.2 // Copyright (C) 1999-2023 Maciej Komosinski and Szymon Ulatowski. 3 3 // See LICENSE.txt for details. 4 4 … … 6 6 // Copyright (C) since 2001 Maciej Komosinski 7 7 // 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 8 22 9 23 #include "f4_oper.h" … … 20 34 21 35 // 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 "<[#" 24 37 25 38 #define FIELDSTRUCT Geno_f4 … … 27 40 static ParamEntry geno_f4_paramtab[] = 28 41 { 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", }, 38 57 { "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 ")", }, 39 58 { 0, }, … … 59 78 mutation_method_names[index++] = "deleted a node"; 60 79 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"); 62 81 } 63 82 … … 67 86 } 68 87 69 int Geno_f4::ValidateRec(f4_ node *geno, int retrycount) const88 int Geno_f4::ValidateRec(f4_Node *geno, int retrycount) const 70 89 { 71 90 // ! the genotype is geno->child (not geno) ! … … 76 95 77 96 // errors not fixed: 78 if ( GENOPER_OPFAIL == cells.geterror())79 { 80 if (cells.get errorpos() >= 0) return 1 + cells.geterrorpos();97 if (cells.getErrorCode() == GENOPER_OPFAIL) 98 { 99 if (cells.getErrorPos() >= 0) return 1 + cells.getErrorPos(); 81 100 return GENOPER_OPFAIL; 82 101 } 83 102 // errors can be fixed 84 if ( GENOPER_REPAIR == cells.geterror())103 if (cells.getErrorCode() == GENOPER_REPAIR) 85 104 { 86 105 cells.repairGeno(geno, 1); … … 102 121 { 103 122 // convert geno to tree, then try to validate 20 times 104 f4_ node root;123 f4_Node root; 105 124 if (f4_processrec(geno, 0, &root) || root.childCount() != 1) return GENOPER_OK; // cannot repair 106 125 if (ValidateRec(&root, 20) == GENOPER_REPAIR) // if repaired, make it back to string … … 115 134 int Geno_f4::checkValidity(const char* geno, const char *genoname) 116 135 { 117 f4_ node root;136 f4_Node root; 118 137 int res = f4_processrec(geno, 0, &root); 119 138 if (res) return res; // errorpos, >0 … … 121 140 f4_Cells cells(root.child, 0); 122 141 cells.simulate(); 123 if (cells.get error() == GENOPER_OPFAIL || cells.geterror() == GENOPER_REPAIR)124 { 125 if (cells.get errorpos() >= 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(); 126 145 else return 1; //earlier: GENOPER_OPFAIL; 127 146 } … … 130 149 131 150 132 int Geno_f4::MutateOne(f4_ node *& g, int &method) const151 int Geno_f4::MutateOne(f4_Node *& g, int &method) const 133 152 { 134 153 // ! the genotype is g->child (not g) ! … … 136 155 // do the mutation 137 156 // 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); ) 142 159 143 160 switch (roulette(prob, F4_COUNT)) … … 151 168 { 152 169 // add division ('<') 153 f4_ node *n3 = n1->parent;154 n 3->removeChild(n1);155 f4_ node *n2 = new f4_node('<', n3, n3->pos);156 n 2->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); 157 174 // new cell is stick or neuron 158 175 // "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); 163 180 else 164 181 { 165 182 // make neuron 166 183 NeuroClass *rndclass = GenoOperators::getRandomNeuroClass(Model::SHAPETYPE_BALL_AND_STICK); 167 if (rndclass == NULL) 184 if (rndclass == NULL) //no active neurons? 168 185 { 169 n 5 = new f4_node('X', n2, n2->pos);186 node_new = new f4_Node('X', node_new_div, node_new_div->pos); 170 187 } 171 188 else 172 189 { 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 175 194 { 176 neuronid = -1; 177 for (int i = 0; i < g->count(); i++) 195 if (rndclass->getPreferredInputs() != 0) //neuron also wants connections? 178 196 { 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 183 200 { 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); 189 203 } 190 204 } 191 if (neuronid == -1)205 else if (rndclass->getPreferredOutput() > 0) //neuron also wants connections? 192 206 { 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, ..., ...); 194 212 } 195 n5 = new f4_node('[', n4, n2->pos);196 linkNodeMakeRandom(n5, neuronid, neulist);197 }198 else {199 n5 = n4;200 213 } 201 214 } 202 215 } 203 new f4_ node('>', n5, n5->pos);204 n 1->parent = n2;205 // now with 50% chance swap children216 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 206 219 if (rndUint(2) == 0) 207 220 { 208 n 3 = n2->child;209 n 2->child = n2->child2;210 n 2->child2 = n3;211 } 212 } 213 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; 214 227 case F4_ADD_CONN: 215 228 { 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; 249 264 case F4_ADD_NEUPAR: 250 265 { 251 266 // add neuron modifier 252 n 1->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); 254 269 nparNodeMakeRandom(n2); 255 n2->addChild(n 1);256 n 1->parent = n2;257 } 258 270 n2->addChild(node_mutated); 271 node_mutated->parent = n2; 272 } 273 break; 259 274 case F4_ADD_REP: 260 275 { 261 276 // add repetition ('#') 262 // repeated code (left child) is the original, right child is empty, count is 2263 f4_ node *n3 = n1->parent;264 n3->removeChild(n 1);265 f4_ node *n2 = new f4_node('#', n3, n3->pos);266 n2-> i1= 2;267 n2->addChild(n 1);268 new f4_ node('>', n2, n2->pos);269 n 1->parent = n2;270 } 271 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; 272 287 case F4_ADD_SIMP: 273 288 { 274 289 // add simple node 275 290 // choose a simple node from ADD_SIMPLE_CODES 276 n 1->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); 278 293 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 } 286 298 break; 299 } 300 } 301 break; 287 302 288 303 case F4_DEL: … … 294 309 for (int i = 0; i < 10; i++) 295 310 { 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) 298 313 break; 299 314 // try a new one 300 n 1= g->child->randomNode();301 } 302 if (( NULL != n1->parent) && (g != n1->parent))303 { 304 switch (n 1->childCount())315 node_mutated = g->child->randomNode(); 316 } 317 if ((node_mutated->parent != NULL) && (g != node_mutated->parent)) 318 { 319 switch (node_mutated->childCount()) 305 320 { 306 321 case 0: break; 307 322 case 1: // one child 308 323 { 309 f4_ node *n2 = n1->parent;310 n 2->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) 312 327 { 313 n 1->child->parent = n2;314 n 2->addChild(n1->child);315 n 1->child = NULL;328 node_mutated->child->parent = node_mutated_parent; 329 node_mutated_parent->addChild(node_mutated->child); 330 node_mutated->child = NULL; 316 331 } 317 if ( NULL != n1->child2)332 if (node_mutated->child2 != NULL) 318 333 { 319 n 1->child2->parent = n2;320 n 2->addChild(n1->child2);321 n 1->child2 = NULL;334 node_mutated->child2->parent = node_mutated_parent; 335 node_mutated_parent->addChild(node_mutated->child2); 336 node_mutated->child2 = NULL; 322 337 } 323 338 // destroy n1 324 n 1->parent = NULL;325 delete n 1;326 } 327 339 node_mutated->parent = NULL; 340 delete node_mutated; 341 } 342 break; 328 343 329 344 case 2: // two children 330 345 { 331 346 // two children 332 f4_ node *n2 = n1->parent;333 n2->removeChild(n 1);347 f4_Node *n2 = node_mutated->parent; 348 n2->removeChild(node_mutated); 334 349 // n1 has two children. pick one randomly 50-50, destroy other 335 350 if (rndUint(2) == 0) 336 351 { 337 n 1->child->parent = n2;338 n2->addChild(n 1->child);339 n 1->child = NULL;340 n 1->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; 341 356 } 342 357 else 343 358 { 344 n 1->child2->parent = n2;345 n2->addChild(n 1->child2);346 n 1->child2 = NULL;347 n 1->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; 348 363 } 349 364 // destroy n1 350 n 1->parent = NULL;351 delete n 1;352 } 353 365 node_mutated->parent = NULL; 366 delete node_mutated; 367 } 368 break; 354 369 } 355 370 } 356 371 else return GENOPER_OPFAIL; 357 372 } 358 373 break; 359 374 case F4_MOD: 360 375 { 361 376 method = F4_ADD_COUNT - 1 + F4_MOD; 362 377 // change a node 363 // the only nodes that are modifiable are MUT_CHAN_CODES378 // the only nodes that are modifiable are F4_MUT_CHANGE_CODES 364 379 // try to get a modifiable node 365 380 // already picked a node, but repeat may be needed … … 367 382 while (1) 368 383 { 369 if (strchr( MUT_CHAN_CODES, n1->name[0])) break;384 if (strchr(F4_MUT_CHANGE_CODES, node_mutated->name[0])) break; 370 385 // try a new one 371 n 1= g->child->randomNode();386 node_mutated = g->child->randomNode(); 372 387 i++; 373 388 if (i >= 20) return GENOPER_OPFAIL; 374 389 } 375 switch (n 1->name[0])390 switch (node_mutated->name[0]) 376 391 { 377 392 case '<': 378 393 { 379 394 // 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; 383 400 case '[': 384 401 { 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; 407 426 408 427 case '#': 409 428 { 410 repeatNodeChangeRandom(n1); 411 } 412 break; 413 } 414 } 429 repeatNodeChangeRandom(node_mutated); 430 } 415 431 break; 432 } 433 } 434 break; 416 435 417 436 default: //no mutations allowed? 418 437 return GENOPER_OPFAIL; 419 438 } 420 421 439 return GENOPER_OK; 422 440 } 423 441 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 443 vector<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 460 bool 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; 455 471 } 456 472 457 473 // 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 } 474 void 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 495 483 496 484 // 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]; 485 void 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; 504 490 } 505 491 506 492 // 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 493 void 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 501 int Geno_f4::MutateOneValid(f4_Node *& g, int &method) const 524 502 // mutate one, until a valid genotype is obtained 525 503 { 526 504 // ! the genotype is g->child (not g) ! 527 505 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++) 533 510 { 534 511 gcopy = g->duplicate(); … … 569 546 int Geno_f4::mutate(char *& g, float & chg, int &method) 570 547 { 571 f4_ node *root = new f4_node;548 f4_Node *root = new f4_Node; 572 549 if (f4_processrec(g, 0, root) || root->childCount() != 1) 573 550 { … … 600 577 601 578 // convert to tree 602 f4_ node *root;603 root = new f4_ node();579 f4_Node *root; 580 root = new f4_Node(); 604 581 res = f4_processrec(g, 0, root); 605 582 if (res) { … … 654 631 655 632 656 int Geno_f4::CrossOverOne(f4_ node *g1, f4_node *g2, float chg) const633 int Geno_f4::CrossOverOne(f4_Node *g1, f4_Node *g2, float chg) const 657 634 { 658 635 // ! the genotypes are g1->child and g2->child (not g1 g2) ! … … 660 637 int smin, smax; 661 638 float size; 662 f4_ node *n1, *n2, *n1p, *n2p;639 f4_Node *n1, *n2, *n1p, *n2p; 663 640 664 641 // determine desired size 665 642 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); 668 645 // get a random node with desired size 669 646 n1 = g1->child->randomNodeWithSize(smin, smax); … … 671 648 // determine desired size 672 649 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); 675 652 // get a random node with desired size 676 653 n2 = g2->child->randomNodeWithSize(smin, smax); … … 691 668 int Geno_f4::crossOver(char *&g1, char *&g2, float &chg1, float &chg2) 692 669 { 693 f4_ node root1, root2, *copy1, *copy2;670 f4_Node root1, root2, *copy1, *copy2; 694 671 695 672 // convert genotype strings into tree structures … … 720 697 // style categories 721 698 #define STYL4CAT_MODIFIC F14_MODIFIERS "," 722 #define STYL4CAT_NEUMOD " []:+-/!="699 #define STYL4CAT_NEUMOD "/!=" 723 700 #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<># " 726 703 727 704 if (!isalpha(ch) && !strchr(STYL4CAT_MODIFIC STYL4CAT_NEUMOD STYL4CAT_NEUSPECIAL STYL4CAT_DIGIT STYL4CAT_REST "\t", ch)) … … 730 707 } 731 708 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 734 714 else if (strchr("<", ch)) style = GENSTYLE_RGBS(0, 0, 200, GENSTYLE_BOLD); 735 715 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); 737 717 else if (strchr(STYL4CAT_MODIFIC, ch)) style = GENSTYLE_RGBS(100, 100, 100, GENSTYLE_NONE); 738 718 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 741 731 while (pos > 0) 742 732 { 743 733 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; 750 741 } 751 742 }
Note: See TracChangeset
for help on using the changeset viewer.