looper

package
v2.0.0-dev0.1.8 Latest Latest
Warning

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

Go to latest
Published: Jan 19, 2026 License: BSD-3-Clause Imports: 9 Imported by: 49

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 levels or time scales of processing, where the processing performed is provided by function closures on the Loop elements. Each Stack is defined by a Mode enum, e.g., Train vs. Test.

Thus, the looper structure is defined by two "coordinate" variables: Mode stack and loop Level, which should be provided by end-user defined enums values (the looper code uses the enums.Enum interface).

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 level associated with the loop runs the full iterations over that level. 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 for current counter value
	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.

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

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, Init methods, etc.

Examples

The following examples use the Modes and Levels enums defined in the levels sub-package, which is intended for testing and example purposes: each use-case should define its own enums for better clarity and flexibility down the road.

type Modes int32 //enums:enum
const (
	Train Modes = iota
	Test
)

type Levels int32 //enums:enum
const (
	Cycle Levels = iota
	Trial
	Epoch
	Run
)

Configuration

From step_test.go ExampleStacks:

	stacks := NewStacks()
	stacks.AddStack(levels.Train, levels.Trial).
		AddLevel(levels.Epoch, 3).
		AddLevel(levels.Trial, 2)

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

	// add events:
	stacks.Loop(levels.Train, levels.Epoch).AddEvent("EpochTwoEvent", 2, func() { fmt.Println("Epoch==2") })
	stacks.Loop(levels.Train, levels.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(level.Train)

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

stacks.ResetAndRun(level.Train)

Step by 1 Trial:

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

Stacks config API

Most configuration can be handled by these helper functions defined on the Stacks type:

// AddEventAllModes adds a new event for all modes at given loop level.
AddEventAllModes(level enums.Enum, name string, atCtr int, fun func())

// AddOnStartToAll adds given function taking mode and level args to OnStart in all stacks, loops
AddOnStartToAll(name string, fun func(mode, level enums.Enum))

// AddOnEndToAll adds given function taking mode and level args to OnEnd in all stacks, loops
AddOnEndToAll(name string, fun func(mode, level enums.Enum))

// AddOnStartToLoop adds given function taking mode arg to OnStart in all stacks for given loop.
AddOnStartToLoop(level enums.Enum, name string, fun func(mode enums.Enum))

// AddOnEndToLoop adds given function taking mode arg to OnEnd in all stacks for given loop.
AddOnEndToLoop(level enums.Enum, name string, fun func(mode enums.Enum))

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// If you want to debug the flow of processing, 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 iteration 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 iteration 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 + Level value. Mode is encoded by multiples of 1000 and Level is added to that.

func ToScope

func ToScope(mode, level enums.Enum) Scope

func (Scope) ModeLevel

func (sc Scope) ModeLevel() (mode, level 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 level enum value.
	// Order is determined by the Order list.
	Loops map[enums.Enum]*Loop

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

	// OnInit are functions to run when Init is called, to restart processing,
	// 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 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, which distinguishes this stack, and is its key in the map of Stacks. The order of Loop stacks is determined by the Order list of loop levels.

func NewStack

func NewStack(mode, stepLevel enums.Enum) *Stack

NewStack returns a new Stack for given mode and default step level.

func (*Stack) AddLevel

func (st *Stack) AddLevel(level enums.Enum, counterMax int) *Stack

AddLevel adds a new level to this Stack with a given counterMax 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 AddLevelIncr for different increment.

func (*Stack) AddLevelIncr

func (st *Stack) AddLevelIncr(level enums.Enum, counterMax, counterIncr int) *Stack

AddLevelIncr adds a new level to this Stack with a given counterMax 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) AddOnEndToAll

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

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

func (*Stack) AddOnStartToAll

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

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

func (*Stack) ClearStep

func (st *Stack) ClearStep()

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

func (*Stack) Counters

func (st *Stack) Counters() []int

Counters returns a slice of the current counter values for this stack, in Order.

func (*Stack) CountersString

func (st *Stack) CountersString() string

CountersString returns a string with loop level and counter values.

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) LevelAbove

func (st *Stack) LevelAbove(level enums.Enum) (enums.Enum, bool)

LevelAbove returns the level above the given level in the stack returning false if this is the highest level, or given level does not exist in order.

func (*Stack) LevelBelow

func (st *Stack) LevelBelow(level enums.Enum) (enums.Enum, bool)

LevelBelow returns the level below the given level in the stack returning false if this is the lowest level, or given level does not exist in order.

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

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(levels.Train, levels.Trial).
	AddLevel(levels.Epoch, 3).
	AddLevel(levels.Trial, 2)

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

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

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

stacks.Run(levels.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(level enums.Enum, name string, atCtr int, fun func())

AddEventAllModes adds a new event for all modes at given loop level.

func (*Stacks) AddOnEndToAll

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

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

func (*Stacks) AddOnEndToLoop

func (ls *Stacks) AddOnEndToLoop(level enums.Enum, name string, fun func(mode enums.Enum))

AddOnEndToLoop adds given function taking mode arg to OnEnd in all stacks for given loop.

func (*Stacks) AddOnStartToAll

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

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

func (*Stacks) AddOnStartToLoop

func (ls *Stacks) AddOnStartToLoop(level enums.Enum, name string, fun func(mode enums.Enum))

AddOnStartToLoop adds given function taking mode arg to OnStart in all stacks for given loop.

func (*Stacks) AddStack

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

AddStack adds a new Stack for given mode and default step level.

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() enums.Enum

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. Returns the level that was running when it stopped.

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, level enums.Enum) *Loop

Loop returns the Loop associated with given mode and loop level.

func (*Stacks) ModeStack

func (ls *Stacks) ModeStack() *Stack

ModeStack returns the Stack for the current Mode

func (*Stacks) Modes

func (ls *Stacks) Modes() []enums.Enum

Modes returns a sorted list of stack modes, for iterating in Mode enum value order.

func (*Stacks) ResetAndRun

func (ls *Stacks) ResetAndRun(mode enums.Enum) 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. Returns the level that was running when it stopped.

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, level 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) 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. Returns the level that was running when it stopped.

func (*Stacks) Step

func (ls *Stacks) Step(mode enums.Enum, numSteps int, stopLevel enums.Enum) 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. Returns the level that was running when it stopped.

func (*Stacks) Stop

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

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

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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