Changeset 1239


Ignore:
Timestamp:
05/08/23 02:10:57 (12 months ago)
Author:
Maciej Komosinski
Message:

More robust stopping condition for organism development: no longer based on declarations of cells (I am active or I am not), but on the observation of their actual development progress

Location:
cpp/frams/genetics/f4
Files:
2 edited

Legend:

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

    r1237 r1239  
    1919
    2020#define BREAK_WHEN_REP_COUNTER_NULL //see comments where it is used
    21 #define EXTRA_STEP_CELL_DEVELOPMENT //see comments where it is used
    2221#define TREAT_BAD_CONNECTIONS_AS_INVALID_GENO //see comments where it is used
    2322
     
    4140        org = NULL;
    4241        genot = NULL;
    43         gcur = NULL;
    44         active = true;
     42        gcur = old_gcur = NULL;
    4543        repeat.clear();
    4644        //genoRange.clear(); -- implicit
     
    8987        org = nO;
    9088        genot = ngeno;
    91         gcur = ngcur;
    92         active = true;
     89        gcur = old_gcur = ngcur;
    9390        repeat.clear();
    9491        //genoRange.clear(); -- implicit
     
    268265                                        }
    269266                                        repeat.clear();
    270                                         active = false;  // stop
    271267                                        // eat up rest
    272268                                        int remaining_nodes = gcur->count() - 1;
     
    421417                        {
    422418                                // wait for other neurons to develop
    423                                 // if there are others still active
    424 
    425                                 int active_count = 0;
    426                                 for (int i = 0; i < org->cell_count; i++)
    427                                         if (org->C[i]->active) active_count++;
    428                                 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)
    429                                 if (active_count > 0)
    430                                         // there is at least one active (including ourselves), halt, try again
     419
     420                                if (!org->development_stagnation) // other cells are developing, the situation is changing, we may continue waiting...
    431421                                        return;  // error code not set -> halt this development and yield to other cells to develop
     422
     423                                //no cells are developing and we are waiting, but there is no chance other cells will create neurons we are waiting for, so we are forced to move on.
    432424
    433425#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.
     
    443435                                        //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.
    444436                                        //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...
    445                                         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
     437
    446438                                        gcur = gcur->child;
     439                                        org->development_stagnation = false; //do not force other potentially waiting cells to hurry and act in this development cycle (which would be the last cycle if development_stagnation stayed true); we just acted and because of this the situation may change, so they can wait until another development_stagnation is detected
    447440                                        return;  // error code not set -> halt this development and yield to other cells to develop
    448441                                }
     
    541534                }
    542535        }
    543         active = false;  // done
    544536}
    545537
     
    650642        C[0] = new f4_Cell(this, 0, genome, genome, NULL, 0, GeneProps::standard_values);
    651643        cell_count = 1;
     644        development_stagnation = false;
    652645}
    653646
     
    670663        int old_cell_count = cell_count; //cell_count may change in the loop as new cells may be appended because cells may be dividing
    671664        for (int i = 0; i < old_cell_count; i++)
    672         {
    673                 C[i]->oneStep(); //keeps calling independently of C[i]->active
     665                C[i]->old_gcur = C[i]->gcur;
     666
     667        for (int i = 0; i < old_cell_count; i++)
     668        {
     669                C[i]->oneStep();
    674670                if (errorcode != GENOPER_OK)
    675                 {
    676                         // error
    677                         C[i]->active = false;  // stop
    678                         return false;
    679                 }
    680         }
    681         for (int i = 0; i < cell_count; i++) //we check all cells, including newly created ones
    682                 if (C[i]->active)
    683                         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==GENOPER_OK AND (gcur of at least one cell changed OR cell_count changed)) ?
    684         return false;
     671                        return false; // error -> end development
     672        }
     673
     674        if (cell_count != old_cell_count) //the number of cells changed - something is going on!
     675                return true; //so continue development!
     676
     677        for (int i = 0; i < old_cell_count; i++)
     678                if (C[i]->old_gcur != C[i]->gcur) // genotype execution pointer changed - something is going on!
     679                        return true; //so continue development!
     680
     681        if (development_stagnation)
     682                return false; //the same number of cells, no progress in development in any cell -> stagnation, end development
     683        else
     684        {
     685                development_stagnation = true; //signal (force) f4_Cell's that wait for neural connection development to make a step, because all cells stagnated and waiting cells cannot hope for new neurons to be created
     686                return true;
     687        }
    685688}
    686689
     
    690693        const bool PRINT_CELLS_DEVELOPMENT = false; //print the state of cells
    691694        errorcode = GENOPER_OK;
    692 
    693         for (int i = 0; i < cell_count; i++)  C[i]->active = true;
     695        development_stagnation = false; //will be detected by oneStep()
    694696
    695697        if (PRINT_CELLS_DEVELOPMENT) f4_Node::print_tree(C[0]->genot, 0);
     
    699701        while (oneStep()) if (PRINT_CELLS_DEVELOPMENT) print_cells("Development step");
    700702        if (PRINT_CELLS_DEVELOPMENT) print_cells("After last development step");
    701 
    702 #ifdef EXTRA_STEP_CELL_DEVELOPMENT
    703         if (errorcode == GENOPER_OK)
    704         {
    705                 oneStep(); if (PRINT_CELLS_DEVELOPMENT) 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.
    706         }
    707 #endif
    708703
    709704        if (errorcode != GENOPER_OK) return errorcode;
     
    762757                default: type = std::to_string(c->type);
    763758                }
    764                 const char *status = c->active ? "active" : (c->gcur != NULL ? "yielding" : ""); //yielding = not active but waiting for other cells
     759                const char *status = c->gcur == c->old_gcur ? (c->gcur != NULL ? "no progress" : "") : (c->gcur != NULL ? "progress" : "finished"); //progress or no progress means the cell is yielding = not finished but decided to halt development and wait for other cells. New cells may be created in case of "no progress" status.
    765760                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");
    766761                if (c->gcur && c->gcur->name == "[")
     
    14091404                        static
    14101405#else
    1411                         thread_local 
     1406                        thread_local
    14121407#endif
    14131408                                vector<int> modifs_counts(strlen(all_modifiers_no_comma)); ///<an array with a known constant size storing counters of each modifier symbol from all_modifiers_no_comma, created once to avoid reallocation every time when modifier genes are simplified during parsing. Initialization of required size; it will never be resized.
  • cpp/frams/genetics/f4/f4_general.h

    r1237 r1239  
    190190        f4_Node *genot;                    ///<genotype tree
    191191        f4_Node *gcur;                 ///<current genotype execution pointer
    192         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
     192        f4_Node *old_gcur;             ///<used externally by f4_Cells::oneStep() to track changes of gcur, i.e., to detect progress in cell development
    193193        repeat_stack repeat;           ///<stack holding repetition nodes and counters
    194194        int recProcessedFlag;          ///<used during recursive traverse
     
    329329        f4_Cell *C[F4_MAX_CELLS];  ///<Array of all cells of an organism
    330330        int     cell_count;        ///<Number of cells in an organism
     331        bool    development_stagnation; ///< simulate() and oneStep() use it to force f4_Cell's waiting to develop their neural connections to progress, indicating that all cells have not had progress during the last step
    331332
    332333private:
Note: See TracChangeset for help on using the changeset viewer.