Skip to content

BrainStatesandLifeTimes

Clifford Bohm edited this page Jan 24, 2021 · 6 revisions

Brains can record their own states. Recording can be switched on an off with:

[brain]->setRecordActivity(true) // on
[brain]->setRecordActivity(false) // off

Brains record three continuous value time series (InputStates, OutputStates, HiddenStates) and lifeTimes(a list with the length of each lifetime). It is up to the brain developer to determine what data should go into each time series. Generally, Input and Output should be the input and output that the brain receives and delivers - and these can in theory be extracted directly using setInput() and readOutput() functions (although these are probably less efficient than other methods available from inside the brain. What data needs to appear in HiddenStates must be determined by the developer and will depend on the design of the brain. For Markov brains will deterministic state, HiddenStates are simply the hidden nodes, but for brains with stateful subcomponents, a proper recording of hidden states may require reading from individual components (such as neuron gates in a Markov Brain).
In addition, brains record lifeTimes. This is just a list with the number of updates between each brain reset. So if a brain existed in a world that had 4 possible initial states and which ran for 20 updates, the lifeTimes would be {20,20,20,20}. lifeTimes is a list so that we can handle cases where worlds don't always evaluate for the same number of updates (e.g. a world that stops evaluation when the agent provides a solution).

Time Series

Brain states are recorded into TS::timeSeries (which are actually vector<vector>). You can read and write to them as any other vector. As a brain developer, you are responsible for loading data into these vectors, which can happen as part of [brain]->update();

TS::TimeSeries InputStates;
TS::TimeSeries OutputStates;
TS::TimeSeries HiddenStates;

Life Times

Life times are recorded in a vector. This is initialized to 1 element with value 0 on creation to indicate that the brain has experienced one life time of duration 0.

std::vector<int> lifeTimes = { 0 }; // a vector of the durration of each lifetime

Every update you must increment lifeTimes.back(). This should probably happen in the [brain]->update() function.

lifeTimes.back()++

Every time [brain]->reset() is called, lifeTimes should have a new empty lifeTime appended. This is the default behavior in AbstractBrain. But if you override [brain]->reset() make sure that you add the following code to your version:

if (recordActivity) {
    if (lifeTimes.back() != 0) {
        lifeTimes.push_back(0);
    }
}

recurrence

Hidden nodes are recurrent. Sometimes Output is also recurrent. If we think about Input data, this is set before [brain]->update() is called and does not change, so for a given update, there is one input state. For non-recurrent Output there is also only one output state per call to [brain]->update(). but for recurrent nodes (hidden and recurrent output) for each [brain]->update() there are two states (before and after). This does not mean that there will be 2 times as many hidden states as input states because each "after" recurrent state on a given update is the "before" recurrent state on the next update. So there only needs to be one extra entry in recurrent state recordings versus non-recurrent state recordings.
As a brain developer, it is up to you to manage this process. it is suggested that on each [brain]->update() before the hidden (and input if recurrent) are altered and (if lifeTimes.back() == 0) that the recurrent state be recoded to it's respective state vector. This will result in recurrent state recordings being oversized or "bloated", but not to worry the time series and information tools are aware of bloated time series.

recurrent output

A special variable (bool recurrentOutput) is provided to establish if the output is recurrent (i.e. if the output from the prior update is provided to the brain on the current update - like hidden). This variable is false by default but should be set to true (in the constructor) if the output is recurrent. See the note above on recurrence regarding recording recurrent states and bloated time series.

continuous value vs. discreet value time series

You may be worried that the state information is being saved as double. This allows us to support brains with continuous value internal states. Functions can be found in Analyze/timeSeries.h to convert continuous value time series to discreet value time series. Information on TimeSeries discretization can be found in TimeSeries.

other functions (you probably should not worry about)

TS::TimeSeries getInputStates() {
    return InputStates;
}
TS::TimeSeries getOutputStates() {
    return OutputStates;
}
TS::TimeSeries getHiddenStates() {
    return HiddenStates;
}
std::vector<int> getLifeTimes() {
    return lifeTimes;
}

void resetStatesAndLifetimes() {
    InputStates.clear();
    OutputStates.clear();
    HiddenStates.clear();
    lifeTimes = { 0 };
}
Clone this wiki locally