Repository: rhayes777/PyAutoFit (python)

Issue:
Make Sensitivity Mapping Support serial and parallel CPU fits
Sensitivity mapping requires that `number_of_cores > 1`.

This means we cannot perform sensitivity mapping in a regime where the map is performed 1 fit at a time, but the parallelization occurs for the non-linear search. 

For high memory use cases this can be a better parallelization scheme.

Can you provide support for both? The `class GridSearch` does this so I would guess a lot of functionality can be reused.

Pull request description:
switch to Serial process runner when sensitivity run for a single core
Resolves #869

Patch:
diff --git a/autofit/non_linear/grid/sensitivity.py b/autofit/non_linear/grid/sensitivity.py
index bdc2deabf..d36066de7 100644
--- a/autofit/non_linear/grid/sensitivity.py
+++ b/autofit/non_linear/grid/sensitivity.py
@@ -9,7 +9,7 @@ from typing import List, Generator, Callable, ClassVar, Union, Tuple
 from autoconf.dictable import to_dict
 from autofit.mapper.model import ModelInstance
 from autofit.mapper.prior_model.abstract import AbstractPriorModel
-from autofit.non_linear.grid.grid_search import make_lists
+from autofit.non_linear.grid.grid_search import make_lists, Sequential
 from autofit.non_linear.parallel import AbstractJob, Process, AbstractJobResult
 from autofit.non_linear.paths.abstract import AbstractPaths
 from autofit.non_linear.result import Result
@@ -209,7 +209,7 @@ class Sensitivity:
         perturb_fit_cls
             The class which fits the perturb model to each simulated dataset of the sensitivity map.
         number_of_cores
-            How many cores does this computer have? Minimum 2.
+            How many cores does this computer have?
         limit_scale
             Scales the priors for each perturbation model.
                 A scale of 1 means priors have limits the same size as the grid square.
@@ -235,7 +235,7 @@ class Sensitivity:
         self.job_cls = job_cls
 
         self.number_of_steps = number_of_steps
-        self.number_of_cores = number_of_cores or 2
+        self.number_of_cores = number_of_cores
 
         self.limit_scale = limit_scale
 
@@ -271,8 +271,10 @@ class Sensitivity:
         ]
         physical_values = list(self._physical_values)
 
+        process_class = Process if self.number_of_cores > 1 else Sequential
+
         results = list()
-        for result in Process.run_jobs(
+        for result in process_class.run_jobs(
             self._make_jobs(), number_of_cores=self.number_of_cores
         ):
             if isinstance(result, Exception):


Test patch:
diff --git a/test_autofit/mapper/model/test_info.py b/test_autofit/mapper/model/test_info.py
index f5c4b1de8..4eaef52c4 100644
--- a/test_autofit/mapper/model/test_info.py
+++ b/test_autofit/mapper/model/test_info.py
@@ -15,5 +15,5 @@ def test_placeholder():
 
 
 
-argument                                                                        No prior configuration found."""
+argument                                                                        Prior Missing: Enter Manually or Add to Config"""
     )
diff --git a/test_autofit/non_linear/grid/test_sensitivity/test_run.py b/test_autofit/non_linear/grid/test_sensitivity/test_run.py
index aa0dc1f51..c22513082 100644
--- a/test_autofit/non_linear/grid/test_sensitivity/test_run.py
+++ b/test_autofit/non_linear/grid/test_sensitivity/test_run.py
@@ -19,3 +19,10 @@ def test_sensitivity(sensitivity):
         )
         assert "     0,  0.25,  0.25,  0.25,   2.0,   2.0,   0.0\n" in all_lines
         assert "     1,  0.25,  0.25,  0.75,   2.0,   2.0,   0.0\n" in all_lines
+
+
+def test_serial(sensitivity):
+    sensitivity.number_of_cores = 1
+
+    results = sensitivity.run()
+    assert len(results) == 8