Skip to content

Commit 85dc717

Browse files
Jammy2211claude
authored andcommitted
Fix assertion failures escaping Fitness.call
Move instance_from_vector inside try/except FitException so that assertion failures on prior violation return resample_figure_of_merit instead of bubbling FitException up into the non-linear search. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent cebec12 commit 85dc717

2 files changed

Lines changed: 36 additions & 3 deletions

File tree

autofit/non_linear/fitness.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,17 +168,18 @@ def call(self, parameters):
168168
-------
169169
The figure of merit returned to the non-linear search, which is either the log likelihood or log posterior.
170170
"""
171-
# Get instance from model
172-
instance = self.model.instance_from_vector(vector=parameters, xp=self._xp)
173-
174171
if self._xp.__name__.startswith("jax"):
175172

173+
# Get instance from model (must be side-effect free and exception-free under JAX)
174+
instance = self.model.instance_from_vector(vector=parameters, xp=self._xp)
175+
176176
# Evaluate log likelihood (must be side-effect free and exception-free)
177177
log_likelihood = self.analysis.log_likelihood_function(instance=instance)
178178

179179
else:
180180

181181
try:
182+
instance = self.model.instance_from_vector(vector=parameters, xp=self._xp)
182183
log_likelihood = self.analysis.log_likelihood_function(instance=instance)
183184
except exc.FitException:
184185
return self.resample_figure_of_merit
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import numpy as np
2+
3+
import autofit as af
4+
from autofit.non_linear.fitness import Fitness
5+
6+
7+
def test_fitness_returns_resample_fom_on_assertion_failure():
8+
"""
9+
If an added assertion rejects a sampled parameter vector, ``Fitness.call``
10+
must return ``resample_figure_of_merit`` rather than letting the
11+
``FitException`` escape into the non-linear search. Regression for
12+
the DynestyStatic-blocking bug where ``instance_from_vector`` was
13+
called outside the ``try/except FitException`` block.
14+
"""
15+
gaussian_0 = af.Model(af.ex.Gaussian)
16+
gaussian_1 = af.Model(af.ex.Gaussian)
17+
gaussian_0.add_assertion(gaussian_0.centre > gaussian_1.centre)
18+
model = af.Collection(gaussian_0=gaussian_0, gaussian_1=gaussian_1)
19+
20+
data = np.ones(20)
21+
noise_map = np.ones(20) * 0.1
22+
analysis = af.ex.Analysis(data=data, noise_map=noise_map)
23+
24+
fitness = Fitness(model=model, analysis=analysis)
25+
26+
# centre_0 (index 0) = 10 < centre_1 (index 3) = 20 → assertion violated
27+
violating = [10.0, 1.0, 1.0, 20.0, 1.0, 1.0]
28+
assert fitness.call(violating) == fitness.resample_figure_of_merit
29+
30+
# centre_0 = 20 > centre_1 = 10 → assertion satisfied, real FOM returned
31+
satisfying = [20.0, 1.0, 1.0, 10.0, 1.0, 1.0]
32+
assert fitness.call(satisfying) != fitness.resample_figure_of_merit

0 commit comments

Comments
 (0)