Skip to content
Merged
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
45 changes: 44 additions & 1 deletion scripts/imaging/jax_likelihood/mge_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,16 @@
profile_list=bulge_gaussian_list,
)

# The main mass is anchored near the `simulator/simple.py` truth (einstein_radius=1.6) with a
# prior whose median is that truth. At the config-default prior median (einstein_radius=4.0) the
# positive-only solver zeroes the source's solved intensities and the vmap literal below is
# bit-identical for ANY source-plane mass structure (audit 2026-08-06, autolens_workspace_test#253)
# — the mass-sensitivity assertion after the literal only works on a retained source.

mass = af.Model(al.mp.Isothermal)
mass.centre = (0.0, 0.0)
mass.ell_comps = al.convert.ell_comps_from(axis_ratio=0.8, angle=45.0)
mass.einstein_radius = af.UniformPrior(lower_limit=1.1, upper_limit=2.1)

shear = af.Model(al.mp.ExternalShear)

Expand Down Expand Up @@ -313,12 +322,46 @@

np.testing.assert_allclose(
np.array(result),
-29060.21511937,
-28830.547173,
rtol=1e-4,
err_msg="mge_group: JAX vmap likelihood mismatch",
)


"""
__Mass Sensitivity__

The literal above is evaluated at the model's prior medians, where a +5% change of
every lens mass parameter moves this likelihood by less than the literal's rtol
(audit 2026-08-06, autolens_workspace_test#253) — the literal alone would pass a
source-plane mass regression. This block pins mass sensitivity directly; the floor
is the audit-measured response divided by five (margin for platform drift).
"""
mass_indices = [
i
for i, name in enumerate(model.model_component_and_parameter_names)
if ".mass." in name
and "centre" not in name
and "ell_comps" not in name
and "redshift" not in name
]
assert mass_indices, "imaging/mge_group: no mass parameters found for sensitivity check"

parameters_perturbed = np.array(model.physical_values_from_prior_medians)
for i in mass_indices:
parameters_perturbed[i] *= 1.05

ll_median = float(np.asarray(result).ravel()[0])
ll_perturbed = float(
np.asarray(fitness._vmap(jnp.array(parameters_perturbed[None, :]))).ravel()[0]
)
assert abs(ll_perturbed - ll_median) > 9.0, (
f"imaging/mge_group: likelihood insensitive to a +5% lens-mass perturbation "
f"(median={ll_median}, perturbed={ll_perturbed}) — source-plane mass pipeline regression?"
)
print("PASS: mass-sensitivity floor exceeded.")


"""
__Path A: jit-wrap ``analysis.fit_from``__
"""
Expand Down
34 changes: 34 additions & 0 deletions scripts/interferometer/datacube/delaunay.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,40 @@
)


"""
__Mass Sensitivity__

The literal above is evaluated at the model's prior medians, where a +5% change of
every lens mass parameter moves this likelihood by less than the literal's rtol
(audit 2026-08-06, autolens_workspace_test#253) — the literal alone would pass a
source-plane mass regression. This block pins mass sensitivity directly; the floor
is the audit-measured response divided by five (margin for platform drift).
"""
mass_indices = [
i
for i, name in enumerate(model.model_component_and_parameter_names)
if ".mass." in name
and "centre" not in name
and "ell_comps" not in name
and "redshift" not in name
]
assert mass_indices, "interferometer/datacube/delaunay: no mass parameters found for sensitivity check"

parameters_perturbed = np.array(model.physical_values_from_prior_medians)
for i in mass_indices:
parameters_perturbed[i] *= 1.05

ll_median = float(np.asarray(result).ravel()[0])
ll_perturbed = float(
np.asarray(fitness._vmap(jnp.array(parameters_perturbed[None, :]))).ravel()[0]
)
assert abs(ll_perturbed - ll_median) > 0.1, (
f"interferometer/datacube/delaunay: likelihood insensitive to a +5% lens-mass perturbation "
f"(median={ll_median}, perturbed={ll_perturbed}) — source-plane mass pipeline regression?"
)
print("PASS: mass-sensitivity floor exceeded.")


"""
__Path A: jit-wrap parameter-vector entry point__

Expand Down
34 changes: 34 additions & 0 deletions scripts/interferometer/datacube/rectangular.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,40 @@
)


"""
__Mass Sensitivity__

The literal above is evaluated at the model's prior medians, where a +5% change of
every lens mass parameter moves this likelihood by less than the literal's rtol
(audit 2026-08-06, autolens_workspace_test#253) — the literal alone would pass a
source-plane mass regression. This block pins mass sensitivity directly; the floor
is the audit-measured response divided by five (margin for platform drift).
"""
mass_indices = [
i
for i, name in enumerate(model.model_component_and_parameter_names)
if ".mass." in name
and "centre" not in name
and "ell_comps" not in name
and "redshift" not in name
]
assert mass_indices, "interferometer/datacube/rectangular: no mass parameters found for sensitivity check"

parameters_perturbed = np.array(model.physical_values_from_prior_medians)
for i in mass_indices:
parameters_perturbed[i] *= 1.05

ll_median = float(np.asarray(result).ravel()[0])
ll_perturbed = float(
np.asarray(fitness._vmap(jnp.array(parameters_perturbed[None, :]))).ravel()[0]
)
assert abs(ll_perturbed - ll_median) > 0.03, (
f"interferometer/datacube/rectangular: likelihood insensitive to a +5% lens-mass perturbation "
f"(median={ll_median}, perturbed={ll_perturbed}) — source-plane mass pipeline regression?"
)
print("PASS: mass-sensitivity floor exceeded.")


"""
__Path A: jit-wrap parameter-vector entry point__

Expand Down
34 changes: 34 additions & 0 deletions scripts/interferometer/jax_likelihood/delaunay.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,40 @@ class in **PyAutoFit**, which pairs the model with likelihood.
err_msg="interferometer/delaunay: JAX vmap likelihood mismatch",
)


"""
__Mass Sensitivity__

The literal above is evaluated at the model's prior medians, where a +5% change of
every lens mass parameter moves this likelihood by less than the literal's rtol
(audit 2026-08-06, autolens_workspace_test#253) — the literal alone would pass a
source-plane mass regression. This block pins mass sensitivity directly; the floor
is the audit-measured response divided by five (margin for platform drift).
"""
mass_indices = [
i
for i, name in enumerate(model.model_component_and_parameter_names)
if ".mass." in name
and "centre" not in name
and "ell_comps" not in name
and "redshift" not in name
]
assert mass_indices, "interferometer/delaunay: no mass parameters found for sensitivity check"

parameters_perturbed = np.array(model.physical_values_from_prior_medians)
for i in mass_indices:
parameters_perturbed[i] *= 1.05

ll_median = float(np.asarray(result).ravel()[0])
ll_perturbed = float(
np.asarray(fitness._vmap(jnp.array(parameters_perturbed[None, :]))).ravel()[0]
)
assert abs(ll_perturbed - ll_median) > 0.03, (
f"interferometer/delaunay: likelihood insensitive to a +5% lens-mass perturbation "
f"(median={ll_median}, perturbed={ll_perturbed}) — source-plane mass pipeline regression?"
)
print("PASS: mass-sensitivity floor exceeded.")

# The sparse inversion path must obey the same lane-isolation contract as
# imaging. Fitness sees the raw NaN after the forward pass and converts only
# that lane to its configured resample value.
Expand Down
34 changes: 34 additions & 0 deletions scripts/interferometer/jax_likelihood/delaunay_mge.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,40 @@ class in **PyAutoFit**, which pairs the model with likelihood.
)


"""
__Mass Sensitivity__

The literal above is evaluated at the model's prior medians, where a +5% change of
every lens mass parameter moves this likelihood by less than the literal's rtol
(audit 2026-08-06, autolens_workspace_test#253) — the literal alone would pass a
source-plane mass regression. This block pins mass sensitivity directly; the floor
is the audit-measured response divided by five (margin for platform drift).
"""
mass_indices = [
i
for i, name in enumerate(model.model_component_and_parameter_names)
if ".mass." in name
and "centre" not in name
and "ell_comps" not in name
and "redshift" not in name
]
assert mass_indices, "interferometer/delaunay_mge: no mass parameters found for sensitivity check"

parameters_perturbed = np.array(model.physical_values_from_prior_medians)
for i in mass_indices:
parameters_perturbed[i] *= 1.05

ll_median = float(np.asarray(result).ravel()[0])
ll_perturbed = float(
np.asarray(fitness._vmap(jnp.array(parameters_perturbed[None, :]))).ravel()[0]
)
assert abs(ll_perturbed - ll_median) > 0.007, (
f"interferometer/delaunay_mge: likelihood insensitive to a +5% lens-mass perturbation "
f"(median={ll_median}, perturbed={ll_perturbed}) — source-plane mass pipeline regression?"
)
print("PASS: mass-sensitivity floor exceeded.")


"""
__Path A: jit-wrap ``analysis.fit_from``__
"""
Expand Down
34 changes: 34 additions & 0 deletions scripts/interferometer/jax_likelihood/mge_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,40 @@
err_msg="interferometer/mge_group: JAX vmap likelihood mismatch",
)


"""
__Mass Sensitivity__

The literal above is evaluated at the model's prior medians, where a +5% change of
every lens mass parameter moves this likelihood by less than the literal's rtol
(audit 2026-08-06, autolens_workspace_test#253) — the literal alone would pass a
source-plane mass regression. This block pins mass sensitivity directly; the floor
is the audit-measured response divided by five (margin for platform drift).
"""
mass_indices = [
i
for i, name in enumerate(model.model_component_and_parameter_names)
if ".mass." in name
and "centre" not in name
and "ell_comps" not in name
and "redshift" not in name
]
assert mass_indices, "interferometer/mge_group: no mass parameters found for sensitivity check"

parameters_perturbed = np.array(model.physical_values_from_prior_medians)
for i in mass_indices:
parameters_perturbed[i] *= 1.05

ll_median = float(np.asarray(result).ravel()[0])
ll_perturbed = float(
np.asarray(fitness._vmap(jnp.array(parameters_perturbed[None, :]))).ravel()[0]
)
assert abs(ll_perturbed - ll_median) > 0.005, (
f"interferometer/mge_group: likelihood insensitive to a +5% lens-mass perturbation "
f"(median={ll_median}, perturbed={ll_perturbed}) — source-plane mass pipeline regression?"
)
print("PASS: mass-sensitivity floor exceeded.")

print("interferometer/mge_group.py checks passed.")


Expand Down
34 changes: 34 additions & 0 deletions scripts/interferometer/jax_likelihood/rectangular.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,40 @@
)


"""
__Mass Sensitivity__

The literal above is evaluated at the model's prior medians, where a +5% change of
every lens mass parameter moves this likelihood by less than the literal's rtol
(audit 2026-08-06, autolens_workspace_test#253) — the literal alone would pass a
source-plane mass regression. This block pins mass sensitivity directly; the floor
is the audit-measured response divided by five (margin for platform drift).
"""
mass_indices = [
i
for i, name in enumerate(model.model_component_and_parameter_names)
if ".mass." in name
and "centre" not in name
and "ell_comps" not in name
and "redshift" not in name
]
assert mass_indices, "interferometer/rectangular: no mass parameters found for sensitivity check"

parameters_perturbed = np.array(model.physical_values_from_prior_medians)
for i in mass_indices:
parameters_perturbed[i] *= 1.05

ll_median = float(np.asarray(result).ravel()[0])
ll_perturbed = float(
np.asarray(fitness._vmap(jnp.array(parameters_perturbed[None, :]))).ravel()[0]
)
assert abs(ll_perturbed - ll_median) > 0.008, (
f"interferometer/rectangular: likelihood insensitive to a +5% lens-mass perturbation "
f"(median={ll_median}, perturbed={ll_perturbed}) — source-plane mass pipeline regression?"
)
print("PASS: mass-sensitivity floor exceeded.")


"""
__Path A: jit-wrap ``analysis.fit_from``__
"""
Expand Down
34 changes: 34 additions & 0 deletions scripts/interferometer/jax_likelihood/rectangular_dspl.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,40 @@ class in **PyAutoFit**, which pairs the model with likelihood.
)


"""
__Mass Sensitivity__

The literal above is evaluated at the model's prior medians, where a +5% change of
every lens mass parameter moves this likelihood by less than the literal's rtol
(audit 2026-08-06, autolens_workspace_test#253) — the literal alone would pass a
source-plane mass regression. This block pins mass sensitivity directly; the floor
is the audit-measured response divided by five (margin for platform drift).
"""
mass_indices = [
i
for i, name in enumerate(model.model_component_and_parameter_names)
if ".mass." in name
and "centre" not in name
and "ell_comps" not in name
and "redshift" not in name
]
assert mass_indices, "interferometer/rectangular_dspl: no mass parameters found for sensitivity check"

parameters_perturbed = np.array(model.physical_values_from_prior_medians)
for i in mass_indices:
parameters_perturbed[i] *= 1.05

ll_median = float(np.asarray(result).ravel()[0])
ll_perturbed = float(
np.asarray(fitness._vmap(jnp.array(parameters_perturbed[None, :]))).ravel()[0]
)
assert abs(ll_perturbed - ll_median) > 0.009, (
f"interferometer/rectangular_dspl: likelihood insensitive to a +5% lens-mass perturbation "
f"(median={ll_median}, perturbed={ll_perturbed}) — source-plane mass pipeline regression?"
)
print("PASS: mass-sensitivity floor exceeded.")


"""
__Path A: jit-wrap ``analysis.fit_from``__
"""
Expand Down
34 changes: 34 additions & 0 deletions scripts/interferometer/jax_likelihood/rectangular_mge.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,40 @@ class in **PyAutoFit**, which pairs the model with likelihood.
)


"""
__Mass Sensitivity__

The literal above is evaluated at the model's prior medians, where a +5% change of
every lens mass parameter moves this likelihood by less than the literal's rtol
(audit 2026-08-06, autolens_workspace_test#253) — the literal alone would pass a
source-plane mass regression. This block pins mass sensitivity directly; the floor
is the audit-measured response divided by five (margin for platform drift).
"""
mass_indices = [
i
for i, name in enumerate(model.model_component_and_parameter_names)
if ".mass." in name
and "centre" not in name
and "ell_comps" not in name
and "redshift" not in name
]
assert mass_indices, "interferometer/rectangular_mge: no mass parameters found for sensitivity check"

parameters_perturbed = np.array(model.physical_values_from_prior_medians)
for i in mass_indices:
parameters_perturbed[i] *= 1.05

ll_median = float(np.asarray(result).ravel()[0])
ll_perturbed = float(
np.asarray(fitness._vmap(jnp.array(parameters_perturbed[None, :]))).ravel()[0]
)
assert abs(ll_perturbed - ll_median) > 0.02, (
f"interferometer/rectangular_mge: likelihood insensitive to a +5% lens-mass perturbation "
f"(median={ll_median}, perturbed={ll_perturbed}) — source-plane mass pipeline regression?"
)
print("PASS: mass-sensitivity floor exceeded.")


"""
__Path A: jit-wrap ``analysis.fit_from``__
"""
Expand Down
Loading