Coverage for tests/test_fitting.py: 100%
107 statements
« prev ^ index » next coverage.py v7.10.5, created at 2025-08-28 09:13 +0000
« prev ^ index » next coverage.py v7.10.5, created at 2025-08-28 09:13 +0000
1"""Test module for the functions in the `fitting.py` module.
3This module contains unit tests for the functions implemented in the `fitting.py` module. The purpose of these tests is to
4ensure the correct functionality of each function in different scenarios and to validate that the expected outputs are
5returned.
7Tests should cover various edge cases, valid inputs, and any other conditions that are necessary to confirm the
8robustness of the functions."""
10import numpy as np
11import pytest
13from app.utility.data import are_close
14from app.fitting import Fit, normalize_to_unit
17class TestNormalizeToUnit:
19 def test_zero(self) -> None:
20 """Test normalization of zero."""
21 value, exponent = normalize_to_unit(0.0)
22 assert value == 0.0
23 assert exponent == 0
25 def test_one(self) -> None:
26 """Test normalization of 1."""
27 value, exponent = normalize_to_unit(1.0)
28 assert value == 1.0
29 assert exponent == 0
31 def test_negative_one(self) -> None:
32 """Test normalization of -1."""
33 value, exponent = normalize_to_unit(-1.0)
34 assert value == -1.0
35 assert exponent == 0
37 def test_smaller_than_one(self) -> None:
38 """Test normalization of number smaller than 1 but greater than 0.1."""
39 value, exponent = normalize_to_unit(0.5)
40 assert value == 0.5
41 assert exponent == 0
43 def test_smaller_than_point_one(self) -> None:
44 """Test normalization of number smaller than 0.1."""
45 value, exponent = normalize_to_unit(0.05)
46 assert value == 0.5
47 assert exponent == -1
49 def test_larger_than_one(self) -> None:
50 """Test normalization of number larger than 1."""
51 value, exponent = normalize_to_unit(42.0)
52 assert are_close(value, 0.42)
53 assert exponent == 2
55 def test_very_large_number(self) -> None:
56 """Test normalization of a very large number."""
57 value, exponent = normalize_to_unit(1.433364345e9)
58 assert are_close(value, 0.1433364345)
59 assert exponent == 10
61 def test_very_small_number(self) -> None:
62 """Test normalization of a very small number."""
63 value, exponent = normalize_to_unit(3.5e-8)
64 assert are_close(value, 0.35)
65 assert exponent == -7
67 def test_negative_small_number(self) -> None:
68 """Test normalization of a negative small number."""
69 value, exponent = normalize_to_unit(-0.0025)
70 assert are_close(value, -0.25)
71 assert exponent == -2
73 def test_negative_large_number(self) -> None:
74 """Test normalization of a negative large number."""
75 value, exponent = normalize_to_unit(-12345.0)
76 assert are_close(value, -0.12345)
77 assert exponent == 5
79 def test_exactly_point_one(self) -> None:
80 """Test normalization of exactly 0.1."""
81 value, exponent = normalize_to_unit(0.1)
82 assert value == 0.1
83 assert exponent == 0
85 def test_almost_point_one(self) -> None:
86 """Test normalization of a number very close to 0.1."""
87 value, exponent = normalize_to_unit(0.099999)
88 assert are_close(value, 0.99999)
89 assert exponent == -1
91 def test_scientific_notation_positive(self) -> None:
92 """Test with number in scientific notation (positive exponent)."""
93 value, exponent = normalize_to_unit(2.5e4)
94 assert are_close(value, 0.25)
95 assert exponent == 5
97 def test_scientific_notation_negative(self) -> None:
98 """Test with number in scientific notation (negative exponent)."""
99 value, exponent = normalize_to_unit(14e-6)
100 assert are_close(value, 0.14)
101 assert exponent == -4
104class TestFit:
106 @pytest.fixture
107 def gaussian_data(
108 self,
109 ) -> tuple[list[np.ndarray], list[np.ndarray], callable, dict[str, float], list[str], list[dict[str, float]]]:
110 """Gaussian data"""
112 def gaussian(x: np.ndarray, a: float, x0: float, c: float) -> np.ndarray:
113 """Gaussian function"""
114 return a * np.exp(-((x - x0) ** 2) / (2 * c))
116 xs_data = [np.linspace(-2, 5, 101)] * 3
117 ys_data = [
118 gaussian(xs_data[0], 1e8, 1, 0.5),
119 gaussian(xs_data[0], 3e8, 1, 0.25),
120 gaussian(xs_data[0], 5e8, 1, 0.25),
121 ]
122 p0 = dict(a=1e8, x0=0, c=0.3)
123 detached_parameters = ["a"]
124 fixed_parameters = [dict(c=0.5), dict(c=0.25), dict(c=0.25)]
125 return xs_data, ys_data, gaussian, p0, detached_parameters, fixed_parameters
127 @pytest.fixture
128 def gaussian_fit(self, gaussian_data) -> Fit:
129 """Gaussian fit object"""
131 xs_data, ys_data, function, p0, detached_parameters, fixed_parameters = gaussian_data
132 return Fit(xs_data, ys_data, function, p0, detached_parameters, fixed_parameters)
134 def test_gaussian_creation(self, gaussian_fit) -> None:
136 assert gaussian_fit.p0_mantissa == {"a": 1, "x0": 0.0}
137 assert gaussian_fit.p0_factors == {"a": 8, "x0": 0}
138 assert gaussian_fit.p0_list == [1.0, 0.0, 1.0, 1.0]
139 assert gaussian_fit.bounds == {"a": [0, np.inf], "x0": [0, np.inf]}
140 assert gaussian_fit.bounds_list == [[0, np.inf], [0, np.inf], [0, np.inf], [0, np.inf]]
142 def test_gaussian_incorrect_fixed_parameters(self, gaussian_data) -> None:
144 xs_data, ys_data, gaussian, p0, detached_parameters = gaussian_data[:-1]
145 fixed_parameters = [dict(c=0.5), dict()]
146 with pytest.raises(AssertionError):
147 Fit(xs_data, ys_data, gaussian, p0, detached_parameters, fixed_parameters)
149 def test_gaussian_list_to_dicts(self, gaussian_fit) -> None:
151 expected = [
152 {"a": 100000000.0, "x0": 0.0},
153 {"a": 100000000.0, "x0": 0.0},
154 {"a": 100000000.0, "x0": 0.0},
155 ]
156 assert are_close(gaussian_fit.list_to_dicts(gaussian_fit.p0_list), expected)
158 def test_gaussian_list_to_dicts_no_fixed(self, gaussian_data) -> None:
160 expected = [{"a": 100000000.0, "x0": 0.0}, {"a": 100000000.0, "x0": 0.0}, {"a": 100000000.0, "x0": 0.0}]
161 xs_data, ys_data, function, p0, detached_parameters, fixed_parameters = gaussian_data
162 fit = Fit(xs_data, ys_data, function, p0, [], fixed_parameters)
163 assert are_close(fit.list_to_dicts(fit.p0_list), expected)
165 def test_gaussian_error_function(self, gaussian_fit) -> None:
167 expected = np.array([1819222.90846475, 2392860.49712516])
168 assert are_close(gaussian_fit.error_function(gaussian_fit.p0_list)[:2], expected)
170 def test_gaussian_fit(self, gaussian_fit) -> None:
172 expected = [
173 {"a": 100000000.0, "x0": 1.0, "c": 0.5},
174 {"a": 300000000.0, "x0": 1.0, "c": 0.25},
175 {"a": 500000000.0, "x0": 1.0, "c": 0.25},
176 ]
177 assert are_close(gaussian_fit.fit(), expected)
179 def test_gaussian_calculate_fits(self, gaussian_fit) -> None:
181 expected = np.array([12340.98040867, 18690.68861775])
182 assert are_close(gaussian_fit.calculate_fits(gaussian_fit.fit())[0][:2], expected)
184 def test_gaussian_calculate_rss(self, gaussian_fit) -> None:
186 fits = gaussian_fit.calculate_fits(gaussian_fit.fit())
187 assert are_close(gaussian_fit.calculate_rss(fits), 1)