Repository: rhayes777/PyAutoFit
Pull request #942 (2024-02-17 18:01:22)

Feature/save results combined
There are a number of `_combined` methods which for summed `Analysis` objects allow one to plot the contents of all individual `Analysis` objects.

This PR adds a `save_results_combined` method which serves the same purpose for saving results to the `files` folder.


Patch:
diff --git a/autofit/config/general.yaml b/autofit/config/general.yaml
index bdfe2afae..0a70040f5 100644
--- a/autofit/config/general.yaml
+++ b/autofit/config/general.yaml
@@ -10,21 +10,19 @@ model:
   ignore_prior_limits: false        # If ``True`` the limits applied to priors will be ignored, where limits set upper / lower limits. This stops PriorLimitException's from being raised.
 output:
   force_pickle_overwrite: false     # If True pickle files output by a search (e.g. samples.pickle) are recreated when a new model-fit is performed.
-  force_visualize_overwrite: false # If True, visualization images output by a search (e.g. subplots of the fit) are recreated when a new model-fit is performed.
+  force_visualize_overwrite: false  # If True, visualization images output by a search (e.g. subplots of the fit) are recreated when a new model-fit is performed.
   info_whitespace_length: 80        # Length of whitespace between the parameter names and values in the model.info / result.info
   log_level: INFO                   # The level of information output by logging.
   log_to_file: false                # If True, outputs the non-linear search log to a file (and not printed to screen).
   log_file: output.log              # The name of the file the logged output is written to (in the non-linear search output folder)
   model_results_decimal_places: 3   # Number of decimal places estimated parameter values / errors are output in model.results.
   remove_files: false               # If True, all output files of a non-linear search (e.g. samples, visualization, etc.) are deleted once the model-fit has completed, such that only the .zip file remains.
+  search_internal : false    # If True, the search internal folder which contains a saved state of the non-linear search is delete, saving on hard-disk space.
   samples_to_csv: true              # If True, non-linear search samples are written to a .csv file.
   unconverged_sample_size : 100     # If outputting results of an unconverged search, the number of samples used to estimate the median PDF values and errors.
 parallel:
   warn_environment_variables: true  # If True, a warning is displayed when the search's number of CPU > 1 and enviromment variables related to threading are also > 1.
-prior_passer:
-  sigma: 3.0                        # For non-linear search chaining and model prior passing, the sigma value of the inferred model parameter used as the sigma of the passed Gaussian prior.
-  use_errors: true                  # If True, the errors of the previous model's results are used when passing priors.
-  use_widths: true                  # If True the width of the model parameters defined in the priors config file are used.
+
 profiling:
   parallel_profile: false           # If True, the parallelization of the fit is profiled outputting a cPython graph.
   should_profile: false             # If True, the ``profile_log_likelihood_function()`` function of an analysis class is called throughout a model-fit, profiling run times.
diff --git a/autofit/config/output.yaml b/autofit/config/output.yaml
index 0fe5fa3b1..72df85bf9 100644
--- a/autofit/config/output.yaml
+++ b/autofit/config/output.yaml
@@ -6,4 +6,42 @@
 
 # If a given file is not listed then the default value is used.
 
-default: true # If true then files which are not explicitly listed here are output anyway. If false then they are not.
\ No newline at end of file
+default: true # If true then files which are not explicitly listed here are output anyway. If false then they are not.
+
+### Samples ###
+
+# The `samples.csv`file contains every sampled value of every free parameter with its log likelihood and weight.
+
+# This file is often large, therefore disabling it can significantly reduce hard-disk space use.
+
+# `samples.csv` is used to perform marginalization, infer model parameter errors and do other analysis of the search
+# chains. Even if output of `samples.csv` is disabled, these tasks are still performed by the fit and output to
+# the `samples_summary.json` file. However, without a `samples.csv` file these types of tasks cannot be performed
+# after the fit is complete, for example via the database.
+
+samples: true
+
+### Search Internal ###
+
+# The search internal folder which contains a saved state of the non-linear search, as a .pickle or .dill file.
+
+# If the entry below is false, the folder is still output during the model-fit, as it is required to resume the fit
+# from where it left off. Therefore, settings `false` below does not impact model-fitting checkpointing and resumption.
+# Instead, the search internal folder is deleted once the fit is completed.
+
+# The search internal folder file is often large, therefore deleting it after a fit is complete can significantly
+# reduce hard-disk space use.
+
+# The search internal representation (e.g. what you can load from the output .pickle file) may have additional
+# quantities specific to the non-linear search that you are interested in inspecting. Deleting the folder means this
+# information is list.
+
+search_internal: true
+
+# Other Files:
+
+covariance: true # `covariance.csv`: The [free parameters x free parameters] covariance matrix.
+derived_quantities: true # `derived_quantities.csv`: Values of every derived quantity, which are not model free parameter but values computed from them.
+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.
+
diff --git a/autofit/example/analysis.py b/autofit/example/analysis.py
index c4f5d9296..3659254f9 100644
--- a/autofit/example/analysis.py
+++ b/autofit/example/analysis.py
@@ -1,5 +1,3 @@
-import json
-from os import path
 import os
 import matplotlib.pyplot as plt
 from typing import List
diff --git a/autofit/mapper/prior_model/abstract.py b/autofit/mapper/prior_model/abstract.py
index d99467b19..e60c87719 100644
--- a/autofit/mapper/prior_model/abstract.py
+++ b/autofit/mapper/prior_model/abstract.py
@@ -905,8 +905,8 @@ class AbstractPriorModel(AbstractModel):
     def gaussian_prior_model_for_arguments(self, arguments):
         raise NotImplementedError()
 
-    def mapper_from_gaussian_tuples(
-        self, tuples, a=None, r=None, use_errors=True, use_widths=True, no_limits=False
+    def mapper_from_prior_means(
+        self, means, a=None, r=None, no_limits=False
     ):
         """
         The widths of the new priors are taken from the
@@ -916,6 +916,8 @@ class AbstractPriorModel(AbstractModel):
         If r is not None then all priors are created with a relative width of r.
         Parameters
         ----------
+        means
+            The median PDF value of every Gaussian, which centres each `GaussianPrior`.
         no_limits
             If `True` generated priors have infinite limits
         r
@@ -923,14 +925,6 @@ class AbstractPriorModel(AbstractModel):
         a
             print(tuples[i][1], width)
             The absolute width to be assigned to gaussian priors
-        use_errors
-            If True, the passed errors of the model components estimated in a previous `NonLinearSearch` (computed
-            at the prior_passer.sigma value) are used to set the pass Gaussian Prior sigma value (if both width and
-            passed errors are used, the maximum of these two values are used).
-        use_widths
-            If True, the minimum prior widths specified in the prior configs of the model components are used to
-            set the passed Gaussian Prior sigma value (if both widths and passed errors are used, the maximum of
-            these two values are used).
         tuples
             A list of tuples each containing the mean and width of a prior
         Returns
@@ -943,10 +937,9 @@ class AbstractPriorModel(AbstractModel):
         prior_class_dict = self.prior_class_dict
         arguments = {}
 
-        for i, prior_tuple in enumerate(prior_tuples):
+        for prior_tuple, mean in zip(prior_tuples, means):
             prior = prior_tuple.prior
             cls = prior_class_dict[prior]
-            mean, sigma = tuples[i]
 
             name = prior_tuple.name
             # Use the name of the collection for configuration when a prior's name
@@ -968,7 +961,7 @@ class AbstractPriorModel(AbstractModel):
             elif r is not None:
                 width = r * mean
             else:
-                width = width_modifier(mean)
+                width = width_modifier(means)
 
             if no_limits:
                 limits = (float("-inf"), float("inf"))
@@ -978,17 +971,7 @@ class AbstractPriorModel(AbstractModel):
                 except ConfigException:
                     limits = prior.limits
 
-            if use_errors and not use_widths:
-                sigma = tuples[i][1]
-            elif not use_errors and use_widths:
-                sigma = width
-            elif use_errors and use_widths:
-                sigma = max(tuples[i][1], width)
-            else:
-                raise exc.PriorException(
-                    "use_passed_errors and use_widths are both False, meaning there is no "
-                    "way to pass priors to set up the new model's Gaussian Priors."
-                )
+            sigma = width
 
             new_prior = GaussianPrior(mean, sigma, *limits)
             new_prior.id = prior.id
diff --git a/autofit/non_linear/analysis/analysis.py b/autofit/non_linear/analysis/analysis.py
index e90094455..89841a881 100644
--- a/autofit/non_linear/analysis/analysis.py
+++ b/autofit/non_linear/analysis/analysis.py
@@ -110,6 +110,9 @@ class Analysis(ABC):
     def save_results(self, paths: AbstractPaths, result: Result):
         pass
 
+    def save_results_combined(self, paths: AbstractPaths, result: Result):
+        pass
+
     def modify_before_fit(self, paths: AbstractPaths, model: AbstractPriorModel):
         """
         Overwrite this method to modify the attributes of the `Analysis` class before the non-linear search begins.
diff --git a/autofit/non_linear/analysis/combined.py b/autofit/non_linear/analysis/combined.py
index 315ad03fc..603bdfa7d 100644
--- a/autofit/non_linear/analysis/combined.py
+++ b/autofit/non_linear/analysis/combined.py
@@ -195,6 +195,12 @@ class CombinedAnalysis(Analysis):
 
         self._for_each_analysis(func, paths, result)
 
+    def save_results_combined(self, paths: AbstractPaths, result: Result):
+        self.analyses[0].save_results_combined(
+            paths=paths,
+            result=result,
+        )
+
     def visualize_before_fit(self, paths: AbstractPaths, model: AbstractPriorModel):
         """
         Visualise the model before fitting.
diff --git a/autofit/non_linear/grid/simple_grid.py b/autofit/non_linear/grid/simple_grid.py
index a19b758fb..438781912 100644
--- a/autofit/non_linear/grid/simple_grid.py
+++ b/autofit/non_linear/grid/simple_grid.py
@@ -54,7 +54,6 @@ class GridSearch:
             samples=MockSamples(
                 max_log_likelihood_instance=best_instance,
                 log_likelihood_list=likelihoods,
-                gaussian_tuples=None
             ),
             model=model
         )
diff --git a/autofit/non_linear/mock/mock_result.py b/autofit/non_linear/mock/mock_result.py
index a8416849a..89d9ae038 100644
--- a/autofit/non_linear/mock/mock_result.py
+++ b/autofit/non_linear/mock/mock_result.py
@@ -21,7 +21,7 @@ class MockResult(Result):
             max_log_likelihood_instance=self.instance, model=model or ModelMapper()
         )
 
-        self.gaussian_tuples = None
+        self.prior_means = None
         self.analysis = analysis
         self.search = search
         self.model = model
diff --git a/autofit/non_linear/mock/mock_samples.py b/autofit/non_linear/mock/mock_samples.py
index bc0c8a489..d3d1d4ed2 100644
--- a/autofit/non_linear/mock/mock_samples.py
+++ b/autofit/non_linear/mock/mock_samples.py
@@ -16,7 +16,7 @@ class MockSamples(SamplesPDF):
         samples_info=None,
         max_log_likelihood_instance=None,
         log_likelihood_list=None,
-        gaussian_tuples=None,
+        prior_means=None,
         **kwargs,
     ):
         self._log_likelihood_list = log_likelihood_list
@@ -35,7 +35,7 @@ class MockSamples(SamplesPDF):
         )
 
         self._max_log_likelihood_instance = max_log_likelihood_instance
-        self._gaussian_tuples = gaussian_tuples
+        self._prior_means = prior_means
 
     @property
     def default_sample_list(self):
@@ -65,11 +65,12 @@ class MockSamples(SamplesPDF):
 
         return self._max_log_likelihood_instance
 
-    def gaussian_priors_at_sigma(self, sigma=None):
-        if self._gaussian_tuples is None:
-            return super().gaussian_priors_at_sigma(sigma=sigma)
+    @property
+    def prior_means(self):
+        if self._prior_means is None:
+            return super().prior_means
 
-        return self._gaussian_tuples
+        return self._prior_means
 
     @property
     def unconverged_sample_size(self):
diff --git a/autofit/non_linear/mock/mock_search.py b/autofit/non_linear/mock/mock_search.py
index f99ba1735..b1bd3c3d3 100644
--- a/autofit/non_linear/mock/mock_search.py
+++ b/autofit/non_linear/mock/mock_search.py
@@ -121,9 +121,8 @@ class MockSearch(NonLinearSearch):
                 self.sample_multiplier * fit, _make_samples(model)
             ),
             model=model,
-            gaussian_tuples=[
-                (prior.mean, prior.width if math.isfinite(prior.width) else 1.0)
-                for prior in sorted(model.priors, key=lambda prior: prior.id)
+            prior_means=[
+                prior.mean for prior in sorted(model.priors, key=lambda prior: prior.id)
             ],
         )
 
@@ -142,9 +141,8 @@ class MockSearch(NonLinearSearch):
             sample_list=samples_with_log_likelihood_list(
                 [1.0, 2.0], _make_samples(model)
             ),
-            gaussian_tuples=[
-                (prior.mean, prior.width if math.isfinite(prior.width) else 1.0)
-                for prior in sorted(model.priors, key=lambda prior: prior.id)
+            prior_means=[
+                prior.mean for prior in sorted(model.priors, key=lambda prior: prior.id)
             ],
             model=model,
         )
diff --git a/autofit/non_linear/paths/abstract.py b/autofit/non_linear/paths/abstract.py
index 0b5d2e805..1c39bb8ac 100644
--- a/autofit/non_linear/paths/abstract.py
+++ b/autofit/non_linear/paths/abstract.py
@@ -393,6 +393,9 @@ class AbstractPaths(ABC):
     def load_search_internal(self):
         raise NotImplementedError
 
+    def remove_search_internal(self):
+        raise NotImplementedError
+
     @property
     @abstractmethod
     def is_complete(self) -> bool:
diff --git a/autofit/non_linear/paths/database.py b/autofit/non_linear/paths/database.py
index 6cb5c62a8..b454b7ac0 100644
--- a/autofit/non_linear/paths/database.py
+++ b/autofit/non_linear/paths/database.py
@@ -226,6 +226,9 @@ class DatabasePaths(AbstractPaths):
     def load_search_internal(self):
         pass
 
+    def remove_search_internal(self):
+        pass
+
     @property
     def fit(self) -> Fit:
         if self._fit is None:
diff --git a/autofit/non_linear/paths/directory.py b/autofit/non_linear/paths/directory.py
index dd04763d1..6cfcd3a2a 100644
--- a/autofit/non_linear/paths/directory.py
+++ b/autofit/non_linear/paths/directory.py
@@ -1,3 +1,5 @@
+import shutil
+
 import dill
 import json
 import os
@@ -206,6 +208,17 @@ class DirectoryPaths(AbstractPaths):
         with open_(filename, "rb") as f:
             return dill.load(f)
 
+    def remove_search_internal(self):
+        """
+        Remove the internal representation of a non-linear search.
+
+        This deletes the entire `search_internal` folder, including a .pickle / .dill file containing the interal
+        results and files with the timer values.
+
+        This folder can often have a large filesize, thus deleting it can reduce hard-disk use of the model-fit.
+        """
+        shutil.rmtree(self.search_internal_path)
+
     def completed(self):
         """
         Mark the search as complete by saving a file
diff --git a/autofit/non_linear/paths/null.py b/autofit/non_linear/paths/null.py
index f15153a35..25cc64f94 100644
--- a/autofit/non_linear/paths/null.py
+++ b/autofit/non_linear/paths/null.py
@@ -87,6 +87,9 @@ class NullPaths(AbstractPaths):
     def load_search_internal(self):
         pass
 
+    def remove_search_internal(self):
+        pass
+
     @property
     def search_internal_path(self):
         pass
diff --git a/autofit/non_linear/result.py b/autofit/non_linear/result.py
index 386892e58..532456ba5 100644
--- a/autofit/non_linear/result.py
+++ b/autofit/non_linear/result.py
@@ -220,14 +220,13 @@ class Result(AbstractResult):
 
     @property
     def model(self):
-        use_errors = conf.instance["general"]["prior_passer"]["use_errors"]
-        use_widths = conf.instance["general"]["prior_passer"]["use_widths"]
 
         if self.__model is None:
-            tuples = self.samples.gaussian_priors_at_sigma(sigma=self.sigma)
-            self.__model = self.samples.model.mapper_from_gaussian_tuples(
-                tuples, use_errors=use_errors, use_widths=use_widths
+
+            self.__model = self.samples.model.mapper_from_prior_means(
+                means=self.samples.prior_means
             )
+
         return self.__model
 
     @model.setter
diff --git a/autofit/non_linear/samples/interface.py b/autofit/non_linear/samples/interface.py
index 21918799c..5b988378d 100644
--- a/autofit/non_linear/samples/interface.py
+++ b/autofit/non_linear/samples/interface.py
@@ -134,8 +134,8 @@ class SamplesInterface(ABC):
         A model mapper created by taking results from this search and creating priors with the defined absolute
         width.
         """
-        return self.model.mapper_from_gaussian_tuples(
-            self.gaussian_priors_at_sigma(sigma=self.sigma), a=a
+        return self.model.mapper_from_prior_means(
+            means=self.prior_means, a=a
         )
 
     def model_relative(self, r: float) -> AbstractPriorModel:
@@ -159,8 +159,8 @@ class SamplesInterface(ABC):
         A model mapper created by taking results from this search and creating priors with the defined relative
         width.
         """
-        return self.model.mapper_from_gaussian_tuples(
-            self.gaussian_priors_at_sigma(sigma=self.sigma), r=r
+        return self.model.mapper_from_prior_means(
+            means=self.prior_means, r=r
         )
 
     def model_bounded(self, b: float) -> AbstractPriorModel:
@@ -187,31 +187,21 @@ class SamplesInterface(ABC):
             floats=self.max_log_likelihood(as_instance=False), b=b
         )
 
-    @property
-    def sigma(self):
-        return conf.instance["general"]["prior_passer"]["sigma"]
+    def _instance_from_vector(self, vector: List[float]) -> ModelInstance:
+        return self.model.instance_from_vector(vector=vector, ignore_prior_limits=True)
 
-    def gaussian_priors_at_sigma(self, sigma: float) -> [List]:
+    @property
+    def prior_means(self) -> [List]:
         """
-        Returns `GaussianPrior`'s of every parameter in a fit for use with non-linear search chaining.
-
-        `GaussianPrior`s of every parameter used to link its inferred values and errors to priors used to sample the
+        The mean of every parameter used to link its inferred values and errors to priors used to sample the
         same (or similar) parameters in a subsequent search, where:
 
-        - The mean is given by maximum log likelihood model values.
-        - Their errors are omitted, as this information is not available from an search. When these priors are
-          used to link to another search, it will thus automatically use the prior config values.
-
-        Parameters
-        ----------
-        sigma
-            The sigma limit within which the PDF is used to estimate errors (e.g. sigma = 1.0 uses 0.6826 of the PDF).
+        - The mean is given by their most-probable values median PDF values if using a sampler that provides this
+          information(using median_pdf(as_instance=False)).
+        - The man is given by the maximum log likelihood values otherwise (e.g. for a maximum likelihood estimator).
         """
-        return list(
-            map(
-                lambda vector: (vector, 0.0), self.max_log_likelihood(as_instance=False)
-            )
-        )
 
-    def _instance_from_vector(self, vector: List[float]) -> ModelInstance:
-        return self.model.instance_from_vector(vector=vector, ignore_prior_limits=True)
+        try:
+            return self.median_pdf(as_instance=False)
+        except AttributeError:
+            return self.max_log_likelihood(as_instance=False)
diff --git a/autofit/non_linear/samples/pdf.py b/autofit/non_linear/samples/pdf.py
index 84b41de06..6e6efc5e3 100644
--- a/autofit/non_linear/samples/pdf.py
+++ b/autofit/non_linear/samples/pdf.py
@@ -304,37 +304,6 @@ class SamplesPDF(Samples):
         lowers = self.values_at_lower_sigma(sigma=sigma, as_instance=False)
         return list(map(lambda upper, lower: upper - lower, uppers, lowers))
 
-    def gaussian_priors_at_sigma(self, sigma: float) -> [List]:
-        """
-        `GaussianPrior`s of every parameter used to link its inferred values and errors to priors used to sample the
-        same (or similar) parameters in a subsequent search, where:
-
-        - The mean is given by their most-probable values (using median_pdf(as_instance=False)).
-        - Their errors are computed at an input sigma value (using errors_at_sigma).
-
-        Parameters
-        ----------
-        sigma
-            The sigma within which the PDF is used to estimate errors (e.g. sigma = 1.0 uses 0.6826 of the PDF).
-        """
-
-        means = self.median_pdf(as_instance=False)
-
-        uppers = self.values_at_upper_sigma(sigma=sigma, as_instance=False)
-        lowers = self.values_at_lower_sigma(sigma=sigma, as_instance=False)
-
-        # noinspection PyArgumentList
-        sigmas = list(
-            map(
-                lambda mean, upper, lower: max([upper - mean, mean - lower]),
-                means,
-                uppers,
-                lowers,
-            )
-        )
-
-        return list(map(lambda mean, sigma: (mean, sigma), means, sigmas))
-
     @to_instance
     def draw_randomly_via_pdf(self) -> Union[List, ModelInstance]:
         """
diff --git a/autofit/non_linear/search/abstract_search.py b/autofit/non_linear/search/abstract_search.py
index 19e02933e..8e018d7e5 100644
--- a/autofit/non_linear/search/abstract_search.py
+++ b/autofit/non_linear/search/abstract_search.py
@@ -697,6 +697,7 @@ class NonLinearSearch(AbstractFactorOptimiser, ABC):
 
         if self.is_master:
             analysis.save_results(paths=self.paths, result=result)
+            analysis.save_results_combined(paths=self.paths, result=result)
 
         model.unfreeze()
 
@@ -764,6 +765,7 @@ class NonLinearSearch(AbstractFactorOptimiser, ABC):
                     )
 
                 analysis.save_results(paths=self.paths, result=result)
+                analysis.save_results_combined(paths=self.paths, result=result)
 
         model.unfreeze()
 
@@ -785,6 +787,10 @@ class NonLinearSearch(AbstractFactorOptimiser, ABC):
         bypass_nuclear_if_on
             Whether to use nuclear mode to delete a lot of files (see nuclear mode description).
         """
+        if not conf.instance["output"]["search_internal"]:
+            self.logger.info("Removing search internal folder.")
+            self.paths.remove_search_internal()
+
         self.logger.info("Removing all files except for .zip file")
         self.paths.zip_remove()
 
@@ -922,7 +928,10 @@ class NonLinearSearch(AbstractFactorOptimiser, ABC):
             self.paths.save_samples(samples=samples)
 
             if not during_analysis:
-                self.paths.save_derived_quantities(samples=samples)
+                try:
+                    self.paths.save_derived_quantities(samples=samples)
+                except exc.FitException:
+                    pass
 
             if not self.skip_save_samples:
                 self.paths.save_json("samples_summary", to_dict(samples.summary()))
diff --git a/docs/cookbooks/result.rst b/docs/cookbooks/result.rst
index fbb68ce14..cb785450f 100644
--- a/docs/cookbooks/result.rst
+++ b/docs/cookbooks/result.rst
@@ -609,7 +609,7 @@ complicated derived quantities.)
 
         fwhm_list.append(fwhm)
 
-    median_fwhm, upper_fwhm, lower_fwhm = af.marginalize(
+    median_fwhm, lower_fwhm, upper_fwhm = af.marginalize(
         parameter_list=fwhm_list, sigma=3.0, weight_list=samples.weight_list
     )
 


Test patch:
diff --git a/test_autofit/config/general.yaml b/test_autofit/config/general.yaml
index 7a4f230e0..24d96ecb5 100644
--- a/test_autofit/config/general.yaml
+++ b/test_autofit/config/general.yaml
@@ -19,10 +19,7 @@ output:
   samples_to_csv: false
 parallel:
   warn_environment_variables: false # If True, a warning is displayed when the search's number of CPU > 1 and enviromment variables related to threading are also > 1.
-prior_passer:
-  sigma: 3.0                        # For non-linear search chaining and model prior passing, the sigma value of the inferred model parameter used as the sigma of the passed Gaussian prior.
-  use_errors: true                  # If True, the errors of the previous model's results are used when passing priors.
-  use_widths: true                  # If True the width of the model parameters defined in the priors config file are used.
+
 profiling:
   parallel_profile: false           # If True, the parallelization of the fit is profiled outputting a cPython graph.
   should_profile: false             # If True, the ``profile_log_likelihood_function()`` function of an analysis class is called throughout a model-fit, profiling run times.
diff --git a/test_autofit/database/identifier/test_identifiers.py b/test_autofit/database/identifier/test_identifiers.py
index 692ea96f5..cd86f82d7 100644
--- a/test_autofit/database/identifier/test_identifiers.py
+++ b/test_autofit/database/identifier/test_identifiers.py
@@ -291,7 +291,7 @@ def test__identifier_description__after_model_and_instance():
 
     samples = af.m.MockSamples(
         max_log_likelihood_instance=max_log_likelihood_instance,
-        gaussian_tuples=[(1.0, 2.0), (3.0, 4.0), (5.0, 6.0)],
+        prior_means=[1.0, 3.0, 5.0],
         model=model,
     )
 
@@ -323,7 +323,7 @@ upper_limit
 mean
 1.0
 sigma
-2.0
+1.0
 normalization
 0.00316228
 sigma
diff --git a/test_autofit/mapper/model/test_model_mapper.py b/test_autofit/mapper/model/test_model_mapper.py
index 20c0f5b0e..41e42d7f8 100644
--- a/test_autofit/mapper/model/test_model_mapper.py
+++ b/test_autofit/mapper/model/test_model_mapper.py
@@ -530,7 +530,7 @@ class TestUtility:
 class TestPriorReplacement:
     def test_prior_replacement(self):
         mapper = af.ModelMapper(mock_class=af.m.MockClassx2)
-        result = mapper.mapper_from_gaussian_tuples([(10, 3), (5, 3)])
+        result = mapper.mapper_from_prior_means([10, 5])
 
         assert isinstance(result.mock_class.one, af.GaussianPrior)
         assert {prior.id for prior in mapper.priors} == {
@@ -539,13 +539,13 @@ class TestPriorReplacement:
 
     def test_replace_priors_with_gaussians_from_tuples(self):
         mapper = af.ModelMapper(mock_class=af.m.MockClassx2)
-        result = mapper.mapper_from_gaussian_tuples([(10, 3), (5, 3)])
+        result = mapper.mapper_from_prior_means([10, 5])
 
         assert isinstance(result.mock_class.one, af.GaussianPrior)
 
     def test_replacing_priors_for_profile(self):
         mapper = af.ModelMapper(mock_class=af.m.MockClassx3TupleFloat)
-        result = mapper.mapper_from_gaussian_tuples([(10, 3), (5, 3), (5, 3)])
+        result = mapper.mapper_from_prior_means([10, 5, 5])
 
         assert isinstance(
             result.mock_class.one_tuple.unique_prior_tuples[0][1], af.GaussianPrior
@@ -558,7 +558,7 @@ class TestPriorReplacement:
     def test_replace_priors_for_two_classes(self):
         mapper = af.ModelMapper(one=af.m.MockClassx2, two=af.m.MockClassx2)
 
-        result = mapper.mapper_from_gaussian_tuples([(1, 1), (2, 1), (3, 1), (4, 1)])
+        result = mapper.mapper_from_prior_means([1, 2, 3, 4])
 
         assert result.one.one.mean == 1
         assert result.one.two.mean == 2
@@ -630,8 +630,8 @@ class TestListPriorModel:
         mapper = af.ModelMapper()
         mapper.list = list_prior_model
 
-        gaussian_mapper = mapper.mapper_from_gaussian_tuples(
-            [(1, 5), (2, 5), (3, 5), (4, 5)]
+        gaussian_mapper = mapper.mapper_from_prior_means(
+            [1, 2, 3, 4]
         )
 
         assert len(gaussian_mapper.list) == 2
@@ -639,10 +639,10 @@ class TestListPriorModel:
         assert gaussian_mapper.list[0].two.mean == 2
         assert gaussian_mapper.list[1].one.mean == 3
         assert gaussian_mapper.list[1].two.mean == 4
-        assert gaussian_mapper.list[0].one.sigma == 5
-        assert gaussian_mapper.list[0].two.sigma == 5
-        assert gaussian_mapper.list[1].one.sigma == 5
-        assert gaussian_mapper.list[1].two.sigma == 5
+        assert gaussian_mapper.list[0].one.sigma == 1
+        assert gaussian_mapper.list[0].two.sigma == 2
+        assert gaussian_mapper.list[1].one.sigma == 1
+        assert gaussian_mapper.list[1].two.sigma == 2
 
     def test_prior_results_for_gaussian_tuples__include_override_from_width_file(
         self, list_prior_model
@@ -650,8 +650,8 @@ class TestListPriorModel:
         mapper = af.ModelMapper()
         mapper.list = list_prior_model
 
-        gaussian_mapper = mapper.mapper_from_gaussian_tuples(
-            [(1, 0), (2, 0), (3, 0), (4, 0)]
+        gaussian_mapper = mapper.mapper_from_prior_means(
+            [1, 2, 3, 4]
         )
 
         assert len(gaussian_mapper.list) == 2
@@ -758,7 +758,7 @@ def make_mapper_with_list():
 class TestGaussianWidthConfig:
     def test_relative_widths(self, mapper):
         mapper.relative_width = af.m.MockClassRelativeWidth
-        new_mapper = mapper.mapper_from_gaussian_tuples([(1, 0), (1, 0), (1, 0)])
+        new_mapper = mapper.mapper_from_prior_means([1, 1, 1])
 
         assert new_mapper.relative_width.one.mean == 1.0
         assert new_mapper.relative_width.one.sigma == 0.1
diff --git a/test_autofit/mapper/model/test_regression.py b/test_autofit/mapper/model/test_regression.py
index d43976fc6..a117389d6 100644
--- a/test_autofit/mapper/model/test_regression.py
+++ b/test_autofit/mapper/model/test_regression.py
@@ -94,12 +94,7 @@ def test_set_centre():
 def test_passing_priors():
     model = af.Model(af.m.MockWithTuple)
 
-    new_model = model.mapper_from_gaussian_tuples(
-        [
-            (1, 1),
-            (1, 1),
-        ]
-    )
+    new_model = model.mapper_from_prior_means([1, 1])
     assert isinstance(new_model.tup_0, af.GaussianPrior)
     assert isinstance(new_model.tup_1, af.GaussianPrior)
 
@@ -109,7 +104,7 @@ def test_passing_fixed():
     model.tup_0 = 0.1
     model.tup_1 = 2.0
 
-    new_model = model.mapper_from_gaussian_tuples([])
+    new_model = model.mapper_from_prior_means([])
     assert new_model.tup_0 == 0.1
     assert new_model.tup_1 == 2.0
 
diff --git a/test_autofit/mapper/prior/test_prior.py b/test_autofit/mapper/prior/test_prior.py
index 469e46bca..ad2207071 100644
--- a/test_autofit/mapper/prior/test_prior.py
+++ b/test_autofit/mapper/prior/test_prior.py
@@ -94,8 +94,8 @@ class TestPriorLimits:
         mm = af.ModelMapper()
         mm.mock_class_gaussian = af.m.MockClassx2
 
-        new_mapper = mm.mapper_from_gaussian_tuples(
-            tuples=[(0.0, 0.5), (0.0, 1)], use_widths=True, use_errors=True
+        new_mapper = mm.mapper_from_prior_means(
+            means=[0.0, 0.0],
         )
 
         prior_tuples = new_mapper.prior_tuples_ordered_by_id
@@ -106,28 +106,12 @@ class TestPriorLimits:
         assert prior_tuples[1].prior.lower_limit == 0
         assert prior_tuples[1].prior.upper_limit == 2
 
-    def test__only_use_passed_errors_to_set_up_gaussian_prior(self):
-        mm = af.ModelMapper()
-        mm.mock_class_gaussian = af.m.MockClassx2
-
-        new_mapper = mm.mapper_from_gaussian_tuples(
-            tuples=[(0.1, 0.2), (0.3, 0.4)], use_widths=False, use_errors=True
-        )
-
-        prior_tuples = new_mapper.prior_tuples_ordered_by_id
-
-        assert prior_tuples[0].prior.mean == 0.1
-        assert prior_tuples[0].prior.sigma == 0.2
-
-        assert prior_tuples[1].prior.mean == 0.3
-        assert prior_tuples[1].prior.sigma == 0.4
-
     def test__only_use_widths_to_pass_priors(self):
         mm = af.ModelMapper()
         mm.mock_class_gaussian = af.m.MockClassx2
 
-        new_mapper = mm.mapper_from_gaussian_tuples(
-            tuples=[(5.0, 5.0), (5.0, 5.0)], use_widths=True, use_errors=False
+        new_mapper = mm.mapper_from_prior_means(
+            means=[5.0, 5.0],
         )
 
         prior_tuples = new_mapper.prior_tuples_ordered_by_id
@@ -138,27 +122,11 @@ class TestPriorLimits:
         assert prior_tuples[1].prior.mean == 5.0
         assert prior_tuples[1].prior.sigma == 2.0
 
-    def test__use_max_of_widths_and_passed_errors_to_pass_priors(self):
-        mm = af.ModelMapper()
-        mm.mock_class_gaussian = af.m.MockClassx2
-
-        new_mapper = mm.mapper_from_gaussian_tuples(
-            tuples=[(5.0, 0.2), (5.0, 5.0)], use_widths=True, use_errors=True
-        )
-
-        prior_tuples = new_mapper.prior_tuples_ordered_by_id
-
-        assert prior_tuples[0].prior.mean == 5.0
-        assert prior_tuples[0].prior.sigma == 1.0
-
-        assert prior_tuples[1].prior.mean == 5.0
-        assert prior_tuples[1].prior.sigma == 5.0
-
     def test_from_gaussian_no_limits(self):
         mm = af.ModelMapper()
         mm.mock_class_gaussian = af.m.MockClassx2
 
-        new_mapper = mm.mapper_from_gaussian_tuples(
+        new_mapper = mm.mapper_from_prior_means(
             [(0.0, 0.5), (0.0, 1)], no_limits=True
         )
 
diff --git a/test_autofit/mapper/test_explicit_width_modifier.py b/test_autofit/mapper/test_explicit_width_modifier.py
index 843e4ccdf..cc43fc367 100644
--- a/test_autofit/mapper/test_explicit_width_modifier.py
+++ b/test_autofit/mapper/test_explicit_width_modifier.py
@@ -13,7 +13,7 @@ def make_updated_model(width_modifier):
     model = af.Model(af.Gaussian)
     model.centre.width_modifier = width_modifier
 
-    return model.mapper_from_gaussian_tuples([(1.0, 1.0), (1.0, 1.0), (1.0, 1.0),])
+    return model.mapper_from_prior_means([1.0, 1.0, 1.0])
 
 
 def test_explicit_width_modifier(updated_model):
diff --git a/test_autofit/non_linear/result/test_result.py b/test_autofit/non_linear/result/test_result.py
index 961e159d3..b4fd41151 100644
--- a/test_autofit/non_linear/result/test_result.py
+++ b/test_autofit/non_linear/result/test_result.py
@@ -9,7 +9,7 @@ def make_result():
     mapper.component = af.m.MockClassx2Tuple
     # noinspection PyTypeChecker
     return af.Result(
-        samples=af.m.MockSamples(max_log_likelihood_instance=[0, 1], gaussian_tuples=[(0, 0), (1, 0)], model=mapper),
+        samples=af.m.MockSamples(max_log_likelihood_instance=[0, 1], prior_means=[0, 1], model=mapper),
     )
 
 
@@ -47,8 +47,8 @@ class TestResult:
 
     def test_raises(self, result):
         with pytest.raises(af.exc.PriorException):
-            result.model.mapper_from_gaussian_tuples(
-                result.samples._gaussian_tuples, a=2.0, r=1.0
+            result.model.mapper_from_prior_means(
+                result.samples.prior_means, a=2.0, r=1.0
             )
 
 
diff --git a/test_autofit/non_linear/samples/test_samples.py b/test_autofit/non_linear/samples/test_samples.py
index b0036ece0..35436b6f4 100644
--- a/test_autofit/non_linear/samples/test_samples.py
+++ b/test_autofit/non_linear/samples/test_samples.py
@@ -104,40 +104,6 @@ def test__max_log_posterior():
     assert instance.mock_class_1.four == 24.0
 
 
-def test__gaussian_priors():
-    parameters = [
-        [1.0, 2.0, 3.0, 4.0],
-        [1.0, 2.0, 3.0, 4.1],
-        [1.0, 2.0, 3.0, 4.1],
-        [0.88, 1.88, 2.88, 3.88],
-        [1.12, 2.12, 3.12, 4.32],
-    ]
-
-    model = af.Collection(mock_class=af.m.MockClassx4)
-    samples_x5 = af.m.MockSamples(
-        model=model,
-        sample_list=af.Sample.from_lists(
-            model=model,
-            parameter_lists=parameters,
-            log_likelihood_list=[10.0, 0.0, 0.0, 0.0, 0.0],
-            log_prior_list=[0.0, 0.0, 0.0, 0.0, 0.0],
-            weight_list=[1.0, 1.0, 1.0, 1.0, 1.0],
-        ),
-    )
-
-    gaussian_priors = samples_x5.gaussian_priors_at_sigma(sigma=1.0)
-
-    assert gaussian_priors[0][0] == 1.0
-    assert gaussian_priors[1][0] == 2.0
-    assert gaussian_priors[2][0] == 3.0
-    assert gaussian_priors[3][0] == 4.0
-
-    assert gaussian_priors[0][1] == pytest.approx(0.12, 1.0e-4)
-    assert gaussian_priors[1][1] == pytest.approx(0.12, 1.0e-4)
-    assert gaussian_priors[2][1] == pytest.approx(0.12, 1.0e-4)
-    assert gaussian_priors[3][1] == pytest.approx(0.32, 1.0e-4)
-
-
 def test__instance_from_sample_index():
     model = af.Collection(mock_class=af.m.MockClassx4)
 
diff --git a/test_autofit/non_linear/search/test_abstract_search.py b/test_autofit/non_linear/search/test_abstract_search.py
index f5f5b1058..117666efb 100644
--- a/test_autofit/non_linear/search/test_abstract_search.py
+++ b/test_autofit/non_linear/search/test_abstract_search.py
@@ -24,7 +24,7 @@ def make_result():
     # noinspection PyTypeChecker
     return af.Result(
         samples=af.m.MockSamples(
-            gaussian_tuples=[(0, 0), (1, 0)],
+            prior_means=[0, 1],
             model=mapper,
         ),
     )
@@ -65,8 +65,8 @@ class TestResult:
 
     def test_raises(self, result):
         with pytest.raises(af.exc.PriorException):
-            result.model.mapper_from_gaussian_tuples(
-                result.samples._gaussian_tuples, a=2.0, r=1.0
+            result.model.mapper_from_prior_means(
+                result.samples.prior_means, a=2.0, r=1.0
             )