Skip to content

Commit 4c6be80

Browse files
committed
refactor: remove n_prior parameter, use fully adaptive warmup
The adaptive temperature scheduler now automatically maintains beta=0 until ESS >= n_effective, eliminating the need for a preset n_prior parameter. This makes warmup fully adaptive without requiring a fixed iteration count. Also simplifies _train() at beta=0 by removing unnecessary proposal parameter initialization.
1 parent ee80cb6 commit 4c6be80

1 file changed

Lines changed: 15 additions & 37 deletions

File tree

tempest/sampler.py

Lines changed: 15 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,16 @@ class Sampler:
107107
Metric used for determining the next temperature (``beta``) level (default is ``metric="ess"``).
108108
Options are ``"ess"`` (Effective Sample Size) or ``"uss"`` (Unique Sample Size). The metric
109109
is used to determine the next temperature level based on the ESS or USS of the importance
110-
weights. If the ESS or USS of the importance weights is below the target threshold, the temperature
111-
is increased. If the ESS or USS is above the target threshold, the temperature is decreased. The
112-
target threshold is set by the ``n_effective`` parameter.
113-
n_prior : int
114-
Number of prior samples to draw (default is ``n_prior=2*(n_effective//n_active)*n_active``). This
115-
is used to initialise the particles at the beginning of the run. The prior samples are used to
116-
warm-up the sampler and ensure that the particles are well distributed across the prior volume.
110+
weights. If the ESS or USS of the importance weights is below the target threshold, the
111+
temperature is increased. If the ESS or USS is above the target threshold, the temperature
112+
is decreased. The target threshold is set by the ``n_effective`` parameter.
113+
114+
Note
115+
----
116+
The sampler uses an adaptive temperature scheduler that automatically maintains
117+
beta=0 (prior sampling) until the effective sample size reaches n_effective.
118+
This adaptive warmup ensures sufficient prior samples are accumulated before
119+
transitioning to tempered sampling, without requiring a preset iteration count.
117120
sample : ``str``
118121
Type of MCMC sampler to use (default is ``sample="tpcn"``). Options are
119122
``"pcn"`` (t-preconditioned Crank-Nicolson) or ``"rwm"`` (Random-walk Metropolis).
@@ -164,7 +167,6 @@ def __init__(
164167
split_threshold: float = 1.0,
165168
n_max_clusters: Optional[int] = None,
166169
metric: str = "ess",
167-
n_prior: Optional[int] = None,
168170
sample: str = "tpcn",
169171
n_steps: Optional[int] = None,
170172
n_max_steps: Optional[int] = None,
@@ -326,17 +328,6 @@ def __init__(
326328
else:
327329
self.resample = resample
328330

329-
# Prior samples to draw
330-
if n_prior is None:
331-
self.n_prior = int(
332-
2 * np.maximum(self.n_effective // self.n_active, 1) * self.n_active
333-
)
334-
else:
335-
self.n_prior = int(np.maximum(n_prior / self.n_active, 1) * self.n_active)
336-
self.prior_samples = None
337-
338-
self.n_warmup_iters = None # Set in run()
339-
340331
self.progress = None
341332
self.pbar = None
342333

@@ -434,10 +425,7 @@ def run(
434425

435426
self.n_total = int(n_total)
436427

437-
# Number of warmup iterations (prior sampling at beta=0)
438-
self.n_warmup_iters = self.n_prior // self.n_active
439-
440-
# Run PS loop (includes warmup iterations at beta=0)
428+
# Run PS loop (adaptive warmup and annealing)
441429
while self._not_termination():
442430
self.sample(save_every=save_every, t0=t0)
443431

@@ -689,12 +677,8 @@ def _train(self):
689677
current_particles : dict
690678
Dictionary containing the updated particles.
691679
"""
692-
# Skip training during warmup (beta=0) - use simple isotropic proposal
680+
# Skip training at beta=0 (mutation draws fresh prior samples)
693681
if self.beta == 0.0:
694-
self.means = np.array([[0.5] * self.n_dim])
695-
self.covariances = np.array([np.eye(self.n_dim) * 0.1])
696-
self.degrees_of_freedom = np.array([self.DOF_FALLBACK])
697-
self.K = 1
698682
return
699683

700684
if self.clustering and (self.iter % self.cluster_every == 0 or self.iter == 0):
@@ -815,18 +799,12 @@ def _reweight(self):
815799
self.iter += 1
816800
self.pbar.update_iter()
817801

818-
# During warmup phase, keep beta=0 to accumulate prior samples
819-
if self.iter <= self.n_warmup_iters:
802+
# Handle first iteration (no particles yet)
803+
if len(self.particles.past.get("beta", [])) == 0:
820804
self.beta = 0.0
821805
self.logz = 0.0
822806
self.ess = self.n_effective
823-
# Uniform weights during warmup
824-
n_particles = (
825-
len(self.particles.get("logl", flat=True))
826-
if self.iter > 1
827-
else self.n_active
828-
)
829-
self.weights = np.ones(n_particles) / n_particles
807+
self.weights = np.ones(self.n_active) / self.n_active
830808
self.pbar.update_stats(
831809
dict(beta=self.beta, ESS=int(self.ess), logZ=self.logz)
832810
)

0 commit comments

Comments
 (0)