Coverage for tests / tests_computing / test_quantities.py: 99%
232 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-09 16:40 +0100
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-09 16:40 +0100
1# SPDX-FileCopyrightText: Copyright INRIA
2#
3# SPDX-License-Identifier: LGPL-3.0-only
4#
5# Copyright INRIA
6#
7# This file is part of PhysioBlocks, a library mostly developed by the
8# [Ananke project-team](https://team.inria.fr/ananke) at INRIA.
9#
10# Authors:
11# - Colin Drieu
12# - Dominique Chapelle
13# - François Kimmig
14# - Philippe Moireau
15#
16# PhysioBlocks is free software: you can redistribute it and/or modify it under the
17# terms of the GNU Lesser General Public License as published by the Free Software
18# Foundation, version 3 of the License.
19#
20# PhysioBlocks is distributed in the hope that it will be useful, but WITHOUT ANY
21# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
22# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
23#
24# You should have received a copy of the GNU Lesser General Public License along with
25# PhysioBlocks. If not, see <https://www.gnu.org/licenses/>.
27import numpy as np
28import pytest
29import regex as re
31from physioblocks.computing.quantities import (
32 Quantity,
33 diff,
34 mid_alpha,
35 mid_point,
36 sign,
37)
40@pytest.fixture
41def scalar_current():
42 return 0.1
45@pytest.fixture
46def scalar_new():
47 return 0.2
50@pytest.fixture
51def vector_current():
52 return np.array([0.1, 0.2])
55@pytest.fixture
56def vector_new():
57 return np.array([0.2, 0.3])
60@pytest.fixture
61def scalar_zero_reference():
62 return 0.0
65@pytest.fixture
66def vector_zero_reference():
67 return np.zeros(
68 2,
69 )
72@pytest.fixture
73def scalar_time_shift():
74 return 0.25
77@pytest.fixture
78def vector_time_shift():
79 return np.array(
80 [0.25, 0.1],
81 )
84class TestQuantity:
85 def test_constructor_scalar(self, scalar_current):
86 qty = Quantity(scalar_current)
88 assert qty.current == pytest.approx(scalar_current)
89 assert qty.new == pytest.approx(scalar_current)
91 def test_constructor_vector(self, vector_current):
92 qty = Quantity(vector_current)
93 assert vector_current == pytest.approx(qty.new)
94 assert vector_current == pytest.approx(qty.current)
96 def test_constructor_quantity(self, vector_current):
97 qty_a = Quantity(vector_current)
98 qty_b = Quantity(qty_a)
100 assert qty_a is not qty_b
101 assert qty_a.current == pytest.approx(qty_b.current)
102 assert qty_a.new == pytest.approx(qty_b.new)
104 def test_set(self, scalar_current, scalar_new):
105 qty = Quantity(scalar_current)
107 with pytest.raises(AttributeError):
108 qty.current = scalar_new
110 with pytest.raises(AttributeError):
111 qty.new = scalar_new
113 def test_initialize_scalar(self, scalar_current, scalar_new):
114 qty = Quantity(scalar_current)
115 qty.initialize(scalar_new)
116 assert qty.new == pytest.approx(scalar_new)
117 assert qty.current == pytest.approx(scalar_new)
119 def test_initialize_vector(self, vector_current, vector_new):
120 qty = Quantity(vector_current)
121 qty.initialize(vector_new)
122 assert qty.new == pytest.approx(vector_new)
123 assert qty.current == pytest.approx(vector_new)
125 def test_initialize_wrong_size(self):
126 init_value = np.array([])
127 err_msg = str.format("Quantity size can not be 0. Got: {0}", init_value)
128 with pytest.raises(ValueError, match=re.escape(err_msg)):
129 Quantity(init_value)
131 def test_update_scalar(self, scalar_current, scalar_new):
132 qty = Quantity(scalar_current)
133 qty.update(scalar_new)
134 assert qty.new == pytest.approx(scalar_new)
135 assert qty.current == pytest.approx(scalar_current)
137 def test_update_vector(self, vector_current, vector_new):
138 qty = Quantity(vector_current)
139 qty.update(vector_new)
140 assert qty.new == pytest.approx(vector_new)
141 assert qty.current == pytest.approx(vector_current)
143 def test_update_value_error(self, vector_current, scalar_new):
144 qty = Quantity(vector_current)
145 with pytest.raises(ValueError):
146 qty.update(scalar_new)
148 def test_eq(self):
149 qty_a = Quantity(0.1)
150 qty_b = Quantity(0.2)
151 qty_c = Quantity(0.1)
153 assert qty_a == pytest.approx(0.1)
154 assert qty_a == qty_c
155 assert qty_a != qty_b
156 assert qty_a != pytest.approx(0.2)
158 def test_compare(self):
159 qty_a = Quantity(0.1)
160 qty_b = Quantity(0.2)
161 qty_c = Quantity(0.1)
163 assert qty_a > 0.01
164 assert qty_a < 1.0
165 assert qty_a < qty_b
166 assert qty_b > qty_a
168 assert qty_a >= 0.01
169 assert qty_a <= 1.0
170 assert qty_a <= qty_b
171 assert qty_b >= qty_a
173 assert qty_a >= qty_c
174 assert qty_a >= 0.1
175 assert qty_a <= qty_c
176 assert qty_a <= 0.1
178 def test_sum(self):
179 qty_a = Quantity(0.1)
180 qty_b = Quantity(0.2)
182 assert qty_a + 0.1 == pytest.approx(0.2)
183 assert 0.1 + qty_a == pytest.approx(0.2)
184 assert qty_b + qty_a == pytest.approx(0.3)
186 test_is_qty = qty_a
187 qty_a += 0.1
188 assert qty_a == pytest.approx(0.2)
189 assert qty_a is test_is_qty
191 def test_sub(self):
192 qty_a = Quantity(0.1)
193 qty_b = Quantity(0.2)
195 assert qty_a - 0.1 == pytest.approx(0.0)
196 assert 0.2 - qty_a == pytest.approx(0.1)
197 assert qty_b - qty_a == pytest.approx(0.1)
199 test_is_qty = qty_a
200 qty_a -= 0.1
201 assert qty_a == pytest.approx(0.0)
202 assert qty_a is test_is_qty
204 def test_neg(self):
205 qty_a = Quantity(0.1)
206 assert -qty_a == pytest.approx(-0.1)
208 def test_mul(self):
209 qty_a = Quantity(0.1)
210 qty_b = Quantity(0.2)
212 assert qty_a * 0.2 == pytest.approx(0.02)
213 assert 0.2 * qty_a == pytest.approx(0.02)
214 assert qty_b * qty_a == pytest.approx(0.02)
216 test_is_qty = qty_a
217 qty_a *= 0.2
218 assert qty_a == pytest.approx(0.02)
219 assert qty_a is test_is_qty
221 def test_mat_mul(self):
222 vec_a = np.array([0.1, 0.2])
223 vec_b = np.array([0.3, 0.4])
224 qty_a = Quantity(vec_a)
225 qty_b = Quantity(vec_b)
227 assert np.array_equal(vec_a @ qty_a, vec_a @ vec_a)
228 assert np.array_equal(qty_a @ vec_b, vec_a @ vec_b)
229 assert np.array_equal(qty_a @ qty_b, vec_a @ vec_b)
231 test_is_qty = qty_a
232 qty_a @= vec_a
233 assert np.array_equal(qty_a, vec_a @ vec_a)
234 assert qty_a is test_is_qty
236 def test_truediv(self):
237 qty_a = Quantity(0.1)
238 qty_b = Quantity(0.2)
240 assert qty_a / 0.2 == pytest.approx(0.5)
241 assert 0.2 / qty_a == pytest.approx(2.0)
242 assert qty_b / qty_a == pytest.approx(2.0)
244 test_is_qty = qty_a
245 qty_a /= 0.2
246 assert qty_a == pytest.approx(0.5)
247 assert qty_a is test_is_qty
249 def test_floordiv(self):
250 qty_a = Quantity(5.4)
251 qty_b = Quantity(2.1)
253 assert qty_a // 2.5 == pytest.approx(2.0)
254 assert 12.0 // qty_a == pytest.approx(2.0)
255 assert qty_a // qty_b == pytest.approx(2.0)
257 test_is_qty = qty_a
258 qty_a //= 2.0
259 assert qty_a == pytest.approx(2.0)
260 assert qty_a is test_is_qty
262 def test_modulo(self):
263 qty_a = Quantity(1.1)
264 qty_b = Quantity(0.5)
266 assert qty_a % 0.5 == pytest.approx(0.1)
267 assert 2.3 % qty_a == pytest.approx(0.1)
268 assert qty_a % qty_b == pytest.approx(0.1)
270 test_is_qty = qty_a
271 qty_a %= 0.5
272 assert qty_a == pytest.approx(0.1)
273 assert qty_a is test_is_qty
275 def test_power(self):
276 qty_a = Quantity(1.1)
277 qty_b = Quantity(2.0)
279 assert qty_a**2.0 == pytest.approx(1.21)
280 assert 1.1**qty_b == pytest.approx(1.21)
281 assert qty_a**qty_b == pytest.approx(1.21)
283 test_is_qty = qty_a
284 qty_a **= 2.0
285 assert qty_a == pytest.approx(1.21)
286 assert qty_a is test_is_qty
289class TestDiff:
290 def test_diff_scalar(self, scalar_current, scalar_new):
291 qty = Quantity(scalar_current)
292 assert diff(qty) == pytest.approx(0.0)
294 qty.update(scalar_new)
295 assert diff(qty) == pytest.approx(0.1)
297 def test_diff_vector(self, vector_current, vector_new, vector_zero_reference):
298 qty = Quantity(vector_current)
299 assert diff(qty) == pytest.approx(vector_zero_reference)
301 qty.update(vector_new)
302 assert diff(qty) == pytest.approx([0.1, 0.1])
305class TestMidPoint:
306 def test_mid_point_scalar(self, scalar_current, scalar_new):
307 qty = Quantity(scalar_current)
308 assert mid_point(qty) == pytest.approx(0.1)
310 qty.update(scalar_new)
311 assert mid_point(qty) == pytest.approx(0.15)
313 def test_mid_point_vector(self, vector_current, vector_new):
314 qty = Quantity(vector_current)
315 assert mid_point(qty) == pytest.approx(vector_current)
317 qty.update(vector_new)
318 assert mid_point(qty) == pytest.approx([0.15, 0.25])
321class TestMidAlpha:
322 def test_mid_alpha_scalar(self, scalar_current, scalar_new, scalar_time_shift):
323 qty = Quantity(scalar_current)
324 assert mid_alpha(qty, scalar_time_shift) == pytest.approx(0.1)
326 qty.update(scalar_new)
327 assert mid_alpha(qty, scalar_time_shift) == pytest.approx(0.175)
329 def test_mid_alpha_vector(self, vector_current, vector_new, vector_time_shift):
330 qty = Quantity(vector_current)
331 assert mid_alpha(qty, vector_time_shift) == pytest.approx(vector_current)
333 qty.update(vector_new)
334 assert mid_alpha(qty, vector_time_shift) == pytest.approx([0.175, 0.26])
336 def test_mid_alpha_vector_ts_scalar(
337 self, vector_current, vector_new, scalar_time_shift
338 ):
339 qty = Quantity(vector_current)
340 assert mid_alpha(qty, scalar_time_shift) == pytest.approx(vector_current)
342 qty.update(vector_new)
343 assert mid_alpha(qty, scalar_time_shift) == pytest.approx([0.175, 0.275])
346class TestSignQuantity:
347 def test_sign_qty(self, scalar_current, scalar_new):
348 flux = Quantity(scalar_current)
349 flux.update(scalar_new)
350 assert sign(flux) == pytest.approx(1.0)
352 flux.update(-scalar_new)
353 assert sign(flux) == pytest.approx(-1.0)