#include "NsSystem.hh" /** * Constructor * @param props Properties */ NsSystem::NsSystem(Props &props) : trainNumStimCycles(props.getUint("trainNumStimCycles")), consNumStimCycles(props.getUint("consNumStimCycles")), reactNumStimCycles(props.getUint("reactNumStimCycles")), numSettleCycles(props.getUint("numSettleCycles")) { } /** * Add a layer * @param id Layer ID */ void NsSystem::addLayer(const string &id, const string &type) { NsLayer *layer = new NsLayer(id, type); std::pair<string, NsLayer *> pair(id, layer); layers.insert(pair); } /** * Add a tract * @param fromLayerId ID of "from" layer * @param toLayerId ID of "to" layer * @param type Type of tract */ void NsSystem::addTract(const string &fromLayerId, const string &toLayerId, const string &type) { string id = fromLayerId + "-" + toLayerId; NsLayer *fromLayer = layers.at(fromLayerId); NsLayer *toLayer = layers.at(toLayerId); NsTract *tract = new NsTract(id, fromLayer, toLayer, type); std::pair<string, NsTract *> pair(id, tract); tracts.insert(pair); } /** * Add bi-directional tract, i.e. two tracts, one in either * direction between two layers. * @param layer1Id ID of one layer * @param layer2Id ID of another layer * @param type Type of tract */ void NsSystem::addBiTract(const string &layer1Id, const string &layer2Id, const string &type) { addTract(layer1Id, layer2Id, type); addTract(layer2Id, layer1Id, type); } /** * Calculate rates current timeStep value for all tracts */ void NsSystem::calcRates() { for (auto &t : tracts) { t.second->calcRates(); } } /** * Acquire the currently presented pattern */ void NsSystem::acquire(uint numStimCycles, const char *tag) { for (auto &t : tracts) { t.second->acquire(numStimCycles, tag); } } /** * Cycle unit activations for a fixed number of times, and call it * settled. Cycling is synchronous: first calculate all units' new * activation, then update them all in parallel. */ void NsSystem::settle() { for (uint c = 0; c < numSettleCycles; c++) { for (auto &l : layers) { if (!l.second->isFrozen) { l.second->computeNewActivations(); } } for (auto &l : layers) { if (!l.second->isFrozen) { l.second->applyNewActivations(); l.second->adjustInhibition(); } } } } /** * Activate and clamp a randomly chosen trained pattern in HPC, * clear the other layers, then cycle and learn whatever pattern * is settled on. */ void NsSystem::consolidate() { // TODO: maybe configurable number of // consolidation trials per day? // Clear all the layers // clear(); // If HPC is frozen (which includes lesioned), then // do nothing. NsLayer *hpcLayer = layers.at(hpcLayerId); if (hpcLayer->isFrozen) { return; } #if 1 // Activate and clamp a randomly chosen defined pattern in // the HPC layer // const string &hpcPid = hpcLayer->setRandomPattern(); hpcLayer->isClamped = true; #else // Randomize the HPC layer const string &hpcPid = ""; hpcLayer->randomize(); #endif printGrids("cons-present"); settle(); printGrids("cons-settled", hpcPid); // Learn the pattern settled into: PSD growth // for (auto &t : tracts) { t.second->consolidate(consNumStimCycles); } } /** * Run maintenance processes */ void NsSystem::maintain() { for (auto &t : tracts) { t.second->maintain(); } for (auto &l : layers) { l.second->maintain(); } } /** * Clear activations and unclamp all layers */ void NsSystem::clear() { for (auto &l : layers) { l.second->clear(); l.second->isClamped = false; } } /** * Freeze or unfreeze a layer */ void NsSystem::setFrozen(const string &layerId, bool state) { NsLayer *layer = layers.at(layerId); layer->setFrozen(state); } /** * Lesion a layer */ void NsSystem::lesion(const string &layerId) { NsLayer *layer = layers.at(layerId); layer->lesion(); } /** * Run background processes in all layers and tracts */ void NsSystem::runBackgroundProcesses() { consolidate(); maintain(); } /** * Present a cue pattern to one layer, then cycle the system until it settles. * Call printGrids to report recall performance. */ void NsSystem::retrieve(const string &cueLayerId, const string &patternId, const string& condition) { clear(); // Set and clamp the specified pattern in the cue layer // NsLayer *cueLayer = layers.at(cueLayerId); cueLayer->setPattern(patternId); cueLayer->isClamped = true; printGrids(fmt::format("{}-present", condition)); // Cycle until settled // settle(); printGrids(fmt::format("{}-settled", condition), patternId); } /** * Test recall by presenting the CS pattern to the specified layer * and executing a retrieval. * @param cueLayerId ID of layer to cue * @param patternId ID of pattern to use as cue * @param condition Condition identifier */ void NsSystem::test(const string &cueLayerId, const string &patternId, const string &condition) { // Record inhibition levels before the test // for (auto l : layers) { l.second->saveInhibition(); } // Do the test // retrieve(cueLayerId, patternId, condition); // Restore inhibition levels, in order to leave the system unaffected by // the test. // for (auto l : layers) { l.second->restoreInhibition(); } } /** * Train the currently presented pattern */ void NsSystem::train() { acquire(trainNumStimCycles, "train"); } /** * Reactivate a CS-US pattern by presenting the CS pattern to SC0 and * executing a retrieval, then (TODO) do some learning */ void NsSystem::reactivate() { // Present the cue (CS) and execute a retrieval // retrieve(sc0LayerId, "CS-US", "reactivate"); // Execute reactivation logic in all tracts // for (auto &t : tracts) { t.second->reactivate(); } // Patterns should disappear from HPC layer's (really, any layer's) // pattern list as they decay. Here we simply clear HPC's pattern list // at reactivation time. This is ok a long as reactivation happens long // after training pattern HPC components have decayed, which is the case // in all current reactivation test cases. // getLayer(hpcLayerId)->clearPatterns(); // Activate a random HPC pattern execute a learning cycle. // getLayer(hpcLayerId)->makePattern("react"); getLayer(hpcLayerId)->setPattern("react"); printGrids(fmt::format("Pattern {}", "react")); acquire(reactNumStimCycles, "react"); } /** * Toggle PSI on or off in all tracts that terminate on the * specified layer. */ void NsSystem::togglePsi(string layerId, bool state) { NsLayer *layer = getLayer(layerId); for (auto &t : tracts) { if (t.second->fromLayer == layer || t.second->toLayer == layer) { t.second->togglePsi(state); } } } void NsSystem::printStateHdrs() { NsConnection::printStateHdr(); NsUnit::printStateHdr(); NsLayer::printScoreHdr(); NsLayer::printNumActiveHdr(); NsTract::printNumPotentiatedHdr(); } void NsSystem::printState() const { for (auto &l : layers) { l.second->printState(); } for (auto &t : tracts) { t.second->printState(); } } void NsSystem::printGrids(const string &tag, const string &targetId) const { for (auto &l : layers) { l.second->printGrid(tag, targetId); } } void NsSystem::printSize() { uint tot = 0; for (auto &l : layers) { uint n = l.second->units.size(); infoTrace("Layer {}: {} units\n", l.second->id, n); tot += n; } infoTrace("Total: {} units\n", tot); tot = 0; for (auto &t : tracts) { uint n = t.second->connections.size(); infoTrace("Tract {}: {} connections\n", t.second->id, n); tot += n; } infoTrace("Total: {} connections\n", tot); } string NsSystem::toStr(uint iLvl, const string &iStr) const { string ret = Util::repeatStr(iStr, iLvl) + "NsSystem:"; for (auto &l : layers) { ret += "\n" + l.second->toStr(iLvl + 1, iStr); } for (auto &t : tracts) { ret += "\n" + t.second->toStr(iLvl + 1, iStr); } return ret; }