Function
Defines the functions used along with ceed.shape to create time-varying
intensities for the shapes during an experiment. CeedStage
combines functions with shapes and displays them during an experiment according
to the stage’s function.
Although the functions’ range is (-infinity, +infinity) and this module
places no restriction on the function output so that it may return any
scalar value, the graphics system can only accept values in the [0, 1]
range for each red, green, or blue channel. Consequently, the graphics system
(at ceed.stage.StageFactoryBase.set_shape_gl_color_values()) will clip
the function output to that range.
Function factory and plugins
The FunctionFactoryBase is a store of the defined FuncBase
sub-classes and customized function instances. Function classes/instances
registered with the FunctionFactoryBase instance used by the
the GUI are available to the user in the GUI. During analysis, functions are
similarly registered with the FunctionFactoryBase instance used in the
analysis and can then be used to get these functions. E.g.:
>>> # create the function store
>>> function_factory = FunctionFactoryBase()
>>> register_all_functions(function_factory)  # register plugins
>>> LinearFunc = function_factory.get('LinearFunc')  # get the class
>>> LinearFunc
ceed.function.plugin.LinearFunc
Classes can be registered manually with
FunctionFactoryBase.register() or they can be registered automatically
with register_all_functions() if they are an internal plugin or
register_external_functions() for an external plugin. The GUI calls
register_all_functions() when started as well as
register_external_functions() if the
external_function_plugin_package configuration
variable contains a package name.
See ceed.function.plugin for details on writing plugins. For details on
how to customize functions, see below for important steps and methods during a
function’s lifecycle.
To get a function class registered with FunctionFactoryBase, e.g.
ceed.function.plugin.CosFunc:
>>> CosFunc = function_factory.get('CosFunc')  # using class name
>>> CosFunc
ceed.function.plugin.CosFunc
Default function instances
When a function class (e.g. a linear function or a cosine function) is
registered, we create a default instance of the class,
and that instance is accessible at FunctionFactoryBase.funcs_inst
as well as from the GUI. The GUI makes available function names listed in
FunctionFactoryBase.funcs_inst:
>>> function_factory.funcs_inst
{'Group': <ceed.function.FuncGroup at 0x2a351743c88>,
 'Const': <ceed.function.plugin.ConstFunc at 0x2a351743e48>,
 'Linear': <ceed.function.plugin.LinearFunc at 0x2a351743c18>,
 'Exp': <ceed.function.plugin.ExponentialFunc at 0x2a351743f98>,
 'Cos': <ceed.function.plugin.CosFunc at 0x2a351743eb8>}
We can also add customized function instances to be available to the user
with FunctionFactoryBase.add_func().
They have unique names, by which we access them from the GUI or from
FunctionFactoryBase.funcs_inst. E.g.:
>>> f = LinearFunc(function_factory=function_factory, duration=2, m=2, name='line')
>>> function_factory.add_func(f)
>>> function_factory.funcs_inst
{...
 'Cos': <ceed.function.plugin.CosFunc at 0x1da866f0ac8>,
 'line': <ceed.function.plugin.LinearFunc at 0x1da866f0278>}
The name will be automatically changed if a function with the given name already exists when it is registered. E.g.
>>> f = LinearFunc(function_factory=function_factory, name='line')
>>> function_factory.add_func(f)
>>> f2 = LinearFunc(function_factory=function_factory, name='line')
>>> function_factory.add_func(f2)
>>> f.name
'line'
>>> f2.name
'line-2'
To use a registered function instance, it needs to be copied before it can be used. e.g.:
>>> from copy import deepcopy
>>> f = deepcopy(function_factory.funcs_inst['line'])
>>> f.init_func_tree()
>>> ...
Function basics
All functions inherit from FuncBase. FuncBase defines the
(stateful) interface that returns a number when called with a time argument.
Ceed functions are not like typical functions that can be called with any value, rather, they keep some internal state, which determines what its current domain is. Specifically, functions may only be called with monotonically increasing time values, like we do in an experiment where time always monotonically increases in multiples of the frame rate period. Here’s some example basic function usage:
>>> # function factory stores all the available function types (classes)
>>> function_factory = FunctionFactoryBase()
>>> # register all plugin functions
>>> register_all_functions(function_factory)
>>> # get cosine function class from internal plugin
>>> CosFunc = function_factory.get('CosFunc')
>>> # cos will have amplitude of 10, frequency of 1Hz and 10s duration
>>> f = CosFunc(function_factory=function_factory, duration=10, A=10, f=1)
>>> f
<ceed.function.plugin.CosFunc at 0x4eadba8>
>>> # we must initialize the function tree before use
>>> f.init_func_tree()
>>> # then we initialize specifically this function
>>> # initialize function base time at 3. I.e. 3 will be subtracted from
>>> # future times to convert from global to function-local time
>>> f.init_func(3)
>>> # evaluate the function at time 3 - 3 = 0 seconds
>>> f(3)
10.0
>>> f(3.25)  # now at 3.25 - 3 = 0.25 seconds
6.123233995736766e-16
>>> f(3.5)
-10.0
>>> f(15)  # calling outside the domain raises an exception
Traceback (most recent call last):
File "g:\python\libs\ceed\ceed\function\__init__.py", line 1042, in __call__
    raise FuncDoneException
ceed.function.FuncDoneException
Initialization
Before a function can be used, it must be initialized with
FuncBase.init_func_tree() and FuncBase.init_func().
FuncBase.init_func_tree() intializes all functions and sub-functions in
the function tree recursively. FuncBase.init_func() on the other hand
intitializes each function in the tree just before it is going to be used.
It takes a time value in seconds (in global time) where the domain
of the function starts. This is how we can evaluate the function independant of
the baseline global time. E.g. if a function computes f(t) = m * t + b,
it’ll actually always be internally computed as
f(t) = m * (t - t_start) + b, where FuncBase.t_start is the value
passed to FuncBase.init_func() (and FuncBase.init_loop_iteration()
at each loop iteration, but FuncBase.init_loop_iteration() is called
internally, not by external code).
To evaluate the function, just call it with a time value as in the example above.
Domain and monotonicity
Functions have a domain, as determined by FuncBase.get_domain().
By default the domain for a just initialized function is
[FuncBase.t_start, FuncBase.t_start +
FuncBase.duration * FuncBase.loop *
FuncBase.get_timebase()).
FuncBase.duration can -1, indicating the domain extends to
+infinity. FuncBase.get_timebase() allows one to state the
FuncBase.duration in FuncBase.timebase units, for better
accuracy, rather than in seconds. If a function
loops (FuncBase.loop), the domain extends until the end of the loops,
but the domain obviously shrinks with each loop iteration.
The domain always starts at FuncBase.t_start, but
FuncBase.t_start is updated internally for each loop to the time at the
start of the current loop (or more accurately the time the last loop ended,
FuncBase.t_end if it’s before the current time). So the domain gets
smaller as we iterate the loops. E.g.:
>>> LinearFunc = function_factory.get('LinearFunc')
>>> # slope of 2, with 2 loop iterations
>>> f = LinearFunc(function_factory=function_factory, duration=10, loop=2, m=2)
>>> f.loop
2
>>> f.init_func_tree()
>>> f.init_func(2)  # the valid domain starts at 2 seconds
>>> f(0)  # zero seconds is outside the domain
Traceback (most recent call last):
    File "g:\python\libs\ceed\ceed\function\__init__.py", line 1032, in __call__
ValueError: Cannot call function <ceed.function.plugin.LinearFunc object at 0x000001B66B022DD8> with time less than the function start 2
>>> f(2)  # intercept at 2 - 2 = 0 seconds is zero
0.0
>>> f.t_start
2
>>> f.loop_count  # it's in the first loop iteration
0
>>> f.get_domain(current_iteration=False)
(2, Fraction(22, 1))
>>> f(10)  # value of time at 10 - 2 = 8 seconds
16.0
>>> # now we called it with a time value at the end of the first loop
>>> # i.e. 12 seconds is 2 + 10, which is the duration of the function,
>>> # so it automatically increments the loop and updates t_start
>>> f(12)
0.0
>>> f.t_start
Fraction(12, 1)
>>> f.loop_count
1
>>> f.get_domain(current_iteration=False)  # the valid domain has updated
(Fraction(12, 1), Fraction(22, 1))
>>> f(20)
16.0
>>> f(22)  # finally this is the end of the second loop end of domain
Traceback (most recent call last):
    File "g:\python\libs\ceed\ceed\function\__init__.py", line 1042, in __call__
ceed.function.FuncDoneException
>>> f.loop_count
2
>>> # there's no more valid domain left
>>> f.get_domain()
(-1, -1)
>>> f(20)  # function may only be called monotonically, it won't always
>>> # raise an error if called non-monotonically, but generally it will
Traceback (most recent call last):
    File "g:\python\libs\ceed\ceed\function\__init__.py", line 1034, in __call__
ceed.function.FuncDoneException
As seen above, the domain of a function changes as it’s called with time values. Consequently, functions may only be called with monotonically increasing time arguments. If violated, it may raise an error, but it doesn’t always.
This rule is required to support functions that perform some IO and therefore may change some state, so calling with the same time input multiple times may not make sense. Similarly, we combine functions in a group (e.g. function A, then B etc), so once we finish function A of the group and moved to B, we don’t support moving to function A again, unless we are in the next loop. E.g.:
>>> ConstFunc = function_factory.get('ConstFunc')
>>> FuncGroup = function_factory.get('FuncGroup')
>>> # two constant functions that output a, and 10 respectively
>>> f1 = ConstFunc(function_factory=function_factory, duration=2, a=2)
>>> f2 = ConstFunc(function_factory=function_factory, duration=2, a=10)
>>> # create a function group of two sequential constant functions
>>> f = FuncGroup(function_factory=function_factory, loop=2)
>>> f.add_func(f1)
>>> f.add_func(f2)
>>> f.init_func_tree()
>>> f.init_func(1)
>>> f.get_domain(current_iteration=False)
(1, Fraction(9, 1))
>>> f(1)  # this is internally calling f1
2
>>> f(3)  # now we're calling f2 internally since we past the 2s duration
10
>>> # even though the domain is still the same, we cannot call it now with
>>> # a value less than 3 (because it'd have to jump back to f1 and
>>> # we don't need or support that)
>>> f.get_domain(current_iteration=False)
(1, Fraction(9, 1))
>>> f.loop_count
0
>>> f(5)
2
>>> f.loop_count
1
>>> f(7)
10
>>> f(9)
Traceback (most recent call last):
    File "g:\python\libs\ceed\ceed\function\__init__.py", line 1341, in __call__
ceed.function.FuncDoneException
Timebase
As alluded to above, functions have a optional timebase to help be more
precise with the function duration. Normally, the FuncBase.duration
is set in seconds to the duration the function should take. But if the
projector is going at say 120 frames per second (fps), we may want to e.g.
set the shape to be intensity 1 for frame 0 (t = 0 / 120) and intensity
0.5 for frame 1 (t = 1 / 120). Expressing
duration = 1 / 120 = 0.008333333333333333 is not possible with decimal
math. Consequently, we allow setting the FuncBase.timebase.
FuncBase.timebase determines the units of the
FuncBase.duration. If it’s zero, then the units is in seconds, like
normal. If it’s non-zero, e.g. the fraction Fraction(1, 120), then
duration is multiplied by it to get the duration in seconds. So e.g. with
Fraction(1, 120), if duration is 12, then the stage duration is
12/120 seconds, or 12 frames.
During an experimental stage when functions are called, we pass time to the
functions represented as fractions rather than decimel, where the denominator
represents the true framerate of the projector, the numerator is the number of
frames elpased, so the time value is the elapsed time since the start. So
e.g. we’d call it with f(Fraction(180, 120)) if we are 1.5 seconds into
the stage and the projector frame rate is 120. This allows us to do more
precise duration math. E.g.:
>>> from fractions import Fraction
>>> # duration will be 2 frames at 120 fps (2/120 seconds)
>>> f = LinearFunc(function_factory=function_factory, duration=2, timebase_numerator=1, timebase_denominator=120, m=2)
>>> f.init_func_tree()
>>> f.init_func(1)  # start at 1 sec
>>> f.get_domain(current_iteration=False)
(1, Fraction(61, 60))
>>> f.get_timebase()  # this is what we need to use to get the timebase
Fraction(1, 120)
>>> f.timebase
Fraction(1, 120)
>>> f(Fraction(120, 120))  # calling it t=frame 0 at 120fps
0.0
>>> f(Fraction(121, 120))  # calling it t=frame 1 at 120fps
0.016666666666666666
>>> f(Fraction(122, 120))  # calling it t=frame 2 at 120fps
Traceback (most recent call last):
    File "g:\python\libs\ceed\ceed\function\__init__.py", line 1150, in __call__
ceed.function.FuncDoneException
Inheriting timebase
Functions can be grouped, e.g. in the example above. We don’t want to have to
specify the FuncBase.timebase for each function. Consequently, if the
FuncBase.timebase is unspecified (i.e. zero), a
function will inherit the timebase from the FuncBase.parent_func they
belong to all the way to the root where FuncBase.parent_func is None.
E.g. we want the function to alternate between 2 and 10 for each frame at 120fps:
>>> # each sub-function is exactly one frame long
>>> f1 = ConstFunc(function_factory=function_factory, duration=1, a=2)
>>> f2 = ConstFunc(function_factory=function_factory, duration=1, a=10)
>>> # so far the timbease are the defaults
>>> f1.timebase
Fraction(0, 1)
>>> f1.get_timebase()  # so it's in seconds
1
>>> # because we specify a timebase for the group, all the sub-functions
>>> # will share the same timebase. Unless a sub function specifically sets
>>> # its own timebase
>>> f = FuncGroup(function_factory=function_factory, timebase_numerator=1, timebase_denominator=120, loop=2)
>>> f.add_func(f1)
>>> f.add_func(f2)
>>> # now, we inherit the timebase from out parent because we were added
>>> # to a parent and we didn't set our own timebase
>>> f1.timebase
Fraction(0, 1)
>>> f1.get_timebase()  # our parents timebase
Fraction(1, 120)
>>> f.init_func(1)  # i.e. 120 / 120
>>> f.get_domain(current_iteration=False)
(1, Fraction(31, 30))
>>> f(Fraction(120, 120))  # loop 0, f1
2
>>> f(Fraction(121, 120))  # loop 0, f2
10
>>> f(Fraction(122, 120))  # loop 1, f1
2
>>> f(Fraction(123, 120))  # loop 1, f2
10
>>> f(Fraction(124, 120))  # done
Traceback (most recent call last):
    File "g:\python\libs\ceed\ceed\function\__init__.py", line 1459, in __call__
ceed.function.FuncDoneException
As we just saw, unless overwritten by a function, the timebase is inherited
from parent functions if they set it. Therefore, to get the actual timebase use
FuncBase.get_timebase(), instead of FuncBase.timebase. The latter
is only used to directly set the function’s timebase. Functions that need to
use the timebase should design all the duration values with that in mind.
The downside to setting a timebase is that it’s specific to that timebase, so to have a function in different timebases, it would need to be replicated for each timebase.
GUI customization
Function properties are customizable in the GUI by the user according to the
values returned by FuncBase.get_gui_props(),
FuncBase.get_prop_pretty_name(), and FuncBase.get_gui_elements().
These methods control what properties are editable by the user and the values they may potentially take.
Randomizing parameters
A FuncBase subclass typically contains parameters. E.g.
ceed.function.plugin.LinearFunc has a offset and slope (
ceed.function.plugin.LinearFunc.b and
ceed.function.plugin.LinearFunc.m). Sometimes it is desireable for the
parameters to be randomly re-sampled before each experiment, or even for each
loop iteration (FuncBase.loop_tree_count).
All parameters that support randomization must be returned by
FuncBase.get_noise_supported_parameters() and the specific distribution
used to randomize each parameter (based on param_noise)
is stored in FuncBase.noisy_parameters. The GUI manages
FuncBase.noisy_parameters from user configuration based on
FuncBase.get_noise_supported_parameters().
Then, when the function is prepared by Ceed it calls
FuncBase.resample_parameters() that re-samples all the randomized
parameters. Parameters that are randomized once for the function lifetime
(sample_each_loop is False)
are sampled once and the parameter is set to that value. Parameters that are
randomized once for each loop iteration (see the docs for
sample_each_loop) are sampled for
as many iterations they’ll experience and the samples are stored in
FuncBase.noisy_parameter_samples. Then, the parameter is set to the
corresponding value for each iteration at the start in
FuncBase.init_func() and FuncBase.init_loop_iteration().
As explained in Reference functions, Ceed supports function re-use by
creating a function and using references to it elsewhere. Parameters changed
in the original function will also be reflected in the references.
You have two options for how randomized parameters are handled in reference
functions, controlled by
lock_after_forked. If it’s True,
all the reference function’s random values will be the same as the original’s
randomly generated values. If False, they will all have uniquely sampled
values.
Possible noise distributions are listed in the
ceed.function.param_noise.ParameterNoiseFactory stored in
FunctionFactoryBase.param_noise_factory. See
ceed.function.plugin for how to add distributions.
lifecycle
The ultimate use of a function is to sample it for all timepoints that the function is active. Before the function is ready, however, it needs to be initialized, as well as for each loop iteration. Folowing are the overall steps performed by stages before using it in an experiment.
Starting with a stage, first the stage and all its functions are copied and
resampled using copy_and_resample(). This calls
FuncBase.resample_parameters() as described there.
Next, we call FuncBase.init_func_tree() on each root function in the
stage. This calls FuncBase.init_func_tree() recursively on
all the functions’s children (for group functions), if any.
Next, when the stage reaches this function as it ticks though all its
functions, it calls FuncBase.init_func() to initialize this function run.
This will be called each time the stage or the function’s parent loops around
and restarts the function. It passes the current global time value to be used
by the function to convert subsequent timestemps from global to local
function values (relative to the start).
Then as the stage gets sequential timestamps to sample, it calls
CeedFunc.__call__() with the time value uses the return value for its
shapes intensity. CeedFunc.__call__() will call
FuncBase.init_loop_iteration() as the timestemps reach the end of its
domain and it loops around (if any). When done with the loops, it’ll
raise a FuncDoneException to indicate the function is done.
At the end of each loop iteration and at the end of the overall function it
calls FuncBase.finalize_loop_iteration(), and
FuncBase.finalize_func(), respectively.
Customizing functions
A FuncBase has multiple methods that can be overwritten by a plugin.
Following are some relevant methods - see their docs for more details.
If the function generates the samples directly without using the public interfaces classes,
FuncBase.resample_parameters()needs to be augmented if the function has any randomness.FuncBase.init_func_tree(),FuncBase.init_func(),FuncBase.init_loop_iteration(),FuncBase.finalize_loop_iteration(), andFuncBase.finalize_func()may be augmented if any of these function lifecycle events requires additional initialization/finalization.CeedFunc.__call__()is the method to inherit from and augment to return custom values from your function. See the built-in plugins, such asceed.function.plugin.LinearFuncfor examples, or below.See the function’s properties for additional customizations.
In all cases, FuncBase.get_state() may also need to
be augmented to return any parameters that are part of the instance, otherwise
they won’t be copied when the function is copied and the function will use
incorrect values during an actual experiment when all functions are copied.
If functions are pre-computed, it may not have any side-effects, because they would occur before the experiment during pre-computing. So if these side-effects are needed (e.g. drug delivery), either turn off pre-computing for its stage or set the function’s duration to negative to disable it for the function.
Saving and restoring functions
Functions can be saved as a data dictionary and later restored into a function instance. E.g.:
>>> f = LinearFunc(function_factory=function_factory, duration=2, m=2, name='line')
>>> state = f.get_state()
>>> state
{'name': 'line',
 'cls': 'LinearFunc',
 'loop': 1,
 'timebase_numerator': 0,
 'timebase_denominator': 1,
 'noisy_parameters': {},
 'duration': 2,
 't_offset': 0,
 'm': 2,
 'b': 0.0}
>>> # this is how we create a function from state
>>> f2 = function_factory.make_func(state)
>>> f2
<ceed.function.plugin.LinearFunc at 0x1a2cf679ac8>
>>> f2.get_state()
{'name': 'Linear',
 'cls': 'LinearFunc',
 'loop': 1,
 'timebase_numerator': 0,
 'timebase_denominator': 1,
 'noisy_parameters': {},
 'duration': 2,
 't_offset': 0,
 'm': 2,
 'b': 0.0}
If you notice, name was not restored to the new function. That’s because
name is only restored if we pass clone=True as name is considered an
internal property and not always user-customizable. Because we ensure
each function’s name in the GUI is unique. E.g. with clone:
>>> f3 = function_factory.make_func(state, clone=True)
>>> f3.get_state()
{'name': 'line',
 'cls': 'LinearFunc',
 'loop': 1,
 ...
 'b': 0.0}
A fundumental part of Ceed is copying and reconstructing function objects.
E.g. this is required to recover functions from a template file, from old data,
or even to be able to run the experiment because it is run from a second
process. Consequently, anything required for the function to be reconstructed
must be returned by FuncBase.get_state().
Reference functions
Sometimes you may want to create a function group containing other functions. Instead of explicitly defining these sub-functions, we may want to refer to existing registered functions and let these sub-functions update when the existing function’s parameters are updated. They are mostly meant to be used from the GUI, although work fine otherwise.
CeedFuncRef allows one to reference functions in such a manner.
Instead of copying a function, just get a reference to it with
FunctionFactoryBase.get_func_ref() and add it to the function group.
When destroyed, such function references must be explicitly released with
FunctionFactoryBase.return_func_ref(), otherwise the original function
cannot be deleted in the GUI.
Methods that accept functions (such as FuncGroup.add_func()) should also
typically accept CeedFuncRef functions.
CeedFuncRef cannot be used directly, unlike normal function.
Therefore, they or any functions that contain them must first copy them and
expand them to refer to the orignal functions being referenced before using
them with FuncBase.copy_expand_ref() or
CeedFuncRef.copy_expand_ref().
E.g.
>>> f = LinearFunc(function_factory=function_factory)
>>> f
<ceed.function.plugin.LinearFunc at 0x1a2cf679c18>
>>> ref_func = function_factory.get_func_ref(func=f)
>>> ref_func
<ceed.function.CeedFuncRef at 0x1a2cf74d828>
>>> ref_func.func
<ceed.function.plugin.LinearFunc at 0x1a2cf679c18>
>>> function_factory.return_func_ref(ref_func)
Copying functions
Functions can be copied automatically using deepcopy or
FuncBase.copy_expand_ref(). The former makes a full copy of all the
functions, but any CeedFuncRef functions will only copy the
CeedFuncRef, not the original function being refered to. The latter,
instead of copying the CeedFuncRef, will replace any
CeedFuncRef with copies of the class it refers to.
Functions can be manually copied with FuncBase.get_state() and
FuncBase.set_state() (although FunctionFactoryBase.make_func() is
more appropriate for end-user creation).
Create function in script
A function and stages/shapes can be created in a script,
saved to a yaml file, and then imported from the GUI ready to be
used in an experiment. See
save_config_to_yaml()
for an example.
Custom plugin function example
As explained above, plugins can register customized FuncBase
sub-classes to be included in the GUI. Following is an example of how
CeedFunc.__call__() can be overwritten in a plugin. See also
ceed.function.plugin for complete examples:
class MyFunc(CeedFunc):
    def __init__(
            self, name='MyFunc', description='y(t) = random() * t',
            **kwargs):
        # this sets the default name of the class and description, but
        # allows it to be overwritten when specified
        super().__init__(name=name, description=description, **kwargs)
    def __call__(self, t: Union[int, float, Fraction]) -> float:
        # alwasy call super first, this handles loop iteration and checking
        # whether the function is done and raises FuncDoneException
        super().__call__(t)
        # return the value
        return random() * self.get_relative_time(t)
If properly registered from the plugin, this function type will be available in the Ceed GUI.
- exception ceed.function.FuncDoneException
 Bases:
ExceptionRaised when the
FuncBaseis called with a time value after its valid time interval.
- class ceed.function.FunctionFactoryBase(**kwargs)
 Bases:
kivy._event.EventDispatcherA global store of the defined
FuncBasesub-classes and customized function instances.See
ceed.functionfor details.- Events
 - on_changed:
 The event is triggered every time a function is added or removed from the factory or if a class is registered.
- on_data_event:
 The event is dispatched by functions whenever the wish to log something. During an experiment, this data is captured and recorded in the file.
To dispatch, you must pass the function’s
ceed_idand a string indicating the event type. You can also pass arbitrary arguments that gets recorded as well. E.g.function_factory.dispatch('on_data_event', func.ceed_id, 'drug', .4, 'h2o').See
event_datafor details on default events as well as data type and argument requirements.
- funcs_inst: Dict[str, ceed.function.FuncBase]
 Dict whose keys is the function
FuncBase.nameand whose values is the corresponding function instances.Contains functions added with
add_func()as well as those automatically created and added whenregister()is called on a class.>>> function_factory.funcs_inst {'Group': <ceed.function.FuncGroup at 0x1da866f00b8>, 'Const': <ceed.function.plugin.ConstFunc at 0x1da866f0978>, 'Linear': <ceed.function.plugin.LinearFunc at 0x1da866f09e8>, 'Exp': <ceed.function.plugin.ExponentialFunc at 0x1da866f0a58>, 'Cos': <ceed.function.plugin.CosFunc at 0x1da866f0ac8>, 'line': <ceed.function.plugin.LinearFunc at 0x1da866f0278>, 'line-2': <ceed.function.plugin.LinearFunc at 0x1da866f0e48>}
- funcs_cls: Dict[str, Type[ceed.function.FuncType]] = {}
 Dict whose keys is the name of the function classes registered with
register()and whose values is the corresponding classes.:>>> function_factory.funcs_cls {'FuncGroup': ceed.function.FuncGroup, 'ConstFunc': ceed.function.plugin.ConstFunc, 'LinearFunc': ceed.function.plugin.LinearFunc, 'ExponentialFunc': ceed.function.plugin.ExponentialFunc, 'CosFunc': ceed.function.plugin.CosFunc}
- funcs_user: List[ceed.function.FuncBase] = []
 List of the function instances registered with
add_func().It does not include the instances automatically created and stored in
funcs_inst_defaultwhen a function class isregister().
- funcs_inst_default: Dict[str, ceed.function.FuncBase] = {}
 Dict whose keys is the function
FuncBase.nameand whose values are the corresponding function instances.Contains only the functions that are automatically created and added when
register()is called on a class.>>> function_factory.funcs_inst_default {'Group': <ceed.function.FuncGroup at 0x1da866f00b8>, 'Const': <ceed.function.plugin.ConstFunc at 0x1da866f0978>, 'Linear': <ceed.function.plugin.LinearFunc at 0x1da866f09e8>, 'Exp': <ceed.function.plugin.ExponentialFunc at 0x1da866f0a58>, 'Cos': <ceed.function.plugin.CosFunc at 0x1da866f0ac8>}
- plugin_sources: Dict[str, List[Tuple[Tuple[str], bytes]]] = {}
 A dictionary of the names of all the plugin packages imported, mapped to the python file contents of the directories in the package. Each value is a list of tuples.
The first item of each tuple is also a tuple containing the names of the directories leading to and including the python filename relative to the package. The second item in the tuple is the bytes content of the file.
- unique_names: ceed.utils.UniqueNames = None
 A set that tracks existing function names to help us ensure all global functions have unique names.
- param_noise_factory: ceed.function.param_noise.ParameterNoiseFactory = None
 An automatically created instance of
ceed.function.param_noise.ParameterNoiseFactorythat is used to register and get noise classes for use with functions.
- get_func_ref(name: Optional[str] = None, func: Optional[ceed.function.FuncBase] = None) ceed.function.CeedFuncRef
 Returns a
CeedFuncRefinstance that refers to the original function. Seeceed.functionfor details.One of
nameorfuncmust be specified. The function being referenced byfuncshould have been registered with this class, although it is not explicitly enforced currently.If used,
return_func_ref()must be called when the reference is not used anymore.- Parameters
 name – The name of the function to lookup in
funcs_inst.func – Or the actual function to use.
- Returns
 A
CeedFuncRefto the original function.
- return_func_ref(func_ref: ceed.function.CeedFuncRef)
 Releases the function ref created by
get_func_ref().- Parameters
 func_ref – Instance returned by
get_func_ref().
- register(cls: Type[ceed.function.FuncType], instance: Optional[ceed.function.FuncBase] = None)
 Registers the class and adds it to
funcs_cls. It also creates an instance (unlessinstanceis provided, in which case that is used) of the class that is added tofuncs_instandfuncs_inst_default.See
ceed.functionfor details.- Params
 - cls: subclass of 
FuncBase The class to register.
- instance: instance of cls
 The instance of cls to use. If None, a default class instance, using the default
FuncBase.nameis stored. Defaults to None.
- cls: subclass of 
 
- add_plugin_source(package: str, contents: List[Tuple[Tuple[str], bytes]])
 Adds the package contents to
plugin_sourcesif it hasn’t already been added. Otherwise raises an error.
- get(name: str) Optional[Type[ceed.function.FuncType]]
 Returns the class with name
namethat was registered withregister().See
ceed.functionfor details.- Params
 - name: str
 The name of the class (e.g.
'ExpFunc').
- Returns
 The class if found, otherwise None.
- get_names() List[str]
 Returns the class names of all classes registered with
register().
- get_classes() List[Type[ceed.function.FuncType]]
 Returns the classes registered with
register().
- add_func(func: ceed.function.FuncBase)
 Adds the function to
funcs_userandfuncs_inst, which makes it available in the GUI.See
ceed.functionfor details.If the
FuncBase.namealready exists infuncs_inst,FuncBase.namewill be set to a unique name based on its original name. Once added until removed, anytime the function’sFuncBase.namechanges, if it clashes with an existing function’s name, it is automatically renamed.- Params
 - func: a 
FuncBasederived instance. The function to add.
- func: a 
 
- remove_func(func: ceed.function.FuncBase, force: bool = False) bool
 Removes a function previously added with
add_func().- Params
 - func: a 
FuncBasederived instance. The function to remove.
- force: bool
 If True, it’ll remove the function even if there are references to it created by
get_func_ref().
- func: a 
 - Returns
 Whether the function was removed successfully. E.g. if force is False and it has a ref, it won’t be removed.
- clear_added_funcs(force=False)
 Removes all the functions registered with
add_func().- Params
 - force: bool
 If True, it’ll remove all functions even if there are references to them created by
get_func_ref().
- make_func(state: dict, instance: Optional[ceed.function.FuncBase] = None, clone: bool = False) ceed.function.FuncBase
 Instantiates the function from the state and returns it.
This method must be used to instantiate a function from state. See
ceed.functionfor details and an example.- Parameters
 state – The state dict representing the function as returned by
FuncBase.get_state().instance – If None, a function instance of the type specified in
statewill be created and state will applied to it. Otherwise, it is applied to the given instance, which must be of the correct class.clone – If False, only user customizable properties of the function will be set, otherwise, all properties from state are applied to the function. Clone is meant to be an complete re-instantiation of the function.
- Returns
 The function instance created.
- save_functions(use_cache=False) List[dict]
 Returns a dict representation of all the functions added with
add_func().- Parameters
 use_cache – If True, it’ll get the state using the cache from previous times the state was read and cached, if the cache exists.
It is a list of dicts where each item is the
FuncBase.get_state()of the corresponding function inuser_funcs.
- recover_funcs(function_states: List[dict]) Tuple[List[ceed.function.FuncBase], Dict[str, str]]
 Takes a list of function states such as returned by
save_functions()and instantiates the functions represented by the states and adds (add_func()) the functions to the factory.- Parameters
 function_states – List of function states.
- Returns
 A tuple
funcs, name_map,funcsis the list of functions,name_mapis a map from the original function’s name to the new name given to the function (in case a function with that name already existed).
- class ceed.function.FuncBase(function_factory, **kwargs)
 Bases:
kivy._event.EventDispatcher,ceed.utils.CeedWithIDThe base class for all functions.
See
ceed.functionfor details.When writing a plugin with new functions, you probably want to inherit from
CeedFuncbecause it provides some convenience methods (calling the function will check if it’s in the valid domain). See e.g.ceed.function.plugin.LinearFuncfor an example.- Events
 - on_changed:
 Triggered whenever a configurable property (i.e. it is returned as key in the
get_state()dict) of this instance is changed.
- name: str
 The name of the function instance. The name must be unique within a
FunctionFactoryBaseonce it is added (FunctionFactoryBase.add_func()) to theFunctionFactoryBase, otherwise it’s automatically renamed.
- description
 A description of the function. This is shown to the user and should describe the function.
- icon
 The function icon. Not used currently.
- duration: Union[float, int]
 How long after the start of the function until the function is complete and the stage continues on to the next
loopor the function is completely done.The loop iteration is done when the function reaches
durationafter the functiont_start. So, if it starts at 0 seconds anddurationis 1 second, it is done at exactly 1 second.-1 means go on forever and it never finishes (except if manually finished). This could be used e.g. if waiting for some external interrupt - set duration to -1, and only finish when the interrupt happens.
For
FuncGroupthis is automatically computed as the sum of all the sub-function duration. If any of them are negative, this will also be negative.See
ceed.functionfor more details.The value is in
get_timebase()units.
- duration_min: Union[float, int]
 Same as
duration, except it excludes any infinite portions of the function duration.I.e. any sub function whose duration is negative will be read as zero. This gives the estimated minimum duration not including any infinite portions of a single loop iteration.
See
ceed.functionfor more details.The value is in
get_timebase()units and is read only.
- duration_min_total: Union[float, int]
 The total duration of the function including all the loops, excluding any infinite portions of the function duration.
Similar to
duration_min, except it includes all the loops. So e.g. ifduration_minis5andloopis2,duration_min_totalwould typically be10.See
ceed.functionfor more details.The value is in
get_timebase()units and is read only.
- loop: int
 - The number of times the function loops through before it is considered
 done.
At the end of each loop
loop_countis incremented until done, starting from zero.See
ceed.functionfor more details.
- parent_func: Optional[ceed.function.FuncBase] = None
 If this function is the child of another function, e.g. it’s a sub-function of a
FuncGroupinstance, thenparent_funcpoints to the parent function.
- has_ref: bool
 Whether there’s a CeedFuncRef pointing to this function. If True, the function should not be deleted from the function factory that holds it.
This is automatically set by
FunctionFactoryBase.get_func_ref()andFunctionFactoryBase.return_func_ref().
- display = None
 The widget that visualize this function, if any.
- timebase: Union[float, fractions.Fraction]
 The (read-only) timebase scale factor as computed by
timebase_numerator/timebase_denominator. It returns either a float, or a Fraction instance when the numerator and denominator are ints.To set,
timebase_numeratorandtimebase_denominatormust be set individually. To use, callget_timebase(), rather than usingtimebasedirectly.The timebase is the scaling factor by which some function properties that relate to time, e.g.
duration, are multiplied to convert from timebase units to time.By default
timebase_numeratoris 0 andtimebase_denominatoris 1 which makestimebase0 indicating that the timebase used is given by the parent function or is 1. Whentimebaseis not 0 thistimebaseis used instead.See
ceed.functionandget_timebase()for more details.Note
This property is dispatched whenever the value returned by
get_timebase()would change, even iftimebasedidn’t change. E.g. when a parent’s timebase changes.
- t_start: Union[float, int, fractions.Fraction] = 0
 The global time offset subtracted from the time passed to the function.
This is the global time with which the function or loop was initialized,
get_relative_time()removes it to get to local time. The value is in seconds. Seeceed.functionfor more details.Don’t set directly, it is set in
init_func()andinit_loop_iteration().
- t_end: Union[float, int, fractions.Fraction] = 0
 The time at which the loop or function ends in global timebase.
Set by the function after each loop is done (i.e.
CeedFunc.is_loop_done()returned True) and is typically the second value fromget_domain(), or the current time value if that is negative.It is updated before
finalize_loop_iteration(), andfinalize_func()are called.
- loop_count: int = 0
 The current loop iteration.
This goes from zero (set by
init_func()/init_loop_iteration()) toloop. The function is done when it is exactlyloop, having loopedtimes. Whenfinalize_loop_iteration()andfinalize_func()are called, it is the value for the loop iteration that just ended.See also
ceed.function.
- loop_tree_count: int = 0
 The current iteration, starting from zero, and incremented for each loop of the function, including outside loops that loop over the function.
See
init_func_tree()for details.
- noisy_parameters: Dict[str, ceed.function.param_noise.NoiseBase]
 A dict mapping parameter names of the function to
NoiseBaseinstances that indicate how the parameter should be sampled, when the parameter needs to be stochastic.Only parameters returned in
get_noise_supported_parameters()may have randomness associated with them.E.g.:
>>> f = LinearFunc(function_factory=function_factory, duration=2, m=2) >>> UniformNoise = function_factory.param_noise_factory.get_cls('UniformNoise') >>> f.set_parameter_noise( ... 'm', noise_obj=UniformNoise(min_val=10, max_val=20)) >>> f.m 2 >>> f.b 0.0 >>> f.resample_parameters() >>> f.m 12.902067284602595 >>> f.resample_parameters() >>> f.m 11.555420807597352 >>> f.b 0.0
- function_factory: ceed.function.FunctionFactoryBase = None
 The
FunctionFactoryBaseinstance with which this function is associated. This should be set by whoever creates the function by passing it to the constructor.
- noisy_parameter_samples: Dict[str, List[float]] = {}
 For each parameter listed in
noisy_parameters, ifsample_each_loop, then during theresample_parameters()stage we pre-compute the values for the parameter for each loop iteration and store it here.The pre-computed values are then used to update the parameter during each
init_func()andinit_loop_iteration(). The total number of samples includes all outside loops, seesample_each_loopandinit_func_tree().
- get_timebase() Union[float, fractions.Fraction]
 Returns the function’s timebase.
If
timebase_numeratorandtimebaseis 0, it returns the timebase of itsparent_funcwithget_timebase()if it has a parent. If it doesn’t have a parent, it return 1. Otherwise, it returnstimebase.
- get_gui_props()
 Called internally by the GUI to get the properties of the function that should be displayed to the user to be customized.
- Returns
 A dict that contains all properties that should be displayed. The values of the property is as follows:
If it’s the string int, float, str or it’s the python type int, float, or str then the GUI will show a editable property for this type.
If it’s None, we look at the value of the property in the instance and display accordingly (e.g. if it’s a str type property, a string property is displayed to the user).
Note
The default value determines the type. So if the default value is
0, the type will be int and a user won’t be able to enter a float. Use e.g.0.0in the latter case.
E.g.:
>>> Cos = function_factory.get('CosFunc') >>> cos = Cos() >>> cos.get_gui_props() {'A': None, 'duration': None, 'f': None, 'loop': None, 'name': None, 't_offset': None, 'th0': None}
- get_prop_pretty_name()
 Called internally by the GUI to get a translation dictionary which converts property names as used in
get_state()into nicer property names used to display the properties to the user for customization. E.g. “timebase_numerator” may be displayed as “TB num” as that is more concise.- Returns
 A dict that contains all properties whose names should be changed when displayed. Keys in the dict are the names as returned by
get_state(), the values are the names that should be displayed instead. If a property is not included it’s original property name is used instead.
E.g.:
>>> f = LinearFunc(function_factory=function_factory) {'timebase_numerator': 'TB num', 'timebase_denominator': 'TB denom'}
- get_gui_elements() dict
 Returns widget instances that should be displayed to the user along with this function’s editable properties of
get_gui_props().These widgets are displayed along with other config parameters for the function and can be used for custom config options. This is called by the Ceed GUI when the settings are first displayed to the user.
- Returns
 It should return a dict of the name of each setting mapped to the widget controlling the setting. It will be displayed in two columns: the name followed by the widget on the same row.
E.g.:
>>> Cos = function_factory.get('CosFunc') >>> cos = Cos() >>> cos.get_gui_elements() {}
- get_noise_supported_parameters() Set[str]
 Returns the set of property names of this function that supports randomness and may have an
ceed.function.param_noise.NoiseBaseinstance associated with it.
- get_cached_state(use_cache=False) Dict
 Like
get_state(), but it caches the result. And next time it is called, ifuse_cacheis True, the cached value will be returned, unless the config changed in between. Helpful for backup so we don’t recompute the full state.- Parameters
 use_cache – If True, it’ll get the state using the cache from previous times the state was read and cached, if the cache exists.
- Returns
 The state dict.
- get_state(recurse=True, expand_ref=False) Dict
 Returns a dict representation of the function so that it can be reconstructed later with
apply_state().- Params
 - recurse: bool
 When the function has children functions, e.g. a
FuncGroup, if True all the children functions’ states will also be returned, otherwise, only this function’s state is returned. See the example.- expand_ref: bool
 If True, if any sub-functions (or this function itself) are
CeedFuncRefinstances, they will be expanded to contain the state of the actual underlying function. Otherwise, it is returned as being a function reference.
- Returns
 A dict with all the configuration data.
E.g.:
>>> Cos = function_factory.get('CosFunc') >>> cos = Cos() >>> cos.get_state() {'A': 1.0, 'cls': 'CosFunc', 'duration': 0, 'f': 1.0, 'loop': 1, 'name': 'Cos', 't_offset': 0, 'th0': 0.0} >>> Group = function_factory.get('FuncGroup') >>> g = Group() >>> g <ceed.function.FuncGroup at 0x4f85800> >>> g.add_func(cos) >>> g.get_state(recurse=True) {'cls': 'FuncGroup', 'funcs': [{'A': 1.0, 'cls': 'CosFunc', 'duration': 0, 'f': 1.0, 'loop': 1, 'name': 'Cos', 't_offset': 0, 'th0': 0.0}], 'loop': 1, 'name': 'Group'} >>> g.get_state(recurse=False) {'cls': 'FuncGroup', 'loop': 1, 'name': 'Group'}
- apply_state(state: Dict, clone=False)
 Takes the state of the function saved with
get_state()and applies it to this function. it also creates any children function e.g. in the case it is aFuncGroupetc.It is called internally and should not be used directly. Use
FunctionFactoryBase.make_func()instead.- Params
 - state: dict
 The dict to use to reconstruct the function as returned by
get_state().- clone: bool
 If True will apply all the state exactly as in the original function, otherwise it doesn’t apply internal parameters listed in
_clone_propsthat are not user customizable.
See
ceed.functionfor an example.
- get_funcs(step_into_ref=True) Generator[Union[ceed.function.FuncBase, ceed.function.CeedFuncRef], None, None]
 Generator that yields the function and all its children functions if it has any, e.g. for
FuncGroup. It’s in DFS order.- Params
 - step_into_ref: bool
 If True, when it encounters a
CeedFuncRefinstance it’ll step into it and return that function and its children. Otherwise, it’ll just return theCeedFuncRefand not step into it.
E.g.:
>>> Cos = function_factory.get('CosFunc') >>> cos = Cos() >>> Group = function_factory.get('FuncGroup') >>> g = Group() >>> g.add_func(cos) >>> [f for f in cos.get_funcs()] [<ceed.function.plugin.CosFunc at 0x4d71c78>] >>> [f for f in g.get_funcs()] [<ceed.function.FuncGroup at 0x4f85800>, <ceed.function.plugin.CosFunc at 0x4d71c78>]
- can_other_func_be_added(other_func: Union[ceed.function.CeedFuncRef, ceed.function.FuncBase]) bool
 Checks whether the other function may be added to this function.
Specifically, it checks whether this function is a child of the other function, and if so we shouldn’t allow the other function to be added to this function as it’ll create a circular tree.
- Parameters
 other_func – Another
FuncBaseinstance.- Returns
 Whether it is safe to add the other function as a child of this function.
- copy_expand_ref() ceed.function.FuncBase
 Copies this function and all its sub-functions.
If any of them are
CeedFuncRefinstances, they are replaced with a copy of their original function.- Returns
 A copy of this function, with all
CeedFuncRefinstances replaced by their original normal function.
- init_func_tree(root: Optional[ceed.function.FuncBase] = None) None
 Initializes the function as part of the function tree so it is ready to be called to get the function values as part of the tree. It is called once for each function of the entire function tree.
- Parameters
 root – The root of the function tree. If None, it’s self.
For example, for the following function structure:
GroupFunc: name: 'root' loop: 5 GroupFunc: name: 'child_a' loop: 3 ConstFunc: name: 'child' loop: 4
when the experiment is ready, after
resample_parameters()and the stage is ready to run, ceed will callinit_func_tree()once forroot,child_a, andchildin that order. Therootparameter passed will be therootfunction.Then, it will call
init_func()forroot,child_a, andchild1, 5, and 15 times, respectively. Once for each time the function is started.Finally, it will call
init_loop_iteration()forroot,child_a, andchild4, 10, and 45 times, respectively. Once for each loop iteration of the function, except the first.
- init_func(t_start: Union[float, int, fractions.Fraction]) None
 Initializes the function so it is ready to be called to get the function values. See also
init_func_tree()andinit_loop_iteration(). If overriding,supermust be called.- Parameters
 t_start – The time in seconds in global time.
t_startwill be set to this value. All subsequent calls to the function with a time value will be relative to this given time.
- init_loop_iteration(t_start: Union[float, int, fractions.Fraction]) None
 Initializes the function at the beginning of each loop.
It’s called internally at the start of every
loopiteration, except the first. See alsoinit_func_tree()andinit_func(). If overriding,supermust be called.- Parameters
 t_start – The time in seconds in global time.
t_startwill be set to this value. All subsequent calls to the function with a time value will be relative to this given time.
- finalize_func() None
 Finalizes the function at the end of all its loops, when the function is done. See also
finalize_loop_iteration(). If overriding,supermust be called.
- finalize_loop_iteration() None
 Finalizes the function at the end of each loop, including the first and last. See also
finalize_func(). If overriding,supermust be called.
- get_domain(current_iteration: bool = True) Tuple[Union[float, int, fractions.Fraction], Union[float, int, fractions.Fraction]]
 Returns the current domain of the function.
- Parameters
 current_iteration – If True, returns the domain for the current loop iteration (i.e. the end point is the expected end time of the current loop). Otherwise, returns the domain ending at the end of the final loop iteration. In both cases, the interval start is the time the current loop iteration started, in global time.
See
ceed.functionfor details.
- get_relative_time(t: Union[float, int, fractions.Fraction]) Union[float, int, fractions.Fraction]
 Converts the global time to the local function time.
At each time-step, the function is called with the global time. This function converts the time into local time, relative to the
t_start. Also adds any function specific offset (e.g.CeedFunc.t_offset.
- tick_loop(t: Union[float, int, fractions.Fraction]) bool
 Increments
loop_countand returns whether the function is done, which is when all theloopiterations are done andloop_countreachedloop.tis in seconds in global time and this is only called when the function time reached the end of its valid domain so that it makes sense to increment the loop. I.e. if the user called the function with a time past the current loop duration, this is called internally to increment the loop.May not be called with time values smaller than the domain. Or if the loop iterations are already done.
- Parameters
 t – The time at which the last function in the tree or loop iteration of this function ended, in global time.
- Returns
 True if it ticked the loop, otherwise False if we cannot tick because we hit the max and the function is done.
- resample_parameters(parent_tree: Optional[List[ceed.function.FuncBase]] = None, is_forked=False, base_loops: int = 1) None
 Resamples all the function parameters that have randomness attached to it in
noisy_parametersand updates their values.For all parameters that are randomized and
sample_each_loopis True, the samples for all the iterations are also pre-sampled and stored innoisy_parameter_samples.If
is_forked, then iflock_after_forked, then it won’t be re-sampled. This allows sampling aCeedFuncReffunction before forking it, and then, when copying and forking the source functions into individual functions we don’t resample them. Then all these individual functions share the same random parameters as the original referenced function.parent_treeis not inclusive.base_loopsindicates the expected number of times the function is expected to loop due to the stage containing the function (if any). This is in addition toloopof the function and its parent tree. So e.g. ifloopis3andbase_loopsis2with no parents, then the function will be looped6times, twice by the stage.E.g.:
>>> # get the classes >>> function_factory = FunctionFactoryBase() >>> register_all_functions(function_factory) >>> LinearFunc = function_factory.get('LinearFunc') >>> UniformNoise = function_factory.param_noise_factory.get_cls( ... 'UniformNoise') >>> # create function and add noise to parameters >>> f = LinearFunc(function_factory=function_factory) >>> f.set_parameter_noise('m', noise_obj=UniformNoise()) >>> f.set_parameter_noise( ... 'b' noise_obj=UniformNoise(lock_after_forked=True)) >>> # now add it to factory and create references to it >>> function_factory.add_func(f) >>> ref1 = function_factory.get_func_ref(func=f) >>> ref2 = function_factory.get_func_ref(func=f) >>> # resample the original function and fork refs into copies >>> f.resample_parameters() >>> f1 = ref1.copy_expand_ref() >>> f2 = ref2.copy_expand_ref() >>> # now resample only those that are not locked >>> f1.resample_parameters(is_forked=True) >>> f2.resample_parameters(is_forked=True) >>> # b is locked to pre-forked value and is not sampled after fork >>> f.m, f.b (0.22856343565686332, 0.3092686616300213) >>> f1.m, f1.b (0.05228392113705038, 0.3092686616300213) >>> f2.m, f2.b (0.9117196772532972, 0.3092686616300213)
- get_ref_src() ceed.function.FuncBase
 Returns the function referenced by this object if we are a
CeedFuncRef, otherwise it just returns this function.Useful to get the referenced function without having to check whether we are a
CeedFuncRef. I.e.func.get_ref_src().name.
- set_parameter_noise(parameter: str, noise_obj: Optional[ceed.function.param_noise.NoiseBase] = None, cls: Optional[str] = None) Optional[ceed.function.param_noise.NoiseBase]
 Sets the noise of the parameter.
- Parameters
 parameter – The name of the function parameter to randomize/reset.
noise_obj – A
NoiseBaseto use for the parameter. If None, we create one fromcls.cls – If
noise_objis None and this is a string, we create a noise class from the noise factory of classcls. If this is also None, if the parameter has a noise object it is removed.
- Returns
 The noise object created/passed in, or None.
- ceed.function.FuncType
 The type-hint type for
FuncBase.alias of TypeVar(‘FuncType’, bound=
FuncBase)
- ceed.function.CeedFuncOrRefInstance
 Instance of either
CeedFuncorCeedFuncRef.alias of
Union[FuncBase,CeedFuncRef]
- class ceed.function.CeedFunc(function_factory, **kwargs)
 Bases:
ceed.function.FuncBaseA base class for typical Ceed functions.
See
ceed.function.pluginfor example functions that are based on this class.- t_offset
 The amount of time in seconds to add the function time when computing the result. It allows some additional control over the function.
All functions that inherit from this class must add this time. E.g. the
LinearFuncdefines its function asy(t) = mt + bwith timet = (t_in - t_start + t_offset).The
durationof the function is not affected by this property as it is independent of this. I.e. we check whether a time value is in the function’sget_domain()ignoringt_offsetbut we then add it to the given time before computing the function’s output.
- get_relative_time(t: Union[float, int, fractions.Fraction]) Union[float, int, fractions.Fraction]
 Converts the global time to the local function time.
At each time-step, the function is called with the global time. This function converts the time into local time, relative to the
t_start. Also adds any function specific offset (e.g.CeedFunc.t_offset.
- is_loop_done(t: Union[float, int, fractions.Fraction]) bool
 Whether the time
t, in global time, is after the end of the current loop iteration.- Parameters
 t – Time in seconds, in global time.
- Returns
 Whether it’s past the end of the current loop iteration.
- get_gui_props()
 Called internally by the GUI to get the properties of the function that should be displayed to the user to be customized.
- Returns
 A dict that contains all properties that should be displayed. The values of the property is as follows:
If it’s the string int, float, str or it’s the python type int, float, or str then the GUI will show a editable property for this type.
If it’s None, we look at the value of the property in the instance and display accordingly (e.g. if it’s a str type property, a string property is displayed to the user).
Note
The default value determines the type. So if the default value is
0, the type will be int and a user won’t be able to enter a float. Use e.g.0.0in the latter case.
E.g.:
>>> Cos = function_factory.get('CosFunc') >>> cos = Cos() >>> cos.get_gui_props() {'A': None, 'duration': None, 'f': None, 'loop': None, 'name': None, 't_offset': None, 'th0': None}
- get_state(recurse=True, expand_ref=False)
 Returns a dict representation of the function so that it can be reconstructed later with
apply_state().- Params
 - recurse: bool
 When the function has children functions, e.g. a
FuncGroup, if True all the children functions’ states will also be returned, otherwise, only this function’s state is returned. See the example.- expand_ref: bool
 If True, if any sub-functions (or this function itself) are
CeedFuncRefinstances, they will be expanded to contain the state of the actual underlying function. Otherwise, it is returned as being a function reference.
- Returns
 A dict with all the configuration data.
E.g.:
>>> Cos = function_factory.get('CosFunc') >>> cos = Cos() >>> cos.get_state() {'A': 1.0, 'cls': 'CosFunc', 'duration': 0, 'f': 1.0, 'loop': 1, 'name': 'Cos', 't_offset': 0, 'th0': 0.0} >>> Group = function_factory.get('FuncGroup') >>> g = Group() >>> g <ceed.function.FuncGroup at 0x4f85800> >>> g.add_func(cos) >>> g.get_state(recurse=True) {'cls': 'FuncGroup', 'funcs': [{'A': 1.0, 'cls': 'CosFunc', 'duration': 0, 'f': 1.0, 'loop': 1, 'name': 'Cos', 't_offset': 0, 'th0': 0.0}], 'loop': 1, 'name': 'Group'} >>> g.get_state(recurse=False) {'cls': 'FuncGroup', 'loop': 1, 'name': 'Group'}
- class ceed.function.FuncGroup(name='Group', **kwargs)
 Bases:
ceed.function.FuncBaseFunction that represents a sequence of sub-functions.
See
ceed.functionfor more details.When the function instance is called it goes through all its sub-functions sequentially until they are done. E.g.:
>>> Group = function_factory.get('FuncGroup') >>> Const = function_factory.get('ConstFunc') >>> g = Group() >>> g.add_func(Const(a=1, duration=5)) >>> g.add_func(Const(a=2, duration=3)) >>> g.add_func(Const(a=3, duration=2)) >>> g.init_func(0) >>> g(0) 1 >>> g(5) 2 >>> g(6) 2 >>> g(8) 3 >>> g(10) Traceback (most recent call last): g(10) File "g:\python\libs\ceed\ceed\function\__init__.py", line 934, in __call__ raise FuncDoneException FuncDoneException
- funcs: List[Union[ceed.function.FuncBase, ceed.function.CeedFuncRef]] = []
 The list of children functions of this function.
- init_func_tree(root: Optional[ceed.function.FuncBase] = None) None
 Initializes the function as part of the function tree so it is ready to be called to get the function values as part of the tree. It is called once for each function of the entire function tree.
- Parameters
 root – The root of the function tree. If None, it’s self.
For example, for the following function structure:
GroupFunc: name: 'root' loop: 5 GroupFunc: name: 'child_a' loop: 3 ConstFunc: name: 'child' loop: 4
when the experiment is ready, after
resample_parameters()and the stage is ready to run, ceed will callinit_func_tree()once forroot,child_a, andchildin that order. Therootparameter passed will be therootfunction.Then, it will call
init_func()forroot,child_a, andchild1, 5, and 15 times, respectively. Once for each time the function is started.Finally, it will call
init_loop_iteration()forroot,child_a, andchild4, 10, and 45 times, respectively. Once for each loop iteration of the function, except the first.
- init_func(t_start: Union[float, int, fractions.Fraction]) None
 Initializes the function so it is ready to be called to get the function values. See also
init_func_tree()andinit_loop_iteration(). If overriding,supermust be called.- Parameters
 t_start – The time in seconds in global time.
t_startwill be set to this value. All subsequent calls to the function with a time value will be relative to this given time.
- init_loop_iteration(t_start: Union[float, int, fractions.Fraction]) None
 Initializes the function at the beginning of each loop.
It’s called internally at the start of every
loopiteration, except the first. See alsoinit_func_tree()andinit_func(). If overriding,supermust be called.- Parameters
 t_start – The time in seconds in global time.
t_startwill be set to this value. All subsequent calls to the function with a time value will be relative to this given time.
- replace_ref_func_with_source(func_ref: ceed.function.CeedFuncRef) Tuple[ceed.function.FuncBase, int]
 Given the
CeedFuncRef, it’ll locate it infuncand replace it with the underlying function.The caller is responsible for returning the reference with
FunctionFactoryBase.return_func_ref().- Parameters
 func_ref – The
CeedFuncRefto replace infunc.- Returns
 A tuple of
(func, i)wherefuncis theFuncBasethat replaced the reference function. Andiis the index infuncswhere it was found.
- add_func(func: Union[ceed.function.FuncBase, ceed.function.CeedFuncRef], after=None, index=None)
 Adds
functo this function as a sub-function infuncs.If calling this manually, remember to check
FuncBase.can_other_func_be_added()before adding if there’s potential for it to return False.
- remove_func(func)
 Removes sub-function
funcfromfuncs. It must exist infuncs.- Params
 - func: 
FuncBase The function instance to remove.
- func: 
 
- get_state(recurse=True, expand_ref=False)
 Returns a dict representation of the function so that it can be reconstructed later with
apply_state().- Params
 - recurse: bool
 When the function has children functions, e.g. a
FuncGroup, if True all the children functions’ states will also be returned, otherwise, only this function’s state is returned. See the example.- expand_ref: bool
 If True, if any sub-functions (or this function itself) are
CeedFuncRefinstances, they will be expanded to contain the state of the actual underlying function. Otherwise, it is returned as being a function reference.
- Returns
 A dict with all the configuration data.
E.g.:
>>> Cos = function_factory.get('CosFunc') >>> cos = Cos() >>> cos.get_state() {'A': 1.0, 'cls': 'CosFunc', 'duration': 0, 'f': 1.0, 'loop': 1, 'name': 'Cos', 't_offset': 0, 'th0': 0.0} >>> Group = function_factory.get('FuncGroup') >>> g = Group() >>> g <ceed.function.FuncGroup at 0x4f85800> >>> g.add_func(cos) >>> g.get_state(recurse=True) {'cls': 'FuncGroup', 'funcs': [{'A': 1.0, 'cls': 'CosFunc', 'duration': 0, 'f': 1.0, 'loop': 1, 'name': 'Cos', 't_offset': 0, 'th0': 0.0}], 'loop': 1, 'name': 'Group'} >>> g.get_state(recurse=False) {'cls': 'FuncGroup', 'loop': 1, 'name': 'Group'}
- apply_state(state={}, clone=False)
 Takes the state of the function saved with
get_state()and applies it to this function. it also creates any children function e.g. in the case it is aFuncGroupetc.It is called internally and should not be used directly. Use
FunctionFactoryBase.make_func()instead.- Params
 - state: dict
 The dict to use to reconstruct the function as returned by
get_state().- clone: bool
 If True will apply all the state exactly as in the original function, otherwise it doesn’t apply internal parameters listed in
_clone_propsthat are not user customizable.
See
ceed.functionfor an example.
- get_funcs(step_into_ref=True) Generator[Union[ceed.function.FuncBase, ceed.function.CeedFuncRef], None, None]
 Generator that yields the function and all its children functions if it has any, e.g. for
FuncGroup. It’s in DFS order.- Params
 - step_into_ref: bool
 If True, when it encounters a
CeedFuncRefinstance it’ll step into it and return that function and its children. Otherwise, it’ll just return theCeedFuncRefand not step into it.
E.g.:
>>> Cos = function_factory.get('CosFunc') >>> cos = Cos() >>> Group = function_factory.get('FuncGroup') >>> g = Group() >>> g.add_func(cos) >>> [f for f in cos.get_funcs()] [<ceed.function.plugin.CosFunc at 0x4d71c78>] >>> [f for f in g.get_funcs()] [<ceed.function.FuncGroup at 0x4f85800>, <ceed.function.plugin.CosFunc at 0x4d71c78>]
- resample_parameters(parent_tree: Optional[List[ceed.function.FuncBase]] = None, is_forked=False, base_loops: int = 1) None
 Resamples all the function parameters that have randomness attached to it in
noisy_parametersand updates their values.For all parameters that are randomized and
sample_each_loopis True, the samples for all the iterations are also pre-sampled and stored innoisy_parameter_samples.If
is_forked, then iflock_after_forked, then it won’t be re-sampled. This allows sampling aCeedFuncReffunction before forking it, and then, when copying and forking the source functions into individual functions we don’t resample them. Then all these individual functions share the same random parameters as the original referenced function.parent_treeis not inclusive.base_loopsindicates the expected number of times the function is expected to loop due to the stage containing the function (if any). This is in addition toloopof the function and its parent tree. So e.g. ifloopis3andbase_loopsis2with no parents, then the function will be looped6times, twice by the stage.E.g.:
>>> # get the classes >>> function_factory = FunctionFactoryBase() >>> register_all_functions(function_factory) >>> LinearFunc = function_factory.get('LinearFunc') >>> UniformNoise = function_factory.param_noise_factory.get_cls( ... 'UniformNoise') >>> # create function and add noise to parameters >>> f = LinearFunc(function_factory=function_factory) >>> f.set_parameter_noise('m', noise_obj=UniformNoise()) >>> f.set_parameter_noise( ... 'b' noise_obj=UniformNoise(lock_after_forked=True)) >>> # now add it to factory and create references to it >>> function_factory.add_func(f) >>> ref1 = function_factory.get_func_ref(func=f) >>> ref2 = function_factory.get_func_ref(func=f) >>> # resample the original function and fork refs into copies >>> f.resample_parameters() >>> f1 = ref1.copy_expand_ref() >>> f2 = ref2.copy_expand_ref() >>> # now resample only those that are not locked >>> f1.resample_parameters(is_forked=True) >>> f2.resample_parameters(is_forked=True) >>> # b is locked to pre-forked value and is not sampled after fork >>> f.m, f.b (0.22856343565686332, 0.3092686616300213) >>> f1.m, f1.b (0.05228392113705038, 0.3092686616300213) >>> f2.m, f2.b (0.9117196772532972, 0.3092686616300213)
- set_ceed_id(min_available: int) int
 Sets the ID of this and any sub objects, each to a number equal or greater than
min_availableand returns the next minimum number available to be used.See
event_datafor more details.- Parameters
 min_available – The minimum number available to be used for the ID so it is unique.
- Returns
 The next minimum available number that can be used. Any number larger or equal to it is free to be used.
- class ceed.function.CeedFuncRef(function_factory, func=None)
 Bases:
objectA function that refers to another function.
This is never manually created, but rather returned by
FunctionFactoryBase.get_func_ref(). SeeFunctionFactoryBase.get_func_ref()andceed.functionfor details.- display = None
 Same as
FuncBase.display.
- parent_func = None
 Same as
FuncBase.parent_func.
- function_factory = None
 Same as
FuncBase.function_factory.
- get_ref_src() ceed.function.FuncBase
 See
CeedStage.get_ref_src().
- ceed.function.register_all_functions(function_factory: ceed.function.FunctionFactoryBase)
 Registers all the internal plugins and built-in functions and function distributions with the
FunctionFactoryBaseandFunctionFactoryBase.param_noise_factoryinstance, respectively.It gets and registers all the plugins functions and function distributions under
ceed/function/pluginusingget_plugin_functions(). See that function for how to make your plugin functions and distributions discoverable.- Parameters
 function_factory – a
FunctionFactoryBaseinstance.
- ceed.function.register_external_functions(function_factory: ceed.function.FunctionFactoryBase, package: str)
 Registers all the plugin functions and function distributions in the package with the
FunctionFactoryBaseandFunctionFactoryBase.param_noise_factoryinstance, respectively.See
get_plugin_functions()for how to make your plugin functions and distributions discoverable.Plugin source code files are copied to the data file when a data file is created. However, it doesn’t copy all files (i.e. it ignores non-python files) so it should be independently tracked for each experiment.
- Parameters
 function_factory – A
FunctionFactoryBaseinstance.package – The name of the package containing the plugins.