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.
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.
I will close this issue for now. We can think about an improved interface for numerical derivatives at a later stage.