|
13 | 13 | - ``hilbert_pixels_from_pixel_scale`` — estimate Hilbert image-mesh pixel count. |
14 | 14 |
|
15 | 15 | 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. |
18 | 18 | """ |
19 | | -import autofit as af |
| 19 | +from typing import Optional, Tuple |
| 20 | + |
| 21 | +import numpy as np |
| 22 | + |
20 | 23 | import autolens as al |
21 | 24 |
|
22 | 25 | from autogalaxy.analysis.model_util import mge_model_from |
23 | 26 | from autogalaxy.analysis.model_util import mge_point_model_from |
24 | 27 | from autogalaxy.analysis.model_util import hilbert_pixels_from_pixel_scale |
25 | 28 |
|
26 | | -def simulator_start_here_model_from( |
27 | | - include_lens_light: bool = True, use_point_source: bool = False |
28 | | -): |
29 | 29 |
|
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() |
32 | 80 |
|
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)), |
46 | 88 | ) |
47 | 89 | else: |
48 | | - bulge = None |
49 | | - |
50 | | - mass = af.Model(al.mp.Isothermal) |
| 90 | + lens_bulge = None |
51 | 91 |
|
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)), |
58 | 96 | ) |
59 | | - mass.einstein_radius = af.UniformPrior(lower_limit=0.2, upper_limit=1.8) |
60 | 97 |
|
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 | + ) |
65 | 102 |
|
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) |
67 | 104 |
|
68 | 105 | 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)), |
92 | 109 | ) |
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)), |
96 | 118 | ) |
97 | | - |
98 | | - source = af.Model(al.Galaxy, redshift=1.0, bulge=bulge) |
| 119 | + source = al.Galaxy(redshift=1.0, bulge=source_bulge) |
99 | 120 |
|
100 | 121 | return lens, source |
0 commit comments