Skip to content

Commit dcfa5f0

Browse files
authored
Merge pull request #765 from samlange04/fix/positions-lh-non-finite
fix: PositionsLH penalty silently zero when positions contain inf rows
2 parents 4636715 + adb419e commit dcfa5f0

2 files changed

Lines changed: 57 additions & 3 deletions

File tree

‎autolens/analysis/positions.py‎

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,27 @@ def __init__(
8888
for a double source plane lens system is being used where the specific plane is required.
8989
"""
9090

91+
# A position that is not finite cannot be traced: a single (inf, inf) row makes
92+
# the maximum source-plane separation nan, `nan > threshold` is False, and the
93+
# penalty is zero for every model -- silently, since `output_positions_info`
94+
# uses a nan-safe maximum and reports the true separation. `Result.
95+
# positions_likelihood_from` passes exactly that: the point solver's
96+
# fixed-length output, padded with (inf, inf). Drop such rows.
97+
values = np.asarray(positions.array if hasattr(positions, "array") else positions)
98+
finite = np.isfinite(values).all(axis=1)
99+
100+
if not finite.all():
101+
positions = aa.Grid2DIrregular(values[finite])
102+
91103
self.positions = positions
92104
self.threshold = threshold
93105
self.plane_redshift = plane_redshift
94106

95-
if len(positions) == 1:
107+
if len(positions) < 2:
96108
raise exc.PositionsException(
97-
f"The positions input into the PositionsLikelihood object have length one "
98-
f"(e.g. it is only one (y,x) coordinate and therefore cannot be compared with other images).\n\n"
109+
f"The positions input into the PositionsLikelihood object have {len(positions)} finite "
110+
f"(y,x) coordinate(s) ({int((~finite).sum())} non-finite dropped), so there is nothing "
111+
f"to compare them with.\n\n"
99112
"Please input more positions into the Positions."
100113
)
101114

‎test_autolens/analysis/test_positions.py‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import os
22
from pathlib import Path
3+
import numpy as np
34
import pytest
45

56
from autonerves.dictable import output_to_json, from_json, from_dict
@@ -20,6 +21,46 @@ def test__check_positions_on_instantiation():
2021
al.PositionsLH(positions=al.Grid2DIrregular([(1.0, 2.0)]), threshold=0.1)
2122

2223

24+
def _penalty(positions_lh, einstein_radius):
25+
"""The penalty for an isothermal lens of the given Einstein radius."""
26+
lens = al.Galaxy(
27+
redshift=0.5, mass=al.mp.Isothermal(centre=(0.0, 0.0), einstein_radius=einstein_radius)
28+
)
29+
tracer = al.Tracer(galaxies=[lens, al.Galaxy(redshift=1.0)])
30+
31+
class MockAnalysis:
32+
def tracer_via_instance_from(self, instance):
33+
return tracer
34+
35+
return float(
36+
positions_lh.log_likelihood_penalty_from(instance=None, analysis=MockAnalysis())
37+
)
38+
39+
40+
def test__non_finite_positions_are_dropped_and_the_penalty_still_fires():
41+
# `Result.positions_likelihood_from` pads the point solver's images with
42+
# (inf, inf). One such row used to make the max separation nan, so the
43+
# penalty was zero for every model.
44+
inf = np.inf
45+
positions = al.Grid2DIrregular([(0.0, 1.0), (inf, inf), (0.0, -1.0)] + [(inf, inf)] * 5)
46+
47+
positions_lh = al.PositionsLH(positions=positions, threshold=0.1)
48+
49+
assert len(positions_lh.positions) == 2
50+
# Images at +/-1" trace together only for theta_E = 1"; theta_E = 0.3" splits them.
51+
assert _penalty(positions_lh, einstein_radius=1.0) == 0.0
52+
assert _penalty(positions_lh, einstein_radius=0.3) > 0.0
53+
54+
55+
def test__fewer_than_two_finite_positions_raises():
56+
inf = np.inf
57+
58+
with pytest.raises(exc.PositionsException):
59+
al.PositionsLH(
60+
positions=al.Grid2DIrregular([(0.0, 1.0), (inf, inf), (inf, inf)]), threshold=0.1
61+
)
62+
63+
2364
def test__output_positions_info():
2465
output_path = Path(__file__).resolve().parent / "files"
2566

0 commit comments

Comments
 (0)