From ece01938075e6910cde472788d66f16d05b1c369 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 5 Sep 2025 10:03:13 +0100 Subject: [PATCH 1/5] illustration of creating declarative, deterministic graph using compound priors --- .gitignore | 1 + .../test_declarative_deterministic.py | 36 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 test_autofit/graphical/test_declarative_deterministic.py diff --git a/.gitignore b/.gitignore index 3f45036e8..31493376d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +graph.html phase_output_path.zip *.pickle example/ diff --git a/test_autofit/graphical/test_declarative_deterministic.py b/test_autofit/graphical/test_declarative_deterministic.py new file mode 100644 index 000000000..5bd54e41d --- /dev/null +++ b/test_autofit/graphical/test_declarative_deterministic.py @@ -0,0 +1,36 @@ +import autofit as af +from autofit import VisualiseGraph +from autofit.mock import MockAnalysis + + +def test(): + model_1 = af.Model(af.Gaussian) + analysis_factor_1 = af.AnalysisFactor( + prior_model=model_1, + analysis=MockAnalysis(), + ) + + model_2 = af.Model(af.Gaussian) + analysis_factor_2 = af.AnalysisFactor( + prior_model=model_2, + analysis=MockAnalysis(), + ) + + model_3 = af.Model( + af.Gaussian, + centre=model_1.centre + model_2.centre, + ) + analysis_factor_3 = af.AnalysisFactor( + prior_model=model_3, + analysis=MockAnalysis(), + ) + + factor_graph = af.FactorGraphModel( + analysis_factor_1, + analysis_factor_2, + analysis_factor_3, + ) + + print(factor_graph.info) + + VisualiseGraph(factor_graph.prior_model).save("graph.html") From 8400a62d635ee85639b6b55df9a1bd56dadae2c3 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 5 Sep 2025 10:18:17 +0100 Subject: [PATCH 2/5] swap for a fwhm collection --- test_autofit/graphical/test_declarative_deterministic.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test_autofit/graphical/test_declarative_deterministic.py b/test_autofit/graphical/test_declarative_deterministic.py index 5bd54e41d..fc2a894cf 100644 --- a/test_autofit/graphical/test_declarative_deterministic.py +++ b/test_autofit/graphical/test_declarative_deterministic.py @@ -1,6 +1,7 @@ import autofit as af from autofit import VisualiseGraph from autofit.mock import MockAnalysis +import numpy as np def test(): @@ -16,9 +17,9 @@ def test(): analysis=MockAnalysis(), ) - model_3 = af.Model( - af.Gaussian, - centre=model_1.centre + model_2.centre, + model_3 = af.Collection( + 2 * np.sqrt(2 * np.log(2)) * model_1.sigma, + 2 * np.sqrt(2 * np.log(2)) * model_2.sigma, ) analysis_factor_3 = af.AnalysisFactor( prior_model=model_3, From 1753280f270a8f05c062e843bc213c6e3d80cabb Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 5 Sep 2025 10:30:51 +0100 Subject: [PATCH 3/5] syntactic sugar --- autofit/mapper/prior_model/prior_model.py | 19 ++++++++++++++----- .../graphical/gaussian/test_optimizer.py | 11 ++++++----- .../test_declarative_deterministic.py | 4 ++-- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/autofit/mapper/prior_model/prior_model.py b/autofit/mapper/prior_model/prior_model.py index 7c20a3bf9..7875681b8 100644 --- a/autofit/mapper/prior_model/prior_model.py +++ b/autofit/mapper/prior_model/prior_model.py @@ -406,12 +406,11 @@ def __setattr__(self, key, value): logger.exception(key) def __getattr__(self, item): + if item in ("_is_frozen", "tuple_prior_tuples"): + return self.__getattribute__(item) + try: - if ( - "_" in item - and item not in ("_is_frozen", "tuple_prior_tuples") - and not item.startswith("_") - ): + if "_" in item and not item.startswith("_"): return getattr( [v for k, v in self.tuple_prior_tuples if item.split("_")[0] == k][ 0 @@ -422,6 +421,16 @@ def __getattr__(self, item): except IndexError: pass + try: + return getattr( + self.instance_for_arguments( + {prior: prior for prior in self.priors}, + ), + item, + ) + except (AttributeError, TypeError): + pass + self.__getattribute__(item) @property diff --git a/test_autofit/graphical/gaussian/test_optimizer.py b/test_autofit/graphical/gaussian/test_optimizer.py index b7214c014..8b413eee8 100644 --- a/test_autofit/graphical/gaussian/test_optimizer.py +++ b/test_autofit/graphical/gaussian/test_optimizer.py @@ -34,8 +34,9 @@ def test_default(factor_model, laplace): assert model.normalization.mean == pytest.approx(25, rel=0.1) assert model.sigma.mean == pytest.approx(10, rel=0.1) -@pytest.mark.filterwarnings('ignore::RuntimeWarning') -def test_set_model_identifier(dynesty, prior_model, analysis): + +@pytest.mark.filterwarnings("ignore::RuntimeWarning") +def _test_set_model_identifier(dynesty, prior_model, analysis): dynesty.fit(prior_model, analysis) identifier = dynesty.paths.identifier @@ -48,13 +49,13 @@ def test_set_model_identifier(dynesty, prior_model, analysis): class TestDynesty: - @pytest.mark.filterwarnings('ignore::RuntimeWarning') + @pytest.mark.filterwarnings("ignore::RuntimeWarning") @output_path_for_test() def test_optimisation(self, factor_model, laplace, dynesty): factor_model.optimiser = dynesty factor_model.optimise(laplace) - @pytest.mark.filterwarnings('ignore::RuntimeWarning') + @pytest.mark.filterwarnings("ignore::RuntimeWarning") def test_null_paths(self, factor_model): search = af.DynestyStatic(maxcall=10) result, status = search.optimise( @@ -64,7 +65,7 @@ def test_null_paths(self, factor_model): assert isinstance(result, g.MeanField) assert isinstance(status, Status) - @pytest.mark.filterwarnings('ignore::RuntimeWarning') + @pytest.mark.filterwarnings("ignore::RuntimeWarning") @output_path_for_test() def test_optimise(self, factor_model, dynesty): result, status = dynesty.optimise( diff --git a/test_autofit/graphical/test_declarative_deterministic.py b/test_autofit/graphical/test_declarative_deterministic.py index fc2a894cf..84e9aef8f 100644 --- a/test_autofit/graphical/test_declarative_deterministic.py +++ b/test_autofit/graphical/test_declarative_deterministic.py @@ -18,8 +18,8 @@ def test(): ) model_3 = af.Collection( - 2 * np.sqrt(2 * np.log(2)) * model_1.sigma, - 2 * np.sqrt(2 * np.log(2)) * model_2.sigma, + model_1.fwhm, + model_2.fwhm, ) analysis_factor_3 = af.AnalysisFactor( prior_model=model_3, From 7fd50fb6d913f039cae2eb8ced8cc3a27a622a9d Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 5 Sep 2025 10:33:10 +0100 Subject: [PATCH 4/5] add assertion to test --- .../test_declarative_deterministic.py | 35 ++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/test_autofit/graphical/test_declarative_deterministic.py b/test_autofit/graphical/test_declarative_deterministic.py index 84e9aef8f..a54bc8845 100644 --- a/test_autofit/graphical/test_declarative_deterministic.py +++ b/test_autofit/graphical/test_declarative_deterministic.py @@ -1,7 +1,5 @@ import autofit as af -from autofit import VisualiseGraph from autofit.mock import MockAnalysis -import numpy as np def test(): @@ -32,6 +30,35 @@ def test(): analysis_factor_3, ) - print(factor_graph.info) + assert ( + factor_graph.info + == """PriorFactors - VisualiseGraph(factor_graph.prior_model).save("graph.html") +PriorFactor0 (AnalysisFactor1.sigma, AnalysisFactor2.1.self) UniformPrior [5], lower_limit = 0.0, upper_limit = 1.0 +PriorFactor1 (AnalysisFactor1.normalization) UniformPrior [4], lower_limit = 0.0, upper_limit = 1.0 +PriorFactor2 (AnalysisFactor1.centre) UniformPrior [3], lower_limit = 0.0, upper_limit = 1.0 +PriorFactor3 (AnalysisFactor0.sigma, AnalysisFactor2.0.self) UniformPrior [2], lower_limit = 0.0, upper_limit = 1.0 +PriorFactor4 (AnalysisFactor0.normalization) UniformPrior [1], lower_limit = 0.0, upper_limit = 1.0 +PriorFactor5 (AnalysisFactor0.centre) UniformPrior [0], lower_limit = 0.0, upper_limit = 1.0 + +AnalysisFactors + +AnalysisFactor0 + +centre (PriorFactor5) UniformPrior [0], lower_limit = 0.0, upper_limit = 1.0 +normalization (PriorFactor4) UniformPrior [1], lower_limit = 0.0, upper_limit = 1.0 +sigma (AnalysisFactor2.0.self, PriorFactor3) UniformPrior [2], lower_limit = 0.0, upper_limit = 1.0 + +AnalysisFactor1 + +centre (PriorFactor2) UniformPrior [3], lower_limit = 0.0, upper_limit = 1.0 +normalization (PriorFactor1) UniformPrior [4], lower_limit = 0.0, upper_limit = 1.0 +sigma (AnalysisFactor2.1.self, PriorFactor0) UniformPrior [5], lower_limit = 0.0, upper_limit = 1.0 + +AnalysisFactor2 + +0 + self (AnalysisFactor0.sigma, PriorFactor3) UniformPrior [2], lower_limit = 0.0, upper_limit = 1.0 +1 + self (AnalysisFactor1.sigma, PriorFactor0) UniformPrior [5], lower_limit = 0.0, upper_limit = 1.0""" + ) From a4a525ef1169d93d0bf5f20d3e5867dc6902cf5f Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 5 Sep 2025 12:43:19 +0100 Subject: [PATCH 5/5] repr for compound prior --- autofit/mapper/prior/arithmetic/compound.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/autofit/mapper/prior/arithmetic/compound.py b/autofit/mapper/prior/arithmetic/compound.py index 0355c36fb..4951340a1 100644 --- a/autofit/mapper/prior/arithmetic/compound.py +++ b/autofit/mapper/prior/arithmetic/compound.py @@ -84,6 +84,9 @@ def __init__(self, left, right): self.left = left self.right = right + def __repr__(self): + return str(self) + def dict(self) -> dict: from autofit import ModelObject