-
Notifications
You must be signed in to change notification settings - Fork 34
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
Support InverseFunctions.jl and ChangesOfVariables.jl #199
Comments
I think it would be neat if we had a such a common interface that would allow combining transformations from different packages in workflows that need LADJ and inverse calculation. Could be called "TransformationTraits.jl" or so. It would like @willtebbutt maybe such a package/interface would be interesting for ParameterHandling.jl as well? |
CC @oxinabox |
I'd be very pro- finding a way to make the indirect deps that Bijectors introduces in ParameterHandling much lighter. A number of people have commented on Bijectors being quite a heavy dependency. I'm not totally sure that a BijectorsCore package would necessarily help ParameterHandling though, because ParameterHandling would continue to need to depend on Bijectors in order to get the actual bijections -- in this sense, ParameterHandling is analogous to an AD in the ChainRules world, rather than a package which defines a couple of rules. |
Yes, I recently looked at using ParameterHandling in a package, but the transitive deps would be a bit heavy (otherwise I like the concept of ParameterHandling very much!).
Ah right. Still, I hope the idea of a lightweight interface-package for transformations can find some support - after all, we've been very successfull with this approach in the ecosystem. Bikeshedding-wise I wouldn't call it "BijectorsCore", it could also cover non-bijective/invertible mappings (or transformations that are in bijective in principle, but for which the inverse is hard to calculate but also not required). |
Yeah -- I'm totally in favour of this. It's the kind of thing that I might even use to define the defaults in ParameterHandling so as to avoid the bijectors dep. |
Most dependencies are quite lightweight actually, the main exemptions are NonlinearSolve and Distributions. I made some improvements and type inference fixes in Roots recently which (this was the plan) would allow us to switch back from NonlinearSolve to the much more lightweight Roots package. Another issue is Requires which eg increases loading times if you actually use the optional dependencies. I guess a core interface package could be useful even though I am not sure if it would have to depend on Distributions as well (if yes, it would maybe be another motivation for a lightweight DistributionsBase package). |
What I had in mind would literally just be the outline above, so really super-lightweight and no dependencies beyond Base. @devmotion, would you support such a super-lightweight "TransformationTraits" (or similar name) package in Bijectors? We could host it at a central place like JuliaMath or JuliaStats. It would just mean adding something like
or maybe even making If "TransformationTraits" is lightweight enough, we should be able to convince packages like DiffEqFlux to support it as well, e.g. for the ffjord normalizing flows and similar. |
I guess you should ask @torfjelde, he's the Bijectors god 😄 I wonder if one should not define a struct |
@torfjelde, sorry for not pulling you in earlier! Would this be Ok with you? |
Yes, I was torn between these options myself. I liked having the I think you're right, better use a function than a special argument. I've updated the code above accordingly. |
I think the whole package would literally just be function with_logabsdet_jacobian end
function with_logabsdet_jacobian(f::Base.ComposedFunction)(x)
y_inner, ladj_inner = with_logabsdet_jacobian(f.inner, x)
y, ladj_outer = with_logabsdet_jacobian(f.outer, y_inner)
(y, ladj_inner + ladj_outer)
end
function inverse end
inverse(trafo::Base.ComposedFunction) = Base.ComposedFunction(inverse(f.inner), inverse(f.outer))
inverse(f) = inv(f) # Until JuliaLang/julia#42421 is decided one way or the other Maybe we should add support for With the One interesting option might be to also define In any case, the change/additions to Bijectors.jl would just be const forward = TransformationTraits.with_logabsdet_jacobian
TransformationTraits.inverse(b::Bijector) = inv(b) # Until JuliaLang/julia#42421 is decided one way or the other or similar. |
First off, sorry for the delay in response here! Been busy with a first year evaluation for my PhD, so have been very much off the grid for the past week. I'm very much in favour of making a lightweight interface package:) |
Thanks @torfjelde (hope your Phd eval went smoothly!). Ok, then how about I prototype a package, based on the outline above? I would include implementations of Bikeshedding: @torfjelde , @devmotion and @willtebbutt : Do you have any preference regarding package name? I was thinking "TransformationTraits.jl", "TransformationIntercase.jl", "VarTrafoTraits.jl", "VarTrafoInterface.jl" or so. |
Hmm maybe it would be good to use a name that makes it clear which transformations the package deals with (transformation of measures/probability distributions/densities)? I think TransformationTraits etc. is a bit too general. And I think I would prefer something like |
No strong preference on the name :) |
I don't think it has to be limited to transformations of measures/probability distributions/densities - IMHO the API above would cover variable transformations in general, I would also cover integration problems and other cases where one needs the local delta-volume of a trafo, but where the target function may not be limited to positive values or may not even be scalar.
How about "VarTrafoInterface.jl" then? Or "VariableTransformationInterface.jl" - but that's awefully long. |
"Variable" is also not very specific I guess 😛 Maybe just LogAbsDetJacobian.jl if its main purpose is the definition of |
Hm, "LogAbsDetJacobian.jl" or "LogAbsDetJacobians.jl"? It's a bit unwieldy, though. However, if we focus completely on |
It will also provides inversion for a bunch functions though, so feel like I'm more so leaning towards TransformTraits.jl, but maybe that is also a bit too broad. There's always InvertibleTransformTraits.jl, but it's a bit of a mouthful + we'd like to have In conclusion: |
Had the same thought, but seems a bit redundant given that the only things we care about right now is invertibility and logabsdetjac. IMO start with it under the same package, and then we can separate into different packages later if the scope expands? |
A few more suggestions: |
Yes, I also think it might be better to do both in one package, it will be very lightweight already, and there may be some interdepencencies/links between |
I'm very happy with both of those - should it be "transform" or "transformation"? I did a bit of a literature search on this terminology and ended up confused. :-) |
Ah yes, I forgot the definitions of the inverses. I still think that Transform or Transformation are very general names and not self explanatory. I like ChangeOfVariables.jl since the name already indicates the intention and area of the interface. |
I'd be happy with that - @torfjelde, fine with you? Oh - should we name it |
Any objections to |
Oh sorry, I forgot to respond to this! Personally, I'm not a huge fan of But I really just want a package with this interface:) So if the consensus is that ChangeOfVariables is superior, then I'm of course fine with that 👍 |
Me too, I need this soon. :-) Hm, not every change of variables is necessarily strictly bijective/invertible (e.g. when changing from a periodic infinite space to the "unit cell", like in solid-state physics), and not every invertible function has a log-abs-det-jacobian (e.g. invertible discrete functions). Proposal: I create a package |
I've seen other use cases of invertible functions/ |
Ok, then I'll make two packages: Since it would be more narrow in focus then, we could name the second one |
Ok, now that we have https://github.com/JuliaMath/InverseFunctions.jl and https://github.com/JuliaMath/ChangesOfVariables.jl, we just need to use them in Bijectors. :-) |
Lovely! Thank you for making these. I've been AWOL for a couple of weeks now (sorry about that), but back now and fixing up Bijectors.jl is high on my priority list so will get to this very soon:) |
@torfjelde , I could prepare a small non-breaking PR to add add initial support for the InverseFunctions.jl and ChangesOfVariables.jl API to Bijector as it is now (similar to what I did for TransformVariables in tpapp/TransformVariables.jl#85), until you more substantial changes. |
That would be awesome and greatly appreciated!:) Feel free to shoot if you have any questions. I'm still somewhat unhappy with how we're going to deal with batches of inputs, i.e. how do I efficiently compute |
Will do!
Yes, I've been fighting with that myself lately - I've been playing with |
This is something discussed quite a bit before: https://github.com/TuringLang/Bijectors.jl/discussions/178. Personally I'm leaning towards generalizing ArraysOfArrays.jl could potentially provide the same, but I think there are a couple of caveats:
|
ArraysOfArrays.jl could potentially provide the same, but I think there are a couple of caveats:
I promised @cscherrer to get rid of that. :-)
Why I 1-dimensional one - wouldn't you lose information about which parts belong to which sample/event/...? |
Nice:)
I'm a bit uncertain what you mean/maybe I confused you. I meant that in our case, we want any representation which allows us to do |
Thanks @oschulz for the ping :) If there's refactoring in the works, here's my wishlist:
I've also been thinking of using |
That I'll happily leave to the Bijectors team, I'll be busy getting VariateTransformation registration-ready (finally) :-) Initially I'll just do something like tpapp/TransformVariables.jl#85 so that code can use all kinds of transformations without depending on specific trafo packages directly. |
Ah, now I get it - yes, of course! So there,
Yes, that what I do in BAT.jl at the moment, it uses ArraysOfArrays extensively (ArraysOfArrays was partially designed exactly for the batch-of-samples use case). |
Or even more generally just an The Bijectors refactoring is discussed in https://github.com/TuringLang/Bijectors.jl/discussions/178 and worked on in #183. |
Exactly. We want to support arbitrary inputs. @cscherrer I'll make a separate issue from your comment since it's very useful feedback, but here we're talking a bit more specifically about adoption of InverseFunctions.jl and ChangeOfVariables.jl rather than a general rewrite (as is being worked on in #183 as mentioned by David). |
Thanks @torfjelde. We've discussed #183 , but the relationship of this issue (199) to that wasn't really clear to me. New issue sounds good :) |
ValueShapes has that - it allows you to view a flat matrix of real numbers as a vector of |
This is just one particular example - another example would be e.g. computing the kernel matrix on a vector of graphs. If the API just expects that collections of inputs are provided as an |
Oh sure - I didn't mean it should be limited in any way! |
@willtebbutt with ChangesOfVariables and InverseFunctions support in LogExpFunctions and Bijectors not, ParameterHandling may be able to drop the explicit dependency on Bijectors now. |
I removed it already a while ago: JuliaGaussianProcesses/ParameterHandling.jl#42 |
Oh right, sorry, should have checked first - I just had this stuck as a "to do after" in my mind. Great! |
To do
InverseFunctions
andChangesOfVariables
in Bijectors (and other packages ...)Context
Bijectors is really neat, but it's also kind of a heavy package with many dependencies (necessarily).
I have some variable transformation stuff in BAT.jl that I want to split out into a separate package, and I've recently been experimenting with some normalizing flow trafos - I'd like to make that kind of stuff compatible with Bijectors, but it would be to heavy as a dependency.
Would you guys (CC @devmotion) be interested in setting up a lightweight variable-trafo-interface package? Maybe just something like
with_logabsdet_jacobian
would be the equivalent ofBijectors.forward
, whilef(x)
would be used to just run the transform without calculating LADJ. So the behavior would beNo autodiff or anything, that we'd leave to implementations like
Bijectors.Bijector
.Long-term, it would be nice to get rid of
inverse
in favor ofinv
, if we can getinv(::ComposedFunction)
into Base (just opened an issue in that regard: JuliaLang/julia#42421).I'd volunteer to prototype a package (would be quite tiny, basically just the code above) if there's interest.
Edit: Replaced special argument type
WithLADJ
in proposal with functionwith_logabsdet_jacobian
, as suggested by @devmotionThe text was updated successfully, but these errors were encountered: