Skip to content

Commit 7437137

Browse files
Jammy2211claude
authored andcommitted
refactor(model_util): replace simulator_start_here_model_from with direct random_galaxies_for_simulation_from
Mirrors the PyAutoGalaxy change. The old helper built af.Model(lp_snr.Sersic) with signal_to_noise_ratio = UniformPrior(20, 60) for both lens light and source, then callers did .random_instance() to sample. Replace with random_galaxies_for_simulation_from(include_lens_light, use_point_source), which returns (lens_galaxy, source_galaxy) concrete instances sampled via numpy.random.Generator — no af.Model priors involved. Same SNR-is-data rationale as the autogalaxy sibling. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 8d4937e commit 7437137

1 file changed

Lines changed: 85 additions & 64 deletions

File tree

autolens/analysis/model_util.py

Lines changed: 85 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -13,88 +13,109 @@
1313
- ``hilbert_pixels_from_pixel_scale`` — estimate Hilbert image-mesh pixel count.
1414
1515
PyAutoLens-specific:
16-
- ``simulator_start_here_model_from`` — builds a default imaging simulator model with
17-
optional lens light and point-source components for start_here scripts.
16+
- ``random_galaxies_for_simulation_from`` — sample concrete (lens, source) ``Galaxy``
17+
instances for synthetic-data generation in ``start_here`` scripts.
1818
"""
19-
import autofit as af
19+
from typing import Optional, Tuple
20+
21+
import numpy as np
22+
2023
import autolens as al
2124

2225
from autogalaxy.analysis.model_util import mge_model_from
2326
from autogalaxy.analysis.model_util import mge_point_model_from
2427
from autogalaxy.analysis.model_util import hilbert_pixels_from_pixel_scale
2528

26-
def simulator_start_here_model_from(
27-
include_lens_light: bool = True, use_point_source: bool = False
28-
):
2929

30-
if include_lens_light:
31-
bulge = af.Model(al.lp_snr.Sersic)
30+
SIMULATOR_RANDOM_LENS_SUMMARY = (
31+
"Each simulated strong lens draws fresh truths from: "
32+
"lens bulge SNR in [20, 60] (when included), "
33+
"lens mass einstein_radius in [0.2, 1.8] with normal-clipped ellipticity, "
34+
"external shear ~ Normal(0, 0.05), "
35+
"source bulge SNR in [10, 30] / point-source flux in [0.0, 2.0] (mode dependent)."
36+
)
37+
38+
39+
def _clipped_ell_comp(rng: np.random.Generator) -> float:
40+
return float(np.clip(rng.normal(0.0, 0.2), -1.0, 1.0))
41+
42+
43+
def random_galaxies_for_simulation_from(
44+
include_lens_light: bool = True,
45+
use_point_source: bool = False,
46+
rng: Optional[np.random.Generator] = None,
47+
) -> Tuple["al.Galaxy", "al.Galaxy"]:
48+
"""
49+
Sample a ``(lens_galaxy, source_galaxy)`` pair for synthetic strong-lens
50+
data generation.
51+
52+
Each parameter is drawn directly from a numpy ``Generator`` and used to
53+
construct concrete profile instances — no ``af.Model`` priors are involved.
54+
SNR-normalised Sersic profiles (``lp_snr.Sersic``) are used for diffuse
55+
light components so that simulator output lands at a controlled target
56+
SNR; the SNR appears as a profile attribute on the *instance*, never as a
57+
fitting parameter.
58+
59+
Do **not** use the returned galaxies as fitting models. They are
60+
instances, suitable for ``Tracer`` / ``simulator.via_tracer_from``.
61+
62+
Parameters
63+
----------
64+
include_lens_light
65+
If True (default), give the lens galaxy an ``lp_snr.Sersic`` bulge.
66+
If False, the lens is mass-only.
67+
use_point_source
68+
If True, source is a ``PointFlux`` with random centre and flux. If
69+
False (default), source is an ``lp_snr.Sersic``.
70+
rng
71+
Optional ``numpy.random.Generator``. If ``None`` a fresh
72+
``default_rng()`` is created on each call.
73+
74+
Returns
75+
-------
76+
(Galaxy, Galaxy)
77+
``(lens_galaxy, source_galaxy)`` at redshifts 0.5 and 1.0 respectively.
78+
"""
79+
rng = rng if rng is not None else np.random.default_rng()
3280

33-
bulge.centre = (0.0, 0.0)
34-
bulge.ell_comps.ell_comps_0 = af.TruncatedGaussianPrior(
35-
mean=0.0, sigma=0.2, lower_limit=-1.0, upper_limit=1.0
36-
)
37-
bulge.ell_comps.ell_comps_1 = af.TruncatedGaussianPrior(
38-
mean=0.0, sigma=0.2, lower_limit=-1.0, upper_limit=1.0
39-
)
40-
bulge.signal_to_noise_ratio = af.UniformPrior(
41-
lower_limit=20.0, upper_limit=60.0
42-
)
43-
bulge.effective_radius = af.UniformPrior(lower_limit=1.0, upper_limit=5.0)
44-
bulge.sersic_index = af.TruncatedGaussianPrior(
45-
mean=4.0, sigma=0.5, lower_limit=0.8, upper_limit=5.0
81+
if include_lens_light:
82+
lens_bulge = al.lp_snr.Sersic(
83+
centre=(0.0, 0.0),
84+
ell_comps=(_clipped_ell_comp(rng), _clipped_ell_comp(rng)),
85+
effective_radius=float(rng.uniform(1.0, 5.0)),
86+
sersic_index=float(rng.uniform(3.5, 4.5)),
87+
signal_to_noise_ratio=float(rng.uniform(20.0, 60.0)),
4688
)
4789
else:
48-
bulge = None
49-
50-
mass = af.Model(al.mp.Isothermal)
90+
lens_bulge = None
5191

52-
mass.centre = (0.0, 0.0)
53-
mass.ell_comps.ell_comps_0 = af.TruncatedGaussianPrior(
54-
mean=0.0, sigma=0.2, lower_limit=-1.0, upper_limit=1.0
55-
)
56-
mass.ell_comps.ell_comps_1 = af.TruncatedGaussianPrior(
57-
mean=0.0, sigma=0.2, lower_limit=-1.0, upper_limit=1.0
92+
mass = al.mp.Isothermal(
93+
centre=(0.0, 0.0),
94+
ell_comps=(_clipped_ell_comp(rng), _clipped_ell_comp(rng)),
95+
einstein_radius=float(rng.uniform(0.2, 1.8)),
5896
)
59-
mass.einstein_radius = af.UniformPrior(lower_limit=0.2, upper_limit=1.8)
6097

61-
shear = af.Model(al.mp.ExternalShear)
62-
63-
shear.gamma_1 = af.GaussianPrior(mean=0.0, sigma=0.05)
64-
shear.gamma_2 = af.GaussianPrior(mean=0.0, sigma=0.05)
98+
shear = al.mp.ExternalShear(
99+
gamma_1=float(rng.normal(0.0, 0.05)),
100+
gamma_2=float(rng.normal(0.0, 0.05)),
101+
)
65102

66-
lens = af.Model(al.Galaxy, redshift=0.5, bulge=bulge, mass=mass, shear=shear)
103+
lens = al.Galaxy(redshift=0.5, bulge=lens_bulge, mass=mass, shear=shear)
67104

68105
if use_point_source:
69-
70-
point_0 = af.Model(al.ps.PointFlux)
71-
72-
point_0.centre.centre_0 = af.GaussianPrior(mean=0.0, sigma=0.3)
73-
point_0.centre.centre_1 = af.GaussianPrior(mean=0.0, sigma=0.3)
74-
point_0.flux = af.UniformPrior(lower_limit=0.0, upper_limit=2.0)
75-
76-
source = af.Model(al.Galaxy, redshift=1.0, point_0=point_0)
77-
78-
else:
79-
80-
bulge = af.Model(al.lp_snr.Sersic)
81-
82-
bulge.centre_0 = af.GaussianPrior(mean=0.0, sigma=0.3)
83-
bulge.centre_1 = af.GaussianPrior(mean=0.0, sigma=0.3)
84-
bulge.ell_comps.ell_comps_0 = af.TruncatedGaussianPrior(
85-
mean=0.0, sigma=0.2, lower_limit=-1.0, upper_limit=1.0
86-
)
87-
bulge.ell_comps.ell_comps_1 = af.TruncatedGaussianPrior(
88-
mean=0.0, sigma=0.2, lower_limit=-1.0, upper_limit=1.0
89-
)
90-
bulge.signal_to_noise_ratio = af.UniformPrior(
91-
lower_limit=10.0, upper_limit=30.0
106+
point_0 = al.ps.PointFlux(
107+
centre=(float(rng.normal(0.0, 0.3)), float(rng.normal(0.0, 0.3))),
108+
flux=float(rng.uniform(0.0, 2.0)),
92109
)
93-
bulge.effective_radius = af.UniformPrior(lower_limit=0.01, upper_limit=3.0)
94-
bulge.sersic_index = af.TruncatedGaussianPrior(
95-
mean=2.0, sigma=0.5, lower_limit=0.8, upper_limit=5.0
110+
source = al.Galaxy(redshift=1.0, point_0=point_0)
111+
else:
112+
source_bulge = al.lp_snr.Sersic(
113+
centre=(float(rng.normal(0.0, 0.3)), float(rng.normal(0.0, 0.3))),
114+
ell_comps=(_clipped_ell_comp(rng), _clipped_ell_comp(rng)),
115+
effective_radius=float(rng.uniform(0.01, 3.0)),
116+
sersic_index=float(rng.uniform(1.5, 2.5)),
117+
signal_to_noise_ratio=float(rng.uniform(10.0, 30.0)),
96118
)
97-
98-
source = af.Model(al.Galaxy, redshift=1.0, bulge=bulge)
119+
source = al.Galaxy(redshift=1.0, bulge=source_bulge)
99120

100121
return lens, source

0 commit comments

Comments
 (0)