def test_eval_points_scaling(Simulator, sample, radius, seed, rng): eval_points = UniformHypersphere() if sample: eval_points = eval_points.sample(500, 3, rng=rng) model = nengo.Network(seed=seed) with model: a = nengo.Ensemble(1, 3, eval_points=eval_points, radius=radius) with Simulator(model) as sim: dists = npext.norm(sim.data[a].eval_points, axis=1) assert np.all(dists <= radius) assert np.any(dists >= 0.9 * radius)
def test_eval_points_scaling(Simulator, sample, radius, seed, rng, scale): eval_points = UniformHypersphere() if sample: eval_points = eval_points.sample(500, 3, rng=rng) model = nengo.Network(seed=seed) with model: a = nengo.Ensemble(1, 3, radius=radius) b = nengo.Ensemble(1, 3) con = nengo.Connection(a, b, eval_points=eval_points, scale_eval_points=scale) sim = Simulator(model) dists = npext.norm(sim.data[con].eval_points, axis=1) limit = radius if scale else 1.0 assert np.all(dists <= limit) assert np.any(dists >= 0.9 * limit)
def test_distributionparam_sample_shape(): """sample_shape dictates the shape of the sample that can be set.""" class Test(object): dp = params.DistributionParam(default=None, sample_shape=['d1', 10]) d1 = 4 inst = Test() # Distributions are still cool inst.dp = UniformHypersphere() assert isinstance(inst.dp, UniformHypersphere) # Must be shape (4, 10) inst.dp = np.ones((4, 10)) assert np.all(inst.dp == np.ones((4, 10))) with pytest.raises(ValueError): inst.dp = np.ones((10, 4)) assert np.all(inst.dp == np.ones((4, 10)))
def test_distorarrayparam_sample_shape(): """sample_shape dictates the shape of the sample that can be set.""" class Test: dp = DistOrArrayParam("dp", default=None, sample_shape=["d1", 10]) d1 = 4 inst = Test() # Distributions are still cool inst.dp = UniformHypersphere() assert isinstance(inst.dp, UniformHypersphere) # Must be shape (4, 10) inst.dp = np.ones((4, 10)) assert np.all(inst.dp == np.ones((4, 10))) with pytest.raises(ValidationError): inst.dp = np.ones((10, 4)) assert np.all(inst.dp == np.ones((4, 10)))
def test_distorarrayparam(): """DistOrArrayParams can be distributions or samples.""" class Test: dp = DistOrArrayParam("dp", default=None, sample_shape=["*", "*"]) inst = Test() inst.dp = UniformHypersphere() assert isinstance(inst.dp, UniformHypersphere) inst.dp = np.array([[1], [2], [3]]) assert np.all(inst.dp == np.array([[1], [2], [3]])) with pytest.raises(ValueError): inst.dp = "a" # Sample must have correct dims with pytest.raises(ValueError): inst.dp = np.array([1])
def random_orthogonal(d, rng=None): """Returns a random orthogonal matrix. Parameters ---------- d : ``integer`` Positive dimension of returned matrix. rng : :class:`numpy.random.RandomState` or ``None``, optional Random number generator state. Returns ------- samples : ``(d, d) np.array`` Random orthogonal matrix (an orthonormal basis); linearly transforms any vector into a uniformly sampled vector on the ``d``--ball with the same L2 norm. See Also -------- :class:`.ScatteredHypersphere` Examples -------- >>> from nengolib.stats import random_orthogonal, sphere >>> rng = np.random.RandomState(seed=0) >>> u = sphere.sample(1000, 3, rng=rng) >>> u[:, 0] = 0 >>> v = u.dot(random_orthogonal(3, rng=rng)) >>> import matplotlib.pyplot as plt >>> from mpl_toolkits.mplot3d import Axes3D >>> ax = plt.subplot(111, projection='3d') >>> ax.scatter(*u.T, alpha=.5, label="u") >>> ax.scatter(*v.T, alpha=.5, label="v") >>> ax.patch.set_facecolor('white') >>> ax.set_xlim3d(-1, 1) >>> ax.set_ylim3d(-1, 1) >>> ax.set_zlim3d(-1, 1) >>> plt.legend() >>> plt.show() """ rng = np.random if rng is None else rng m = UniformHypersphere(surface=True).sample(d, d, rng=rng) u, s, v = svd(m) return np.dot(u, v)
def test_ndarrays(Simulator, rng, allclose): encoders = UniformHypersphere(surface=True).sample(10, 1, rng=rng) max_rates = rng.uniform(200, 400, size=10) intercepts = rng.uniform(-1, 1, size=10) eval_points = rng.uniform(-1, 1, size=(100, 1)) with nengo.Network() as net: ea = nengo.networks.EnsembleArray( 10, 2, encoders=encoders, max_rates=max_rates, intercepts=intercepts, eval_points=eval_points, n_eval_points=eval_points.shape[0], ) with Simulator(net) as sim: pass built = sim.data[ea.ea_ensembles[0]] assert allclose(built.encoders, encoders) assert allclose(built.max_rates, max_rates) assert allclose(built.intercepts, intercepts) assert allclose(built.eval_points, eval_points) assert built.eval_points.shape == eval_points.shape with nengo.Network() as net: # Incorrect shapes don't fail until build ea = nengo.networks.EnsembleArray( 10, 2, encoders=encoders, max_rates=max_rates, intercepts=intercepts, eval_points=eval_points[:10], n_eval_points=eval_points.shape[0], ) with pytest.raises(ValidationError): with Simulator(net) as sim: pass
def test_target_function(Simulator, nl_nodirect, plt, dimensions, radius, seed, rng): eval_points = UniformHypersphere().sample(1000, dimensions, rng=rng) eval_points *= radius f = lambda x: x**2 targets = f(eval_points) model = nengo.Network(seed=seed) with model: model.config[nengo.Ensemble].neuron_type = nl_nodirect() inp = nengo.Node(lambda t: np.sin(t * 2 * np.pi) * radius) ens1 = nengo.Ensemble(40 * dimensions, dimensions, radius=radius) n1 = nengo.Node(size_in=dimensions) n2 = nengo.Node(size_in=dimensions) transform = np.linspace(1, -1, num=dimensions).reshape(-1, 1) nengo.Connection(inp, ens1, transform=transform) # pass in eval_points and targets nengo.Connection(ens1, n1, **target_function(eval_points, targets)) # same, but let the builder apply f nengo.Connection(ens1, n2, function=f) probe1 = nengo.Probe(n1, synapse=0.03) probe2 = nengo.Probe(n2, synapse=0.03) sim = Simulator(model) sim.run(0.5) plt.subplot(2, 1, 1) plt.plot(sim.trange(), sim.data[probe1]) plt.title('Square manually with target_function') plt.subplot(2, 1, 2) plt.plot(sim.trange(), sim.data[probe2]) plt.title('Square by passing in function to connection') plt.saveas = ('utils.test_connection.test_target_function_%d_%g.pdf' % (dimensions, radius)) assert np.allclose(sim.data[probe1], sim.data[probe2], atol=0.2 * radius)
def get_eval_points(n_points, dims, rng=None, sort=False): points = UniformHypersphere(surface=False).sample(n_points, dims, rng=rng) return points[np.argsort(points[:, 0])] if sort else points
def get_encoders(n_neurons, dims, rng=None): return UniformHypersphere(surface=True).sample(n_neurons, dims, rng=rng).T
# objective = 'rms' # objective = 'sp' if objective == 'nll': cost = nll_cost_on_inds error = class_error_on_inds dout = n_labels dtp_cheat = True elif objective == 'rms': cost = squared_cost_on_inds error = rms_error_on_inds dout = n_labels dtp_cheat = True elif objective == 'sp': dout = 50 pointers = UniformHypersphere(surface=True).sample(n_labels, d=dout, rng=rng) dtp_cheat = False def cost(y, yinds, pointers=pointers): return pointer_squared_cost_on_inds(y, yinds, pointers) def error(y, yinds, pointers=pointers): return pointer_class_error_on_inds(y, yinds, pointers) din = trainX.shape[1] sizes = [din] + dhids + [dout] batch_fn = make_flat_batch_fn(trainX, trainY, n_per_batch)
def test_hypersphere_warns(): with pytest.warns(UserWarning, match="min_magnitude ignored because surface"): UniformHypersphere(surface=True, min_magnitude=0.1)
def test_hypersphere_errors(): with pytest.raises(ValidationError, match="Must be of type 'bool'"): UniformHypersphere(0) with pytest.raises(ValidationError, match="Dimensions must be a positive integer"): UniformHypersphere().sample(1, d=-1)
def __init__(self, x, ystar, initial_weights, t0=None, t1=None, n_output=None, o_kind='ensemble', o_encoders=UniformHypersphere(surface=True), o_intercepts=Uniform(-1, 0.9), o_rates=Uniform(100, 120), n_error=None, e_kind='ensemble', e_encoders=UniformHypersphere(surface=True), e_intercepts=Uniform(-1, 0.9), e_rates=Uniform(100, 120), psynapse=None, pdt=None, **kwargs): super(FeedforwardNetwork, self).__init__(**kwargs) din = initial_weights[0].shape[0] dout = initial_weights[-1].shape[1] dhids = [w.shape[1] for w in initial_weights[:-1]] self.n_output = n_output self.n_error = n_error self.e_kind = e_kind self.e_encoders = e_encoders self.e_intercepts = e_intercepts self.e_rates = e_rates self.pargs = dict(synapse=psynapse, sample_every=pdt) with self: # hidden layers self.layers = [ nengo.Ensemble(dhid, dhid, gain=Choice([1.]), bias=Choice([1.]), label='layer%d' % i) for i, dhid in enumerate(dhids)] # output layer if n_output is None: self.output = EAIO( nengo.Node, size_in=dout, label='output') elif o_kind == 'ensemble': self.output = EAIO( nengo.Ensemble, n_output*dout, dout, label='output', encoders=o_encoders, intercepts=o_intercepts, max_rates=o_rates) elif o_kind == 'array': self.output = nengo.networks.EnsembleArray( n_output, dout, label='output', encoders=o_encoders, intercepts=o_intercepts, max_rates=o_rates) self.yp = nengo.Probe(self.output.output, **self.pargs) # error layer tmask = ((lambda t: (t > t0) & (t < t1)) if t0 is not None and t1 is not None else \ (lambda t: (t > t0)) if t0 is not None else \ (lambda t: (t < t1)) if t1 is not None else \ (lambda t: (t >= 0))) nmask = lambda t: 20 * (tmask(t) - 1) if n_error is None: ferror = lambda t, x: tmask(t) * x self.error = EAIO( nengo.Node, ferror, size_in=dout, label='error') elif e_kind == 'ensemble': self.error = EAIO( nengo.Ensemble, n_error*dout, dout, label='error', encoders=e_encoders, intercepts=e_intercepts, max_rates=e_rates) error_switch = nengo.Node(nmask) nengo.Connection(error_switch, self.error.neuron_input, transform=np.ones((n_error*dout, 1)), synapse=None) elif e_kind == 'array': self.error = nengo.networks.EnsembleArray( n_error, dout, label='error', encoders=e_encoders, intercepts=e_intercepts, max_rates=e_rates) self.error.add_neuron_input() error_switch = nengo.Node(nmask) nengo.Connection(error_switch, self.error.neuron_input, transform=np.ones((n_error*dout, 1)), synapse=None) else: raise ValueError(e_kind) self.ep = nengo.Probe(self.error.output, **self.pargs) # connections nengo.Connection(ystar, self.error.input, transform=-1) with self: nengo.Connection(self.output.output, self.error.input) self.conns = [] self.conns.append(nengo.Connection( x.neurons, self.layers[0].neurons, transform=initial_weights[0].T)) with self: layers = self.layers # layers = self.layers + [self.output] for layer0, layer1, w in zip(layers, layers[1:], initial_weights[1:]): self.conns.append(nengo.Connection( layer0.neurons, layer1.neurons, transform=w.T)) self.conns.append(nengo.Connection( self.layers[-1].neurons, self.output.input, transform=initial_weights[-1].T))
def random_orthogonal(d, rng=None): rng = np.random if rng is None else rng m = UniformHypersphere(surface=True).sample(d, d, rng=rng) u, s, v = svd(m) return np.dot(u, v)
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(ScatteredHypersphere, ["surface", "min_magnitude", "base", "method"]) check_repr(ScatteredHypersphere()) check_repr(ScatteredHypersphere(surface=True)) check_repr(ScatteredHypersphere(min_magnitude=0.3)) check_repr(ScatteredHypersphere(base=Uniform(0, 1))) check_repr(ScatteredHypersphere(method="tfww")) assert repr(ScatteredHypersphere()) == "ScatteredHypersphere()" assert (repr(ScatteredHypersphere( surface=True)) == "ScatteredHypersphere(surface=True)") assert (repr(ScatteredHypersphere(base=Uniform(0, 1), method="tfww")) == "ScatteredHypersphere(base=Uniform(low=0, high=1), method='tfww')") 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_voja_encoders(Simulator, nl_nodirect, rng, seed, allclose): """Tests that voja changes active encoders to the input.""" n = 200 learned_vector = np.asarray([0.3, -0.4, 0.6]) learned_vector /= np.linalg.norm(learned_vector) n_change = n // 2 # modify first half of the encoders # Set the first half to always fire with random encoders, and the # remainder to never fire due to their encoder's dot product with the input intercepts = np.asarray([-1] * n_change + [0.99] * (n - n_change)) rand_encoders = UniformHypersphere(surface=True).sample( n_change, len(learned_vector), rng=rng) encoders = np.append(rand_encoders, [-learned_vector] * (n - n_change), axis=0) m = nengo.Network(seed=seed) with m: m.config[nengo.Ensemble].neuron_type = nl_nodirect() u = nengo.Node(output=learned_vector) x = nengo.Ensemble( n, dimensions=len(learned_vector), intercepts=intercepts, encoders=encoders, max_rates=nengo.dists.Uniform(300.0, 400.0), radius=2.0, ) # to test encoder scaling conn = nengo.Connection(u, x, synapse=None, learning_rule_type=Voja(learning_rate=1e-1)) p_enc = nengo.Probe(conn.learning_rule, "scaled_encoders") p_enc_ens = nengo.Probe(x, "scaled_encoders") with Simulator(m) as sim: sim.run(1.0) t = sim.trange() tend = t > 0.5 # Voja's rule relies on knowing exactly how the encoders were scaled # during the build process, because it modifies the scaled_encoders signal # proportional to this factor. Therefore, we should check that its # assumption actually holds. encoder_scale = (sim.data[x].gain / x.radius)[:, np.newaxis] assert allclose(sim.data[x].encoders, sim.data[x].scaled_encoders / encoder_scale) # Check that the last half kept the same encoders throughout the simulation assert allclose(sim.data[p_enc][0, n_change:], sim.data[p_enc][:, n_change:]) # and that they are also equal to their originally assigned value assert allclose(sim.data[p_enc][0, n_change:] / encoder_scale[n_change:], -learned_vector) # Check that the first half converged to the input assert allclose( sim.data[p_enc][tend, :n_change] / encoder_scale[:n_change], learned_vector, atol=0.01, ) # Check that encoders probed from ensemble equal encoders probed from Voja assert allclose(sim.data[p_enc], sim.data[p_enc_ens])
def test_hypersphere_dimension_fail(rng): with pytest.raises(ValueError): UniformHypersphere(0).sample(1, 0)
class Ensemble(NengoObject): """A group of neurons that collectively represent a vector. Parameters ---------- n_neurons : int The number of neurons. dimensions : int The number of representational dimensions. radius : int, optional (Default: 1.0) The representational radius of the ensemble. encoders : Distribution or (n_neurons, dimensions) array_like, optional \ (Default: UniformHypersphere(surface=True)) The encoders used to transform from representational space to neuron space. Each row is a neuron's encoder; each column is a representational dimension. intercepts : Distribution or (n_neurons,) array_like, optional \ (Default: ``nengo.dists.Uniform(-1.0, 1.0)``) The point along each neuron's encoder where its activity is zero. If ``e`` is the neuron's encoder, then the activity will be zero when ``dot(x, e) <= c``, where ``c`` is the given intercept. max_rates : Distribution or (n_neurons,) array_like, optional \ (Default: ``nengo.dists.Uniform(200, 400)``) The activity of each neuron when the input signal ``x`` is magnitude 1 and aligned with that neuron's encoder ``e``; i.e., when ``dot(x, e) = 1``. eval_points : Distribution or (n_eval_points, dims) array_like, optional \ (Default: ``nengo.dists.UniformHypersphere()``) The evaluation points used for decoder solving, spanning the interval (-radius, radius) in each dimension, or a distribution from which to choose evaluation points. n_eval_points : int, optional (Default: None) The number of evaluation points to be drawn from the ``eval_points`` distribution. If None, then a heuristic is used to determine the number of evaluation points. neuron_type : `~nengo.neurons.NeuronType`, optional \ (Default: ``nengo.LIF()``) The model that simulates all neurons in the ensemble (see `~nengo.neurons.NeuronType`). gain : Distribution or (n_neurons,) array_like (Default: None) The gains associated with each neuron in the ensemble. If None, then the gain will be solved for using ``max_rates`` and ``intercepts``. bias : Distribution or (n_neurons,) array_like (Default: None) The biases associated with each neuron in the ensemble. If None, then the gain will be solved for using ``max_rates`` and ``intercepts``. noise : Process, optional (Default: None) Random noise injected directly into each neuron in the ensemble as current. A sample is drawn for each individual neuron on every simulation step. normalize_encoders : bool, optional (Default: True) Indicates whether the encoders should be normalized. label : str, optional (Default: None) A name for the ensemble. Used for debugging and visualization. seed : int, optional (Default: None) The seed used for random number generation. Attributes ---------- bias : Distribution or (n_neurons,) array_like or None The biases associated with each neuron in the ensemble. dimensions : int The number of representational dimensions. encoders : Distribution or (n_neurons, dimensions) array_like The encoders, used to transform from representational space to neuron space. Each row is a neuron's encoder, each column is a representational dimension. eval_points : Distribution or (n_eval_points, dims) array_like The evaluation points used for decoder solving, spanning the interval (-radius, radius) in each dimension, or a distribution from which to choose evaluation points. gain : Distribution or (n_neurons,) array_like or None The gains associated with each neuron in the ensemble. intercepts : Distribution or (n_neurons) array_like or None The point along each neuron's encoder where its activity is zero. If ``e`` is the neuron's encoder, then the activity will be zero when ``dot(x, e) <= c``, where ``c`` is the given intercept. label : str or None A name for the ensemble. Used for debugging and visualization. max_rates : Distribution or (n_neurons,) array_like or None The activity of each neuron when ``dot(x, e) = 1``, where ``e`` is the neuron's encoder. n_eval_points : int or None The number of evaluation points to be drawn from the ``eval_points`` distribution. If None, then a heuristic is used to determine the number of evaluation points. n_neurons : int or None The number of neurons. neuron_type : NeuronType The model that simulates all neurons in the ensemble (see ``nengo.neurons``). noise : Process or None Random noise injected directly into each neuron in the ensemble as current. A sample is drawn for each individual neuron on every simulation step. radius : int The representational radius of the ensemble. seed : int or None The seed used for random number generation. """ probeable = ('decoded_output', 'input', 'scaled_encoders') n_neurons = IntParam('n_neurons', low=1) dimensions = IntParam('dimensions', low=1) radius = NumberParam('radius', default=1.0, low=1e-10) encoders = DistOrArrayParam('encoders', default=UniformHypersphere(surface=True), sample_shape=('n_neurons', 'dimensions')) intercepts = DistOrArrayParam('intercepts', default=Uniform(-1.0, 1.0), optional=True, sample_shape=('n_neurons', )) max_rates = DistOrArrayParam('max_rates', default=Uniform(200, 400), optional=True, sample_shape=('n_neurons', )) eval_points = DistOrArrayParam('eval_points', default=UniformHypersphere(), sample_shape=('*', 'dimensions')) n_eval_points = IntParam('n_eval_points', default=None, optional=True) neuron_type = NeuronTypeParam('neuron_type', default=LIF()) gain = DistOrArrayParam('gain', default=None, optional=True, sample_shape=('n_neurons', )) bias = DistOrArrayParam('bias', default=None, optional=True, sample_shape=('n_neurons', )) noise = ProcessParam('noise', default=None, optional=True) normalize_encoders = BoolParam('normalize_encoders', default=True, optional=True) def __init__(self, n_neurons, dimensions, radius=Default, encoders=Default, intercepts=Default, max_rates=Default, eval_points=Default, n_eval_points=Default, neuron_type=Default, gain=Default, bias=Default, noise=Default, normalize_encoders=Default, label=Default, seed=Default): super(Ensemble, self).__init__(label=label, seed=seed) self.n_neurons = n_neurons self.dimensions = dimensions self.radius = radius self.encoders = encoders self.intercepts = intercepts self.max_rates = max_rates self.n_eval_points = n_eval_points self.eval_points = eval_points self.bias = bias self.gain = gain self.neuron_type = neuron_type self.noise = noise self.normalize_encoders = normalize_encoders def __getitem__(self, key): return ObjView(self, key) def __len__(self): return self.dimensions @property def neurons(self): """A direct interface to the neurons in the ensemble.""" return Neurons(self) @neurons.setter def neurons(self, dummy): raise ReadonlyError(attr="neurons", obj=self) @property def size_in(self): """The dimensionality of the ensemble.""" return self.dimensions @property def size_out(self): """The dimensionality of the ensemble.""" return self.dimensions
def test_hypersphere_warns(rng): with pytest.warns(UserWarning): UniformHypersphere(surface=True, min_magnitude=0.1)
class Ensemble(NengoObject): """A group of neurons that collectively represent a vector. Parameters ---------- n_neurons : int The number of neurons. dimensions : int The number of representational dimensions. radius : int, optional The representational radius of the ensemble. encoders : Distribution or ndarray (`n_neurons`, `dimensions`), optional The encoders, used to transform from representational space to neuron space. Each row is a neuron's encoder, each column is a representational dimension. intercepts : Distribution or ndarray (`n_neurons`), optional The point along each neuron's encoder where its activity is zero. If e is the neuron's encoder, then the activity will be zero when dot(x, e) <= c, where c is the given intercept. max_rates : Distribution or ndarray (`n_neurons`), optional The activity of each neuron when dot(x, e) = 1, where e is the neuron's encoder. eval_points : Distribution or ndarray (`n_eval_points`, `dims`), optional The evaluation points used for decoder solving, spanning the interval (-radius, radius) in each dimension, or a distribution from which to choose evaluation points. Default: ``UniformHypersphere``. n_eval_points : int, optional The number of evaluation points to be drawn from the `eval_points` distribution. If None (the default), then a heuristic is used to determine the number of evaluation points. neuron_type : Neurons, optional The model that simulates all neurons in the ensemble. noise : StochasticProcess, optional Random noise injected directly into each neuron in the ensemble as current. A sample is drawn for each individual neuron on every simulation step. seed : int, optional The seed used for random number generation. label : str, optional A name for the ensemble. Used for debugging and visualization. """ n_neurons = IntParam(default=None, low=1) dimensions = IntParam(default=None, low=1) radius = NumberParam(default=1, low=1e-10) neuron_type = NeuronTypeParam(default=LIF()) encoders = DistributionParam(default=UniformHypersphere(surface=True), sample_shape=('n_neurons', 'dimensions')) intercepts = DistributionParam(default=Uniform(-1.0, 1.0), optional=True, sample_shape=('n_neurons', )) max_rates = DistributionParam(default=Uniform(200, 400), optional=True, sample_shape=('n_neurons', )) n_eval_points = IntParam(default=None, optional=True) eval_points = DistributionParam(default=UniformHypersphere(), sample_shape=('*', 'dimensions')) bias = DistributionParam(default=None, optional=True, sample_shape=('n_neurons', )) gain = DistributionParam(default=None, optional=True, sample_shape=('n_neurons', )) noise = StochasticProcessParam(default=None, optional=True) seed = IntParam(default=None, optional=True) label = StringParam(default=None, optional=True) def __init__(self, n_neurons, dimensions, radius=Default, encoders=Default, intercepts=Default, max_rates=Default, eval_points=Default, n_eval_points=Default, neuron_type=Default, gain=Default, bias=Default, noise=Default, seed=Default, label=Default): self.n_neurons = n_neurons self.dimensions = dimensions self.radius = radius self.encoders = encoders self.intercepts = intercepts self.max_rates = max_rates self.label = label self.n_eval_points = n_eval_points self.eval_points = eval_points self.bias = bias self.gain = gain self.neuron_type = neuron_type self.noise = noise self.seed = seed self._neurons = Neurons(self) def __getitem__(self, key): return ObjView(self, key) def __len__(self): return self.dimensions @property def neurons(self): return self._neurons @neurons.setter def neurons(self, dummy): raise AttributeError("neurons cannot be overwritten.") @property def probeable(self): return ["decoded_output", "input"] @property def size_in(self): return self.dimensions @property def size_out(self): return self.dimensions
def test_scattered_hypersphere(dims, surface, seed, plt): scipy_special = pytest.importorskip("scipy.special") n = 3000 dists = [ UniformHypersphere(surface=surface), ScatteredHypersphere(surface=surface, method="sct"), ScatteredHypersphere(surface=surface, method="sct-approx"), ScatteredHypersphere(surface=surface, method="tfww"), ] assert isinstance(dists[0], UniformHypersphere) xx = [] # generated points, for each dist times = [] # time taken to generate the points, for each dist for dist in dists: rng = np.random.RandomState(seed) timer = time.time() x = dist.sample(n, d=dims, rng=rng) timer = time.time() - timer rng.shuffle(x) # shuffle so we can compute distances in blocks without bias xx.append(x) times.append(timer) dd = [] # distance to the nearest point for each point, for each dist rr = [] # radii (norms) of all the generated points, for each dist for x in xx: # compute distances in blocks for efficiency (this means we're not actually # getting the minimum distance, just a proxy) n_split = 1000 d_min = [] for i in range(0, n, n_split): xi = x[i : i + n_split] d2 = ((xi[:, :, None] - xi.T[None, :, :]) ** 2).sum(axis=1) np.fill_diagonal(d2, np.inf) d_min.append(np.sqrt(d2.min(axis=1))) d_min = np.concatenate(d_min) dd.append(d_min) rr.append(np.sqrt((x ** 2).sum(axis=1))) # compute the approximate distance between points if they were evenly spread volume = np.pi ** (0.5 * dims) / scipy_special.gamma(0.5 * dims + 1) if surface: volume *= dims even_distance = (volume / n) ** (1 / (dims - 1 if surface else dims)) # --- plots colors = ["b", "g", "r", "m", "c"] plt.subplot(211) bins = np.linspace(np.min(dd), np.max(dd), 31) for i, d in enumerate(dd): histogram, _ = np.histogram(d, bins=bins) plt.plot( 0.5 * (bins[:-1] + bins[1:]), histogram, colors[i], ) plt.plot([d.min()], [0], colors[i] + "x") plt.plot([even_distance], [0], "kx") plt.title("surface=%s, dims=%d, n=%d" % (surface, dims, n)) plt.subplot(212) bins = np.linspace(0, 1.1, 31) for i, r in enumerate(rr): histogram, _ = np.histogram(r, bins=bins) plt.plot( 0.5 * (bins[:-1] + bins[1:]), histogram, colors[i], label=f"{dists[i]}: t={times[i]:0.2e}", ) plt.legend() # --- checks uniform_min = dd[0].min() for i, dist in enumerate(dists): if i == 0: continue # check that we're significantly better than UniformHypersphere d_min = dd[i].min() assert d_min > 1.2 * uniform_min, str(dist) # check that all surface points are on the surface if surface: assert np.allclose(rr[i], 1.0, atol=1e-5), str(dist)