A `Connection` object has an attribute `W` which is its connection
matrix.

Brian’s system for connection matrices can be slightly confusing. The way it
works is roughly as follows. There are two types of connection matrix data
structures, `ConstructionMatrix` and `ConnectionMatrix`. The
construction matrix types are used for building connectivity, and are optimised
for insertion and deletion of elements, but access is slow. The connection
matrix types are used when the simulation is running, and are optimised for
fast access, but not for adding/removing or modifying elements. When a
`Connection` object is created, it is given a construction matrix data
type, and when the network is run, this matrix is converted to its corresponding
connection matrix type. As well as this construction/connection matrix type
distinction, there is also the distinction between dense/sparse/dynamic matrices,
each of which have their own construction and connection versions.

The dense matrix structure is very simple, both the construction and connection types are basically just 2D numpy arrays.

The sparse and dynamic matrix structures are very different for construction
and connection. Both the sparse and dynamic construction matrices are
essentially just the `scipy.lil_matrix` sparse matrix type, however we add
some slight improvements to scipy’s matrix data type to make it more efficient
for our case.

The sparse and dynamic connection matrix structures are documented in more
detail in the reference pages for `SparseConnectionMatrix` and
`DynamicConnectionMatrix`.

For customised run-time modifications to sparse and dense connection matrices
you have two options. You can modify the data structures directly using the
information in the reference pages linked to in the paragraph above, or you can
use the methods defined in the `ConnectionMatrix` class, which work for
dense, sparse and dynamic matrix structures, and do not depend on implementation
specific details. These methods provide element, row and column access. The
row and column access methods use either `DenseConnectionVector`
or `SparseConnectionVector` objects. The dense connection vector is just
a 1D numpy array of length the size of the row/column. The sparse connection
vector is slightly more complicated (but not much), see its documentation for
details. The idea is that in most cases, both dense and sparse connection vectors
can be operated on without having to know how they work, so for example if `v`
is a `ConnectionVector` then `2*v` is of the same type. So for a
`ConnectionMatrix` `W`, this should work, whatever the structure of
`W`:

```
W.set_row(i, 2*W.get_row(i))
```

Or equivalently:

```
W[i,:] = 2*W[i,:]
```

The syntax `W[i,:]`, `W[:,i]` and `W[i,j]` is supported for integers `i`
and `j` for (respectively) row, column and element access.