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

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/>. 

26 

27import numpy as np 

28import pytest 

29import regex as re 

30 

31from physioblocks.computing.quantities import ( 

32 Quantity, 

33 diff, 

34 mid_alpha, 

35 mid_point, 

36 sign, 

37) 

38 

39 

40@pytest.fixture 

41def scalar_current(): 

42 return 0.1 

43 

44 

45@pytest.fixture 

46def scalar_new(): 

47 return 0.2 

48 

49 

50@pytest.fixture 

51def vector_current(): 

52 return np.array([0.1, 0.2]) 

53 

54 

55@pytest.fixture 

56def vector_new(): 

57 return np.array([0.2, 0.3]) 

58 

59 

60@pytest.fixture 

61def scalar_zero_reference(): 

62 return 0.0 

63 

64 

65@pytest.fixture 

66def vector_zero_reference(): 

67 return np.zeros( 

68 2, 

69 ) 

70 

71 

72@pytest.fixture 

73def scalar_time_shift(): 

74 return 0.25 

75 

76 

77@pytest.fixture 

78def vector_time_shift(): 

79 return np.array( 

80 [0.25, 0.1], 

81 ) 

82 

83 

84class TestQuantity: 

85 def test_constructor_scalar(self, scalar_current): 

86 qty = Quantity(scalar_current) 

87 

88 assert qty.current == pytest.approx(scalar_current) 

89 assert qty.new == pytest.approx(scalar_current) 

90 

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) 

95 

96 def test_constructor_quantity(self, vector_current): 

97 qty_a = Quantity(vector_current) 

98 qty_b = Quantity(qty_a) 

99 

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) 

103 

104 def test_set(self, scalar_current, scalar_new): 

105 qty = Quantity(scalar_current) 

106 

107 with pytest.raises(AttributeError): 

108 qty.current = scalar_new 

109 

110 with pytest.raises(AttributeError): 

111 qty.new = scalar_new 

112 

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) 

118 

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) 

124 

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) 

130 

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) 

136 

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) 

142 

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) 

147 

148 def test_eq(self): 

149 qty_a = Quantity(0.1) 

150 qty_b = Quantity(0.2) 

151 qty_c = Quantity(0.1) 

152 

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) 

157 

158 def test_compare(self): 

159 qty_a = Quantity(0.1) 

160 qty_b = Quantity(0.2) 

161 qty_c = Quantity(0.1) 

162 

163 assert qty_a > 0.01 

164 assert qty_a < 1.0 

165 assert qty_a < qty_b 

166 assert qty_b > qty_a 

167 

168 assert qty_a >= 0.01 

169 assert qty_a <= 1.0 

170 assert qty_a <= qty_b 

171 assert qty_b >= qty_a 

172 

173 assert qty_a >= qty_c 

174 assert qty_a >= 0.1 

175 assert qty_a <= qty_c 

176 assert qty_a <= 0.1 

177 

178 def test_sum(self): 

179 qty_a = Quantity(0.1) 

180 qty_b = Quantity(0.2) 

181 

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) 

185 

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 

190 

191 def test_sub(self): 

192 qty_a = Quantity(0.1) 

193 qty_b = Quantity(0.2) 

194 

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) 

198 

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 

203 

204 def test_neg(self): 

205 qty_a = Quantity(0.1) 

206 assert -qty_a == pytest.approx(-0.1) 

207 

208 def test_mul(self): 

209 qty_a = Quantity(0.1) 

210 qty_b = Quantity(0.2) 

211 

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) 

215 

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 

220 

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) 

226 

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) 

230 

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 

235 

236 def test_truediv(self): 

237 qty_a = Quantity(0.1) 

238 qty_b = Quantity(0.2) 

239 

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) 

243 

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 

248 

249 def test_floordiv(self): 

250 qty_a = Quantity(5.4) 

251 qty_b = Quantity(2.1) 

252 

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) 

256 

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 

261 

262 def test_modulo(self): 

263 qty_a = Quantity(1.1) 

264 qty_b = Quantity(0.5) 

265 

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) 

269 

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 

274 

275 def test_power(self): 

276 qty_a = Quantity(1.1) 

277 qty_b = Quantity(2.0) 

278 

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) 

282 

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 

287 

288 

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) 

293 

294 qty.update(scalar_new) 

295 assert diff(qty) == pytest.approx(0.1) 

296 

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) 

300 

301 qty.update(vector_new) 

302 assert diff(qty) == pytest.approx([0.1, 0.1]) 

303 

304 

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) 

309 

310 qty.update(scalar_new) 

311 assert mid_point(qty) == pytest.approx(0.15) 

312 

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) 

316 

317 qty.update(vector_new) 

318 assert mid_point(qty) == pytest.approx([0.15, 0.25]) 

319 

320 

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) 

325 

326 qty.update(scalar_new) 

327 assert mid_alpha(qty, scalar_time_shift) == pytest.approx(0.175) 

328 

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) 

332 

333 qty.update(vector_new) 

334 assert mid_alpha(qty, vector_time_shift) == pytest.approx([0.175, 0.26]) 

335 

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) 

341 

342 qty.update(vector_new) 

343 assert mid_alpha(qty, scalar_time_shift) == pytest.approx([0.175, 0.275]) 

344 

345 

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) 

351 

352 flux.update(-scalar_new) 

353 assert sign(flux) == pytest.approx(-1.0)