From ac570e85aa0db7d0be03a5b721a955c9c7ef81e6 Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 1 Jul 2024 09:28:51 +0100 Subject: [PATCH 01/19] fixed test --- test_autofit/serialise/test_samples.py | 32 +++++++++++++++++++------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/test_autofit/serialise/test_samples.py b/test_autofit/serialise/test_samples.py index 918f05e2a..9c333d1ce 100644 --- a/test_autofit/serialise/test_samples.py +++ b/test_autofit/serialise/test_samples.py @@ -52,17 +52,21 @@ def make_summary_dict(): "type": "instance", "class_path": "autofit.non_linear.samples.summary.SamplesSummary", "arguments": { - "errors_at_sigma_1": { + "values_at_sigma_3": { "type": "list", - "values": [(2.0, 0.0), (3.0, 0.0), (4.0, 0.0)], + "values": [ + {"type": "tuple", "values": [0.0, 2.0]}, + {"type": "tuple", "values": [1.0, 4.0]}, + {"type": "tuple", "values": [2.0, 6.0]}, + ], }, "errors_at_sigma_3": { "type": "list", - "values": [(2.0, 0.0), (3.0, 0.0), (4.0, 0.0)], - }, - "values_at_sigma_3": { - "type": "list", - "values": [(0.0, 2.0), (1.0, 4.0), (2.0, 6.0)], + "values": [ + {"type": "tuple", "values": [2.0, 0.0]}, + {"type": "tuple", "values": [3.0, 0.0]}, + {"type": "tuple", "values": [4.0, 0.0]}, + ], }, "max_log_likelihood_sample": { "type": "instance", @@ -83,7 +87,19 @@ def make_summary_dict(): }, "values_at_sigma_1": { "type": "list", - "values": [(0.0, 2.0), (1.0, 4.0), (2.0, 6.0)], + "values": [ + {"type": "tuple", "values": [0.0, 2.0]}, + {"type": "tuple", "values": [1.0, 4.0]}, + {"type": "tuple", "values": [2.0, 6.0]}, + ], + }, + "errors_at_sigma_1": { + "type": "list", + "values": [ + {"type": "tuple", "values": [2.0, 0.0]}, + {"type": "tuple", "values": [3.0, 0.0]}, + {"type": "tuple", "values": [4.0, 0.0]}, + ], }, "log_evidence": None, "median_pdf_sample": { From 10ad4f9b0cde10ee66a5db6e3cccdf5bf3d0416a Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 1 Jul 2024 10:03:06 +0100 Subject: [PATCH 02/19] creating an Array model --- autofit/__init__.py | 2 +- autofit/mapper/prior_model/array.py | 25 +++++++++++++++++++ test_autofit/mapper/functionality/__init__.py | 0 .../{ => functionality}/test_by_path.py | 0 .../test_explicit_width_modifier.py | 0 .../test_from_data_names.py | 0 .../mapper/{ => functionality}/test_has.py | 0 .../test_take_attributes.py | 0 test_autofit/mapper/test_array.py | 9 +++++++ 9 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 autofit/mapper/prior_model/array.py create mode 100644 test_autofit/mapper/functionality/__init__.py rename test_autofit/mapper/{ => functionality}/test_by_path.py (100%) rename test_autofit/mapper/{ => functionality}/test_explicit_width_modifier.py (100%) rename test_autofit/mapper/{ => functionality}/test_from_data_names.py (100%) rename test_autofit/mapper/{ => functionality}/test_has.py (100%) rename test_autofit/mapper/{ => functionality}/test_take_attributes.py (100%) create mode 100644 test_autofit/mapper/test_array.py diff --git a/autofit/__init__.py b/autofit/__init__.py index a02199d53..4852f150a 100644 --- a/autofit/__init__.py +++ b/autofit/__init__.py @@ -57,7 +57,7 @@ from .mapper.prior_model.annotation import AnnotationPriorModel from .mapper.prior_model.collection import Collection from .mapper.prior_model.prior_model import Model -from .mapper.prior_model.prior_model import Model +from .mapper.prior_model.array import Array from .non_linear.search.abstract_search import NonLinearSearch from .non_linear.analysis.visualize import Visualizer from .non_linear.analysis.analysis import Analysis diff --git a/autofit/mapper/prior_model/array.py b/autofit/mapper/prior_model/array.py new file mode 100644 index 000000000..cfbcc7a80 --- /dev/null +++ b/autofit/mapper/prior_model/array.py @@ -0,0 +1,25 @@ +from typing import Tuple + +from .abstract import AbstractPriorModel +from autofit.mapper.prior.abstract import Prior +import numpy as np + + +class Array(AbstractPriorModel): + def __init__(self, shape: Tuple[int, ...], prior: Prior): + """ + An array of priors. + + Parameters + ---------- + shape : (int, int) + The shape of the array. + prior : Prior + The prior of every entry in the array. + """ + super().__init__() + self.shape = shape + + for key in np.ndindex(*shape): + suffix = "_".join(map(str, key)) + setattr(self, f"prior_{suffix}", prior.new()) diff --git a/test_autofit/mapper/functionality/__init__.py b/test_autofit/mapper/functionality/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test_autofit/mapper/test_by_path.py b/test_autofit/mapper/functionality/test_by_path.py similarity index 100% rename from test_autofit/mapper/test_by_path.py rename to test_autofit/mapper/functionality/test_by_path.py diff --git a/test_autofit/mapper/test_explicit_width_modifier.py b/test_autofit/mapper/functionality/test_explicit_width_modifier.py similarity index 100% rename from test_autofit/mapper/test_explicit_width_modifier.py rename to test_autofit/mapper/functionality/test_explicit_width_modifier.py diff --git a/test_autofit/mapper/test_from_data_names.py b/test_autofit/mapper/functionality/test_from_data_names.py similarity index 100% rename from test_autofit/mapper/test_from_data_names.py rename to test_autofit/mapper/functionality/test_from_data_names.py diff --git a/test_autofit/mapper/test_has.py b/test_autofit/mapper/functionality/test_has.py similarity index 100% rename from test_autofit/mapper/test_has.py rename to test_autofit/mapper/functionality/test_has.py diff --git a/test_autofit/mapper/test_take_attributes.py b/test_autofit/mapper/functionality/test_take_attributes.py similarity index 100% rename from test_autofit/mapper/test_take_attributes.py rename to test_autofit/mapper/functionality/test_take_attributes.py diff --git a/test_autofit/mapper/test_array.py b/test_autofit/mapper/test_array.py new file mode 100644 index 000000000..269de1e9a --- /dev/null +++ b/test_autofit/mapper/test_array.py @@ -0,0 +1,9 @@ +import autofit as af + + +def test_instantiate(): + array = af.Array( + shape=(2, 2), + prior=af.GaussianPrior(mean=0.0, sigma=1.0), + ) + assert array.prior_count == 4 From 258885450ea96da5b5f82ffb3a3e7f45a973c00a Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 1 Jul 2024 10:07:57 +0100 Subject: [PATCH 03/19] instance from prior medians for special case of 2d array --- autofit/mapper/prior_model/array.py | 17 ++++++++++++++++- test_autofit/mapper/test_array.py | 15 +++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/autofit/mapper/prior_model/array.py b/autofit/mapper/prior_model/array.py index cfbcc7a80..84103f908 100644 --- a/autofit/mapper/prior_model/array.py +++ b/autofit/mapper/prior_model/array.py @@ -1,4 +1,4 @@ -from typing import Tuple +from typing import Tuple, Dict from .abstract import AbstractPriorModel from autofit.mapper.prior.abstract import Prior @@ -23,3 +23,18 @@ def __init__(self, shape: Tuple[int, ...], prior: Prior): for key in np.ndindex(*shape): suffix = "_".join(map(str, key)) setattr(self, f"prior_{suffix}", prior.new()) + + def _instance_for_arguments( + self, + arguments: Dict[Prior, float], + ignore_assertions: bool = False, + ): + return np.array( + [ + [ + arguments[getattr(self, f"prior_{i}_{j}")] + for j in range(self.shape[1]) + ] + for i in range(self.shape[0]) + ] + ) diff --git a/test_autofit/mapper/test_array.py b/test_autofit/mapper/test_array.py index 269de1e9a..d16358a32 100644 --- a/test_autofit/mapper/test_array.py +++ b/test_autofit/mapper/test_array.py @@ -1,9 +1,20 @@ +import pytest + import autofit as af -def test_instantiate(): - array = af.Array( +@pytest.fixture +def array(): + return af.Array( shape=(2, 2), prior=af.GaussianPrior(mean=0.0, sigma=1.0), ) + + +def test_instantiate(array): assert array.prior_count == 4 + + +def test_instance(array): + instance = array.instance_from_prior_medians() + assert (instance == [[0.0, 0.0], [0.0, 0.0]]).all() From f9e4820c3e2c60beea7f8163754f6339994edc9b Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 1 Jul 2024 10:27:28 +0100 Subject: [PATCH 04/19] generalised instance for method --- autofit/mapper/prior_model/array.py | 31 ++++++++++++++++++----------- test_autofit/mapper/test_array.py | 25 ++++++++++++++++++++++- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/autofit/mapper/prior_model/array.py b/autofit/mapper/prior_model/array.py index 84103f908..7282596c0 100644 --- a/autofit/mapper/prior_model/array.py +++ b/autofit/mapper/prior_model/array.py @@ -19,22 +19,29 @@ def __init__(self, shape: Tuple[int, ...], prior: Prior): """ super().__init__() self.shape = shape + self.indices = np.ndindex(*shape) - for key in np.ndindex(*shape): - suffix = "_".join(map(str, key)) - setattr(self, f"prior_{suffix}", prior.new()) + for index in self.indices: + setattr( + self, + self._make_key(index), + prior.new(), + ) + + @staticmethod + def _make_key(index): + suffix = "_".join(map(str, index)) + return f"prior_{suffix}" def _instance_for_arguments( self, arguments: Dict[Prior, float], ignore_assertions: bool = False, ): - return np.array( - [ - [ - arguments[getattr(self, f"prior_{i}_{j}")] - for j in range(self.shape[1]) - ] - for i in range(self.shape[0]) - ] - ) + array = np.zeros(self.shape) + for index in self.indices: + key = self._make_key(index) + array[index] = getattr(self, key).instance_for_arguments( + arguments, ignore_assertions + ) + return array diff --git a/test_autofit/mapper/test_array.py b/test_autofit/mapper/test_array.py index d16358a32..e67fe8a24 100644 --- a/test_autofit/mapper/test_array.py +++ b/test_autofit/mapper/test_array.py @@ -11,10 +11,33 @@ def array(): ) -def test_instantiate(array): +@pytest.fixture +def array_3d(): + return af.Array( + shape=(2, 2, 2), + prior=af.GaussianPrior(mean=0.0, sigma=1.0), + ) + + +def test_prior_count(array): assert array.prior_count == 4 +def test_prior_count_3d(array_3d): + assert array_3d.prior_count == 8 + + def test_instance(array): instance = array.instance_from_prior_medians() assert (instance == [[0.0, 0.0], [0.0, 0.0]]).all() + + +def test_instance_3d(array_3d): + instance = array_3d.instance_from_prior_medians() + assert ( + instance + == [ + [[0.0, 0.0], [0.0, 0.0]], + [[0.0, 0.0], [0.0, 0.0]], + ] + ).all() From 66d055e86d982a9ff2f9c55c38f572f4defcffc4 Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 1 Jul 2024 10:42:21 +0100 Subject: [PATCH 05/19] use set and get item to simplify implementation --- autofit/mapper/prior_model/array.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/autofit/mapper/prior_model/array.py b/autofit/mapper/prior_model/array.py index 7282596c0..21aa57fd8 100644 --- a/autofit/mapper/prior_model/array.py +++ b/autofit/mapper/prior_model/array.py @@ -22,11 +22,7 @@ def __init__(self, shape: Tuple[int, ...], prior: Prior): self.indices = np.ndindex(*shape) for index in self.indices: - setattr( - self, - self._make_key(index), - prior.new(), - ) + self[index] = prior.new() @staticmethod def _make_key(index): @@ -40,8 +36,13 @@ def _instance_for_arguments( ): array = np.zeros(self.shape) for index in self.indices: - key = self._make_key(index) - array[index] = getattr(self, key).instance_for_arguments( + array[index] = self[index].instance_for_arguments( arguments, ignore_assertions ) return array + + def __setitem__(self, key, value): + setattr(self, self._make_key(key), value) + + def __getitem__(self, key): + return getattr(self, self._make_key(key)) From 08f8a73e2bf389ff7773ec423e484d18d253327a Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 1 Jul 2024 10:43:48 +0100 Subject: [PATCH 06/19] test modification --- test_autofit/mapper/test_array.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test_autofit/mapper/test_array.py b/test_autofit/mapper/test_array.py index e67fe8a24..01407830a 100644 --- a/test_autofit/mapper/test_array.py +++ b/test_autofit/mapper/test_array.py @@ -41,3 +41,8 @@ def test_instance_3d(array_3d): [[0.0, 0.0], [0.0, 0.0]], ] ).all() + + +def test_modify_prior(array): + array[0, 0] = 1.0 + assert array.prior_count == 3 From a0f0b87ab548e49bea1d112a7986a943dc3bcf3d Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 1 Jul 2024 10:57:14 +0100 Subject: [PATCH 07/19] modifying values + fix --- autofit/mapper/prior_model/array.py | 15 +++++++++++---- test_autofit/mapper/test_array.py | 7 +++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/autofit/mapper/prior_model/array.py b/autofit/mapper/prior_model/array.py index 21aa57fd8..8923c6126 100644 --- a/autofit/mapper/prior_model/array.py +++ b/autofit/mapper/prior_model/array.py @@ -19,7 +19,7 @@ def __init__(self, shape: Tuple[int, ...], prior: Prior): """ super().__init__() self.shape = shape - self.indices = np.ndindex(*shape) + self.indices = list(np.ndindex(*shape)) for index in self.indices: self[index] = prior.new() @@ -36,9 +36,16 @@ def _instance_for_arguments( ): array = np.zeros(self.shape) for index in self.indices: - array[index] = self[index].instance_for_arguments( - arguments, ignore_assertions - ) + value = self[index] + try: + value = value.instance_for_arguments( + arguments, + ignore_assertions, + ) + except AttributeError: + pass + + array[index] = value return array def __setitem__(self, key, value): diff --git a/test_autofit/mapper/test_array.py b/test_autofit/mapper/test_array.py index 01407830a..ef3a7d518 100644 --- a/test_autofit/mapper/test_array.py +++ b/test_autofit/mapper/test_array.py @@ -46,3 +46,10 @@ def test_instance_3d(array_3d): def test_modify_prior(array): array[0, 0] = 1.0 assert array.prior_count == 3 + assert ( + array.instance_from_prior_medians() + == [ + [1.0, 0.0], + [0.0, 0.0], + ] + ).all() From dcc1195367a4bcaf84af392eaaa45f09b99d1615 Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 1 Jul 2024 10:58:15 +0100 Subject: [PATCH 08/19] more testing --- test_autofit/mapper/test_array.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test_autofit/mapper/test_array.py b/test_autofit/mapper/test_array.py index ef3a7d518..2002d6b96 100644 --- a/test_autofit/mapper/test_array.py +++ b/test_autofit/mapper/test_array.py @@ -53,3 +53,13 @@ def test_modify_prior(array): [0.0, 0.0], ] ).all() + + +def test_correlation(array): + array[0, 0] = array[1, 1] + array[0, 1] = array[1, 0] + + instance = array.random_instance() + + assert instance[0, 0] == instance[1, 1] + assert instance[0, 1] == instance[1, 0] From f5116ab76317205d17e9ad3900e3097464f1b481 Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 1 Jul 2024 11:11:21 +0100 Subject: [PATCH 09/19] array from dict --- autofit/mapper/model_object.py | 12 +++++++++++- test_autofit/mapper/test_array.py | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/autofit/mapper/model_object.py b/autofit/mapper/model_object.py index aea48e872..10e79962e 100644 --- a/autofit/mapper/model_object.py +++ b/autofit/mapper/model_object.py @@ -234,7 +234,17 @@ def get_class_path(): f"Could not find type for class path {class_path}. Defaulting to Instance placeholder." ) instance = ModelInstance() - + elif type_ == "array": + from autofit.mapper.prior_model.array import Array + + return Array( + shape=d["shape"], + prior=from_dict( + d["prior"], + reference=dereference(reference, "prior"), + loaded_ids=loaded_ids, + ), + ) else: try: return Prior.from_dict(d, loaded_ids=loaded_ids) diff --git a/test_autofit/mapper/test_array.py b/test_autofit/mapper/test_array.py index 2002d6b96..2ecefd0fd 100644 --- a/test_autofit/mapper/test_array.py +++ b/test_autofit/mapper/test_array.py @@ -63,3 +63,21 @@ def test_correlation(array): assert instance[0, 0] == instance[1, 1] assert instance[0, 1] == instance[1, 0] + + +def test_from_dict(): + array = af.AbstractPriorModel.from_dict( + { + "type": "array", + "shape": (2, 2), + "prior": {"type": "Gaussian", "mean": 0.0, "sigma": 1.0}, + } + ) + assert array.prior_count == 4 + assert ( + array.instance_from_prior_medians() + == [ + [0.0, 0.0], + [0.0, 0.0], + ] + ).all() From fc30f866d7b1eb16f8f430bb61ece3a3cfa8c059 Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 1 Jul 2024 11:47:52 +0100 Subject: [PATCH 10/19] testing complex dict --- autofit/mapper/model_object.py | 3 ++ test_autofit/mapper/test_array.py | 56 +++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/autofit/mapper/model_object.py b/autofit/mapper/model_object.py index 10e79962e..fb24e7d6c 100644 --- a/autofit/mapper/model_object.py +++ b/autofit/mapper/model_object.py @@ -286,6 +286,7 @@ def dict(self) -> dict: from autofit.mapper.prior_model.collection import Collection from autofit.mapper.prior_model.prior_model import Model from autofit.mapper.prior.tuple_prior import TuplePrior + from autofit.mapper.prior_model.array import Array if isinstance(self, Collection): type_ = "collection" @@ -295,6 +296,8 @@ def dict(self) -> dict: type_ = "model" elif isinstance(self, TuplePrior): type_ = "tuple_prior" + elif isinstance(self, Array): + type_ = "array" else: raise AssertionError( f"{self.__class__.__name__} cannot be serialised to dict" diff --git a/test_autofit/mapper/test_array.py b/test_autofit/mapper/test_array.py index 2ecefd0fd..c636d029c 100644 --- a/test_autofit/mapper/test_array.py +++ b/test_autofit/mapper/test_array.py @@ -1,6 +1,7 @@ import pytest import autofit as af +from autoconf.dictable import to_dict @pytest.fixture @@ -81,3 +82,58 @@ def test_from_dict(): [0.0, 0.0], ] ).all() + + +@pytest.fixture +def array_dict(): + return { + "arguments": { + "indices": { + "type": "list", + "values": [ + {"type": "tuple", "values": [0, 0]}, + {"type": "tuple", "values": [0, 1]}, + {"type": "tuple", "values": [1, 0]}, + {"type": "tuple", "values": [1, 1]}, + ], + }, + "prior_0_0": { + "id": 1, + "lower_limit": float("-inf"), + "mean": 0.0, + "sigma": 1.0, + "type": "Gaussian", + "upper_limit": float("inf"), + }, + "prior_0_1": { + "id": 2, + "lower_limit": float("-inf"), + "mean": 0.0, + "sigma": 1.0, + "type": "Gaussian", + "upper_limit": float("inf"), + }, + "prior_1_0": { + "id": 3, + "lower_limit": float("-inf"), + "mean": 0.0, + "sigma": 1.0, + "type": "Gaussian", + "upper_limit": float("inf"), + }, + "prior_1_1": { + "id": 4, + "lower_limit": float("-inf"), + "mean": 0.0, + "sigma": 1.0, + "type": "Gaussian", + "upper_limit": float("inf"), + }, + "shape": {"type": "tuple", "values": [2, 2]}, + }, + "type": "array", + } + + +def test_to_dict(array, array_dict): + assert to_dict(array) == array_dict From 7a50b5a46560300c128d85a6f901cdd3b41452d0 Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 1 Jul 2024 11:58:47 +0100 Subject: [PATCH 11/19] properly handling from dict --- autofit/mapper/model_object.py | 10 ++------ autofit/mapper/prior_model/array.py | 30 ++++++++++++++++++++--- test_autofit/mapper/test_array.py | 38 +++++++++++------------------ 3 files changed, 42 insertions(+), 36 deletions(-) diff --git a/autofit/mapper/model_object.py b/autofit/mapper/model_object.py index fb24e7d6c..05be46ef7 100644 --- a/autofit/mapper/model_object.py +++ b/autofit/mapper/model_object.py @@ -150,6 +150,7 @@ def from_dict( from autofit.mapper.prior_model.collection import Collection from autofit.mapper.prior_model.prior_model import Model from autofit.mapper.prior.abstract import Prior + from autofit.mapper.prior.gaussian import GaussianPrior from autofit.mapper.prior.tuple_prior import TuplePrior from autofit.mapper.prior.arithmetic.compound import Compound from autofit.mapper.prior.arithmetic.compound import ModifiedPrior @@ -237,14 +238,7 @@ def get_class_path(): elif type_ == "array": from autofit.mapper.prior_model.array import Array - return Array( - shape=d["shape"], - prior=from_dict( - d["prior"], - reference=dereference(reference, "prior"), - loaded_ids=loaded_ids, - ), - ) + return Array.from_dict(d) else: try: return Prior.from_dict(d, loaded_ids=loaded_ids) diff --git a/autofit/mapper/prior_model/array.py b/autofit/mapper/prior_model/array.py index 8923c6126..6dbf6ab32 100644 --- a/autofit/mapper/prior_model/array.py +++ b/autofit/mapper/prior_model/array.py @@ -1,12 +1,17 @@ -from typing import Tuple, Dict +from typing import Tuple, Dict, Optional +from autoconf.dictable import from_dict from .abstract import AbstractPriorModel from autofit.mapper.prior.abstract import Prior import numpy as np class Array(AbstractPriorModel): - def __init__(self, shape: Tuple[int, ...], prior: Prior): + def __init__( + self, + shape: Tuple[int, ...], + prior: Optional[Prior] = None, + ): """ An array of priors. @@ -21,8 +26,9 @@ def __init__(self, shape: Tuple[int, ...], prior: Prior): self.shape = shape self.indices = list(np.ndindex(*shape)) - for index in self.indices: - self[index] = prior.new() + if prior is not None: + for index in self.indices: + self[index] = prior.new() @staticmethod def _make_key(index): @@ -53,3 +59,19 @@ def __setitem__(self, key, value): def __getitem__(self, key): return getattr(self, self._make_key(key)) + + @classmethod + def from_dict( + cls, + d, + reference: Optional[Dict[str, str]] = None, + loaded_ids: Optional[dict] = None, + ): + arguments = d["arguments"] + shape = from_dict(arguments["shape"]) + array = cls(shape) + for key, value in arguments.items(): + if key.startswith("prior"): + setattr(array, key, from_dict(value)) + + return array diff --git a/test_autofit/mapper/test_array.py b/test_autofit/mapper/test_array.py index c636d029c..322d69203 100644 --- a/test_autofit/mapper/test_array.py +++ b/test_autofit/mapper/test_array.py @@ -66,24 +66,6 @@ def test_correlation(array): assert instance[0, 1] == instance[1, 0] -def test_from_dict(): - array = af.AbstractPriorModel.from_dict( - { - "type": "array", - "shape": (2, 2), - "prior": {"type": "Gaussian", "mean": 0.0, "sigma": 1.0}, - } - ) - assert array.prior_count == 4 - assert ( - array.instance_from_prior_medians() - == [ - [0.0, 0.0], - [0.0, 0.0], - ] - ).all() - - @pytest.fixture def array_dict(): return { @@ -98,7 +80,6 @@ def array_dict(): ], }, "prior_0_0": { - "id": 1, "lower_limit": float("-inf"), "mean": 0.0, "sigma": 1.0, @@ -106,7 +87,6 @@ def array_dict(): "upper_limit": float("inf"), }, "prior_0_1": { - "id": 2, "lower_limit": float("-inf"), "mean": 0.0, "sigma": 1.0, @@ -114,7 +94,6 @@ def array_dict(): "upper_limit": float("inf"), }, "prior_1_0": { - "id": 3, "lower_limit": float("-inf"), "mean": 0.0, "sigma": 1.0, @@ -122,7 +101,6 @@ def array_dict(): "upper_limit": float("inf"), }, "prior_1_1": { - "id": 4, "lower_limit": float("-inf"), "mean": 0.0, "sigma": 1.0, @@ -135,5 +113,17 @@ def array_dict(): } -def test_to_dict(array, array_dict): - assert to_dict(array) == array_dict +def test_to_dict(array, array_dict, remove_ids): + assert remove_ids(to_dict(array)) == array_dict + + +def test_from_dict(array_dict): + array = af.AbstractPriorModel.from_dict(array_dict) + assert array.prior_count == 4 + assert ( + array.instance_from_prior_medians() + == [ + [0.0, 0.0], + [0.0, 0.0], + ] + ).all() From 718fed9f0f6b85292d2d65f36d051e8167631514 Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 1 Jul 2024 14:15:35 +0100 Subject: [PATCH 12/19] docs and typws --- autofit/mapper/prior_model/array.py | 103 +++++++++++++++++++++++++--- 1 file changed, 94 insertions(+), 9 deletions(-) diff --git a/autofit/mapper/prior_model/array.py b/autofit/mapper/prior_model/array.py index 6dbf6ab32..39866d71b 100644 --- a/autofit/mapper/prior_model/array.py +++ b/autofit/mapper/prior_model/array.py @@ -1,4 +1,4 @@ -from typing import Tuple, Dict, Optional +from typing import Tuple, Dict, Optional, Union from autoconf.dictable import from_dict from .abstract import AbstractPriorModel @@ -31,15 +31,48 @@ def __init__( self[index] = prior.new() @staticmethod - def _make_key(index): - suffix = "_".join(map(str, index)) + def _make_key(index: Tuple[int, ...]) -> str: + """ + Make a key for the prior. + + This is so an index (e.g. (1, 2)) can be used to access a + prior (e.g. prior_1_2). + + Parameters + ---------- + index + The index of an element in an array. + + Returns + ------- + The attribute name for the prior. + """ + if isinstance(index, int): + suffix = f"_{index}" + else: + suffix = "_".join(map(str, index)) return f"prior_{suffix}" def _instance_for_arguments( self, arguments: Dict[Prior, float], ignore_assertions: bool = False, - ): + ) -> np.ndarray: + """ + Create an array where the prior at each index is replaced with the + a concrete value. + + Parameters + ---------- + arguments + The arguments to replace the priors with. + ignore_assertions + Whether to ignore assertions in the priors. + + Returns + ------- + The array with the priors replaced. + """ array = np.zeros(self.shape) for index in self.indices: value = self[index] @@ -54,11 +87,47 @@ def _instance_for_arguments( array[index] = value return array - def __setitem__(self, key, value): - setattr(self, self._make_key(key), value) + def __setitem__( + self, + index: Union[int, Tuple[int, ...]], + value: Union[float, Prior], + ): + """ + Set the value at an index. - def __getitem__(self, key): - return getattr(self, self._make_key(key)) + Parameters + ---------- + index + The index of the prior. + value + The new value. + """ + setattr( + self, + self._make_key(index), + value, + ) + + def __getitem__( + self, + index: Union[int, Tuple[int, ...]], + ) -> Union[float, Prior]: + """ + Get the value at an index. + + Parameters + ---------- + index + The index of the value. + + Returns + ------- + The value at the index. + """ + return getattr( + self, + self._make_key(index), + ) @classmethod def from_dict( @@ -66,7 +135,23 @@ def from_dict( d, reference: Optional[Dict[str, str]] = None, loaded_ids: Optional[dict] = None, - ): + ) -> "Array": + """ + Create an array from a dictionary. + + Parameters + ---------- + d + The dictionary. + reference + A dictionary of references. + loaded_ids + A dictionary of loaded ids. + + Returns + ------- + The array. + """ arguments = d["arguments"] shape = from_dict(arguments["shape"]) array = cls(shape) From 00ba187bdadabb431a6a214754f3e71aa327ee20 Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 1 Jul 2024 14:16:57 +0100 Subject: [PATCH 13/19] test 1d array --- test_autofit/mapper/test_array.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test_autofit/mapper/test_array.py b/test_autofit/mapper/test_array.py index 322d69203..6cf1ebfea 100644 --- a/test_autofit/mapper/test_array.py +++ b/test_autofit/mapper/test_array.py @@ -127,3 +127,16 @@ def test_from_dict(array_dict): [0.0, 0.0], ] ).all() + + +@pytest.fixture +def array_1d(): + return af.Array( + shape=(2,), + prior=af.GaussianPrior(mean=0.0, sigma=1.0), + ) + + +def test_1d_array(array_1d): + assert array_1d.prior_count == 2 + assert (array_1d.instance_from_prior_medians() == [0.0, 0.0]).all() From df708b72bab7c985a3241f6170f466b4d383d447 Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 1 Jul 2024 14:18:18 +0100 Subject: [PATCH 14/19] modifying values on 1d arrays --- autofit/mapper/prior_model/array.py | 2 +- test_autofit/mapper/test_array.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/autofit/mapper/prior_model/array.py b/autofit/mapper/prior_model/array.py index 39866d71b..62a9204cb 100644 --- a/autofit/mapper/prior_model/array.py +++ b/autofit/mapper/prior_model/array.py @@ -48,7 +48,7 @@ def _make_key(index: Tuple[int, ...]) -> str: The attribute name for the prior. """ if isinstance(index, int): - suffix = f"_{index}" + suffix = str(index) else: suffix = "_".join(map(str, index)) return f"prior_{suffix}" diff --git a/test_autofit/mapper/test_array.py b/test_autofit/mapper/test_array.py index 6cf1ebfea..099993f81 100644 --- a/test_autofit/mapper/test_array.py +++ b/test_autofit/mapper/test_array.py @@ -140,3 +140,9 @@ def array_1d(): def test_1d_array(array_1d): assert array_1d.prior_count == 2 assert (array_1d.instance_from_prior_medians() == [0.0, 0.0]).all() + + +def test_1d_array_modify_prior(array_1d): + array_1d[0] = 1.0 + assert array_1d.prior_count == 1 + assert (array_1d.instance_from_prior_medians() == [1.0, 0.0]).all() From 5353631de0579bd436ed2749b586af40c066cc7a Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 8 Jul 2024 09:34:07 +0100 Subject: [PATCH 15/19] tree flatten and unflatten for pytrees (jax) --- autofit/mapper/prior/abstract.py | 2 -- autofit/mapper/prior_model/array.py | 22 ++++++++++++++++++++++ test_autofit/mapper/test_array.py | 16 ++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/autofit/mapper/prior/abstract.py b/autofit/mapper/prior/abstract.py index fd8669fc3..a1e3c30db 100644 --- a/autofit/mapper/prior/abstract.py +++ b/autofit/mapper/prior/abstract.py @@ -1,5 +1,4 @@ import itertools -import os import random from abc import ABC, abstractmethod from copy import copy @@ -115,7 +114,6 @@ def factor(self): return self.message.factor def assert_within_limits(self, value): - if jax_wrapper.use_jax: return diff --git a/autofit/mapper/prior_model/array.py b/autofit/mapper/prior_model/array.py index 62a9204cb..ac7d50a33 100644 --- a/autofit/mapper/prior_model/array.py +++ b/autofit/mapper/prior_model/array.py @@ -5,7 +5,10 @@ from autofit.mapper.prior.abstract import Prior import numpy as np +from autofit.jax_wrapper import register_pytree_node_class + +@register_pytree_node_class class Array(AbstractPriorModel): def __init__( self, @@ -160,3 +163,22 @@ def from_dict( setattr(array, key, from_dict(value)) return array + + def tree_flatten(self): + """ + Flatten this array model as a PyTree. + """ + members = [self[index] for index in self.indices] + return members, (self.shape,) + + @classmethod + def tree_unflatten(cls, aux_data, children): + """ + Unflatten a PyTree into an array model. + """ + (shape,) = aux_data + instance = cls(shape) + for index, child in zip(instance.indices, children): + instance[index] = child + + return instance diff --git a/test_autofit/mapper/test_array.py b/test_autofit/mapper/test_array.py index 099993f81..282fed3af 100644 --- a/test_autofit/mapper/test_array.py +++ b/test_autofit/mapper/test_array.py @@ -146,3 +146,19 @@ def test_1d_array_modify_prior(array_1d): array_1d[0] = 1.0 assert array_1d.prior_count == 1 assert (array_1d.instance_from_prior_medians() == [1.0, 0.0]).all() + + +def test_tree_flatten(array): + children, aux = array.tree_flatten() + assert len(children) == 4 + assert aux == ((2, 2),) + + new_array = af.Array.tree_unflatten(aux, children) + assert new_array.prior_count == 4 + assert ( + new_array.instance_from_prior_medians() + == [ + [0.0, 0.0], + [0.0, 0.0], + ] + ).all() From fd44f538170ab61d5e8bf302f742c6bed353c266 Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 8 Jul 2024 10:42:03 +0100 Subject: [PATCH 16/19] array prior passing --- autofit/mapper/prior_model/array.py | 18 ++++++++++++++ test_autofit/mapper/test_array.py | 37 +++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/autofit/mapper/prior_model/array.py b/autofit/mapper/prior_model/array.py index ac7d50a33..aa4482204 100644 --- a/autofit/mapper/prior_model/array.py +++ b/autofit/mapper/prior_model/array.py @@ -182,3 +182,21 @@ def tree_unflatten(cls, aux_data, children): instance[index] = child return instance + + @property + def prior_class_dict(self): + return { + **{ + prior: cls + for prior_model in self.direct_prior_model_tuples + for prior, cls in prior_model[1].prior_class_dict.items() + }, + **{prior: np.ndarray for _, prior in self.direct_prior_tuples}, + } + + def gaussian_prior_model_for_arguments(self, arguments): + new_array = Array(self.shape) + for index in self.indices: + new_array[index] = self[index].gaussian_prior_model_for_arguments(arguments) + + return new_array diff --git a/test_autofit/mapper/test_array.py b/test_autofit/mapper/test_array.py index 282fed3af..836eb91f9 100644 --- a/test_autofit/mapper/test_array.py +++ b/test_autofit/mapper/test_array.py @@ -1,4 +1,5 @@ import pytest +import numpy as np import autofit as af from autoconf.dictable import to_dict @@ -162,3 +163,39 @@ def test_tree_flatten(array): [0.0, 0.0], ] ).all() + + +class Analysis(af.Analysis): + def log_likelihood_function(self, instance): + return -float( + np.mean( + ( + np.array( + [ + [0.1, 0.2], + [0.3, 0.4], + ] + ) + - instance + ) + ** 2 + ) + ) + + +def test_optimisation(): + array = af.Array( + shape=(2, 2), + prior=af.UniformPrior( + lower_limit=0.0, + upper_limit=1.0, + ), + ) + result = af.DynestyStatic().fit(model=array, analysis=Analysis()) + + posterior = result.model + array[0, 0] = posterior[0, 0] + array[0, 1] = posterior[0, 1] + + result = af.DynestyStatic().fit(model=array, analysis=Analysis()) + assert isinstance(result.instance, np.ndarray) From ed5c58271ed126c810ebe53aa4093ada4c02ba1f Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 8 Jul 2024 10:45:53 +0100 Subject: [PATCH 17/19] docs --- autofit/mapper/prior_model/array.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/autofit/mapper/prior_model/array.py b/autofit/mapper/prior_model/array.py index aa4482204..c37c786b2 100644 --- a/autofit/mapper/prior_model/array.py +++ b/autofit/mapper/prior_model/array.py @@ -194,7 +194,20 @@ def prior_class_dict(self): **{prior: np.ndarray for _, prior in self.direct_prior_tuples}, } - def gaussian_prior_model_for_arguments(self, arguments): + def gaussian_prior_model_for_arguments(self, arguments: Dict[Prior, Prior]): + """ + Returns a new instance of model mapper with a set of Gaussian priors based on + tuples provided by a previous nonlinear search. + + Parameters + ---------- + arguments + Tuples providing the mean and sigma of gaussians + + Returns + ------- + A new model mapper populated with Gaussian priors + """ new_array = Array(self.shape) for index in self.indices: new_array[index] = self[index].gaussian_prior_model_for_arguments(arguments) From b3ad3eb613963e686696e63b1dc17316ec601a14 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Tue, 23 Jul 2024 11:17:24 +0100 Subject: [PATCH 18/19] model cookbook doc --- docs/cookbooks/model.rst | 334 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 333 insertions(+), 1 deletion(-) diff --git a/docs/cookbooks/model.rst b/docs/cookbooks/model.rst index 1f9081d75..84590ccca 100644 --- a/docs/cookbooks/model.rst +++ b/docs/cookbooks/model.rst @@ -10,6 +10,8 @@ This cookbook provides an overview of basic model composition tools. **Contents:** +**Models:** + If first describes how to use the ``af.Model`` object to define models with a single model component from single Python classes, with the following sections: @@ -21,6 +23,8 @@ Python classes, with the following sections: - **Tuple Parameters (Model)**: Defining model components with parameters that are tuples. - **Json Output (Model)**: Output a model in human readable text via a .json file and loading it back again. +**Collections:** + It then describes how to use the ``af.Collection`` object to define models with many model components from multiple Python classes, with the following sections: @@ -31,6 +35,19 @@ Python classes, with the following sections: - **Json Output (Collection)**: Output a collection in human readable text via a .json file and loading it back again. - **Extensible Models (Collection)**: Using collections to extend models with new model components, including the use of Python dictionaries and lists. +**Arrays:** + +The cookbook next describes using NumPy arrays via tbe `af.Array` object to compose models, where each entry of the +array is a free parameters, therefore offering maximum flexibility with the number of free parameter. This has +the following sections: + + - **Model Composition (af.Array)**: Composing models using NumPy arrays and `af.Array`(). + - **Prior Customization (af.Array)**: How to customize the priors of a numpy array model. + - **Instances (af.Array)**: Create an instance of a numpy array model via input parameters. + - **Model Customization (af.Array):** Customize a numpy array model (e.g. fixing parameters or linking them to one another). + - **Json Output (af.Array)**: Output a numpy array model in human readable text via a .json file and loading it back again. + - **Extensible Models (af.Array)**: Using numpy arrays to compose models with a flexible number of parameters. + Python Class Template --------------------- @@ -718,7 +735,7 @@ Python dictionaries can easily be saved to hard disk as a ``.json`` file. This means we can save any **PyAutoFit** model to hard-disk. -Checkout the file ``autofit_workspace/*/model/jsons/model.json`` to see the model written as a .json. +Checkout the file ``autofit_workspace/*/model/jsons/collection.json`` to see the model written as a .json. .. code-block:: python @@ -910,6 +927,321 @@ This gives the following output: normalization (Gaussian) = 5.0 sigma (Gaussian) = 6.0 +Model Composition (af.Array) +---------------------------- + +Models can be composed using NumPy arrays, where each element of the array is a free parameter. + +This offers a lot more flexibility than using ``Model`` and ``Collection`` objects, as the number of parameters in the +model is chosen on initialization via the input of the ``shape`` attribute. + +For many use cases, this flexibility is key to ensuring model composition is as easy as possible, for example when +a part of the model being fitted is a matrix of parameters which may change shape depending on the dataset being +fitted. + +To compose models using NumPy arrays, we use the ``af.Array`` object. + +.. code-block:: python + + model = af.Array( + shape=(2, 2), + prior=af.GaussianPrior(mean=0.0, sigma=1.0), + ) + +Each element of the array is a free parameter, which for ``shape=(2,2)`` means the model has 4 free parameters. + +.. code-block:: python + + print(f"Model Total Free Parameters = {model.total_free_parameters}") + +The ``info`` attribute of the model gives information on all of the parameters and their priors. + +.. code-block:: python + + print(model.info) + +This gives the following output: + +.. code-block:: bash + + Total Free Parameters = 4 + + model Array (N=4) + indices list (N=0) + + shape (2, 2) + indices + 0 (0, 0) + 1 (0, 1) + 2 (1, 0) + 3 (1, 1) + prior_0_0 GaussianPrior [124], mean = 0.0, sigma = 1.0 + prior_0_1 GaussianPrior [125], mean = 0.0, sigma = 1.0 + prior_1_0 GaussianPrior [126], mean = 0.0, sigma = 1.0 + prior_1_1 GaussianPrior [127], mean = 0.0, sigma = 1.0 + +Prior Customization (af.Array) +------------------------------ + +The prior of every parameter in the array is set via the ``prior`` input above. + +NumPy array models do not currently support default priors via config files, so all priors must be manually specified. + +The prior of every parameter in the array can be customized by normal NumPy array indexing: + +.. code-block:: python + + model = af.Array(shape=(2, 2), prior=af.GaussianPrior(mean=0.0, sigma=1.0)) + + model.array[0, 0] = af.UniformPrior(lower_limit=0.0, upper_limit=1.0) + model.array[0, 1] = af.LogUniformPrior(lower_limit=1e-4, upper_limit=1e4) + model.array[1, 0] = af.GaussianPrior(mean=0.0, sigma=2.0) + +The ``info`` attribute shows the customized priors. + +.. code-block:: python + + print(model.info) + +The output is as follows: + +.. code-block:: bash + + Total Free Parameters = 4 + + model Array (N=4) + indices list (N=0) + + shape (2, 2) + indices + 0 (0, 0) + 1 (0, 1) + 2 (1, 0) + 3 (1, 1) + prior_0_0 UniformPrior [133], lower_limit = 0.0, upper_limit = 1.0 + prior_0_1 LogUniformPrior [134], lower_limit = 0.0001, upper_limit = 10000.0 + prior_1_0 GaussianPrior [135], mean = 0.0, sigma = 2.0 + prior_1_1 GaussianPrior [132], mean = 0.0, sigma = 1.0 + +Instances (af.Array) +-------------------- + +Instances of numpy array model components can be created, where an input ``vector`` of parameters is mapped to create +an instance of the Python class of the model. + +If the priors of the numpy array are not customized, ordering of parameters goes from element [0,0] to [0,1] to [1,0], +as shown by the ``paths`` attribute. + +.. code-block:: python + + model = af.Array( + shape=(2, 2), + prior=af.GaussianPrior(mean=0.0, sigma=1.0), + ) + + print(model.paths) + +The output is as follows: + +.. code-block:: bash + + ['prior_0_0', 'prior_0_1', 'prior_1_0', 'prior_1_1'] + +An instance can then be created by passing a vector of parameters to the model via the ``instance_from_vector`` method. + +The ``instance`` created is a NumPy array, where each element is the value passed in the vector. + +.. code-block:: python + + instance = model.instance_from_vector(vector=[0.0, 1.0, 2.0, 3.0]) + + print("\nModel Instance:") + print(instance) + +The output is as follows: + +.. code-block:: bash + + Model Instance: + [[0. 1.] + [2. 3.]] + +Prior customization changes the order of the parameters, therefore if you customize the priors of the numpy +array you must check the ordering of the parameters in the ``paths`` attribute before passing a vector to +the ``instance_from_vector`` + + +.. code-block:: python + + model[0, 0] = af.UniformPrior(lower_limit=0.0, upper_limit=1.0) + model[0, 1] = af.LogUniformPrior(lower_limit=1e-4, upper_limit=1e4) + model[1, 0] = af.GaussianPrior(mean=0.0, sigma=2.0) + + print(model.paths) + +The output is as follows: + +.. code-block:: bash + + [('prior_1_1',), ('prior_0_0',), ('prior_0_1',), ('prior_1_0',)] + +If we create a vector and print its values from this customized model: + +.. code-block:: python + + instance = model.instance_from_vector(vector=[0.0, 1.0, 2.0, 3.0]) + + print("\nModel Instance:") + print(instance) + +The output is as follows: + +.. code-block:: bash + + Model Instance: + [[1. 2.] + [3. 0.]] + +Model Customization (af.Array) +------------------------------ + +The model customization API for numpy array models is the same as for ``af.Model`` and ``af.Collection`` objects. + +.. code-block:: python + + model = af.Array( + shape=(2, 2), + prior=af.GaussianPrior(mean=0.0, sigma=1.0), + ) + + model[0,0] = 50.0 + model[0,1] = model[1,0] + model.add_assertion(model[1,1] > 0.0) + + print(model.info) + +The output is as follows: + +.. code-block:: bash + Total Free Parameters = 2 + + model Array (N=2) + indices list (N=0) + + shape (2, 2) + indices + 0 (0, 0) + 1 (0, 1) + 2 (1, 0) + 3 (1, 1) + prior_0_0 50.0 + prior_0_1 - prior_1_0 GaussianPrior [147], mean = 0.0, sigma = 1.0 + prior_1_1 GaussianPrior [148], mean = 0.0, sigma = 1.0 + + +JSon Outputs (af.Array) +------------------------ + +An ``Array`` has a ``dict`` attribute, which express all information about the model as a Python dictionary. + +By printing this dictionary we can therefore get a concise summary of the model. + +.. code-block:: python + + model = af.Array( + shape=(2, 2), + prior=af.GaussianPrior(mean=0.0, sigma=1.0), + ) + + print(model.dict()) + +Python dictionaries can easily be saved to hard disk as a ``.json`` file. + +This means we can save any **PyAutoFit** model to hard-disk. + +Checkout the file ``autofit_workspace/*/model/jsons/array.json`` to see the model written as a .json. + +.. code-block:: python + + model_path = path.join("scripts", "model", "jsons") + + os.makedirs(model_path, exist_ok=True) + + model_file = path.join(model_path, "array.json") + + with open(model_file, "w+") as f: + json.dump(model.dict(), f, indent=4) + +We can load the model from its ``.json`` file, meaning that one can easily save a model to hard disk and load it +elsewhere. + +.. code-block:: python + + model = af.Array.from_json(file=model_file) + + print(f"\n Model via Json Prior Count = {model.prior_count}") + +Extensible Models (af.Array) +---------------------------- + +For ``Model`` objects, the number of parameters is fixed to those listed in the input Python class when the model is +created. + +For ``Collection`` objects, the use of dictionaries and lists allows for the number of parameters to be extended, but it +was still tied to the input Python classes when the model was created. + +For ``Array`` objects, the number of parameters is fully customizable, you choose the shape of the array and therefore +the number of parameters in the model when you create it. + +This makes ``Array`` objects the most extensible and flexible way to compose models. + +You can also combine ``Array`` objects with ``Collection`` objects to create models with a mix of fixed and extensible +parameters. + +.. code-block:: python + + model = af.Collection( + gaussian=Gaussian, + array=af.Array(shape=(3, 2), prior=af.GaussianPrior(mean=0.0, sigma=1.0)) + ) + + model.gaussian.sigma = 2.0 + model.array[0, 0] = 1.0 + + print(model.info) + +The output is as follows: + +.. code-block:: python + + Total Free Parameters = 7 + + model Collection (N=7) + gaussian Gaussian (N=2) + array Array (N=5) + indices list (N=0) + + gaussian + centre UniformPrior [165], lower_limit = 0.0, upper_limit = 100.0 + normalization LogUniformPrior [166], lower_limit = 1e-06, upper_limit = 1000000.0 + sigma 2.0 + array + shape (3, 2) + indices + 0 (0, 0) + 1 (0, 1) + 2 (1, 0) + 3 (1, 1) + 4 (2, 0) + 5 (2, 1) + prior_0_0 1.0 + prior_0_1 GaussianPrior [160], mean = 0.0, sigma = 1.0 + prior_1_0 GaussianPrior [161], mean = 0.0, sigma = 1.0 + prior_1_1 GaussianPrior [162], mean = 0.0, sigma = 1.0 + prior_2_0 GaussianPrior [163], mean = 0.0, sigma = 1.0 + prior_2_1 GaussianPrior [164], mean = 0.0, sigma = 1.0 + + Wrap Up ------- From 526f4c8cc7ebc3c0b35451a79625bde4ba17b0d9 Mon Sep 17 00:00:00 2001 From: James Nightingale Date: Tue, 23 Jul 2024 11:28:15 +0100 Subject: [PATCH 19/19] docs --- docs/overview/scientific_workflow.rst | 2 +- docs/overview/the_basics.rst | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/overview/scientific_workflow.rst b/docs/overview/scientific_workflow.rst index f6f1ed9be..ff670803a 100644 --- a/docs/overview/scientific_workflow.rst +++ b/docs/overview/scientific_workflow.rst @@ -525,7 +525,7 @@ For simpler scenarios, adjustments might include: In more intricate cases, models might involve numerous parameters and complex compositions of multiple model components. **PyAutoFit** offers a sophisticated model composition API designed to handle these complexities. It provides -tools for constructing elaborate models using lists of Python classes and hierarchical structures of Python classes. +tools for constructing elaborate models using lists of Python classes, NumPy arrays and hierarchical structures of Python classes. For a detailed exploration of these capabilities, you can refer to the `model cookbook `_, which provides comprehensive diff --git a/docs/overview/the_basics.rst b/docs/overview/the_basics.rst index a67e9302c..da3d213d5 100644 --- a/docs/overview/the_basics.rst +++ b/docs/overview/the_basics.rst @@ -298,13 +298,13 @@ Analysis We now tell **PyAutoFit** how to fit the model to the data. -We define an `Analysis` class, which includes: +We define an ``Analysis`` class, which includes: -- An `__init__` constructor that takes `data` and `noise_map` as inputs (this can be extended with additional elements necessary for fitting the model to the data). +- An ``__init__`` constructor that takes ``data`` and ``noise_map`` as inputs (this can be extended with additional elements necessary for fitting the model to the data). -- A `log_likelihood_function` that defines how to fit an `instance` of the model to the data and return a log likelihood value. +- A ``log_likelihood_function`` that defines how to fit an ``instance`` of the model to the data and return a log likelihood value. -Read the comments and docstrings of the `Analysis` class in detail for a full description of how the analysis works. +Read the comments and docstrings of the ``Analysis`` class in detail for a full description of how the analysis works. .. code-block:: python @@ -623,7 +623,7 @@ examples demonstrating more complex model-fitting tasks. This includes cookbooks, which provide a concise reference guide to the **PyAutoFit** API for advanced model-fitting: -- [Model Cookbook](https://pyautofit.readthedocs.io/en/latest/cookbooks/model.html): Learn how to compose complex models using multiple Python classes, lists, dictionaries, and customize their parameterization. +- [Model Cookbook](https://pyautofit.readthedocs.io/en/latest/cookbooks/model.html): Learn how to compose complex models using multiple Python classes, lists, dictionaries, NumPy arrays and customize their parameterization. - [Analysis Cookbook](https://pyautofit.readthedocs.io/en/latest/cookbooks/search.html): Customize the analysis with model-specific output and visualization to gain deeper insights into your model fits.