Cython program with numpy arrays does not allow ve

2019-07-24 13:35发布

问题:

FIXED: see updated code below.

This is my first Cython attempt and have a working build but it doesn't allow numpy array (vectors) as inputs, which is my real purpose here. It is the Black model (Black Scholes for European option pricing without a dividend). It will only accept length 1 arrays as inputs (if I try to calculate with length 2 arrays it errors: TypeError: CyBlack() takes exactly 7 positional arguments (14 given), if I pass length 10 arrays, (70 given), etc.). I'm not sure why as I have defined numpy arrays within the Cython code. For reference you can compile it with the below code then use it as such: from CyBlack.CyBlack import CyBlack then calling CyBlack(BlackPnL, Black_S, Black_Texpiry, Black_strike, Black_volatility, Black_IR, Black_callput) So here's the code (save as CyBlack.pyx file to compile):

from numpy cimport ndarray
cimport numpy as np
cimport cython

cdef extern from "math.h":
    double exp(double)
    double sqrt(double)
    double log(double)
    double erf(double)

cdef double std_norm_cdf(double x):
    return 0.5*(1+erf(x/sqrt(2.0)))

@cython.boundscheck(False)
cpdef CyBlack(ndarray[np.float64_t, ndim=1] BlackPnL, ndarray[np.float64_t, ndim=1] Black_S, ndarray[np.float64_t, ndim=1] Black_Texpiry, ndarray[np.float64_t, ndim=1] Black_strike, ndarray [np.float64_t, ndim=1] Black_volatility, ndarray[np.float64_t, ndim=1] Black_IR, ndarray[np.int64_t, ndim=1] Black_callput):

    cdef Py_ssize_t i
    cdef Py_ssize_t N = BlackPnL.shape[0]
    cdef double d1, d2


    for i in range(N):
        d1 = ((log(Black_S[i] / Black_strike[i]) + Black_Texpiry[i] * Black_volatility[i] **2 / 2)) / (Black_volatility[i] * sqrt(Black_Texpiry[i]))
        d2 = d1 - Black_volatility[i] * sqrt(Black_Texpiry[i])
        BlackPnL[i] = exp(-Black_IR[i] * Black_Texpiry[i]) * (Black_callput[i] * Black_S[i] * std_norm_cdf(Black_callput[i] * d1) - Black_callput[i] * Black_strike[i] * std_norm_cdf(Black_callput[i] * d2)) 

    return BlackPnL

Here is the setup.py so others can build this typing: python setup.py build_ext --inplace built with VS2015 for Python 3.5 64bit Windows.

from distutils.core import setup
from Cython.Build import cythonize
import numpy

extra_compile_args = ['/EHsc', '/openmp', '/favor:INTEL64']

setup(
ext_modules=cythonize("CyBlack.pyx"),
include_dirs=['.', numpy.get_include()],
extra_compile_args=extra_compile_args)

The above compiles and works after using the fix on the variable type (Black_callput was actually an int64) and you have to pass the numpy arrays as they are (no star) and it works correctly.

回答1:

There were two issues here:

  1. TypeError: CyBlack() takes exactly 7 positional arguments (14 given)

    This was caused by the fact that you were unpacking your input arrays using the * ("splat") operator. You needed to pass them in as 7 separate arguments instead.

  2. ValueError: Buffer dtype mismatch, expected 'float64_t' but got 'long long':

    This error was telling you that one of the input arrays had a C type of long long (which is a 64 bit integer), rather than float64_tas specified in your input type declarations. You needed to cast all of your input arrays to np.float64.