[Gsll-devel] Recurring callbacks
Eugene Zhemchugov
jini.zh at gmail.com
Fri Jan 9 18:37:24 UTC 2015
I was trying to evaluate a recurring integral when I ran into what I
think a design flaw in the callbacks implementation. Consider the
following code:
(integration-qag (lambda (x)
(integration-qag (lambda(y)
(* (sin x) y))
0d0 1d0 :gauss41))
0d0 pi :gauss41)
The result of the integration should be 1, but I get 4.93. Digging
through the code, I figured out that callbacks are implemented in the
following way:
1. During the GSLL compilation, a callback is defined via the
CFFI:DEFCALLBACK macro.
2. The GSLL wrapper function assigns the function to be called to a
dynamic variable and calls the underlying GSL function.
3. The GSL function calls the CFFI callback.
4. The CFFI callback calls the value stored in the dynamic variable.
The relevant part of the GSLL wrapper function is generated by
BODY-EXPAND. Here is a simplified version of the code generated for
INTEGRATION-QAG:
(defun integration-qag (function ...)
(declare (special integration-qag-dynfn0))
(setf integration-qag-dynfn0 function)
(foreign-funcall "gsl_integration_qag" ...))
The problem here is that if the function passed as a parameter to the
GSLL function calls the same GSLL function again, as in the example
above, the dynamic variable value is overwritten during the second call,
and then the first GSL function ends up calling the second callback all
the time. Seemingly, this can be fixed by binding instead of assigning
the dynamic variable, i.e., by making BODY-EXPAND to generate code like
this:
(defun integration-qag (function ...)
(let ((integration-qag-dynfn0 function))
(declare (special integration-qag-dynfn0))
(foreign-funcall "gsl_integration_qag" ...)))
In this case the value of INTEGRATION-QAG-DYNFN0 is restored upon
leaving the LET form. However, having patched the code this way, I broke
callbacks introduced via the DEFMOBJECT macro. In this case the
assignment happens in the REINITIALIZE-INSTANCE method specialized on
this object, which is usually called in the construction phase. As an
example, consider NONLINEAR-LEAST-SQUARES-EXAMPLE. The assignment is
made during the call to MAKE-NONLINEAR-FDFFIT, however the callback is
executed during the call to ITERATE. I think that the binding should be
performed in ITERATE, as soon as it is needed. ITERATE receives an
instance of an object subclassed from CALLBACK-INCLUDED, hence it has
access both to the functions to be called (the FUNCALLABLES slot of
CALLBACK-INCLUDED) and the callback specification (the CBINFO slot). But
I don't know what would be the best way to implement it. One possibility
is to loop through function parameters in BODY-EXPAND and figure out
which are instances of CALLBACK-INCLUDED. That will work only for
methods, and only for the specialized parameters. Is it sufficient?
Other approaches I can think of would require changing the DEFMFUN
interface to specify which parameters should be used to construct the
bindings. What solution would you propose? I can make a patch for it if
you don't think that the changes are too drastic to trust not an
upstream developer.
You may find my partial patch in the attachement.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-Fix-recurring-callbacks-partial.patch
Type: text/x-patch
Size: 6840 bytes
Desc: not available
URL: <https://mailman.common-lisp.net/pipermail/gsll-devel/attachments/20150109/05a82ec9/attachment.bin>
More information about the gsll-devel
mailing list