-
-
Notifications
You must be signed in to change notification settings - Fork 5.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ENH: Add derivatives for BarycentricInterpolator #18197
Conversation
…ut and Trefethen (cf scipy#2725)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @jcwhitehead, didn't have time to check the maths in any detail but a few suggestions to start
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @jcwhitehead. Some additional suggestions to help with the review.
(Thanks @j-bowhay! A few things for you to note 😉)
I just removed @ev-br as a requested reviewer by accident - apologies (I don't seem to have a way to undo this). I think I've addressed all the points raised but if there's any more feedback I can keep iterating further. Please do just 'unresolve' a conversation I've marked as resolved if you disagree and think there's more to be done. |
No worries, this has no impact for us and how we approach PR reviews. (We don't use these features that much.)
In general please don't resolve discussions at all. This is important for maintainers to know where they are at in their review. You can add a quick comment or some thumbs up if you want on specific discussions to indicate you took care of it. Or make a general message listing what you did and what's left to be done. |
scipy/interpolate/_polyint.py
Outdated
x : array_like | ||
1D array of points at which to evaluate the derivatives | ||
der : integer, optional | ||
The number of derivatives to evaluate, from 'order 0' (der=1) to order der-1. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Our linter is a bit lax at the moment, but we still would like to have everything under 80 chars (79 max).
@@ -665,8 +738,8 @@ def _evaluate(self, x): | |||
c = x[..., np.newaxis] - self.xi | |||
z = c == 0 | |||
c[z] = 1 | |||
c = self.wi/c | |||
with np.errstate(divide='ignore'): | |||
c = self.wi / c |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not 100% sure about this one, but noting this here. Please refrain from updating anything else than what your work requires. Unless of course the linter is unhappy and ask you to do it.
scipy/interpolate/_polyint.py
Outdated
c = self.wi/c | ||
with np.errstate(divide='ignore'): | ||
c = self.wi / c | ||
with np.errstate(divide='ignore', invalid='ignore'): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where is the warning coming from? We try to avoid things like that and if we do, it's best to document
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for spotting this - I think this is probably now unnecessary after I added the ValueError
in __init__
to catch the special (nonsensical) case in which nodes coincide, so I will remove it.
scipy/interpolate/_polyint.py
Outdated
def __init__(self, xi, yi=None, axis=0): | ||
_Interpolator1D.__init__(self, xi, yi, axis) | ||
def __init__(self, xi, yi=None, axis=0, *, wi=None): | ||
_Interpolator1DWithDerivatives.__init__(self, xi, yi, axis) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_Interpolator1DWithDerivatives.__init__(self, xi, yi, axis) | |
super().__init__(self, xi, yi, axis) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the corresponding line in KroghInterpolator
also be updated to use super()
or is this kind of style change only for new code?
…more specific with match='...'
Those latest three CI failures don't look like they're related to anything that's changed in the commit:
From my side I think everything raised so far has now been addressed - if not or if there's more feedback I'm happy to keep iterating to get it approved and merged. |
Thanks @jcwhitehead this is on my list to have another look at but it might be a couple of weeks before I get enough time |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few minor changes but otherwise I think this is looking good. See https://numpydoc.readthedocs.io/en/latest/format.html#common-rest-concepts for when to use single vs double ticks.
Finally, as this is a new feature please could you write a post to the mailing list inviting others to take a look.
# fill in correct diagonal entries: each column sums to 0 | ||
np.fill_diagonal(c, 0) | ||
# c[j,j] = -sum_{i != j} c[i,j] | ||
d = -c.sum(axis=1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
eq 9.5?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Equation numbers of the original paper would be great to add actually.
Friendly ping @jcwhitehead, are you interested in finishing this up? This is looking in good shape with only some minor changes required so it would be great to see this in the next SciPy release |
@ev-br @Kai-Striega @AtsushiSakai I have pushed some changes to hopefully get this over the line. Since I have made some edits would one of you be able to have a look over this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM modulo minor nits.
scipy/interpolate/_polyint.py
Outdated
c = self.xi[..., np.newaxis] - self.xi | ||
# c[i,j] = (w[j] / w[i]) / (xi[i] - xi[j]) | ||
c = np.divide(self.wi, c * self.wi[..., np.newaxis], | ||
out=np.zeros_like(c), where=c != 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what is going on here. out=np.zeros_like(c)
signals "throw away the result", but then it is returned, ouch. It is probably deliberately ingenious (which it is!), but can we please write it in a more boring way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was one of the changes I made to avoid rewriting the diagonal back a forth (as c is 0 along the diagonal). Here the division is performed normally apart from where c==0
where the result is instead left unchanged in the out
array (hence is zero as desired).
Would you be happy with a comment explaining what is happening?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately I haven't had any time to keep up with this lately so I'm out of the loop, but I'd actually be a bit surprised if the new version with its quadratic comparisons was faster than the previous version with two linear fills.
If it's a meaningful difference it's probably worth propagating to the older barycentric interpolation code which uses the same two diagonal fills.
@j-bowhay I guess you've checked the new version is faster - do you have some numbers? I'd be especially interested in the high derivatives, huge xi
case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have to admit I thought I benchmarked this but clearly not. There is no applicable difference so I will revert the change:
import numpy as np
import perfplot
def setup(n):
xi = np.random.random(n)
c = xi - xi[:, np.newaxis]
np.fill_diagonal(c, 0)
return c
def old(x):
c = np.copy(x)
np.fill_diagonal(c, 1)
c = 1/c
np.fill_diagonal(c,0)
return c
def new(x):
c = np.copy(x)
return np.divide(1, c, out=np.zeros_like(c), where=c!=0)
perfplot.show(setup=setup, kernels=[old, new], n_range=[2**k for k in range(15)])
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with just adding a comment, FTR.
scipy/interpolate/_polyint.py
Outdated
|
||
if self._diff_cij is None: | ||
# c[i,j] = xi[i] - xi[j] | ||
c = self.xi[..., np.newaxis] - self.xi |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: The comment hints that self.xi
is 1D, is this always correct? If it is, I'd ask to consider writing a more explicit xi[:, np.newaxis]
.
Rationale: an Ellipsis signals an unspecified number of multiple dimensions. So if it really possibly multidim, could you please make it more explicit in the comment line above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes good spot self.xi
is the x coordinates of the points the polynomial should pass through so is 1d
# fill in correct diagonal entries: each column sums to 0 | ||
np.fill_diagonal(c, 0) | ||
# c[j,j] = -sum_{i != j} c[i,j] | ||
d = -c.sum(axis=1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Equation numbers of the original paper would be great to add actually.
Thanks @ev-br @jcwhitehead, I have hopefully addressed all comments including reverting the use of |
This now LGTM. Anything you want to add/tweak @jcwhitehead @j-bowhay ? One minor hiccup is 11 commits looking a bit too much here, would be nice to squash some, while preserving authorship. |
[skip actions] [skip cirrus]
I had one final look and all happy from my end. I am happy just to squash merge if that works for everyone (my contributions were mostly just housekeeping) |
OK, let's merge this then if everyone's happy. Thank you @jcwhitehead , @j-bowhay ! P.S. Turns out GH adds a co-authored-by stanza to commit messages on squash-merges. |
Thanks @jcwhitehead for getting the ball rolling on this, it was a great first contribution. I hope we can tempt you back for more when you have the time! |
What does this implement/fix?
Implements derivatives for
BarycentricInterpolator
.I've extended the existing tests for the
KroghInterpolator
derivatives to also test the new methods, and added a few additional tests.Reference issue
This should close #2725.
Additional information
I haven't contributed to scipy before – let me know if I've missed anything! Happy to implement changes before it can be merged.