diff --git a/actrf/actrf.go b/actrf/actrf.go index 72010364..8a272b1e 100644 --- a/actrf/actrf.go +++ b/actrf/actrf.go @@ -25,22 +25,22 @@ type RF struct { Name string // computed receptive field, as SumProd / SumSrc -- only after Avg has been called - RF tensor.Float32 `view:"no-inline"` + RF tensor.Float32 `display:"no-inline"` // unit normalized version of RF per source (inner 2D dimensions) -- good for display - NormRF tensor.Float32 `view:"no-inline"` + NormRF tensor.Float32 `display:"no-inline"` // normalized version of SumSrc -- sum of each point in the source -- good for viewing the completeness and uniformity of the sampling of the source space - NormSrc tensor.Float32 `view:"no-inline"` + NormSrc tensor.Float32 `display:"no-inline"` // sum of the products of act * src - SumProd tensor.Float32 `view:"no-inline"` + SumProd tensor.Float32 `display:"no-inline"` // sum of the sources (denomenator) - SumSrc tensor.Float32 `view:"no-inline"` + SumSrc tensor.Float32 `display:"no-inline"` // temporary destination sum for MPI -- only used when MPISum called - MPITmp tensor.Float32 `view:"no-inline"` + MPITmp tensor.Float32 `display:"no-inline"` } // Init initializes this RF based on name and shapes of given diff --git a/confusion/confusion.go b/confusion/confusion.go index 5269752e..554bfbb2 100644 --- a/confusion/confusion.go +++ b/confusion/confusion.go @@ -21,25 +21,25 @@ import ( type Matrix struct { //git:add // normalized probability of confusion: Row = ground truth class, Col = actual response for that class. - Prob tensor.Float64 `view:"no-inline"` + Prob tensor.Float64 `display:"no-inline"` // incremental sums - Sum tensor.Float64 `view:"no-inline"` + Sum tensor.Float64 `display:"no-inline"` // counts per ground truth (rows) - N tensor.Float64 `view:"no-inline"` + N tensor.Float64 `display:"no-inline"` // visualization using SimMat - Vis simat.SimMat `view:"no-inline"` + Vis simat.SimMat `display:"no-inline"` // true pos/neg, false pos/neg for each class, generated from the confusion matrix - TFPN tensor.Float64 `view:"no-inline"` + TFPN tensor.Float64 `display:"no-inline"` // precision, recall and F1 score by class - ClassScores tensor.Float64 `view:"no-inline"` + ClassScores tensor.Float64 `display:"no-inline"` // micro F1, macro F1 and weighted F1 scores for entire matrix ignoring class - MatrixScores tensor.Float64 `view:"no-inline"` + MatrixScores tensor.Float64 `display:"no-inline"` } // Init initializes the Matrix for given number of classes, diff --git a/decoder/linear.go b/decoder/linear.go index a173ae8d..35e112d3 100644 --- a/decoder/linear.go +++ b/decoder/linear.go @@ -40,7 +40,7 @@ type Linear struct { Inputs []float32 // for holding layer values - ValuesTsrs map[string]*tensor.Float32 `view:"-"` + ValuesTsrs map[string]*tensor.Float32 `display:"-"` // synaptic weights: outer loop is units, inner loop is inputs Weights tensor.Float32 @@ -52,7 +52,7 @@ type Linear struct { PoolIndex int // mpi communicator -- MPI users must set this to their comm -- do direct assignment - Comm *mpi.Comm `view:"-"` + Comm *mpi.Comm `display:"-"` // delta weight changes: only for MPI mode -- outer loop is units, inner loop is inputs MPIDWts tensor.Float32 diff --git a/decoder/softmax.go b/decoder/softmax.go index 4329dd2d..2e9ad8da 100644 --- a/decoder/softmax.go +++ b/decoder/softmax.go @@ -50,13 +50,13 @@ type SoftMax struct { Target int // for holding layer values - ValuesTsrs map[string]*tensor.Float32 `view:"-"` + ValuesTsrs map[string]*tensor.Float32 `display:"-"` // synaptic weights: outer loop is units, inner loop is inputs Weights tensor.Float32 // mpi communicator -- MPI users must set this to their comm -- do direct assignment - Comm *mpi.Comm `view:"-"` + Comm *mpi.Comm `display:"-"` // delta weight changes: only for MPI mode -- outer loop is units, inner loop is inputs MPIDWts tensor.Float32 diff --git a/econfig/README.md b/econfig/README.md index 4a303c3d..f196aa57 100644 --- a/econfig/README.md +++ b/econfig/README.md @@ -70,7 +70,7 @@ The [Cogent Core](https://cogentcore.org/core) GUI processes `default:"value"` s # Standard Config Example -Here's the `Config` struct from [axon/examples/ra25](https://github.com/emer/axon), which can provide a useful starting point. It uses Params, Run and Log sub-structs to better organize things. For sims with extensive Env config, that should be added as a separate sub-struct as well. The `view:"add-fields"` struct tag shows all of the fields in one big dialog in the GUI -- if you want separate ones, omit that. +Here's the `Config` struct from [axon/examples/ra25](https://github.com/emer/axon), which can provide a useful starting point. It uses Params, Run and Log sub-structs to better organize things. For sims with extensive Env config, that should be added as a separate sub-struct as well. The `display:"add-fields"` struct tag shows all of the fields in one big dialog in the GUI -- if you want separate ones, omit that. ```Go // ParamConfig has config parameters related to sim params @@ -179,13 +179,13 @@ type Config struct { Debug bool // parameter related configuration options - Params ParamConfig `view:"add-fields"` + Params ParamConfig `display:"add-fields"` // sim running related configuration options - Run RunConfig `view:"add-fields"` + Run RunConfig `display:"add-fields"` // data logging related configuration options - Log LogConfig `view:"add-fields"` + Log LogConfig `display:"add-fields"` } func (cfg *Config) IncludesPtr() *[]string { return &cfg.Includes } diff --git a/econfig/include.go b/econfig/include.go index 3bd07221..f6051313 100644 --- a/econfig/include.go +++ b/econfig/include.go @@ -54,7 +54,7 @@ func includesStackImpl(clone Includeser, includes []string) ([]string, error) { var errs []error for _, inc := range incs { *clone.IncludesPtr() = nil - err := tomlx.OpenFromPaths(clone, inc, IncludePaths) + err := tomlx.OpenFromPaths(clone, inc, IncludePaths...) if err == nil { includes, err = includesStackImpl(clone, includes) if err != nil { @@ -88,7 +88,7 @@ func includeStackImpl(clone Includer, includes []string) ([]string, error) { includes = append(includes, inc) var errs []error *clone.IncludePtr() = "" - err := tomlx.OpenFromPaths(clone, inc, IncludePaths) + err := tomlx.OpenFromPaths(clone, inc, IncludePaths...) if err == nil { includes, err = includeStackImpl(clone, includes) if err != nil { diff --git a/econfig/io.go b/econfig/io.go index 6efc650f..615bd918 100644 --- a/econfig/io.go +++ b/econfig/io.go @@ -19,7 +19,7 @@ import ( // Is equivalent to Open if there are no Includes. // Returns an error if any of the include files cannot be found on IncludePath. func OpenWithIncludes(cfg any, file string) error { - err := tomlx.OpenFromPaths(cfg, file, IncludePaths) + err := tomlx.OpenFromPaths(cfg, file, IncludePaths...) if err != nil { return err } @@ -40,13 +40,13 @@ func OpenWithIncludes(cfg any, file string) error { } for i := ni - 1; i >= 0; i-- { inc := incs[i] - err = tomlx.OpenFromPaths(cfg, inc, IncludePaths) + err = tomlx.OpenFromPaths(cfg, inc, IncludePaths...) if err != nil { mpi.Println(err) } } // reopen original - tomlx.OpenFromPaths(cfg, file, IncludePaths) + tomlx.OpenFromPaths(cfg, file, IncludePaths...) if hasIncludes { *incsObj.IncludesPtr() = incs } else { diff --git a/egui/grids.go b/egui/grids.go index 34023458..8d9315c1 100644 --- a/egui/grids.go +++ b/egui/grids.go @@ -34,12 +34,12 @@ func (gui *GUI) SetGrid(name string, tg *tensorcore.TensorGrid) { // ConfigRasterGrid configures a raster grid for given layer name. // Uses Raster_laynm and given Tensor that has the raster data. -func (gui *GUI) ConfigRasterGrid(lay *core.Layout, laynm string, rast *tensor.Float32) *tensorcore.TensorGrid { +func (gui *GUI) ConfigRasterGrid(lay *core.Frame, laynm string, rast *tensor.Float32) *tensorcore.TensorGrid { tg := gui.Grid(laynm) tg.SetName(laynm + "Raster") - core.NewText(lay, laynm, laynm+":") + core.NewText(lay).SetText(laynm + ":") lay.AddChild(tg) - core.NewSpace(lay, laynm+"_spc") + core.NewSpace(lay) rast.SetMetaData("grid-fill", "1") tg.SetTensor(rast) return tg diff --git a/egui/gui.go b/egui/gui.go index 67f5b7fb..47572002 100644 --- a/egui/gui.go +++ b/egui/gui.go @@ -22,13 +22,13 @@ type GUI struct { CycleUpdateInterval int // true if the GUI is configured and running - Active bool `view:"-"` + Active bool `display:"-"` // true if sim is running - IsRunning bool `view:"-"` + IsRunning bool `display:"-"` // flag to stop running - StopNow bool `view:"-"` + StopNow bool `display:"-"` // plots by scope Plots map[etime.ScopeKey]*plotcore.PlotEditor @@ -40,19 +40,19 @@ type GUI struct { Grids map[string]*tensorcore.TensorGrid // the view update for managing updates of netview - ViewUpdate *netview.ViewUpdate `view:"-"` + ViewUpdate *netview.ViewUpdate `display:"-"` // net data for recording in nogui mode, if !nil - NetData *netview.NetData `view:"-"` + NetData *netview.NetData `display:"-"` // displays Sim fields on left - StructView *core.Form `view:"-"` + StructView *core.Form `display:"-"` // tabs for different view elements: plots, rasters - Tabs *core.Tabs `view:"-"` + Tabs *core.Tabs `display:"-"` // Body is the content of the sim window - Body *core.Body `view:"-"` + Body *core.Body `display:"-"` } // UpdateWindow triggers an update on window body, @@ -61,7 +61,7 @@ type GUI struct { func (gui *GUI) UpdateWindow() { tb := gui.Body.GetTopAppBar() if tb != nil { - tb.ApplyStyleUpdate() + tb.Restyle() } gui.Body.Scene.NeedsRender() // todo: could update other stuff but not really neccesary @@ -75,7 +75,7 @@ func (gui *GUI) GoUpdateWindow() { tb := gui.Body.GetTopAppBar() if tb != nil { - tb.ApplyStyleUpdate() + tb.Restyle() } gui.Body.Scene.NeedsRender() // todo: could update other stuff but not really neccesary @@ -101,13 +101,15 @@ func (gui *GUI) MakeBody(sim any, appname, title, about string) { gui.Body = core.NewBody(appname).SetTitle(title) // gui.Body.App().About = about - split := core.NewSplits(gui.Body, "split") + split := core.NewSplits(gui.Body) + split.Name = "split" gui.StructView = core.NewForm(split).SetStruct(sim) gui.StructView.Name = "sv" - if tb, ok := sim.(core.Toolbarer); ok { - gui.Body.AddAppBar(tb.ConfigToolbar) + if tb, ok := sim.(core.ToolbarMaker); ok { + gui.Body.AddAppBar(tb.MakeToolbar) } - gui.Tabs = core.NewTabs(split, "tv") + gui.Tabs = core.NewTabs(split) + gui.Tabs.Name = "tv" split.SetSplits(.2, .8) } diff --git a/egui/loopctrl.go b/egui/loopctrl.go index 28edb3f3..e6cbf8e0 100644 --- a/egui/loopctrl.go +++ b/egui/loopctrl.go @@ -16,8 +16,8 @@ import ( // AddLooperCtrl adds toolbar control for looper.Stack // with Run, Step controls. -func (gui *GUI) AddLooperCtrl(tb *core.Toolbar, loops *looper.Manager, modes []etime.Modes) { - gui.AddToolbarItem(tb, ToolbarItem{Label: "Stop", +func (gui *GUI) AddLooperCtrl(p *core.Plan, loops *looper.Manager, modes []etime.Modes) { + gui.AddToolbarItem(p, ToolbarItem{Label: "Stop", Icon: icons.Stop, Tooltip: "Interrupts running. running / stepping picks back up where it left off.", Active: ActiveRunning, @@ -30,21 +30,22 @@ func (gui *GUI) AddLooperCtrl(tb *core.Toolbar, loops *looper.Manager, modes []e for _, m := range modes { mode := m - core.NewButton(tb).SetText(mode.String() + " Run").SetIcon(icons.PlayArrow). - SetTooltip("Run the " + mode.String() + " process"). - StyleFirst(func(s *styles.Style) { s.SetEnabled(!gui.IsRunning) }). - OnClick(func(e events.Event) { - if !gui.IsRunning { - gui.IsRunning = true - tb.ApplyStyleUpdate() - go func() { - loops.Run(mode) - gui.Stopped() - }() - } - }) + core.AddAt(p, mode.String()+"-run", func(w *core.Button) { + w.SetText(mode.String() + " Run").SetIcon(icons.PlayArrow). + SetTooltip("Run the " + mode.String() + " process"). + FirstStyler(func(s *styles.Style) { s.SetEnabled(!gui.IsRunning) }). + OnClick(func(e events.Event) { + if !gui.IsRunning { + gui.IsRunning = true + // tb.Restyle() // todo: need obj on plan + go func() { + loops.Run(mode) + gui.Stopped() + }() + } + }) + }) - //stepLevel := evalLoops.Step.Default stepN := make(map[string]int) steps := loops.Stacks[mode].Order stringToEnumTime := make(map[string]etime.Times) @@ -52,43 +53,51 @@ func (gui *GUI) AddLooperCtrl(tb *core.Toolbar, loops *looper.Manager, modes []e stepN[st.String()] = 1 stringToEnumTime[st.String()] = st } - core.NewButton(tb).SetText("Step").SetIcon(icons.SkipNext). - SetTooltip("Step the " + mode.String() + " process according to the following step level and N"). - StyleFirst(func(s *styles.Style) { - s.SetEnabled(!gui.IsRunning) - s.SetAbilities(true, abilities.RepeatClickable) - }). - OnClick(func(e events.Event) { - if !gui.IsRunning { - gui.IsRunning = true - tb.ApplyStyleUpdate() - go func() { - stack := loops.Stacks[mode] - loops.Step(mode, stepN[stack.StepLevel.String()], stack.StepLevel) - gui.Stopped() - }() - } - }) - - scb := core.NewChooser(tb, "step") - stepStrs := []string{} - for _, s := range steps { - stepStrs = append(stepStrs, s.String()) - } - scb.SetStrings(stepStrs...) - stack := loops.Stacks[mode] - scb.SetCurrentValue(stack.StepLevel.String()) - sb := core.NewSpinner(tb, "step-n").SetTooltip("number of iterations per step"). - SetStep(1).SetMin(1).SetValue(1) - sb.OnChange(func(e events.Event) { - stepN[scb.CurrentItem.Value.(string)] = int(sb.Value) + core.AddAt(p, mode.String()+"-step", func(w *core.Button) { + w.SetText("Step").SetIcon(icons.SkipNext). + SetTooltip("Step the " + mode.String() + " process according to the following step level and N"). + FirstStyler(func(s *styles.Style) { + s.SetEnabled(!gui.IsRunning) + s.SetAbilities(true, abilities.RepeatClickable) + }). + OnClick(func(e events.Event) { + if !gui.IsRunning { + gui.IsRunning = true + // tb.Restyle() + go func() { + stack := loops.Stacks[mode] + loops.Step(mode, stepN[stack.StepLevel.String()], stack.StepLevel) + gui.Stopped() + }() + } + }) }) - scb.OnChange(func(e events.Event) { + var chs *core.Chooser + core.AddAt(p, mode.String()+"-level", func(w *core.Chooser) { + chs = w + stepStrs := []string{} + for _, s := range steps { + stepStrs = append(stepStrs, s.String()) + } + w.SetStrings(stepStrs...) stack := loops.Stacks[mode] - stack.StepLevel = stringToEnumTime[scb.CurrentItem.Value.(string)] - sb.Value = float32(stepN[stack.StepLevel.String()]) + w.SetCurrentValue(stack.StepLevel.String()) + }) + + core.AddAt(p, mode.String()+"-n", func(w *core.Spinner) { + w.SetStep(1).SetMin(1).SetValue(1) + w.SetTooltip("number of iterations per step"). + OnChange(func(e events.Event) { + stepN[chs.CurrentItem.Value.(string)] = int(w.Value) + }) + + w.OnChange(func(e events.Event) { + stack := loops.Stacks[mode] + stack.StepLevel = stringToEnumTime[chs.CurrentItem.Value.(string)] + w.Value = float32(stepN[stack.StepLevel.String()]) + }) }) } } diff --git a/egui/plots.go b/egui/plots.go index cc1f9e19..20524956 100644 --- a/egui/plots.go +++ b/egui/plots.go @@ -34,7 +34,6 @@ func (gui *GUI) AddPlots(title string, lg *elog.Logs) { plt := gui.NewPlotTab(key, mode+" "+time+" Plot") plt.SetTable(lt.Table) - plt.UpdatePlot() plt.Params.FromMetaMap(lt.Meta) ConfigPlotFromLog(title, plt, lg, key) @@ -70,7 +69,6 @@ func ConfigPlotFromLog(title string, plt *plotcore.PlotEditor, lg *elog.Logs, ke } plt.ColumnsFromMetaMap(lt.Table.MetaData) plt.ColumnsFromMetaMap(lt.Meta) - plt.Update() } // Plot returns plot for mode, time scope @@ -154,7 +152,7 @@ func (gui *GUI) AddTableView(lg *elog.Logs, mode etime.Modes, time etime.Times) } tt := gui.Tabs.NewTab(mode.String() + " " + time.String() + " ") - tv := tensorcore.NewTableView(tt) + tv := tensorcore.NewTable(tt) gui.TableViews[key] = tv tv.SetReadOnly(true) tv.SetTable(lt.Table) diff --git a/egui/toolbar.go b/egui/toolbar.go index 531af2e0..7e0b5739 100644 --- a/egui/toolbar.go +++ b/egui/toolbar.go @@ -21,15 +21,17 @@ type ToolbarItem struct { } // AddToolbarItem adds a toolbar item but also checks when it be active in the UI -func (gui *GUI) AddToolbarItem(tb *core.Toolbar, item ToolbarItem) { - itm := core.NewButton(tb).SetText(item.Label).SetIcon(item.Icon). - SetTooltip(item.Tooltip).OnClick(func(e events.Event) { - item.Func() +func (gui *GUI) AddToolbarItem(p *core.Plan, item ToolbarItem) { + core.AddAt(p, item.Label, func(w *core.Button) { + w.SetText(item.Label).SetIcon(item.Icon). + SetTooltip(item.Tooltip).OnClick(func(e events.Event) { + item.Func() + }) + switch item.Active { + case ActiveStopped: + w.FirstStyler(func(s *styles.Style) { s.SetEnabled(!gui.IsRunning) }) + case ActiveRunning: + w.FirstStyler(func(s *styles.Style) { s.SetEnabled(gui.IsRunning) }) + } }) - switch item.Active { - case ActiveStopped: - itm.StyleFirst(func(s *styles.Style) { s.SetEnabled(!gui.IsRunning) }) - case ActiveRunning: - itm.StyleFirst(func(s *styles.Style) { s.SetEnabled(gui.IsRunning) }) - } } diff --git a/elog/logs.go b/elog/logs.go index 37ce5a96..01510c90 100644 --- a/elog/logs.go +++ b/elog/logs.go @@ -42,22 +42,22 @@ type Logs struct { MiscTables map[string]*table.Table // A list of the items that should be logged. Each item should describe one column that you want to log, and how. Order in list determines order in logs. - Items []*Item `view:"-"` + Items []*Item `display:"-"` // context information passed to logging Write functions -- has all the information needed to compute and write log values -- is updated for each item in turn - Context Context `view:"-"` + Context Context `display:"-"` // All the eval modes that appear in any of the items of this log. - Modes map[string]bool `view:"-"` + Modes map[string]bool `display:"-"` // All the timescales that appear in any of the items of this log. - Times map[string]bool `view:"-"` + Times map[string]bool `display:"-"` // map of item indexes by name, for rapid access to items if they need to be modified after adding. - ItemIndexMap map[string]int `view:"-"` + ItemIndexMap map[string]int `display:"-"` // sorted order of table scopes - TableOrder []etime.ScopeKey `view:"-"` + TableOrder []etime.ScopeKey `display:"-"` } // AddItem adds an item to the list. The items are stored in the order diff --git a/elog/table.go b/elog/table.go index bcf1f202..3087f999 100644 --- a/elog/table.go +++ b/elog/table.go @@ -20,16 +20,16 @@ type LogTable struct { Meta map[string]string // Index View of the table -- automatically updated when a new row of data is logged to the table. - IndexView *table.IndexView `view:"-"` + IndexView *table.IndexView `display:"-"` // named index views onto the table that can be saved and used across multiple items -- these are reset to nil after a new row is written -- see NamedIndexView funtion for more details. - NamedViews map[string]*table.IndexView `view:"-"` + NamedViews map[string]*table.IndexView `display:"-"` // File to store the log into. - File *os.File `view:"-"` + File *os.File `display:"-"` // true if headers for File have already been written - WroteHeaders bool `view:"-"` + WroteHeaders bool `display:"-"` } // NewLogTable returns a new LogTable entry for given table, initializing values diff --git a/emer/netparams.go b/emer/netparams.go index eb5ab35b..45a865ac 100644 --- a/emer/netparams.go +++ b/emer/netparams.go @@ -22,7 +22,7 @@ import ( type NetParams struct { // full collection of param sets to use - Params netparams.Sets `view:"no-inline"` + Params netparams.Sets `display:"no-inline"` // optional additional sheets of parameters to apply after Base -- can use multiple names separated by spaces (don't put spaces in Sheet names!) ExtraSheets string @@ -31,10 +31,10 @@ type NetParams struct { Tag string // the network to apply parameters to - Network Network `view:"-"` + Network Network `display:"-"` // list of hyper parameters compiled from the network parameters, using the layers and pathways from the network, so that the same styling logic as for regular parameters can be used - NetHypers params.Flex `view:"-"` + NetHypers params.Flex `display:"-"` // print out messages for each parameter that is set SetMsg bool diff --git a/emer/params.go b/emer/params.go index 0e7c0de4..4929b4b2 100644 --- a/emer/params.go +++ b/emer/params.go @@ -20,7 +20,7 @@ import ( type Params struct { // full collection of param sets to use - Params params.Sets `view:"no-inline"` + Params params.Sets `display:"no-inline"` // optional additional set(s) of parameters to apply after Base -- can use multiple names separated by spaces (don't put spaces in Set names!) ExtraSets string @@ -29,10 +29,10 @@ type Params struct { Tag string // map of objects to apply parameters to -- the key is the name of the Sheet for each object, e.g., - Objects map[string]any `view:"-" Network", "Sim" are typically used"` + Objects map[string]any `display:"-" Network", "Sim" are typically used"` // list of hyper parameters compiled from the network parameters, using the layers and pathways from the network, so that the same styling logic as for regular parameters can be used - NetHypers params.Flex `view:"-"` + NetHypers params.Flex `display:"-"` // print out messages for each parameter that is set SetMsg bool diff --git a/env/ctr.go b/env/ctr.go index 61ce8e20..bf4c0f41 100644 --- a/env/ctr.go +++ b/env/ctr.go @@ -13,16 +13,16 @@ type Ctr struct { Cur int // previous counter value, prior to last Incr() call (init to -1) - Prv int `view:"-"` + Prv int `display:"-"` // did this change on the last Step() call or not? - Chg bool `view:"-"` + Chg bool `display:"-"` // where relevant, this is a fixed maximum counter value, above which the counter will reset back to 0 -- only used if > 0 Max int // the unit of time scale represented by this counter (just FYI) - Scale TimeScales `view:"-"` + Scale TimeScales `display:"-"` } // Init initializes counter -- Cur = 0, Prv = -1 diff --git a/env/fixed.go b/env/fixed.go index 17284492..7b776adf 100644 --- a/env/fixed.go +++ b/env/fixed.go @@ -38,13 +38,13 @@ type FixedTable struct { Order []int // current run of model as provided during Init - Run Ctr `view:"inline"` + Run Ctr `display:"inline"` // number of times through entire set of patterns - Epoch Ctr `view:"inline"` + Epoch Ctr `display:"inline"` // current ordinal item in Table -- if Sequential then = row number in table, otherwise is index in Order list that then gives row number in Table - Trial Ctr `view:"inline"` + Trial Ctr `display:"inline"` // if Table has a Name column, this is the contents of that TrialName CurPrvString diff --git a/env/freq.go b/env/freq.go index 79363fa3..6bf88a6a 100644 --- a/env/freq.go +++ b/env/freq.go @@ -47,13 +47,13 @@ type FreqTable struct { Order []int // current run of model as provided during Init - Run Ctr `view:"inline"` + Run Ctr `display:"inline"` // number of times through entire set of patterns - Epoch Ctr `view:"inline"` + Epoch Ctr `display:"inline"` // current ordinal item in Table -- if Sequential then = row number in table, otherwise is index in Order list that then gives row number in Table - Trial Ctr `view:"inline"` + Trial Ctr `display:"inline"` // if Table has a Name column, this is the contents of that TrialName CurPrvString diff --git a/env/mpifixed.go b/env/mpifixed.go index 37d50ced..b8010978 100644 --- a/env/mpifixed.go +++ b/env/mpifixed.go @@ -44,13 +44,13 @@ type MPIFixedTable struct { Order []int // current run of model as provided during Init - Run Ctr `view:"inline"` + Run Ctr `display:"inline"` // number of times through entire set of patterns - Epoch Ctr `view:"inline"` + Epoch Ctr `display:"inline"` // current ordinal item in Table -- if Sequential then = row number in table, otherwise is index in Order list that then gives row number in Table - Trial Ctr `view:"inline"` + Trial Ctr `display:"inline"` // if Table has a Name column, this is the contents of that TrialName CurPrvString diff --git a/estats/stats.go b/estats/stats.go index d83f90ba..28440c20 100644 --- a/estats/stats.go +++ b/estats/stats.go @@ -36,7 +36,7 @@ type Stats struct { IntTensors map[string]*tensor.Int // confusion matrix - Confusion confusion.Matrix `view:"no-inline"` + Confusion confusion.Matrix `display:"no-inline"` // similarity matrix for comparing pattern similarities SimMats map[string]*simat.SimMat @@ -51,7 +51,7 @@ type Stats struct { SVD pca.SVD // activation-based receptive fields - ActRFs actrf.RFs `view:"no-inline"` + ActRFs actrf.RFs `display:"no-inline"` // list of layer names configured for recording raster plots Rasters []string diff --git a/netview/events.go b/netview/events.go index 7b8db4b7..770134e9 100644 --- a/netview/events.go +++ b/netview/events.go @@ -52,7 +52,7 @@ func (sw *Scene) MouseDownEvent(e events.Event) { if ok { lay := ln.NetView.Net.LayerByName(ln.Text) if lay != nil { - core.FormDialog(sw, lay, "Layer: "+lay.Name(), true) + FormDialog(sw, lay, "Layer: "+lay.Name()) } e.SetHandled() return @@ -168,3 +168,14 @@ func (sw *Scene) LayerUnitAtPoint(pos image.Point) (lay emer.Layer, lx, ly, unIn lay = nil return } + +// FormDialog opens a dialog in a new, separate window +// for viewing / editing the given struct object, in the context of given ctx widget +func FormDialog(ctx core.Widget, stru any, title string) { + d := core.NewBody().AddTitle(title) + core.NewForm(d).SetStruct(stru) + if tb, ok := stru.(core.ToolbarMaker); ok { + d.AddAppBar(tb.MakeToolbar) + } + d.NewFullDialog(ctx).SetNewWindow(true).Run() +} diff --git a/netview/layobj.go b/netview/layobj.go index 216abd82..7f18a647 100644 --- a/netview/layobj.go +++ b/netview/layobj.go @@ -16,7 +16,7 @@ type LayObj struct { //types:add LayName string // our netview - NetView *NetView `copier:"-" json:"-" xml:"-" view:"-"` + NetView *NetView `copier:"-" json:"-" xml:"-" display:"-"` } // LayName is the Layer name as a Text2D within the NetView @@ -24,5 +24,5 @@ type LayName struct { xyz.Text2D // our netview - NetView *NetView `copier:"-" json:"-" xml:"-" view:"-"` + NetView *NetView `copier:"-" json:"-" xml:"-" display:"-"` } diff --git a/netview/netview.go b/netview/netview.go index 7ecbc573..e9271051 100644 --- a/netview/netview.go +++ b/netview/netview.go @@ -60,7 +60,7 @@ type NetView struct { VarParams map[string]*VarParams // current var params -- only valid during Update of display - CurVarParams *VarParams `json:"-" xml:"-" view:"-"` + CurVarParams *VarParams `json:"-" xml:"-" display:"-"` // parameters controlling how the view is rendered Params Params @@ -81,7 +81,7 @@ type NetView struct { Data NetData // mutex on data access - DataMu sync.RWMutex `view:"-" copier:"-" json:"-" xml:"-"` + DataMu sync.RWMutex `display:"-" copier:"-" json:"-" xml:"-"` } func (nv *NetView) Init() { @@ -96,9 +96,9 @@ func (nv *NetView) Init() { }) core.AddChildAt(nv, "tbar", func(w *core.Toolbar) { - nv.ConfigToolbar(w) + w.Maker(nv.MakeToolbar) }) - core.AddChildAt(nv, "nframe", func(w *core.Frame) { + core.AddChildAt(nv, "netframe", func(w *core.Frame) { w.Styler(func(s *styles.Style) { s.Direction = styles.Row s.Grow.Set(1, 1) @@ -115,18 +115,18 @@ func (nv *NetView) Init() { }) core.AddChildAt(w, "scene", func(w *Scene) { w.NetView = nv - nv.ViewConfig() + se := w.Scene.XYZ + nv.ViewDefaults(se) + laysGp := xyz.NewGroup(se) + laysGp.Name = "Layers" }) }) core.AddChildAt(nv, "counters", func(w *core.Text) { w.SetText("Counters: " + strings.Repeat(" ", 200)) }) core.AddChildAt(nv, "vbar", func(w *core.Toolbar) { - nv.ConfigViewbar(w) + w.Maker(nv.MakeViewbar) }) - - nv.Data.Init(nv.Net, nv.Params.MaxRecs, nv.Params.NoSynData, nv.Net.MaxParallelData()) - nv.ReconfigMeshes() } // SetNet sets the network to view and updates view @@ -135,7 +135,6 @@ func (nv *NetView) SetNet(net emer.Network) { nv.DataMu.Lock() nv.Data.Init(nv.Net, nv.Params.MaxRecs, nv.Params.NoSynData, nv.Net.MaxParallelData()) nv.DataMu.Unlock() - nv.Update() } // SetVar sets the variable to view and updates the display @@ -283,10 +282,7 @@ func (nv *NetView) UpdateImpl() { } se := nv.SceneXYZ() - // laysGp := se.ChildByName("Layers", 0).AsTree() - // if laysGp == nil || laysGp.NumChildren() != nv.Net.NLayers() { - // nv.ConfigNetView() - // } + nv.ConfigLayers() nv.SetCounters(nv.Data.CounterRec(nv.RecNo)) nv.UpdateRecNo() nv.DataMu.Unlock() @@ -348,8 +344,8 @@ func (nv *NetView) Toolbar() *core.Toolbar { return nv.ChildByName("tbar", 0).(*core.Toolbar) } -func (nv *NetView) NetLay() *core.Frame { - return nv.ChildByName("net", 1).(*core.Frame) +func (nv *NetView) NetFrame() *core.Frame { + return nv.ChildByName("netframe", 1).(*core.Frame) } func (nv *NetView) Counters() *core.Text { @@ -361,16 +357,15 @@ func (nv *NetView) Viewbar() *core.Toolbar { } func (nv *NetView) SceneWidget() *Scene { - return nv.NetLay().ChildByName("scene", 1).(*Scene) + return nv.NetFrame().ChildByName("scene", 1).(*Scene) } func (nv *NetView) SceneXYZ() *xyz.Scene { return nv.SceneWidget().Scene.XYZ - } -func (nv *NetView) VarsLay() *core.Frame { - return nv.NetLay().ChildByName("vars", 0).(*core.Frame) +func (nv *NetView) VarsFrame() *core.Frame { + return nv.NetFrame().ChildByName("vars", 0).(*core.Frame) } // SetCounters sets the counters widget view display at bottom of netview @@ -384,8 +379,10 @@ func (nv *NetView) SetCounters(ctrs string) { // UpdateRecNo updates the record number viewing func (nv *NetView) UpdateRecNo() { vbar := nv.Viewbar() - rlbl := vbar.ChildByName("rec", 10).(*core.Text) - rlbl.SetText(fmt.Sprintf("%4d ", nv.RecNo)).Update() + rlbl := vbar.ChildByName("rec", 10) + if rlbl != nil { + rlbl.(*core.Text).SetText(fmt.Sprintf("%4d ", nv.RecNo)).Update() + } } // RecFullBkwd move view record to start of history. @@ -533,7 +530,7 @@ func (nv *NetView) VarsListUpdate() { // VarsUpdate updates the selection status of the variables // and the view range state too func (nv *NetView) VarsUpdate() { - vl := nv.VarsLay() + vl := nv.VarsFrame() for _, vbi := range vl.Children { vb := vbi.(*core.Button) vb.SetSelected(vb.Text == nv.Var) @@ -624,30 +621,19 @@ func (nv *NetView) makeVars(p *core.Plan) { } } -// ViewConfig configures the 3D view -func (nv *NetView) ViewConfig() { +// ConfigLayers configures the layers +func (nv *NetView) ConfigLayers() { sw := nv.SceneWidget() - se := sw.Scene.XYZ if nv.Net == nil || nv.Net.NLayers() == 0 { se.DeleteChildren() se.Meshes.Reset() return } - if se.Lights.Len() == 0 { - nv.ViewDefaults() - } // todo: // vs.BgColor = core.Prefs.Colors.Background // reset in case user changes nlay := nv.Net.NLayers() - laysGpi := se.ChildByName("Layers", 0) - var laysGp *xyz.Group - if laysGpi == nil { - laysGp = xyz.NewGroup(se) - laysGp.Name = "Layers" - } else { - laysGp = laysGpi.(*xyz.Group) - } + laysGp := se.ChildByName("Layers", 0).(*xyz.Group) layConfig := tree.TypePlan{} for li := 0; li < nlay; li++ { lay := nv.Net.Layer(li) @@ -663,7 +649,9 @@ func (nv *NetView) ViewConfig() { gpConfig.Add(LayObjType, "layer") gpConfig.Add(LayNameType, "name") - tree.Update(laysGp, layConfig) + if !tree.Update(laysGp, layConfig) { + return + } nmin, nmax := nv.Net.Bounds() nsz := nmax.Sub(nmin).Sub(math32.Vec3(1, 1, 0)).Max(math32.Vec3(1, 1, 1)) @@ -709,8 +697,7 @@ func (nv *NetView) ViewConfig() { } // ViewDefaults are the default 3D view params -func (nv *NetView) ViewDefaults() { - se := nv.SceneXYZ() +func (nv *NetView) ViewDefaults(se *xyz.Scene) { se.Camera.Pose.Pos.Set(0, 1.5, 2.5) // more "top down" view shows more of layers // vs.Camera.Pose.Pos.Set(0, 1, 2.75) // more "head on" for larger / deeper networks se.Camera.Near = 0.1 @@ -876,321 +863,387 @@ func (nv *NetView) LayerByName(lay string) *xyz.Group { return ly.(*xyz.Group) } -func (nv *NetView) ConfigToolbar(tb *core.Toolbar) { - core.NewFuncButton(tb, nv.Update).SetText("Init").SetIcon(icons.Update) - core.NewFuncButton(tb, nv.Current).SetIcon(icons.Update) - core.NewButton(tb).SetText("Config").SetIcon(icons.Settings). - SetTooltip("set parameters that control display (font size etc)"). - OnClick(func(e events.Event) { - core.FormDialog(nv, &nv.Params, nv.Name+" Params", true) +func (nv *NetView) MakeToolbar(p *core.Plan) { + core.Add(p, func(w *core.FuncButton) { + w.SetFunc(nv.Update).SetText("Init").SetIcon(icons.Update) + }) + core.Add(p, func(w *core.FuncButton) { + w.SetFunc(nv.Current).SetIcon(icons.Update) + }) + core.Add(p, func(w *core.Button) { + w.SetText("Config").SetIcon(icons.Settings). + SetTooltip("set parameters that control display (font size etc)"). + OnClick(func(e events.Event) { + FormDialog(nv, &nv.Params, nv.Name+" Params") + }) + }) + core.Add(p, func(w *core.Separator) {}) + core.Add(p, func(w *core.Separator) {}) + core.Add(p, func(w *core.Button) { + w.SetText("Weights").SetType(core.ButtonAction).SetMenu(func(m *core.Scene) { + fb := core.NewFuncButton(m, nv.SaveWeights) + fb.SetIcon(icons.Save) + fb.Args[0].SetTag(`"ext:".wts,.wts.gz"`) + fb = core.NewFuncButton(m, nv.OpenWeights) + fb.SetIcon(icons.Open) + fb.Args[0].SetTag(`"ext:".wts,.wts.gz"`) }) - core.NewSeparator(tb) - core.NewButton(tb).SetText("Weights").SetType(core.ButtonAction).SetMenu(func(m *core.Scene) { - fb := core.NewFuncButton(m, nv.SaveWeights) - fb.SetIcon(icons.Save) - fb.Args[0].SetTag(`"ext:".wts,.wts.gz"`) - fb = core.NewFuncButton(m, nv.OpenWeights) - fb.SetIcon(icons.Open) - fb.Args[0].SetTag(`"ext:".wts,.wts.gz"`) }) - core.NewButton(tb).SetText("Params").SetIcon(icons.Info).SetMenu(func(m *core.Scene) { - core.NewFuncButton(m, nv.ShowNonDefaultParams).SetIcon(icons.Info) - core.NewFuncButton(m, nv.ShowAllParams).SetIcon(icons.Info) - core.NewFuncButton(m, nv.ShowKeyLayerParams).SetIcon(icons.Info) - core.NewFuncButton(m, nv.ShowKeyPathParams).SetIcon(icons.Info) + core.Add(p, func(w *core.Button) { + w.SetText("Params").SetIcon(icons.Info).SetMenu(func(m *core.Scene) { + core.NewFuncButton(m, nv.ShowNonDefaultParams).SetIcon(icons.Info) + core.NewFuncButton(m, nv.ShowAllParams).SetIcon(icons.Info) + core.NewFuncButton(m, nv.ShowKeyLayerParams).SetIcon(icons.Info) + core.NewFuncButton(m, nv.ShowKeyPathParams).SetIcon(icons.Info) + }) }) - core.NewButton(tb).SetText("Net Data").SetIcon(icons.Save).SetMenu(func(m *core.Scene) { - core.NewFuncButton(m, nv.Data.SaveJSON).SetText("Save Net Data").SetIcon(icons.Save) - core.NewFuncButton(m, nv.Data.OpenJSON).SetText("Open Net Data").SetIcon(icons.Open) - core.NewSeparator(m) - core.NewFuncButton(m, nv.PlotSelectedUnit).SetIcon(icons.Open) + core.Add(p, func(w *core.Button) { + w.SetText("Net Data").SetIcon(icons.Save).SetMenu(func(m *core.Scene) { + core.NewFuncButton(m, nv.Data.SaveJSON).SetText("Save Net Data").SetIcon(icons.Save) + core.NewFuncButton(m, nv.Data.OpenJSON).SetText("Open Net Data").SetIcon(icons.Open) + core.NewSeparator(m) + core.NewFuncButton(m, nv.PlotSelectedUnit).SetIcon(icons.Open) + }) }) - core.NewSeparator(tb) + core.Add(p, func(w *core.Separator) {}) ditp := "data parallel index -- for models running multiple input patterns in parallel, this selects which one is viewed" - core.NewText(tb).SetText("Di:").SetTooltip(ditp) - dis := core.NewSpinner(tb) - dis.SetMin(0).SetStep(1).SetValue(float32(nv.Di)).SetTooltip(ditp) - dis.OnChange(func(e events.Event) { - maxData := nv.Net.MaxParallelData() - md := int(dis.Value) - if md < maxData && md >= 0 { - nv.Di = md - } - dis.SetValue(float32(nv.Di)) - nv.UpdateView() + core.Add(p, func(w *core.Text) { + w.SetText("Di:").SetTooltip(ditp) }) - core.NewSeparator(tb) - rchk := core.NewSwitch(tb) - rchk.SetText("Raster").SetChecked(nv.Params.Raster.On). - SetTooltip("Toggles raster plot mode -- displays values on one axis (Z by default) and raster counter (time) along the other (X by default)") - rchk.OnChange(func(e events.Event) { - nv.Params.Raster.On = rchk.IsChecked() - nv.ReconfigMeshes() - nv.UpdateView() - }) - xchk := core.NewSwitch(tb) - xchk.SetText("X").SetType(core.SwitchCheckbox).SetChecked(nv.Params.Raster.XAxis). - SetTooltip("If checked, the raster (time) dimension is plotted along the X (horizontal) axis of the layers, otherwise it goes in the depth (Z) dimension"). - OnChange(func(e events.Event) { - nv.Params.Raster.XAxis = xchk.IsChecked() + core.Add(p, func(w *core.Spinner) { + w.SetMin(0).SetStep(1).SetValue(float32(nv.Di)).SetTooltip(ditp) + w.OnChange(func(e events.Event) { + maxData := nv.Net.MaxParallelData() + md := int(w.Value) + if md < maxData && md >= 0 { + nv.Di = md + } + w.SetValue(float32(nv.Di)) nv.UpdateView() }) + }) + + core.Add(p, func(w *core.Separator) {}) + core.Add(p, func(w *core.Switch) { + w.SetText("Raster").SetChecked(nv.Params.Raster.On). + SetTooltip("Toggles raster plot mode -- displays values on one axis (Z by default) and raster counter (time) along the other (X by default)"). + OnChange(func(e events.Event) { + nv.Params.Raster.On = w.IsChecked() + nv.ReconfigMeshes() + nv.UpdateView() + }) + }) + core.Add(p, func(w *core.Switch) { + w.SetText("X").SetType(core.SwitchCheckbox).SetChecked(nv.Params.Raster.XAxis). + SetTooltip("If checked, the raster (time) dimension is plotted along the X (horizontal) axis of the layers, otherwise it goes in the depth (Z) dimension"). + OnChange(func(e events.Event) { + nv.Params.Raster.XAxis = w.IsChecked() + nv.UpdateView() + }) + }) vp, ok := nv.VarParams[nv.Var] if !ok { vp = &VarParams{} vp.Defaults() } - core.NewSeparator(tb) - mnsw := core.NewSwitch(tb) - mnsw.Name = "mnsw" - mnsw.SetText("Min").SetType(core.SwitchCheckbox).SetChecked(vp.Range.FixMin). - SetTooltip("Fix the minimum end of the displayed value range to value shown in next box. Having both min and max fixed is recommended where possible for speed and consistent interpretability of the colors.").OnChange(func(e events.Event) { - vp := nv.VarParams[nv.Var] - vp.Range.FixMin = mnsw.IsChecked() - nv.VarScaleUpdate(nv.Var) // todo: before update? - nv.UpdateView() - }) - mnsp := core.NewSpinner(tb) - mnsp.Name = "mnsp" - mnsp.SetValue(vp.Range.Min). - OnChange(func(e events.Event) { + core.Add(p, func(w *core.Separator) {}) + core.AddAt(p, "mnsw", func(w *core.Switch) { + w.SetText("Min").SetType(core.SwitchCheckbox).SetChecked(vp.Range.FixMin). + SetTooltip("Fix the minimum end of the displayed value range to value shown in next box. Having both min and max fixed is recommended where possible for speed and consistent interpretability of the colors.").OnChange(func(e events.Event) { vp := nv.VarParams[nv.Var] - vp.Range.SetMin(mnsp.Value) - if vp.ZeroCtr && vp.Range.Min < 0 && vp.Range.FixMax { - vp.Range.SetMax(-vp.Range.Min) - } - nv.VarScaleUpdate(nv.Var) + vp.Range.FixMin = w.IsChecked() + nv.VarScaleUpdate(nv.Var) // todo: before update? nv.UpdateView() }) - - cmb := core.NewColorMapButton(tb) - nv.ColorMapButton = cmb - cmb.MapName = string(nv.Params.ColorMap) - cmb.Name = "cmap" - cmb.SetTooltip("Color map for translating values into colors -- click to select alternative."). - Styler(func(s *styles.Style) { - s.Min.X.Em(10) - s.Min.Y.Em(1.2) - s.Grow.Set(0, 1) - }).OnChange(func(e events.Event) { - cmap, ok := colormap.AvailableMaps[string(nv.ColorMapButton.MapName)] - if ok { - nv.ColorMap = cmap - } - nv.UpdateView() + }) + core.AddAt(p, "mnsp", func(w *core.Spinner) { + w.SetValue(vp.Range.Min). + OnChange(func(e events.Event) { + vp := nv.VarParams[nv.Var] + vp.Range.SetMin(w.Value) + if vp.ZeroCtr && vp.Range.Min < 0 && vp.Range.FixMax { + vp.Range.SetMax(-vp.Range.Min) + } + nv.VarScaleUpdate(nv.Var) + nv.UpdateView() + }) }) - mxsw := core.NewSwitch(tb) - mxsw.Name = "mxsw" - mxsw.SetText("Max").SetType(core.SwitchCheckbox).SetChecked(vp.Range.FixMax). - SetTooltip("Fix the maximum end of the displayed value range to value shown in next box. Having both min and max fixed is recommended where possible for speed and consistent interpretability of the colors."). - OnChange(func(e events.Event) { - vp := nv.VarParams[nv.Var] - vp.Range.FixMax = mxsw.IsChecked() - nv.VarScaleUpdate(nv.Var) + core.AddAt(p, "cmap", func(w *core.ColorMapButton) { + nv.ColorMapButton = w + w.MapName = string(nv.Params.ColorMap) + w.SetTooltip("Color map for translating values into colors -- click to select alternative."). + Styler(func(s *styles.Style) { + s.Min.X.Em(10) + s.Min.Y.Em(1.2) + s.Grow.Set(0, 1) + }).OnChange(func(e events.Event) { + cmap, ok := colormap.AvailableMaps[string(nv.ColorMapButton.MapName)] + if ok { + nv.ColorMap = cmap + } nv.UpdateView() }) - mxsp := core.NewSpinner(tb) - mxsp.Name = "mxsp" - mxsp.SetValue(vp.Range.Max).OnChange(func(e events.Event) { - vp := nv.VarParams[nv.Var] - vp.Range.SetMax(mxsp.Value) - if vp.ZeroCtr && vp.Range.Max > 0 && vp.Range.FixMin { - vp.Range.SetMin(-vp.Range.Max) - } - nv.VarScaleUpdate(nv.Var) - nv.UpdateView() }) - zcsw := core.NewSwitch(tb) - zcsw.Name = "zcsw" - zcsw.SetText("ZeroCtr").SetChecked(vp.ZeroCtr). - SetTooltip("keep Min - Max centered around 0, and use negative heights for units -- else use full min-max range for height (no negative heights)"). - OnChange(func(e events.Event) { + + core.AddAt(p, "mxsw", func(w *core.Switch) { + w.SetText("Max").SetType(core.SwitchCheckbox).SetChecked(vp.Range.FixMax). + SetTooltip("Fix the maximum end of the displayed value range to value shown in next box. Having both min and max fixed is recommended where possible for speed and consistent interpretability of the colors."). + OnChange(func(e events.Event) { + vp := nv.VarParams[nv.Var] + vp.Range.FixMax = w.IsChecked() + nv.VarScaleUpdate(nv.Var) + nv.UpdateView() + }) + }) + + core.AddAt(p, "mxsp", func(w *core.Spinner) { + w.SetValue(vp.Range.Max).OnChange(func(e events.Event) { vp := nv.VarParams[nv.Var] - vp.ZeroCtr = zcsw.IsChecked() + vp.Range.SetMax(w.Value) + if vp.ZeroCtr && vp.Range.Max > 0 && vp.Range.FixMin { + vp.Range.SetMin(-vp.Range.Max) + } nv.VarScaleUpdate(nv.Var) nv.UpdateView() }) -} + }) -func (nv *NetView) ConfigViewbar(tb *core.Toolbar) { - core.NewButton(tb).SetIcon(icons.Update).SetTooltip("reset to default initial display"). - OnClick(func(e events.Event) { - nv.SceneXYZ().SetCamera("default") - nv.UpdateView() - }) - core.NewButton(tb).SetIcon(icons.ZoomIn).SetTooltip("zoom in"). - Styler(func(s *styles.Style) { - s.SetAbilities(true, abilities.RepeatClickable) - }). - OnClick(func(e events.Event) { - nv.SceneXYZ().Camera.Zoom(-.05) - nv.UpdateView() - }) - core.NewButton(tb).SetIcon(icons.ZoomOut).SetTooltip("zoom out"). - Styler(func(s *styles.Style) { - s.SetAbilities(true, abilities.RepeatClickable) - }). - OnClick(func(e events.Event) { - nv.SceneXYZ().Camera.Zoom(.05) - nv.UpdateView() - }) - core.NewSeparator(tb) - core.NewText(tb).SetText("Rot:").SetTooltip("rotate display") - core.NewButton(tb).SetIcon(icons.KeyboardArrowLeft). - Styler(func(s *styles.Style) { - s.SetAbilities(true, abilities.RepeatClickable) - }). - OnClick(func(e events.Event) { - nv.SceneXYZ().Camera.Orbit(5, 0) - nv.UpdateView() - }) - core.NewButton(tb).SetIcon(icons.KeyboardArrowUp). - Styler(func(s *styles.Style) { - s.SetAbilities(true, abilities.RepeatClickable) - }). - OnClick(func(e events.Event) { - nv.SceneXYZ().Camera.Orbit(0, 5) - nv.UpdateView() - }) - core.NewButton(tb).SetIcon(icons.KeyboardArrowDown). - Styler(func(s *styles.Style) { - s.SetAbilities(true, abilities.RepeatClickable) - }). - OnClick(func(e events.Event) { - nv.SceneXYZ().Camera.Orbit(0, -5) - nv.UpdateView() - }) - core.NewButton(tb).SetIcon(icons.KeyboardArrowRight). - Styler(func(s *styles.Style) { - s.SetAbilities(true, abilities.RepeatClickable) - }). - OnClick(func(e events.Event) { - nv.SceneXYZ().Camera.Orbit(-5, 0) - nv.UpdateView() - }) - core.NewSeparator(tb) - - core.NewText(tb).SetText("Pan:").SetTooltip("pan display") - core.NewButton(tb).SetIcon(icons.KeyboardArrowLeft). - Styler(func(s *styles.Style) { - s.SetAbilities(true, abilities.RepeatClickable) - }). - OnClick(func(e events.Event) { - nv.SceneXYZ().Camera.Pan(-.2, 0) - nv.UpdateView() - }) - core.NewButton(tb).SetIcon(icons.KeyboardArrowUp). - Styler(func(s *styles.Style) { - s.SetAbilities(true, abilities.RepeatClickable) - }). - OnClick(func(e events.Event) { - nv.SceneXYZ().Camera.Pan(0, .2) - nv.UpdateView() - }) - core.NewButton(tb).SetIcon(icons.KeyboardArrowDown). - Styler(func(s *styles.Style) { - s.SetAbilities(true, abilities.RepeatClickable) - }). - OnClick(func(e events.Event) { - nv.SceneXYZ().Camera.Pan(0, -.2) - nv.UpdateView() - }) - core.NewButton(tb).SetIcon(icons.KeyboardArrowRight). - Styler(func(s *styles.Style) { - s.SetAbilities(true, abilities.RepeatClickable) - }). - OnClick(func(e events.Event) { - nv.SceneXYZ().Camera.Pan(.2, 0) - nv.UpdateView() - }) - core.NewSeparator(tb) + core.AddAt(p, "zcsw", func(w *core.Switch) { + w.SetText("ZeroCtr").SetChecked(vp.ZeroCtr). + SetTooltip("keep Min - Max centered around 0, and use negative heights for units -- else use full min-max range for height (no negative heights)"). + OnChange(func(e events.Event) { + vp := nv.VarParams[nv.Var] + vp.ZeroCtr = w.IsChecked() + nv.VarScaleUpdate(nv.Var) + nv.UpdateView() + }) + }) +} - core.NewText(tb).SetText("Save:") - for i := 1; i <= 4; i++ { - i := i - nm := fmt.Sprintf("%d", i) - core.NewButton(tb).SetText(nm). - SetTooltip("first click (or + Shift) saves current view, second click restores to saved state"). +func (nv *NetView) MakeViewbar(p *core.Plan) { + core.Add(p, func(w *core.Button) { + w.SetIcon(icons.Update).SetTooltip("reset to default initial display"). OnClick(func(e events.Event) { - sc := nv.SceneXYZ() - cam := nm - if e.HasAllModifiers(e.Modifiers(), key.Shift) { - sc.SaveCamera(cam) - } else { - err := sc.SetCamera(cam) - if err != nil { - sc.SaveCamera(cam) - } - } - fmt.Printf("Camera %s: %v\n", cam, sc.Camera.GenGoSet("")) + nv.SceneXYZ().SetCamera("default") nv.UpdateView() }) - } - core.NewSeparator(tb) - - core.NewText(tb).SetText("Time:"). - SetTooltip("states are recorded over time -- last N can be reviewed using these buttons") - - rec := core.NewText(tb).SetText(fmt.Sprintf("%4d ", nv.RecNo)) - rec.Name = "rec" - rec.SetTooltip("current view record: -1 means latest, 0 = earliest") - core.NewButton(tb).SetIcon(icons.FirstPage).SetTooltip("move to first record (start of history)"). - OnClick(func(e events.Event) { - if nv.RecFullBkwd() { + }) + core.Add(p, func(w *core.Button) { + w.SetIcon(icons.ZoomIn).SetTooltip("zoom in"). + Styler(func(s *styles.Style) { + s.SetAbilities(true, abilities.RepeatClickable) + }). + OnClick(func(e events.Event) { + nv.SceneXYZ().Camera.Zoom(-.05) nv.UpdateView() - } - }) - core.NewButton(tb).SetIcon(icons.FastRewind).SetTooltip("move earlier by N records (default 10)"). - Styler(func(s *styles.Style) { - s.SetAbilities(true, abilities.RepeatClickable) - }). - OnClick(func(e events.Event) { - if nv.RecFastBkwd() { + }) + }) + core.Add(p, func(w *core.Button) { + w.SetIcon(icons.ZoomOut).SetTooltip("zoom out"). + Styler(func(s *styles.Style) { + s.SetAbilities(true, abilities.RepeatClickable) + }). + OnClick(func(e events.Event) { + nv.SceneXYZ().Camera.Zoom(.05) nv.UpdateView() - } - }) - core.NewButton(tb).SetIcon(icons.SkipPrevious).SetTooltip("move earlier by 1"). - Styler(func(s *styles.Style) { - s.SetAbilities(true, abilities.RepeatClickable) - }). - OnClick(func(e events.Event) { - if nv.RecBkwd() { + }) + }) + core.Add(p, func(w *core.Separator) {}) + core.Add(p, func(w *core.Text) { + w.SetText("Rot:").SetTooltip("rotate display") + }) + core.Add(p, func(w *core.Button) { + w.SetIcon(icons.KeyboardArrowLeft). + Styler(func(s *styles.Style) { + s.SetAbilities(true, abilities.RepeatClickable) + }). + OnClick(func(e events.Event) { + nv.SceneXYZ().Camera.Orbit(5, 0) nv.UpdateView() - } - }) - core.NewButton(tb).SetIcon(icons.PlayArrow).SetTooltip("move to latest and always display latest (-1)"). - OnClick(func(e events.Event) { - if nv.RecTrackLatest() { + }) + }) + core.Add(p, func(w *core.Button) { + w.SetIcon(icons.KeyboardArrowUp). + Styler(func(s *styles.Style) { + s.SetAbilities(true, abilities.RepeatClickable) + }). + OnClick(func(e events.Event) { + nv.SceneXYZ().Camera.Orbit(0, 5) nv.UpdateView() - } - }) - core.NewButton(tb).SetIcon(icons.SkipNext).SetTooltip("move later by 1"). - Styler(func(s *styles.Style) { - s.SetAbilities(true, abilities.RepeatClickable) - }). - OnClick(func(e events.Event) { - if nv.RecFwd() { + }) + }) + core.Add(p, func(w *core.Button) { + w.SetIcon(icons.KeyboardArrowDown). + Styler(func(s *styles.Style) { + s.SetAbilities(true, abilities.RepeatClickable) + }). + OnClick(func(e events.Event) { + nv.SceneXYZ().Camera.Orbit(0, -5) nv.UpdateView() - } - }) - core.NewButton(tb).SetIcon(icons.FastForward).SetTooltip("move later by N (default 10)"). - Styler(func(s *styles.Style) { - s.SetAbilities(true, abilities.RepeatClickable) - }). - OnClick(func(e events.Event) { - if nv.RecFastFwd() { + }) + }) + core.Add(p, func(w *core.Button) { + w.SetIcon(icons.KeyboardArrowRight). + Styler(func(s *styles.Style) { + s.SetAbilities(true, abilities.RepeatClickable) + }). + OnClick(func(e events.Event) { + nv.SceneXYZ().Camera.Orbit(-5, 0) nv.UpdateView() - } - }) - core.NewButton(tb).SetIcon(icons.LastPage).SetTooltip("move to end (current time, tracking latest updates)"). - OnClick(func(e events.Event) { - if nv.RecTrackLatest() { + }) + }) + core.Add(p, func(w *core.Separator) {}) + + core.Add(p, func(w *core.Text) { + w.SetText("Pan:").SetTooltip("pan display") + }) + core.Add(p, func(w *core.Button) { + w.SetIcon(icons.KeyboardArrowLeft). + Styler(func(s *styles.Style) { + s.SetAbilities(true, abilities.RepeatClickable) + }). + OnClick(func(e events.Event) { + nv.SceneXYZ().Camera.Pan(-.2, 0) nv.UpdateView() - } + }) + }) + core.Add(p, func(w *core.Button) { + w.SetIcon(icons.KeyboardArrowUp). + Styler(func(s *styles.Style) { + s.SetAbilities(true, abilities.RepeatClickable) + }). + OnClick(func(e events.Event) { + nv.SceneXYZ().Camera.Pan(0, .2) + nv.UpdateView() + }) + }) + core.Add(p, func(w *core.Button) { + w.SetIcon(icons.KeyboardArrowDown). + Styler(func(s *styles.Style) { + s.SetAbilities(true, abilities.RepeatClickable) + }). + OnClick(func(e events.Event) { + nv.SceneXYZ().Camera.Pan(0, -.2) + nv.UpdateView() + }) + }) + core.Add(p, func(w *core.Button) { + w.SetIcon(icons.KeyboardArrowRight). + Styler(func(s *styles.Style) { + s.SetAbilities(true, abilities.RepeatClickable) + }). + OnClick(func(e events.Event) { + nv.SceneXYZ().Camera.Pan(.2, 0) + nv.UpdateView() + }) + }) + core.Add(p, func(w *core.Separator) {}) + + core.Add(p, func(w *core.Text) { w.SetText("Save:") }) + + for i := 1; i <= 4; i++ { + nm := fmt.Sprintf("%d", i) + core.AddAt(p, "saved-"+nm, func(w *core.Button) { + w.SetText(nm). + SetTooltip("first click (or + Shift) saves current view, second click restores to saved state"). + OnClick(func(e events.Event) { + sc := nv.SceneXYZ() + cam := nm + if e.HasAllModifiers(e.Modifiers(), key.Shift) { + sc.SaveCamera(cam) + } else { + err := sc.SetCamera(cam) + if err != nil { + sc.SaveCamera(cam) + } + } + fmt.Printf("Camera %s: %v\n", cam, sc.Camera.GenGoSet("")) + nv.UpdateView() + }) }) + } + core.Add(p, func(w *core.Separator) {}) + + core.Add(p, func(w *core.Text) { + w.SetText("Time:"). + SetTooltip("states are recorded over time -- last N can be reviewed using these buttons") + }) + + core.AddAt(p, "rec", func(w *core.Text) { + w.SetText(fmt.Sprintf("%4d ", nv.RecNo)). + SetTooltip("current view record: -1 means latest, 0 = earliest") + }) + core.Add(p, func(w *core.Button) { + w.SetIcon(icons.FirstPage).SetTooltip("move to first record (start of history)"). + OnClick(func(e events.Event) { + if nv.RecFullBkwd() { + nv.UpdateView() + } + }) + }) + core.Add(p, func(w *core.Button) { + w.SetIcon(icons.FastRewind).SetTooltip("move earlier by N records (default 10)"). + Styler(func(s *styles.Style) { + s.SetAbilities(true, abilities.RepeatClickable) + }). + OnClick(func(e events.Event) { + if nv.RecFastBkwd() { + nv.UpdateView() + } + }) + }) + core.Add(p, func(w *core.Button) { + w.SetIcon(icons.SkipPrevious).SetTooltip("move earlier by 1"). + Styler(func(s *styles.Style) { + s.SetAbilities(true, abilities.RepeatClickable) + }). + OnClick(func(e events.Event) { + if nv.RecBkwd() { + nv.UpdateView() + } + }) + }) + core.Add(p, func(w *core.Button) { + w.SetIcon(icons.PlayArrow).SetTooltip("move to latest and always display latest (-1)"). + OnClick(func(e events.Event) { + if nv.RecTrackLatest() { + nv.UpdateView() + } + }) + }) + core.Add(p, func(w *core.Button) { + w.SetIcon(icons.SkipNext).SetTooltip("move later by 1"). + Styler(func(s *styles.Style) { + s.SetAbilities(true, abilities.RepeatClickable) + }). + OnClick(func(e events.Event) { + if nv.RecFwd() { + nv.UpdateView() + } + }) + }) + core.Add(p, func(w *core.Button) { + w.SetIcon(icons.FastForward).SetTooltip("move later by N (default 10)"). + Styler(func(s *styles.Style) { + s.SetAbilities(true, abilities.RepeatClickable) + }). + OnClick(func(e events.Event) { + if nv.RecFastFwd() { + nv.UpdateView() + } + }) + }) + core.Add(p, func(w *core.Button) { + w.SetIcon(icons.LastPage).SetTooltip("move to end (current time, tracking latest updates)"). + OnClick(func(e events.Event) { + if nv.RecTrackLatest() { + nv.UpdateView() + } + }) + }) } // SaveWeights saves the network weights. diff --git a/netview/params.go b/netview/params.go index 9ce003c0..27ca6550 100644 --- a/netview/params.go +++ b/netview/params.go @@ -51,7 +51,7 @@ func (nv *RasterParams) Defaults() { type Params struct { //types:add // raster plot parameters - Raster RasterParams `view:"inline"` + Raster RasterParams `display:"inline"` // do not record synapse level data -- turn this on for very large networks where recording the entire synaptic state would be prohibitive NoSynData bool @@ -78,7 +78,7 @@ type Params struct { //types:add ZeroAlpha float32 `min:"0" max:"1" step:"0.1" default:"0.5"` // our netview, for update method - NetView *NetView `copier:"-" json:"-" xml:"-" view:"-"` + NetView *NetView `copier:"-" json:"-" xml:"-" display:"-"` // the number of records to jump for fast forward/backward NFastSteps int @@ -126,10 +126,10 @@ type VarParams struct { //types:add ZeroCtr bool // range to display - Range minmax.Range32 `view:"inline"` + Range minmax.Range32 `display:"inline"` // if not using fixed range, this is the actual range of data - MinMax minmax.F32 `view:"inline"` + MinMax minmax.F32 `display:"inline"` } // Defaults sets default values if otherwise not set diff --git a/netview/typegen.go b/netview/typegen.go index 8dd599e5..75e6add8 100644 --- a/netview/typegen.go +++ b/netview/typegen.go @@ -3,6 +3,10 @@ package netview import ( + "sync" + + "cogentcore.org/core/colors/colormap" + "cogentcore.org/core/core" "cogentcore.org/core/tree" "cogentcore.org/core/types" ) @@ -69,7 +73,71 @@ func (t *LayName) SetNetView(v *NetView) *LayName { t.NetView = v; return t } var _ = types.AddType(&types.Type{Name: "github.com/emer/emergent/v2/netview.NetData", IDName: "net-data", Doc: "NetData maintains a record of all the network data that has been displayed\nup to a given maximum number of records (updates), using efficient ring index logic\nwith no copying to store in fixed-sized buffers.", Directives: []types.Directive{{Tool: "types", Directive: "add"}}, Methods: []types.Method{{Name: "OpenJSON", Doc: "OpenJSON opens colors from a JSON-formatted file.", Directives: []types.Directive{{Tool: "types", Directive: "add"}}, Args: []string{"filename"}, Returns: []string{"error"}}, {Name: "SaveJSON", Doc: "SaveJSON saves colors to a JSON-formatted file.", Directives: []types.Directive{{Tool: "types", Directive: "add"}}, Args: []string{"filename"}, Returns: []string{"error"}}}, Fields: []types.Field{{Name: "Net", Doc: "the network that we're viewing"}, {Name: "NoSynData", Doc: "copied from Params -- do not record synapse level data -- turn this on for very large networks where recording the entire synaptic state would be prohibitive"}, {Name: "PathLay", Doc: "name of the layer with unit for viewing pathways (connection / synapse-level values)"}, {Name: "PathUnIndex", Doc: "1D index of unit within PathLay for for viewing pathways"}, {Name: "PathType", Doc: "copied from NetView Params: if non-empty, this is the type pathway to show when there are multiple pathways from the same layer -- e.g., Inhib, Lateral, Forward, etc"}, {Name: "UnVars", Doc: "the list of unit variables saved"}, {Name: "UnVarIndexes", Doc: "index of each variable in the Vars slice"}, {Name: "SynVars", Doc: "the list of synaptic variables saved"}, {Name: "SynVarIndexes", Doc: "index of synaptic variable in the SynVars slice"}, {Name: "Ring", Doc: "the circular ring index -- Max here is max number of values to store, Len is number stored, and Index(Len-1) is the most recent one, etc"}, {Name: "MaxData", Doc: "max data parallel data per unit"}, {Name: "LayData", Doc: "the layer data -- map keyed by layer name"}, {Name: "UnMinPer", Doc: "unit var min values for each Ring.Max * variable"}, {Name: "UnMaxPer", Doc: "unit var max values for each Ring.Max * variable"}, {Name: "UnMinVar", Doc: "min values for unit variables"}, {Name: "UnMaxVar", Doc: "max values for unit variables"}, {Name: "SynMinVar", Doc: "min values for syn variables"}, {Name: "SynMaxVar", Doc: "max values for syn variables"}, {Name: "Counters", Doc: "counter strings"}, {Name: "RasterCtrs", Doc: "raster counter values"}, {Name: "RasterMap", Doc: "map of raster counter values to record numbers"}, {Name: "RastCtr", Doc: "dummy raster counter when passed a -1 -- increments and wraps around"}}}) -var _ = types.AddType(&types.Type{Name: "github.com/emer/emergent/v2/netview.NetView", IDName: "net-view", Doc: "NetView is a Cogent Core Widget that provides a 3D network view using the Cogent Core gi3d\n3D framework.", Methods: []types.Method{{Name: "PlotSelectedUnit", Doc: "PlotSelectedUnit opens a window with a plot of all the data for the\ncurrently selected unit.\nUseful for replaying detailed trace for units of interest.", Directives: []types.Directive{{Tool: "types", Directive: "add"}}, Returns: []string{"Table", "PlotEditor"}}, {Name: "Current", Doc: "Current records the current state of the network, including synaptic values,\nand updates the display. Use this when switching to NetView tab after network\nhas been running while viewing another tab, because the network state\nis typically not recored then.", Directives: []types.Directive{{Tool: "types", Directive: "add"}}}, {Name: "SaveWeights", Doc: "SaveWeights saves the network weights.", Directives: []types.Directive{{Tool: "types", Directive: "add"}}, Args: []string{"filename"}}, {Name: "OpenWeights", Doc: "OpenWeights opens the network weights.", Directives: []types.Directive{{Tool: "types", Directive: "add"}}, Args: []string{"filename"}}, {Name: "ShowNonDefaultParams", Doc: "ShowNonDefaultParams shows a dialog of all the parameters that\nare not at their default values in the network. Useful for setting params.", Directives: []types.Directive{{Tool: "types", Directive: "add"}}, Returns: []string{"string"}}, {Name: "ShowAllParams", Doc: "ShowAllParams shows a dialog of all the parameters in the network.", Directives: []types.Directive{{Tool: "types", Directive: "add"}}, Returns: []string{"string"}}, {Name: "ShowKeyLayerParams", Doc: "ShowKeyLayerParams shows a dialog with a listing for all layers in the network,\nof the most important layer-level params (specific to each algorithm)", Directives: []types.Directive{{Tool: "types", Directive: "add"}}, Returns: []string{"string"}}, {Name: "ShowKeyPathParams", Doc: "ShowKeyPathParams shows a dialog with a listing for all Recv pathways in the network,\nof the most important pathway-level params (specific to each algorithm)", Directives: []types.Directive{{Tool: "types", Directive: "add"}}, Returns: []string{"string"}}}, Embeds: []types.Field{{Name: "invalid type"}}, Fields: []types.Field{{Name: "Net", Doc: "the network that we're viewing"}, {Name: "Var", Doc: "current variable that we're viewing"}, {Name: "Di", Doc: "current data parallel index di, for networks capable of processing input patterns in parallel."}, {Name: "Vars", Doc: "the list of variables to view"}, {Name: "SynVars", Doc: "list of synaptic variables"}, {Name: "SynVarsMap", Doc: "map of synaptic variable names to index"}, {Name: "VarParams", Doc: "parameters for the list of variables to view"}, {Name: "CurVarParams", Doc: "current var params -- only valid during Update of display"}, {Name: "Params", Doc: "parameters controlling how the view is rendered"}, {Name: "ColorMap", Doc: "color map for mapping values to colors -- set by name in Params"}, {Name: "ColorMapButton", Doc: "color map value representing ColorMap"}, {Name: "RecNo", Doc: "record number to display -- use -1 to always track latest, otherwise in range"}, {Name: "LastCtrs", Doc: "last non-empty counters string provided -- re-used if no new one"}, {Name: "Data", Doc: "contains all the network data with history"}, {Name: "DataMu", Doc: "mutex on data access"}}}) +// NetViewType is the [types.Type] for [NetView] +var NetViewType = types.AddType(&types.Type{Name: "github.com/emer/emergent/v2/netview.NetView", IDName: "net-view", Doc: "NetView is a Cogent Core Widget that provides a 3D network view using the Cogent Core gi3d\n3D framework.", Methods: []types.Method{{Name: "PlotSelectedUnit", Doc: "PlotSelectedUnit opens a window with a plot of all the data for the\ncurrently selected unit.\nUseful for replaying detailed trace for units of interest.", Directives: []types.Directive{{Tool: "types", Directive: "add"}}, Returns: []string{"Table", "PlotEditor"}}, {Name: "Current", Doc: "Current records the current state of the network, including synaptic values,\nand updates the display. Use this when switching to NetView tab after network\nhas been running while viewing another tab, because the network state\nis typically not recored then.", Directives: []types.Directive{{Tool: "types", Directive: "add"}}}, {Name: "SaveWeights", Doc: "SaveWeights saves the network weights.", Directives: []types.Directive{{Tool: "types", Directive: "add"}}, Args: []string{"filename"}}, {Name: "OpenWeights", Doc: "OpenWeights opens the network weights.", Directives: []types.Directive{{Tool: "types", Directive: "add"}}, Args: []string{"filename"}}, {Name: "ShowNonDefaultParams", Doc: "ShowNonDefaultParams shows a dialog of all the parameters that\nare not at their default values in the network. Useful for setting params.", Directives: []types.Directive{{Tool: "types", Directive: "add"}}, Returns: []string{"string"}}, {Name: "ShowAllParams", Doc: "ShowAllParams shows a dialog of all the parameters in the network.", Directives: []types.Directive{{Tool: "types", Directive: "add"}}, Returns: []string{"string"}}, {Name: "ShowKeyLayerParams", Doc: "ShowKeyLayerParams shows a dialog with a listing for all layers in the network,\nof the most important layer-level params (specific to each algorithm)", Directives: []types.Directive{{Tool: "types", Directive: "add"}}, Returns: []string{"string"}}, {Name: "ShowKeyPathParams", Doc: "ShowKeyPathParams shows a dialog with a listing for all Recv pathways in the network,\nof the most important pathway-level params (specific to each algorithm)", Directives: []types.Directive{{Tool: "types", Directive: "add"}}, Returns: []string{"string"}}}, Embeds: []types.Field{{Name: "Frame"}}, Fields: []types.Field{{Name: "Net", Doc: "the network that we're viewing"}, {Name: "Var", Doc: "current variable that we're viewing"}, {Name: "Di", Doc: "current data parallel index di, for networks capable of processing input patterns in parallel."}, {Name: "Vars", Doc: "the list of variables to view"}, {Name: "SynVars", Doc: "list of synaptic variables"}, {Name: "SynVarsMap", Doc: "map of synaptic variable names to index"}, {Name: "VarParams", Doc: "parameters for the list of variables to view"}, {Name: "CurVarParams", Doc: "current var params -- only valid during Update of display"}, {Name: "Params", Doc: "parameters controlling how the view is rendered"}, {Name: "ColorMap", Doc: "color map for mapping values to colors -- set by name in Params"}, {Name: "ColorMapButton", Doc: "color map value representing ColorMap"}, {Name: "RecNo", Doc: "record number to display -- use -1 to always track latest, otherwise in range"}, {Name: "LastCtrs", Doc: "last non-empty counters string provided -- re-used if no new one"}, {Name: "Data", Doc: "contains all the network data with history"}, {Name: "DataMu", Doc: "mutex on data access"}}, Instance: &NetView{}}) + +// NewNetView returns a new [NetView] with the given optional parent: +// NetView is a Cogent Core Widget that provides a 3D network view using the Cogent Core gi3d +// 3D framework. +func NewNetView(parent ...tree.Node) *NetView { return tree.New[*NetView](parent...) } + +// NodeType returns the [*types.Type] of [NetView] +func (t *NetView) NodeType() *types.Type { return NetViewType } + +// New returns a new [*NetView] value +func (t *NetView) New() tree.Node { return &NetView{} } + +// SetDi sets the [NetView.Di]: +// current data parallel index di, for networks capable of processing input patterns in parallel. +func (t *NetView) SetDi(v int) *NetView { t.Di = v; return t } + +// SetVars sets the [NetView.Vars]: +// the list of variables to view +func (t *NetView) SetVars(v ...string) *NetView { t.Vars = v; return t } + +// SetSynVars sets the [NetView.SynVars]: +// list of synaptic variables +func (t *NetView) SetSynVars(v ...string) *NetView { t.SynVars = v; return t } + +// SetSynVarsMap sets the [NetView.SynVarsMap]: +// map of synaptic variable names to index +func (t *NetView) SetSynVarsMap(v map[string]int) *NetView { t.SynVarsMap = v; return t } + +// SetVarParams sets the [NetView.VarParams]: +// parameters for the list of variables to view +func (t *NetView) SetVarParams(v map[string]*VarParams) *NetView { t.VarParams = v; return t } + +// SetCurVarParams sets the [NetView.CurVarParams]: +// current var params -- only valid during Update of display +func (t *NetView) SetCurVarParams(v *VarParams) *NetView { t.CurVarParams = v; return t } + +// SetParams sets the [NetView.Params]: +// parameters controlling how the view is rendered +func (t *NetView) SetParams(v Params) *NetView { t.Params = v; return t } + +// SetColorMap sets the [NetView.ColorMap]: +// color map for mapping values to colors -- set by name in Params +func (t *NetView) SetColorMap(v *colormap.Map) *NetView { t.ColorMap = v; return t } + +// SetColorMapButton sets the [NetView.ColorMapButton]: +// color map value representing ColorMap +func (t *NetView) SetColorMapButton(v *core.ColorMapButton) *NetView { t.ColorMapButton = v; return t } + +// SetRecNo sets the [NetView.RecNo]: +// record number to display -- use -1 to always track latest, otherwise in range +func (t *NetView) SetRecNo(v int) *NetView { t.RecNo = v; return t } + +// SetLastCtrs sets the [NetView.LastCtrs]: +// last non-empty counters string provided -- re-used if no new one +func (t *NetView) SetLastCtrs(v string) *NetView { t.LastCtrs = v; return t } + +// SetData sets the [NetView.Data]: +// contains all the network data with history +func (t *NetView) SetData(v NetData) *NetView { t.Data = v; return t } + +// SetDataMu sets the [NetView.DataMu]: +// mutex on data access +func (t *NetView) SetDataMu(v sync.RWMutex) *NetView { t.DataMu = v; return t } var _ = types.AddType(&types.Type{Name: "github.com/emer/emergent/v2/netview.RasterParams", IDName: "raster-params", Doc: "RasterParams holds parameters controlling the raster plot view", Directives: []types.Directive{{Tool: "types", Directive: "add"}}, Fields: []types.Field{{Name: "On", Doc: "if true, show a raster plot over time, otherwise units"}, {Name: "XAxis", Doc: "if true, the raster counter (time) is plotted across the X axis -- otherwise the Z depth axis"}, {Name: "Max", Doc: "maximum count for the counter defining the raster plot"}, {Name: "UnitSize", Doc: "size of a single unit, where 1 = full width and no space.. 1 default"}, {Name: "UnitHeight", Doc: "height multiplier for units, where 1 = full height.. 0.2 default"}}}) diff --git a/netview/viewupdt.go b/netview/viewupdt.go index 3ce76ed9..ca30d826 100644 --- a/netview/viewupdt.go +++ b/netview/viewupdt.go @@ -14,13 +14,13 @@ import ( type ViewUpdate struct { // the network view - View *NetView `view:"-"` + View *NetView `display:"-"` // whether in testing mode -- can be set in advance to drive appropriate updating - Testing bool `view:"-"` + Testing bool `display:"-"` // text to display at the bottom of the view - Text string `view:"-"` + Text string `display:"-"` // toggles update of display on On bool diff --git a/params/flex.go b/params/flex.go index 876f7a6f..c5652a77 100644 --- a/params/flex.go +++ b/params/flex.go @@ -31,7 +31,7 @@ type FlexVal struct { Obj any // History of params applied - History HistoryImpl `tableview:"-"` + History HistoryImpl `tabledisplay:"-"` } func (fv *FlexVal) TypeName() string { diff --git a/params/params.go b/params/params.go index 962ec85d..ea11ccc3 100644 --- a/params/params.go +++ b/params/params.go @@ -62,16 +62,16 @@ type Sel struct { //types:add Desc string `width:"60"` // parameter values to apply to whatever matches the selector - Params Params `view:"no-inline"` + Params Params `display:"no-inline"` // Put your hyperparams here Hypers Hypers // number of times this selector matched a target during the last Apply process -- a warning is issued for any that remain at 0 -- see Sheet SelMatchReset and SelNoMatchWarn methods - NMatch int `tableview:"-" toml:"-" json:"-" xml:"-" edit:"-"` + NMatch int `tabledisplay:"-" toml:"-" json:"-" xml:"-" edit:"-"` // name of current Set being applied - SetName string `tableview:"-" toml:"-" json:"-" xml:"-" edit:"-"` + SetName string `tabledisplay:"-" toml:"-" json:"-" xml:"-" edit:"-"` } // SetFloat sets the value of given parameter diff --git a/paths/typegen.go b/paths/typegen.go index 89f2bee8..974fb07c 100644 --- a/paths/typegen.go +++ b/paths/typegen.go @@ -28,8 +28,8 @@ var _ = types.AddType(&types.Type{Name: "github.com/emer/emergent/v2/paths.Sigmo var _ = types.AddType(&types.Type{Name: "github.com/emer/emergent/v2/paths.PoolTileSub", IDName: "pool-tile-sub", Doc: "PoolTileSub implements tiled 2D connectivity between pools within layers, where\na 2D rectangular receptive field (defined over pools, not units) is tiled\nacross the sending layer pools, with specified level of overlap.\nPools are the outer-most two dimensions of a 4D layer shape.\nSub version has sub-pools within each pool to encourage more independent\nrepresentations.\n2D layers are assumed to have 1x1 pool.\nThis is a standard form of convolutional connectivity, where pools are\nthe filters and the outer dims are locations filtered.\nVarious initial weight / scaling patterns are also available -- code\nmust specifically apply these to the receptive fields.", Fields: []types.Field{{Name: "Recip", Doc: "reciprocal topographic connectivity -- logic runs with recv <-> send -- produces symmetric back-pathway or topo path when sending layer is larger than recv"}, {Name: "Size", Doc: "size of receptive field tile, in terms of pools on the sending layer"}, {Name: "Skip", Doc: "how many pools to skip in tiling over sending layer -- typically 1/2 of Size"}, {Name: "Start", Doc: "starting pool offset for lower-left corner of first receptive field in sending layer"}, {Name: "Subs", Doc: "number of sub-pools within each pool"}, {Name: "SendSubs", Doc: "sending layer has sub-pools"}, {Name: "Wrap", Doc: "if true, pool coordinates wrap around sending shape -- otherwise truncated at edges, which can lead to assymmetries in connectivity etc"}, {Name: "GaussFull", Doc: "gaussian topographic weights / scaling parameters for full receptive field width. multiplies any other factors present"}, {Name: "GaussInPool", Doc: "gaussian topographic weights / scaling parameters within individual sending pools (i.e., unit positions within their parent pool drive distance for gaussian) -- this helps organize / differentiate units more within pools, not just across entire receptive field. multiplies any other factors present"}, {Name: "SigFull", Doc: "sigmoidal topographic weights / scaling parameters for full receptive field width. left / bottom half have increasing sigmoids, and second half decrease. Multiplies any other factors present (only used if Gauss versions are not On!)"}, {Name: "SigInPool", Doc: "sigmoidal topographic weights / scaling parameters within individual sending pools (i.e., unit positions within their parent pool drive distance for sigmoid) -- this helps organize / differentiate units more within pools, not just across entire receptive field. multiplies any other factors present (only used if Gauss versions are not On!). left / bottom half have increasing sigmoids, and second half decrease."}, {Name: "TopoRange", Doc: "min..max range of topographic weight values to generate"}}}) -var _ = types.AddType(&types.Type{Name: "github.com/emer/emergent/v2/paths.PoolUniformRand", IDName: "pool-unif-rnd", Doc: "PoolUniformRand implements random pattern of connectivity between pools within layers.\nPools are the outer-most two dimensions of a 4D layer shape.\nIf either layer does not have pools, PoolUniformRand works as UniformRand does.\nIf probability of connection (PCon) is 1, PoolUniformRand works as PoolOnetoOne does.", Embeds: []types.Field{{Name: "PoolOneToOne"}, {Name: "UniformRand"}}}) +var _ = types.AddType(&types.Type{Name: "github.com/emer/emergent/v2/paths.PoolUniformRand", IDName: "pool-uniform-rand", Doc: "PoolUniformRand implements random pattern of connectivity between pools within layers.\nPools are the outer-most two dimensions of a 4D layer shape.\nIf either layer does not have pools, PoolUniformRand works as UniformRand does.\nIf probability of connection (PCon) is 1, PoolUniformRand works as PoolOnetoOne does.", Embeds: []types.Field{{Name: "PoolOneToOne"}, {Name: "UniformRand"}}}) var _ = types.AddType(&types.Type{Name: "github.com/emer/emergent/v2/paths.Rect", IDName: "rect", Doc: "Rect implements a rectangular pattern of connectivity between two layers\nwhere the lower-left corner moves in proportion to receiver position with offset\nand multiplier factors (with wrap-around optionally).\n4D layers are automatically flattened to 2D for this pathway.", Fields: []types.Field{{Name: "Size", Doc: "size of rectangle in sending layer that each receiving unit receives from"}, {Name: "Start", Doc: "starting offset in sending layer, for computing the corresponding sending lower-left corner relative to given recv unit position"}, {Name: "Scale", Doc: "scaling to apply to receiving unit position to compute corresponding position in sending layer of the lower-left corner of rectangle"}, {Name: "AutoScale", Doc: "auto-set the Scale as function of the relative sizes of send and recv layers (e.g., if sending layer is 2x larger than receiving, Scale = 2)"}, {Name: "RoundScale", Doc: "if true, use Round when applying scaling factor -- otherwise uses Floor which makes Scale work like a grouping factor -- e.g., .25 will effectively group 4 recv units with same send position"}, {Name: "Wrap", Doc: "if true, connectivity wraps around all edges if it would otherwise go off the edge -- if false, then edges are clipped"}, {Name: "SelfCon", Doc: "if true, and connecting layer to itself (self pathway), then make a self-connection from unit to itself"}, {Name: "Recip", Doc: "make the reciprocal of the specified connections -- i.e., symmetric for swapping recv and send"}, {Name: "RecvStart", Doc: "starting position in receiving layer -- if > 0 then units below this starting point remain unconnected"}, {Name: "RecvN", Doc: "number of units in receiving layer to connect -- if 0 then all (remaining after RecvStart) are connected -- otherwise if < remaining then those beyond this point remain unconnected"}}}) -var _ = types.AddType(&types.Type{Name: "github.com/emer/emergent/v2/paths.UniformRand", IDName: "unif-rnd", Doc: "UniformRand implements uniform random pattern of connectivity between two layers\nusing a permuted (shuffled) list for without-replacement randomness,\nand maintains its own local random number source and seed\nwhich are initialized if Rand == nil -- usually best to keep this\nspecific to each instance of a pathway so it is fully reproducible\nand doesn't interfere with other random number streams.", Fields: []types.Field{{Name: "PCon", Doc: "probability of connection (0-1)"}, {Name: "SelfCon", Doc: "if true, and connecting layer to itself (self pathway), then make a self-connection from unit to itself"}, {Name: "Recip", Doc: "reciprocal connectivity: if true, switch the sending and receiving layers to create a symmetric top-down pathway -- ESSENTIAL to use same RandSeed between two paths to ensure symmetry"}, {Name: "Rand", Doc: "random number source -- is created with its own separate source if nil"}, {Name: "RandSeed", Doc: "the current random seed -- will be initialized to a new random number from the global random stream when Rand is created."}}}) +var _ = types.AddType(&types.Type{Name: "github.com/emer/emergent/v2/paths.UniformRand", IDName: "uniform-rand", Doc: "UniformRand implements uniform random pattern of connectivity between two layers\nusing a permuted (shuffled) list for without-replacement randomness,\nand maintains its own local random number source and seed\nwhich are initialized if Rand == nil -- usually best to keep this\nspecific to each instance of a pathway so it is fully reproducible\nand doesn't interfere with other random number streams.", Fields: []types.Field{{Name: "PCon", Doc: "probability of connection (0-1)"}, {Name: "SelfCon", Doc: "if true, and connecting layer to itself (self pathway), then make a self-connection from unit to itself"}, {Name: "Recip", Doc: "reciprocal connectivity: if true, switch the sending and receiving layers to create a symmetric top-down pathway -- ESSENTIAL to use same RandSeed between two paths to ensure symmetry"}, {Name: "Rand", Doc: "random number source -- is created with its own separate source if nil"}, {Name: "RandSeed", Doc: "the current random seed -- will be initialized to a new random number from the global random stream when Rand is created."}}}) diff --git a/paths/uniformrand.go b/paths/uniformrand.go index 3f142e1e..d5f6d926 100644 --- a/paths/uniformrand.go +++ b/paths/uniformrand.go @@ -31,10 +31,10 @@ type UniformRand struct { Recip bool // random number source -- is created with its own separate source if nil - Rand randx.Rand `view:"-"` + Rand randx.Rand `display:"-"` // the current random seed -- will be initialized to a new random number from the global random stream when Rand is created. - RandSeed int64 `view:"-"` + RandSeed int64 `display:"-"` } func NewUniformRand() *UniformRand { diff --git a/popcode/ring.go b/popcode/ring.go index a1d77a8e..32008290 100644 --- a/popcode/ring.go +++ b/popcode/ring.go @@ -19,10 +19,10 @@ type Ring struct { OneD // low-end encoding vector - LowVec []float32 `view:"-"` + LowVec []float32 `display:"-"` // high-end encoding vector - HighVec []float32 `view:"-"` + HighVec []float32 `display:"-"` } // AllocVecs allocates internal LowVec, HighVec storage,