from nengo.builder import Builder, Operator, Signal
from nengo.processes import Process
from nengo.rc import rc
[docs]class SimProcess(Operator):
"""Simulate a process.
Parameters
----------
process : Process
The `.Process` to simulate.
input : Signal or None
Input to the process, or None if no input.
output : Signal or None
Output from the process, or None if no output.
t : Signal
The signal associated with the time (a float, in seconds).
mode : str, optional
Denotes what type of update this operator performs.
Must be one of ``'update'``, ``'inc'`` or ``'set'``.
tag : str, optional
A label associated with the operator, for debugging purposes.
Attributes
----------
input : Signal or None
Input to the process, or None if no input.
mode : str
Denotes what type of update this operator performs.
output : Signal or None
Output from the process, or None if no output.
process : Process
The `.Process` to simulate.
t : Signal
The signal associated with the time (a float, in seconds).
tag : str or None
A label associated with the operator, for debugging purposes.
Notes
-----
1. sets ``[output] if output is not None and mode=='set' else []``
2. incs ``[output] if output is not None and mode=='inc' else []``
3. reads ``[t, input] if input is not None else [t]``
4. updates ``[output] if output is not None and mode=='update' else []``
"""
def __init__(self, process, input, output, t, mode="set", state=None, tag=None):
super().__init__(tag=tag)
self.process = process
self.mode = mode
assert output is not None
self.reads = [t, input] if input is not None else [t]
self.sets = []
self.incs = []
self.updates = []
if mode == "update":
self.updates.extend([output])
elif mode == "inc":
self.incs.extend([output])
elif mode == "set":
self.sets.extend([output])
else:
raise ValueError("Unrecognized mode %r" % mode)
self.state = {} if state is None else state
self.updates.extend(self.state.values())
@property
def input(self):
return None if len(self.reads) == 1 else self.reads[1]
@property
def output(self):
if len(self.updates) <= len(self.incs) <= len(self.sets) <= 0:
return None
elif self.mode == "update":
return self.updates[0]
elif self.mode == "inc":
return self.incs[0]
elif self.mode == "set":
return self.sets[0]
@property
def t(self):
return self.reads[0]
def _descstr(self):
return "%s, %s -> %s" % (self.process, self.input, self.output)
[docs] def make_step(self, signals, dt, rng):
t = signals[self.t]
input = signals[self.input] if self.input is not None else None
output = signals[self.output]
shape_in = input.shape if input is not None else (0,)
shape_out = output.shape
rng = self.process.get_rng(rng)
state = {name: signals[sig] for name, sig in self.state.items()}
step_f = self.process.make_step(shape_in, shape_out, dt, rng, state)
args = (t,) if input is None else (t, input)
if self.mode == "inc":
def step_simprocess():
output[...] += step_f(args[0].item(), *args[1:])
else:
def step_simprocess():
output[...] = step_f(args[0].item(), *args[1:])
return step_simprocess
[docs]@Builder.register(Process)
def build_process(model, process, sig_in=None, sig_out=None, mode="set"):
"""Builds a `.Process` object into a model.
Parameters
----------
model : Model
The model to build into.
process : Process
Process to build.
sig_in : Signal, optional
The input signal, or None if no input signal.
sig_out : Signal, optional
The output signal, or None if no output signal.
mode : "set" or "inc" or "update", optional
The ``mode`` of the built `.SimProcess`.
Notes
-----
Does not modify ``model.params[]`` and can therefore be called
more than once with the same `.Process` instance.
"""
if sig_out is None:
sig_out = Signal(shape=sig_in.shape, name="%s.%s" % (sig_in.name, process))
shape_in = sig_in.shape if sig_in is not None else (0,)
shape_out = sig_out.shape if sig_out is not None else (0,)
dtype = (
sig_out.dtype
if sig_out is not None
else sig_in.dtype
if sig_in is not None
else rc.float_dtype
)
state_init = process.make_state(shape_in, shape_out, model.dt, dtype=dtype)
state = {}
for name, value in state_init.items():
state[name] = Signal(value)
model.sig[process]["_state_" + name] = state[name]
model.add_op(
SimProcess(process, sig_in, sig_out, model.time, mode=mode, state=state)
)
return sig_out