|
24 | 24 | "\n", |
25 | 25 | " - Galaxy Latents in PyAutoGalaxy: The curated default catalogue from ``autogalaxy/config/latent.yaml``.\n", |
26 | 26 | " - Toggling Latents: The workspace ``config/latent.yaml`` override and why it shadows the library default.\n", |
27 | | - " - Model Fit: A quick fit that produces real latent output for the loading section below.\n", |
28 | | - " - Loading Latent Results: Reading ``latent/samples.csv`` / ``latent_summary.json`` via\n", |
29 | | - " ``analysis.compute_latent_samples(result.samples)`` and the standard ``Samples`` API.\n", |
| 27 | + " - Model Fit: Reuse the shared quick fit (``_quick_fit.py``) that produces real latent output.\n", |
| 28 | + " - Loading Latent Results: ``analysis.compute_latent_samples`` over a subset of PDF draws, and the two\n", |
| 29 | + " config surfaces (``latent.yaml`` / ``output.yaml``) that control which latents and how many draws.\n", |
30 | 30 | " - Extending with a Custom Latent: Subclass ``ag.AnalysisImaging``, override ``LATENT_KEYS`` and\n", |
31 | 31 | " ``compute_latent_variables`` to add your own derived quantity.\n", |
32 | 32 | " - Contributing Upstream: When your custom latent is general enough, consider promoting it to the library." |
|
115 | 115 | "'''\n", |
116 | 116 | "__Model Fit__\n", |
117 | 117 | "\n", |
118 | | - "To make the loading and extending sections below concrete, we run a quick model fit on the standard simple-\n", |
119 | | - "imaging dataset that ships with the workspace. The model is a single galaxy with a Sersic bulge \u2014 keeping it\n", |
120 | | - "small so the example runs in a reasonable time.\n", |
| 118 | + "The loading and extending sections below need a completed fit to read latents from. Rather than run a bespoke\n", |
| 119 | + "fit here, we reuse the shared quick fit that the other results guides use: ``_quick_fit.py`` writes a capped\n", |
| 120 | + "single-galaxy (Sersic bulge + Exponential disk) fit of the standard ``simple`` imaging dataset to\n", |
| 121 | + "``output/results_folder/``. It is idempotent \u2014 it returns immediately if those results already exist \u2014 so the\n", |
| 122 | + "non-linear search is paid once across the whole guide suite rather than repeated in every example.\n", |
121 | 123 | "\n", |
122 | | - "We pass ``magzero=25.0`` to ``ag.AnalysisImaging`` so ``total_galaxy_0_flux_mujy`` populates with a real value\n", |
123 | | - "rather than NaN. If you forget it, ``total_galaxy_0_flux_mujy`` will be NaN and the library will log a single\n", |
124 | | - "warning per process noting the conversion was skipped \u2014 the fit itself is unaffected, and the raw\n", |
125 | | - "``total_galaxy_0_flux`` column populates normally.\n", |
| 124 | + "We then load that fit's samples via the aggregator, exactly as ``start_here.py`` and ``aggregator/models.py`` do.\n", |
126 | 125 | "'''" |
127 | 126 | ], |
128 | 127 | "outputs": [], |
|
132 | 131 | "cell_type": "code", |
133 | 132 | "metadata": {}, |
134 | 133 | "source": [ |
135 | | - "dataset_name = \"simple\"\n", |
136 | | - "dataset_path = Path(\"dataset\") / \"imaging\" / dataset_name\n", |
| 134 | + "import subprocess\n", |
| 135 | + "import sys\n", |
| 136 | + "\n", |
| 137 | + "subprocess.run(\n", |
| 138 | + " [sys.executable, \"scripts/guides/results/_quick_fit.py\"],\n", |
| 139 | + " check=True,\n", |
| 140 | + ")\n", |
137 | 141 | "\n", |
138 | | - "if not dataset_path.exists():\n", |
139 | | - " import subprocess\n", |
140 | | - " import sys\n", |
| 142 | + "from autofit.aggregator.aggregator import Aggregator\n", |
141 | 143 | "\n", |
142 | | - " subprocess.run(\n", |
143 | | - " [sys.executable, \"scripts/imaging/simulator.py\"],\n", |
144 | | - " check=True,\n", |
145 | | - " )\n", |
| 144 | + "agg = Aggregator.from_directory(directory=Path(\"output\") / \"results_folder\")\n", |
| 145 | + "samples = list(agg)[0].samples" |
| 146 | + ], |
| 147 | + "outputs": [], |
| 148 | + "execution_count": null |
| 149 | + }, |
| 150 | + { |
| 151 | + "cell_type": "markdown", |
| 152 | + "metadata": {}, |
| 153 | + "source": [ |
| 154 | + "The samples carry the parameter posterior; the ``analysis`` carries the machinery that turns each posterior\n", |
| 155 | + "draw into latent values. We rebuild the dataset and an ``ag.AnalysisImaging`` with ``magzero=25.0`` so\n", |
| 156 | + "``total_galaxy_0_flux_mujy`` populates with a real value rather than NaN (without it that column is NaN and the\n", |
| 157 | + "library logs a single warning per process; the raw ``total_galaxy_0_flux`` column populates normally)." |
| 158 | + ] |
| 159 | + }, |
| 160 | + { |
| 161 | + "cell_type": "code", |
| 162 | + "metadata": {}, |
| 163 | + "source": [ |
| 164 | + "dataset_name = \"simple\"\n", |
| 165 | + "dataset_path = Path(\"dataset\") / \"imaging\" / dataset_name\n", |
146 | 166 | "\n", |
147 | 167 | "dataset = ag.Imaging.from_fits(\n", |
148 | 168 | " data_path=dataset_path / \"data.fits\",\n", |
|
158 | 178 | ")\n", |
159 | 179 | "dataset = dataset.apply_mask(mask=mask)\n", |
160 | 180 | "\n", |
161 | | - "galaxy_model = af.Model(ag.Galaxy, redshift=0.5, bulge=af.Model(ag.lp.Sersic))\n", |
162 | | - "model = af.Collection(galaxies=af.Collection(galaxy=galaxy_model))\n", |
163 | | - "\n", |
164 | | - "analysis = ag.AnalysisImaging(dataset=dataset, use_jax=False, magzero=25.0)\n", |
165 | | - "\n", |
166 | | - "search = af.Nautilus(\n", |
167 | | - " name=\"cookbook_latent_variables\",\n", |
168 | | - " n_live=50,\n", |
169 | | - " n_like_max=300,\n", |
170 | | - ")\n", |
171 | | - "\n", |
172 | | - "result = search.fit(model=model, analysis=analysis)" |
| 181 | + "analysis = ag.AnalysisImaging(dataset=dataset, use_jax=False, magzero=25.0)" |
173 | 182 | ], |
174 | 183 | "outputs": [], |
175 | 184 | "execution_count": null |
|
180 | 189 | "source": [ |
181 | 190 | "__Loading Latent Results__\n", |
182 | 191 | "\n", |
183 | | - "The fit above produces ``latent/samples.csv`` and ``latent/latent_summary.json`` under the search's output\n", |
184 | | - "directory. You can read them back into a ``Samples`` object via ``analysis.compute_latent_samples(result.samples)``.\n", |
185 | | - "The returned object exposes the same API as the parameter ``Samples`` \u2014 ``median_pdf``, ``max_log_likelihood``,\n", |
186 | | - "``values_at_sigma_1``, and so on \u2014 but reports on the induced latent posterior rather than the parameter posterior.\n", |
| 192 | + "``analysis.compute_latent_samples(samples)`` reads the posterior into a ``Samples`` object that exposes the same\n", |
| 193 | + "API as the parameter ``Samples`` \u2014 ``median_pdf``, ``max_log_likelihood``, ``values_at_sigma_1``, and so on \u2014 but\n", |
| 194 | + "reports on the induced latent posterior. Because both ``total_galaxy_0_flux`` and ``total_galaxy_0_flux_mujy`` are\n", |
| 195 | + "enabled in this workspace, the returned instance exposes both attributes; enabling additional latents in the\n", |
| 196 | + "workspace yaml makes them all appear as attributes on the same instance.\n", |
| 197 | + "\n", |
| 198 | + "__Controlling the Cost via Config__\n", |
| 199 | + "\n", |
| 200 | + "Latents are computed by reconstructing a fit for every posterior sample, so the cost scales with the number of\n", |
| 201 | + "samples. Two workspace config files control this:\n", |
| 202 | + "\n", |
| 203 | + " - ``config/latent.yaml`` \u2014 controls *which* latents are computed. Disable the ones you don't need.\n", |
| 204 | + "\n", |
| 205 | + " - ``config/output.yaml`` \u2014 ``latent_draw_via_pdf`` / ``latent_draw_via_pdf_size`` control *how many* posterior\n", |
| 206 | + " draws the latents are computed over when a live search updates. Drawing a representative subset from the PDF\n", |
| 207 | + " gives faithful latent errors at a fraction of the every-sample cost.\n", |
187 | 208 | "\n", |
188 | | - "Because both ``total_galaxy_0_flux`` and ``total_galaxy_0_flux_mujy`` are enabled in this workspace, the\n", |
189 | | - "returned instance exposes both attributes. If you enable additional latents in the workspace yaml, they all\n", |
190 | | - "appear as attributes on the same instance." |
| 209 | + "Here we mirror that draw-from-PDF behaviour explicitly with ``samples.samples_drawn_randomly_via_pdf_from``,\n", |
| 210 | + "computing the latents over 20 PDF draws so this guide runs quickly while still producing a real, representative\n", |
| 211 | + "latent posterior. For a publication-quality result, compute over all samples (or a larger number of draws)." |
191 | 212 | ] |
192 | 213 | }, |
193 | 214 | { |
194 | 215 | "cell_type": "code", |
195 | 216 | "metadata": {}, |
196 | 217 | "source": [ |
197 | | - "latent_samples = analysis.compute_latent_samples(result.samples)\n", |
| 218 | + "latent_draws = samples.samples_drawn_randomly_via_pdf_from(total_draws=20)\n", |
| 219 | + "latent_samples = analysis.compute_latent_samples(latent_draws)\n", |
198 | 220 | "\n", |
199 | 221 | "median_instance = latent_samples.median_pdf()\n", |
200 | 222 | "print(f\"Median PDF total_galaxy_0_flux: {median_instance.total_galaxy_0_flux}\")\n", |
|
0 commit comments