Adding delay differential equation solver

Hi. This is certainly an interesting topic, and one that we started discussing more than 5 years ago (see this issue: https://github.com/brian-team/brian2/issues/467) :grimacing:. I have to admit we did not think about specific algorithms to solve these kind of equations, but rather considered using the standard schemes with access to previous values of variables. But I have to say I donā€™t know much about DDEs and how to solve them, so pointers/code would definitely appreciated.

I do not so much see the problem in generating the code from the equations. Our code generation machinery takes care of this, and we can probably extend it quite easily to support this use case (if I am not mistaken about what is needed for the numerical solution). In the above-linked issue and similar discussions, we encountered two major roadblocks:

  1. We need a data structure to store values from previous time steps. As stated in the issue, this is not too difficult to do, but it means changing internal core machinery in Brian and this is therefore not something that is easy for non-core developers to doā€¦
  2. We need to decide the exact syntax. We kind of settled on a syntax in the issue, but this still needs a bit more thought.

One thing regarding the syntax I am still not sure about: the proposed syntax is super-general in the sense that the delay can be different for different variables, can change over time, etc. This is certainly nice, but I think the most common use case is a single delay per connection. In this case, you could also expect something like

delayed_connection = Synapses(..., '''w : 1
                                      inp_post = rate_pre * w : Hz (summed)''',
                              delay=5*ms)

to work, but synaptic delays are tied to pathways, i.e. ā€œon_preā€ or ā€œon_postā€ events. But maybe an easier solution than the proposed new syntax for ā€œbuffered variablesā€, would be to somehow integrate things in the Synapses definition. E.g. something like:

delayed_connection = Synapses(..., '''w : 1
                                      inp_post = rate_pre * w : Hz (summed, delay=5*ms)''')

or

delayed_connection = Synapses(..., '''w : 1
                                      inp_post = rate_pre * w : Hz (summed)''')
delayed_connection.inp_post_update.delay = 5*ms

Just to get some idea. What do delays look like in your use case (e.g. do they differ across connections, do they change over time, ā€¦?)

And finally, at the risk of losing users to competitors: I think the ANNarchy simulator supports rate-based connections with delaysā€¦