diff --git a/examples/pfcmaint/README.md b/examples/pfcmaint/README.md
new file mode 100644
index 000000000..d65937838
--- /dev/null
+++ b/examples/pfcmaint/README.md
@@ -0,0 +1,7 @@
+# VSPatch
+
+This is a test for representing graded numerical values in terms of the overall population activity, which is essential for the striatum neurons used in the [pcore](../../PCORE_BG.md) and [pvlv](../../PVLV.md) models. The specific test case here is the `VSPatch` neurons that predict and discount graded rewards.
+
+The model only has a `VSPatch` layer which is trained to predict the amount of reward for N different "conditions", each of which has T time steps of neural activity, culminating in the reward. It needs to _not_ respond for the first T-1 time steps, and then accurately predict the graded reward value at T.
+
+
diff --git a/examples/pfcmaint/config.go b/examples/pfcmaint/config.go
new file mode 100644
index 000000000..3e1461680
--- /dev/null
+++ b/examples/pfcmaint/config.go
@@ -0,0 +1,131 @@
+// Copyright (c) 2023, The Emergent Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// EnvConfig has config params for environment
+// note: only adding fields for key Env params that matter for both Network and Env
+// other params are set via the Env map data mechanism.
+type EnvConfig struct {
+
+ // env parameters -- can set any field/subfield on Env struct, using standard TOML formatting
+ Env map[string]any
+}
+
+// ParamConfig has config parameters related to sim params
+type ParamConfig struct {
+
+ // number of units per dimension in the PFC
+ NUnits int `default:"7"`
+
+ // If true, perform automated parameter tweaking for parameters marked Hypers Tweak = log,incr, or [vals]
+ Tweak bool
+
+ // for Tweak, if true, first run a baseline with current default params
+ Baseline bool
+
+ // for Tweak, if true, only print what would be done, don't run
+ DryRun bool
+
+ // network parameters
+ Network map[string]any
+
+ // Extra Param Sheet name(s) to use (space separated if multiple) -- must be valid name as listed in compiled-in params or loaded params
+ Sheet string
+
+ // extra tag to add to file names and logs saved from this run
+ Tag string
+
+ // user note -- describe the run params etc -- like a git commit message for the run
+ Note string
+
+ // Name of the JSON file to input saved parameters from.
+ File string `nest:"+"`
+
+ // Save a snapshot of all current param and config settings in a directory named params_ (or _good if Good is true), then quit -- useful for comparing to later changes and seeing multiple views of current params
+ SaveAll bool `nest:"+"`
+
+ // for SaveAll, save to params_good for a known good params state. This can be done prior to making a new release after all tests are passing -- add results to git to provide a full diff record of all params over time.
+ Good bool `nest:"+"`
+}
+
+// RunConfig has config parameters related to running the sim
+type RunConfig struct {
+
+ // use the GPU for computation -- generally faster even for small models if NData ~16
+ GPU bool `default:"false"`
+
+ // number of data-parallel items to process in parallel per trial -- works (and is significantly faster) for both CPU and GPU. Results in an effective mini-batch of learning.
+ NData int `default:"1" min:"1"`
+
+ // number of parallel threads for CPU computation -- 0 = use default
+ NThreads int `default:"0"`
+
+ // starting run number -- determines the random seed -- runs counts from there -- can do all runs in parallel by launching separate jobs with each run, runs = 1
+ Run int `default:"0"`
+
+ // total number of runs to do when running Train
+ NRuns int `default:"1" min:"1"`
+
+ // total number of epochs per run
+ NEpochs int `default:"30"`
+
+ // total number of trials per epoch. Should be an even multiple of NData.
+ NTrials int `default:"128"`
+}
+
+// LogConfig has config parameters related to logging data
+type LogConfig struct {
+
+ // if true, save final weights after each run
+ SaveWts bool
+
+ // if true, save train epoch log to file, as .epc.tsv typically
+ Epoch bool `default:"true" nest:"+"`
+
+ // if true, save run log to file, as .run.tsv typically
+ Run bool `default:"true" nest:"+"`
+
+ // if true, save train trial log to file, as .trl.tsv typically. May be large.
+ Trial bool `default:"false" nest:"+"`
+
+ // if true, save testing epoch log to file, as .tst_epc.tsv typically. In general it is better to copy testing items over to the training epoch log and record there.
+ TestEpoch bool `default:"false" nest:"+"`
+
+ // if true, save testing trial log to file, as .tst_trl.tsv typically. May be large.
+ TestTrial bool `default:"false" nest:"+"`
+
+ // if true, save network activation etc data from testing trials, for later viewing in netview
+ NetData bool
+
+ // activates testing mode -- records detailed data for Go CI tests (not the same as running test mode on network, via Looper)
+ Testing bool
+}
+
+// Config is a standard Sim config -- use as a starting point.
+type Config struct {
+
+ // specify include files here, and after configuration, it contains list of include files added
+ Includes []string
+
+ // open the GUI -- does not automatically run -- if false, then runs automatically and quits
+ GUI bool `default:"true"`
+
+ // log debugging information
+ Debug bool
+
+ // environment configuration options
+ Env EnvConfig `view:"add-fields"`
+
+ // parameter related configuration options
+ Params ParamConfig `view:"add-fields"`
+
+ // sim running related configuration options
+ Run RunConfig `view:"add-fields"`
+
+ // data logging related configuration options
+ Log LogConfig `view:"add-fields"`
+}
+
+func (cfg *Config) IncludesPtr() *[]string { return &cfg.Includes }
diff --git a/examples/pfcmaint/params.go b/examples/pfcmaint/params.go
new file mode 100644
index 000000000..23e2debb6
--- /dev/null
+++ b/examples/pfcmaint/params.go
@@ -0,0 +1,52 @@
+// Copyright (c) 2022, The Emergent Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "github.com/emer/emergent/v2/netparams"
+ "github.com/emer/emergent/v2/params"
+)
+
+// ParamSets is the default set of parameters -- Base is always applied,
+// and others can be optionally selected to apply on top of that
+var ParamSets = netparams.Sets{
+ "Base": {
+ {Sel: "Layer", Desc: "",
+ Params: params.Params{
+ "Layer.Acts.Clamp.Ge": "1.0", // 1.5 is def, was 0.6 (too low)
+ // "Layer.Inhib.ActAvg.Nominal": "0.2",
+ }},
+ {Sel: ".Time", Desc: "",
+ Params: params.Params{
+ "Layer.Inhib.ActAvg.Nominal": "0.05",
+ }},
+ {Sel: ".PFCPrjn", Desc: "",
+ Params: params.Params{
+ "Prjn.PrjnScale.Abs": "2.0",
+ }},
+ {Sel: "#GPiToPFCThal", Desc: "",
+ Params: params.Params{
+ "Prjn.PrjnScale.Abs": "4.0",
+ }},
+ {Sel: ".PTMaintLayer", Desc: "time integration params",
+ Params: params.Params{
+ "Layer.Acts.Dend.ModGain": "1.5",
+ "Layer.Inhib.Layer.Gi": "2.6",
+ "Layer.Inhib.Pool.Gi": "3.6",
+ }},
+ {Sel: ".BGThalLayer", Desc: "",
+ Params: params.Params{
+ "Layer.Learn.NeuroMod.AChDisInhib": "0",
+ }},
+ {Sel: ".PTSelfMaint", Desc: "",
+ Params: params.Params{
+ "Prjn.PrjnScale.Abs": "5.0", // note: too much! need a better strat
+ }},
+ {Sel: ".SuperToThal", Desc: "",
+ Params: params.Params{
+ "Prjn.PrjnScale.Abs": "4.0", // 4 > 2 for gating sooner
+ }},
+ },
+}
diff --git a/examples/pfcmaint/paramtweak.go b/examples/pfcmaint/paramtweak.go
new file mode 100644
index 000000000..b9a748499
--- /dev/null
+++ b/examples/pfcmaint/paramtweak.go
@@ -0,0 +1,91 @@
+// Copyright (c) 2019, The Emergent Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "time"
+
+ "cogentcore.org/core/laser"
+ "github.com/emer/emergent/v2/elog"
+ "github.com/emer/emergent/v2/emer"
+ "github.com/emer/emergent/v2/etime"
+ "github.com/emer/emergent/v2/params"
+ "github.com/emer/empi/v2/mpi"
+)
+
+func (ss *Sim) RunParamTweak() {
+ ss.Config.Run.NRuns = 25 // 10
+ ss.Config.Log.Run = true
+ ss.Config.Log.Epoch = true
+
+ tstamp := time.Now().Format("2006-01-02-15-04")
+
+ ctag := ss.Params.Tag
+ ss.Params.Tag = tstamp
+ if ctag != "" {
+ ss.Params.Tag += "_" + ctag
+ }
+ runName := ss.Params.RunName(ss.Config.Run.Run)
+ ss.Stats.SetString("RunName", runName) // used for naming logs, stats, etc
+ netName := ss.Net.Name()
+
+ if !ss.Config.Params.DryRun {
+ elog.SetLogFile(&ss.Logs, ss.Config.Log.Epoch, etime.Train, etime.Epoch, "epc", netName, runName)
+ elog.SetLogFile(&ss.Logs, ss.Config.Log.Run, etime.Train, etime.Run, "run", netName, runName)
+ elog.SetLogFile(&ss.Logs, true, etime.Train, etime.Expt, "expt", netName, runName)
+ }
+
+ ss.Init()
+
+ srch := params.TweaksFromHypers(ss.Params.NetHypers)
+ if len(srch) == 0 {
+ fmt.Println("no tweak items to search!")
+ return
+ }
+
+ if ss.Config.Params.DryRun {
+ fmt.Println("Searching:", laser.StringJSON(srch))
+ }
+
+ ss.Loops.GetLoop(etime.Train, etime.Run).Counter.SetCurMaxPlusN(ss.Config.Run.Run, ss.Config.Run.NRuns)
+ if ss.Config.Run.GPU {
+ ss.Net.ConfigGPUnoGUI(&ss.Context)
+ }
+ mpi.Printf("Set NThreads to: %d\n", ss.Net.NThreads)
+
+ if !ss.Config.Params.DryRun && ss.Config.Params.Baseline {
+ fmt.Println("Running baseline")
+ ss.Loops.Run(etime.Train)
+ ss.Init() // start fresh next time
+ }
+
+ for _, twk := range srch {
+ sv0 := twk.Search[0]
+ for i, val := range sv0.Values {
+ tag := fmt.Sprintf("%s_%s_%g", twk.Sel.Sel, twk.Param, val)
+ for _, sv := range twk.Search {
+ val := sv.Values[i] // should be the same
+ emer.SetFloatParam(ss.Net, sv.Name, sv.Type, sv.Path, val)
+ }
+ ss.Params.Tag = tag
+ runName := ss.Params.RunName(ss.Config.Run.Run)
+ ss.Stats.SetString("RunName", runName) // used for naming logs, stats, etc
+ fmt.Println("Running:", tag)
+ if !ss.Config.Params.DryRun {
+ ss.Net.UpdateParams()
+ ss.Net.InitGScale(&ss.Net.Ctx)
+ ss.Net.GPU.SyncParamsToGPU() // critical!
+ ss.Loops.Run(etime.Train)
+ ss.Init() // start fresh next time -- param will be applied on top if this
+ }
+ }
+ for _, sv := range twk.Search {
+ emer.SetFloatParam(ss.Net, sv.Name, sv.Type, sv.Path, sv.Start) // restore
+ }
+ }
+
+ ss.Net.GPU.Destroy() // safe even if no GPU
+}
diff --git a/examples/pfcmaint/pfcmaint.go b/examples/pfcmaint/pfcmaint.go
new file mode 100644
index 000000000..690f34862
--- /dev/null
+++ b/examples/pfcmaint/pfcmaint.go
@@ -0,0 +1,596 @@
+// Copyright (c) 2019, The Emergent Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+pfcmaint: This project tests prefrontal cortex (PFC) active maintenance mechanisms supported by the pyramidal tract (PT) neurons, in the PTMaint layer type.
+*/
+package main
+
+//go:generate core generate -add-types
+
+import (
+ "os"
+
+ "cogentcore.org/core/gi"
+ "cogentcore.org/core/icons"
+ "cogentcore.org/core/mat32"
+ "github.com/emer/axon/v2/axon"
+ "github.com/emer/emergent/v2/ecmd"
+ "github.com/emer/emergent/v2/econfig"
+ "github.com/emer/emergent/v2/egui"
+ "github.com/emer/emergent/v2/elog"
+ "github.com/emer/emergent/v2/emer"
+ "github.com/emer/emergent/v2/env"
+ "github.com/emer/emergent/v2/erand"
+ "github.com/emer/emergent/v2/estats"
+ "github.com/emer/emergent/v2/etime"
+ "github.com/emer/emergent/v2/looper"
+ "github.com/emer/emergent/v2/netview"
+ "github.com/emer/emergent/v2/params"
+ "github.com/emer/emergent/v2/prjn"
+ "github.com/emer/empi/v2/mpi"
+ "github.com/emer/etable/v2/eplot"
+ "github.com/emer/etable/v2/etable"
+)
+
+func main() {
+ sim := &Sim{}
+ sim.New()
+ sim.ConfigAll()
+ if sim.Config.GUI {
+ sim.RunGUI()
+ } else if sim.Config.Params.Tweak {
+ sim.RunParamTweak()
+ } else {
+ sim.RunNoGUI()
+ }
+}
+
+// see params.go for network params, config.go for Config
+
+// Sim encapsulates the entire simulation model, and we define all the
+// functionality as methods on this struct. This structure keeps all relevant
+// state information organized and available without having to pass everything around
+// as arguments to methods, and provides the core GUI interface (note the view tags
+// for the fields which provide hints to how things should be displayed).
+type Sim struct {
+
+ // simulation configuration parameters -- set by .toml config file and / or args
+ Config Config
+
+ // the network -- click to view / edit parameters for layers, prjns, etc
+ Net *axon.Network `view:"no-inline"`
+
+ // all parameter management
+ Params emer.NetParams `view:"inline"`
+
+ // contains looper control loops for running sim
+ Loops *looper.Manager `view:"no-inline"`
+
+ // contains computed statistic values
+ Stats estats.Stats
+
+ // Contains all the logs and information about the logs.'
+ Logs elog.Logs
+
+ // Environments
+ Envs env.Envs `view:"no-inline"`
+
+ // axon timing parameters and state
+ Context axon.Context
+
+ // netview update parameters
+ ViewUpdt netview.ViewUpdt `view:"inline"`
+
+ // manages all the gui elements
+ GUI egui.GUI `view:"-"`
+
+ // a list of random seeds to use for each run
+ RndSeeds erand.Seeds `view:"-"`
+}
+
+// New creates new blank elements and initializes defaults
+func (ss *Sim) New() {
+ ss.Net = &axon.Network{}
+ econfig.Config(&ss.Config, "config.toml")
+ ss.Params.Config(ParamSets, ss.Config.Params.Sheet, ss.Config.Params.Tag, ss.Net)
+ ss.Stats.Init()
+ ss.RndSeeds.Init(100) // max 100 runs
+ ss.InitRndSeed(0)
+ ss.Context.Defaults()
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+// Configs
+
+// ConfigAll configures all the elements using the standard functions
+func (ss *Sim) ConfigAll() {
+ ss.ConfigEnv()
+ ss.ConfigNet(ss.Net)
+ ss.ConfigLogs()
+ ss.ConfigLoops()
+ if ss.Config.Params.SaveAll {
+ ss.Config.Params.SaveAll = false
+ ss.Net.SaveParamsSnapshot(&ss.Params.Params, &ss.Config, ss.Config.Params.Good)
+ os.Exit(0)
+ }
+}
+
+func (ss *Sim) ConfigEnv() {
+ // Can be called multiple times -- don't re-create
+ newEnv := (len(ss.Envs) == 0)
+
+ for di := 0; di < ss.Config.Run.NData; di++ {
+ var trn, tst *PFCMaintEnv
+ if newEnv {
+ trn = &PFCMaintEnv{}
+ tst = &PFCMaintEnv{}
+ } else {
+ trn = ss.Envs.ByModeDi(etime.Train, di).(*PFCMaintEnv)
+ tst = ss.Envs.ByModeDi(etime.Test, di).(*PFCMaintEnv)
+ }
+
+ // note: names must be standard here!
+ trn.Nm = env.ModeDi(etime.Train, di)
+ trn.Defaults()
+ if ss.Config.Env.Env != nil {
+ params.ApplyMap(trn, ss.Config.Env.Env, ss.Config.Debug)
+ }
+ trn.Config(etime.Train, 73+int64(di)*73)
+ trn.Validate()
+
+ tst.Nm = env.ModeDi(etime.Test, di)
+ tst.Defaults()
+ if ss.Config.Env.Env != nil {
+ params.ApplyMap(tst, ss.Config.Env.Env, ss.Config.Debug)
+ }
+ tst.Config(etime.Test, 181+int64(di)*181)
+ tst.Validate()
+
+ trn.Init(0)
+ tst.Init(0)
+
+ // note: names must be in place when adding
+ ss.Envs.Add(trn, tst)
+ }
+}
+
+func (ss *Sim) ConfigNet(net *axon.Network) {
+ ctx := &ss.Context
+ ev := ss.Envs.ByModeDi(etime.Train, 0).(*PFCMaintEnv)
+
+ net.InitName(net, "PFCMaint")
+ net.SetMaxData(ctx, ss.Config.Run.NData)
+ net.SetRndSeed(ss.RndSeeds[0]) // init new separate random seed, using run = 0
+
+ space := float32(2)
+ full := prjn.NewFull()
+
+ nun := ss.Config.Params.NUnits
+ if nun <= 0 {
+ nun = 7
+ }
+ in, inP := net.AddInputPulv2D("Item", ev.NUnitsY, ev.NUnitsX, space)
+ time, timeP := net.AddInputPulv2D("Time", ev.NUnitsY, ev.NTrials, space)
+ gpi := net.AddLayer2D("GPi", ev.NUnitsY, ev.NUnitsX, axon.InputLayer)
+ pfc, pfcCT, pfcPT, pfcPTp, pfcThal := net.AddPFC2D("PFC", "Thal", nun, nun, true, space)
+ _ = pfcPT
+ _ = pfcThal
+
+ net.ConnectToPFCBack(in, inP, pfc, pfcCT, pfcPTp, full)
+ net.ConnectToPFCBack(time, timeP, pfc, pfcCT, pfcPTp, full)
+
+ net.ConnectLayers(gpi, pfcThal, full, axon.InhibPrjn)
+
+ time.PlaceRightOf(in, space)
+ gpi.PlaceRightOf(time, space)
+ pfcThal.PlaceRightOf(gpi, space)
+ pfc.PlaceAbove(in)
+ pfcPT.PlaceRightOf(pfc, space)
+
+ net.Build(ctx)
+ net.Defaults()
+ net.SetNThreads(ss.Config.Run.NThreads)
+ ss.ApplyParams()
+ net.InitWts(ctx)
+}
+
+func (ss *Sim) ApplyParams() {
+ ss.Params.SetAll() // first hard-coded defaults
+ if ss.Config.Params.Network != nil {
+ ss.Params.SetNetworkMap(ss.Net, ss.Config.Params.Network)
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Init, utils
+
+// Init restarts the run, and initializes everything, including network weights
+// and resets the epoch log table
+func (ss *Sim) Init() {
+ if ss.Config.GUI {
+ ss.Stats.SetString("RunName", ss.Params.RunName(0)) // in case user interactively changes tag
+ }
+ ss.Loops.ResetCounters()
+ ss.InitRndSeed(0)
+ ss.ConfigEnv() // always do -- otherwise env params not reset after run
+ // selected or patterns have been modified etc
+ ss.GUI.StopNow = false
+ ss.ApplyParams()
+ ss.Net.GPU.SyncParamsToGPU()
+ ss.NewRun()
+ ss.ViewUpdt.Update()
+ ss.ViewUpdt.RecordSyns()
+}
+
+// InitRndSeed initializes the random seed based on current training run number
+func (ss *Sim) InitRndSeed(run int) {
+ ss.RndSeeds.Set(run)
+ ss.RndSeeds.Set(run, &ss.Net.Rand)
+}
+
+// ConfigLoops configures the control loops: Training, Testing
+func (ss *Sim) ConfigLoops() {
+ man := looper.NewManager()
+
+ ev := ss.Envs.ByModeDi(etime.Train, 0).(*PFCMaintEnv)
+
+ trls := int(mat32.IntMultipleGE(float32(ss.Config.Run.NTrials), float32(ss.Config.Run.NData)))
+
+ man.AddStack(etime.Train).
+ AddTime(etime.Run, ss.Config.Run.NRuns).
+ AddTime(etime.Epoch, ss.Config.Run.NEpochs).
+ AddTimeIncr(etime.Sequence, trls, ss.Config.Run.NData).
+ AddTime(etime.Trial, ev.NTrials).
+ AddTime(etime.Cycle, 200)
+
+ man.AddStack(etime.Test).
+ AddTime(etime.Epoch, 1).
+ AddTimeIncr(etime.Sequence, trls, ss.Config.Run.NData).
+ AddTime(etime.Trial, ev.NTrials).
+ AddTime(etime.Cycle, 200)
+
+ axon.LooperStdPhases(man, &ss.Context, ss.Net, 150, 199) // plus phase timing
+ axon.LooperSimCycleAndLearn(man, ss.Net, &ss.Context, &ss.ViewUpdt) // std algo code
+
+ for m, _ := range man.Stacks {
+ mode := m // For closures
+ stack := man.Stacks[mode]
+ stack.Loops[etime.Trial].OnStart.Add("ApplyInputs", func() {
+ seq := man.Stacks[mode].Loops[etime.Sequence].Counter.Cur
+ trial := man.Stacks[mode].Loops[etime.Trial].Counter.Cur
+ ss.ApplyInputs(mode, seq, trial)
+ })
+ }
+
+ man.GetLoop(etime.Train, etime.Run).OnStart.Add("NewRun", ss.NewRun)
+
+ /////////////////////////////////////////////
+ // Logging
+
+ man.AddOnEndToAll("Log", ss.Log)
+ axon.LooperResetLogBelow(man, &ss.Logs, etime.Sequence)
+
+ // Save weights to file, to look at later
+ man.GetLoop(etime.Train, etime.Run).OnEnd.Add("SaveWeights", func() {
+ ctrString := ss.Stats.PrintVals([]string{"Run", "Epoch"}, []string{"%03d", "%05d"}, "_")
+ axon.SaveWeightsIfConfigSet(ss.Net, ss.Config.Log.SaveWts, ctrString, ss.Stats.String("RunName"))
+ })
+
+ man.GetLoop(etime.Train, etime.Run).Main.Add("TestAll", func() {
+ ss.Loops.Run(etime.Test)
+ })
+
+ ////////////////////////////////////////////
+ // GUI
+
+ if !ss.Config.GUI {
+ if ss.Config.Log.NetData {
+ man.GetLoop(etime.Test, etime.Trial).Main.Add("NetDataRecord", func() {
+ ss.GUI.NetDataRecord(ss.ViewUpdt.Text)
+ })
+ }
+ } else {
+ axon.LooperUpdtNetView(man, &ss.ViewUpdt, ss.Net, ss.NetViewCounters)
+ axon.LooperUpdtPlots(man, &ss.GUI)
+ }
+
+ if ss.Config.Debug {
+ mpi.Println(man.DocString())
+ }
+ ss.Loops = man
+}
+
+// ApplyInputs applies input patterns from given environment.
+// It is good practice to have this be a separate method with appropriate
+// args so that it can be used for various different contexts
+// (training, testing, etc).
+func (ss *Sim) ApplyInputs(mode etime.Modes, seq, trial int) {
+ ctx := &ss.Context
+ net := ss.Net
+ ss.Net.InitExt(ctx)
+
+ lays := []string{"Item", "Time", "GPi"}
+
+ for di := 0; di < ss.Config.Run.NData; di++ {
+ ev := ss.Envs.ByModeDi(mode, di).(*PFCMaintEnv)
+ ev.Step()
+ for _, lnm := range lays {
+ ly := net.AxonLayerByName(lnm)
+ itsr := ev.State(lnm)
+ ly.ApplyExt(ctx, uint32(di), itsr)
+ }
+ }
+ net.ApplyExts(ctx) // now required for GPU mode
+}
+
+// NewRun intializes a new run of the model, using the TrainEnv.Run counter
+// for the new run value
+func (ss *Sim) NewRun() {
+ ctx := &ss.Context
+ ss.InitRndSeed(ss.Loops.GetLoop(etime.Train, etime.Run).Counter.Cur)
+ for di := 0; di < int(ctx.NetIdxs.NData); di++ {
+ ss.Envs.ByModeDi(etime.Train, di).Init(0)
+ ss.Envs.ByModeDi(etime.Test, di).Init(0)
+ }
+ ctx.Reset()
+ ctx.Mode = etime.Train
+ ss.Net.InitWts(ctx)
+ ss.InitStats()
+ ss.StatCounters(0)
+ ss.Logs.ResetLog(etime.Train, etime.Epoch)
+ ss.Logs.ResetLog(etime.Test, etime.Epoch)
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+// Stats
+
+// InitStats initializes all the statistics.
+// called at start of new run
+func (ss *Sim) InitStats() {
+ // ss.Stats.SetFloat("Rew", 0)
+}
+
+// StatCounters saves current counters to Stats, so they are available for logging etc
+// Also saves a string rep of them for ViewUpdt.Text
+func (ss *Sim) StatCounters(di int) {
+ mode := ss.Context.Mode
+ ss.Loops.Stacks[mode].CtrsToStats(&ss.Stats)
+ // always use training epoch..
+ trnEpc := ss.Loops.Stacks[etime.Train].Loops[etime.Epoch].Counter.Cur
+ ss.Stats.SetInt("Epoch", trnEpc)
+ trl := ss.Stats.Int("Trial")
+ ss.Stats.SetInt("Trial", trl+di)
+ ss.Stats.SetInt("Di", di)
+ ss.Stats.SetInt("Cycle", int(ss.Context.Cycle))
+ ss.Stats.SetString("TrialName", "")
+}
+
+func (ss *Sim) NetViewCounters(tm etime.Times) {
+ if ss.ViewUpdt.View == nil {
+ return
+ }
+ di := ss.ViewUpdt.View.Di
+ if tm == etime.Trial {
+ ss.TrialStats(di) // get trial stats for current di
+ }
+ ss.StatCounters(di)
+ ss.ViewUpdt.Text = ss.Stats.Print([]string{"Run", "Epoch", "Sequence", "Trial", "Di", "TrialName", "Cycle"})
+}
+
+// TrialStats records the trial-level statistics
+func (ss *Sim) TrialStats(di int) {
+ ctx := &ss.Context
+ // diu := uint32(di)
+ ev := ss.Envs.ByModeDi(ctx.Mode, di).(*PFCMaintEnv)
+ ss.Stats.SetInt("Item", ev.Sequence.Cur)
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Logging
+
+func (ss *Sim) ConfigLogs() {
+ ss.Stats.SetString("RunName", ss.Params.RunName(0)) // used for naming logs, stats, etc
+
+ ss.Logs.AddCounterItems(etime.Run, etime.Epoch, etime.Sequence, etime.Trial, etime.Cycle)
+ ss.Logs.AddStatIntNoAggItem(etime.AllModes, etime.Trial, "Di")
+ ss.Logs.AddStatStringItem(etime.AllModes, etime.AllTimes, "RunName")
+ ss.Logs.AddStatStringItem(etime.AllModes, etime.Trial, "TrialName")
+ ss.Logs.AddStatStringItem(etime.AllModes, etime.Sequence, "TrialName")
+ ss.Logs.AddStatStringItem(etime.Test, etime.Sequence, "TrialName")
+
+ ss.Logs.AddPerTrlMSec("PerTrlMSec", etime.Run, etime.Epoch, etime.Sequence)
+
+ // axon.LogAddDiagnosticItems(&ss.Logs, ss.Net, etime.Epoch, etime.Trial)
+
+ // ss.Logs.PlotItems("Rew", "RewPred", "RewPred_NR", "DA", "DA_NR")
+
+ ss.Logs.CreateTables()
+
+ ss.Logs.SetContext(&ss.Stats, ss.Net)
+ // don't plot certain combinations we don't use
+ ss.Logs.NoPlot(etime.Train, etime.Cycle)
+ ss.Logs.NoPlot(etime.Train, etime.Trial)
+ ss.Logs.NoPlot(etime.Test, etime.Cycle)
+ ss.Logs.NoPlot(etime.Test, etime.Trial)
+ ss.Logs.NoPlot(etime.Test, etime.Run)
+ // note: Analyze not plotted by default
+ ss.Logs.SetMeta(etime.Train, etime.Run, "LegendCol", "RunName")
+ // ss.Logs.SetMeta(etime.Test, etime.Cycle, "LegendCol", "RunName")
+}
+
+// Log is the main logging function, handles special things for different scopes
+func (ss *Sim) Log(mode etime.Modes, time etime.Times) {
+ if mode != etime.Analyze {
+ ss.Context.Mode = mode // Also set specifically in a Loop callback.
+ }
+
+ dt := ss.Logs.Table(mode, time)
+ if dt == nil {
+ return
+ }
+ row := dt.Rows
+
+ switch {
+ case time == etime.Cycle:
+ return
+ // row = ss.Stats.Int("Cycle")
+ case time == etime.Trial:
+ return // skip
+ case time == etime.Sequence:
+ for di := 0; di < ss.Config.Run.NData; di++ {
+ ss.TrialStats(di)
+ ss.StatCounters(di)
+ ss.Logs.LogRowDi(mode, time, row, di)
+ }
+ return // don't do reg
+ }
+
+ ss.Logs.LogRow(mode, time, row) // also logs to file, etc
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+// Gui
+
+// ConfigGUI configures the Cogent Core GUI interface for this simulation.
+func (ss *Sim) ConfigGUI() {
+ title := "PFCMaint"
+ ss.GUI.MakeBody(ss, "pfcmaint", title, `This project tests prefrontal cortex (PFC) active maintenance mechanisms supported by the pyramidal tract (PT) neurons, in the PTMaint layer type. See axon on GitHub.
`)
+ ss.GUI.CycleUpdateInterval = 20
+
+ nv := ss.GUI.AddNetView("NetView")
+ nv.Params.MaxRecs = 300
+ nv.Params.LayNmSize = 0.03
+ nv.SetNet(ss.Net)
+ ss.ViewUpdt.Config(nv, etime.Phase, etime.Phase)
+
+ nv.SceneXYZ().Camera.Pose.Pos.Set(0, 2.15, 2.45)
+ nv.SceneXYZ().Camera.LookAt(mat32.V3(0, 0, 0), mat32.V3(0, 1, 0))
+
+ ss.GUI.ViewUpdt = &ss.ViewUpdt
+
+ ss.GUI.AddPlots(title, &ss.Logs)
+
+ tststnm := "TestTrialStats"
+ tstst := ss.Logs.MiscTable(tststnm)
+ plt := eplot.NewSubPlot(ss.GUI.Tabs.NewTab(tststnm + " Plot"))
+ ss.GUI.Plots[etime.ScopeKey(tststnm)] = plt
+ plt.Params.Title = tststnm
+ plt.Params.XAxisCol = "Trial"
+ plt.SetTable(tstst)
+
+ ss.GUI.Body.AddAppBar(func(tb *gi.Toolbar) {
+ ss.GUI.AddToolbarItem(tb, egui.ToolbarItem{Label: "Init", Icon: icons.Update,
+ Tooltip: "Initialize everything including network weights, and start over. Also applies current params.",
+ Active: egui.ActiveStopped,
+ Func: func() {
+ ss.Init()
+ ss.GUI.UpdateWindow()
+ },
+ })
+
+ ss.GUI.AddLooperCtrl(tb, ss.Loops, []etime.Modes{etime.Train, etime.Test})
+ ss.GUI.AddToolbarItem(tb, egui.ToolbarItem{Label: "TestInit", Icon: icons.Update,
+ Tooltip: "reinitialize the testing control so it re-runs.",
+ Active: egui.ActiveStopped,
+ Func: func() {
+ ss.Loops.ResetCountersByMode(etime.Test)
+ ss.GUI.UpdateWindow()
+ },
+ })
+
+ ////////////////////////////////////////////////
+ gi.NewSeparator(tb)
+ ss.GUI.AddToolbarItem(tb, egui.ToolbarItem{Label: "Reset RunLog",
+ Icon: icons.Reset,
+ Tooltip: "Reset the accumulated log of all Runs, which are tagged with the ParamSet used",
+ Active: egui.ActiveAlways,
+ Func: func() {
+ ss.Logs.ResetLog(etime.Train, etime.Run)
+ ss.GUI.UpdatePlot(etime.Train, etime.Run)
+ },
+ })
+ ////////////////////////////////////////////////
+ gi.NewSeparator(tb)
+ ss.GUI.AddToolbarItem(tb, egui.ToolbarItem{Label: "New Seed",
+ Icon: icons.Add,
+ Tooltip: "Generate a new initial random seed to get different results. By default, Init re-establishes the same initial seed every time.",
+ Active: egui.ActiveAlways,
+ Func: func() {
+ ss.RndSeeds.NewSeeds()
+ },
+ })
+ ss.GUI.AddToolbarItem(tb, egui.ToolbarItem{Label: "README",
+ Icon: "file-markdown",
+ Tooltip: "Opens your browser on the README file that contains instructions for how to run this model.",
+ Active: egui.ActiveAlways,
+ Func: func() {
+ gi.TheApp.OpenURL("https://github.com/emer/axon/blob/master/examples/pcore/README.md")
+ },
+ })
+ })
+ ss.GUI.FinalizeGUI(false)
+ if ss.Config.Run.GPU {
+ // vgpu.Debug = ss.Config.Debug
+ ss.Net.ConfigGPUwithGUI(&ss.Context) // must happen after gui or no gui
+ gi.TheApp.AddQuitCleanFunc(func() {
+ ss.Net.GPU.Destroy()
+ })
+ }
+}
+
+func (ss *Sim) RunGUI() {
+ ss.Init()
+ ss.ConfigGUI()
+ ss.GUI.Body.RunMainWindow()
+}
+
+func (ss *Sim) RunNoGUI() {
+ if ss.Config.Params.Note != "" {
+ mpi.Printf("Note: %s\n", ss.Config.Params.Note)
+ }
+ if ss.Config.Log.SaveWts {
+ mpi.Printf("Saving final weights per run\n")
+ }
+ runName := ss.Params.RunName(ss.Config.Run.Run)
+ ss.Stats.SetString("RunName", runName) // used for naming logs, stats, etc
+ netName := ss.Net.Name()
+
+ elog.SetLogFile(&ss.Logs, ss.Config.Log.Trial, etime.Train, etime.Trial, "trl", netName, runName)
+ elog.SetLogFile(&ss.Logs, ss.Config.Log.Epoch, etime.Train, etime.Epoch, "epc", netName, runName)
+ elog.SetLogFile(&ss.Logs, ss.Config.Log.Run, etime.Train, etime.Run, "run", netName, runName)
+ elog.SetLogFile(&ss.Logs, ss.Config.Log.TestTrial, etime.Test, etime.Trial, "tst_trl", netName, runName)
+
+ netdata := ss.Config.Log.NetData
+ if netdata {
+ mpi.Printf("Saving NetView data from testing\n")
+ ss.GUI.InitNetData(ss.Net, 200)
+ }
+
+ ss.Init()
+
+ mpi.Printf("Running %d Runs starting at %d\n", ss.Config.Run.NRuns, ss.Config.Run.Run)
+ ss.Loops.GetLoop(etime.Train, etime.Run).Counter.SetCurMaxPlusN(ss.Config.Run.Run, ss.Config.Run.NRuns)
+
+ if ss.Config.Run.GPU {
+ ss.Net.ConfigGPUnoGUI(&ss.Context)
+ }
+ mpi.Printf("Set NThreads to: %d\n", ss.Net.NThreads)
+
+ ss.Loops.Run(etime.Train)
+
+ ss.Logs.CloseLogFiles()
+
+ if netdata {
+ ss.GUI.SaveNetData(ss.Stats.String("RunName"))
+ }
+
+ if ss.Config.Log.TestEpoch {
+ dt := ss.Logs.MiscTable("TestTrialStats")
+ fnm := ecmd.LogFilename("tst_epc", netName, runName)
+ dt.SaveCSV(gi.Filename(fnm), etable.Tab, etable.Headers)
+ }
+
+ ss.Net.GPU.Destroy() // safe even if no GPU
+}
diff --git a/examples/pfcmaint/pfcmaint_env.go b/examples/pfcmaint/pfcmaint_env.go
new file mode 100644
index 000000000..dfcb6c13f
--- /dev/null
+++ b/examples/pfcmaint/pfcmaint_env.go
@@ -0,0 +1,174 @@
+// Copyright (c) 2022, The Emergent Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+
+ "github.com/emer/emergent/v2/env"
+ "github.com/emer/emergent/v2/erand"
+ "github.com/emer/emergent/v2/etime"
+ "github.com/emer/emergent/v2/patgen"
+ "github.com/emer/etable/v2/etable"
+ "github.com/emer/etable/v2/etensor"
+)
+
+// PFCMaintEnv implements a simple store-maintain-recall active maintenance task
+type PFCMaintEnv struct {
+
+ // name of environment -- Train or Test
+ Nm string
+
+ // training or testing env?
+ Mode etime.Modes
+
+ // sequence counter is for the outer loop of maint per item
+ Sequence env.Ctr `view:"inline"`
+
+ // trial counter is for the maint step within item
+ Trial env.Ctr `view:"inline"`
+
+ // number of different items to maintain
+ NItems int
+
+ // number of trials to maintain
+ NTrials int
+
+ // state rep, number of units, Y
+ NUnitsY int `view:"-"`
+
+ // state rep, number of units, X
+ NUnitsX int `view:"-"`
+
+ // total number of units
+ NUnits int `view:"-"`
+
+ // item patterns
+ Pats etable.Table
+
+ // pattern vocab
+ PatVocab patgen.Vocab
+
+ // random number generator for the env -- all random calls must use this
+ Rand erand.SysRand `view:"-"`
+
+ // random seed
+ RndSeed int64 `edit:"-"`
+
+ // named states: ACCPos, ACCNeg
+ States map[string]*etensor.Float32
+}
+
+func (ev *PFCMaintEnv) Name() string {
+ return ev.Nm
+}
+
+func (ev *PFCMaintEnv) Desc() string {
+ return "PFCMaintEnv"
+}
+
+func (ev *PFCMaintEnv) Defaults() {
+ ev.NItems = 4
+ ev.NTrials = 10
+ ev.NUnitsY = 5
+ ev.NUnitsX = 5
+ ev.NUnits = ev.NUnitsY * ev.NUnitsX
+}
+
+// Config configures the world
+func (ev *PFCMaintEnv) Config(mode etime.Modes, rndseed int64) {
+ ev.Mode = mode
+ ev.RndSeed = rndseed
+ ev.Rand.NewRand(ev.RndSeed)
+ ev.States = make(map[string]*etensor.Float32)
+ ev.States["Item"] = etensor.NewFloat32([]int{ev.NUnitsY, ev.NUnitsX}, nil, []string{"Y", "X"})
+ ev.States["Time"] = etensor.NewFloat32([]int{ev.NUnitsY, ev.NTrials}, nil, []string{"Y", "Time"})
+ ev.States["GPi"] = etensor.NewFloat32([]int{ev.NUnitsY, ev.NUnitsX}, nil, []string{"Y", "X"})
+ ev.Sequence.Max = ev.NItems
+ ev.Trial.Max = ev.NTrials
+ ev.ConfigPats()
+}
+
+func (ev *PFCMaintEnv) ConfigPats() {
+ ev.PatVocab = patgen.Vocab{}
+
+ pctAct := float32(0.2)
+ minDiff := float32(0.5)
+ patgen.AddVocabPermutedBinary(ev.PatVocab, "Pats", ev.NItems, ev.NUnitsY, ev.NUnitsX, pctAct, minDiff)
+
+ npats := ev.NItems * ev.NTrials
+ sch := etable.Schema{
+ {"Name", etensor.STRING, nil, nil},
+ {"Item", etensor.FLOAT32, []int{ev.NUnitsY, ev.NUnitsX}, []string{"Y", "X"}},
+ }
+ ev.Pats.SetFromSchema(sch, npats)
+
+ tsr := ev.PatVocab["Pats"]
+ for i := 0; i < ev.NItems; i++ {
+ itmNm := fmt.Sprintf("item%d", i)
+ ev.Pats.SetCellTensor("Item", i, tsr.SubSpace([]int{i}))
+ ev.Pats.SetCellString("Name", i, itmNm)
+ }
+}
+
+func (ev *PFCMaintEnv) Validate() error {
+ return nil
+}
+
+func (ev *PFCMaintEnv) Init(run int) {
+ ev.Sequence.Init()
+ ev.Trial.Init()
+}
+
+func (ev *PFCMaintEnv) Counter(scale env.TimeScales) (cur, prv int, changed bool) {
+ switch scale {
+ case env.Sequence:
+ return ev.Sequence.Query()
+ case env.Trial:
+ return ev.Trial.Query()
+ }
+ return 0, 0, false
+}
+
+func (ev *PFCMaintEnv) State(el string) etensor.Tensor {
+ return ev.States[el]
+}
+
+// RenderLocalist renders localist * NUnitsPer
+func (ev *PFCMaintEnv) RenderLocalist(name string, idx int) {
+ av := ev.States[name]
+ av.SetZeros()
+ for yi := 0; yi < ev.NUnitsY; yi++ {
+ av.Set([]int{yi, idx}, 1)
+ }
+}
+
+// RenderState renders the given condition, trial
+func (ev *PFCMaintEnv) RenderState(item, trial int) {
+ st := ev.States["Item"]
+ st.CopyFrom(ev.Pats.CellTensor("Item", item))
+ ev.RenderLocalist("Time", trial)
+ st = ev.States["GPi"]
+ st.CopyFrom(ev.Pats.CellTensor("Item", item))
+ if trial == 0 {
+ st.SetZeros()
+ }
+}
+
+// Step does one step -- must set Trial.Cur first if doing testing
+func (ev *PFCMaintEnv) Step() bool {
+ ev.RenderState(ev.Sequence.Cur, ev.Trial.Cur)
+ ev.Sequence.Same()
+ if ev.Trial.Incr() {
+ ev.Sequence.Incr()
+ }
+ return true
+}
+
+func (ev *PFCMaintEnv) Action(action string, nop etensor.Tensor) {
+}
+
+func (ev *PFCMaintEnv) ComputeDA(rew float32) {
+}
diff --git a/examples/vspatch/config.go b/examples/vspatch/config.go
index be9e412c1..f88eddaee 100644
--- a/examples/vspatch/config.go
+++ b/examples/vspatch/config.go
@@ -15,6 +15,7 @@ type EnvConfig struct {
// ParamConfig has config parameters related to sim params
type ParamConfig struct {
+
// If true, perform automated parameter tweaking for parameters marked Hypers Tweak = log,incr, or [vals]
Tweak bool
diff --git a/examples/vspatch/gtigen.go b/examples/vspatch/gtigen.go
new file mode 100644
index 000000000..233fae442
--- /dev/null
+++ b/examples/vspatch/gtigen.go
@@ -0,0 +1,21 @@
+// Code generated by "core generate -add-types"; DO NOT EDIT.
+
+package main
+
+import (
+ "cogentcore.org/core/gti"
+)
+
+var _ = gti.AddType(>i.Type{Name: "main.EnvConfig", IDName: "env-config", Doc: "EnvConfig has config params for environment\nnote: only adding fields for key Env params that matter for both Network and Env\nother params are set via the Env map data mechanism.", Fields: []gti.Field{{Name: "Env", Doc: "env parameters -- can set any field/subfield on Env struct, using standard TOML formatting"}}})
+
+var _ = gti.AddType(>i.Type{Name: "main.ParamConfig", IDName: "param-config", Doc: "ParamConfig has config parameters related to sim params", Fields: []gti.Field{{Name: "Tweak", Doc: "If true, perform automated parameter tweaking for parameters marked Hypers Tweak = log,incr, or [vals]"}, {Name: "Baseline", Doc: "for Tweak, if true, first run a baseline with current default params"}, {Name: "DryRun", Doc: "for Tweak, if true, only print what would be done, don't run"}, {Name: "Network", Doc: "network parameters"}, {Name: "Sheet", Doc: "Extra Param Sheet name(s) to use (space separated if multiple) -- must be valid name as listed in compiled-in params or loaded params"}, {Name: "Tag", Doc: "extra tag to add to file names and logs saved from this run"}, {Name: "Note", Doc: "user note -- describe the run params etc -- like a git commit message for the run"}, {Name: "File", Doc: "Name of the JSON file to input saved parameters from."}, {Name: "SaveAll", Doc: "Save a snapshot of all current param and config settings in a directory named params_ (or _good if Good is true), then quit -- useful for comparing to later changes and seeing multiple views of current params"}, {Name: "Good", Doc: "for SaveAll, save to params_good for a known good params state. This can be done prior to making a new release after all tests are passing -- add results to git to provide a full diff record of all params over time."}}})
+
+var _ = gti.AddType(>i.Type{Name: "main.RunConfig", IDName: "run-config", Doc: "RunConfig has config parameters related to running the sim", Fields: []gti.Field{{Name: "GPU", Doc: "use the GPU for computation -- generally faster even for small models if NData ~16"}, {Name: "NData", Doc: "number of data-parallel items to process in parallel per trial -- works (and is significantly faster) for both CPU and GPU. Results in an effective mini-batch of learning."}, {Name: "NThreads", Doc: "number of parallel threads for CPU computation -- 0 = use default"}, {Name: "Run", Doc: "starting run number -- determines the random seed -- runs counts from there -- can do all runs in parallel by launching separate jobs with each run, runs = 1"}, {Name: "NRuns", Doc: "total number of runs to do when running Train"}, {Name: "NEpochs", Doc: "total number of epochs per run"}, {Name: "NTrials", Doc: "total number of trials per epoch. Should be an even multiple of NData."}}})
+
+var _ = gti.AddType(>i.Type{Name: "main.LogConfig", IDName: "log-config", Doc: "LogConfig has config parameters related to logging data", Fields: []gti.Field{{Name: "SaveWts", Doc: "if true, save final weights after each run"}, {Name: "Epoch", Doc: "if true, save train epoch log to file, as .epc.tsv typically"}, {Name: "Run", Doc: "if true, save run log to file, as .run.tsv typically"}, {Name: "Trial", Doc: "if true, save train trial log to file, as .trl.tsv typically. May be large."}, {Name: "TestEpoch", Doc: "if true, save testing epoch log to file, as .tst_epc.tsv typically. In general it is better to copy testing items over to the training epoch log and record there."}, {Name: "TestTrial", Doc: "if true, save testing trial log to file, as .tst_trl.tsv typically. May be large."}, {Name: "NetData", Doc: "if true, save network activation etc data from testing trials, for later viewing in netview"}, {Name: "Testing", Doc: "activates testing mode -- records detailed data for Go CI tests (not the same as running test mode on network, via Looper)"}}})
+
+var _ = gti.AddType(>i.Type{Name: "main.Config", IDName: "config", Doc: "Config is a standard Sim config -- use as a starting point.", Fields: []gti.Field{{Name: "Includes", Doc: "specify include files here, and after configuration, it contains list of include files added"}, {Name: "GUI", Doc: "open the GUI -- does not automatically run -- if false, then runs automatically and quits"}, {Name: "Debug", Doc: "log debugging information"}, {Name: "Env", Doc: "environment configuration options"}, {Name: "Params", Doc: "parameter related configuration options"}, {Name: "Run", Doc: "sim running related configuration options"}, {Name: "Log", Doc: "data logging related configuration options"}}})
+
+var _ = gti.AddType(>i.Type{Name: "main.Sim", IDName: "sim", Doc: "Sim encapsulates the entire simulation model, and we define all the\nfunctionality as methods on this struct. This structure keeps all relevant\nstate information organized and available without having to pass everything around\nas arguments to methods, and provides the core GUI interface (note the view tags\nfor the fields which provide hints to how things should be displayed).", Fields: []gti.Field{{Name: "Config", Doc: "simulation configuration parameters -- set by .toml config file and / or args"}, {Name: "Net", Doc: "the network -- click to view / edit parameters for layers, prjns, etc"}, {Name: "Params", Doc: "all parameter management"}, {Name: "Loops", Doc: "contains looper control loops for running sim"}, {Name: "Stats", Doc: "contains computed statistic values"}, {Name: "Logs", Doc: "Contains all the logs and information about the logs.'"}, {Name: "Envs", Doc: "Environments"}, {Name: "Context", Doc: "axon timing parameters and state"}, {Name: "ViewUpdt", Doc: "netview update parameters"}, {Name: "GUI", Doc: "manages all the gui elements"}, {Name: "RndSeeds", Doc: "a list of random seeds to use for each run"}}})
+
+var _ = gti.AddType(>i.Type{Name: "main.VSPatchEnv", IDName: "vs-patch-env", Doc: "VSPatchEnv implements simple Go vs. NoGo input patterns to test BG learning.", Fields: []gti.Field{{Name: "Nm", Doc: "name of environment -- Train or Test"}, {Name: "Mode", Doc: "training or testing env?"}, {Name: "Sequence", Doc: "sequence counter is for the condition"}, {Name: "Trial", Doc: "trial counter is for the step within condition"}, {Name: "Probs", Doc: "if true, reward value is a probability of getting a 1 reward"}, {Name: "NConds", Doc: "number of conditions, each of which can have a different reward value"}, {Name: "NTrials", Doc: "number of trials"}, {Name: "CondVals", Doc: "condition current values"}, {Name: "NUnitsY", Doc: "state rep, number of units, Y"}, {Name: "NUnitsX", Doc: "state rep, number of units, X"}, {Name: "NUnits", Doc: "total number of units"}, {Name: "Pats", Doc: "condition, time-step patterns"}, {Name: "PatVocab", Doc: "pattern vocab"}, {Name: "Rand", Doc: "random number generator for the env -- all random calls must use this"}, {Name: "RndSeed", Doc: "random seed"}, {Name: "States", Doc: "named states: ACCPos, ACCNeg"}, {Name: "Rew", Doc: "current reward value"}, {Name: "RewPred", Doc: "reward prediction from model"}, {Name: "DA", Doc: "DA = reward prediction error: Rew - RewPred"}}})
diff --git a/examples/vspatch/vspatch.go b/examples/vspatch/vspatch.go
index 8bf6fcfbb..1c55cf35e 100644
--- a/examples/vspatch/vspatch.go
+++ b/examples/vspatch/vspatch.go
@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
/*
-pcore_vs: This project simulates the inhibitory dynamics in the STN and GPe leading to integration of Go vs. NoGo signal in the basal ganglia, for the Ventral Striatum (VS) global Go vs. No case.
+vspatch: This project simulates the Ventral Striatum (VS) Patch (striosome) neurons that predict reward to generate an RPE (reward prediction error). It is a testbed for learning the quantitative value representations needed for this.
*/
package main