Repository: rhayes777/PyAutoFit (python)

Issue:
Concise `model.info` for Models with lists of priors
In PyAutoLens, we often compose a model where a galaxy is made of 90 Gaussian's:

```
    analysis = al.AnalysisImaging(
        dataset=dataset,
        adapt_images=source_pix_results[0].adapt_images_from()
    )

    centre_0 = af.UniformPrior(lower_limit=-0.1, upper_limit=0.1)
    centre_1 = af.UniformPrior(lower_limit=-0.1, upper_limit=0.1)

    total_gaussians = 30
    gaussian_per_basis = 3

    log10_sigma_list = np.linspace(-2, np.log10(mask_radius), total_gaussians)

    bulge_gaussian_list = []

    for j in range(gaussian_per_basis):

        gaussian_list = af.Collection(af.Model(al.lp_linear.Gaussian) for _ in range(total_gaussians))

        for i, gaussian in enumerate(gaussian_list):
            gaussian.centre.centre_0 = centre_0
            gaussian.centre.centre_1 = centre_1
            gaussian.ell_comps = gaussian_list[0].ell_comps
            gaussian.sigma = 10 ** log10_sigma_list[i]

        bulge_gaussian_list += gaussian_list

    lens_bulge = af.Model(
        al.lp_basis.Basis,
        light_profile_list=bulge_gaussian_list,
    )
```

The `model.info` file of this model is... large:

```
Total Free Parameters = 12

model                                                                           Collection (N=12)
    galaxies                                                                    Collection (N=12)
        lens                                                                    Galaxy (N=12)
            bulge                                                               Basis (N=8)
                light_profile_list                                              Collection (N=8)
                    0                                                           Gaussian (N=4)
                    1                                                           Gaussian (N=4)
                    2                                                           Gaussian (N=4)
                    3                                                           Gaussian (N=4)
                    4                                                           Gaussian (N=4)
                    5                                                           Gaussian (N=4)
                    6                                                           Gaussian (N=4)
                    7                                                           Gaussian (N=4)
                    8                                                           Gaussian (N=4)
                    9                                                           Gaussian (N=4)
                    10                                                          Gaussian (N=4)
                    11                                                          Gaussian (N=4)
                    12                                                          Gaussian (N=4)
                    13                                                          Gaussian (N=4)
                    14                                                          Gaussian (N=4)
                    15                                                          Gaussian (N=4)
                    16                                                          Gaussian (N=4)
                    17                                                          Gaussian (N=4)
                    18                                                          Gaussian (N=4)
                    19                                                          Gaussian (N=4)
                    20                                                          Gaussian (N=4)
                    21                                                          Gaussian (N=4)
                    22                                                          Gaussian (N=4)
                    23                                                          Gaussian (N=4)
                    24                                                          Gaussian (N=4)
                    25                                                          Gaussian (N=4)
                    26                                                          Gaussian (N=4)
                    27                                                          Gaussian (N=4)
                    28                                                          Gaussian (N=4)
                    29                                                          Gaussian (N=4)
                    30                                                          Gaussian (N=4)
                    31                                                          Gaussian (N=4)
                    32                                                          Gaussian (N=4)
                    33                                                          Gaussian (N=4)
                    34                                                          Gaussian (N=4)
                    35                                                          Gaussian (N=4)
                    36                                                          Gaussian (N=4)
                    37                                                          Gaussian (N=4)
                    38                                                          Gaussian (N=4)
                    39                                                          Gaussian (N=4)
                    40                                                          Gaussian (N=4)
                    41                                                          Gaussian (N=4)
                    42                                                          Gaussian (N=4)
                    43                                                          Gaussian (N=4)
                    44                                                          Gaussian (N=4)
                    45                                                          Gaussian (N=4)
                    46                                                          Gaussian (N=4)
                    47                                                          Gaussian (N=4)
                    48                                                          Gaussian (N=4)
                    49                                                          Gaussian (N=4)
                    50                                                          Gaussian (N=4)
                    51                                                          Gaussian (N=4)
                    52                                                          Gaussian (N=4)
                    53                                                          Gaussian (N=4)
                    54                                                          Gaussian (N=4)
                    55                                                          Gaussian (N=4)
                    56                                                          Gaussian (N=4)
                    57                                                          Gaussian (N=4)
                    58                                                          Gaussian (N=4)
                    59                                                          Gaussian (N=4)
                    60                                                          Gaussian (N=4)
                    61                                                          Gaussian (N=4)
                    62                                                          Gaussian (N=4)
                    63                                                          Gaussian (N=4)
                    64                                                          Gaussian (N=4)
                    65                                                          Gaussian (N=4)
                    66                                                          Gaussian (N=4)
                    67                                                          Gaussian (N=4)
                    68                                                          Gaussian (N=4)
                    69                                                          Gaussian (N=4)
                    70                                                          Gaussian (N=4)
                    71                                                          Gaussian (N=4)
                    72                                                          Gaussian (N=4)
                    73                                                          Gaussian (N=4)
                    74                                                          Gaussian (N=4)
                    75                                                          Gaussian (N=4)
                    76                                                          Gaussian (N=4)
                    77                                                          Gaussian (N=4)
                    78                                                          Gaussian (N=4)
                    79                                                          Gaussian (N=4)
                    80                                                          Gaussian (N=4)
                    81                                                          Gaussian (N=4)
                    82                                                          Gaussian (N=4)
                    83                                                          Gaussian (N=4)
                    84                                                          Gaussian (N=4)
                    85                                                          Gaussian (N=4)
                    86                                                          Gaussian (N=4)
                    87                                                          Gaussian (N=4)
                    88                                                          Gaussian (N=4)
                    89                                                          Gaussian (N=4)
            point                                                               Basis (N=4)
                light_profile_list                                              Collection (N=4)
                    0                                                           Gaussian (N=4)
                    1                                                           Gaussian (N=4)
                    2                                                           Gaussian (N=4)
                    3                                                           Gaussian (N=4)
                    4                                                           Gaussian (N=4)
                    5                                                           Gaussian (N=4)
                    6                                                           Gaussian (N=4)
                    7                                                           Gaussian (N=4)
                    8                                                           Gaussian (N=4)
                    9                                                           Gaussian (N=4)
            mass                                                                Isothermal (N=0)
            shear                                                               ExternalShear (N=0)
        source                                                                  Galaxy (N=0)
            pixelization                                                        Pixelization (N=0)
                regularization                                                  AdaptiveBrightness (N=0)
                image_mesh                                                      Hilbert (N=0)

galaxies
    lens
        redshift                                                                0.44
        bulge
            light_profile_list
                0
                    centre
                        centre_0                                                UniformPrior [425], lower_limit = -0.1, upper_limit = 0.1
                        centre_1                                                UniformPrior [426], lower_limit = -0.1, upper_limit = 0.1
                    ell_comps
                        ell_comps_0                                             GaussianPrior [429], mean = 0.0, sigma = 0.3
                        ell_comps_1                                             GaussianPrior [430], mean = 0.0, sigma = 0.3
                    sigma                                                       0.01
                1
                    centre
                        centre_0                                                UniformPrior [425], lower_limit = -0.1, upper_limit = 0.1
                        centre_1                                                UniformPrior [426], lower_limit = -0.1, upper_limit = 0.1
                    ell_comps
                        ell_comps_0                                             GaussianPrior [429], mean = 0.0, sigma = 0.3
                        ell_comps_1                                             GaussianPrior [430], mean = 0.0, sigma = 0.3
                    sigma                                                       0.012294934136946248
                2
                    centre
                        centre_0                                                UniformPrior [425], lower_limit = -0.1, upper_limit = 0.1
                        centre_1                                                UniformPrior [426], lower_limit = -0.1, upper_limit = 0.1
                    ell_comps
                        ell_comps_0                                             GaussianPrior [429], mean = 0.0, sigma = 0.3
                        ell_comps_1                                             GaussianPrior [430], mean = 0.0, sigma = 0.3
                    sigma                                                       0.01511654054318462
                3
                    centre
                        centre_0                                                UniformPrior [425], lower_limit = -0.1, upper_limit = 0.1
                        centre_1                                                UniformPrior [426], lower_limit = -0.1, upper_limit = 0.1
                    ell_comps
                        ell_comps_0                                             GaussianPrior [429], mean = 0.0, sigma = 0.3
                        ell_comps_1                                             GaussianPrior [430], mean = 0.0, sigma = 0.3
                    sigma                                                       0.018585687035693257
                4
                    centre
                        centre_0                                                UniformPrior [425], lower_limit = -0.1, upper_limit = 0.1
                        centre_1                                                UniformPrior [426], lower_limit = -0.1, upper_limit = 0.1
                    ell_comps
                        ell_comps_0                                             GaussianPrior [429], mean = 0.0, sigma = 0.3
                        ell_comps_1                                             GaussianPrior [430], mean = 0.0, sigma = 0.3
                    sigma                                                       0.022850979799374435
                5
                    centre
                        centre_0                                                UniformPrior [425], lower_limit = -0.1, upper_limit = 0.1
                        centre_1                                                UniformPrior [426], lower_limit = -0.1, upper_limit = 0.1
                    ell_comps
                        ell_comps_0                                             GaussianPrior [429], mean = 0.0, sigma = 0.3
                        ell_comps_1                                             GaussianPrior [430], mean = 0.0, sigma = 0.3
                    sigma                                                       0.02809512915979979
                6
                    centre
                        centre_0                                                UniformPrior [425], lower_limit = -0.1, upper_limit = 0.1
                        centre_1                                                UniformPrior [426], lower_limit = -0.1, upper_limit = 0.1
                    ell_comps
                        ell_comps_0                                             GaussianPrior [429], mean = 0.0, sigma = 0.3
                        ell_comps_1                                             GaussianPrior [430], mean = 0.0, sigma = 0.3
                    sigma                                                       0.03454277625887364
                7
                    centre
                        centre_0                                                UniformPrior [425], lower_limit = -0.1, upper_limit = 0.1
                        centre_1                                                UniformPrior [426], lower_limit = -0.1, upper_limit = 0.1
                    ell_comps
                        ell_comps_0                                             GaussianPrior [429], mean = 0.0, sigma = 0.3
                        ell_comps_1                                             GaussianPrior [430], mean = 0.0, sigma = 0.3
                    sigma                                                       0.04247011590101218
                8
                    centre
                        centre_0                                                UniformPrior [425], lower_limit = -0.1, upper_limit = 0.1
                        centre_1                                                UniformPrior [426], lower_limit = -0.1, upper_limit = 0.1
                    ell_comps
                        ell_comps_0                                             GaussianPrior [429], mean = 0.0, sigma = 0.3
                        ell_comps_1                                             GaussianPrior [430], mean = 0.0, sigma = 0.3
                    sigma                                                       0.05221672777914186
                9
                    centre
                        centre_0                                                UniformPrior [425], lower_limit = -0.1, upper_limit = 0.1
                        centre_1                                                UniformPrior [426], lower_limit = -0.1, upper_limit = 0.1
                    ell_comps
                        ell_comps_0                                             GaussianPrior [429], mean = 0.0, sigma = 0.3
                        ell_comps_1                                             GaussianPrior [430], mean = 0.0, sigma = 0.3
                    sigma                                                       0.06420012288914004
                10
                    centre
                        centre_0                                                UniformPrior [425], lower_limit = -0.1, upper_limit = 0.1
                        centre_1                                                UniformPrior [426], lower_limit = -0.1, upper_limit = 0.1
                    ell_comps
                        ell_comps_0                                             GaussianPrior [429], mean = 0.0, sigma = 0.3
                        ell_comps_1                                             GaussianPrior [430], mean = 0.0, sigma = 0.3
                    sigma                                                       0.07893362825058325
   
        ....and so on ...


        mass
            centre                                                              (0.00601884753478887, 0.023999143834638678)
            ell_comps                                                           (0.16939516274352906, 0.0024119266740726814)
            einstein_radius                                                     1.1677486883648802
            slope                                                               2.0
            core_radius                                                         0.0
        shear
            centre                                                              (0.0, 0.0)
            ell_comps                                                           (0.0, 0.0)
            gamma_1                                                             -0.02979114689616666
            gamma_2                                                             -0.0030780392697618564
    source
        redshift                                                                1.192
        pixelization
            regularization
                inner_coefficient                                               0.0010924117924697946
                outer_coefficient                                               92.20305832737559
                signal_scale                                                    0.03604121120465
            image_mesh
                pixels                                                          1000
                weight_floor                                                    7.284022314666776e-05
                weight_power                                                    2.62031156076238
```

The question is, how do we make this more concise?

There is repetition in the list of model component types:

```
                light_profile_list                                              Collection (N=8)
                    0                                                           Gaussian (N=4)
                    1                                                           Gaussian (N=4)
                    2                                                           Gaussian (N=4)
                    3                                                           Gaussian (N=4)
                     ...
```

We can condense this as follows:

```
                light_profile_list                                              Collection (N=8)
                    0 -89                                                      Gaussian (N=4)
```


There is constant repetition of priors, for example all `Gaussian` objects from 0 to 29 have the same priors:

```        bulge
            light_profile_list
                0
                    centre
                        centre_0                                                UniformPrior [425], lower_limit = -0.1, upper_limit = 0.1
                        centre_1                                                UniformPrior [426], lower_limit = -0.1, upper_limit = 0.1
                    ell_comps
                        ell_comps_0                                             GaussianPrior [429], mean = 0.0, sigma = 0.3
                        ell_comps_1                                             GaussianPrior [430], mean = 0.0, sigma = 0.3
                    sigma                                                       0.01
                1
                    centre
                        centre_0                                                UniformPrior [425], lower_limit = -0.1, upper_limit = 0.1
                        centre_1                                                UniformPrior [426], lower_limit = -0.1, upper_limit = 0.1
                    ell_comps
                        ell_comps_0                                             GaussianPrior [429], mean = 0.0, sigma = 0.3
                        ell_comps_1                                             GaussianPrior [430], mean = 0.0, sigma = 0.3
                    sigma                                                       0.012294934136946248
                2
                    centre
                        centre_0                                                UniformPrior [425], lower_limit = -0.1, upper_limit = 0.1
                        centre_1                                                UniformPrior [426], lower_limit = -0.1, upper_limit = 0.1
                    ell_comps
                        ell_comps_0                                             GaussianPrior [429], mean = 0.0, sigma = 0.3
                        ell_comps_1                                             GaussianPrior [430], mean = 0.0, sigma = 0.3
                    sigma                                                       0.01511654054318462
                3
                    centre
                        centre_0                                                UniformPrior [425], lower_limit = -0.1, upper_limit = 0.1
                        centre_1                                                UniformPrior [426], lower_limit = -0.1, upper_limit = 0.1
                    ell_comps
                        ell_comps_0                                             GaussianPrior [429], mean = 0.0, sigma = 0.3
                        ell_comps_1                                             GaussianPrior [430], mean = 0.0, sigma = 0.3
                    sigma                                                       0.018585687035693257
```

We could identify all model components with overlapping priors and condence as follows:

```        bulge
            light_profile_list
                0-29
                    centre
                        centre_0                                                UniformPrior [425], lower_limit = -0.1, upper_limit = 0.1
                        centre_1                                                UniformPrior [426], lower_limit = -0.1, upper_limit = 0.1
                    ell_comps
                        ell_comps_0                                             GaussianPrior [429], mean = 0.0, sigma = 0.3
                        ell_comps_1                                             GaussianPrior [430], mean = 0.0, sigma = 0.3
                0
                    sigma                                                       0.01
                1
                    sigma                                                       0.012294934136946248
                2
                    sigma                                                       0.01511654054318462
                3
                    sigma                                                       0.018585687035693257
```

I suspect this will be useful for graph visualization...


Pull request description:
feature/concise model info
Resolves #925 and #926

When an object in a collection in the info tree is repeated four times or more it is summarised with a representative as per #925


Patch:
diff --git a/autofit/non_linear/mock/mock_samples.py b/autofit/non_linear/mock/mock_samples.py
index d3d1d4ed2..dd48c1cee 100644
--- a/autofit/non_linear/mock/mock_samples.py
+++ b/autofit/non_linear/mock/mock_samples.py
@@ -63,7 +63,9 @@ class MockSamples(SamplesPDF):
             except (KeyError, AttributeError):
                 pass
 
-        return self._max_log_likelihood_instance
+        if as_instance:
+            return self._max_log_likelihood_instance
+        return list(self.sample_list[0].kwargs.values())
 
     @property
     def prior_means(self):
diff --git a/autofit/text/formatter.py b/autofit/text/formatter.py
index 52d335ee3..ed55bb624 100644
--- a/autofit/text/formatter.py
+++ b/autofit/text/formatter.py
@@ -3,6 +3,7 @@ import logging
 from typing import Tuple
 
 from autoconf import conf
+from autofit.text.representative import Representative
 from autofit.tools.util import open_
 
 logger = logging.getLogger(__name__)
@@ -24,6 +25,29 @@ class FormatNode:
     def items(self):
         return self._dict.items()
 
+    def groups(self):
+        return Representative.find_representatives(self.items())
+
+    def list(self, indent=4, line_length=90):
+        lines = []
+        for key, value in self.groups():
+            indent_string = indent * " "
+            if value.value is not None:
+                value_string = str(value.value)
+                space_string = max((line_length - len(str(key))), 1) * " "
+                lines.append(f"{key}{space_string}{value_string}")
+
+            if len(value) > 0:
+                sub_lines = value.list(
+                    indent=indent,
+                    line_length=line_length - indent,
+                )
+                if value.value is None:
+                    lines.append(key)
+                for line in sub_lines:
+                    lines.append(f"{indent_string}{line}")
+        return lines
+
 
 class TextFormatter:
     def __init__(self, line_length=90, indent=4):
@@ -42,33 +66,17 @@ class TextFormatter:
     def add(self, path: Tuple[str, ...], value):
         self.add_to_dict(path, value, self.dict)
 
-    def dict_to_list(self, info_dict, line_length):
-        lines = []
-        for key, value in info_dict.items():
-            indent_string = self.indent * " "
-            if value.value is not None:
-                value_string = str(value.value)
-                space_string = max((line_length - len(str(key))), 1) * " "
-                lines.append(f"{key}{space_string}{value_string}")
-
-            if len(value) > 0:
-                sub_lines = self.dict_to_list(
-                    value, line_length=line_length - self.indent
-                )
-                if value.value is None:
-                    lines.append(key)
-                for line in sub_lines:
-                    lines.append(f"{indent_string}{line}")
-        return lines
-
-    @property
-    def list(self):
-        return self.dict_to_list(self.dict, line_length=self.line_length)
-
     @property
     def text(self):
         return "\n".join(self.list)
 
+    @property
+    def list(self):
+        return self.dict.list(
+            indent=self.indent,
+            line_length=self.line_length,
+        )
+
 
 def format_string_for_parameter_name(parameter_name: str) -> str:
     """
diff --git a/autofit/text/representative.py b/autofit/text/representative.py
new file mode 100644
index 000000000..364cfcabe
--- /dev/null
+++ b/autofit/text/representative.py
@@ -0,0 +1,124 @@
+from collections import defaultdict
+from typing import Dict, List, Iterable
+
+import autofit as af
+
+
+class Representative:
+    def __init__(self, items: List[tuple]):
+        """
+        Collects together items that are the same except for ids. This is to make
+        the output of the info method more concise.
+
+        Parameters
+        ----------
+        items
+            A list of tuples of the form (key, object)
+        """
+        self.items = items
+
+    @property
+    def keys(self):
+        return [key for key, _ in self.items]
+
+    @property
+    def children(self):
+        return [obj for _, obj in self.items]
+
+    @property
+    def key(self) -> str:
+        """
+        A string representation of the range of keys in this representative.
+        """
+        keys = sorted(self.keys)
+        return f"{keys[0]} - {keys[-1]}"
+
+    @property
+    def representative(self):
+        """
+        The first object in the group represents the group.
+        """
+        return self.items[0][1]
+
+    def __getattr__(self, item):
+        return getattr(self.representative, item)
+
+    def __len__(self):
+        return len(self.representative)
+
+    @classmethod
+    def find_representatives(cls, items: Iterable[tuple], minimum: int = 4) -> list:
+        """
+        Find representatives in a list of items. This includes items from
+        the original list where there are not enough repetitions to form
+        a representative.
+
+        Parameters
+        ----------
+        items
+            A list of tuples of the form (key, object)
+        minimum
+            The minimum number of items that must be the same for a representative
+            to be formed.
+
+        Returns
+        -------
+        A list of representatives and items that are not part of a representative.
+        """
+        representative_dict: Dict[tuple, list] = defaultdict(list)
+        for key, obj in items:
+            blueprint = cls.get_blueprint(obj)
+            representative_dict[blueprint].append((key, obj))
+
+        representatives = []
+        for blueprint, items in representative_dict.items():
+            if len(items) >= minimum:
+                representative = Representative(items)
+                representatives.append((representative.key, representative))
+            else:
+                representatives.extend(items)
+
+        return representatives
+
+    @classmethod
+    def get_blueprint(cls, obj):
+        """
+        Get a blueprint for an object. This is a tuple of tuples of the form
+        (path, value) where path is a tuple of strings and value is a float, int,
+        tuple, or af.Prior.
+
+        Blueprints are unique per unique object in the model but are not sensitive
+        to ids.
+
+        Parameters
+        ----------
+        obj
+            The object to get a blueprint for.
+
+        Returns
+        -------
+        A blueprint for the object.
+        """
+        from autofit.text.formatter import FormatNode
+
+        if obj is None:
+            return None
+
+        if isinstance(obj, FormatNode):
+            return cls.get_blueprint(obj.value)
+        if isinstance(obj, (float, int, tuple, str)):
+            return obj
+        if isinstance(obj, af.Prior):
+            return type(obj), obj.parameter_string
+        if isinstance(obj, af.AbstractModel):
+            blueprint = tuple(
+                (path, cls.get_blueprint(value))
+                for path, value in obj.path_instance_tuples_for_class(
+                    (float, int, tuple, af.Prior), ignore_children=True
+                )
+                if path != ("id",)
+            )
+            if isinstance(obj, af.Model):
+                return blueprint + (obj.cls,)
+            return blueprint
+        raise ValueError(f"Cannot get blueprint for {obj} of type {type(obj)}")


Test patch:
diff --git a/test_autofit/mapper/model/derived_quantities/test_derived_quantities.py b/test_autofit/mapper/model/derived_quantities/test_derived_quantities.py
index 146a76f77..b37a3af21 100644
--- a/test_autofit/mapper/model/derived_quantities/test_derived_quantities.py
+++ b/test_autofit/mapper/model/derived_quantities/test_derived_quantities.py
@@ -1,3 +1,5 @@
+import os
+
 import pytest
 
 import autofit as af
@@ -65,6 +67,10 @@ def test_persist(samples, model):
 def test_conditional_persist(samples, model):
     paths = DirectoryPaths()
     paths.model = model
+    try:
+        os.remove(paths._derived_quantities_file)
+    except FileNotFoundError:
+        pass
     paths.save_derived_quantities(samples)
     assert not paths._derived_quantities_file.exists()
 
diff --git a/test_autofit/non_linear/result/test_result.py b/test_autofit/non_linear/result/test_result.py
index b4fd41151..ee816d2df 100644
--- a/test_autofit/non_linear/result/test_result.py
+++ b/test_autofit/non_linear/result/test_result.py
@@ -1,6 +1,7 @@
 import pytest
 
 import autofit as af
+from autofit import Sample
 
 
 @pytest.fixture(name="result")
@@ -9,7 +10,22 @@ def make_result():
     mapper.component = af.m.MockClassx2Tuple
     # noinspection PyTypeChecker
     return af.Result(
-        samples=af.m.MockSamples(max_log_likelihood_instance=[0, 1], prior_means=[0, 1], model=mapper),
+        samples=af.m.MockSamples(
+            sample_list=[
+                Sample(
+                    log_likelihood=1.0,
+                    log_prior=0.0,
+                    weight=0.0,
+                    kwargs={
+                        "component.one_tuple.one_tuple_0": 0,
+                        "component.one_tuple.one_tuple_1": 1,
+                    },
+                ),
+            ],
+            max_log_likelihood_instance=[0, 1],
+            prior_means=[0, 1],
+            model=mapper,
+        ),
     )
 
 
diff --git a/test_autofit/text/test_concise.py b/test_autofit/text/test_concise.py
new file mode 100644
index 000000000..a18f55c49
--- /dev/null
+++ b/test_autofit/text/test_concise.py
@@ -0,0 +1,101 @@
+import itertools
+
+import autofit as af
+import pytest
+
+from autofit.text.representative import Representative
+from autofit.text.text_util import result_info_from
+
+
+@pytest.fixture(autouse=True)
+def reset_ids():
+    af.Prior._ids = itertools.count()
+
+
+@pytest.fixture(name="collection")
+def make_collection():
+    return af.Collection([af.Model(af.Gaussian) for _ in range(20)])
+
+
+def test_model_info(collection):
+    assert (
+        collection.info
+        == """Total Free Parameters = 60
+
+model                                                                           Collection (N=60)
+    0 - 9                                                                       Gaussian (N=3)
+
+0 - 9
+    centre                                                                      UniformPrior [0], lower_limit = 0.0, upper_limit = 1.0
+    normalization                                                               UniformPrior [1], lower_limit = 0.0, upper_limit = 1.0
+    sigma                                                                       UniformPrior [2], lower_limit = 0.0, upper_limit = 1.0"""
+    )
+
+
+def test_representative(collection):
+    ((key, representative),) = Representative.find_representatives(collection.items())
+
+    assert len(representative.children) == 20
+    assert key == "0 - 9"
+
+
+def test_get_blueprint():
+    assert Representative.get_blueprint(af.Model(af.Gaussian)) == (
+        (("centre",), (af.UniformPrior, "lower_limit = 0.0, upper_limit = 1.0")),
+        (("normalization",), (af.UniformPrior, "lower_limit = 0.0, upper_limit = 1.0")),
+        (("sigma",), (af.UniformPrior, "lower_limit = 0.0, upper_limit = 1.0")),
+        af.Gaussian,
+    )
+
+
+@pytest.fixture(name="samples")
+def make_samples(collection):
+    parameters = [len(collection) * [1.0, 2.0, 3.0]]
+
+    log_likelihood_list = [1.0]
+
+    return af.m.MockSamples(
+        model=collection,
+        sample_list=af.Sample.from_lists(
+            parameter_lists=parameters,
+            log_likelihood_list=log_likelihood_list,
+            log_prior_list=[0.0],
+            weight_list=log_likelihood_list,
+            model=collection,
+        ),
+        max_log_likelihood_instance=collection.instance_from_prior_medians(),
+    )
+
+
+def test_model_results(samples):
+    assert (
+        result_info_from(samples)
+        == """Maximum Log Likelihood                                                          1.00000000
+Maximum Log Posterior                                                           1.00000000
+
+model                                                                           Collection (N=60)
+    0 - 9                                                                       Gaussian (N=3)
+
+Maximum Log Likelihood Model:
+
+0 - 9
+    centre                                                                      1.000
+    normalization                                                               2.000
+    sigma                                                                       3.000
+
+ WARNING: The samples have not converged enough to compute a PDF and model errors. 
+ The model below over estimates errors. 
+
+
+
+Summary (1.0 sigma limits):
+
+0 - 9
+    centre                                                                      1.00 (1.00, 1.00)
+    normalization                                                               2.00 (2.00, 2.00)
+    sigma                                                                       3.00 (3.00, 3.00)
+
+instances
+
+"""
+    )