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)
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)
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})
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)
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)
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)
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)
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)
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)
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)
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)