Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions autogalaxy/profiles/basis.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
When linear light profiles are used in a basis, their individual intensities are solved simultaneously via a
linear inversion (a single matrix solve), making the inference highly efficient regardless of how many basis
components are included.

A `Basis` declares no solved amplitude of its own (it has no `__solved_parameters__`):
the solved intensities belong to its linear member profiles, one per member, so a model
figure draws a `solved` `intensity` inside each member and none on the `Basis` itself.
"""

import numpy as np
Expand Down
13 changes: 11 additions & 2 deletions autogalaxy/profiles/light/linear/abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ class LightProfileLinear(LightProfile):
_pytree_token_counter = itertools.count()
__exclude_identifier_fields__ = ("pytree_token",)

# The `intensity` of a linear light profile is solved for by the inversion, not
# sampled by the non-linear search, so it has no prior and never appears in
# `model.info`. PyAutoFit's `graph_spec` reads this class attribute (the
# `__solved_parameters__` protocol) to draw `intensity` as a `solved` row in
# model figures. Subclasses (e.g. `lp_linear.Sersic`) inherit it.
__solved_parameters__ = ("intensity",)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.pytree_token = next(LightProfileLinear._pytree_token_counter)
Expand Down Expand Up @@ -259,12 +266,14 @@ def __init__(
"""
for light_profile in light_profile_list:
if not isinstance(light_profile, LightProfileLinear):
raise exc.ProfileException("""
raise exc.ProfileException(
"""
A light profile that is not a LightProfileLinear object has been input into the
LightProfileLinearObjFuncList object.

Only children of the LightProfileLinear class can be used in a linear inversion.
""")
"""
)

super().__init__(
grid=grid, regularization=regularization, settings=settings, xp=xp
Expand Down
6 changes: 5 additions & 1 deletion autogalaxy/profiles/point_sources.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,8 @@ class PointSolved:
and `autolens.point.fit.fluxes`).
"""

pass
# The `centre` is solved for analytically by the `*Solved` fit classes, not sampled
# by the non-linear search, so it has no prior and never appears in `model.info`.
# PyAutoFit's `graph_spec` reads this class attribute (the `__solved_parameters__`
# protocol) to draw `centre` as a `solved` row in model figures.
__solved_parameters__ = ("centre",)
Binary file added docs/general/images/model_cookbook/complex.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/general/images/model_cookbook/concise.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
191 changes: 191 additions & 0 deletions docs/general/images/model_cookbook/make_figures.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
"""
Regenerate the committed model-figure renders for the PyAutoGalaxy docs.

Run from the PyAutoGalaxy repo root, with the worktree environment active::

python docs/general/images/model_cookbook/make_figures.py

Every PNG is written at ``width=14.0`` inches and DPI 100, so none exceeds the
1400 px width budget of the ``model-figures`` epic.

One figure is written per ``print(model.info)`` stage of
``autogalaxy_workspace/scripts/guides/modeling/cookbook.py`` -- the models below
are that script's compositions -- and
``docs/overview/images/overview_3/mge_model.png`` is the multi-Gaussian
expansion model of ``scripts/imaging/features/multi_gaussian_expansion/modeling.py``.
"""

import sys
from pathlib import Path

import matplotlib

matplotlib.use("Agg")

REPO_ROOT = Path(__file__).resolve().parents[4]
sys.path.insert(0, str(REPO_ROOT))

import numpy as np

import autofit as af
import autogalaxy as ag

OUTPUT_PATH = Path(__file__).resolve().parent
OVERVIEW_PATH = REPO_ROOT / "docs" / "overview" / "images" / "overview_3"
WIDTH = 14.0


def write(model, name: str, path: Path = OUTPUT_PATH, **kwargs):
"""Render one model to ``<name>.png`` in ``path``."""
path.mkdir(parents=True, exist_ok=True)

af.ModelPlotter(model).figure(
path=str(path),
filename=name,
format="png",
width=WIDTH,
**kwargs,
)
print(f"wrote {path.name}/{name}.png")


# ---------------------------------------------------------------------------
# the cookbook stages -- `scripts/guides/modeling/cookbook.py`
# ---------------------------------------------------------------------------


def simple():
"""``__Simple Model__`` -- cookbook.py:44-48."""
bulge = af.Model(ag.lp_linear.Sersic)

galaxy = af.Model(ag.Galaxy, redshift=0.5, bulge=bulge)

return af.Collection(galaxies=af.Collection(galaxy=galaxy))


def complex_model():
"""``__More Complex Models__`` -- cookbook.py:68-76."""
bulge = af.Model(ag.lp_linear.Sersic)
disk = af.Model(ag.lp_linear.Exponential)
bar = af.Model(ag.lp_linear.Sersic)

galaxy = af.Model(ag.Galaxy, redshift=0.5, bulge=bulge, disk=disk, bar=bar)

return af.Collection(galaxies=af.Collection(galaxy=galaxy))


def two_galaxies():
"""``__More Complex Models__``, two galaxies -- cookbook.py:83-107."""
galaxy_0 = af.Model(ag.Galaxy, redshift=0.5, bulge=af.Model(ag.lp_linear.Sersic))
galaxy_1 = af.Model(ag.Galaxy, redshift=0.5, bulge=af.Model(ag.lp_linear.Sersic))

return af.Collection(galaxies=af.Collection(galaxy_0=galaxy_0, galaxy_1=galaxy_1))


def concise():
"""``__Concise API__`` -- cookbook.py:118-127."""
galaxy = af.Model(
ag.Galaxy,
redshift=0.5,
bulge=ag.lp_linear.Sersic,
disk=ag.lp_linear.Exponential,
bar=ag.lp_linear.Sersic,
)

return af.Collection(galaxies=af.Collection(galaxy=galaxy))


def prior_custom():
"""``__Prior Customization__`` -- cookbook.py:134-149."""
bulge = af.Model(ag.lp_linear.Sersic)
bulge.centre.centre_0 = af.UniformPrior(lower_limit=-0.1, upper_limit=0.1)
bulge.centre.centre_1 = af.UniformPrior(lower_limit=-0.1, upper_limit=0.1)
bulge.sersic_index = af.TruncatedGaussianPrior(
mean=4.0, sigma=1.0, lower_limit=1.0, upper_limit=8.0
)

galaxy = af.Model(ag.Galaxy, redshift=0.5, bulge=bulge)

return af.Collection(galaxies=af.Collection(galaxy=galaxy))


def model_custom():
"""``__Model Customization__`` -- cookbook.py:156-194."""
bulge = af.Model(ag.lp_linear.Sersic)
disk = af.Model(ag.lp_linear.Exponential)

bulge.centre = disk.centre
bulge.sersic_index = 4.0
bulge.effective_radius = disk.effective_radius + 0.1

galaxy = af.Model(ag.Galaxy, redshift=0.5, bulge=bulge, disk=disk)

model = af.Collection(galaxies=af.Collection(galaxy=galaxy))

model.add_assertion(
model.galaxies.galaxy.bulge.effective_radius
> model.galaxies.galaxy.disk.effective_radius
)
model.add_assertion(model.galaxies.galaxy.bulge.effective_radius < 3.0)

return model


# ---------------------------------------------------------------------------
# the overview_3 MGE figure
# ---------------------------------------------------------------------------


def mge_overview(total_gaussians: int = 30, gaussian_per_basis: int = 2):
"""
The MGE model of ``features/multi_gaussian_expansion/modeling.py``.

``gaussian_per_basis`` bases of ``total_gaussians`` linear Gaussians, all
sharing one ``centre``, each basis sharing its own ``ell_comps`` and every
``sigma`` fixed to its own log-spaced value.
"""
mask_radius = 3.0
pixel_scale = 0.1

log10_sigma_list = np.linspace(
np.log10(pixel_scale / 10.0), np.log10(mask_radius), total_gaussians
)

centre_0 = af.UniformPrior(lower_limit=-0.1, upper_limit=0.1)
centre_1 = af.UniformPrior(lower_limit=-0.1, upper_limit=0.1)

bulge_gaussian_list = []

for _ in range(gaussian_per_basis):
gaussian_list = af.Collection(
af.Model(ag.lp_linear.Gaussian) for _ in range(total_gaussians)
)

for i, gaussian in enumerate(gaussian_list):
gaussian.centre.centre_0 = centre_0
gaussian.centre.centre_1 = centre_1
gaussian.ell_comps = gaussian_list[0].ell_comps
gaussian.sigma = 10 ** log10_sigma_list[i]

bulge_gaussian_list += gaussian_list

bulge = af.Model(ag.lp_basis.Basis, profile_list=bulge_gaussian_list)

galaxy = af.Model(ag.Galaxy, redshift=0.5, bulge=bulge)

return af.Collection(galaxies=af.Collection(galaxy=galaxy))


def main():
write(simple(), "simple")
write(complex_model(), "complex")
write(two_galaxies(), "two_galaxies")
write(concise(), "concise")
write(prior_custom(), "prior_custom")
write(model_custom(), "model_custom")

write(mge_overview(), "mge_model", path=OVERVIEW_PATH)


if __name__ == "__main__":
main()
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/general/images/model_cookbook/simple.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading