Ejemplo n.º 1
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)

        for target, func in BinomialFunction.implementations.iteritems():
            code, dependencies = func(n=n, p=p, use_normal=use_normal,
                                      name=self.name)
            self.implementations.add_implementation(target, code,
                                                    dependencies=dependencies,
                                                    name=self.name)
Ejemplo n.º 2
0
    def _init_2d(self):
        dimensions = self.dim
        unit = get_unit(dimensions)
        values = self.values
        dt = self.dt

        # Python implementation (with units), used when calling the TimedArray
        # directly, outside of a simulation
        @check_units(i=1, t=second, result=unit)
        def timed_array_func(t, i):
            # We round according to the current defaultclock.dt
            K = _find_K(float(defaultclock.dt), dt)
            epsilon = dt / K
            time_step = np.clip(np.int_(np.round(np.asarray(t/epsilon)) / K),
                        0, len(values)-1)
            return Quantity(values[time_step, i], dim=dimensions)

        Function.__init__(self, pyfunc=timed_array_func)

        # we use dynamic implementations because we want to do upsampling
        # in a way that avoids rounding problems with the group's dt
        def create_numpy_implementation(owner):
            group_dt = owner.clock.dt_

            K = _find_K(group_dt, dt)
            n_values = len(values)
            epsilon = dt / K
            def unitless_timed_array_func(t, i):
                timestep = np.clip(np.int_(np.round(t/epsilon) / K),
                                   0, n_values-1)
                return values[timestep, i]

            unitless_timed_array_func._arg_units = [second]
            unitless_timed_array_func._return_unit = unit

            return unitless_timed_array_func

        self.implementations.add_dynamic_implementation('numpy',
                                                        create_numpy_implementation)
        values_flat = self.values.astype(np.double,
                                       order='C',
                                       copy=False).ravel()
        namespace = lambda owner: {'%s_values' % self.name: values_flat}

        for target, (_, func_2d) in TimedArray.implementations.items():
            self.implementations.add_dynamic_implementation(target,
                                                            func_2d(self.values,
                                                                    self.dt,
                                                                    self.name),
                                                            namespace=namespace,
                                                            name=self.name)
Ejemplo n.º 3
0
    def __init__(self, values, dt, name=None):
        if name is None:
            name = '_timedarray*'
        Nameable.__init__(self, name)
        unit = get_unit(values)
        values = np.asarray(values)
        self.values = values
        dt = float(dt)
        self.dt = dt

        # Python implementation (with units), used when calling the TimedArray
        # directly, outside of a simulation
        @check_units(t=second, result=unit)
        def timed_array_func(t):
            i = np.clip(np.int_(np.float_(t) / dt + 0.5), 0, len(values)-1)
            return values[i] * unit

        Function.__init__(self, pyfunc=timed_array_func)

        # Implementation for numpy, without units
        def unitless_timed_array_func(t):
            i = np.clip(np.int_(np.float_(t) / dt + 0.5), 0, len(values)-1)
            return values[i]
        unitless_timed_array_func._arg_units = [second]
        unitless_timed_array_func._return_unit = unit

        # Implementation for C++
        cpp_code = {'support_code': '''
        inline double _timedarray_%NAME%(const double t, const double _dt, const int _num_values, const double* _values)
        {
            int i = (int)(t/_dt + 0.5); // rounds to nearest int for positive values
            if(i<0)
                i = 0;
            if(i>=_num_values)
                i = _num_values-1;
            return _values[i];
        }
        '''.replace('%NAME%', self.name),
                                       'hashdefine_code': '''
        #define %NAME%(t) _timedarray_%NAME%(t, _%NAME%_dt, _%NAME%_num_values, _%NAME%_values)
        '''.replace('%NAME%', self.name)}
        namespace = {'_%s_dt' % self.name: self.dt,
                     '_%s_num_values' % self.name: len(self.values),
                     '_%s_values' % self.name: self.values}

        add_implementations(self, codes={'cpp': cpp_code,
                                         'numpy': unitless_timed_array_func},
                            namespaces={'cpp': namespace},
                            names={'cpp': self.name})
Ejemplo n.º 4
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)
Ejemplo n.º 5
0
    def __init__(self, values, dt, name=None):
        if name is None:
            name = '_timedarray*'
        Nameable.__init__(self, name)
        unit = get_unit(values)
        values = np.asarray(values)
        self.values = values
        dt = float(dt)
        self.dt = dt

        # Python implementation (with units), used when calling the TimedArray
        # directly, outside of a simulation
        @check_units(t=second, result=unit)
        def timed_array_func(t):
            i = np.clip(np.int_(np.float_(t) / dt + 0.5), 0, len(values)-1)
            return values[i] * unit

        Function.__init__(self, pyfunc=timed_array_func)

        # we use dynamic implementations because we want to do upsampling
        # in a way that avoids rounding problems with the group's dt
        def create_numpy_implementation(owner):
            group_dt = owner.clock.dt_
            K = _find_K(group_dt, dt)
            epsilon = dt / K
            n_values = len(values)
            def unitless_timed_array_func(t):
                timestep = np.clip(np.int_(np.round(t/epsilon)) / K, 0, n_values-1)
                return values[timestep]
            unitless_timed_array_func._arg_units = [second]
            unitless_timed_array_func._return_unit = unit

            return unitless_timed_array_func

        self.implementations.add_dynamic_implementation('numpy',
                                                        create_numpy_implementation)

        def create_cpp_implementation(owner):
            group_dt = owner.clock.dt_
            K = _find_K(group_dt, dt)
            cpp_code = {'support_code': '''
            inline double _timedarray_%NAME%(const double t, const int _num_values, const double* _values)
            {
                const double epsilon = %DT% / %K%;
                int i = (int)((t/epsilon + 0.5)/%K%); // rounds to nearest int for positive values
                if(i<0)
                    i = 0;
                if(i>=_num_values)
                    i = _num_values-1;
                return _values[i];
            }
            '''.replace('%NAME%', self.name).replace('%DT%', '%.18f' % dt).replace('%K%', str(K)),
                                           'hashdefine_code': '''
            #define %NAME%(t) _timedarray_%NAME%(t, _%NAME%_num_values, _%NAME%_values)
            '''.replace('%NAME%', self.name)}

            return cpp_code

        def create_cpp_namespace(owner):
            return {'_%s_num_values' % self.name: len(self.values),
                    '_%s_values' % self.name: self.values}

        self.implementations.add_dynamic_implementation('cpp',
                                                        create_cpp_implementation,
                                                        create_cpp_namespace,
                                                        name=self.name)
Ejemplo n.º 6
0
    def _init_2d(self):
        unit = self.unit
        values = self.values
        dt = self.dt

        # Python implementation (with units), used when calling the TimedArray
        # directly, outside of a simulation
        @check_units(i=1, t=second, result=unit)
        def timed_array_func(t, i):
            # We round according to the current defaultclock.dt
            K = _find_K(float(defaultclock.dt), dt)
            epsilon = dt / K
            time_step = np.clip(np.int_(np.round(np.asarray(t/epsilon)) / K),
                        0, len(values)-1)
            return values[time_step, i] * unit

        Function.__init__(self, pyfunc=timed_array_func)

        # we use dynamic implementations because we want to do upsampling
        # in a way that avoids rounding problems with the group's dt
        def create_numpy_implementation(owner):
            group_dt = owner.clock.dt_

            K = _find_K(group_dt, dt)
            n_values = len(values)
            epsilon = dt / K
            def unitless_timed_array_func(t, i):
                timestep = np.clip(np.int_(np.round(t/epsilon) / K),
                                   0, n_values-1)
                return values[timestep, i]

            unitless_timed_array_func._arg_units = [second]
            unitless_timed_array_func._return_unit = unit

            return unitless_timed_array_func

        self.implementations.add_dynamic_implementation('numpy',
                                                        create_numpy_implementation)


        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

        def create_cpp_namespace(owner):
            return {'%s_values' % self.name: self.values.astype(np.double,
                                                                order='C',
                                                                copy=False).ravel()}

        self.implementations.add_dynamic_implementation('cpp',
                                                        code=create_cpp_implementation,
                                                        namespace=create_cpp_namespace,
                                                        name=self.name)

        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

        def create_cython_namespace(owner):
            return {'%s_values' % self.name: self.values.astype(np.double,
                                                                order='C',
                                                                copy=False).ravel()}

        self.implementations.add_dynamic_implementation('cython',
                                                        code=create_cython_implementation,
                                                        namespace=create_cython_namespace,
                                                        name=self.name)
Ejemplo n.º 7
0
    def _init_1d(self):
        unit = self.unit
        values = self.values
        dt = self.dt

        # Python implementation (with units), used when calling the TimedArray
        # directly, outside of a simulation
        @check_units(t=second, result=unit)
        def timed_array_func(t):
            # We round according to the current defaultclock.dt
            K = _find_K(float(defaultclock.dt), dt)
            epsilon = dt / K
            i = np.clip(np.int_(np.round(np.asarray(t/epsilon)) / K),
                        0, len(values)-1)
            return values[i] * unit

        Function.__init__(self, pyfunc=timed_array_func)

        # we use dynamic implementations because we want to do upsampling
        # in a way that avoids rounding problems with the group's dt
        def create_numpy_implementation(owner):
            group_dt = owner.clock.dt_

            K = _find_K(group_dt, dt)
            n_values = len(values)
            epsilon = dt / K
            def unitless_timed_array_func(t):
                timestep = np.clip(np.int_(np.round(t/epsilon) / K),
                                   0, n_values-1)
                return values[timestep]

            unitless_timed_array_func._arg_units = [second]
            unitless_timed_array_func._return_unit = unit

            return unitless_timed_array_func

        self.implementations.add_dynamic_implementation('numpy',
                                                        create_numpy_implementation)

        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 double epsilon = %DT% / %K%;
                int i = (int)((t/epsilon + 0.5)/%K%);
                if(i < 0)
                   i = 0;
                if(i >= %NUM_VALUES%)
                    i = %NUM_VALUES%-1;
                return _namespace%NAME%_values[i];
            }
            '''.replace('%NAME%', self.name).replace('%DT%', '%.18f' % dt).replace('%K%', str(K)).replace('%NUM_VALUES%', str(len(self.values)))
            cpp_code = {'support_code': support_code}

            return cpp_code

        def create_cpp_namespace(owner):
            return {'%s_values' % self.name: self.values}

        self.implementations.add_dynamic_implementation('cpp',
                                                        code=create_cpp_implementation,
                                                        namespace=create_cpp_namespace,
                                                        name=self.name)
        def create_cython_implementation(owner):
            group_dt = owner.clock.dt_
            K = _find_K(group_dt, dt)
            code = '''
            cdef double %NAME%(const double t):
                global _namespace%NAME%_values
                cdef double epsilon = %DT% / %K%
                cdef int i = (int)((t/epsilon + 0.5)/%K%)
                if i < 0:
                   i = 0
                if i >= %NUM_VALUES%:
                    i = %NUM_VALUES% - 1
                return _namespace%NAME%_values[i]
            '''.replace('%NAME%', self.name).replace('%DT%', '%.18f' % dt).replace('%K%', str(K)).replace('%NUM_VALUES%', str(len(self.values)))

            return code

        def create_cython_namespace(owner):
            return {'%s_values' % self.name: self.values}

        self.implementations.add_dynamic_implementation('cython',
                                                        code=create_cython_implementation,
                                                        namespace=create_cython_namespace,
                                                        name=self.name)
Ejemplo n.º 8
0
    def _init_2d(self):
        dimensions = self.dim
        unit = get_unit(dimensions)
        values = self.values
        dt = self.dt

        # Python implementation (with units), used when calling the TimedArray
        # directly, outside of a simulation
        @check_units(i=1, t=second, result=unit)
        def timed_array_func(t, i):
            # We round according to the current defaultclock.dt
            K = _find_K(float(defaultclock.dt), dt)
            epsilon = dt / K
            time_step = np.clip(np.int_(np.round(np.asarray(t / epsilon)) / K),
                                0,
                                len(values) - 1)
            return Quantity(values[time_step, i], dim=dimensions)

        Function.__init__(self, pyfunc=timed_array_func)

        # we use dynamic implementations because we want to do upsampling
        # in a way that avoids rounding problems with the group's dt
        def create_numpy_implementation(owner):
            group_dt = owner.clock.dt_

            K = _find_K(group_dt, dt)
            n_values = len(values)
            epsilon = dt / K

            def unitless_timed_array_func(t, i):
                timestep = np.clip(np.int_(np.round(t / epsilon) / K), 0,
                                   n_values - 1)
                return values[timestep, i]

            unitless_timed_array_func._arg_units = [second]
            unitless_timed_array_func._return_unit = unit

            return unitless_timed_array_func

        self.implementations.add_dynamic_implementation(
            'numpy', create_numpy_implementation)

        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

        def create_cpp_namespace(owner):
            return {
                '%s_values' % self.name:
                self.values.astype(np.double, order='C', copy=False).ravel()
            }

        self.implementations.add_dynamic_implementation(
            'cpp',
            code=create_cpp_implementation,
            namespace=create_cpp_namespace,
            name=self.name)

        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

        def create_cython_namespace(owner):
            return {
                '%s_values' % self.name:
                self.values.astype(np.double, order='C', copy=False).ravel()
            }

        self.implementations.add_dynamic_implementation(
            'cython',
            code=create_cython_implementation,
            namespace=create_cython_namespace,
            name=self.name)
Ejemplo n.º 9
0
    def _init_1d(self):
        dimensions = self.dim
        unit = get_unit(dimensions)
        values = self.values
        dt = self.dt

        # Python implementation (with units), used when calling the TimedArray
        # directly, outside of a simulation
        @check_units(t=second, result=unit)
        def timed_array_func(t):
            # We round according to the current defaultclock.dt
            K = _find_K(float(defaultclock.dt), dt)
            epsilon = dt / K
            i = np.clip(np.int_(np.round(np.asarray(t / epsilon)) / K), 0,
                        len(values) - 1)
            return Quantity(values[i], dim=dimensions)

        Function.__init__(self, pyfunc=timed_array_func)

        # we use dynamic implementations because we want to do upsampling
        # in a way that avoids rounding problems with the group's dt
        def create_numpy_implementation(owner):
            group_dt = owner.clock.dt_

            K = _find_K(group_dt, dt)
            n_values = len(values)
            epsilon = dt / K

            def unitless_timed_array_func(t):
                timestep = np.clip(np.int_(np.round(t / epsilon) / K), 0,
                                   n_values - 1)
                return values[timestep]

            unitless_timed_array_func._arg_units = [second]
            unitless_timed_array_func._return_unit = unit

            return unitless_timed_array_func

        self.implementations.add_dynamic_implementation(
            'numpy', create_numpy_implementation)

        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 double epsilon = %DT% / %K%;
                int i = (int)((t/epsilon + 0.5)/%K%);
                if(i < 0)
                   i = 0;
                if(i >= %NUM_VALUES%)
                    i = %NUM_VALUES%-1;
                return _namespace%NAME%_values[i];
            }
            '''.replace('%NAME%',
                        self.name).replace('%DT%', '%.18f' % dt).replace(
                            '%K%', str(K)).replace('%NUM_VALUES%',
                                                   str(len(self.values)))
            cpp_code = {'support_code': support_code}

            return cpp_code

        def create_cpp_namespace(owner):
            return {'%s_values' % self.name: self.values}

        self.implementations.add_dynamic_implementation(
            'cpp',
            code=create_cpp_implementation,
            namespace=create_cpp_namespace,
            name=self.name)

        def create_cython_implementation(owner):
            group_dt = owner.clock.dt_
            K = _find_K(group_dt, dt)
            code = '''
            cdef double %NAME%(const double t):
                global _namespace%NAME%_values
                cdef double epsilon = %DT% / %K%
                cdef int i = (int)((t/epsilon + 0.5)/%K%)
                if i < 0:
                   i = 0
                if i >= %NUM_VALUES%:
                    i = %NUM_VALUES% - 1
                return _namespace%NAME%_values[i]
            '''.replace('%NAME%',
                        self.name).replace('%DT%', '%.18f' % dt).replace(
                            '%K%', str(K)).replace('%NUM_VALUES%',
                                                   str(len(self.values)))

            return code

        def create_cython_namespace(owner):
            return {'%s_values' % self.name: self.values}

        self.implementations.add_dynamic_implementation(
            'cython',
            code=create_cython_implementation,
            namespace=create_cython_namespace,
            name=self.name)
Ejemplo n.º 10
0
    def __init__(self, values, dt, name=None):
        if name is None:
            name = '_timedarray*'
        Nameable.__init__(self, name)
        unit = get_unit(values)
        values = np.asarray(values)
        self.values = values
        dt = float(dt)
        self.dt = dt

        # Python implementation (with units), used when calling the TimedArray
        # directly, outside of a simulation
        @check_units(t=second, result=unit)
        def timed_array_func(t):
            i = np.clip(np.int_(np.float_(t) / dt + 0.5), 0, len(values) - 1)
            return values[i] * unit

        Function.__init__(self, pyfunc=timed_array_func)

        # we use dynamic implementations because we want to do upsampling
        # in a way that avoids rounding problems with the group's dt
        def create_numpy_implementation(owner):
            group_dt = owner.clock.dt_
            K = _find_K(group_dt, dt)
            epsilon = dt / K
            n_values = len(values)

            def unitless_timed_array_func(t):
                timestep = np.clip(
                    np.int_(np.round(t / epsilon)) / K, 0, n_values - 1)
                return values[timestep]

            unitless_timed_array_func._arg_units = [second]
            unitless_timed_array_func._return_unit = unit

            return unitless_timed_array_func

        self.implementations.add_dynamic_implementation(
            'numpy', create_numpy_implementation)

        def create_cpp_implementation(owner):
            group_dt = owner.clock.dt_
            K = _find_K(group_dt, dt)
            cpp_code = {
                'support_code':
                '''
            inline double _timedarray_%NAME%(const double t, const int _num_values, const double* _values)
            {
                const double epsilon = %DT% / %K%;
                int i = (int)((t/epsilon + 0.5)/%K%); // rounds to nearest int for positive values
                if(i<0)
                    i = 0;
                if(i>=_num_values)
                    i = _num_values-1;
                return _values[i];
            }
            '''.replace('%NAME%', self.name).replace('%DT%',
                                                     '%.18f' % dt).replace(
                                                         '%K%', str(K)),
                'hashdefine_code':
                '''
            #define %NAME%(t) _timedarray_%NAME%(t, _%NAME%_num_values, _%NAME%_values)
            '''.replace('%NAME%', self.name)
            }

            return cpp_code

        def create_cpp_namespace(owner):
            return {
                '_%s_num_values' % self.name: len(self.values),
                '_%s_values' % self.name: self.values
            }

        self.implementations.add_dynamic_implementation(
            'cpp',
            create_cpp_implementation,
            create_cpp_namespace,
            name=self.name)
Ejemplo n.º 11
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)