Table of Contents¶
1 Requirements
2 KL Functions
2.1 klGauss
2.2 klBern
3 Threshold functions
3.1 Threshold for GLR Bernoulli
KL Functions¶
klGauss¶
Generating some data:
[3]:
def random_Gaussian(sig2=0.25):
return sig2 * np.random.randn()
[4]:
%timeit (random_Gaussian(), random_Gaussian())
1.12 µs ± 74.1 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In pure Python:
[5]:
def klGauss(x: float, y: float, sig2=0.25) -> float:
return (x - y)**2 / (2 * sig2**x)
[6]:
%timeit klGauss(random_Gaussian(), random_Gaussian())
1.37 µs ± 54.3 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
With numba:
[7]:
@numba.jit(nopython=True)
def klGauss_numba(x: float, y: float, sig2=0.25) -> float:
return (x - y)**2 / (2 * sig2**x)
[8]:
help(klGauss_numba)
Help on CPUDispatcher in module __main__:
klGauss_numba(x:float, y:float, sig2=0.25) -> float
[9]:
%timeit klGauss_numba(random_Gaussian(), random_Gaussian())
The slowest run took 13.64 times longer than the fastest. This could mean that an intermediate result is being cached.
4.87 µs ± 7.2 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
[10]:
%timeit klGauss_numba(random_Gaussian(), random_Gaussian())
1.43 µs ± 29.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
[11]:
print(f"Speed up using Numba for klGauss was: {(1290-993)/(20300-993):.2g} faster!")
Speed up using Numba for klGauss was: 0.015 faster!
With Cython
[12]:
%%cython --annotate
def klGauss_cython(double x, double y, double sig2=0.25) -> double:
return (x - y)**2 / (2 * sig2**x)
[12]:
Generated by Cython 0.29.1
Yellow lines hint at Python interaction.
Click on a line that starts with a "+
" to see the C code that Cython generated for it.
1:
+2: def klGauss_cython(double x, double y, double sig2=0.25) -> double:
/* Python wrapper */ static PyObject *__pyx_pw_46_cython_magic_49fb421fab37e1decb48fdb471e97e6b_1klGauss_cython(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ static PyMethodDef __pyx_mdef_46_cython_magic_49fb421fab37e1decb48fdb471e97e6b_1klGauss_cython = {"klGauss_cython", (PyCFunction)(void*)(PyCFunctionWithKeywords)__pyx_pw_46_cython_magic_49fb421fab37e1decb48fdb471e97e6b_1klGauss_cython, METH_VARARGS|METH_KEYWORDS, 0}; static PyObject *__pyx_pw_46_cython_magic_49fb421fab37e1decb48fdb471e97e6b_1klGauss_cython(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) { double __pyx_v_x; double __pyx_v_y; double __pyx_v_sig2; PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("klGauss_cython (wrapper)", 0); { static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_x,&__pyx_n_s_y,&__pyx_n_s_sig2,0}; PyObject* values[3] = {0,0,0}; if (unlikely(__pyx_kwds)) { Py_ssize_t kw_args; const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args); switch (pos_args) { case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2); CYTHON_FALLTHROUGH; case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1); CYTHON_FALLTHROUGH; case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0); CYTHON_FALLTHROUGH; case 0: break; default: goto __pyx_L5_argtuple_error; } kw_args = PyDict_Size(__pyx_kwds); switch (pos_args) { case 0: if (likely((values[0] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_x)) != 0)) kw_args--; else goto __pyx_L5_argtuple_error; CYTHON_FALLTHROUGH; case 1: if (likely((values[1] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_y)) != 0)) kw_args--; else { __Pyx_RaiseArgtupleInvalid("klGauss_cython", 0, 2, 3, 1); __PYX_ERR(0, 2, __pyx_L3_error) } CYTHON_FALLTHROUGH; case 2: if (kw_args > 0) { PyObject* value = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_sig2); if (value) { values[2] = value; kw_args--; } } } if (unlikely(kw_args > 0)) { if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "klGauss_cython") < 0)) __PYX_ERR(0, 2, __pyx_L3_error) } } else { switch (PyTuple_GET_SIZE(__pyx_args)) { case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2); CYTHON_FALLTHROUGH; case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1); values[0] = PyTuple_GET_ITEM(__pyx_args, 0); break; default: goto __pyx_L5_argtuple_error; } } __pyx_v_x = __pyx_PyFloat_AsDouble(values[0]); if (unlikely((__pyx_v_x == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 2, __pyx_L3_error) __pyx_v_y = __pyx_PyFloat_AsDouble(values[1]); if (unlikely((__pyx_v_y == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 2, __pyx_L3_error) if (values[2]) { __pyx_v_sig2 = __pyx_PyFloat_AsDouble(values[2]); if (unlikely((__pyx_v_sig2 == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 2, __pyx_L3_error) } else { __pyx_v_sig2 = ((double)0.25); } } goto __pyx_L4_argument_unpacking_done; __pyx_L5_argtuple_error:; __Pyx_RaiseArgtupleInvalid("klGauss_cython", 0, 2, 3, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 2, __pyx_L3_error) __pyx_L3_error:; __Pyx_AddTraceback("_cython_magic_49fb421fab37e1decb48fdb471e97e6b.klGauss_cython", __pyx_clineno, __pyx_lineno, __pyx_filename); __Pyx_RefNannyFinishContext(); return NULL; __pyx_L4_argument_unpacking_done:; __pyx_r = __pyx_pf_46_cython_magic_49fb421fab37e1decb48fdb471e97e6b_klGauss_cython(__pyx_self, __pyx_v_x, __pyx_v_y, __pyx_v_sig2); /* function exit code */ __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_46_cython_magic_49fb421fab37e1decb48fdb471e97e6b_klGauss_cython(CYTHON_UNUSED PyObject *__pyx_self, double __pyx_v_x, double __pyx_v_y, double __pyx_v_sig2) { PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("klGauss_cython", 0); /* … */ /* function exit code */ __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_3); __Pyx_AddTraceback("_cython_magic_49fb421fab37e1decb48fdb471e97e6b.klGauss_cython", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* … */ __pyx_tuple_ = PyTuple_Pack(3, __pyx_n_s_x, __pyx_n_s_y, __pyx_n_s_sig2); if (unlikely(!__pyx_tuple_)) __PYX_ERR(0, 2, __pyx_L1_error) __Pyx_GOTREF(__pyx_tuple_); __Pyx_GIVEREF(__pyx_tuple_); /* … */ __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_46_cython_magic_49fb421fab37e1decb48fdb471e97e6b_1klGauss_cython, NULL, __pyx_n_s_cython_magic_49fb421fab37e1decb); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 2, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); if (PyDict_SetItem(__pyx_d, __pyx_n_s_klGauss_cython, __pyx_t_1) < 0) __PYX_ERR(0, 2, __pyx_L1_error) __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+3: return (x - y)**2 / (2 * sig2**x)
__Pyx_XDECREF(__pyx_r); __pyx_t_1 = pow((__pyx_v_x - __pyx_v_y), 2.0); __pyx_t_2 = (2.0 * pow(__pyx_v_sig2, __pyx_v_x)); if (unlikely(__pyx_t_2 == 0)) { PyErr_SetString(PyExc_ZeroDivisionError, "float division"); __PYX_ERR(0, 3, __pyx_L1_error) } __pyx_t_3 = PyFloat_FromDouble((__pyx_t_1 / __pyx_t_2)); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 3, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_3); __pyx_r = __pyx_t_3; __pyx_t_3 = 0; goto __pyx_L0;
[13]:
help(klGauss_cython)
Help on built-in function klGauss_cython in module _cython_magic_49fb421fab37e1decb48fdb471e97e6b:
klGauss_cython(...)
[14]:
%timeit klGauss_cython(random_Gaussian(), random_Gaussian())
1.21 µs ± 50.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
[15]:
print(f"Speed up using Cython for klGauss was: {(1290-993)/(1100-993):.2g} faster!")
Speed up using Cython for klGauss was: 2.8 faster!
klBern¶
Generating some data:
[16]:
def random_Bern():
return np.random.random()
[17]:
%timeit (random_Bern(), random_Bern())
786 ns ± 30.3 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In pure Python:
[18]:
from math import log
def klBern(x: float, y: float) -> float:
x = max(1e-7, min(1 - 1e-7, x))
x = max(1e-7, min(1 - 1e-7, x))
return x * log(x/y) + (1-x) * log((1-x)/(1-y))
[19]:
%timeit klBern(random_Bern(), random_Bern())
1.93 µs ± 96.3 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
With numba:
[20]:
from math import log
@numba.jit(nopython=True)
def klBern_numba(x: float, y: float) -> float:
x = max(1e-7, min(1 - 1e-7, x))
x = max(1e-7, min(1 - 1e-7, x))
return x * log(x/y) + (1-x) * log((1-x)/(1-y))
[21]:
help(klBern_numba)
Help on CPUDispatcher in module __main__:
klBern_numba(x:float, y:float) -> float
[22]:
%timeit klBern_numba(random_Bern(), random_Bern())
1.27 µs ± 128 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
[23]:
%timeit klBern_numba(random_Bern(), random_Bern())
1.14 µs ± 42.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
[24]:
print(f"Speed up using Numba for klBern was: {(1740-753)/(996-753):.2g} faster!")
Speed up using Numba for klBern was: 4.1 faster!
With Cython
[25]:
%load_ext cython
The cython extension is already loaded. To reload it, use:
%reload_ext cython
[26]:
%%cython --annotate
from libc.math cimport log
def klBern_cython(double x, double y) -> double:
x = max(1e-7, min(1 - 1e-7, x))
x = max(1e-7, min(1 - 1e-7, x))
return x * log(x/y) + (1-x) * log((1-x)/(1-y))
[26]:
Generated by Cython 0.29.1
Yellow lines hint at Python interaction.
Click on a line that starts with a "+
" to see the C code that Cython generated for it.
1: from libc.math cimport log
2:
+3: def klBern_cython(double x, double y) -> double:
/* Python wrapper */ static PyObject *__pyx_pw_46_cython_magic_0ce0e64bf037172e33037b12661fd5c4_1klBern_cython(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ static PyMethodDef __pyx_mdef_46_cython_magic_0ce0e64bf037172e33037b12661fd5c4_1klBern_cython = {"klBern_cython", (PyCFunction)(void*)(PyCFunctionWithKeywords)__pyx_pw_46_cython_magic_0ce0e64bf037172e33037b12661fd5c4_1klBern_cython, METH_VARARGS|METH_KEYWORDS, 0}; static PyObject *__pyx_pw_46_cython_magic_0ce0e64bf037172e33037b12661fd5c4_1klBern_cython(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) { double __pyx_v_x; double __pyx_v_y; PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("klBern_cython (wrapper)", 0); { static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_x,&__pyx_n_s_y,0}; PyObject* values[2] = {0,0}; if (unlikely(__pyx_kwds)) { Py_ssize_t kw_args; const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args); switch (pos_args) { case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1); CYTHON_FALLTHROUGH; case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0); CYTHON_FALLTHROUGH; case 0: break; default: goto __pyx_L5_argtuple_error; } kw_args = PyDict_Size(__pyx_kwds); switch (pos_args) { case 0: if (likely((values[0] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_x)) != 0)) kw_args--; else goto __pyx_L5_argtuple_error; CYTHON_FALLTHROUGH; case 1: if (likely((values[1] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_y)) != 0)) kw_args--; else { __Pyx_RaiseArgtupleInvalid("klBern_cython", 1, 2, 2, 1); __PYX_ERR(0, 3, __pyx_L3_error) } } if (unlikely(kw_args > 0)) { if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "klBern_cython") < 0)) __PYX_ERR(0, 3, __pyx_L3_error) } } else if (PyTuple_GET_SIZE(__pyx_args) != 2) { goto __pyx_L5_argtuple_error; } else { values[0] = PyTuple_GET_ITEM(__pyx_args, 0); values[1] = PyTuple_GET_ITEM(__pyx_args, 1); } __pyx_v_x = __pyx_PyFloat_AsDouble(values[0]); if (unlikely((__pyx_v_x == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 3, __pyx_L3_error) __pyx_v_y = __pyx_PyFloat_AsDouble(values[1]); if (unlikely((__pyx_v_y == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 3, __pyx_L3_error) } goto __pyx_L4_argument_unpacking_done; __pyx_L5_argtuple_error:; __Pyx_RaiseArgtupleInvalid("klBern_cython", 1, 2, 2, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 3, __pyx_L3_error) __pyx_L3_error:; __Pyx_AddTraceback("_cython_magic_0ce0e64bf037172e33037b12661fd5c4.klBern_cython", __pyx_clineno, __pyx_lineno, __pyx_filename); __Pyx_RefNannyFinishContext(); return NULL; __pyx_L4_argument_unpacking_done:; __pyx_r = __pyx_pf_46_cython_magic_0ce0e64bf037172e33037b12661fd5c4_klBern_cython(__pyx_self, __pyx_v_x, __pyx_v_y); /* function exit code */ __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_46_cython_magic_0ce0e64bf037172e33037b12661fd5c4_klBern_cython(CYTHON_UNUSED PyObject *__pyx_self, double __pyx_v_x, double __pyx_v_y) { PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("klBern_cython", 0); /* … */ /* function exit code */ __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_4); __Pyx_AddTraceback("_cython_magic_0ce0e64bf037172e33037b12661fd5c4.klBern_cython", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* … */ __pyx_tuple_ = PyTuple_Pack(2, __pyx_n_s_x, __pyx_n_s_y); if (unlikely(!__pyx_tuple_)) __PYX_ERR(0, 3, __pyx_L1_error) __Pyx_GOTREF(__pyx_tuple_); __Pyx_GIVEREF(__pyx_tuple_); /* … */ __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_46_cython_magic_0ce0e64bf037172e33037b12661fd5c4_1klBern_cython, NULL, __pyx_n_s_cython_magic_0ce0e64bf037172e33); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 3, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); if (PyDict_SetItem(__pyx_d, __pyx_n_s_klBern_cython, __pyx_t_1) < 0) __PYX_ERR(0, 3, __pyx_L1_error) __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+4: x = max(1e-7, min(1 - 1e-7, x))
__pyx_t_1 = __pyx_v_x; __pyx_t_2 = (1.0 - 1e-7); if (((__pyx_t_1 < __pyx_t_2) != 0)) { __pyx_t_3 = __pyx_t_1; } else { __pyx_t_3 = __pyx_t_2; } __pyx_t_1 = __pyx_t_3; __pyx_t_3 = 1e-7; if (((__pyx_t_1 > __pyx_t_3) != 0)) { __pyx_t_2 = __pyx_t_1; } else { __pyx_t_2 = __pyx_t_3; } __pyx_v_x = __pyx_t_2;
+5: x = max(1e-7, min(1 - 1e-7, x))
__pyx_t_2 = __pyx_v_x; __pyx_t_1 = (1.0 - 1e-7); if (((__pyx_t_2 < __pyx_t_1) != 0)) { __pyx_t_3 = __pyx_t_2; } else { __pyx_t_3 = __pyx_t_1; } __pyx_t_2 = __pyx_t_3; __pyx_t_3 = 1e-7; if (((__pyx_t_2 > __pyx_t_3) != 0)) { __pyx_t_1 = __pyx_t_2; } else { __pyx_t_1 = __pyx_t_3; } __pyx_v_x = __pyx_t_1;
+6: return x * log(x/y) + (1-x) * log((1-x)/(1-y))
__Pyx_XDECREF(__pyx_r); if (unlikely(__pyx_v_y == 0)) { PyErr_SetString(PyExc_ZeroDivisionError, "float division"); __PYX_ERR(0, 6, __pyx_L1_error) } __pyx_t_1 = (1.0 - __pyx_v_x); __pyx_t_2 = (1.0 - __pyx_v_y); if (unlikely(__pyx_t_2 == 0)) { PyErr_SetString(PyExc_ZeroDivisionError, "float division"); __PYX_ERR(0, 6, __pyx_L1_error) } __pyx_t_4 = PyFloat_FromDouble(((__pyx_v_x * log((__pyx_v_x / __pyx_v_y))) + ((1.0 - __pyx_v_x) * log((__pyx_t_1 / __pyx_t_2))))); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 6, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_4); __pyx_r = __pyx_t_4; __pyx_t_4 = 0; goto __pyx_L0;
[27]:
help(klBern_cython)
Help on built-in function klBern_cython in module _cython_magic_0ce0e64bf037172e33037b12661fd5c4:
klBern_cython(...)
[28]:
%timeit klBern_cython(random_Bern(), random_Bern())
922 ns ± 36.6 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
[29]:
print(f"Speed up using Cython for klBern was: {(1740-753)/(861-753):.2g} faster!")
Speed up using Cython for klBern was: 9.1 faster!
Threshold functions¶
Threshold for GLR Bernoulli¶
Generating some data:
[30]:
def random_t0_s_t_delta(min_t: int=100, max_t: int=1000) -> (int, int, int, float):
t0 = 0
t = np.random.randint(min_t, max_t + 1)
s = np.random.randint(t0, t)
delta = np.random.choice([0.1, 0.05, 0.01, 0.005, 0.001, max(0.0005, 1/t)])
return (t0, s, t, delta)
[31]:
%timeit random_t0_s_t_delta()
7.04 µs ± 148 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In pure Python:
[32]:
def threshold(t0: int, s: int, t: int, delta: float) -> float:
return np.log((s - t0 + 1) * (t - s) / delta)
[33]:
%timeit threshold(*random_t0_s_t_delta())
10.2 µs ± 175 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
It’s way faster to use math.log
instead of numpy.log
(of course)!
[34]:
from math import log
def threshold2(t0: int, s: int, t: int, delta: float) -> float:
return log((s - t0 + 1) * (t - s) / delta)
[35]:
%timeit threshold2(*random_t0_s_t_delta())
7.93 µs ± 132 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In numba:
[36]:
from math import log
@numba.jit(nopython=True)
def threshold_numba(t0: int, s: int, t: int, delta: float) -> float:
return log((s - t0 + 1) * (t - s) / delta)
[37]:
help(threshold_numba)
Help on CPUDispatcher in module __main__:
threshold_numba(t0:int, s:int, t:int, delta:float) -> float
[38]:
%timeit threshold_numba(*random_t0_s_t_delta())
7.57 µs ± 105 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
[39]:
print(f"Speed up using Cython for thresold was: {(7510-7200)/(7750-7200):.2g} faster!")
Speed up using Cython for thresold was: 0.56 faster!
In Cython:
[40]:
%%cython --annotate
from libc.math cimport log
cpdef double threshold_cython(int t0, int s, int t, double delta):
return log((s - t0 + 1) * (t - s) / delta)
[40]:
Generated by Cython 0.29.1
Yellow lines hint at Python interaction.
Click on a line that starts with a "+
" to see the C code that Cython generated for it.
1: from libc.math cimport log
2:
+3: cpdef double threshold_cython(int t0, int s, int t, double delta):
static PyObject *__pyx_pw_46_cython_magic_4e48b0e25538ff2883ba5a92832943be_1threshold_cython(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ static double __pyx_f_46_cython_magic_4e48b0e25538ff2883ba5a92832943be_threshold_cython(int __pyx_v_t0, int __pyx_v_s, int __pyx_v_t, double __pyx_v_delta, CYTHON_UNUSED int __pyx_skip_dispatch) { double __pyx_r; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("threshold_cython", 0); /* … */ /* function exit code */ __pyx_L1_error:; __Pyx_WriteUnraisable("_cython_magic_4e48b0e25538ff2883ba5a92832943be.threshold_cython", __pyx_clineno, __pyx_lineno, __pyx_filename, 1, 0); __pyx_r = 0; __pyx_L0:; __Pyx_RefNannyFinishContext(); return __pyx_r; } /* Python wrapper */ static PyObject *__pyx_pw_46_cython_magic_4e48b0e25538ff2883ba5a92832943be_1threshold_cython(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ static PyObject *__pyx_pw_46_cython_magic_4e48b0e25538ff2883ba5a92832943be_1threshold_cython(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) { int __pyx_v_t0; int __pyx_v_s; int __pyx_v_t; double __pyx_v_delta; PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("threshold_cython (wrapper)", 0); { static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_t0,&__pyx_n_s_s,&__pyx_n_s_t,&__pyx_n_s_delta,0}; PyObject* values[4] = {0,0,0,0}; if (unlikely(__pyx_kwds)) { Py_ssize_t kw_args; const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args); switch (pos_args) { case 4: values[3] = PyTuple_GET_ITEM(__pyx_args, 3); CYTHON_FALLTHROUGH; case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2); CYTHON_FALLTHROUGH; case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1); CYTHON_FALLTHROUGH; case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0); CYTHON_FALLTHROUGH; case 0: break; default: goto __pyx_L5_argtuple_error; } kw_args = PyDict_Size(__pyx_kwds); switch (pos_args) { case 0: if (likely((values[0] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_t0)) != 0)) kw_args--; else goto __pyx_L5_argtuple_error; CYTHON_FALLTHROUGH; case 1: if (likely((values[1] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_s)) != 0)) kw_args--; else { __Pyx_RaiseArgtupleInvalid("threshold_cython", 1, 4, 4, 1); __PYX_ERR(0, 3, __pyx_L3_error) } CYTHON_FALLTHROUGH; case 2: if (likely((values[2] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_t)) != 0)) kw_args--; else { __Pyx_RaiseArgtupleInvalid("threshold_cython", 1, 4, 4, 2); __PYX_ERR(0, 3, __pyx_L3_error) } CYTHON_FALLTHROUGH; case 3: if (likely((values[3] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_delta)) != 0)) kw_args--; else { __Pyx_RaiseArgtupleInvalid("threshold_cython", 1, 4, 4, 3); __PYX_ERR(0, 3, __pyx_L3_error) } } if (unlikely(kw_args > 0)) { if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "threshold_cython") < 0)) __PYX_ERR(0, 3, __pyx_L3_error) } } else if (PyTuple_GET_SIZE(__pyx_args) != 4) { goto __pyx_L5_argtuple_error; } else { values[0] = PyTuple_GET_ITEM(__pyx_args, 0); values[1] = PyTuple_GET_ITEM(__pyx_args, 1); values[2] = PyTuple_GET_ITEM(__pyx_args, 2); values[3] = PyTuple_GET_ITEM(__pyx_args, 3); } __pyx_v_t0 = __Pyx_PyInt_As_int(values[0]); if (unlikely((__pyx_v_t0 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 3, __pyx_L3_error) __pyx_v_s = __Pyx_PyInt_As_int(values[1]); if (unlikely((__pyx_v_s == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 3, __pyx_L3_error) __pyx_v_t = __Pyx_PyInt_As_int(values[2]); if (unlikely((__pyx_v_t == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 3, __pyx_L3_error) __pyx_v_delta = __pyx_PyFloat_AsDouble(values[3]); if (unlikely((__pyx_v_delta == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 3, __pyx_L3_error) } goto __pyx_L4_argument_unpacking_done; __pyx_L5_argtuple_error:; __Pyx_RaiseArgtupleInvalid("threshold_cython", 1, 4, 4, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 3, __pyx_L3_error) __pyx_L3_error:; __Pyx_AddTraceback("_cython_magic_4e48b0e25538ff2883ba5a92832943be.threshold_cython", __pyx_clineno, __pyx_lineno, __pyx_filename); __Pyx_RefNannyFinishContext(); return NULL; __pyx_L4_argument_unpacking_done:; __pyx_r = __pyx_pf_46_cython_magic_4e48b0e25538ff2883ba5a92832943be_threshold_cython(__pyx_self, __pyx_v_t0, __pyx_v_s, __pyx_v_t, __pyx_v_delta); /* function exit code */ __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_46_cython_magic_4e48b0e25538ff2883ba5a92832943be_threshold_cython(CYTHON_UNUSED PyObject *__pyx_self, int __pyx_v_t0, int __pyx_v_s, int __pyx_v_t, double __pyx_v_delta) { PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("threshold_cython", 0); __Pyx_XDECREF(__pyx_r); __pyx_t_1 = PyFloat_FromDouble(__pyx_f_46_cython_magic_4e48b0e25538ff2883ba5a92832943be_threshold_cython(__pyx_v_t0, __pyx_v_s, __pyx_v_t, __pyx_v_delta, 0)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 3, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); __pyx_r = __pyx_t_1; __pyx_t_1 = 0; goto __pyx_L0; /* function exit code */ __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_1); __Pyx_AddTraceback("_cython_magic_4e48b0e25538ff2883ba5a92832943be.threshold_cython", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; }
+4: return log((s - t0 + 1) * (t - s) / delta)
__pyx_t_1 = (((__pyx_v_s - __pyx_v_t0) + 1) * (__pyx_v_t - __pyx_v_s)); if (unlikely(__pyx_v_delta == 0)) { PyErr_SetString(PyExc_ZeroDivisionError, "float division"); __PYX_ERR(0, 4, __pyx_L1_error) } __pyx_r = log((((double)__pyx_t_1) / __pyx_v_delta)); goto __pyx_L0;
[41]:
%%cython --annotate
from libc.math cimport log
def threshold_cython2(int t0, int s, int t, double delta) -> double:
return log((s - t0 + 1) * (t - s) / delta)
[41]:
Generated by Cython 0.29.1
Yellow lines hint at Python interaction.
Click on a line that starts with a "+
" to see the C code that Cython generated for it.
1: from libc.math cimport log
2:
+3: def threshold_cython2(int t0, int s, int t, double delta) -> double:
/* Python wrapper */ static PyObject *__pyx_pw_46_cython_magic_8a5a8f9968b73b8b7b920f92f30405bc_1threshold_cython2(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/ static PyMethodDef __pyx_mdef_46_cython_magic_8a5a8f9968b73b8b7b920f92f30405bc_1threshold_cython2 = {"threshold_cython2", (PyCFunction)(void*)(PyCFunctionWithKeywords)__pyx_pw_46_cython_magic_8a5a8f9968b73b8b7b920f92f30405bc_1threshold_cython2, METH_VARARGS|METH_KEYWORDS, 0}; static PyObject *__pyx_pw_46_cython_magic_8a5a8f9968b73b8b7b920f92f30405bc_1threshold_cython2(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) { int __pyx_v_t0; int __pyx_v_s; int __pyx_v_t; double __pyx_v_delta; PyObject *__pyx_r = 0; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("threshold_cython2 (wrapper)", 0); { static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_t0,&__pyx_n_s_s,&__pyx_n_s_t,&__pyx_n_s_delta,0}; PyObject* values[4] = {0,0,0,0}; if (unlikely(__pyx_kwds)) { Py_ssize_t kw_args; const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args); switch (pos_args) { case 4: values[3] = PyTuple_GET_ITEM(__pyx_args, 3); CYTHON_FALLTHROUGH; case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2); CYTHON_FALLTHROUGH; case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1); CYTHON_FALLTHROUGH; case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0); CYTHON_FALLTHROUGH; case 0: break; default: goto __pyx_L5_argtuple_error; } kw_args = PyDict_Size(__pyx_kwds); switch (pos_args) { case 0: if (likely((values[0] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_t0)) != 0)) kw_args--; else goto __pyx_L5_argtuple_error; CYTHON_FALLTHROUGH; case 1: if (likely((values[1] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_s)) != 0)) kw_args--; else { __Pyx_RaiseArgtupleInvalid("threshold_cython2", 1, 4, 4, 1); __PYX_ERR(0, 3, __pyx_L3_error) } CYTHON_FALLTHROUGH; case 2: if (likely((values[2] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_t)) != 0)) kw_args--; else { __Pyx_RaiseArgtupleInvalid("threshold_cython2", 1, 4, 4, 2); __PYX_ERR(0, 3, __pyx_L3_error) } CYTHON_FALLTHROUGH; case 3: if (likely((values[3] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_delta)) != 0)) kw_args--; else { __Pyx_RaiseArgtupleInvalid("threshold_cython2", 1, 4, 4, 3); __PYX_ERR(0, 3, __pyx_L3_error) } } if (unlikely(kw_args > 0)) { if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "threshold_cython2") < 0)) __PYX_ERR(0, 3, __pyx_L3_error) } } else if (PyTuple_GET_SIZE(__pyx_args) != 4) { goto __pyx_L5_argtuple_error; } else { values[0] = PyTuple_GET_ITEM(__pyx_args, 0); values[1] = PyTuple_GET_ITEM(__pyx_args, 1); values[2] = PyTuple_GET_ITEM(__pyx_args, 2); values[3] = PyTuple_GET_ITEM(__pyx_args, 3); } __pyx_v_t0 = __Pyx_PyInt_As_int(values[0]); if (unlikely((__pyx_v_t0 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 3, __pyx_L3_error) __pyx_v_s = __Pyx_PyInt_As_int(values[1]); if (unlikely((__pyx_v_s == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 3, __pyx_L3_error) __pyx_v_t = __Pyx_PyInt_As_int(values[2]); if (unlikely((__pyx_v_t == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 3, __pyx_L3_error) __pyx_v_delta = __pyx_PyFloat_AsDouble(values[3]); if (unlikely((__pyx_v_delta == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 3, __pyx_L3_error) } goto __pyx_L4_argument_unpacking_done; __pyx_L5_argtuple_error:; __Pyx_RaiseArgtupleInvalid("threshold_cython2", 1, 4, 4, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 3, __pyx_L3_error) __pyx_L3_error:; __Pyx_AddTraceback("_cython_magic_8a5a8f9968b73b8b7b920f92f30405bc.threshold_cython2", __pyx_clineno, __pyx_lineno, __pyx_filename); __Pyx_RefNannyFinishContext(); return NULL; __pyx_L4_argument_unpacking_done:; __pyx_r = __pyx_pf_46_cython_magic_8a5a8f9968b73b8b7b920f92f30405bc_threshold_cython2(__pyx_self, __pyx_v_t0, __pyx_v_s, __pyx_v_t, __pyx_v_delta); /* function exit code */ __Pyx_RefNannyFinishContext(); return __pyx_r; } static PyObject *__pyx_pf_46_cython_magic_8a5a8f9968b73b8b7b920f92f30405bc_threshold_cython2(CYTHON_UNUSED PyObject *__pyx_self, int __pyx_v_t0, int __pyx_v_s, int __pyx_v_t, double __pyx_v_delta) { PyObject *__pyx_r = NULL; __Pyx_RefNannyDeclarations __Pyx_RefNannySetupContext("threshold_cython2", 0); /* … */ /* function exit code */ __pyx_L1_error:; __Pyx_XDECREF(__pyx_t_2); __Pyx_AddTraceback("_cython_magic_8a5a8f9968b73b8b7b920f92f30405bc.threshold_cython2", __pyx_clineno, __pyx_lineno, __pyx_filename); __pyx_r = NULL; __pyx_L0:; __Pyx_XGIVEREF(__pyx_r); __Pyx_RefNannyFinishContext(); return __pyx_r; } /* … */ __pyx_tuple_ = PyTuple_Pack(4, __pyx_n_s_t0, __pyx_n_s_s, __pyx_n_s_t, __pyx_n_s_delta); if (unlikely(!__pyx_tuple_)) __PYX_ERR(0, 3, __pyx_L1_error) __Pyx_GOTREF(__pyx_tuple_); __Pyx_GIVEREF(__pyx_tuple_); /* … */ __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_46_cython_magic_8a5a8f9968b73b8b7b920f92f30405bc_1threshold_cython2, NULL, __pyx_n_s_cython_magic_8a5a8f9968b73b8b7b); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 3, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_1); if (PyDict_SetItem(__pyx_d, __pyx_n_s_threshold_cython2, __pyx_t_1) < 0) __PYX_ERR(0, 3, __pyx_L1_error) __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+4: return log((s - t0 + 1) * (t - s) / delta)
__Pyx_XDECREF(__pyx_r); __pyx_t_1 = (((__pyx_v_s - __pyx_v_t0) + 1) * (__pyx_v_t - __pyx_v_s)); if (unlikely(__pyx_v_delta == 0)) { PyErr_SetString(PyExc_ZeroDivisionError, "float division"); __PYX_ERR(0, 4, __pyx_L1_error) } __pyx_t_2 = PyFloat_FromDouble(log((((double)__pyx_t_1) / __pyx_v_delta))); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 4, __pyx_L1_error) __Pyx_GOTREF(__pyx_t_2); __pyx_r = __pyx_t_2; __pyx_t_2 = 0; goto __pyx_L0;
[42]:
help(threshold_cython)
Help on built-in function threshold_cython in module _cython_magic_4e48b0e25538ff2883ba5a92832943be:
threshold_cython(...)
[43]:
%timeit threshold_cython(*random_t0_s_t_delta())
7.24 µs ± 103 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
[44]:
%timeit threshold_cython2(*random_t0_s_t_delta())
8.8 µs ± 994 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
[45]:
print(f"Speed up using Cython for thresold was: {abs(7510-7200)/abs(7070-7200):.2g} faster!")
Speed up using Cython for thresold was: 2.4 faster!
[ ]: