def test_distributions(): check_init_args(PDF, ["x", "p"]) check_repr(PDF([1, 2, 3], [0.1, 0.8, 0.1])) assert (repr(PDF( [1, 2], [0.4, 0.6])) == "PDF(x=array([1., 2.]), p=array([0.4, 0.6]))") check_init_args(Uniform, ["low", "high", "integer"]) check_repr(Uniform(1, 3)) check_repr(Uniform(1, 4, integer=True)) assert repr(Uniform(0, 1)) == "Uniform(low=0, high=1)" assert repr(Uniform( 0, 5, integer=True)) == "Uniform(low=0, high=5, integer=True)" check_init_args(Gaussian, ["mean", "std"]) check_repr(Gaussian(0, 2)) assert repr(Gaussian(1, 0.1)) == "Gaussian(mean=1, std=0.1)" check_init_args(Exponential, ["scale", "shift", "high"]) check_repr(Exponential(2.0)) check_repr(Exponential(2.0, shift=0.1)) check_repr(Exponential(2.0, shift=0.1, high=10.0)) assert repr(Exponential(2.0)) == "Exponential(scale=2.0)" check_init_args(UniformHypersphere, ["surface", "min_magnitude"]) check_repr(UniformHypersphere()) check_repr(UniformHypersphere(surface=True)) check_repr(UniformHypersphere(min_magnitude=0.3)) assert repr(UniformHypersphere()) == "UniformHypersphere()" assert repr( UniformHypersphere(surface=True)) == "UniformHypersphere(surface=True)" check_init_args(Choice, ["options", "weights"]) check_repr(Choice([3, 2, 1])) check_repr(Choice([3, 2, 1], weights=[0.1, 0.2, 0.7])) assert repr(Choice([1, 2, 3])) == "Choice(options=array([1., 2., 3.]))" assert (repr( Choice([1, 2, 3], weights=[0.1, 0.5, 0.4]) ) == "Choice(options=array([1., 2., 3.]), weights=array([0.1, 0.5, 0.4]))") check_init_args(Samples, ["samples"]) check_repr(Samples([3, 2, 1])) assert repr(Samples([3, 2, 1])) == "Samples(samples=array([3., 2., 1.]))" check_init_args(SqrtBeta, ["n", "m"]) check_repr(SqrtBeta(3)) check_repr(SqrtBeta(3, m=2)) assert repr(SqrtBeta(3)) == "SqrtBeta(n=3)" assert repr(SqrtBeta(3, 2)) == "SqrtBeta(n=3, m=2)" check_init_args(SubvectorLength, ["dimensions", "subdimensions"]) check_repr(SubvectorLength(6)) check_repr(SubvectorLength(6, 2)) assert repr(SubvectorLength(3)) == "SubvectorLength(dimensions=3)" check_init_args(CosineSimilarity, ["dimensions"]) check_repr(CosineSimilarity(6)) assert repr(CosineSimilarity(6)) == "CosineSimilarity(dimensions=6)"
def test_exponential(scale, shift, high, rng): n = 100 dist = Exponential(scale, shift=shift, high=high) samples = dist.sample(n, rng=rng) assert np.all(samples >= shift) assert np.all(samples <= high) # approximation of 95% confidence interval ci = scale * 1.96 / np.sqrt(n) if scale + ci < high: assert abs(np.mean(samples - shift) - scale) < ci
def RhythmicDMP(n_per_d, freq, forcing_f, tau=0.025, net=None): if net is None: net = nengo.Network(label="Rhythmic DMP") out_dims = forcing_f(0.).size omega = freq * tau * 2 * np.pi with net: # --- Decode forcing_f from oscillator net.osc = nengo.Ensemble(n_per_d * 2, dimensions=2, intercepts=Exponential(0.15, 0.3, 0.6), label=forcing_f.__name__) nengo.Connection(net.osc, net.osc, synapse=tau, transform=[[1, -omega], [omega, 1]]) net.output = nengo.Node(size_in=out_dims) nengo.Connection(net.osc, net.output, function=radial_f(forcing_f), synapse=None) # --- Drive the oscillator to a starting position net.reset = nengo.Node(size_in=1) d_intercepts = Exponential(0.2, -0.5, 0.1) net.diff_inhib = nengo.Ensemble(20, dimensions=1, intercepts=d_intercepts, encoders=Choice([[1]])) net.diff = nengo.Ensemble(n_per_d, dimensions=2) nengo.Connection(net.reset, net.diff_inhib, transform=-1, synapse=None) nengo.Connection(net.diff_inhib.neurons, net.diff.neurons, transform=-np.ones((n_per_d, 20))) nengo.Connection(net.osc, net.diff) reset_goal = np.array([-1, omega * 0]) nengo.Connection(net.diff, net.osc, function=lambda x: reset_goal - x) # --- Inhibit the oscillator by default i_intercepts = Exponential(0.15, -0.5, 0.1) net.inhibit = nengo.Ensemble(20, dimensions=1, intercepts=i_intercepts, encoders=Choice([[1]])) nengo.Connection(net.inhibit.neurons, net.osc.neurons, transform=-np.ones((n_per_d * 2, 20))) # --- Disinhibit when appropriate net.disinhibit = nengo.Node(size_in=1) nengo.Connection(net.disinhibit, net.inhibit, transform=-1) return net
def make_thresh_ens_net(self, threshold=0.5, thresh_func=lambda x: 1, exp_scale=None, num_ens=1, net=None, **args): if net is None: label_str = args.get('label', 'Threshold_Ens_Net') net = nengo.Network(label=label_str) if exp_scale is None: exp_scale = (1 - threshold) / 10.0 with net: ens_args = dict(args) ens_args['n_neurons'] = args.get('n_neurons', self.n_neurons_ens) ens_args['dimensions'] = args.get('dimensions', 1) ens_args['intercepts'] = \ Exponential(scale=exp_scale, shift=threshold, high=1) ens_args['encoders'] = Choice([[1]]) ens_args['eval_points'] = Uniform(min(threshold + 0.1, 1.0), 1.1) ens_args['n_eval_points'] = 5000 net.input = nengo.Node(size_in=num_ens) net.output = nengo.Node(size_in=num_ens) for i in range(num_ens): thresh_ens = nengo.Ensemble(**ens_args) nengo.Connection(net.input[i], thresh_ens, synapse=None) nengo.Connection(thresh_ens, net.output[i], function=thresh_func, synapse=None) return net
def make_mem_network(net, n_neurons, dimensions, make_mem_func, make_mem_args, make_diff_func, make_diff_args, mem_synapse=0.1, fdbk_transform=1.0, input_transform=1.0, difference_gain=1.0, gate_gain=3): with net: net.input = nengo.Node(size_in=dimensions) # integrator to store value if np.isscalar(fdbk_transform): fdbk_matrix = np.eye(dimensions) * fdbk_transform else: fdbk_matrix = np.matrix(fdbk_transform) net.mem = make_mem_func(n_neurons=n_neurons, dimensions=dimensions, label="mem", **make_mem_args) if isinstance(net.mem, nengo.Network): mem_output = net.mem.output mem_input = net.mem.input else: mem_output = mem_input = net.mem nengo.Connection(mem_output, mem_input, synapse=mem_synapse, transform=fdbk_matrix) # calculate difference between stored value and input net.diff = make_diff_func(n_neurons=n_neurons, dimensions=dimensions, label="Diff", **make_diff_args) if isinstance(net.diff, nengo.Network): net.diff_input = net.diff.input diff_output = net.diff.output else: net.diff_input = diff_output = net.diff nengo.Connection(net.input, net.diff_input, synapse=None, transform=net.input_transform) nengo.Connection(mem_output, net.diff_input, transform=-1) # feed difference into integrator nengo.Connection(diff_output, mem_input, transform=difference_gain, synapse=mem_synapse) # gate difference (if gate==0, update stored value, # otherwise retain stored value) # Note: A node is used for the input to make reset circuit more # straightforward net.gate = nengo.Ensemble(n_neurons, 1, encoders=Choice([[1]]), intercepts=Exponential(0.15, 0.5, 1), label='Gate') if isinstance(net.diff, nengo.Network): for e in net.diff.ensembles: nengo.Connection(net.gate, e.neurons, transform=[[-gate_gain]] * e.n_neurons) else: nengo.Connection(net.gate, net.diff.neurons, transform=[[-gate_gain]] * e.n_neurons) # Make output net.output = net.mem.output
def test_argreprs(): def check_init_args(cls, args): assert getfullargspec(cls.__init__).args[1:] == args def check_repr(obj): assert eval(repr(obj)) == obj check_init_args(PDF, ['x', 'p']) check_repr(PDF([1, 2, 3], [0.1, 0.8, 0.1])) check_init_args(Uniform, ['low', 'high', 'integer']) check_repr(Uniform(1, 3)) check_repr(Uniform(1, 4, integer=True)) check_init_args(Gaussian, ['mean', 'std']) check_repr(Gaussian(0, 2)) check_init_args(Exponential, ['scale', 'shift', 'high']) check_repr(Exponential(2.)) check_repr(Exponential(2., shift=0.1)) check_repr(Exponential(2., shift=0.1, high=10.)) check_init_args(UniformHypersphere, ['surface', 'min_magnitude']) check_repr(UniformHypersphere()) check_repr(UniformHypersphere(surface=True)) check_repr(UniformHypersphere(min_magnitude=0.3)) check_init_args(Choice, ['options', 'weights']) check_repr(Choice([3, 2, 1])) check_repr(Choice([3, 2, 1], weights=[0.1, 0.2, 0.7])) check_init_args(Samples, ['samples']) check_repr(Samples([3, 2, 1])) check_init_args(SqrtBeta, ['n', 'm']) check_repr(SqrtBeta(3)) check_repr(SqrtBeta(3, m=2)) check_init_args(SubvectorLength, ['dimensions', 'subdimensions']) check_repr(SubvectorLength(6)) check_repr(SubvectorLength(6, 2)) check_init_args(CosineSimilarity, ['dimensions']) check_repr(CosineSimilarity(6))
def test_argreprs(): def check_init_args(cls, args): assert getfullargspec(cls.__init__).args[1:] == args def check_repr(obj): assert eval(repr(obj)) == obj check_init_args(PDF, ["x", "p"]) check_repr(PDF([1, 2, 3], [0.1, 0.8, 0.1])) check_init_args(Uniform, ["low", "high", "integer"]) check_repr(Uniform(1, 3)) check_repr(Uniform(1, 4, integer=True)) check_init_args(Gaussian, ["mean", "std"]) check_repr(Gaussian(0, 2)) check_init_args(Exponential, ["scale", "shift", "high"]) check_repr(Exponential(2.0)) check_repr(Exponential(2.0, shift=0.1)) check_repr(Exponential(2.0, shift=0.1, high=10.0)) check_init_args(UniformHypersphere, ["surface", "min_magnitude"]) check_repr(UniformHypersphere()) check_repr(UniformHypersphere(surface=True)) check_repr(UniformHypersphere(min_magnitude=0.3)) check_init_args(Choice, ["options", "weights"]) check_repr(Choice([3, 2, 1])) check_repr(Choice([3, 2, 1], weights=[0.1, 0.2, 0.7])) check_init_args(Samples, ["samples"]) check_repr(Samples([3, 2, 1])) check_init_args(SqrtBeta, ["n", "m"]) check_repr(SqrtBeta(3)) check_repr(SqrtBeta(3, m=2)) check_init_args(SubvectorLength, ["dimensions", "subdimensions"]) check_repr(SubvectorLength(6)) check_repr(SubvectorLength(6, 2)) check_init_args(CosineSimilarity, ["dimensions"]) check_repr(CosineSimilarity(6))
def default_ens_config(self): """(Config) Defaults for other ensemble creation.""" cfg = nengo.Config(nengo.Ensemble) cfg[nengo.Ensemble].update({ 'radius': 1, 'intercepts': Exponential(self.exp_scale, 0.0, 1.0), 'encoders': Choice([[1]]), 'eval_points': Uniform(0.0, 1.0), 'n_eval_points': self.n_eval_points, }) return cfg
def am_ens_config(self): """(Config) Defaults for associative memory ensemble creation.""" cfg = nengo.Config(nengo.Ensemble, nengo.Connection) cfg[nengo.Ensemble].update({ 'radius': 1, 'intercepts': Exponential(self.exp_scale, 0.0, 1.0), 'encoders': Choice([[1]]), 'eval_points': Uniform(0.0, 1.0), 'n_eval_points': self.n_eval_points, }) cfg[nengo.Connection].synapse = None return cfg
def build_classifier(self, net): intercepts = Exponential(0.15, self.classifier.reset_th, 1.0) net.classifier = nengo.Ensemble(20, dimensions=1, encoders=Choice([[1]]), neuron_type=nengo.AdaptiveLIF(), intercepts=intercepts) for dmp in net.syllables: transform = -self.classifier.inhib_scale * np.ones( (dmp.state.n_neurons, net.classifier.n_neurons)) nengo.Connection(net.classifier.neurons, dmp.state.neurons, transform=transform, synapse=0.01)
def default_ens_config(self): """(Config) Defaults for other ensemble creation.""" cfg = Config(Ensemble) cfg[Ensemble].update({ "radius": 1, "intercepts": Exponential(self.exp_scale, 0.0, 1.0), "encoders": Choice([[1]]), "eval_points": Uniform(0.0, 1.0), "n_eval_points": self.n_eval_points, }) return cfg
def am_ens_config(self): """(Config) Defaults for associative memory ensemble creation.""" cfg = Config(Ensemble, Connection) cfg[Ensemble].update({ "radius": 1, "intercepts": Exponential(self.exp_scale, 0.0, 1.0), "encoders": Choice([[1]]), "eval_points": Uniform(0.0, 1.0), "n_eval_points": self.n_eval_points, }) cfg[Connection].synapse = None return cfg
def InverseDMP(n_per_d, forcing_f, scale=0.7, reset_scale=2.5, similarity_th=0.6, tau=0.05, net=None): if net is None: net = nengo.Network(label="Inverse Rhythmic DMP") obs_dims = forcing_f(0.).size state_dims = 1 dims = state_dims + obs_dims with net: net.input = nengo.Node(size_in=obs_dims) # --- iDMP state contains system state and observations net.state = nengo.Ensemble(n_per_d * dims, dimensions=dims, n_eval_points=10000, radius=1.4) nengo.Connection(net.input, net.state[1:], synapse=None) # --- Update state based on the current observation f = ff_inv(forcing_f, thresh=similarity_th, scale=scale) nengo.Connection(net.state, net.state[0], function=f, synapse=tau) # --- Reset system state net.reset = nengo.Node(size_in=1) d_intercepts = Exponential(0.15, -0.5, 0.1) net.diff_inhib = nengo.Ensemble(20, dimensions=1, intercepts=d_intercepts, encoders=Choice([[1]])) net.diff = nengo.Ensemble(n_per_d, dimensions=1) nengo.Connection(net.reset, net.diff_inhib, transform=-1, synapse=None) nengo.Connection(net.diff_inhib.neurons, net.diff.neurons, transform=-np.ones((n_per_d, 20))) nengo.Connection(net.state[0], net.diff) nengo.Connection(net.diff, net.state[0], transform=-reset_scale) return net
def DMP(n_per_d, c, forcing_f, tau=0.05, net=None): if net is None: net = nengo.Network(label="DMP") out_dims = forcing_f(0.).size with net: # --- Decode forcing_f from oscillator net.state = nengo.Ensemble(n_per_d, dimensions=1, label=forcing_f.__name__) nengo.Connection(net.state, net.state, synapse=tau) net.output = nengo.Node(size_in=out_dims) nengo.Connection(net.state, net.output, function=forcing_f, synapse=None) # --- Input is a small biased oscillator to ramp up smoothly net.osc = nengo.Ensemble(n_per_d * 2, dimensions=2, radius=0.01) nengo.Connection(net.osc, net.osc, transform=np.array([[2, -1], [1, 2]])) # actually? nengo.Connection(net.osc, net.state, transform=c, function=lambda x: x[0] + 0.5) # Kick start the oscillator kick = nengo.Node(lambda t: 1 if t < 0.1 else 0) nengo.Connection(kick, net.osc, transform=np.ones((2, 1))) # --- Inhibit the state by default i_intercepts = Exponential(0.15, -0.5, 0.1) net.inhibit = nengo.Ensemble(20, dimensions=1, intercepts=i_intercepts, encoders=Choice([[1]])) nengo.Connection(net.inhibit.neurons, net.state.neurons, transform=-np.ones((n_per_d, 20))) # --- Disinhibit when appropriate net.disinhibit = nengo.Node(size_in=1) nengo.Connection(net.disinhibit, net.inhibit, transform=-1) return net
def ThresholdingEnsembles(threshold, intercept_width=0.15, radius=1.0): """Configuration preset for a thresholding ensemble. This preset adjust ensemble parameters for thresholding. The ensemble's neurons will only fire for values above threshold. One can either decode the represented value (if it is above the threshold) or decode a step function if binary classification is desired. This preset: - Sets intercepts to be between ``threshold`` and ``radius`` with an exponential distribution (shape parameter of ``intercept_width``). This clusters intercepts near the threshold for better approximation. - Sets encoders to 1. - Sets evaluation points to be uniformly distributed between ``threshold`` and ``radius``. - Sets the radius. Parameters ---------- threshold : float Point at which ensembles should start firing. intercept_width : float, optional Controls how widely distributed the intercepts are. Smaller values give more clustering at the threshold, larger values give a more uniform distribution. radius : float, optional Ensemble radius. Returns ------- `nengo.Config` Configuration with presets. """ config = Config(Ensemble) config[Ensemble].radius = radius config[Ensemble].intercepts = Exponential(intercept_width, threshold, radius) config[Ensemble].encoders = Choice([[1]]) config[Ensemble].eval_points = Uniform(threshold / radius, 1) return config
def build_syllables(self, net): assert len(self.syllables) > 0, "No syllables added" # Make a readout for the production info coming from the DMPs intercepts = Exponential(0.15, self.production_info.threshold, 1) net.production_info = EnsembleArray(self.production_info.n_per_d, n_ensembles=48, encoders=Choice([[1]]), intercepts=intercepts, radius=1.1) net.syllables = [] dt = self.trial.dt for syllable in self.syllables: forcing_f, gesture_ix = traj2func(syllable.trajectory, dt=dt) forcing_f.__name__ = syllable.label dmp = RhythmicDMP(forcing_f=forcing_f, freq=syllable.freq, **self.syllable.kwargs()) nengo.Connection(dmp.output, net.production_info.input[gesture_ix]) net.syllables.append(dmp)
def DeadzoneDMP(n_per_d, freq, forcing_f, tau=0.025, deadzone=0.6 * np.pi, net=None): if net is None: net = nengo.Network(label="Rhythmic DMP") out_dims = forcing_f(0.).size omega = freq * tau * 2 * np.pi theta = UniformWithDeadzone(deadzone=deadzone).sample(n_per_d * 2, 1) encoders = np.hstack([np.cos(theta), np.sin(theta)]) with net: # --- Decode forcing_f from oscillator net.osc = nengo.Ensemble(n_per_d * 2, dimensions=2, intercepts=Exponential(0.15, 0.3, 0.6), encoders=encoders, label=forcing_f.__name__) nengo.Connection(net.osc, net.osc, synapse=tau, function=zone(deadzone), transform=[[1, -omega], [omega, 1]]) net.output = nengo.Node(size_in=out_dims) nengo.Connection(net.osc, net.output, function=radial_f(forcing_f, deadzone), synapse=None) # --- Kick start the oscillator net.kick = nengo.Node(size_in=1) nengo.Connection(net.kick, net.osc, transform=[[-1], [0.05]], synapse=None) return net
def Sequencer(n_per_d, timer_tau=0.05, timer_freq=2., reset_time=0.7, reset_threshold=0.5, reset_to_gate=-0.7, gate_threshold=0.4, net=None): if net is None: net = nengo.Network(label="Syllable sequencer") omega = timer_freq * timer_tau * 2 * np.pi with net: # --- Aggregate all the syllable DMPs to keep track of time net.timer = nengo.Ensemble(n_per_d * 2, dimensions=2) # --- Reset all DMPs at the right time r_intercepts = Exponential(0.15, reset_threshold, 1) net.reset = nengo.Ensemble(60, dimensions=1, encoders=Choice([[1]]), intercepts=r_intercepts) def reset_f(x): if x > reset_time: return 1. elif x < 0.05: return 0.8 else: return 0 nengo.Connection(net.timer, net.reset, function=radial_f(reset_f)) # There's a catch 22 here. The reset forces the DMPs to a specific # point in state space, but that space is also where the timer starts # resetting DMPs. If we do nothing, then the DMPs will just stop. # To keep things moving, when the reset is active, we allow the # timer to oscillate on its own, rather than just using DMP input. # --- Make `timer` oscillate when reset is active net.timer_recur = nengo.Ensemble(n_per_d * 2, dimensions=2) nengo.Connection(net.timer, net.timer_recur) nengo.Connection(net.timer_recur, net.timer, synapse=timer_tau, transform=[[1, -omega], [omega, 1]]) # Usually, the recurrent ensemble is inhibited inh_intercepts = Exponential(0.15, -0.5, 0.1) net.tr_inhibit = nengo.Ensemble(20, dimensions=1, encoders=Choice([[1]]), intercepts=inh_intercepts) nengo.Connection(net.tr_inhibit.neurons, net.timer_recur.neurons, transform=-np.ones((n_per_d * 2, 20))) # But the reset disinhibits the recurrence nengo.Connection(net.reset, net.tr_inhibit, transform=-1) # --- `gate` switches the working memory representations g_intercepts = Exponential(0.15, gate_threshold, 1) net.gate = nengo.Ensemble(60, dimensions=1, encoders=Choice([[1]]), intercepts=g_intercepts) # Gate is normally always active net.gate_bias = nengo.Node(output=1) nengo.Connection(net.gate_bias, net.gate, synapse=None) # Reset inhibits gate, causing switch to next syllable nengo.Connection(net.reset, net.gate, transform=reset_to_gate) return net
def init_module(self): bias_node = nengo.Node(output=1) # ---------------------- Inputs and outputs ------------------------- # # Motor SP input node self.motor_sp_in = nengo.Node(size_in=vocab.mtr_dim) # Motor bypass signal (runs the ramp, but doesn't output to the arm) self.motor_bypass = cfg.make_thresh_ens_net() # Motor init signal self.motor_init_vis = cfg.make_thresh_ens_net(0.75, n_neurons=100) self.motor_init_ps_task = cfg.make_thresh_ens_net(0.75, n_neurons=100) self.motor_init_ps_dec = cfg.make_thresh_ens_net(0.75, n_neurons=100) # --------------- MOTOR SIGNALLING SYSTEM (STOP / GO) -------------- # Motor go signal self.motor_go = nengo.Ensemble(cfg.n_neurons_ens, 1) nengo.Connection(bias_node, self.motor_go) # Motor stop signal self.motor_stop_input = cfg.make_thresh_ens_net() nengo.Connection(bias_node, self.motor_stop_input.input, synapse=None) nengo.Connection(self.motor_stop_input.output, self.motor_go.neurons, transform=[[-3]] * cfg.n_neurons_ens) # --------------- MOTOR SIGNALLING SYSTEM (RAMP SIG) -------------- self.ramp_sig = Ramp_Signal_Network() # Signal used to drive the ramp (the constant input signal) nengo.Connection(self.motor_go, self.ramp_sig.ramp, transform=cfg.mtr_ramp_synapse * cfg.mtr_ramp_scale, synapse=cfg.mtr_ramp_synapse) # Signal to hold the ramp reset as long as the motor system is still # initializing (e.g. arm is still going to INIT target) # nengo.Connection(self.motor_init.output, self.ramp_sig.init, # transform=1.75, synapse=0.015) nengo.Connection(self.motor_init_vis.output, self.ramp_sig.init, transform=3, synapse=0.008) # nengo.Connection(self.motor_init_vis.output, self.ramp_sig.init, # transform=-6, synapse=0.05) nengo.Connection(self.motor_init_ps_task.output, self.ramp_sig.init, transform=5, synapse=0.008) nengo.Connection(self.motor_init_ps_task.output, self.ramp_sig.init, transform=-6.5, synapse=0.05) nengo.Connection(self.motor_init_ps_dec.output, self.ramp_sig.init, transform=5, synapse=0.008) nengo.Connection(self.motor_init_ps_dec.output, self.ramp_sig.init, transform=-6.5, synapse=0.05) # Stop the ramp from starting if the stop command has been given nengo.Connection(self.motor_stop_input.output, self.ramp_sig.go, transform=-1) # --------------- FUNCTION REPLICATOR SYSTEM -------------- mtr_func_dim = vocab.mtr_dim // 2 func_eval_net = DiffFuncEvaltr(mtr_func_dim, mtr_data.sp_scaling_factor, 2) func_eval_net.make_inhibitable(-5) nengo.Connection(self.ramp_sig.ramp, func_eval_net.func_input) nengo.Connection(self.motor_bypass.output, func_eval_net.inhibit) # Motor path x information - Note conversion to difference of points # from path of points nengo.Connection(self.motor_sp_in[:mtr_func_dim], func_eval_net.diff_func_pts[0]) nengo.Connection(self.motor_sp_in[:mtr_func_dim - 1], func_eval_net.diff_func_pts[0][1:], transform=-1) # Motor path y information - Note conversion to difference of points # from path of points nengo.Connection(self.motor_sp_in[mtr_func_dim:], func_eval_net.diff_func_pts[1]) nengo.Connection(self.motor_sp_in[mtr_func_dim:-1], func_eval_net.diff_func_pts[1][1:], transform=-1) # --------------- MOTOR ARM CONTROL ----------------- arm_obj = cfg.mtr_arm_class() if arm_obj is not None: arm_rest_coord = np.array( arm_obj.position(q=arm_obj.rest_angles, ee_only=True)) # Note: arm_rest_coord is only used for initialization & startup # transients arm_node = nengo.Node(output=lambda t, x, dt=cfg.sim_dt, arm= arm_obj: arm.apply_torque(x, dt), size_in=arm_obj.DOF) kp = mtr_data.kp if cfg.mtr_kp is None else cfg.mtr_kp kv1 = mtr_data.kv1 if cfg.mtr_kv1 is None else cfg.mtr_kv1 kv2 = mtr_data.kv2 if cfg.mtr_kv2 is None else cfg.mtr_kv2 ctrl_obj = Controller(dt=cfg.sim_dt, arm=arm_obj, kp=kp, kv=kv1, kv2=kv2, init_target=arm_rest_coord, arm_class_name=cfg.mtr_arm_type) # Make the osc control ctrl_net = ctrl_obj.initialize_model() self.ctrl_net = ctrl_net # Connect output of motor path evaluator to ctrl_net nengo.Connection(func_eval_net.func_output, ctrl_net.target, synapse=0.01) # Add bias values to the motor path evaluator output (to shift the # drawn digit into the drawing box of the arm) nengo.Connection(bias_node, ctrl_net.target, transform=[[cfg.mtr_arm_rest_x_bias], [cfg.mtr_arm_rest_y_bias]], synapse=None) # Feed the torque control signal to the arm nengo.Connection(ctrl_net.output, arm_node) if cfg.mtr_dyn_adaptation: # Arm state arm_state = nengo.Node( output=lambda t, arm=arm_obj: np.hstack([arm.q, arm.dq])) # Create ensemble for arm dynamics adaptation adapt_ens = nengo.Ensemble(cfg.mtr_dyn_adaptation_n_neurons, arm_obj.DOF * 2) # Get information from the arm and connect to learning ensemble nengo.Connection( arm_state, adapt_ens, function=lambda x: ctrl_obj.config.CB_scaledown(x)) # Make the learning connection adapt_conn = nengo.Connection( adapt_ens, arm_node, function=lambda x: [0, 0, 0], learning_rule_type=nengo.PES( cfg.mtr_dyn_adaptation_learning_rate), synapse=cfg.mtr_dyn_adaptation_synapse) nengo.Connection(ctrl_net.output, adapt_conn.learning_rule, transform=-1) self.adapt_conn = adapt_conn self.adapt_ens = adapt_ens # ------ MOTOR ARM FORCE FIELD ADDITION ------ forcefield_obj = getattr(forcefield, cfg.mtr_forcefield)(arm_obj) forcefield_node = nengo.Node( size_in=arm_obj.DOF, output=lambda t, x: forcefield_obj.generate(x)) nengo.Connection(ctrl_net.output, forcefield_node, synapse=None) nengo.Connection(forcefield_node, arm_node, synapse=cfg.mtr_forcefield_synapse) self.ff_node = forcefield_node # ------ ARM ZERO-CENTERED END EFFECTOR POSITION ------ # ## Note: ctrl_net already has an internal node that gets info # from arm_obj (i.e. state information). So an external # connection is not required zero_centered_arm_ee_loc = \ nengo.Node(output=lambda t, bias=np.array([cfg.mtr_arm_rest_x_bias, cfg.mtr_arm_rest_y_bias]), arm=arm_obj: arm.x - bias, label='Centered Arm EE') # ------ MOTOR ARM CONTROL SIGNAL FEEDBACK ------ # X to target norm calculation target_thresh = cfg.mtr_tgt_threshold target_diff_norm = \ nengo.Ensemble(150, 2, intercepts=Exponential(0.09, target_thresh, target_thresh * 2), radius=target_thresh * 2) nengo.Connection(func_eval_net.func_output, target_diff_norm, synapse=0.01) if arm_obj is not None: nengo.Connection(zero_centered_arm_ee_loc, target_diff_norm, transform=-1, synapse=0.01) else: nengo.Connection(func_eval_net.func_output, target_diff_norm, synapse=0.01) target_diff_norm_thresh = cfg.make_thresh_ens_net() nengo.Connection(target_diff_norm, target_diff_norm_thresh.input, function=lambda x: (np.sqrt(x[0]**2 + x[1]**2)) > 0, transform=2) # When the target != arm ee position, stop the ramp signal from # starting (reaching to start position) # nengo.Connection(target_diff_norm, self.ramp_sig.go, transform=-6, # function=lambda x: # (np.sqrt(x[0] ** 2 + x[1] ** 2)) > 0, # synapse=0.01) nengo.Connection(target_diff_norm_thresh.output, self.ramp_sig.go, transform=-2, synapse=0.01) # When the target != arm ee position, stop the ramp signal from # stopping (reaching to end position) # nengo.Connection(target_diff_norm, self.ramp_sig.end, # transform=-8, # function=lambda x: # (np.sqrt(x[0] ** 2 + x[1] ** 2)) > 0, # synapse=0.01) nengo.Connection(target_diff_norm_thresh.output, self.ramp_sig.end, transform=-2, synapse=0.01) # Disable the target_diff_norm when motor bypass nengo.Connection(self.motor_bypass.output, target_diff_norm.neurons, transform=[[-10.0]] * target_diff_norm.n_neurons, synapse=0.01) if arm_obj is None: # Disable the target_diff_norm when there is no arm object nengo.Connection(bias_node, target_diff_norm.neurons, transform=[[-10.0]] * target_diff_norm.n_neurons, synapse=0.01) # ------ MOTOR PEN DOWN CONTROL ------ pen_down = cfg.make_thresh_ens_net() # Pen is down by default nengo.Connection(bias_node, pen_down.input) # Cases when the pen should NOT be down nengo.Connection(self.ramp_sig.reset_hold, pen_down.input, transform=-1) nengo.Connection(self.ramp_sig.stop, pen_down.input, transform=-1) nengo.Connection(self.motor_stop_input.output, pen_down.input, transform=-1, synapse=0.05) nengo.Connection(self.motor_bypass.output, pen_down.input, transform=-1) # Pen down signal feedback to rest of motor system (tells the ramp to # keep going, and the ctrl_net to use only kv1) nengo.Connection(pen_down.output, self.ramp_sig.go, transform=12) if arm_obj is not None: nengo.Connection(pen_down.output, ctrl_net.CB2_inhibit) # --------------- For external probes --------------- self.ramp_int_stop = self.ramp_sig.stop # Motor target output self.mtr_path_func_out = nengo.Node(size_in=2) nengo.Connection(func_eval_net.diff_func_outputs[0], self.mtr_path_func_out[0], transform=np.ones((1, mtr_func_dim))) nengo.Connection(func_eval_net.diff_func_outputs[1], self.mtr_path_func_out[1], transform=np.ones((1, mtr_func_dim))) # Arm segments joint locations if arm_obj is not None: self.arm_px_node = \ nengo.Node(output=lambda t, arm=arm_obj: arm.position()[0]) self.arm_py_node = \ nengo.Node(output=lambda t, arm=arm_obj: arm.position()[1]) else: self.arm_px_node = nengo.Node(0) self.arm_py_node = nengo.Node(0) # Arm ee zero_centered location if arm_obj is not None: self.zero_centered_arm_ee_loc = zero_centered_arm_ee_loc # Target ee zero_centered location self.zero_centered_tgt_ee_loc = func_eval_net.func_output # Pen down status self.pen_down = pen_down.output # Ramp signal outputs self.ramp = self.ramp_sig.ramp self.ramp_init_hold = self.ramp_sig.init_hold self.ramp_reset_hold = self.ramp_sig.reset_hold self.ramp_50_75 = self.ramp_sig.ramp_50_75 # Decode index (number) bypass to monitor module self.dec_ind = nengo.Node(size_in=len(vocab.mtr.keys) + 1) # Target diff norm # self.target_diff_norm_out = nengo.Node(size_in=1) # nengo.Connection(target_diff_norm, self.target_diff_norm_out, # transform=-6, # function=lambda x: # (np.sqrt(x[0] ** 2 + x[1] ** 2)) > 0, # synapse=0.01) self.target_diff_norm_out = target_diff_norm_thresh.output # ################ DEBUG CODE ################### # motor_init signal self.motor_init = nengo.Node(size_in=1) # nengo.Connection(self.motor_init_vis.output, self.motor_init) # nengo.Connection(self.motor_init_ps_task.output, self.motor_init) # nengo.Connection(self.motor_init_ps_dec.output, self.motor_init) nengo.Connection(self.motor_init_vis.output, self.motor_init, transform=3, synapse=0.0035) nengo.Connection(self.motor_init_ps_task.output, self.motor_init, transform=5, synapse=0.0035) nengo.Connection(self.motor_init_ps_task.output, self.motor_init, transform=-6, synapse=0.05) nengo.Connection(self.motor_init_ps_dec.output, self.motor_init, transform=5, synapse=0.0035) nengo.Connection(self.motor_init_ps_dec.output, self.motor_init, transform=-6, synapse=0.05) self.arm_state = nengo.Node( output=lambda t, arm=arm_obj: np.hstack([arm.q, arm.dq])) self.arm_dq = nengo.Node(output=lambda t, arm=arm_obj: arm.dq)
def make_route_connections_common(net, n_neurons, dimensions, num_items, make_ens_func, gate_gain, default_sel, threshold_sel_in, **ens_args): with net: bias_node = nengo.Node(output=1) net.sel_none = nengo.Ensemble(20, 1) nengo.Connection(bias_node, net.sel_none, synapse=None) for n in range(num_items): sel_node = nengo.Node(size_in=1) sel_in = sel_node ens = make_ens_func(n_neurons=n_neurons, dimensions=dimensions, label='Gate %d' % n, **ens_args) if threshold_sel_in: sel_node = nengo.Ensemble(50, 1, intercepts=Exponential( 0.05, 0.25, 0.5), encoders=Choice([[1]]), label='Sel In %d' % n) nengo.Connection(sel_in, sel_node, synapse=None) nengo.Connection(sel_node, net.sel_none.neurons, transform=([[-gate_gain]] * net.sel_none.n_neurons)) if isinstance(ens, nengo.Network): if n != default_sel: for e in ens.all_ensembles: nengo.Connection(net.sel_none, e.neurons, transform=[[-gate_gain]] * e.n_neurons) for sn in net.sel_nodes: for e in ens.all_ensembles: nengo.Connection(sn, e.neurons, transform=[[-gate_gain]] * e.n_neurons) for ee in net.ens_elements: for e in ee.all_ensembles: nengo.Connection(sel_node, e.neurons, transform=[[-gate_gain]] * e.n_neurons) else: if n != default_sel: nengo.Connection(net.sel_none, ens.neurons, transform=[[-gate_gain]] * ens.n_neurons) for sn in net.sel_nodes: nengo.Connection(sn, ens.neurons, transform=[[-gate_gain]] * ens.n_neurons) for ee in net.ens_elements: nengo.Connection(sel_node, ee.neurons, transform=[[-gate_gain]] * ee.n_neurons) net.ens_elements.append(ens) net.sel_nodes.append(sel_node) setattr(net, 'sel%i' % n, sel_in) setattr(net, 'ens%i' % n, ens)
def __init__(self, n_neurons, dimensions, vocab, radius=None, gate_mode=1, reset_mode=3, cleanup_mode=0, cleanup_keys=None, reset_key=None, threshold_gate_in=False, gate_threshold=0.5, label=None, seed=None, add_to_container=None, **mem_args): super(MemoryBlock, self).__init__(label, seed, add_to_container) if radius is None: radius = 3.5 / np.sqrt(dimensions) if n_neurons == nengo.Default: n_neurons = 100 if cleanup_keys is not None: cleanup_vecs = vocab.create_subset(cleanup_keys).vectors elif cleanup_mode != 0: cleanup_vecs = vocab.vectors else: cleanup_vecs = None if isinstance(reset_key, str): reset_vec = vocab.parse(reset_key).v elif (not isinstance(reset_key, (np.ndarray, np.generic)) and reset_key is not None and reset_key != 0): reset_vec = np.ones(dimensions) * reset_key else: reset_vec = reset_key if reset_key is None: reset_mode = 0 with self: # cleanup_mode: # - 0 (or cleanup_vecs == None): No cleanup # - 1: Cleanup memory vectors using provided vectors. Only store # vectors that can be cleaned up. # - 2: Cleanup memory vectors using provided vectors but also allow # vectors that do not match any of the cleanup vectors to be # stored as well. wm_args = dict(mem_args) if cleanup_vecs is None or cleanup_mode == 0: self.mem1 = WM(n_neurons, dimensions, radius=radius, reset_value=reset_vec, **wm_args) wm_args.pop('input_transform', None) wm_args.pop('wta_output', None) self.mem2 = WM(n_neurons, dimensions, radius=radius, reset_value=reset_vec, **wm_args) elif cleanup_mode == 1: self.mem1 = WMC(n_neurons, dimensions, radius=radius, cleanup_values=cleanup_vecs, reset_value=reset_vec, **wm_args) wm_args.pop('input_transform', None) wm_args.pop('wta_output', None) self.mem2 = WMC(n_neurons, dimensions, radius=radius, cleanup_values=cleanup_vecs, reset_value=reset_vec, **wm_args) else: self.mem1 = WMCP(n_neurons, dimensions, radius=radius, cleanup_values=cleanup_vecs, reset_value=reset_vec, **wm_args) wm_args.pop('input_transform', None) wm_args.pop('wta_output', None) self.mem2 = WMCP(n_neurons, dimensions, radius=radius, cleanup_values=cleanup_vecs, reset_value=reset_vec, **wm_args) # Create gating threshold ensembles if needed. Note that the these # are needed if the WM network does not have its own gating # threshold population. The gating signal needs to be dead-zero # (no neural activity) when WM is non-gated. if threshold_gate_in: gate1 = nengo.Ensemble(n_neurons, 1, label="gate1", intercepts=Exponential(0.15, 0.5, 1), encoders=Choice([[1]])) nengo.Connection(gate1, self.mem1.gate) gate2 = nengo.Ensemble(n_neurons, 1, label="gate2", intercepts=Exponential(0.15, 0.5, 1), encoders=Choice([[1]])) nengo.Connection(gate2, self.mem2.gate) else: gate1 = self.mem1.gate gate2 = self.mem2.gate # Gate_modes: # - 1: Gate mem1 on gate high, gate mem2 on gate low (default) # - 2: Gate mem1 on gate low, gate mem2 on gate high if gate_mode == 1: self.gateX = gate1 self.gateN = gate2 else: self.gateX = gate2 self.gateN = gate1 self.gate = nengo.Node(size_in=1, label="gate") bias_node = nengo.Node(output=1) # Adjust gate thresholds nengo.Connection(bias_node, self.gateX, transform=0.5 - gate_threshold) nengo.Connection(bias_node, self.gateN, transform=gate_threshold - 0.5) nengo.Connection(self.gate, self.gateX) nengo.Connection(self.gate, self.gateN, transform=-1) nengo.Connection(bias_node, self.gateN) # Reset_modes: # - 1: Reset only mem1 # - 2: Reset only mem2 # - 3: Reset both mem1 and mem2 if reset_mode: self.reset = \ nengo.Ensemble(n_neurons, 1, label="reset", intercepts=Exponential(0.15, 0.5, 1), encoders=Choice([[1]])) if reset_mode & 1: nengo.Connection(self.reset, self.mem1.reset, synapse=0.005) if reset_mode & 2: nengo.Connection(self.reset, self.mem2.reset, synapse=0.005) nengo.Connection(self.mem1.output, self.mem2.input, synapse=0.005) # Input and output nodes self.input = self.mem1.input self.output = self.mem2.output # Configure SPA default input and output vocabularies self.inputs = dict(default=(self.input, vocab)) self.outputs = dict(default=(self.output, vocab))
def make_resettable(net, n_neurons, dimensions, reset_value, make_reset_func, make_reset_args, gate_gain=3): # Why have all this extra hardware to reset WM when inhibition will do # it? # - Makes the reset more reliable (storing a zero, rather than just # wiping it out), so that reset signal can be shorter. if np.isscalar(reset_value): reset_value = np.matrix(np.ones(dimensions) * reset_value) else: reset_value = np.matrix(reset_value) with net: bias = nengo.Node(output=1, label='bias') net.reset = nengo.Node(size_in=1, label='reset') # Create resetX and resetN signals. resetX is to disable the WM gate # signal when reset is high. resetN is to disable the reset circuitry # when reset is low. resetX = nengo.Ensemble(n_neurons, 1, encoders=Choice([[1]]), intercepts=Exponential(0.15, 0.5, 1), label='ResetX') resetN = nengo.Ensemble(n_neurons, 1, encoders=Choice([[1]]), intercepts=Exponential(0.15, 0.75, 1), label='ResetN') resetN_delay = nengo.Ensemble(n_neurons, 1, encoders=Choice([[1]]), intercepts=Exponential(0.15, 0.75, 1), label='ResetN delay') nengo.Connection(net.reset, resetX, synapse=None) # nengo.Connection(net.reset, resetN, transform=-1, synapse=None) nengo.Connection(resetX, resetN, transform=-gate_gain) # Note: gate_gain transform is to match net.gate -- so that the 'turn # off' time is identical to net.gate nengo.Connection(bias, resetN, synapse=None) nengo.Connection(resetN, resetN_delay, synapse=0.01) # THe reset gate. Controls reset information going into the difference # population reset_gate = make_reset_func(n_neurons=n_neurons, dimensions=dimensions, label="Reset gate", **make_reset_args) if isinstance(reset_gate, nengo.Network): reset_gate_input = reset_gate.input reset_gate_output = reset_gate.output else: reset_gate_input = reset_gate_output = reset_gate # The desired reset value. nengo.Connection(bias, reset_gate_input, transform=reset_value.T, synapse=None) # Need to negate whatever is being fed to the input of the WM (since # the WM difference population is going to be enabled). nengo.Connection(net.input, reset_gate_input, transform=-net.input_transform, synapse=None) nengo.Connection(reset_gate_output, net.diff_input) # Note: Synapse is there to give slight delay for reset signal (to # gate) to dissipate. # Enable the WM difference population nengo.Connection(resetX, net.gate, transform=-gate_gain) # Disable the reset gate when reset signal is not active. if isinstance(reset_gate, nengo.Network): for e in reset_gate.ensembles: nengo.Connection(resetN_delay, e.neurons, transform=[[-gate_gain]] * e.n_neurons) else: nengo.Connection(resetN_delay, reset_gate.neurons, transform=[[-gate_gain]] * e.n_neurons)
def init_module(self): bias_node = nengo.Node(1) # Number of actions in this spaun setup num_actions = experiment.num_learn_actions # ------------------- Action detection network ------------------------ # Translates self.action_input = nengo.Node(size_in=num_actions) self.bg_utilities_input = nengo.Node(size_in=num_actions) self.vis_sp_input = nengo.Node(size_in=vocab.sp_dim) # ------------------- Reward detection network ------------------------ # Translates visual input into reward yes/no signals # Note: Output of reward_detect is inverted num_reward_sps = len(vocab.reward.keys) self.reward_detect = cfg.make_thresh_ens_net(num_ens=num_reward_sps) nengo.Connection(bias_node, self.reward_detect.input, transform=np.ones(num_reward_sps)[:, None]) nengo.Connection(self.vis_sp_input, self.reward_detect.input, transform=-vocab.reward.vectors, synapse=None) # Calculate positive reward values self.pos_reward_vals = \ cfg.make_ens_array(n_ensembles=num_actions, radius=1) nengo.Connection(self.action_input, self.pos_reward_vals.input, transform=np.eye(num_actions), synapse=None) # Calculate negative reward values self.neg_reward_vals = \ cfg.make_ens_array(n_ensembles=num_actions, radius=1) nengo.Connection(self.action_input, self.neg_reward_vals.input, transform=np.ones(num_actions) - np.eye(num_actions), synapse=None) # Do the appropriate reward cross linking for i in range(num_actions): # No reward detect --> disinhibit neg_reward_vals nengo.Connection(self.reward_detect.output[0], self.neg_reward_vals.ensembles[i].neurons, transform=[[-3]] * self.neg_reward_vals.ensembles[i].n_neurons) # Yes reward detect --> disinhibit pos_reward_vals nengo.Connection(self.reward_detect.output[1], self.pos_reward_vals.ensembles[i].neurons, transform=[[-3]] * self.pos_reward_vals.ensembles[i].n_neurons) # Calculate the utility bias needed (so that the rewards don't send # the utilities to +inf, -inf) self.util_vals = EnsembleArray(100, num_actions, encoders=Choice([[1]]), intercepts=Exponential(0.15, 0.3, 1)) nengo.Connection(self.reward_detect.output, self.util_vals.input, transform=-np.ones((num_actions, 2))) nengo.Connection(self.action_input, self.util_vals.input, transform=np.ones((num_actions, num_actions)), synapse=None) nengo.Connection(self.bg_utilities_input, self.util_vals.input, transform=1, synapse=None)
def init_module(self): bias_node = nengo.Node(output=1) # ---------------------- Inputs and outputs ------------------------- # # Motor SP input node self.motor_sp_in = nengo.Node(size_in=vocab.mtr_dim) # Motor bypass signal (runs the ramp, but doesn't output to the arm) self.motor_bypass = cfg.make_thresh_ens_net() # Motor init signal self.motor_init = cfg.make_thresh_ens_net(0.75) # --------------- MOTOR SIGNALLING SYSTEM (STOP / GO) -------------- # Motor go signal self.motor_go = nengo.Ensemble(cfg.n_neurons_ens, 1) nengo.Connection(bias_node, self.motor_go) # Motor stop signal self.motor_stop_input = cfg.make_thresh_ens_net() nengo.Connection(bias_node, self.motor_stop_input.input, synapse=None) nengo.Connection(self.motor_stop_input.output, self.motor_go.neurons, transform=[[-3]] * cfg.n_neurons_ens) # --------------- MOTOR SIGNALLING SYSTEM (RAMP SIG) -------------- self.ramp_sig = Ramp_Signal_Network() # Signal used to drive the ramp (the constant input signal) nengo.Connection(self.motor_go, self.ramp_sig.ramp, transform=cfg.mtr_ramp_synapse * cfg.mtr_ramp_scale, synapse=cfg.mtr_ramp_synapse) # Signal to hold the ramp reset as long as the motor system is still # initializing (e.g. arm is still going to INIT target) nengo.Connection(self.motor_init.output, self.ramp_sig.init, transform=1.75, synapse=0.015) # Stop the ramp from starting if the stop command has been given nengo.Connection(self.motor_stop_input.output, self.ramp_sig.go, transform=-1) # --------------- FUNCTION REPLICATOR SYSTEM -------------- mtr_func_dim = vocab.mtr_dim // 2 func_eval_net = DiffFuncEvaltr(mtr_func_dim, mtr_data.sp_scaling_factor, 2) func_eval_net.make_inhibitable(-5) nengo.Connection(self.ramp_sig.ramp, func_eval_net.func_input) nengo.Connection(self.motor_bypass.output, func_eval_net.inhibit) # Motor path x information - Note conversion to difference of points # from path of points nengo.Connection(self.motor_sp_in[:mtr_func_dim], func_eval_net.diff_func_pts[0]) nengo.Connection(self.motor_sp_in[:mtr_func_dim - 1], func_eval_net.diff_func_pts[0][1:], transform=-1) # Motor path y information - Note conversion to difference of points # from path of points nengo.Connection(self.motor_sp_in[mtr_func_dim:], func_eval_net.diff_func_pts[1]) nengo.Connection(self.motor_sp_in[mtr_func_dim:-1], func_eval_net.diff_func_pts[1][1:], transform=-1) # --------------- MOTOR ARM CONTROL ----------------- arm_obj = cfg.mtr_arm_class() if arm_obj is not None: arm_rest_coord = np.array( arm_obj.position(q=arm_obj.rest_angles, ee_only=True)) # Note: arm_rest_coord is only used for initialization & startup # transients arm_node = nengo.Node( output=lambda t, x, dt=cfg.sim_dt: arm_obj.apply_torque(x, dt), size_in=arm_obj.DOF) osc_obj = OSController(dt=cfg.sim_dt, arm=arm_obj, kp=cfg.mtr_kp, kv=cfg.mtr_kv1, kv2=cfg.mtr_kv2, init_target=arm_rest_coord) # Make the osc control osc_net = osc_obj.initialize_model() # Connect output of motor path evaluator to osc_net nengo.Connection(func_eval_net.func_output, osc_net.target, synapse=0.01) # Add bias values to the motor path evaluator output (to shift the # drawn digit into the drawing box of the arm) nengo.Connection(bias_node, osc_net.target, transform=[[cfg.mtr_arm_rest_x_bias], [cfg.mtr_arm_rest_y_bias]], synapse=None) # Feed the torque control signal to the arm nengo.Connection(osc_net.output, arm_node) # ## Note: osc_net already has an internal node that gets info # from arm_obj (i.e. state information). So an external # connection is not required zero_centered_arm_ee_loc = \ nengo.Node(output=lambda t, bias=np.array([cfg.mtr_arm_rest_x_bias, cfg.mtr_arm_rest_y_bias]): arm_obj.x - bias, label='Centered Arm EE') # ------ MOTOR ARM CONTROL SIGNAL FEEDBACK ------ # X to target norm calculation target_thresh = cfg.mtr_tgt_threshold target_diff_norm = \ nengo.Ensemble(150, 2, intercepts=Exponential(0.05, target_thresh, target_thresh * 2), radius=target_thresh * 2) nengo.Connection(func_eval_net.func_output, target_diff_norm, synapse=0.01) if arm_obj is not None: nengo.Connection(zero_centered_arm_ee_loc, target_diff_norm, transform=-1, synapse=0.01) else: nengo.Connection(func_eval_net.func_output, target_diff_norm, synapse=0.01) # When the target != arm ee position, stop the ramp signal from # starting (reaching to start position) nengo.Connection(target_diff_norm, self.ramp_sig.go, transform=-5, function=lambda x: (np.sqrt(x[0]**2 + x[1]**2)) > 0, synapse=0.01) # When the target != arm ee position, stop the ramp signal from # stopping (reaching to end position) nengo.Connection(target_diff_norm, self.ramp_sig.end, transform=-5, function=lambda x: (np.sqrt(x[0]**2 + x[1]**2)) > 0, synapse=0.01) # Disable the target_diff_norm when motor bypass nengo.Connection(self.motor_bypass.output, target_diff_norm.neurons, transform=[[-3.0]] * target_diff_norm.n_neurons) # ------ MOTOR PEN DOWN CONTROL ------ pen_down = cfg.make_thresh_ens_net() # Pen is down by default nengo.Connection(bias_node, pen_down.input) # Cases when the pen should NOT be down nengo.Connection(self.ramp_sig.reset_hold, pen_down.input, transform=-1) nengo.Connection(self.ramp_sig.stop, pen_down.input, transform=-1) nengo.Connection(self.motor_stop_input.output, pen_down.input, transform=-1, synapse=0.05) nengo.Connection(self.motor_bypass.output, pen_down.input, transform=-1) # Pen down signal feedback to rest of motor system (tells the ramp to # keep going, and the osc_net to use only kv1) nengo.Connection(pen_down.output, self.ramp_sig.go, transform=12) if arm_obj is not None: nengo.Connection(pen_down.output, osc_net.CB2_inhibit) # --------------- For external probes --------------- self.ramp_int_stop = self.ramp_sig.stop # Motor target output self.mtr_path_func_out = nengo.Node(size_in=2) nengo.Connection(func_eval_net.diff_func_outputs[0], self.mtr_path_func_out[0], transform=np.ones((1, mtr_func_dim))) nengo.Connection(func_eval_net.diff_func_outputs[1], self.mtr_path_func_out[1], transform=np.ones((1, mtr_func_dim))) # Arm segments joint locations if arm_obj is not None: self.arm_px_node = \ nengo.Node(output=lambda t: arm_obj.position()[0]) self.arm_py_node = \ nengo.Node(output=lambda t: arm_obj.position()[1]) else: self.arm_px_node = nengo.Node(0) self.arm_py_node = nengo.Node(0) # Arm ee zero_centered location self.zero_centered_arm_ee_loc = zero_centered_arm_ee_loc # Target ee zero_centered location self.zero_centered_tgt_ee_loc = func_eval_net.func_output # Pen down status self.pen_down = pen_down.output # Ramp signal outputs self.ramp = self.ramp_sig.ramp self.ramp_reset_hold = self.ramp_sig.reset_hold self.ramp_50_75 = self.ramp_sig.ramp_50_75 # Decode index (number) bypass to monitor module self.dec_ind = nengo.Node(size_in=len(vocab.mtr.keys) + 1)