brian-team / brian2

Brian is a free, open source simulator for spiking neural networks.
http://briansimulator.org
Other
946 stars 222 forks source link

Allow functions to depend on other functions #366

Closed mstimberg closed 9 years ago

mstimberg commented 10 years ago

Some mathematical functions depend other functions, so our function mechanism needs to deal with that. One example is a binomial random number function that will make use of a uniformly distributed random number (i.e. rand()). We'll need that for #221

mstimberg commented 10 years ago

I thought about this a bit in the context of the binomial function. We'll have to decide whether we do function dependencies on the level of the Function object or for FunctionImplementation. For the binomial function, the simplest approach would be to do it on the FunctionImplementation level, because for numpy we will use numpy's binomial function directly and have no need of rand while for C, we'll write our own binomial implementation that makes use of rand.

One could argue for a more complicated approach where we always base it on the rand function, i.e. we'd also write our own version for numpy. This has the advantage that by replacing rand one would have full control about the random number generation, even if it is done indirectly via binomial. I'm not sure it is worth the effort, though.

thesamovar commented 10 years ago

Well we do want to make randomness reproducible and controllable. On the other hand, I don't think there's any need to make it reproducible between different implementation types (e.g. between runtime and standalone), so if we use the numpy RNG for the numpy implementation then that's fine we get reproducibility. So I'd agree: the more complicated system is not necessary.

mstimberg commented 10 years ago

Um, I think I have to propose the exact opposite of what I said before: it should actually be easier to do it on the Function level... The problem is that we do all the namespace resolution, including getting the Function objects for all functions that were used in the code on the abstract code level, before everything is passed into the code generation process. If we take care of function dependencies in the language-dependent code generation, then it is too late to get the Function object for the dependency.

So what about specifying dependencies on the Function level, even though not all implementations might use this dependency? The worst that can happen is that we might have a reference to a function in the namespace dictionary that is not used (numpy) or support code that is not used (weave) -- both should not affect performance. What do you think?

thesamovar commented 10 years ago

I don't think that really works. What if a new language is introduced external to Brian that requires that an implementation depend on a new function. Doing it this way would make that impossible. Is it not possible to do some refactoring to make it implementation-specific or is that going to be a nightmare?

mstimberg commented 10 years ago

Is it not possible to do some refactoring to make it implementation-specific or is that going to be a nightmare?

Well, I realized there is actually an easy solution: instead of specifying the name of the dependency we can give a reference to the Function object. This way, we can access it later even though the name resolution process is already over. Strictly speaking we'd only need the concrete FunctionImplementation object instead of the more general Function object, but I think it's more convenient to write something like:

myfunction = Function(...)
myfunction.implementations.add_implementation('cpp', ...,
                                              dependencies=[DEFAULT_FUNCTIONS['rand']])

instead of:

myfunction.implementations.add_implemenration('cpp', ...,
                                              dependencies=[DEFAULT_FUNCTIONS['rand'].implementations['cpp'])
thesamovar commented 10 years ago

Ok sounds perfect to me.

On 2 December 2014 10:33:39 GMT+00:00, Marcel Stimberg notifications@github.com wrote:

Is it not possible to do some refactoring to make it implementation-specific or is that going to be a nightmare?

Well, I realized there is actually an easy solution: instead of specifying the name of the dependency we can give a reference to the Function object. This way, we can access it later even though the name resolution process is already over. Strictly speaking we'd only need the concrete FunctionImplementation object instead of the more general Function object, but I think it's more convenient to write something like:

myfunction = Function(...)
myfunction.implementations.add_implementation('cpp', ...,
                             dependencies=[DEFAULT_FUNCTIONS['rand']])

instead of:

myfunction.implementations.add_implemenration('cpp', ...,
       dependencies=[DEFAULT_FUNCTIONS['rand'].implementations['cpp'])

Reply to this email directly or view it on GitHub: https://github.com/brian-team/brian2/issues/366#issuecomment-65210920

mstimberg commented 10 years ago

Ok, it seems to work fine this way and it's fairly straightforward (I used a dictionary instead of a list, though, because we also need the name of the function). So this should be fixed now in the poisson_input branch.

mstimberg commented 9 years ago

Closed via #428