fjosw/pyerrors

numerical differentiation in derived_obs not working

s-kuberski opened this issue · 5 comments

Currently, the numerical derivative is not working in derived_obs. Minimal working example:

pe.derived_observable(np.exp, pe.cov_Obs(1, .1, 'test'), num_grad=True)

gives me

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-75-1c77dc9277a6> in <module>
----> 1 pe.derived_observable(np.exp, pe.cov_Obs(1, .1, 'test'), num_grad=True, base_step=.1)

~/phd/git/pyerrors_github/pyerrors/obs.py in derived_observable(func, data, array_mode, **kwargs)
   1186         values = np.vectorize(lambda x: x.value)(data)
   1187 
-> 1188     new_values = func(values, **kwargs)
   1189 
   1190     multi = int(isinstance(new_values, np.ndarray))

TypeError: 'num_grad' is an invalid keyword to ufunc 'exp'

The problem is the use of kwargs inside this function that are passed to func although it might not be able to cope with this.

An easy fix would be to manually pop the keywords man_grad and num_grad, if they are present. However, since the user could add all the kwargs of numdifftools.step_generators.MaxStepGenerator, this would not be sufficient (except you explicitly exclude all of them, as well. This should work, but could break if new keywords are defined in an upcoming version of this function). I tried to use the solution of https://stackoverflow.com/a/44052550, but this does not seem to work for numpy functions.

Also, when I fix this issue and provide a list of Obs, as suggested by the documentation, I get the error

Exception: Multi mode currently not supported for numerical derivative

This exception is not thrown, if I provide a plain Obs, but there is no way for the user to guess that. Also, if the function itself returns an array, even if it is 0-dimensional, the method fails.

In general, I think that one or two additional examples how to construct derived observables could make it easier for users. The minimal example with the lambda function in the description might not be sufficient for more complex applications.

fjosw commented

One easy solution to circumvent this issue is to wrap the function in a lambda function:

pe.derived_observable(lambda x, **kwargs: np.exp(x), pe.cov_Obs(1, .1, 'test'), num_grad=True)

we could add this as another example to the documentation.
I haven't looked at the numerical derivative for quite some time, I think it is only used in tests as of now. We could of course think about exposing a better interface for numerical derivatives if you think that this is useful.

That is indeed a good solution! I have tested some cases where numerical derivatives, or at least a cross-check of explicitly provided man_grad derivatives would be useful. I don't know if someone should use this feature in production code, but it could help in cases where special functions enter in iterative procedures and analytical derivatives are not easily known.

fjosw commented

I will close this issue for now. We can think about an improved interface for numerical derivatives at a later stage.