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

Restructure to add flexibility #70

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

Restructure to add flexibility #70

wants to merge 28 commits into from

Conversation

zsunberg
Copy link
Member

The primary goal is to make it easier to add/propose particles when there is a depletion

@zsunberg
Copy link
Member Author

zsunberg commented Jan 26, 2025

Hey everyone,

I just wrapped up a refactor of this package that is relatively minor on the surface (i.e. BootstrapFilter should be backwards compatible), but has some major changes under the hood. Documentation for this PR can be found here: https://juliapomdp.github.io/ParticleFilters.jl/previews/PR70/

The main thing that I wanted to accomplish is making it easier to provide custom logic to recover from particle depletion. See https://juliapomdp.github.io/ParticleFilters.jl/previews/PR70/depletion/ for docs about this. I think it is much easier now.

Other significant changes are:

  • update does not resample on every step by default anymore for the BasicParticleFilter or BootstrapFilter. This means that the belief at every step is a WeightedParticleBelief rather than a ParticleCollection. I am not sure whether this is more efficient, reliable, or conventional. My guess is that it is probably more conventional and reliable, but less efficient. Maybe there should be another filter that implements the old way of doing it?
  • Adding functions to the particle belief interface for modifying beliefs (e.g. set_particle and set_weight, see https://juliapomdp.github.io/ParticleFilters.jl/previews/PR70/beliefs/#Particle-Interface). particles(b) and weights(b) now return read-only copies.
  • Restructuring the BasicParticleFilter to have four steps: preprocess, predict, reweight, and postprocess, see https://juliapomdp.github.io/ParticleFilters.jl/previews/PR70/basic/#Basic-Particle-Filter
  • Deprecating the ParticleFilterModel type (instead BootstrapFilter just requires a dynamics and observation likelihood function)
  • rand(::WeightedParticleBelief) now uses an alias table for much faster repeated sampling. (I profiled it with POMCP and creating the alias table seems to take a negligible amount of time)

@kylewray @jihodori @mykelk @dylan-asmar @mossr @WhiffleFish @himanshugupta1009 @lassepe tagging you because you've been using this package recently or involved in POMDPs.jl discussions, so you might have some useful feedback. It's probably too hard to look at all the changes, but reading the new docs might be useful. If you'd like to leave a review, that is welcome!

My main questions are:

  • Does the new way of handling particle depletion by providing a postprocess function make sense?
  • Based on your usage of this package, do you think any of these changes will make your use cases much harder/worse?

I'll probably merge and tag this next weekend if no one has big suggestions.

@zsunberg zsunberg changed the title WIP: Restructure to add flexibility Restructure to add flexibility Jan 26, 2025
@zsunberg zsunberg requested a review from jihodori January 26, 2025 22:32
@mykelk
Copy link
Member

mykelk commented Jan 27, 2025

This looks great @zsunberg ! Thanks for your hard work on this!

@jihodori
Copy link

jihodori commented Feb 2, 2025

Hi Professor @zsunberg,

This looks great!

Does the new way of handling particle depletion by providing a postprocess function make sense?

Providing a postprocess function makes sense, and I think it gives much flexibility to users. However, I am still trying to understand why having both preprocess and postprocess functions is necessary. Conceptually, your design seems to me that preprocess ensures a healthy particle set before propagation (i.e. resampling based on ESS) while postprocess prevents particle collapse and degeneracy after the update step.

That being said, I have two concerns:
1. Resampling before propagation. To my understanding, resampling is where we lose diversity. Hence, calling it after postprocess and before propagation could actually lead to particle collapse, especially if ESS remains low even after customized postprocess steps. I think we could end up duplicating high weight particles and propagating a degraded set.

2. Calling postprocess in every iteration. If ESS is already pretty good, then I think modifying the particle set or simply invoking postprocess (even if it does not change the particle set) might be unnecessary overhead.

I am not sure if this is a better design, but would it make sense to combine preprocess and postprocess (under a different name) and check ESS first after the weight update and if ESS is below the threshold, then we do 1. reampling AND THEN 2. all necessary postprocess steps via PostprocessChain. This way we remove preprocess and handle particle set health in a single method and only when needed.

Lastly, I have a couple of smaller questions

  1. Why are we passing in o to the predict function? My understanding is that observation should only come into play during the weight update step where we compute the likelihood. Shouldn't predict be only responsible for propagating particles using the dynamics?
  2. Why are we not passing in rng to the reweight function? Is the observation likelihood always deterministic? If we want to introduce noise in likelihood evaluation, shouldn't we need to pass rng?
    e.g. Using your example:
reweight(b, a, particles, o, rng) = weights(b) .* [pdf(Normal(p, 1.0), o + randn(rng)) for p in particles]

Let me know what you think!

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