Changeset 1227 for cpp/frams/genetics/f4/f4_general.cpp
- Timestamp:
- 04/27/23 04:04:06 (17 months ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
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 "); )
Note: See TracChangeset
for help on using the changeset viewer.