diff --git a/examples/dls/README.md b/examples/dls/README.md deleted file mode 100644 index 49cb05cc..00000000 --- a/examples/dls/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# dls: Dorsal Lateral Striatum Motor Learning - -# TODO - -* VSgpi gating drives ALM gating, code to set DS gating window (instead of ach, da) - -* DLS gating actually affects M1 action selection - - diff --git a/examples/dls/armaze/README.md b/examples/dls/armaze/README.md deleted file mode 100644 index 68ec882e..00000000 --- a/examples/dls/armaze/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Arm Maze (armaze) Environment - -The Arm Maze environment represents an N-armed maze ("bandit") with each Arm having a distinctive CS stimulus at the start (could be one of multiple possibilities) and (some probability of) a US outcome at the end of the maze (could be either positive or negative, with (variable) magnitude and probability. - -It has a full 3D GUI showing the current state. - -The controlling Sim is responsible for calling the NewStart() method when the agent should be placed back at the starting point. Action() records the action and updates the State rendering of the action, but doesn't update the environment until Step, so the environment at the end of a trial always reflects the current trial state. - - diff --git a/examples/dls/armaze/arm.go b/examples/dls/armaze/arm.go deleted file mode 100644 index b13d6ab2..00000000 --- a/examples/dls/armaze/arm.go +++ /dev/null @@ -1,70 +0,0 @@ -// 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 armaze - -import "cogentcore.org/core/math32/minmax" - -// Arm represents the properties of a given arm of the N-maze. -// Arms have characteristic distance and effort factors for getting -// down the arm, and typically have a distinctive CS visible at the start -// and a US at the end, which has US-specific parameters for -// actually delivering reward or punishment. -type Arm struct { - - // length of arm: distance from CS start to US end for this arm - Length int - - // range of different effort levels per step (uniformly randomly sampled per step) for going down this arm - Effort minmax.F32 - - // todo: later - // indexes of US[s] present at the end of this arm -- nil if none - // USs []int `desc:"indexes of US[s] present at the end of this arm -- nil if none"` - - // index of US present at the end of this arm -- -1 if none - US int - - // index of CS visible at the start of this arm, -1 if none - CS int - - // current expected value = US.Prob * US.Mag * Drives-- computed at start of new approach - ExValue float32 `edit:"-"` - - // current expected PVpos value = normalized ExValue -- computed at start of new approach - ExPVpos float32 `edit:"-"` - - // current expected PVneg value = normalized time and effort costs - ExPVneg float32 `edit:"-"` - - // current expected utility = effort discounted version of ExPVpos -- computed at start of new approach - ExUtil float32 `edit:"-"` -} - -func (arm *Arm) Defaults() { - arm.Length = 4 - arm.Effort.Set(1, 1) - arm.Empty() -} - -// Empty sets all state to -1 -func (arm *Arm) Empty() { - arm.US = -1 - arm.CS = -1 - arm.ExValue = 0 - arm.ExUtil = 0 -} - -// USParams has parameters for different USs -type USParams struct { - - // if true is a negative valence US -- these are after the first NDrives USs - Negative bool - - // range of different magnitudes (uniformly sampled) - Mag minmax.F32 - - // probability of delivering the US - Prob float32 -} diff --git a/examples/dls/armaze/config.go b/examples/dls/armaze/config.go deleted file mode 100644 index 760f3533..00000000 --- a/examples/dls/armaze/config.go +++ /dev/null @@ -1,83 +0,0 @@ -// 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 armaze - -import "cogentcore.org/core/math32/minmax" - -// Params are misc environment parameters -type Params struct { - - // effort for turning - TurnEffort minmax.F32 `nest:"+" default:"{'Min':0.5, 'Max':0.5}"` - - // effort for consuming US - ConsumeEffort minmax.F32 `nest:"+" default:"{'Min':0.5, 'Max':0.5}"` - - // permute the order of CSs prior to applying them to arms -- having this off makes it easier to visually determine match between Drive and arm approach, and shouldn't make any difference to behavior (model doesn't know about this ordering). - PermuteCSs bool `default:"false"` - - // after running down an Arm, a new random starting location is selected (otherwise same arm as last run) - RandomStart bool `default:"true"` - - // if true, allow movement between arms just by going Left or Right -- otherwise once past the start, no switching is allowed - OpenArms bool `default:"true"` - - // strength of inactive inputs (e.g., Drives in Approach paradigm) - Inactive minmax.F32 `nest:"+" default:"{'Min':0, 'Max':0}" display:"inline"` - - // number of Y-axis repetitions of localist stimuli -- for redundancy in spiking nets - NYReps int `default:"4"` -} - -// Config has environment configuration -type Config struct { - - // experimental paradigm that governs the configuration and updating of environment state over time and the appropriate evaluation criteria. - Paradigm Paradigms - - // for debugging, print out key steps including a trace of the action generation logic - Debug bool - - // number of different drive-like body states (hunger, thirst, etc), that are satisfied by a corresponding positive US outcome -- this does not include the first curiosity drive - NDrives int - - // number of negative US outcomes -- these are added after NDrives positive USs to total US list - NNegUSs int - - // total number of USs = NDrives + NNegUSs - NUSs int `edit:"-"` - - // number of different arms - NArms int - - // maximum arm length (distance) - MaxArmLength int - - // number of different CSs -- typically at least a unique CS per US -- relationship is determined in the US params - NCSs int - - // parameters associated with each US. The first NDrives are positive USs, and beyond that are negative USs - USs []*USParams - - // state of each arm: dist, effort, US, CS - Arms []*Arm - - // misc params - Params Params `display:"add-fields"` -} - -func (cfg *Config) Defaults() { - if cfg.NDrives == 0 { - cfg.NDrives = 4 - } - cfg.Update() - if cfg.NCSs == 0 { - cfg.NCSs = cfg.NUSs - } -} - -func (cfg *Config) Update() { - cfg.NUSs = cfg.NDrives + cfg.NNegUSs -} diff --git a/examples/dls/armaze/enumgen.go b/examples/dls/armaze/enumgen.go deleted file mode 100644 index 4dd4bfcb..00000000 --- a/examples/dls/armaze/enumgen.go +++ /dev/null @@ -1,134 +0,0 @@ -// Code generated by "core generate -add-types"; DO NOT EDIT. - -package armaze - -import ( - "cogentcore.org/core/enums" -) - -var _ActionsValues = []Actions{0, 1, 2, 3, 4} - -// ActionsN is the highest valid value for type Actions, plus one. -const ActionsN Actions = 5 - -var _ActionsValueMap = map[string]Actions{`Forward`: 0, `Left`: 1, `Right`: 2, `Consume`: 3, `None`: 4} - -var _ActionsDescMap = map[Actions]string{0: ``, 1: ``, 2: ``, 3: ``, 4: ``} - -var _ActionsMap = map[Actions]string{0: `Forward`, 1: `Left`, 2: `Right`, 3: `Consume`, 4: `None`} - -// String returns the string representation of this Actions value. -func (i Actions) String() string { return enums.String(i, _ActionsMap) } - -// SetString sets the Actions value from its string representation, -// and returns an error if the string is invalid. -func (i *Actions) SetString(s string) error { - return enums.SetString(i, s, _ActionsValueMap, "Actions") -} - -// Int64 returns the Actions value as an int64. -func (i Actions) Int64() int64 { return int64(i) } - -// SetInt64 sets the Actions value from an int64. -func (i *Actions) SetInt64(in int64) { *i = Actions(in) } - -// Desc returns the description of the Actions value. -func (i Actions) Desc() string { return enums.Desc(i, _ActionsDescMap) } - -// ActionsValues returns all possible values for the type Actions. -func ActionsValues() []Actions { return _ActionsValues } - -// Values returns all possible values for the type Actions. -func (i Actions) Values() []enums.Enum { return enums.Values(_ActionsValues) } - -// MarshalText implements the [encoding.TextMarshaler] interface. -func (i Actions) MarshalText() ([]byte, error) { return []byte(i.String()), nil } - -// UnmarshalText implements the [encoding.TextUnmarshaler] interface. -func (i *Actions) UnmarshalText(text []byte) error { return enums.UnmarshalText(i, text, "Actions") } - -var _ParadigmsValues = []Paradigms{0} - -// ParadigmsN is the highest valid value for type Paradigms, plus one. -const ParadigmsN Paradigms = 1 - -var _ParadigmsValueMap = map[string]Paradigms{`Approach`: 0} - -var _ParadigmsDescMap = map[Paradigms]string{0: `Approach is a basic case where one Drive (chosen at random each trial) is fully active and others are at InactiveDrives levels -- goal is to approach the CS associated with the Drive-satisfying US, and avoid negative any negative USs. USs are always placed in same Arms (NArms must be >= NUSs -- any additional Arms are filled at random with additional US copies)`} - -var _ParadigmsMap = map[Paradigms]string{0: `Approach`} - -// String returns the string representation of this Paradigms value. -func (i Paradigms) String() string { return enums.String(i, _ParadigmsMap) } - -// SetString sets the Paradigms value from its string representation, -// and returns an error if the string is invalid. -func (i *Paradigms) SetString(s string) error { - return enums.SetString(i, s, _ParadigmsValueMap, "Paradigms") -} - -// Int64 returns the Paradigms value as an int64. -func (i Paradigms) Int64() int64 { return int64(i) } - -// SetInt64 sets the Paradigms value from an int64. -func (i *Paradigms) SetInt64(in int64) { *i = Paradigms(in) } - -// Desc returns the description of the Paradigms value. -func (i Paradigms) Desc() string { return enums.Desc(i, _ParadigmsDescMap) } - -// ParadigmsValues returns all possible values for the type Paradigms. -func ParadigmsValues() []Paradigms { return _ParadigmsValues } - -// Values returns all possible values for the type Paradigms. -func (i Paradigms) Values() []enums.Enum { return enums.Values(_ParadigmsValues) } - -// MarshalText implements the [encoding.TextMarshaler] interface. -func (i Paradigms) MarshalText() ([]byte, error) { return []byte(i.String()), nil } - -// UnmarshalText implements the [encoding.TextUnmarshaler] interface. -func (i *Paradigms) UnmarshalText(text []byte) error { - return enums.UnmarshalText(i, text, "Paradigms") -} - -var _TraceStatesValues = []TraceStates{0, 1, 2, 3, 4, 5, 6, 7} - -// TraceStatesN is the highest valid value for type TraceStates, plus one. -const TraceStatesN TraceStates = 8 - -var _TraceStatesValueMap = map[string]TraceStates{`TrSearching`: 0, `TrDeciding`: 1, `TrJustEngaged`: 2, `TrApproaching`: 3, `TrConsuming`: 4, `TrRewarded`: 5, `TrGiveUp`: 6, `TrBumping`: 7} - -var _TraceStatesDescMap = map[TraceStates]string{0: `Searching is not yet goal engaged, looking for a goal`, 1: `Deciding is having some partial gating but not in time for action`, 2: `JustEngaged means just decided to engage in a goal`, 3: `Approaching is goal engaged, approaching the goal`, 4: `Consuming is consuming the US, first step (prior to getting reward, step1)`, 5: `Rewarded is just received reward from a US`, 6: `GiveUp is when goal is abandoned`, 7: `Bumping is bumping into a wall`} - -var _TraceStatesMap = map[TraceStates]string{0: `TrSearching`, 1: `TrDeciding`, 2: `TrJustEngaged`, 3: `TrApproaching`, 4: `TrConsuming`, 5: `TrRewarded`, 6: `TrGiveUp`, 7: `TrBumping`} - -// String returns the string representation of this TraceStates value. -func (i TraceStates) String() string { return enums.String(i, _TraceStatesMap) } - -// SetString sets the TraceStates value from its string representation, -// and returns an error if the string is invalid. -func (i *TraceStates) SetString(s string) error { - return enums.SetString(i, s, _TraceStatesValueMap, "TraceStates") -} - -// Int64 returns the TraceStates value as an int64. -func (i TraceStates) Int64() int64 { return int64(i) } - -// SetInt64 sets the TraceStates value from an int64. -func (i *TraceStates) SetInt64(in int64) { *i = TraceStates(in) } - -// Desc returns the description of the TraceStates value. -func (i TraceStates) Desc() string { return enums.Desc(i, _TraceStatesDescMap) } - -// TraceStatesValues returns all possible values for the type TraceStates. -func TraceStatesValues() []TraceStates { return _TraceStatesValues } - -// Values returns all possible values for the type TraceStates. -func (i TraceStates) Values() []enums.Enum { return enums.Values(_TraceStatesValues) } - -// MarshalText implements the [encoding.TextMarshaler] interface. -func (i TraceStates) MarshalText() ([]byte, error) { return []byte(i.String()), nil } - -// UnmarshalText implements the [encoding.TextUnmarshaler] interface. -func (i *TraceStates) UnmarshalText(text []byte) error { - return enums.UnmarshalText(i, text, "TraceStates") -} diff --git a/examples/dls/armaze/gui.go b/examples/dls/armaze/gui.go deleted file mode 100644 index f99cf7ee..00000000 --- a/examples/dls/armaze/gui.go +++ /dev/null @@ -1,684 +0,0 @@ -// 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 armaze - -import ( - "fmt" - "image" - "image/color" - "log" - - "cogentcore.org/core/base/errors" - "cogentcore.org/core/colors" - "cogentcore.org/core/colors/colormap" - "cogentcore.org/core/core" - "cogentcore.org/core/events" - "cogentcore.org/core/icons" - "cogentcore.org/core/math32" - "cogentcore.org/core/plot/plotcore" - "cogentcore.org/core/styles" - "cogentcore.org/core/styles/abilities" - "cogentcore.org/core/tensor/table" - "cogentcore.org/core/tensor/tensorcore" - "cogentcore.org/core/tree" - "cogentcore.org/core/xyz" - "cogentcore.org/core/xyz/physics" - "cogentcore.org/core/xyz/physics/world" - "cogentcore.org/core/xyz/xyzcore" - "github.com/emer/axon/v2/axon" -) - -// Geom is overall geometry of the space -type Geom struct { - - // width of arm -- emery rodent is 1 unit wide - ArmWidth float32 `default:"2"` - - // total space between arms, ends up being divided on either side - ArmSpace float32 `default:"1"` - - // multiplier per unit arm length -- keep square with width - LengthScale float32 `default:"2"` - - // thickness of walls, floor - Thick float32 `default:"0.1"` - - // height of walls - Height float32 `default:"0.2"` - - // width + space - ArmWidthTot float32 `edit:"-"` - - // computed total depth, starts at 0 goes deep - Depth float32 `edit:"-"` - - // computed total width - Width float32 `edit:"-"` - - // half width for centering on 0 X - HalfWidth float32 `edit:"-"` -} - -func (ge *Geom) Config(nArms int, maxLen int) { - ge.ArmSpace = 1 - ge.ArmWidth = 2 - ge.LengthScale = 2 - ge.Thick = 0.1 - ge.Height = 0.2 - ge.ArmWidthTot = ge.ArmWidth + ge.ArmSpace - ge.Width = float32(nArms) * ge.ArmWidthTot - ge.Depth = float32(maxLen) * ge.LengthScale - ge.HalfWidth = ge.Width / 2 -} - -// pos returns the center position for given arm, position coordinate -func (ge *Geom) Pos(arm, pos int) (x, y float32) { - x = (float32(arm)+.5)*ge.ArmWidthTot - ge.HalfWidth - y = -(float32(pos) + .5) * ge.LengthScale // not centered -- going back in depth - return -} - -// GUI renders multiple views of the flat world env -type GUI struct { - - // update display -- turn off to make it faster - Disp bool - - // the env being visualized - Env *Env - - // name of current env -- number is NData index - EnvName string - - // 3D visualization of the Scene - SceneEditor *xyzcore.SceneEditor - - // 2D visualization of the Scene - Scene2D *core.SVG - - // list of material colors - MatColors []string - - // internal state colors - StateColors map[string]string - - // thickness (X) and height (Y) of walls - WallSize math32.Vector2 - - // current internal / behavioral state - State TraceStates - - // trace record of recent activity - Trace StateTrace - - // view of the gui obj - SimForm *core.Form `display:"-"` - - // ArmMaze TabView - WorldTabs *core.Tabs `display:"-"` - - // ArmMaze is running - IsRunning bool `display:"-"` - - // current depth map - DepthValues []float32 - - // offscreen render camera settings - Camera world.Camera - - // color map to use for rendering depth map - DepthMap core.ColorMapName - - // first-person right-eye full field view - EyeRFullImage *core.Image `display:"-"` - - // first-person right-eye fovea view - EyeRFovImage *core.Image `display:"-"` - - // depth map bitmap view - DepthImage *core.Image `display:"-"` - - // plot of positive valence drives, active OFC US state, and reward - USposPlot *plotcore.PlotEditor - - // data for USPlot - USposData *table.Table - - // plot of negative valence active OFC US state, and outcomes - USnegPlot *plotcore.PlotEditor - - // data for USPlot - USnegData *table.Table - - // geometry of world - Geom Geom - - // world - World *physics.Group - - // 3D view of world - View3D *world.View `display:"-"` - - // emer group - Emery *physics.Group `display:"-"` - - // arms group - Arms *physics.Group `display:"-"` - - // stims group - Stims *physics.Group `display:"-"` - - // Right eye of emery - EyeR physics.Body `display:"-"` - - // contacts from last step, for body - Contacts physics.Contacts `display:"-"` -} - -// ConfigWorldGUI configures all the world view GUI elements -// pass an initial env to use for configuring -func (vw *GUI) ConfigWorldGUI(ev *Env) *core.Body { - vw.Disp = true - vw.Env = ev - vw.EnvName = ev.Name - vw.WallSize.Set(0.1, 2) - vw.Camera.Defaults() - vw.Camera.FOV = 90 - - vw.StateColors = map[string]string{ - "TrSearching": "aqua", - "TrDeciding": "coral", - "TrJustEngaged": "yellow", - "TrApproaching": "cornflowerblue", - "TrConsuming": "purple", - "TrRewarded": "green", - "TrGiveUp": "black", - "TrBumping": "red", - } - vw.MatColors = []string{"blue", "orange", "red", "violet", "navy", "brown", "pink", "purple", "olive", "chartreuse", "cyan", "magenta", "salmon", "goldenrod", "SykBlue"} - - b := core.NewBody("armaze").SetTitle("Arm Maze") - - split := core.NewSplits(b) - - svfr := core.NewFrame(split) - svfr.SetName("svfr") - svfr.Styler(func(s *styles.Style) { - s.Direction = styles.Column - }) - - vw.SimForm = core.NewForm(svfr).SetStruct(vw) - imfr := core.NewFrame(svfr) - imfr.Styler(func(s *styles.Style) { - s.Display = styles.Grid - s.Columns = 2 - s.Grow.Set(0, 0) - }) - core.NewText(imfr).SetText("Eye-View, Fovea:") - core.NewText(imfr).SetText("Full Field:") - - vw.EyeRFovImage = core.NewImage(imfr) - vw.EyeRFovImage.Name = "eye-r-fov-img" - vw.EyeRFovImage.Image = image.NewRGBA(image.Rectangle{Max: vw.Camera.Size}) - - vw.EyeRFullImage = core.NewImage(imfr) - vw.EyeRFullImage.Name = "eye-r-full-img" - vw.EyeRFullImage.Image = image.NewRGBA(image.Rectangle{Max: vw.Camera.Size}) - - wd := float32(200) - ht := float32(100) - vw.USposPlot = plotcore.NewPlotEditor(svfr) - vw.USposPlot.Name = "us-pos" - vw.USposPlot.Styler(func(s *styles.Style) { - s.Min.X.Px(wd) - s.Min.Y.Px(ht) - s.Grow.Set(0, 0) - }) - - vw.USnegPlot = plotcore.NewPlotEditor(svfr) - vw.USnegPlot.Name = "us-neg" - vw.USnegPlot.Styler(func(s *styles.Style) { - s.Min.X.Px(wd) - s.Min.Y.Px(ht) - s.Grow.Set(0, 0) - }) - vw.ConfigUSPlots() - - tv := core.NewTabs(split) - vw.WorldTabs = tv - - scfr, _ := tv.NewTab("3D View") - twofr, _ := tv.NewTab("2D View") - - ////////////////////////////////////////// - // 3D Scene - - vw.ConfigWorld() - - vw.SceneEditor = xyzcore.NewSceneEditor(scfr) - vw.SceneEditor.UpdateWidget() - se := vw.SceneEditor.SceneXYZ() - vw.ConfigView3D(se) - - se.Camera.Pose.Pos = math32.Vec3(0, 29, -4) - se.Camera.LookAt(math32.Vec3(0, 4, -5), math32.Vec3(0, 1, 0)) - se.SaveCamera("2") - - se.Camera.Pose.Pos = math32.Vec3(0, 24, 32) - se.Camera.LookAt(math32.Vec3(0, 3.6, 0), math32.Vec3(0, 1, 0)) - se.SaveCamera("1") - se.SaveCamera("default") - - ////////////////////////////////////////// - // 2D Scene - - twov := core.NewSVG(twofr) - twov.Name = "sceneview" - twov.Styler(func(s *styles.Style) { - twov.SVG.Root.ViewBox.Size.Set(vw.Geom.Width+4, vw.Geom.Depth+4) - twov.SVG.Root.ViewBox.Min.Set(-0.5*(vw.Geom.Width+4), -0.5*(vw.Geom.Depth+4)) - twov.SetReadOnly(false) - }) - - ////////////////////////////////////////// - // Toolbar - - split.SetSplits(.4, .6) - - b.AddTopBar(func(bar *core.Frame) { - core.NewToolbar(bar).Maker(func(p *tree.Plan) { - tree.Add(p, func(w *core.Button) { - w.SetText("Init").SetIcon(icons.ClearAll). - SetTooltip("Init env"). - OnClick(func(e events.Event) { - vw.Env.Init(0) - }) - }) - tree.Add(p, func(w *core.Button) { - w.SetText("Reset Trace").SetIcon(icons.Undo). - SetTooltip("Reset trace of position, etc, shown in 2D View"). - OnClick(func(e events.Event) { - vw.Trace = nil - }) - }) - tree.Add(p, func(w *core.FuncButton) { - w.SetFunc(vw.Forward).SetText("Fwd").SetIcon(icons.SkipNext). - Styler(func(s *styles.Style) { - s.SetAbilities(true, abilities.RepeatClickable) - }) - }) - tree.Add(p, func(w *core.FuncButton) { - w.SetFunc(vw.Left).SetText("Left").SetIcon(icons.KeyboardArrowLeft). - Styler(func(s *styles.Style) { - s.SetAbilities(true, abilities.RepeatClickable) - }) - }) - tree.Add(p, func(w *core.FuncButton) { - w.SetFunc(vw.Right).SetText("Right").SetIcon(icons.KeyboardArrowRight). - Styler(func(s *styles.Style) { - s.SetAbilities(true, abilities.RepeatClickable) - }) - }) - tree.Add(p, func(w *core.FuncButton) { - w.SetFunc(vw.Consume).SetText("Consume").SetIcon(icons.SentimentExcited). - Styler(func(s *styles.Style) { - s.SetAbilities(true, abilities.RepeatClickable) - }) - }) - - tree.Add(p, func(w *core.Separator) {}) - }) - }) - return b -} - -// ConfigWorld constructs a new virtual physics world for flat world -func (vw *GUI) ConfigWorld() { - ev := vw.Env - - vw.World = physics.NewGroup() - vw.World.SetName("RoomWorld") - - vw.Geom.Config(ev.Config.NArms, ev.MaxLength) - - vw.AddFloor(vw.World, "floor") - vw.Arms = vw.ConfigArms(vw.World) - vw.Stims = vw.ConfigStims(vw.World, "stims", .9, .1) - - vw.Emery = vw.ConfigEmery(vw.World, 1) - vw.EyeR = vw.Emery.ChildByName("head", 1).AsTree().ChildByName("eye-r", 2).(physics.Body) - - vw.World.WorldInit() - - vw.SetEmeryPose() -} - -// AddFloor adds a floor -func (vw *GUI) AddFloor(par *physics.Group, name string) *physics.Group { - ge := &vw.Geom - dp := ge.Depth + 3*ge.LengthScale - rm := physics.NewGroup(par) - rm.SetName(name) - physics.NewBox(rm).SetSize(math32.Vec3(ge.Width, ge.Thick, dp)). - SetColor("grey").SetInitPos(math32.Vec3(0, -ge.Thick/2, -ge.Depth/2-ge.LengthScale)).SetName("floor") - - return rm -} - -// ConfigArms adds all the arms -func (vw *GUI) ConfigArms(par *physics.Group) *physics.Group { - ev := vw.Env - rm := physics.NewGroup(par) - rm.SetName("arms") - ge := &vw.Geom - exln := ge.LengthScale - halfarm := .5 * ge.ArmWidth - halfht := .5 * ge.Height - for i, arm := range ev.Config.Arms { - anm := fmt.Sprintf("arm_%d\n", i) - agp := physics.NewGroup(rm) - agp.SetName(anm) - x, _ := ge.Pos(i, 0) - ln := ge.LengthScale * float32(arm.Length) - halflen := .5*ln + exln - - physics.NewBox(agp).SetSize(math32.Vec3(ge.Thick, ge.Height, ln)). - SetColor("black").SetInitPos(math32.Vec3(x-halfarm, halfht, -halflen)).SetName("left-wall") - - physics.NewBox(agp).SetSize(math32.Vec3(ge.Thick, ge.Height, ln)). - SetColor("black").SetInitPos(math32.Vec3(x+halfarm, halfht, -halflen)).SetName("right-wall") - } - return rm -} - -// ConfigStims constructs stimuli: CSs, USs -func (vw *GUI) ConfigStims(par *physics.Group, name string, width, height float32) *physics.Group { - ev := vw.Env - ge := &vw.Geom - stms := physics.NewGroup(par) - stms.SetName(name) - exln := ge.LengthScale - // halfarm := .5 * ge.ArmWidth - usHt := ge.Height - usDp := 0.2 * ge.LengthScale - csHt := ge.LengthScale - - for i, arm := range ev.Config.Arms { - x, _ := ge.Pos(i, 0) - ln := ge.LengthScale * float32(arm.Length) - usnm := fmt.Sprintf("us_%d\n", i) - csnm := fmt.Sprintf("cs_%d\n", i) - - physics.NewBox(stms).SetSize(math32.Vec3(ge.ArmWidth, usHt, usDp)). - SetColor(vw.MatColors[arm.US]).SetInitPos(math32.Vec3(x, 0.5*usHt, -ln-1.1*exln)).SetName(usnm) - - physics.NewBox(stms).SetSize(math32.Vec3(ge.ArmWidth, csHt, ge.Thick)). - SetColor(vw.MatColors[arm.CS]).SetInitPos(math32.Vec3(x, usHt+0.5*csHt, -ln-2*exln)).SetName(csnm) - } - return stms -} - -func (vw *GUI) UpdateStims() { - var updts []string - ev := vw.Env - stms := vw.Stims.Children - for i, moi := range stms { - mo := moi.(*physics.Box) - if i%2 == 1 { // CS - armi := i / 2 - arm := ev.Config.Arms[armi] - clr := vw.MatColors[arm.CS] - if mo.Color != clr { - mo.Color = clr - updts = append(updts, mo.Name) - } - } - } - if len(updts) > 0 { - vw.View3D.UpdateBodyView(updts...) - } -} - -// ConfigEmery constructs a new Emery virtual hamster -func (vw *GUI) ConfigEmery(par *physics.Group, length float32) *physics.Group { - emr := physics.NewGroup(par) - emr.SetName("emery") - height := length / 2 - width := height - - physics.NewBox(emr).SetSize(math32.Vec3(width, height, length)). - SetColor("purple").SetDynamic(true). - SetInitPos(math32.Vec3(0, height/2, 0)).SetName("body") - - headsz := height * 0.75 - hhsz := .5 * headsz - hgp := physics.NewGroup(emr).SetInitPos(math32.Vec3(0, hhsz, -(length/2 + hhsz))) - hgp.SetName("head") - physics.NewBox(hgp).SetSize(math32.Vec3(headsz, headsz, headsz)). - SetColor("tan").SetDynamic(true).SetInitPos(math32.Vec3(0, 0, 0)). - SetName("head") - - eyesz := headsz * .2 - - physics.NewBox(hgp).SetSize(math32.Vec3(eyesz, eyesz*.5, eyesz*.2)). - SetColor("green").SetDynamic(true). - SetInitPos(math32.Vec3(-hhsz*.6, headsz*.1, -(hhsz + eyesz*.3))). - SetName("eye-l") - - // note: centering this in head for now to get straight-on view - physics.NewBox(hgp).SetSize(math32.Vec3(eyesz, eyesz*.5, eyesz*.2)). - SetColor("green").SetDynamic(true). - SetInitPos(math32.Vec3(0, headsz*.1, -(hhsz + eyesz*.3))). - SetName("eye-r") - - return emr -} - -// ConfigView3D makes the 3D view -func (vw *GUI) ConfigView3D(se *xyz.Scene) { - se.Background = colors.Uniform(colors.FromRGB(230, 230, 255)) // sky blue-ish - xyz.NewAmbient(se, "ambient", 0.3, xyz.DirectSun) - - dir := xyz.NewDirectional(se, "dir", 1, xyz.DirectSun) - dir.Pos.Set(0, 2, 1) // default: 0,1,1 = above and behind us (we are at 0,0,X) - - // sc.MultiSample = 1 // we are using depth grab so we need this = 1 - wgp := xyz.NewGroup(se) - wgp.SetName("world") - vw.View3D = world.NewView(vw.World, se, wgp) - vw.View3D.InitLibrary() // this makes a basic library based on body shapes, sizes - // at this point the library can be updated to configure custom visualizations - // for any of the named bodies. - vw.View3D.Sync() -} - -func (vw *GUI) ConfigUSPlots() { - dp := table.NewTable() - dp.AddStringColumn("US") - dp.AddFloat64Column("Drive") - dp.AddFloat64Column("OFC") - dp.AddFloat64Column("USin") - dp.SetNumRows(vw.Env.Config.NDrives + 1) - - vw.USposData = dp - vw.USposPlot.Options.Type = plotcore.Bar - vw.USposPlot.Options.Title = "Positive USs" - vw.USposPlot.Options.Scale = 1 - vw.USposPlot.Options.XAxis = "US" - - dn := table.NewTable() - dn.AddStringColumn("US") - dn.AddFloat64Column("OFC") - dn.AddFloat64Column("USin") - dn.SetNumRows(vw.Env.Config.NNegUSs + 2) - - vw.USnegData = dn - vw.USnegPlot.Options.Type = plotcore.Bar - vw.USnegPlot.Options.Title = "Negative USs" - vw.USnegPlot.Options.Scale = 1 - vw.USnegPlot.Options.XAxis = "US" - - cols := []string{"Drive", "USin", "OFC"} - for i, cl := range cols { - dp.SetMetaData(cl+":On", "true") - dp.SetMetaData(cl+":FixMin", "true") - dp.SetMetaData(cl+":FixMax", "true") - dp.SetMetaData(cl+":Max", "1") - if i > 0 { - dn.SetMetaData(cl+":On", "true") - dn.SetMetaData(cl+":FixMin", "true") - dn.SetMetaData(cl+":FixMax", "true") - dn.SetMetaData(cl+":Max", "1") - } - } - vw.USposPlot.SetTable(dp) - vw.USnegPlot.SetTable(dn) -} - -// GrabEyeImg takes a snapshot from the perspective of Emer's right eye -func (vw *GUI) GrabEyeImg() { - vw.Camera.FOV = 90 - err := vw.View3D.RenderOffNode(vw.EyeR, &vw.Camera) - if err != nil { - log.Println(err) - return - } - img, err := vw.View3D.Image() - if err == nil && img != nil { - vw.EyeRFullImage.SetImage(img) - vw.EyeRFullImage.NeedsRender() - } else { - log.Println(err) - } - - vw.Camera.FOV = 10 - err = vw.View3D.RenderOffNode(vw.EyeR, &vw.Camera) - if err != nil { - log.Println(err) - return - } - img, err = vw.View3D.Image() - if err == nil && img != nil { - vw.EyeRFovImage.SetImage(img) - } else { - log.Println(err) - } - - // depth, err := vw.View3D.DepthImage() - // if err == nil && depth != nil { - // vw.DepthValues = depth - // vw.ViewDepth(depth) - // } -} - -// ViewDepth updates depth bitmap with depth data -func (vw *GUI) ViewDepth(depth []float32) { - cmap := colormap.AvailableMaps[string(vw.DepthMap)] - vw.DepthImage.Image = image.NewRGBA(image.Rectangle{Max: vw.Camera.Size}) - world.DepthImage(vw.DepthImage.Image.(*image.RGBA), depth, cmap, &vw.Camera) -} - -func (vw *GUI) ConfigWorldView(tg *tensorcore.TensorGrid) { - cnm := "ArmMazeColors" - cm, ok := colormap.AvailableMaps[cnm] - if !ok { - ev := vw.Env - cm = &colormap.Map{} - cm.Name = cnm - cm.Indexed = true - nc := ev.Config.NArms - cm.Colors = make([]color.RGBA, nc) - cm.NoColor = colors.Black - for i, cnm := range vw.MatColors { - cm.Colors[i] = errors.Log1(colors.FromString(cnm)) - } - colormap.AvailableMaps[cnm] = cm - } - tg.Display.Defaults() - tg.Display.ColorMap = core.ColorMapName(cnm) - tg.Display.GridFill = 1 -} - -func (vw *GUI) UpdateWorld(ctx *axon.Context, ev *Env, net *axon.Network, state TraceStates) { - vw.State = state - vw.Trace.AddRec(ctx, uint32(ev.Di), ev, net, state) - if vw.SceneEditor == nil || !vw.Disp { - return - } - - if vw.Env != ev { - vw.Env = ev - vw.EnvName = ev.Name - vw.Trace = nil - vw.SimForm.Update() - } - - vw.UpdateWorldGUI() -} - -func (vw *GUI) SetEmeryPose() { - ev := vw.Env - x, y := vw.Geom.Pos(ev.Arm, ev.Pos) - vw.Emery.Rel.Pos.Set(x, 0, y) - bod := vw.Emery.ChildByName("body", 0).(physics.Body).AsBodyBase() - bod.Color = vw.StateColors[vw.State.String()] -} - -func (vw *GUI) UpdateWorldGUI() { - if vw.SceneEditor == nil || !vw.Disp { - return - } - vw.SceneEditor.AsyncLock() - defer vw.SceneEditor.AsyncUnlock() - - // update state: - vw.SetEmeryPose() - vw.UpdateStims() - vw.World.WorldRelToAbs() - vw.View3D.UpdatePose() - vw.View3D.UpdateBodyView("body") - // vw.View2D.UpdatePose() - - // update views: - vw.GrabEyeImg() - if vw.SceneEditor.IsVisible() { - vw.SceneEditor.NeedsRender() - } - // if vw.Scene2D.IsVisible() { - // vw.Scene2D.SetNeedsRender(true) - // } -} - -func (vw *GUI) Left() { //types:add - ev := vw.Env - ev.InstinctAct() - ev.Action("Left", nil) - ev.Step() - vw.UpdateWorldGUI() -} - -func (vw *GUI) Right() { //types:add - ev := vw.Env - ev.InstinctAct() - ev.Action("Right", nil) - ev.Step() - vw.UpdateWorldGUI() -} - -func (vw *GUI) Forward() { //types:add - ev := vw.Env - ev.InstinctAct() - ev.Action("Forward", nil) - ev.Step() - vw.UpdateWorldGUI() -} - -func (vw *GUI) Consume() { //types:add - ev := vw.Env - ev.InstinctAct() - ev.Action("Consume", nil) - ev.Step() - vw.UpdateWorldGUI() -} diff --git a/examples/dls/armaze/maze.go b/examples/dls/armaze/maze.go deleted file mode 100644 index dbd284bb..00000000 --- a/examples/dls/armaze/maze.go +++ /dev/null @@ -1,551 +0,0 @@ -// 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 armaze represents an N-armed maze ("bandit") -// with each Arm having a distinctive CS stimulus at the start -// (could be one of multiple possibilities) and (some probability of) -// a US outcome at the end of the maze (could be either positive -// or negative, with (variable) magnitude and probability. -// -// The maze can have open or closed arms -- open arms allow -// switching to a neighboring arm anytime, while closed arms -// only allow switching at the start. -package armaze - -//go:generate core generate -add-types - -import ( - "log" - - "cogentcore.org/core/base/num" - "cogentcore.org/core/base/randx" - "cogentcore.org/core/math32/minmax" - "cogentcore.org/core/tensor" - "github.com/emer/axon/v2/axon" - "github.com/emer/emergent/v2/econfig" -) - -// Actions is a list of mutually exclusive states -// for tracing the behavior and internal state of Emery -type Actions int32 //enums:enum - -const ( - Forward Actions = iota - Left - Right - Consume - None -) - -// General note on US / Drive indexes: -// The env does _not_ represent any built-in drives or USs (curiosity, effort, urgency) -// 0 = start of the sim-specific USs and Drives - -// Env implements an N-armed maze ("bandit") -// with each Arm having a distinctive CS stimulus visible at the start -// (could be one of multiple possibilities) and (some probability of) -// a US outcome at the end of the maze (could be either positive -// or negative, with (variable) magnitude and probability. -type Env struct { - - // name of environment -- Train or Test - Name string - - // our data parallel index - Di int `edit:"-"` - - // configuration parameters - Config Config `new-window:"+"` - - // current drive strength for each of Config.NDrives in normalized 0-1 units of each drive: 0 = first sim drive, not curiosity - Drives []float32 - - // arm-wise location: either facing (Pos=0) or in (Pos > 0) - Arm int `edit:"-"` - - // current position in the Arm: 0 = at start looking in, otherwise at given distance into the arm - Pos int `edit:"-"` - - // current integer time step since last NewStart - Tick int `edit:"-"` - - // current target drive, in paradigms where that is used (e.g., Approach) - TrgDrive int `edit:"-"` - - // Current US being consumed -- is -1 unless being consumed - USConsumed int `edit:"-"` - - // reward or punishment value generated by the current US being consumed -- just the Magnitude of the US -- does NOT include any modulation by Drive - USValue float32 `edit:"-"` - - // just finished consuming a US -- ready to start doing something new - JustConsumed bool `edit:"-"` - - // arm(s) with maximum Drive * Mag * Prob US outcomes - ArmsMaxValue []int `edit:"-"` - - // maximum value for ArmsMaxValue arms - MaxValue float32 `edit:"-"` - - // arm(s) with maximum Value outcome discounted by Effort - ArmsMaxUtil []int `edit:"-"` - - // maximum utility for ArmsMaxUtil arms - MaxUtil float32 `edit:"-"` - - // arm(s) with negative US outcomes - ArmsNeg []int `edit:"-"` - - // last action taken - LastAct Actions `edit:"-"` - - // effort on current trial - Effort float32 `edit:"-"` - - // last CS seen - LastCS int `edit:"-"` - - // last US -- previous trial - LastUS int `edit:"-"` - - // just gated on this trial -- set by sim-- used for instinct - JustGated bool `edit:"-"` - - // has gated at some point during sequence -- set by sim -- used for instinct - HasGated bool `edit:"-"` - - // named states -- e.g., USs, CSs, etc - States map[string]*tensor.Float32 - - // maximum length of any arm - MaxLength int `edit:"-"` - - // random number generator for the env -- all random calls must use this - Rand randx.SysRand `display:"-"` - - // random seed - RandSeed int64 `edit:"-"` -} - -const noUS = -1 - -// Defaults sets default params -func (ev *Env) Defaults() { - ev.Config.Defaults() - econfig.SetFromDefaults(&ev.Config) - ev.Config.Update() -} - -// ConfigEnv configures the environment. -// additional parameterization via specific configs -// is applied after this step, which initializes -// everything according to basic Ns -// takes the data parallel index di -func (ev *Env) ConfigEnv(di int) { - ev.Di = di - cfg := &ev.Config - - if ev.Rand.Rand == nil { - ev.Rand.NewRand(ev.RandSeed) - } else { - ev.Rand.Seed(ev.RandSeed) - } - - switch cfg.Paradigm { - case Approach: - ev.ConfigApproach() - } - - cfg.Update() - - ev.Drives = make([]float32, cfg.NDrives) - ev.Config.USs = make([]*USParams, cfg.NUSs) - ev.Config.Arms = make([]*Arm, cfg.NArms) - - log.Printf("drives: %d, USs: %d, CSs: %d", cfg.NDrives, cfg.NUSs, cfg.NCSs) - log.Printf("max arm length: %d", cfg.MaxArmLength) - - // defaults - for i := range ev.Config.Arms { - // TODO: if we permute CSs do we also want to keep the USs aligned? - length := 4 - if ev.Config.MaxArmLength > 0 { - length = length + ev.Rand.Intn(ev.Config.MaxArmLength) - } - arm := &Arm{Length: length, CS: i % cfg.NCSs, US: i % cfg.NUSs} - ev.Config.Arms[i] = arm - arm.Effort.Set(1, 1) - } - - // defaults - for i := range ev.Config.USs { - us := &USParams{Prob: 1} - ev.Config.USs[i] = us - if i < cfg.NDrives { - us.Negative = false - } else { - us.Negative = true - } - us.Mag.Set(1, 1) - } - - ev.UpdateMaxLength() -} - -func (ev *Env) Validate() error { - return nil -} - -// Init does updating preparing to run -- params could have changed since initial config -// so updates everything except broad overall config stuff. -func (ev *Env) Init(run int) { - cfg := &ev.Config - - ev.UpdateMaxLength() - - ev.States = make(map[string]*tensor.Float32) - ev.States["CS"] = tensor.NewFloat32([]int{cfg.Params.NYReps, cfg.NCSs}) - ev.States["Pos"] = tensor.NewFloat32([]int{cfg.Params.NYReps, ev.MaxLength + 1}) - ev.States["Arm"] = tensor.NewFloat32([]int{cfg.Params.NYReps, ev.Config.NArms}) - ev.States["Action"] = tensor.NewFloat32([]int{cfg.Params.NYReps, int(ActionsN)}) - ev.States["VSgpi"] = tensor.NewFloat32([]int{cfg.Params.NYReps, 4}) - ev.States["OFC"] = tensor.NewFloat32([]int{cfg.Params.NYReps, 4}) - - ev.NewStart() - ev.JustConsumed = true // will trigger a new start again on Step -} - -func (ev *Env) State(el string) tensor.Tensor { - return ev.States[el] -} - -// NewStart starts a new approach run -func (ev *Env) NewStart() { - arm := ev.Config.Arms[ev.Arm] - // choose a new CS that maps to the same US - arm.CS = (arm.CS + ev.Config.NCSs) % ev.Config.NCSs - - if ev.Config.Params.RandomStart { - ev.Arm = ev.Rand.Intn(len(ev.Config.Arms)) - } - ev.Pos = 0 - ev.Tick = 0 - ev.JustGated = false - ev.HasGated = false - ev.USConsumed = -1 - ev.USValue = 0 - ev.JustConsumed = false - - switch ev.Config.Paradigm { - case Approach: - ev.StartApproach() - } - ev.RenderState() -} - -func (ev *Env) ExValueUtil(pv *axon.Rubicon, ctx *axon.Context) { - maxval := float32(0) - maxutil := float32(0) - ev.ArmsNeg = nil - usPos := make([]float32, pv.NPosUSs) - usNeg := make([]float32, pv.NNegUSs) - for i, arm := range ev.Config.Arms { - us := ev.Config.USs[arm.US] - if us.Negative { - ev.ArmsNeg = append(ev.ArmsNeg, i) - continue - } - val := ev.Drives[arm.US] * us.Mag.Midpoint() * us.Prob - arm.ExValue = val - for j := range usPos { // reset - usPos[j] = 0 - } - for j := range usNeg { // reset - usNeg[j] = 0 - } - usPos[arm.US+1] = val - _, pvPos := pv.PVposEstFromUSs(ctx, uint32(ev.Di), usPos) - exTime := float32(arm.Length) + 1 // time - usNeg[0] = exTime - usNeg[1] = exTime * arm.Effort.Midpoint() - _, pvNeg := pv.PVnegEstFromUSs(usNeg) - burst, dip, da, rew := pv.DAFromPVs(pvPos, pvNeg, 0, 0) - _, _, _ = burst, dip, rew - arm.ExPVpos = pvPos - arm.ExPVneg = pvNeg - arm.ExUtil = da - if val > maxval { - maxval = val - } - if da > maxutil { - maxutil = da - } - } - ev.MaxValue = maxval - ev.MaxUtil = maxutil - ev.ArmsMaxValue = nil - ev.ArmsMaxUtil = nil - for i, arm := range ev.Config.Arms { - if arm.ExValue == maxval { - ev.ArmsMaxValue = append(ev.ArmsMaxValue, i) - } - if arm.ExUtil == maxutil { - ev.ArmsMaxUtil = append(ev.ArmsMaxUtil, i) - } - } -} - -// ArmIsMaxValue returns true if the given arm is (one of) the arms with the best -// current expected outcome value -func (ev *Env) ArmIsMaxValue(arm int) bool { - for _, ai := range ev.ArmsMaxValue { - if arm == ai { - return true - } - } - return false -} - -// ArmIsMaxUtil returns true if the given arm is (one of) the arms with the best -// current expected outcome utility -func (ev *Env) ArmIsMaxUtil(arm int) bool { - for _, ai := range ev.ArmsMaxUtil { - if arm == ai { - return true - } - } - return false -} - -// ArmIsNegative returns true if the given arm is (one of) the arms with -// negative outcomes -func (ev *Env) ArmIsNegative(arm int) bool { - for _, ai := range ev.ArmsNeg { - if arm == ai { - return true - } - } - return false -} - -// Step does one step. it is up to the driving sim to decide when to call NewStart -func (ev *Env) Step() bool { - ev.LastCS = ev.CurCS() - if ev.JustConsumed { // from last time, not this time. - ev.NewStart() - } else { - ev.Tick++ - } - ev.TakeAct(ev.LastAct) - switch ev.Config.Paradigm { - case Approach: - ev.StepApproach() - } - ev.RenderState() - return true -} - -////////////////////////////////////////////////// -// Render - -// RenderLocalist renders one localist state -func (ev *Env) RenderLocalist(name string, val int) { - st := ev.States[name] - st.SetZeros() - if val >= st.DimSize(1) || val < 0 { - return - } - for y := 0; y < ev.Config.Params.NYReps; y++ { - st.Set([]int{y, val}, 1.0) - } -} - -// RenderLocalist4D renders one localist state in 4D -func (ev *Env) RenderLocalist4D(name string, val int) { - st := ev.States[name] - st.SetZeros() - for y := 0; y < ev.Config.Params.NYReps; y++ { - st.Set([]int{0, val, y, 0}, 1.0) - } -} - -// RenderState renders the current state -func (ev *Env) RenderState() { - ev.RenderLocalist("CS", ev.CurCS()) - ev.RenderLocalist("Pos", ev.Pos) - ev.RenderLocalist("Arm", ev.Arm) - ofcn := 2 * num.FromBool[int](ev.HasGated) - ev.RenderLocalist("OFC", ofcn) - vsn := 2 * (num.FromBool[int](ev.JustGated) - 1) - ev.RenderLocalist("VSgpi", vsn) -} - -// RenderAction renders the action -func (ev *Env) RenderAction(act Actions) { - ev.RenderLocalist("Action", int(act)) -} - -////////////////////////////////////////////////// -// Action - -func (ev *Env) DecodeAct(vt *tensor.Float32) Actions { - mxi := ev.DecodeLocalist(vt) - return Actions(mxi) -} - -func (ev *Env) DecodeLocalist(vt *tensor.Float32) int { - dx := vt.DimSize(1) - var max float32 - var mxi int - for i := 0; i < dx; i++ { - var sum float32 - for j := 0; j < ev.Config.Params.NYReps; j++ { - sum += vt.Value([]int{j, i}) - } - if sum > max { - max = sum - mxi = i - } - } - return mxi -} - -// Action records the LastAct and renders it, but does not -// update the state accordingly. -func (ev *Env) Action(action string, nop tensor.Tensor) { - act := None - act.SetString(action) - ev.LastAct = act - ev.RenderAction(act) // plus phase input is action - // note: action not taken via TakeAct until start of trial in Step() -} - -func (ev *Env) TakeAct(act Actions) { - narms := ev.Config.NArms - arm := ev.Config.Arms[ev.Arm] - switch act { - case Forward: - ev.Effort = ev.ForwardEffort(arm) // pay effort regardless - npos := ev.Pos + 1 - if npos <= arm.Length { - ev.Pos = npos - } else { - // todo: bump into wall? - } - case Left: - ev.Effort = ev.TurnEffort() // pay effort regardless - if ev.Config.Params.OpenArms || ev.Pos == 0 { - ev.Arm-- - } - if ev.Arm < 0 { - ev.Arm += narms - } - case Right: - ev.Effort = ev.TurnEffort() // pay effort regardless - if ev.Config.Params.OpenArms || ev.Pos == 0 { - ev.Arm++ - } - if ev.Arm >= narms { - ev.Arm -= narms - } - case Consume: - ev.Effort = ev.ConsumeEffort() - if ev.Pos == arm.Length { - if ev.USConsumed < 0 { - ev.ConsumeUS(arm) - } else { - ev.JustConsumed = true - } - } - } -} - -// ConsumeUS implements the consume action at current position in given arm -func (ev *Env) ConsumeUS(arm *Arm) { - us := ev.Config.USs[arm.US] - mag := MinMaxRand(us.Mag, ev.Rand) - got := randx.BoolP32(us.Prob, &ev.Rand) - if got { - ev.USConsumed = arm.US - ev.USValue = mag - } else { - ev.USConsumed = -1 - ev.USValue = 0 - } -} - -// InstinctAct returns an "instinctive" action that implements a basic policy -func (ev *Env) InstinctAct() Actions { - ev.JustGated = !ev.HasGated && ev.ArmIsMaxUtil(ev.Arm) - if ev.JustGated { - ev.HasGated = true - } - if ev.HasGated && ev.USConsumed >= 0 { // gate at consume - ev.JustGated = true - } - arm := ev.CurArm() - if ev.Pos >= arm.Length { - return Consume - } - if ev.HasGated { - return Forward - } - if ev.LastAct == Left || ev.LastAct == Right { - return ev.LastAct - } - if randx.BoolP(.5, &ev.Rand) { - return Left - } - return Right -} - -////////////////////////////////////////////////// -// Utils - -// CurArm returns current Arm -func (ev *Env) CurArm() *Arm { - return ev.Config.Arms[ev.Arm] -} - -// CurCS returns current CS from current Arm -func (ev *Env) CurCS() int { - return ev.CurArm().CS -} - -// MinMaxRand returns a random number in the range between Min and Max -func MinMaxRand(mm minmax.F32, rand randx.SysRand) float32 { - return mm.Min + rand.Float32()*mm.Range() -} - -// InactiveVal returns a new random inactive value from Config.Params.Inactive -// param range. -func (ev *Env) InactiveValue() float32 { - return MinMaxRand(ev.Config.Params.Inactive, ev.Rand) -} - -// ForwardEffort returns a new random Effort value from Arm Effort range -func (ev *Env) ForwardEffort(arm *Arm) float32 { - return MinMaxRand(arm.Effort, ev.Rand) -} - -// TurnEffort returns a new random Effort value from Config.Params.TurnEffort -// param range. -func (ev *Env) TurnEffort() float32 { - return MinMaxRand(ev.Config.Params.TurnEffort, ev.Rand) -} - -// ConsumeEffort returns a new random Effort value from Config.Params.ConsumeEffort -// param range. -func (ev *Env) ConsumeEffort() float32 { - return MinMaxRand(ev.Config.Params.ConsumeEffort, ev.Rand) -} - -func (ev *Env) UpdateMaxLength() { - ev.MaxLength = 0 - for _, arm := range ev.Config.Arms { - if arm.Length > ev.MaxLength { - ev.MaxLength = arm.Length - } - } -} diff --git a/examples/dls/armaze/paradigms.go b/examples/dls/armaze/paradigms.go deleted file mode 100644 index 1e38f5e9..00000000 --- a/examples/dls/armaze/paradigms.go +++ /dev/null @@ -1,46 +0,0 @@ -// 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 armaze - -// Paradigms is a list of experimental paradigms that -// govern the configuration and updating of environment -// state over time and the appropriate evaluation criteria. -type Paradigms int32 //enums:enum - -const ( - // Approach is a basic case where one Drive (chosen at random each trial) is fully active and others are at InactiveDrives levels -- goal is to approach the CS associated with the Drive-satisfying US, and avoid negative any negative USs. USs are always placed in same Arms (NArms must be >= NUSs -- any additional Arms are filled at random with additional US copies) - Approach Paradigms = iota -) - -/////////////////////////////////////////////// -// Approach - -// ConfigApproach does initial config for Approach paradigm -func (ev *Env) ConfigApproach() { - if ev.Config.NArms < ev.Config.NUSs { - ev.Config.NArms = ev.Config.NUSs - } - if ev.Config.NCSs < ev.Config.NUSs { - ev.Config.NCSs = ev.Config.NUSs - } -} - -// StartApproach does new start state setting for Approach -// Selects a new TrgDrive at random, sets that to 1, -// others to inactive levels -func (ev *Env) StartApproach() { - ev.TrgDrive = ev.Rand.Intn(ev.Config.NDrives) - for i := range ev.Drives { - if i == ev.TrgDrive { - ev.Drives[i] = 1 - } else { - ev.Drives[i] = ev.InactiveValue() - } - } -} - -func (ev *Env) StepApproach() { - -} diff --git a/examples/dls/armaze/trace.go b/examples/dls/armaze/trace.go deleted file mode 100644 index 415bb1ed..00000000 --- a/examples/dls/armaze/trace.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) 2020, 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 armaze - -import ( - "github.com/emer/axon/v2/axon" -) - -// TraceStates is a list of mutually exclusive states -// for tracing the behavior and internal state of Emery -type TraceStates int32 //enums:enum - -const ( - // Searching is not yet goal engaged, looking for a goal - TrSearching TraceStates = iota - - // Deciding is having some partial gating but not in time for action - TrDeciding - - // JustEngaged means just decided to engage in a goal - TrJustEngaged - - // Approaching is goal engaged, approaching the goal - TrApproaching - - // Consuming is consuming the US, first step (prior to getting reward, step1) - TrConsuming - - // Rewarded is just received reward from a US - TrRewarded - - // GiveUp is when goal is abandoned - TrGiveUp - - // Bumping is bumping into a wall - TrBumping -) - -// TraceRec holds record of info for tracing behavior, state -type TraceRec struct { - - // absolute time - Time float32 - - // trial counter - Trial int - - // current arm - Arm int - - // position in arm - Pos int - - // behavioral / internal state summary - State TraceStates - - // NDrives current drive state level - Drives []float32 -} - -// StateTrace holds trace records -type StateTrace []*TraceRec - -// AddRec adds a record with data from given sources -func (tr *StateTrace) AddRec(ctx *axon.Context, di uint32, ev *Env, net *axon.Network, state TraceStates) *TraceRec { - rec := &TraceRec{Arm: ev.Arm, Pos: ev.Pos, State: state} - rec.Drives = make([]float32, ev.Config.NDrives) - if ctx != nil { - rec.Time = ctx.Time - rec.Trial = int(ctx.TrialsTotal) - for i := 0; i < ev.Config.NDrives; i++ { - rec.Drives[i] = axon.GlbUSposV(ctx, di, axon.GvDrives, uint32(1+i)) - } - } - - *tr = append(*tr, rec) - return rec -} diff --git a/examples/dls/armaze/typegen.go b/examples/dls/armaze/typegen.go deleted file mode 100644 index 49cf10d1..00000000 --- a/examples/dls/armaze/typegen.go +++ /dev/null @@ -1,31 +0,0 @@ -// Code generated by "core generate -add-types"; DO NOT EDIT. - -package armaze - -import ( - "cogentcore.org/core/types" -) - -var _ = types.AddType(&types.Type{Name: "github.com/emer/axon/v2/examples/dls/armaze.Arm", IDName: "arm", Doc: "Arm represents the properties of a given arm of the N-maze.\nArms have characteristic distance and effort factors for getting\ndown the arm, and typically have a distinctive CS visible at the start\nand a US at the end, which has US-specific parameters for\nactually delivering reward or punishment.", Fields: []types.Field{{Name: "Length", Doc: "length of arm: distance from CS start to US end for this arm"}, {Name: "Effort", Doc: "range of different effort levels per step (uniformly randomly sampled per step) for going down this arm"}, {Name: "US", Doc: "index of US present at the end of this arm -- -1 if none"}, {Name: "CS", Doc: "index of CS visible at the start of this arm, -1 if none"}, {Name: "ExValue", Doc: "current expected value = US.Prob * US.Mag * Drives-- computed at start of new approach"}, {Name: "ExPVpos", Doc: "current expected PVpos value = normalized ExValue -- computed at start of new approach"}, {Name: "ExPVneg", Doc: "current expected PVneg value = normalized time and effort costs"}, {Name: "ExUtil", Doc: "current expected utility = effort discounted version of ExPVpos -- computed at start of new approach"}}}) - -var _ = types.AddType(&types.Type{Name: "github.com/emer/axon/v2/examples/dls/armaze.USParams", IDName: "us-params", Doc: "USParams has parameters for different USs", Fields: []types.Field{{Name: "Negative", Doc: "if true is a negative valence US -- these are after the first NDrives USs"}, {Name: "Mag", Doc: "range of different magnitudes (uniformly sampled)"}, {Name: "Prob", Doc: "probability of delivering the US"}}}) - -var _ = types.AddType(&types.Type{Name: "github.com/emer/axon/v2/examples/dls/armaze.Params", IDName: "params", Doc: "Params are misc environment parameters", Fields: []types.Field{{Name: "TurnEffort", Doc: "effort for turning"}, {Name: "ConsumeEffort", Doc: "effort for consuming US"}, {Name: "PermuteCSs", Doc: "permute the order of CSs prior to applying them to arms -- having this off makes it easier to visually determine match between Drive and arm approach, and shouldn't make any difference to behavior (model doesn't know about this ordering)."}, {Name: "RandomStart", Doc: "after running down an Arm, a new random starting location is selected (otherwise same arm as last run)"}, {Name: "OpenArms", Doc: "if true, allow movement between arms just by going Left or Right -- otherwise once past the start, no switching is allowed"}, {Name: "Inactive", Doc: "strength of inactive inputs (e.g., Drives in Approach paradigm)"}, {Name: "NYReps", Doc: "number of Y-axis repetitions of localist stimuli -- for redundancy in spiking nets"}}}) - -var _ = types.AddType(&types.Type{Name: "github.com/emer/axon/v2/examples/dls/armaze.Config", IDName: "config", Doc: "Config has environment configuration", Fields: []types.Field{{Name: "Paradigm", Doc: "experimental paradigm that governs the configuration and updating of environment state over time and the appropriate evaluation criteria."}, {Name: "Debug", Doc: "for debugging, print out key steps including a trace of the action generation logic"}, {Name: "NDrives", Doc: "number of different drive-like body states (hunger, thirst, etc), that are satisfied by a corresponding positive US outcome -- this does not include the first curiosity drive"}, {Name: "NNegUSs", Doc: "number of negative US outcomes -- these are added after NDrives positive USs to total US list"}, {Name: "NUSs", Doc: "total number of USs = NDrives + NNegUSs"}, {Name: "NArms", Doc: "number of different arms"}, {Name: "MaxArmLength", Doc: "maximum arm length (distance)"}, {Name: "NCSs", Doc: "number of different CSs -- typically at least a unique CS per US -- relationship is determined in the US params"}, {Name: "USs", Doc: "parameters associated with each US. The first NDrives are positive USs, and beyond that are negative USs"}, {Name: "Arms", Doc: "state of each arm: dist, effort, US, CS"}, {Name: "Params", Doc: "misc params"}}}) - -var _ = types.AddType(&types.Type{Name: "github.com/emer/axon/v2/examples/dls/armaze.Geom", IDName: "geom", Doc: "Geom is overall geometry of the space", Fields: []types.Field{{Name: "ArmWidth", Doc: "width of arm -- emery rodent is 1 unit wide"}, {Name: "ArmSpace", Doc: "total space between arms, ends up being divided on either side"}, {Name: "LengthScale", Doc: "multiplier per unit arm length -- keep square with width"}, {Name: "Thick", Doc: "thickness of walls, floor"}, {Name: "Height", Doc: "height of walls"}, {Name: "ArmWidthTot", Doc: "width + space"}, {Name: "Depth", Doc: "computed total depth, starts at 0 goes deep"}, {Name: "Width", Doc: "computed total width"}, {Name: "HalfWidth", Doc: "half width for centering on 0 X"}}}) - -var _ = types.AddType(&types.Type{Name: "github.com/emer/axon/v2/examples/dls/armaze.GUI", IDName: "gui", Doc: "GUI renders multiple views of the flat world env", Methods: []types.Method{{Name: "Left", Directives: []types.Directive{{Tool: "types", Directive: "add"}}}, {Name: "Right", Directives: []types.Directive{{Tool: "types", Directive: "add"}}}, {Name: "Forward", Directives: []types.Directive{{Tool: "types", Directive: "add"}}}, {Name: "Consume", Directives: []types.Directive{{Tool: "types", Directive: "add"}}}}, Fields: []types.Field{{Name: "Disp", Doc: "update display -- turn off to make it faster"}, {Name: "Env", Doc: "the env being visualized"}, {Name: "EnvName", Doc: "name of current env -- number is NData index"}, {Name: "SceneEditor", Doc: "3D visualization of the Scene"}, {Name: "Scene2D", Doc: "2D visualization of the Scene"}, {Name: "MatColors", Doc: "list of material colors"}, {Name: "StateColors", Doc: "internal state colors"}, {Name: "WallSize", Doc: "thickness (X) and height (Y) of walls"}, {Name: "State", Doc: "current internal / behavioral state"}, {Name: "Trace", Doc: "trace record of recent activity"}, {Name: "SimForm", Doc: "view of the gui obj"}, {Name: "WorldTabs", Doc: "ArmMaze TabView"}, {Name: "IsRunning", Doc: "ArmMaze is running"}, {Name: "DepthValues", Doc: "current depth map"}, {Name: "Camera", Doc: "offscreen render camera settings"}, {Name: "DepthMap", Doc: "color map to use for rendering depth map"}, {Name: "EyeRFullImage", Doc: "first-person right-eye full field view"}, {Name: "EyeRFovImage", Doc: "first-person right-eye fovea view"}, {Name: "DepthImage", Doc: "depth map bitmap view"}, {Name: "USposPlot", Doc: "plot of positive valence drives, active OFC US state, and reward"}, {Name: "USposData", Doc: "data for USPlot"}, {Name: "USnegPlot", Doc: "plot of negative valence active OFC US state, and outcomes"}, {Name: "USnegData", Doc: "data for USPlot"}, {Name: "Geom", Doc: "geometry of world"}, {Name: "World", Doc: "world"}, {Name: "View3D", Doc: "3D view of world"}, {Name: "Emery", Doc: "emer group"}, {Name: "Arms", Doc: "arms group"}, {Name: "Stims", Doc: "stims group"}, {Name: "EyeR", Doc: "Right eye of emery"}, {Name: "Contacts", Doc: "contacts from last step, for body"}}}) - -var _ = types.AddType(&types.Type{Name: "github.com/emer/axon/v2/examples/dls/armaze.Actions", IDName: "actions", Doc: "Actions is a list of mutually exclusive states\nfor tracing the behavior and internal state of Emery"}) - -var _ = types.AddType(&types.Type{Name: "github.com/emer/axon/v2/examples/dls/armaze.Env", IDName: "env", Doc: "Env implements an N-armed maze (\"bandit\")\nwith each Arm having a distinctive CS stimulus visible at the start\n(could be one of multiple possibilities) and (some probability of)\na US outcome at the end of the maze (could be either positive\nor negative, with (variable) magnitude and probability.", Fields: []types.Field{{Name: "Name", Doc: "name of environment -- Train or Test"}, {Name: "Di", Doc: "our data parallel index"}, {Name: "Config", Doc: "configuration parameters"}, {Name: "Drives", Doc: "current drive strength for each of Config.NDrives in normalized 0-1 units of each drive: 0 = first sim drive, not curiosity"}, {Name: "Arm", Doc: "arm-wise location: either facing (Pos=0) or in (Pos > 0)"}, {Name: "Pos", Doc: "current position in the Arm: 0 = at start looking in, otherwise at given distance into the arm"}, {Name: "Tick", Doc: "current integer time step since last NewStart"}, {Name: "TrgDrive", Doc: "current target drive, in paradigms where that is used (e.g., Approach)"}, {Name: "USConsumed", Doc: "Current US being consumed -- is -1 unless being consumed"}, {Name: "USValue", Doc: "reward or punishment value generated by the current US being consumed -- just the Magnitude of the US -- does NOT include any modulation by Drive"}, {Name: "JustConsumed", Doc: "just finished consuming a US -- ready to start doing something new"}, {Name: "ArmsMaxValue", Doc: "arm(s) with maximum Drive * Mag * Prob US outcomes"}, {Name: "MaxValue", Doc: "maximum value for ArmsMaxValue arms"}, {Name: "ArmsMaxUtil", Doc: "arm(s) with maximum Value outcome discounted by Effort"}, {Name: "MaxUtil", Doc: "maximum utility for ArmsMaxUtil arms"}, {Name: "ArmsNeg", Doc: "arm(s) with negative US outcomes"}, {Name: "LastAct", Doc: "last action taken"}, {Name: "Effort", Doc: "effort on current trial"}, {Name: "LastCS", Doc: "last CS seen"}, {Name: "LastUS", Doc: "last US -- previous trial"}, {Name: "JustGated", Doc: "just gated on this trial -- set by sim-- used for instinct"}, {Name: "HasGated", Doc: "has gated at some point during sequence -- set by sim -- used for instinct"}, {Name: "States", Doc: "named states -- e.g., USs, CSs, etc"}, {Name: "MaxLength", Doc: "maximum length of any arm"}, {Name: "Rand", Doc: "random number generator for the env -- all random calls must use this"}, {Name: "RandSeed", Doc: "random seed"}}}) - -var _ = types.AddType(&types.Type{Name: "github.com/emer/axon/v2/examples/dls/armaze.Paradigms", IDName: "paradigms", Doc: "Paradigms is a list of experimental paradigms that\ngovern the configuration and updating of environment\nstate over time and the appropriate evaluation criteria."}) - -var _ = types.AddType(&types.Type{Name: "github.com/emer/axon/v2/examples/dls/armaze.TraceStates", IDName: "trace-states", Doc: "TraceStates is a list of mutually exclusive states\nfor tracing the behavior and internal state of Emery"}) - -var _ = types.AddType(&types.Type{Name: "github.com/emer/axon/v2/examples/dls/armaze.TraceRec", IDName: "trace-rec", Doc: "TraceRec holds record of info for tracing behavior, state", Fields: []types.Field{{Name: "Time", Doc: "absolute time"}, {Name: "Trial", Doc: "trial counter"}, {Name: "Arm", Doc: "current arm"}, {Name: "Pos", Doc: "position in arm"}, {Name: "State", Doc: "behavioral / internal state summary"}, {Name: "Drives", Doc: "NDrives current drive state level"}}}) - -var _ = types.AddType(&types.Type{Name: "github.com/emer/axon/v2/examples/dls/armaze.StateTrace", IDName: "state-trace", Doc: "StateTrace holds trace records"}) diff --git a/examples/dls/config.go b/examples/dls/config.go deleted file mode 100644 index 818980b4..00000000 --- a/examples/dls/config.go +++ /dev/null @@ -1,157 +0,0 @@ -// 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. - -//go:build notyet - -package main - -import "cogentcore.org/core/base/mpi" - -// 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 { - - // name of config file that loads into Env.Config for setting environment parameters directly - Config string `desc:"name of config file that loads into Env.Config for setting environment parameters directly"` - - // gain on the softmax for choosing actions: lower values are more noisy - ActSoftMaxGain float32 `default:"1"` - - // number of different drive-like body states (hunger, thirst, etc), that are satisfied by a corresponding US outcome - NDrives int `default:"4"` - - // epoch when PctCortex starts increasing - PctCortexStEpc int `default:"10"` - - // number of epochs over which PctCortexMax is reached - PctCortexNEpc int `default:"1"` - - // proportion of behavioral approach sequences driven by the cortex vs. hard-coded reflexive subcortical - PctCortex float32 `edit:"-"` - - // for testing, force each env to use same seed - SameSeed bool -} - -// CurPctCortex returns current PctCortex and updates field, based on epoch counter -func (cfg *EnvConfig) CurPctCortex(epc int) float32 { - if epc >= cfg.PctCortexStEpc && cfg.PctCortex < 1 { - cfg.PctCortex = float32(epc-cfg.PctCortexStEpc) / float32(cfg.PctCortexNEpc) - if cfg.PctCortex > 1 { - cfg.PctCortex = 1 - } else { - mpi.Printf("PctCortex updated to: %g at epoch: %d\n", cfg.PctCortex, epc) - } - } - return cfg.PctCortex -} - -// ParamConfig has config parameters related to sim params -type ParamConfig struct { - - // Rubicon parameters -- can set any field/subfield on Net.Rubicon params, using standard TOML formatting - Rubicon map[string]any - - // 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:"true"` - - // 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:"16" 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:"5" min:"1"` - - // total number of epochs per run - NEpochs int `default:"100"` - - // total number of trials per epoch. Should be an even multiple of NData. - NTrials int `default:"128"` - - // how frequently (in epochs) to compute PCA on hidden representations to measure variance? - PCAInterval int `default:"10"` -} - -// LogConfig has config parameters related to logging data -type LogConfig struct { - - // if true, save final weights after each run - SaveWeights 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 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 - - // if set, open given weights file at start of training - OpenWeights string - - // environment configuration options - Env EnvConfig `display:"add-fields"` - - // parameter related configuration options - Params ParamConfig `display:"add-fields"` - - // sim running related configuration options - Run RunConfig `display:"add-fields"` - - // data logging related configuration options - Log LogConfig `display:"add-fields"` -} - -func (cfg *Config) IncludesPtr() *[]string { return &cfg.Includes } diff --git a/examples/dls/config.toml b/examples/dls/config.toml deleted file mode 100644 index cb760574..00000000 --- a/examples/dls/config.toml +++ /dev/null @@ -1,18 +0,0 @@ -GUI = true -Debug = false - -[Env] -# Config = "4.uncertain_cs.toml" -# OpenWts = "tmp.wts.gz" - -[Params] -# SaveAll = true -# Good = true - -[Run] -# GPU = true -# NData = 16 - GPU = false - NData = 1 - - diff --git a/examples/dls/dls.go b/examples/dls/dls.go deleted file mode 100644 index 9b27bf62..00000000 --- a/examples/dls/dls.go +++ /dev/null @@ -1,1125 +0,0 @@ -// 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. - -/* -dls: This project tests Dorsal Lateral Striatum Motor Action Learning. -*/ - -package main - -func main() { - -} - -/* -//go:generate core generate -add-types - -import ( - "fmt" - "log" - "math" - "os" - - "cogentcore.org/core/gi" - "cogentcore.org/core/icons" - "cogentcore.org/core/math32" - "github.com/emer/axon/v2/axon" - "github.com/emer/axon/v2/examples/dls/armaze" - "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" - "cogentcore.org/core/base/randx" - "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/paths" - "github.com/emer/emergent/v2/relpos" - "cogentcore.org/core/base/timer" - "cogentcore.org/core/base/mpi" - "cogentcore.org/core/tensor/stats/stats" - "cogentcore.org/core/tensor/table" - "cogentcore.org/core/tensor" - "cogentcore.org/core/math32/minmax" - "cogentcore.org/core/tensor/stats/split" -) - -func main() { - sim := &Sim{} - sim.New() - sim.ConfigAll() - if sim.Config.GUI { - sim.RunGUI() - } 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 `new-window:"+"` - - // the network -- click to view / edit parameters for layers, paths, etc - Net *axon.Network `new-window:"+" display:"no-inline"` - - // if true, stop running at end of a sequence (for NetView Di data parallel index) - StopOnSeq bool - - // if true, stop running when an error programmed into the code occurs - StopOnErr bool - - // network parameter management - Params emer.NetParams `display:"add-fields"` - - // contains looper control loops for running sim - Loops *looper.Manager `new-window:"+" display:"no-inline"` - - // contains computed statistic values - Stats estats.Stats `new-window:"+"` - - // Contains all the logs and information about the logs.' - Logs elog.Logs `new-window:"+"` - - // Environments - Envs env.Envs `new-window:"+" display:"no-inline"` - - // axon timing parameters and state - Context axon.Context `new-window:"+"` - - // netview update parameters - ViewUpdate netview.ViewUpdate `display:"add-fields"` - - // manages all the gui elements - GUI egui.GUI `display:"-"` - - // gui for viewing env - EnvGUI *armaze.GUI `display:"-"` - - // a list of random seeds to use for each run - RandSeeds randx.Seeds `display:"-"` - - // testing data, from -test arg - TestData map[string]float32 `display:"-"` -} - -// New creates new blank elements and initializes defaults -func (ss *Sim) New() { - ss.Net = axon.NewNetwork("") - econfig.Config(&ss.Config, "config.toml") - ss.Params.Config(ParamSets, ss.Config.Params.Sheet, ss.Config.Params.Tag, ss.Net) - ss.Stats.Init() - ss.RandSeeds.Init(100) // max 100 runs - ss.InitRandSeed(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 *armaze.Env - if newEnv { - trn = &armaze.Env{} - } else { - trn = ss.Envs.ByModeDi(etime.Train, di).(*armaze.Env) - } - - // note: names must be standard here! - trn.Name = env.ModeDi(etime.Train, di) - trn.Defaults() - trn.RandSeed = 73 - if !ss.Config.Env.SameSeed { - trn.RandSeed += int64(di) * 73 - } - trn.Config.NDrives = ss.Config.Env.NDrives - if ss.Config.Env.Config != "" { - econfig.Config(&trn.Config, ss.Config.Env.Config) - } - trn.ConfigEnv(di) - trn.Validate() - trn.Init(0) - - // note: names must be in place when adding - ss.Envs.Add(trn) - if di == 0 { - ss.Config.Rubicon(trn) - } - } -} - -func (ss *Sim) Config.Rubicon(trn *armaze.Env) { - pv := &ss.Net.Rubicon - pv.SetNUSs(&ss.Context, trn.Config.NDrives, 1) - pv.Defaults() - pv.USs.PVposGain = 2 // higher = more pos reward (saturating logistic func) - pv.USs.PVnegGain = .1 // global scaling of PV neg level -- was 1 - - pv.USs.USnegGains[0] = 0.1 // time: if USneg pool is saturating, reduce - pv.USs.USnegGains[1] = 0.1 // effort: if USneg pool is saturating, reduce - pv.USs.USnegGains[2] = 2 // big salient input! - - pv.USs.PVnegWts[0] = 0.02 // time: controls overall PVneg -- if too high, not enough reward.. - pv.USs.PVnegWts[1] = 0.02 // effort: controls overall PVneg -- if too high, not enough reward.. - pv.USs.PVnegWts[2] = 1 - - pv.Drive.DriveMin = 0.5 // 0.5 -- should be - pv.Urgency.U50 = 10 - if ss.Config.Params.Rubicon != nil { - params.ApplyMap(pv, ss.Config.Params.Rubicon, ss.Config.Debug) - } -} - -func (ss *Sim) ConfigNet(net *axon.Network) { - ctx := &ss.Context - ev := ss.Envs.ByModeDi(etime.Train, 0).(*armaze.Env) - net.InitName(net, "Dls") - net.SetMaxData(ctx, ss.Config.Run.NData) - net.SetRandSeed(ss.RandSeeds[0]) // init new separate random seed, using run = 0 - - nuBgY := 5 - nuBgX := 5 - nuCtxY := 6 - nuCtxX := 6 - nAct := int(armaze.ActionsN) - popY := 4 - popX := 4 - space := float32(2) - - full := paths.NewFull() - // pathClass := "PFCPath" - - ny := ev.Config.Params.NYReps - narm := ev.Config.NArms - - vta, _, _ := net.AddVTALHbLDTLayers(relpos.Behind, space) - usPos, usNeg := net.AddUSLayers(popY, popX, relpos.Behind, space) - pvPos, _ := net.AddPVLayers(popY, popX, relpos.Behind, space) - drv := net.AddDrivesLayer(ctx, popY, popX) - - cs, csP := net.AddInputPulv2D("CS", ny, ev.Config.NCSs, space) - pos, posP := net.AddInputPulv2D("Pos", ny, ev.MaxLength+1, space) - arm, armP := net.AddInputPulv2D("Arm", ny, narm, space) - - vSgpi := net.AddLayer2D("VSgpi", axon.InputLayer, ny, nuBgX) // fake ventral BG - ofc := net.AddLayer2D("OFC", axon.InputLayer, ny, nuBgX) // fake OFC - - /////////////////////////////////////////// - // Dorsal lateral Striatum / BG - - dSMtxGo, dSMtxNo, _, dSSTNP, dSSTNS, dSGPi := net.AddBG("Ds", 1, 4, nuBgY, nuBgX, nuBgY, nuBgX, space) - dSMtxGo.SetClass("DLSMatrixLayer") - dSMtxNo.SetClass("DLSMatrixLayer") - - // Spiral the BG loops so that goal selection influencces action selection. - // vSSTNp := ss.Net.LayerByName("VsSTNp") - // vSSTNs := ss.Net.LayerByName("VsSTNs") - // net.ConnectLayers(vSSTNp, dSGPi, full, axon.ForwardPath).SetClass(vSSTNp.SendPaths[0].Cls) - // net.ConnectLayers(vSSTNs, dSGPi, full, axon.ForwardPath).SetClass(vSSTNs.SendPaths[0].Cls) - - /////////////////////////////////////////// - // M1, VL, ALM - - act := net.AddLayer2D("Act", axon.InputLayer, ny, nAct) // Action: what is actually done - vl := net.AddPulvLayer2D("VL", ny, nAct) // VL predicts brainstem Action - vl.SetBuildConfig("DriveLayName", act.Name) - - m1, m1CT, m1PT, m1PTp, m1VM := net.AddPFC2D("M1", "VM", nuCtxY, nuCtxX, false, space) - m1P := net.AddPulvForSuper(m1, space) - - alm, almCT, almPT, almPTp, almMD := net.AddPFC2D("ALM", "MD", nuCtxY, nuCtxX, true, space) - _ = almPT - - net.ConnectLayers(vSgpi, almMD, full, axon.InhibPath) - - net.ConnectToPFCBidir(m1, m1P, alm, almCT, almPTp, full) // alm predicts m1 - - // vl is a predictive thalamus but we don't have direct access to its source - // net.ConnectToPulv(m1, m1CT, vl, full, full, pathClass) - net.ConnectToPFC(nil, vl, m1, m1CT, m1PTp, full) // m1 predicts vl - net.ConnectToPFC(nil, vl, alm, almCT, almPTp, full) // alm predicts vl - - // sensory inputs guiding action - // note: alm gets effort, pos via predictive coding below - - // these pathways are *essential* -- must get current state here - net.ConnectLayers(m1, vl, full, axon.ForwardPath).SetClass("ToVL") - net.ConnectLayers(alm, vl, full, axon.ForwardPath).SetClass("ToVL") - - // alm predicts cs, pos etc - net.ConnectToPFCBack(cs, csP, alm, almCT, almPTp, full) - net.ConnectToPFCBack(pos, posP, alm, almCT, almPTp, full) - net.ConnectToPFCBack(arm, armP, alm, almCT, almPTp, full) - - net.ConnectToPFCBack(cs, csP, m1, m1CT, m1PTp, full) - net.ConnectToPFCBack(pos, posP, m1, m1CT, m1PTp, full) - net.ConnectToPFCBack(arm, armP, m1, m1CT, m1PTp, full) - - net.ConnectLayers(dSGPi, m1VM, full, axon.InhibPath) - - // m1 and all of its inputs go to DS. - for _, dSLy := range []*axon.Layer{dSMtxGo, dSMtxNo, dSSTNP, dSSTNS} { - net.ConnectToMatrix(m1, dSLy, full) - net.ConnectToMatrix(m1PT, dSLy, full) - net.ConnectToMatrix(m1PTp, dSLy, full) - net.ConnectToMatrix(alm, dSLy, full) - net.ConnectToMatrix(almPT, dSLy, full) - net.ConnectToMatrix(almPTp, dSLy, full) - } - - //////////////////////////////////////////////// - // position - - usPos.PlaceRightOf(vta, space) - pvPos.PlaceRightOf(usPos, space) - drv.PlaceBehind(usNeg, space) - - cs.PlaceAbove(vta) - pos.PlaceRightOf(cs, space) - arm.PlaceRightOf(pos, space) - - vl.PlaceRightOf(arm, space) - act.PlaceBehind(vl, space) - - vSgpi.PlaceBehind(csP, space) - ofc.PlaceRightOf(vSgpi, space) - - dSGPi.PlaceRightOf(pvPos, space) - dSMtxNo.PlaceBehind(dSMtxGo, space) - - m1.PlaceAbove(dSGPi) - m1P.PlaceBehind(m1VM, space) - alm.PlaceRightOf(m1, space) - - net.Build(ctx) - net.Defaults() - net.SetNThreads(ss.Config.Run.NThreads) - ss.ApplyParams() - ss.Net.InitWeights(ctx) -} - -func (ss *Sim) ApplyParams() { - net := ss.Net - ss.Params.SetAll() // first hard-coded defaults - - // params that vary as number of CSs - ev := ss.Envs.ByModeDi(etime.Train, 0).(*armaze.Env) - - nCSTot := ev.Config.NCSs - - cs := net.LayerByName("CS") - cs.Params.Inhib.ActAvg.Nominal = 0.32 / float32(nCSTot) - csp := net.LayerByName("CSP") - csp.Params.Inhib.ActAvg.Nominal = 0.32 / float32(nCSTot) - - // then apply config-set params. - 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.InitRandSeed(0) - ss.ConfigEnv() // re-config env just in case a different set of patterns was - // selected or patterns have been modified etc - ss.GUI.StopNow = false - ss.ApplyParams() - ss.Net.GPU.SyncParamsToGPU() - ss.NewRun() - ss.ViewUpdate.RecordSyns() - ss.ViewUpdate.Update() -} - -// InitRandSeed initializes the random seed based on current training run number -func (ss *Sim) InitRandSeed(run int) { - ss.RandSeeds.Set(run) - ss.RandSeeds.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).(*armaze.Env) - - // note: sequence stepping does not work in NData > 1 mode -- just going back to raw trials - trls := int(math32.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.Trial, trls, ss.Config.Run.NData). - AddTime(etime.Cycle, 200) - - axon.LooperStdPhases(man, &ss.Context, ss.Net, 150, 199) // plus phase timing - axon.LooperSimCycleAndLearn(man, ss.Net, &ss.Context, &ss.ViewUpdate) // std algo code - - for m := range man.Stacks { - stack := man.Stacks[m] - stack.Loops[etime.Trial].OnStart.Add("ApplyInputs", func() { - ss.ApplyInputs() - }) - } - - // note: phase is shared between all stacks! - plusPhase, _ := man.Stacks[etime.Train].Loops[etime.Cycle].EventByName("PlusPhase") - plusPhase.OnEvent.InsertBefore("PlusPhase:Start", "TakeAction", func() { - // note: critical to have this happen *after* MinusPhase:End and *before* PlusPhase:Start - // because minus phase end has gated info, and plus phase start applies action input - ss.TakeAction(ss.Net) - }) - - man.GetLoop(etime.Train, etime.Run).OnStart.Add("NewRun", ss.NewRun) - - ///////////////////////////////////////////// - // Logging - - man.GetLoop(etime.Train, etime.Epoch).OnEnd.Add("PCAStats", func() { - trnEpc := man.Stacks[etime.Train].Loops[etime.Epoch].Counter.Cur - if (ss.Config.Run.PCAInterval > 0) && (trnEpc%ss.Config.Run.PCAInterval == 0) { - axon.PCAStats(ss.Net, &ss.Logs, &ss.Stats) - ss.Logs.ResetLog(etime.Analyze, etime.Trial) - } - }) - - man.AddOnEndToAll("Log", ss.Log) - axon.LooperResetLogBelow(man, &ss.Logs) - if ss.Config.GUI { - man.GetLoop(etime.Train, etime.Trial).OnStart.Add("ResetDebugTrial", func() { - di := uint32(ss.ViewUpdate.View.Di) - hadRew := axon.GlbV(&ss.Context, di, axon.GvHadRew) > 0 - if hadRew { - ss.Logs.ResetLog(etime.Debug, etime.Trial) - } - }) - } - - man.GetLoop(etime.Train, etime.Trial).OnEnd.Add("LogAnalyze", func() { - trnEpc := man.Stacks[etime.Train].Loops[etime.Epoch].Counter.Cur - if (ss.Config.Run.PCAInterval > 0) && (trnEpc%ss.Config.Run.PCAInterval == 0) { - ss.Log(etime.Analyze, etime.Trial) - } - }) - - if ss.Config.Log.Testing { - man.GetLoop(etime.Train, etime.Trial).OnEnd.Add("RecordTestData", func() { - ss.RecordTestData() - }) - } - - // Save weights to file, to look at later - man.GetLoop(etime.Train, etime.Run).OnEnd.Add("SaveWeights", func() { - ctrString := ss.Stats.PrintValues([]string{"Run", "Epoch"}, []string{"%03d", "%05d"}, "_") - axon.SaveWeightsIfConfigSet(ss.Net, ss.Config.Log.SaveWeights, ctrString, ss.Stats.String("RunName")) - }) - - //////////////////////////////////////////// - // GUI - - if !ss.Config.GUI { - if ss.Config.Log.NetData { - man.GetLoop(etime.Test, etime.Trial).Main.Add("NetDataRecord", func() { - ss.GUI.NetDataRecord(ss.ViewUpdate.Text) - }) - } - } else { - axon.LooperUpdateNetView(man, &ss.ViewUpdate, ss.Net, ss.NetViewCounters) - axon.LooperUpdatePlots(man, &ss.GUI) - - man.GetLoop(etime.Train, etime.Trial).OnEnd.Add("UpdateWorldGui", func() { - ss.UpdateEnvGUI(etime.Train) - }) - } - - if ss.Config.Debug { - mpi.Println(man.DocString()) - } - ss.Loops = man -} - -// TakeAction takes action for this step, using either decoded cortical -// or reflexive subcortical action from env. -// Called at end of minus phase. However, it can still gate sometimes -// after this point, so that is dealt with at end of plus phase. -func (ss *Sim) TakeAction(net *axon.Network) { - ctx := &ss.Context - pv := &ss.Net.Rubicon - // vlly := ss.Net.LayerByName("VL") - for di := 0; di < int(ctx.NData); di++ { - diu := uint32(di) - ev := ss.Envs.ByModeDi(ctx.Mode, di).(*armaze.Env) - netAct := ss.DecodeAct(ev, di) - genAct := ev.InstinctAct() - trSt := armaze.TrSearching - if ev.HasGated { - trSt = armaze.TrApproaching - } - ss.Stats.SetStringDi("NetAction", di, netAct.String()) - ss.Stats.SetStringDi("Instinct", di, genAct.String()) - if netAct == genAct { - ss.Stats.SetFloatDi("ActMatch", di, 1) - } else { - ss.Stats.SetFloatDi("ActMatch", di, 0) - } - actAct := netAct // net always driving - if ev.USConsumed >= 0 { - actAct = armaze.Consume // have to do it 2x to reset -- just a random timing thing - } - ss.Stats.SetStringDi("ActAction", di, actAct.String()) - - ev.Action(actAct.String(), nil) - ss.ApplyAction(di) - - switch { - case pv.HasPosUS(ctx, diu): - trSt = armaze.TrRewarded - case actAct == armaze.Consume: - trSt = armaze.TrConsuming - } - if axon.GlobalScalars[axon.GvGiveUp, diu] > 0 { - trSt = armaze.TrGiveUp - } - ss.Stats.SetIntDi("TraceStateInt", di, int(trSt)) - ss.Stats.SetStringDi("TraceState", di, trSt.String()) - - } - ss.Net.ApplyExts(ctx) - ss.Net.GPU.SyncPoolsToGPU() -} - -// DecodeAct decodes the VL ActM state to find closest action pattern -func (ss *Sim) DecodeAct(ev *armaze.Env, di int) armaze.Actions { - vt := ss.Stats.SetLayerTensor(ss.Net, "VL", "CaD", di) // was "Act" - return armaze.Actions(ss.SoftMaxChoose(ev, vt)) -} - -func (ss *Sim) SoftMaxChoose(ev *armaze.Env, vt *tensor.Float32) int { - dx := vt.DimSize(1) - var tot float32 - probs := make([]float32, dx) - for i := range probs { - var sum float32 - for j := 0; j < ev.Config.Params.NYReps; j++ { - sum += vt.Value([]int{j, i}) - } - p := math32.FastExp(ss.Config.Env.ActSoftMaxGain * sum) - probs[i] = p - tot += p - } - for i, p := range probs { - probs[i] = p / tot - } - chs := randx.PChoose32(probs, -1) - return chs -} - -func (ss *Sim) ApplyAction(di int) { - ctx := &ss.Context - net := ss.Net - ev := ss.Envs.ByModeDi(ss.Context.Mode, di).(*armaze.Env) - ap := ev.State("Action") - ly := net.LayerByName("Act") - ly.ApplyExt(ctx, uint32(di), ap) -} - -// 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() { - ctx := &ss.Context - ss.Stats.SetString("Debug", "") // start clear - net := ss.Net - lays := []string{"Pos", "Arm", "CS", "VSgpi", "OFC"} - - ss.Net.InitExt(ctx) - for di := uint32(0); di < ctx.NData; di++ { - ev := ss.Envs.ByModeDi(ctx.Mode, int(di)).(*armaze.Env) - giveUp := axon.GlobalScalars[axon.GvGiveUp, di] > 0 - if giveUp { - ev.JustConsumed = true // triggers a new start -- we just consumed the giving up feeling :) - } - ev.Step() - if ev.Tick == 0 { - ev.ExValueUtil(&ss.Net.Rubicon, ctx) - } - for _, lnm := range lays { - ly := net.LayerByName(lnm) - itsr := ev.State(lnm) - ly.ApplyExt(ctx, di, itsr) - } - ss.Apply.Rubicon(ctx, ev, di) - } - ss.Net.ApplyExts(ctx) -} - -// Apply.Rubicon applies current Rubicon values to Context.Rubicon, -// from given trial data. -func (ss *Sim) ApplyRubicon(ctx *axon.Context, ev *armaze.Env, di uint32) { - pv := &ss.Net.Rubicon - pv.NewState(ctx, di, &ss.Net.Rand) // first before anything else is updated - pv.EffortUrgencyUpdate(ctx, di, 1) // note: effort can vary with terrain! - if ev.USConsumed >= 0 { - pv.SetUS(ctx, di, axon.Positive, ev.USConsumed, ev.USValue) - } - pv.SetDrives(ctx, di, 0.5, ev.Drives...) - pv.Step(ctx, di, &ss.Net.Rand) -} - -// 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.InitRandSeed(ss.Loops.GetLoop(etime.Train, etime.Run).Counter.Cur) - for di := 0; di < int(ctx.NData); di++ { - ss.Envs.ByModeDi(etime.Train, di).Init(0) - } - ctx.Reset() - ctx.Mode = etime.Train - ss.Net.InitWeights(ctx) - ss.StatsInit() - ss.StatCounters(0) - ss.Logs.ResetLog(etime.Train, etime.Epoch) - if ss.Config.OpenWeights != "" { - ss.Net.OpenWeightsJSON(core.Filename(ss.Config.OpenWeights)) - log.Println("Opened weights:", ss.Config.OpenWeights) - } -} - -//////////////////////////////////////////////////////////////////////////////////////////// -// Stats - -// StatsInit initializes all the statistics. -// called at start of new run -func (ss *Sim) StatsInit() { - ss.Stats.SetInt("Di", 0) - ss.Stats.SetFloat("Pos", 0) - ss.Stats.SetFloat("Drive", 0) - ss.Stats.SetFloat("CS", 0) - ss.Stats.SetFloat("US", 0) - ss.Stats.SetFloat("HasRew", 0) - ss.Stats.SetString("NetAction", "") - ss.Stats.SetString("Instinct", "") - ss.Stats.SetString("ActAction", "") - ss.Stats.SetString("TraceState", "") - ss.Stats.SetInt("TraceStateInt", 0) - ss.Stats.SetFloat("ActMatch", 0) - - ss.Stats.SetFloat("Rew", 0) - ss.Stats.SetFloat("DA", 0) - ss.Stats.SetFloat("RewPred", 0) - ss.Stats.SetFloat("DA_NR", 0) - ss.Stats.SetFloat("RewPred_NR", 0) - ss.Stats.SetFloat("DA_GiveUp", 0) - - ss.Stats.SetFloat("Time", 0) - ss.Stats.SetFloat("Effort", 0) - ss.Stats.SetFloat("Urgency", 0) - - ss.Stats.SetFloat("NegUSOutcome", 0) - ss.Stats.SetFloat("PVpos", 0) - ss.Stats.SetFloat("PVneg", 0) - - ss.Stats.SetFloat("PVposEst", 0) - ss.Stats.SetFloat("PVposEstDisc", 0) - ss.Stats.SetFloat("GiveUpDiff", 0) - ss.Stats.SetFloat("GiveUpProb", 0) - ss.Stats.SetFloat("GiveUp", 0) - - ss.Stats.SetString("Debug", "") // special debug notes per trial -} - -// StatCounters saves current counters to Stats, so they are available for logging etc -func (ss *Sim) StatCounters(di int) { - ctx := &ss.Context - mode := ctx.Mode - ss.ActionStatsDi(di) - ev := ss.Envs.ByModeDi(mode, di).(*armaze.Env) - ss.Loops.Stacks[mode].CountersToStats(&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(ctx.Cycle)) - ss.Stats.SetFloat32("Pos", float32(ev.Pos)) - ss.Stats.SetFloat32("Arm", float32(ev.Arm)) - // ss.Stats.SetFloat32("Drive", float32(ev.Drive)) - ss.Stats.SetFloat32("CS", float32(ev.CurCS())) - ss.Stats.SetFloat32("US", float32(ev.USConsumed)) - ss.Stats.SetFloat32("HasRew", axon.GlobalScalars[axon.GvHasRew), uint32(di)] - ss.Stats.SetString("TrialName", "trl") // todo: could have dist, US etc -} - -func (ss *Sim) NetViewCounters(tm etime.Times) { - if ss.ViewUpdate.View == nil { - return - } - di := ss.ViewUpdate.View.Di - if tm == etime.Trial { - ss.TrialStats(di) // get trial stats for current di - } - ss.StatCounters(di) - ss.ViewUpdate.Text = ss.Stats.Print([]string{"Run", "Epoch", "Trial", "Di", "Cycle", "NetAction", "Instinct", "ActAction", "ActMatch", "JustGated", "Should", "Rew"}) -} - -// TrialStats computes the trial-level statistics. -// Aggregation is done directly from log data. -func (ss *Sim) TrialStats(di int) { - diu := uint32(di) - ctx := &ss.Context - pv := &ss.Net.Rubicon - nan := math.NaN() - ss.Stats.SetFloat("DA", nan) - ss.Stats.SetFloat("RewPred", nan) - ss.Stats.SetFloat("Rew", nan) - ss.Stats.SetFloat("HasRew", nan) - ss.Stats.SetFloat("DA_NR", nan) - ss.Stats.SetFloat("RewPred_NR", nan) - ss.Stats.SetFloat("DA_GiveUp", nan) - if pv.HasPosUS(ctx, diu) { - ss.Stats.SetFloat32("DA", axon.GlobalScalars[axon.GvDA), diu] - ss.Stats.SetFloat32("RewPred", axon.GlobalScalars[axon.GvRewPred), diu] // gets from VSPatch or RWPred etc - ss.Stats.SetFloat32("Rew", axon.GlobalScalars[axon.GvRew), diu] - ss.Stats.SetFloat("HasRew", 1) - } else { - if axon.GlbV(ctx, diu, axon.GvGiveUp) > 0 || axon.GlbV(ctx, diu, axon.GvNegUSOutcome) > 0 { - ss.Stats.SetFloat32("DA_GiveUp", axon.GlobalScalars[axon.GvDA), diu] - } else { - ss.Stats.SetFloat32("DA_NR", axon.GlobalScalars[axon.GvDA), diu] - ss.Stats.SetFloat32("RewPred_NR", axon.GlobalScalars[axon.GvRewPred), diu] - ss.Stats.SetFloat("HasRew", 0) - } - } - - ss.Stats.SetFloat32("Time", axon.GlobalScalars[axon.GvTime), diu] - ss.Stats.SetFloat32("Effort", axon.GlobalScalars[axon.GvEffort), diu] - ss.Stats.SetFloat32("Urgency", axon.GlobalScalars[axon.GvUrgency), diu] - - ss.Stats.SetFloat32("NegUSOutcome", axon.GlobalScalars[axon.GvNegUSOutcome), diu] - ss.Stats.SetFloat32("PVpos", axon.GlobalScalars[axon.GvPVpos), diu] - ss.Stats.SetFloat32("PVneg", axon.GlobalScalars[axon.GvPVneg), diu] - - ss.Stats.SetFloat32("PVposEst", axon.GlobalScalars[axon.GvPVposEst), diu] - ss.Stats.SetFloat32("PVposEstDisc", axon.GlobalScalars[axon.GvPVposEstDisc), diu] - ss.Stats.SetFloat32("GiveUpDiff", axon.GlobalScalars[axon.GvGiveUpDiff), diu] - ss.Stats.SetFloat32("GiveUpProb", axon.GlobalScalars[axon.GvGiveUpProb), diu] - ss.Stats.SetFloat32("GiveUp", axon.GlobalScalars[axon.GvGiveUp), diu] - - ss.Stats.SetFloat32("ACh", axon.GlobalScalars[axon.GvACh), diu] - ss.Stats.SetFloat32("AChRaw", axon.GlobalScalars[axon.GvAChRaw), diu] -} - -// ActionStatsDi copies the action info from given data parallel index -// into the global action stats -func (ss *Sim) ActionStatsDi(di int) { - if _, has := ss.Stats.Strings[estats.DiName("NetAction", di)]; !has { - return - } - ss.Stats.SetString("NetAction", ss.Stats.StringDi("NetAction", di)) - ss.Stats.SetString("Instinct", ss.Stats.StringDi("Instinct", di)) - ss.Stats.SetFloat("ActMatch", ss.Stats.FloatDi("ActMatch", di)) - ss.Stats.SetString("ActAction", ss.Stats.StringDi("ActAction", di)) - ss.Stats.SetString("TraceState", ss.Stats.StringDi("TraceState", di)) - ss.Stats.SetInt("TraceStateInt", ss.Stats.IntDi("TraceStateInt", di)) -} - -////////////////////////////////////////////////////////////////////////////// -// 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.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.Trial, "NetAction", "Instinct", "ActAction", "TraceState") - - ss.Logs.AddPerTrlMSec("PerTrlMSec", etime.Run, etime.Epoch, etime.Trial) - - ss.ConfigLogItems() - - axon.LogAddPulvPhaseDiffItems(&ss.Logs, ss.Net, etime.Train, etime.Run, etime.Epoch, etime.Trial) - - // ss.ConfigActRFs() - - layers := ss.Net.LayersByType(axon.SuperLayer, axon.CTLayer, axon.TargetLayer, axon.CeMLayer) - axon.LogAddDiagnosticItems(&ss.Logs, layers, etime.Train, etime.Epoch, etime.Trial) - axon.LogInputLayer(&ss.Logs, ss.Net, etime.Train) - - // todo: PCA items should apply to CT layers too -- pass a type here. - axon.LogAddPCAItems(&ss.Logs, ss.Net, etime.Train, etime.Run, etime.Epoch, etime.Trial) - - ss.Logs.PlotItems("ActMatch", "Rew", "RewPred") - - 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.Test, etime.Run, etime.Epoch, etime.Trial, etime.Cycle) - // note: Analyze not plotted by default - ss.Logs.SetMeta(etime.Train, etime.Run, "LegendCol", "RunName") - // ss.Logs.SetMeta(etime.Test, etime.Cycle, "LegendCol", "RunName") - - axon.LayerActsLogConfig(ss.Net, &ss.Logs) -} - -func (ss *Sim) ConfigLogItems() { - ss.Logs.AddStatAggItem("ActMatch", etime.Run, etime.Epoch, etime.Trial) - - li := ss.Logs.AddStatAggItem("Rew", etime.Run, etime.Epoch, etime.Trial) - li.FixMin = false - li = ss.Logs.AddStatAggItem("DA", etime.Run, etime.Epoch, etime.Trial) - li.FixMin = false - li = ss.Logs.AddStatAggItem("ACh", etime.Run, etime.Epoch, etime.Trial) - li.FixMin = false - li = ss.Logs.AddStatAggItem("AChRaw", etime.Run, etime.Epoch, etime.Trial) - li.FixMin = false - li = ss.Logs.AddStatAggItem("RewPred", etime.Run, etime.Epoch, etime.Trial) - li.FixMin = false - li = ss.Logs.AddStatAggItem("DA_NR", etime.Run, etime.Epoch, etime.Trial) - li.FixMin = false - li = ss.Logs.AddStatAggItem("RewPred_NR", etime.Run, etime.Epoch, etime.Trial) - li.FixMin = false - li = ss.Logs.AddStatAggItem("DA_GiveUp", etime.Run, etime.Epoch, etime.Trial) - li.FixMin = false - - ss.Logs.AddStatAggItem("Time", etime.Run, etime.Epoch, etime.Trial) - ss.Logs.AddStatAggItem("Effort", etime.Run, etime.Epoch, etime.Trial) - ss.Logs.AddStatAggItem("Urgency", etime.Run, etime.Epoch, etime.Trial) - - ss.Logs.AddStatAggItem("NegUSOutcome", etime.Run, etime.Epoch, etime.Trial) - ss.Logs.AddStatAggItem("PVpos", etime.Run, etime.Epoch, etime.Trial) - ss.Logs.AddStatAggItem("PVneg", etime.Run, etime.Epoch, etime.Trial) - - ss.Logs.AddStatAggItem("PVposEst", etime.Run, etime.Epoch, etime.Trial) - ss.Logs.AddStatAggItem("PVposEstDisc", etime.Run, etime.Epoch, etime.Trial) - ss.Logs.AddStatAggItem("GiveUpDiff", etime.Run, etime.Epoch, etime.Trial) - ss.Logs.AddStatAggItem("GiveUpProb", etime.Run, etime.Epoch, etime.Trial) - ss.Logs.AddStatAggItem("GiveUp", etime.Run, etime.Epoch, etime.Trial) - - // Add a special debug message -- use of etime.Debug triggers - // inclusion - if ss.Config.GUI { - ss.Logs.AddStatStringItem(etime.Debug, etime.Trial, "Debug") - } - - ss.Logs.AddItem(&elog.Item{ - Name: "ActCor", - Type: reflect.Float64, - CellShape: []int{int(armaze.ActionsN)}, - DimNames: []string{"Acts"}, - // Plot: true, - Range: minmax.F32{Min: 0}, - TensorIndex: -1, // plot all values - Write: elog.WriteMap{ - etime.Scope(etime.Train, etime.Epoch): func(ctx *elog.Context) { - ix := ctx.Logs.IndexView(ctx.Mode, etime.Trial) - spl := split.GroupBy(ix, []string{"Instinct"}) - split.AggTry(spl, "ActMatch", stats.Mean) - ags := spl.AggsToTable(table.ColumnNameOnly) - ss.Logs.MiscTables["ActCor"] = ags - ctx.SetTensor(ags.Columns[0]) // cors - }}}) - for act := armaze.Actions(0); act < armaze.ActionsN; act++ { // per-action % correct - anm := act.String() - ss.Logs.AddItem(&elog.Item{ - Name: anm + "Cor", - Type: reflect.Float64, - // Plot: true, - Range: minmax.F32{Min: 0}, - Write: elog.WriteMap{ - etime.Scope(etime.Train, etime.Epoch): func(ctx *elog.Context) { - ags := ss.Logs.MiscTables["ActCor"] - rw := ags.RowsByString("Instinct", anm, table.Equals, table.UseCase) - if len(rw) > 0 { - ctx.SetFloat64(ags.Float("ActMatch", rw[0])) - } - }}}) - } -} - -// Log is the main logging function, handles special things for different scopes -func (ss *Sim) Log(mode etime.Modes, time etime.Times) { - ctx := &ss.Context - pv := &ss.Net.Rubicon - if mode != etime.Analyze && mode != etime.Debug { - ctx.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 /// not doing cycle-level logging -- too slow for gpu in general - // row = ss.Stats.Int("Cycle") - case time == etime.Trial: - if mode == etime.Train { - for di := 0; di < int(ctx.NData); di++ { - diu := uint32(di) - ss.TrialStats(di) - ss.StatCounters(di) - ss.Logs.LogRowDi(mode, time, row, di) - if !pv.HasPosUS(ctx, diu) && axon.GlobalScalars[axon.GvVSMatrixHasGated, diu] > 0 { // maint - axon.LayerActsLog(ss.Net, &ss.Logs, di, &ss.GUI) - } - if ss.ViewUpdate.View != nil && di == ss.ViewUpdate.View.Di { - drow := ss.Logs.Table(etime.Debug, time).Rows - ss.Logs.LogRow(etime.Debug, time, drow) - if ss.StopOnSeq { - hasRew := axon.GlobalScalars[axon.GvHasRew, uint32(di)] > 0 - if hasRew { - ss.Loops.Stop(etime.Trial) - } - } - ss.GUI.UpdateTableView(etime.Debug, etime.Trial) - } - // if ss.Stats.Float("GatedEarly") > 0 { - // fmt.Printf("STOPPED due to gated early: %d %g\n", ev.US, ev.Rew) - // ss.Loops.Stop(etime.Trial) - // } - // ev := ss.Envs.ByModeDi(etime.Train, di).(*armaze.Env) - // if ss.StopOnErr && trnEpc > 5 && ss.Stats.Float("MaintEarly") > 0 { - // fmt.Printf("STOPPED due to early maint for US: %d\n", ev.US) - // ss.Loops.Stop(etime.Trial) - // } - } - return // don't do reg - } - case mode == etime.Train && time == etime.Epoch: - axon.LayerActsLogAvg(ss.Net, &ss.Logs, &ss.GUI, true) // reset recs - } - - ss.Logs.LogRow(mode, time, row) // also logs to file, etc -} - -//////////////////////////////////////////////////////////////////////////////////////////// -// GUI - -func (ss *Sim) UpdateEnvGUI(mode etime.Modes) { - di := ss.GUI.ViewUpdate.View.Di - // diu := uint32(di) - ev := ss.Envs.ByModeDi(mode, di).(*armaze.Env) - ctx := &ss.Context - net := ss.Net - /* - pv := &net.Rubicon - dp := ss.EnvGUI.USposData - for i := uint32(0); i < np; i++ { - drv := axon.GlbUSposV(ctx, diu, axon.GvDrives, i) - us := axon.GlbUSposV(ctx, diu, axon.GvUSpos, i) - ofcP := ofcPosUS.Pool(i+1, diu) - ofc := ofcP.AvgMax.CaD.Plus.Avg * ofcmul - dp.SetFloat("Drive", int(i), float64(drv)) - dp.SetFloat("USin", int(i), float64(us)) - dp.SetFloat("OFC", int(i), float64(ofc)) - } - dn := ss.EnvGUI.USnegData - nn := pv.NNegUSs - for i := uint32(0); i < nn; i++ { - us := axon.GlbUSneg(ctx, diu, axon.GvUSneg, i) - ofcP := ofcNegUS.Pool(i+1, diu) - ofc := ofcP.AvgMax.CaD.Plus.Avg * ofcmul - dn.SetFloat("USin", int(i), float64(us)) - dn.SetFloat("OFC", int(i), float64(ofc)) - } - ss.EnvGUI.USposPlot.Async(ss.EnvGUI.USposPlot.UpdatePlot) - ss.EnvGUI.USnegPlot.Async(ss.EnvGUI.USnegPlot.UpdatePlot) - / - ss.EnvGUI.UpdateWorld(ctx, ev, net, armaze.TraceStates(ss.Stats.IntDi("TraceStateInt", di))) -} - -// ConfigGUI configures the Cogent Core GUI interface for this simulation. -func (ss *Sim) ConfigGUI() { - title := "DLS: Dorsal Lateral Striatum motor learning" - ss.GUI.MakeBody(ss, "dls", title, `This project tests motor sequence learning in the DLS dorsal lateral striatum and associated motor cortex. See axon on GitHub.

`) - ss.GUI.CycleUpdateInterval = 20 - - nv := ss.GUI.AddNetView("Network") - nv.Options.MaxRecs = 300 - nv.Options.LayerNameSize = 0.04 - nv.SetNet(ss.Net) - ss.ViewUpdate.Config(nv, etime.Phase, etime.Phase) - - nv.SceneXYZ().Camera.Pose.Pos.Set(0, 2.3, 1.8) - nv.SceneXYZ().Camera.LookAt(math32.Vector3{}, math32.Vec3(0, 1, 0)) - - ss.GUI.ViewUpdate = &ss.ViewUpdate - - ss.GUI.AddPlots(title, &ss.Logs) - - ss.GUI.AddTableView(&ss.Logs, etime.Debug, etime.Trial) - - axon.LayerActsLogConfigGUI(&ss.Logs, &ss.GUI) - - ss.GUI.Body.AddAppBar(func(p *tree.Plan) { - ss.GUI.AddToolbarItem(p, 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(p, ss.Loops, []etime.Modes{etime.Train}) - - //////////////////////////////////////////////// - tree.Add(p, func(w *core.Separator) {}) - ss.GUI.AddToolbarItem(p, egui.ToolbarItem{Label: "Reset RunLog", - Icon: icons.Reset, - Tooltip: "Reset the accumulated log of all NRuns, 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) - }, - }) - //////////////////////////////////////////////// - tree.Add(p, func(w *core.Separator) {}) - ss.GUI.AddToolbarItem(p, 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.RandSeeds.NewSeeds() - }, - }) - ss.GUI.AddToolbarItem(p, egui.ToolbarItem{Label: "README", - Icon: icons.FileMarkdown, - Tooltip: "Opens your browser on the README file that contains instructions for how to run this model.", - Active: egui.ActiveAlways, - Func: func() { - core.TheApp.OpenURL("https://github.com/emer/axon/blob/main/examples/dls/README.md") - }, - }) - }) - ss.GUI.FinalizeGUI(false) - if ss.Config.Run.GPU { - ss.Net.ConfigGPUnoGUI(&ss.Context) - core.TheApp.AddQuitCleanFunc(func() { - ss.Net.GPU.Destroy() - }) - } -} - -func (ss *Sim) RunGUI() { - ss.Init() - ss.ConfigGUI() - ev := ss.Envs.ByModeDi(etime.Train, 0).(*armaze.Env) - ss.EnvGUI = &armaze.GUI{} - eb := ss.EnvGUI.ConfigWorldGUI(ev) - eb.RunWindow() - ss.GUI.Body.RunMainWindow() -} - -// RecordTestData returns key testing data from the network -func (ss *Sim) RecordTestData() { - net := ss.Net - lays := net.LayersByType(axon.PTMaintLayer, axon.PTNotMaintLayer, axon.MatrixLayer, axon.STNLayer, axon.BLALayer, axon.CeMLayer, axon.VSPatchLayer, axon.LHbLayer, axon.LDTLayer, axon.VTALayer) - - key := ss.Stats.Print([]string{"Run", "Epoch", "Trial", "Di"}) - - net.AllGlobalValues(key, ss.TestData) - for _, lnm := range lays { - ly := net.LayerByName(lnm) - ly.TestValues(key, ss.TestData) - } -} - -func (ss *Sim) RunNoGUI() { - if ss.Config.Params.Note != "" { - mpi.Printf("Note: %s\n", ss.Config.Params.Note) - } - if ss.Config.Log.SaveWeights { - 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) - - netdata := ss.Config.Log.NetData - if netdata { - mpi.Printf("Saving NetView data from testing\n") - ss.GUI.InitNetData(ss.Net, 200) - } - - if ss.Config.Log.Testing { - ss.TestData = make(map[string]float32) - } - - 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) // must happen after gui or no gui - } - mpi.Printf("Set NThreads to: %d\n", ss.Net.NThreads) - - tmr := timer.Time{} - tmr.Start() - - ss.Loops.Run(etime.Train) - - tmr.Stop() - fmt.Printf("Total Time: %6.3g\n", tmr.TotalSecs()) - ss.Net.TimerReport() - - ss.Logs.CloseLogFiles() - - if netdata { - ss.GUI.SaveNetData(ss.Stats.String("RunName")) - } - - ss.Net.GPU.Destroy() // safe even if no GPU -} - -*/ diff --git a/examples/dls/params.go b/examples/dls/params.go deleted file mode 100644 index 5536c9dc..00000000 --- a/examples/dls/params.go +++ /dev/null @@ -1,90 +0,0 @@ -// 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. - -//go:build notyet - -package main - -import ( - "github.com/emer/emergent/v2/params" -) - -// ParamSets is the active set of parameters -- Base is always applied, -// and others can be optionally selected to apply on top of that -var ParamSets = params.Sets{ - "Base": { - {Sel: "Layer", Doc: "generic params for all layers", - Params: params.Params{ - ly.Acts.Clamp.Ge = "1.5", - }}, - {Sel: ".PFCLayer", Doc: "pfc layers: slower trgavgact", - Params: params.Params{ - ly.Learn.TrgAvgAct.SynScaleRate = "0.0002", // also now set by default - }}, - {Sel: ".PTMaintLayer", Doc: "time integration params", - Params: params.Params{ - ly.Inhib.Layer.Gi = "2.4", - ly.Inhib.Pool.Gi = "2.4", - ly.Acts.Dend.ModGain = "1.5", // 2 min -- reduces maint early - ly.Learn.NeuroMod.AChDisInhib = "0.0", // not much effect here.. - }}, - {Sel: ".PTPredLayer", Doc: "", - Params: params.Params{ - ly.Inhib.ActAvg.Nominal = "0.1", - ly.CT.GeGain = "0.05", // 0.05 key for stronger activity - ly.CT.DecayTau = "50", - ly.Learn.NeuroMod.AChDisInhib = "0", // 0.2, 0.5 not much diff - }}, - {Sel: ".CS", Doc: "need to adjust Nominal for number of CSs -- now down automatically", - Params: params.Params{ - ly.Inhib.ActAvg.Nominal = "0.1", // 0.1 for 4, divide by N/4 from there - }}, - {Sel: ".MatrixLayer", Doc: "all mtx", - Params: params.Params{ - ly.Inhib.Layer.On = "false", // todo: explore -- could be bad for gating - ly.Inhib.Pool.Gi = "0.3", // go lower, get more inhib from elsewhere? - ly.Inhib.Pool.FB = "1", - ly.Acts.Dend.ModGain = "1", // todo: 2 is default - }}, - //////////////////////////////////////////// - // Cortical Paths - {Sel: ".PFCPath", Doc: "pfc path params -- more robust to long-term training", - Params: params.Params{ - pt.Learn.Trace.SubMean = "1", // 1 > 0 for long-term stability - pt.Learn.LRate.Base = "0.01", // 0.04 def; 0.02 more stable; 0.01 even more - }}, - {Sel: ".PTtoPred", Doc: "stronger drive on pt pred", - Params: params.Params{ - pt.PathScale.Abs = "6", - }}, - {Sel: ".PTSelfMaint", Doc: "", - Params: params.Params{ - pt.PathScale.Abs = "4", - pt.Learn.LRate.Base = "0.0001", // this is not a problem - }}, - {Sel: ".ToPTp", Doc: "", - Params: params.Params{ - pt.PathScale.Abs = "4", - }}, - //////////////////////////////////////////// - // Rubicon Paths - {Sel: ".MatrixPath", Doc: "", - Params: params.Params{ - pt.Matrix.NoGateLRate = "1", // this is KEY for robustness when failing initially! - pt.Learn.Trace.LearnThr = "0.0", - }}, - {Sel: ".SuperToThal", Doc: "", - Params: params.Params{ - pt.PathScale.Abs = "4", // 4 def - }}, - {Sel: ".SuperToPT", Doc: "", - Params: params.Params{ - pt.PathScale.Abs = "0.5", // 0.5 def - }}, - {Sel: ".GPiToBGThal", Doc: "inhibition from GPi to MD", - Params: params.Params{ - pt.PathScale.Abs = "5", // with new mod, this can be stronger - }}, - }, -}