diff --git a/notebooks/guides/results/latent_variables.ipynb b/notebooks/guides/results/latent_variables.ipynb index 28950757..876eae0c 100644 --- a/notebooks/guides/results/latent_variables.ipynb +++ b/notebooks/guides/results/latent_variables.ipynb @@ -232,15 +232,16 @@ "\n", "The library catalogue is intentionally small. If you want a different derived quantity \u2014 a Sersic effective\n", "radius converted to kiloparsecs, a colour from two-band fits, a velocity dispersion from a virial estimate \u2014\n", - "you subclass ``ag.AnalysisImaging`` and override ``LATENT_KEYS`` + ``compute_latent_variables``.\n", + "subclass ``ag.LatentGalaxy`` (override ``keys`` and ``variables``) and declare it on your analysis through the\n", + "``Latent`` class attribute. This is the same first-class, swappable mechanism you use to customise a\n", + "``Visualizer`` \u2014 the latent catalogue is a component of the analysis, not a pair of methods you monkey-patch.\n", "\n", "The example below adds ``bulge_axis_ratio`` (the axis ratio of the Sersic bulge, computed from its\n", - "``ell_comps`` parameters). The same pattern works for any function of the model instance.\n", - "\n", - "A subclass-level ``LATENT_KEYS`` shadows the library's config-driven ``@property``, so once you define your\n", - "own list the workspace yaml stops controlling it. If you want to keep the library latents AND add your own,\n", - "make ``LATENT_KEYS`` a property that returns ``super().LATENT_KEYS + [\"your.key\"]`` \u2014 the Euclid pipeline\n", - "(``euclid_strong_lens_modeling_pipeline/util.py``) shows this pattern in practice.\n", + "``ell_comps`` parameters). The same pattern works for any function of the model instance. Calling\n", + "``ag.LatentGalaxy.keys(analysis)`` / ``ag.LatentGalaxy.variables(...)`` from your overrides keeps the\n", + "config-driven library latents alongside your custom one (subclass the base ``ag.Latent`` instead if you want a\n", + "catalogue from scratch). The Euclid pipeline (``euclid_strong_lens_modeling_pipeline/util.py``, ``LatentEuclid``)\n", + "uses this exact composition pattern in production.\n", "'''" ], "outputs": [], @@ -254,30 +255,24 @@ "import numpy as np\n", "\n", "\n", - "class AnalysisImagingWithAxisRatio(ag.AnalysisImaging):\n", + "class LatentBulgeAxisRatio(ag.LatentGalaxy):\n", " \"\"\"\n", - " AnalysisImaging extended with a custom ``bulge_axis_ratio`` latent \u2014 the axis ratio of the Sersic\n", - " bulge derived from its ``ell_comps``. Demonstrates how to add a user-defined latent without modifying\n", - " the library.\n", + " The library galaxy latents plus a custom ``bulge_axis_ratio`` \u2014 the axis\n", + " ratio of the Sersic bulge derived from its ``ell_comps``. Demonstrates adding\n", + " a user-defined latent without modifying the library: subclass\n", + " ``ag.LatentGalaxy`` and compose its ``keys`` / ``variables`` static methods.\n", " \"\"\"\n", "\n", - " @property\n", - " def LATENT_KEYS(self):\n", - " return list(super().LATENT_KEYS) + [\"bulge_axis_ratio\"]\n", + " @staticmethod\n", + " def keys(analysis):\n", + " return list(ag.LatentGalaxy.keys(analysis)) + [\"bulge_axis_ratio\"]\n", "\n", - " def compute_latent_variables(self, parameters, model):\n", - " from autogalaxy.imaging.model.latent import LATENT_FUNCTIONS\n", + " @staticmethod\n", + " def variables(analysis, parameters, model):\n", + " library_values = ag.LatentGalaxy.variables(analysis, parameters, model)\n", "\n", - " xp = self._xp\n", - " magzero = self.kwargs.get(\"magzero\", None)\n", " instance = model.instance_from_vector(vector=parameters)\n", - " fit = self.fit_from(instance=instance)\n", - " context = {\"fit\": fit, \"magzero\": magzero, \"xp\": xp}\n", - "\n", - " library_keys = [k for k in super().LATENT_KEYS]\n", - " library_values = tuple(LATENT_FUNCTIONS[k](**context) for k in library_keys)\n", - "\n", - " # Custom latent: axis ratio of the Sersic bulge from its ell_comps.\n", + " xp = analysis._xp\n", " try:\n", " ell_y, ell_x = instance.galaxies.galaxy.bulge.ell_comps\n", " axis_ratio = (1.0 - np.sqrt(ell_y**2 + ell_x**2)) / (\n", @@ -286,7 +281,17 @@ " except AttributeError:\n", " axis_ratio = xp.nan\n", "\n", - " return library_values + (axis_ratio,)\n" + " return library_values + (axis_ratio,)\n", + "\n", + "\n", + "class AnalysisImagingWithAxisRatio(ag.AnalysisImaging):\n", + " \"\"\"\n", + " ``AnalysisImaging`` that swaps in the custom ``LatentBulgeAxisRatio`` catalogue\n", + " via the ``Latent`` class attribute \u2014 the same one-line mechanism used to\n", + " declare a custom ``Visualizer`` or ``Result``. No library code is modified.\n", + " \"\"\"\n", + "\n", + " Latent = LatentBulgeAxisRatio\n" ], "outputs": [], "execution_count": null diff --git a/notebooks/guides/results/workflow/csv_make.ipynb b/notebooks/guides/results/workflow/csv_make.ipynb index c1462481..993464c4 100644 --- a/notebooks/guides/results/workflow/csv_make.ipynb +++ b/notebooks/guides/results/workflow/csv_make.ipynb @@ -156,55 +156,23 @@ " n_like_max=300, # samples capped for quick result generation\n", " )\n", "\n", - " class AnalysisLatent(ag.AnalysisImaging):\n", - "\n", - " LATENT_KEYS = [\"galaxies.galaxy.bulge.sersic_index_x2\"]\n", - "\n", - " def compute_latent_variables(self, parameters, model):\n", - " \"\"\"\n", - " A latent variable is not a model parameter but can be derived from the model. Its value and errors may be\n", - " of interest and aid in the interpretation of a model-fit.\n", - "\n", - " This code implements a simple example of a latent variable, the magn\n", - "\n", - " By overwriting this method we can manually specify latent variables that are calculated and output to\n", - " a `latent.csv` file, which mirrors the `samples.csv` file.\n", - "\n", - " In the example below, the `latent.csv` file will contain at least two columns with the shear magnitude and\n", - " angle sampled by the non-linear search.\n", - "\n", - " You can add your own custom latent variables here, if you have particular quantities that you\n", - " would like to output to the `latent.csv` file.\n", + " class LatentSersicIndex(ag.Latent):\n", + " \"\"\"Custom catalogue replacing library defaults; subclass ag.Latent (base) and override keys/variables;\n", + " declare via Latent = LatentSersicIndex; note: subclass ag.LatentGalaxy instead to keep library latents.\"\"\"\n", "\n", - " This function is called at the end of search, following one of two schemes depending on the settings in\n", - " `output.yaml`:\n", + " @staticmethod\n", + " def keys(analysis):\n", + " return [\"galaxies.galaxy.bulge.sersic_index_x2\"]\n", "\n", - " 1) Call for every search sample, which produces a complete `latent/samples.csv` which mirrors the normal\n", - " `samples.csv` file but takes a long time to compute.\n", - "\n", - " 2) Call only for N random draws from the posterior inferred at the end of the search, which only produces a\n", - " `latent/latent_summary.json` file with the median and 1 and 3 sigma errors of the latent variables but is\n", - " fast to compute.\n", - "\n", - "\n", - " Parameters\n", - " ----------\n", - " parameters : array-like\n", - " The parameter vector of the model sample. This will typically come from the non-linear search.\n", - " Inside this method it is mapped back to a model instance via `model.instance_from_vector`.\n", - " model : Model\n", - " The model object defining how the parameter vector is mapped to an instance. Passed explicitly\n", - " so that this function can be used inside JAX transforms (`vmap`, `jit`) with `functools.partial`.\n", - "\n", - " Returns\n", - " -------\n", - " A dictionary mapping every latent variable name to its value.\n", - "\n", - " \"\"\"\n", + " @staticmethod\n", + " def variables(analysis, parameters, model):\n", " instance = model.instance_from_vector(vector=parameters)\n", "\n", " return (instance.galaxies.galaxy.bulge.sersic_index * 2.0,)\n", "\n", + " class AnalysisLatent(ag.AnalysisImaging):\n", + " Latent = LatentSersicIndex\n", + "\n", " analysis = AnalysisLatent(dataset=dataset)\n", "\n", " result = search.fit(model=model, analysis=analysis)" diff --git a/notebooks/guides/results/workflow/fits_make.ipynb b/notebooks/guides/results/workflow/fits_make.ipynb index 742368de..322a6fba 100644 --- a/notebooks/guides/results/workflow/fits_make.ipynb +++ b/notebooks/guides/results/workflow/fits_make.ipynb @@ -167,53 +167,23 @@ " n_like_max=300, # samples capped for quick result generation\n", " )\n", "\n", - " class AnalysisLatent(ag.AnalysisImaging):\n", - " LATENT_KEYS = [\"galaxies.galaxy.bulge.sersic_index\"]\n", - "\n", - " def compute_latent_variables(self, parameters, model):\n", - " \"\"\"\n", - " A latent variable is not a model parameter but can be derived from the model. Its value and errors may be\n", - " of interest and aid in the interpretation of a model-fit.\n", - "\n", - " This code implements a simple example of a latent variable, the magn\n", - "\n", - " By overwriting this method we can manually specify latent variables that are calculated and output to\n", - " a `latent.csv` file, which mirrors the `samples.csv` file.\n", - "\n", - " In the example below, the `latent.csv` file will contain at least two columns with the shear magnitude and\n", - " angle sampled by the non-linear search.\n", - "\n", - " You can add your own custom latent variables here, if you have particular quantities that you\n", - " would like to output to the `latent.csv` file.\n", + " class LatentSersicIndex(ag.Latent):\n", + " \"\"\"Custom catalogue replacing library defaults; subclass ag.Latent (base) and override keys/variables;\n", + " declare via Latent = LatentSersicIndex; note: subclass ag.LatentGalaxy instead to keep library latents.\"\"\"\n", "\n", - " This function is called at the end of search, following one of two schemes depending on the settings in\n", - " `output.yaml`:\n", + " @staticmethod\n", + " def keys(analysis):\n", + " return [\"galaxies.galaxy.bulge.sersic_index\"]\n", "\n", - " 1) Call for every search sample, which produces a complete `latent/samples.csv` which mirrors the normal\n", - " `samples.csv` file but takes a long time to compute.\n", - "\n", - " 2) Call only for N random draws from the posterior inferred at the end of the search, which only produces a\n", - " `latent/latent_summary.json` file with the median and 1 and 3 sigma errors of the latent variables but is\n", - " fast to compute.\n", - "\n", - " Parameters\n", - " ----------\n", - " parameters : array-like\n", - " The parameter vector of the model sample. This will typically come from the non-linear search.\n", - " Inside this method it is mapped back to a model instance via `model.instance_from_vector`.\n", - " model : Model\n", - " The model object defining how the parameter vector is mapped to an instance. Passed explicitly\n", - " so that this function can be used inside JAX transforms (`vmap`, `jit`) with `functools.partial`.\n", - "\n", - " Returns\n", - " -------\n", - " A dictionary mapping every latent variable name to its value.\n", - "\n", - " \"\"\"\n", + " @staticmethod\n", + " def variables(analysis, parameters, model):\n", " instance = model.instance_from_vector(vector=parameters)\n", "\n", " return (instance.galaxies.galaxy.bulge.sersic_index * 2.0,)\n", "\n", + " class AnalysisLatent(ag.AnalysisImaging):\n", + " Latent = LatentSersicIndex\n", + "\n", " analysis = AnalysisLatent(dataset=dataset)\n", "\n", " result = search.fit(model=model, analysis=analysis)" diff --git a/notebooks/guides/results/workflow/png_make.ipynb b/notebooks/guides/results/workflow/png_make.ipynb index faa6ac3c..923cffa2 100644 --- a/notebooks/guides/results/workflow/png_make.ipynb +++ b/notebooks/guides/results/workflow/png_make.ipynb @@ -172,53 +172,23 @@ " n_like_max=300, # samples capped for quick result generation\n", " )\n", "\n", - " class AnalysisLatent(ag.AnalysisImaging):\n", - " LATENT_KEYS = [\"galaxies.galaxy.bulge.sersic_index\"]\n", - "\n", - " def compute_latent_variables(self, parameters, model):\n", - " \"\"\"\n", - " A latent variable is not a model parameter but can be derived from the model. Its value and errors may be\n", - " of interest and aid in the interpretation of a model-fit.\n", - "\n", - " This code implements a simple example of a latent variable, the magn\n", - "\n", - " By overwriting this method we can manually specify latent variables that are calculated and output to\n", - " a `latent.csv` file, which mirrors the `samples.csv` file.\n", - "\n", - " In the example below, the `latent.csv` file will contain at least two columns with the shear magnitude and\n", - " angle sampled by the non-linear search.\n", - "\n", - " You can add your own custom latent variables here, if you have particular quantities that you\n", - " would like to output to the `latent.csv` file.\n", + " class LatentSersicIndex(ag.Latent):\n", + " \"\"\"Custom catalogue replacing library defaults; subclass ag.Latent (base) and override keys/variables;\n", + " declare via Latent = LatentSersicIndex; note: subclass ag.LatentGalaxy instead to keep library latents.\"\"\"\n", "\n", - " This function is called at the end of search, following one of two schemes depending on the settings in\n", - " `output.yaml`:\n", + " @staticmethod\n", + " def keys(analysis):\n", + " return [\"galaxies.galaxy.bulge.sersic_index\"]\n", "\n", - " 1) Call for every search sample, which produces a complete `latent/samples.csv` which mirrors the normal\n", - " `samples.csv` file but takes a long time to compute.\n", - "\n", - " 2) Call only for N random draws from the posterior inferred at the end of the search, which only produces a\n", - " `latent/latent_summary.json` file with the median and 1 and 3 sigma errors of the latent variables but is\n", - " fast to compute.\n", - "\n", - " Parameters\n", - " ----------\n", - " parameters : array-like\n", - " The parameter vector of the model sample. This will typically come from the non-linear search.\n", - " Inside this method it is mapped back to a model instance via `model.instance_from_vector`.\n", - " model : Model\n", - " The model object defining how the parameter vector is mapped to an instance. Passed explicitly\n", - " so that this function can be used inside JAX transforms (`vmap`, `jit`) with `functools.partial`.\n", - "\n", - " Returns\n", - " -------\n", - " A dictionary mapping every latent variable name to its value.\n", - "\n", - " \"\"\"\n", + " @staticmethod\n", + " def variables(analysis, parameters, model):\n", " instance = model.instance_from_vector(vector=parameters)\n", "\n", " return (instance.galaxies.galaxy.bulge.sersic_index * 2.0,)\n", "\n", + " class AnalysisLatent(ag.AnalysisImaging):\n", + " Latent = LatentSersicIndex\n", + "\n", " analysis = AnalysisLatent(dataset=dataset)\n", "\n", " result = search.fit(model=model, analysis=analysis)\n" diff --git a/scripts/guides/results/latent_variables.py b/scripts/guides/results/latent_variables.py index 7824f95a..ea99e364 100644 --- a/scripts/guides/results/latent_variables.py +++ b/scripts/guides/results/latent_variables.py @@ -170,44 +170,39 @@ The library catalogue is intentionally small. If you want a different derived quantity — a Sersic effective radius converted to kiloparsecs, a colour from two-band fits, a velocity dispersion from a virial estimate — -you subclass ``ag.AnalysisImaging`` and override ``LATENT_KEYS`` + ``compute_latent_variables``. +subclass ``ag.LatentGalaxy`` (override ``keys`` and ``variables``) and declare it on your analysis through the +``Latent`` class attribute. This is the same first-class, swappable mechanism you use to customise a +``Visualizer`` — the latent catalogue is a component of the analysis, not a pair of methods you monkey-patch. The example below adds ``bulge_axis_ratio`` (the axis ratio of the Sersic bulge, computed from its -``ell_comps`` parameters). The same pattern works for any function of the model instance. - -A subclass-level ``LATENT_KEYS`` shadows the library's config-driven ``@property``, so once you define your -own list the workspace yaml stops controlling it. If you want to keep the library latents AND add your own, -make ``LATENT_KEYS`` a property that returns ``super().LATENT_KEYS + ["your.key"]`` — the Euclid pipeline -(``euclid_strong_lens_modeling_pipeline/util.py``) shows this pattern in practice. +``ell_comps`` parameters). The same pattern works for any function of the model instance. Calling +``ag.LatentGalaxy.keys(analysis)`` / ``ag.LatentGalaxy.variables(...)`` from your overrides keeps the +config-driven library latents alongside your custom one (subclass the base ``ag.Latent`` instead if you want a +catalogue from scratch). The Euclid pipeline (``euclid_strong_lens_modeling_pipeline/util.py``, ``LatentEuclid``) +uses this exact composition pattern in production. """ import numpy as np -class AnalysisImagingWithAxisRatio(ag.AnalysisImaging): +class LatentBulgeAxisRatio(ag.LatentGalaxy): """ - AnalysisImaging extended with a custom ``bulge_axis_ratio`` latent — the axis ratio of the Sersic - bulge derived from its ``ell_comps``. Demonstrates how to add a user-defined latent without modifying - the library. + The library galaxy latents plus a custom ``bulge_axis_ratio`` — the axis + ratio of the Sersic bulge derived from its ``ell_comps``. Demonstrates adding + a user-defined latent without modifying the library: subclass + ``ag.LatentGalaxy`` and compose its ``keys`` / ``variables`` static methods. """ - @property - def LATENT_KEYS(self): - return list(super().LATENT_KEYS) + ["bulge_axis_ratio"] + @staticmethod + def keys(analysis): + return list(ag.LatentGalaxy.keys(analysis)) + ["bulge_axis_ratio"] - def compute_latent_variables(self, parameters, model): - from autogalaxy.imaging.model.latent import LATENT_FUNCTIONS + @staticmethod + def variables(analysis, parameters, model): + library_values = ag.LatentGalaxy.variables(analysis, parameters, model) - xp = self._xp - magzero = self.kwargs.get("magzero", None) instance = model.instance_from_vector(vector=parameters) - fit = self.fit_from(instance=instance) - context = {"fit": fit, "magzero": magzero, "xp": xp} - - library_keys = [k for k in super().LATENT_KEYS] - library_values = tuple(LATENT_FUNCTIONS[k](**context) for k in library_keys) - - # Custom latent: axis ratio of the Sersic bulge from its ell_comps. + xp = analysis._xp try: ell_y, ell_x = instance.galaxies.galaxy.bulge.ell_comps axis_ratio = (1.0 - np.sqrt(ell_y**2 + ell_x**2)) / ( @@ -219,6 +214,16 @@ def compute_latent_variables(self, parameters, model): return library_values + (axis_ratio,) +class AnalysisImagingWithAxisRatio(ag.AnalysisImaging): + """ + ``AnalysisImaging`` that swaps in the custom ``LatentBulgeAxisRatio`` catalogue + via the ``Latent`` class attribute — the same one-line mechanism used to + declare a custom ``Visualizer`` or ``Result``. No library code is modified. + """ + + Latent = LatentBulgeAxisRatio + + """ With the subclass defined, running a fit that uses it produces a ``latent.csv`` with one extra column (``bulge_axis_ratio``) on top of the library defaults. We don't actually run a second fit here — the pattern diff --git a/scripts/guides/results/workflow/csv_make.py b/scripts/guides/results/workflow/csv_make.py index 51e4b4d6..3dbd26c2 100644 --- a/scripts/guides/results/workflow/csv_make.py +++ b/scripts/guides/results/workflow/csv_make.py @@ -135,55 +135,23 @@ n_like_max=300, # samples capped for quick result generation ) - class AnalysisLatent(ag.AnalysisImaging): - - LATENT_KEYS = ["galaxies.galaxy.bulge.sersic_index_x2"] - - def compute_latent_variables(self, parameters, model): - """ - A latent variable is not a model parameter but can be derived from the model. Its value and errors may be - of interest and aid in the interpretation of a model-fit. - - This code implements a simple example of a latent variable, the magn - - By overwriting this method we can manually specify latent variables that are calculated and output to - a `latent.csv` file, which mirrors the `samples.csv` file. - - In the example below, the `latent.csv` file will contain at least two columns with the shear magnitude and - angle sampled by the non-linear search. - - You can add your own custom latent variables here, if you have particular quantities that you - would like to output to the `latent.csv` file. + class LatentSersicIndex(ag.Latent): + """Custom catalogue replacing library defaults; subclass ag.Latent (base) and override keys/variables; + declare via Latent = LatentSersicIndex; note: subclass ag.LatentGalaxy instead to keep library latents.""" - This function is called at the end of search, following one of two schemes depending on the settings in - `output.yaml`: + @staticmethod + def keys(analysis): + return ["galaxies.galaxy.bulge.sersic_index_x2"] - 1) Call for every search sample, which produces a complete `latent/samples.csv` which mirrors the normal - `samples.csv` file but takes a long time to compute. - - 2) Call only for N random draws from the posterior inferred at the end of the search, which only produces a - `latent/latent_summary.json` file with the median and 1 and 3 sigma errors of the latent variables but is - fast to compute. - - - Parameters - ---------- - parameters : array-like - The parameter vector of the model sample. This will typically come from the non-linear search. - Inside this method it is mapped back to a model instance via `model.instance_from_vector`. - model : Model - The model object defining how the parameter vector is mapped to an instance. Passed explicitly - so that this function can be used inside JAX transforms (`vmap`, `jit`) with `functools.partial`. - - Returns - ------- - A dictionary mapping every latent variable name to its value. - - """ + @staticmethod + def variables(analysis, parameters, model): instance = model.instance_from_vector(vector=parameters) return (instance.galaxies.galaxy.bulge.sersic_index * 2.0,) + class AnalysisLatent(ag.AnalysisImaging): + Latent = LatentSersicIndex + analysis = AnalysisLatent(dataset=dataset) result = search.fit(model=model, analysis=analysis) diff --git a/scripts/guides/results/workflow/fits_make.py b/scripts/guides/results/workflow/fits_make.py index 5409825d..ed9588e7 100644 --- a/scripts/guides/results/workflow/fits_make.py +++ b/scripts/guides/results/workflow/fits_make.py @@ -146,53 +146,23 @@ n_like_max=300, # samples capped for quick result generation ) - class AnalysisLatent(ag.AnalysisImaging): - LATENT_KEYS = ["galaxies.galaxy.bulge.sersic_index"] - - def compute_latent_variables(self, parameters, model): - """ - A latent variable is not a model parameter but can be derived from the model. Its value and errors may be - of interest and aid in the interpretation of a model-fit. - - This code implements a simple example of a latent variable, the magn - - By overwriting this method we can manually specify latent variables that are calculated and output to - a `latent.csv` file, which mirrors the `samples.csv` file. - - In the example below, the `latent.csv` file will contain at least two columns with the shear magnitude and - angle sampled by the non-linear search. - - You can add your own custom latent variables here, if you have particular quantities that you - would like to output to the `latent.csv` file. + class LatentSersicIndex(ag.Latent): + """Custom catalogue replacing library defaults; subclass ag.Latent (base) and override keys/variables; + declare via Latent = LatentSersicIndex; note: subclass ag.LatentGalaxy instead to keep library latents.""" - This function is called at the end of search, following one of two schemes depending on the settings in - `output.yaml`: + @staticmethod + def keys(analysis): + return ["galaxies.galaxy.bulge.sersic_index"] - 1) Call for every search sample, which produces a complete `latent/samples.csv` which mirrors the normal - `samples.csv` file but takes a long time to compute. - - 2) Call only for N random draws from the posterior inferred at the end of the search, which only produces a - `latent/latent_summary.json` file with the median and 1 and 3 sigma errors of the latent variables but is - fast to compute. - - Parameters - ---------- - parameters : array-like - The parameter vector of the model sample. This will typically come from the non-linear search. - Inside this method it is mapped back to a model instance via `model.instance_from_vector`. - model : Model - The model object defining how the parameter vector is mapped to an instance. Passed explicitly - so that this function can be used inside JAX transforms (`vmap`, `jit`) with `functools.partial`. - - Returns - ------- - A dictionary mapping every latent variable name to its value. - - """ + @staticmethod + def variables(analysis, parameters, model): instance = model.instance_from_vector(vector=parameters) return (instance.galaxies.galaxy.bulge.sersic_index * 2.0,) + class AnalysisLatent(ag.AnalysisImaging): + Latent = LatentSersicIndex + analysis = AnalysisLatent(dataset=dataset) result = search.fit(model=model, analysis=analysis) diff --git a/scripts/guides/results/workflow/png_make.py b/scripts/guides/results/workflow/png_make.py index b742fa6d..e995368e 100644 --- a/scripts/guides/results/workflow/png_make.py +++ b/scripts/guides/results/workflow/png_make.py @@ -151,53 +151,23 @@ n_like_max=300, # samples capped for quick result generation ) - class AnalysisLatent(ag.AnalysisImaging): - LATENT_KEYS = ["galaxies.galaxy.bulge.sersic_index"] - - def compute_latent_variables(self, parameters, model): - """ - A latent variable is not a model parameter but can be derived from the model. Its value and errors may be - of interest and aid in the interpretation of a model-fit. - - This code implements a simple example of a latent variable, the magn - - By overwriting this method we can manually specify latent variables that are calculated and output to - a `latent.csv` file, which mirrors the `samples.csv` file. - - In the example below, the `latent.csv` file will contain at least two columns with the shear magnitude and - angle sampled by the non-linear search. - - You can add your own custom latent variables here, if you have particular quantities that you - would like to output to the `latent.csv` file. + class LatentSersicIndex(ag.Latent): + """Custom catalogue replacing library defaults; subclass ag.Latent (base) and override keys/variables; + declare via Latent = LatentSersicIndex; note: subclass ag.LatentGalaxy instead to keep library latents.""" - This function is called at the end of search, following one of two schemes depending on the settings in - `output.yaml`: + @staticmethod + def keys(analysis): + return ["galaxies.galaxy.bulge.sersic_index"] - 1) Call for every search sample, which produces a complete `latent/samples.csv` which mirrors the normal - `samples.csv` file but takes a long time to compute. - - 2) Call only for N random draws from the posterior inferred at the end of the search, which only produces a - `latent/latent_summary.json` file with the median and 1 and 3 sigma errors of the latent variables but is - fast to compute. - - Parameters - ---------- - parameters : array-like - The parameter vector of the model sample. This will typically come from the non-linear search. - Inside this method it is mapped back to a model instance via `model.instance_from_vector`. - model : Model - The model object defining how the parameter vector is mapped to an instance. Passed explicitly - so that this function can be used inside JAX transforms (`vmap`, `jit`) with `functools.partial`. - - Returns - ------- - A dictionary mapping every latent variable name to its value. - - """ + @staticmethod + def variables(analysis, parameters, model): instance = model.instance_from_vector(vector=parameters) return (instance.galaxies.galaxy.bulge.sersic_index * 2.0,) + class AnalysisLatent(ag.AnalysisImaging): + Latent = LatentSersicIndex + analysis = AnalysisLatent(dataset=dataset) result = search.fit(model=model, analysis=analysis)