brian-team/brian2 — pull request #223 by mstimberg (2014-02-24 20:42:23+00:00)
Title: Scalar variable write

This is not ready to merge yet, it doesn't have any tests yet and the documentation is lacking as well.

The major work for this feature is done and it seems to be working fine (I think we postpone integrating #174 into it to after the next release?).

The basic approach is the following:

* Instead of one `code_lines` list/string/dictionary, the templates get a `scalar_code` and a `vector_code` and need to have two corresponding blocks (in C++, the `scalar_code` block is outside of the loop).
* To implement the transformation from abstract code to these two blocks, the solution I implemented (because it meant the least changes to the code) is to call all translation functions (e.g. `translate_one_statement_sequence`) twice with *all the statements* each time, but only generating code for the scalar/non-scalar statements.
* Contrary to what I said in #205, we *do* actually need to put restrictions on the code since all the abstract code will be executed before all the vector code, abstract code such as `vector += scalar; scalar +=1` would therefore *first* increase `scalar`... So we raise an error when scalar code follows vector code
* The `make_statements` function figures out whether newly introduced variables in the abstract code are scalar (all the variables on the RHS are scalar) or not. This actually leads to a subtle distinction in abstract code: `s = rand(); v += s` will produce a vector of random numbers if `s` is not defined and a single random number if `s` is defined as a scalar variable.
* Templates have to "allow" writing to scalar variables (i.e. state variables -- writing to scalar temporary variables is always possible) by using `ALLOWS_SCALAR_WRITE`. Only the state update template (for `CodeRunner`) and the `group_variable_set_conditional` template (see below) do that currently. All templates need to deal with `scalar_code` blocks nevertheless, since the reading/calculation of scalar expressions should be done outside of the loop.

Some issues:
* Mostly for standalone convenience, writing `G.var = 'expression'` is equivalent to writing `G.var['True'] = 'expression'`. If we want to allow to write to scalar variables with string expressions (of course only referring to scalar variables), we therefore have to allow the `group_variable_set_conditional` template to allow scalar writes. `Group.set_with_expression_conditional` has a check at the beginning and does only allow writing to scalar variables when the condition equals `'True'`. Maybe this could be handled more elegantly. Also, in runtime code we bypass code generation for array indices if the RHS is not a string expression. This means that `G.var[0] = x` or `G.var[:] = x` is ok for a scalar variable `v` but `G.scalar[0] = 'x'` is not.
* I did not to implement special recording for scalar variables in `StateMonitor` (and I'm not 100% sure whether it's necessary): recording works but it will record the scalar value once for every neuron. It currently raises a warning if one does record a scalar variable for more than one neuron.

That's all I can think of right now, I changed the barrelcortex example a bit to work with the scalar variables and it seems to run fine (after making `Group.runner` work again), the code is not yet fully correct, though. If it works, it will be a really nice example, I think.

I'll have a look into scalar sub expressions before we finalizing this branch, I think it can be done with almost no additional work.

Closes #205.