From 5ddf3a822b7d4f55363a24f65a983d5cba8e91f6 Mon Sep 17 00:00:00 2001 From: Anthony Lim Date: Mon, 24 Feb 2025 16:09:22 +0000 Subject: [PATCH 01/21] add optional deps --- .github/workflows/run_tests.yml | 2 +- pyproject.toml | 6 +- src/quickBayes/fitting/gofit_engine.py | 146 ++++++++----------------- 3 files changed, 50 insertions(+), 104 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 54dd69a2..638811f2 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -48,7 +48,7 @@ jobs: shell: bash -l {0} run: | conda activate quickBayes-dev - python -m pip install . + python -m pip install .[gofit] - name: run tests timeout-minutes: 10 shell: bash -l {0} diff --git a/pyproject.toml b/pyproject.toml index 5147364b..0c1546fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = 'hatchling.build' name = 'quickBayes' version = "1.0.0b23" requires-python = ">=3.7.1" -dependencies = ['numpy<2.0.0', 'scipy', 'pybind11[global]', 'eigen', 'gofit'] +dependencies = ['numpy<2.0.0', 'scipy'] authors = [{name='Anthony Lim', email='anthony.lim@stfc.ac.uk'}] description = "A Bayesian fitting package used for model selection and grid searches of fits for neutron and muon data." keywords=['bayesian', 'fitting', 'QENS', 'muons'] @@ -17,6 +17,10 @@ license = {text = 'BSD'} docs = ["sphinx==7.3.7", 'jupyter-book', "nbsphinx==0.9.4"] +gofit = ['pybind11[global]', + 'eigen', + 'gofit'] + [project.urls] Homepage = "https://quickbayes.readthedocs.io/en/latest/" diff --git a/src/quickBayes/fitting/gofit_engine.py b/src/quickBayes/fitting/gofit_engine.py index f92804b3..1c3b890d 100644 --- a/src/quickBayes/fitting/gofit_engine.py +++ b/src/quickBayes/fitting/gofit_engine.py @@ -1,106 +1,48 @@ -from gofit import multistart from numpy import ndarray -from typing import Callable from quickBayes.fitting.fit_engine import FitEngine -""" -This file contains all of the code needed for a gofit -engine, because of the way gofit works we need to provide -a cost function. However, when the cost function is called -by gofit it is not provided with the original data. Hence, -we need to construct a cost function class first that has -the data as a member variable. -""" - - -class ChiSquared(object): - def __init__(self, x_data: ndarray, y_data: ndarray, - e_data: ndarray, func: Callable): - """ - A chi^2 cost function class for use with gofit - :param x_data: x data that fitted against - :param y_data: y data that fitted against - :param e_data: e data that fitted against - :param func: the fitting function used - """ - self._x_data = x_data - self._y_data = y_data - self._e_data = e_data - self._func = func - - def __call__(self, params: ndarray) -> float: - """ - Calls the evaluation of the cost function - :param params: the fit parameters - :return the cost function evaluation - """ - fit = self._func(self._x_data, *params) - return (fit - self._y_data)**2 / self._e_data**2 - - -class GoFitEngine(FitEngine): - """ - A gofit multistart fit engine. - This will use gofit's multistart to - fit data. - """ - - def __init__(self, x_data: ndarray, y_data: ndarray, e_data: ndarray, - lower: ndarray, upper: ndarray, samples: int = 10, - max_iterations: int = 220000): - """ - Creates the scipy curve fit engine class - Stores useful information about each fit - :param name: name of the fit engine - :param x_data: original x data (can fit to an interpolation) - :param y_data: original y data (can fit to an interpolation) - :param e_data: original e data (can fit to an interpolation) - :param lower: the lower bounds for the fit parameters - :param upper: the upper bounds for the fit parameters - :param samples: the number of samples to use in multistart - :param max_iterations: the maximum number of iterations for the fit - """ - super().__init__("gofit", x_data, y_data, e_data) - # extra parameters - self.set_bounds_and_N_params(lower, upper) - self._max_iterations = max_iterations - self._samples = samples - - def set_bounds_and_N_params(self, lower: ndarray, upper: ndarray) -> None: - """ - Sets the current bounds and number of parameters for the fit function. - If the functional form changes this method will need to be called - with updated values. - :param lower: the lower bound for the function parameters - :param upper: the upper bound for the function parameters - """ - # validate - if len(upper) != len(lower): - raise ValueError(f"The lower {lower} and " - f"upper {upper} bounds must " - "be the same length") - self._lower = lower - self._upper = upper - self._N_params = len(upper) - - def _do_fit(self, x_data: ndarray, y_data: ndarray, e_data: ndarray, - func: Callable) -> ndarray: - """ - Calls gofit multistart - :param x_data: the x data to fit - :param y_data: the y data to fit - :param e_data: the error data to fit - :param func: the fitting function - :return the fit parameters - """ - cost_function = ChiSquared(x_data, y_data, e_data, func) - - data_length = len(x_data) - - params, _ = multistart(data_length, self._N_params, - self._lower, self._upper, - cost_function, samples=self._samples, - maxit=self._max_iterations) - - return params +try: + from quickBayes.fitting.gofit_base import _GoFitEngine + + class GoFitEngine(_GoFitEngine): + + def __init__(self, x_data: ndarray, y_data: ndarray, e_data: ndarray, + lower: ndarray, upper: ndarray, samples: int = 10, + max_iterations: int = 220000): + """ + Creates the scipy curve fit engine class + Stores useful information about each fit + :param name: name of the fit engine + :param x_data: original x data (can fit to an interpolation) + :param y_data: original y data (can fit to an interpolation) + :param e_data: original e data (can fit to an interpolation) + :param lower: the lower bounds for the fit parameters + :param upper: the upper bounds for the fit parameters + :param samples: the number of samples to use in multistart + :param max_iterations: the maximum number of iterations for the fit + """ + super().__init__(x_data, y_data, e_data, + lower, upper, samples, max_iterations) + +except ImportError: + + class GoFitEngine(FitEngine): + + def __init__(self, x_data: ndarray, y_data: ndarray, e_data: ndarray, + lower: ndarray, upper: ndarray, samples: int = 10, + max_iterations: int = 220000): + """ + Creates the scipy curve fit engine class + Stores useful information about each fit + :param name: name of the fit engine + :param x_data: original x data (can fit to an interpolation) + :param y_data: original y data (can fit to an interpolation) + :param e_data: original e data (can fit to an interpolation) + :param lower: the lower bounds for the fit parameters + :param upper: the upper bounds for the fit parameters + :param samples: the number of samples to use in multistart + :param max_iterations: the maximum number of iterations for the fit + """ + raise RuntimeError("gofit is not installed. Please " + "install gofit to use this functionality") From 06d89862e45ee53b88e153f3c8d4259c33e5f750 Mon Sep 17 00:00:00 2001 From: Anthony Lim Date: Mon, 24 Feb 2025 16:34:59 +0000 Subject: [PATCH 02/21] test --- .github/workflows/run_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 638811f2..e27d35c1 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -48,7 +48,7 @@ jobs: shell: bash -l {0} run: | conda activate quickBayes-dev - python -m pip install .[gofit] + python -m pip install --upgrade .[gofit] - name: run tests timeout-minutes: 10 shell: bash -l {0} From a9986c09e3e085bb62c9c8f63d8aafbd60e4fd25 Mon Sep 17 00:00:00 2001 From: Anthony Lim Date: Mon, 24 Feb 2025 16:44:40 +0000 Subject: [PATCH 03/21] add missing file --- src/quickBayes/fitting/gofit_base.py | 106 +++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 src/quickBayes/fitting/gofit_base.py diff --git a/src/quickBayes/fitting/gofit_base.py b/src/quickBayes/fitting/gofit_base.py new file mode 100644 index 00000000..ddfe6b63 --- /dev/null +++ b/src/quickBayes/fitting/gofit_base.py @@ -0,0 +1,106 @@ +from numpy import ndarray +from typing import Callable +from quickBayes.fitting.fit_engine import FitEngine +from gofit import multistart + + +""" +This file contains all of the code needed for a gofit +engine, because of the way gofit works we need to provide +a cost function. However, when the cost function is called +by gofit it is not provided with the original data. Hence, +we need to construct a cost function class first that has +the data as a member variable. +""" + + +class ChiSquared(object): + def __init__(self, x_data: ndarray, y_data: ndarray, + e_data: ndarray, func: Callable): + """ + A chi^2 cost function class for use with gofit + :param x_data: x data that fitted against + :param y_data: y data that fitted against + :param e_data: e data that fitted against + :param func: the fitting function used + """ + self._x_data = x_data + self._y_data = y_data + self._e_data = e_data + self._func = func + + def __call__(self, params: ndarray) -> float: + """ + Calls the evaluation of the cost function + :param params: the fit parameters + :return the cost function evaluation + """ + fit = self._func(self._x_data, *params) + return (fit - self._y_data)**2 / self._e_data**2 + + +class _GoFitEngine(FitEngine): + """ + A gofit multistart fit engine. + This will use gofit's multistart to + fit data. + """ + + def __init__(self, x_data: ndarray, y_data: ndarray, e_data: ndarray, + lower: ndarray, upper: ndarray, samples: int = 10, + max_iterations: int = 220000): + """ + Creates the scipy curve fit engine class + Stores useful information about each fit + :param name: name of the fit engine + :param x_data: original x data (can fit to an interpolation) + :param y_data: original y data (can fit to an interpolation) + :param e_data: original e data (can fit to an interpolation) + :param lower: the lower bounds for the fit parameters + :param upper: the upper bounds for the fit parameters + :param samples: the number of samples to use in multistart + :param max_iterations: the maximum number of iterations for the fit + """ + super().__init__("gofit", x_data, y_data, e_data) + # extra parameters + self.set_bounds_and_N_params(lower, upper) + self._max_iterations = max_iterations + self._samples = samples + + def set_bounds_and_N_params(self, lower: ndarray, upper: ndarray) -> None: + """ + Sets the current bounds and number of parameters for the fit function. + If the functional form changes this method will need to be called + with updated values. + :param lower: the lower bound for the function parameters + :param upper: the upper bound for the function parameters + """ + # validate + if len(upper) != len(lower): + raise ValueError(f"The lower {lower} and " + f"upper {upper} bounds must " + "be the same length") + self._lower = lower + self._upper = upper + self._N_params = len(upper) + + def _do_fit(self, x_data: ndarray, y_data: ndarray, e_data: ndarray, + func: Callable) -> ndarray: + """ + Calls gofit multistart + :param x_data: the x data to fit + :param y_data: the y data to fit + :param e_data: the error data to fit + :param func: the fitting function + :return the fit parameters + """ + cost_function = ChiSquared(x_data, y_data, e_data, func) + + data_length = len(x_data) + + params, _ = multistart(data_length, self._N_params, + self._lower, self._upper, + cost_function, samples=self._samples, + maxit=self._max_iterations) + + return params From 72d0d21f60ff71ee3f373672cd591518c4582d6b Mon Sep 17 00:00:00 2001 From: Anthony Lim Date: Tue, 25 Feb 2025 15:59:52 +0000 Subject: [PATCH 04/21] tests restructure --- .../test_helpers/model_selection.py | 23 +- test/{fit_engines => default}/__init__.py | 0 test/default/gofitEngine_test.py | 13 + test/default/modelSelectionTemplate_test.py | 18 + test/{fit_functions => gofit}/__init__.py | 0 .../gofitEngine_test.py | 0 test/gofit/modelSelectionTemplate_test.py | 32 + test/{utils => shared}/__init__.py | 0 .../data/muon/muon_expdecay_1.npy | 0 .../data/muon/muon_expdecay_2.npy | 0 .../data/muon/muon_expdecay_3.npy | 0 .../data/muon/muon_expdecay_3_big.npy | 0 test/{ => shared}/data/qse_res.npy | Bin .../{ => shared}/data/resolution_data_red.npy | Bin test/{ => shared}/data/sample_data_red.npy | Bin test/shared/fitting/__init__.py | 0 .../fitting}/fitUtils_test.py | 0 .../fitting}/fitengine_test.py | 0 .../fitting}/scipyEngine_test.py | 0 .../functions}/SEWithFixes_test.py | 0 .../functions}/SE_test.py | 0 test/shared/functions/__init__.py | 0 .../functions}/composite_test.py | 0 .../functions}/convolution_test.py | 0 .../functions}/delta_test.py | 0 .../functions}/expdecay_test.py | 0 .../fit_functions/SEWithFixes_test.py | 133 ++++ .../shared/functions/fit_functions/SE_test.py | 117 ++++ .../functions/fit_functions/__init__.py | 0 .../functions/fit_functions/composite_test.py | 223 +++++++ .../fit_functions/convolution_test.py | 231 +++++++ .../functions/fit_functions/delta_test.py | 94 +++ .../functions/fit_functions/expdecay_test.py | 73 +++ .../functions}/fit_functions/flatBG_test.py | 0 .../functions}/fit_functions/gaussian_test.py | 0 .../functions}/fit_functions/linearBG_test.py | 0 .../fit_functions/lorentzian_test.py | 0 .../functions}/fit_functions/noBG_test.py | 0 .../fit_functions/qldataFunction_test.py | 0 .../fit_functions/qseFixedFunction_test.py | 0 .../fit_functions/qseFunction_test.py | 0 test/shared/functions/flatBG_test.py | 80 +++ test/shared/functions/gaussian_test.py | 74 +++ test/shared/functions/linearBG_test.py | 92 +++ test/shared/functions/lorentzian_test.py | 73 +++ test/shared/functions/noBG_test.py | 51 ++ test/shared/functions/qldataFunction_test.py | 598 ++++++++++++++++++ .../shared/functions/qseFixedFunction_test.py | 502 +++++++++++++++ test/shared/functions/qseFunction_test.py | 453 +++++++++++++ test/{ => shared}/logLikelihood_test.py | 0 test/shared/utils/__init__.py | 0 test/{ => shared}/utils/crop_test.py | 0 test/{ => shared}/utils/getBG_test.py | 0 test/{ => shared}/utils/parallel_test.py | 0 test/{ => shared}/utils/spline_test.py | 0 test/{ => shared}/utils/updateGuess_test.py | 0 .../grid_search/gridSearchTemplate_test.py | 0 .../workflows/grid_search/quest_test.py | 0 .../model_selection/muon_decay_test.py | 0 .../workflows/model_selection/qldata_test.py | 0 .../workflows/model_selection/qlse_test.py | 0 61 files changed, 2858 insertions(+), 22 deletions(-) rename test/workflows/model_selection/modelSelectionTemplate_test.py => src/quickBayes/test_helpers/model_selection.py (86%) rename test/{fit_engines => default}/__init__.py (100%) create mode 100644 test/default/gofitEngine_test.py create mode 100644 test/default/modelSelectionTemplate_test.py rename test/{fit_functions => gofit}/__init__.py (100%) rename test/{fit_engines => gofit}/gofitEngine_test.py (100%) create mode 100644 test/gofit/modelSelectionTemplate_test.py rename test/{utils => shared}/__init__.py (100%) rename test/{ => shared}/data/muon/muon_expdecay_1.npy (100%) rename test/{ => shared}/data/muon/muon_expdecay_2.npy (100%) rename test/{ => shared}/data/muon/muon_expdecay_3.npy (100%) rename test/{ => shared}/data/muon/muon_expdecay_3_big.npy (100%) rename test/{ => shared}/data/qse_res.npy (100%) rename test/{ => shared}/data/resolution_data_red.npy (100%) rename test/{ => shared}/data/sample_data_red.npy (100%) create mode 100644 test/shared/fitting/__init__.py rename test/{fit_engines => shared/fitting}/fitUtils_test.py (100%) rename test/{fit_engines => shared/fitting}/fitengine_test.py (100%) rename test/{fit_engines => shared/fitting}/scipyEngine_test.py (100%) rename test/{fit_functions => shared/functions}/SEWithFixes_test.py (100%) rename test/{fit_functions => shared/functions}/SE_test.py (100%) create mode 100644 test/shared/functions/__init__.py rename test/{fit_functions => shared/functions}/composite_test.py (100%) rename test/{fit_functions => shared/functions}/convolution_test.py (100%) rename test/{fit_functions => shared/functions}/delta_test.py (100%) rename test/{fit_functions => shared/functions}/expdecay_test.py (100%) create mode 100644 test/shared/functions/fit_functions/SEWithFixes_test.py create mode 100644 test/shared/functions/fit_functions/SE_test.py create mode 100644 test/shared/functions/fit_functions/__init__.py create mode 100644 test/shared/functions/fit_functions/composite_test.py create mode 100644 test/shared/functions/fit_functions/convolution_test.py create mode 100644 test/shared/functions/fit_functions/delta_test.py create mode 100644 test/shared/functions/fit_functions/expdecay_test.py rename test/{ => shared/functions}/fit_functions/flatBG_test.py (100%) rename test/{ => shared/functions}/fit_functions/gaussian_test.py (100%) rename test/{ => shared/functions}/fit_functions/linearBG_test.py (100%) rename test/{ => shared/functions}/fit_functions/lorentzian_test.py (100%) rename test/{ => shared/functions}/fit_functions/noBG_test.py (100%) rename test/{ => shared/functions}/fit_functions/qldataFunction_test.py (100%) rename test/{ => shared/functions}/fit_functions/qseFixedFunction_test.py (100%) rename test/{ => shared/functions}/fit_functions/qseFunction_test.py (100%) create mode 100644 test/shared/functions/flatBG_test.py create mode 100644 test/shared/functions/gaussian_test.py create mode 100644 test/shared/functions/linearBG_test.py create mode 100644 test/shared/functions/lorentzian_test.py create mode 100644 test/shared/functions/noBG_test.py create mode 100644 test/shared/functions/qldataFunction_test.py create mode 100644 test/shared/functions/qseFixedFunction_test.py create mode 100644 test/shared/functions/qseFunction_test.py rename test/{ => shared}/logLikelihood_test.py (100%) create mode 100644 test/shared/utils/__init__.py rename test/{ => shared}/utils/crop_test.py (100%) rename test/{ => shared}/utils/getBG_test.py (100%) rename test/{ => shared}/utils/parallel_test.py (100%) rename test/{ => shared}/utils/spline_test.py (100%) rename test/{ => shared}/utils/updateGuess_test.py (100%) rename test/{ => shared}/workflows/grid_search/gridSearchTemplate_test.py (100%) rename test/{ => shared}/workflows/grid_search/quest_test.py (100%) rename test/{ => shared}/workflows/model_selection/muon_decay_test.py (100%) rename test/{ => shared}/workflows/model_selection/qldata_test.py (100%) rename test/{ => shared}/workflows/model_selection/qlse_test.py (100%) diff --git a/test/workflows/model_selection/modelSelectionTemplate_test.py b/src/quickBayes/test_helpers/model_selection.py similarity index 86% rename from test/workflows/model_selection/modelSelectionTemplate_test.py rename to src/quickBayes/test_helpers/model_selection.py index 01309d42..c8755117 100644 --- a/test/workflows/model_selection/modelSelectionTemplate_test.py +++ b/src/quickBayes/test_helpers/model_selection.py @@ -1,4 +1,3 @@ -import unittest from quickBayes.workflow.model_selection.template import ModelSelectionWorkflow from quickBayes.functions.BG import FlatBG from quickBayes.functions.exp_decay import ExpDecay @@ -17,7 +16,7 @@ def _update_function(func): return func -class WorkflowTemplateTest(unittest.TestCase): +class WorkflowTemplateTest(object): def setUp(self): self.func = CompositeFunction() @@ -144,26 +143,6 @@ def test_update_scipy_fit_engine(self): self.assertEqual(self.wf.fit_engine._lower, [-1]) self.assertEqual(self.wf.fit_engine._upper, [1]) - def test_set_gofit_engine(self): - self.assertEqual(self.wf.fit_engine, None) - x, y, e = gen_model_selection_data() - self.wf.preprocess_data(x, y, e) - self.wf.set_gofit_engine(5, [], []) - self.assertEqual(self.wf.fit_engine.name, 'gofit') - - def test_update_gofit_engine(self): - self.assertEqual(self.wf.fit_engine, None) - x, y, e = gen_model_selection_data() - self.wf.preprocess_data(x, y, e) - self.wf.set_gofit_engine(5, [], []) - self.assertEqual(self.wf.fit_engine._lower, []) - self.assertEqual(self.wf.fit_engine._upper, []) - - bg = FlatBG() - self.wf.update_fit_engine(bg, [5]) - self.assertEqual(self.wf.fit_engine._lower, [-1]) - self.assertEqual(self.wf.fit_engine._upper, [1]) - if __name__ == '__main__': unittest.main() diff --git a/test/fit_engines/__init__.py b/test/default/__init__.py similarity index 100% rename from test/fit_engines/__init__.py rename to test/default/__init__.py diff --git a/test/default/gofitEngine_test.py b/test/default/gofitEngine_test.py new file mode 100644 index 00000000..87eb99b0 --- /dev/null +++ b/test/default/gofitEngine_test.py @@ -0,0 +1,13 @@ +import unittest +from quickBayes.fitting.gofit_engine import GoFitEngine + + +class GoFitEngineNotInstalledTest(unittest.TestCase): + def test_gofit(self): + with self.assertRaises(RuntimeError): + GoFitEngine([1], [1], [1], + [1], [1], 1, 1) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/default/modelSelectionTemplate_test.py b/test/default/modelSelectionTemplate_test.py new file mode 100644 index 00000000..1748ca90 --- /dev/null +++ b/test/default/modelSelectionTemplate_test.py @@ -0,0 +1,18 @@ +import unittest +from quickBayes.test_helpers.model_selection import WorkflowTemplateTest +from quickBayes.test_helpers.workflow_helper import gen_model_selection_data + + +class WorkflowTemplateWithoutGoFitTest(WorkflowTemplateTest, + unittest.TestCase): + + def test_set_gofit_engine(self): + self.assertEqual(self.wf.fit_engine, None) + x, y, e = gen_model_selection_data() + self.wf.preprocess_data(x, y, e) + with self.assertRaises(RuntimeError): + self.wf.set_gofit_engine(5, [], []) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/fit_functions/__init__.py b/test/gofit/__init__.py similarity index 100% rename from test/fit_functions/__init__.py rename to test/gofit/__init__.py diff --git a/test/fit_engines/gofitEngine_test.py b/test/gofit/gofitEngine_test.py similarity index 100% rename from test/fit_engines/gofitEngine_test.py rename to test/gofit/gofitEngine_test.py diff --git a/test/gofit/modelSelectionTemplate_test.py b/test/gofit/modelSelectionTemplate_test.py new file mode 100644 index 00000000..5280e7f4 --- /dev/null +++ b/test/gofit/modelSelectionTemplate_test.py @@ -0,0 +1,32 @@ +import unittest +from quickBayes.functions.BG import FlatBG +from quickBayes.test_helpers.model_selection import WorkflowTemplateTest +from quickBayes.test_helpers.workflow_helper import gen_model_selection_data + + +class WorkflowTemplateWithGoFitTest(WorkflowTemplateTest, + unittest.TestCase): + + def test_set_gofit_engine(self): + self.assertEqual(self.wf.fit_engine, None) + x, y, e = gen_model_selection_data() + self.wf.preprocess_data(x, y, e) + self.wf.set_gofit_engine(5, [], []) + self.assertEqual(self.wf.fit_engine.name, 'gofit') + + def test_update_gofit_engine(self): + self.assertEqual(self.wf.fit_engine, None) + x, y, e = gen_model_selection_data() + self.wf.preprocess_data(x, y, e) + self.wf.set_gofit_engine(5, [], []) + self.assertEqual(self.wf.fit_engine._lower, []) + self.assertEqual(self.wf.fit_engine._upper, []) + + bg = FlatBG() + self.wf.update_fit_engine(bg, [5]) + self.assertEqual(self.wf.fit_engine._lower, [-1]) + self.assertEqual(self.wf.fit_engine._upper, [1]) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/utils/__init__.py b/test/shared/__init__.py similarity index 100% rename from test/utils/__init__.py rename to test/shared/__init__.py diff --git a/test/data/muon/muon_expdecay_1.npy b/test/shared/data/muon/muon_expdecay_1.npy similarity index 100% rename from test/data/muon/muon_expdecay_1.npy rename to test/shared/data/muon/muon_expdecay_1.npy diff --git a/test/data/muon/muon_expdecay_2.npy b/test/shared/data/muon/muon_expdecay_2.npy similarity index 100% rename from test/data/muon/muon_expdecay_2.npy rename to test/shared/data/muon/muon_expdecay_2.npy diff --git a/test/data/muon/muon_expdecay_3.npy b/test/shared/data/muon/muon_expdecay_3.npy similarity index 100% rename from test/data/muon/muon_expdecay_3.npy rename to test/shared/data/muon/muon_expdecay_3.npy diff --git a/test/data/muon/muon_expdecay_3_big.npy b/test/shared/data/muon/muon_expdecay_3_big.npy similarity index 100% rename from test/data/muon/muon_expdecay_3_big.npy rename to test/shared/data/muon/muon_expdecay_3_big.npy diff --git a/test/data/qse_res.npy b/test/shared/data/qse_res.npy similarity index 100% rename from test/data/qse_res.npy rename to test/shared/data/qse_res.npy diff --git a/test/data/resolution_data_red.npy b/test/shared/data/resolution_data_red.npy similarity index 100% rename from test/data/resolution_data_red.npy rename to test/shared/data/resolution_data_red.npy diff --git a/test/data/sample_data_red.npy b/test/shared/data/sample_data_red.npy similarity index 100% rename from test/data/sample_data_red.npy rename to test/shared/data/sample_data_red.npy diff --git a/test/shared/fitting/__init__.py b/test/shared/fitting/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/test/fit_engines/fitUtils_test.py b/test/shared/fitting/fitUtils_test.py similarity index 100% rename from test/fit_engines/fitUtils_test.py rename to test/shared/fitting/fitUtils_test.py diff --git a/test/fit_engines/fitengine_test.py b/test/shared/fitting/fitengine_test.py similarity index 100% rename from test/fit_engines/fitengine_test.py rename to test/shared/fitting/fitengine_test.py diff --git a/test/fit_engines/scipyEngine_test.py b/test/shared/fitting/scipyEngine_test.py similarity index 100% rename from test/fit_engines/scipyEngine_test.py rename to test/shared/fitting/scipyEngine_test.py diff --git a/test/fit_functions/SEWithFixes_test.py b/test/shared/functions/SEWithFixes_test.py similarity index 100% rename from test/fit_functions/SEWithFixes_test.py rename to test/shared/functions/SEWithFixes_test.py diff --git a/test/fit_functions/SE_test.py b/test/shared/functions/SE_test.py similarity index 100% rename from test/fit_functions/SE_test.py rename to test/shared/functions/SE_test.py diff --git a/test/shared/functions/__init__.py b/test/shared/functions/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/test/fit_functions/composite_test.py b/test/shared/functions/composite_test.py similarity index 100% rename from test/fit_functions/composite_test.py rename to test/shared/functions/composite_test.py diff --git a/test/fit_functions/convolution_test.py b/test/shared/functions/convolution_test.py similarity index 100% rename from test/fit_functions/convolution_test.py rename to test/shared/functions/convolution_test.py diff --git a/test/fit_functions/delta_test.py b/test/shared/functions/delta_test.py similarity index 100% rename from test/fit_functions/delta_test.py rename to test/shared/functions/delta_test.py diff --git a/test/fit_functions/expdecay_test.py b/test/shared/functions/expdecay_test.py similarity index 100% rename from test/fit_functions/expdecay_test.py rename to test/shared/functions/expdecay_test.py diff --git a/test/shared/functions/fit_functions/SEWithFixes_test.py b/test/shared/functions/fit_functions/SEWithFixes_test.py new file mode 100644 index 00000000..8f563722 --- /dev/null +++ b/test/shared/functions/fit_functions/SEWithFixes_test.py @@ -0,0 +1,133 @@ +import unittest +import numpy as np +from quickBayes.functions.SE import StretchExp +from quickBayes.functions.SE_fix import StretchExpWithFixes + + +class StretchExpWithFixesTest(unittest.TestCase): + + """ + Since this function is calling a tested fit + function, but with some fixed parameters + we will test the values by using the original + function. Hence, we are assuming that the + values of the function are sufficiently + tested by the StretchExp tests. + Allowing us to focus on the fixes. + """ + def test_beta(self): + se = StretchExpWithFixes() + self.assertEqual(se.get_beta, 0.8) + + se.set_beta(0.9) + self.assertEqual(se.get_beta, 0.9) + + def test_init_values(self): + se = StretchExpWithFixes(beta=0.9, FWHM=0.12) + + self.assertEqual(se.get_beta, 0.9) + self.assertAlmostEqual(se.get_tau, 10.970, 3) + + def test_FWHM(self): + se = StretchExpWithFixes() + self.assertAlmostEqual(se.get_tau, 6.582, 3) + + se.set_FWHM(0.9) + self.assertAlmostEqual(se.get_tau, 1.463, 3) + + def test_call(self): + x = np.linspace(-0.4, 0.4, 6) + + se = StretchExp() + se_fix = StretchExpWithFixes() + + expect = se(x, 1.0, 0.01, se.tau(.1), .7) + + se_fix.set_beta(0.7) + se_fix.set_FWHM(.1) + y = se_fix(x, 1.0, 0.01) + + self.assertEqual(len(y), len(expect)) + for j in range(len(y)): + self.assertAlmostEqual(y[j], expect[j], 3) + + def test_report(self): + report = {"old": [1]} + + se = StretchExpWithFixes() + se.set_beta(0.5) + se.set_FWHM(0.132) + out = se.report(report, 1, 0.1) + + self.assertEqual(out["Amplitude"], [1]) + self.assertEqual(out["Peak Centre"], [0.1]) + self.assertAlmostEqual(out["tau"][0], 9.973, 3) + self.assertEqual(out["FWHM"], [0.132]) + self.assertEqual(out["beta"], [0.5]) + self.assertEqual(out["old"], [1]) + self.assertEqual(len(out.keys()), 6) + + def test_errors_report(self): + report = {"old": [1]} + + se = StretchExpWithFixes() + errors = np.array([0.1, 0.02]) + params = np.array([1, .2]) + out = se.report_errors(report, errors, params) + + self.assertEqual(out["Amplitude"], [0.1]) + self.assertEqual(out["Peak Centre"], [0.02]) + self.assertEqual(out["tau"], [0.]) + self.assertEqual(out["FWHM"], [0.0]) + self.assertEqual(out["beta"], [0.0]) + self.assertEqual(out["old"], [1]) + self.assertEqual(len(out.keys()), 6) + + def test_read(self): + report = {"old": [1]} + + se = StretchExp() + out = se.report(report, 1, 0.1, 10, .5) + + se_fix = StretchExpWithFixes() + params = se_fix.read_from_report(out, 0) + + self.assertEqual(params, [1, 0.1]) + self.assertEqual(se_fix.get_tau, 10) + self.assertEqual(se_fix.get_beta, .5) + + def test_set_FWHM(self): + se = StretchExpWithFixes(FWHM=.2) + self.assertAlmostEqual(se.get_tau, 6.582, 3) + se.set_FWHM(0.376) + self.assertAlmostEqual(se.get_tau, 3.501, 3) + + def test_N_params(self): + se = StretchExpWithFixes() + self.assertEqual(se.N_params, 2) + + def test_guess(self): + se = StretchExpWithFixes() + guess = se.get_guess() + expect = [0.1, 0.0] + self.assertEqual(len(guess), len(expect)) + for k in range(len(guess)): + self.assertAlmostEqual(guess[k], expect[k], 3) + + se.set_guess([1, 2]) + self.assertEqual(se.get_guess(), [1, 2]) + + def test_bounds(self): + se = StretchExpWithFixes() + bounds = se.get_bounds() + self.assertEqual(bounds[0], [0, -1.]) + self.assertEqual(bounds[1], [1., 1]) + + se.set_bounds([-1, -2], [3, 4]) + bounds = se.get_bounds() + self.assertEqual(bounds[0], [-1, -2.]) + self.assertEqual(bounds[1], [3., 4]) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/shared/functions/fit_functions/SE_test.py b/test/shared/functions/fit_functions/SE_test.py new file mode 100644 index 00000000..f59f5031 --- /dev/null +++ b/test/shared/functions/fit_functions/SE_test.py @@ -0,0 +1,117 @@ +import unittest +import numpy as np +from quickBayes.functions.SE import StretchExp + + +class StretchExpTest(unittest.TestCase): + + def test_call_1(self): + x = np.linspace(-0.4, 0.4, 6) + + se = StretchExp() + + y = se(x, 1.0, 0.0, 25.0, 0.5) + # from Mantid version 6.5 + expect = [0.192, 0.299, 1.001, 1.001, 0.299, 0.192] + + self.assertEqual(len(y), len(expect)) + for j in range(len(y)): + self.assertAlmostEqual(y[j], expect[j], 3) + + def test_call_2(self): + x = np.linspace(-0.4, 0.4, 6) + + se = StretchExp() + + y = se(x, 0.5, 0.1, 25.0, 0.5) + # from Mantid version 6.5 + expect = [0.083, 0.109, 0.201, 2.2993, 0.264, 0.122] + + self.assertEqual(len(y), len(expect)) + for j in range(len(y)): + self.assertAlmostEqual(y[j], expect[j], 3) + + def test_report(self): + report = {"old": [1]} + + se = StretchExp() + out = se.report(report, 1, 0.1, 10, .5) + + self.assertEqual(out["Amplitude"], [1]) + self.assertEqual(out["Peak Centre"], [0.1]) + self.assertEqual(out["tau"], [10.]) + self.assertAlmostEqual(out["FWHM"][0], 0.132, 3) + self.assertEqual(out["beta"], [0.5]) + self.assertEqual(out["old"], [1]) + self.assertEqual(len(out.keys()), 6) + + def test_errors_report(self): + report = {"old": [1]} + + se = StretchExp() + errors = np.array([0.1, 0.02, 0.5, 0.01]) + params = np.array([1, .1, 10, .5]) + out = se.report_errors(report, errors, params) + + self.assertEqual(out["Amplitude"], [0.1]) + self.assertEqual(out["Peak Centre"], [0.02]) + self.assertEqual(out["tau"], [0.5]) + self.assertAlmostEqual(out["FWHM"][0], 0.007, 3) + self.assertEqual(out["beta"], [0.01]) + self.assertEqual(out["old"], [1]) + self.assertEqual(len(out.keys()), 6) + + def test_read(self): + report = {"old": [1]} + + se = StretchExp() + out = se.report(report, 1, 0.1, 10, .5) + params = se.read_from_report(out, 0) + + self.assertEqual(params, [1, 0.1, 10, 0.5]) + + def test_FWHM(self): + se = StretchExp() + FWHM = se.FWHM(3.5) + self.assertAlmostEqual(FWHM, 0.376, 3) + # check round trip works + self.assertAlmostEqual(se.tau(FWHM), 3.5) + + def test_N_params(self): + se = StretchExp() + self.assertEqual(se.N_params, 4) + + def test_guess(self): + se = StretchExp() + guess = se.get_guess() + expect = [0.1, 0.0, 6.582, 0.7] + self.assertEqual(len(guess), len(expect)) + for k in range(len(guess)): + self.assertAlmostEqual(guess[k], expect[k], 3) + + def test_guess_set_guess(self): + se = StretchExp() + guess = se.get_guess() + expect = [0.1, 0.0, 6.582, 0.7] + self.assertEqual(len(guess), len(expect)) + for k in range(len(guess)): + self.assertAlmostEqual(guess[k], expect[k], 3) + se.set_guess([1, 2, 3, 4]) + self.assertEqual(se.get_guess(), [1, 2, 3, 4]) + + se.set_guess_FWHM([.2, .1, .4, .3]) + expect = [0.2, 0.1, 3.291, 0.3] + self.assertEqual(len(guess), len(expect)) + guess = se.get_guess() + for k in range(len(guess)): + self.assertAlmostEqual(guess[k], expect[k], 3) + + def test_bounds(self): + se = StretchExp() + bounds = se.get_bounds() + self.assertEqual(bounds[0], [0, -1., 0, 0]) + self.assertEqual(bounds[1], [1., 1, 100., 1.]) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/shared/functions/fit_functions/__init__.py b/test/shared/functions/fit_functions/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/test/shared/functions/fit_functions/composite_test.py b/test/shared/functions/fit_functions/composite_test.py new file mode 100644 index 00000000..6a21941c --- /dev/null +++ b/test/shared/functions/fit_functions/composite_test.py @@ -0,0 +1,223 @@ +import unittest +import numpy as np +from quickBayes.functions.SE import StretchExp +from quickBayes.functions.lorentz import Lorentzian +from quickBayes.functions.BG import LinearBG +from quickBayes.functions.composite import CompositeFunction + + +class CompositeFunctionTest(unittest.TestCase): + + def test_empty(self): + x = np.linspace(0, 5, 6) + c = CompositeFunction() + y = c(x) + + for j in range(len(x)): + self.assertAlmostEqual(y[j], 0.0, 8) + + def test_add_function(self): + x = np.linspace(0, 5, 6) + c = CompositeFunction() + bg = LinearBG('f0.') + + self.assertEqual(c.N_params, 0) + c.add_function(bg) + self.assertEqual(c.N_params, 2) + + a = 1.3 + b = -3.1 + expect = bg(x, a, b) + y = c(x, a, b) + + for j in range(len(x)): + self.assertAlmostEqual(y[j], expect[j], 8) + + def test_too_few_params(self): + x = np.linspace(0, 5, 6) + c = CompositeFunction() + bg = LinearBG('f0.') + c.add_function(bg) + with self.assertRaises(ValueError): + _ = c(x, 1.) # should have 2 params + + def test_too_many_params(self): + x = np.linspace(0, 5, 6) + c = CompositeFunction() + bg = LinearBG('f0.') + c.add_function(bg) + + with self.assertRaises(ValueError): + _ = c(x, 1, 2, 3) # should have 2 params + + def test_sum(self): + x = np.linspace(-0.4, 0.4, 6) + + lor = Lorentzian() + c = CompositeFunction() + bg = LinearBG('f0.') + c.add_function(bg) + c.add_function(lor) + + y_l = lor(x, 20.3, 0.031, 0.3) + y_bg = bg(x, 1, -2) + + y = c(x, 1, -2, 20.3, 0.031, 0.3) + + for j in range(5): + self.assertAlmostEqual(y[j], y_l[j] + y_bg[j], 3) + + def test_report(self): + report = {"old": [1]} + + lor = Lorentzian() + c = CompositeFunction() + c.add_function(lor) + + out = c.report(report, 3.2, -1, 2.5) + self.assertEqual(out["f1.Amplitude"], [3.2]) + self.assertEqual(out["f1.Peak Centre"], [-1]) + self.assertEqual(out["f1.Gamma"], [2.5]) + self.assertEqual(out["old"], [1]) + self.assertEqual(len(out.keys()), 4) + + def test_report2(self): + report = {"old": [1]} + + lor = Lorentzian() + c = CompositeFunction() + c.add_function(lor) + lor2 = Lorentzian() + c.add_function(lor2) + + out = c.report(report, 3.2, -1, 2.5, 5, 6, 7) + + self.assertEqual(out["f1.Amplitude"], [3.2]) + self.assertEqual(out["f1.Peak Centre"], [-1]) + self.assertEqual(out["f1.Gamma"], [2.5]) + + self.assertEqual(out["f2.Amplitude"], [5]) + self.assertEqual(out["f2.Peak Centre"], [6]) + self.assertEqual(out["f2.Gamma"], [7]) + self.assertEqual(out["old"], [1]) + self.assertEqual(len(out.keys()), 7) + + def test_custom_report_errors(self): + """ + stretch exp calculates the error for FWHM, + test that this is used when its part of + a composite function. + """ + se = StretchExp() + c = CompositeFunction() + c.add_function(se) + + params = [1.97e-1, -1.43e-3, 2.10e1, 7.73e-1] + sigma = [3.e-4, 5.e-5, 5.e-2, 1.7e-3] + + errors = c.report_errors({}, sigma, params) + + self.assertAlmostEqual(errors['f1.FWHM'][0], 0.00015, 5) + + def test_read(self): + report = {"old": [1]} + + lor = Lorentzian() + c = CompositeFunction() + c.add_function(lor) + + out = c.report(report, 3.2, -1, 2.5) + params = c.read_from_report(out, 0) + self.assertEqual(params, [3.2, -1, 2.5]) + + def test_read2(self): + report = {"old": [1]} + + lor = Lorentzian() + c = CompositeFunction() + c.add_function(lor) + lor2 = Lorentzian() + c.add_function(lor2) + + out = c.report(report, 3.2, -1, 2.5, 5, 6, 7) + params = c.read_from_report(out, 0) + self.assertEqual(params, [3.2, -1, 2.5, 5, 6, 7]) + + def test_guess(self): + lor = Lorentzian() + c = CompositeFunction() + bg = LinearBG() + c.add_function(bg) + c.add_function(lor) + + self.assertEqual(c.get_guess(), [0., 0., 0.01, 0., 0.02]) + + def test_bounds(self): + lor = Lorentzian() + c = CompositeFunction() + bg = LinearBG() + c.add_function(bg) + c.add_function(lor) + + bounds = c.get_bounds() + + self.assertEqual(bounds[0], [-1., -1., 0., -1., 1.e-6]) + self.assertEqual(bounds[1], [1., 1., 1., 1., 1.]) + + def test_set_guess(self): + lor = Lorentzian() + c = CompositeFunction() + bg = LinearBG() + c.add_function(bg) + c.add_function(lor) + + self.assertEqual(c.get_guess(), [0., 0., 0.01, 0., 0.02]) + + c.set_guess([1, 2], 0) + self.assertEqual(c.get_guess(), [1., 2., 0.01, 0., 0.02]) + + c.set_guess([3, 4, 5]) + self.assertEqual(c.get_guess(), [1., 2., 3, 4., 5]) + + def test_set_bounds(self): + lor = Lorentzian() + c = CompositeFunction() + bg = LinearBG() + c.add_function(bg) + c.add_function(lor) + + bounds = c.get_bounds() + + self.assertEqual(bounds[0], [-1., -1., 0., -1., 1.e-6]) + self.assertEqual(bounds[1], [1., 1., 1., 1., 1.]) + + c.set_bounds([-2, -3], [2, 3], 0) + bounds = c.get_bounds() + + self.assertEqual(bounds[0], [-2., -3., 0., -1., 1.e-6]) + self.assertEqual(bounds[1], [2., 3., 1., 1., 1.]) + + c.set_bounds([-4, -5, -6], [4, 5, 6]) + bounds = c.get_bounds() + + self.assertEqual(bounds[0], [-2., -3., -4., -5., -6]) + self.assertEqual(bounds[1], [2., 3., 4., 5., 6.]) + + def test_update_prefix(self): + lor = Lorentzian() + c = CompositeFunction() + bg = LinearBG() + c.add_function(bg) + c.add_function(lor) + + for j, fun in enumerate(c._funcs): + self.assertEqual(fun._prefix, f'f{j+1}.') + + c.update_prefix("test:") + + for j, fun in enumerate(c._funcs): + self.assertEqual(fun._prefix, f'test:f{j+1}.') + + +if __name__ == '__main__': + unittest.main() diff --git a/test/shared/functions/fit_functions/convolution_test.py b/test/shared/functions/fit_functions/convolution_test.py new file mode 100644 index 00000000..ba448fbb --- /dev/null +++ b/test/shared/functions/fit_functions/convolution_test.py @@ -0,0 +1,231 @@ +import unittest +from numpy import ndarray +import numpy as np +from quickBayes.functions.gaussian import Gaussian +from quickBayes.functions.convolution import ( + ConvolutionWithResolution as conv) +from quickBayes.utils.crop_data import crop + + +def analytic(x: ndarray, amp: float, mu: float, sig: float, + r_mu: float, r_sig: float) -> ndarray: + """ + Expected results from wofram site on convolution + """ + expect = np.exp(- pow(x-(mu + r_mu), 2)/(2.*(r_sig**2 + sig**2))) + expect *= amp/np.sqrt(2.*np.pi*(r_sig**2 + sig**2)) + return expect + + +class ConvolutionTest(unittest.TestCase): + + def test_update_resolution(self): + def func(x): + return x*x - 3.*x + 1.2 + + x = np.linspace(-5, 5, 6) + y = func(x) + c = conv(x, y, -6, 6) + + new_x = np.linspace(-5, 5, 100) + c.update_x_range(new_x) + ry = c._ry + expect = func(new_x) + # normalise the expected values: + expect /= sum(expect) + + self.assertEqual(len(ry), len(new_x)) + for j in range(len(ry)): + self.assertAlmostEqual(ry[j], expect[j], 3) + + def test_conv_call(self): + """ + Need the x range to go to zero at the ends to + prevent edge effects + Also need lots of data points as + the shape needs to be well defined + """ + x = np.linspace(-15., 15, 9000) + """ + need to set amp=1 + Since the kernel is normalised + Let the other fit parameters + absorb the value + """ + res = Gaussian() + r_a = 1.0 + r_mu = 4.0 + r_sig = 1.1 + res_y = res(x, r_a, r_mu, r_sig) + + c = conv(x, res_y, -16.0, 16.) + + g = Gaussian() + c.add_function(g) + + amp = 1. + mu = -2.4 + sig = 0.8 + + # expected results from wofram site on convolution + expect = analytic(x, amp, mu, sig, r_mu, r_sig) + y = c(x, amp, mu, sig) + + for j in range(len(x)): + self.assertAlmostEqual(y[j], expect[j], 3) + + def test_conv_call_with_crop(self): + """ + Need the x range to go to zero at the ends to + prevent edge effects + Also need lots of data points as + the shape needs to be well defined + """ + x = np.linspace(-15., 15, 9000) + """ + need to set amp=1 + Since the kernel is normalised + Let the other fit parameters + absorb the value + """ + res = Gaussian() + r_a = 1.0 + r_mu = 4.0 + r_sig = 1.1 + res_y = res(x, r_a, r_mu, r_sig) + + c = conv(x, res_y, -10.0, 10.) + + g = Gaussian() + c.add_function(g) + + amp = 1. + mu = -2.4 + sig = 0.8 + x, _, _ = crop(x, res_y, None, -10, 10) + + expect = analytic(x, amp, mu, sig, r_mu, r_sig) + y = c(x, amp, mu, sig) + + for j in range(len(x)): + self.assertAlmostEqual(y[j], expect[j], 3) + + def test_conv_report(self): + report = {"old": [1]} + x = np.linspace(0, 1) + g = Gaussian() + c = conv(x, x, 0, 1) + c.add_function(g) + out = c.report(report, 3.2, -1, 2.5) + + self.assertEqual(out["f1.f1.Amplitude"], [3.2]) + self.assertEqual(out["f1.f1.Mean"], [-1]) + self.assertEqual(out["f1.f1.Sigma"], [2.5]) + self.assertEqual(out["old"], [1]) + self.assertEqual(len(out.keys()), 4) + + def test_conv_report2(self): + report = {"old": [1]} + x = np.linspace(0, 1) + g = Gaussian() + g2 = Gaussian() + c = conv(x, x, 0, 1) + c.add_function(g) + c.add_function(g2) + out = c.report(report, 3.2, -1, 2.5, 5, 6, 7) + + self.assertEqual(out["f1.f1.Amplitude"], [3.2]) + self.assertEqual(out["f1.f1.Mean"], [-1]) + self.assertEqual(out["f1.f1.Sigma"], [2.5]) + self.assertEqual(out["f1.f2.Amplitude"], [5]) + self.assertEqual(out["f1.f2.Mean"], [6]) + self.assertEqual(out["f1.f2.Sigma"], [7]) + self.assertEqual(out["old"], [1]) + self.assertEqual(len(out.keys()), 7) + + def test_read(self): + report = {"old": [1]} + x = np.linspace(0, 1) + g = Gaussian() + c = conv(x, x, 0, 1) + c.add_function(g) + out = c.report(report, 3.2, -1, 2.5) + params = c.read_from_report(out, 0) + + self.assertEqual(params, [3.2, -1, 2.5]) + + def test_read2(self): + report = {"old": [1]} + x = np.linspace(0, 1) + g = Gaussian() + g2 = Gaussian() + c = conv(x, x, 0, 1) + c.add_function(g) + c.add_function(g2) + out = c.report(report, 3.2, -1, 2.5, 5, 6, 7) + params = c.read_from_report(out, 0) + self.assertEqual(params, [3.2, -1, 2.5, 5, 6, 7]) + + def test_N_params(self): + x = np.linspace(0, 1) + c = conv(x, x, 1, 2) + self.assertEqual(c.N_params, 0) + g = Gaussian() + c.add_function(g) + self.assertEqual(c.N_params, 3) + + def test_guess(self): + x = np.linspace(0, 1) + c = conv(x, x, 1, 2) + g = Gaussian() + c.add_function(g) + self.assertEqual(c.get_guess(), [1., 0., 0.1]) + + def test_bounds(self): + x = np.linspace(0, 1) + c = conv(x, x, 1, 2) + g = Gaussian() + c.add_function(g) + lower, upper = c.get_bounds() + self.assertEqual(lower, [0., -1., 0.0]) + self.assertEqual(upper, [np.inf, 1., np.inf]) + + def test_set_guess(self): + x = np.linspace(0, 1) + c = conv(x, x, 1, 2) + g = Gaussian() + g2 = Gaussian() + c.add_function(g) + c.add_function(g2) + self.assertEqual(c.get_guess(), [1., 0., 0.1, 1., 0, 0.1]) + + c.set_guess([2, 3, 4], 0) + self.assertEqual(c.get_guess(), [2., 3., 4, 1., 0, 0.1]) + + c.set_guess([6, 5, 8]) + self.assertEqual(c.get_guess(), [2., 3., 4, 6, 5, 8]) + + def test_set_bounds(self): + x = np.linspace(0, 1) + c = conv(x, x, 1, 2) + g = Gaussian() + g2 = Gaussian() + c.add_function(g) + c.add_function(g2) + lower, upper = c.get_bounds() + self.assertEqual(lower, [0., -1., 0.0, 0, -1, 0]) + self.assertEqual(upper, [np.inf, 1., np.inf, np.inf, 1, np.inf]) + + c.set_bounds([-1, -2, -3], [1, 2, 3], 0) + lower, upper = c.get_bounds() + self.assertEqual(lower, [-1., -2., -3, 0, -1, 0]) + self.assertEqual(upper, [1., 2, 3, np.inf, 1, np.inf]) + + c.set_bounds([-4, -5, -6], [3, 4, 5]) + lower, upper = c.get_bounds() + self.assertEqual(lower, [-1., -2., -3, -4, -5, -6]) + self.assertEqual(upper, [1., 2, 3, 3, 4, 5]) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/shared/functions/fit_functions/delta_test.py b/test/shared/functions/fit_functions/delta_test.py new file mode 100644 index 00000000..7b2ff230 --- /dev/null +++ b/test/shared/functions/fit_functions/delta_test.py @@ -0,0 +1,94 @@ +import unittest +import numpy as np +from quickBayes.functions.delta import Delta + + +class DeltaTest(unittest.TestCase): + + def test_delta_call(self): + x = np.linspace(0.0, 5.0, 11) + d = Delta() + y = d(x, 2.3, 1.2) + # x : 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5. + expect = [0, 0.0, 2.3/0.5, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0] + + for j in range(len(expect)): + self.assertAlmostEqual(y[j], expect[j], 3) + + def test_delta_first_edge(self): + x = np.linspace(0.0, 5.0, 11) + d = Delta() + y = d(x, 2.3, 0.2) + # x : 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5. + expect = [2.3/0.5, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0] + + for j in range(len(expect)): + self.assertAlmostEqual(y[j], expect[j], 3) + + def test_delta_top_edge(self): + x = np.linspace(0.0, 5.0, 11) + d = Delta() + y = d(x, 2.3, 4.7) + # this makes sure that the last bin is occupied + # x : 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5. + expect = [0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 2.3/0.5, 0] + + for j in range(len(expect)): + self.assertAlmostEqual(y[j], expect[j], 3) + + def test_delta_report(self): + report = {"old": [1]} + + d = Delta() + out = d.report(report, 3.2, -1) + + self.assertEqual(out["Amplitude"], [3.2]) + self.assertEqual(out["Centre"], [-1]) + self.assertEqual(out["old"], [1]) + self.assertEqual(len(out.keys()), 3) + + def test_read(self): + report = {"old": [1]} + + d = Delta() + out = d.report(report, 3.2, -1) + params = d.read_from_report(out, 0) + self.assertEqual(params, [3.2, -1]) + + def test_N_params(self): + d = Delta() + self.assertEqual(d.N_params, 2) + + def test_guess(self): + d = Delta() + self.assertEqual(d.get_guess(), [1., 0.]) + + def test_bounds(self): + d = Delta() + bounds = d.get_bounds() + + self.assertEqual(bounds[0], [0, -1]) + self.assertEqual(bounds[1], [np.inf, 1]) + + def test_set_guess(self): + d = Delta() + self.assertEqual(d.get_guess(), [1., 0.]) + d.set_guess([10., 1.]) + self.assertEqual(d.get_guess(), [10., 1.]) + + def test_set_bounds(self): + d = Delta() + bounds = d.get_bounds() + + self.assertEqual(bounds[0], [0, -1]) + self.assertEqual(bounds[1], [np.inf, 1]) + + bounds = d.set_bounds([1, 2], [5, 6]) + bounds = d.get_bounds() + + self.assertEqual(bounds[0], [1, 2]) + self.assertEqual(bounds[1], [5, 6]) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/shared/functions/fit_functions/expdecay_test.py b/test/shared/functions/fit_functions/expdecay_test.py new file mode 100644 index 00000000..efedfb81 --- /dev/null +++ b/test/shared/functions/fit_functions/expdecay_test.py @@ -0,0 +1,73 @@ +import unittest +import numpy as np +from quickBayes.functions.exp_decay import ExpDecay + + +class ExpDecayTest(unittest.TestCase): + + def test_exp_decay_call(self): + x = np.linspace(0., 10., 11) + + fun = ExpDecay() + + y = fun(x, 0.4, 0.2) + expect = [0.4, 0.327, 0.268, 0.220, 0.180, 0.147, + 0.120, 0.0986, 0.0808, 0.0661, 0.0541] + + for j in range(len(x)): + self.assertAlmostEqual(y[j], expect[j], 3) + + def test_exp_decay_report(self): + report = {"old": [1]} + + fun = ExpDecay() + out = fun.report(report, 3.2, 2.5) + + self.assertEqual(out["Amplitude"], [3.2]) + self.assertEqual(out["lambda"], [2.5]) + self.assertEqual(out["old"], [1]) + self.assertEqual(len(out.keys()), 3) + + def test_read(self): + report = {"old": [1]} + + fun = ExpDecay() + out = fun.report(report, 3.2, -1) + params = fun.read_from_report(out, 0) + + self.assertEqual(params, [3.2, -1]) + + def test_N_params(self): + fun = ExpDecay() + self.assertEqual(fun.N_params, 2) + + def test_guess(self): + fun = ExpDecay() + self.assertEqual(fun.get_guess(), [1.0, 0.1]) + + def test_bounds(self): + fun = ExpDecay() + bounds = fun.get_bounds() + self.assertEqual(bounds[0], [0., 0.001]) + self.assertEqual(bounds[1], [1., 20.]) + + def test_set_guess(self): + fun = ExpDecay() + self.assertEqual(fun.get_guess(), [1.0, 0.1]) + fun.set_guess([2.0, 0.5]) + self.assertEqual(fun.get_guess(), [2.0, 0.5]) + + def test_set_bounds(self): + fun = ExpDecay() + bounds = fun.get_bounds() + self.assertEqual(bounds[0], [0., 0.001]) + self.assertEqual(bounds[1], [1., 20.]) + + fun.set_bounds([1, 2], [3, 4]) + bounds = fun.get_bounds() + self.assertEqual(bounds[0], [1, 2]) + self.assertEqual(bounds[1], [3, 4]) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/fit_functions/flatBG_test.py b/test/shared/functions/fit_functions/flatBG_test.py similarity index 100% rename from test/fit_functions/flatBG_test.py rename to test/shared/functions/fit_functions/flatBG_test.py diff --git a/test/fit_functions/gaussian_test.py b/test/shared/functions/fit_functions/gaussian_test.py similarity index 100% rename from test/fit_functions/gaussian_test.py rename to test/shared/functions/fit_functions/gaussian_test.py diff --git a/test/fit_functions/linearBG_test.py b/test/shared/functions/fit_functions/linearBG_test.py similarity index 100% rename from test/fit_functions/linearBG_test.py rename to test/shared/functions/fit_functions/linearBG_test.py diff --git a/test/fit_functions/lorentzian_test.py b/test/shared/functions/fit_functions/lorentzian_test.py similarity index 100% rename from test/fit_functions/lorentzian_test.py rename to test/shared/functions/fit_functions/lorentzian_test.py diff --git a/test/fit_functions/noBG_test.py b/test/shared/functions/fit_functions/noBG_test.py similarity index 100% rename from test/fit_functions/noBG_test.py rename to test/shared/functions/fit_functions/noBG_test.py diff --git a/test/fit_functions/qldataFunction_test.py b/test/shared/functions/fit_functions/qldataFunction_test.py similarity index 100% rename from test/fit_functions/qldataFunction_test.py rename to test/shared/functions/fit_functions/qldataFunction_test.py diff --git a/test/fit_functions/qseFixedFunction_test.py b/test/shared/functions/fit_functions/qseFixedFunction_test.py similarity index 100% rename from test/fit_functions/qseFixedFunction_test.py rename to test/shared/functions/fit_functions/qseFixedFunction_test.py diff --git a/test/fit_functions/qseFunction_test.py b/test/shared/functions/fit_functions/qseFunction_test.py similarity index 100% rename from test/fit_functions/qseFunction_test.py rename to test/shared/functions/fit_functions/qseFunction_test.py diff --git a/test/shared/functions/flatBG_test.py b/test/shared/functions/flatBG_test.py new file mode 100644 index 00000000..58df2411 --- /dev/null +++ b/test/shared/functions/flatBG_test.py @@ -0,0 +1,80 @@ +import unittest +import numpy as np +from quickBayes.functions.BG import FlatBG + + +class FlatBGTest(unittest.TestCase): + + def test_flat_BG_call(self): + x = np.linspace(0, 5, 6) + + bg = FlatBG() + + y = bg(x, 1.2) + + for j in range(5): + self.assertAlmostEqual(y[j], 1.2) + + def test_flat_BG_report(self): + report = {"old": [1]} + + bg = FlatBG() + out = bg.report(report, 3.2) + + self.assertEqual(out["BG constant"], [3.2]) + self.assertEqual(out["old"], [1]) + self.assertEqual(len(out.keys()), 2) + + def test_flat_BG_report_error(self): + report = {"old": [1]} + + bg = FlatBG() + out = bg.report_errors(report, [0.1], [3.2]) + + self.assertEqual(out["BG constant"], [0.1]) + self.assertEqual(out["old"], [1]) + self.assertEqual(len(out.keys()), 2) + + def test_N_params(self): + bg = FlatBG() + self.assertEqual(bg.N_params, 1) + + def test_guess(self): + bg = FlatBG() + self.assertEqual(bg.get_guess(), [0.]) + + def test_bounds(self): + bg = FlatBG() + lower, upper = bg.get_bounds() + self.assertEqual(lower, [-1.]) + self.assertEqual(upper, [1.]) + + def test_read(self): + report = {"old": [1]} + + bg = FlatBG() + report = bg.report(report, 3.2) + params = bg.read_from_report(report, 0) + self.assertEqual(params, [3.2]) + + def test_set_guess(self): + bg = FlatBG() + self.assertEqual(bg.get_guess(), [0.]) + + bg.set_guess([1.]) + self.assertEqual(bg.get_guess(), [1.]) + + def test_set_bounds(self): + bg = FlatBG() + lower, upper = bg.get_bounds() + self.assertEqual(lower, [-1.]) + self.assertEqual(upper, [1.]) + + bg.set_bounds([0], [2]) + lower, upper = bg.get_bounds() + self.assertEqual(lower, [0.]) + self.assertEqual(upper, [2.]) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/shared/functions/gaussian_test.py b/test/shared/functions/gaussian_test.py new file mode 100644 index 00000000..053e4062 --- /dev/null +++ b/test/shared/functions/gaussian_test.py @@ -0,0 +1,74 @@ +import unittest +import numpy as np +from quickBayes.functions.gaussian import Gaussian + + +class GaussianTest(unittest.TestCase): + + def test_gaussian_call(self): + x = np.linspace(0., 10., 11) + + g = Gaussian() + + y = g(x, 2.5, 4.2, 1.3) + expect = [4e-3, 3.71e-2, 0.183, 0.501, 0.758, + 0.635, 0.294, 7.54e-2, 1.07e-2, 8e-4, 0] + + for j in range(len(x)): + self.assertAlmostEqual(y[j], expect[j], 3) + + def test_lorentzian_report(self): + report = {"old": [1]} + + g = Gaussian() + out = g.report(report, 3.2, -1, 2.5) + + self.assertEqual(out["Amplitude"], [3.2]) + self.assertEqual(out["Mean"], [-1]) + self.assertEqual(out["Sigma"], [2.5]) + self.assertEqual(out["old"], [1]) + self.assertEqual(len(out.keys()), 4) + + def test_read(self): + report = {"old": [1]} + + g = Gaussian() + out = g.report(report, 3.2, -1, 2.5) + params = g.read_from_report(out, 0) + + self.assertEqual(params, [3.2, -1, 2.5]) + + def test_N_params(self): + g = Gaussian() + self.assertEqual(g.N_params, 3) + + def test_guess(self): + g = Gaussian() + self.assertEqual(g.get_guess(), [1.0, 0.0, 0.1]) + + def test_bounds(self): + g = Gaussian() + bounds = g.get_bounds() + self.assertEqual(bounds[0], [0., -1, 0.]) + self.assertEqual(bounds[1], [np.inf, 1, np.inf]) + + def test_set_guess(self): + g = Gaussian() + self.assertEqual(g.get_guess(), [1.0, 0.0, 0.1]) + g.set_guess([2.0, 3.0, 0.4]) + self.assertEqual(g.get_guess(), [2.0, 3.0, 0.4]) + + def test_set_bounds(self): + g = Gaussian() + bounds = g.get_bounds() + self.assertEqual(bounds[0], [0., -1, 0.]) + self.assertEqual(bounds[1], [np.inf, 1, np.inf]) + + g.set_bounds([1, 2, 3], [6, 7, 8]) + bounds = g.get_bounds() + self.assertEqual(bounds[0], [1., 2, 3.]) + self.assertEqual(bounds[1], [6, 7, 8]) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/shared/functions/linearBG_test.py b/test/shared/functions/linearBG_test.py new file mode 100644 index 00000000..519321da --- /dev/null +++ b/test/shared/functions/linearBG_test.py @@ -0,0 +1,92 @@ +import unittest +import numpy as np +from quickBayes.functions.BG import LinearBG + + +class LinearBGTest(unittest.TestCase): + + def test_linear_BG_call(self): + x = np.linspace(0, 5, 6) + + lbg = LinearBG() + + y = lbg(x, 1.2, -3.) + expect = [-3., -1.8, -0.6, 0.6, 1.8] + + for j in range(5): + self.assertAlmostEqual(y[j], expect[j]) + + def test_linear_BG_report(self): + report = {"old": [1]} + + lbg = LinearBG() + out = lbg.report(report, 3.2, -1) + + self.assertEqual(out["BG gradient"], [3.2]) + self.assertEqual(out["BG constant"], [-1]) + self.assertEqual(out["old"], [1]) + self.assertEqual(len(out.keys()), 3) + + def test_N_params(self): + lbg = LinearBG() + self.assertEqual(lbg.N_params, 2) + + def test_guess(self): + lbg = LinearBG() + self.assertEqual(lbg.get_guess(), [0., 0.]) + + def test_bounds(self): + lbg = LinearBG() + lower, upper = lbg.get_bounds() + self.assertEqual(lower, [-1., -1.]) + self.assertEqual(upper, [1., 1.]) + + def test_read(self): + report = {"old": [1]} + + lbg = LinearBG() + report = lbg.report(report, 3.2, -1) + params = lbg.read_from_report(report, 0) + self.assertEqual(params, [3.2, -1]) + + def test_multiple_report(self): + report = {} + lbg = LinearBG() + report = lbg.report(report, 3.2, -1) + report = lbg.report(report, 4.1, .5) + report = lbg.report(report, 0, -.4) + + self.assertEqual(report[lbg.constant], [-1, .5, -.4]) + self.assertEqual(report[lbg.grad], [3.2, 4.1, 0.]) + + def test_read_index_1(self): + report = {} + lbg = LinearBG() + report = lbg.report(report, 3.2, -1) + report = lbg.report(report, 4.1, .5) + report = lbg.report(report, 0, -.4) + + params = lbg.read_from_report(report, 1) + self.assertEqual(params, [4.1, .5]) + + def test_set_guess(self): + lbg = LinearBG() + self.assertEqual(lbg.get_guess(), [0., 0.]) + + lbg.set_guess([1., 1.]) + self.assertEqual(lbg.get_guess(), [1., 1.]) + + def test_set_bounds(self): + lbg = LinearBG() + lower, upper = lbg.get_bounds() + self.assertEqual(lower, [-1., -1]) + self.assertEqual(upper, [1., 1.]) + + lbg.set_bounds([0, 0], [2, 2]) + lower, upper = lbg.get_bounds() + self.assertEqual(lower, [0., 0]) + self.assertEqual(upper, [2., 2]) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/shared/functions/lorentzian_test.py b/test/shared/functions/lorentzian_test.py new file mode 100644 index 00000000..cd3c7be0 --- /dev/null +++ b/test/shared/functions/lorentzian_test.py @@ -0,0 +1,73 @@ +import unittest +import numpy as np +from quickBayes.functions.lorentz import Lorentzian + + +class LorentzianTest(unittest.TestCase): + + def test_lorentzian_call(self): + x = np.linspace(-0.4, 0.4, 6) + + lor = Lorentzian() + + y = lor(x, 20.3, 0.031, 0.3) + # from Mantid version 6.5 + expect = [4.654, 10.103, 27.835, 38.924, 14.645, 6.109] + + for j in range(5): + self.assertAlmostEqual(y[j], expect[j], 3) + + def test_read(self): + report = {"old": [1]} + + lor = Lorentzian() + out = lor.report(report, 3.2, -1, 2.5) + params = lor.read_from_report(out, 0) + self.assertEqual(params, [3.2, -1, 2.5]) + + def test_lorentzian_report(self): + report = {"old": [1]} + + lor = Lorentzian() + out = lor.report(report, 3.2, -1, 2.5) + + self.assertEqual(out["Amplitude"], [3.2]) + self.assertEqual(out["Peak Centre"], [-1]) + self.assertEqual(out["Gamma"], [2.5]) + self.assertEqual(out["old"], [1]) + self.assertEqual(len(out.keys()), 4) + + def test_N_params(self): + lor = Lorentzian() + self.assertEqual(lor.N_params, 3) + + def test_guess(self): + lor = Lorentzian() + self.assertEqual(lor.get_guess(), [0.01, 0., 0.02]) + + def test_bounds(self): + lor = Lorentzian() + bounds = lor.get_bounds() + self.assertEqual(bounds[0], [0., -1, 1.e-6]) + self.assertEqual(bounds[1], [1., 1, 1.]) + + def test_set_guess(self): + lor = Lorentzian() + self.assertEqual(lor.get_guess(), [0.01, 0., 0.02]) + lor.set_guess([1., 2., 3]) + self.assertEqual(lor.get_guess(), [1, 2., 3]) + + def test_set_bounds(self): + lor = Lorentzian() + bounds = lor.get_bounds() + self.assertEqual(bounds[0], [0., -1, 1.e-6]) + self.assertEqual(bounds[1], [1., 1, 1.]) + + lor.set_bounds([-1, -2, -3], [2, 3, 4]) + bounds = lor.get_bounds() + self.assertEqual(bounds[0], [-1, -2, -3]) + self.assertEqual(bounds[1], [2, 3, 4]) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/shared/functions/noBG_test.py b/test/shared/functions/noBG_test.py new file mode 100644 index 00000000..06b3d9c8 --- /dev/null +++ b/test/shared/functions/noBG_test.py @@ -0,0 +1,51 @@ +import unittest +import numpy as np +from quickBayes.functions.BG import NoBG + + +class NoBGTest(unittest.TestCase): + + def test_no_BG_call(self): + x = np.linspace(0, 5, 6) + + bg = NoBG() + + y = bg(x) + + for j in range(5): + self.assertAlmostEqual(y[j], 0.0) + + def test_no_BG_report(self): + report = {"old": [1]} + + bg = NoBG() + out = bg.report(report) + + self.assertEqual(out["old"], [1]) + self.assertEqual(len(out.keys()), 1) + + def test_N_params(self): + bg = NoBG() + self.assertEqual(bg.N_params, 0) + + def test_guess(self): + bg = NoBG() + self.assertEqual(bg.get_guess(), []) + + def test_bounds(self): + bg = NoBG() + lower, upper = bg.get_bounds() + self.assertEqual(lower, []) + self.assertEqual(upper, []) + + def test_read(self): + report = {"old": [1]} + + bg = NoBG() + report = bg.report(report) + params = bg.read_from_report(report, 0) + self.assertEqual(params, []) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/shared/functions/qldataFunction_test.py b/test/shared/functions/qldataFunction_test.py new file mode 100644 index 00000000..0b2d822e --- /dev/null +++ b/test/shared/functions/qldataFunction_test.py @@ -0,0 +1,598 @@ +import unittest +import numpy as np +from quickBayes.functions.lorentz import Lorentzian +from quickBayes.functions.BG import LinearBG +from quickBayes.functions.qldata_function import QlDataFunction + + +class QLDataFunctionTest(unittest.TestCase): + + def test_update_resolution(self): + def func(x): + return x*x - 3.*x + 1.2 + + x = np.linspace(-5, 5, 6) + y = func(x) + bg = LinearBG() + ql = QlDataFunction(bg, False, x, y, -6, 6) + + new_x = np.linspace(-5, 5, 100) + ql.update_x_range(new_x) + ry = ql.conv._ry + expect = func(new_x) + # normalise the expected values: + expect /= sum(expect) + + self.assertEqual(len(ry), len(new_x)) + for j in range(len(ry)): + self.assertAlmostEqual(ry[j], expect[j], 3) + + def test_get_guess(self): + x = np.linspace(0, 5, 6) + bg = LinearBG() + ql = QlDataFunction(bg, False, x, x, 0, 6) + with self.assertRaises(RuntimeError): + ql.set_guess([1, 2]) + + def test_just_bg(self): + x = np.linspace(0, 5, 6) + bg = LinearBG() + ql = QlDataFunction(bg, False, x, x, 0, 6) + y = ql(x, 1.2, 3) + expect = 1.2*x + 3 + + self.assertEqual(ql.N_params, 2) + for j in range(len(x)): + self.assertAlmostEqual(y[j], expect[j]) + + self.assertEqual(ql.get_guess(), [0., 0.]) + + bounds = ql.get_bounds() + self.assertEqual(bounds[0], [-1, -1]) + self.assertEqual(bounds[1], [1, 1]) + + report = {} + report = ql.report(report, 1, 2) + self.assertEqual(report["N0:f1.BG gradient"], [1.]) + self.assertEqual(report["N0:f1.BG constant"], [2.]) + + ql.set_BG_guess([1., 2.]) + self.assertEqual(ql.get_guess(), [1., 2.]) + + def test_bg_and_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + lor = Lorentzian() + y = lor(x, 1., -.2, .6) + ql = QlDataFunction(bg, True, x, y, -6, 6) + y = ql(x, 1.2, 3, .2, .1) + expect = [-3, 0.00184, 3.076, 6.001, 9.000] + + g = ql.get_guess() + self.assertEqual(g, [0., 0., 1., 0.]) + + bounds = ql.get_bounds() + self.assertEqual(bounds[0], [-1, -1, 0., -1]) + self.assertEqual(bounds[1], [1, 1, np.inf, 1]) + + self.assertEqual(ql.N_params, 4) + for j in range(len(x)): + self.assertAlmostEqual(y[j], expect[j], 3) + + report = {} + report = ql.report(report, 1, 2, 3, 4) + self.assertEqual(len(report.keys()), 4) + self.assertEqual(report["N0:f1.BG gradient"], [1.]) + self.assertEqual(report["N0:f1.BG constant"], [2.]) + + self.assertEqual(report["N0:f2.f1.Amplitude"], [3.]) + self.assertEqual(report["N0:f2.f1.Centre"], [4]) + + ql.set_BG_guess([1., 2.]) + self.assertEqual(ql.get_guess(), [1., 2., 1., 0.]) + + ql.set_delta_guess([3., 4.]) + self.assertEqual(ql.get_guess(), [1., 2., 3., 4.]) + + def test_bg_and_delta_and_1_lorentzian(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + lor = Lorentzian() + y = lor(x, 1., -.2, .6) + ql = QlDataFunction(bg, True, x, y, -6, 6) + ql.add_single_lorentzian() + + y = ql(x, .02, 1, .2, .1, 1, .6) + expect = [0.909, 0.987, 1.984, 1.083, 1.109] + + self.assertEqual(ql.get_guess(), [0., 0., 1., 0., 0.01, 0.02]) + + bounds = ql.get_bounds() + self.assertEqual(bounds[0], [-1, -1, 0., -1, 0., 1.e-6]) + self.assertEqual(bounds[1], [1, 1, np.inf, 1, 1., 1.]) + + # shared param (peak centre) + self.assertEqual(ql.N_params, 6) + for j in range(len(x)): + self.assertAlmostEqual(y[j], expect[j], 3) + + report = {} + report = ql.report(report, 1, 2, 3., 4, 5., 6) + self.assertEqual(len(report.keys()), 8) + self.assertEqual(report["N1:f1.BG gradient"], [1.]) + self.assertEqual(report["N1:f1.BG constant"], [2.]) + + self.assertEqual(report["N1:f2.f1.Amplitude"], [3.]) + self.assertEqual(report["N1:f2.f1.Centre"], [4]) + + self.assertEqual(report["N1:f2.f2.Amplitude"], [5]) + self.assertEqual(report["N1:f2.f2.Peak Centre"], [4]) + self.assertEqual(report["N1:f2.f2.Gamma"], [6]) + + self.assertEqual(report["N1:f2.f2.EISF"], [3./8.]) + + def test_bg_and_1_lorentzian(self): + + x = np.linspace(-5, 5, 5) + bg = LinearBG() + lor = Lorentzian() + y = lor(x, 1., -.2, .6) + ql = QlDataFunction(bg, False, x, y, -6, 6) + ql.add_single_lorentzian() + + y = ql(x, .02, 1, 1, 0.1, .6) + expect = [0.909, 0.985, 1.908, 1.082, 1.108] + + self.assertEqual(ql.get_guess(), [0., 0., 0.01, 0.0, 0.02]) + + bounds = ql.get_bounds() + self.assertEqual(bounds[0], [-1, -1, 0, -1, 1.e-6]) + self.assertEqual(bounds[1], [1, 1, 1, 1, 1.]) + + # shared param (peak centre) + self.assertEqual(ql.N_params, 5) + for j in range(len(x)): + self.assertAlmostEqual(y[j], expect[j], 3) + + report = {} + report = ql.report(report, 1, 2, 4, 5., 6) + self.assertEqual(len(report.keys()), 5) + self.assertEqual(report["N1:f1.BG gradient"], [1.]) + self.assertEqual(report["N1:f1.BG constant"], [2.]) + + self.assertEqual(report["N1:f2.f1.Amplitude"], [4]) + self.assertEqual(report["N1:f2.f1.Peak Centre"], [5]) + self.assertEqual(report["N1:f2.f1.Gamma"], [6]) + + def test_read_bg_and_delta_and_1_lorentzian(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + lor = Lorentzian() + y = lor(x, 1., -.2, .6) + ql = QlDataFunction(bg, True, x, y, -6, 6) + ql.add_single_lorentzian() + report = {} + report = ql.report(report, 1, 2, 3., 4, 5., 6) + params = ql.read_from_report(report, 1, 0) + self.assertEqual(params, [1., 2., 3., 4., 5., 6.]) + + def test_read_bg_and_1_lorentzian(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + lor = Lorentzian() + y = lor(x, 1., -.2, .6) + ql = QlDataFunction(bg, False, x, y, -6, 6) + ql.add_single_lorentzian() + report = {} + report = ql.report(report, 1, 2, 4, 5., 6) + params = ql.read_from_report(report, 1, 0) + self.assertEqual(params, [1., 2., 4., 5., 6.]) + + def test_bg_and_delta_and_2_lorentzians(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + lor = Lorentzian() + y = lor(x, 1., -.2, .6) + + ql = QlDataFunction(bg, True, x, y, -6, 6) + ql.add_single_lorentzian() + ql.add_single_lorentzian() + + y = ql(x, .02, 1, .2, .1, 1, .6, .7, .3) + expect = [0.916, 1.016, 2.962, 1.106, 1.115] + + # shared param (peak centre) + self.assertEqual(ql.N_params, 8) + for j in range(len(x)): + self.assertAlmostEqual(y[j], expect[j], 3) + + report = {} + report = ql.report(report, 1, 2, 3., 4, 5., 7, 8., 10) + self.assertEqual(len(report.keys()), 12) + self.assertEqual(report["N2:f1.BG gradient"], [1.]) + self.assertEqual(report["N2:f1.BG constant"], [2.]) + + self.assertEqual(report["N2:f2.f1.Amplitude"], [3.]) + self.assertEqual(report["N2:f2.f1.Centre"], [4]) + + self.assertEqual(report["N2:f2.f2.Amplitude"], [5]) + self.assertEqual(report["N2:f2.f2.Peak Centre"], [4]) + self.assertEqual(report["N2:f2.f2.Gamma"], [7]) + + self.assertEqual(report["N2:f2.f3.Amplitude"], [8]) + self.assertEqual(report["N2:f2.f3.Peak Centre"], [4]) + self.assertEqual(report["N2:f2.f3.Gamma"], [10]) + + self.assertEqual(report["N2:f2.f2.EISF"], [3./8.]) + self.assertEqual(report["N2:f2.f3.EISF"], [3./11.]) + + def test_bg_and_2_lorentzians(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + lor = Lorentzian() + y = lor(x, 1., -.2, .6) + + ql = QlDataFunction(bg, False, x, y, -6, 6) + ql.add_single_lorentzian() + ql.add_single_lorentzian() + + y = ql(x, .02, 1, .2, .1, 1, .6, .7, .3) + expect = [0.907, 0.978, 1.596, 1.076, 1.107] + + # shared param (peak centre) + self.assertEqual(ql.N_params, 7) + for j in range(len(x)): + self.assertAlmostEqual(y[j], expect[j], 3) + + report = {} + report = ql.report(report, 1, 2, 3., 4, 5., 6, 7) + self.assertEqual(len(report.keys()), 8) + self.assertEqual(report["N2:f1.BG gradient"], [1.]) + self.assertEqual(report["N2:f1.BG constant"], [2.]) + + self.assertEqual(report["N2:f2.f1.Amplitude"], [3]) + self.assertEqual(report["N2:f2.f1.Peak Centre"], [4]) + self.assertEqual(report["N2:f2.f1.Gamma"], [5]) + + self.assertEqual(report["N2:f2.f2.Amplitude"], [6]) + self.assertEqual(report["N2:f2.f2.Peak Centre"], [4]) + self.assertEqual(report["N2:f2.f2.Gamma"], [7]) + + def test_complex_read(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + lor = Lorentzian() + report = {} + y = lor(x, 1., -.2, .6) + + ql = QlDataFunction(bg, True, x, y, -6, 6) + report = ql.report(report, -1, -2, -3., -4) + + ql.add_single_lorentzian() + report = ql.report(report, 1, 2, 3., 4, 5., 6) + + ql.add_single_lorentzian() + report = ql.report(report, 11, 12, 13., 14, 15., 16, 17, 18) + + ql.add_single_lorentzian() + report = ql.report(report, 21, 22, 23., 24, 25., 26, 27, 28, 29, 30) + self.assertEqual(ql._N_peaks, 3) + + # get params for single lorentzian + params = ql.read_from_report(report, 1, 0) + self.assertEqual(params, [1., 2., 3., 4., 5., 6.]) + self.assertEqual(ql._N_peaks, 3) + + # get params for three lorentzians + params = ql.read_from_report(report, 3, 0) + self.assertEqual(params, [21., 22., 23., 24., 25., 26., 27., + 28., 29., 30.]) + self.assertEqual(ql._N_peaks, 3) + + # get params for no lorentzians + params = ql.read_from_report(report, 0, 0) + self.assertEqual(params, [-1., -2., -3., -4.]) + self.assertEqual(ql._N_peaks, 3) + + # get params for two lorentzians + params = ql.read_from_report(report, 2, 0) + self.assertEqual(params, [11., 12., 13., 14., 15., 16., 17., 18.]) + self.assertEqual(ql._N_peaks, 3) + + def test_complex_read_no_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + lor = Lorentzian() + report = {} + y = lor(x, 1., -.2, .6) + + ql = QlDataFunction(bg, False, x, y, -6, 6) + report = ql.report(report, -1, -2) + + ql.add_single_lorentzian() + report = ql.report(report, 1, 2, 4, 5., 6) + + ql.add_single_lorentzian() + report = ql.report(report, 11, 12, 14, 15., 16, 17, 18) + + ql.add_single_lorentzian() + report = ql.report(report, 21, 22, 24, 25., 26, 27, 28, 29, 30) + self.assertEqual(ql._N_peaks, 3) + + # get params for single lorentzian + params = ql.read_from_report(report, 1, 0) + self.assertEqual(params, [1., 2., 4., 5., 6.]) + self.assertEqual(ql._N_peaks, 3) + + # get params for three lorentzians + params = ql.read_from_report(report, 3, 0) + self.assertEqual(params, [21., 22., 24., 25., 26., 27., 28., 29., 30.]) + self.assertEqual(ql._N_peaks, 3) + + # get params for no lorentzians + params = ql.read_from_report(report, 0, 0) + self.assertEqual(params, [-1., -2.]) + self.assertEqual(ql._N_peaks, 3) + + # get params for two lorentzians + params = ql.read_from_report(report, 2, 0) + self.assertEqual(params, [11., 12., 14., 15., 16., 17., 18.]) + self.assertEqual(ql._N_peaks, 3) + + def test_errors(self): + """ + Since only the EISF is interesting for errors + we will only test delta + 1 lorentzian + """ + x = np.linspace(-5, 5, 5) + bg = LinearBG() + lor = Lorentzian() + y = lor(x, 1., -.2, .6) + ql = QlDataFunction(bg, True, x, y, -6, 6) + ql.add_single_lorentzian() + report = {} + params = [1, 2, 3, 4, 5, 6] + errors = [.1, .2, .3, .4, .5, .6] + + report = ql.report_errors(report, errors, params) + # EISF is the only interesting one + self.assertEqual(len(report["N1:f2.f2.EISF"]), 1) + self.assertAlmostEqual(report["N1:f2.f2.EISF"][0], 0.04, 2) + # check the others + self.assertEqual(len(report.keys()), 8) + self.assertEqual(report["N1:f1.BG gradient"], [.1]) + self.assertEqual(report["N1:f1.BG constant"], [.2]) + + self.assertEqual(report["N1:f2.f1.Amplitude"], [.3]) + self.assertEqual(report["N1:f2.f1.Centre"], [.4]) + + self.assertEqual(report["N1:f2.f2.Amplitude"], [.5]) + self.assertEqual(report["N1:f2.f2.Peak Centre"], [.4]) + self.assertEqual(report["N1:f2.f2.Gamma"], [.6]) + + def test_set_delta_guess(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QlDataFunction(bg, True, x, x + 1, -6, 6) + self.assertEqual(ql.get_guess(), [0, 0, 1., 0]) + + ql.set_delta_guess([3, 1]) + self.assertEqual(ql.get_guess(), [0, 0, 3., 1]) + + def test_set_delta_guess_fail(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QlDataFunction(bg, False, x, x + 1, -6, 6) + self.assertEqual(ql.get_guess(), [0, 0]) + + ql.set_delta_guess([3, 1]) + self.assertEqual(ql.get_guess(), [0, 0]) + + def test_set_BG_guess(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QlDataFunction(bg, True, x, x + 1, -6, 6) + self.assertEqual(ql.get_guess(), [0, 0, 1., 0]) + + ql.set_BG_guess([3, 1]) + self.assertEqual(ql.get_guess(), [3, 1, 1., 0]) + + def test_set_func_no_peak(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QlDataFunction(bg, True, x, x + 1, -6, 6) + self.assertEqual(ql.get_guess(), [0, 0, 1., 0]) + + ql.set_func_guess([3, 2, 1]) + self.assertEqual(ql.get_guess(), [0, 0, 1., 0]) + + def test_set_func_one_peak_no_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QlDataFunction(bg, False, x, x + 1, -6, 6) + ql.add_single_lorentzian() + self.assertEqual(ql.get_guess(), [0, 0, 0.01, 0, 0.02]) + + ql.set_func_guess([3, 2, 1]) + self.assertEqual(ql.get_guess(), [0, 0, 3., 2, 1]) + + def test_set_func_one_peak_and_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QlDataFunction(bg, True, x, x + 1, -6, 6) + ql.add_single_lorentzian() + self.assertEqual(ql.get_guess(), [0, 0, 1, 0, 0.01, 0.02]) + + ql.set_func_guess([3, 2, 1]) + self.assertEqual(ql.get_guess(), [0, 0, 1, 2, 3., 1]) + + def test_set_func_two_peak_no_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QlDataFunction(bg, False, x, x + 1, -6, 6) + ql.add_single_lorentzian() + ql.add_single_lorentzian() + self.assertEqual(ql.get_guess(), [0, 0, 0.01, 0, 0.02, 0.01, 0.02]) + + ql.set_func_guess([3, 2, 1]) + self.assertEqual(ql.get_guess(), [0, 0, 0.01, 2, 0.02, 3, 1]) + + ql.set_func_guess([4, 5, -1], 0) + self.assertEqual(ql.get_guess(), [0, 0, 4, 5, -1, 3, 1]) + + def test_set_func_two_peak_and_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QlDataFunction(bg, True, x, x + 1, -6, 6) + ql.add_single_lorentzian() + ql.add_single_lorentzian() + self.assertEqual(ql.get_guess(), [0, 0, 1, 0, 0.01, 0.02, 0.01, 0.02]) + + ql.set_func_guess([3, 2, 1]) + self.assertEqual(ql.get_guess(), [0, 0, 1, 2, 0.01, 0.02, 3, 1]) + + ql.set_func_guess([4, 5, -1], 0) + self.assertEqual(ql.get_guess(), [0, 0, 1, 5, 4, -1, 3, 1]) + + def test_get_func_guess(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QlDataFunction(bg, True, x, x + 1, -6, 6) + ql.add_single_lorentzian() + ql.add_single_lorentzian() + self.assertEqual(ql.get_guess(), [0, 0, 1, 0, 0.01, 0.02, 0.01, 0.02]) + + ql.set_func_guess([3, 2, 1]) + self.assertEqual(ql.get_guess(), [0, 0, 1, 2, 0.01, 0.02, 3, 1]) + + ql.set_func_guess([4, 5, -1], 0) + self.assertEqual(ql.get_guess(), [0, 0, 1, 5, 4, -1, 3, 1]) + + self.assertEqual(ql.get_func_guess(), [3, 5, 1]) + self.assertEqual(ql.get_func_guess(0), [4, 5, -1]) + + def test_set_delta_bounds(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QlDataFunction(bg, True, x, x + 1, -6, 6) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -1]) + self.assertEqual(upper, [1, 1, np.inf, 1]) + + ql.set_delta_bounds([-3, -2], [2, 4]) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, -3, -2]) + self.assertEqual(upper, [1, 1, 2, 4]) + + def test_set_delta_bounds_fail(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QlDataFunction(bg, False, x, x + 1, -6, 6) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1]) + self.assertEqual(upper, [1, 1]) + + ql.set_delta_bounds([-3, -2], [2, 4]) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1]) + self.assertEqual(upper, [1, 1]) + + def test_set_BG_bounds(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QlDataFunction(bg, True, x, x + 1, -6, 6) + + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -1]) + self.assertEqual(upper, [1, 1, np.inf, 1]) + + ql.set_BG_bounds([-3, -2], [2, 4]) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-3, -2, 0, -1]) + self.assertEqual(upper, [2, 4, np.inf, 1]) + + def test_set_func_bounds_no_peak(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QlDataFunction(bg, True, x, x + 1, -6, 6) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -1]) + self.assertEqual(upper, [1, 1, np.inf, 1]) + + ql.set_func_bounds([-3, -2, 1], [1, 2, 4]) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -1]) + self.assertEqual(upper, [1, 1, np.inf, 1]) + + def test_set_func_bounds_one_peak_no_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QlDataFunction(bg, False, x, x + 1, -6, 6) + ql.add_single_lorentzian() + + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -1, 1e-6]) + self.assertEqual(upper, [1, 1, 1, 1, 1]) + + ql.set_func_bounds([-3, -2, 1], [3, 2, 4]) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, -3, -2, 1]) + self.assertEqual(upper, [1, 1, 3, 2, 4]) + + def test_set_func_bounds_one_peak_and_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QlDataFunction(bg, True, x, x + 1, -6, 6) + ql.add_single_lorentzian() + + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -1, 0, 1e-6]) + self.assertEqual(upper, [1, 1, np.inf, 1, 1, 1]) + + ql.set_func_bounds([-3, -2, 1], [3, 2, 4]) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -2, -3, 1]) + self.assertEqual(upper, [1, 1, np.inf, 2, 3, 4]) + + def test_set_func_bounds_two_peak_no_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QlDataFunction(bg, False, x, x + 1, -6, 6) + ql.add_single_lorentzian() + ql.add_single_lorentzian() + + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -1, 1e-6, 0, 1e-6]) + self.assertEqual(upper, [1, 1, 1, 1, 1, 1, 1]) + + ql.set_func_bounds([-3, -2, 1], [3, 2, 4]) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -2, 1e-6, -3, 1]) + self.assertEqual(upper, [1, 1, 1, 2, 1, 3, 4]) + + def test_set_func_bounds_two_peak_and_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QlDataFunction(bg, True, x, x + 1, -6, 6) + ql.add_single_lorentzian() + ql.add_single_lorentzian() + + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -1, 0, 1e-6, 0, 1e-6]) + self.assertEqual(upper, [1, 1, np.inf, 1, 1, 1, 1, 1]) + + ql.set_func_bounds([-3, -2, 1], [3, 2, 4]) + lower, upper = ql.get_bounds() + + self.assertEqual(lower, [-1, -1, 0, -2, 0, 1e-6, -3, 1]) + self.assertEqual(upper, [1, 1, np.inf, 2, 1, 1, 3, 4]) + + ql.set_func_bounds([-5, -6, -7], [5, 6, 7], 0) + lower, upper = ql.get_bounds() + + self.assertEqual(lower, [-1, -1, 0, -6, -5, -7, -3, 1]) + self.assertEqual(upper, [1, 1, np.inf, 6, 5, 7, 3, 4]) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/shared/functions/qseFixedFunction_test.py b/test/shared/functions/qseFixedFunction_test.py new file mode 100644 index 00000000..33ca0699 --- /dev/null +++ b/test/shared/functions/qseFixedFunction_test.py @@ -0,0 +1,502 @@ +import unittest +import numpy as np +from quickBayes.functions.BG import LinearBG +from quickBayes.functions.SE import StretchExp +from quickBayes.functions.qse_fixed import QSEFixFunction + + +class QSEFixedFunctionTest(unittest.TestCase): + + def test_just_bg(self): + x = np.linspace(0, 5, 6) + bg = LinearBG() + qse = QSEFixFunction(bg, False, x, x, 0, 6) + y = qse(x, 1.2, 3) + expect = 1.2*x + 3 + + self.assertEqual(qse.N_params, 2) + for j in range(len(x)): + self.assertAlmostEqual(y[j], expect[j]) + + self.assertEqual(qse.get_guess(), [0., 0.]) + + bounds = qse.get_bounds() + self.assertEqual(bounds[0], [-1, -1]) + self.assertEqual(bounds[1], [1, 1]) + + report = {} + report = qse.report(report, 1, 2) + self.assertEqual(report["N0:f1.BG gradient"], [1.]) + self.assertEqual(report["N0:f1.BG constant"], [2.]) + + def test_read_just_bg(self): + x = np.linspace(0, 5, 6) + bg = LinearBG() + qse = QSEFixFunction(bg, False, x, x, 0, 6) + report = {} + report = qse.report(report, 1., 2.3) + params = qse.read_from_report(report, 0) + self.assertEqual(params, [1., 2.3]) + + def test_bg_and_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + + se = StretchExp() + y = se(x, 1., 0.01, 11, .7) + + qse = QSEFixFunction(bg, True, x, y, -6, 6) + y = qse(x, 1.2, 3, .2, .1) + expect = [-3, 0.0001, 3.080, 6.000, 9.000] + + self.assertEqual(qse.get_guess(), [0., 0., 1., 0.]) + + bounds = qse.get_bounds() + self.assertEqual(bounds[0], [-1, -1, 0., -1]) + self.assertEqual(bounds[1], [1, 1, np.inf, 1]) + + self.assertEqual(qse.N_params, 4) + for j in range(len(x)): + self.assertAlmostEqual(y[j], expect[j], 3) + + report = {} + report = qse.report(report, 1, 2, 3, 4) + self.assertEqual(len(report.keys()), 4) + self.assertEqual(report["N0:f1.BG gradient"], [1.]) + self.assertEqual(report["N0:f1.BG constant"], [2.]) + + self.assertEqual(report["N0:f2.f1.Amplitude"], [3.]) + self.assertEqual(report["N0:f2.f1.Centre"], [4]) + + def test_read_bg_and_delta(self): + x = np.linspace(0, 5, 6) + bg = LinearBG() + qse = QSEFixFunction(bg, True, x, x, 0, 6) + report = {} + report = qse.report(report, 1., 2.3, .5, -.1) + params = qse.read_from_report(report, 0) + self.assertEqual(params, [1., 2.3, .5, -.1]) + + def test_bg_and_delta_and_1_SE(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + + se = StretchExp() + y = se(x, 1., 0.01, 11, .7) + + qse = QSEFixFunction(bg, True, x, y, -6, 6) + qse.add_single_SE() + + y = qse(x, .02, 1, .2, .1, 1) + expect = [0.904, 0.961, 2.445, 1.062, 1.104] + + bounds = qse.get_bounds() + self.assertEqual(bounds[0], [-1, -1, 0., -1, 0.]) + self.assertEqual(bounds[1], [1, 1, np.inf, 1, 1]) + + # shared param (peak centre) + self.assertEqual(qse.N_params, 5) + for j in range(len(x)): + self.assertAlmostEqual(y[j], expect[j], 3) + + guess = qse.get_guess() + expect = [0., 0., 1., 0., 0.1] + self.assertEqual(len(guess), len(expect)) + for k in range(len(expect)): + self.assertAlmostEqual(guess[k], expect[k], 3) + + report = {} + report = qse.report(report, 1, 2, 3., 4, 5.) + self.assertEqual(len(report.keys()), 9) + self.assertEqual(report["N1:f1.BG gradient"], [1.]) + self.assertEqual(report["N1:f1.BG constant"], [2.]) + + self.assertEqual(report["N1:f2.f1.Amplitude"], [3.]) + self.assertEqual(report["N1:f2.f1.Centre"], [4]) + + self.assertEqual(report["N1:f2.f2.Amplitude"], [5]) + self.assertEqual(report["N1:f2.f2.Peak Centre"], [4]) + self.assertAlmostEqual(report["N1:f2.f2.tau"][0], 6.582, 3) + self.assertEqual(report["N1:f2.f2.FWHM"], [0.2]) + self.assertEqual(report["N1:f2.f2.beta"], [0.8]) + + def test_read_bg_and_delta_and_1se(self): + x = np.linspace(0, 5, 6) + bg = LinearBG() + qse = QSEFixFunction(bg, True, x, x, 0, 6) + qse.add_single_SE() + report = {} + report = qse.report(report, 1., 2.3, .5, -.1, .1) + params = qse.read_from_report(report, 1) + self.assertEqual(params, [1., 2.3, .5, -.1, .1]) + + def test_bg_and_1_SE(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + + se = StretchExp() + y = se(x, 1., 0.01, 11, .7) + + qse = QSEFixFunction(bg, False, x, y, -6, 6) + qse.add_single_SE() + + y = qse(x, .02, 1, 1, .1) + expect = [0.904, 0.961, 2.365, 1.062, 1.104] + + bounds = qse.get_bounds() + self.assertEqual(bounds[0], [-1, -1, 0., -1]) + self.assertEqual(bounds[1], [1, 1, 1, 1]) + + # shared param (peak centre) + self.assertEqual(qse.N_params, 4) + for j in range(len(x)): + self.assertAlmostEqual(y[j], expect[j], 3) + + expect = [0., 0., 0.1, 0.] + guess = qse.get_guess() + self.assertEqual(len(guess), len(expect)) + for k in range(len(expect)): + self.assertAlmostEqual(guess[k], expect[k], 3) + + report = {} + report = qse.report(report, 1, 2, 3., 4) + self.assertEqual(len(report.keys()), 7) + self.assertEqual(report["N1:f1.BG gradient"], [1.]) + self.assertEqual(report["N1:f1.BG constant"], [2.]) + + self.assertEqual(report["N1:f2.f1.Amplitude"], [3]) + self.assertEqual(report["N1:f2.f1.Peak Centre"], [4]) + self.assertAlmostEqual(report["N1:f2.f1.tau"][0], 6.582, 3) + self.assertEqual(report["N1:f2.f1.FWHM"][0], 0.2) + self.assertEqual(report["N1:f2.f1.beta"], [0.8]) + + def test_read_bg_and_1se(self): + x = np.linspace(0, 5, 6) + bg = LinearBG() + qse = QSEFixFunction(bg, False, x, x, 0, 6) + qse.add_single_SE() + report = {} + report = qse.report(report, 1., 2.3, .1, -.1) + params = qse.read_from_report(report, 1) + self.assertEqual(params, [1., 2.3, .1, -.1]) + + def assertList(self, values, expected): + self.assertEqual(len(values), len(expected)) + for k in range(len(values)): + self.assertAlmostEqual(values[k], values[k], 3) + + def test_set_delta_guess(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFixFunction(bg, True, x, x + 1, -6, 6) + self.assertEqual(ql.get_guess(), [0, 0, 1., 0]) + + ql.set_delta_guess([3, 1]) + self.assertEqual(ql.get_guess(), [0, 0, 3., 1]) + + def test_set_delta_guess_fail(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFixFunction(bg, False, x, x + 1, -6, 6) + self.assertEqual(ql.get_guess(), [0, 0]) + + ql.set_delta_guess([3, 1]) + self.assertEqual(ql.get_guess(), [0, 0]) + + def test_set_BG_guess(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFixFunction(bg, True, x, x + 1, -6, 6) + self.assertEqual(ql.get_guess(), [0, 0, 1., 0]) + + ql.set_BG_guess([3, 1]) + self.assertEqual(ql.get_guess(), [3, 1, 1., 0]) + + def test_set_func_no_peak(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFixFunction(bg, True, x, x + 1, -6, 6) + self.assertEqual(ql.get_guess(), [0, 0, 1., 0]) + + ql.set_func_guess([3, 2, 1, 4]) + self.assertEqual(ql.get_guess(), [0, 0, 1., 0]) + + def test_set_func_one_peak_no_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFixFunction(bg, False, x, x + 1, -6, 6) + ql.add_single_SE() + self.assertList(ql.get_guess(), [0, 0, 0.1, 0]) + + ql.set_func_guess([3, 2]) + self.assertList(ql.get_guess(), [0, 0, 3, 2]) + + def test_set_func_one_peak_and_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFixFunction(bg, True, x, x + 1, -6, 6) + ql.add_single_SE() + self.assertList(ql.get_guess(), [0, 0, 1, 0, 0.1]) + + ql.set_func_guess([3, 2]) + self.assertList(ql.get_guess(), [0, 0, 1, 2, 3]) + + def test_set_func_two_peak_no_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFixFunction(bg, False, x, x + 1, -6, 6) + ql.add_single_SE() + ql.add_single_SE() + self.assertList(ql.get_guess(), [0, 0, 0.1, 0, 0.1]) + + ql.set_func_guess([3, 2]) + self.assertList(ql.get_guess(), [0, 0, 0.1, 2, 3]) + + ql.set_func_guess([6, 5], 0) + self.assertList(ql.get_guess(), [0, 0, 6, 5, 3]) + + def test_set_func_two_peak_and_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFixFunction(bg, True, x, x + 1, -6, 6) + ql.add_single_SE() + ql.add_single_SE() + self.assertList(ql.get_guess(), [0, 0, 1., 0, 0.1, 0.1]) + + ql.set_func_guess([3, 2]) + self.assertList(ql.get_guess(), [0, 0, 1., 2, 0.1, 3]) + + ql.set_func_guess([4, 5], 0) + self.assertList(ql.get_guess(), [0, 0, 1., 5, 4, 3]) + + def test_get_func_guess(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFixFunction(bg, True, x, x + 1, -6, 6) + ql.add_single_SE() + ql.add_single_SE() + self.assertList(ql.get_guess(), [0, 0, 1., 0, 0.1, 0.1]) + + ql.set_func_guess([3, 2]) + self.assertList(ql.get_guess(), [0, 0, 1., 2, 0.1, 3]) + + ql.set_func_guess([4, 5], 0) + self.assertList(ql.get_guess(), [0, 0, 1., 5, 4, 3]) + self.assertList(ql.get_guess(), [0, 0, 1., 5, 4, 3]) + + self.assertEqual(ql.get_func_guess(), [3, 5]) + self.assertEqual(ql.get_func_guess(0), [4, 5]) + + def test_set_delta_bounds(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFixFunction(bg, True, x, x + 1, -6, 6) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -1]) + self.assertEqual(upper, [1, 1, np.inf, 1]) + + ql.set_delta_bounds([-3, -2], [2, 4]) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, -3, -2]) + self.assertEqual(upper, [1, 1, 2, 4]) + + def test_set_delta_bounds_fail(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFixFunction(bg, False, x, x + 1, -6, 6) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1]) + self.assertEqual(upper, [1, 1]) + + ql.set_delta_bounds([-3, -2], [2, 4]) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1]) + self.assertEqual(upper, [1, 1]) + + def test_set_BG_bounds(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFixFunction(bg, True, x, x + 1, -6, 6) + + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -1]) + self.assertEqual(upper, [1, 1, np.inf, 1]) + + ql.set_BG_bounds([-3, -2], [2, 4]) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-3, -2, 0, -1]) + self.assertEqual(upper, [2, 4, np.inf, 1]) + + def test_set_func_bounds_no_peak(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFixFunction(bg, True, x, x + 1, -6, 6) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -1]) + self.assertEqual(upper, [1, 1, np.inf, 1]) + + ql.set_func_bounds([-3, -2, 1], [1, 2, 4]) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -1]) + self.assertEqual(upper, [1, 1, np.inf, 1]) + + def test_set_func_bounds_one_peak_no_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFixFunction(bg, False, x, x + 1, -6, 6) + ql.add_single_SE() + + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -1]) + self.assertEqual(upper, [1, 1, 1, 1]) + + ql.set_func_bounds([-3, -2], [3, 2]) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, -3, -2]) + self.assertEqual(upper, [1, 1, 3, 2]) + + def test_set_func_bounds_one_peak_and_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFixFunction(bg, True, x, x + 1, -6, 6) + ql.add_single_SE() + + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -1, 0]) + self.assertEqual(upper, [1, 1, np.inf, 1, 1]) + + ql.set_func_bounds([-3, -2], [3, 2]) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -2, -3]) + self.assertEqual(upper, [1, 1, np.inf, 2, 3]) + + def test_set_func_bounds_two_peak_no_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFixFunction(bg, False, x, x + 1, -6, 6) + ql.add_single_SE() + ql.add_single_SE() + + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -1, 0]) + self.assertEqual(upper, [1, 1, 1, 1, 1]) + + ql.set_func_bounds([-3, -2], [3, 2]) + lower, upper = ql.get_bounds() + + self.assertEqual(lower, [-1, -1, 0, -2, -3]) + self.assertEqual(upper, [1, 1, 1, 2, 3]) + + def test_set_func_bounds_two_peak_and_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFixFunction(bg, True, x, x + 1, -6, 6) + ql.add_single_SE() + ql.add_single_SE() + + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -1, 0, 0]) + self.assertEqual(upper, [1, 1, np.inf, 1, 1, 1]) + + ql.set_func_bounds([-3, -2], [3, 2]) + lower, upper = ql.get_bounds() + + self.assertEqual(lower, [-1, -1, 0, -2, 0, -3]) + self.assertEqual(upper, [1, 1, np.inf, 2, 1, 3]) + + ql.set_func_bounds([-5, -6], [5, 6], 0) + lower, upper = ql.get_bounds() + + self.assertEqual(lower, [-1, -1, 0, -6, -5, -3]) + self.assertEqual(upper, [1, 1, np.inf, 6, 5, 3]) + + @staticmethod + def get_se(func, index): + return func.conv._funcs[index] + + def test_beta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFixFunction(bg, True, x, x + 1, -6, 6) + ql.add_single_SE() + + self.assertEqual(self.get_se(ql, 1)._beta, 0.8) + + ql.set_beta(0.9) + + self.assertEqual(self.get_se(ql, 1)._beta, 0.9) + + report = {} + report = ql.report(report, 1, 2, 3., 4, 5.) + self.assertEqual(report["N1:f2.f2.beta"], [0.9]) + + ql.set_beta(1.0) + self.assertEqual(self.get_se(ql, 1)._beta, 1.0) + + _ = ql.read_from_report(report, 1) + + self.assertEqual(self.get_se(ql, 1)._beta, 0.9) + + def test_2_betas(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFixFunction(bg, True, x, x + 1, -6, 6) + ql.add_single_SE() + ql.add_single_SE() + + self.assertEqual(self.get_se(ql, 1).get_beta, 0.8) + self.assertEqual(self.get_se(ql, 2).get_beta, 0.8) + + ql.set_beta(0.9) + self.assertEqual(self.get_se(ql, 1).get_beta, 0.8) + self.assertEqual(self.get_se(ql, 2).get_beta, 0.9) + + ql.set_beta(1.0, 0) + self.assertEqual(self.get_se(ql, 1).get_beta, 1.0) + self.assertEqual(self.get_se(ql, 2).get_beta, 0.9) + + def test_tau(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFixFunction(bg, True, x, x + 1, -6, 6) + ql.add_single_SE() + + self.assertAlmostEqual(self.get_se(ql, 1).get_tau, 6.582, 3) + + ql.set_FWHM(0.4) + + self.assertAlmostEqual(self.get_se(ql, 1).get_tau, 3.291, 3) + + report = {} + report = ql.report(report, 1, 2, 3., 4, 5.) + self.assertEqual(report["N1:f2.f2.FWHM"], [0.4]) + + ql.set_FWHM(.1) + self.assertAlmostEqual(self.get_se(ql, 1).get_tau, 13.164, 3) + + _ = ql.read_from_report(report, 1) + + self.assertAlmostEqual(self.get_se(ql, 1).get_tau, 3.291, 3) + + def test_2_taus(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFixFunction(bg, True, x, x + 1, -6, 6) + ql.add_single_SE() + ql.add_single_SE() + + self.assertAlmostEqual(self.get_se(ql, 1).get_tau, 6.582, 3) + self.assertAlmostEqual(self.get_se(ql, 2).get_tau, 6.582, 3) + + ql.set_FWHM(0.4) + + self.assertAlmostEqual(self.get_se(ql, 1).get_tau, 6.582, 3) + self.assertAlmostEqual(self.get_se(ql, 2).get_tau, 3.291, 3) + + ql.set_FWHM(.1, 0) + self.assertAlmostEqual(self.get_se(ql, 1).get_tau, 13.164, 3) + self.assertAlmostEqual(self.get_se(ql, 2).get_tau, 3.291, 3) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/shared/functions/qseFunction_test.py b/test/shared/functions/qseFunction_test.py new file mode 100644 index 00000000..f2ea37bd --- /dev/null +++ b/test/shared/functions/qseFunction_test.py @@ -0,0 +1,453 @@ +import unittest +import numpy as np +from quickBayes.functions.BG import LinearBG +from quickBayes.functions.SE import StretchExp +from quickBayes.functions.qse_function import QSEFunction + + +class QSEFunctionTest(unittest.TestCase): + + def test_just_bg(self): + x = np.linspace(0, 5, 6) + bg = LinearBG() + qse = QSEFunction(bg, False, x, x, 0, 6) + y = qse(x, 1.2, 3) + expect = 1.2*x + 3 + + self.assertEqual(qse.N_params, 2) + for j in range(len(x)): + self.assertAlmostEqual(y[j], expect[j]) + + self.assertEqual(qse.get_guess(), [0., 0.]) + + bounds = qse.get_bounds() + self.assertEqual(bounds[0], [-1, -1]) + self.assertEqual(bounds[1], [1, 1]) + + report = {} + report = qse.report(report, 1, 2) + self.assertEqual(report["N0:f1.BG gradient"], [1.]) + self.assertEqual(report["N0:f1.BG constant"], [2.]) + + def test_read_just_bg(self): + x = np.linspace(0, 5, 6) + bg = LinearBG() + qse = QSEFunction(bg, False, x, x, 0, 6) + report = {} + report = qse.report(report, 1., 2.3) + params = qse.read_from_report(report, 0) + self.assertEqual(params, [1., 2.3]) + + def test_bg_and_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + + se = StretchExp() + y = se(x, 1., 0.01, 11, .7) + + qse = QSEFunction(bg, True, x, y, -6, 6) + y = qse(x, 1.2, 3, .2, .1) + expect = [-3, 0.0001, 3.080, 6.000, 9.000] + + self.assertEqual(qse.get_guess(), [0., 0., 1., 0.]) + + bounds = qse.get_bounds() + self.assertEqual(bounds[0], [-1, -1, 0., -1]) + self.assertEqual(bounds[1], [1, 1, np.inf, 1]) + + self.assertEqual(qse.N_params, 4) + for j in range(len(x)): + self.assertAlmostEqual(y[j], expect[j], 3) + + report = {} + report = qse.report(report, 1, 2, 3, 4) + self.assertEqual(len(report.keys()), 4) + self.assertEqual(report["N0:f1.BG gradient"], [1.]) + self.assertEqual(report["N0:f1.BG constant"], [2.]) + + self.assertEqual(report["N0:f2.f1.Amplitude"], [3.]) + self.assertEqual(report["N0:f2.f1.Centre"], [4]) + + def test_read_bg_and_delta(self): + x = np.linspace(0, 5, 6) + bg = LinearBG() + qse = QSEFunction(bg, True, x, x, 0, 6) + report = {} + report = qse.report(report, 1., 2.3, .5, -.1) + params = qse.read_from_report(report, 0) + self.assertEqual(params, [1., 2.3, .5, -.1]) + + def test_bg_and_delta_and_1_SE(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + + se = StretchExp() + y = se(x, 1., 0.01, 11, .7) + + qse = QSEFunction(bg, True, x, y, -6, 6) + qse.add_single_SE() + + y = qse(x, .02, 1, .2, .1, 1, 10., 0.5) + expect = [0.911, 0.972, 2.345, 1.074, 1.112] + + bounds = qse.get_bounds() + self.assertEqual(bounds[0], [-1, -1, 0., -1, 0., 0, 0]) + self.assertEqual(bounds[1], [1, 1, np.inf, 1, 1, 100, 1]) + + # shared param (peak centre) + self.assertEqual(qse.N_params, 7) + for j in range(len(x)): + self.assertAlmostEqual(y[j], expect[j], 3) + + guess = qse.get_guess() + expect = [0., 0., 1., 0., 0.1, 6.582, 0.7] + self.assertEqual(len(guess), len(expect)) + for k in range(len(expect)): + self.assertAlmostEqual(guess[k], expect[k], 3) + + report = {} + report = qse.report(report, 1, 2, 3., 4, 5., 6, 7) + self.assertEqual(len(report.keys()), 9) + self.assertEqual(report["N1:f1.BG gradient"], [1.]) + self.assertEqual(report["N1:f1.BG constant"], [2.]) + + self.assertEqual(report["N1:f2.f1.Amplitude"], [3.]) + self.assertEqual(report["N1:f2.f1.Centre"], [4]) + + self.assertEqual(report["N1:f2.f2.Amplitude"], [5]) + self.assertEqual(report["N1:f2.f2.Peak Centre"], [4]) + self.assertEqual(report["N1:f2.f2.tau"], [6]) + self.assertAlmostEqual(report["N1:f2.f2.FWHM"][0], 0.219, 3) + self.assertEqual(report["N1:f2.f2.beta"], [7]) + + def test_report_errors(self): + """ + stretch exp calculates the error for FWHM, + test that this is used when its part of + a quasielastic function. + """ + + x = np.linspace(-5, 5, 5) + bg = LinearBG() + + se = StretchExp() + y = se(x, 1., 0.01, 11, .7) + + qse = QSEFunction(bg, True, x, y, -6, 6) + qse.add_single_SE() + + y = qse(x, .02, 1, .2, .1, 1, 10., 0.5) + + params = [0., 0., 0.911, 0.972, 2.345, 2.10e1, 7.73e-1] + sigma = [0., 0., 1.2e-2, 3.e-4, 5.e-5, 5.e-2, 1.7e-3] + + errors = qse.report_errors({}, sigma, params) + self.assertAlmostEqual(errors['N1:f2.f2.FWHM'][0], 0.00015, 5) + + def test_read_bg_and_delta_and_1se(self): + x = np.linspace(0, 5, 6) + bg = LinearBG() + qse = QSEFunction(bg, True, x, x, 0, 6) + qse.add_single_SE() + report = {} + report = qse.report(report, 1., 2.3, .5, -.1, .1, 10, .7) + params = qse.read_from_report(report, 1) + self.assertEqual(params, [1., 2.3, .5, -.1, .1, 10, .7]) + + def test_bg_and_1_SE(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + + se = StretchExp() + y = se(x, 1., 0.01, 11, .7) + + qse = QSEFunction(bg, False, x, y, -6, 6) + qse.add_single_SE() + + y = qse(x, .02, 1, 1, .1, 10., 0.5) + expect = [0.911, 0.972, 2.265, 1.074, 1.112] + + bounds = qse.get_bounds() + self.assertEqual(bounds[0], [-1, -1, 0., -1, 0, 0]) + self.assertEqual(bounds[1], [1, 1, 1, 1, 100, 1]) + + # shared param (peak centre) + self.assertEqual(qse.N_params, 6) + for j in range(len(x)): + self.assertAlmostEqual(y[j], expect[j], 3) + + expect = [0., 0., 0.1, 0., 6.582, 0.7] + guess = qse.get_guess() + self.assertEqual(len(guess), len(expect)) + for k in range(len(expect)): + self.assertAlmostEqual(guess[k], expect[k], 3) + + report = {} + report = qse.report(report, 1, 2, 3., 4, 5., 6) + self.assertEqual(len(report.keys()), 7) + self.assertEqual(report["N1:f1.BG gradient"], [1.]) + self.assertEqual(report["N1:f1.BG constant"], [2.]) + + self.assertEqual(report["N1:f2.f1.Amplitude"], [3]) + self.assertEqual(report["N1:f2.f1.Peak Centre"], [4]) + self.assertEqual(report["N1:f2.f1.tau"], [5]) + self.assertAlmostEqual(report["N1:f2.f1.FWHM"][0], 0.263, 3) + self.assertEqual(report["N1:f2.f1.beta"], [6]) + + def test_read_bg_and_1se(self): + x = np.linspace(0, 5, 6) + bg = LinearBG() + qse = QSEFunction(bg, False, x, x, 0, 6) + qse.add_single_SE() + report = {} + report = qse.report(report, 1., 2.3, .1, -.1, 10, .7) + params = qse.read_from_report(report, 1) + self.assertEqual(params, [1., 2.3, .1, -.1, 10, .7]) + + def assertList(self, values, expected): + self.assertEqual(len(values), len(expected)) + for k in range(len(values)): + self.assertAlmostEqual(values[k], values[k], 3) + + def test_set_delta_guess(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFunction(bg, True, x, x + 1, -6, 6) + self.assertEqual(ql.get_guess(), [0, 0, 1., 0]) + + ql.set_delta_guess([3, 1]) + self.assertEqual(ql.get_guess(), [0, 0, 3., 1]) + + def test_set_delta_guess_fail(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFunction(bg, False, x, x + 1, -6, 6) + self.assertEqual(ql.get_guess(), [0, 0]) + + ql.set_delta_guess([3, 1]) + self.assertEqual(ql.get_guess(), [0, 0]) + + def test_set_BG_guess(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFunction(bg, True, x, x + 1, -6, 6) + self.assertEqual(ql.get_guess(), [0, 0, 1., 0]) + + ql.set_BG_guess([3, 1]) + self.assertEqual(ql.get_guess(), [3, 1, 1., 0]) + + def test_set_func_no_peak(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFunction(bg, True, x, x + 1, -6, 6) + self.assertEqual(ql.get_guess(), [0, 0, 1., 0]) + + ql.set_func_guess([3, 2, 1, 4]) + self.assertEqual(ql.get_guess(), [0, 0, 1., 0]) + + def test_set_func_one_peak_no_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFunction(bg, False, x, x + 1, -6, 6) + ql.add_single_SE() + self.assertList(ql.get_guess(), [0, 0, 0.1, 0, 6.582, 0.7]) + + ql.set_func_guess([3, 2, 1, 4]) + self.assertList(ql.get_guess(), [0, 0, 3, 2, 1, 4]) + + def test_set_func_one_peak_and_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFunction(bg, True, x, x + 1, -6, 6) + ql.add_single_SE() + self.assertList(ql.get_guess(), [0, 0, 1, 0, 0.1, 6.582, 0.7]) + + ql.set_func_guess([3, 2, 1, 4]) + self.assertList(ql.get_guess(), [0, 0, 1, 2, 3, 1, 4]) + + def test_set_func_two_peak_no_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFunction(bg, False, x, x + 1, -6, 6) + ql.add_single_SE() + ql.add_single_SE() + self.assertList(ql.get_guess(), [0, 0, 0.1, 0, 6.582, 0.7, + 0.1, 6.582, 0.7]) + + ql.set_func_guess([3, 2, 1, 4]) + self.assertList(ql.get_guess(), [0, 0, 0.1, 2, 6.582, 0.7, 3, 1, 4]) + + ql.set_func_guess([6, 5, -1, -2], 0) + self.assertList(ql.get_guess(), [0, 0, 6, 5, -1, -2, 3, 1, 4]) + + def test_set_func_two_peak_and_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFunction(bg, True, x, x + 1, -6, 6) + ql.add_single_SE() + ql.add_single_SE() + self.assertList(ql.get_guess(), [0, 0, 1., 0, 0.1, + 6.582, 0.7, 0.1, 6.582, 0.7]) + + ql.set_func_guess([3, 2, 1, 4]) + self.assertList(ql.get_guess(), [0, 0, 1., 2, 0.1, + 6.582, 0.7, 3, 1, 4]) + + ql.set_func_guess([4, 5, -1, -2], 0) + self.assertList(ql.get_guess(), [0, 0, 1., 5, 4, -1, -2, 3, 1, 4]) + + def test_set_func_guess_FWHM(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFunction(bg, True, x, x + 1, -6, 6) + ql.add_single_SE() + self.assertList(ql.get_guess(), [0, 0, 1, 0, 0.1, 6.582, 0.7]) + ql.set_func_guess_FWHM([3, 2, 0.4, 4]) + self.assertList(ql.get_guess(), [0, 0, 1, 2, 3, 3.291, 4]) + + def test_get_func_guess(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFunction(bg, True, x, x + 1, -6, 6) + ql.add_single_SE() + ql.add_single_SE() + self.assertList(ql.get_guess(), [0, 0, 1., 0, 0.1, + 6.582, 0.7, 0.1, 6.582, 0.7]) + + ql.set_func_guess([3, 2, 1, 4]) + self.assertList(ql.get_guess(), [0, 0, 1., 2, 0.1, + 6.582, 0.7, 3, 1, 4]) + + ql.set_func_guess([4, 5, -1, -2], 0) + self.assertList(ql.get_guess(), [0, 0, 1., 5, 4, -1, -2, 3, 1, 4]) + self.assertList(ql.get_guess(), [0, 0, 1., 5, 4, -1, -2, 3, 1, 4]) + + self.assertEqual(ql.get_func_guess(), [3, 5, 1, 4]) + self.assertEqual(ql.get_func_guess(0), [4, 5, -1, -2]) + + def test_set_delta_bounds(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFunction(bg, True, x, x + 1, -6, 6) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -1]) + self.assertEqual(upper, [1, 1, np.inf, 1]) + + ql.set_delta_bounds([-3, -2], [2, 4]) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, -3, -2]) + self.assertEqual(upper, [1, 1, 2, 4]) + + def test_set_delta_bounds_fail(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFunction(bg, False, x, x + 1, -6, 6) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1]) + self.assertEqual(upper, [1, 1]) + + ql.set_delta_bounds([-3, -2], [2, 4]) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1]) + self.assertEqual(upper, [1, 1]) + + def test_set_BG_bounds(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFunction(bg, True, x, x + 1, -6, 6) + + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -1]) + self.assertEqual(upper, [1, 1, np.inf, 1]) + + ql.set_BG_bounds([-3, -2], [2, 4]) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-3, -2, 0, -1]) + self.assertEqual(upper, [2, 4, np.inf, 1]) + + def test_set_func_bounds_no_peak(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFunction(bg, True, x, x + 1, -6, 6) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -1]) + self.assertEqual(upper, [1, 1, np.inf, 1]) + + ql.set_func_bounds([-3, -2, 1], [1, 2, 4]) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -1]) + self.assertEqual(upper, [1, 1, np.inf, 1]) + + def test_set_func_bounds_one_peak_no_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFunction(bg, False, x, x + 1, -6, 6) + ql.add_single_SE() + + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -1, 0, 0]) + self.assertEqual(upper, [1, 1, 1, 1, 100, 1]) + + ql.set_func_bounds([-3, -2, 1, -4], [3, 2, 4, 5]) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, -3, -2, 1, -4]) + self.assertEqual(upper, [1, 1, 3, 2, 4, 5]) + + def test_set_func_bounds_one_peak_and_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFunction(bg, True, x, x + 1, -6, 6) + ql.add_single_SE() + + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -1, 0, 0, 0]) + self.assertEqual(upper, [1, 1, np.inf, 1, 1, 100, 1]) + + ql.set_func_bounds([-3, -2, 1, -4], [3, 2, 4, 5]) + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -2, -3, 1, -4]) + self.assertEqual(upper, [1, 1, np.inf, 2, 3, 4, 5]) + + def test_set_func_bounds_two_peak_no_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFunction(bg, False, x, x + 1, -6, 6) + ql.add_single_SE() + ql.add_single_SE() + + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -1, 0, 0, 0, 0, 0]) + self.assertEqual(upper, [1, 1, 1, 1, 100, 1, 1, 100, 1]) + + ql.set_func_bounds([-3, -2, 1, -4], [3, 2, 4, 5]) + lower, upper = ql.get_bounds() + + self.assertEqual(lower, [-1, -1, 0, -2, 0, 0, -3, 1, -4]) + self.assertEqual(upper, [1, 1, 1, 2, 100, 1, 3, 4, 5]) + + def test_set_func_bounds_two_peak_and_delta(self): + x = np.linspace(-5, 5, 5) + bg = LinearBG() + ql = QSEFunction(bg, True, x, x + 1, -6, 6) + ql.add_single_SE() + ql.add_single_SE() + + lower, upper = ql.get_bounds() + self.assertEqual(lower, [-1, -1, 0, -1, 0, 0, 0, 0, 0, 0]) + self.assertEqual(upper, [1, 1, np.inf, 1, 1, 100, 1, 1, 100, 1]) + + ql.set_func_bounds([-3, -2, 1, -4], [3, 2, 4, 5]) + lower, upper = ql.get_bounds() + + self.assertEqual(lower, [-1, -1, 0, -2, 0, 0, 0, -3, 1, -4]) + self.assertEqual(upper, [1, 1, np.inf, 2, 1, 100, 1, 3, 4, 5]) + + ql.set_func_bounds([-5, -6, -7, -8], [5, 6, 7, 8], 0) + lower, upper = ql.get_bounds() + + self.assertEqual(lower, [-1, -1, 0, -6, -5, -7, -8, -3, 1, -4]) + self.assertEqual(upper, [1, 1, np.inf, 6, 5, 7, 8, 3, 4, 5]) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/logLikelihood_test.py b/test/shared/logLikelihood_test.py similarity index 100% rename from test/logLikelihood_test.py rename to test/shared/logLikelihood_test.py diff --git a/test/shared/utils/__init__.py b/test/shared/utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/test/utils/crop_test.py b/test/shared/utils/crop_test.py similarity index 100% rename from test/utils/crop_test.py rename to test/shared/utils/crop_test.py diff --git a/test/utils/getBG_test.py b/test/shared/utils/getBG_test.py similarity index 100% rename from test/utils/getBG_test.py rename to test/shared/utils/getBG_test.py diff --git a/test/utils/parallel_test.py b/test/shared/utils/parallel_test.py similarity index 100% rename from test/utils/parallel_test.py rename to test/shared/utils/parallel_test.py diff --git a/test/utils/spline_test.py b/test/shared/utils/spline_test.py similarity index 100% rename from test/utils/spline_test.py rename to test/shared/utils/spline_test.py diff --git a/test/utils/updateGuess_test.py b/test/shared/utils/updateGuess_test.py similarity index 100% rename from test/utils/updateGuess_test.py rename to test/shared/utils/updateGuess_test.py diff --git a/test/workflows/grid_search/gridSearchTemplate_test.py b/test/shared/workflows/grid_search/gridSearchTemplate_test.py similarity index 100% rename from test/workflows/grid_search/gridSearchTemplate_test.py rename to test/shared/workflows/grid_search/gridSearchTemplate_test.py diff --git a/test/workflows/grid_search/quest_test.py b/test/shared/workflows/grid_search/quest_test.py similarity index 100% rename from test/workflows/grid_search/quest_test.py rename to test/shared/workflows/grid_search/quest_test.py diff --git a/test/workflows/model_selection/muon_decay_test.py b/test/shared/workflows/model_selection/muon_decay_test.py similarity index 100% rename from test/workflows/model_selection/muon_decay_test.py rename to test/shared/workflows/model_selection/muon_decay_test.py diff --git a/test/workflows/model_selection/qldata_test.py b/test/shared/workflows/model_selection/qldata_test.py similarity index 100% rename from test/workflows/model_selection/qldata_test.py rename to test/shared/workflows/model_selection/qldata_test.py diff --git a/test/workflows/model_selection/qlse_test.py b/test/shared/workflows/model_selection/qlse_test.py similarity index 100% rename from test/workflows/model_selection/qlse_test.py rename to test/shared/workflows/model_selection/qlse_test.py From bb623a1e4648b74e8244c7bbc9011d76d3fe1f51 Mon Sep 17 00:00:00 2001 From: Anthony Lim Date: Tue, 25 Feb 2025 16:06:10 +0000 Subject: [PATCH 05/21] update tests --- .github/workflows/run_tests.yml | 15 +++++++++++++-- src/quickBayes/test_helpers/model_selection.py | 4 ---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index e27d35c1..7678b369 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -49,8 +49,19 @@ jobs: run: | conda activate quickBayes-dev python -m pip install --upgrade .[gofit] - - name: run tests + - name: run tests with gofit timeout-minutes: 10 shell: bash -l {0} run: | - python -m pytest + python -m pytest test/shared test/gofit + - name: without gofit setup + shell: bash -l {0} + run: | + python -m pip uninstall gofit quickBayes + python -m pip install --upgrade . + - name: run tests without gofit + timeout-minutes: 10 + shell: bash -l {0} + run: | + python -m pytest test/shared test/default + diff --git a/src/quickBayes/test_helpers/model_selection.py b/src/quickBayes/test_helpers/model_selection.py index c8755117..9f9ac8a9 100644 --- a/src/quickBayes/test_helpers/model_selection.py +++ b/src/quickBayes/test_helpers/model_selection.py @@ -142,7 +142,3 @@ def test_update_scipy_fit_engine(self): self.assertEqual(self.wf.fit_engine._guess, [2]) self.assertEqual(self.wf.fit_engine._lower, [-1]) self.assertEqual(self.wf.fit_engine._upper, [1]) - - -if __name__ == '__main__': - unittest.main() From 70bcf892b336aef5dddcd1789842f7e30af3a43e Mon Sep 17 00:00:00 2001 From: Anthony Lim Date: Tue, 25 Feb 2025 16:12:34 +0000 Subject: [PATCH 06/21] update tests --- .github/workflows/run_tests.yml | 7 ++++--- docs/source/examples/QENS.ipynb | 2 +- docs/source/examples/muon.ipynb | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 7678b369..ea662700 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -53,15 +53,16 @@ jobs: timeout-minutes: 10 shell: bash -l {0} run: | - python -m pytest test/shared test/gofit + python -m pytest test/gofit - name: without gofit setup shell: bash -l {0} run: | - python -m pip uninstall gofit quickBayes + python -m pip uninstall quickBayes + python -m pip uninstall gofit python -m pip install --upgrade . - name: run tests without gofit timeout-minutes: 10 shell: bash -l {0} run: | - python -m pytest test/shared test/default + python -m pytest test/default diff --git a/docs/source/examples/QENS.ipynb b/docs/source/examples/QENS.ipynb index 8e8d96fe..3c12a34b 100644 --- a/docs/source/examples/QENS.ipynb +++ b/docs/source/examples/QENS.ipynb @@ -45,7 +45,7 @@ "metadata": {}, "outputs": [], "source": [ - "DATA_DIR = os.path.join('..', '..', '..', 'test', 'data')\n", + "DATA_DIR = os.path.join('..', '..', '..', 'test', 'shared', 'data')\n", "\n", "sample_file = os.path.join(DATA_DIR, 'sample_data_red.npy')\n", "resolution_file = os.path.join(DATA_DIR, 'resolution_data_red.npy')\n", diff --git a/docs/source/examples/muon.ipynb b/docs/source/examples/muon.ipynb index 359096d5..60f7a589 100644 --- a/docs/source/examples/muon.ipynb +++ b/docs/source/examples/muon.ipynb @@ -45,7 +45,7 @@ "metadata": {}, "outputs": [], "source": [ - "DATA_DIR = os.path.join('..', '..', '..', 'test', 'data', 'muon')\n", + "DATA_DIR = os.path.join('..', '..', '..', 'test', 'shared', 'data', 'muon')\n", "data_file = os.path.join(DATA_DIR, 'muon_expdecay_2.npy')\n", "\n", "sx, sy, se = np.loadtxt(data_file)\n", From acb5d813bbf3aba7bbad15e9bfc6fd6ac0452933 Mon Sep 17 00:00:00 2001 From: Anthony Lim Date: Tue, 25 Feb 2025 16:15:58 +0000 Subject: [PATCH 07/21] update tests --- .github/workflows/run_tests.yml | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index ea662700..4ef9a00f 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -48,21 +48,17 @@ jobs: shell: bash -l {0} run: | conda activate quickBayes-dev - python -m pip install --upgrade .[gofit] - - name: run tests with gofit + - name: run tests without gofit timeout-minutes: 10 shell: bash -l {0} run: | - python -m pytest test/gofit - - name: without gofit setup - shell: bash -l {0} - run: | - python -m pip uninstall quickBayes - python -m pip uninstall gofit - python -m pip install --upgrade . - - name: run tests without gofit + python -m pip install . + python -m pytest test/default + - name: run tests with gofit timeout-minutes: 10 shell: bash -l {0} run: | - python -m pytest test/default + python -m pip uninstall quickBayes + python -m pip install .[gofit] + python -m pytest test/gofit From 19d654189c86a9b14cc7be8591a092ddc95b43cb Mon Sep 17 00:00:00 2001 From: Anthony Lim Date: Tue, 25 Feb 2025 16:21:15 +0000 Subject: [PATCH 08/21] update conda --- quickBayes-dev.yml | 1 - tools/create_conda_yml.py | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/quickBayes-dev.yml b/quickBayes-dev.yml index 4ff69763..53b5288b 100644 --- a/quickBayes-dev.yml +++ b/quickBayes-dev.yml @@ -18,5 +18,4 @@ dependencies: - pybind11[global] - eigen - pip: - - gofit - readthedocs-sphinx-ext diff --git a/tools/create_conda_yml.py b/tools/create_conda_yml.py index 6e4b3b90..4cb50ec5 100644 --- a/tools/create_conda_yml.py +++ b/tools/create_conda_yml.py @@ -76,8 +76,7 @@ def create_default(version): """ default_yml = {} - pip_dict = {'readthedocs-sphinx-ext': '', - 'gofit': ''} + pip_dict = {'readthedocs-sphinx-ext': ''} default_yml['name'] = 'quickBayes-dev' default_yml['channels'] = 'conda-forge' From 69d7a897d04601cc83a11dcc96730468e9be8128 Mon Sep 17 00:00:00 2001 From: Anthony Lim Date: Tue, 25 Feb 2025 16:27:44 +0000 Subject: [PATCH 09/21] update conda --- .github/workflows/run_tests.yml | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 4ef9a00f..7055807f 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -48,17 +48,9 @@ jobs: shell: bash -l {0} run: | conda activate quickBayes-dev - - name: run tests without gofit + python -m pip install . + - name: run tests timeout-minutes: 10 shell: bash -l {0} run: | - python -m pip install . - python -m pytest test/default - - name: run tests with gofit - timeout-minutes: 10 - shell: bash -l {0} - run: | - python -m pip uninstall quickBayes - python -m pip install .[gofit] - python -m pytest test/gofit - + python -m pytest/default From ea9cc2de0f105573852729ab98aa48986f93f907 Mon Sep 17 00:00:00 2001 From: Anthony Lim Date: Tue, 25 Feb 2025 16:29:35 +0000 Subject: [PATCH 10/21] run test update --- .github/workflows/run_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 7055807f..37dc6e27 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -53,4 +53,4 @@ jobs: timeout-minutes: 10 shell: bash -l {0} run: | - python -m pytest/default + python -m pytest test/default From 2a309f69cd347dd9605dd571c52983340097e0c2 Mon Sep 17 00:00:00 2001 From: Anthony Lim Date: Tue, 25 Feb 2025 16:31:10 +0000 Subject: [PATCH 11/21] run test update --- .github/workflows/run_tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 37dc6e27..5ad3b0b8 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -48,9 +48,9 @@ jobs: shell: bash -l {0} run: | conda activate quickBayes-dev - python -m pip install . - name: run tests timeout-minutes: 10 shell: bash -l {0} run: | - python -m pytest test/default + python -m pip install . + python -m pytest test/default From 1e24325d97dcbd0b689c9d152bec00573bb28921 Mon Sep 17 00:00:00 2001 From: Anthony Lim Date: Tue, 25 Feb 2025 16:33:19 +0000 Subject: [PATCH 12/21] run test update --- .github/workflows/run_tests.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 5ad3b0b8..9e5ca880 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -48,9 +48,16 @@ jobs: shell: bash -l {0} run: | conda activate quickBayes-dev - - name: run tests + python -m pip install . + - name: run tests default timeout-minutes: 10 shell: bash -l {0} run: | - python -m pip install . - python -m pytest test/default + python -m pytest test/default + - name: run tests gofit + timeout-minutes: 10 + shell: bash -l {0} + run: | + python -m pip uninstall quickBayes + python -m pip install .[gofit] + python -m pytest test/gofit From 55ba3829ee3bfccf7744c265bcb88abcdf169625 Mon Sep 17 00:00:00 2001 From: Anthony Lim Date: Tue, 25 Feb 2025 16:36:10 +0000 Subject: [PATCH 13/21] update conda --- .github/workflows/release.yml | 6 ++++++ .github/workflows/run_tests.yml | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ad447bf4..d26b3114 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,6 +1,12 @@ name: Build and upload to PyPI on: + push: + branches: + - 'main' + pull_request: + branches: + - 'main' release: types: - published diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 9e5ca880..8a5eaa6e 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -53,11 +53,11 @@ jobs: timeout-minutes: 10 shell: bash -l {0} run: | - python -m pytest test/default + python -m pytest test/default test/shared - name: run tests gofit timeout-minutes: 10 shell: bash -l {0} run: | python -m pip uninstall quickBayes python -m pip install .[gofit] - python -m pytest test/gofit + python -m pytest test/gofit test/shared From 8cd055201de5611dd1148a9a773720b750d32ddd Mon Sep 17 00:00:00 2001 From: Anthony Lim Date: Tue, 25 Feb 2025 16:50:26 +0000 Subject: [PATCH 14/21] update tests --- src/quickBayes/test_helpers/grid_template.py | 199 +++++++++++++++++++ test/default/gridSearchTemplate_test.py | 18 ++ test/gofit/gridSearchTemplate_test.py | 33 +++ 3 files changed, 250 insertions(+) create mode 100644 src/quickBayes/test_helpers/grid_template.py create mode 100644 test/default/gridSearchTemplate_test.py create mode 100644 test/gofit/gridSearchTemplate_test.py diff --git a/src/quickBayes/test_helpers/grid_template.py b/src/quickBayes/test_helpers/grid_template.py new file mode 100644 index 00000000..8d00a37e --- /dev/null +++ b/src/quickBayes/test_helpers/grid_template.py @@ -0,0 +1,199 @@ +from quickBayes.workflow.grid_search.template import GridSearchTemplate +from quickBayes.functions.BG import FlatBG +from quickBayes.functions.exp_decay import ExpDecay +from quickBayes.test_helpers.workflow_helper import (gen_grid_search_data, + FixedBG, + FixedComposite) + + +class SimpleWorkflow(GridSearchTemplate): + @staticmethod + def _update_function(func): + """ + Add a flat BG term + """ + ed = ExpDecay() + func.add_function(ed) + return func + + @staticmethod + def _set_x_value(func, value): + func.set_c(value) + return func + + @staticmethod + def _set_y_value(func, value): + func.set_m(value) + return func + + @staticmethod + def N(func): + return 1 + + +class GridSearchTemplateTest(object): + + def setUp(self): + self.func = FixedComposite() + lin = FixedBG() + self.func.add_function(lin) + self.wf = SimpleWorkflow() + + def test_set_x_axis(self): + self.wf.set_x_axis(1, 3, 5, 'test') + x_axis = self.wf.get_x_axis + expect = [1, 1.5, 2., 2.5, 3] + values = x_axis.values + self.assertEqual(len(values), len(expect)) + for j in range(len(expect)): + self.assertEqual(values[j], expect[j]) + self.assertEqual(x_axis.len, 5) + self.assertEqual(x_axis.label, "test") + + def test_set_y_axis(self): + self.wf.set_y_axis(6, 8, 5, 'test2') + y_axis = self.wf.get_y_axis + expect = [6, 6.5, 7., 7.5, 8] + values = y_axis.values + self.assertEqual(len(values), len(expect)) + for j in range(len(expect)): + self.assertEqual(values[j], expect[j]) + self.assertEqual(y_axis.len, 5) + self.assertEqual(y_axis.label, "test2") + + def test_grid(self): + self.wf.set_x_axis(0, 1, 2, 'x') + self.wf.set_y_axis(1, 2, 2, 'y') + X, Y = self.wf._generate_grid() + grid = self.wf.get_grid + + expect_x = [[0, 1], [0, 1]] + expect_y = [[1, 1], [2, 2]] + + for i in range(2): + for j in range(2): + self.assertEqual(X[i][j], expect_x[i][j]) + self.assertEqual(Y[i][j], expect_y[i][j]) + self.assertEqual(grid[i][j], 0) + + def test_preprocess_data(self): + # same + x, y, e = gen_grid_search_data() + self.wf.preprocess_data(x, y, e) + self.assertEqual(len(self.wf._data), 3) + self.assertEqual(len(self.wf._data['x']), len(x)) + self.assertEqual(len(self.wf._data['y']), len(y)) + self.assertEqual(len(self.wf._data['e']), len(e)) + for j in range(len(x)): + self.assertEqual(self.wf._data['x'][j], x[j]) + self.assertEqual(self.wf._data['y'][j], y[j]) + self.assertEqual(self.wf._data['e'][j], e[j]) + + def test_execute(self): + # indirectly tests normalise_grid and get_z_value + + # setup workflow + generate data + x, y, e = gen_grid_search_data() + self.wf.preprocess_data(x, y, e) + self.wf.set_x_axis(0, 1, 2, 'x') + self.wf.set_y_axis(1, 2, 2, 'y') + self.func.add_function(ExpDecay()) + self.wf.set_scipy_engine([0, 0], [-9, -9], [9, 9]) + X, Y = self.wf.execute(self.func) + + grid = self.wf.get_grid + expect_z = [[0.449, 1], [0, 0.115]] + expect_x = [[0, 1], [0, 1]] + expect_y = [[1, 1], [2, 2]] + + for i in range(2): + for j in range(2): + self.assertEqual(X[i][j], expect_x[i][j]) + self.assertEqual(Y[i][j], expect_y[i][j]) + self.assertAlmostEqual(grid[i][j], + expect_z[i][j], 3) + + def test_get_slices(self): + # setup workflow + generate data + x, y, e = gen_grid_search_data() + self.wf.preprocess_data(x, y, e) + self.wf.set_x_axis(0, 1, 2, 'x') + self.wf.set_y_axis(1, 2, 2, 'y') + self.func.add_function(ExpDecay()) + self.wf.set_scipy_engine([0, 0], [-9, -9], [9, 9]) + _, _ = self.wf.execute(self.func) + + x, y = self.wf.get_slices() + expect_x = [0.449, 1] + expect_y = [1, 0.115] + + self.assertEqual(len(x), len(expect_x)) + self.assertEqual(len(y), len(expect_y)) + + for j in range(len(x)): + self.assertAlmostEqual(x[j], expect_x[j], 3) + self.assertAlmostEqual(y[j], expect_y[j], 3) + + def test_get_parameters_and_errors(self): + # rm + pass + + def test_fails_if_no_data(self): + # same + with self.assertRaises(ValueError): + self.wf.set_scipy_engine([0], [-9], [9]) + + def test_execute_no_engine(self): + x, y, e = gen_grid_search_data() + self.wf.preprocess_data(x, y, e) + self.wf.set_x_axis(0, 1, 2, 'x') + self.wf.set_y_axis(1, 2, 2, 'y') + with self.assertRaises(ValueError): + _, _ = self.wf.execute(self.func) + + def test_execute_no_x_axis(self): + x, y, e = gen_grid_search_data() + self.wf.preprocess_data(x, y, e) + self.wf.set_y_axis(1, 2, 2, 'y') + with self.assertRaises(ValueError): + _, _ = self.wf.execute(self.func) + + def test_execute_no_y_axis(self): + x, y, e = gen_grid_search_data() + self.wf.preprocess_data(x, y, e) + self.wf.set_x_axis(0, 1, 2, 'x') + with self.assertRaises(ValueError): + _, _ = self.wf.execute(self.func) + + def test_add_second_engine_errors(self): + x, y, e = gen_grid_search_data() + self.wf.preprocess_data(x, y, e) + self.wf.set_x_axis(0, 1, 2, 'x') + self.wf.set_y_axis(1, 2, 2, 'y') + self.wf.set_scipy_engine([], [], []) + with self.assertRaises(RuntimeError): + self.wf.set_scipy_engine([], [], []) + + def test_set_scipy_engine(self): + # same + self.assertEqual(self.wf.fit_engine, None) + x, y, e = gen_grid_search_data() + self.wf.preprocess_data(x, y, e) + self.wf.set_scipy_engine([], [], []) + self.assertEqual(self.wf.fit_engine.name, 'scipy') + + def test_update_scipy_fit_engine(self): + # same + self.assertEqual(self.wf.fit_engine, None) + x, y, e = gen_grid_search_data() + self.wf.preprocess_data(x, y, e) + self.wf.set_scipy_engine([1.], [-4], [4]) + self.assertEqual(self.wf.fit_engine._guess, [1.]) + self.assertEqual(self.wf.fit_engine._lower, [-4]) + self.assertEqual(self.wf.fit_engine._upper, [4]) + + bg = FlatBG() + self.wf.update_fit_engine(bg, [2]) + self.assertEqual(self.wf.fit_engine._guess, [2.]) + self.assertEqual(self.wf.fit_engine._lower, [-4]) + self.assertEqual(self.wf.fit_engine._upper, [4]) diff --git a/test/default/gridSearchTemplate_test.py b/test/default/gridSearchTemplate_test.py new file mode 100644 index 00000000..be7a51fc --- /dev/null +++ b/test/default/gridSearchTemplate_test.py @@ -0,0 +1,18 @@ +import unittest +from quickBayes.test_helpers.workflow_helper import gen_grid_search_data +from quickBayes.test_helpers.grid_template import GridSearchTemplateTest + + +class GridSearchTemplateWithoutGoFitTest(GridSearchTemplateTest, + unittest.TestCase): + def test_set_gofit_engine(self): + # same + self.assertEqual(self.wf.fit_engine, None) + x, y, e = gen_grid_search_data() + self.wf.preprocess_data(x, y, e) + with self.assertRaises(RuntimeError): + self.wf.set_gofit_engine(5, [], []) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/gofit/gridSearchTemplate_test.py b/test/gofit/gridSearchTemplate_test.py new file mode 100644 index 00000000..3372d0dc --- /dev/null +++ b/test/gofit/gridSearchTemplate_test.py @@ -0,0 +1,33 @@ +import unittest +from quickBayes.functions.BG import FlatBG +from quickBayes.test_helpers.workflow_helper import gen_grid_search_data +from quickBayes.test_helpers.grid_template import GridSearchTemplateTest + + +class GridSearchTemplateWithGoFitTest(GridSearchTemplateTest, + unittest.TestCase): + def test_set_gofit_engine(self): + # same + self.assertEqual(self.wf.fit_engine, None) + x, y, e = gen_grid_search_data() + self.wf.preprocess_data(x, y, e) + self.wf.set_gofit_engine(5, [], []) + self.assertEqual(self.wf.fit_engine.name, 'gofit') + + def test_update_gofit_engine(self): + # same + self.assertEqual(self.wf.fit_engine, None) + x, y, e = gen_grid_search_data() + self.wf.preprocess_data(x, y, e) + self.wf.set_gofit_engine(5, [], []) + self.assertEqual(self.wf.fit_engine._lower, []) + self.assertEqual(self.wf.fit_engine._upper, []) + + bg = FlatBG() + self.wf.update_fit_engine(bg, [5]) + self.assertEqual(self.wf.fit_engine._lower, [-1]) + self.assertEqual(self.wf.fit_engine._upper, [1]) + + +if __name__ == '__main__': + unittest.main() From 7e4a2d92545013281a73e86b34836c2dda90d98d Mon Sep 17 00:00:00 2001 From: Anthony Lim Date: Tue, 25 Feb 2025 16:52:52 +0000 Subject: [PATCH 15/21] update tests --- .../grid_search/gridSearchTemplate_test.py | 226 ------------------ 1 file changed, 226 deletions(-) delete mode 100644 test/shared/workflows/grid_search/gridSearchTemplate_test.py diff --git a/test/shared/workflows/grid_search/gridSearchTemplate_test.py b/test/shared/workflows/grid_search/gridSearchTemplate_test.py deleted file mode 100644 index 4a6d0f7f..00000000 --- a/test/shared/workflows/grid_search/gridSearchTemplate_test.py +++ /dev/null @@ -1,226 +0,0 @@ -import unittest -from quickBayes.workflow.grid_search.template import GridSearchTemplate -from quickBayes.functions.BG import FlatBG -from quickBayes.functions.exp_decay import ExpDecay -from quickBayes.test_helpers.workflow_helper import (gen_grid_search_data, - FixedBG, - FixedComposite) - - -class SimpleWorkflow(GridSearchTemplate): - @staticmethod - def _update_function(func): - """ - Add a flat BG term - """ - ed = ExpDecay() - func.add_function(ed) - return func - - @staticmethod - def _set_x_value(func, value): - func.set_c(value) - return func - - @staticmethod - def _set_y_value(func, value): - func.set_m(value) - return func - - @staticmethod - def N(func): - return 1 - - -class GridSearchTemplateTest(unittest.TestCase): - - def setUp(self): - self.func = FixedComposite() - lin = FixedBG() - self.func.add_function(lin) - self.wf = SimpleWorkflow() - - def test_set_x_axis(self): - self.wf.set_x_axis(1, 3, 5, 'test') - x_axis = self.wf.get_x_axis - expect = [1, 1.5, 2., 2.5, 3] - values = x_axis.values - self.assertEqual(len(values), len(expect)) - for j in range(len(expect)): - self.assertEqual(values[j], expect[j]) - self.assertEqual(x_axis.len, 5) - self.assertEqual(x_axis.label, "test") - - def test_set_y_axis(self): - self.wf.set_y_axis(6, 8, 5, 'test2') - y_axis = self.wf.get_y_axis - expect = [6, 6.5, 7., 7.5, 8] - values = y_axis.values - self.assertEqual(len(values), len(expect)) - for j in range(len(expect)): - self.assertEqual(values[j], expect[j]) - self.assertEqual(y_axis.len, 5) - self.assertEqual(y_axis.label, "test2") - - def test_grid(self): - self.wf.set_x_axis(0, 1, 2, 'x') - self.wf.set_y_axis(1, 2, 2, 'y') - X, Y = self.wf._generate_grid() - grid = self.wf.get_grid - - expect_x = [[0, 1], [0, 1]] - expect_y = [[1, 1], [2, 2]] - - for i in range(2): - for j in range(2): - self.assertEqual(X[i][j], expect_x[i][j]) - self.assertEqual(Y[i][j], expect_y[i][j]) - self.assertEqual(grid[i][j], 0) - - def test_preprocess_data(self): - # same - x, y, e = gen_grid_search_data() - self.wf.preprocess_data(x, y, e) - self.assertEqual(len(self.wf._data), 3) - self.assertEqual(len(self.wf._data['x']), len(x)) - self.assertEqual(len(self.wf._data['y']), len(y)) - self.assertEqual(len(self.wf._data['e']), len(e)) - for j in range(len(x)): - self.assertEqual(self.wf._data['x'][j], x[j]) - self.assertEqual(self.wf._data['y'][j], y[j]) - self.assertEqual(self.wf._data['e'][j], e[j]) - - def test_execute(self): - # indirectly tests normalise_grid and get_z_value - - # setup workflow + generate data - x, y, e = gen_grid_search_data() - self.wf.preprocess_data(x, y, e) - self.wf.set_x_axis(0, 1, 2, 'x') - self.wf.set_y_axis(1, 2, 2, 'y') - self.func.add_function(ExpDecay()) - self.wf.set_scipy_engine([0, 0], [-9, -9], [9, 9]) - X, Y = self.wf.execute(self.func) - - grid = self.wf.get_grid - expect_z = [[0.449, 1], [0, 0.115]] - expect_x = [[0, 1], [0, 1]] - expect_y = [[1, 1], [2, 2]] - - for i in range(2): - for j in range(2): - self.assertEqual(X[i][j], expect_x[i][j]) - self.assertEqual(Y[i][j], expect_y[i][j]) - self.assertAlmostEqual(grid[i][j], - expect_z[i][j], 3) - - def test_get_slices(self): - # setup workflow + generate data - x, y, e = gen_grid_search_data() - self.wf.preprocess_data(x, y, e) - self.wf.set_x_axis(0, 1, 2, 'x') - self.wf.set_y_axis(1, 2, 2, 'y') - self.func.add_function(ExpDecay()) - self.wf.set_scipy_engine([0, 0], [-9, -9], [9, 9]) - _, _ = self.wf.execute(self.func) - - x, y = self.wf.get_slices() - expect_x = [0.449, 1] - expect_y = [1, 0.115] - - self.assertEqual(len(x), len(expect_x)) - self.assertEqual(len(y), len(expect_y)) - - for j in range(len(x)): - self.assertAlmostEqual(x[j], expect_x[j], 3) - self.assertAlmostEqual(y[j], expect_y[j], 3) - - def test_get_parameters_and_errors(self): - # rm - pass - - def test_fails_if_no_data(self): - # same - with self.assertRaises(ValueError): - self.wf.set_scipy_engine([0], [-9], [9]) - - def test_execute_no_engine(self): - x, y, e = gen_grid_search_data() - self.wf.preprocess_data(x, y, e) - self.wf.set_x_axis(0, 1, 2, 'x') - self.wf.set_y_axis(1, 2, 2, 'y') - with self.assertRaises(ValueError): - _, _ = self.wf.execute(self.func) - - def test_execute_no_x_axis(self): - x, y, e = gen_grid_search_data() - self.wf.preprocess_data(x, y, e) - self.wf.set_y_axis(1, 2, 2, 'y') - with self.assertRaises(ValueError): - _, _ = self.wf.execute(self.func) - - def test_execute_no_y_axis(self): - x, y, e = gen_grid_search_data() - self.wf.preprocess_data(x, y, e) - self.wf.set_x_axis(0, 1, 2, 'x') - with self.assertRaises(ValueError): - _, _ = self.wf.execute(self.func) - - def test_add_second_engine_errors(self): - x, y, e = gen_grid_search_data() - self.wf.preprocess_data(x, y, e) - self.wf.set_x_axis(0, 1, 2, 'x') - self.wf.set_y_axis(1, 2, 2, 'y') - self.wf.set_scipy_engine([], [], []) - with self.assertRaises(RuntimeError): - self.wf.set_scipy_engine([], [], []) - - def test_set_scipy_engine(self): - # same - self.assertEqual(self.wf.fit_engine, None) - x, y, e = gen_grid_search_data() - self.wf.preprocess_data(x, y, e) - self.wf.set_scipy_engine([], [], []) - self.assertEqual(self.wf.fit_engine.name, 'scipy') - - def test_update_scipy_fit_engine(self): - # same - self.assertEqual(self.wf.fit_engine, None) - x, y, e = gen_grid_search_data() - self.wf.preprocess_data(x, y, e) - self.wf.set_scipy_engine([1.], [-4], [4]) - self.assertEqual(self.wf.fit_engine._guess, [1.]) - self.assertEqual(self.wf.fit_engine._lower, [-4]) - self.assertEqual(self.wf.fit_engine._upper, [4]) - - bg = FlatBG() - self.wf.update_fit_engine(bg, [2]) - self.assertEqual(self.wf.fit_engine._guess, [2.]) - self.assertEqual(self.wf.fit_engine._lower, [-4]) - self.assertEqual(self.wf.fit_engine._upper, [4]) - - def test_set_gofit_engine(self): - # same - self.assertEqual(self.wf.fit_engine, None) - x, y, e = gen_grid_search_data() - self.wf.preprocess_data(x, y, e) - self.wf.set_gofit_engine(5, [], []) - self.assertEqual(self.wf.fit_engine.name, 'gofit') - - def test_update_gofit_engine(self): - # same - self.assertEqual(self.wf.fit_engine, None) - x, y, e = gen_grid_search_data() - self.wf.preprocess_data(x, y, e) - self.wf.set_gofit_engine(5, [], []) - self.assertEqual(self.wf.fit_engine._lower, []) - self.assertEqual(self.wf.fit_engine._upper, []) - - bg = FlatBG() - self.wf.update_fit_engine(bg, [5]) - self.assertEqual(self.wf.fit_engine._lower, [-1]) - self.assertEqual(self.wf.fit_engine._upper, [1]) - - -if __name__ == '__main__': - unittest.main() From f65bc255b316677e858bd1dd6d7f1341b202110a Mon Sep 17 00:00:00 2001 From: Anthony Lim Date: Tue, 25 Feb 2025 17:06:21 +0000 Subject: [PATCH 16/21] release --- .github/workflows/release.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d26b3114..adf451f1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,9 +7,6 @@ on: pull_request: branches: - 'main' - release: - types: - - published jobs: build_wheels: From 90ef9f2a4051ad0d466bf4d0c907a2e85b990301 Mon Sep 17 00:00:00 2001 From: Anthony Lim Date: Tue, 25 Feb 2025 17:09:33 +0000 Subject: [PATCH 17/21] release --- .github/workflows/release.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index adf451f1..39a19b95 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: "0" - name: install build @@ -22,7 +22,7 @@ jobs: - name: build wheel run: python -m build --sdist --wheel - name: upload wheel - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: path: dist/ @@ -36,10 +36,12 @@ jobs: id: mint uses: tschm/token-mint-action@v1.0.2 - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: artifact path: dist + pattern: cibw-* + merge-multiple: true - name: Publish package distribution to PYPI uses: pypa/gh-action-pypi-publish@release/v1 From 4a642fb650e779d4423cc0468dcc8da9cf0c63e5 Mon Sep 17 00:00:00 2001 From: Anthony Lim Date: Tue, 25 Feb 2025 17:12:02 +0000 Subject: [PATCH 18/21] release --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 0c1546fc..15c9c051 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = 'hatchling.build' [project] name = 'quickBayes' -version = "1.0.0b23" +version = "1.0.1b0" requires-python = ">=3.7.1" dependencies = ['numpy<2.0.0', 'scipy'] authors = [{name='Anthony Lim', email='anthony.lim@stfc.ac.uk'}] From 265e801cf7ceeeeb3648695664241ad83fe74fc4 Mon Sep 17 00:00:00 2001 From: Anthony Lim Date: Tue, 25 Feb 2025 17:29:35 +0000 Subject: [PATCH 19/21] update doc --- .github/workflows/release.yml | 9 +++------ docs/source/install.rst | 8 ++++++++ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 39a19b95..4b5cdb40 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,12 +1,9 @@ name: Build and upload to PyPI on: - push: - branches: - - 'main' - pull_request: - branches: - - 'main' + release: + types: + - published jobs: build_wheels: diff --git a/docs/source/install.rst b/docs/source/install.rst index 186d8350..92e65aa4 100644 --- a/docs/source/install.rst +++ b/docs/source/install.rst @@ -15,6 +15,14 @@ The library is available on `PyPi `_. +To install quickBayes with gofit the command is + +.. code-block:: python + + python -m pip install quickBayes[gofit] + + Reporting Issues ---------------- From a0b14711e9b282c1e03628dff3ac40d8e736a0b0 Mon Sep 17 00:00:00 2001 From: Anthony Lim Date: Tue, 25 Feb 2025 17:39:13 +0000 Subject: [PATCH 20/21] update doc --- docs/source/install.rst | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/source/install.rst b/docs/source/install.rst index 92e65aa4..e206d5cc 100644 --- a/docs/source/install.rst +++ b/docs/source/install.rst @@ -23,6 +23,34 @@ To install quickBayes with gofit the command is python -m pip install quickBayes[gofit] +Running the Tests +----------------- + +The tests are split into three sections: + +- shared +- default +- gofit + +and these distinctions exist to allow the testing for both with and without gofit. +The tests in shared correspond to those that should be the same regardless of the installed packages. +The default tests are what should happen if an optional dependency is missing. +Typically this is producing an error at initialisation. +The gofit tests are to test the functionality of gofit. + +To run the default tests + +.. code-block:: python + + pytest test/default + +and to run both the gofit and shared tests + +.. code-block:: python + + pytest test/shared test/gofit + + Reporting Issues ---------------- From 130e0160f2f8f089b82b78b8046a75a9314a92bc Mon Sep 17 00:00:00 2001 From: Anthony Date: Wed, 26 Feb 2025 10:18:45 +0000 Subject: [PATCH 21/21] Apply suggestions from code review Co-authored-by: Jessica Huntley <79837359+jess-farmer@users.noreply.github.com> --- src/quickBayes/fitting/gofit_base.py | 2 +- src/quickBayes/fitting/gofit_engine.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/quickBayes/fitting/gofit_base.py b/src/quickBayes/fitting/gofit_base.py index ddfe6b63..e5110db0 100644 --- a/src/quickBayes/fitting/gofit_base.py +++ b/src/quickBayes/fitting/gofit_base.py @@ -50,7 +50,7 @@ def __init__(self, x_data: ndarray, y_data: ndarray, e_data: ndarray, lower: ndarray, upper: ndarray, samples: int = 10, max_iterations: int = 220000): """ - Creates the scipy curve fit engine class + Creates the gofit multistart fit engine class Stores useful information about each fit :param name: name of the fit engine :param x_data: original x data (can fit to an interpolation) diff --git a/src/quickBayes/fitting/gofit_engine.py b/src/quickBayes/fitting/gofit_engine.py index 1c3b890d..2a50e164 100644 --- a/src/quickBayes/fitting/gofit_engine.py +++ b/src/quickBayes/fitting/gofit_engine.py @@ -11,7 +11,7 @@ def __init__(self, x_data: ndarray, y_data: ndarray, e_data: ndarray, lower: ndarray, upper: ndarray, samples: int = 10, max_iterations: int = 220000): """ - Creates the scipy curve fit engine class + Creates the gofit multistart fit engine class Stores useful information about each fit :param name: name of the fit engine :param x_data: original x data (can fit to an interpolation) @@ -33,7 +33,8 @@ def __init__(self, x_data: ndarray, y_data: ndarray, e_data: ndarray, lower: ndarray, upper: ndarray, samples: int = 10, max_iterations: int = 220000): """ - Creates the scipy curve fit engine class + Creates a dummy gofit engine class. + This is to prevent errors if gofit is not installed. Stores useful information about each fit :param name: name of the fit engine :param x_data: original x data (can fit to an interpolation)