1import copy
2import operator as op
3import pickle
4import warnings
5
6import pytest
7
8from pint import DimensionalityError, OffsetUnitCalculusError, UnitStrippedWarning
9from pint.compat import np
10from pint.testsuite import helpers
11from pint.testsuite.test_umath import TestUFuncs
12
13
14@helpers.requires_numpy
15class TestNumpyMethods:
16    @classmethod
17    def setup_class(cls):
18        from pint import _DEFAULT_REGISTRY
19
20        cls.ureg = _DEFAULT_REGISTRY
21        cls.Q_ = cls.ureg.Quantity
22
23    @classmethod
24    def teardown_class(cls):
25        cls.ureg = None
26        cls.Q_ = None
27
28    @property
29    def q(self):
30        return [[1, 2], [3, 4]] * self.ureg.m
31
32    @property
33    def q_scalar(self):
34        return np.array(5) * self.ureg.m
35
36    @property
37    def q_nan(self):
38        return [[1, 2], [3, np.nan]] * self.ureg.m
39
40    @property
41    def q_zero_or_nan(self):
42        return [[0, 0], [0, np.nan]] * self.ureg.m
43
44    @property
45    def q_temperature(self):
46        return self.Q_([[1, 2], [3, 4]], self.ureg.degC)
47
48    def assertNDArrayEqual(self, actual, desired):
49        # Assert that the given arrays are equal, and are not Quantities
50        np.testing.assert_array_equal(actual, desired)
51        assert not isinstance(actual, self.Q_)
52        assert not isinstance(desired, self.Q_)
53
54
55class TestNumpyArrayCreation(TestNumpyMethods):
56    # https://docs.scipy.org/doc/numpy/reference/routines.array-creation.html
57
58    @helpers.requires_array_function_protocol()
59    def test_ones_like(self):
60        self.assertNDArrayEqual(np.ones_like(self.q), np.array([[1, 1], [1, 1]]))
61
62    @helpers.requires_array_function_protocol()
63    def test_zeros_like(self):
64        self.assertNDArrayEqual(np.zeros_like(self.q), np.array([[0, 0], [0, 0]]))
65
66    @helpers.requires_array_function_protocol()
67    def test_empty_like(self):
68        ret = np.empty_like(self.q)
69        assert ret.shape == (2, 2)
70        assert isinstance(ret, np.ndarray)
71
72    @helpers.requires_array_function_protocol()
73    def test_full_like(self):
74        helpers.assert_quantity_equal(
75            np.full_like(self.q, self.Q_(0, self.ureg.degC)),
76            self.Q_([[0, 0], [0, 0]], self.ureg.degC),
77        )
78        self.assertNDArrayEqual(np.full_like(self.q, 2), np.array([[2, 2], [2, 2]]))
79
80
81class TestNumpyArrayManipulation(TestNumpyMethods):
82    # TODO
83    # https://www.numpy.org/devdocs/reference/routines.array-manipulation.html
84    # copyto
85    # broadcast , broadcast_arrays
86    # asarray	asanyarray	asmatrix	asfarray	asfortranarray	ascontiguousarray	asarray_chkfinite	asscalar	require
87
88    # Changing array shape
89
90    def test_flatten(self):
91        helpers.assert_quantity_equal(self.q.flatten(), [1, 2, 3, 4] * self.ureg.m)
92
93    def test_flat(self):
94        for q, v in zip(self.q.flat, [1, 2, 3, 4]):
95            assert q == v * self.ureg.m
96
97    def test_reshape(self):
98        helpers.assert_quantity_equal(
99            self.q.reshape([1, 4]), [[1, 2, 3, 4]] * self.ureg.m
100        )
101
102    def test_ravel(self):
103        helpers.assert_quantity_equal(self.q.ravel(), [1, 2, 3, 4] * self.ureg.m)
104
105    @helpers.requires_array_function_protocol()
106    def test_ravel_numpy_func(self):
107        helpers.assert_quantity_equal(np.ravel(self.q), [1, 2, 3, 4] * self.ureg.m)
108
109    # Transpose-like operations
110
111    @helpers.requires_array_function_protocol()
112    def test_moveaxis(self):
113        helpers.assert_quantity_equal(
114            np.moveaxis(self.q, 1, 0), np.array([[1, 2], [3, 4]]).T * self.ureg.m
115        )
116
117    @helpers.requires_array_function_protocol()
118    def test_rollaxis(self):
119        helpers.assert_quantity_equal(
120            np.rollaxis(self.q, 1), np.array([[1, 2], [3, 4]]).T * self.ureg.m
121        )
122
123    @helpers.requires_array_function_protocol()
124    def test_swapaxes(self):
125        helpers.assert_quantity_equal(
126            np.swapaxes(self.q, 1, 0), np.array([[1, 2], [3, 4]]).T * self.ureg.m
127        )
128
129    def test_transpose(self):
130        helpers.assert_quantity_equal(
131            self.q.transpose(), [[1, 3], [2, 4]] * self.ureg.m
132        )
133
134    @helpers.requires_array_function_protocol()
135    def test_transpose_numpy_func(self):
136        helpers.assert_quantity_equal(
137            np.transpose(self.q), [[1, 3], [2, 4]] * self.ureg.m
138        )
139
140    @helpers.requires_array_function_protocol()
141    def test_flip_numpy_func(self):
142        helpers.assert_quantity_equal(
143            np.flip(self.q, axis=0), [[3, 4], [1, 2]] * self.ureg.m
144        )
145
146    # Changing number of dimensions
147
148    @helpers.requires_array_function_protocol()
149    def test_atleast_1d(self):
150        actual = np.atleast_1d(self.Q_(0, self.ureg.degC), self.q.flatten())
151        expected = (self.Q_(np.array([0]), self.ureg.degC), self.q.flatten())
152        for ind_actual, ind_expected in zip(actual, expected):
153            helpers.assert_quantity_equal(ind_actual, ind_expected)
154        helpers.assert_quantity_equal(np.atleast_1d(self.q), self.q)
155
156    @helpers.requires_array_function_protocol()
157    def test_atleast_2d(self):
158        actual = np.atleast_2d(self.Q_(0, self.ureg.degC), self.q.flatten())
159        expected = (
160            self.Q_(np.array([[0]]), self.ureg.degC),
161            np.array([[1, 2, 3, 4]]) * self.ureg.m,
162        )
163        for ind_actual, ind_expected in zip(actual, expected):
164            helpers.assert_quantity_equal(ind_actual, ind_expected)
165        helpers.assert_quantity_equal(np.atleast_2d(self.q), self.q)
166
167    @helpers.requires_array_function_protocol()
168    def test_atleast_3d(self):
169        actual = np.atleast_3d(self.Q_(0, self.ureg.degC), self.q.flatten())
170        expected = (
171            self.Q_(np.array([[[0]]]), self.ureg.degC),
172            np.array([[[1], [2], [3], [4]]]) * self.ureg.m,
173        )
174        for ind_actual, ind_expected in zip(actual, expected):
175            helpers.assert_quantity_equal(ind_actual, ind_expected)
176        helpers.assert_quantity_equal(
177            np.atleast_3d(self.q), np.array([[[1], [2]], [[3], [4]]]) * self.ureg.m
178        )
179
180    @helpers.requires_array_function_protocol()
181    def test_broadcast_to(self):
182        helpers.assert_quantity_equal(
183            np.broadcast_to(self.q[:, 1], (2, 2)),
184            np.array([[2, 4], [2, 4]]) * self.ureg.m,
185        )
186
187    @helpers.requires_array_function_protocol()
188    def test_expand_dims(self):
189        helpers.assert_quantity_equal(
190            np.expand_dims(self.q, 0), np.array([[[1, 2], [3, 4]]]) * self.ureg.m
191        )
192
193    @helpers.requires_array_function_protocol()
194    def test_squeeze(self):
195        helpers.assert_quantity_equal(np.squeeze(self.q), self.q)
196        helpers.assert_quantity_equal(
197            self.q.reshape([1, 4]).squeeze(), [1, 2, 3, 4] * self.ureg.m
198        )
199
200    # Changing number of dimensions
201    # Joining arrays
202    @helpers.requires_array_function_protocol()
203    def test_concat_stack(self, subtests):
204        for func in (np.concatenate, np.stack, np.hstack, np.vstack, np.dstack):
205            with subtests.test(func=func):
206                helpers.assert_quantity_equal(
207                    func([self.q] * 2), self.Q_(func([self.q.m] * 2), self.ureg.m)
208                )
209                # One or more of the args is a bare array full of zeros or NaNs
210                helpers.assert_quantity_equal(
211                    func([self.q_zero_or_nan.m, self.q]),
212                    self.Q_(func([self.q_zero_or_nan.m, self.q.m]), self.ureg.m),
213                )
214                # One or more of the args is a bare array with at least one non-zero,
215                # non-NaN element
216                nz = self.q_zero_or_nan
217                nz.m[0, 0] = 1
218                with pytest.raises(DimensionalityError):
219                    func([nz.m, self.q])
220
221    @helpers.requires_array_function_protocol()
222    def test_block_column_stack(self, subtests):
223        for func in (np.block, np.column_stack):
224            with subtests.test(func=func):
225
226                helpers.assert_quantity_equal(
227                    func([self.q[:, 0], self.q[:, 1]]),
228                    self.Q_(func([self.q[:, 0].m, self.q[:, 1].m]), self.ureg.m),
229                )
230
231                # One or more of the args is a bare array full of zeros or NaNs
232                helpers.assert_quantity_equal(
233                    func(
234                        [
235                            self.q_zero_or_nan[:, 0].m,
236                            self.q[:, 0],
237                            self.q_zero_or_nan[:, 1].m,
238                        ]
239                    ),
240                    self.Q_(
241                        func(
242                            [
243                                self.q_zero_or_nan[:, 0].m,
244                                self.q[:, 0].m,
245                                self.q_zero_or_nan[:, 1].m,
246                            ]
247                        ),
248                        self.ureg.m,
249                    ),
250                )
251                # One or more of the args is a bare array with at least one non-zero,
252                # non-NaN element
253                nz = self.q_zero_or_nan
254                nz.m[0, 0] = 1
255                with pytest.raises(DimensionalityError):
256                    func([nz[:, 0].m, self.q[:, 0]])
257
258    @helpers.requires_array_function_protocol()
259    def test_append(self):
260        helpers.assert_quantity_equal(
261            np.append(self.q, [[0, 0]] * self.ureg.m, axis=0),
262            [[1, 2], [3, 4], [0, 0]] * self.ureg.m,
263        )
264
265    def test_astype(self):
266        actual = self.q.astype(np.float32)
267        expected = self.Q_(np.array([[1.0, 2.0], [3.0, 4.0]], dtype=np.float32), "m")
268        helpers.assert_quantity_equal(actual, expected)
269        assert actual.m.dtype == expected.m.dtype
270
271    def test_item(self):
272        helpers.assert_quantity_equal(self.Q_([[0]], "m").item(), 0 * self.ureg.m)
273
274
275class TestNumpyMathematicalFunctions(TestNumpyMethods):
276    # https://www.numpy.org/devdocs/reference/routines.math.html
277    # Trigonometric functions
278    @helpers.requires_array_function_protocol()
279    def test_unwrap(self):
280        helpers.assert_quantity_equal(
281            np.unwrap([0, 3 * np.pi] * self.ureg.radians), [0, np.pi]
282        )
283        helpers.assert_quantity_equal(
284            np.unwrap([0, 540] * self.ureg.deg), [0, 180] * self.ureg.deg
285        )
286
287    # Rounding
288
289    @helpers.requires_array_function_protocol()
290    def test_fix(self):
291        helpers.assert_quantity_equal(np.fix(3.14 * self.ureg.m), 3.0 * self.ureg.m)
292        helpers.assert_quantity_equal(np.fix(3.0 * self.ureg.m), 3.0 * self.ureg.m)
293        helpers.assert_quantity_equal(
294            np.fix([2.1, 2.9, -2.1, -2.9] * self.ureg.m),
295            [2.0, 2.0, -2.0, -2.0] * self.ureg.m,
296        )
297
298    # Sums, products, differences
299
300    @helpers.requires_array_function_protocol()
301    def test_prod(self):
302        axis = 0
303        where = [[True, False], [True, True]]
304
305        helpers.assert_quantity_equal(self.q.prod(), 24 * self.ureg.m ** 4)
306        helpers.assert_quantity_equal(self.q.prod(axis=axis), [3, 8] * self.ureg.m ** 2)
307        helpers.assert_quantity_equal(self.q.prod(where=where), 12 * self.ureg.m ** 3)
308
309    @helpers.requires_array_function_protocol()
310    def test_prod_numpy_func(self):
311        axis = 0
312        where = [[True, False], [True, True]]
313
314        helpers.assert_quantity_equal(np.prod(self.q), 24 * self.ureg.m ** 4)
315        helpers.assert_quantity_equal(
316            np.prod(self.q, axis=axis), [3, 8] * self.ureg.m ** 2
317        )
318        helpers.assert_quantity_equal(
319            np.prod(self.q, where=where), 12 * self.ureg.m ** 3
320        )
321
322        with pytest.raises(DimensionalityError):
323            np.prod(self.q, axis=axis, where=where)
324        helpers.assert_quantity_equal(
325            np.prod(self.q, axis=axis, where=[[True, False], [False, True]]),
326            [1, 4] * self.ureg.m,
327        )
328        helpers.assert_quantity_equal(
329            np.prod(self.q, axis=axis, where=[True, False]), [3, 1] * self.ureg.m ** 2
330        )
331
332    def test_sum(self):
333        assert self.q.sum() == 10 * self.ureg.m
334        helpers.assert_quantity_equal(self.q.sum(0), [4, 6] * self.ureg.m)
335        helpers.assert_quantity_equal(self.q.sum(1), [3, 7] * self.ureg.m)
336
337    @helpers.requires_array_function_protocol()
338    def test_sum_numpy_func(self):
339        helpers.assert_quantity_equal(np.sum(self.q, axis=0), [4, 6] * self.ureg.m)
340        with pytest.raises(OffsetUnitCalculusError):
341            np.sum(self.q_temperature)
342
343    @helpers.requires_array_function_protocol()
344    def test_nansum_numpy_func(self):
345        helpers.assert_quantity_equal(
346            np.nansum(self.q_nan, axis=0), [4, 2] * self.ureg.m
347        )
348
349    def test_cumprod(self):
350        with pytest.raises(DimensionalityError):
351            self.q.cumprod()
352        helpers.assert_quantity_equal((self.q / self.ureg.m).cumprod(), [1, 2, 6, 24])
353
354    @helpers.requires_array_function_protocol()
355    def test_cumprod_numpy_func(self):
356        with pytest.raises(DimensionalityError):
357            np.cumprod(self.q)
358        with pytest.raises(DimensionalityError):
359            np.cumproduct(self.q)
360        helpers.assert_quantity_equal(np.cumprod(self.q / self.ureg.m), [1, 2, 6, 24])
361        helpers.assert_quantity_equal(
362            np.cumproduct(self.q / self.ureg.m), [1, 2, 6, 24]
363        )
364        helpers.assert_quantity_equal(
365            np.cumprod(self.q / self.ureg.m, axis=1), [[1, 2], [3, 12]]
366        )
367
368    @helpers.requires_array_function_protocol()
369    def test_nancumprod_numpy_func(self):
370        with pytest.raises(DimensionalityError):
371            np.nancumprod(self.q_nan)
372        helpers.assert_quantity_equal(
373            np.nancumprod(self.q_nan / self.ureg.m), [1, 2, 6, 6]
374        )
375
376    @helpers.requires_array_function_protocol()
377    def test_diff(self):
378        helpers.assert_quantity_equal(np.diff(self.q, 1), [[1], [1]] * self.ureg.m)
379        helpers.assert_quantity_equal(
380            np.diff(self.q_temperature, 1), [[1], [1]] * self.ureg.delta_degC
381        )
382
383    @helpers.requires_array_function_protocol()
384    def test_ediff1d(self):
385        helpers.assert_quantity_equal(np.ediff1d(self.q), [1, 1, 1] * self.ureg.m)
386        helpers.assert_quantity_equal(
387            np.ediff1d(self.q_temperature), [1, 1, 1] * self.ureg.delta_degC
388        )
389
390    @helpers.requires_array_function_protocol()
391    def test_gradient(self):
392        grad = np.gradient([[1, 1], [3, 4]] * self.ureg.m, 1 * self.ureg.J)
393        helpers.assert_quantity_equal(
394            grad[0], [[2.0, 3.0], [2.0, 3.0]] * self.ureg.m / self.ureg.J
395        )
396        helpers.assert_quantity_equal(
397            grad[1], [[0.0, 0.0], [1.0, 1.0]] * self.ureg.m / self.ureg.J
398        )
399
400        grad = np.gradient(self.Q_([[1, 1], [3, 4]], self.ureg.degC), 1 * self.ureg.J)
401        helpers.assert_quantity_equal(
402            grad[0], [[2.0, 3.0], [2.0, 3.0]] * self.ureg.delta_degC / self.ureg.J
403        )
404        helpers.assert_quantity_equal(
405            grad[1], [[0.0, 0.0], [1.0, 1.0]] * self.ureg.delta_degC / self.ureg.J
406        )
407
408    @helpers.requires_array_function_protocol()
409    def test_cross(self):
410        a = [[3, -3, 1]] * self.ureg.kPa
411        b = [[4, 9, 2]] * self.ureg.m ** 2
412        helpers.assert_quantity_equal(
413            np.cross(a, b), [[-15, -2, 39]] * self.ureg.kPa * self.ureg.m ** 2
414        )
415
416    @helpers.requires_array_function_protocol()
417    def test_trapz(self):
418        helpers.assert_quantity_equal(
419            np.trapz([1.0, 2.0, 3.0, 4.0] * self.ureg.J, dx=1 * self.ureg.m),
420            7.5 * self.ureg.J * self.ureg.m,
421        )
422
423    @helpers.requires_array_function_protocol()
424    def test_dot(self):
425        helpers.assert_quantity_equal(
426            self.q.ravel().dot(np.array([1, 0, 0, 1])), 5 * self.ureg.m
427        )
428
429    @helpers.requires_array_function_protocol()
430    def test_dot_numpy_func(self):
431        helpers.assert_quantity_equal(
432            np.dot(self.q.ravel(), [0, 0, 1, 0] * self.ureg.dimensionless),
433            3 * self.ureg.m,
434        )
435
436    @helpers.requires_array_function_protocol()
437    def test_einsum(self):
438        a = np.arange(25).reshape(5, 5) * self.ureg.m
439        b = np.arange(5) * self.ureg.m
440        helpers.assert_quantity_equal(np.einsum("ii", a), 60 * self.ureg.m)
441        helpers.assert_quantity_equal(
442            np.einsum("ii->i", a), np.array([0, 6, 12, 18, 24]) * self.ureg.m
443        )
444        helpers.assert_quantity_equal(np.einsum("i,i", b, b), 30 * self.ureg.m ** 2)
445        helpers.assert_quantity_equal(
446            np.einsum("ij,j", a, b),
447            np.array([30, 80, 130, 180, 230]) * self.ureg.m ** 2,
448        )
449
450    @helpers.requires_array_function_protocol()
451    def test_solve(self):
452        A = self.q
453        b = [[3], [7]] * self.ureg.s
454        x = np.linalg.solve(A, b)
455
456        helpers.assert_quantity_almost_equal(x, self.Q_([[1], [1]], "s / m"))
457
458        helpers.assert_quantity_almost_equal(np.dot(A, x), b)
459
460    # Arithmetic operations
461    def test_addition_with_scalar(self):
462        a = np.array([0, 1, 2])
463        b = 10.0 * self.ureg("gram/kilogram")
464        helpers.assert_quantity_almost_equal(
465            a + b, self.Q_([0.01, 1.01, 2.01], self.ureg.dimensionless)
466        )
467        helpers.assert_quantity_almost_equal(
468            b + a, self.Q_([0.01, 1.01, 2.01], self.ureg.dimensionless)
469        )
470
471    def test_addition_with_incompatible_scalar(self):
472        a = np.array([0, 1, 2])
473        b = 1.0 * self.ureg.m
474        with pytest.raises(DimensionalityError):
475            op.add(a, b)
476        with pytest.raises(DimensionalityError):
477            op.add(b, a)
478
479    def test_power(self):
480        arr = np.array(range(3), dtype=np.float)
481        q = self.Q_(arr, "meter")
482
483        for op_ in [op.pow, op.ipow, np.power]:
484            q_cp = copy.copy(q)
485            with pytest.raises(DimensionalityError):
486                op_(2.0, q_cp)
487            arr_cp = copy.copy(arr)
488            arr_cp = copy.copy(arr)
489            q_cp = copy.copy(q)
490            with pytest.raises(DimensionalityError):
491                op_(q_cp, arr_cp)
492            q_cp = copy.copy(q)
493            q2_cp = copy.copy(q)
494            with pytest.raises(DimensionalityError):
495                op_(q_cp, q2_cp)
496
497        helpers.assert_quantity_equal(
498            np.power(self.q, self.Q_(2)), self.Q_([[1, 4], [9, 16]], "m**2")
499        )
500        helpers.assert_quantity_equal(
501            self.q ** self.Q_(2), self.Q_([[1, 4], [9, 16]], "m**2")
502        )
503        self.assertNDArrayEqual(arr ** self.Q_(2), np.array([0, 1, 4]))
504
505    def test_sqrt(self):
506        q = self.Q_(100, "m**2")
507        helpers.assert_quantity_equal(np.sqrt(q), self.Q_(10, "m"))
508
509    def test_cbrt(self):
510        q = self.Q_(1000, "m**3")
511        helpers.assert_quantity_equal(np.cbrt(q), self.Q_(10, "m"))
512
513    @pytest.mark.xfail
514    @helpers.requires_numpy
515    def test_exponentiation_array_exp_2(self):
516        arr = np.array(range(3), dtype=np.float)
517        # q = self.Q_(copy.copy(arr), None)
518        q = self.Q_(copy.copy(arr), "meter")
519        arr_cp = copy.copy(arr)
520        q_cp = copy.copy(q)
521        # this fails as expected since numpy 1.8.0 but...
522        with pytest.raises(DimensionalityError):
523            op.pow(arr_cp, q_cp)
524        # ..not for op.ipow !
525        # q_cp is treated as if it is an array. The units are ignored.
526        # Quantity.__ipow__ is never called
527        arr_cp = copy.copy(arr)
528        q_cp = copy.copy(q)
529        with pytest.raises(DimensionalityError):
530            op.ipow(arr_cp, q_cp)
531
532
533class TestNumpyUnclassified(TestNumpyMethods):
534    def test_tolist(self):
535        with pytest.raises(AttributeError):
536            (5 * self.ureg.m).tolist()
537
538        assert self.q.tolist() == [
539            [1 * self.ureg.m, 2 * self.ureg.m],
540            [3 * self.ureg.m, 4 * self.ureg.m],
541        ]
542
543    def test_fill(self):
544        tmp = self.q
545        tmp.fill(6 * self.ureg.ft)
546        helpers.assert_quantity_equal(tmp, [[6, 6], [6, 6]] * self.ureg.ft)
547        tmp.fill(5 * self.ureg.m)
548        helpers.assert_quantity_equal(tmp, [[5, 5], [5, 5]] * self.ureg.m)
549
550    def test_take(self):
551        helpers.assert_quantity_equal(self.q.take([0, 1, 2, 3]), self.q.flatten())
552
553    def test_put(self):
554        q = [1.0, 2.0, 3.0, 4.0] * self.ureg.m
555        q.put([0, 2], [10.0, 20.0] * self.ureg.m)
556        helpers.assert_quantity_equal(q, [10.0, 2.0, 20.0, 4.0] * self.ureg.m)
557
558        q = [1.0, 2.0, 3.0, 4.0] * self.ureg.m
559        q.put([0, 2], [1.0, 2.0] * self.ureg.mm)
560        helpers.assert_quantity_equal(q, [0.001, 2.0, 0.002, 4.0] * self.ureg.m)
561
562        q = [1.0, 2.0, 3.0, 4.0] * self.ureg.m / self.ureg.mm
563        q.put([0, 2], [1.0, 2.0])
564        helpers.assert_quantity_equal(
565            q, [0.001, 2.0, 0.002, 4.0] * self.ureg.m / self.ureg.mm
566        )
567
568        q = [1.0, 2.0, 3.0, 4.0] * self.ureg.m
569        with pytest.raises(DimensionalityError):
570            q.put([0, 2], [4.0, 6.0] * self.ureg.J)
571        with pytest.raises(DimensionalityError):
572            q.put([0, 2], [4.0, 6.0])
573
574    def test_repeat(self):
575        helpers.assert_quantity_equal(
576            self.q.repeat(2), [1, 1, 2, 2, 3, 3, 4, 4] * self.ureg.m
577        )
578
579    def test_sort(self):
580        q = [4, 5, 2, 3, 1, 6] * self.ureg.m
581        q.sort()
582        helpers.assert_quantity_equal(q, [1, 2, 3, 4, 5, 6] * self.ureg.m)
583
584    @helpers.requires_array_function_protocol()
585    def test_sort_numpy_func(self):
586        q = [4, 5, 2, 3, 1, 6] * self.ureg.m
587        helpers.assert_quantity_equal(np.sort(q), [1, 2, 3, 4, 5, 6] * self.ureg.m)
588
589    def test_argsort(self):
590        q = [1, 4, 5, 6, 2, 9] * self.ureg.MeV
591        self.assertNDArrayEqual(q.argsort(), [0, 4, 1, 2, 3, 5])
592
593    @helpers.requires_array_function_protocol()
594    def test_argsort_numpy_func(self):
595        self.assertNDArrayEqual(np.argsort(self.q, axis=0), np.array([[0, 0], [1, 1]]))
596
597    def test_diagonal(self):
598        q = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] * self.ureg.m
599        helpers.assert_quantity_equal(q.diagonal(offset=1), [2, 3] * self.ureg.m)
600
601    @helpers.requires_array_function_protocol()
602    def test_diagonal_numpy_func(self):
603        q = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] * self.ureg.m
604        helpers.assert_quantity_equal(np.diagonal(q, offset=-1), [1, 2] * self.ureg.m)
605
606    def test_compress(self):
607        helpers.assert_quantity_equal(
608            self.q.compress([False, True], axis=0), [[3, 4]] * self.ureg.m
609        )
610        helpers.assert_quantity_equal(
611            self.q.compress([False, True], axis=1), [[2], [4]] * self.ureg.m
612        )
613
614    @helpers.requires_array_function_protocol()
615    def test_compress_nep18(self):
616        helpers.assert_quantity_equal(
617            np.compress([False, True], self.q, axis=1), [[2], [4]] * self.ureg.m
618        )
619
620    def test_searchsorted(self):
621        q = self.q.flatten()
622        self.assertNDArrayEqual(q.searchsorted([1.5, 2.5] * self.ureg.m), [1, 2])
623        q = self.q.flatten()
624        with pytest.raises(DimensionalityError):
625            q.searchsorted([1.5, 2.5])
626
627    @helpers.requires_array_function_protocol()
628    def test_searchsorted_numpy_func(self):
629        """Test searchsorted as numpy function."""
630        q = self.q.flatten()
631        self.assertNDArrayEqual(np.searchsorted(q, [1.5, 2.5] * self.ureg.m), [1, 2])
632
633    def test_nonzero(self):
634        q = [1, 0, 5, 6, 0, 9] * self.ureg.m
635        self.assertNDArrayEqual(q.nonzero()[0], [0, 2, 3, 5])
636
637    @helpers.requires_array_function_protocol()
638    def test_nonzero_numpy_func(self):
639        q = [1, 0, 5, 6, 0, 9] * self.ureg.m
640        self.assertNDArrayEqual(np.nonzero(q)[0], [0, 2, 3, 5])
641
642    @helpers.requires_array_function_protocol()
643    def test_any_numpy_func(self):
644        q = [0, 1] * self.ureg.m
645        assert np.any(q)
646        with pytest.raises(ValueError):
647            np.any(self.q_temperature)
648
649    @helpers.requires_array_function_protocol()
650    def test_all_numpy_func(self):
651        q = [0, 1] * self.ureg.m
652        assert not np.all(q)
653        with pytest.raises(ValueError):
654            np.all(self.q_temperature)
655
656    @helpers.requires_array_function_protocol()
657    def test_count_nonzero_numpy_func(self):
658        q = [1, 0, 5, 6, 0, 9] * self.ureg.m
659        assert np.count_nonzero(q) == 4
660
661    def test_max(self):
662        assert self.q.max() == 4 * self.ureg.m
663
664    def test_max_numpy_func(self):
665        assert np.max(self.q) == 4 * self.ureg.m
666
667    @helpers.requires_array_function_protocol()
668    def test_max_with_axis_arg(self):
669        helpers.assert_quantity_equal(np.max(self.q, axis=1), [2, 4] * self.ureg.m)
670
671    @helpers.requires_array_function_protocol()
672    def test_max_with_initial_arg(self):
673        helpers.assert_quantity_equal(
674            np.max(self.q[..., None], axis=2, initial=3 * self.ureg.m),
675            [[3, 3], [3, 4]] * self.ureg.m,
676        )
677
678    @helpers.requires_array_function_protocol()
679    def test_nanmax(self):
680        assert np.nanmax(self.q_nan) == 3 * self.ureg.m
681
682    def test_argmax(self):
683        assert self.q.argmax() == 3
684
685    @helpers.requires_array_function_protocol()
686    def test_argmax_numpy_func(self):
687        self.assertNDArrayEqual(np.argmax(self.q, axis=0), np.array([1, 1]))
688
689    @helpers.requires_array_function_protocol()
690    def test_nanargmax_numpy_func(self):
691        self.assertNDArrayEqual(np.nanargmax(self.q_nan, axis=0), np.array([1, 0]))
692
693    def test_maximum(self):
694        helpers.assert_quantity_equal(
695            np.maximum(self.q, self.Q_([0, 5], "m")), self.Q_([[1, 5], [3, 5]], "m")
696        )
697
698    def test_min(self):
699        assert self.q.min() == 1 * self.ureg.m
700
701    @helpers.requires_array_function_protocol()
702    def test_min_numpy_func(self):
703        assert np.min(self.q) == 1 * self.ureg.m
704
705    @helpers.requires_array_function_protocol()
706    def test_min_with_axis_arg(self):
707        helpers.assert_quantity_equal(np.min(self.q, axis=1), [1, 3] * self.ureg.m)
708
709    @helpers.requires_array_function_protocol()
710    def test_min_with_initial_arg(self):
711        helpers.assert_quantity_equal(
712            np.min(self.q[..., None], axis=2, initial=3 * self.ureg.m),
713            [[1, 2], [3, 3]] * self.ureg.m,
714        )
715
716    @helpers.requires_array_function_protocol()
717    def test_nanmin(self):
718        assert np.nanmin(self.q_nan) == 1 * self.ureg.m
719
720    def test_argmin(self):
721        assert self.q.argmin() == 0
722
723    @helpers.requires_array_function_protocol()
724    def test_argmin_numpy_func(self):
725        self.assertNDArrayEqual(np.argmin(self.q, axis=0), np.array([0, 0]))
726
727    @helpers.requires_array_function_protocol()
728    def test_nanargmin_numpy_func(self):
729        self.assertNDArrayEqual(np.nanargmin(self.q_nan, axis=0), np.array([0, 0]))
730
731    def test_minimum(self):
732        helpers.assert_quantity_equal(
733            np.minimum(self.q, self.Q_([0, 5], "m")), self.Q_([[0, 2], [0, 4]], "m")
734        )
735
736    def test_ptp(self):
737        assert self.q.ptp() == 3 * self.ureg.m
738
739    @helpers.requires_array_function_protocol()
740    def test_ptp_numpy_func(self):
741        helpers.assert_quantity_equal(np.ptp(self.q, axis=0), [2, 2] * self.ureg.m)
742
743    def test_clip(self):
744        helpers.assert_quantity_equal(
745            self.q.clip(max=2 * self.ureg.m), [[1, 2], [2, 2]] * self.ureg.m
746        )
747        helpers.assert_quantity_equal(
748            self.q.clip(min=3 * self.ureg.m), [[3, 3], [3, 4]] * self.ureg.m
749        )
750        helpers.assert_quantity_equal(
751            self.q.clip(min=2 * self.ureg.m, max=3 * self.ureg.m),
752            [[2, 2], [3, 3]] * self.ureg.m,
753        )
754        helpers.assert_quantity_equal(
755            self.q.clip(3 * self.ureg.m, None), [[3, 3], [3, 4]] * self.ureg.m
756        )
757        helpers.assert_quantity_equal(
758            self.q.clip(3 * self.ureg.m), [[3, 3], [3, 4]] * self.ureg.m
759        )
760        with pytest.raises(DimensionalityError):
761            self.q.clip(self.ureg.J)
762        with pytest.raises(DimensionalityError):
763            self.q.clip(1)
764
765    @helpers.requires_array_function_protocol()
766    def test_clip_numpy_func(self):
767        helpers.assert_quantity_equal(
768            np.clip(self.q, 150 * self.ureg.cm, None), [[1.5, 2], [3, 4]] * self.ureg.m
769        )
770
771    def test_round(self):
772        q = [1, 1.33, 5.67, 22] * self.ureg.m
773        helpers.assert_quantity_equal(q.round(0), [1, 1, 6, 22] * self.ureg.m)
774        helpers.assert_quantity_equal(q.round(-1), [0, 0, 10, 20] * self.ureg.m)
775        helpers.assert_quantity_equal(q.round(1), [1, 1.3, 5.7, 22] * self.ureg.m)
776
777    @helpers.requires_array_function_protocol()
778    def test_round_numpy_func(self):
779        helpers.assert_quantity_equal(
780            np.around(1.0275 * self.ureg.m, decimals=2), 1.03 * self.ureg.m
781        )
782        helpers.assert_quantity_equal(
783            np.round_(1.0275 * self.ureg.m, decimals=2), 1.03 * self.ureg.m
784        )
785
786    def test_trace(self):
787        assert self.q.trace() == (1 + 4) * self.ureg.m
788
789    def test_cumsum(self):
790        helpers.assert_quantity_equal(self.q.cumsum(), [1, 3, 6, 10] * self.ureg.m)
791
792    @helpers.requires_array_function_protocol()
793    def test_cumsum_numpy_func(self):
794        helpers.assert_quantity_equal(
795            np.cumsum(self.q, axis=0), [[1, 2], [4, 6]] * self.ureg.m
796        )
797
798    @helpers.requires_array_function_protocol()
799    def test_nancumsum_numpy_func(self):
800        helpers.assert_quantity_equal(
801            np.nancumsum(self.q_nan, axis=0), [[1, 2], [4, 2]] * self.ureg.m
802        )
803
804    def test_mean(self):
805        assert self.q.mean() == 2.5 * self.ureg.m
806
807    @helpers.requires_array_function_protocol()
808    def test_mean_numpy_func(self):
809        assert np.mean(self.q) == 2.5 * self.ureg.m
810        assert np.mean(self.q_temperature) == self.Q_(2.5, self.ureg.degC)
811
812    @helpers.requires_array_function_protocol()
813    def test_nanmean_numpy_func(self):
814        assert np.nanmean(self.q_nan) == 2 * self.ureg.m
815
816    @helpers.requires_array_function_protocol()
817    def test_average_numpy_func(self):
818        helpers.assert_quantity_almost_equal(
819            np.average(self.q, axis=0, weights=[1, 2]),
820            [2.33333, 3.33333] * self.ureg.m,
821            rtol=1e-5,
822        )
823
824    @helpers.requires_array_function_protocol()
825    def test_median_numpy_func(self):
826        assert np.median(self.q) == 2.5 * self.ureg.m
827
828    @helpers.requires_array_function_protocol()
829    def test_nanmedian_numpy_func(self):
830        assert np.nanmedian(self.q_nan) == 2 * self.ureg.m
831
832    def test_var(self):
833        assert self.q.var() == 1.25 * self.ureg.m ** 2
834
835    @helpers.requires_array_function_protocol()
836    def test_var_numpy_func(self):
837        assert np.var(self.q) == 1.25 * self.ureg.m ** 2
838
839    @helpers.requires_array_function_protocol()
840    def test_nanvar_numpy_func(self):
841        helpers.assert_quantity_almost_equal(
842            np.nanvar(self.q_nan), 0.66667 * self.ureg.m ** 2, rtol=1e-5
843        )
844
845    def test_std(self):
846        helpers.assert_quantity_almost_equal(
847            self.q.std(), 1.11803 * self.ureg.m, rtol=1e-5
848        )
849
850    @helpers.requires_array_function_protocol()
851    def test_std_numpy_func(self):
852        helpers.assert_quantity_almost_equal(
853            np.std(self.q), 1.11803 * self.ureg.m, rtol=1e-5
854        )
855        with pytest.raises(OffsetUnitCalculusError):
856            np.std(self.q_temperature)
857
858    def test_cumprod(self):
859        with pytest.raises(DimensionalityError):
860            self.q.cumprod()
861        helpers.assert_quantity_equal((self.q / self.ureg.m).cumprod(), [1, 2, 6, 24])
862
863    @helpers.requires_array_function_protocol()
864    def test_nanstd_numpy_func(self):
865        helpers.assert_quantity_almost_equal(
866            np.nanstd(self.q_nan), 0.81650 * self.ureg.m, rtol=1e-5
867        )
868
869    def test_conj(self):
870        helpers.assert_quantity_equal((self.q * (1 + 1j)).conj(), self.q * (1 - 1j))
871        helpers.assert_quantity_equal(
872            (self.q * (1 + 1j)).conjugate(), self.q * (1 - 1j)
873        )
874
875    def test_getitem(self):
876        with pytest.raises(IndexError):
877            self.q.__getitem__((0, 10))
878        helpers.assert_quantity_equal(self.q[0], [1, 2] * self.ureg.m)
879        assert self.q[1, 1] == 4 * self.ureg.m
880
881    def test_setitem(self):
882        with pytest.raises(TypeError):
883            self.q[0, 0] = 1
884        with pytest.raises(DimensionalityError):
885            self.q[0, 0] = 1 * self.ureg.J
886        with pytest.raises(DimensionalityError):
887            self.q[0] = 1
888        with pytest.raises(DimensionalityError):
889            self.q[0] = np.ndarray([1, 2])
890        with pytest.raises(DimensionalityError):
891            self.q[0] = 1 * self.ureg.J
892
893        q = self.q.copy()
894        q[0] = 1 * self.ureg.m
895        helpers.assert_quantity_equal(q, [[1, 1], [3, 4]] * self.ureg.m)
896
897        q = self.q.copy()
898        q[...] = 1 * self.ureg.m
899        helpers.assert_quantity_equal(q, [[1, 1], [1, 1]] * self.ureg.m)
900
901        q = self.q.copy()
902        q[:] = 1 * self.ureg.m
903        helpers.assert_quantity_equal(q, [[1, 1], [1, 1]] * self.ureg.m)
904
905        # check and see that dimensionless num  bers work correctly
906        q = [0, 1, 2, 3] * self.ureg.dimensionless
907        q[0] = 1
908        helpers.assert_quantity_equal(q, np.asarray([1, 1, 2, 3]))
909        q[0] = self.ureg.m / self.ureg.mm
910        helpers.assert_quantity_equal(q, np.asarray([1000, 1, 2, 3]))
911
912        q = [0.0, 1.0, 2.0, 3.0] * self.ureg.m / self.ureg.mm
913        q[0] = 1.0
914        helpers.assert_quantity_equal(q, [0.001, 1, 2, 3] * self.ureg.m / self.ureg.mm)
915
916        # Check that this properly masks the first item without warning
917        q = self.ureg.Quantity(
918            np.ma.array([0.0, 1.0, 2.0, 3.0], mask=[False, True, False, False]), "m"
919        )
920        with warnings.catch_warnings(record=True) as w:
921            q[0] = np.ma.masked
922            # Check for no warnings
923            assert not w
924            assert q.mask[0]
925
926    def test_iterator(self):
927        for q, v in zip(self.q.flatten(), [1, 2, 3, 4]):
928            assert q == v * self.ureg.m
929
930    def test_iterable(self):
931        assert np.iterable(self.q)
932        assert not np.iterable(1 * self.ureg.m)
933
934    def test_reversible_op(self):
935        """ """
936        x = self.q.magnitude
937        u = self.Q_(np.ones(x.shape))
938        helpers.assert_quantity_equal(x / self.q, u * x / self.q)
939        helpers.assert_quantity_equal(x * self.q, u * x * self.q)
940        helpers.assert_quantity_equal(x + u, u + x)
941        helpers.assert_quantity_equal(x - u, -(u - x))
942
943    def test_pickle(self, subtests):
944        for protocol in range(pickle.HIGHEST_PROTOCOL + 1):
945            with subtests.test(protocol):
946                q1 = [10, 20] * self.ureg.m
947                q2 = pickle.loads(pickle.dumps(q1, protocol))
948                self.assertNDArrayEqual(q1.magnitude, q2.magnitude)
949                assert q1.units == q2.units
950
951    def test_equal(self):
952        x = self.q.magnitude
953        u = self.Q_(np.ones(x.shape))
954        true = np.ones_like(x, dtype=np.bool_)
955        false = np.zeros_like(x, dtype=np.bool_)
956
957        helpers.assert_quantity_equal(u, u)
958        helpers.assert_quantity_equal(u == u, u.magnitude == u.magnitude)
959        helpers.assert_quantity_equal(u == 1, u.magnitude == 1)
960
961        v = self.Q_(np.zeros(x.shape), "m")
962        w = self.Q_(np.ones(x.shape), "m")
963        self.assertNDArrayEqual(v == 1, false)
964        self.assertNDArrayEqual(
965            self.Q_(np.zeros_like(x), "m") == self.Q_(np.zeros_like(x), "s"),
966            false,
967        )
968        self.assertNDArrayEqual(v == v, true)
969        self.assertNDArrayEqual(v == w, false)
970        self.assertNDArrayEqual(v == w.to("mm"), false)
971        self.assertNDArrayEqual(u == v, false)
972
973    def test_shape(self):
974        u = self.Q_(np.arange(12))
975        u.shape = 4, 3
976        assert u.magnitude.shape == (4, 3)
977
978    @helpers.requires_array_function_protocol()
979    def test_shape_numpy_func(self):
980        assert np.shape(self.q) == (2, 2)
981
982    @helpers.requires_array_function_protocol()
983    def test_alen_numpy_func(self):
984        assert np.alen(self.q) == 2
985
986    @helpers.requires_array_function_protocol()
987    def test_ndim_numpy_func(self):
988        assert np.ndim(self.q) == 2
989
990    @helpers.requires_array_function_protocol()
991    def test_copy_numpy_func(self):
992        q_copy = np.copy(self.q)
993        helpers.assert_quantity_equal(self.q, q_copy)
994        assert self.q is not q_copy
995
996    @helpers.requires_array_function_protocol()
997    def test_trim_zeros_numpy_func(self):
998        q = [0, 4, 3, 0, 2, 2, 0, 0, 0] * self.ureg.m
999        helpers.assert_quantity_equal(np.trim_zeros(q), [4, 3, 0, 2, 2] * self.ureg.m)
1000
1001    @helpers.requires_array_function_protocol()
1002    def test_result_type_numpy_func(self):
1003        assert np.result_type(self.q) == np.dtype("int")
1004
1005    @helpers.requires_array_function_protocol()
1006    def test_nan_to_num_numpy_func(self):
1007        helpers.assert_quantity_equal(
1008            np.nan_to_num(self.q_nan, nan=-999 * self.ureg.mm),
1009            [[1, 2], [3, -0.999]] * self.ureg.m,
1010        )
1011
1012    @helpers.requires_array_function_protocol()
1013    def test_meshgrid_numpy_func(self):
1014        x = [1, 2] * self.ureg.m
1015        y = [0, 50, 100] * self.ureg.mm
1016        xx, yy = np.meshgrid(x, y)
1017        helpers.assert_quantity_equal(xx, [[1, 2], [1, 2], [1, 2]] * self.ureg.m)
1018        helpers.assert_quantity_equal(yy, [[0, 0], [50, 50], [100, 100]] * self.ureg.mm)
1019
1020    @helpers.requires_array_function_protocol()
1021    def test_isclose_numpy_func(self):
1022        q2 = [[1000.05, 2000], [3000.00007, 4001]] * self.ureg.mm
1023        self.assertNDArrayEqual(
1024            np.isclose(self.q, q2), np.array([[False, True], [True, False]])
1025        )
1026        self.assertNDArrayEqual(
1027            np.isclose(self.q, q2, atol=1e-5, rtol=1e-7),
1028            np.array([[False, True], [True, False]]),
1029        )
1030
1031    @helpers.requires_array_function_protocol()
1032    def test_interp_numpy_func(self):
1033        x = [1, 4] * self.ureg.m
1034        xp = np.linspace(0, 3, 5) * self.ureg.m
1035        fp = self.Q_([0, 5, 10, 15, 20], self.ureg.degC)
1036        helpers.assert_quantity_almost_equal(
1037            np.interp(x, xp, fp), self.Q_([6.66667, 20.0], self.ureg.degC), rtol=1e-5
1038        )
1039
1040        x_ = np.array([1, 4])
1041        xp_ = np.linspace(0, 3, 5)
1042        fp_ = [0, 5, 10, 15, 20]
1043
1044        helpers.assert_quantity_almost_equal(
1045            np.interp(x_, xp_, fp), self.Q_([6.6667, 20.0], self.ureg.degC), rtol=1e-5
1046        )
1047        helpers.assert_quantity_almost_equal(
1048            np.interp(x, xp, fp_), [6.6667, 20.0], rtol=1e-5
1049        )
1050
1051    def test_comparisons(self):
1052        self.assertNDArrayEqual(
1053            self.q > 2 * self.ureg.m, np.array([[False, False], [True, True]])
1054        )
1055        self.assertNDArrayEqual(
1056            self.q < 2 * self.ureg.m, np.array([[True, False], [False, False]])
1057        )
1058
1059    @helpers.requires_array_function_protocol()
1060    def test_where(self):
1061        helpers.assert_quantity_equal(
1062            np.where(self.q >= 2 * self.ureg.m, self.q, 20 * self.ureg.m),
1063            [[20, 2], [3, 4]] * self.ureg.m,
1064        )
1065        helpers.assert_quantity_equal(
1066            np.where(self.q >= 2 * self.ureg.m, self.q, 0),
1067            [[0, 2], [3, 4]] * self.ureg.m,
1068        )
1069        helpers.assert_quantity_equal(
1070            np.where(self.q >= 2 * self.ureg.m, self.q, np.nan),
1071            [[np.nan, 2], [3, 4]] * self.ureg.m,
1072        )
1073        helpers.assert_quantity_equal(
1074            np.where(self.q >= 3 * self.ureg.m, 0, self.q),
1075            [[1, 2], [0, 0]] * self.ureg.m,
1076        )
1077        helpers.assert_quantity_equal(
1078            np.where(self.q >= 3 * self.ureg.m, np.nan, self.q),
1079            [[1, 2], [np.nan, np.nan]] * self.ureg.m,
1080        )
1081        helpers.assert_quantity_equal(
1082            np.where(self.q >= 2 * self.ureg.m, self.q, np.array(np.nan)),
1083            [[np.nan, 2], [3, 4]] * self.ureg.m,
1084        )
1085        helpers.assert_quantity_equal(
1086            np.where(self.q >= 3 * self.ureg.m, np.array(np.nan), self.q),
1087            [[1, 2], [np.nan, np.nan]] * self.ureg.m,
1088        )
1089        with pytest.raises(DimensionalityError):
1090            np.where(
1091                self.q < 2 * self.ureg.m,
1092                self.q,
1093                0 * self.ureg.J,
1094            )
1095
1096    @helpers.requires_array_function_protocol()
1097    def test_fabs(self):
1098        helpers.assert_quantity_equal(
1099            np.fabs(self.q - 2 * self.ureg.m), self.Q_([[1, 0], [1, 2]], "m")
1100        )
1101
1102    @helpers.requires_array_function_protocol()
1103    def test_isin(self):
1104        self.assertNDArrayEqual(
1105            np.isin(self.q, self.Q_([0, 2, 4], "m")),
1106            np.array([[False, True], [False, True]]),
1107        )
1108        self.assertNDArrayEqual(
1109            np.isin(self.q, self.Q_([0, 2, 4], "J")),
1110            np.array([[False, False], [False, False]]),
1111        )
1112        self.assertNDArrayEqual(
1113            np.isin(self.q, [self.Q_(2, "m"), self.Q_(4, "J")]),
1114            np.array([[False, True], [False, False]]),
1115        )
1116        self.assertNDArrayEqual(
1117            np.isin(self.q, self.q.m), np.array([[False, False], [False, False]])
1118        )
1119        self.assertNDArrayEqual(
1120            np.isin(self.q / self.ureg.cm, [1, 3]),
1121            np.array([[True, False], [True, False]]),
1122        )
1123        with pytest.raises(ValueError):
1124            np.isin(self.q.m, self.q)
1125
1126    @helpers.requires_array_function_protocol()
1127    def test_percentile(self):
1128        helpers.assert_quantity_equal(np.percentile(self.q, 25), self.Q_(1.75, "m"))
1129
1130    @helpers.requires_array_function_protocol()
1131    def test_nanpercentile(self):
1132        helpers.assert_quantity_equal(
1133            np.nanpercentile(self.q_nan, 25), self.Q_(1.5, "m")
1134        )
1135
1136    @helpers.requires_array_function_protocol()
1137    def test_quantile(self):
1138        helpers.assert_quantity_equal(np.quantile(self.q, 0.25), self.Q_(1.75, "m"))
1139
1140    @helpers.requires_array_function_protocol()
1141    def test_nanquantile(self):
1142        helpers.assert_quantity_equal(
1143            np.nanquantile(self.q_nan, 0.25), self.Q_(1.5, "m")
1144        )
1145
1146    @helpers.requires_array_function_protocol()
1147    def test_copyto(self):
1148        a = self.q.m
1149        q = copy.copy(self.q)
1150        np.copyto(q, 2 * q, where=[True, False])
1151        helpers.assert_quantity_equal(q, self.Q_([[2, 2], [6, 4]], "m"))
1152        np.copyto(q, 0, where=[[False, False], [True, False]])
1153        helpers.assert_quantity_equal(q, self.Q_([[2, 2], [0, 4]], "m"))
1154        np.copyto(a, q)
1155        self.assertNDArrayEqual(a, np.array([[2, 2], [0, 4]]))
1156
1157    @helpers.requires_array_function_protocol()
1158    def test_tile(self):
1159        helpers.assert_quantity_equal(
1160            np.tile(self.q, 2), np.array([[1, 2, 1, 2], [3, 4, 3, 4]]) * self.ureg.m
1161        )
1162
1163    @helpers.requires_numpy_at_least("1.20")
1164    @helpers.requires_array_function_protocol()
1165    def test_sliding_window_view(self):
1166        q = self.Q_([[1, 2, 2, 1], [2, 1, 1, 2], [1, 2, 2, 1]], "m")
1167        actual = np.lib.stride_tricks.sliding_window_view(q, window_shape=(3, 3))
1168        expected = self.Q_(
1169            [[[[1, 2, 2], [2, 1, 1], [1, 2, 2]], [[2, 2, 1], [1, 1, 2], [2, 2, 1]]]],
1170            "m",
1171        )
1172        helpers.assert_quantity_equal(actual, expected)
1173
1174    @helpers.requires_array_function_protocol()
1175    def test_rot90(self):
1176        helpers.assert_quantity_equal(
1177            np.rot90(self.q), np.array([[2, 4], [1, 3]]) * self.ureg.m
1178        )
1179
1180    @helpers.requires_array_function_protocol()
1181    def test_insert(self):
1182        helpers.assert_quantity_equal(
1183            np.insert(self.q, 1, 0 * self.ureg.m, axis=1),
1184            np.array([[1, 0, 2], [3, 0, 4]]) * self.ureg.m,
1185        )
1186
1187    def test_ndarray_downcast(self):
1188        with pytest.warns(UnitStrippedWarning):
1189            np.asarray(self.q)
1190
1191    def test_ndarray_downcast_with_dtype(self):
1192        with pytest.warns(UnitStrippedWarning):
1193            qarr = np.asarray(self.q, dtype=np.float64)
1194            assert qarr.dtype == np.float64
1195
1196    def test_array_protocol_unavailable(self):
1197        for attr in ("__array_struct__", "__array_interface__"):
1198            with pytest.raises(AttributeError):
1199                getattr(self.q, attr)
1200
1201    @helpers.requires_array_function_protocol()
1202    def test_resize(self):
1203        helpers.assert_quantity_equal(
1204            np.resize(self.q, (2, 4)), [[1, 2, 3, 4], [1, 2, 3, 4]] * self.ureg.m
1205        )
1206
1207    @helpers.requires_array_function_protocol()
1208    def test_pad(self):
1209        # Tests reproduced with modification from NumPy documentation
1210        a = [1, 2, 3, 4, 5] * self.ureg.m
1211        b = self.Q_([4.0, 6.0, 8.0, 9.0, -3.0], "degC")
1212
1213        helpers.assert_quantity_equal(
1214            np.pad(a, (2, 3), "constant"), [0, 0, 1, 2, 3, 4, 5, 0, 0, 0] * self.ureg.m
1215        )
1216        helpers.assert_quantity_equal(
1217            np.pad(a, (2, 3), "constant", constant_values=(0, 600 * self.ureg.cm)),
1218            [0, 0, 1, 2, 3, 4, 5, 6, 6, 6] * self.ureg.m,
1219        )
1220        helpers.assert_quantity_equal(
1221            np.pad(
1222                b, (2, 1), "constant", constant_values=(np.nan, self.Q_(10, "degC"))
1223            ),
1224            self.Q_([np.nan, np.nan, 4, 6, 8, 9, -3, 10], "degC"),
1225        )
1226        with pytest.raises(DimensionalityError):
1227            np.pad(a, (2, 3), "constant", constant_values=4)
1228        helpers.assert_quantity_equal(
1229            np.pad(a, (2, 3), "edge"), [1, 1, 1, 2, 3, 4, 5, 5, 5, 5] * self.ureg.m
1230        )
1231        helpers.assert_quantity_equal(
1232            np.pad(a, (2, 3), "linear_ramp"),
1233            [0, 0, 1, 2, 3, 4, 5, 3, 1, 0] * self.ureg.m,
1234        )
1235        helpers.assert_quantity_equal(
1236            np.pad(a, (2, 3), "linear_ramp", end_values=(5, -4) * self.ureg.m),
1237            [5, 3, 1, 2, 3, 4, 5, 2, -1, -4] * self.ureg.m,
1238        )
1239        helpers.assert_quantity_equal(
1240            np.pad(a, (2,), "maximum"), [5, 5, 1, 2, 3, 4, 5, 5, 5] * self.ureg.m
1241        )
1242        helpers.assert_quantity_equal(
1243            np.pad(a, (2,), "mean"), [3, 3, 1, 2, 3, 4, 5, 3, 3] * self.ureg.m
1244        )
1245        helpers.assert_quantity_equal(
1246            np.pad(a, (2,), "median"), [3, 3, 1, 2, 3, 4, 5, 3, 3] * self.ureg.m
1247        )
1248        helpers.assert_quantity_equal(
1249            np.pad(self.q, ((3, 2), (2, 3)), "minimum"),
1250            [
1251                [1, 1, 1, 2, 1, 1, 1],
1252                [1, 1, 1, 2, 1, 1, 1],
1253                [1, 1, 1, 2, 1, 1, 1],
1254                [1, 1, 1, 2, 1, 1, 1],
1255                [3, 3, 3, 4, 3, 3, 3],
1256                [1, 1, 1, 2, 1, 1, 1],
1257                [1, 1, 1, 2, 1, 1, 1],
1258            ]
1259            * self.ureg.m,
1260        )
1261        helpers.assert_quantity_equal(
1262            np.pad(a, (2, 3), "reflect"), [3, 2, 1, 2, 3, 4, 5, 4, 3, 2] * self.ureg.m
1263        )
1264        helpers.assert_quantity_equal(
1265            np.pad(a, (2, 3), "reflect", reflect_type="odd"),
1266            [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8] * self.ureg.m,
1267        )
1268        helpers.assert_quantity_equal(
1269            np.pad(a, (2, 3), "symmetric"), [2, 1, 1, 2, 3, 4, 5, 5, 4, 3] * self.ureg.m
1270        )
1271        helpers.assert_quantity_equal(
1272            np.pad(a, (2, 3), "symmetric", reflect_type="odd"),
1273            [0, 1, 1, 2, 3, 4, 5, 5, 6, 7] * self.ureg.m,
1274        )
1275        helpers.assert_quantity_equal(
1276            np.pad(a, (2, 3), "wrap"), [4, 5, 1, 2, 3, 4, 5, 1, 2, 3] * self.ureg.m
1277        )
1278
1279        def pad_with(vector, pad_width, iaxis, kwargs):
1280            pad_value = kwargs.get("padder", 10)
1281            vector[: pad_width[0]] = pad_value
1282            vector[-pad_width[1] :] = pad_value
1283
1284        b = self.Q_(np.arange(6).reshape((2, 3)), "degC")
1285        helpers.assert_quantity_equal(
1286            np.pad(b, 2, pad_with),
1287            self.Q_(
1288                [
1289                    [10, 10, 10, 10, 10, 10, 10],
1290                    [10, 10, 10, 10, 10, 10, 10],
1291                    [10, 10, 0, 1, 2, 10, 10],
1292                    [10, 10, 3, 4, 5, 10, 10],
1293                    [10, 10, 10, 10, 10, 10, 10],
1294                    [10, 10, 10, 10, 10, 10, 10],
1295                ],
1296                "degC",
1297            ),
1298        )
1299        helpers.assert_quantity_equal(
1300            np.pad(b, 2, pad_with, padder=100),
1301            self.Q_(
1302                [
1303                    [100, 100, 100, 100, 100, 100, 100],
1304                    [100, 100, 100, 100, 100, 100, 100],
1305                    [100, 100, 0, 1, 2, 100, 100],
1306                    [100, 100, 3, 4, 5, 100, 100],
1307                    [100, 100, 100, 100, 100, 100, 100],
1308                    [100, 100, 100, 100, 100, 100, 100],
1309                ],
1310                "degC",
1311            ),
1312        )  # Note: Does not support Quantity pad_with vectorized callable use
1313
1314    @helpers.requires_array_function_protocol()
1315    def test_allclose(self):
1316        assert np.allclose([1e10, 1e-8] * self.ureg.m, [1.00001e10, 1e-9] * self.ureg.m)
1317        assert not np.allclose(
1318            [1e10, 1e-8] * self.ureg.m, [1.00001e10, 1e-9] * self.ureg.mm
1319        )
1320
1321    @helpers.requires_array_function_protocol()
1322    def test_intersect1d(self):
1323        helpers.assert_quantity_equal(
1324            np.intersect1d([1, 3, 4, 3] * self.ureg.m, [3, 1, 2, 1] * self.ureg.m),
1325            [1, 3] * self.ureg.m,
1326        )
1327
1328
1329@pytest.mark.skip
1330class TestBitTwiddlingUfuncs(TestUFuncs):
1331    """Universal functions (ufuncs) >  Bittwiddling functions
1332
1333    http://docs.scipy.org/doc/numpy/reference/ufuncs.html#bittwiddlingfunctions
1334
1335    bitwise_and(x1, x2[, out])         Compute the bitwise AND of two arrays elementwise.
1336    bitwise_or(x1, x2[, out])  Compute the bitwise OR of two arrays elementwise.
1337    bitwise_xor(x1, x2[, out])         Compute the bitwise XOR of two arrays elementwise.
1338    invert(x[, out])   Compute bitwise inversion, or bitwise NOT, elementwise.
1339    left_shift(x1, x2[, out])  Shift the bits of an integer to the left.
1340    right_shift(x1, x2[, out])         Shift the bits of an integer to the right.
1341
1342    Parameters
1343    ----------
1344
1345    Returns
1346    -------
1347
1348    """
1349
1350    @property
1351    def qless(self):
1352        return np.asarray([1, 2, 3, 4], dtype=np.uint8) * self.ureg.dimensionless
1353
1354    @property
1355    def qs(self):
1356        return 8 * self.ureg.J
1357
1358    @property
1359    def q1(self):
1360        return np.asarray([1, 2, 3, 4], dtype=np.uint8) * self.ureg.J
1361
1362    @property
1363    def q2(self):
1364        return 2 * self.q1
1365
1366    @property
1367    def qm(self):
1368        return np.asarray([1, 2, 3, 4], dtype=np.uint8) * self.ureg.m
1369
1370    def test_bitwise_and(self):
1371        self._test2(np.bitwise_and, self.q1, (self.q2, self.qs), (self.qm,), "same")
1372
1373    def test_bitwise_or(self):
1374        self._test2(
1375            np.bitwise_or, self.q1, (self.q1, self.q2, self.qs), (self.qm,), "same"
1376        )
1377
1378    def test_bitwise_xor(self):
1379        self._test2(
1380            np.bitwise_xor, self.q1, (self.q1, self.q2, self.qs), (self.qm,), "same"
1381        )
1382
1383    def test_invert(self):
1384        self._test1(np.invert, (self.q1, self.q2, self.qs), (), "same")
1385
1386    def test_left_shift(self):
1387        self._test2(
1388            np.left_shift, self.q1, (self.qless, 2), (self.q1, self.q2, self.qs), "same"
1389        )
1390
1391    def test_right_shift(self):
1392        self._test2(
1393            np.right_shift,
1394            self.q1,
1395            (self.qless, 2),
1396            (self.q1, self.q2, self.qs),
1397            "same",
1398        )
1399