Skip to content
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

fix inverse(^) domain #34

Merged
merged 11 commits into from
Sep 16, 2024
Merged

fix inverse(^) domain #34

merged 11 commits into from
Sep 16, 2024

Conversation

aplavin
Copy link
Contributor

@aplavin aplavin commented Jul 13, 2023

Technically breaking, but the old behavior was wrong - so this is a bug fix.
Inverse to y=x^2 at y=4 isn't 2 - it's not defined.

Maybe there are better and less error-prone approaches to handling these domains?..

@codecov
Copy link

codecov bot commented Jul 13, 2023

Codecov Report

Attention: Patch coverage is 90.00000% with 1 line in your changes missing coverage. Please review.

Project coverage is 97.82%. Comparing base (ad56514) to head (50f3fca).
Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
src/functions.jl 83.33% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master      #34      +/-   ##
==========================================
- Coverage   99.25%   97.82%   -1.43%     
==========================================
  Files           6        6              
  Lines         134      138       +4     
==========================================
+ Hits          133      135       +2     
- Misses          1        3       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@oschulz
Copy link
Collaborator

oschulz commented Jul 13, 2023

Inverse to y=x^2 at y=4 isn't 2 - it's not defined.

Why can't it be defined? x^2 is invertible when restricted to positive values.

@aplavin
Copy link
Contributor Author

aplavin commented Jul 13, 2023

g(y) is inverse to f(x) requires g(f(x)) == x for all x, and it seems only reasonable to require this from definitions in a generic package.
You won't do inverse(sin) = asin here, right? This is exactly the same situation: sin is invertible on -pi/2..pi/2 but not on its whole range; x^2 is invertible on 0..Inf but not on its whole range.

These usecase-specific inverses are what setinverse for, imo.

@oschulz
Copy link
Collaborator

oschulz commented Jul 13, 2023

Yes, you do have a point @aplavin . In any case, I don't think people will often need the inverse for pow in typical function chains. The question is, it it problematic enough to warrant a breaking release?

@aplavin
Copy link
Contributor Author

aplavin commented Jul 13, 2023

That's a bugfix, so a regular release should be fine - without changing the major version.

@oschulz
Copy link
Collaborator

oschulz commented Jul 13, 2023

I have no strong opinion here - @devmotion ?

@oschulz oschulz requested a review from devmotion July 14, 2023 12:47
Copy link
Member

@devmotion devmotion left a comment

Choose a reason for hiding this comment

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

I have the feeling this should be addressed more generally at some point, I don't like that there's no easy way to say "give me the inverse of some function whose domain is the positive real line". Using setinverse is cumbersome and redundant in these cases, and if you want to deal with generic functions this workaround is not even possible.

For the time being, both adopting a convention (always positive real line in these cases?) and throwing an error seem fine to me. I am not sure if there are examples but I think it's not unlikely that someone relied on or assumed we had such a convention - so I would rather be more cautious here and tag a breaking release.

Also relates to #10.

src/functions.jl Outdated Show resolved Hide resolved
@aplavin
Copy link
Contributor Author

aplavin commented Jul 14, 2023

Also relates to #10.

Yeah, a nice way to support those would be nice! Personally, I have multiple usecases for inverses, and some of them in principle require different kinds of inverse.

  • Computing preimage given a function y=f(x) and a set Y = y1..y2. If f⁻¹ exists and f⁻¹(y1) < f⁻¹(y2), then the preimage of Y is X = f⁻¹(y1) .. f⁻¹(y2) (reversed for >).
    This requires the proper inverse, as inverse() is defined to be in this package. Otherwise, for f(x) = x^2 and Y=1..4, one gets X = 1..2 which is not the correct answer. The correct one is an error: the function isn't invertible.

  • Using @set from Accessors with "invertible" functions, like @set a.x^2 = 4. Here, a right inverse is enough: "set a.x to whatever value, so that a.x^2 == 4".

Given a single function inverse, it's best to go with the "proper inverse" meaning, otherwise it's possible to get silently incorrect results.

@oschulz
Copy link
Collaborator

oschulz commented Jul 15, 2023

So should be do a breaking release with this now, or keep it until we have to do a breaking release anyway?

@devmotion
Copy link
Member

Depends on how urgent we want this to be released and if any other breaking changes are expected I assume 🤷 Maybe it would be good to address #10 in the same release.

@oschulz
Copy link
Collaborator

oschulz commented Jul 15, 2023

Yes doing something about #10 would be great - @aplavin would you like to draft something?

@aplavin
Copy link
Contributor Author

aplavin commented Jul 31, 2023

Well, unfortunate that this change is considered breaking instead of a bugfix: changing from objectively incorrect to correct behavior. Supporting left/right inverses is orthogonal, because this PR is only concerned with the correctness of inverse.

@oschulz
Copy link
Collaborator

oschulz commented Aug 1, 2023

Well, unfortunate that this change is considered breaking instead of a bugfix: changing from objectively incorrect to correct behavior.

I wouldn't say the current behavior is "obviously incorrect" - it's a question of how narrow one defined invertibility (full domain or part of the domain). I agree this change should be made, the question is whether it's of any urgency, since it just removes some (probably little-used) "incorect" functionality, or whether it should wait until we have to make other breaking changes anyway. At some point, we may also want to do a v1.0 release, that would also be a good opportunity.

@ParadaCarleton
Copy link

I agree this change should be made

If that's the case, better sooner than later I think. The longer we wait the more likely it is someone will depend on this (incorrect) functionality.

@oschulz
Copy link
Collaborator

oschulz commented Nov 2, 2023

You make a good point @ParadaCarleton , if there are no objections (@devmotion ?), I'll merge and release this. @aplavin , should we do #38 as well then?

@oschulz oschulz mentioned this pull request Nov 7, 2023
@aplavin
Copy link
Contributor Author

aplavin commented Nov 25, 2023

Yes I think this should be merged, as a bugfix.

@aplavin
Copy link
Contributor Author

aplavin commented Nov 25, 2023

I also looked at other functions and fixed their inverse domains. It's quite hard to be sure that no edgecase is missed there! (most definitely some still remain)
Nice that it should only be defined in a single place in this library, users shouldn't care about these details themselves.

@oschulz
Copy link
Collaborator

oschulz commented Nov 25, 2023

Yes I think this should be merged, as a bugfix.

If I understood @devmotion correctly, he would prefer a breaking release. @devmotion ?

@aplavin
Copy link
Contributor Author

aplavin commented Nov 26, 2023

Sorry, I didn't imply any specific numbering of the next release. No matter the version number, this PR is still fundamentally a fix making behavior correct, following the function semantics and docs.

Would appreciate another look at the conditions in the code, especially important if such a fix involves a "breaking" version bump.

@oschulz
Copy link
Collaborator

oschulz commented Nov 26, 2023

Thank let's maybe just make a v0.2 and be done with it. :-) Good from your side @devmotion ?

@aplavin
Copy link
Contributor Author

aplavin commented Aug 20, 2024

Not that I have a lot of hope, but... Is there a chance this bugfix could be accepted anytime soon?
I've stumbled across InverseFunctions silently doing a wrong thing several times over this year, would really prefer if it immediately threw an exception.

Another recent example:

julia> f = Base.Fix1(log, 1)

# would throw a DomainError with this PR:
julia> inverse(f)(f(10))
1.0

@oschulz
Copy link
Collaborator

oschulz commented Aug 20, 2024

Not that I have a lot of hope, but... Is there a chance this bugfix could be accepted anytime soon

My apologies, I kinda lost track of this one. Let's get it in (I'll add some minor change requests in a minute). (looks fine to me).

@oschulz
Copy link
Collaborator

oschulz commented Aug 20, 2024

Looking at this again, I think it's indeed a bugfix, and so non-breaking. @devmotion any objections?

src/functions.jl Outdated Show resolved Hide resolved
src/functions.jl Outdated Show resolved Hide resolved
src/inverse.jl Outdated Show resolved Hide resolved
src/inverse.jl Outdated Show resolved Hide resolved
src/inverse.jl Outdated Show resolved Hide resolved
@@ -80,7 +80,9 @@ InverseFunctions.inverse(f::Bar) = Bar(inv(f.A))
@test_throws DomainError inverse(Base.Fix2(^, 0.5))(-5)
@test_throws DomainError inverse(Base.Fix2(^, 0.51))(complex(-5))
InverseFunctions.test_inverse(Base.Fix2(^, 0.5), complex(-5))
@test_throws DomainError inverse(Base.Fix2(^, 2))(-5)
@test_throws DomainError inverse(Base.Fix2(^, 2))
@test_throws DomainError inverse(Base.Fix2(^, -4))
Copy link
Member

Choose a reason for hiding this comment

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

Are the errors for Base.Fix1(^ and log tested somewhere?

Copy link
Collaborator

Choose a reason for hiding this comment

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

@aplavin I think there's not test for that yet, correct?

aplavin and others added 2 commits August 20, 2024 19:58
Co-authored-by: David Widmann <[email protected]>
Co-authored-by: David Widmann <[email protected]>
aplavin and others added 3 commits August 20, 2024 19:59
Co-authored-by: David Widmann <[email protected]>
Co-authored-by: David Widmann <[email protected]>
@oschulz
Copy link
Collaborator

oschulz commented Aug 26, 2024

@aplavin could you look into adding the tests that @devmotion mentioned, before this gets merged?

@oschulz
Copy link
Collaborator

oschulz commented Sep 13, 2024

@devmotion : Are the errors for Base.Fix1(^ and log tested somewhere?

@aplavin is that covered already?

@aplavin
Copy link
Contributor Author

aplavin commented Sep 13, 2024

sorry, almost forgot about it!

@oschulz
Copy link
Collaborator

oschulz commented Sep 13, 2024

Thanks @aplavin !

@oschulz
Copy link
Collaborator

oschulz commented Sep 13, 2024

@devmotion , good from your side now?

@devmotion
Copy link
Member

There are merge conflicts?

@oschulz
Copy link
Collaborator

oschulz commented Sep 13, 2024

There are merge conflicts?

Oh, so there are, silly me. @aplavin , pretty please?

@aplavin
Copy link
Contributor Author

aplavin commented Sep 13, 2024

Indeed, there were quite some code changes during this year :) Not sure what exactly went wrong here and caused this delay, even the original feature was accepted faster than the bugfix...

@oschulz
Copy link
Collaborator

oschulz commented Sep 15, 2024

Thanks for rebasing @aplavin (and sorry we've dragged our feet on this one in between). @devmotion ?

@oschulz
Copy link
Collaborator

oschulz commented Sep 15, 2024

@aplavin could you add a minor version bump? Then I'll merge and release.

@aplavin
Copy link
Contributor Author

aplavin commented Sep 16, 2024

You mean patch version, right? Bumped!

@oschulz oschulz merged commit beb5746 into JuliaMath:master Sep 16, 2024
6 of 9 checks passed
@oschulz
Copy link
Collaborator

oschulz commented Sep 22, 2024

Sorry, @aplavin , forgot to trigger registrator. Just did:

JuliaRegistries/General#115708

@oschulz
Copy link
Collaborator

oschulz commented Sep 23, 2024

@devmotion do you know why TagBot keeps failing now?

@devmotion
Copy link
Member

Probably JuliaRegistries/TagBot#350 which seems to be fixed in the latest release.

@oschulz
Copy link
Collaborator

oschulz commented Sep 23, 2024

Do we have to take any action, re-run it or so?

@devmotion
Copy link
Member

@oschulz
Copy link
Collaborator

oschulz commented Sep 23, 2024

Oh, right - sorry, just saw it. Thanks!

@aplavin aplavin deleted the patch-2 branch September 24, 2024 21:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants