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

Pointpriors #663

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open

Pointpriors #663

wants to merge 6 commits into from

Conversation

bgctw
Copy link

@bgctw bgctw commented Sep 17, 2024

Tackles #662: Querying the log-density of components of the prior.

The implementation does not decompose the log-density of dot-tilde expressions, because a possible solution (first commit, but removed in 3rd commit again) would need to decompose dot_assume, which is not under context control. However, I do need to pass computation to child-contexts, because I want to inspect log-density transformation by child-contexts. Therefore, I called it varwise_logpriors rather than pointwise_logpriors.

In addition, I decided for a different handling of a Chains of samples compared to pointwise_likelihoods, because I did not fully comprehend its different push!! methods and different initializers for the collecting OrderedDict and what applies at which conditions. Rather, I tried separating concerns of querying densities for a single sample and applying it to a Chains object. I hope that the mutation of a pre-accocated array is ok here.

Copy link
Member

@torfjelde torfjelde left a comment

Choose a reason for hiding this comment

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

Hey @bgctw !

Can you clarify why you want the .~ statements to be treated as a single log-prob in your case? You mention that your motivation is tempering; it's a but unclear to me why varwise_logpriors are needed for this. And why is the Chain needed in this case? When I think of tempering in our context, I'm imaging altering the likelihood / prior weightings during sampling, not as a post-inference step.

Maybe even write a short bit of psuedo-code outlining what you want to do with this could help!

From your initial motivation in #662, I feel like we can probably find alternative approaches that might be a bit simpler:)

r, lp, vi = dot_assume_vec(dist, var, vns, vi)
return r, sum(lp), vi
end
function dot_assume_vec(
Copy link
Member

Choose a reason for hiding this comment

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

Is this actually needed for what you're trying to achieve here or was this "a step along the way"?

Arguably it seems like a nice improvement to the current code-base regardless:)

Copy link
Author

Choose a reason for hiding this comment

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

Yes, this was only needed for the initial pointwise resolving the prior components. With the last commit, I reverted to a simpler coarser resolution. I will revert this.

@bgctw
Copy link
Author

bgctw commented Sep 17, 2024

My goal is to modify the log-density during sampling. I imagine putting something similar to TestLogModifyingChildContext in src/test_utils.jl between the SamplingContext and the DefaultContext. For example, I want to relax the parameter priors of an ODE model during burnin or initial optimization of a Turing model, but keep the original priors on additive effects that modify parameters for simulated replicates around a population mean. However, before tackling this, I want to be able to query/see the corresponding log-densities that are used during the sampling or optimization.

Hence, I want to query the log-densities of the prior components as seen by a sampler that generated the samples in a AbstractMCMC.AbstractChains object.

The single number provided by logprior(m, vi) for a single sample is too coarse, because I want to experiment with components.

The pointwise resolution, i.e. resolving also the components of the log-density components of dot_tilde_assume such as (s[1], s[2], ...s[n]) of a s .~ Normal(...), would be nice, but it is more complex to implement together with the requirement, that those densities can be modified by a child-context. Reporting their cumulated logdensity only is an acceptable tradeoff. If this is prohibitive, the user could reformulate the .~ as an explicit loop, because then those components are resolved with the currently suggested implementation.

@coveralls
Copy link

coveralls commented Sep 17, 2024

Pull Request Test Coverage Report for Build 10918143575

Details

  • 58 of 67 (86.57%) changed or added relevant lines in 2 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage decreased (-0.6%) to 76.882%

Changes Missing Coverage Covered Lines Changed/Added Lines %
src/test_utils.jl 10 19 52.63%
Totals Coverage Status
Change from base Build 10856622994: -0.6%
Covered Lines: 2757
Relevant Lines: 3586

💛 - Coveralls

use loop for prior in example

Unfortunately cannot make it a jldoctest, because relies on Turing for sampling
@torfjelde
Copy link
Member

Hence, I want to query the log-densities of the prior components as seen by a sampler that generated the samples in a AbstractMCMC.AbstractChains object.

Ah, gotcha; this was the aspect I was missing 👍

The pointwise resolution, i.e. resolving also the components of the log-density components of dot_tilde_assume such as (s[1], s[2], ...s[n]) of a s .~ Normal(...), would be nice, but it is more complex to implement together with the requirement, that those densities can be modified by a child-context. Reporting their cumulated logdensity only is an acceptable tradeoff. If this is prohibitive, the user could reformulate the .~ as an explicit loop, because then those components are resolved with the currently suggested implementation.

Makes sense 👍

Taking this into account, I'm wondering if maybe it would be better to just generalize the existing PointwiseLikelihoodContext that we have here

struct PointwiseLikelihoodContext{A,Ctx} <: AbstractContext
loglikelihoods::A
context::Ctx
end

We can just add a "switch" to it (or maybe just inspect the leaf context) to determine what logprobs we should keep around. AFAIK this should just require implementing the following:

  1. tilde_assume and dot_tilde_assume
  2. A quick check in (1) to determine whether we should include a variable or not.

Then we can just add alternatives to the following user-facing method

function pointwise_loglikelihoods(model::Model, chain, keytype::Type{T}=String) where {T}
# Get the data by executing the model once
vi = VarInfo(model)
context = PointwiseLikelihoodContext(OrderedDict{T,Vector{Float64}}())
iters = Iterators.product(1:size(chain, 1), 1:size(chain, 3))
for (sample_idx, chain_idx) in iters
# Update the values
setval!(vi, chain, sample_idx, chain_idx)
# Execute model
model(vi, context)
end
niters = size(chain, 1)
nchains = size(chain, 3)
loglikelihoods = OrderedDict(
varname => reshape(logliks, niters, nchains) for
(varname, logliks) in context.loglikelihoods
)
return loglikelihoods
end
function pointwise_loglikelihoods(model::Model, varinfo::AbstractVarInfo)
context = PointwiseLikelihoodContext(OrderedDict{VarName,Vector{Float64}}())
model(varinfo, context)
return context.loglikelihoods
end

e.g. pointwise_prior_logprobs or something.

So all in all, basically what you've already done, but just as part of the PointwiseLikelihoodContext (which we should then subsequently rename of course).

Thoughts?

@bgctw
Copy link
Author

bgctw commented Sep 18, 2024

Trying to unify those two is a good idea. In fact, I originally started exploring/modifying based on PointwiseLikelihoodContext.

However, I did not come far with this. PointwiseLikelihoodContext, resolves the dot_tilde_observe by intercepting before the agglogp!, but still can forward the density computation to the child context. I did not manage to do that with the priors. Hence, I do not know how to implement your unifying suggestion.

@bgctw
Copy link
Author

bgctw commented Sep 19, 2024

I will attempt the implementation that you suggested, assuming that components of the prior are not resolved to the same detail as the components of the likelihood.

@bgctw
Copy link
Author

bgctw commented Sep 19, 2024

I pushed a new commit that integrates pointwise_loglikelihoods and varwise_logpriors to the new function pointwise_logdensities.

The hardest part was to create a single VarName from the AbstractVector{VarName} for the case where only the summed logdensity for several prior components in dot_tilde_assume is to be recorded. varwise_logpriors simply used a Symbol, but the generalized pointwise_loglikelihoods requires a single VarName. The implementation at src/pointwise_logdensities.jl around line 153 has to assume several details of the Optics in the given VarNames.

Another issue, is that now pointwise_loglikelihoods provides information on all variables, although the logdensity of the priors is zero. Hence, one cannot check on empty Result (around line 29) to catch the case of literal observations. How can I ask the model or VarInfo which variables are priors and which are observations?

I could not yet recreate julia-repl block in the documentation of the function, because current Turing, which is required for sampling in the docstring, is not compatible with current DynamicPPL.

@torfjelde
Copy link
Member

Lovely @bgctw ! I'll a proper have a look at it a bit later today:)

@bgctw
Copy link
Author

bgctw commented Sep 19, 2024

In order for the user to select relevant information and for saving processing time, it could be helpful to have two keyword arguments with defaults: report_logpriors=true and report_loglikelihoods=true. If the corresponding flag is false, log-densities would not be calculated (not passed to child context) and would not appear in the results. The report_logpriors could be set to false in the forwarding of pointwise_loglikelihoods which would also allow to check on empty results in tests again.

Would these be reasonable?

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.

3 participants