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

BreadCrumbs interface: an easier way to feed pigeons #99

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

miguelbiron
Copy link
Collaborator

No description provided.

@codecov-commenter
Copy link

codecov-commenter commented Jul 28, 2023

Codecov Report

Merging #99 (e9bb079) into main (49c0560) will decrease coverage by 0.06%.
The diff coverage is 77.77%.

❗ Your organization is not using the GitHub App Integration. As a result you may experience degraded service beginning May 15th. Please install the Github App Integration for your organization. Read more.

@@            Coverage Diff             @@
##             main      #99      +/-   ##
==========================================
- Coverage   84.28%   84.22%   -0.06%     
==========================================
  Files          79       80       +1     
  Lines        2214     2232      +18     
==========================================
+ Hits         1866     1880      +14     
- Misses        348      352       +4     
Files Changed Coverage Δ
src/Pigeons.jl 100.00% <ø> (ø)
src/pt/BreadCrumbs.jl 76.47% <76.47%> (ø)
src/api.jl 100.00% <100.00%> (ø)

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

Copy link
Collaborator

@nikola-sur nikola-sur left a comment

Choose a reason for hiding this comment

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

I think the name BreadCrumbs is super clever and cute, but maybe too cute. Can we name it something more descriptive?

@miguelbiron
Copy link
Collaborator Author

@nikola-sur yeah we can definitely change it. To make it very explicit, we could call it "FunctionDistributionPair" or something like that. It conveys the specific inputs requested.

At the same time, we should think if there are other use cases that could be captured with a similar interface. E.g. instead of a Distribution object, we could specify the reference as a generic logpdf function and a sampler function.

@nikola-sur
Copy link
Collaborator

Maybe we call it something like CustomLogPotential to keep in line with TuringLogPotential, Stan, Comrade? Then CustomLogPotential() can accept various forms of input (two or three arguments):

  • target logpdf + Distributions object for the reference from which you know how to sample and evaluate
  • target logpdf and reference logpdf, defaults to identity kernel with warning
  • target logpdf, reference logpdf, reference rand for iid samples

Also, can we compose distributions like Uniform * Uniform, Uniform ^100, etc? Or is that dangerous?

@nikola-sur
Copy link
Collaborator

Because Turing implicitly couples a reference and target and that's all contained in TuringLogPotential, maybe we should unify and do CustomLogPotential? That's my line of thought but am curious what others think.

Copy link
Member

@alexandrebouchard alexandrebouchard left a comment

Choose a reason for hiding this comment

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

Very nice!!! See some thoughts below

eltype(bct.bc.reference_distribution)(-Inf)
end
end
default_explorer(::BreadCrumbsTarget) = SliceSampler()
Copy link
Member

Choose a reason for hiding this comment

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

SliceSampler() is already the global default, no need to have that line.


The PT state is initialized using a random sample from the reference.
"""
struct BreadCrumbs{TRefDist <: Distributions.Distribution, TTarget}
Copy link
Member

Choose a reason for hiding this comment

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

Can you use @auto instead? Otherwise the standard out gets cluttered with pages of mostly useless type information when showing the stack trace.

Also ref should be more general. We don't want Distributions to only work in BreadCrumbs. It should be seamless with the reference = .. method as well. (unless we remove the reference = .. method; which I don't think is a good idea since it works well for PPLs).

Conversely, things that can be fed into reference = .. should work in the target_log_potential argument. Similarly things that gets fed into target = should work in target_log_potential (I think this is already the case?).

end

# Target for a BreadCrumbs input
struct BreadCrumbsTarget{TBC <: BreadCrumbs}
Copy link
Member

Choose a reason for hiding this comment

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

This seems superfluous?

Copy link
Member

Choose a reason for hiding this comment

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

I.e. instead can we write the dispatches on Distributions types directly?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ah! I think you're right! At least for the reference. I don't think people are thinking of passing the loglikelihood as a Distribution on the data given the parameter. That would also be very restrictive given that Distributions.jl only has simple models.

end
# univariate case: need to wrap in vector to make the state mutable
function initialization(
bct::TBCT,
Copy link
Member

Choose a reason for hiding this comment

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

isn't this equivalent to bct::Distributions.UnivariateDistribution? etc for the other arguments

@@ -63,7 +63,7 @@ const use_auto_exec_folder = ""

include("includes.jl")

export pigeons, Inputs, PT,
export pigeons, Inputs, PT, BreadCrumbs,
Copy link
Member

Choose a reason for hiding this comment

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

Sounds good to export it, but then the PR should contain documentation (in code and in the website).

@alexandrebouchard
Copy link
Member

About the naming: since it's subjective I suggest we discuss in person when I come back--or even better, over beers :)

@alexandrebouchard
Copy link
Member

Further comments: one idea to get this merged faster is to split into two phases.

First, a non-breaking PR which just adds Distribution.jl support to the existing API, i.e. via reference = ... To achieve this, we would first internally dispatch on a more informative version of initialization() where the first argument is the full path of distributions instead of just the target. This way when the reference is specified with a distributions.jl (or similar situations), we can just use the reference to initialize. But also by default we dispatch to the current version of initialization taking only the target so the change is not too drastic. With this the one liner could be something like that for a truncated Gaussian example:

pigeons(reference = Uniform()^2) do x 
   -0.5 * sum(abs2, x)
end

Then we can discuss further API-changing PRs, e.g. user-facing BreadCrumbs, unifications of LogPotentials, etc.

@alexandrebouchard
Copy link
Member

alexandrebouchard commented Jul 29, 2023

(in the above, we partly use the fact that the type Function already conforms the informal interface log_potential.)

@alexandrebouchard

This comment was marked as outdated.

@miguelbiron
Copy link
Collaborator Author

@nikola-sur

Also, can we compose distributions like Uniform * Uniform, Uniform ^100, etc? Or is that dangerous?

Yeah that would be type piracy... But it would be nice to suggest them implement the product and power operator for distributions!

@miguelbiron
Copy link
Collaborator Author

@alexandrebouchard

To achieve this, we would first internally dispatch on a more informative version of initialization() where the first argument is the full path of distributions instead of just the target.

This was precisely the reason why I needed to have special types so that initialization could talk to the reference through the pointer in the target object. But changing initialization seems like a more robust option going forward.

Also: splitting the PR sounds good to me!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@alexandrebouchard unrelated to this PR but I had to change this limit so that the test would pass. Are you ok with lowering this limit?

@miguelbiron
Copy link
Collaborator Author

@alexandrebouchard Since initialization is only used here

states = [initialization(inputs.target, split_rngs[i], my_global_indices[i]) for i in eachindex(split_rngs)]

It seems like the best option would be to make it accept the full Inputs object -- which already holds the pointer to the reference object -- and then have a default method that dispatches on the target alone. We could then also have methods that dispatch on the type of the reference object or any other thing in the Inputs. This approach would probably break the least stuff.

@alexandrebouchard
Copy link
Member

Dispatching on full input obj may be a bit messy BC inputs has several type params, and for maintenance if we change input type params that would be on more thing to change. But not opposed if that's what makes most sense

@miguelbiron
Copy link
Collaborator Author

It seems doable! I'm almost done with a prototype but I have encountered a different difficulty. The issue with using a distribution directly as a reference is that making them callable within Pigeons would constitute type piracy, if I'm not mistaken. From this point of view, it would make sense to wrap the Distribution object inside another one created by us. What do you think?

@miguelbiron
Copy link
Collaborator Author

miguelbiron commented Jul 30, 2023

Ok I expanded on the above idea on a separate PR (#102 ) with a wrapping struct called DistributionReference. We can leave the BreadCrumbs name for a more ambitious revamp of the API.

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