8000 ENH: Added optional `scale` parameter to `sinc` to enable non-normali… by madphysicist · Pull Request #7322 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

ENH: Added optional scale parameter to sinc to enable non-normali… #7322

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

Closed
wants to merge 1 commit into from

Conversation

madphysicist
Copy link
Contributor

…zed function.

Currently, sinc(x) is defined as sin(pi*x)/(pi*x). It is sometimes convenient to have the mathematical definition available: `sin(x)/(x). In fact, any scaling is now possible with a scalar input. Tests included.

Also modified the docs for where very slightly.

if scale == 1.0:
y = where(x == 0, 1.0e-20, x)
else:
y = scale * where(x == 0, 1.0e-20, x)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To reduce code duplication, might be better to write:

y = where(x == 0, 1.0e-20, x)
if scale != 1.0:
    y *= scale

@njsmith
Copy link
Member
njsmith commented Feb 23, 2016

Code looks fine to me modulo small comments noted above.

This is a public API change, so can you send a note to the list soliciting feedback? (I can read the code, but I've never used np.sinc in my life, so can't really comment on the design :-).)

@madphysicist
Copy link
Contributor Author

I made the changes you requested, and some extra. I think broadcasting is fine (certainly harmless if done correctly), so I updated the docs accordingly and added a test.

This PR came from the fact that I started using sinc recently and noticed that it was not the mathematical function I expected. I did not see a reason not to get the numbers I wanted without breaking existing code. I will also propose making this a ufunc long-term.

@mhvk
Copy link
Contributor
mhvk commented Feb 24, 2016

I like how the PR cleans up min_val in sync, but is there a real need for a scale keyword argument? At present, it seems one can do exactly the same with np.sinc(x/np.pi*scale); is it really better to be able to do np.sinc(x, scale=scale)? I ask in part since adding scale will make it more difficult to make it a ufunc.

@madphysicist
Copy link
Contributor Author

@mhvk. I was trying to avoid A) creating as many temp arrays as possible, B) Doing a multiply-then-divide by pi. I think that the scale keyword helps materially with this. For the use case I had in mind, the data is pretty sensitive and large, so it makes a lot more sense this way.

As far as the ufunc issue goes, I am looking into how to enable arbitrary parameters to ufuncs. That will have to be done before I can turn this into a ufunc.

@ewmoore
Copy link
Contributor
ewmoore commented Feb 24, 2016

The easiest thing to do is probably to make a sinc(x, scale) ufunc, and then wrap the rest of handling in python. OTOH, making a sinc(x) ufunc and a sinc_scaled(x, scale) ufunc and just exposing them both seems just as good. This will broadcast scale which doesn't seem particularly useful, but isn't harmful either.

I'm not all that convinced that there is some huge need for setting scale to be anything other than pi or 1 that couldn't be solved by just exposing both of them. Given how small the current sinc code is I'd be sympathetic to just suggesting that it be copied over where needed if scale should be anything other than pi.

@madphysicist
Copy link
Contributor Author

@ewmoore I am very hesitant to make sinc a two argument ufunc, not because of the broadcasting, which I explicitly supported with this PR, but because it would enable the reduce-like functionality, which I would like to avoid. In fact, since you mentioned that, I have modified this version of the function to only accept a keyword for scale and adjusted the tests accordingly. Adding arbitrary keyword arguments to ufuncs seems like a better long-term solution than exposing multiple versions of a sinc ufunc.

@ewmoore
Copy link
Contributor
ewmoore commented Feb 24, 2016

Sure. Extending ufuncs to take keyword args is also a large, backward incompable project since there is no mechanism for passing them to the inner loop function right now. That reduce is defined for two argument ufuncs doesn't really matter. There are lots of them in scipy.special for which reduce makes no sense and at least one (arctan2) in numpy.

@madphysicist
Copy link
Contributor Author

@ewmoore I am not sure why the project is backward incompatible. Previous ufuncs would implicitly have no keyword arguments defined. It would not break anything if done carefully.

Also, it looks like Python 2 is unhappy with keyword-only arguments. I will revert my last change but still note in the docs that support for scale as a positional argument may be removed in the future.

@njsmith
Copy link
Member
njsmith commented Feb 25, 2016

@madphysicist: adding kwargs to ufuncs will require rewriting big chunks of how ufuncs work -- it's very very not trivial.

@madphysicist
Copy link
Contributor Author

@njsmith. I am aware of that. In fact, that is part of my motivation for the undertaking. It will probably take a while, but I suspect that I will know a lot more about numpy internals if I succeed.

@madphysicist
Copy link
Contributor Author

Btw, my initial idea was to just add a hook function to the ufunc structure for processing the kwargs. The hook would be NULL by default. It would return a void pointer to some structure that the loop would know how to use and an optional deallocator for the struct. The struct only gets passed to the loop if the hook is non-NULL, so backwards compatibility will not be broken (much) since existing loops would not need a rewrite. The deallocator gets applied at the end if it is non-NULL. Let me know if this sounds reasonable or even sane. I still don't know enough to have worked out any of the major details.

@njsmith
Copy link
Member
njsmith commented Feb 25, 2016

@madphysicist: you will have to break ABI to make this happen. Which is okay -- we desperately need to break the ufunc ABI for a number of reasons :-). But if you're interested in working on this -- which is awesome -- then the first place to start is to read this thread and it would probably be good to schedule a video conference or something to go over how this fits into the bigger plan? (Nothing discussed in that thread has actually been implemented yet, because I've been too busy with other stuff, so you can see how I'd be eager to help someone else get started ;-))

@madphysicist
Copy link
Contributor Author

@njsmith I am very interested in working on this after looking at the thread, your NEP and the related reports you made. However, I feel the need to point out (again) that I am currently very new to the C internals. I am in the process of reading the docs at http://docs.scipy.org/doc/numpy/reference/c-api.html and following along in the code. While I am beginning to grasp the overall concept, and even form some ideas on how to approach the subject, it will take some time before I can get up to speed to the point where I can help with meaningful design decisions. If you are OK with that, I would be glad to make time for a video conference or something equivalent in the next couple of weeks to discuss the path going forward.

@madphysicist
Copy link
Contributor Author

@njsmith Is there a copy of the NEP draft somewhere outside the thread? Also, would you mind if I joined the thread? I have an idea that I would like to throw out there.

@homu
Copy link
Contributor
homu commented Mar 5, 2016 8000

☔ The latest upstream changes (presumably #7373) made this pull request unmergeable. Please resolve the merge conflicts.

@madphysicist madphysicist force-pushed the sinc-scale branch 2 times, most recently from e5d4d21 to 55bd798 Compare March 8, 2016 04:18
@madphysicist
Copy link
Contributor Author

Squawk

@homu
Copy link
Contributor
homu commented Mar 17, 2016

☔ The latest upstream changes (presumably #7198) made this pull request unmergeable. Please resolve the merge conflicts.

@shoyer
Copy link
Member
shoyer commented Mar 17, 2016

@madphysicist you should definitely speak up on the numpy-discussions list. You might start another thread if that one is very old, but we are always happy to hear from new contributors with ideas!

@njsmith
Copy link
Member
njsmith commented Mar 17, 2016

@madphysicist: Sorry I missed replying to your comments above earlier. Yeah, like @shoyer says, please do speak up on the mailing list about your ideas :-). And for setting up a video call, if you're still interested send me an email @ njs@pobox.com with some possible times and we can figure it out there? I'm on California time, and usually free in the afternoons -- but no need to clutter up the bug tracker with scheduling details :-).

@madphysicist
Copy link
Contributor Author

Is it worth keeping this PR around given the upcoming ufunc changes?

Personally I would lean towards yes since A) the changes might take a while, and B) I will do my best to ensure that the extra parameter is retained when sinc becomes a ufunc of the new variety.

@homu
Copy link
Contributor
homu commented Apr 7, 2016

☔ The latest upstream changes (presumably #7518) made this pull request unmergeable. Please resolve the merge conflicts.

@argriffing
Copy link
Contributor

This is just my opinion, but I think it's overkill to allow a general scaling factor. An alternative could be to have separate trig functions analogous to cos vs. cos_pi; I think 'boost' and other libraries do this.

@pv
Copy link
Member
pv commented Apr 19, 2016

AFAIK, one reason for cos_pi, sin_pi is that sometimes you need to get the zeros exactly right. Not sure if there's an use case for sinc where this is important, but the current implementation doesn't guarantee it either.

@madphysicist
Copy link
Contributor Author

@argriffing. I think your solution may be the right one. If I can not figure out a way to guarantee the zeros for arbitrary scaling, I will go with your suggestion. In the meantime, this PR can be considered to be on hold.

@homu
Copy link
Contributor
homu commented Jun 4, 2016

☔ The latest upstream changes (presumably #7704) made this pull request unmergeable. Please resolve the merge conflicts.

@homu
Copy link
Contributor
homu commented Apr 6, 2017

☔ The latest upstream changes (presumably eb12b71) made this pull request unmergeable. Please resolve the merge conflicts.

elif scale == 'unnormalized':
scale = 1.0
else:
raise ValueError("{} is not a valid scaling for `sinc`".format(scale))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like overkill - I don't think passing strings helps at all here.

if isinstance(x.dtype, np.inexact):
min_val = np.finfo(x.dtype).tiny
else:
min_val = np.finfo(np.float_).tiny
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we try and at least get this bit in?


y = where(x == 0, min_val, x)
if np.any(scale != 1.0):
y *= scale
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is doing the check here really buying anything?


The sinc function is :math:`\\sin(\\pi x)/(\\pi x)`.
For arbitrary scales, the function is defined as
:math:`\frac{\sin(scale \cdot x)}{scale \cdot x}`. In this case, the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

\cdot is misleading, as it implies np.dot()

@madphysicist
Copy link
Contributor Author

@eric-wieser. This PR has been on hold for a while, and I think I may end up closing it and starting anew (but not sure yet). The correct implementation would be to make two functions, sinc and sinc_pi as @argriffing suggests and @pv elaborates. I will take this up again as soon as I have a bit of free time.

…zed function.

Currently, `sinc(x)` is defined as `sin(pi*x)/(pi*x)`. It is sometimes convenient
to have the mathematical definition available: `sin(x)/(x). In fact, any scaling
is now possible with a scalar input. Tests included.

Also modified the docs for `where` very slightly.
@seberg
Copy link
Member
seberg commented May 2, 2019

@madphysicist I am closing this, since I guess this lost traction and it sounds like we probably need some decision on the best way forward (you mentioned that this PR is currently not quite the right way).

We can reopen this at any point. I have opened the issue gh-13457 about this so your start does not get lost in either case!

@seberg seberg closed this May 2, 2019
@madphysicist
Copy link
Contributor Author

@seberg. Under the circumstances, I think that's the right thing to do. I frankly don't even remember what I was doing back then to motivate the PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

0