Skip to content

Function implementations

Denis Alevi edited this page Aug 19, 2020 · 1 revision

Brian2 function implementations

Different implementations for host and device code

  • Currently used e.g. in synapses_create_generator templates, which are exclusively run on the host but might need function implementations which are also needed in device code.
  • Use __host__ __device__ function declarations to generate a function code for both, host and device usage.
  • To have different implementations, use preprocessor makros in the function implementation:
    #if (defined(__CUDA_ARCH__) && (__CUDA_ARCH__ > 0))
    // device code
    #else
    // host code
    #endif
    

Implementations depending on codeobject owner

  • Use Function.implementations.add_dynamic_implementation(code_generating_function). The code_generating_function is called with the codeobject owner, which can be accessed inside the code generating function.

How adding implementations works

  • Brian2 Function objects have different implementations which are used depending on the device that is set for a simulation (through set_device(...)).
  • The Function class defines a Function.implementations attribute, which is an instance of FunctionImplementationContainer, which inherits from collections.Mapping, which is a write-only container. This container is a one-way mapping, where the __getitem__ method (which is called for when an instance is used with []) returns the code for a given target language (e.g. some_function.implementations['cpp'] returns the cpp implementation of that function), but it can't be used to set the implemention (read-only).
  • To add an implementations, one has to use the Function.implementations.add_implementation() or Function.implementation.add_dynamic_implementation() method.
  • The BinomialFunction class on the other hand does not need that. It implements a global class attribute BinomialFunction.implementations, which is a dictionary mapping language (e.g. cpp or cuda) to a Python function that returns the code implementation. Once an instance of BinomialFunction is created, the inherited Function class is initialized, which then instantiates its Function.implementations attribute. Therefore, there are two implementations attributes. One is BinomialFunction.implementations, which is just a dictionary. And binomial_function_instance.implementations, which is a FunctionImplementationContainer instance.
  • To add an implementation to BinomialFunction, just assign it to the BinomialFunction.implementations dictionary. Whenever the class is instatiated, all implementations in that dictionary will be automatically added to the instances binomial_function_instance.implementations objects (which is a read-only FunctionImplementationContainer instance from above).