def __init__( self, input_vocab, output_vocab=None, # noqa: C901 default_output_vector=None, threshold=0.3, input_scale=1.0, inhibitable=False, inhibit_scale=1.0, wta_output=False, wta_inhibit_scale=2.0, wta_synapse=0.005, output_utilities=False, output_thresholded_utilities=False, neuron_type=nengo.LIF(), n_neurons_per_ensemble=10, label=None, seed=None, add_to_container=None): super(AssociativeMemory, self).__init__(label, seed, add_to_container) # If output vocabulary is not specified, use input vocabulary # (i.e autoassociative memory) if output_vocab is None: output_vocab = input_vocab # Handle different vocabulary types if isinstance(input_vocab, Vocabulary): input_vectors = input_vocab.vectors elif is_iterable(input_vocab): input_vectors = np.matrix(input_vocab) else: input_vectors = input_vocab if isinstance(output_vocab, Vocabulary): output_vectors = output_vocab.vectors elif is_iterable(output_vocab): output_vectors = np.matrix(output_vocab) else: output_vectors = output_vocab # Fail if number of input items and number of output items don't match if input_vectors.shape[0] != output_vectors.shape[0]: raise ValueError( 'number of input vectors does not match number of output ' 'vectors. %d != %d' % (input_vectors.shape[0], output_vectors.shape[0])) N = len(input_vectors) n_eval_points = 500 eval_point_margin = 0.1 eval_points = Uniform(threshold + eval_point_margin, 1 + eval_point_margin) # Ensemble array parameters ea_params = { 'radius': 1.0, 'neuron_type': neuron_type, 'n_neurons': n_neurons_per_ensemble, 'n_ensembles': N, 'intercepts': Uniform(threshold, 1), 'max_rates': Uniform(100, 200), 'encoders': Choice([[1]]), 'n_eval_points': n_eval_points, 'eval_points': eval_points } # Thresholding function def threshold_func(x): return x > threshold with self: # Input and output nodes self.input = nengo.Node(size_in=input_vectors.shape[1], label="input") self.output = nengo.Node(size_in=output_vectors.shape[1], label="output") # Ensemble array to do the thresholding stuff self.thresholded_ens_array = EnsembleArray( label="thresholded ens array", **ea_params) # Connect input and output nodes nengo.Connection(self.input, self.thresholded_ens_array.input, synapse=None, transform=input_vectors * input_scale) nengo.Connection(self.thresholded_ens_array.add_output( 'thresholded_output', threshold_func), self.output, synapse=None, transform=output_vectors.T) # Configure associative memory to be inhibitable if inhibitable: # Input node for inhibitory gating signal (if enabled) self.inhibit = nengo.Node(size_in=1, label="inhibit") nengo.Connection(self.inhibit, self.thresholded_ens_array.input, synapse=None, transform=-np.ones((N, 1))) # Note: We can use decoded connection here because all the # encoding vectors are [1] # Configure associative memory to have mutually inhibited output if wta_output: nengo.Connection(self.thresholded_ens_array.output, self.thresholded_ens_array.input, synapse=wta_synapse, transform=(np.eye(N) - 1) * inhibit_scale) # Configure utilities output if output_utilities: self.utilities = nengo.Node(size_in=N, label="utilities") nengo.Connection(self.thresholded_ens_array.output, self.utilities, synapse=None) # Configure utilities output if output_thresholded_utilities: self.thresholded_utilities = nengo.Node( size_in=N, label="thresholded_utilities") nengo.Connection(self.thresholded_ens_array.thresholded_output, self.thresholded_utilities, synapse=None) # Configure default output vector if default_output_vector is not None: eval_points = Uniform(0.8, 1) bias = nengo.Node(output=[1]) default_vector_gate = nengo.Ensemble( n_neurons_per_ensemble, dimensions=1, encoders=Choice([[1]]), intercepts=Uniform(0.5, 1), max_rates=ea_params['max_rates'], n_eval_points=n_eval_points, eval_points=eval_points, label="default vector gate") nengo.Connection(bias, default_vector_gate, synapse=None) nengo.Connection(self.thresholded_ens_array.thresholded_output, default_vector_gate, transform=-np.ones((1, N)), synapse=0.005) nengo.Connection(default_vector_gate, self.output, transform=np.matrix(default_output_vector).T, synapse=None) if inhibitable: nengo.Connection(self.inhibit, default_vector_gate, synapse=None, transform=[[-1]]) if isinstance(input_vocab, Vocabulary): self.inputs = dict(default=(self.input, input_vocab)) if isinstance(output_vocab, Vocabulary): self.outputs = dict(default=(self.output, output_vocab))
def __init__( self, dimensions, n_neurons_per_ensemble=100, output_weight=-3.0, input_bias=0.0, ampa_config=None, gaba_config=None, **kwargs, ): if "net" in kwargs: raise ObsoleteError("The 'net' argument is no longer supported.") kwargs.setdefault("label", "Basal Ganglia") super().__init__(**kwargs) ampa_config, override_ampa = config_with_default_synapse( ampa_config, Lowpass(0.002) ) gaba_config, override_gaba = config_with_default_synapse( gaba_config, Lowpass(0.008) ) # Affects all ensembles / connections in the BG # unless they've been overridden on `self.config` config = Config(Ensemble, Connection) config[Ensemble].radius = 1.5 config[Ensemble].encoders = Choice([[1]]) try: # Best, if we have SciPy config[Connection].solver = NnlsL2nz() except ImportError: # Warn if we can't use the better decoder solver. warnings.warn( "SciPy is not installed, so BasalGanglia will " "use the default decoder solver. Installing SciPy " "may improve BasalGanglia performance." ) ea_params = {"n_neurons": n_neurons_per_ensemble, "n_ensembles": dimensions} with self, config: self.strD1 = EnsembleArray( label="Striatal D1 neurons", intercepts=Uniform(Weights.e, 1), **ea_params, ) self.strD2 = EnsembleArray( label="Striatal D2 neurons", intercepts=Uniform(Weights.e, 1), **ea_params, ) self.stn = EnsembleArray( label="Subthalamic nucleus", intercepts=Uniform(Weights.ep, 1), **ea_params, ) self.gpi = EnsembleArray( label="Globus pallidus internus", intercepts=Uniform(Weights.eg, 1), **ea_params, ) self.gpe = EnsembleArray( label="Globus pallidus externus", intercepts=Uniform(Weights.ee, 1), **ea_params, ) self.input = Node(label="input", size_in=dimensions) self.output = Node(label="output", size_in=dimensions) # add bias input (BG performs best in the range 0.5--1.5) if abs(input_bias) > 0.0: self.bias_input = Node( np.ones(dimensions) * input_bias, label="basal ganglia bias" ) Connection(self.bias_input, self.input) # spread the input to StrD1, StrD2, and STN Connection( self.input, self.strD1.input, synapse=None, transform=Weights.ws * (1 + Weights.lg), ) Connection( self.input, self.strD2.input, synapse=None, transform=Weights.ws * (1 - Weights.le), ) Connection(self.input, self.stn.input, synapse=None, transform=Weights.wt) # connect the striatum to the GPi and GPe (inhibitory) strD1_output = self.strD1.add_output("func_str", Weights.str_func) strD2_output = self.strD2.add_output("func_str", Weights.str_func) with gaba_config: Connection(strD1_output, self.gpi.input, transform=-Weights.wm) Connection(strD2_output, self.gpe.input, transform=-Weights.wm) # connect the STN to GPi and GPe (broad and excitatory) tr = Weights.wp * np.ones((dimensions, dimensions)) stn_output = self.stn.add_output("func_stn", Weights.stn_func) with ampa_config: Connection(stn_output, self.gpi.input, transform=tr) Connection(stn_output, self.gpe.input, transform=tr) # connect the GPe to GPi and STN (inhibitory) gpe_output = self.gpe.add_output("func_gpe", Weights.gpe_func) with gaba_config: Connection(gpe_output, self.gpi.input, transform=-Weights.we) Connection(gpe_output, self.stn.input, transform=-Weights.wg) # connect GPi to output (inhibitory) gpi_output = self.gpi.add_output("func_gpi", Weights.gpi_func) Connection(gpi_output, self.output, synapse=None, transform=output_weight) # Return ampa_config and gaba_config to previous states, if changed if override_ampa: del ampa_config[Connection].synapse if override_gaba: del gaba_config[Connection].synapse
def BasalGanglia(dimensions, n_neurons_per_ensemble=100, output_weight=-3, input_bias=0.0, ampa_config=None, gaba_config=None, net=None): """Winner takes all; outputs 0 at max dimension, negative elsewhere.""" if net is None: net = nengo.Network("Basal Ganglia") ampa_config, override_ampa = config_with_default_synapse( ampa_config, nengo.Lowpass(0.002)) gaba_config, override_gaba = config_with_default_synapse( gaba_config, nengo.Lowpass(0.008)) # Affects all ensembles / connections in the BG # unless they've been overridden on `net.config` config = nengo.Config(nengo.Ensemble, nengo.Connection) config[nengo.Ensemble].radius = 1.5 config[nengo.Ensemble].encoders = Choice([[1]]) try: # Best, if we have SciPy config[nengo.Connection].solver = NnlsL2nz() except ImportError: # Warn if we can't use the better decoder solver. warnings.warn("SciPy is not installed, so BasalGanglia will " "use the default decoder solver. Installing SciPy " "may improve BasalGanglia performance.") ea_params = { 'n_neurons': n_neurons_per_ensemble, 'n_ensembles': dimensions } with nested(config, net): net.strD1 = EnsembleArray(label="Striatal D1 neurons", intercepts=Uniform(Weights.e, 1), **ea_params) net.strD2 = EnsembleArray(label="Striatal D2 neurons", intercepts=Uniform(Weights.e, 1), **ea_params) net.stn = EnsembleArray(label="Subthalamic nucleus", intercepts=Uniform(Weights.ep, 1), **ea_params) net.gpi = EnsembleArray(label="Globus pallidus internus", intercepts=Uniform(Weights.eg, 1), **ea_params) net.gpe = EnsembleArray(label="Globus pallidus externus", intercepts=Uniform(Weights.ee, 1), **ea_params) net.input = nengo.Node(label="input", size_in=dimensions) net.output = nengo.Node(label="output", size_in=dimensions) # add bias input (BG performs best in the range 0.5--1.5) if abs(input_bias) > 0.0: net.bias_input = nengo.Node(np.ones(dimensions) * input_bias) nengo.Connection(net.bias_input, net.input) # spread the input to StrD1, StrD2, and STN nengo.Connection(net.input, net.strD1.input, synapse=None, transform=Weights.ws * (1 + Weights.lg)) nengo.Connection(net.input, net.strD2.input, synapse=None, transform=Weights.ws * (1 - Weights.le)) nengo.Connection(net.input, net.stn.input, synapse=None, transform=Weights.wt) # connect the striatum to the GPi and GPe (inhibitory) strD1_output = net.strD1.add_output('func_str', Weights.str_func) strD2_output = net.strD2.add_output('func_str', Weights.str_func) with gaba_config: nengo.Connection(strD1_output, net.gpi.input, transform=-Weights.wm) nengo.Connection(strD2_output, net.gpe.input, transform=-Weights.wm) # connect the STN to GPi and GPe (broad and excitatory) tr = Weights.wp * np.ones((dimensions, dimensions)) stn_output = net.stn.add_output('func_stn', Weights.stn_func) with ampa_config: nengo.Connection(stn_output, net.gpi.input, transform=tr) nengo.Connection(stn_output, net.gpe.input, transform=tr) # connect the GPe to GPi and STN (inhibitory) gpe_output = net.gpe.add_output('func_gpe', Weights.gpe_func) with gaba_config: nengo.Connection(gpe_output, net.gpi.input, transform=-Weights.we) nengo.Connection(gpe_output, net.stn.input, transform=-Weights.wg) # connect GPi to output (inhibitory) gpi_output = net.gpi.add_output('func_gpi', Weights.gpi_func) nengo.Connection(gpi_output, net.output, synapse=None, transform=output_weight) # Return ampa_config and gaba_config to previous states, if changed if override_ampa: del ampa_config[nengo.Connection].synapse if override_gaba: del gaba_config[nengo.Connection].synapse return net
def BasalGanglia(dimensions, n_neurons_per_ensemble=100, output_weight=-3., input_bias=0., ampa_config=None, gaba_config=None, net=None): """Winner take all network, typically used for action selection. The basal ganglia network outputs approximately 0 at the dimension with the largest value, and is negative elsewhere. While the basal ganglia is primarily defined by its winner-take-all function, it is also organized to match the organization of the human basal ganglia. It consists of five ensembles: * Striatal D1 dopamine-receptor neurons (``strD1``) * Striatal D2 dopamine-receptor neurons (``strD2``) * Subthalamic nucleus (``stn``) * Globus pallidus internus / substantia nigra reticulata (``gpi``) * Globus pallidus externus (``gpe``) Interconnections between these areas are also based on known neuroanatomical connections. See [1]_ for more details, and [2]_ for the original non-spiking basal ganglia model by Gurney, Prescott & Redgrave that this model is based on. .. note:: The default `.Solver` for the basal ganglia is `.NnlsL2nz`, which requires SciPy. If SciPy is not installed, the global default solver will be used instead. Parameters ---------- dimensions : int Number of dimensions (i.e., actions). n_neuron_per_ensemble : int, optional (Default: 100) Number of neurons in each ensemble in the network. output_weight : float, optional (Default: -3.) A scaling factor on the output of the basal ganglia (specifically on the connection out of the GPi). input_bias : float, optional (Default: 0.) An amount by which to bias all dimensions of the input node. Biasing the input node is important for ensuring that all input dimensions are positive and easily comparable. ampa_config : config, optional (Default: None) Configuration for connections corresponding to biological connections to AMPA receptors (i.e., connections from STN to to GPi and GPe). If None, a default configuration using a 2 ms lowpass synapse will be used. gaba_config : config, optional (Default: None) Configuration for connections corresponding to biological connections to GABA receptors (i.e., connections from StrD1 to GPi, StrD2 to GPe, and GPe to GPi and STN). If None, a default configuration using an 8 ms lowpass synapse will be used. net : Network, optional (Default: None) A network in which the network components will be built. This is typically used to provide a custom set of Nengo object defaults through modifying ``net.config``. Returns ------- net : Network The newly built basal ganglia network, or the provided ``net``. Attributes ---------- net.bias_input : Node or None If ``input_bias`` is non-zero, this node will be created to bias all of the dimensions of the input signal. net.gpe : EnsembleArray Globus pallidus externus ensembles. net.gpi : EnsembleArray Globus pallidus internus ensembles. net.input : Node Accepts the input signal. net.output : Node Provides the output signal. net.stn : EnsembleArray Subthalamic nucleus ensembles. net.strD1 : EnsembleArray Striatal D1 ensembles. net.strD2 : EnsembleArray Striatal D2 ensembles. References ---------- .. [1] Stewart, T. C., Choo, X., & Eliasmith, C. (2010). Dynamic behaviour of a spiking model of action selection in the basal ganglia. In Proceedings of the 10th international conference on cognitive modeling (pp. 235-40). .. [2] Gurney, K., Prescott, T., & Redgrave, P. (2001). A computational model of action selection in the basal ganglia. Biological Cybernetics 84, 401-423. """ if net is None: net = nengo.Network("Basal Ganglia") ampa_config, override_ampa = config_with_default_synapse( ampa_config, nengo.Lowpass(0.002)) gaba_config, override_gaba = config_with_default_synapse( gaba_config, nengo.Lowpass(0.008)) # Affects all ensembles / connections in the BG # unless they've been overridden on `net.config` config = nengo.Config(nengo.Ensemble, nengo.Connection) config[nengo.Ensemble].radius = 1.5 config[nengo.Ensemble].encoders = Choice([[1]]) try: # Best, if we have SciPy config[nengo.Connection].solver = NnlsL2nz() except ImportError: # Warn if we can't use the better decoder solver. warnings.warn("SciPy is not installed, so BasalGanglia will " "use the default decoder solver. Installing SciPy " "may improve BasalGanglia performance.") ea_params = { 'n_neurons': n_neurons_per_ensemble, 'n_ensembles': dimensions } with config, net: net.strD1 = EnsembleArray(label="Striatal D1 neurons", intercepts=Uniform(Weights.e, 1), **ea_params) net.strD2 = EnsembleArray(label="Striatal D2 neurons", intercepts=Uniform(Weights.e, 1), **ea_params) net.stn = EnsembleArray(label="Subthalamic nucleus", intercepts=Uniform(Weights.ep, 1), **ea_params) net.gpi = EnsembleArray(label="Globus pallidus internus", intercepts=Uniform(Weights.eg, 1), **ea_params) net.gpe = EnsembleArray(label="Globus pallidus externus", intercepts=Uniform(Weights.ee, 1), **ea_params) net.input = nengo.Node(label="input", size_in=dimensions) net.output = nengo.Node(label="output", size_in=dimensions) # add bias input (BG performs best in the range 0.5--1.5) if abs(input_bias) > 0.0: net.bias_input = nengo.Node(np.ones(dimensions) * input_bias, label="basal ganglia bias") nengo.Connection(net.bias_input, net.input) # spread the input to StrD1, StrD2, and STN nengo.Connection(net.input, net.strD1.input, synapse=None, transform=Weights.ws * (1 + Weights.lg)) nengo.Connection(net.input, net.strD2.input, synapse=None, transform=Weights.ws * (1 - Weights.le)) nengo.Connection(net.input, net.stn.input, synapse=None, transform=Weights.wt) # connect the striatum to the GPi and GPe (inhibitory) strD1_output = net.strD1.add_output('func_str', Weights.str_func) strD2_output = net.strD2.add_output('func_str', Weights.str_func) with gaba_config: nengo.Connection(strD1_output, net.gpi.input, transform=-Weights.wm) nengo.Connection(strD2_output, net.gpe.input, transform=-Weights.wm) # connect the STN to GPi and GPe (broad and excitatory) tr = Weights.wp * np.ones((dimensions, dimensions)) stn_output = net.stn.add_output('func_stn', Weights.stn_func) with ampa_config: nengo.Connection(stn_output, net.gpi.input, transform=tr) nengo.Connection(stn_output, net.gpe.input, transform=tr) # connect the GPe to GPi and STN (inhibitory) gpe_output = net.gpe.add_output('func_gpe', Weights.gpe_func) with gaba_config: nengo.Connection(gpe_output, net.gpi.input, transform=-Weights.we) nengo.Connection(gpe_output, net.stn.input, transform=-Weights.wg) # connect GPi to output (inhibitory) gpi_output = net.gpi.add_output('func_gpi', Weights.gpi_func) nengo.Connection(gpi_output, net.output, synapse=None, transform=output_weight) # Return ampa_config and gaba_config to previous states, if changed if override_ampa: del ampa_config[nengo.Connection].synapse if override_gaba: del gaba_config[nengo.Connection].synapse return net
def Thalamus(dimensions, n_neurons_per_ensemble=50, mutual_inhib=1., threshold=0., net=None): """Inhibits non-selected actions. The thalamus is intended to work in tandem with a basal ganglia network. It converts basal ganglia output into a signal with (approximately) 1 for the selected action and 0 elsewhere. In order to suppress low responses and strengthen high responses, a constant bias is added to each dimension (i.e., action), and dimensions mutually inhibit each other. Additionally, the ensemble representing each dimension is created with positive encoders and can be assigned positive x-intercepts to threshold low responses. Parameters ---------- dimensions : int Number of dimensions (i.e., actions). n_neuron_per_ensemble : int, optional (Default: 50) Number of neurons in each ensemble in the network. mutual_inhib : float, optional (Default: 1.) Strength of the mutual inhibition between actions. threshold : float, optional (Default: 0.) The threshold below which values will not be represented. net : Network, optional (Default: None) A network in which the network components will be built. This is typically used to provide a custom set of Nengo object defaults through modifying ``net.config``. Returns ------- net : Network The newly built thalamus network, or the provided ``net``. Attributes ---------- net.actions : EnsembleArray Each ensemble represents one dimension (action). net.bias : Node The constant bias injected in each ``actions`` ensemble. net.input : Node Input to the ``actions`` ensembles. net.output : Node Output from the ``actions`` ensembles. """ if net is None: net = nengo.Network("Thalamus") with net: net.actions = EnsembleArray(n_neurons_per_ensemble, dimensions, intercepts=Uniform(threshold, 1), encoders=Choice([[1.0]]), label="actions") nengo.Connection(net.actions.output, net.actions.input, transform=(np.eye(dimensions) - 1) * mutual_inhib) net.bias = nengo.Node([1], label="thalamus bias") nengo.Connection(net.bias, net.actions.input, transform=np.ones((dimensions, 1))) net.input = net.actions.input net.output = net.actions.output return net
def Product(n_neurons, dimensions, input_magnitude=1., net=None): """Computes the element-wise product of two equally sized vectors. The network used to calculate the product is described in `Gosmann, 2015`_. A simpler version of this network can be found in the `Multiplication example <http://pythonhosted.org/nengo/examples/multiplication.html>`_. .. _Gosmann, 2015: http://nbviewer.jupyter.org/github/ctn-archive/technical-reports/blob/ master/Precise-multiplications-with-the-NEF.ipynb#An-alternative-network Parameters ---------- n_neurons : int Number of neurons per dimension in the vector. .. note:: These neurons will be distributed evenly across two ensembles. If an odd number of neurons is specified, the extra neuron will not be used. dimensions : int Number of dimensions in each of the vectors to be multiplied. input_magnitude : float, optional (Default: 1.) The expected magnitude of the vectors to be multiplied. This value is used to determine the radius of the ensembles computing the element-wise product. net : Network, optional (Default: None) A network in which the network components will be built. This is typically used to provide a custom set of Nengo object defaults through modifying ``net.config``. Returns ------- net : Network The newly built product network, or the provided ``net``. Attributes ---------- net.A : Node The first vector to be multiplied. net.B : Node The second vector to be multiplied. net.output : Node The resulting product. net.sq1 : EnsembleArray Represents the first squared term. See `Gosmann, 2015`_ for details. net.sq2 : EnsembleArray Represents the second squared term. See `Gosmann, 2015`_ for details. """ if net is None: net = nengo.Network(label="Product") with net: net.A = nengo.Node(size_in=dimensions, label="A") net.B = nengo.Node(size_in=dimensions, label="B") net.output = nengo.Node(size_in=dimensions, label="output") net.sq1 = EnsembleArray( max(1, n_neurons // 2), n_ensembles=dimensions, ens_dimensions=1, radius=input_magnitude * np.sqrt(2)) net.sq2 = EnsembleArray( max(1, n_neurons // 2), n_ensembles=dimensions, ens_dimensions=1, radius=input_magnitude * np.sqrt(2)) tr = 1. / np.sqrt(2.) nengo.Connection(net.A, net.sq1.input, transform=tr, synapse=None) nengo.Connection(net.B, net.sq1.input, transform=tr, synapse=None) nengo.Connection(net.A, net.sq2.input, transform=tr, synapse=None) nengo.Connection(net.B, net.sq2.input, transform=-tr, synapse=None) sq1_out = net.sq1.add_output('square', np.square) nengo.Connection(sq1_out, net.output, transform=.5, synapse=None) sq2_out = net.sq2.add_output('square', np.square) nengo.Connection(sq2_out, net.output, transform=-.5, synapse=None) return net
def Product(n_neurons, dimensions, input_magnitude=1., net=None, **kwargs): """Computes the element-wise product of two equally sized vectors. The network used to calculate the product is described in `Gosmann, 2015`_. A simpler version of this network can be found in the `Multiplication example <https://www.nengo.ai/nengo/examples/multiplication.html>`_. Note that this network is optimized under the assumption that both input values (or both values for each input dimensions of the input vectors) are uniformly and independently distributed. Visualized in a joint 2D space, this would give a square of equal probabilities for pairs of input values. This assumption is violated with non-uniform input value distributions (for example, if the input values follow a Gaussian or cosine similarity distribution). In that case, no square of equal probabilities is obtained, but a probability landscape with circular equi-probability lines. To obtain the optimal network accuracy, scale the *input_magnitude* by a factor of ``1 / sqrt(2)``. .. _Gosmann, 2015: https://nbviewer.jupyter.org/github/ctn-archive/technical-reports/blob/ master/Precise-multiplications-with-the-NEF.ipynb#An-alternative-network Parameters ---------- n_neurons : int Number of neurons per dimension in the vector. .. note:: These neurons will be distributed evenly across two ensembles. If an odd number of neurons is specified, the extra neuron will not be used. dimensions : int Number of dimensions in each of the vectors to be multiplied. input_magnitude : float, optional (Default: 1.) The expected magnitude of the vectors to be multiplied. This value is used to determine the radius of the ensembles computing the element-wise product. kwargs Keyword arguments passed through to ``nengo.Network``. Returns ------- net : Network The newly built product network, or the provided ``net``. Attributes ---------- net.input_a : Node The first vector to be multiplied. net.input_b : Node The second vector to be multiplied. net.output : Node The resulting product. net.sq1 : EnsembleArray Represents the first squared term. See `Gosmann, 2015`_ for details. net.sq2 : EnsembleArray Represents the second squared term. See `Gosmann, 2015`_ for details. """ if net is None: kwargs.setdefault('label', "Product") net = nengo.Network(**kwargs) else: warnings.warn("The 'net' argument is deprecated.", DeprecationWarning) with net: net.input_a = net.A = nengo.Node(size_in=dimensions, label="input_a") net.input_b = net.B = nengo.Node(size_in=dimensions, label="input_b") net.output = nengo.Node(size_in=dimensions, label="output") net.sq1 = EnsembleArray(max(1, n_neurons // 2), n_ensembles=dimensions, ens_dimensions=1, radius=input_magnitude * np.sqrt(2)) net.sq2 = EnsembleArray(max(1, n_neurons // 2), n_ensembles=dimensions, ens_dimensions=1, radius=input_magnitude * np.sqrt(2)) tr = 1. / np.sqrt(2.) nengo.Connection(net.input_a, net.sq1.input, transform=tr, synapse=None) nengo.Connection(net.input_b, net.sq1.input, transform=tr, synapse=None) nengo.Connection(net.input_a, net.sq2.input, transform=tr, synapse=None) nengo.Connection(net.input_b, net.sq2.input, transform=-tr, synapse=None) sq1_out = net.sq1.add_output('square', np.square) nengo.Connection(sq1_out, net.output, transform=.5, synapse=None) sq2_out = net.sq2.add_output('square', np.square) nengo.Connection(sq2_out, net.output, transform=-.5, synapse=None) return net