Exemplo n.º 1
0
def _generate_cpp_code(n, p, use_normal, name):
    # C++ implementation
    # Inversion transform sampling
    if use_normal:
        loc, scale = _pre_calc_constants_approximated(n, p)
        loc = n * p
        scale = np.sqrt(n * p * (1 - p))
        cpp_code = '''
        float %NAME%(const int vectorisation_idx)
        {
            return _randn(vectorisation_idx) * %SCALE% + %LOC%;
        }
        '''
        cpp_code = replace(cpp_code, {
            '%SCALE%': '%.15f' % scale,
            '%LOC%': '%.15f' % loc,
            '%NAME%': name
        })
        dependencies = {'_randn': DEFAULT_FUNCTIONS['randn']}
    else:
        reverse, q, P, qn, bound = _pre_calc_constants(n, p)
        # The following code is an almost exact copy of numpy's
        # rk_binomial_inversion function
        # (numpy/random/mtrand/distributions.c)
        cpp_code = '''
        long %NAME%(const int vectorisation_idx)
        {
            long X = 0;
            double px = %QN%;
            double U = _rand(vectorisation_idx);
            while (U > px)
            {
                X++;
                if (X > %BOUND%)
                {
                    X = 0;
                    px = %QN%;
                    U = _rand(vectorisation_idx);
                } else
                {
                    U -= px;
                    px = ((%N%-X+1) * %P% * px)/(X*%Q%);
                }
            }
            return %RETURN_VALUE%;
        }
        '''
        cpp_code = replace(
            cpp_code, {
                '%N%': '%d' % n,
                '%P%': '%.15f' % P,
                '%Q%': '%.15f' % q,
                '%QN%': '%.15f' % qn,
                '%BOUND%': '%.15f' % bound,
                '%RETURN_VALUE%': '%d-X' % n if reverse else 'X',
                '%NAME%': name
            })
        dependencies = {'_rand': DEFAULT_FUNCTIONS['rand']}

    return {'support_code': cpp_code}, dependencies
Exemplo n.º 2
0
def _generate_cpp_code(n, p, use_normal, name):
    # C++ implementation
    # Inversion transform sampling
    if use_normal:
        loc, scale = _pre_calc_constants_approximated(n, p)
        loc = n*p
        scale = np.sqrt(n*p*(1-p))
        cpp_code = '''
        float %NAME%(const int vectorisation_idx)
        {
            return _randn(vectorisation_idx) * %SCALE% + %LOC%;
        }
        '''
        cpp_code = replace(cpp_code, {'%SCALE%': '%.15f' % scale,
                                      '%LOC%': '%.15f' % loc,
                                      '%NAME%': name})
        dependencies = {'_randn': DEFAULT_FUNCTIONS['randn']}
    else:
        reverse, q, P, qn, bound = _pre_calc_constants(n, p)
        # The following code is an almost exact copy of numpy's
        # rk_binomial_inversion function
        # (numpy/random/mtrand/distributions.c)
        cpp_code = '''
        long %NAME%(const int vectorisation_idx)
        {
            long X = 0;
            double px = %QN%;
            double U = _rand(vectorisation_idx);
            while (U > px)
            {
                X++;
                if (X > %BOUND%)
                {
                    X = 0;
                    px = %QN%;
                    U = _rand(vectorisation_idx);
                } else
                {
                    U -= px;
                    px = ((%N%-X+1) * %P% * px)/(X*%Q%);
                }
            }
            return %RETURN_VALUE%;
        }
        '''
        cpp_code = replace(cpp_code, {'%N%': '%d' % n,
                                      '%P%': '%.15f' % P,
                                      '%Q%': '%.15f' % q,
                                      '%QN%': '%.15f' % qn,
                                      '%BOUND%': '%.15f' % bound,
                                      '%RETURN_VALUE%': '%d-X' % n if reverse else 'X',
                                      '%NAME%': name})
        dependencies = {'_rand': DEFAULT_FUNCTIONS['rand']}

    return {'support_code': cpp_code}, dependencies
Exemplo n.º 3
0
def _generate_cython_code(n, p, use_normal, name):
    # Cython implementation
    # Inversion transform sampling
    if use_normal:
        loc, scale = _pre_calc_constants_approximated(n, p)
        cython_code = """
        cdef float %NAME%(const int vectorisation_idx):
            return _randn(vectorisation_idx) * %SCALE% + %LOC%
        """
        cython_code = replace(cython_code, {
            '%SCALE%': f'{scale:.15f}',
            '%LOC%': f'{loc:.15f}',
            '%NAME%': name
        })
        dependencies = {'_randn': DEFAULT_FUNCTIONS['randn']}
    else:
        reverse, q, P, qn, bound = _pre_calc_constants(n, p)
        # The following code is an almost exact copy of numpy's
        # rk_binomial_inversion function
        # (numpy/random/mtrand/distributions.c)
        cython_code = """
        cdef long %NAME%(const int vectorisation_idx):
            cdef long X = 0
            cdef double px = %QN%
            cdef double U = _rand(vectorisation_idx)
            while U > px:
                X += 1
                if X > %BOUND%:
                    X = 0
                    px = %QN%
                    U = _rand(vectorisation_idx)
                else:
                    U -= px
                    px = ((%N%-X+1) * %P% * px)/(X*%Q%)
            return %RETURN_VALUE%
        """
        cython_code = replace(
            cython_code, {
                '%N%': f'{int(n)}',
                '%P%': f'{p:.15f}',
                '%Q%': f'{q:.15f}',
                '%QN%': f'{qn:.15f}',
                '%BOUND%': f'{bound:.15f}',
                '%RETURN_VALUE%': f'{int(n)}-X' if reverse else 'X',
                '%NAME%': name
            })
        dependencies = {'_rand': DEFAULT_FUNCTIONS['rand']}

    return cython_code, dependencies
Exemplo n.º 4
0
        def create_cython_implementation(owner):
            group_dt = owner.clock.dt_
            K = _find_K(group_dt, dt)
            code = '''
            cdef double %NAME%(const double t, const int i):
                global _namespace%NAME%_values
                cdef double epsilon = %DT% / %K%
                if i < 0 or i >= %COLS%:
                    return _numpy.nan
                cdef int timestep = (int)((t/epsilon + 0.5)/%K%)
                if timestep < 0:
                   timestep = 0
                elif timestep >= %ROWS%:
                    timestep = %ROWS%-1
                return _namespace%NAME%_values[timestep*%COLS% + i]
            '''
            code = replace(
                code, {
                    '%NAME%': self.name,
                    '%DT%': '%.18f' % dt,
                    '%K%': str(K),
                    '%COLS%': str(self.values.shape[1]),
                    '%ROWS%': str(self.values.shape[0])
                })

            return code
Exemplo n.º 5
0
        def create_cpp_implementation(owner):
            group_dt = owner.clock.dt_
            K = _find_K(group_dt, dt)
            support_code = '''
            inline double %NAME%(const double t, const int i)
            {
                const double epsilon = %DT% / %K%;
                if (i < 0 || i >= %COLS%)
                    return NAN;
                int timestep = (int)((t/epsilon + 0.5)/%K%);
                if(timestep < 0)
                   timestep = 0;
                else if(timestep >= %ROWS%)
                    timestep = %ROWS%-1;
                return _namespace%NAME%_values[timestep*%COLS% + i];
            }
            '''
            support_code = replace(support_code, {'%NAME%': self.name,
                                                  '%DT%': '%.18f' % dt,
                                                  '%K%': str(K),
                                                  '%COLS%': str(self.values.shape[1]),
                                                  '%ROWS%': str(self.values.shape[0])})
            cpp_code = {'support_code': support_code}

            return cpp_code
Exemplo n.º 6
0
        def create_cython_implementation(owner):
            group_dt = owner.clock.dt_
            K = _find_K(group_dt, dt)
            code = """
            cdef double %NAME%(const double t, const int i):
                global _namespace%NAME%_values
                cdef double epsilon = %DT% / %K%
                if i < 0 or i >= %COLS%:
                    return _numpy.nan
                cdef int timestep = (int)(((t-%OFFSET%)/epsilon + 0.5)/%K%)
                if timestep < 0:
                   timestep = 0
                elif timestep >= %ROWS%:
                    timestep = %ROWS%-1
                return _namespace%NAME%_values[timestep*%COLS% + i]
            """
            code = replace(
                code,
                {
                    "%NAME%": self.name,
                    "%DT%": "%.18f" % dt,
                    "%K%": str(K),
                    "%COLS%": str(self.values.shape[1]),
                    "%ROWS%": str(self.values.shape[0]),
                    "%OFFSET%": str(np.asarray(tOffset)),
                },
            )

            return code
Exemplo n.º 7
0
        def create_cpp_implementation(owner):
            group_dt = owner.clock.dt_
            K = _find_K(group_dt, dt)
            support_code = """
            inline double %NAME%(const double t, const int i)
            {
                const double epsilon = %DT% / %K%;
                if (i < 0 || i >= %COLS%)
                    return NAN;
                int timestep = (int)(((t-%OFFSET%)/epsilon + 0.5)/%K%);
                if(timestep < 0)
                   timestep = 0;
                else if(timestep >= %ROWS%)
                    timestep = %ROWS%-1;
                return _namespace%NAME%_values[timestep*%COLS% + i];
            }
            """
            support_code = replace(
                support_code,
                {
                    "%NAME%": self.name,
                    "%DT%": "%.18f" % dt,
                    "%K%": str(K),
                    "%COLS%": str(self.values.shape[1]),
                    "%ROWS%": str(self.values.shape[0]),
                    "%OFFSET%": str(np.asarray(tOffset)),
                },
            )
            cpp_code = {"support_code": support_code}

            return cpp_code
Exemplo n.º 8
0
    def cuda_impl(owner):
        K = _find_K(owner.clock.dt_, dt)
        code = '''
        __host__ __device__
        static inline double %NAME%(const double t, const int i)
        {
            const double epsilon = %DT% / %K%;
            if (i < 0 || i >= %COLS%)
                return NAN;
            int timestep = (int)((t/epsilon + 0.5)/%K%);
            if(timestep < 0)
               timestep = 0;
            else if(timestep >= %ROWS%)
                timestep = %ROWS%-1;
            return _namespace%NAME%_values[timestep*%COLS% + i];

        }
        '''
        code = replace(
            code, {
                '%NAME%': name,
                '%DT%': f'{dt:.18f}',
                '%K%': str(K),
                '%COLS%': str(values.shape[1]),
                '%ROWS%': str(values.shape[0])
            })
        return code
Exemplo n.º 9
0
        def create_cpp_implementation(owner):
            group_dt = owner.clock.dt_
            K = _find_K(group_dt, dt)
            support_code = '''
            static inline double %NAME%(const double t, const int i)
            {
                const double epsilon = %DT% / %K%;
                if (i < 0 || i >= %COLS%)
                    return NAN;
                int timestep = (int)((t/epsilon + 0.5)/%K%);
                if(timestep < 0)
                   timestep = 0;
                else if(timestep >= %ROWS%)
                    timestep = %ROWS%-1;
                return _namespace%NAME%_values[timestep*%COLS% + i];
            }
            '''
            support_code = replace(
                support_code, {
                    '%NAME%': self.name,
                    '%DT%': '%.18f' % dt,
                    '%K%': str(K),
                    '%COLS%': str(self.values.shape[1]),
                    '%ROWS%': str(self.values.shape[0])
                })
            cpp_code = {'support_code': support_code}

            return cpp_code
Exemplo n.º 10
0
def _generate_cython_code(n, p, use_normal, name):
    # Cython implementation
    # Inversion transform sampling
    if use_normal:
        loc, scale = _pre_calc_constants_approximated(n, p)
        cython_code = '''
        cdef float %NAME%(const int vectorisation_idx):
            return _randn(vectorisation_idx) * %SCALE% + %LOC%
        '''
        cython_code = replace(cython_code, {'%SCALE%': '%.15f' % scale,
                                            '%LOC%': '%.15f' % loc,
                                            '%NAME%': name})
        dependencies = {'_randn': DEFAULT_FUNCTIONS['randn']}
    else:
        reverse, q, P, qn, bound = _pre_calc_constants(n, p)
        # The following code is an almost exact copy of numpy's
        # rk_binomial_inversion function
        # (numpy/random/mtrand/distributions.c)
        cython_code = '''
        cdef long %NAME%(const int vectorisation_idx):
            cdef long X = 0
            cdef double px = %QN%
            cdef double U = _rand(vectorisation_idx)
            while U > px:
                X += 1
                if X > %BOUND%:
                    X = 0
                    px = %QN%
                    U = _rand(vectorisation_idx)
                else:
                    U -= px
                    px = ((%N%-X+1) * %P% * px)/(X*%Q%)
            return %RETURN_VALUE%
        '''
        cython_code = replace(cython_code, {'%N%': '%d' % n,
                                            '%P%': '%.15f' % p,
                                            '%Q%': '%.15f' % q,
                                            '%QN%': '%.15f' % qn,
                                            '%BOUND%': '%.15f' % bound,
                                            '%RETURN_VALUE%': '%d-X' % n if reverse else 'X',
                                            '%NAME%': name})
        dependencies = {'_rand': DEFAULT_FUNCTIONS['rand']}

    return cython_code, dependencies
Exemplo n.º 11
0
 def cython_impl(owner):
     K = _find_K(owner.clock.dt_, dt)
     code = """
     cdef double %NAME%(const double t, const int i):
         global _namespace%NAME%_values
         cdef double epsilon = %DT% / %K%
         if i < 0 or i >= %COLS%:
             return _numpy.nan
         cdef int timestep = (int)((t/epsilon + 0.5)/%K%)
         if timestep < 0:
            timestep = 0
         elif timestep >= %ROWS%:
             timestep = %ROWS%-1
         return _namespace%NAME%_values[timestep*%COLS% + i]
     """
     code = replace(code, {'%NAME%': name,
                           '%DT%': f'{dt:.18f}',
                           '%K%': str(K),
                           '%COLS%': str(values.shape[1]),
                           '%ROWS%': str(values.shape[0])})
     return code
Exemplo n.º 12
0
def generate_rand_code(rand_func, owner):
    nb_threads = prefs.devices.cpp_standalone.openmp_threads
    if nb_threads == 0:  # no OpenMP
        thread_number = '0'
    else:
        thread_number = 'omp_get_thread_num()'
    if rand_func == 'rand':
        rk_call = 'rk_double'
    elif rand_func == 'randn':
        rk_call = 'rk_gauss'
    else:
        raise AssertionError(rand_func)
    code = '''
           double _%RAND_FUNC%(const int _vectorisation_idx) {
               return %RK_CALL%(brian::_mersenne_twister_states[%THREAD_NUMBER%]);
           }
           '''
    code = replace(code, {'%THREAD_NUMBER%': thread_number,
                          '%RAND_FUNC%': rand_func,
                          '%RK_CALL%': rk_call})
    return {'support_code': code}
Exemplo n.º 13
0
def generate_rand_code(rand_func, owner):
    nb_threads = prefs.devices.cpp_standalone.openmp_threads
    if nb_threads == 0:  # no OpenMP
        thread_number = '0'
    else:
        thread_number = 'omp_get_thread_num()'
    if rand_func == 'rand':
        rk_call = 'rk_double'
    elif rand_func == 'randn':
        rk_call = 'rk_gauss'
    else:
        raise AssertionError(rand_func)
    code = """
           double _%RAND_FUNC%(const int _vectorisation_idx) {
               return %RK_CALL%(brian::_mersenne_twister_states[%THREAD_NUMBER%]);
           }
           """
    code = replace(code, {'%THREAD_NUMBER%': thread_number,
                          '%RAND_FUNC%': rand_func,
                          '%RK_CALL%': rk_call})
    return {'support_code': code}
Exemplo n.º 14
0
        def create_cython_implementation(owner):
            group_dt = owner.clock.dt_
            K = _find_K(group_dt, dt)
            code = '''
            cdef double %NAME%(const double t, const int i):
                global _namespace%NAME%_values
                cdef double epsilon = %DT% / %K%;
                if i < 0 or i >= %COLS%:
                    return _numpy.nan
                cdef int timestep = (int)((t/epsilon + 0.5)/%K%)
                if timestep < 0:
                   timestep = 0
                elif timestep >= %ROWS%:
                    timestep = %ROWS%-1
                return _namespace%NAME%_values[timestep*%COLS% + i]
            '''
            code = replace(code, {'%NAME%': self.name,
                                  '%DT%': '%.18f' % dt,
                                  '%K%': str(K),
                                  '%COLS%': str(self.values.shape[1]),
                                  '%ROWS%': str(self.values.shape[0])})

            return code
Exemplo n.º 15
0
    def __init__(self, n, p, approximate=True, name='_binomial*'):
        Nameable.__init__(self, name)

        #Python implementation
        use_normal = approximate and (n * p > 5) and n * (1 - p) > 5
        if use_normal:
            loc = n * p
            scale = np.sqrt(n * p * (1 - p))

            def sample_function(vectorisation_idx):
                try:
                    N = len(vectorisation_idx)
                except TypeError:
                    N = int(vectorisation_idx)
                return np.random.normal(loc, scale, size=N)
        else:

            def sample_function(vectorisation_idx):
                try:
                    N = len(vectorisation_idx)
                except TypeError:
                    N = int(vectorisation_idx)
                return np.random.binomial(n, p, size=N)

        Function.__init__(self,
                          pyfunc=lambda: sample_function(1),
                          arg_units=[],
                          return_unit=1,
                          stateless=False)
        self.implementations.add_implementation('numpy', sample_function)

        # Common pre-calculations for C++ and Cython
        if use_normal:
            loc = n * p
            scale = np.sqrt(n * p * (1 - p))
        else:
            reverse = p > 0.5
            if reverse:
                P = 1.0 - p
            else:
                P = p
            q = 1.0 - P
            qn = np.exp(n * np.log(q))
            bound = min(n, n * P + 10.0 * np.sqrt(n * P * q + 1))

        # C++ implementation
        # Inversion transform sampling
        if use_normal:
            loc = n * p
            scale = np.sqrt(n * p * (1 - p))
            cpp_code = '''
            float %NAME%(const int vectorisation_idx)
            {
                return _randn(vectorisation_idx) * %SCALE% + %LOC%;
            }
            '''
            cpp_code = replace(
                cpp_code, {
                    '%SCALE%': '%.15f' % scale,
                    '%LOC%': '%.15f' % loc,
                    '%NAME%': self.name
                })
            dependencies = {'_randn': DEFAULT_FUNCTIONS['randn']}
        else:
            # The following code is an almost exact copy of numpy's
            # rk_binomial_inversion function
            # (numpy/random/mtrand/distributions.c)
            cpp_code = '''
            long %NAME%(const int vectorisation_idx)
            {
                long X = 0;
                double px = %QN%;
                double U = _rand(vectorisation_idx);
                while (U > px)
                {
                    X++;
                    if (X > %BOUND%)
                    {
                        X = 0;
                        px = %QN%;
                        U = _rand(vectorisation_idx);
                    } else
                    {
                        U -= px;
                        px = ((%N%-X+1) * %P% * px)/(X*%Q%);
                    }
                }
                return %RETURN_VALUE%;
            }
            '''
            cpp_code = replace(
                cpp_code, {
                    '%N%': '%d' % n,
                    '%P%': '%.15f' % P,
                    '%Q%': '%.15f' % q,
                    '%QN%': '%.15f' % qn,
                    '%BOUND%': '%.15f' % bound,
                    '%RETURN_VALUE%': '%d-X' % n if reverse else 'X',
                    '%NAME%': self.name
                })
            dependencies = {'_rand': DEFAULT_FUNCTIONS['rand']}

        self.implementations.add_implementation('cpp',
                                                {'support_code': cpp_code},
                                                dependencies=dependencies,
                                                name=self.name)

        # Cython implementation
        # Inversion transform sampling
        if use_normal:
            cython_code = '''
            cdef float %NAME%(const int vectorisation_idx):
                return _randn(vectorisation_idx) * %SCALE% + %LOC%
            '''
            cython_code = replace(
                cython_code, {
                    '%SCALE%': '%.15f' % scale,
                    '%LOC%': '%.15f' % loc,
                    '%NAME%': self.name
                })
            dependencies = {'_randn': DEFAULT_FUNCTIONS['randn']}
        else:
            # The following code is an almost exact copy of numpy's
            # rk_binomial_inversion function
            # (numpy/random/mtrand/distributions.c)
            cython_code = '''
            cdef long %NAME%(const int vectorisation_idx):
                cdef long X = 0
                cdef double px = %QN%
                cdef double U = _rand(vectorisation_idx)
                while U > px:
                    X += 1
                    if X > %BOUND%:
                        X = 0
                        px = %QN%
                        U = _rand(vectorisation_idx)
                    else:
                        U -= px
                        px = ((%N%-X+1) * %P% * px)/(X*%Q%)
                return %RETURN_VALUE%
            '''
            cython_code = replace(
                cython_code, {
                    '%N%': '%d' % n,
                    '%P%': '%.15f' % p,
                    '%Q%': '%.15f' % q,
                    '%QN%': '%.15f' % qn,
                    '%BOUND%': '%.15f' % bound,
                    '%RETURN_VALUE%': '%d-X' % n if reverse else 'X',
                    '%NAME%': self.name
                })
            dependencies = {'_rand': DEFAULT_FUNCTIONS['rand']}

        self.implementations.add_implementation('cython',
                                                cython_code,
                                                dependencies=dependencies,
                                                name=self.name)
Exemplo n.º 16
0
def _generate_cuda_code(n, p, use_normal, name):
    # CUDA implementation
    # Inversion transform sampling
    if not '_binomial' in name:
        # TODO: mark issue
        raise NameError("Currently the `name` parameter of `BinomialFunction` "
                        "needs to have '_binomial' in it, got "
                        "'{}'".format(name))
    float_suffix = ''
    float_dtype = 'float'
    if prefs['core.default_float_dtype'] == np.float64:
        float_suffix = '_double'
        float_dtype = 'double'
    # TODO: we should load the state once in scalar code and pass as function
    # argument, needs modification of CUDAGenerator._add_user_function to
    # modify scalar code
    if use_normal:
        loc, scale = _pre_calc_constants_approximated(n, p)
        cuda_code = '''
        __host__ __device__
        %DTYPE% %NAME%(const int vectorisation_idx)
        {
        #if (defined(__CUDA_ARCH__) && (__CUDA_ARCH__ > 0))
            return curand_normal%SUFFIX%(brian::d_curand_states + vectorisation_idx) * %SCALE% + %LOC%;
        #else
            return host_randn(vectorisation_idx) * %SCALE% + %LOC%;
        #endif
        }
        '''
        cuda_code = replace(
            cuda_code, {
                '%SCALE%': '%.15f' % scale,
                '%LOC%': '%.15f' % loc,
                '%NAME%': name,
                '%DTYPE%': float_dtype,
                '%SUFFIX%': float_suffix
            })
        dependencies = {'_randn': DEFAULT_FUNCTIONS['randn']}
    else:
        reverse, q, P, qn, bound = _pre_calc_constants(n, p)
        # The following code is an almost exact copy of numpy's
        # rk_binomial_inversion function
        # (numpy/random/mtrand/distributions.c)
        cuda_code = '''
        __host__ __device__
        long %NAME%(const int vectorisation_idx)
        {
        #if (defined(__CUDA_ARCH__) && (__CUDA_ARCH__ > 0))
            curandState localState = brian::d_curand_states[vectorisation_idx];
            %DTYPE% U = curand_uniform%SUFFIX%(&localState);
        #else
            %DTYPE% U = host_rand(vectorisation_idx);
        #endif
            long X = 0;
            %DTYPE% px = %QN%;
            while (U > px)
            {
                X++;
                if (X > %BOUND%)
                {
                    X = 0;
                    px = %QN%;
        #if (defined(__CUDA_ARCH__) && (__CUDA_ARCH__ > 0))
                    U = curand_uniform%SUFFIX%(&localState);
        #else
                    U = host_rand(vectorisation_idx);
        #endif
                } else
                {
                    U -= px;
                    px = ((%N%-X+1) * %P% * px)/(X*%Q%);
                }
            }
        #if (defined(__CUDA_ARCH__) && (__CUDA_ARCH__ > 0))
            // copy the locally changed CuRAND state back to global memory
            brian::d_curand_states[vectorisation_idx] = localState;
        #endif
            return %RETURN_VALUE%;
        }
        '''
        cuda_code = replace(
            cuda_code, {
                '%N%': '%d' % n,
                '%P%': '%.15f' % P,
                '%Q%': '%.15f' % q,
                '%QN%': '%.15f' % qn,
                '%BOUND%': '%.15f' % bound,
                '%RETURN_VALUE%': '%d-X' % n if reverse else 'X',
                '%NAME%': name,
                '%DTYPE%': float_dtype,
                '%SUFFIX%': float_suffix
            })
        dependencies = {'_rand': DEFAULT_FUNCTIONS['rand']}

    return {'support_code': cuda_code}, dependencies
Exemplo n.º 17
0
    def __init__(self, n, p, approximate=True, name='_binomial*'):
        Nameable.__init__(self, name)

        #Python implementation
        use_normal = approximate and (n*p > 5) and n*(1-p) > 5
        if use_normal:
            loc = n*p
            scale = np.sqrt(n*p*(1-p))
            def sample_function(vectorisation_idx):
                try:
                    N = len(vectorisation_idx)
                except TypeError:
                    N = int(vectorisation_idx)
                return np.random.normal(loc, scale, size=N)
        else:
            def sample_function(vectorisation_idx):
                try:
                    N = len(vectorisation_idx)
                except TypeError:
                    N = int(vectorisation_idx)
                return np.random.binomial(n, p, size=N)

        Function.__init__(self, pyfunc=lambda: sample_function(1),
                          arg_units=[], return_unit=1, stateless=False)
        self.implementations.add_implementation('numpy', sample_function)

        # Common pre-calculations for C++ and Cython
        if use_normal:
            loc = n*p
            scale = np.sqrt(n*p*(1-p))
        else:
            reverse = p > 0.5
            if reverse:
                P = 1.0 - p
            else:
                P = p
            q = 1.0 - P
            qn = np.exp(n * np.log(q))
            bound = min(n, n*P + 10.0*np.sqrt(n*P*q + 1))

        # C++ implementation
        # Inversion transform sampling
        if use_normal:
            loc = n*p
            scale = np.sqrt(n*p*(1-p))
            cpp_code = '''
            float %NAME%(const int vectorisation_idx)
            {
                return _randn(vectorisation_idx) * %SCALE% + %LOC%;
            }
            '''
            cpp_code = replace(cpp_code, {'%SCALE%': '%.15f' % scale,
                                          '%LOC%': '%.15f' % loc,
                                          '%NAME%': self.name})
            dependencies = {'_randn': DEFAULT_FUNCTIONS['randn']}
        else:
            # The following code is an almost exact copy of numpy's
            # rk_binomial_inversion function
            # (numpy/random/mtrand/distributions.c)
            cpp_code = '''
            long %NAME%(const int vectorisation_idx)
            {
                long X = 0;
                double px = %QN%;
                double U = _rand(vectorisation_idx);
                while (U > px)
                {
                    X++;
                    if (X > %BOUND%)
                    {
                        X = 0;
                        px = %QN%;
                        U = _rand(vectorisation_idx);
                    } else
                    {
                        U -= px;
                        px = ((%N%-X+1) * %P% * px)/(X*%Q%);
                    }
                }
                return %RETURN_VALUE%;
            }
            '''
            cpp_code = replace(cpp_code, {'%N%': '%d' % n,
                                          '%P%': '%.15f' % P,
                                          '%Q%': '%.15f' % q,
                                          '%QN%': '%.15f' % qn,
                                          '%BOUND%': '%.15f' % bound,
                                          '%RETURN_VALUE%': '%d-X' % n if reverse else 'X',
                                          '%NAME%': self.name})
            dependencies = {'_rand': DEFAULT_FUNCTIONS['rand']}

        self.implementations.add_implementation('cpp', {'support_code': cpp_code},
                                                dependencies=dependencies,
                                                name=self.name)

        # Cython implementation
        # Inversion transform sampling
        if use_normal:
            cython_code = '''
            cdef float %NAME%(const int vectorisation_idx):
                return _randn(vectorisation_idx) * %SCALE% + %LOC%
            '''
            cython_code = replace(cython_code, {'%SCALE%': '%.15f' % scale,
                                                '%LOC%': '%.15f' % loc,
                                                '%NAME%': self.name})
            dependencies = {'_randn': DEFAULT_FUNCTIONS['randn']}
        else:
            # The following code is an almost exact copy of numpy's
            # rk_binomial_inversion function
            # (numpy/random/mtrand/distributions.c)
            cython_code = '''
            cdef long %NAME%(const int vectorisation_idx):
                cdef long X = 0
                cdef double px = %QN%
                cdef double U = _rand(vectorisation_idx)
                while U > px:
                    X += 1
                    if X > %BOUND%:
                        X = 0
                        px = %QN%
                        U = _rand(vectorisation_idx)
                    else:
                        U -= px
                        px = ((%N%-X+1) * %P% * px)/(X*%Q%)
                return %RETURN_VALUE%
            '''
            cython_code = replace(cython_code, {'%N%': '%d' % n,
                                                '%P%': '%.15f' % p,
                                                '%Q%': '%.15f' % q,
                                                '%QN%': '%.15f' % qn,
                                                '%BOUND%': '%.15f' % bound,
                                                '%RETURN_VALUE%': '%d-X' % n if reverse else 'X',
                                                '%NAME%': self.name})
            dependencies = {'_rand': DEFAULT_FUNCTIONS['rand']}

        self.implementations.add_implementation('cython', cython_code,
                                                dependencies=dependencies,
                                                name=self.name)