#include <vector> using std::vector; #include <getopt.h> #include "Util.hh" #include "Sched.hh" #include "NsSystem.hh" #include "NsTract.hh" #include "NsLayer.hh" static NsSystem *nsSystem; static uint stopTime; // hours static uint numBackgroundPatterns; /** * Print the system size (number of units and connections) and the system * state if the debug tags "psize" and "psys" are set, respectively. */ static void printSystem() { if (TTRACE_DEBUG_IS_ON("psize")) { nsSystem->printSize(); } TTRACE_DEBUG("psys", "\n------- System ------\n{}\n--------------------\n", nsSystem->toStr(0, " ")); } /** * Build the system */ static void buildSystem() { // Create the layers (units) nsSystem->addLayer(hpcLayerId, hpcLayerTypeId); nsSystem->addLayer(accLayerId, ncLayerTypeId); nsSystem->addLayer(sc0LayerId, ncLayerTypeId); nsSystem->addLayer(sc1LayerId, ncLayerTypeId); // Create the tracts (connections) nsSystem->addBiTract(hpcLayerId, accLayerId, hpcTractTypeId); nsSystem->addBiTract(hpcLayerId, sc0LayerId, hpcTractTypeId); nsSystem->addBiTract(hpcLayerId, sc1LayerId, hpcTractTypeId); nsSystem->addBiTract(accLayerId, sc0LayerId, ncTractTypeId); nsSystem->addBiTract(accLayerId, sc1LayerId, ncTractTypeId); } /** * Convert time in days::hours format to hours */ static uint dhToH(string dh) { string errMsg; uint days = 0; uint hours = 0; vector<string> tokens = Util::tokenize(dh, ":", errMsg); if(tokens.size() == 1) { days = Util::strToUint(tokens[0], errMsg); } else if (tokens.size() == 2) { days = Util::strToUint(tokens[0], errMsg); hours = Util::strToUint(tokens[1], errMsg); } else { TRACE_FATAL("Bad days::hours string: {}", dh); } ABORT_IF(!errMsg.empty(), errMsg.c_str()); return 24 * days + hours; } /** * struct used to schedule a time step change event */ struct TimeStepChangeData { uint timeStep; }; /** * This function is called from the event scheduler. */ static void changeTimeStep( double stime, double now, TimeStepChangeData *data) { TRACE_INFO("Changing time step to {}: scheduled time={} now={}", data->timeStep, stime, now); timeStep = data->timeStep; nsSystem->calcRates(); delete data; } /** * struct used to schedule a reactivate event */ struct ReactivateData { }; /** * Reactivate * This function is called from the event scheduler. * @param stime Scheduled time for this event * @param now Current time * @param data Reactivation data */ static void reactivate( double stime, double now, ReactivateData *data) { TRACE_INFO("Reactivating: scheduled time={} now={}", stime, now); nsSystem->reactivate(); delete data; } /** * struct used to schedule a freezing/unfreezing event */ struct FreezeData { FreezeData(const string &layerId, bool state) : layerId(layerId), state(state) {} const string &layerId; bool state; }; /** * Freeze/unfreeze a layer * This function is called from the event scheduler. * @param stime Scheduled time for this event * @param now Current time * @param data Freeze data */ static void setFrozen( double stime, double now, FreezeData *data) { TRACE_INFO("{} {}: scheduled time={} now={}", data->state ? "Freezing" : "Unfreezing", data->layerId, stime, now); nsSystem->setFrozen(data->layerId, data->state); delete data; } /** * struct used to schedule a lesioning event */ struct LesionData { LesionData(const string &layerId) : layerId(layerId) {} const string &layerId; }; /** * Lesion a layer * This function is called from the event scheduler. * @param stime Scheduled time for this event * @param now Current time * @param data Lesion data */ static void lesion( double stime, double now, LesionData *data) { TRACE_INFO("Lesioning {}: scheduled time={} now={}", data->layerId, stime, now); nsSystem->lesion(data->layerId); delete data; } /** * struct used to schedule Psi on/off event */ struct PsiData { string layerId; bool state; PsiData(string layerId, bool state) : layerId(layerId), state(state) {} }; /** * Toggle PSI on or off in the specified layer * This function is called from the event scheduler. * @param stime Scheduled time for this event * @param now Current time * @param data PSI event data */ static void togglePsi( double stime, double now, PsiData *data) { TRACE_INFO("{} PSI in {}: scheduled time={} now={}", (data->state ? "Starting" : "Stopping"), data->layerId, stime, now); nsSystem->togglePsi(data->layerId, data->state); delete data; } /** * Schedule events to toggle PSI on or off in a specified layer * @param layerId Layer ID * @param psiTimes Vector of times at which to toggle PSI on or off */ void schedulePsiEvents(const string &layerId, const vector<string> psiTimes) { for (uint i = 0; i < psiTimes.size(); i++) { PsiData *pd = new PsiData(layerId, Util::isEven(i)); Sched::scheduleEvent( dhToH(psiTimes[i]), (Sched::VoidPtrCallback) togglePsi, pd); } } /** * Schedule events */ static void scheduleEvents() { // Schedule timeStep changes // vector<string> timeStepChanges = props.getStringVector("timeStepChanges", vector<string>()); ABORT_IF(Util::isOdd(timeStepChanges.size()), "timeStepChanges must have even number of elements"); for (uint i = 0; i < timeStepChanges.size(); i += 2) { TimeStepChangeData *tscd = new TimeStepChangeData(); tscd->timeStep = strtoul(timeStepChanges[i + 1].c_str(), NULL, 10); Sched::scheduleEvent( dhToH(timeStepChanges[i]), (Sched::VoidPtrCallback) changeTimeStep, tscd); } // schedule reactivations // vector<string> reactivateTimes = props.getStringVector("reactivateTimes", vector<string>()); for (uint i = 0; i < reactivateTimes.size(); i++) { ReactivateData *rd = new ReactivateData(); Sched::scheduleEvent( dhToH(reactivateTimes[i]), (Sched::VoidPtrCallback) reactivate, rd); } // schedule HPC freezing/unfreezing // vector<string> hpcFreezeTimes = props.getStringVector("hpcFreezeTimes", vector<string>()); for (uint i = 0; i < hpcFreezeTimes.size(); i++) { FreezeData *ld = new FreezeData(hpcLayerId, Util::isEven(i)); Sched::scheduleEvent( dhToH(hpcFreezeTimes[i]), (Sched::VoidPtrCallback) setFrozen, ld); } // schedule ACC freezing/unfreezing // vector<string> accFreezeTimes = props.getStringVector("accFreezeTimes", vector<string>()); for (uint i = 0; i < accFreezeTimes.size(); i++) { FreezeData *ld = new FreezeData(accLayerId, Util::isEven(i)); Sched::scheduleEvent( dhToH(accFreezeTimes[i]), (Sched::VoidPtrCallback) setFrozen, ld); } // schedule HPC lesioning // string hpcLesionTime = props.getString("hpcLesionTime", ""); if (!hpcLesionTime.empty()) { LesionData *ld = new LesionData(hpcLayerId); Sched::scheduleEvent( dhToH(hpcLesionTime), (Sched::VoidPtrCallback) lesion, ld); } // schedule ACC lesioning // string accLesionTime = props.getString("accLesionTime", ""); if (!accLesionTime.empty()) { LesionData *ld = new LesionData(accLayerId); Sched::scheduleEvent( dhToH(accLesionTime), (Sched::VoidPtrCallback) lesion, ld); } // Schedule PSI infusions // vector<string> none; // default: no PSI events schedulePsiEvents(hpcLayerId, props.getStringVector("hpcPsiTimes", none)); schedulePsiEvents(accLayerId, props.getStringVector("accPsiTimes", none)); schedulePsiEvents(sc0LayerId, props.getStringVector("sc0PsiTimes", none)); schedulePsiEvents(sc1LayerId, props.getStringVector("sc1PsiTimes", none)); } /** * Create and present a random pattern to all layers * @param id Pattern ID */ static void presentPattern(const string &id) { nsSystem->getLayer(sc0LayerId)->makePattern(id); nsSystem->getLayer(sc0LayerId)->setPattern(id); nsSystem->getLayer(sc1LayerId)->makePattern(id); nsSystem->getLayer(sc1LayerId)->setPattern(id); nsSystem->getLayer(hpcLayerId)->makePattern(id); nsSystem->getLayer(hpcLayerId)->setPattern(id); nsSystem->getLayer(accLayerId)->makePattern(id); nsSystem->getLayer(accLayerId)->setPattern(id); nsSystem->printGrids(fmt::format("Pattern {}", id)); } /** * Execute a time step of simulation */ static void iterate() { Sched::processEvents(simTime); nsSystem->runBackgroundProcesses(); simTime += timeStep; } /** * Test recall by cueing the CS pattern in the SC0 and evaluating * the retrieved pattern in the SC1 layer against the US pattern. */ void test() { nsSystem->test(sc0LayerId, "CS-US", "intact"); bool accWasFrozen = nsSystem->getLayer(accLayerId)->isFrozen; if (!accWasFrozen) { nsSystem->setFrozen(accLayerId, true); } nsSystem->test(sc0LayerId, "CS-US", "acc-frozen"); if (!accWasFrozen) { nsSystem->setFrozen(accLayerId, false); } bool hpcWasFrozen = nsSystem->getLayer(hpcLayerId)->isFrozen; if (!hpcWasFrozen) { nsSystem->setFrozen(hpcLayerId, true); } nsSystem->test(sc0LayerId, "CS-US", "hpc-frozen"); if (!hpcWasFrozen) { nsSystem->setFrozen(hpcLayerId, false); } } /** * Run the simulation */ static void run() { simTime = 0; nsSystem->calcRates(); // Process events scheduled for time=0, if any. // Sched::processEvents(simTime); // Present background pattens if defined // for (uint i = 0; i < numBackgroundPatterns; i++) { presentPattern(fmt::format("dummy-{}", i)); nsSystem->train(); iterate(); } // Present the training (CS-US) pattern // presentPattern("CS-US"); nsSystem->train(); // Print the initial system state // printSystem(); // Print state headers // nsSystem->printStateHdrs(); // Run the simulation, printing out state after every iteration // while (simTime < stopTime) { iterate(); if (TRACE_INFO_IS_ON) nsSystem->printState(); test(); } } // Data structures for commandline processing // OptSpec type abbreviations // const int NONE = Util::OPTARG_NONE; const int INT = Util::OPTARG_INT; const int UINT = Util::OPTARG_UINT; const int DBLE = Util::OPTARG_DBLE; const int STR = Util::OPTARG_STR; bool help = false; const char *traceLevel = "undefined"; const char *traceTags = "undefined"; char *pname; vector<Util::ParseOptSpec> optSpecs = { { "tl", STR, &traceLevel, "traceLevel", "" }, { "tt", STR, &traceTags, "traceTags", "" }, { "help", NONE, &help, "", "" }, }; vector<string>nonFlags = { "[propname=value...] propsFilePath" }; /** * Construct a syntax string for use in an invocation error message */ static const char *syntax() { return parseOptsUsage(pname, optSpecs, true, nonFlags).c_str(); } /** * Main program */ int main(int argc, char *argv[]) { // Initialize the random number generator // Util::initRand(); // Process command line arguments // pname = argv[0]; if (Util::parseOpts(argc, argv, optSpecs) != 0 || help) { Util::usageExit(syntax(), NULL); } // The rest of the arguments must consist of exactly one // propertyFileName and zero or more command line // props of the name=value format. Command-line property // values override those given in the props file. typedef struct { string name; string value; } NameValue; const char *propsFilePath = NULL; std::vector<NameValue> cmdLineProps; while (optind < argc) { TRACE_DEBUG("argv[{}]='{}'\n", optind, argv[optind]); if (strchr(argv[optind], '=') != NULL) { const char *errMsg; std::vector<string> tokens = Util::tokenize(argv[optind], "=", errMsg, "'\""); if (errMsg != NULL) { Util::usageExit(syntax(), errMsg, NULL); } if (tokens.size() != 2) { Util::usageExit(syntax(), NULL); } NameValue prop = { tokens[0], tokens[1] }; cmdLineProps.push_back(prop); } else { if (propsFilePath == NULL) { propsFilePath = argv[optind]; } else { Util::usageExit(syntax(), "Extra arg: {}", argv[optind]); } } optind++; } if (propsFilePath == NULL) { Util::usageExit(syntax(), "No propsFilePath."); } // Set command line props. // // - Processing cmd line props before props file allows props variable // substitution to work as expected, e.g if a = 5 and b = a in the // props file, then cmd line a = 7 will set both to 7. // // - Specifying immutable = true prevents props file values from // overriding cmd line values. // for (uint i = 0; i < cmdLineProps.size(); i++) { props.setString(cmdLineProps[i].name, cmdLineProps[i].value, true); } // Load properties from the props file // props.readProps(propsFilePath); // Retrieve 'title' property to suppress 'unused property' error // volatile string title = props.getString("title", ""); // Allow traceLevel and and traceTag to be set using tl and tt // properties, unless they've already been set by -tl and -tt flags // (cmdline args override cmdline properties override prop file // if (Util::strEq(traceLevel, "undefined")) { traceLevel = props.getString("tl", "warn").c_str(); } if (Util::strEq(traceTags, "undefined")) { traceTags = props.getString("tt", "").c_str(); } // Now set traceLevel and traceTags // if (!Trace::setTraceLevel(traceLevel)) { Util::usageExit(syntax(), NULL); } string errMsg; for (auto tag : Util::tokenize(traceTags, ",", errMsg)) { Trace::setTraceTag(tag); } // Print out the property value // fmt::print("===================================\n"); fmt::print("{}", props.toString()); fmt::print("===================================\n"); // Create the system // nsSystem = new NsSystem(props); // Initialize the simulation time step to 24h; It may be changed // dynamically during the simulation, as specified by the // 'timeStepChanges' property to allow more fine-grained // simulation during selected intervals. timeStep = 24; stopTime = dhToH(props.getString("stopTime")); numBackgroundPatterns = props.getUint("numBackgroundPatterns"); // Initialize the system and schedule events // buildSystem(); printSystem(); scheduleEvents(); props.reportUnused(true); // Run the simulation // run(); }