Changeset 1227 for cpp/frams/genetics/f4
- Timestamp:
- 04/27/23 04:04:06 (19 months ago)
- Location:
- cpp/frams/genetics/f4
- Files:
-
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
cpp/frams/genetics/f4/f4_conv.cpp
r1212 r1227 90 90 if (cells) delete cells; 91 91 cells = new f4_Cells(geno, 0); 92 if (GENOPER_OK != cells->get error())93 { 94 error = cells->get error();95 errorpos = cells->get errorpos();92 if (GENOPER_OK != cells->getErrorCode()) 93 { 94 error = cells->getErrorCode(); 95 errorpos = cells->getErrorPos(); 96 96 //delete cells; 97 97 return error; … … 99 99 100 100 cells->simulate(); 101 if (GENOPER_OK != cells->get error())102 { 103 error = cells->get error();104 errorpos = cells->get errorpos();101 if (GENOPER_OK != cells->getErrorCode()) 102 { 103 error = cells->getErrorCode(); 104 errorpos = cells->getErrorPos(); 105 105 return error; 106 106 } 107 107 108 108 // reset recursive traverse flags 109 for (i = 0; i < cells-> nc; i++)109 for (i = 0; i < cells->cell_count; i++) 110 110 cells->C[i]->recProcessedFlag = 0; 111 111 … … 114 114 // process every cell 115 115 int res; 116 for (i = 0; i < cells-> nc; i++)116 for (i = 0; i < cells->cell_count; i++) 117 117 { 118 118 res = buildModelRec(cells->C[i]); 119 119 if (res) 120 120 { 121 logMessage("f4_Model", "build ModelRec", 2, "Error in buildingModel");121 logMessage("f4_Model", "buildFromF4", LOG_ERROR, "Error in building a Model"); 122 122 error = res; 123 123 break; … … 135 135 f4_Cell* f4_Model::getStick(f4_Cell *C) 136 136 { 137 if ( T_STICK4 == C->type) return C;137 if (C->type == CELL_STICK) return C; 138 138 if (NULL != C->dadlink) 139 139 return getStick(C->dadlink); 140 140 // we have no more dadlinks, find any stick 141 for (int i = 0; i < cells-> nc; i++)142 if (cells->C[i]->type == T_STICK4)141 for (int i = 0; i < cells->cell_count; i++) 142 if (cells->C[i]->type == CELL_STICK) 143 143 return cells->C[i]; 144 144 // none! 145 logMessage("f4_Model", "getStick", 2, "Not a single stick");145 logMessage("f4_Model", "getStick", LOG_ERROR, "Not a single stick"); 146 146 return NULL; 147 147 } 148 148 149 149 150 /// updated by MacKo to follow new SDK standards (no more neuroitems)151 150 int f4_Model::buildModelRec(f4_Cell *C) 152 151 { … … 164 163 // make sure parent is a stick 165 164 if (NULL != C->dadlink) 166 if (C->dadlink->type != T_STICK4)167 { 168 C->dadlink = getStick(C->dadlink);165 if (C->dadlink->type != CELL_STICK) 166 { 167 C->dadlink = getStick(C->dadlink); 169 168 } 170 169 … … 179 178 180 179 range = C->genoRange; 181 if (C->type == T_STICK4)180 if (C->type == CELL_STICK) 182 181 { 183 182 int jj_p1_refno; // save for later … … 190 189 /*1.0/C->P.mass,*/ C->P.friction, C->P.ingestion, C->P.assimilation 191 190 //C->firstend.x, C->firstend.y, C->firstend.z 192 191 ); 193 192 partidx = addFromString(PartType, tmpLine, &range); 194 193 if (partidx < 0) return -1; … … 207 206 //C->lastend.x, C->lastend.y, C->lastend.z 208 207 /*"vol=" 1.0/C->P.mass,*/ C->P.friction, C->P.ingestion, C->P.assimilation 209 208 ); 210 209 partidx = addFromString(PartType, tmpLine, &range); 211 210 if (partidx < 0) return -2; … … 227 226 //C->P.ruch, // rotstif 228 227 C->P.stamina 229 228 ); 230 229 partidx = addFromString(JointType, tmpLine, &range); 231 230 if (partidx < 0) return -13; … … 234 233 } 235 234 236 if (C->type == T_NEURON4) ///<this case was updated by MacKo235 if (C->type == CELL_NEURON) 237 236 { 238 237 const char* nclass = C->neuclass->name.c_str(); … … 259 258 partno = C->dadlink->p2_refno; 260 259 if ((partno < 0) || (partno >= getPartCount())) return -21; 260 261 261 if (strcmp(nclass, "N") == 0) 262 {263 262 sprintf(tmpLine, "p=%d,d=\"N:in=%g,fo=%g,si=%g\"", partno, C->inertia, C->force, C->sigmo); 264 }265 263 else 266 {267 264 sprintf(tmpLine, "p=%d,d=\"%s\"", partno, nclass); 268 } 265 269 266 partidx = addFromString(NeuronType, tmpLine, &range); 270 267 if (partidx < 0) return -22; … … 275 272 { 276 273 jointno = C->dadlink->joint_refno; 277 sprintf(tmpLine, "j=%d,d=\"%s\"", jointno, nclass); 274 275 if (strcmp(nclass, "@") == 0) 276 sprintf(tmpLine, "j=%d,d=\"@:p=%g\"", jointno, C->P.muscle_power); 277 else if (strcmp(nclass, "|") == 0) 278 sprintf(tmpLine, "j=%d,d=\"|:p=%g,r=%g\"", jointno, C->P.muscle_power, C->mz); 279 else 280 sprintf(tmpLine, "j=%d,d=\"%s\"", jointno, nclass); 281 278 282 partidx = addFromString(NeuronType, tmpLine, &range); 279 283 if (partidx < 0) return -32; … … 283 287 int n_refno = C->neuro_refno; 284 288 285 if ((strcmp(nclass,"N") == 0) && C->ctrl) 286 { 287 if (1 == C->ctrl) 288 sprintf(tmpLine, "j=%d,d=\"@:p=%g\"", C->dadlink->joint_refno, C->P.muscle_power); 289 else 290 sprintf(tmpLine, "j=%d,d=\"|:p=%g,r=%g\"", C->dadlink->joint_refno, C->P.muscle_power, C->mz); 291 partidx = addFromString(NeuronType, tmpLine, &range); 292 if (partidx < 0) return -32; 293 sprintf(tmpLine, "%d,%d", partidx, n_refno); 294 if (addFromString(NeuronConnectionType, tmpLine, &range) < 0) return -33; 295 this->checkpoint(); 296 } 297 298 for (j = 0; j < C->nolink; j++) 299 { 300 if (NULL != C->links[j]->from) 301 buildModelRec(C->links[j]->from); 289 for (j = 0; j < C->conns_count; j++) 290 { 291 if (C->conns[j]->from != NULL) 292 buildModelRec(C->conns[j]->from); 302 293 303 294 tmpLine[0] = 0; 304 if (C-> links[j]->from == NULL)295 if (C->conns[j]->from == NULL) 305 296 { 306 const char* nclass = C->links[j]->t.c_str(); 307 char* temp = (char*)C->links[j]->t.c_str(); 308 NeuroClass *sensortest = GenoOperators::parseNeuroClass(temp); 309 //backward compatibility for G*TS 310 if (C->links[j]->t == "*" || C->links[j]->t == "S" || C->links[j]->t == "T") 311 { 312 partno = C->dadlink->p2_refno; 313 sprintf(tmpLine, "p=%d,d=\"%s\"", partno, nclass); 314 } 315 else if (C->links[j]->t == "G") 316 { 317 jointno = C->dadlink->joint_refno; 318 sprintf(tmpLine, "j=%d,d=\"%s\"", jointno, nclass); 319 } 320 else if (sensortest->getPreferredLocation() == 0) 321 { 322 sprintf(tmpLine, "d=\"%s\"",nclass); 323 } 324 else if (sensortest->getPreferredLocation() == 1) 325 { 326 partno = C->dadlink->p2_refno; 327 sprintf(tmpLine, "p=%d,d=\"%s\"", partno, nclass); 328 } 329 else 330 { 331 jointno = C->dadlink->joint_refno; 332 sprintf(tmpLine, "j=%d,d=\"%s\"", jointno, nclass); 333 } 334 297 logMessage("f4_Model", "buildModelRec", LOG_ERROR, "Old code for sensors as inputs embedded in [connection]: C->conns[j]->from == NULL"); 335 298 } 336 299 int from = -1; 337 if (tmpLine[0]) //input from receptor 338 { 339 from = addFromString(NeuronType, tmpLine, &range); 340 if (from < 0) return -34; 341 this->checkpoint(); 342 } /*could be 'else'...*/ 343 344 if (NULL != C->links[j]->from) // input from another neuron 345 from = C->links[j]->from->neuro_refno; 300 if (C->conns[j]->from != NULL) // input from another neuron 301 from = C->conns[j]->from->neuro_refno; 346 302 if (from >= 0) 347 303 { 348 sprintf(tmpLine, "%d,%d,%g", n_refno, from, C-> links[j]->w);304 sprintf(tmpLine, "%d,%d,%g", n_refno, from, C->conns[j]->weight); 349 305 if (addFromString(NeuronConnectionType, tmpLine, &range) < 0) return -35; 350 306 this->checkpoint(); -
cpp/frams/genetics/f4/f4_general.cpp
r1213 r1227 17 17 #endif 18 18 19 20 #define BREAK_WHEN_REP_COUNTER_NULL //see comments where it is used 21 #define EXTRA_STEP_CELL_DEVELOPMENT //see comments where it is used 22 #define TREAT_BAD_CONNECTIONS_AS_INVALID_GENO //see comments where it is used 23 24 19 25 void rolling_dec(double *v) 20 26 { … … 27 33 } 28 34 29 int scanrec(const char* s, unsigned int slen, char stopchar)30 { 31 unsigned int i = 0;35 int scanrec(const char* s, size_t slen, char stopchar) 36 { 37 size_t i = 0; 32 38 //DB( printf(" scan('%s', '%c')\n", s, stopchar); ) 33 39 while (1) … … 36 42 return 1; 37 43 if (stopchar == s[i]) // bumped into stopchar 38 return i ;44 return int(i); 39 45 if (i < slen - 1) // s[i] is not the last char 40 46 { … … 58 64 i++; 59 65 } 60 return i; 61 } 62 63 64 f4_Cell::f4_Cell(int nname, 65 f4_Cell *ndad, int nangle, GeneProps newP) 66 { 67 name = nname; 68 type = T_UNDIFF4; 66 return int(i); 67 } 68 69 70 f4_Cell::f4_Cell(int nnr, f4_Cell *ndad, int nangle, GeneProps newP) 71 { 72 nr = nnr; 73 type = CELL_UNDIFF; 69 74 dadlink = ndad; 70 75 org = NULL; 71 76 genot = NULL; 72 77 gcur = NULL; 73 active = 1;78 active = true; 74 79 repeat.clear(); 75 80 //genoRange.clear(); -- implicit … … 83 88 zrot = 0; 84 89 //OM = Orient_1; 85 ctrl = 0;86 90 inertia = 0.8; 87 91 force = 0.04; 88 92 sigmo = 2; 89 nolink= 0;93 conns_count = 0; 90 94 91 95 // adjust firstend and OM if there is a stick dad … … 93 97 { 94 98 // make sure it is a stick (and not a stick f4_Cell!) 95 if ( T_STICK4 == ndad->type)99 if (ndad->type == CELL_STICK) 96 100 { 97 101 //firstend = ndad->lastend; … … 99 103 ndad->childcount++; 100 104 } 101 if ( T_NEURON4 == ndad->type)105 if (ndad->type == CELL_NEURON) 102 106 { 103 107 inertia = ndad->inertia; … … 112 116 113 117 114 f4_Cell::f4_Cell(f4_Cells *nO, int nn ame, f4_node *ngeno, f4_node *ngcur, f4_Cell *ndad, int nangle, GeneProps newP)115 { 116 n ame = nname;117 type = T_UNDIFF4;118 f4_Cell::f4_Cell(f4_Cells *nO, int nnr, f4_Node *ngeno, f4_Node *ngcur, f4_Cell *ndad, int nangle, GeneProps newP) 119 { 120 nr = nnr; 121 type = CELL_UNDIFF; 118 122 dadlink = ndad; 119 123 org = nO; 120 124 genot = ngeno; 121 125 gcur = ngcur; 122 active = 1;126 active = true; 123 127 repeat.clear(); 124 128 //genoRange.clear(); -- implicit … … 135 139 zrot = 0; 136 140 //OM = Orient_1; 137 ctrl = 0;138 141 inertia = 0.8; 139 142 force = 0.04; 140 143 sigmo = 2; 141 nolink= 0;144 conns_count = 0; 142 145 143 146 // adjust firstend and OM if there is a stick dad … … 145 148 { 146 149 // make sure it is a stick (and not a stick f4_Cell!) 147 if ( T_STICK4 == ndad->type)150 if (ndad->type == CELL_STICK) 148 151 { 149 152 //firstend = ndad->lastend; … … 151 154 ndad->childcount++; 152 155 } 153 if ( T_NEURON4 == ndad->type)156 if (ndad->type == CELL_NEURON) 154 157 { 155 158 inertia = ndad->inertia; … … 166 169 f4_Cell::~f4_Cell() 167 170 { 168 // remove links169 if ( nolink)171 // remove connections 172 if (conns_count) 170 173 { 171 174 int i; 172 for (i = nolink- 1; i >= 0; i--)173 delete links[i];174 nolink= 0;175 for (i = conns_count - 1; i >= 0; i--) 176 delete conns[i]; 177 conns_count = 0; 175 178 } 176 179 } … … 178 181 179 182 /* return codes: 180 >1 error at pos 181 0 halt development for a cycle 182 -1 development finished OK 183 1 error at pos 184 0 halt development (yield) for a cycle 183 185 */ 184 int f4_Cell::onestep() 185 { 186 if (gcur == NULL) 187 { 188 active = 0; 189 return 0; // stop 190 } 191 while (NULL != gcur) 186 int f4_Cell::oneStep() 187 { 188 while (gcur != NULL) 192 189 { 193 190 //DB( printf(" %d (%d) executing '%c' %d\n", name, type, gcur->name, gcur->pos); ) … … 195 192 // the current genotype code is processed 196 193 //genoRange.add(gcur->pos,gcur->pos+gcur->name.length()-1); 197 bool neuclasshandler = false; // if set to true, then there is a set of characters that can be assigned to a neuron class type 198 // old semantics, one-character 199 if (gcur->name.length() == 1) 194 bool neuclasshandler = false; // if set to true, then a separate neuron handler below will identify the neuroclass and assign the cell to the neuron type 195 196 // To detect what genes are valid neuroclass names, but do NOT have is_neuroclass==true 197 // (just as a curiosity to ensure we properly distinguish between, for example, the "G" neuron and "G" modifier): 198 //char *TMP = (char*)gcur->name.c_str(); 199 //if (gcur->is_neuroclass==false && GenoOperators::parseNeuroClass(TMP,ModelEnum::SHAPETYPE_BALL_AND_STICK)) 200 // printf("Could be a valid neuroclass, but is_neuroclass==false: %s\n", gcur->name.c_str()); 201 202 if (gcur->name.length() == 1 && gcur->neuclass == NULL) //one-character genes and not neuroclass names 200 203 { 201 204 genoRange.add(gcur->pos, gcur->pos); … … 209 212 210 213 // error: sticks cannot divide 211 if ( T_STICK4 == type)214 if (type == CELL_STICK) 212 215 { 213 216 // cannot fix … … 217 220 218 221 // undiff divides 219 if ( T_UNDIFF4 == type)222 if (type == CELL_UNDIFF) 220 223 { 221 224 // commacount is set only when daughter turns into X … … 224 227 GeneProps newP = P; 225 228 newP.propagateAlong(false); 226 f4_Cell *tmp = new f4_Cell(org, org-> nc, genot, gcur->child2, this, commacount, newP);229 f4_Cell *tmp = new f4_Cell(org, org->cell_count, genot, gcur->child2, this, commacount, newP); 227 230 tmp->repeat = repeat; 228 231 repeat.clear(); 229 232 org->addCell(tmp); 230 233 } 231 // a neuron divides: create a new, duplicate links 232 if (T_NEURON4 == type) { 234 // a neuron divides: create a new, duplicate connections 235 if (type == CELL_NEURON) 236 { 233 237 // daughter cell 234 f4_Cell *tmp = new f4_Cell(org, org-> nc, genot, gcur->child2,238 f4_Cell *tmp = new f4_Cell(org, org->cell_count, genot, gcur->child2, 235 239 // has the same dadlink 236 240 this->dadlink, commacount, P); … … 238 242 repeat.clear(); 239 243 // it is a neuron from start 240 tmp->type = T_NEURON4;244 tmp->type = CELL_NEURON; 241 245 // it has the same type as the parent neuron 242 246 tmp->neuclass = neuclass; 243 // duplicate links244 f4_Cell Link *ll;245 for (int i = 0; i < nolink; i++)247 // duplicate connections 248 f4_CellConn *conn; 249 for (int i = 0; i < conns_count; i++) 246 250 { 247 ll = links[i];248 tmp->add link(ll->from, ll->w, ll->t);251 conn = conns[i]; 252 tmp->addConnection(conn->from, conn->weight); 249 253 } 250 254 org->addCell(tmp); … … 258 262 { 259 263 // finish 260 // see if there is a repe t count264 // see if there is a repeat count 261 265 if (repeat.top > 0) 262 266 { // there is a repeat counter … … 280 284 { 281 285 repeat.pop(); 286 // MacKo 2023-04: originally, there was no "break" nor "return" here (hence [[fallthrough]]; needed below for modern compilers) - not sure if this was intentional or overlooking. 287 // This case can be tested with "#0" in the genotype. Anyway, there seems to be no difference in outcomes with and without "break". 288 // However, falling through [[fallthrough]] below for count==0 causes performing repeat.push(repeat_ptr(gcur, 0)) while the very reason 289 // we are here is that repeat count==0 (one of the conditions for isNull()), so I opted to add "break", but marked this tentative decision using #define. 290 // The ultimate informed decision would require understanding all the logic and testing all the edge cases. 291 #ifdef BREAK_WHEN_REP_COUNTER_NULL 292 break; 293 #endif 282 294 } 283 295 } … … 285 297 { 286 298 // error: still undiff 287 if ( T_UNDIFF4 == type)299 if (type == CELL_UNDIFF) 288 300 { 289 301 // fix it: insert an 'X' 290 f4_ node *insertnode = new f4_node("X", NULL, gcur->pos);302 f4_Node *insertnode = new f4_Node("X", NULL, gcur->pos); 291 303 if (org->setRepairInsert(gcur->pos, gcur, insertnode)) // not in repair mode, release 292 304 delete insertnode; … … 294 306 } 295 307 repeat.clear(); 296 active = 0; // stop308 active = false; // stop 297 309 // eat up rest 310 int remaining_nodes = gcur->count() - 1; 311 if (remaining_nodes > 0) 312 logPrintf("f4_Cell", "oneStep", LOG_WARN, "Ignoring junk genetic code: %d node(s) at position %d", remaining_nodes, gcur->child->pos); //let's see an example of such a genotype... 298 313 gcur = NULL; 299 314 return 0; 300 315 } 301 316 } 317 #ifndef BREAK_WHEN_REP_COUNTER_NULL 302 318 [[fallthrough]]; 319 #endif 303 320 case '#': 304 321 { … … 311 328 return 1; // stop 312 329 } 313 repeat.push(repeat_ptr(gcur, gcur-> i1));330 repeat.push(repeat_ptr(gcur, gcur->reps)); 314 331 gcur = gcur->child; 315 332 break; … … 324 341 { 325 342 // error: if neuron 326 if ( T_NEURON4 == type)343 if (type == CELL_NEURON) 327 344 { 328 345 // fix: delete it … … 354 371 { 355 372 // error: if neuron 356 if (T_NEURON4 == type) 357 { 373 if (type == CELL_NEURON) //some neurons have the same single-letter names as modifiers (for example G,S,D), but they are supposed to have is_neuroclass==true so they should indeed not be handled here 374 {//however, what we see here is actually modifiers such as IdqEbWL (so not valid neuroclasses) that occurred within an already differentiated cell type==CELL_NEURON. 375 //printf("Handled as a modifier, but type==CELL_NEURON: '%c'\n", name); 358 376 // fix: delete it 359 377 org->setRepairRemove(gcur->pos, gcur); … … 368 386 // turn undiff. cell into a stick 369 387 // error: already differentiated 370 if ( T_UNDIFF4 != type)388 if (type != CELL_UNDIFF) 371 389 { 372 390 // fix: delete this node … … 374 392 return 1; // stop 375 393 } 376 type = T_STICK4;394 type = CELL_STICK; 377 395 // fix dad commacount and own anglepos 378 396 if (NULL != dadlink) … … 381 399 anglepos = dadlink->commacount; 382 400 } 383 // change of type halts developments, see comment at ' N'401 // change of type halts developments, see comment at 'neuclasshandler' below 384 402 gcur = gcur->child; 385 403 return 0; 386 404 } 387 case 'N': 388 { 389 // turn undiff. cell into a neuron 390 // error: already differentiated 391 if (T_UNDIFF4 != type) 392 { 393 // fix: delete this node 394 org->setRepairRemove(gcur->pos, gcur); 395 return 1; // stop 396 } 397 // error: if no previous 398 if (NULL == dadlink) 405 case '[': 406 { 407 // connection to neuron 408 // error: not a neuron 409 if (type != CELL_NEURON) 399 410 { 400 411 // fix: delete it … … 402 413 return 1; // stop 403 414 } 404 string temp1 = "N"; 405 char *temp = (char*)temp1.c_str(); 406 neuclass = GenoOperators::parseNeuroClass(temp); 407 type = T_NEURON4; 408 // change of type also halts development, to give other 409 // cells a chance for adjustment. Namely, it is important 410 // to wait for other cells to turn N before adding links 415 // input [%d:%g] 416 int relfrom = gcur->conn_from; 417 double weight = gcur->conn_weight; 418 f4_Cell *neu_from = NULL; 419 420 // input from other neuron 421 // find neuron at relative i 422 // find own index 423 int this_index = 0, neu_counter = 0; 424 for (int i = 0; i < org->cell_count; i++) 425 { 426 if (org->C[i]->type == CELL_NEURON) neu_counter++; 427 if (org->C[i] == this) { this_index = neu_counter - 1; break; } 428 } 429 // find index of incoming 430 int from_index = this_index + relfrom; 431 432 if (from_index < 0) goto wait_conn; 433 if (from_index >= org->cell_count) goto wait_conn; 434 435 // find that neuron 436 neu_counter = 0; 437 int from; 438 for (from = 0; from < org->cell_count; from++) 439 { 440 if (org->C[from]->type == CELL_NEURON) neu_counter++; 441 if (from_index == (neu_counter - 1)) break; 442 } 443 if (from >= org->cell_count) goto wait_conn; 444 neu_from = org->C[from]; 445 446 // add connection 447 // error: could not add connection (too many?) 448 if (addConnection(neu_from, weight)) 449 { 450 // cannot fix 451 org->setError(gcur->pos); 452 return 1; // stop 453 } 411 454 gcur = gcur->child; 412 return 0; 413 } 414 case '@': 415 case '|': 416 { 417 // neuron rotating / bending 418 int j = 1; 419 if ('@' == name) j = 1; // rot 455 break; 456 } 457 wait_conn: 458 { 459 // wait for other neurons to develop 460 // if there are others still active 461 462 int active_count = 0; 463 for (int i = 0; i < org->cell_count; i++) 464 if (org->C[i]->active) active_count++; 465 active = false; // next time when we reach wait_conn, we will not count ourselves as active (if we were the last active cell, we got a chance to continue development for one development step only) 466 if (active_count > 0) 467 return 0; // there is at least one active (including ourselves), halt, try again 468 469 #ifdef TREAT_BAD_CONNECTIONS_AS_INVALID_GENO // MacKo 2023-04: there were so many invalid connections accumulating in the genotype (and stopping processing of the chain of gcur->child) that it looks like treating them as errors is better... in 2000's, Framsticks neurons were flexible when it comes to inputs and outputs (for example, when asked, muscles would provide an output too, and neurons that ignored inputs would still accept them when connected) so f4 could create connections pretty randomly, but after 2000's we attempt to respect neurons' getPreferredInputs() and getPreferredOutput() so the network of connections has more constraints. 470 if (gcur->parent->name == "#") 471 { 472 // MacKo 2023-04: Unfortunately the logic of multiplicating connections is not ideal... 473 //TREAT_BAD_CONNECTIONS_AS_INVALID_GENO without this "#" exception would break /*4*/<X>N:N#5<[1:1]> 474 // because every neuron wants to get an input from the neuron that will be created next 475 // and all is fine until the last created neuron, which wants to get an input from another one which will not be created 476 // (3 gets from 4, 4 gets from 5, 5 wants to get from 6 (relative connection offset for each of them is 1), 477 // but 6 will not get created and if we want to TREAT_BAD_CONNECTIONS_AS_INVALID_GENO, we produce an error... 478 // We would like to have this multiplication working, but OTAH we don't want to accept bad connections because then they tend to multiply as junk genes and bloat the genotype also causing more and more neutral mutations... 479 //so this condition and checking for "#" is a simple way to be kind to some, but not all, bad connections, and not raise errors. Perhaps too kind and we open the door for too many cases with invalid connections. 480 //Maybe it would be better to perform this check before addConnection(), seeing that for example we process the last iteration of the repetition counter? But how would we know that the (needed here) input neuron will not be developed later by other dividing cells... 481 active = true; //not sure if needed, but let this cell have the chance to continue for as long as many children in the tree are left 482 gcur = gcur->child; 483 return 0; 484 } 420 485 else 421 if ('|' == name) j = 2; // bend 422 423 // if undiff, then this is a new muscle. Thanks to f4_processrec @ and | case we can skip repairing 424 if (T_UNDIFF4 == type) 425 { 426 neuclasshandler = true; 427 break; 428 } 429 430 // error: not a neuron (stick) 431 if (T_NEURON4 != type) 486 { 487 //org->setError(gcur->pos); //in case setRepairRemove() would not always produce reasonable results 488 org->setRepairRemove(gcur->pos, gcur); //produces unexpected results? or NOT? TODO verify, some genotypes earlier produced strange outcomes of this repair (produced a valid genotype, but some neurons were multiplied/copied after repair - maybe because when a branch of '<' (or something else) is missing, the other branch is copied?) 489 return 1; // stop 490 } 491 #else 492 // no more actives, cannot add connection, ignore, but treat not as an error - before 2023-04 493 gcur = gcur->child; 494 #endif 495 } 496 break; 497 case ':': 498 { 499 // neuron parameter 500 // error: not a neuron 501 if (type != CELL_NEURON) 432 502 { 433 503 // fix: delete it … … 435 505 return 1; // stop 436 506 } 437 // error: already has control 438 if (ctrl != 0) 439 { 440 // fix: delete it 441 org->setRepairRemove(gcur->pos, gcur); 442 return 1; // stop 443 } 444 // make neuron ctrl = 1 or 2 445 ctrl = j; 446 gcur = gcur->child; 447 break; 448 } 449 case '[': 450 { 451 // link to neuron 452 // error: not a neuron 453 if (T_NEURON4 != type) 454 { 455 // fix: delete it 456 org->setRepairRemove(gcur->pos, gcur); 457 return 1; // stop 458 } 459 // input (sensor or %d) 460 int t = gcur->i1; 461 int relfrom = gcur->l1; 462 float w = gcur->f1; 463 f4_Cell *tneu = NULL; 464 if (t < 0) // wrong sensor 465 { 466 string buf = "wrong sensor in link '"; 467 buf.append(gcur->s1); 468 buf.append("'"); 469 logMessage("f4_Cell", "onestep", LOG_ERROR, buf.c_str()); 470 org->setRepairRemove(gcur->pos, gcur); 471 return 1; 472 } 473 else if (t > 0) // sensors 474 { 475 char *temp = (char*)gcur->s1.c_str(); 476 NeuroClass *sensortest = GenoOperators::parseNeuroClass(temp); 477 if (sensortest == NULL || sensortest->getPreferredInputs() != 0) 478 { 479 // error: unknown code 480 string buf = "wrong sensor in link '"; 481 buf.append(gcur->s1); 482 buf.append("'"); 483 logMessage("f4_Cell", "onestep", LOG_ERROR, buf.c_str()); 484 org->setRepairRemove(gcur->pos, gcur); 485 return 1; 486 } 487 } 488 else { 489 // input from other neuron 490 // find neuron at relative i 491 // find own index 492 int j = 0, k = 0; 493 for (int i = 0; i < org->nc; i++) 494 { 495 if (org->C[i]->type == T_NEURON4) k++; 496 if (org->C[i] == this) { j = k - 1; break; } 497 } 498 // find index of incoming 499 j = j + relfrom; 500 if (j < 0) goto wait_link; 501 if (j >= org->nc) goto wait_link; 502 // find that neuron 503 k = 0; 504 int i; 505 for (i = 0; i < org->nc; i++) 506 { 507 if (org->C[i]->type == T_NEURON4) k++; 508 if (j == (k - 1)) break; 509 } 510 if (i >= org->nc) goto wait_link; 511 tneu = org->C[i]; 512 } 513 // add link 514 // error: could not add link (too many?) 515 if (addlink(tneu, w, gcur->s1)) 516 { 517 // cannot fix 518 org->setError(gcur->pos); 519 return 1; // stop 520 } 521 gcur = gcur->child; 522 break; 523 } 524 wait_link: 525 { 526 // wait for other neurons to develop 527 // if there are others still active 528 active = 0; 529 int j = 0; 530 for (int i = 0; i < org->nc; i++) 531 { 532 if (org->C[i]->active) j++; 533 } 534 if (j > 0) 535 return 0; // there is other active, halt, try again 536 // no more actives, cannot add link, ignore, but treat not as an error 537 gcur = gcur->child; 538 } 539 break; 540 case ':': 541 { 542 // neuron parameter 543 // error: not a neuron 544 if (T_NEURON4 != type) 545 { 546 // fix: delete it 547 org->setRepairRemove(gcur->pos, gcur); 548 return 1; // stop 549 } 550 int j = gcur->l1; 551 switch ((char)gcur->i1) 507 switch (gcur->prop_symbol) 552 508 { 553 509 case '!': 554 if ( j)510 if (gcur->prop_increase) 555 511 force += (1.0 - force) * 0.2; 556 512 else … … 558 514 break; 559 515 case '=': 560 if ( j)516 if (gcur->prop_increase) 561 517 inertia += (1.0 - inertia) * 0.2; 562 518 else … … 564 520 break; 565 521 case '/': 566 if ( j)522 if (gcur->prop_increase) 567 523 sigmo *= 1.4; 568 524 else … … 586 542 default: 587 543 { 588 // because there are one-character neuron classes, default move control to neuclasshandler544 // because there are one-character neuron classes, default is move control to neuclasshandler 589 545 neuclasshandler = true; 590 546 } … … 593 549 else 594 550 { 595 // if many characters , then it needs to be parsed below551 // if many characters or single character but is_neuroclass, then it will be handled below 596 552 neuclasshandler = true; 597 553 } … … 599 555 if (neuclasshandler) 600 556 { 601 genoRange.add(gcur->pos, gcur->pos + gcur->name.length() + 2 - 1); // +2 for N:602 if ( T_UNDIFF4 != type)557 genoRange.add(gcur->pos, gcur->pos + int(gcur->name.length()) + 2 - 1); // +2 for N: 558 if (type != CELL_UNDIFF) 603 559 { 604 560 // fix: delete this node … … 607 563 } 608 564 // error: if no previous 609 if ( NULL == dadlink)565 if (dadlink == NULL) 610 566 { 611 567 // fix: delete it … … 613 569 return 1; // stop 614 570 } 615 // multiple characters are neuron types. Need to check if exists571 // multiple characters are neuron types. Let's check if exists in the current configuration of Framsticks 616 572 char *temp = (char*)gcur->name.c_str(); 617 neuclass = GenoOperators::parseNeuroClass(temp );573 neuclass = GenoOperators::parseNeuroClass(temp, ModelEnum::SHAPETYPE_BALL_AND_STICK); 618 574 if (neuclass == NULL) 619 575 { 620 576 // error: unknown code 621 string buf = "unknown code '"; 622 buf.append(gcur->name); 623 buf.append("'"); 624 logMessage("f4_Cell", "onestep", 2, buf.c_str()); 577 string buf = "Unknown code '" + gcur->name + "'"; 578 logMessage("f4_Cell", "oneStep", LOG_ERROR, buf.c_str()); 625 579 org->setRepairRemove(gcur->pos, gcur); 626 580 return 1; 627 581 } 628 type = T_NEURON4; //they belong to neurons 582 type = CELL_NEURON; 583 // change of type also halts development, to give other 584 // cells a chance for adjustment. Namely, it is important 585 // to wait for other cells to turn to neurons before adding connections 629 586 gcur = gcur->child; 630 587 return 0; //stop 631 588 } 632 589 } 633 active = 0; // done590 active = false; // done 634 591 return 0; 635 592 } 636 593 637 594 638 int f4_Cell::addlink(f4_Cell *nfrom, double nw, string nt) 639 { 640 // if incoming neuron does not produce output, return error 641 if (nfrom != NULL && nfrom->neuclass->getPreferredOutput() == 0) return -1; 642 if (neuclass->getPreferredInputs() != -1 && nolink >= neuclass->getPreferredInputs()) return -1; 643 if (nolink >= MAXINPUTS - 1) return -1; // full! 644 links[nolink] = new f4_CellLink(nfrom, nw, nt); 645 nolink++; 595 int f4_Cell::addConnection(f4_Cell *nfrom, double nweight) 596 { 597 if (nfrom->neuclass->getPreferredOutput() == 0) return -1; // if incoming neuron does not produce output, return error 598 if (neuclass->getPreferredInputs() != -1 && conns_count >= neuclass->getPreferredInputs()) return -1; //cannot add more inputs to this neuron 599 if (conns_count >= F4_MAX_CELL_INPUTS - 1) return -1; // over hardcoded limit 600 conns[conns_count] = new f4_CellConn(nfrom, nweight); 601 conns_count++; 646 602 return 0; 647 603 } … … 661 617 662 618 // make sure its parent is processed first 663 if ( NULL != dadlink)619 if (dadlink != NULL) 664 620 dadlink->adjustRec(); 665 621 666 622 // count children 667 623 childcount = 0; 668 for (i = 0; i < org-> nc; i++)624 for (i = 0; i < org->cell_count; i++) 669 625 { 670 626 if (org->C[i]->dadlink == this) 671 if (org->C[i]->type == T_STICK4)627 if (org->C[i]->type == CELL_STICK) 672 628 childcount++; 673 629 } 674 630 675 if (type == T_STICK4)676 { 677 if ( NULL == dadlink)631 if (type == CELL_STICK) 632 { 633 if (dadlink == NULL) 678 634 { 679 635 //firstend = Pt3D_0; … … 721 677 722 678 723 f4_Cell Link::f4_CellLink(f4_Cell *nfrom, double nw, string nt)679 f4_CellConn::f4_CellConn(f4_Cell *nfrom, double nweight) 724 680 { 725 681 from = nfrom; 726 w = nw; 727 t = nt; 728 } 729 730 731 732 f4_Cells::f4_Cells(f4_node *genome, int nrepair) 682 weight = nweight; 683 } 684 685 686 687 f4_Cells::f4_Cells(f4_Node *genome, int nrepair) 733 688 { 734 689 // create ancestor cell 735 690 repair = nrepair; 736 error = 0;691 errorcode = GENOPER_OK; 737 692 errorpos = -1; 738 693 repair_remove = NULL; … … 742 697 f4rootnode = NULL; 743 698 C[0] = new f4_Cell(this, 0, genome, genome, NULL, 0, GeneProps::standard_values); 744 nc= 1;699 cell_count = 1; 745 700 } 746 701 … … 750 705 int res; 751 706 repair = nrepair; 752 error = 0;707 errorcode = GENOPER_OK; 753 708 errorpos = -1; 754 709 repair_remove = NULL; … … 759 714 760 715 // transform geno from string to nodes 761 f4rootnode = new f4_ node();762 res = f4_processrec(genome.c_str(), (unsigned)0, f4rootnode);716 f4rootnode = new f4_Node(); 717 res = f4_processrec(genome.c_str(), 0, f4rootnode); 763 718 if ((res < 0) || (1 != f4rootnode->childCount())) 764 719 { 765 error = GENOPER_OPFAIL;720 errorcode = GENOPER_OPFAIL; 766 721 errorpos = -1; 767 722 } … … 769 724 // create ancestor cell 770 725 C[0] = new f4_Cell(this, 0, f4rootnode->child, f4rootnode->child, NULL, 0, GeneProps::standard_values); 771 nc= 1;726 cell_count = 1; 772 727 } 773 728 … … 776 731 // release cells 777 732 int i; 778 if ( nc)779 { 780 for (i = nc- 1; i >= 0; i--)733 if (cell_count) 734 { 735 for (i = cell_count - 1; i >= 0; i--) 781 736 delete C[i]; 782 nc= 0;737 cell_count = 0; 783 738 } 784 739 if (f4rootnode) … … 787 742 788 743 789 int f4_Cells::onestep() 790 { 791 int i, ret, oldnc, ret2; 792 oldnc = nc; 793 ret = 0; 794 for (i = 0; i < oldnc; i++) 795 { 796 ret2 = C[i]->onestep(); 797 if (ret2 > 0) 744 bool f4_Cells::oneStep() 745 { 746 int old_cell_count = cell_count; //cell_count may change in the loop as new cells may be appended because cells may be dividing 747 for (int i = 0; i < old_cell_count; i++) 748 { 749 int cellstep_ret = C[i]->oneStep(); //keeps calling independently of C[i]->active 750 if (cellstep_ret > 0) 798 751 { 799 752 // error 800 C[i]->active = 0; // stop 801 return 0; 802 } 803 // if still active 753 C[i]->active = false; // stop 754 return false; 755 } 756 } 757 for (int i = 0; i < cell_count; i++) //we check all cells, including newly created ones 804 758 if (C[i]->active) 805 ret = 1; 806 } 807 return ret; 759 return true; //at least one cell is still active. TODO maybe the development should stop NOT because of the "active" field (there is this strange "yielding" state too), but by observing the progress of all cells and continuing the development while (errorcode==0 AND (gcur of at least one cell changed OR cell_count changed)) ? Also get rid of return 1/return 0, just observe error. 760 return false; 808 761 } 809 762 … … 811 764 int f4_Cells::simulate() 812 765 { 813 int i; 814 error = GENOPER_OK; 815 816 for (i = 0; i < nc; i++) C[i]->active = 1; 817 818 // execute onestep() in a cycle 819 while (onestep()); 820 821 if (GENOPER_OK != error) return error; 766 constexpr bool print_debugging = false; //print the state of cells during development 767 errorcode = GENOPER_OK; 768 769 for (int i = 0; i < cell_count; i++) C[i]->active = true; 770 771 if (print_debugging) f4_Node::print_tree(C[0]->genot, 0); 772 if (print_debugging) print_cells("Initialization"); 773 774 // execute oneStep() in a cycle 775 while (oneStep()) if (print_debugging) print_cells("Development step"); 776 if (print_debugging) print_cells("After last development step"); 777 778 #ifdef EXTRA_STEP_CELL_DEVELOPMENT 779 if (errorcode == GENOPER_OK) 780 { 781 oneStep(); if (print_debugging) print_cells("After extra step"); //for these "halted" (yielding) cells (they have active==false) that wait for other cells to develop. Without this step, the last, recently halted one(s) may miss the "retrying" step if all active==true cells became active==false in the last step. 782 } 783 #endif 784 785 if (errorcode != GENOPER_OK) return errorcode; 822 786 823 787 // fix neuron attachements 824 for (i = 0; i < nc; i++)825 { 826 if (C[i]->type == T_NEURON4)827 { 828 while ( T_NEURON4 == C[i]->dadlink->type)788 for (int i = 0; i < cell_count; i++) 789 { 790 if (C[i]->type == CELL_NEURON) 791 { 792 while (C[i]->dadlink->type == CELL_NEURON) 829 793 { 830 794 C[i]->dadlink = C[i]->dadlink->dadlink; … … 835 799 // there should be no undiff. cells 836 800 // make undifferentiated cells sticks 837 for (i = 0; i < nc; i++)838 { 839 if (C[i]->type == T_UNDIFF4)840 { 841 C[i]->type = T_STICK4;842 //set error();801 for (int i = 0; i < cell_count; i++) 802 { 803 if (C[i]->type == CELL_UNDIFF) 804 { 805 C[i]->type = CELL_STICK; 806 //setError(); 843 807 } 844 808 } … … 846 810 // recursive adjust 847 811 // reset recursive traverse flags 848 for (i = 0; i < nc; i++)812 for (int i = 0; i < cell_count; i++) 849 813 C[i]->recProcessedFlag = 0; 850 814 // process every cell 851 for (i = 0; i < nc; i++)815 for (int i = 0; i < cell_count; i++) 852 816 C[i]->adjustRec(); 853 817 854 818 //DB( printf("Cell simulation done, %d cells. \n", nc); ) 855 819 856 return error; 820 if (print_debugging) print_cells("Final"); 821 822 return errorcode; 823 } 824 825 826 void f4_Cells::print_cells(const char* description) 827 { 828 printf("------ %-55s ------ errorcode=%d, errorpos=%d\n", description, getErrorCode(), getErrorPos()); 829 for (int i = 0; i < cell_count; i++) 830 { 831 f4_Cell *c = C[i]; 832 string type; 833 switch (c->type) 834 { 835 case CELL_UNDIFF: type = "UNDIFF"; break; 836 case CELL_STICK: type = "STICK"; break; 837 case CELL_NEURON: type = string("NEURON:") + c->neuclass->name.c_str(); break; 838 default: type = std::to_string(c->type); 839 } 840 const char *status = c->active ? "active" : (c->gcur != NULL ? "yielding" : ""); //yielding = not active but waiting for other cells 841 printf("%2d(%-8s) nr=%d \t type=%-15s \t genot=%s \t gcurrent=%s", i, status, c->nr, type.c_str(), c->genot->name.c_str(), c->gcur ? c->gcur->name.c_str() : "null"); 842 if (c->gcur && c->gcur->name == "[") 843 printf("\tfrom=%d weight=%g", c->gcur->conn_from, c->gcur->conn_weight); 844 printf("\n"); 845 for (int l = 0; l < c->conns_count; l++) 846 printf("\tconn:%d from=%d weight=%g\n", l, c->conns[l]->from->nr, c->conns[l]->weight); 847 } 848 printf("\n"); 857 849 } 858 850 … … 860 852 void f4_Cells::addCell(f4_Cell *newcell) 861 853 { 862 if ( nc >= MAX4CELLS - 1)854 if (cell_count >= F4_MAX_CELLS - 1) 863 855 { 864 856 delete newcell; 865 857 return; 866 858 } 867 C[ nc] = newcell;868 nc++;859 C[cell_count] = newcell; 860 cell_count++; 869 861 } 870 862 … … 872 864 void f4_Cells::setError(int nerrpos) 873 865 { 874 error = GENOPER_OPFAIL;866 errorcode = GENOPER_OPFAIL; 875 867 errorpos = nerrpos; 876 868 } 877 869 878 void f4_Cells::setRepairRemove(int nerrpos, f4_ node *rem)870 void f4_Cells::setRepairRemove(int nerrpos, f4_Node *rem) 879 871 { 880 872 if (!repair) 881 873 { 882 874 // not in repair mode, treat as repairable error 883 error = GENOPER_REPAIR;875 errorcode = GENOPER_REPAIR; 884 876 errorpos = nerrpos; 885 877 } 886 878 else 887 879 { 888 error = GENOPER_REPAIR;880 errorcode = GENOPER_REPAIR; 889 881 errorpos = nerrpos; 890 882 repair_remove = rem; … … 892 884 } 893 885 894 int f4_Cells::setRepairInsert(int nerrpos, f4_ node *parent, f4_node *insert)886 int f4_Cells::setRepairInsert(int nerrpos, f4_Node *parent, f4_Node *insert) 895 887 { 896 888 if (!repair) 897 889 { 898 890 // not in repair mode, treat as repairable error 899 error = GENOPER_REPAIR;891 errorcode = GENOPER_REPAIR; 900 892 errorpos = nerrpos; 901 893 return -1; … … 903 895 else 904 896 { 905 error = GENOPER_REPAIR;897 errorcode = GENOPER_REPAIR; 906 898 errorpos = nerrpos; 907 899 repair_parent = parent; … … 911 903 } 912 904 913 void f4_Cells::repairGeno(f4_ node *geno, int whichchild)905 void f4_Cells::repairGeno(f4_Node *geno, int whichchild) 914 906 { 915 907 // assemble repaired geno, if the case 916 908 if (!repair) return; 917 if (( NULL == repair_remove) && (NULL == repair_insert)) return;909 if ((repair_remove == NULL) && (repair_insert == NULL)) return; 918 910 // traverse genotype tree, remove / insert node 919 f4_node *g2; 920 if (1 == whichchild) g2 = geno->child; 921 else g2 = geno->child2; 922 if (NULL == g2) 911 f4_Node *g2; 912 if (whichchild == 1) 913 g2 = geno->child; 914 else 915 g2 = geno->child2; 916 if (g2 == NULL) 923 917 return; 924 918 if (g2 == repair_remove) 925 919 { 926 f4_ node *oldgeno;920 f4_Node *oldgeno; 927 921 geno->removeChild(g2); 928 922 if (g2->child) 929 923 { 930 924 // add g2->child as child to geno 931 if (1 == whichchild) geno->child = g2->child; 932 else geno->child2 = g2->child; 925 if (whichchild == 1) 926 geno->child = g2->child; 927 else 928 geno->child2 = g2->child; 933 929 g2->child->parent = geno; 934 930 } … … 936 932 oldgeno->child = NULL; 937 933 delete oldgeno; 938 if ( NULL == geno->child) return;934 if (geno->child == NULL) return; 939 935 // check this new 940 936 repairGeno(geno, whichchild); … … 973 969 char buf[200]; 974 970 975 if (curc >= nc) return;976 977 if ( T_STICK4 != C[curc]->type) return;971 if (curc >= cell_count) return; 972 973 if (C[curc]->type != CELL_STICK) return; 978 974 979 975 thisti = C[curc]; 980 if ( NULL != thisti->dadlink)976 if (thisti->dadlink != NULL) 981 977 *tmpcel = *(thisti->dadlink); 982 978 … … 1018 1014 1019 1015 // neurons attached to it 1020 for (i = 0; i < nc; i++)1021 { 1022 if (C[i]->type == T_NEURON4)1016 for (i = 0; i < cell_count; i++) 1017 { 1018 if (C[i]->type == CELL_NEURON) 1023 1019 { 1024 1020 if (C[i]->dadlink == thisti) … … 1027 1023 out += "["; 1028 1024 // ctrl 1029 if (1 == thneu->ctrl) out += "@"; 1030 if (2 == thneu->ctrl) out += "|"; 1031 // links 1032 for (j = 0; j < thneu->nolink; j++) 1025 //if (1 == thneu->ctrl) out += "@"; // old code; this can be easily generalized to any neuroclass if ever needed 1026 //if (2 == thneu->ctrl) out += "|"; 1027 out += thneu->neuclass->name.c_str(); // not tested, but something like that 1028 // connections 1029 for (j = 0; j < thneu->conns_count; j++) 1033 1030 { 1034 1031 if (j) out += ","; 1035 if (NULL == thneu->links[j]->from) 1036 { 1037 // sensors 1038 out += thneu->links[j]->t.c_str(); 1039 } 1040 else 1041 { 1042 sprintf(buf, "%d", thneu->links[j]->from->name - thneu->name); 1043 out += buf; 1044 } 1032 sprintf(buf, "%d", thneu->conns[j]->from->nr - thneu->nr); 1033 out += buf; 1045 1034 out += ":"; 1046 1035 // connection weight 1047 sprintf(buf, "%g", thneu-> links[j]->w);1036 sprintf(buf, "%g", thneu->conns[j]->weight); 1048 1037 out += buf; 1049 1038 } … … 1058 1047 1059 1048 ccount = 1; 1060 for (i = 0; i < nc; i++)1061 { 1062 if (C[i]->type == T_STICK4)1049 for (i = 0; i < cell_count; i++) 1050 { 1051 if (C[i]->type == CELL_STICK) 1063 1052 { 1064 1053 if (C[i]->dadlink == thisti) … … 1088 1077 // to organize an f4 genotype in a tree structure 1089 1078 1090 f4_ node::f4_node()1079 f4_Node::f4_Node() 1091 1080 { 1092 1081 name = "?"; … … 1095 1084 child2 = NULL; 1096 1085 pos = -1; 1097 l1 = 0; 1098 i1 = 0; 1099 f1 = 0.0f; 1100 } 1101 1102 f4_node::f4_node(string nname, f4_node *nparent, int npos) 1086 1087 reps = 0; 1088 prop_symbol = '\0'; 1089 prop_increase = false; 1090 conn_from = 0; 1091 conn_weight = 0.0; 1092 neuclass = NULL; 1093 } 1094 1095 f4_Node::f4_Node(string nname, f4_Node *nparent, int npos) 1103 1096 { 1104 1097 name = nname; … … 1108 1101 pos = npos; 1109 1102 if (parent) parent->addChild(this); 1110 l1 = 0; 1111 i1 = 0; 1112 f1 = 0.0f; 1113 } 1114 1115 f4_node::f4_node(char nname, f4_node *nparent, int npos) 1103 1104 reps = 0; 1105 prop_symbol = '\0'; 1106 prop_increase = false; 1107 conn_from = 0; 1108 conn_weight = 0.0; 1109 neuclass = NULL; 1110 } 1111 1112 f4_Node::f4_Node(char nname, f4_Node *nparent, int npos) 1116 1113 { 1117 1114 name = nname; … … 1121 1118 pos = npos; 1122 1119 if (parent) parent->addChild(this); 1123 l1 = 0; 1124 i1 = 0; 1125 f1 = 0.0f; 1126 } 1127 1128 f4_node::~f4_node() 1129 { 1130 // (destroy() copied here for efficiency) 1131 // children are destroyed (recursively) through the destructor 1132 if (NULL != child2) delete child2; 1133 if (NULL != child) delete child; 1134 } 1135 1136 int f4_node::addChild(f4_node *nchi) 1137 { 1138 if (NULL == child) 1120 1121 reps = 0; 1122 prop_symbol = '\0'; 1123 prop_increase = false; 1124 conn_from = 0; 1125 conn_weight = 0.0; 1126 neuclass = NULL; 1127 } 1128 1129 f4_Node::~f4_Node() 1130 { 1131 destroy(); 1132 } 1133 1134 void f4_Node::print_tree(const f4_Node *root, int indent) 1135 { 1136 for (int i = 0; i < indent; i++) printf(" "); 1137 printf("%s (%d)", root->name.c_str(), root->count()); 1138 if (root->name == "[") 1139 printf(" from=%-3d weight=%g", root->conn_from, root->conn_weight); 1140 printf("\n"); 1141 if (root->child) print_tree(root->child, indent + 1); 1142 if (root->child2) print_tree(root->child2, indent + 1); 1143 } 1144 1145 int f4_Node::addChild(f4_Node *nchi) 1146 { 1147 if (child == NULL) 1139 1148 { 1140 1149 child = nchi; 1141 1150 return 0; 1142 1151 } 1143 if ( NULL == child2)1152 if (child2 == NULL) 1144 1153 { 1145 1154 child2 = nchi; … … 1149 1158 } 1150 1159 1151 int f4_ node::removeChild(f4_node *nchi)1160 int f4_Node::removeChild(f4_Node *nchi) 1152 1161 { 1153 1162 if (nchi == child2) … … 1164 1173 } 1165 1174 1166 int f4_ node::childCount()1167 { 1168 if ( NULL != child)1169 { 1170 if ( NULL != child2) return 2;1175 int f4_Node::childCount() 1176 { 1177 if (child != NULL) 1178 { 1179 if (child2 != NULL) return 2; 1171 1180 else return 1; 1172 1181 } 1173 1182 else 1174 1183 { 1175 if ( NULL != child2) return 1;1184 if (child2 != NULL) return 1; 1176 1185 else return 0; 1177 1186 } 1178 1187 } 1179 1188 1180 int f4_ node::count()1189 int f4_Node::count() const 1181 1190 { 1182 1191 int c = 1; 1183 if ( NULL != child) c += child->count();1184 if ( NULL != child2) c += child2->count();1192 if (child != NULL) c += child->count(); 1193 if (child2 != NULL) c += child2->count(); 1185 1194 return c; 1186 1195 } 1187 1196 1188 f4_ node* f4_node::ordNode(int n)1197 f4_Node* f4_Node::ordNode(int n) 1189 1198 { 1190 1199 int n1; 1191 if ( 0 == n) return this;1200 if (n == 0) return this; 1192 1201 n--; 1193 if ( NULL != child)1202 if (child != NULL) 1194 1203 { 1195 1204 n1 = child->count(); … … 1197 1206 n -= n1; 1198 1207 } 1199 if ( NULL != child2)1208 if (child2 != NULL) 1200 1209 { 1201 1210 n1 = child2->count(); … … 1206 1215 } 1207 1216 1208 f4_ node* f4_node::randomNode()1217 f4_Node* f4_Node::randomNode() 1209 1218 { 1210 1219 int n = count(); … … 1213 1222 } 1214 1223 1215 f4_ node* f4_node::randomNodeWithSize(int mn, int mx)1224 f4_Node* f4_Node::randomNodeWithSize(int mn, int mx) 1216 1225 { 1217 1226 // try random nodes, and accept if size in range 1218 1227 // limit to maxlim tries 1219 1228 int i, n, maxlim; 1220 f4_ node *nod = NULL;1229 f4_Node *nod = NULL; 1221 1230 maxlim = count(); 1222 1231 for (i = 0; i < maxlim; i++) … … 1230 1239 } 1231 1240 1232 void f4_ node::sprint(SString& out)1241 void f4_Node::sprint(SString& out) 1233 1242 { 1234 1243 char buf2[20]; … … 1237 1246 { 1238 1247 out += "#"; 1239 if (i1 != 1) 1240 { 1241 sprintf(buf2, "%d", i1); 1248 sprintf(buf2, "%d", reps); 1249 out += buf2; 1250 } 1251 else { 1252 // special case: neuron connection 1253 if (name == "[") 1254 { 1255 out += "["; 1256 sprintf(buf2, "%d", conn_from); 1242 1257 out += buf2; 1243 } 1244 } 1245 else { 1246 // special case: neuron link 1247 if (name == "[") 1248 { 1249 out += "["; 1250 if (i1 > 0) 1251 { 1252 // sensor input 1253 out += s1.c_str(); 1254 } 1255 else 1256 { 1257 sprintf(buf2, "%d", l1); 1258 out += buf2; 1259 } 1260 sprintf(buf2, ":%g]", f1); 1258 sprintf(buf2, ":%g]", conn_weight); 1261 1259 out += buf2; 1262 1260 } 1263 1261 else if (name == ":") 1264 1262 { 1265 sprintf(buf2, ":%c%c:", l1 ? '+' : '-', (char)i1);1263 sprintf(buf2, ":%c%c:", prop_increase ? '+' : '-', prop_symbol); 1266 1264 out += buf2; 1267 1265 } 1268 else if (name == "@" || name == "|") 1269 { 1270 if (parent->name == "N") 1271 { 1272 out += name.c_str(); 1273 } 1274 else 1275 { 1276 out += "N:"; 1277 out += name.c_str(); 1278 } 1266 else if (neuclass != NULL) 1267 { 1268 out += "N:"; 1269 out += neuclass->name.c_str(); 1279 1270 } 1280 1271 else 1281 1272 { 1282 char *temp = (char*)name.c_str();1283 NeuroClass *nc = GenoOperators::parseNeuroClass(temp);1284 if (nc != NULL)1285 {1286 out += "N:";1287 }1288 1273 out += name.c_str(); 1289 1274 } 1290 1275 } 1291 if (NULL != child) child->sprint(out); 1276 1277 if (child != NULL) 1278 child->sprint(out); 1292 1279 // if two children, make sure last char is a '>' 1293 if (2 == childCount()) 1294 if (0 == out[0]) out += ">"; else 1295 if ('>' != out[out.length() - 1]) out += ">"; 1296 if (NULL != child2) child2->sprint(out); 1280 if (childCount() == 2) 1281 if (out[0] == 0) out += ">"; else 1282 if (out[out.length() - 1] != '>') out += ">"; 1283 1284 if (child2 != NULL) 1285 child2->sprint(out); 1297 1286 // make sure last char is a '>' 1298 if ( 0 == out[0]) out += ">"; else1299 if ( '>' != out[out.length() - 1]) out += ">";1300 } 1301 1302 void f4_ node::sprintAdj(char *& buf)1287 if (out[0] == 0) out += ">"; else 1288 if (out[out.length() - 1] != '>') out += ">"; 1289 } 1290 1291 void f4_Node::sprintAdj(char *& buf) 1303 1292 { 1304 1293 unsigned int len; … … 1312 1301 len = out.length(); 1313 1302 if (len > 1) 1314 if ( '>' == out[len - 1]) { (out.directWrite())[len - 1] = 0; out.endWrite(); };1303 if (out[len - 1] == '>') { (out.directWrite())[len - 1] = 0; out.endWrite(); }; 1315 1304 // copy back to string 1316 1305 // if new is longer, reallocate buf … … 1322 1311 } 1323 1312 1324 f4_ node* f4_node::duplicate()1325 { 1326 f4_ node *copy;1327 copy = new f4_ node(*this);1313 f4_Node* f4_Node::duplicate() 1314 { 1315 f4_Node *copy; 1316 copy = new f4_Node(*this); 1328 1317 copy->parent = NULL; // set later 1329 1318 copy->child = NULL; 1330 1319 copy->child2 = NULL; 1331 if ( NULL != child)1320 if (child != NULL) 1332 1321 { 1333 1322 copy->child = child->duplicate(); 1334 1323 copy->child->parent = copy; 1335 1324 } 1336 if ( NULL != child2)1325 if (child2 != NULL) 1337 1326 { 1338 1327 copy->child2 = child2->duplicate(); … … 1343 1332 1344 1333 1345 void f4_ node::destroy()1334 void f4_Node::destroy() 1346 1335 { 1347 1336 // children are destroyed (recursively) through the destructor 1348 if ( NULL != child2)delete child2;1349 if ( NULL != child)delete child;1337 if (child2 != NULL) delete child2; 1338 if (child != NULL) delete child; 1350 1339 } 1351 1340 1352 1341 // scan genotype string and build tree 1353 1342 // return >1 for error (errorpos) 1354 int f4_processrec(const char* genot, unsigned pos0, f4_node *parent) 1355 { 1356 int i, j, res, t; 1357 char tc1, tc2, tc3[2]; // tc3 is only to ensure that neuron parameter definition is completed 1358 int relfrom; 1359 double w; 1343 int f4_processrec(const char* genot, unsigned pos0, f4_Node *parent) 1344 { 1345 int i, res; 1360 1346 unsigned gpos, oldpos; 1361 f4_ node *node1, *par;1347 f4_Node *node1, *par; 1362 1348 unsigned beginindex; 1363 string neutype = "";1364 1349 1365 1350 gpos = pos0; … … 1368 1353 while (gpos < strlen(genot)) 1369 1354 { 1370 neutype = "";1371 1355 // first switch across cell dividers and old semantics 1372 1356 switch (genot[gpos]) … … 1375 1359 { 1376 1360 // find out genotype start for child 1377 j = scanrec(genot + gpos + 1, strlen(genot + gpos + 1), '>');1378 1379 node1 = new f4_ node("<", par, gpos);1361 int j = scanrec(genot + gpos + 1, strlen(genot + gpos + 1), '>'); 1362 1363 node1 = new f4_Node("<", par, gpos); 1380 1364 par = node1; 1381 1365 res = f4_processrec(genot, gpos + 1, par); … … 1388 1372 else // ran out 1389 1373 { 1390 node1 = new f4_ node(">", par, int(strlen(genot)) - 1);1374 node1 = new f4_Node(">", par, int(strlen(genot)) - 1); 1391 1375 par = node1; 1392 1376 } … … 1396 1380 case '>': 1397 1381 { 1398 node1 = new f4_ node(">", par, gpos);1382 node1 = new f4_Node(">", par, gpos); 1399 1383 par = node1; 1400 gpos = strlen(genot);1384 gpos = (unsigned int)strlen(genot); 1401 1385 return 0; // OK 1402 1386 } … … 1409 1393 else i = val.getInt(); 1410 1394 // find out genotype start for continuation 1411 j = scanrec(genot + gpos + 1, strlen(genot + gpos + 1), '>');1395 int j = scanrec(genot + gpos + 1, strlen(genot + gpos + 1), '>'); 1412 1396 // skip number 1413 1397 oldpos = gpos; 1414 1398 gpos += end - (genot + gpos); 1415 1399 //gpos++; 1416 //while ((genot[gpos] >= '0') && (genot[gpos] <= '9')) gpos++;node1 = new f4_ node("#", par, oldpos);1417 node1 = new f4_ node("#", par, oldpos);1418 node1-> i1= i;1400 //while ((genot[gpos] >= '0') && (genot[gpos] <= '9')) gpos++;node1 = new f4_Node("#", par, oldpos); 1401 node1 = new f4_Node("#", par, oldpos); 1402 node1->reps = i; 1419 1403 par = node1; 1420 1404 res = f4_processrec(genot, gpos, node1); … … 1427 1411 else // ran out 1428 1412 { 1429 node1 = new f4_ node(">", par, int(strlen(genot)) - 1);1413 node1 = new f4_Node(">", par, int(strlen(genot)) - 1); 1430 1414 } 1431 1415 return 0; // OK … … 1440 1424 break; 1441 1425 } 1442 case 'l': case 'L': 1443 case 'c': case 'C': 1444 case 'q': case 'Q': 1445 case 'r': case 'R': 1446 case 'X': case ',': 1447 case 'a': case 'A': 1448 case 's': case 'S': 1449 case 'm': case 'M': 1450 case 'i': case 'I': 1451 case 'f': case 'F': 1452 case 'w': case 'W': 1453 case 'e': case 'E': 1454 { 1455 node1 = new f4_node(genot[gpos], par, gpos); 1426 case 'N': 1427 { 1428 int forgenorange = gpos; 1429 if (genot[gpos + 1] != ':') 1430 return gpos + 1; //error 1431 gpos += 2; //skipping "N:" 1432 beginindex = gpos; 1433 char* end = (char*)genot + beginindex; 1434 NeuroClass *neuclass = GenoOperators::parseNeuroClass(end, ModelEnum::SHAPETYPE_BALL_AND_STICK); 1435 if (neuclass == NULL) 1436 return gpos + 1; //error 1437 gpos += end - genot - beginindex; 1438 string neutype = string(genot + beginindex, genot + gpos); 1439 node1 = new f4_Node(neutype, par, forgenorange); 1440 node1->neuclass = neuclass; 1441 par = node1; 1442 // if it continues with a colon that determines a neuron parameter (e.g. N:N:+=: ), then let the switch case for colon handle this 1443 break; 1444 } 1445 case ':': 1446 { 1447 // neuron parameter +! -! += -= +/ or -/ 1448 // in the future this could be generalized to all neuron properties, for example N:|:power:0.6:range:1.4, or can even use '=' or ',' instead of ':' if no ambiguity 1449 char prop_dir, prop_symbol, prop_end[2]; // prop_end is only to ensure that neuron parameter definition is completed 1450 if (sscanf(genot + gpos, ":%c%c%1[:]", &prop_dir, &prop_symbol, &prop_end) != 3) 1451 // error: incorrect format 1452 return gpos + 1 + 1; 1453 if (prop_dir != '-' && prop_dir != '+') 1454 return gpos + 1 + 1; //error 1455 switch (prop_symbol) 1456 { 1457 case '!': case '=': case '/': break; 1458 default: 1459 return gpos + 1 + 1; //error 1460 } 1461 node1 = new f4_Node(":", par, gpos); 1462 node1->prop_symbol = prop_symbol; 1463 node1->prop_increase = prop_dir == '+' ? true : false; // + or - 1464 par = node1; 1465 int chars = scanrec(genot + gpos + 1, strlen(genot + gpos + 1), ':'); 1466 gpos += chars + 2; 1467 break; 1468 } 1469 case '[': 1470 { 1471 double weight = 0; 1472 int relfrom; 1473 const char *end = parseConnection(genot + gpos, relfrom, weight); 1474 if (end == NULL) 1475 return gpos + 1; //error 1476 1477 node1 = new f4_Node("[", par, gpos); 1478 node1->conn_from = relfrom; 1479 node1->conn_weight = weight; 1480 par = node1; 1481 int j = scanrec(genot + gpos + 1, strlen(genot + gpos + 1), ']'); 1482 gpos += j + 2; 1483 break; 1484 } 1485 default: // 'X' and ',' and all modifiers and also invalid symbols - add a node, for invalid symbols build will give the error or repair 1486 { 1487 //printf("any regular character '%c'\n", genot[gpos]); 1488 node1 = new f4_Node(genot[gpos], par, gpos); 1456 1489 par = node1; 1457 1490 gpos++; 1458 1491 break; 1459 1492 } 1460 case '@': case '|':1461 {1462 // in order to prevent the presence of "free muscles", we need to ensure that a muscle is written as N@/N| or N:@/N:|1463 if (par != NULL && par->name == "N")1464 {1465 node1 = new f4_node(genot[gpos], par, gpos);1466 par = node1;1467 gpos++;1468 }1469 else1470 {1471 return gpos + 1;1472 }1473 break;1474 }1475 1476 case 'N':1477 {1478 // if there is no colon after N, then there is no class definition1479 if (gpos + 1 >= strlen(genot) || genot[gpos + 1] != ':')1480 {1481 node1 = new f4_node(genot[gpos], par, gpos);1482 par = node1;1483 gpos++;1484 break;1485 }1486 // if there is a colon determining neuron parameter, then let the switch case colon handle this1487 else if (sscanf(genot + gpos + 1, ":%c%c%1[:]", &tc1, &tc2, &tc3) == 3)1488 {1489 node1 = new f4_node(genot[gpos], par, gpos);1490 par = node1;1491 gpos++;1492 break;1493 }1494 int forgenorange = gpos;1495 gpos += 2; //skipping "N:"1496 beginindex = gpos;1497 char* end = (char*)genot + beginindex;1498 GenoOperators::parseNeuroClass(end);1499 gpos += end - genot - beginindex;1500 neutype = string(genot + beginindex, genot + gpos);1501 node1 = new f4_node(neutype, par, forgenorange);1502 par = node1;1503 break;1504 }1505 case ':':1506 {1507 // neuron parameter +! -! += -= +/ or -/1508 if (sscanf(genot + gpos, ":%c%c%1[:]", &tc1, &tc2, &tc3) != 3)1509 // error: incorrect format1510 return gpos + 1 + 1;1511 if ('+' == tc1) j = 1;1512 else if ('-' == tc1) j = 0;1513 else return gpos + 1 + 1;1514 switch (tc2)1515 {1516 case '!': case '=': case '/': break;1517 default:1518 return gpos + 1 + 1;1519 }1520 node1 = new f4_node(":", par, gpos);1521 node1->l1 = j;1522 node1->i1 = (int)tc2;1523 par = node1;1524 j = scanrec(genot + gpos + 1, strlen(genot + gpos + 1), ':');1525 gpos += j + 2;1526 break;1527 }1528 case '[':1529 {1530 const char *end = parseConnection(genot + gpos, relfrom, w);1531 if (end == NULL)1532 {1533 end = parseConnectionWithNeuron(genot + gpos, neutype, w);1534 if (end == NULL) t = -1;1535 else t = 1;1536 }1537 else1538 {1539 t = 0;1540 }1541 node1 = new f4_node("[", par, gpos);1542 node1->s1 = neutype;1543 node1->i1 = t;1544 node1->l1 = relfrom;1545 node1->f1 = w;1546 par = node1;1547 j = scanrec(genot + gpos + 1, strlen(genot + gpos + 1), ']');1548 gpos += j + 2;1549 break;1550 }1551 default:1552 {1553 //DB( printf("unknown character '%c' ! \n", genot[gpos]); )1554 //add it, build will give the error or repair1555 node1 = new f4_node(genot[gpos], par, gpos);1556 par = node1;1557 gpos++;1558 break;1559 }1560 1493 } 1561 1494 } … … 1566 1499 if (par->name != ">") 1567 1500 { 1568 node1 = new f4_ node('>', par, int(strlen(genot)) - 1);1501 node1 = new f4_Node('>', par, int(strlen(genot)) - 1); 1569 1502 par = node1; 1570 1503 } … … 1593 1526 } 1594 1527 1595 const char* parseConnectionWithNeuron(const char *fragm, string &neutype, double &weight) 1596 { 1597 const char *parser = fragm; 1598 if (*parser != '[') return NULL; 1599 parser++; 1600 char* p = (char*)parser; 1601 if (GenoOperators::parseNeuroClass(p) == NULL) return NULL; 1602 neutype = string(parser, (const char *)p); 1603 parser = p; 1604 if (*parser != ':') return NULL; 1605 parser++; 1606 ExtValue val; 1607 parser = val.parseNumber(parser, ExtPType::TDouble); 1608 if (parser == NULL) return NULL; 1609 weight = val.getDouble(); 1610 if (*parser != ']') return NULL; 1611 parser++; 1612 return parser; 1613 } 1614 1615 f4_node* f4_processtree(const char* geno) 1616 { 1617 f4_node *root; 1618 int res; 1619 root = new f4_node(); 1620 res = f4_processrec(geno, 0, root); 1528 f4_Node* f4_processtree(const char* geno) 1529 { 1530 f4_Node *root = new f4_Node(); 1531 int res = f4_processrec(geno, 0, root); 1621 1532 if (res) return NULL; 1622 1533 //DB( printf("test f4 "); ) -
cpp/frams/genetics/f4/f4_general.h
r829 r1227 1 1 // This file is a part of Framsticks SDK. http://www.framsticks.com/ 2 // Copyright (C) 1999-20 15Maciej Komosinski and Szymon Ulatowski.2 // Copyright (C) 1999-2023 Maciej Komosinski and Szymon Ulatowski. 3 3 // See LICENSE.txt for details. 4 4 … … 29 29 void rolling_inc(double *v); 30 30 31 class f4_ node; // later31 class f4_Node; // later 32 32 class f4_Cell; // later 33 33 class f4_Cells; // later … … 36 36 /** @name Types of f4_Cell's */ 37 37 //@{ 38 #define T_UNDIFF440 ///<undifferentiated cell39 #define T_STICK441 ///<differentiated to stick, cannot divide40 #define T_NEURON442 ///<differentiated to neuron, can divide38 #define CELL_UNDIFF 40 ///<undifferentiated cell 39 #define CELL_STICK 41 ///<differentiated to stick, cannot divide 40 #define CELL_NEURON 42 ///<differentiated to neuron, can divide 41 41 //@} 42 42 … … 54 54 55 55 56 class f4_Cell Link;56 class f4_CellConn; 57 57 58 58 /** @name Constraints of f4 genotype structures */ 59 59 //@{ 60 #define MAXINPUTS 100 ///<maximum number of neuron inputs61 #define MAX4CELLS 100 ///<maximum number of f4 organism cells60 #define F4_MAX_CELL_INPUTS 10 ///<maximum number of neuron inputs in a developing organism 61 #define F4_MAX_CELLS 100 ///<maximum number of f4 organism cells 62 62 //@} 63 63 64 64 /** 65 * Abstract cell type - the representation of single component in thedevelopmental65 * Abstract cell type - the representation of a single component in the developmental 66 66 * encoding. In the beginning, each f4_Cell is undifferentiated. During the process 67 67 * of development it can divide or differentiate into a stick or a neuron. If it … … 86 86 /** 87 87 * A constructor that takes the pointer to the repetition node and the count of repetitions. 88 * @param a pointer to f4_ node for repetition character88 * @param a pointer to f4_Node for repetition character 89 89 * @param b the number of repetitions 90 90 */ 91 repeat_ptr(f4_ node *a, int b) : node(a), count(b) { };91 repeat_ptr(f4_Node *a, int b) : node(a), count(b) { }; 92 92 93 93 inline void makeNull() { node = NULL; count = -1; }; … … 96 96 97 97 inline void dec() { count--; }; 98 f4_ node *node; ///<pointer to the repetition code98 f4_Node *node; ///<pointer to the repetition code 99 99 int count; ///<repetition counter 100 100 }; … … 130 130 static const int stackSize = 4; ///<max 4 nested levels 131 131 repeat_ptr ptr[stackSize]; ///<array holding pointers to repeat_ptr 132 shortint top; ///<index of the top of the stack132 int top; ///<index of the top of the stack 133 133 }; 134 134 135 135 /** 136 136 * Creates a new f4_Cell object. 137 * @param nn ame name of a cell, can be T_UNDIFF4, T_STICK4 or T_NEURON4137 * @param nnr number of the cell 138 138 * @param ndad pointer to the parent of the created cell 139 139 * @param nangle the amount of commas affecting branch angles 140 140 * @param newP genotype properties of a given cell 141 141 */ 142 f4_Cell(int nn ame, f4_Cell *ndad, int nangle, GeneProps newP);142 f4_Cell(int nnr, f4_Cell *ndad, int nangle, GeneProps newP); 143 143 /** 144 144 * Creates a new f4_Cell object. 145 145 * @param nO pointer to an organism containing the cell 146 * @param nn ame name of the cell, can be T_UNDIFF4, T_STICK4 or T_NEURON4146 * @param nnr number of the cell 147 147 * @param ngeno pointer to the root of the genotype tree 148 * @param ngcur pointer to the f4_ node representing the current cell in the genotype tree148 * @param ngcur pointer to the f4_Node representing the current cell in the genotype tree 149 149 * @param ndad pointer to the parent of the created cell 150 150 * @param nangle the number of commas affecting branch angles 151 151 * @param newP genotype properties of a given cell 152 152 */ 153 f4_Cell(f4_Cells *nO, int nn ame, f4_node *ngeno, f4_node *ngcur, f4_Cell *ndad, int nangle, GeneProps newP);153 f4_Cell(f4_Cells *nO, int nnr, f4_Node *ngeno, f4_Node *ngcur, f4_Cell *ndad, int nangle, GeneProps newP); 154 154 155 155 ~f4_Cell(); … … 172 172 * - the stick modifiers, like rotation, will be applied on neuron cell, 173 173 * - the differentiated cell will be differentiated again, 174 * - the neuron class inside cell connection (i.e. N[G:5]) is not a sensor,175 174 * - the connection between neurons cannot be established, 176 175 * - the neuron class is not valid. … … 178 177 * @return 0 if development was successful, 1 if there was an error in genotype tree 179 178 */ 180 int onestep(); 181 182 /** 183 * Adds a link between this neuron cell and a given neuron cell in nfrom. If the nfrom object 184 * is not given, neuron type in nt should be a sensor type. 185 * @param nfrom input neuron cell, or NULL if not given 186 * @param nw weight of connection 187 * @param nt empty string or name of sensor class 188 * @return 0 if link is established, -1 otherwise 189 */ 190 int addlink(f4_Cell *nfrom, double nw, string nt); 179 int oneStep(); 180 181 /** 182 * Adds a connection between this neuron cell and a given neuron cell in nfrom. 183 * @param nfrom input neuron cell 184 * @param nweight weight of connection 185 * @return 0 if connection is established, -1 otherwise 186 */ 187 int addConnection(f4_Cell *nfrom, double nweight); 191 188 192 189 /** … … 195 192 void adjustRec(); 196 193 197 int n ame; ///<name of cell (number)194 int nr; ///<number of cell (seems to be used only in old f1 converter for neuron connections) 198 195 int type; ///<type 199 196 f4_Cell *dadlink; ///<pointer to cell parent 200 197 f4_Cells *org; ///<uplink to organism 201 198 202 f4_ node *genot; ///<genotype tree203 f4_ node *gcur; ///<current genotype execution pointer204 int active; ///<determines whether development is still active199 f4_Node *genot; ///<genotype tree 200 f4_Node *gcur; ///<current genotype execution pointer 201 bool active; ///<determines whether development is still active; even if false, the cell may "yield" - may be halted (but still having its onStep() called) due to neural connections waiting for other cells to potentially develop neurons 205 202 repeat_stack repeat; ///<stack holding repetition nodes and counters 206 203 int recProcessedFlag; ///<used during recursive traverse … … 220 217 int neuro_refno; ///<the number of the neuro object, used in f0 221 218 222 int ctrl; ///<neuron type223 219 double inertia; ///<inertia of neuron 224 220 double force; ///<force of neuron 225 221 double sigmo; ///<sigmoid of neuron 226 f4_Cell Link *links[MAXINPUTS]; ///<array of neuron links227 int nolink; ///<number of links222 f4_CellConn *conns[F4_MAX_CELL_INPUTS]; ///<array of neuron connections 223 int conns_count; ///<number of connections 228 224 NeuroClass *neuclass; ///<pointer to neuron class 229 225 }; 230 226 231 227 /** 232 * Class representing linkbetween neuron cells.233 */ 234 class f4_Cell Link228 * Class representing a connection between neuron cells. 229 */ 230 class f4_CellConn 235 231 { 236 232 public: 237 233 /** 238 234 * Constructor for f4_CellLink class. Parameter nfrom represents input 239 * neuron cell or NULL if connection has defined sensor type inside, like "[G:5]". 240 * The name of sensor class defined inside neuron connection is stored in the nt 241 * parameter. 242 * @param nfrom pointer to input neuron cell or NULL 243 * @param nw weight of connection 244 * @param nt name of neuron class or empty string 245 */ 246 f4_CellLink(f4_Cell *nfrom, double nw, string nt); 247 248 f4_Cell *from; ///<pointer to input neuron cell 249 string t; ///<empty if 'from' cell is given, NeuroClass name otherwise 250 double w; ///<weight of connection 235 * neuron cell. 236 * @param nfrom pointer to input neuron cell 237 * @param nweight weight of connection 238 */ 239 f4_CellConn(f4_Cell *nfrom, double nweight); 240 241 f4_Cell *from; ///<pointer to input neuron cell 242 double weight; ///<weight of connection 251 243 }; 252 244 … … 264 256 * @param nrepair 0 if nothing to repair 265 257 */ 266 f4_Cells(f4_ node *genome, int nrepair);258 f4_Cells(f4_Node *genome, int nrepair); 267 259 268 260 /** … … 291 283 292 284 /** 293 * Performs a single step of organism development. It runs each active cell 294 * in the organism. 295 * @return 0 if all cells are developed, or 1 otherwise 296 */ 297 int onestep(); 285 * Performs a single step of organism development. It runs each active cell in the organism. 286 * @return false if all cells are developed or there is an error, true otherwise 287 */ 288 bool oneStep(); 298 289 299 290 /** … … 302 293 * @return 0 if organism developed successfully, error code if something went wrong 303 294 */ 304 int simulate(); 295 int simulate(); 296 297 /** 298 * Prints the current state of the organism (for debugging purposes). 299 * @param description printout header 300 */ 301 void print_cells(const char* description); 305 302 306 303 /** … … 308 305 * @return error code 309 306 */ 310 int geterror() { return error; };307 int getErrorCode() { return errorcode; }; 311 308 312 309 /** … … 314 311 * @return position of an error 315 312 */ 316 int geterrorpos() { return errorpos; };313 int getErrorPos() { return errorpos; }; 317 314 318 315 /** … … 325 322 * Sets the element of genotype to be repaired by removal. 326 323 * @param nerrpos position of an error in genotype 327 * @param rem the f4_ node to be removed from the genotype tree in order to repair328 */ 329 void setRepairRemove(int nerrpos, f4_ node *rem);324 * @param rem the f4_Node to be removed from the genotype tree in order to repair 325 */ 326 void setRepairRemove(int nerrpos, f4_Node *rem); 330 327 331 328 /** … … 336 333 * @return 0 if repair can be performed, or -1 otherwise because the repair flag wasn't set in the constructor 337 334 */ 338 int setRepairInsert(int nerrpos, f4_node *parent, f4_node *insert);335 int setRepairInsert(int nerrpos, f4_Node *parent, f4_Node *insert); 339 336 340 337 /** … … 343 340 * @param whichchild 1 if first child, 2 otherwise 344 341 */ 345 void repairGeno(f4_ node *geno, int whichchild);342 void repairGeno(f4_Node *geno, int whichchild); 346 343 347 344 // the cells 348 f4_Cell *C[ MAX4CELLS]; ///<Array of all cells of an organism349 int nc;///<Number of cells in an organism345 f4_Cell *C[F4_MAX_CELLS]; ///<Array of all cells of an organism 346 int cell_count; ///<Number of cells in an organism 350 347 351 348 private: 352 349 // for error reporting / genotype fixing 353 350 int repair; 354 int error ;351 int errorcode; 355 352 int errorpos; 356 f4_ node *repair_remove;357 f4_ node *repair_parent;358 f4_ node *repair_insert;353 f4_Node *repair_remove; 354 f4_Node *repair_parent; 355 f4_Node *repair_insert; 359 356 void toF1GenoRec(int curc, SString &out); 360 357 f4_Cell *tmpcel; // needed by toF1Geno 361 f4_ node *f4rootnode; // used by constructor358 f4_Node *f4rootnode; // used by constructor 362 359 }; 363 360 … … 366 363 * A class to organize a f4 genotype in a tree structure. 367 364 */ 368 class f4_ node365 class f4_Node 369 366 { 370 367 public: 371 string name; ///<one-letter 'name', multiple characters for classes372 f4_ node *parent; ///<parent link or NULL373 f4_ node *child; ///<child or NULL374 f4_ node *child2; ///<second child or NULL368 string name; ///<one-letter gene code or multiple characters for neuron classes (then neuclass != NULL) 369 f4_Node *parent; ///<parent link or NULL 370 f4_Node *child; ///<child or NULL 371 f4_Node *child2; ///<second child or NULL 375 372 int pos; ///<original position in the string 376 int i1; ///<internal int parameter1 377 int l1; ///<internal long parameter1 (now also int, since long is not well specified and it is in our scenarios equivalent to int) 378 double f1; ///<internal double parameter1 379 string s1; ///<internal string parameter1 380 381 f4_node(); 373 374 int reps; ///<repetition counter for the '#' gene 375 char prop_symbol; ///<old-style properties (force,intertia,sigmoid) of the N neuron: !=/ 376 bool prop_increase; ///<false=decrease neuron property (force,intertia,sigmoid), true=increase it 377 int conn_from; ///<relative number of the neuron this neuron get an input from 378 double conn_weight; ///<neuron connection weight 379 NeuroClass *neuclass; ///< NULL or not if "name" is a neuroclass name with a proper genotype context ("N:neuroclassname"). New in 2023-04 - to fix fatal flaw with fundamental assumptions: it was impossible to distinguish between single-character neuron names such as S, D, G and single-character modifiers. They were all stored in the "name" field. Before 2018 this was never a problem because the only supported neuroclasses had distinctive symbols such as @|*GTS, and the set of supported modifiers was small and different from neuroclass letters (no G,D,S clash). 380 381 f4_Node(); 382 382 383 383 /** … … 387 387 * @param npos position of node substring in the genotype string 388 388 */ 389 f4_ node(string nname, f4_node *nparent, int npos);389 f4_Node(string nname, f4_Node *nparent, int npos); 390 390 391 391 /** … … 395 395 * @param npos position of node character in the genotype string 396 396 */ 397 f4_node(char nname, f4_node *nparent, int npos); 398 399 ~f4_node(); 397 f4_Node(char nname, f4_Node *nparent, int npos); 398 399 ~f4_Node(); 400 401 /** 402 * Recursively print subtree (for debugging). 403 * @param root starting node 404 * @param indent initial indentation 405 */ 406 static void print_tree(const f4_Node *root, int indent); 400 407 401 408 /** … … 404 411 * @return 0 if the child could be added, -1 otherwise 405 412 */ 406 int addChild(f4_ node *nchi);413 int addChild(f4_Node *nchi); 407 414 408 415 /** … … 411 418 * @return 0 if child could be removed, -1 otherwise 412 419 */ 413 int removeChild(f4_ node *nchi);420 int removeChild(f4_Node *nchi); 414 421 415 422 /** … … 423 430 * @return the number of nodes from this node 424 431 */ 425 int count() ;432 int count() const; 426 433 427 434 /** … … 430 437 * @return pointer to the nth subnode or NULL if not found 431 438 */ 432 f4_ node* ordNode(int n);439 f4_Node* ordNode(int n); 433 440 434 441 /** … … 436 443 * @return random subnode 437 444 */ 438 f4_ node* randomNode();445 f4_Node* randomNode(); 439 446 440 447 /** … … 444 451 * @return a random subnode with a given size or NULL 445 452 */ 446 f4_ node* randomNodeWithSize(int min, int max);453 f4_Node* randomNodeWithSize(int min, int max); 447 454 448 455 /** … … 456 463 * @return pointer to a tree copy 457 464 */ 458 f4_ node* duplicate();465 f4_Node* duplicate(); 459 466 460 467 /** … … 468 475 /** 469 476 * The main function for converting a string of f4 encoding to a tree structure. Prepares 470 * f4_ node root of tree and runs f4_processrec function for it.477 * f4_Node root of tree and runs f4_processrec function for it. 471 478 * @param geno the string representing an f4 genotype 472 * @return a pointer to the f4_ node object representing the f4 tree root473 */ 474 f4_ node* f4_processtree(const char *geno);479 * @return a pointer to the f4_Node object representing the f4 tree root 480 */ 481 f4_Node* f4_processtree(const char *geno); 475 482 476 483 /** 477 484 * Scans a genotype string starting from a given position. This recursive method creates 478 * a tree of f4_ node objects. This method extracts each potentially functional element479 * of a genotype string to a separate f4_ nodes. When the branching character '<' occurs,480 * f4_processrec is deployed for the latest f4_ node element. This method does not485 * a tree of f4_Node objects. This method extracts each potentially functional element 486 * of a genotype string to a separate f4_Nodes. When the branching character '<' occurs, 487 * f4_processrec is deployed for the latest f4_Node element. This method does not 481 488 * analyse the genotype semantically, it only checks if the syntax is proper. The only 482 489 * semantic aspect is neuron class name extraction, where the GenoOperators … … 487 494 * @return 0 if processing was successful, otherwise returns the position of an error in the genotype 488 495 */ 489 int f4_processrec(const char *genot, unsigned pos0, f4_ node *parent);496 int f4_processrec(const char *genot, unsigned pos0, f4_Node *parent); 490 497 491 498 /** … … 502 509 const char *parseConnection(const char *fragm, int &relfrom, double &weight); 503 510 504 /**505 * Parses the notation of the neuron connection with neuron definition - takes506 * the beginning of the connection definition, extracts the name of neuron class507 * that will be the input for the current neuron and the weight of the connection.508 * After successful parsing, returns a pointer to the first character after the connection509 * definition, or NULL if the connection definition was not valid due to the lack of [, :, ]510 * characters, an invalid value of the weight or an invalid neuron class name.511 * @param fragm the beginning of the connection definition, should be the '[' character512 * @param neutype the reference to a string representing the input neuron class name. The name of the class is validated with GenoOperators::parseNeuroClass()513 * @param weight the reference to a double variable in which the weight of the connection will be stored514 * @return the pointer to the first character in string after the connection definition515 */516 const char *parseConnectionWithNeuron(const char *fragm, string &neutype, double &weight);517 518 511 #endif -
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 } -
cpp/frams/genetics/f4/f4_oper.h
r779 r1227 1 1 // This file is a part of Framsticks SDK. http://www.framsticks.com/ 2 // Copyright (C) 1999-20 17Maciej Komosinski and Szymon Ulatowski.2 // Copyright (C) 1999-2023 Maciej Komosinski and Szymon Ulatowski. 3 3 // See LICENSE.txt for details. 4 4 … … 33 33 //@} 34 34 35 /** @name Codes for specific F4_MOD neuron mutation subtypes (not included in mutation_method_names, all are considered F4_MOD there) */ 36 //@{ 37 #define F4_MODNEU_CONN 0 38 #define F4_MODNEU_WEIGHT 1 39 #define F4_MODNEU_COUNT 2 40 //@} 41 35 42 class Geno_f4 : public GenoOperators 36 43 { … … 48 55 // mutation probabilities 49 56 double prob[F4_COUNT]; ///<relative probabilities of selecting mutation types in f4 genotype 50 double probadd[F4_ADD_COUNT]; ///<relative probabilities of selecting mutation addition subtypes 57 double probadd[F4_ADD_COUNT]; ///<relative probabilities of selecting addition mutation subtypes 58 double probmodneu[F4_MODNEU_COUNT]; ///<relative probabilities of selecting neuron mutation subtypes 59 paInt mut_max_rep; ///maximum allowed number of repetitions for the '#' repetition gene 51 60 52 61 SString excluded_modifiers; ///<Modifiers that are excluded in mutation process … … 63 72 * @return GENOOPER_OK if genotype is valid, GENOPER_REPAIR if genotype can be repaired, GENOPER_OPFAIL if genotype can't be repaired 64 73 */ 65 int ValidateRec(f4_ node *geno, int retrycount) const;74 int ValidateRec(f4_Node *geno, int retrycount) const; 66 75 67 76 /** … … 86 95 * @return GENOPER_OK if mutation was performed successfully, GENOPER_FAIL otherwise 87 96 */ 88 int MutateOne(f4_ node *& g, int &method) const;97 int MutateOne(f4_Node *& g, int &method) const; 89 98 90 99 /** 91 * Creates a random connection to an existing neuron or creates an additional 100 * Finds all neurons in g (in the order of ordNode()) and returns their neuroclasses in a vector. 101 * Additionally, looks for the needle_neuron node and returns its index (in the list of the returned vector) as found_index, 102 * or -1 if not found (for example, it was not a neuroclass node or not added to the "g" tree). 103 * @param g root node 104 * @param needle_neuron neuroclass node to look for in all nodes 105 * @param found_index returned index of needle 106 * @return all nodes that are neurons 107 */ 108 static vector<NeuroClass*> findAllNeuronsAndNode(f4_Node * const & g, f4_Node* const &needle_neuron, int &found_index); 109 110 /** 111 * Finds indexes of a given neuron and another random (output- or input-providing) neuron in the list of all neurons present in the "g" tree. 112 * @param g root node 113 * @param neuron neuroclass node to look for in all nodes in g 114 * @param other_has_output if true, other neuron will provide output; otherwise, it will accept input(s) 115 * @param neuron_index returned index of neuron 116 * @param other_neuron_index returned index of a random neuron that provides an output or accepts inputs 117 * @return true if succeeded, false otherwise 118 */ 119 static bool findConnectionNeuronIndexes(f4_Node * const &g, f4_Node *neuron, bool other_has_output, int &neuron_index, int &other_neuron_index); 120 121 /** 122 * Creates a random connection to an existing neuron and randomizes connection weight 92 123 * sensor for a neuron. 93 124 * @param nn neuron class node 94 * @param n euid id of aneuron95 * @param neulist list of genotype neuron classes125 * @param nn_index index of the nn neuron 126 * @param other_index index of the neuron providing output, to get input from 96 127 */ 97 void linkNodeMakeRandom(f4_node *nn, int neuid, std::vector<NeuroClass*> neulist) const; 98 99 /** 100 * Changes connection to an existing neuron or creates an additional 101 * sensor for neuron. 102 * @param nn neuron connection node 103 * @param neuid id of a neuron 104 * @param neulist list of genotype neuron classes 105 */ 106 void linkNodeChangeRandom(f4_node *nn, int neuid, std::vector<NeuroClass*> neulist) const; 128 void connectionNodeChangeRandom(f4_Node *nn, int nn_index, int other_index) const; 107 129 108 130 /** … … 110 132 * @param nn neuron node 111 133 */ 112 void nparNodeMakeRandom(f4_ node *nn) const;134 void nparNodeMakeRandom(f4_Node *nn) const; 113 135 114 136 /** … … 116 138 * @param nn repetition node 117 139 */ 118 void repeatNodeChangeRandom(f4_ node *nn) const;140 void repeatNodeChangeRandom(f4_Node *nn) const; 119 141 120 142 /** … … 124 146 * @return GENOPER_OK if performed successful mutation, GENOPER_FAIL otherwise 125 147 */ 126 int MutateOneValid(f4_ node *&g, int &method) const;148 int MutateOneValid(f4_Node *&g, int &method) const; 127 149 128 150 /** … … 134 156 * @param chg percentage of the first parent in offspring (the second parent has the rest) 135 157 */ 136 int CrossOverOne(f4_ node *g1, f4_node *g2, float chg) const;158 int CrossOverOne(f4_Node *g1, f4_Node *g2, float chg) const; 137 159 }; 138 160
Note: See TracChangeset
for help on using the changeset viewer.