looper

package
v1.2.4 Latest Latest
Warning

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

Go to latest
Published: May 17, 2022 License: BSD-3-Clause Imports: 5 Imported by: 35

README

Looper: A flexible steppable control hierarchy

Looper implements a fully generic looping control system with extensible functionality at each level of the loop, with logic that supports reentrant stepping so each time it is Run it advances at any specified step size, with results that are identical to running.

This steppability constraint requires that no code is run at the start of a loop, which is equivalent to a do..while loop with no initialization condition:

do {
    Main()
} while !Stop()
End()

The Loop object has these three function lists: Main(), Stop(), End() where function closures can be added to perform any relevant functionality.

In Go syntax, including the running of sub-loops under a given level, this would be:

for {
   for { <subloops here> } // drills down levels for each subloop
   Main()                  // Main is called after subloops -- increment counters!
   if Stop() {
       break
   }
}
End()                      // Reset counters here so next pass starts over

To make this work, an initialization function must be run prior to starting, which puts the system in a ready-to-run state. The End() function at each level must likewise ensure that it is ready to start again properly the next time through.

The envlp Env is designed to work in concert with the looper control, where the Env holds counter values, and looper automatically increments and uses these counters to stop looping at a given level. Each Stack of loops is associated with a given etime.Mode, corresponding to that of the Env.

Algorithm and Sim Integration

Specific algorithms use AddLevels to add inner, lower levels of loops to implement specific algorithm code (typically Phase and Cycle). Leabra and Axon use the Time struct as a context for holding the relevant counters and mode, which is then accessed directly in the callback functions as needed.

In cases where something must be done prior to looping through cycles (e.g., ApplyInputs and new phase startup methods), trigger it on the first cycle, before calling other functions, using a provided AddCycle0 function.

Concrete Example of Looping Logic

The stack_test.go output shows the logic of the looping functions:

Here's the trace of a Run with 2 Run iterations, 3 Epoch iterations, and 3 Trials per epoch:

		Trial Main: 1
		Trial Main: 2
		Trial Main: 3
		Trial Stop: 3
		Trial End: 0
	Epoch Main: 1
		Trial Main: 1
		Trial Main: 2
		Trial Main: 3
		Trial Stop: 3
		Trial End: 0
	Epoch Main: 2
		Trial Main: 1
		Trial Main: 2
		Trial Main: 3
		Trial Stop: 3
		Trial End: 0
	Epoch Main: 3
	Epoch Stop: 3
	Epoch End: 0
Run Main: 1
		Trial Main: 1
   .... (repeat of above)
	Epoch Main: 3
	Epoch Stop: 3
	Epoch End: 0
Run Main: 2
Run Stop: 2
Run End: 0

Here is stepping 1 Trial at a time:

##############
Step Trial 1
		Trial Main: 1

##############
Step Trial 1
		Trial Main: 2

##############
Step Trial 1
		Trial Main: 3
		Trial Stop: 3
		Trial End: 0
	Epoch Main: 1

##############
Step Trial 1
		Trial Main: 1

##############
Step Trial 2
		Trial Main: 1
		Trial Main: 2

Here is stepping 2 Trials at a time:

##############
Step Trial 2
		Trial Main: 3
		Trial Stop: 3
		Trial End: 0
	Epoch Main: 1
		Trial Main: 1

##############
Step Trial 2
		Trial Main: 2
		Trial Main: 3
		Trial Stop: 3
		Trial End: 0
	Epoch Main: 2

##############
Step Trial 2
		Trial Main: 1
		Trial Main: 2

And here's stepping 1 Epoch at a time:

##############
Step Epoch 1
		Trial Main: 1
		Trial Main: 2
		Trial Main: 3
		Trial Stop: 3
		Trial End: 0
	Epoch Main: 1

##############
Step Epoch 1
		Trial Main: 1
		Trial Main: 2
		Trial Main: 3
		Trial Stop: 3
		Trial End: 0
	Epoch Main: 2

##############
Step Epoch 1
		Trial Main: 1
		Trial Main: 2
		Trial Main: 3
		Trial Stop: 3
		Trial End: 0
	Epoch Main: 3
	Epoch Stop: 3
	Epoch End: 0
Run Main: 1

##############
Step Epoch 1
		Trial Main: 1
		Trial Main: 2
		Trial Main: 3
		Trial Stop: 3
		Trial End: 0
	Epoch Main: 1

Documentation

Index

Constants

This section is empty.

Variables

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

	// If PrintControlFlow = true, this cuts off printing at timescales that are faster than this -- default is to print all.
	NoPrintBelow = etime.AllTimes
)

Functions

This section is empty.

Types

type Ctr added in v1.2.3

type Ctr struct {
	Cur int `desc:"current counter value"`
	Max int `desc:"maximum counter value -- only used if > 0"`
}

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

func (*Ctr) Incr added in v1.2.3

func (ct *Ctr) Incr()

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

func (*Ctr) IsOverMax added in v1.2.3

func (ct *Ctr) IsOverMax() bool

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

func (*Ctr) Set added in v1.2.3

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

Set sets the Cur value if different from Cur, while preserving previous value. Returns true if changed

type Event added in v1.2.3

type Event struct {
	Name    string     `desc:"Might be 'plus' or 'minus' for example."`
	AtCtr   int        `desc:"The time that this Event occurs."`
	OnEvent NamedFuncs `desc:"Callback function for the Event."`
}

A Event represents a length of time within a loop, if behavior is expected to change in distinct phases.

func (Event) String added in v1.2.3

func (event Event) String() string

String describes the Event in human readable text.

type Loop

type Loop struct {
	Counter Ctr `` /* 167-byte string literal not displayed */

	OnStart NamedFuncs `desc:"OnStart is called at the beginning of each loop."`
	// Either Main or the inner loop occurs between OnStart and OnEnd
	Main   NamedFuncs     `` /* 158-byte string literal not displayed */
	OnEnd  NamedFuncs     `desc:"OnStart is called at the end of each loop."`
	IsDone NamedFuncsBool `desc:"If true, end loop. Maintained as an unordered map because they should not have side effects."`

	Events []Event `desc:"Events occur when Ctr.Cur gets to their OccurTime."`
}

Loop contains one level of a multi-level iteration scheme. It wraps around an inner loop recorded in a Stack, or around Main functions. It records how many times the loop should be repeated in the Counter. It records what happens at the beginning and end of each loop. For example, a loop with 1 start, 1 end, and a Counter with max=3 will do: Start, Inner, End, Start, Inner, End, Start, Inner, End Where the Inner loop is specified by a Stack or by Main, and Start and End are functions on the loop. See Stack for more details on how loops are combined.

func (*Loop) AddEvents added in v1.2.3

func (loops *Loop) AddEvents(events ...Event)

AddEvents to the list.

type Manager added in v1.2.3

type Manager struct {
	Stacks map[etime.Modes]*Stack

	StopFlag       bool        `desc:"If true, stop model ASAP."`
	StopNext       bool        `desc:"If true, stop model at the end of the current StopLevel."`
	StopLevel      etime.Times `desc:"Time level to stop at the end of."`
	StepIterations int         `desc:"How many steps to do."`
	Mode           etime.Modes `desc:"The current evaluation mode."`
	// contains filtered or unexported fields
}

Manager 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.

func (*Manager) AddEventAllModes added in v1.2.3

func (loopman *Manager) AddEventAllModes(t etime.Times, event Event)

AddEventAllModes adds a Event to the stack for all modes.

func (Manager) AddStack added in v1.2.3

func (loopman Manager) AddStack(mode etime.Modes) *Stack

AddStack adds a new Stack for given mode

func (*Manager) ApplyAcrossAllModesAndTimes added in v1.2.3

func (loopman *Manager) ApplyAcrossAllModesAndTimes(fun func(etime.Modes, etime.Times))

ApplyAcrossAllModesAndTimes applies a function across all evaluation modes and timescales within the Manager. The function might call GetLoop(curMode, curTime) and modify it.

func (Manager) DocString added in v1.2.3

func (loopman Manager) DocString() string

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

func (*Manager) GetLoop added in v1.2.3

func (loopman *Manager) GetLoop(modes etime.Modes, times etime.Times) *Loop

GetLoop returns the Loop associated with an evaluation mode and timescale.

func (Manager) Init added in v1.2.3

func (loopman Manager) Init() *Manager

Init initializes variables on the Manager.

func (Manager) IsRunning added in v1.2.3

func (stepper Manager) IsRunning() bool

IsRunning is True if running.

func (*Manager) ResetCounters added in v1.2.3

func (stepper *Manager) ResetCounters()

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

func (*Manager) ResetCountersByMode added in v1.2.3

func (stepper *Manager) ResetCountersByMode(modes etime.Modes)

ResetCountersByMode is like ResetCounters, but only for one mode.

func (*Manager) Run added in v1.2.3

func (stepper *Manager) Run()

Run runs the loops contained within the stepper. If you want it to stop before the full end of the loop, set variables on the Stepper.

func (*Manager) Step added in v1.2.3

func (stepper *Manager) Step(numSteps int, stopscale etime.Times)

Step numSteps stopscales. Use this if you want to do exactly one trial or two epochs or 50 cycles or something.

type NamedFunc

type NamedFunc struct {
	Name string
	Func func()
}

NamedFunc lets you keep an ordered map of functions.

type NamedFuncs added in v1.2.3

type NamedFuncs []NamedFunc

NamedFunc is an ordered map of functions.

func (*NamedFuncs) Add added in v1.2.3

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

Add adds a named function to a list.

func (NamedFuncs) HasNameLike added in v1.2.3

func (funcs NamedFuncs) HasNameLike(nameSubstring string) bool

HasNameLike is a helper function to check if there's an existing function that contains a substring. This could be helpful to ensure that you don't add duplicate logic to a list of functions. If you plan on using this, add a comment documenting which name is important, because the default assumption is that names are just documentation.

func (NamedFuncs) String added in v1.2.3

func (funcs NamedFuncs) String() string

String describes named functions.

type NamedFuncsBool added in v1.2.3

type NamedFuncsBool map[string]func() bool

NamedFuncsBool is like NamedFuncs, but for functions that return a bool.

func (*NamedFuncsBool) Add added in v1.2.3

func (funcs *NamedFuncsBool) Add(name string, f func() bool)

Add adds a function by name.

type Stack

type Stack struct {
	Loops map[etime.Times]*Loop `desc:"An ordered map of Loops, from the outer loop at the start to the inner loop at the end."`
	Order []etime.Times         `` /* 183-byte string literal not displayed */
}

Stack contains a list of Loops Ordered from top to bottom. For example, a Stack might be created like this:

myStack.Init().AddTime(etime.Run, 2).AddTime(etime.Trial, 3)
myStack.Loops[etime.Run].OnStart.Add("NewRun", initRunFunc)
myStack.Loops[etime.Trial].OnStart.Add("PresentTrial", trialFunc)

When run, myStack will behave like this: initRunFunc, trialFunc, trialFunc, trialFunc, initRunFunc, trialFunc, trialFunc, trialFunc

func (*Stack) AddTime added in v1.2.3

func (loops *Stack) AddTime(time etime.Times, max int) *Stack

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

func (*Stack) Init

func (loops *Stack) Init() *Stack

Init makes sure data structures are initialized, and empties them if they are.

Jump to

Keyboard shortcuts

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