looper

package
v2.0.0-dev0.1.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 7, 2024 License: BSD-3-Clause Imports: 9 Imported by: 47

README

Looper: A flexible steppable control hierarchy

Docs: GoDoc

Looper implements a fully generic looping control system, with a Stack of Loop elements that iterate over different time scales of processing, where the processing performed is provided by function closures on the Loop elements.

Critically, the loop logic supports reentrant stepping, such that you can iteratively Step through the loop processing and accomplish exactly the same outcomes as if you did a complete Run from the start.

Each loop implements the following logic, where it is key to understand that the time scale associated with the loop runs the full iterations over that time scale. For example, a Trial loop iterates over Trials -- it is not a single trial, but the whole sequence (loop) of trials.

for {
	Events[Counter == AtCounter] // run events at counter
	OnStart()
	    Run Sub-Loop to completion
	OnEnd() 
	Counter += Inc
	if Counter >= Max || IsDone() {
	  break
	}
}

The Loop object has the above function lists (OnStart, OnEnd, and IsDone), where function closures can be added to perform any relevant functionality. Events have the trigger AtCounter and a list of functions to call. If the Loop is the last one in the Stack, then,

Each level of loop holds a corresponding Counter value, which increments at each iteration, and its Max value determines when the loop iteration terminates.

Each Stack of loops has an associated Mode enum, e.g., Train or Test, and each Loop has an associated Time level, e.g., Run, Epoch, Trial.

The collection of Stacks has a high-level API for configuring and controlling the set of Stack elements, and has the logic for running everything, in the form of Run, Step, Stop methods, etc.

Examples

The following examples use the etime Modes and Times enums. It is recommended that you define your own Modes enums if not using the basic Train and Test cases, to provide a better egui representation of the loop stack.

Configuration

From step_test.go ExampleStacks:

	stacks := NewStacks()
	stacks.AddStack(etime.Train).
		AddTime(etime.Epoch, 3).
		AddTime(etime.Trial, 2)

	// add function closures:         
	stacks.Loop(etime.Train, etime.Epoch).OnStart.Add("Epoch Start", func() { fmt.Println("Epoch Start") })
	stacks.Loop(etime.Train, etime.Epoch).OnEnd.Add("Epoch End", func() { fmt.Println("Epoch End") })
	stacks.Loop(etime.Train, etime.Trial).OnStart.Add("Trial Run", func() { fmt.Println("  Trial Run") })

   // add events:
	stacks.Loop(etime.Train, etime.Epoch).AddEvent("EpochTwoEvent", 2, func() { fmt.Println("Epoch==2") })
	stacks.Loop(etime.Train, etime.Trial).AddEvent("TrialOneEvent", 1, func() { fmt.Println("  Trial==1") })

The DocString for this stack is:

Stack Train:
   Epoch[0 : 3]:
      Events:
         EpochTwoEvent: [at 2] Events: EpochTwoEvent 
      Start:  Epoch Start 
      Trial[0 : 2]:
         Events:
            TrialOneEvent: [at 1] Events: TrialOneEvent 
         Start:  Trial Run 
      End:    Epoch End 

and the output when run is:

Epoch Start
  Trial Run
  Trial==1
  Trial Run
Epoch End
Epoch Start
  Trial Run
  Trial==1
  Trial Run
Epoch End
Epoch==2
Epoch Start
  Trial Run
  Trial==1
  Trial Run
Epoch End

Running, Stepping

Run a full stack:

stacks.Run(etime.Train)

Reset first and Run, ensures that the full sequence is run even if it might have been stopped or stepped previously:

stacks.ResetAndRun(etime.Train)

Step by 1 Trial:

stacks.Step(etime.Train, 1, etime.Trial)

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// If you want to debug the flow of time, set this to true.
	PrintControlFlow = false
)

Functions

This section is empty.

Types

type Counter

type Counter struct {

	// Cur is the current counter value.
	Cur int

	// Max is the maximum counter value.
	// Only used if > 0 ([Loop] requires an IsDone condition to stop).
	Max int

	// Inc is the increment per iteration.
	Inc int
}

Counter combines an integer with a maximum value. It supports time tracking within looper.

func (*Counter) Incr

func (ct *Counter) Incr()

Incr increments the counter by Inc. Does not interact with Max.

func (*Counter) IsOverMax

func (ct *Counter) IsOverMax() bool

IsOverMax returns true if counter is at or over Max (only if Max > 0).

func (*Counter) Set

func (ct *Counter) Set(cur int) bool

Set sets the Cur value with return value indicating whether it is different from current Cur.

func (*Counter) SetCurMax

func (ct *Counter) SetCurMax(cur, max int)

SetCurMax sets the Cur and Max values, as a convenience.

func (*Counter) SetCurMaxPlusN

func (ct *Counter) SetCurMaxPlusN(cur, n int)

SetCurMaxPlusN sets the Cur value and Max as Cur + N -- run N more beyond current.

func (*Counter) SetMaxInc

func (ct *Counter) SetMaxInc(mx, inc int)

SetMaxIncr sets the given Max and Inc value for the counter.

func (*Counter) SkipToMax

func (ct *Counter) SkipToMax()

SkipToMax sets the counter to its Max value, for skipping over rest of loop iterations.

type Event

type Event struct {

	// Name of this event.
	Name string

	// AtCounter is the counter value upon which this Event occurs.
	AtCounter int

	// OnEvent are the functions to run when Counter == AtCounter.
	OnEvent NamedFuncs
}

A Event has function(s) that can be called at a particular point in the loop, when the counter is AtCounter value.

func NewEvent

func NewEvent(name string, atCtr int, fun func()) *Event

NewEvent returns a new event with given name, function, at given counter

func (*Event) String

func (event *Event) String() string

String describes the Event in human readable text.

type Loop

type Loop struct {

	// Counter increments every time through the loop, up to [Counter.Max].
	Counter Counter

	// Events occur when Counter.Cur is at their AtCounter.
	Events []*Event

	// OnStart functions are called at the beginning of each loop iteration.
	OnStart NamedFuncs

	// OnEnd functions are called at the end of each loop iteration.
	OnEnd NamedFuncs

	// IsDone functions are called after each loop iteration,
	// and if any return true, then the loop iteration is terminated.
	IsDone NamedFuncs

	// StepCount is the default step count for this loop level.
	StepCount int
}

Loop contains one level of a multi-level iteration stack, with functions that can be called at the start and end of each iteration of the loop, and a Counter that increments for each iteration, terminating if >= Max, or IsDone returns true. Within each iteration, any sub-loop at the next level down in its Stack runs its full set of iterations. The control flow is:

for {
	Events[Counter == AtCounter] // run events at counter
	OnStart()
	    Run Sub-Loop to completion
	OnEnd()
	Counter += Inc
	if Counter >= Max || IsDone() {
	    break
	}
}

func NewLoop

func NewLoop(ctrMax, ctrIncr int) *Loop

NewLoop returns a new loop with given Counter Max and increment.

func (*Loop) AddEvent

func (lp *Loop) AddEvent(name string, atCtr int, fun func()) *Event

AddEvent adds a new event at given counter. If an event already exists for that counter, the function is added to the list for that event.

func (*Loop) DocString

func (lp *Loop) DocString(st *Stack, level int) string

DocString returns an indented summary of this loop and those below it.

func (*Loop) EventByCounter

func (lp *Loop) EventByCounter(atCtr int) *Event

EventByCounter returns event for given atCounter value, nil if not found.

func (*Loop) EventByName

func (lp *Loop) EventByName(name string) *Event

EventByName returns event by name, nil if not found.

func (*Loop) SkipToMax

func (lp *Loop) SkipToMax()

SkipToMax sets the counter to its Max value for this level. for skipping over rest of loop.

type NamedFunc

type NamedFunc struct {
	Name string
	Func func() bool
}

NamedFunc is a function closure with a name. Function returns a bool which is needed for stopping condition but is otherwise not used.

type NamedFuncs

type NamedFuncs []NamedFunc

NamedFuncs is an ordered list of named functions.

func (*NamedFuncs) Add

func (funcs *NamedFuncs) Add(name string, fun func()) *NamedFuncs

Add adds a named function (with no bool return value).

func (*NamedFuncs) AddBool

func (funcs *NamedFuncs) AddBool(name string, fun func() bool) *NamedFuncs

AddBool adds a named function with a bool return value, for IsDone case.

func (*NamedFuncs) Delete

func (funcs *NamedFuncs) Delete(name string) error

Delete deletes function of given name.

func (*NamedFuncs) FuncIndex

func (funcs *NamedFuncs) FuncIndex(name string) (int, error)

FuncIndex finds index of function by name. Returns not found err message if not found.

func (*NamedFuncs) InsertAfter

func (funcs *NamedFuncs) InsertAfter(after, name string, fun func() bool) error

InsertAfter inserts function after other function of given name.

func (*NamedFuncs) InsertAt

func (funcs *NamedFuncs) InsertAt(i int, name string, fun func() bool)

InsertAt inserts function at given index.

func (*NamedFuncs) InsertBefore

func (funcs *NamedFuncs) InsertBefore(before, name string, fun func() bool) error

InsertBefore inserts function before other function of given name.

func (*NamedFuncs) Prepend

func (funcs *NamedFuncs) Prepend(name string, fun func() bool)

Prepend adds a function to the start of the list.

func (*NamedFuncs) Replace

func (funcs *NamedFuncs) Replace(name string, fun func() bool) error

Replace replaces function with other function of given name.

func (NamedFuncs) Run

func (funcs NamedFuncs) Run() bool

Run runs all of the functions, returning true if any of the functions returned true.

func (*NamedFuncs) String

func (funcs *NamedFuncs) String() string

String prints the list of named functions.

type Scope

type Scope int

Scope is a combined Mode + Time value. Mode is encoded by multiples of 1000 and Time is added to that.

func ToScope

func ToScope(mode, time enums.Enum) Scope

func (Scope) ModeTime

func (sc Scope) ModeTime() (mode, time int64)

type Stack

type Stack struct {

	// Mode identifies the mode of processing this stack performs, e.g., Train or Test.
	Mode enums.Enum

	// Loops is the set of Loops for this Stack, keyed by the timescale.
	// Order is determined by the Order list.
	Loops map[enums.Enum]*Loop

	// Order is the list and order of time scales looped over by this stack of loops.
	// The ordered is from top to bottom, so longer timescales like Run should be at
	// the beginning and shorter timescales like Trial should be and the end.
	Order []enums.Enum

	// OnInit are functions to run for Init function of this stack,
	// which also resets the counters for this stack.
	OnInit NamedFuncs

	// StopNext will stop running at the end of the current StopLevel if set.
	StopNext bool

	// StopFlag will stop running ASAP if set.
	StopFlag bool

	// StopLevel sets the Time level to stop at the end of.
	// This is the current active Step level, which will be reset when done.
	StopLevel enums.Enum

	// StopCount determines how many iterations at StopLevel before actually stopping.
	// This is the current active Step control value.
	StopCount int

	// StepLevel is a saved copy of StopLevel for stepping.
	// This is what was set for last Step call (which sets StopLevel) or by GUI.
	StepLevel enums.Enum

	// StepCount is a saved copy of StopCount for stepping.
	// This is what was set for last Step call (which sets StopCount) or by GUI.
	StepCount int
}

Stack contains a list of Loops to run, for a given Mode of processing. The order of Loop stacks is determined by the Order list.

func NewStack

func NewStack(mode enums.Enum) *Stack

NewStack returns a new Stack for given mode.

func (*Stack) AddOnEndToAll

func (st *Stack) AddOnEndToAll(name string, fun func(mode, time enums.Enum))

AddOnEndToAll adds given function taking mode and time args to OnEnd in all loops.

func (*Stack) AddOnStartToAll

func (st *Stack) AddOnStartToAll(name string, fun func(mode, time enums.Enum))

AddOnStartToAll adds given function taking mode and time args to OnStart in all loops.

func (*Stack) AddTime

func (st *Stack) AddTime(time enums.Enum, ctrMax int) *Stack

AddTime adds a new timescale to this Stack with a given ctrMax number of iterations. The order in which this method is invoked is important, as it adds loops in order from top to bottom. Sets a default increment of 1 for the counter -- see AddTimeIncr for different increment.

func (*Stack) AddTimeIncr

func (st *Stack) AddTimeIncr(time enums.Enum, ctrMax, ctrIncr int) *Stack

AddTimeIncr adds a new timescale to this Stack with a given ctrMax number of iterations, and increment per step. The order in which this method is invoked is important, as it adds loops in order from top to bottom.

func (*Stack) ClearStep

func (st *Stack) ClearStep()

ClearStep clears the active stepping control state: StopNext and StopFlag.

func (*Stack) CountersToStats

func (st *Stack) CountersToStats(stats *estats.Stats)

CountersToStats sets the current counter values to estats Int values by their time names only (no eval Mode). These values can then be read by elog LogItems to record the counters in logs. Typically, a TrialName string is also expected to be set, to describe the current trial (Step) contents in a useful way, and other relevant info (e.g., group / category info) can also be set.

func (*Stack) DocString

func (st *Stack) DocString() string

DocString returns an indented summary of the loops and functions in the Stack.

func (*Stack) Level

func (st *Stack) Level(i int) *Loop

Level returns the Loop at the given ordinal level in the Order list. Will panic if out of range.

func (*Stack) SetStep

func (st *Stack) SetStep(numSteps int, stopLevel enums.Enum)

SetStep sets stepping to given level and number of iterations. If numSteps == 0 then the default for the given stops

func (*Stack) TimeAbove

func (st *Stack) TimeAbove(time enums.Enum) enums.Enum

TimeAbove returns the time above the given time in the stack returning etime.NoTime if this is the highest level, or given time does not exist in order.

func (*Stack) TimeBelow

func (st *Stack) TimeBelow(time enums.Enum) enums.Enum

TimeBelow returns the time below the given time in the stack returning etime.NoTime if this is the lowest level, or given time does not exist in order.

type Stacks

type Stacks struct {

	// Stacks is the map of stacks by Mode.
	Stacks map[enums.Enum]*Stack

	// Mode has the current evaluation mode.
	Mode enums.Enum
	// contains filtered or unexported fields
}

Stacks holds data relating to multiple stacks of loops, as well as the logic for stepping through it. It also holds helper methods for constructing the data. It's also a control object for stepping through Stacks of Loops. It holds data about how the flow is going.

Example
stacks := NewStacks()
stacks.AddStack(etime.Train).
	AddTime(etime.Epoch, 3).
	AddTime(etime.Trial, 2)

// add function closures:
stacks.Loop(etime.Train, etime.Epoch).OnStart.Add("Epoch Start", func() { fmt.Println("Epoch Start") })
stacks.Loop(etime.Train, etime.Epoch).OnEnd.Add("Epoch End", func() { fmt.Println("Epoch End") })
stacks.Loop(etime.Train, etime.Trial).OnStart.Add("Trial Run", func() { fmt.Println("  Trial Run") })

// add events:
stacks.Loop(etime.Train, etime.Epoch).AddEvent("EpochTwoEvent", 2, func() { fmt.Println("Epoch==2") })
stacks.Loop(etime.Train, etime.Trial).AddEvent("TrialOneEvent", 1, func() { fmt.Println("  Trial==1") })

// fmt.Println(stacks.DocString())

stacks.Run(etime.Train)
Output:

Epoch Start
  Trial Run
  Trial==1
  Trial Run
Epoch End
Epoch Start
  Trial Run
  Trial==1
  Trial Run
Epoch End
Epoch==2
Epoch Start
  Trial Run
  Trial==1
  Trial Run
Epoch End

func NewStacks

func NewStacks() *Stacks

NewStacks returns a new initialized collection of Stacks.

func (*Stacks) AddEventAllModes

func (ls *Stacks) AddEventAllModes(time enums.Enum, name string, atCtr int, fun func())

AddEventAllModes adds a new event for all modes at given timescale.

func (*Stacks) AddOnEndToAll

func (ls *Stacks) AddOnEndToAll(name string, fun func(mode, time enums.Enum))

AddOnEndToAll adds given function taking mode and time args to OnEnd in all stacks, loops

func (*Stacks) AddOnStartToAll

func (ls *Stacks) AddOnStartToAll(name string, fun func(mode, time enums.Enum))

AddOnStartToAll adds given function taking mode and time args to OnStart in all stacks, loops

func (*Stacks) AddStack

func (ls *Stacks) AddStack(mode enums.Enum) *Stack

AddStack adds a new Stack for given mode

func (*Stacks) ClearStep

func (ls *Stacks) ClearStep(mode enums.Enum)

ClearStep clears stepping variables from given mode, so it will run to completion in a subsequent Cont(). Called by Run

func (*Stacks) Cont

func (ls *Stacks) Cont()

Cont continues running based on current state of the stacks. This is common pathway for Step and Run, which set state and call Cont. Programatic calling of Step can continue with Cont.

func (*Stacks) DocString

func (ls *Stacks) DocString() string

DocString returns an indented summary of the loops and functions in the stack.

func (*Stacks) Init

func (ls *Stacks) Init()

Init initializes all stacks. See Stacks.InitMode for more info.

func (*Stacks) InitMode

func (ls *Stacks) InitMode(mode enums.Enum)

InitMode initializes Stack of given mode, resetting counters and calling the OnInit functions.

func (*Stacks) IsRunning

func (ls *Stacks) IsRunning() bool

IsRunning is True if running.

func (*Stacks) Loop

func (ls *Stacks) Loop(mode, time enums.Enum) *Loop

Loop returns the Loop associated with given mode and timescale.

func (*Stacks) ModeStack

func (ls *Stacks) ModeStack() *Stack

ModeStack returns the Stack for the current Mode

func (*Stacks) ResetAndRun

func (ls *Stacks) ResetAndRun(mode enums.Enum)

ResetAndRun calls ResetCountersByMode on this mode and then Run. This ensures that the Stack is run from the start, regardless of what state it might have been in.

func (*Stacks) ResetCounters

func (ls *Stacks) ResetCounters()

ResetCounters resets the Cur on all loop Counters, and resets the Stacks's place in the loops.

func (*Stacks) ResetCountersBelow

func (ls *Stacks) ResetCountersBelow(mode enums.Enum, time enums.Enum)

ResetCountersBelow resets the Cur on all loop Counters below given level (inclusive), and resets the Stacks's place in the loops.

func (*Stacks) ResetCountersByMode

func (ls *Stacks) ResetCountersByMode(mode enums.Enum)

ResetCountersByMode resets counters for given mode.

func (*Stacks) Run

func (ls *Stacks) Run(mode enums.Enum)

Run runs the stack of loops for given mode (Train, Test, etc). This resets any stepping settings for this stack and runs until completion or stopped externally.

func (*Stacks) Step

func (ls *Stacks) Step(mode enums.Enum, numSteps int, stopLevel enums.Enum)

Step numSteps at given stopLevel. Use this if you want to do exactly one trial or two epochs or 50 cycles or whatever. If numSteps <= 0 then the default number of steps for given step level is used.

func (*Stacks) Stop

func (ls *Stacks) Stop(level enums.Enum)

Stop stops currently running stack of loops at given run time level

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL