Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/visualizer refactor #995

Merged
merged 10 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from 9 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
1 change: 1 addition & 0 deletions autofit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
from .mapper.prior_model.prior_model import Model
from .mapper.prior_model.util import PriorModelNameValue
from .non_linear.search.abstract_search import NonLinearSearch
from .non_linear.analysis.visualize import Visualizer
from .non_linear.analysis.analysis import Analysis
from .non_linear.analysis.combined import CombinedAnalysis
from .non_linear.analysis.latent_variables import LatentVariables
Expand Down
4 changes: 1 addition & 3 deletions autofit/config/output.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,4 @@ covariance: true # `covariance.csv`: The [free parameters x free parameters] cov
data: true # `data.json`: The value of every data point in the data.
noise_map: true # `noise_map.json`: The value of every RMS noise map value.

search_log: true # `search.log`: logging produced whilst running the fit or fit_sequential method

default: false
search_log: true # `search.log`: logging produced whilst running the fit or fit_sequential method
108 changes: 19 additions & 89 deletions autofit/example/analysis.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import os
import matplotlib.pyplot as plt

from typing import Dict, List, Optional

from autofit.example.result import ResultExample
from autofit.jax_wrapper import numpy as np

import autofit as af

from autofit.example.result import ResultExample
from autofit.example.visualize import VisualizerExample

"""
The `analysis.py` module contains the dataset and log likelihood function which given a model instance (set up by
the non-linear search) fits the dataset and returns the log likelihood of that model.
Expand All @@ -15,10 +16,22 @@
class Analysis(af.Analysis):

"""
This overwrite means the `ResultExample` class is returned after the model-fit.
This over-write means the `Visualizer` class is used for visualization throughout the model-fit.

This `VisualizerExample` object is in the `autofit.example.visualize` module and is used to customize the
plots output during the model-fit.

It has been extended with visualize methods that output visuals specific to the fitting of `1D` data.
"""
Visualizer = VisualizerExample

"""
This over-write means the `ResultExample` class is returned after the model-fit.

This result has been extended, based on the model that is input into the analysis, to include a property
`max_log_likelihood_model_data`, which is the model data of the best-fit model.
This `ResultExample` object in the `autofit.example.result` module.

It has been extended, based on the model that is input into the analysis, to include a
property `max_log_likelihood_model_data`, which is the model data of the best-fit model.
"""
Result = ResultExample

Expand Down Expand Up @@ -96,89 +109,6 @@ def model_data_1d_from(self, instance : af.ModelInstance) -> np.ndarray:

return model_data_1d

def visualize(self, paths: af.DirectoryPaths, instance: af.ModelInstance, during_analysis : bool):
"""
During a model-fit, the `visualize` method is called throughout the non-linear search and is used to output
images indicating the quality of the fit so far..

The `instance` passed into the visualize method is maximum log likelihood solution obtained by the model-fit
so far and it can be used to provide on-the-fly images showing how the model-fit is going.

For your model-fitting problem this function will be overwritten with plotting functions specific to your
problem.

Parameters
----------
paths
The PyAutoFit paths object which manages all paths, e.g. where the non-linear search outputs are stored,
visualization, and the pickled objects used by the aggregator output by this function.
instance
An instance of the model that is being fitted to the data by this analysis (whose parameters have been set
via a non-linear search).
during_analysis
If True the visualization is being performed midway through the non-linear search before it is finished,
which may change which images are output.
"""

xvalues = np.arange(self.data.shape[0])
model_data_1d = np.zeros(self.data.shape[0])

try:
for profile in instance:
try:
model_data_1d += profile.model_data_1d_via_xvalues_from(xvalues=xvalues)
except AttributeError:
pass
except TypeError:
model_data_1d += instance.model_data_1d_via_xvalues_from(xvalues=xvalues)

plt.errorbar(
x=xvalues,
y=self.data,
yerr=self.noise_map,
color="k",
ecolor="k",
elinewidth=1,
capsize=2,
)
plt.plot(range(self.data.shape[0]), model_data_1d, color="r")
plt.title("Dynesty model fit to 1D Gaussian + Exponential dataset.")
plt.xlabel("x values of profile")
plt.ylabel("Profile normalization")

os.makedirs(paths.image_path, exist_ok=True)
plt.savefig(paths.image_path / "model_fit.png")
plt.clf()
plt.close()

def visualize_combined(
self,
analyses: List[af.Analysis],
paths: af.DirectoryPaths,
instance: af.ModelInstance,
during_analysis: bool,
):
"""
Visualise the instance using images and quantities which are shared across all analyses.

For example, each Analysis may have a different dataset, where the fit to each dataset is intended to all
be plotted on the same matplotlib subplot. This function can be overwritten to allow the visualization of such
a plot.

Only the first analysis is used to visualize the combined results, where it is assumed that it uses the
`analyses` property to access the other analyses and perform visualization.

Parameters
----------
paths
An object describing the paths for saving data (e.g. hard-disk directories or entries in sqlite database).
instance
The maximum likelihood instance of the model so far in the non-linear search.
during_analysis
Is this visualisation during analysis?
"""
pass

def save_attributes(self, paths: af.DirectoryPaths):
"""
Before the model-fit via the non-linear search begins, this routine saves attributes of the `Analysis` object
Expand Down
203 changes: 203 additions & 0 deletions autofit/example/visualize.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
import os
import matplotlib.pyplot as plt
import numpy as np
from typing import List

import autofit as af


class VisualizerExample(af.Visualizer):
"""
Methods associated with visualising analysis, model and data before, during
or after an optimisation.
"""

@staticmethod
def visualize_before_fit(
analysis,
paths: af.AbstractPaths,
model: af.AbstractPriorModel,
):
"""
Before a model-fit begins, the `visualize_before_fit` method is called and is used to output images
of quantities that do not change during the fit (e.g. the data).

The function receives as input an instance of the `Analysis` class which is being used to perform the fit,
which is used to perform the visualization (e.g. it contains the data and noise map which are plotted).

For your model-fitting problem this function will be overwritten with plotting functions specific to your
problem.

Parameters
----------
analysis
The analysis class used to perform the model-fit whose quantities are being visualized.
paths
The PyAutoFit paths object which manages all paths, e.g. where the non-linear search outputs are stored,
visualization, and the pickled objects used by the aggregator output by this function.
model
The model which is fitted to the data, which may be used to customize the visualization.
"""

xvalues = np.arange(analysis.data.shape[0])

plt.errorbar(
x=xvalues,
y=analysis.data,
yerr=analysis.noise_map,
color="k",
ecolor="k",
elinewidth=1,
capsize=2,
)
plt.title("The 1D Dataset.")
plt.xlabel("x values of profile")
plt.ylabel("Profile normalization")

os.makedirs(paths.image_path, exist_ok=True)
plt.savefig(paths.image_path / "data.png")
plt.clf()
plt.close()

@staticmethod
def visualize(
analysis,
paths: af.DirectoryPaths,
instance: af.ModelInstance,
during_analysis : bool
):
"""
During a model-fit, the `visualize` method is called throughout the non-linear search and is used to output
images indicating the quality of the fit so far.

The function receives as input an instance of the `Analysis` class which is being used to perform the fit,
which is used to perform the visualization (e.g. it generates the model data which is plotted).

The `instance` passed into the visualize method is maximum log likelihood solution obtained by the model-fit
so far which can output on-the-fly images showing the best-fit model so far.

For your model-fitting problem this function will be overwritten with plotting functions specific to your
problem.

Parameters
----------
analysis
The analysis class used to perform the model-fit whose quantities are being visualized.
paths
The PyAutoFit paths object which manages all paths, e.g. where the non-linear search outputs are stored,
visualization, and the pickled objects used by the aggregator output by this function.
instance
An instance of the model that is being fitted to the data by this analysis (whose parameters have been set
via a non-linear search).
during_analysis
If True the visualization is being performed midway through the non-linear search before it is finished,
which may change which images are output.
"""

xvalues = np.arange(analysis.data.shape[0])
model_data_1d = np.zeros(analysis.data.shape[0])

try:
for profile in instance:
try:
model_data_1d += profile.model_data_1d_via_xvalues_from(xvalues=xvalues)
except AttributeError:
pass
except TypeError:
model_data_1d += instance.model_data_1d_via_xvalues_from(xvalues=xvalues)

plt.errorbar(
x=xvalues,
y=analysis.data,
yerr=analysis.noise_map,
color="k",
ecolor="k",
elinewidth=1,
capsize=2,
)
plt.plot(range(analysis.shape[0]), model_data_1d, color="r")
plt.title("Model fit to 1D Gaussian + Exponential dataset.")
plt.xlabel("x values of profile")
plt.ylabel("Profile normalization")

os.makedirs(paths.image_path, exist_ok=True)
plt.savefig(paths.image_path / "model_fit.png")
plt.clf()
plt.close()

@staticmethod
def visualize_before_fit_combined(
analyses,
paths: af.AbstractPaths,
model: af.AbstractPriorModel,
):
"""
Multiple instances of the `Analysis` class can be summed together, meaning that the model is fitted to all
datasets simultaneously via a summed likelihood function.

The function receives as input a list of instances of every `Analysis` class which is being used to perform
the summed analysis fit. This is used which is used to perform the visualization which combines the
information spread across all analyses (e.g. plotting the data of each analysis on the same subplot).

The `visualize_before_fit_combined` method is called before the model-fit begins and is used to output images
of quantities that do not change during the fit (e.g. the data).

When summed analysis is used, the `visualize_before_fit` method is also called for each individual analysis.
Each individual dataset may therefore also be visualized in that function. This method is specifically for
visualizing the combined information of all datasets.

For your model-fitting problem this function will be overwritten with plotting functions specific to your
problem.

The example does not use analysis summing and therefore this function is not implemented.

Parameters
----------
analyses
A list of the analysis classes used to perform the model-fit whose quantities are being visualized.
paths
The PyAutoFit paths object which manages all paths, e.g. where the non-linear search outputs are stored,
visualization, and the pickled objects used by the aggregator output by this function.
model
The model which is fitted to the data, which may be used to customize the visualization.
"""
pass

@staticmethod
def visualize_combined(
analyses: List[af.Analysis],
paths: af.DirectoryPaths,
instance: af.ModelInstance,
during_analysis: bool,
):
"""
Multiple instances of the `Analysis` class can be summed together, meaning that the model is fitted to all
datasets simultaneously via a summed likelihood function.

The function receives as input a list of instances of every `Analysis` class which is being used to perform
the summed analysis fit. This is used which is used to perform the visualization which combines the
information spread across all analyses (e.g. plotting the data of each analysis on the same subplot).

The `visualize_combined` method is called throughout the non-linear search and is used to output images
indicating the quality of the fit so far.

When summed analysis is used, the `visualize_before_fit` method is also called for each individual analysis.
Each individual dataset may therefore also be visualized in that function. This method is specifically for
visualizing the combined information of all datasets.

For your model-fitting problem this function will be overwritten with plotting functions specific to your
problem.

The example does not use analysis summing and therefore this function is not implemented.

Parameters
----------
analyses
A list of the analysis classes used to perform the model-fit whose quantities are being visualized.
paths
The PyAutoFit paths object which manages all paths, e.g. where the non-linear search outputs are stored,
visualization, and the pickled objects used by the aggregator output by this function.
model
The model which is fitted to the data, which may be used to customize the visualization.
"""
pass
Loading
Loading