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

Convenient way to specify/override the inverse of a function #18

Closed
oschulz opened this issue May 13, 2022 · 8 comments · Fixed by #25
Closed

Convenient way to specify/override the inverse of a function #18

oschulz opened this issue May 13, 2022 · 8 comments · Fixed by #25

Comments

@oschulz
Copy link
Collaborator

oschulz commented May 13, 2022

It would be convenient to have a function setinverse (or other name) that behaves like this:

f_with_inv = setinverse(f, f_inv)

f_with_inv(x) == f(x)
inverse(f_with_inv) === f_inv

It would make it easy to specify the inverse of an anonymous function:

some_algorithm(setinverse(x -> ..., y -> ...), algo_args...)

In some scenarios users may also want to override the default inverse of a function, setinverse would provide an easy and very readable way to do that.

@devmotion , would you support adding this?

@cscherrer
Copy link
Contributor

This sounds great! One thing that I think comes up a lot is pairs of functions where the functions themselves are not inverses, but restrictions of both are inverses. In these cases it doesn't make sense to have a global inverse, but it would be great to still be able to use this interface.

@devmotion
Copy link
Member

The benefit of such a function setinverse (or maybe with_inverse?) is not immediately clear to me. You can already define inverses of anonymous functions, and if you want to use different inverses you can just use a different f, e.g., f(x) = f_orig(x) with a custom inverse(::typeof(f)) = ....

@oschulz
Copy link
Collaborator Author

oschulz commented Sep 15, 2022

The benefit of such a function setinverse (or maybe with_inverse?) is not immediately clear to me.

The main benefits would be that one can attach missing inverses to functions quickly on the fly - and, maybe even more importantly (like @cscherrer noted) easily attach inverses to functions that are only valid inverses for specific uses cases, e.g. because it's ensured that the function resp. inverse will only be used on a limited value range.

We were discussing this in the context of JuliaMath/MeasureBase.jl#89 today - pushfwd(setinverse(f, finv), μ) would be quick and very readable. On the other hand, first defining a wrapper function and then inverse for it would require several lines of code.

I think a setinverse would come in very handy, I could have used it myself a few times in practice already. And there's no drawback to adding it. I'm not super happy with setinverse name-wise though, maybe someone has a better name?

@devmotion
Copy link
Member

I skimmed through the issue but it's still not clear to me why you would want to use pushfwd(setinverse(f, finv), mu) instead of pushfwd(f, finv, mu).

If you use e.g. specific transformations and only operate on domains where inverses actually exist, I think custom functions, maybe even on the module level, with their inverses don't seem too bad.

In any case, I think with_inverse might be a bit closer to what it is supposed to do.

@oschulz
Copy link
Collaborator Author

oschulz commented Sep 15, 2022

I skimmed through the issue but it's still not clear to me why you would want to use pushfwd(setinverse(f, finv), mu) instead of pushfwd(f, finv, mu).

pushfwd(f, finv, mu) woudl be a new API. And it's not ideal because in more complex use cases, f might get extracted from a PushforwardMeasure and passed on to another place in the application, where the code might need to invert it again. PushforwardMeasure only stores both function and inverse as a caching mechanism since in some cases generating an inverse may be costly, at least in terms of memory allocations.

If you use e.g. specific transformations and only operate on domains where inverses actually exist, I think custom functions

The use case above may actually occur in "user-level" code (e.g. in scripts/notebooks) as often (if not more often) than in "library-level" code. An inline solution would be very user-friendly, I think.

In any case, I think with_inverse might be a bit closer to what it is supposed to do.

with_inverse was actually me first idea for the name, but we use the with_... very differently in ChangesOfVariables, that why I thought it might not be the best choice.

@oschulz
Copy link
Collaborator Author

oschulz commented Sep 16, 2022

@devmotion unless you have strong objections to this, I'd like to prep a PR to add setinverse/with_inverse.

@devmotion
Copy link
Member

I'm fine with it. I am still not sure if it's a good design and what exactly the advantages and disadvantages are here but I don't have a strong opinion on this matter as it should not negatively impact in any way.

Based on my experience with wrapper types, e.g., I was also wondering whether that could lead to downstream issues - if there are dispatches on f somewhere down the line, you would miss them if you pass around a wrapper, so it seems you would like to unpack the wrapper before forwarding it - or otherwise you would have to demand that all downstream applications support InverseFunctions, and in particular the wrapper, as well.

@oschulz
Copy link
Collaborator Author

oschulz commented Sep 17, 2022

I'm fine with it.

Thanks, I'll put a PR together then. I'll see if I can come up with a better name for the function.

if there are dispatches on f somewhere down the line, ...

I would hope that wouldn't be a problem in typical applications, but that's hard to predict, of course. But since this is for cases where one can't specialize inverse for the type of f directly (without committing type piracy or overwriting a default inverse) some kind of wrapper would likely be unavoidable anyway. Having a standard wrapper might actually help to enable additional specializations in certain cases, I'd guess.

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 a pull request may close this issue.

3 participants