Skip to content

Commit

Permalink
netview / network supports category tabs for variables -- allows ever…
Browse files Browse the repository at this point in the history
…ything to work nicely even on a phone, and is conceptually and practically useful to find variables, especially critical for axon
  • Loading branch information
rcoreilly committed Aug 24, 2024
1 parent a2f0334 commit 4d98334
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 33 deletions.
28 changes: 22 additions & 6 deletions emer/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ import (
"github.com/emer/emergent/v2/relpos"
)

// VarCategory represents one category of unit, synapse variables.
type VarCategory struct {
// Category name.
Cat string
// Description of the category, used as a tooltip.
Desc string
}

// Network defines the minimal interface for a neural network,
// used for managing the structural elements of a network,
// and for visualization, I/O, etc.
Expand Down Expand Up @@ -81,14 +89,22 @@ type Network interface {
// and the value gives a space-separated list of
// go-tag-style properties for that variable.
// The NetView recognizes the following properties:
// range:"##" = +- range around 0 for default display scaling
// min:"##" max:"##" = min, max display range
// auto-scale:"+" or "-" = use automatic scaling instead of fixed range or not.
// zeroctr:"+" or "-" = control whether zero-centering is used
// desc:"txt" tooltip description of the variable
// Note: this is typically a global list so do not modify!
// - range:"##" = +- range around 0 for default display scaling
// - min:"##" max:"##" = min, max display range
// - auto-scale:"+" or "-" = use automatic scaling instead of fixed range or not.
// - zeroctr:"+" or "-" = control whether zero-centering is used
// - desc:"txt" tooltip description of the variable
// - cat:"cat" variable category, for category tabs
UnitVarProps() map[string]string

// VarCategories is a list of unit & synapse variable categories,
// which organizes the variables into separate tabs in the network view.
// Using categories results in a more compact display and makes it easier
// to find variables.
// Set the 'cat' property in the UnitVarProps, SynVarProps for each variable.
// If no categories returned, the default is Unit, Wt.
VarCategories() []VarCategory

// SynVarNames returns the names of all the variables
// on the synapses in this network.
// This list determines what is shown in the NetView
Expand Down
83 changes: 56 additions & 27 deletions netview/netview.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"fmt"
"image/color"
"log"
"log/slog"
"reflect"
"strings"
"sync"
Expand Down Expand Up @@ -111,16 +112,7 @@ func (nv *NetView) Init() {
s.Direction = styles.Row
s.Grow.Set(1, 1)
})
tree.AddChildAt(w, "vars", func(w *core.Frame) {
w.Styler(func(s *styles.Style) {
s.Display = styles.Grid
s.Columns = nv.Params.NVarCols
s.Grow.Set(0, 1)
s.Overflow.Y = styles.OverflowAuto
s.Background = colors.Scheme.SurfaceContainerLow
})
w.Maker(nv.makeVars)
})
nv.makeVars(w)
tree.AddChildAt(w, "scene", func(w *Scene) {
w.NetView = nv
se := w.SceneXYZ()
Expand All @@ -132,7 +124,7 @@ func (nv *NetView) Init() {
tree.AddChildAt(nv, "counters", func(w *core.Text) {
w.SetText("Counters: " + strings.Repeat(" ", 200)).
Styler(func(s *styles.Style) {
s.Min.X.Ch(200)
s.Grow.Set(1, 0)
})
w.Updater(func() {
if w.Text != nv.CurCtrs && nv.CurCtrs != "" {
Expand Down Expand Up @@ -342,8 +334,8 @@ func (nv *NetView) SceneXYZ() *xyz.Scene {
return nv.SceneWidget().SceneXYZ()
}

func (nv *NetView) VarsFrame() *core.Frame {
return nv.NetFrame().ChildByName("vars", 0).(*core.Frame)
func (nv *NetView) VarsFrame() *core.Tabs {
return nv.NetFrame().ChildByName("vars", 0).(*core.Tabs)
}

// SetCounters sets the counters widget view display at bottom of netview
Expand Down Expand Up @@ -457,11 +449,11 @@ func (nv *NetView) NetVarsList(net emer.Network, layEven bool) (nvars, synvars [
unvars := net.UnitVarNames()
synvars = net.SynVarNames()
ulen := len(unvars)
ncols := NVarCols // nv.Params.NVarCols
nr := ulen % ncols
if layEven && nr != 0 { // make it an even number
ulen += ncols - nr
}
// ncols := NVarCols // nv.Params.NVarCols
// nr := ulen % ncols
// if layEven && nr != 0 { // make it an even number
// ulen += ncols - nr
// }

tlen := ulen + 2*len(synvars)
nvars = make([]string, tlen)
Expand Down Expand Up @@ -508,36 +500,73 @@ func (nv *NetView) VarsListUpdate() {
}

// makeVars configures the variables
func (nv *NetView) makeVars(p *tree.Plan) {
func (nv *NetView) makeVars(netframe *core.Frame) {
nv.VarsListUpdate()
if nv.Net == nil {
return
}
unprops := nv.Net.UnitVarProps()
pathprops := nv.Net.SynVarProps()
for _, vn := range nv.Vars {
tree.AddAt(p, vn, func(w *core.Button) {
w.SetText(vn).SetType(core.ButtonAction)
cats := nv.Net.VarCategories()
if len(cats) == 0 {
cats = []emer.VarCategory{
{"Unit", "unit variables"},
{"Wt", "connection weight variables"},
}
}
tree.AddChildAt(netframe, "vars", func(w *core.Tabs) {
w.Styler(func(s *styles.Style) {
s.Grow.Set(0, 1)
s.Overflow.Y = styles.OverflowAuto
})
tabs := make(map[string]*core.Frame)
for _, ct := range cats {
tf := w.NewTab(ct.Cat)
// todo: no way to set tooltip!?
tabs[ct.Cat] = tf
tf.Styler(func(s *styles.Style) {
s.Display = styles.Grid
s.Columns = nv.Params.NVarCols
s.Grow.Set(1, 1)
s.Overflow.Y = styles.OverflowAuto
s.Background = colors.Scheme.SurfaceContainerLow
})
}
for _, vn := range nv.Vars {
cat := ""
pstr := ""
desc := ""
if strings.HasPrefix(vn, "r.") || strings.HasPrefix(vn, "s.") {
pstr = pathprops[vn[2:]]
cat = "Wt" // default
} else {
pstr = unprops[vn]
cat = "Unit"
}
if pstr != "" {
rstr := reflect.StructTag(pstr)
if desc, ok := rstr.Lookup("desc"); ok {
w.Tooltip = vn + ": " + desc
}
desc = rstr.Get("desc")
cat = rstr.Get("cat")
}
tf, ok := tabs[cat]
if !ok {
slog.Error("emergent.NetView UnitVarProps 'cat' name not found in VarCategories list", "cat", cat, "variable", vn)
cat = cats[0].Cat
tf = tabs[cat]
}
w := core.NewButton(tf).SetText(vn)
if desc != "" {
w.Tooltip = vn + ": " + desc
}
w.SetText(vn).SetType(core.ButtonAction)
w.OnClick(func(e events.Event) {
nv.SetVar(vn)
})
w.Updater(func() {
w.SetSelected(w.Text == nv.Var)
})
})
}
}
})
}

// UpdateLayers updates the layer display with any structural or
Expand Down

0 comments on commit 4d98334

Please sign in to comment.