-
Notifications
You must be signed in to change notification settings - Fork 32
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
@measure combinator #7
Comments
cc: @mschauer |
Here's where we are currently: julia> (@macroexpand @measure Normal(μ,σ) ≃ Lebesgue{X}) |> MacroTools.prettify
quote
struct Normal{P, X} <: AbstractMeasure{X}
par::P
end
function Normal(nt::NamedTuple)
P = typeof(nt)
return Normal{P, eltype(Normal{P})}(nt)
end
Normal(; kwargs...) = Normal((; kwargs...))
(baseMeasure(μ::Normal{P, X}) where {P, X}) = Lebesgue{X}
Normal(μ, σ) = Normal(; μ, σ)
((:≪)(::Normal{P, X}, ::Lebesgue{X}) where {P, X}) = true
((:≪)(::Lebesgue{X}, ::Normal{P, X}) where {P, X}) = true
end Let's step through this: struct Normal{P, X} <: AbstractMeasure{X}
par::P
end This allows function Normal(nt::NamedTuple)
P = typeof(nt)
return Normal{P, eltype(Normal{P})}(nt)
end When a named tuple is given as an argument, the resulting type should include the type of that named tuple. Note that there should be a functional type dependency Normal(; kwargs...) = Normal((; kwargs...)) Passing keyword arguments dispatches to the appropriate named tuple. (baseMeasure(μ::Normal{P, X}) where {P, X}) = Lebesgue{X} Each measure should have a Normal(μ, σ) = Normal(; μ, σ) This method corresponds to the default parameters given to the macro. As it is it will only work (I think) in Julia 1.5+. We need to change the right side of this to ((:≪)(::Normal{P, X}, ::Lebesgue{X}) where {P, X}) = true
((:≪)(::Lebesgue{X}, ::Normal{P, X}) where {P, X}) = true The first of these methods will always be specified, and just says that the measure is absolutely continuous wrt its base measure. The second is only added because in the macro we specified |
Hmm, there are some complications.... In long-running inner loops, we shouldn't have to mess with the constant. So I Normal(μ,σ) ≃ (1/sqrt2π) * Lebesgue(X) This creates a
We could consider making separating these, so a base measure is for specifying the density, and a "primitive" measure is given that's equivalent ( In general, say the user asks for the density of μ wrt ν, for arbitrary This may be undecidable, or at least very tricky. So I suggest something like
So I think only (3) is the challenge. |
I’m having a tough time seeing how (3) can be determined automatically. Is the expectation that the user will “assert” (3) for measures they define? This seems consistent with what we’ve discussed. The picture I’m getting here: we already know how to define distributions with reference to base measures, and we can methodically go through and fill out the AC relation - then offer an interface which allows the user to assert that their measure is AC. |
Yes, almost just as setting a fallback
and hoping that people fill the method tables for |
I have the feeling that this has been discussed somewhere before, but isn't
a bit problematic, in the sense that the parametrization is different for different orderings of the Maybe there's a nice way to ensure a canonical ordering on |
Perhaps something like this? function canonicalize(nt::NamedTuple{_k,T}) where {_k,T}
if @generated
k = collect(_k)
sp = sortperm(k)
return quote
sp = ($(sp...),)
keys = ($(Meta.quot.(k[sp])...),)
types = Tuple{$(T.parameters[sp]...)}
NamedTuple{keys,types}(map(Base.Fix1(getfield, values(nt)), sp))
end
else
sp = sortperm(collect(keys(nt)))
return NamedTuple{keys(nt)[sp]}(values(nt)[sp])
end
end |
The internal representation of the parameters as named tuple isn’t something measures.jl enforces or even strongly suggests. It is just one Way of doing it. Do I get this |
I think it is only implicit, in the sense that the Although imposing any special type of course goes against the spirit to impose as little as possible. |
@femtomc :
I think we're on the same page. One fine point is, I wouldn't suggest filling out the AC relation before it's needed. I assume the overhead of that would bog things down (but I might be wrong about that). I was thinking the base-to-base approach, together with a way to let the user know what method is missing if it fails. And user could also add methods returning (:≪)(a, b) = isfinite(kullbackleibler(a, b)) I don't know about the direction here. To me ≪ seems more "primitive". KL might be more expensive to compute, and we may not even know how. Oh, but... if they do know how to compute it for some type-constrained
Great point!
This looks great! I've mostly swept this issue under the rug out of concern for the overhead of sorting each time. But making the
That's right, though I expect it would be a very common one (hence the convenience macro). But yes, users can parameterize by and |
@phipsgabler @simeonschaub I just remembered @simonbyrne had mentioned something similar, just found his KeywordDispatch.jl. We should see how this compares before committing to an approach. |
I think the ideas are rather similar :D |
Closing in favor of #9 |
We already have a start on this, but I think we can do better. Something like
Maybe most confusing here are that
≪
or≃
For example,
should generate (something like)
We'd then specify the
logdensity
asThis definition (with no base measure) would be wrt the default base measure, here
(twoπ/sqrt2)*Lebesgue(X)
.We'll also have a method
logdensity(μ::Measure, ν::Measure)
that returns the Radon-Nikodym derivative as a function, assuming μ≪ν, and fails otherwise.The text was updated successfully, but these errors were encountered: