ワンクリックで
develop-sampler
Guide for adding a new internal sampler (proposal strategy) to JNesty.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Guide for adding a new internal sampler (proposal strategy) to JNesty.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
JNesty code architecture — modules, data flow, key data structures, JAX patterns, and demo scripts.
Diagnosing convergence problems, poor acceptance rates, and incorrect results in JNesty.
Guide for adding a new bounding method to JNesty.
JNesty usage guide — public API, parameters, results access, FITS I/O, bounding methods, tuning, and plotting.
| name | /develop-sampler |
| description | Guide for adding a new internal sampler (proposal strategy) to JNesty. |
Internal samplers live in src/jnesty/internal_samplers.py. Each implements the InternalSampler interface and is registered in SAMPLER_REGISTRY for lookup by string name.
The core NS loop in src/jnesty/sampler.py calls sampler_obj.sample() to propose replacement live points and sampler_obj.tune() to adapt the proposal scale.
There is also a standalone vmap_queue_refill() function for Dynesty-style queue mode that generates multiple candidates in parallel outside of any sampler object.
class InternalSampler:
"""Base class for proposal samplers."""
def __init__(self, ndim, **kwargs):
self.ndim = ndim
def sample(self, key, x_starts, logL_constraint, loglikelihood_fn,
axes, scale, n_steps, prior_bounds=None, walk_schedule=None):
"""
Generate a replacement point.
Parameters
----------
key : jax.random.PRNGKey
x_starts : array
If batch_size=1: (ndim,) single starting point.
If batch_size>1: (batch_size, ndim) diverse starting points.
logL_constraint : float, minimum log-likelihood
loglikelihood_fn : callable, log-likelihood
axes : array
If batch_size=1: (ncdim, ncdim) single axes matrix.
If batch_size>1: (batch_size, ncdim, ncdim) per-walk axes.
scale : float, current proposal scale
n_steps : int, number of walk/slice steps
prior_bounds : optional (2, ndim) array for rejection
walk_schedule : optional array, not used in current code (legacy)
Returns
-------
(x_new, logL_new, n_accepted, n_total) tuple
n_total includes boundary rejections (matches Dynesty semantics)
"""
raise NotImplementedError
def tune(self, scale, acceptance_rate, ndim, iteration):
"""Adapt scale based on acceptance rate. Returns new scale."""
raise NotImplementedError
Important: sample() returns a 4-tuple (x_new, logL_new, n_accepted, n_total), not a 3-tuple. The n_total counts all proposals including boundary rejections.
All code inside sample() must be JAX-compatible because it runs inside lax.while_loop:
jnp.where(), jax.lax.cond(), jax.lax.select()jax.debug.print() or io_callback for debugging onlylax.scan for loops, not Python forkey, subkey = random.split(key)jnp.where(in_bounds, loglikelihood_fn(x), -inf) for XLA fusion — lax.cond with data-dependent predicates inside lax.scan kills GPU kernel fusionThe existing RWalkSampler demonstrates the pattern:
_single_walk(key, x_start, logL_constraint, loglikelihood_fn, axes, scale, n_steps, ndim, n_cluster, prior_bounds, walk_schedule)
Core walk function using lax.scan over n_steps. Each step:
randsphere() transformed by axes matrix (clustered dims)(x_final, n_accepted, n_total)_propose_one(key, x, axes, scale, n_cluster, ndim, walk_schedule, step_idx)
Single proposal generation. First n_cluster dimensions perturbed via axes transform; remaining dims uniform from [0,1].
apply_reflect(u)
Iterative reflection into [0,1]. Matches Dynesty's apply_reflect.
vmap_queue_refill(refill_key, live_x, live_logL, loglstar, loglikelihood_fn, me_axes, me_logvol_ells, bound_axes, scale, rwalk_K, ndim, ncdim, queue_size, use_multi_ellipsoid, prior_bounds=None)
Generates queue_size candidates in parallel. Each gets full rwalk_K steps from a random starting point (live point above loglstar) with a random volume-weighted ellipsoid. Returns (queue_x, queue_logL, queue_nacc, queue_ntot).
When batch_size > 1, RWalkSampler.sample() runs batch_size independent walks in parallel via jax.vmap:
rwalk_K // batch_sizeRWalkSampler.tune() uses Robbins-Munro adaptation matching Dynesty:
proposed_scale = scale * jnp.exp(
(acceptance_rate - target_acceptance)
/ ncdim / target_acceptance
)
Uses ncdim (clustered dimensions) not full ndim, matching Dynesty.
Add your class to src/jnesty/internal_samplers.py:
class SliceSampler(InternalSampler):
def __init__(self, ndim, **kwargs):
super().__init__(ndim, **kwargs)
def sample(self, key, x_starts, logL_constraint, loglikelihood_fn,
axes, scale, n_steps, prior_bounds=None, walk_schedule=None):
# Your sampling logic
# Must return (x_new, logL_new, n_accepted, n_total)
pass
def tune(self, scale, acceptance_rate, ndim, iteration):
# Your scale adaptation (or return scale unchanged)
return scale
Add to the SAMPLER_REGISTRY dict at the bottom of the file:
SAMPLER_REGISTRY = {
'rwalk': RWalkSampler,
'slice': SliceSampler, # Add this
}
In src/jnesty/sampler.py, the sampler is currently hardcoded:
sampler_obj = get_sampler('rwalk', ndim, ...) # line ~400
To make it configurable, add a sampler field to WhileLoopNSConfig and pass it through.
In src/jnesty/jnesty.py, add a sampler parameter to NestedSampler.__init__:
def __init__(self, ..., sampler='rwalk', ...):
If the sampler should work with queue mode, you may need a queue-refill function similar to vmap_queue_refill(). This function generates queue_size candidates in parallel, each using the full rwalk_K steps independently.
python -m py_compile src/jnesty/internal_samplers.pyInternalSamplersample() returns (x_new, logL_new, n_accepted, n_total) — 4-tuplesample() is JAX-compatible (no Python control flow in hot path)sample() handles both single and batch modes (x_starts shape varies)tune() returns a valid scale (or passes through unchanged)SAMPLER_REGISTRYWhileLoopNSConfig, NestedSampler)