Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 8 additions & 17 deletions bilby/core/sampler/dynesty.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,9 @@ class Dynesty(NestedSampler):
nlive: int, (1000)
The number of live points, note this can also equivalently be given as
one of [nlive, nlives, n_live_points, npoints]
bound: {'live', 'live-multi', 'none', 'single', 'multi', 'balls', 'cubes'}, ('live')
Method used to select new points
bound: {'none', 'single', 'multi', 'balls', 'cubes'}, ('none')
Bounding method used for dynesty-native samplers to select new points.
For bilby-implemented sampling methods this only affects volumetric proposals.
sample: {'act-walk', 'acceptance-walk', 'unif', 'rwalk', 'slice',
'rslice', 'hslice', 'rwalk_dynesty'}, ('act-walk')
Method used to sample uniformly within the likelihood constraints,
Expand Down Expand Up @@ -159,7 +160,7 @@ def _dynesty_init_kwargs(self):
if param.default != param.empty
}
kwargs["sample"] = "act-walk"
kwargs["bound"] = "live"
kwargs["bound"] = "none"
kwargs["update_interval"] = 600
kwargs["facc"] = 0.2
return kwargs
Expand Down Expand Up @@ -258,8 +259,7 @@ def sampler_function_kwargs(self):
def sampler_init_kwargs(self):
kwargs = {key: self.kwargs[key] for key in self._dynesty_init_kwargs}
# if we're using a Bilby implemented sampling method we need to register the
# method. If we aren't we need to make sure the default "live" isn't set as
# the bounding method
# method.
internal_kwargs = dict(
ndim=self.ndim,
nonbounded=self.kwargs.get("nonbounded", None),
Expand All @@ -268,10 +268,12 @@ def sampler_init_kwargs(self):
maxmcmc=self.maxmcmc,
)

if self.proposals:
internal_kwargs["proposals"] = self.proposals

if kwargs["sample"] == "act-walk":
internal_kwargs["nact"] = self.nact
internal_sampler = dynesty_utils.ACTTrackingEnsembleWalk(**internal_kwargs)
bound = "none"
logger.info(
f"Using the bilby-implemented ensemble rwalk sampling tracking the "
f"autocorrelation function and thinning by {internal_sampler.thin} with "
Expand All @@ -281,7 +283,6 @@ def sampler_init_kwargs(self):
internal_kwargs["naccept"] = self.naccept
internal_kwargs["walks"] = self.kwargs["walks"]
internal_sampler = dynesty_utils.EnsembleWalkSampler(**internal_kwargs)
bound = "none"
logger.info(
f"Using the bilby-implemented ensemble rwalk sampling method with an "
f"average of {internal_sampler.naccept} accepted steps up to chain "
Expand All @@ -290,24 +291,14 @@ def sampler_init_kwargs(self):
elif kwargs["sample"] == "rwalk":
internal_kwargs["nact"] = self.nact
internal_sampler = dynesty_utils.AcceptanceTrackingRWalk(**internal_kwargs)
bound = "none"
logger.info(
f"Using the bilby-implemented ensemble rwalk sampling method with ACT "
f"estimated chain length. An average of {2 * internal_sampler.nact} "
f"steps will be accepted up to chain length {internal_sampler.maxmcmc}."
)
elif kwargs["bound"] == "live":

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think we need to reinstate this clause.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Alternatively, bound = "none" could be dropped entirely as a default keyword argument for the dynesty sampler. That would make "live-multi" (or whatever the upstream default bounding option is if it ever changes) the default option. If it is preferred that the behavior of "live" stays the default, I'll reinstate the clause, otherwise I'll push a fix that removes the default option entirely.

logger.info(
"Live-point based bound method requested with dynesty sample "
f"'{kwargs['sample']}', overwriting to 'multi'"
)
internal_sampler = kwargs["sample"]
bound = "multi"
else:
internal_sampler = kwargs["sample"]
bound = kwargs["bound"]
kwargs["sample"] = internal_sampler
kwargs["bound"] = bound
return kwargs

def _translate_kwargs(self, kwargs):
Expand Down
37 changes: 20 additions & 17 deletions docs/dynesty-guide.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,27 +68,30 @@ There are a number of keyword arguments that influence these sampling methods:
above. The allowed values are

* :code:`diff`: `ter Braak + (2006) <https://doi.org/10.1007/s11222-006-8769-1>`_
differential evolution. This is the default for :code:`bound="live"` and
:code:`bound="live-multi"`.
differential evolution. This is the default.

* :code:`volumetric`: sample from an ellipsoid centered on the current point.
This is the proposal distribution implemented in :code:`dynesty` and the
default for all other :code:`bound` options. This was the default proposal
* :code:`volumetric`: sample from region centered on the current point.
This is the proposal distribution implemented in :code:`dynesty`. This was the default proposal
distribution for :code:`Bilby<2`, however, in many applications it leads to
longer autocorrelation times and struggles to explore multi-modal distributions.
Setting, eg., :code:`bound=multi` adapts the proposal region shape similar to :code:`dynesty` implemented
multi-ellipsoid volumetric sampling.
Note that bilby does not further tune the scale of individual proposals as done in dynesty.
Performance for :code:`bound='none'` is expected to be very poor.

Finally, we implement two custom :code:`dynesty.sampler.Sampler` classes to
facilitate the differential evolution proposal and average acceptance tracking.

#. :code:`bound="live"` uses the :code:`LivePointSampler` which adapts the
:code:`walks` to average :code:`naccept` accepted steps when in
:code:`acceptance-walk` mode and passes the current live points to the
sample method.

#. :code:`bound="live-multi"` combines the functionality of :code:`"live"` with
the :code:`dynesty` implemented :code:`multi` method for multi-ellipsoid
:code:`volumetric` sampling. This method is intended when using both the
:code:`diff` and :code:`volumetric` proposals.
.. note::
For :code:`bilby<3.0` and :code:`dynesty<3.0.0`, we implement two custom
:code:`dynesty.sampler.Sampler` classes to facilitate the differential evolution proposal and average acceptance tracking.

#. :code:`bound="live"` uses the :code:`LivePointSampler` which adapts the
:code:`walks` to average :code:`naccept` accepted steps when in
:code:`acceptance-walk` mode and passes the current live points to the
sample method.

#. :code:`bound="live-multi"` combines the functionality of :code:`"live"` with
the :code:`dynesty` implemented :code:`multi` method for multi-ellipsoid
:code:`volumetric` sampling. This method is intended when using both the
:code:`diff` and :code:`volumetric` proposals.

Understanding the output
------------------------
Expand Down
2 changes: 1 addition & 1 deletion test/core/sampler/dynamic_dynesty_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def test_default_kwargs(self):
"""Only test the kwargs where we specify different defaults to dynesty"""
expected = dict(
sample="act-walk",
bound="live",
bound="none",
facc=0.2,
save_bounds=False,
update_interval=600,
Expand Down
9 changes: 8 additions & 1 deletion test/core/sampler/dynesty_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def test_default_kwargs(self):
facc=0.2,
save_bounds=False,
dlogz=0.1,
bound="live",
bound="none",
update_interval=600,
)
for key in expected:
Expand Down Expand Up @@ -159,6 +159,13 @@ def test_dynesty_native_methods_initialize(self, sample, bound):
"""
self.init_sampler(sample=sample, bound=bound)

def test_proposals_set_on_init(self):
self.init_sampler(proposals=["diff", "volumetric"])
self.assertEqual(
self.dysampler.internal_sampler_next.sampler_kwargs["proposals"],
["diff", "volumetric"]
)


def test_get_expected_outputs():
label = "par0"
Expand Down
Loading