======================================================== Representing spatial structure and calculating distances ======================================================== .. currentmodule: pyNN.space The :mod:`space` module contains classes for specifying the locations of neurons in space and for calculating the distances between them. Neuron positions can be defined either manually, using the :attr:`positions` attribute of a :class:`Population` or using a :class:`Structure` instance which is passed to the :class:`Population` constructor. A number of different structures are available in :mod:`space`. It is simple to define your own :class:`Structure` sub-class if you need something that is not already provided. The simplest structure is a grid, whether 1D, 2D or 3D, e.g.: .. testsetup:: from pyNN.random import NumpyRNG .. doctest:: :options: +NORMALIZE_WHITESPACE >>> from pyNN.space import * >>> line = Line(dx=100.0, x0=0.0, y=200.0, z=500.0) >>> line.generate_positions(7) array([[ 0., 100., 200., 300., 400., 500., 600.], [ 200., 200., 200., 200., 200., 200., 200.], [ 500., 500., 500., 500., 500., 500., 500.]]) >>> grid = Grid2D(aspect_ratio=3, dx=10.0, dy=25.0, z=-3.0) >>> grid.generate_positions(3) array([[ 0., 10., 20.], [ 0., 0., 0.], [ -3., -3., -3.]]) >>> grid.generate_positions(12) array([[ 0., 0., 10., 10., 20., 20., 30., 30., 40., 40., 50., 50.], [ 0., 25., 0., 25., 0., 25., 0., 25., 0., 25., 0., 25.], [ -3., -3., -3., -3., -3., -3., -3., -3., -3., -3., -3., -3.]]) Here we have specified an *x*:*y* ratio of 3, so if we ask the grid to generate positions for 3 neurons, we get a 3x1 grid, 12 neurons a 6x2 grid, 27 neurons 9x3, etc. BY default, grid positions are filled sequentially, iterating first over the *z* dimension, then *y*, then *x*, but we can also fill the grid randomly: .. doctest:: >>> rgrid = Grid2D(aspect_ratio=1, dx=10.0, dy=10.0, fill_order='random', rng=NumpyRNG(seed=13886)) >>> rgrid.generate_positions(9) array([[ 10., 10., 10., 0., 0., 0., 20., 20., 20.], [ 0., 10., 20., 10., 20., 0., 0., 10., 20.], [ 0., 0., 0., 0., 0., 0., 0., 0., 0.]]) The :mod:`space` module also provides the :class:`RandomStructure` class, which distributes neurons randomly and uniformly within a given volume: .. doctest:: :options: +NORMALIZE_WHITESPACE >>> glomerulus = RandomStructure(boundary=Sphere(radius=200.0), rng=NumpyRNG(seed=34534)) >>> glomerulus.generate_positions(5) array([[ -19.78455022, 33.21412264, -79.4314059 , 143.39033263, -63.18242977], [ 56.17281502, -23.15159309, 131.89071845, -73.73583484, -8.86422999], [ -78.88348228, -3.97408513, -95.03056844, 45.13969087, -111.67070498]]) The volume classes currently available are :class:`Sphere` and :class:`Cuboid`. Defining your own :class:`Structure` classes is straightforward, just inherit from :class:`BaseStructure` and implement a :meth:`generate_positions` method:: class MyStructure(BaseStructure): parameter_names = ("spam", "eggs") def __init__(self, spam=3, eggs=1): ... def generate_positions(self, n): ... # must return a 3xn numpy array To define your own :class:`Shape` class for use with :class:`RandomStructure`, subclass :class:`Shape` and implement a :meth:`sample` method:: class Tetrahedron(Shape): def __init__(self, side_length): ... def sample(self, n, rng): ... # return a nx3 numpy array. .. note:: rotation of structures is currently missing, but is planned for a future release.