I have converted a python function into a cython equivalent by adding types to some variables. However, the cython function to produces slightly different output than the original python function.
I've learnt some of the reasons for this difference in this post Cython: unsigned int indices for numpy arrays gives different result But even with what I've learnt in this post I still can't get the cython function to produce the same results as the python one.
So I have put together 4 functions illustrating what I have tried. Could someone help to unveil why I get slightly different results for each function? and how to get a cython function that returns the same exact values as function1? I make some comments bellow:
%%cython
import numpy as np
cimport numpy as np
def function1(response, max_loc):
x, y = int(max_loc[0]), int(max_loc[1])
tmp1 = (response[y,x+1] - response[y,x-1]) / 2*(response[y,x] - min(response[y,x-1], response[y,x+1]))
tmp2 = (response[y,x+1] - response[y,x-1])
tmp3 = 2*(response[y,x] - min(response[y,x-1], response[y,x+1]))
print tmp1, tmp2, tmp3
return tmp1, tmp2, tmp3
cpdef function2(np.ndarray[np.float32_t, ndim=2] response, np.ndarray[np.float64_t, ndim=1] max_loc):
cdef unsigned int x, y
x, y = int(max_loc[0]), int(max_loc[1])
tmp1 = (response[y,x+1] - response[y,x-1]) / 2*(response[y,x] - min(response[y,x-1], response[y,x+1]))
tmp2 = (response[y,x+1] - response[y,x-1])
tmp3 = 2*(response[y,x] - min(response[y,x-1], response[y,x+1]))
print tmp1, tmp2, tmp3
return tmp1, tmp2, tmp3
cpdef function3(np.ndarray[np.float32_t, ndim=2] response, np.ndarray[np.float64_t, ndim=1] max_loc):
cdef unsigned int x, y
x, y = int(max_loc[0]), int(max_loc[1])
cdef np.float32_t tmp1, tmp2, tmp3
cdef np.float32_t r1 =response[y,x+1]
cdef np.float32_t r2 =response[y,x-1]
cdef np.float32_t r3 =response[y,x]
cdef np.float32_t r4 =response[y,x-1]
cdef np.float32_t r5 =response[y,x+1]
tmp1 = (r1 - r2) / 2*(r3 - min(r4, r5))
tmp2 = (r1 - r2)
tmp3 = 2*(r3 - min(r4, r5))
print tmp1, tmp2, tmp3
return tmp1, tmp2, tmp3
def function4(response, max_loc):
x, y = int(max_loc[0]), int(max_loc[1])
tmp1 = (float(response[y,x+1]) - response[y,x-1]) / 2*(float(response[y,x]) - min(response[y,x-1], response[y,x+1]))
tmp2 = (float(response[y,x+1]) - response[y,x-1])
tmp3 = 2*(float(response[y,x]) - min(response[y,x-1], response[y,x+1]))
print tmp1, tmp2, tmp3
return tmp1, tmp2, tmp3
max_loc = np.asarray([ 15., 25.], dtype=np.float64)
response = np.zeros((49,49), dtype=np.float32)
x, y = int(max_loc[0]), int(max_loc[1])
response[y,x] = 0.959878861904
response[y,x-1] = 0.438348740339
response[y,x+1] = 0.753262758255
result1 = function1(response, max_loc)
result2 = function2(response, max_loc)
result3 = function3(response, max_loc)
result4 = function4(response, max_loc)
print result1
print result2
print result3
print result4
and the results:
0.0821185777156 0.314914 1.04306030273
0.082118573023 0.314914017916 1.04306024313
0.0821185708046 0.314914017916 1.04306030273
0.082118573023 0.314914017916 1.04306024313
(0.082118577715618812, 0.31491402, 1.043060302734375)
(0.08211857302303427, 0.3149140179157257, 1.0430602431297302)
(0.08211857080459595, 0.3149140179157257, 1.043060302734375)
(0.082118573023034269, 0.31491401791572571, 1.0430602431297302)
function1 represents the operations I did in my original python function. tmp1 is the result.
function2 is my first cython version, which produces slightly different results. Apparently, if the response array is indexed with a typed variable, unsigned int or int, the result is coerced to a double (using PyFloat_FromDouble) even if the type of the array is np.float32_t. But if the array is indexed with a python int the function PyObject_GetItem is used instead and I get the np.float32_t, which is what happens in function1. So the expressions in function1 are calculated using np.float32_t operands, whereas the expressions in function2 are calculated using doubles. I get slightly different print out than in function1.
function3 is my second cython attempt trying to get the same output as function1. Here I use unsigned int indices to access the array response but the results are left on np.float32_t intermediate variables that then I use in the calculation. I get slightly different result. Apparently the print statement will use PyFloat_FromDouble so it won't be able to print the np.float32_t.
Then I tried to change the python function to match the cython one. function4 tries to achieve this by converting to float at least one operand in each expression so the rest of operands are coerced to python float too, which is a double in cython, and the expression is calculated with doubles, as in function2. The prints inside the function are exactly the same as function2, but the returned values are slightly different?!