Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Ctypes callback #190

Merged
merged 1 commit into from

4 participants

@teoliphant
Owner

This Pull Request allows integrate.quad to by-pass the Python-layer when given a Ctypes function pointer of the correct signature. When the callable passed to integrate.quad is a ctypes function pointer, it is checked for the correct signature, and then the raw C-function is handed to the underlying integration routine which does not call-back into Python.

@rgommers
Owner

On the C code changes I can't really comment (except that it looks useful).

The test is going to fail on some systems, because ctypes is not always available. So the import should be conditional, and the test should be decorated with @skipif.

About the timing test, doesn't that belong under benchmarks instead of tests? Each module has a bench() function for this sort of thing.

scipy/integrate/__quadpack.h
((62 lines not shown))
+}
+
+#define CHECK_FUNC(func_type, fcn) { \
+ func_type = get_func_type(fcn); \
+ if (func_type == ERROR) return NULL; \
+ if (func_type == NOTCALLABLE) { \
+ PyErr_SetString(PyExc_TypeError, "quad: first argument is not callable"); \
+ return NULL; \
+ } \
+ if (func_type == INVALID_CTYPE) { \
+ PyErr_SetString(PyExc_TypeError, \
+ "quad: first argument is a ctypes function pointer with incorrect signature"); \
+ return NULL; \
+ }}
+
+/* Just the first part of the ctypes CFuncPtrObject */
@pv Owner
pv added a note

This hack should be replaced with a call to ctypes.addressof via Python space -- I'd like to avoid mucking around with CPython internals.

@teoliphant Owner

This part is not a hack --- look at get_func_type again. It's just making calls to the ctypes module to identify what fcn is (a callable, a ctype-function with the right signature, or whatnot).

It sounds like you might be talking about the _GET_FUNC macro which is assuming something about the structure of ctypes objects. We could use ctypes.addressof via Python to get the function pointer, but that is a lot of C-code and reference count manipulation when it's a simple pointer de-reference in C-space. It would be very surprising if ctypes ever changed it's ABI so that the pointer immediately following PyObject_HEAD is not the function pointer we are after.

I just don't see it as a major problem in this case.

@pv Owner
pv added a note

Yep, it's about the _GET_FUNC below. I agree with you that it's unlikely that the ctypes API will change. However, this call is not on a hot path, so I would rather prefer the code use public APIs, if the performance gain from the hack is not significant.

@teoliphant Owner

This is going to come-up again on the NumPy list. I'm not excited about being dependent on a slow Python-only API to get this information out of CTypes whenever we need it --- so we are going to do something like this in NumPy.

One thought is to do this in NumPy and then have SciPy rely on the NumPy API. This seems like a reasonable path forward.

@teoliphant Owner

To that end, I will create a function for getting the raw function pointer using the Python code: ctypes.cast(func, ctypes.c_void_p).value. This is a bit un-pleasant in C and I would rather replace the code with something more direct (like an API that is supported somewhere else --- perhaps in NumPy).

@pv Owner
pv added a note

Ok, sounds good. Doing Python on C level is indeed messy (a similar situation with no C-level APIs occurs also with I/O on Python 3). It's probably best to move grabbing the pointer to a helper routine in a separate utilities header file. Also using the direct access hack could be considered if it is better encapsulated this way. But anyway, this is a minor issue, so it's best not to waste too much time on it.

@teoliphant Owner

I wrote a C-function to do the casting and my timing test is now 4x slower instead of 2x faster. Of course this is in a loop of 100 calls to the integration routine for a simple function.

This is really unfortunate. Let me see if I can play with it some more.

@teoliphant Owner

I changed the code back to using the direct function-pointer access. It was much, much faster. It makes me also wonder how much it is costing to do the ctypes type checks as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
scipy/integrate/__quadpack.h
@@ -67,6 +67,94 @@
already_printed_python_error = 0;\
}
+#define VALID_CTYPE 1
@pv Owner
pv added a note

Maybe enum?

@teoliphant Owner

Sure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
scipy/integrate/__quadpack.h
((28 lines not shown))
- if (PyErr_Occurred()) {
- ier = 80; /* Python error */
- PyErr_Clear();
+ if (PyErr_Occurred()) {
+ ier = 80; /* Python error */
+ PyErr_Clear();
+ }
+ }
+ else { /* func_type == VALID_CTYPE */
+ /* Can't allow another thread to run because of the global variables
+ quadpack_raw_function and quad_function2 being used */
@pv Owner
pv added a note

Could also this be made re-entrant by storing the quadpack_raw_function in store_quadpack_globals, like is done for the Python function? Granted, it's less likely that someone calls quad again from C code, but it could happen.

A thunk library would make life easier...

@teoliphant Owner

Yes, I thought about that. It would be easy enough to make it re-entrant and probably worth it, even with the low-probability event.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@teoliphant
Owner

Ralf and Pauli thanks for the feedback. I've commented on Pauli's notes in-line.

Ralf, thanks for the skipif suggestion. The timing test is not really a bench-mark. It's more of a "is this code calling the C-function pointer directly, or is it interpreting the Ctypes object as a Python-callback". So, I'm thinking of it as a test to see whether or not the C-function pointer is being called directly by QUADPACK. But, other ways to test this would be helpful. The speed test is a bit brittle.

scipy/integrate/__quadpack.h
@@ -20,35 +20,35 @@
*/
#if defined(NO_APPEND_FORTRAN)
- #if defined(UPPERCASE_FORTRAN)
- /* nothing to do here */
- #else
- #define DQAGSE dqagse
- #define DQAGIE dqagie
- #define DQAGPE dqagpe
- #define DQAWOE dqawoe
- #define DQAWFE dqawfe
- #define DQAWSE dqawse
- #define DQAWCE dqawce
- #endif
+ #if defined(UPPERCASE_FORTRAN)
+ /* nothing to do here */
+ #else
+ #define DQAGSE dqagse
@charris Collaborator
charris added a note

Tabs?

@teoliphant Owner

I'm not really sure what is going on here. Perhaps there are tabs in the original file. These have been replaced with spaces now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
scipy/integrate/__quadpack.h
((13 lines not shown))
+ Returns INVALID_CTYPE if it is a CType Function but of the incorrect type
+ Returns NOTCALLABLE if it is not a Python callable
+ Returns ERROR if other error occurs.
+*/
+
+static int
+get_func_type(PyObject *func) {
+ PyObject *ctypes_module, *CFuncPtr, *check, *c_double;
+ int result;
+
+ if (!PyCallable_Check(func)) return NOTCALLABLE;
+ ctypes_module = PyImport_ImportModule("ctypes");
+ if (ctypes_module == NULL) return ERROR;
+ CFuncPtr = PyObject_GetAttrString(ctypes_module, "_CFuncPtr");
+ if (CFuncPtr == NULL) {
+ Py_DECREF(ctypes_module);
@charris Collaborator
charris added a note

Indentation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
scipy/integrate/__quadpack.h
((54 lines not shown))
+ goto fail;
+
+ return VALID_CTYPE;
+
+fail:
+ Py_DECREF(check);
+ Py_XDECREF(c_double);
+ return INVALID_CTYPE;
+}
+
+#define CHECK_FUNC(func_type, fcn) { \
+ func_type = get_func_type(fcn); \
+ if (func_type == ERROR) return NULL; \
+ if (func_type == NOTCALLABLE) { \
+ PyErr_SetString(PyExc_TypeError, "quad: first argument is not callable"); \
+ return NULL; \
@charris Collaborator
charris added a note

Putting control transfer in a macro is just sinful. I cry every time I see this in Numpy, and I'm sure that somewhere a puppy dies.

@teoliphant Owner

That is a bit melo-dramatic. However, this is not general "control transfer" it's early exit from the function because of errors. It's semantically contained and easily identified upon reviewing the code. The point of any software engineering "ethics" should be to make code easier to write, read, and maintain. You always have to balance many things in that process. I find that zealous over-adherence to any one principle typically causes unintended effects that make things more difficult.

In this particular case, do you have an alternative suggestion that does not require removing the benefit of the macro in encapsulating code that is identical?

@charris Collaborator
charris added a note

What benefits? Modern compilers will inline small functions without prompting, plus you get type checking on the arguments and your eye can follow the flow of control without reference to some macro off in the woods. Really, I'm trying to break you of your macro habit, they simply aren't necessary for efficiency and when used like this obfuscate the code.

@teoliphant Owner

I told you the benefit. It prevents writing a lot of similar code over and over again. I'm not convinced that being able to "follow the flow of control" actually matters when the flow on error is out-of-the-function.

Any desire to "break" a habit requires first seeing the need to. You do a lot of complaining about style, code re-writing, but not a lot of demonstrating a better approach. This rarely works and generally alienates the people you are purportedly trying to "help". It also takes a lot of time and resources -- resources that are already in short supply. You have to also look at how often someone will care about a particular piece of code. The fact that this bit of code (its supposedly ugly macros and all) has not been really touched in 10 years says something about the need to spend time agonizing over how it gets written.

I'm left to do the work of inferring what you would rather see. Are you preferring to see something like:

if (!check_func(fcn)) return NULL;

where check_func is a regular function? How else would you do things?

-Travis

@teoliphant Owner

I've got code working that does the check_func and replaces that macro. I do not know how to replace the other macros and keep the benefit of storage variables and error-returning from within the other macros without major code re-design. Eventually, this C-code might be replaced with Cython anyway, and I'd rather not spend more time on it.

@teoliphant Owner

The presumed check_func function and the get_func_type function have been merged into a single function (no macros). There was the need to create a macro to do the temporary function storage trick which is similar to the python-function code-path. If a better approach can be written, I would be happy to see it.

@teoliphant Owner

Chuck: Sorry to be a bit prickly before on your observations about the use of Macros. My tendency is towards getting as quickly as possible to working code. Coupling that with most of the code being written when I knew less about the real reasons for object-oriented style and when it matters and when it doesn't often leaves me copying style instead of trying to improve it. I'm sure I'll continue to be more focused on getting something working rather than style, but I don't want to discourage you (or really anyone else as I know you are unflappable) from voicing your stylistic opinions.

@charris Collaborator
charris added a note

Not to worry. I figure it's my job to play the critic in these situations as your standing and reputation could easily intimidate some folks. Now I'm waiting for Mark to instruct me in the finer points...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
scipy/integrate/quadpack.h
@@ -60,6 +59,18 @@
quadpack_extra_arguments = store_quadpack_globals[1]; \
memcpy(&quadpack_jmpbuf, &store_jmp, sizeof(jmp_buf));
+#define INIT_CTYPES_FUNC(fcn) \
@charris Collaborator
charris added a note

In C++ this would be a class with a constructor and a restore method. I'd suggest a C implementation along the lines:

struct cfunc_store {
    void *qfunc;
    void *cfunc;
}

NPY_INLINE int
cfunc_store_init(struct cfunc_store *store, void *cfunc, void *qfunc)
{
    store->qfunc = qfunc;
    if ((store->cfunc = get_ctypes_function_pointer(cfunc)) == NULL) {
        return -1;
    }
    return 0;
}

NPY_INLINE void
cfunc_store_restore(struct cfunc_store *store)
{
    store->cfunc = store->qfunc;
}
@teoliphant Owner

Thanks. This is helpful. In just trying to get something to work, I was just mirroring the code that was there (from 1999). I'm not sure if it's the best use of time or not, but I agree it's better to define a structure like this. I'm changing all the code to reflect this style.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@teoliphant teoliphant ENH: Add ability to use ctypes function pointers in quadpack and clea…
…n-up quadpack to use Object-style C instead of Macros.
9ba5fcc
@teoliphant
Owner

O.K. I've redone the PR to address the major concerns. The ctypes implementation dependent stuff is reduced to a single function which could be updated to use NumPy's function call when it is created.

@pv
Owner
pv commented

Is there something additional that needs to be done to this? As far as I see, we don't need to wait here for the Cython/Numba/Numpy fast callback discussion to settle down. The quadpack wrapper code would call for some more reorganization, but that can maybe wait.

@teoliphant
Owner

I think this is ready to merge. I agree, we don't need to wait for the fast callback discussion.

@rgommers rgommers merged commit b2afa94 into from
@rgommers
Owner

OK, merged to get it in for 0.11.

@rgommers
Owner

Ctypes is used in quadpack, this doesn't work. See http://projects.scipy.org/scipy/ticket/1670.

@teoliphant
Owner

I will fix this up. It's an import in the C-code. I thought the only reference to ctypes itself was commented out. But, there is one more place. I will submit a new PR.

@teoliphant teoliphant referenced this pull request
Closed

Fix Issue #1670 #246

@jnothman jnothman referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 10, 2012
  1. @teoliphant

    ENH: Add ability to use ctypes function pointers in quadpack and clea…

    teoliphant authored
    …n-up quadpack to use Object-style C instead of Macros.
This page is out of date. Refresh to see the latest.
View
450 scipy/integrate/__quadpack.h
@@ -20,35 +20,35 @@
*/
#if defined(NO_APPEND_FORTRAN)
- #if defined(UPPERCASE_FORTRAN)
- /* nothing to do here */
- #else
- #define DQAGSE dqagse
- #define DQAGIE dqagie
- #define DQAGPE dqagpe
- #define DQAWOE dqawoe
- #define DQAWFE dqawfe
- #define DQAWSE dqawse
- #define DQAWCE dqawce
- #endif
+ #if defined(UPPERCASE_FORTRAN)
+ /* nothing to do here */
+ #else
+ #define DQAGSE dqagse
+ #define DQAGIE dqagie
+ #define DQAGPE dqagpe
+ #define DQAWOE dqawoe
+ #define DQAWFE dqawfe
+ #define DQAWSE dqawse
+ #define DQAWCE dqawce
+ #endif
#else
- #if defined(UPPERCASE_FORTRAN)
- #define DQAGSE DQAGSE_
- #define DQAGIE DQAGIE_
- #define DQAGPE DQAGPE_
- #define DQAWOE DQAWOE_
- #define DQAWFE DQAWFE_
- #define DQAWSE DQAWSE_
- #define DQAWCE DQAWCE_
- #else
- #define DQAGSE dqagse_
- #define DQAGIE dqagie_
- #define DQAGPE dqagpe_
- #define DQAWOE dqawoe_
- #define DQAWFE dqawfe_
- #define DQAWSE dqawse_
- #define DQAWCE dqawce_
- #endif
+ #if defined(UPPERCASE_FORTRAN)
+ #define DQAGSE DQAGSE_
+ #define DQAGIE DQAGIE_
+ #define DQAGPE DQAGPE_
+ #define DQAWOE DQAWOE_
+ #define DQAWFE DQAWFE_
+ #define DQAWSE DQAWSE_
+ #define DQAWCE DQAWCE_
+#else
+ #define DQAGSE dqagse_
+ #define DQAGIE dqagie_
+ #define DQAGPE dqagpe_
+ #define DQAWOE dqawoe_
+ #define DQAWFE dqawfe_
+ #define DQAWSE dqawse_
+ #define DQAWCE dqawce_
+ #endif
#endif
void DQAGSE();
@@ -60,11 +60,123 @@ void DQAWSE();
void DQAWCE();
-static int already_printed_python_error = 0;
+typedef enum {
+ Error=-3,
+ Not_Callable=-2,
+ Invalid_Ctype=-1,
+ /* Acceptable returns below */
+ Callable=1,
+ Valid_Ctype=2
+} FuncType;
+
+
+/* Checks a callable object:
+ Returns Valid_Ctype if the Python Object is a CType Function of the type (double) -> (double)
+ Return Callable if it is a Python callable (including a Ctypes function with no stored types)
+ Returns Invalid_Ctype if it is a CType Function but of the incorrect type
+ Returns Not_Callable if it is not a Python callable
+ Returns Error if other error occurs.
+*/
+
+static FuncType
+get_func_type(PyObject *func) {
+ PyObject *ctypes_module, *CFuncPtr, *check, *c_double;
+ int result;
+
+ if (!PyCallable_Check(func)) {
+ PyErr_SetString(quadpack_error, "quad: first argument is not callable");
+ return Not_Callable;
+ }
+ ctypes_module = PyImport_ImportModule("ctypes");
+ if (ctypes_module == NULL) return Error;
+ CFuncPtr = PyObject_GetAttrString(ctypes_module, "_CFuncPtr");
+ if (CFuncPtr == NULL) {
+ Py_DECREF(ctypes_module);
+ return Error;
+ }
+ result = PyObject_TypeCheck(func, (PyTypeObject *)CFuncPtr);
+ Py_DECREF(CFuncPtr);
+ if (!result) {
+ Py_DECREF(ctypes_module);
+ return Callable;
+ }
+
+ /* We are a ctypes function */
+ /* Check for restype and argtypes */
+ if (!PyObject_HasAttrString(func, "restype") ||
+ !PyObject_HasAttrString(func, "argtypes")) {
+ Py_DECREF(ctypes_module);
+ return Callable;
+ }
+
+ c_double = PyObject_GetAttrString(ctypes_module, "c_double");
+ Py_DECREF(ctypes_module);
+
+ check = PyObject_GetAttrString(func, "restype");
+ if (check != c_double) { /* Checking for pointer identity */
+ goto fail;
+ }
+ Py_DECREF(check);
+ check = PyObject_GetAttrString(func, "argtypes");
+ if (!PyTuple_Check(check) || (PyTuple_GET_SIZE(check) != 1) ||
+ (PyTuple_GET_ITEM(check, 0) != c_double))
+ goto fail;
+
+ Py_DECREF(check);
+ Py_DECREF(c_double);
+ return Valid_Ctype;
+
+fail:
+ Py_DECREF(check);
+ Py_XDECREF(c_double);
+ PyErr_SetString(quadpack_error,
+ "quad: first argument is a ctypes function pointer with incorrect signature");
+ return Invalid_Ctype;
+}
-#define QUAD_INIT_FUNC(fun,arg) {\
- INIT_FUNC(fun,arg,quadpack_error); \
- already_printed_python_error = 0;\
+/*
+ This is the equivalent to the above which only uses ctypes calls... However it is about 8x slower in a tight loop
+ than the above code:
+
+static _sp_double_func
+get_ctypes_function_pointer(PyObject *obj) {
+ PyObject *ctypes_module=NULL, *c_void_p=NULL, *castfunc=NULL, *value=NULL, *result=NULL;
+ void *final;
+
+ ctypes_module = PyImport_ImportModule("ctypes");
+ if (ctypes_module == NULL) return NULL;
+
+ castfunc = PyObject_GetAttrString(ctypes_module, "cast");
+ if (castfunc == NULL) goto fail;
+ c_void_p = PyObject_GetAttrString(ctypes_module, "c_void_p");
+ if (c_void_p == NULL) goto fail;
+ result = PyObject_CallFunctionObjArgs(castfunc, obj, c_void_p);
+ if (result == NULL) goto fail;
+ value = PyObject_GetAttrString(result, "value");
+ if (value == NULL) goto fail;
+ final = PyLong_AsVoidPtr(value);
+ if (PyErr_Occurred()) goto fail;
+
+ Py_DECREF(ctypes_module);
+ Py_DECREF(castfunc);
+ Py_DECREF(c_void_p);
+ Py_DECREF(result);
+ Py_DECREF(value);
+
+ return (_sp_double_func) final;
+
+fail:
+ Py_XDECREF(ctypes_module);
+ Py_XDECREF(castfunc);
+ Py_XDECREF(c_void_p);
+ Py_XDECREF(result);
+ Py_XDECREF(value);
+ return NULL;
+}
+*/
+
+double quad_function2(double *x) {
+ return quadpack_ctypes_function(*x);
}
double quad_function(double *x) {
@@ -87,7 +199,6 @@ double quad_function(double *x) {
/* Have to do own error checking because PyFloat_AsDouble returns -1 on
error -- making that return value from the function unusable.
-
No; Solution is to test for Python Error Occurrence if -1 is return of PyFloat_AsDouble.
*/
@@ -126,8 +237,8 @@ static PyObject *quadpack_qagse(PyObject *dummy, PyObject *args) {
int neval=0, ier=6, last=0, *iord;
double result=0.0, abserr=0.0;
double *alist, *blist, *rlist, *elist;
-
- STORE_VARS();
+ FuncType func_type;
+ QStorage storevar;
if (!PyArg_ParseTuple(args, "Odd|Oiddi", &fcn, &a, &b, &extra_args, &full_output, &epsabs, &epsrel, &limit)) return NULL;
limit_shape[0] = limit;
@@ -136,7 +247,8 @@ static PyObject *quadpack_qagse(PyObject *dummy, PyObject *args) {
if (limit < 1)
return Py_BuildValue("ddi",result,abserr,ier);
- QUAD_INIT_FUNC(fcn,extra_args)
+ if ((func_type = get_func_type(fcn)) < Callable)
+ return NULL;
/* Setup iwork and work arrays */
ap_iord = (PyArrayObject *)PyArray_SimpleNew(1,limit_shape,NPY_INT);
@@ -151,20 +263,30 @@ static PyObject *quadpack_qagse(PyObject *dummy, PyObject *args) {
rlist = (double *)ap_rlist->data;
elist = (double *)ap_elist->data;
- if (setjmp(quadpack_jmpbuf)) {
- goto fail;
- }
- else {
- DQAGSE(quad_function, &a, &b, &epsabs, &epsrel, &limit, &result, &abserr, &neval, &ier, alist, blist, rlist, elist, iord, &last);
- }
+ if (func_type == Callable) {
+ if (quad_init_func(&storevar, fcn, extra_args) == NPY_FAIL)
+ goto fail;
- RESTORE_FUNC();
+ if (setjmp(quadpack_jmpbuf)) {
+ quad_restore_func(&storevar, NULL);
+ goto fail;
+ }
+ else {
+ DQAGSE(quad_function, &a, &b, &epsabs, &epsrel, &limit, &result, &abserr, &neval, &ier, alist,
+ blist, rlist, elist, iord, &last);
+ }
- if (PyErr_Occurred()) {
- ier = 80; /* Python error */
- PyErr_Clear();
+ quad_restore_func(&storevar, &ier);
+ }
+ else { /* func_type == VALID_CTYPE */
+ /* Can't allow another thread to run because of the global variables
+ quadpack_raw_function and quad_function2 being used */
+ if (init_ctypes_func(&storevar, fcn) == NPY_FAIL)
+ goto fail;
+ DQAGSE(quad_function2, &a, &b, &epsabs, &epsrel, &limit, &result, &abserr, &neval, &ier, alist,
+ blist, rlist, elist, iord, &last);
+ restore_ctypes_func(&storevar);
}
- Py_DECREF(extra_args);
if (full_output) {
return Py_BuildValue("dd{s:i,s:i,s:N,s:N,s:N,s:N,s:N}i", result, abserr, "neval", neval, "last", last, "iord", PyArray_Return(ap_iord), "alist", PyArray_Return(ap_alist), "blist", PyArray_Return(ap_blist), "rlist", PyArray_Return(ap_rlist), "elist", PyArray_Return(ap_elist),ier);
@@ -179,8 +301,6 @@ static PyObject *quadpack_qagse(PyObject *dummy, PyObject *args) {
}
fail:
- RESTORE_FUNC();
- Py_XDECREF(extra_args);
Py_XDECREF(ap_alist);
Py_XDECREF(ap_blist);
Py_XDECREF(ap_rlist);
@@ -207,17 +327,20 @@ static PyObject *quadpack_qagie(PyObject *dummy, PyObject *args) {
int inf, neval=0, ier=6, last=0, *iord;
double result=0.0, abserr=0.0;
double *alist, *blist, *rlist, *elist;
+ FuncType func_type;
+ QStorage storevar;
- STORE_VARS();
-
- if (!PyArg_ParseTuple(args, "Odi|Oiddi", &fcn, &bound, &inf, &extra_args, &full_output, &epsabs, &epsrel, &limit)) return NULL;
+ if (!PyArg_ParseTuple(args, "Odi|Oiddi", &fcn, &bound, &inf, &extra_args,
+ &full_output, &epsabs, &epsrel, &limit))
+ return NULL;
limit_shape[0] = limit;
/* Need to check that limit is bigger than 1 */
if (limit < 1)
return Py_BuildValue("ddi",result,abserr,ier);
- QUAD_INIT_FUNC(fcn,extra_args);
+ if ((func_type = get_func_type(fcn)) < Callable)
+ return NULL;
/* Setup iwork and work arrays */
ap_iord = (PyArrayObject *)PyArray_SimpleNew(1,limit_shape,NPY_INT);
@@ -225,28 +348,34 @@ static PyObject *quadpack_qagie(PyObject *dummy, PyObject *args) {
ap_blist = (PyArrayObject *)PyArray_SimpleNew(1,limit_shape,NPY_DOUBLE);
ap_rlist = (PyArrayObject *)PyArray_SimpleNew(1,limit_shape,NPY_DOUBLE);
ap_elist = (PyArrayObject *)PyArray_SimpleNew(1,limit_shape,NPY_DOUBLE);
- if (ap_iord == NULL || ap_alist == NULL || ap_blist == NULL || ap_rlist == NULL || ap_elist == NULL) goto fail;
+ if (ap_iord == NULL || ap_alist == NULL || ap_blist == NULL || ap_rlist == NULL
+ || ap_elist == NULL) goto fail;
iord = (int *)ap_iord->data;
alist = (double *)ap_alist->data;
blist = (double *)ap_blist->data;
rlist = (double *)ap_rlist->data;
elist = (double *)ap_elist->data;
- if (setjmp(quadpack_jmpbuf)) {
- goto fail;
- }
- else {
- DQAGIE(quad_function, &bound, &inf, &epsabs, &epsrel, &limit, &result, &abserr, &neval, &ier, alist, blist, rlist, elist, iord, &last);
- }
-
- RESTORE_FUNC();
+ if (func_type == Callable) {
+ if (quad_init_func(&storevar, fcn, extra_args) == NPY_FAIL)
+ goto fail;
- if (PyErr_Occurred()) {
- ier = 80; /* Python error */
- PyErr_Clear();
+ if (setjmp(quadpack_jmpbuf)) {
+ quad_restore_func(&storevar, NULL);
+ goto fail;
+ }
+ else {
+ DQAGIE(quad_function, &bound, &inf, &epsabs, &epsrel, &limit, &result, &abserr, &neval, &ier, alist, blist, rlist, elist, iord, &last);
+ }
+ quad_restore_func(&storevar, &ier);
+ }
+ else { /* func_type == VALID_CTYPE */
+ /* Can't allow another thread to run because of the global variables
+ quadpack_raw_function and quad_function2 being used */
+ if (init_ctypes_func(&storevar, fcn) == NPY_FAIL) goto fail;
+ DQAGIE(quad_function2, &bound, &inf, &epsabs, &epsrel, &limit, &result, &abserr, &neval, &ier, alist, blist, rlist, elist, iord, &last);
+ restore_ctypes_func(&storevar);
}
-
- Py_DECREF(extra_args);
if (full_output) {
return Py_BuildValue("dd{s:i,s:i,s:N,s:N,s:N,s:N,s:N}i", result, abserr, "neval", neval, "last", last, "iord", PyArray_Return(ap_iord), "alist", PyArray_Return(ap_alist), "blist", PyArray_Return(ap_blist), "rlist", PyArray_Return(ap_rlist), "elist", PyArray_Return(ap_elist),ier);
@@ -261,8 +390,6 @@ static PyObject *quadpack_qagie(PyObject *dummy, PyObject *args) {
}
fail:
- RESTORE_FUNC();
- Py_XDECREF(extra_args);
Py_XDECREF(ap_alist);
Py_XDECREF(ap_blist);
Py_XDECREF(ap_rlist);
@@ -294,8 +421,8 @@ static PyObject *quadpack_qagpe(PyObject *dummy, PyObject *args) {
double result=0.0, abserr=0.0;
double *alist, *blist, *rlist, *elist;
double *pts, *points;
-
- STORE_VARS();
+ FuncType func_type;
+ QStorage storevar;
if (!PyArg_ParseTuple(args, "OddO|Oiddi", &fcn, &a, &b, &o_points, &extra_args, &full_output, &epsabs, &epsrel, &limit)) return NULL;
limit_shape[0] = limit;
@@ -304,7 +431,8 @@ static PyObject *quadpack_qagpe(PyObject *dummy, PyObject *args) {
if (limit < 1)
return Py_BuildValue("ddi",result,abserr,ier);
- QUAD_INIT_FUNC(fcn,extra_args)
+ if ((func_type = get_func_type(fcn)) < Callable)
+ return NULL;
ap_points = (PyArrayObject *)PyArray_ContiguousFromObject(o_points, NPY_DOUBLE, 1, 1);
if (ap_points == NULL) goto fail;
@@ -331,20 +459,26 @@ static PyObject *quadpack_qagpe(PyObject *dummy, PyObject *args) {
level = (int *)ap_level->data;
ndin = (int *)ap_level->data;
- if (setjmp(quadpack_jmpbuf)) {
- goto fail;
+ if (func_type == Callable) {
+ if (quad_init_func(&storevar, fcn, extra_args) == NPY_FAIL)
+ goto fail;
+
+ if (setjmp(quadpack_jmpbuf)) {
+ quad_restore_func(&storevar, NULL);
+ goto fail;
+ }
+ else {
+ DQAGPE(quad_function, &a, &b, &npts2, points, &epsabs, &epsrel, &limit, &result, &abserr, &neval, &ier, alist, blist, rlist, elist, pts, iord, level, ndin, &last);
+ }
+
+ quad_restore_func(&storevar, &ier);
}
else {
- DQAGPE(quad_function, &a, &b, &npts2, points, &epsabs, &epsrel, &limit, &result, &abserr, &neval, &ier, alist, blist, rlist, elist, pts, iord, level, ndin, &last);
+ if (init_ctypes_func(&storevar, fcn) == NPY_FAIL) goto fail;
+ DQAGPE(quad_function2, &a, &b, &npts2, points, &epsabs, &epsrel, &limit, &result, &abserr, &neval, &ier, alist, blist, rlist, elist, pts, iord, level, ndin, &last);
+ restore_ctypes_func(&storevar);
}
- RESTORE_FUNC()
-
- if (PyErr_Occurred()) {
- ier = 80; /* Python error */
- PyErr_Clear();
- }
- Py_DECREF(extra_args);
Py_DECREF(ap_points);
if (full_output) {
@@ -363,8 +497,6 @@ static PyObject *quadpack_qagpe(PyObject *dummy, PyObject *args) {
}
fail:
- RESTORE_FUNC();
- Py_XDECREF(extra_args);
Py_XDECREF(ap_alist);
Py_XDECREF(ap_blist);
Py_XDECREF(ap_rlist);
@@ -399,8 +531,8 @@ static PyObject *quadpack_qawoe(PyObject *dummy, PyObject *args) {
double result=0.0, abserr=0.0, omega=0.0;
double *chebmo;
double *alist, *blist, *rlist, *elist;
-
- STORE_VARS();
+ FuncType func_type;
+ QStorage storevar;
if (!PyArg_ParseTuple(args, "Odddi|OiddiiiiO", &fcn, &a, &b, &omega, &integr, &extra_args, &full_output, &epsabs, &epsrel, &limit, &maxp1, &icall, &momcom, &o_chebmo)) return NULL;
limit_shape[0] = limit;
@@ -409,7 +541,8 @@ static PyObject *quadpack_qawoe(PyObject *dummy, PyObject *args) {
if (limit < 1)
return Py_BuildValue("ddi",result,abserr,ier);
- QUAD_INIT_FUNC(fcn,extra_args)
+ if ((func_type = get_func_type(fcn)) < Callable)
+ return NULL;
if (o_chebmo != NULL) {
ap_chebmo = (PyArrayObject *)PyArray_ContiguousFromObject(o_chebmo, NPY_DOUBLE, 2, 2);
@@ -440,20 +573,26 @@ static PyObject *quadpack_qawoe(PyObject *dummy, PyObject *args) {
rlist = (double *)ap_rlist->data;
elist = (double *)ap_elist->data;
- if (setjmp(quadpack_jmpbuf)) {
- goto fail;
- }
- else {
- DQAWOE(quad_function, &a, &b, &omega, &integr, &epsabs, &epsrel, &limit, &icall, &maxp1, &result, &abserr, &neval, &ier, &last, alist, blist, rlist, elist, iord, nnlog, &momcom, chebmo);
- }
+ if (func_type == Callable) {
+ if (quad_init_func(&storevar, fcn, extra_args) == NPY_FAIL)
+ goto fail;
+
+ if (setjmp(quadpack_jmpbuf)) {
+ quad_restore_func(&storevar, NULL);
+ goto fail;
+ }
+ else {
+ DQAWOE(quad_function, &a, &b, &omega, &integr, &epsabs, &epsrel, &limit, &icall, &maxp1, &result, &abserr, &neval, &ier, &last, alist, blist, rlist, elist, iord, nnlog, &momcom, chebmo);
+ }
- RESTORE_FUNC();
+ quad_restore_func(&storevar, &ier);
- if (PyErr_Occurred()) {
- ier = 80; /* Python error */
- PyErr_Clear();
}
- Py_DECREF(extra_args);
+ else {
+ if (init_ctypes_func(&storevar, fcn) == NPY_FAIL) goto fail;
+ DQAWOE(quad_function2, &a, &b, &omega, &integr, &epsabs, &epsrel, &limit, &icall, &maxp1, &result, &abserr, &neval, &ier, &last, alist, blist, rlist, elist, iord, nnlog, &momcom, chebmo);
+ restore_ctypes_func(&storevar);
+ }
if (full_output) {
return Py_BuildValue("dd{s:i,s:i,s:N,s:N,s:N,s:N,s:N,s:N,s:i,s:N}i", result, abserr, "neval", neval, "last", last, "iord", PyArray_Return(ap_iord), "alist", PyArray_Return(ap_alist), "blist", PyArray_Return(ap_blist), "rlist", PyArray_Return(ap_rlist), "elist", PyArray_Return(ap_elist), "nnlog", PyArray_Return(ap_nnlog), "momcom", momcom, "chebmo", PyArray_Return(ap_chebmo),ier);
@@ -470,8 +609,6 @@ static PyObject *quadpack_qawoe(PyObject *dummy, PyObject *args) {
}
fail:
- RESTORE_FUNC();
- Py_XDECREF(extra_args);
Py_XDECREF(ap_alist);
Py_XDECREF(ap_blist);
Py_XDECREF(ap_rlist);
@@ -505,8 +642,8 @@ static PyObject *quadpack_qawfe(PyObject *dummy, PyObject *args) {
double *chebmo, *rslst, *erlst;
double result=0.0, abserr=0.0, omega=0.0;
double *alist, *blist, *rlist, *elist;
-
- STORE_VARS();
+ FuncType func_type;
+ QStorage storevar;
if (!PyArg_ParseTuple(args, "Oddi|Oidiii", &fcn, &a, &omega, &integr, &extra_args, &full_output, &epsabs, &limlst, &limit, &maxp1)) return NULL;
limit_shape[0] = limit;
@@ -516,7 +653,8 @@ static PyObject *quadpack_qawfe(PyObject *dummy, PyObject *args) {
if (limit < 1)
return Py_BuildValue("ddi",result,abserr,ier);
- QUAD_INIT_FUNC(fcn,extra_args)
+ if ((func_type = get_func_type(fcn)) < Callable)
+ return NULL;
sz[0] = 25;
sz[1] = maxp1;
@@ -545,20 +683,26 @@ static PyObject *quadpack_qawfe(PyObject *dummy, PyObject *args) {
erlst = (double *)ap_erlst->data;
ierlst = (int *)ap_ierlst->data;
- if (setjmp(quadpack_jmpbuf)) {
- goto fail;
+ if (func_type == Callable) {
+ if (quad_init_func(&storevar, fcn, extra_args) == NPY_FAIL)
+ goto fail;
+
+ if (setjmp(quadpack_jmpbuf)) {
+ quad_restore_func(&storevar, NULL);
+ goto fail;
+ }
+ else {
+ DQAWFE(quad_function, &a, &omega, &integr, &epsabs, &limlst, &limit, &maxp1, &result, &abserr, &neval, &ier, rslst, erlst, ierlst, &lst, alist, blist, rlist, elist, iord, nnlog, chebmo);
+ }
+
+ quad_restore_func(&storevar, &ier);
}
else {
- DQAWFE(quad_function, &a, &omega, &integr, &epsabs, &limlst, &limit, &maxp1, &result, &abserr, &neval, &ier, rslst, erlst, ierlst, &lst, alist, blist, rlist, elist, iord, nnlog, chebmo);
+ if (init_ctypes_func(&storevar, fcn) == NPY_FAIL) goto fail;
+ DQAWFE(quad_function2, &a, &omega, &integr, &epsabs, &limlst, &limit, &maxp1, &result, &abserr, &neval, &ier, rslst, erlst, ierlst, &lst, alist, blist, rlist, elist, iord, nnlog, chebmo);
+ restore_ctypes_func(&storevar);
}
- RESTORE_FUNC();
-
- if (PyErr_Occurred()) {
- ier = 80; /* Python error */
- PyErr_Clear();
- }
- Py_DECREF(extra_args);
Py_DECREF(ap_nnlog);
Py_DECREF(ap_alist);
Py_DECREF(ap_blist);
@@ -578,8 +722,6 @@ static PyObject *quadpack_qawfe(PyObject *dummy, PyObject *args) {
}
fail:
- RESTORE_FUNC();
- Py_XDECREF(extra_args);
Py_XDECREF(ap_alist);
Py_XDECREF(ap_blist);
Py_XDECREF(ap_rlist);
@@ -612,8 +754,8 @@ static PyObject *quadpack_qawce(PyObject *dummy, PyObject *args) {
int neval=0, ier=6, last=0, *iord;
double result=0.0, abserr=0.0;
double *alist, *blist, *rlist, *elist;
-
- STORE_VARS();
+ FuncType func_type;
+ QStorage storevar;
if (!PyArg_ParseTuple(args, "Oddd|Oiddi", &fcn, &a, &b, &c, &extra_args, &full_output, &epsabs, &epsrel, &limit)) return NULL;
limit_shape[0] = limit;
@@ -622,7 +764,8 @@ static PyObject *quadpack_qawce(PyObject *dummy, PyObject *args) {
if (limit < 1)
return Py_BuildValue("ddi",result,abserr,ier);
- QUAD_INIT_FUNC(fcn,extra_args)
+ if ((func_type = get_func_type(fcn)) < Callable)
+ return NULL;
/* Setup iwork and work arrays */
ap_iord = (PyArrayObject *)PyArray_SimpleNew(1,limit_shape,NPY_INT);
@@ -637,20 +780,26 @@ static PyObject *quadpack_qawce(PyObject *dummy, PyObject *args) {
rlist = (double *)ap_rlist->data;
elist = (double *)ap_elist->data;
- if (setjmp(quadpack_jmpbuf)) {
- goto fail;
- }
- else {
- DQAWCE(quad_function, &a, &b, &c, &epsabs, &epsrel, &limit, &result, &abserr, &neval, &ier, alist, blist, rlist, elist, iord, &last);
- }
+ if (func_type == Callable) {
+ if (quad_init_func(&storevar, fcn, extra_args) == NPY_FAIL)
+ goto fail;
+
+ if (setjmp(quadpack_jmpbuf)) {
+ quad_restore_func(&storevar, NULL);
+ goto fail;
+ }
+ else {
+ DQAWCE(quad_function, &a, &b, &c, &epsabs, &epsrel, &limit, &result, &abserr, &neval, &ier, alist, blist, rlist, elist, iord, &last);
+ }
- RESTORE_FUNC();
+ quad_restore_func(&storevar, &ier);
- if (PyErr_Occurred()) {
- ier = 80; /* Python error */
- PyErr_Clear();
+ }
+ else {
+ if (init_ctypes_func(&storevar, fcn) == NPY_FAIL) goto fail;
+ DQAWCE(quad_function2, &a, &b, &c, &epsabs, &epsrel, &limit, &result, &abserr, &neval, &ier, alist, blist, rlist, elist, iord, &last);
+ restore_ctypes_func(&storevar);
}
- Py_DECREF(extra_args);
if (full_output) {
return Py_BuildValue("dd{s:i,s:i,s:N,s:N,s:N,s:N,s:N}i", result, abserr, "neval", neval, "last", last, "iord", PyArray_Return(ap_iord), "alist", PyArray_Return(ap_alist), "blist", PyArray_Return(ap_blist), "rlist", PyArray_Return(ap_rlist), "elist", PyArray_Return(ap_elist),ier);
@@ -665,8 +814,6 @@ static PyObject *quadpack_qawce(PyObject *dummy, PyObject *args) {
}
fail:
- RESTORE_FUNC();
- Py_XDECREF(extra_args);
Py_XDECREF(ap_alist);
Py_XDECREF(ap_blist);
Py_XDECREF(ap_rlist);
@@ -695,8 +842,8 @@ static PyObject *quadpack_qawse(PyObject *dummy, PyObject *args) {
int neval=0, ier=6, last=0, *iord;
double result=0.0, abserr=0.0;
double *alist, *blist, *rlist, *elist;
-
- STORE_VARS();
+ FuncType func_type;
+ QStorage storevar;
if (!PyArg_ParseTuple(args, "Odd(dd)i|Oiddi", &fcn, &a, &b, &alfa, &beta, &integr, &extra_args, &full_output, &epsabs, &epsrel, &limit)) return NULL;
limit_shape[0] = limit;
@@ -705,7 +852,8 @@ static PyObject *quadpack_qawse(PyObject *dummy, PyObject *args) {
if (limit < 1)
return Py_BuildValue("ddi",result,abserr,ier);
- QUAD_INIT_FUNC(fcn,extra_args)
+ if ((func_type = get_func_type(fcn)) < Callable)
+ return NULL;
/* Setup iwork and work arrays */
ap_iord = (PyArrayObject *)PyArray_SimpleNew(1,limit_shape,NPY_INT);
@@ -720,21 +868,26 @@ static PyObject *quadpack_qawse(PyObject *dummy, PyObject *args) {
rlist = (double *)ap_rlist->data;
elist = (double *)ap_elist->data;
- if (setjmp(quadpack_jmpbuf)) {
- goto fail;
+ if (func_type == Callable) {
+ if (quad_init_func(&storevar, fcn, extra_args) == NPY_FAIL)
+ goto fail;
+
+ if (setjmp(quadpack_jmpbuf)) {
+ quad_restore_func(&storevar, NULL);
+ goto fail;
+ }
+ else {
+ DQAWSE(quad_function, &a, &b, &alfa, &beta, &integr, &epsabs, &epsrel, &limit, &result, &abserr, &neval, &ier, alist, blist, rlist, elist, iord, &last);
+ }
+
+ quad_restore_func(&storevar, &ier);
}
else {
- DQAWSE(quad_function, &a, &b, &alfa, &beta, &integr, &epsabs, &epsrel, &limit, &result, &abserr, &neval, &ier, alist, blist, rlist, elist, iord, &last);
+ if (init_ctypes_func(&storevar, fcn) == NPY_FAIL) goto fail;
+ DQAWSE(quad_function2, &a, &b, &alfa, &beta, &integr, &epsabs, &epsrel, &limit, &result, &abserr, &neval, &ier, alist, blist, rlist, elist, iord, &last);
+ restore_ctypes_func(&storevar);
}
-
- RESTORE_FUNC();
-
- if (PyErr_Occurred()) {
- ier = 80; /* Python error */
- PyErr_Clear();
- }
- Py_DECREF(extra_args);
-
+
if (full_output) {
return Py_BuildValue("dd{s:i,s:i,s:N,s:N,s:N,s:N,s:N}i", result, abserr, "neval", neval, "last", last, "iord", PyArray_Return(ap_iord), "alist", PyArray_Return(ap_alist), "blist", PyArray_Return(ap_blist), "rlist", PyArray_Return(ap_rlist), "elist", PyArray_Return(ap_elist),ier);
}
@@ -748,8 +901,6 @@ static PyObject *quadpack_qawse(PyObject *dummy, PyObject *args) {
}
fail:
- RESTORE_FUNC();
- Py_XDECREF(extra_args);
Py_XDECREF(ap_alist);
Py_XDECREF(ap_blist);
Py_XDECREF(ap_rlist);
@@ -758,6 +909,3 @@ static PyObject *quadpack_qawse(PyObject *dummy, PyObject *args) {
return NULL;
}
-
-
-
View
1  scipy/integrate/_quadpackmodule.c
@@ -2,7 +2,6 @@
From Multipack project
*/
#include "quadpack.h"
-static PyObject *quadpack_error;
#include "__quadpack.h"
static struct PyMethodDef quadpack_module_methods[] = {
{"_qagse", quadpack_qagse, METH_VARARGS, doc_qagse},
View
100 scipy/integrate/quadpack.h
@@ -27,44 +27,96 @@ results are placed in a dictionary and returned as an optional member of
the result tuple when the full_output argument is non-zero.
*/
-#include "Python.h"
+#include "Python.h"
#include "numpy/npy_3kcompat.h"
#include "numpy/arrayobject.h"
#include <setjmp.h>
+
#define PYERR(errobj,message) {PyErr_SetString(errobj,message); goto fail;}
#define PYERR2(errobj,message) {PyErr_Print(); PyErr_SetString(errobj, message); goto fail;}
#define ISCONTIGUOUS(m) ((m)->flags & CONTIGUOUS)
-#define STORE_VARS() PyObject *store_quadpack_globals[2]; jmp_buf store_jmp;
-
-#define INIT_FUNC(fun,arg,errobj) { /* Get extra arguments or set to zero length tuple */ \
- store_quadpack_globals[0] = quadpack_python_function; \
- store_quadpack_globals[1] = quadpack_extra_arguments; \
- memcpy(&store_jmp,&quadpack_jmpbuf,sizeof(jmp_buf)); \
- if (arg == NULL) { \
- if ((arg = PyTuple_New(0)) == NULL) goto fail; \
- } \
- else \
- Py_INCREF(arg); /* We decrement on exit. */ \
- if (!PyTuple_Check(arg)) \
- PYERR(errobj,"Extra Arguments must be in a tuple"); \
- /* Set up callback functions */ \
- if (!PyCallable_Check(fun)) \
- PYERR(errobj,"First argument must be a callable function."); \
- quadpack_python_function = fun; \
- quadpack_extra_arguments = arg;}
-
-#define RESTORE_FUNC() quadpack_python_function = store_quadpack_globals[0]; \
- quadpack_extra_arguments = store_quadpack_globals[1]; \
- memcpy(&quadpack_jmpbuf, &store_jmp, sizeof(jmp_buf));
static PyObject *quadpack_python_function=NULL;
static PyObject *quadpack_extra_arguments=NULL; /* a tuple */
static jmp_buf quadpack_jmpbuf;
-
+static double (*quadpack_ctypes_function)(double) = NULL;
+
+static PyObject *quadpack_error;
+
+/* Stack Storage for re-entrant capability */
+typedef struct {
+ void *global0;
+ void *global1;
+ jmp_buf jmp;
+ PyObject *arg;
+} QStorage;
+
+typedef double (*_sp_double_func)(double);
+
+typedef struct {
+ PyObject_HEAD
+ char *b_ptr;
+} _sp_cfuncptr_object;
+
+static _sp_double_func
+get_ctypes_function_pointer(PyObject *obj) {
+ return (*((void **)(((_sp_cfuncptr_object *)(obj))->b_ptr)));
+}
+
+static int
+quad_init_func(QStorage *store, PyObject *fun, PyObject *arg) {
+ store->global0 = (void *)quadpack_python_function;
+ store->global1 = (void *)quadpack_extra_arguments;
+ memcpy(&(store->jmp), &quadpack_jmpbuf, sizeof(jmp_buf));
+ store->arg = arg;
+ if (store->arg == NULL) {
+ if ((store->arg = PyTuple_New(0)) == NULL)
+ return NPY_FAIL;
+ }
+ else {
+ Py_INCREF(store->arg); /* We decrement on restore */
+ }
+ if (!PyTuple_Check(store->arg)) {
+ PyErr_SetString(quadpack_error, "Extra Arguments must be in a tuple");
+ Py_XDECREF(store->arg);
+ return NPY_FAIL;
+ }
+ quadpack_python_function = fun;
+ quadpack_extra_arguments = store->arg;
+ return NPY_SUCCEED;
+}
+
+static void
+quad_restore_func(QStorage *store, int *ierr) {
+ quadpack_python_function = (PyObject *)store->global0;
+ quadpack_extra_arguments = (PyObject *)store->global1;
+ memcpy(&quadpack_jmpbuf, &(store->jmp), sizeof(jmp_buf));
+ Py_XDECREF(store->arg);
+ if (ierr != NULL) {
+ if (PyErr_Occurred()) {
+ *ierr = 80; /* Python error */
+ PyErr_Clear();
+ }
+ }
+}
+
+static int
+init_ctypes_func(QStorage *store, PyObject *fun) {
+ store->global0 = quadpack_ctypes_function;
+ store->global1 = get_ctypes_function_pointer(fun);
+ if (store->global1 == NULL) return NPY_FAIL;
+ quadpack_ctypes_function = store->global1;
+ return NPY_SUCCEED;
+}
+
+static void
+restore_ctypes_func(QStorage *store) {
+ quadpack_ctypes_function = store->global0;
+}
View
47 scipy/integrate/tests/test_quadpack.py
@@ -1,12 +1,57 @@
from numpy import sqrt, cos, sin, arctan, exp, log, pi, Inf
-from numpy.testing import assert_, TestCase, run_module_suite
+from numpy.testing import assert_, TestCase, run_module_suite, dec
from scipy.integrate import quad, dblquad, tplquad
+import sys
+import math
+
+try:
+ import ctypes
+ _ctypes_missing = False
+except ImportError:
+ _ctypes_missing = True
def assert_quad((value, err), tabledValue, errTol=1.5e-8):
assert_(abs(value-tabledValue) < err, (value, tabledValue, err))
if errTol is not None:
assert_(err < errTol, (err, errTol))
+
+class TestCtypesQuad(TestCase):
+ @dec.skipif(_ctypes_missing, msg="Ctypes library could not be found")
+ def setUp(self):
+ if sys.platform == 'win32':
+ file = 'msvcrt.dll'
+ elif sys.platform == 'darwin':
+ file = 'libm.dylib'
+ else:
+ file = 'libm.so'
+ self.lib = ctypes.CDLL(file)
+ restype = ctypes.c_double
+ argtypes = (ctypes.c_double,)
+ for name in ['sin', 'cos', 'tan']:
+ func = getattr(self.lib, name)
+ func.restype = restype
+ func.argtypes = argtypes
+
+ @dec.skipif(_ctypes_missing, msg="Ctypes library could not be found")
+ def test_typical(self):
+ assert_quad(quad(self.lib.sin,0,5),quad(math.sin,0,5)[0])
+ assert_quad(quad(self.lib.cos,0,5),quad(math.cos,0,5)[0])
+ assert_quad(quad(self.lib.tan,0,1),quad(math.tan,0,1)[0])
+
+ @dec.skipif(_ctypes_missing, msg="Ctypes library could not be found")
+ def test_improvement(self):
+ import time
+ start = time.time()
+ for i in xrange(100):
+ quad(self.lib.sin, 0, 100)
+ fast = time.time() - start
+ start = time.time()
+ for i in xrange(100):
+ quad(math.sin, 0, 100)
+ slow = time.time() - start
+ assert_(fast < 0.5*slow, (fast, slow))
+
class TestQuad(TestCase):
def test_typical(self):
# 1) Typical function with two extra arguments:
Something went wrong with that request. Please try again.