brian-team/brian2 — pull request #91 by mstimberg (2013-08-13 14:30:34+00:00)
Title: Specifiers rewrite

Ok, I think this is the big chunk of what I want to do for now, it's missing quite a bit of documentation but apart from that it's mostly ready (some subgroup details are still open, see below). I made a couple of decisions that are worth discussing before merging (since it's a massive merge, we do want to get this right...).

I decided to have only exactly one `Variable` (previously: `Specifier`) for each state variable -- this means that `Variable` does neither store the name of the variable (since the name might be e.g. `v_pre` in `Synapses` instead of `v`) nor the index (since the index for `v` can be `_element` in `NeuronGroup` but e.g. `_postsynaptic` in `Synapses`). The advantage: In `Synapses`, `Subgroup` and other future places where we refer to the variables from another `Group`, we can simply refer to the `Variable` objects instead of copying them by hand, replacing some parts like the name. This means we don't have to change anything if a new attribute appears, etc. How are names and indices handled: names are simply the keys in the dictionary, a mapping to indices is done via a `variable_indices` dictionary in `Group` -- by default it is a `defaultdict` mapping everything to `_element` (this means that `NeuronGroup` does not have to care about this at all). Also it means no longer writing redundant things like `specifiers['var'] = Specifier('var', ...)`.

Somewhat related to this: The `iterate_all` information is now stored in the templates (only used in Python anyway), so `NeuronGroup` etc. do not have to care about this.

I also implemented subgroups, although the implementation is not quite correct yet. I think the best thing for subgroups is that it is a user-interface feature and the internal code does not have to care about this. So for example, if we write `Synapses(G[5:], G[5:]); S.connect(0, 0)` this will be internally stored as a connection from neuron 5 to neuron 5, so the propagation code does not have to care about this. This would also avoid having to copy the spikes at every timestep, the only place where we need spike numbers that are specific to the subgroup is in `SpikeMonitor` -- but I think it's best to make `SpikeMonitor` handle this explicitly. This also means that subgroups do not have to be added to a `Network`. There's one weakness in the current implementation, though: If one uses subgroups in the `Synapses` constructor, we store source/target numbers twice to both support correct propagation and state variable assignments involving ``i`` and ``j`` (relative to the subgroup).

To summarize some of the renames: `brian2.core.specifiers` --> `brian2.core.variables`, `Specifier` --> `Variable`, `USE_SPECIFIERS` --> `USES_VARIABLES`, `_neuron_idx` --> `_element_idx` (since it's used for synapses etc. as well).