The best way to understand the concept of a Connection in Brian is to work through Tutorial 2: Connections.
Mechanism for propagating spikes from one group to another
A Connection object declares that when spikes in a source group are generated, certain neurons in the target group should have a value added to specific states. See Tutorial 2: Connections to understand this better.
With arguments:
Methods
Additionally, you can directly access the matrix of weights by writing:
C = Connection(P,Q)
print C[i,j]
C[i,j] = ...
Where here i is the source neuron and j is the target neuron. Note: if C[i,j] should be zero, it is more efficient not to write C[i,j]=0, if you write this then when neuron i fires all the targets will have the value 0 added to them rather than just the nonzero ones. WARNING: No unit checking is currently done if you use this method. Take care to set the right units.
Connection matrix structures
Brian currently features three types of connection matrix structures, each of which is suited for different situations. Brian has two stages of connection matrix. The first is the construction stage, used for building a weight matrix. This stage is optimised for the construction of matrices, with lots of features, but would be slow for runtime behaviour. Consequently, the second stage is the connection stage, used when Brian is being run. The connection stage is optimised for run time behaviour, but many features which are useful for construction are absent (e.g. the ability to add or remove synapses). Conversion between construction and connection stages is done by the compress() method of Connection which is called automatically when it is used for the first time.
The structures are:
Low level methods
Bypasses the usual Brian mechanisms for constructing and compressing matrices, and allows you to directly set the weight (and optionally delay) matrices from scipy sparse matrix objects. This can be more memory efficient, because the default sparse matrix used for construction is scipy.sparse.lil_matrix which is very memory inefficient.
Arguments:
Warning
This is a low-level method that bypasses the usual checks on the data. In particular, make sure that if you pass a weight and delay matrix, that they have the same structure.
Advanced information
The following methods are also defined and used internally, if you are writing your own derived connection class you need to understand what these do.
Connection which implements heterogeneous postsynaptic delays
Initialised as for a Connection, but with the additional keyword:
Overrides the following attribute of Connection:
A matrix of delays. This array can be changed during a run, but at no point should it be greater than max_delay.
In addition, the methods connect, connect_random, connect_full, and connect_one_to_one have a new keyword delay=... for setting the initial values of the delays, where delay can be one of:
Finally, there is a method:
Notes
This class implements post-synaptic delays. This means that the spike is propagated immediately from the presynaptic neuron with the synaptic weight at the time of the spike, but arrives at the postsynaptic neuron with the given delay. At the moment, Brian only provides support for presynaptic delays if they are homogeneous, using the delay keyword of a standard Connection.
Implementation
DelayConnection stores an array of size (n,m) where n is max_delay/dt for dt of the target NeuronGroup‘s clock, and m is the number of neurons in the target. This array can potentially be quite large. Each row in this array represents the array that should be added to the target state variable at some particular future time. Which row corresponds to which time is tracked using a circular indexing scheme.
When a spike from neuron i in the source is encountered, the delay time of neuron i is looked up, the row corresponding to the current time plus that delay time is found using the circular indexing scheme, and then the spike is propagated to that row as for a standard connection (although this won’t be propagated to the target until a later time).
Warning
If you are using a dynamic connection matrix, it is your responsibility to ensure that the nonzero entries of the weight matrix and the delay matrix exactly coincide. This is not an issue for sparse or dense matrices.
A Connection between two groups of the same size, where neuron i in the source group is connected to neuron i in the target group.
Initialised with arguments:
The benefit of this class is that it has no storage requirements and is optimised for this special case.
Base class for connection matrix objects
Connection matrix objects support a subset of the following methods:
The __getitem__ and __setitem__ methods are implemented by default, and automatically select the appropriate methods from the above in the cases where the item to be got or set is of the form :, i,:, :,j or i,j.
Dense connection matrix
See documentation for ConnectionMatrix for details on connection matrix types.
This matrix implements a dense connection matrix. It is just a numpy array. The get_row and get_col methods return DenseConnectionVector` objects.
Sparse connection matrix
See documentation for ConnectionMatrix for details on connection matrix types.
This class implements a sparse matrix with a fixed number of nonzero entries. Row access is very fast, and if the column_access keyword is True then column access is also supported (but is not as fast as row access). If the use_minimal_indices keyword is True then the neuron and synapse indices will use the smallest possible integer type (16 bits for neuron indices if the number of neurons is less than 2**16, otherwise 32 bits). Otherwise, it will use the word size for the CPU architecture (32 or 64 bits).
The matrix should be initialised with a scipy sparse matrix.
The get_row and get_col methods return SparseConnectionVector objects. In addition to the usual slicing operations supported, M[:]=val is supported, where val must be a scalar or an array of length nnz.
Implementation details:
The values are stored in an array alldata of length nnz (number of nonzero entries). The slice alldata[rowind[i]:rowind[i+1]] gives the values for row i. These slices are stored in the list rowdata so that rowdata[i] is the data for row i. The array rowj[i] gives the corresponding column j indices. For row access, the memory requirements are 12 bytes per entry (8 bytes for the float value, and 4 bytes for the column indices). The array allj of length nnz gives the column j coordinates for each element in alldata (the elements of rowj are slices of this array so no extra memory is used).
If column access is being used, then in addition to the above there are lists coli and coldataindices. For column j, the array coli[j] gives the row indices for the data values in column j, while coldataindices[j] gives the indices in the array alldata for the values in column j. Column access therefore involves a copy operation rather than a slice operation. Column access increases the memory requirements to 20 bytes per entry (4 extra bytes for the row indices and 4 extra bytes for the data indices).
TODO: update size numbers when use_minimal_indices=True for different architectures.
Dynamic (sparse) connection matrix
See documentation for ConnectionMatrix for details on connection matrix types.
This class implements a sparse matrix with a variable number of nonzero entries. Row access and column access are provided, but are not as fast as for SparseConnectionMatrix.
The matrix should be initialised with a scipy sparse matrix.
The get_row and get_col methods return SparseConnectionVector objects. In addition to the usual slicing operations supported, M[:]=val is supported, where val must be a scalar or an array of length nnz.
Implementation details
The values are stored in an array alldata of length nnzmax (maximum number of nonzero entries). This is a dynamic array, see:
You can set the resizing constant with the argument dynamic_array_const. Normally the default value 2 is fine but if memory is a worry it could be made smaller.
Rows and column point in to this data array, and the list rowj consists of an array of column indices for each row, with coli containing arrays of row indices for each column. Similarly, rowdataind and coldataind consist of arrays of pointers to the indices in the alldata array.
Base class for construction matrices
A construction matrix is used to initialise and build connection matrices. A ConstructionMatrix class has to implement a method connection_matrix(*args, **kwds) which returns a ConnectionMatrix object of the appropriate type.
Dense construction matrix. Essentially just numpy.ndarray.
The connection_matrix method returns a DenseConnectionMatrix object.
The __setitem__ method is overloaded so that you can set values with a sparse matrix.
SparseConstructionMatrix is converted to SparseConnectionMatrix.
DynamicConstructionMatrix is converted to DynamicConnectionMatrix.
Base class for connection vectors, just used for defining the interface
ConnectionVector objects are returned by ConnectionMatrix objects when they retrieve rows or columns. At the moment, there are two choices, sparse or dense.
This class has no real function at the moment.
Just a numpy array.
Sparse vector class
A sparse vector is typically a row or column of a sparse matrix. This class can be treated in many cases as if it were just a vector without worrying about the fact that it is sparse. For example, if you write 2*v it will evaluate to a new sparse vector. There is one aspect of the semantics which is potentially confusing. In a binary operation with a dense vector such as sv+dv where sv is sparse and dv is dense, the result will be a sparse vector with zeros where sv has zeros, the potentially nonzero elements of dv where sv has no entry will be simply ignored. It is for this reason that it is a SparseConnectionVector and not a general SparseVector, because these semantics make sense for rows and columns of connection matrices but not in general.
Implementation details:
The underlying numpy array contains the values, the attribute n is the length of the sparse vector, and ind is an array of the indices of the nonzero elements.