Changeset 1239

05/08/23 02:10:57 (10 months ago)
Maciej Komosinski

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

2 edited


  • cpp/frams/genetics/f4/f4_general.cpp

    r1237 r1239  
    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
    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
    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
     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
     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.
    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
    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
    650642        C[0] = new f4_Cell(this, 0, genome, genome, NULL, 0, GeneProps::standard_values);
    651643        cell_count = 1;
     644        development_stagnation = false;
    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;
     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        }
     674        if (cell_count != old_cell_count) //the number of cells changed - something is going on!
     675                return true; //so continue development!
     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!
     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        }
    690693        const bool PRINT_CELLS_DEVELOPMENT = false; //print the state of cells
    691694        errorcode = GENOPER_OK;
    693         for (int i = 0; i < cell_count; i++)  C[i]->active = true;
     695        development_stagnation = false; //will be detected by oneStep()
    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");
    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
    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
    1411                         thread_local 
     1406                        thread_local
    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
Note: See TracChangeset for help on using the changeset viewer.