From 55208b0ae738c6d99e196e8295056db9b10b2206 Mon Sep 17 00:00:00 2001 From: crusaderky Date: Mon, 20 Jan 2025 16:39:31 +0000 Subject: [PATCH 1/7] ENH: new functions `isclose` and `allclose` --- docs/api-reference.md | 2 + pixi.lock | 2 +- pyproject.toml | 1 + src/array_api_extra/__init__.py | 4 +- src/array_api_extra/_delegation.py | 140 ++++++++++++++++++++++++++- src/array_api_extra/_lib/_funcs.py | 35 +++++++ src/array_api_extra/_lib/_testing.py | 15 +++ tests/test_funcs.py | 137 +++++++++++++++++++++++++- 8 files changed, 332 insertions(+), 4 deletions(-) diff --git a/docs/api-reference.md b/docs/api-reference.md index e2c115c..2e08055 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -6,11 +6,13 @@ :nosignatures: :toctree: generated + allclose at atleast_nd cov create_diagonal expand_dims + isclose kron nunique pad diff --git a/pixi.lock b/pixi.lock index 5ea4574..9107942 100644 --- a/pixi.lock +++ b/pixi.lock @@ -3659,7 +3659,7 @@ packages: - pypi: . name: array-api-extra version: 0.6.1.dev0 - sha256: 22c9e9830a088aff4480ecea8495d2ebcf91f65596886a12012bebfb238181d6 + sha256: a1b2089d0b3ce0b6e017ad54a21903a53aeafe710f3a2ed821b648c0e91bc4dd requires_dist: - array-api-compat>=1.10.0,<2 requires_python: '>=3.10' diff --git a/pyproject.toml b/pyproject.toml index 2fae4f8..99c7862 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -293,6 +293,7 @@ checks = [ "all", # report on all checks, except the below "EX01", # most docstrings do not need an example "SA01", # data-apis/array-api-extra#87 + "SA04", # Missing description for See Also cross-reference "ES01", # most docstrings do not need an extended summary ] exclude = [ # don't report on objects that match any of these regex diff --git a/src/array_api_extra/__init__.py b/src/array_api_extra/__init__.py index 65d2f73..85a9b88 100644 --- a/src/array_api_extra/__init__.py +++ b/src/array_api_extra/__init__.py @@ -1,6 +1,6 @@ """Extra array functions built on top of the array API standard.""" -from ._delegation import pad +from ._delegation import allclose, isclose, pad from ._lib._at import at from ._lib._funcs import ( atleast_nd, @@ -18,11 +18,13 @@ # pylint: disable=duplicate-code __all__ = [ "__version__", + "allclose", "at", "atleast_nd", "cov", "create_diagonal", "expand_dims", + "isclose", "kron", "nunique", "pad", diff --git a/src/array_api_extra/_delegation.py b/src/array_api_extra/_delegation.py index 195dd88..18dd32b 100644 --- a/src/array_api_extra/_delegation.py +++ b/src/array_api_extra/_delegation.py @@ -8,7 +8,7 @@ from ._lib._utils._compat import array_namespace from ._lib._utils._typing import Array -__all__ = ["pad"] +__all__ = ["allclose", "isclose", "pad"] def _delegate(xp: ModuleType, *backends: Backend) -> bool: @@ -30,6 +30,144 @@ def _delegate(xp: ModuleType, *backends: Backend) -> bool: return any(backend.is_namespace(xp) for backend in backends) +def allclose( + a: Array, + b: Array, + *, + rtol: float = 1e-05, + atol: float = 1e-08, + equal_nan: bool = False, + xp: ModuleType | None = None, +) -> Array: + """ + Return True if two arrays are element-wise equal within a tolerance. + + This is a simple convenience reduction around `isclose`. + + Parameters + ---------- + a, b : Array + Input arrays to compare. + rtol : array_like, optional + The relative tolerance parameter. + atol : array_like, optional + The absolute tolerance parameter. + equal_nan : bool, optional + Whether to compare NaN's as equal. If True, NaN's in `a` will be considered + equal to NaN's in `b` in the output array. + xp : array_namespace, optional + The standard-compatible namespace for `a` and `b`. Default: infer. + + Returns + ------- + Array + A 0-dimensional boolean array, containing `True` if all `a` is elementwise close + to `b` and `False` otherwise. + + See Also + -------- + isclose + math.isclose + + Notes + ----- + If `xp` is a lazy backend (e.g. Dask, JAX), you may not be able to test the result + contents with ``bool(allclose(a, b))`` or ``if allclose(a, b): ...``. + """ + xp = array_namespace(a, b) if xp is None else xp + return xp.all(isclose(a, b, rtol=rtol, atol=atol, equal_nan=equal_nan, xp=xp)) + + +def isclose( + a: Array, + b: Array, + *, + rtol: float = 1e-05, + atol: float = 1e-08, + equal_nan: bool = False, + xp: ModuleType | None = None, +) -> Array: + """ + Return a boolean array where two arrays are element-wise equal within a tolerance. + + The tolerance values are positive, typically very small numbers. The relative + difference `(rtol * abs(b))` and the absolute difference atol are added together to + compare against the absolute difference between a and b. + + NaNs are treated as equal if they are in the same place and if equal_nan=True. Infs + are treated as equal if they are in the same place and of the same sign in both + arrays. + + Parameters + ---------- + a, b : Array + Input arrays to compare. + rtol : array_like, optional + The relative tolerance parameter (see Notes). + atol : array_like, optional + The absolute tolerance parameter (see Notes). + equal_nan : bool, optional + Whether to compare NaN's as equal. If True, NaN's in `a` will be considered + equal to NaN's in `b` in the output array. + xp : array_namespace, optional + The standard-compatible namespace for `a` and `b`. Default: infer. + + Returns + ------- + Array + A boolean array of shape broadcasted from `a` and `b`, containing `True` where + ``a`` is close to ``b``, and `False` otherwise. + + Warnings + -------- + The default atol is not appropriate for comparing numbers with magnitudes much + smaller than one ) (see notes). + + See Also + -------- + allclose + math.isclose + + Notes + ----- + For finite values, `isclose` uses the following equation to test whether two + floating point values are equivalent:: + + absolute(a - b) <= (atol + rtol * absolute(b)) + + Unlike the built-in `math.isclose`, the above equation is not symmetric in a and b, + so that `isclose(a, b)` might be different from `isclose(b, a)` in some rare + cases. + + The default value of `atol` is not appropriate when the reference value `b` has + magnitude smaller than one. For example, it is unlikely that ``a = 1e-9`` and + ``b = 2e-9`` should be considered "close", yet ``isclose(1e-9, 2e-9)`` is `True` + with default settings. Be sure to select atol for the use case at hand, especially + for defining the threshold below which a non-zero value in `a` will be considered + "close" to a very small or zero value in `b`. + + The comparison of `a` and `b` uses standard broadcasting, which means that `a` and + `b` need not have the same shape in order for `isclose(a, b)` to evaluate to + `True`. + + `isclose` is not defined for non-numeric data types. `bool` is considered a numeric + data-type for this purpose. + """ + xp = array_namespace(a, b) if xp is None else xp + + if _delegate( + xp, + Backend.NUMPY, + Backend.CUPY, + Backend.DASK, + Backend.JAX, + Backend.TORCH, + ): + return xp.isclose(a, b, rtol=rtol, atol=atol, equal_nan=equal_nan) + + return _funcs.isclose(a, b, rtol=rtol, atol=atol, equal_nan=equal_nan, xp=xp) + + def pad( x: Array, pad_width: int | tuple[int, int] | Sequence[tuple[int, int]], diff --git a/src/array_api_extra/_lib/_funcs.py b/src/array_api_extra/_lib/_funcs.py index c1c39f5..4b6769a 100644 --- a/src/array_api_extra/_lib/_funcs.py +++ b/src/array_api_extra/_lib/_funcs.py @@ -305,6 +305,41 @@ def expand_dims( return a +def isclose( + a: Array, + b: Array, + *, + rtol: float = 1e-05, + atol: float = 1e-08, + equal_nan: bool = False, + xp: ModuleType | None = None, +) -> Array: # numpydoc ignore=PR01,RT01 + """See docstring in array_api_extra._delegation.""" + xp = array_namespace(a, b) if xp is None else xp + + a_inexact = xp.isdtype(a.dtype, ("real floating", "complex floating")) + b_inexact = xp.isdtype(b.dtype, ("real floating", "complex floating")) + if a_inexact or b_inexact: + # FIXME: use scipy's lazywhere to suppress warnings on inf + out = xp.abs(a - b) <= (atol + rtol * xp.abs(b)) + out = xp.where(xp.isinf(a) & xp.isinf(b), xp.sign(a) == xp.sign(b), out) + if equal_nan: + out = xp.where(xp.isnan(a) & xp.isnan(b), xp.asarray(True), out) + return out + + if xp.isdtype(a.dtype, "bool") or xp.isdtype(b.dtype, "bool"): + if atol >= 1 or rtol >= 1: + return xp.ones_like(a == b) + return a == b + + # integer types + atol = int(atol) + if rtol == 0: + return xp.abs(a - b) <= atol + nrtol = int(1.0 / rtol) + return xp.abs(a - b) <= (atol + xp.abs(b) // nrtol) + + def kron(a: Array, b: Array, /, *, xp: ModuleType | None = None) -> Array: """ Kronecker product of two arrays. diff --git a/src/array_api_extra/_lib/_testing.py b/src/array_api_extra/_lib/_testing.py index b6993a1..91a783d 100644 --- a/src/array_api_extra/_lib/_testing.py +++ b/src/array_api_extra/_lib/_testing.py @@ -61,6 +61,11 @@ def xp_assert_equal(actual: Array, desired: Array, err_msg: str = "") -> None: The expected array (typically hardcoded). err_msg : str, optional Error message to display on failure. + + See Also + -------- + xp_assert_close + numpy.testing.assert_array_equal """ xp = _check_ns_shape_dtype(actual, desired) @@ -112,6 +117,16 @@ def xp_assert_close( Absolute tolerance. Default: 0. err_msg : str, optional Error message to display on failure. + + See Also + -------- + xp_assert_equal + allclose + numpy.testing.assert_allclose + + Notes + ----- + The default `atol` and `rtol` differ from `xpx.allclose`. """ xp = _check_ns_shape_dtype(actual, desired) diff --git a/tests/test_funcs.py b/tests/test_funcs.py index 9be0150..a59734f 100644 --- a/tests/test_funcs.py +++ b/tests/test_funcs.py @@ -6,11 +6,13 @@ import pytest from array_api_extra import ( + allclose, at, atleast_nd, cov, create_diagonal, expand_dims, + isclose, kron, nunique, pad, @@ -23,7 +25,7 @@ from array_api_extra._lib._utils._typing import Array, Device # some xp backends are untyped -# mypy: disable-error-code=no-untyped-usage +# mypy: disable-error-code=no-untyped-def @pytest.mark.skip_xp_backend(Backend.SPARSE, reason="no expand_dims") @@ -252,6 +254,139 @@ def test_xp(self, xp: ModuleType): assert y.shape == (1, 1, 1, 3) +@pytest.mark.skip_xp_backend(Backend.SPARSE, reason="no isdtype") +class TestIsClose: + # FIXME use lazywhere to avoid warnings on inf + @pytest.mark.filterwarnings("ignore:invalid value encountered") + @pytest.mark.parametrize( + ("a", "b"), + [ + (0.0, 0.0), + (1.0, 1.0), + (1.0, 2.0), + (1.0, -1.0), + (100.0, 101.0), + (0, 0), + (1, 1), + (1, 2), + (1, -1), + (1.0 + 1j, 1.0 + 1j), + (1.0 + 1j, 1.0 - 1j), + (float("inf"), float("inf")), + (float("inf"), 100.0), + (float("inf"), float("-inf")), + (float("nan"), float("nan")), + (float("nan"), 0.0), + (0.0, float("nan")), + (1e6, 1e6 + 1), # True - within rtol + (1e6, 1e6 + 100), # False - outside rtol + (1e-6, 1.1e-6), # False - outside atol + (1e-7, 1.1e-7), # True - outside atol + (1e6 + 0j, 1e6 + 1j), # True - within rtol + (1e6 + 0j, 1e6 + 100j), # False - outside rtol + ], + ) + def test_basic(self, a: float, b: float, xp: ModuleType): + a_xp = xp.asarray(a) + b_xp = xp.asarray(b) + + xp_assert_equal(isclose(a_xp, b_xp), xp.asarray(np.isclose(a, b))) + xp_assert_equal(allclose(a_xp, b_xp), xp.asarray(np.allclose(a, b))) + + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + r_xp = xp.asarray(np.arange(10), dtype=a_xp.dtype) + ar_xp = a_xp * r_xp + br_xp = b_xp * r_xp + ar_np = a * np.arange(10) + br_np = b * np.arange(10) + + xp_assert_equal(isclose(ar_xp, br_xp), xp.asarray(np.isclose(ar_np, br_np))) + + @pytest.mark.parametrize("dtype", ["float32", "int32"]) + def test_broadcast(self, dtype: str, xp: ModuleType): + dtype = getattr(xp, dtype) + a = xp.asarray([1, 2, 3], dtype=dtype) + b = xp.asarray([[1], [5]], dtype=dtype) + actual = isclose(a, b) + expect = xp.asarray( + [[True, False, False], [False, False, False]], dtype=xp.bool + ) + + xp_assert_equal(actual, expect) + + # FIXME use lazywhere to avoid warnings on inf + @pytest.mark.filterwarnings("ignore:invalid value encountered") + def test_some_inf(self, xp: ModuleType): + a = xp.asarray([0.0, 1.0, float("inf"), float("inf"), float("inf")]) + b = xp.asarray([1e-9, 1.0, float("inf"), float("-inf"), 2.0]) + actual = isclose(a, b) + xp_assert_equal(actual, xp.asarray([True, True, True, False, False])) + + def test_equal_nan(self, xp: ModuleType): + a = xp.asarray([float("nan"), float("nan"), 1.0]) + b = xp.asarray([float("nan"), 1.0, float("nan")]) + xp_assert_equal(isclose(a, b), xp.asarray([False, False, False])) + xp_assert_equal(isclose(a, b, equal_nan=True), xp.asarray([True, False, False])) + xp_assert_equal(allclose(a[:1], b[:1]), xp.asarray(False)) + xp_assert_equal(allclose(a[:1], b[:1], equal_nan=True), xp.asarray(True)) + + @pytest.mark.parametrize("dtype", ["float32", "complex64", "int32"]) + def test_tolerance(self, dtype: str, xp: ModuleType): + dtype = getattr(xp, dtype) + a = xp.asarray([100, 100], dtype=dtype) + b = xp.asarray([101, 102], dtype=dtype) + xp_assert_equal(isclose(a, b), xp.asarray([False, False])) + xp_assert_equal(isclose(a, b, atol=1), xp.asarray([True, False])) + xp_assert_equal(isclose(a, b, rtol=0.01), xp.asarray([True, False])) + xp_assert_equal(allclose(a[:1], b[:1]), xp.asarray(False)) + xp_assert_equal(allclose(a[:1], b[:1], atol=1), xp.asarray(True)) + xp_assert_equal(allclose(a[:1], b[:1], rtol=0.01), xp.asarray(True)) + + # Attempt to trigger division by 0 in rtol on int dtype + xp_assert_equal(isclose(a, b, rtol=0), xp.asarray([False, False])) + xp_assert_equal(isclose(a, b, atol=1, rtol=0), xp.asarray([True, False])) + xp_assert_equal(allclose(a[:1], b[:1], rtol=0), xp.asarray(False)) + xp_assert_equal(allclose(a[:1], b[:1], atol=1, rtol=0), xp.asarray(True)) + + def test_very_small_numbers(self, xp: ModuleType): + a = xp.asarray([1e-9, 1e-9]) + b = xp.asarray([1.0001e-9, 1.00001e-9]) + # Difference is below default atol + xp_assert_equal(isclose(a, b), xp.asarray([True, True])) + # Use only rtol + xp_assert_equal(isclose(a, b, atol=0), xp.asarray([False, True])) + xp_assert_equal(isclose(a, b, atol=0, rtol=0), xp.asarray([False, False])) + + def test_bool_dtype(self, xp: ModuleType): + a = xp.asarray([False, True, False]) + b = xp.asarray([True, True, False]) + xp_assert_equal(isclose(a, b), xp.asarray([False, True, True])) + xp_assert_equal(isclose(a, b, atol=1), xp.asarray([True, True, True])) + xp_assert_equal(isclose(a, b, atol=2), xp.asarray([True, True, True])) + xp_assert_equal(isclose(a, b, rtol=1), xp.asarray([True, True, True])) + xp_assert_equal(isclose(a, b, rtol=2), xp.asarray([True, True, True])) + + xp_assert_equal(allclose(a, b), xp.asarray(False)) + xp_assert_equal(allclose(a, b, atol=1), xp.asarray(True)) + xp_assert_equal(allclose(a, b, atol=2), xp.asarray(True)) + xp_assert_equal(allclose(a, b, rtol=1), xp.asarray(True)) + xp_assert_equal(allclose(a, b, rtol=2), xp.asarray(True)) + + # Test broadcasting + xp_assert_equal( + isclose(a, xp.asarray(True), atol=1), xp.asarray([True, True, True]) + ) + xp_assert_equal( + isclose(xp.asarray(True), b, atol=1), xp.asarray([True, True, True]) + ) + + def test_xp(self, xp: ModuleType): + a = xp.asarray([0.0, 0.0]) + b = xp.asarray([1e-9, 1e-4]) + xp_assert_equal(isclose(a, b, xp=xp), xp.asarray([True, False])) + + @pytest.mark.skip_xp_backend(Backend.SPARSE, reason="no expand_dims") class TestKron: def test_basic(self, xp: ModuleType): From 5f54c925e37496d568bf80d088d24020eed07e59 Mon Sep 17 00:00:00 2001 From: crusaderky Date: Tue, 21 Jan 2025 07:01:39 +0000 Subject: [PATCH 2/7] Remove allclose --- docs/api-reference.md | 1 - src/array_api_extra/__init__.py | 3 +- src/array_api_extra/_delegation.py | 51 +--------------------------- src/array_api_extra/_lib/_testing.py | 4 +-- tests/test_funcs.py | 15 -------- 5 files changed, 4 insertions(+), 70 deletions(-) diff --git a/docs/api-reference.md b/docs/api-reference.md index 2e08055..3220524 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -6,7 +6,6 @@ :nosignatures: :toctree: generated - allclose at atleast_nd cov diff --git a/src/array_api_extra/__init__.py b/src/array_api_extra/__init__.py index 85a9b88..840dd8e 100644 --- a/src/array_api_extra/__init__.py +++ b/src/array_api_extra/__init__.py @@ -1,6 +1,6 @@ """Extra array functions built on top of the array API standard.""" -from ._delegation import allclose, isclose, pad +from ._delegation import isclose, pad from ._lib._at import at from ._lib._funcs import ( atleast_nd, @@ -18,7 +18,6 @@ # pylint: disable=duplicate-code __all__ = [ "__version__", - "allclose", "at", "atleast_nd", "cov", diff --git a/src/array_api_extra/_delegation.py b/src/array_api_extra/_delegation.py index 18dd32b..c89a478 100644 --- a/src/array_api_extra/_delegation.py +++ b/src/array_api_extra/_delegation.py @@ -8,7 +8,7 @@ from ._lib._utils._compat import array_namespace from ._lib._utils._typing import Array -__all__ = ["allclose", "isclose", "pad"] +__all__ = ["isclose", "pad"] def _delegate(xp: ModuleType, *backends: Backend) -> bool: @@ -30,54 +30,6 @@ def _delegate(xp: ModuleType, *backends: Backend) -> bool: return any(backend.is_namespace(xp) for backend in backends) -def allclose( - a: Array, - b: Array, - *, - rtol: float = 1e-05, - atol: float = 1e-08, - equal_nan: bool = False, - xp: ModuleType | None = None, -) -> Array: - """ - Return True if two arrays are element-wise equal within a tolerance. - - This is a simple convenience reduction around `isclose`. - - Parameters - ---------- - a, b : Array - Input arrays to compare. - rtol : array_like, optional - The relative tolerance parameter. - atol : array_like, optional - The absolute tolerance parameter. - equal_nan : bool, optional - Whether to compare NaN's as equal. If True, NaN's in `a` will be considered - equal to NaN's in `b` in the output array. - xp : array_namespace, optional - The standard-compatible namespace for `a` and `b`. Default: infer. - - Returns - ------- - Array - A 0-dimensional boolean array, containing `True` if all `a` is elementwise close - to `b` and `False` otherwise. - - See Also - -------- - isclose - math.isclose - - Notes - ----- - If `xp` is a lazy backend (e.g. Dask, JAX), you may not be able to test the result - contents with ``bool(allclose(a, b))`` or ``if allclose(a, b): ...``. - """ - xp = array_namespace(a, b) if xp is None else xp - return xp.all(isclose(a, b, rtol=rtol, atol=atol, equal_nan=equal_nan, xp=xp)) - - def isclose( a: Array, b: Array, @@ -125,7 +77,6 @@ def isclose( See Also -------- - allclose math.isclose Notes diff --git a/src/array_api_extra/_lib/_testing.py b/src/array_api_extra/_lib/_testing.py index 91a783d..16205b6 100644 --- a/src/array_api_extra/_lib/_testing.py +++ b/src/array_api_extra/_lib/_testing.py @@ -121,12 +121,12 @@ def xp_assert_close( See Also -------- xp_assert_equal - allclose + isclose numpy.testing.assert_allclose Notes ----- - The default `atol` and `rtol` differ from `xpx.allclose`. + The default `atol` and `rtol` differ from `xp.all(xpx.allclose(a, b))`. """ xp = _check_ns_shape_dtype(actual, desired) diff --git a/tests/test_funcs.py b/tests/test_funcs.py index a59734f..9ac94f6 100644 --- a/tests/test_funcs.py +++ b/tests/test_funcs.py @@ -6,7 +6,6 @@ import pytest from array_api_extra import ( - allclose, at, atleast_nd, cov, @@ -291,7 +290,6 @@ def test_basic(self, a: float, b: float, xp: ModuleType): b_xp = xp.asarray(b) xp_assert_equal(isclose(a_xp, b_xp), xp.asarray(np.isclose(a, b))) - xp_assert_equal(allclose(a_xp, b_xp), xp.asarray(np.allclose(a, b))) with warnings.catch_warnings(): warnings.simplefilter("ignore") @@ -328,8 +326,6 @@ def test_equal_nan(self, xp: ModuleType): b = xp.asarray([float("nan"), 1.0, float("nan")]) xp_assert_equal(isclose(a, b), xp.asarray([False, False, False])) xp_assert_equal(isclose(a, b, equal_nan=True), xp.asarray([True, False, False])) - xp_assert_equal(allclose(a[:1], b[:1]), xp.asarray(False)) - xp_assert_equal(allclose(a[:1], b[:1], equal_nan=True), xp.asarray(True)) @pytest.mark.parametrize("dtype", ["float32", "complex64", "int32"]) def test_tolerance(self, dtype: str, xp: ModuleType): @@ -339,15 +335,10 @@ def test_tolerance(self, dtype: str, xp: ModuleType): xp_assert_equal(isclose(a, b), xp.asarray([False, False])) xp_assert_equal(isclose(a, b, atol=1), xp.asarray([True, False])) xp_assert_equal(isclose(a, b, rtol=0.01), xp.asarray([True, False])) - xp_assert_equal(allclose(a[:1], b[:1]), xp.asarray(False)) - xp_assert_equal(allclose(a[:1], b[:1], atol=1), xp.asarray(True)) - xp_assert_equal(allclose(a[:1], b[:1], rtol=0.01), xp.asarray(True)) # Attempt to trigger division by 0 in rtol on int dtype xp_assert_equal(isclose(a, b, rtol=0), xp.asarray([False, False])) xp_assert_equal(isclose(a, b, atol=1, rtol=0), xp.asarray([True, False])) - xp_assert_equal(allclose(a[:1], b[:1], rtol=0), xp.asarray(False)) - xp_assert_equal(allclose(a[:1], b[:1], atol=1, rtol=0), xp.asarray(True)) def test_very_small_numbers(self, xp: ModuleType): a = xp.asarray([1e-9, 1e-9]) @@ -367,12 +358,6 @@ def test_bool_dtype(self, xp: ModuleType): xp_assert_equal(isclose(a, b, rtol=1), xp.asarray([True, True, True])) xp_assert_equal(isclose(a, b, rtol=2), xp.asarray([True, True, True])) - xp_assert_equal(allclose(a, b), xp.asarray(False)) - xp_assert_equal(allclose(a, b, atol=1), xp.asarray(True)) - xp_assert_equal(allclose(a, b, atol=2), xp.asarray(True)) - xp_assert_equal(allclose(a, b, rtol=1), xp.asarray(True)) - xp_assert_equal(allclose(a, b, rtol=2), xp.asarray(True)) - # Test broadcasting xp_assert_equal( isclose(a, xp.asarray(True), atol=1), xp.asarray([True, True, True]) From c3861a79e537d465004c2ed6fd28b7be9ba3cbc9 Mon Sep 17 00:00:00 2001 From: crusaderky Date: Tue, 21 Jan 2025 08:10:41 +0000 Subject: [PATCH 3/7] Test none shapes --- src/array_api_extra/_lib/_testing.py | 14 +++++++-- tests/test_funcs.py | 14 +++++++++ tests/test_testing.py | 44 ++++++++++++++++++++-------- 3 files changed, 58 insertions(+), 14 deletions(-) diff --git a/src/array_api_extra/_lib/_testing.py b/src/array_api_extra/_lib/_testing.py index 16205b6..928ad68 100644 --- a/src/array_api_extra/_lib/_testing.py +++ b/src/array_api_extra/_lib/_testing.py @@ -4,11 +4,13 @@ Note that this is private API; don't expect it to be stable. """ +import math from types import ModuleType from ._utils._compat import ( array_namespace, is_cupy_namespace, + is_dask_namespace, is_pydata_sparse_namespace, is_torch_namespace, ) @@ -40,8 +42,16 @@ def _check_ns_shape_dtype( msg = f"namespaces do not match: {actual_xp} != f{desired_xp}" assert actual_xp == desired_xp, msg - msg = f"shapes do not match: {actual.shape} != f{desired.shape}" - assert actual.shape == desired.shape, msg + actual_shape = actual.shape + desired_shape = desired.shape + if is_dask_namespace(desired_xp): + if any(math.isnan(i) for i in actual_shape): + actual_shape = actual.compute().shape + if any(math.isnan(i) for i in desired_shape): + desired_shape = desired.compute().shape + + msg = f"shapes do not match: {actual_shape} != f{desired_shape}" + assert actual_shape == desired_shape, msg msg = f"dtypes do not match: {actual.dtype} != {desired.dtype}" assert actual.dtype == desired.dtype, msg diff --git a/tests/test_funcs.py b/tests/test_funcs.py index 9ac94f6..fbb530b 100644 --- a/tests/test_funcs.py +++ b/tests/test_funcs.py @@ -366,6 +366,20 @@ def test_bool_dtype(self, xp: ModuleType): isclose(xp.asarray(True), b, atol=1), xp.asarray([True, True, True]) ) + def test_none_shape(self, xp: ModuleType): + a = xp.asarray([1, 5, 0]) + b = xp.asarray([1, 4, 2]) + b = b[a < 5] + a = a[a < 5] + xp_assert_equal(isclose(a, b), xp.asarray([True, False])) + + def test_none_shape_bool(self, xp: ModuleType): + a = xp.asarray([True, True, False]) + b = xp.asarray([True, False, True]) + b = b[a] + a = a[a] + xp_assert_equal(isclose(a, b), xp.asarray([True, False])) + def test_xp(self, xp: ModuleType): a = xp.asarray([0.0, 0.0]) b = xp.asarray([1e-9, 1e-4]) diff --git a/tests/test_testing.py b/tests/test_testing.py index dc31566..ec5023b 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -10,8 +10,7 @@ # mypy: disable-error-code=no-any-decorated # pyright: reportUnknownParameterType=false,reportMissingParameterType=false - -@pytest.mark.parametrize( +param_assert_equal_close = pytest.mark.parametrize( "func", [ xp_assert_equal, @@ -21,6 +20,9 @@ ), ], ) + + +@param_assert_equal_close def test_assert_close_equal_basic(xp: ModuleType, func: Callable[..., None]): # type: ignore[no-any-explicit] func(xp.asarray(0), xp.asarray(0)) func(xp.asarray([1, 2]), xp.asarray([1, 2])) @@ -40,16 +42,7 @@ def test_assert_close_equal_basic(xp: ModuleType, func: Callable[..., None]): # @pytest.mark.skip_xp_backend(Backend.NUMPY, reason="test other ns vs. numpy") @pytest.mark.skip_xp_backend(Backend.NUMPY_READONLY, reason="test other ns vs. numpy") -@pytest.mark.parametrize( - "func", - [ - xp_assert_equal, - pytest.param( - xp_assert_close, - marks=pytest.mark.skip_xp_backend(Backend.SPARSE, reason="no isdtype"), - ), - ], -) +@param_assert_equal_close def test_assert_close_equal_namespace(xp: ModuleType, func: Callable[..., None]): # type: ignore[no-any-explicit] with pytest.raises(AssertionError): func(xp.asarray(0), np.asarray(0)) @@ -68,3 +61,30 @@ def test_assert_close_tolerance(xp: ModuleType): xp_assert_close(xp.asarray([100.0]), xp.asarray([102.0]), atol=3) with pytest.raises(AssertionError): xp_assert_close(xp.asarray([100.0]), xp.asarray([102.0]), atol=1) + + +@param_assert_equal_close +@pytest.mark.skip_xp_backend(Backend.SPARSE, reason="no bool indexing by sparse arrays") +def test_assert_close_equal_none_shape(xp: ModuleType, func: Callable[..., None]): # type: ignore[no-any-explicit] + """On dask and other lazy backends, test that a shape with NaN's or None's + can be compared to a real shape. + """ + a = xp.asarray([1, 2]) + a = a[a > 1] + + func(a, xp.asarray([2])) + with pytest.raises(AssertionError): + func(a, xp.asarray([2, 3])) + with pytest.raises(AssertionError): + func(a, xp.asarray(2)) + with pytest.raises(AssertionError): + func(a, xp.asarray([3])) + + # Swap actual and desired + func(xp.asarray([2]), a) + with pytest.raises(AssertionError): + func(xp.asarray([2, 3]), a) + with pytest.raises(AssertionError): + func(xp.asarray(2), a) + with pytest.raises(AssertionError): + func(xp.asarray([3]), a) From 33b37df58e00a16964769e1c8ab6d83056a8025d Mon Sep 17 00:00:00 2001 From: Lucas Colley Date: Tue, 21 Jan 2025 12:22:57 +0000 Subject: [PATCH 4/7] tweaks --- pyproject.toml | 1 - src/array_api_extra/_delegation.py | 30 ++++++++++++++-------------- src/array_api_extra/_lib/_testing.py | 10 +++++----- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 99c7862..2fae4f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -293,7 +293,6 @@ checks = [ "all", # report on all checks, except the below "EX01", # most docstrings do not need an example "SA01", # data-apis/array-api-extra#87 - "SA04", # Missing description for See Also cross-reference "ES01", # most docstrings do not need an extended summary ] exclude = [ # don't report on objects that match any of these regex diff --git a/src/array_api_extra/_delegation.py b/src/array_api_extra/_delegation.py index c89a478..44c97ec 100644 --- a/src/array_api_extra/_delegation.py +++ b/src/array_api_extra/_delegation.py @@ -43,10 +43,10 @@ def isclose( Return a boolean array where two arrays are element-wise equal within a tolerance. The tolerance values are positive, typically very small numbers. The relative - difference `(rtol * abs(b))` and the absolute difference atol are added together to - compare against the absolute difference between a and b. + difference ``(rtol * abs(b))`` and the absolute difference `atol` are added together to + compare against the absolute difference between `a` and `b`. - NaNs are treated as equal if they are in the same place and if equal_nan=True. Infs + NaNs are treated as equal if they are in the same place and if ``equal_nan=True``. Infs are treated as equal if they are in the same place and of the same sign in both arrays. @@ -67,17 +67,17 @@ def isclose( Returns ------- Array - A boolean array of shape broadcasted from `a` and `b`, containing `True` where - ``a`` is close to ``b``, and `False` otherwise. + A boolean array of shape broadcasted from `a` and `b`, containing ``True`` where + `a` is close to `b`, and ``False`` otherwise. Warnings -------- - The default atol is not appropriate for comparing numbers with magnitudes much - smaller than one ) (see notes). + The default `atol` is not appropriate for comparing numbers with magnitudes much + smaller than one (see notes). See Also -------- - math.isclose + math.isclose : Similar function in stdlib for Python scalars. Notes ----- @@ -86,22 +86,22 @@ def isclose( absolute(a - b) <= (atol + rtol * absolute(b)) - Unlike the built-in `math.isclose`, the above equation is not symmetric in a and b, - so that `isclose(a, b)` might be different from `isclose(b, a)` in some rare + Unlike the built-in `math.isclose`, the above equation is not symmetric in `a` and `b`, + so that ``isclose(a, b)`` might be different from ``isclose(b, a)`` in some rare cases. The default value of `atol` is not appropriate when the reference value `b` has magnitude smaller than one. For example, it is unlikely that ``a = 1e-9`` and - ``b = 2e-9`` should be considered "close", yet ``isclose(1e-9, 2e-9)`` is `True` - with default settings. Be sure to select atol for the use case at hand, especially + ``b = 2e-9`` should be considered "close", yet ``isclose(1e-9, 2e-9)`` is ``True`` + with default settings. Be sure to select `atol` for the use case at hand, especially for defining the threshold below which a non-zero value in `a` will be considered "close" to a very small or zero value in `b`. The comparison of `a` and `b` uses standard broadcasting, which means that `a` and - `b` need not have the same shape in order for `isclose(a, b)` to evaluate to - `True`. + `b` need not have the same shape in order for ``isclose(a, b)`` to evaluate to + ``True``. - `isclose` is not defined for non-numeric data types. `bool` is considered a numeric + `isclose` is not defined for non-numeric data types. ``bool`` is considered a numeric data-type for this purpose. """ xp = array_namespace(a, b) if xp is None else xp diff --git a/src/array_api_extra/_lib/_testing.py b/src/array_api_extra/_lib/_testing.py index 928ad68..70152a4 100644 --- a/src/array_api_extra/_lib/_testing.py +++ b/src/array_api_extra/_lib/_testing.py @@ -74,8 +74,8 @@ def xp_assert_equal(actual: Array, desired: Array, err_msg: str = "") -> None: See Also -------- - xp_assert_close - numpy.testing.assert_array_equal + xp_assert_close : Similar function for inexact equality checks. + numpy.testing.assert_array_equal : Similar function for NumPy arrays. """ xp = _check_ns_shape_dtype(actual, desired) @@ -130,9 +130,9 @@ def xp_assert_close( See Also -------- - xp_assert_equal - isclose - numpy.testing.assert_allclose + xp_assert_equal : Similar function for exact equality checks. + isclose : Public function for checking closeness. + numpy.testing.assert_allclose : Similar function for NumPy arrays. Notes ----- From 542da616685fb322c8666d957d1d09f1ccd298dc Mon Sep 17 00:00:00 2001 From: Lucas Colley Date: Tue, 21 Jan 2025 12:25:31 +0000 Subject: [PATCH 5/7] lockfile --- pixi.lock | 255 +++++++++++++++++++++++++++--------------------------- 1 file changed, 126 insertions(+), 129 deletions(-) diff --git a/pixi.lock b/pixi.lock index 9107942..db42596 100644 --- a/pixi.lock +++ b/pixi.lock @@ -139,10 +139,10 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/linux-64/icu-75.1-he02047a_0.conda - - conda: https://prefix.dev/conda-forge/noarch/identify-2.6.5-pyhd8ed1ab_0.conda + - conda: https://prefix.dev/conda-forge/noarch/identify-2.6.6-pyhd8ed1ab_0.conda - conda: https://prefix.dev/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2 - - conda: https://prefix.dev/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + - conda: https://prefix.dev/conda-forge/noarch/importlib-metadata-8.6.1-pyha770c72_0.conda - conda: https://prefix.dev/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/ipython-8.31.0-pyh707e725_0.conda - conda: https://prefix.dev/conda-forge/noarch/isort-5.13.2-pyhd8ed1ab_1.conda @@ -248,8 +248,8 @@ environments: - conda: https://prefix.dev/conda-forge/linux-64/pillow-11.1.0-py312h80c1187_0.conda - conda: https://prefix.dev/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_1.conda - - conda: https://prefix.dev/conda-forge/noarch/pre-commit-4.0.1-pyha770c72_1.conda - - conda: https://prefix.dev/conda-forge/noarch/prompt-toolkit-3.0.48-pyha770c72_1.conda + - conda: https://prefix.dev/conda-forge/noarch/pre-commit-4.1.0-pyha770c72_0.conda + - conda: https://prefix.dev/conda-forge/noarch/prompt-toolkit-3.0.50-pyha770c72_0.conda - conda: https://prefix.dev/conda-forge/linux-64/psutil-6.1.1-py312h66e93f0_0.conda - conda: https://prefix.dev/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda - conda: https://prefix.dev/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda @@ -268,7 +268,7 @@ environments: - conda: https://prefix.dev/conda-forge/linux-64/python_abi-3.12-5_cp312.conda - conda: https://prefix.dev/conda-forge/linux-64/pytorch-2.5.1-cpu_mkl_py312_hf462abe_109.conda - conda: https://prefix.dev/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda - - conda: https://prefix.dev/conda-forge/linux-64/pyyaml-6.0.2-py312h66e93f0_1.conda + - conda: https://prefix.dev/conda-forge/linux-64/pyyaml-6.0.2-py312h178313f_2.conda - conda: https://prefix.dev/conda-forge/linux-64/re2-2024.07.02-h9925aae_2.conda - conda: https://prefix.dev/conda-forge/linux-64/readline-8.2-h8228510_1.conda - conda: https://prefix.dev/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda @@ -387,10 +387,10 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda - - conda: https://prefix.dev/conda-forge/noarch/identify-2.6.5-pyhd8ed1ab_0.conda + - conda: https://prefix.dev/conda-forge/noarch/identify-2.6.6-pyhd8ed1ab_0.conda - conda: https://prefix.dev/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2 - - conda: https://prefix.dev/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + - conda: https://prefix.dev/conda-forge/noarch/importlib-metadata-8.6.1-pyha770c72_0.conda - conda: https://prefix.dev/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/ipython-8.31.0-pyh707e725_0.conda - conda: https://prefix.dev/conda-forge/noarch/isort-5.13.2-pyhd8ed1ab_1.conda @@ -468,7 +468,7 @@ environments: - conda: https://prefix.dev/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_2.conda - conda: https://prefix.dev/conda-forge/noarch/networkx-3.4.2-pyh267e887_2.conda - conda: https://prefix.dev/conda-forge/noarch/nodeenv-1.9.1-pyhd8ed1ab_1.conda - - conda: https://prefix.dev/conda-forge/osx-arm64/nodejs-22.12.0-h02a13b7_0.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/nodejs-22.13.0-h02a13b7_0.conda - conda: https://prefix.dev/conda-forge/noarch/nodejs-wheel-22.13.0-pyhd8ed1ab_0.conda - conda: https://prefix.dev/conda-forge/noarch/nomkl-1.0-h5ca1d4c_0.tar.bz2 - conda: https://prefix.dev/conda-forge/osx-arm64/numba-0.60.0-py312h41cea2d_0.conda @@ -488,8 +488,8 @@ environments: - conda: https://prefix.dev/conda-forge/osx-arm64/pillow-11.1.0-py312h50aef2c_0.conda - conda: https://prefix.dev/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_1.conda - - conda: https://prefix.dev/conda-forge/noarch/pre-commit-4.0.1-pyha770c72_1.conda - - conda: https://prefix.dev/conda-forge/noarch/prompt-toolkit-3.0.48-pyha770c72_1.conda + - conda: https://prefix.dev/conda-forge/noarch/pre-commit-4.1.0-pyha770c72_0.conda + - conda: https://prefix.dev/conda-forge/noarch/prompt-toolkit-3.0.50-pyha770c72_0.conda - conda: https://prefix.dev/conda-forge/osx-arm64/psutil-6.1.1-py312hea69d52_0.conda - conda: https://prefix.dev/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda - conda: https://prefix.dev/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda @@ -508,7 +508,7 @@ environments: - conda: https://prefix.dev/conda-forge/osx-arm64/python_abi-3.12-5_cp312.conda - conda: https://prefix.dev/conda-forge/osx-arm64/pytorch-2.5.1-cpu_generic_py312_h6e42039_9.conda - conda: https://prefix.dev/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda - - conda: https://prefix.dev/conda-forge/osx-arm64/pyyaml-6.0.2-py312h024a12e_1.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/pyyaml-6.0.2-py312h998013c_2.conda - conda: https://prefix.dev/conda-forge/osx-arm64/re2-2024.07.02-h6589ca4_2.conda - conda: https://prefix.dev/conda-forge/osx-arm64/readline-8.2-h92ec313_1.conda - conda: https://prefix.dev/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda @@ -615,10 +615,10 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda - - conda: https://prefix.dev/conda-forge/noarch/identify-2.6.5-pyhd8ed1ab_0.conda + - conda: https://prefix.dev/conda-forge/noarch/identify-2.6.6-pyhd8ed1ab_0.conda - conda: https://prefix.dev/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2 - - conda: https://prefix.dev/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + - conda: https://prefix.dev/conda-forge/noarch/importlib-metadata-8.6.1-pyha770c72_0.conda - conda: https://prefix.dev/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/win-64/intel-openmp-2024.2.1-h57928b3_1083.conda - conda: https://prefix.dev/conda-forge/noarch/ipython-8.31.0-pyh7428d3b_0.conda @@ -687,7 +687,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/myst-parser-4.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/networkx-3.4.2-pyh267e887_2.conda - conda: https://prefix.dev/conda-forge/noarch/nodeenv-1.9.1-pyhd8ed1ab_1.conda - - conda: https://prefix.dev/conda-forge/win-64/nodejs-22.12.0-hfeaa22a_0.conda + - conda: https://prefix.dev/conda-forge/win-64/nodejs-22.13.0-hfeaa22a_0.conda - conda: https://prefix.dev/conda-forge/noarch/nodejs-wheel-22.13.0-pyhd8ed1ab_0.conda - conda: https://prefix.dev/conda-forge/win-64/numba-0.60.0-py312hcccf92d_0.conda - conda: https://prefix.dev/conda-forge/win-64/numpy-2.0.2-py312h49bc9c5_1.conda @@ -703,8 +703,8 @@ environments: - conda: https://prefix.dev/conda-forge/win-64/pillow-11.1.0-py312h078707f_0.conda - conda: https://prefix.dev/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_1.conda - - conda: https://prefix.dev/conda-forge/noarch/pre-commit-4.0.1-pyha770c72_1.conda - - conda: https://prefix.dev/conda-forge/noarch/prompt-toolkit-3.0.48-pyha770c72_1.conda + - conda: https://prefix.dev/conda-forge/noarch/pre-commit-4.1.0-pyha770c72_0.conda + - conda: https://prefix.dev/conda-forge/noarch/prompt-toolkit-3.0.50-pyha770c72_0.conda - conda: https://prefix.dev/conda-forge/win-64/psutil-6.1.1-py312h4389bb4_0.conda - conda: https://prefix.dev/conda-forge/win-64/pthread-stubs-0.4-h0e40799_1002.conda - conda: https://prefix.dev/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda @@ -722,7 +722,7 @@ environments: - conda: https://prefix.dev/conda-forge/win-64/python_abi-3.12-5_cp312.conda - conda: https://prefix.dev/conda-forge/win-64/pytorch-2.5.1-cpu_mkl_py312_h71c54e9_109.conda - conda: https://prefix.dev/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda - - conda: https://prefix.dev/conda-forge/win-64/pyyaml-6.0.2-py312h4389bb4_1.conda + - conda: https://prefix.dev/conda-forge/win-64/pyyaml-6.0.2-py312h31fea79_2.conda - conda: https://prefix.dev/conda-forge/win-64/re2-2024.07.02-haf4117d_2.conda - conda: https://prefix.dev/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/win-64/scipy-1.15.1-py312h928f2a1_0.conda @@ -888,10 +888,10 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/linux-64/icu-75.1-he02047a_0.conda - - conda: https://prefix.dev/conda-forge/noarch/identify-2.6.5-pyhd8ed1ab_0.conda + - conda: https://prefix.dev/conda-forge/noarch/identify-2.6.6-pyhd8ed1ab_0.conda - conda: https://prefix.dev/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2 - - conda: https://prefix.dev/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + - conda: https://prefix.dev/conda-forge/noarch/importlib-metadata-8.6.1-pyha770c72_0.conda - conda: https://prefix.dev/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/ipython-8.31.0-pyh707e725_0.conda - conda: https://prefix.dev/conda-forge/noarch/isort-5.13.2-pyhd8ed1ab_1.conda @@ -1024,8 +1024,8 @@ environments: - conda: https://prefix.dev/conda-forge/linux-64/pillow-11.1.0-py312h80c1187_0.conda - conda: https://prefix.dev/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_1.conda - - conda: https://prefix.dev/conda-forge/noarch/pre-commit-4.0.1-pyha770c72_1.conda - - conda: https://prefix.dev/conda-forge/noarch/prompt-toolkit-3.0.48-pyha770c72_1.conda + - conda: https://prefix.dev/conda-forge/noarch/pre-commit-4.1.0-pyha770c72_0.conda + - conda: https://prefix.dev/conda-forge/noarch/prompt-toolkit-3.0.50-pyha770c72_0.conda - conda: https://prefix.dev/conda-forge/linux-64/psutil-6.1.1-py312h66e93f0_0.conda - conda: https://prefix.dev/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda - conda: https://prefix.dev/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda @@ -1044,7 +1044,7 @@ environments: - conda: https://prefix.dev/conda-forge/linux-64/python_abi-3.12-5_cp312.conda - conda: https://prefix.dev/conda-forge/linux-64/pytorch-2.5.1-cuda126_mkl_py312_h968936e_309.conda - conda: https://prefix.dev/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda - - conda: https://prefix.dev/conda-forge/linux-64/pyyaml-6.0.2-py312h66e93f0_1.conda + - conda: https://prefix.dev/conda-forge/linux-64/pyyaml-6.0.2-py312h178313f_2.conda - conda: https://prefix.dev/conda-forge/linux-64/rdma-core-55.0-h5888daf_0.conda - conda: https://prefix.dev/conda-forge/linux-64/re2-2024.07.02-h9925aae_2.conda - conda: https://prefix.dev/conda-forge/linux-64/readline-8.2-h8228510_1.conda @@ -1166,10 +1166,10 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda - - conda: https://prefix.dev/conda-forge/noarch/identify-2.6.5-pyhd8ed1ab_0.conda + - conda: https://prefix.dev/conda-forge/noarch/identify-2.6.6-pyhd8ed1ab_0.conda - conda: https://prefix.dev/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2 - - conda: https://prefix.dev/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + - conda: https://prefix.dev/conda-forge/noarch/importlib-metadata-8.6.1-pyha770c72_0.conda - conda: https://prefix.dev/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/ipython-8.31.0-pyh707e725_0.conda - conda: https://prefix.dev/conda-forge/noarch/isort-5.13.2-pyhd8ed1ab_1.conda @@ -1247,7 +1247,7 @@ environments: - conda: https://prefix.dev/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_2.conda - conda: https://prefix.dev/conda-forge/noarch/networkx-3.4.2-pyh267e887_2.conda - conda: https://prefix.dev/conda-forge/noarch/nodeenv-1.9.1-pyhd8ed1ab_1.conda - - conda: https://prefix.dev/conda-forge/osx-arm64/nodejs-22.12.0-h02a13b7_0.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/nodejs-22.13.0-h02a13b7_0.conda - conda: https://prefix.dev/conda-forge/noarch/nodejs-wheel-22.13.0-pyhd8ed1ab_0.conda - conda: https://prefix.dev/conda-forge/noarch/nomkl-1.0-h5ca1d4c_0.tar.bz2 - conda: https://prefix.dev/conda-forge/osx-arm64/numba-0.60.0-py312h41cea2d_0.conda @@ -1267,8 +1267,8 @@ environments: - conda: https://prefix.dev/conda-forge/osx-arm64/pillow-11.1.0-py312h50aef2c_0.conda - conda: https://prefix.dev/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_1.conda - - conda: https://prefix.dev/conda-forge/noarch/pre-commit-4.0.1-pyha770c72_1.conda - - conda: https://prefix.dev/conda-forge/noarch/prompt-toolkit-3.0.48-pyha770c72_1.conda + - conda: https://prefix.dev/conda-forge/noarch/pre-commit-4.1.0-pyha770c72_0.conda + - conda: https://prefix.dev/conda-forge/noarch/prompt-toolkit-3.0.50-pyha770c72_0.conda - conda: https://prefix.dev/conda-forge/osx-arm64/psutil-6.1.1-py312hea69d52_0.conda - conda: https://prefix.dev/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda - conda: https://prefix.dev/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda @@ -1287,7 +1287,7 @@ environments: - conda: https://prefix.dev/conda-forge/osx-arm64/python_abi-3.12-5_cp312.conda - conda: https://prefix.dev/conda-forge/osx-arm64/pytorch-2.5.1-cpu_generic_py312_h6e42039_9.conda - conda: https://prefix.dev/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda - - conda: https://prefix.dev/conda-forge/osx-arm64/pyyaml-6.0.2-py312h024a12e_1.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/pyyaml-6.0.2-py312h998013c_2.conda - conda: https://prefix.dev/conda-forge/osx-arm64/re2-2024.07.02-h6589ca4_2.conda - conda: https://prefix.dev/conda-forge/osx-arm64/readline-8.2-h92ec313_1.conda - conda: https://prefix.dev/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda @@ -1405,10 +1405,10 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda - - conda: https://prefix.dev/conda-forge/noarch/identify-2.6.5-pyhd8ed1ab_0.conda + - conda: https://prefix.dev/conda-forge/noarch/identify-2.6.6-pyhd8ed1ab_0.conda - conda: https://prefix.dev/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2 - - conda: https://prefix.dev/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + - conda: https://prefix.dev/conda-forge/noarch/importlib-metadata-8.6.1-pyha770c72_0.conda - conda: https://prefix.dev/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/win-64/intel-openmp-2024.2.1-h57928b3_1083.conda - conda: https://prefix.dev/conda-forge/noarch/ipython-8.31.0-pyh7428d3b_0.conda @@ -1484,7 +1484,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/myst-parser-4.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/networkx-3.4.2-pyh267e887_2.conda - conda: https://prefix.dev/conda-forge/noarch/nodeenv-1.9.1-pyhd8ed1ab_1.conda - - conda: https://prefix.dev/conda-forge/win-64/nodejs-22.12.0-hfeaa22a_0.conda + - conda: https://prefix.dev/conda-forge/win-64/nodejs-22.13.0-hfeaa22a_0.conda - conda: https://prefix.dev/conda-forge/noarch/nodejs-wheel-22.13.0-pyhd8ed1ab_0.conda - conda: https://prefix.dev/conda-forge/win-64/numba-0.60.0-py312hcccf92d_0.conda - conda: https://prefix.dev/conda-forge/win-64/numpy-2.0.2-py312h49bc9c5_1.conda @@ -1500,8 +1500,8 @@ environments: - conda: https://prefix.dev/conda-forge/win-64/pillow-11.1.0-py312h078707f_0.conda - conda: https://prefix.dev/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_1.conda - - conda: https://prefix.dev/conda-forge/noarch/pre-commit-4.0.1-pyha770c72_1.conda - - conda: https://prefix.dev/conda-forge/noarch/prompt-toolkit-3.0.48-pyha770c72_1.conda + - conda: https://prefix.dev/conda-forge/noarch/pre-commit-4.1.0-pyha770c72_0.conda + - conda: https://prefix.dev/conda-forge/noarch/prompt-toolkit-3.0.50-pyha770c72_0.conda - conda: https://prefix.dev/conda-forge/win-64/psutil-6.1.1-py312h4389bb4_0.conda - conda: https://prefix.dev/conda-forge/win-64/pthread-stubs-0.4-h0e40799_1002.conda - conda: https://prefix.dev/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda @@ -1519,7 +1519,7 @@ environments: - conda: https://prefix.dev/conda-forge/win-64/python_abi-3.12-5_cp312.conda - conda: https://prefix.dev/conda-forge/win-64/pytorch-2.5.1-cuda126_mkl_py312_h836905d_309.conda - conda: https://prefix.dev/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda - - conda: https://prefix.dev/conda-forge/win-64/pyyaml-6.0.2-py312h4389bb4_1.conda + - conda: https://prefix.dev/conda-forge/win-64/pyyaml-6.0.2-py312h31fea79_2.conda - conda: https://prefix.dev/conda-forge/win-64/re2-2024.07.02-haf4117d_2.conda - conda: https://prefix.dev/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/win-64/scipy-1.15.1-py312h928f2a1_0.conda @@ -1630,7 +1630,7 @@ environments: - conda: https://prefix.dev/conda-forge/linux-64/python-3.12.8-h9e4cc4f_1_cpython.conda - conda: https://prefix.dev/conda-forge/linux-64/python_abi-3.12-5_cp312.conda - conda: https://prefix.dev/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda - - conda: https://prefix.dev/conda-forge/linux-64/pyyaml-6.0.2-py312h66e93f0_1.conda + - conda: https://prefix.dev/conda-forge/linux-64/pyyaml-6.0.2-py312h178313f_2.conda - conda: https://prefix.dev/conda-forge/linux-64/readline-8.2-h8228510_1.conda - conda: https://prefix.dev/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/snowballstemmer-2.2.0-pyhd8ed1ab_0.tar.bz2 @@ -1693,7 +1693,7 @@ environments: - conda: https://prefix.dev/conda-forge/osx-arm64/python-3.12.8-hc22306f_1_cpython.conda - conda: https://prefix.dev/conda-forge/osx-arm64/python_abi-3.12-5_cp312.conda - conda: https://prefix.dev/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda - - conda: https://prefix.dev/conda-forge/osx-arm64/pyyaml-6.0.2-py312h024a12e_1.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/pyyaml-6.0.2-py312h998013c_2.conda - conda: https://prefix.dev/conda-forge/osx-arm64/readline-8.2-h92ec313_1.conda - conda: https://prefix.dev/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/snowballstemmer-2.2.0-pyhd8ed1ab_0.tar.bz2 @@ -1754,7 +1754,7 @@ environments: - conda: https://prefix.dev/conda-forge/win-64/python-3.12.8-h3f84c4b_1_cpython.conda - conda: https://prefix.dev/conda-forge/win-64/python_abi-3.12-5_cp312.conda - conda: https://prefix.dev/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda - - conda: https://prefix.dev/conda-forge/win-64/pyyaml-6.0.2-py312h4389bb4_1.conda + - conda: https://prefix.dev/conda-forge/win-64/pyyaml-6.0.2-py312h31fea79_2.conda - conda: https://prefix.dev/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/snowballstemmer-2.2.0-pyhd8ed1ab_0.tar.bz2 - conda: https://prefix.dev/conda-forge/noarch/soupsieve-2.5-pyhd8ed1ab_1.conda @@ -1815,7 +1815,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/linux-64/icu-75.1-he02047a_0.conda - - conda: https://prefix.dev/conda-forge/noarch/identify-2.6.5-pyhd8ed1ab_0.conda + - conda: https://prefix.dev/conda-forge/noarch/identify-2.6.6-pyhd8ed1ab_0.conda - conda: https://prefix.dev/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2 - conda: https://prefix.dev/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda @@ -1856,7 +1856,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://prefix.dev/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_1.conda - - conda: https://prefix.dev/conda-forge/noarch/pre-commit-4.0.1-pyha770c72_1.conda + - conda: https://prefix.dev/conda-forge/noarch/pre-commit-4.1.0-pyha770c72_0.conda - conda: https://prefix.dev/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda - conda: https://prefix.dev/conda-forge/noarch/pygments-2.19.1-pyhd8ed1ab_0.conda - conda: https://prefix.dev/conda-forge/noarch/pylint-3.3.3-pyhd8ed1ab_0.conda @@ -1865,7 +1865,7 @@ environments: - conda: https://prefix.dev/conda-forge/linux-64/python-3.12.8-h9e4cc4f_1_cpython.conda - conda: https://prefix.dev/conda-forge/linux-64/python_abi-3.12-5_cp312.conda - conda: https://prefix.dev/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda - - conda: https://prefix.dev/conda-forge/linux-64/pyyaml-6.0.2-py312h66e93f0_1.conda + - conda: https://prefix.dev/conda-forge/linux-64/pyyaml-6.0.2-py312h178313f_2.conda - conda: https://prefix.dev/conda-forge/linux-64/readline-8.2-h8228510_1.conda - conda: https://prefix.dev/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-75.8.0-pyhff2d567_0.conda @@ -1919,7 +1919,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda - - conda: https://prefix.dev/conda-forge/noarch/identify-2.6.5-pyhd8ed1ab_0.conda + - conda: https://prefix.dev/conda-forge/noarch/identify-2.6.6-pyhd8ed1ab_0.conda - conda: https://prefix.dev/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2 - conda: https://prefix.dev/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda @@ -1944,7 +1944,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_1.conda - conda: https://prefix.dev/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_2.conda - conda: https://prefix.dev/conda-forge/noarch/nodeenv-1.9.1-pyhd8ed1ab_1.conda - - conda: https://prefix.dev/conda-forge/osx-arm64/nodejs-22.12.0-h02a13b7_0.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/nodejs-22.13.0-h02a13b7_0.conda - conda: https://prefix.dev/conda-forge/noarch/nodejs-wheel-22.13.0-pyhd8ed1ab_0.conda - conda: https://prefix.dev/conda-forge/osx-arm64/numpy-2.0.2-py312h94ee1e1_1.conda - conda: https://prefix.dev/conda-forge/noarch/numpydoc-1.8.0-pyhd8ed1ab_1.conda @@ -1952,7 +1952,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://prefix.dev/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_1.conda - - conda: https://prefix.dev/conda-forge/noarch/pre-commit-4.0.1-pyha770c72_1.conda + - conda: https://prefix.dev/conda-forge/noarch/pre-commit-4.1.0-pyha770c72_0.conda - conda: https://prefix.dev/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda - conda: https://prefix.dev/conda-forge/noarch/pygments-2.19.1-pyhd8ed1ab_0.conda - conda: https://prefix.dev/conda-forge/noarch/pylint-3.3.3-pyhd8ed1ab_0.conda @@ -1961,7 +1961,7 @@ environments: - conda: https://prefix.dev/conda-forge/osx-arm64/python-3.12.8-hc22306f_1_cpython.conda - conda: https://prefix.dev/conda-forge/osx-arm64/python_abi-3.12-5_cp312.conda - conda: https://prefix.dev/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda - - conda: https://prefix.dev/conda-forge/osx-arm64/pyyaml-6.0.2-py312h024a12e_1.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/pyyaml-6.0.2-py312h998013c_2.conda - conda: https://prefix.dev/conda-forge/osx-arm64/readline-8.2-h92ec313_1.conda - conda: https://prefix.dev/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-75.8.0-pyhff2d567_0.conda @@ -2013,7 +2013,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda - - conda: https://prefix.dev/conda-forge/noarch/identify-2.6.5-pyhd8ed1ab_0.conda + - conda: https://prefix.dev/conda-forge/noarch/identify-2.6.6-pyhd8ed1ab_0.conda - conda: https://prefix.dev/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2 - conda: https://prefix.dev/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda @@ -2037,7 +2037,7 @@ environments: - conda: https://prefix.dev/conda-forge/win-64/mkl-2024.2.2-h66d3029_15.conda - conda: https://prefix.dev/conda-forge/noarch/mypy_extensions-1.0.0-pyha770c72_1.conda - conda: https://prefix.dev/conda-forge/noarch/nodeenv-1.9.1-pyhd8ed1ab_1.conda - - conda: https://prefix.dev/conda-forge/win-64/nodejs-22.12.0-hfeaa22a_0.conda + - conda: https://prefix.dev/conda-forge/win-64/nodejs-22.13.0-hfeaa22a_0.conda - conda: https://prefix.dev/conda-forge/noarch/nodejs-wheel-22.13.0-pyhd8ed1ab_0.conda - conda: https://prefix.dev/conda-forge/win-64/numpy-2.0.2-py312h49bc9c5_1.conda - conda: https://prefix.dev/conda-forge/noarch/numpydoc-1.8.0-pyhd8ed1ab_1.conda @@ -2045,7 +2045,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://prefix.dev/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_1.conda - - conda: https://prefix.dev/conda-forge/noarch/pre-commit-4.0.1-pyha770c72_1.conda + - conda: https://prefix.dev/conda-forge/noarch/pre-commit-4.1.0-pyha770c72_0.conda - conda: https://prefix.dev/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda - conda: https://prefix.dev/conda-forge/noarch/pygments-2.19.1-pyhd8ed1ab_0.conda - conda: https://prefix.dev/conda-forge/noarch/pylint-3.3.3-pyhd8ed1ab_0.conda @@ -2054,7 +2054,7 @@ environments: - conda: https://prefix.dev/conda-forge/win-64/python-3.12.8-h3f84c4b_1_cpython.conda - conda: https://prefix.dev/conda-forge/win-64/python_abi-3.12-5_cp312.conda - conda: https://prefix.dev/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda - - conda: https://prefix.dev/conda-forge/win-64/pyyaml-6.0.2-py312h4389bb4_1.conda + - conda: https://prefix.dev/conda-forge/win-64/pyyaml-6.0.2-py312h31fea79_2.conda - conda: https://prefix.dev/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-75.8.0-pyhff2d567_0.conda - conda: https://prefix.dev/conda-forge/noarch/snowballstemmer-2.2.0-pyhd8ed1ab_0.tar.bz2 @@ -2274,7 +2274,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda - - conda: https://prefix.dev/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + - conda: https://prefix.dev/conda-forge/noarch/importlib-metadata-8.6.1-pyha770c72_0.conda - conda: https://prefix.dev/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/jax-0.4.35-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/linux-64/jaxlib-0.4.35-cpu_py310h430587c_0.conda @@ -2375,7 +2375,7 @@ environments: - conda: https://prefix.dev/conda-forge/linux-64/python_abi-3.10-5_cp310.conda - conda: https://prefix.dev/conda-forge/linux-64/pytorch-2.5.1-cpu_mkl_py310_h1c118fa_109.conda - conda: https://prefix.dev/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda - - conda: https://prefix.dev/conda-forge/linux-64/pyyaml-6.0.2-py310ha75aee5_1.conda + - conda: https://prefix.dev/conda-forge/linux-64/pyyaml-6.0.2-py310h89163eb_2.conda - conda: https://prefix.dev/conda-forge/linux-64/re2-2024.07.02-h9925aae_2.conda - conda: https://prefix.dev/conda-forge/linux-64/readline-8.2-h8228510_1.conda - conda: https://prefix.dev/conda-forge/linux-64/s2n-1.5.11-h072c03f_0.conda @@ -2455,7 +2455,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda - - conda: https://prefix.dev/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + - conda: https://prefix.dev/conda-forge/noarch/importlib-metadata-8.6.1-pyha770c72_0.conda - conda: https://prefix.dev/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/jax-0.4.35-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/osx-arm64/jaxlib-0.4.35-cpu_py310h604521f_0.conda @@ -2548,7 +2548,7 @@ environments: - conda: https://prefix.dev/conda-forge/osx-arm64/python_abi-3.10-5_cp310.conda - conda: https://prefix.dev/conda-forge/osx-arm64/pytorch-2.5.1-cpu_generic_py310_h3256795_9.conda - conda: https://prefix.dev/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda - - conda: https://prefix.dev/conda-forge/osx-arm64/pyyaml-6.0.2-py310h493c2e1_1.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/pyyaml-6.0.2-py310hc74094e_2.conda - conda: https://prefix.dev/conda-forge/osx-arm64/re2-2024.07.02-h6589ca4_2.conda - conda: https://prefix.dev/conda-forge/osx-arm64/readline-8.2-h92ec313_1.conda - conda: https://prefix.dev/conda-forge/osx-arm64/scipy-1.15.1-py310hd50a768_0.conda @@ -2616,7 +2616,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda - - conda: https://prefix.dev/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + - conda: https://prefix.dev/conda-forge/noarch/importlib-metadata-8.6.1-pyha770c72_0.conda - conda: https://prefix.dev/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/win-64/intel-openmp-2024.2.1-h57928b3_1083.conda - conda: https://prefix.dev/conda-forge/noarch/jinja2-3.1.5-pyhd8ed1ab_0.conda @@ -2697,7 +2697,7 @@ environments: - conda: https://prefix.dev/conda-forge/win-64/python_abi-3.10-5_cp310.conda - conda: https://prefix.dev/conda-forge/win-64/pytorch-2.5.1-cpu_mkl_py310_h45c3603_109.conda - conda: https://prefix.dev/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda - - conda: https://prefix.dev/conda-forge/win-64/pyyaml-6.0.2-py310ha8f682b_1.conda + - conda: https://prefix.dev/conda-forge/win-64/pyyaml-6.0.2-py310h38315fa_2.conda - conda: https://prefix.dev/conda-forge/win-64/re2-2024.07.02-haf4117d_2.conda - conda: https://prefix.dev/conda-forge/win-64/scipy-1.15.1-py310h164493e_0.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-75.8.0-pyhff2d567_0.conda @@ -2824,7 +2824,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda - - conda: https://prefix.dev/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + - conda: https://prefix.dev/conda-forge/noarch/importlib-metadata-8.6.1-pyha770c72_0.conda - conda: https://prefix.dev/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/jax-0.4.35-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/linux-64/jaxlib-0.4.35-cuda126py310h5e1a0f3_200.conda @@ -2952,7 +2952,7 @@ environments: - conda: https://prefix.dev/conda-forge/linux-64/python_abi-3.10-5_cp310.conda - conda: https://prefix.dev/conda-forge/linux-64/pytorch-2.5.1-cuda126_mkl_py310_h069c2fa_309.conda - conda: https://prefix.dev/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda - - conda: https://prefix.dev/conda-forge/linux-64/pyyaml-6.0.2-py310ha75aee5_1.conda + - conda: https://prefix.dev/conda-forge/linux-64/pyyaml-6.0.2-py310h89163eb_2.conda - conda: https://prefix.dev/conda-forge/linux-64/rdma-core-55.0-h5888daf_0.conda - conda: https://prefix.dev/conda-forge/linux-64/re2-2024.07.02-h9925aae_2.conda - conda: https://prefix.dev/conda-forge/linux-64/readline-8.2-h8228510_1.conda @@ -3035,7 +3035,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda - - conda: https://prefix.dev/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + - conda: https://prefix.dev/conda-forge/noarch/importlib-metadata-8.6.1-pyha770c72_0.conda - conda: https://prefix.dev/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/jax-0.4.35-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/osx-arm64/jaxlib-0.4.35-cpu_py310h604521f_0.conda @@ -3128,7 +3128,7 @@ environments: - conda: https://prefix.dev/conda-forge/osx-arm64/python_abi-3.10-5_cp310.conda - conda: https://prefix.dev/conda-forge/osx-arm64/pytorch-2.5.1-cpu_generic_py310_h3256795_9.conda - conda: https://prefix.dev/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda - - conda: https://prefix.dev/conda-forge/osx-arm64/pyyaml-6.0.2-py310h493c2e1_1.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/pyyaml-6.0.2-py310hc74094e_2.conda - conda: https://prefix.dev/conda-forge/osx-arm64/re2-2024.07.02-h6589ca4_2.conda - conda: https://prefix.dev/conda-forge/osx-arm64/readline-8.2-h92ec313_1.conda - conda: https://prefix.dev/conda-forge/osx-arm64/scipy-1.15.1-py310hd50a768_0.conda @@ -3207,7 +3207,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/hpack-4.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_1.conda - - conda: https://prefix.dev/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda + - conda: https://prefix.dev/conda-forge/noarch/importlib-metadata-8.6.1-pyha770c72_0.conda - conda: https://prefix.dev/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/win-64/intel-openmp-2024.2.1-h57928b3_1083.conda - conda: https://prefix.dev/conda-forge/noarch/jinja2-3.1.5-pyhd8ed1ab_0.conda @@ -3295,7 +3295,7 @@ environments: - conda: https://prefix.dev/conda-forge/win-64/python_abi-3.10-5_cp310.conda - conda: https://prefix.dev/conda-forge/win-64/pytorch-2.5.1-cuda126_mkl_py310_h6518810_309.conda - conda: https://prefix.dev/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda - - conda: https://prefix.dev/conda-forge/win-64/pyyaml-6.0.2-py310ha8f682b_1.conda + - conda: https://prefix.dev/conda-forge/win-64/pyyaml-6.0.2-py310h38315fa_2.conda - conda: https://prefix.dev/conda-forge/win-64/re2-2024.07.02-haf4117d_2.conda - conda: https://prefix.dev/conda-forge/win-64/scipy-1.15.1-py310h164493e_0.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-75.8.0-pyhff2d567_0.conda @@ -3659,7 +3659,7 @@ packages: - pypi: . name: array-api-extra version: 0.6.1.dev0 - sha256: a1b2089d0b3ce0b6e017ad54a21903a53aeafe710f3a2ed821b648c0e91bc4dd + sha256: 22c9e9830a088aff4480ecea8495d2ebcf91f65596886a12012bebfb238181d6 requires_dist: - array-api-compat>=1.10.0,<2 requires_python: '>=3.10' @@ -6377,9 +6377,9 @@ packages: purls: [] size: 11857802 timestamp: 1720853997952 -- conda: https://prefix.dev/conda-forge/noarch/identify-2.6.5-pyhd8ed1ab_0.conda - sha256: e8ea11b8e39a98a9c34efb5c21c3fca718e31e1f41fd9ae5f6918b8eb402da59 - md5: c1b0f663ff141265d1be1242259063f0 +- conda: https://prefix.dev/conda-forge/noarch/identify-2.6.6-pyhd8ed1ab_0.conda + sha256: bb7483a113966d3d10b6e91edb79e7006f050fd40a842935848c15d12eff56d3 + md5: d751c3b4a973ed15b57be90d68c716d1 depends: - python >=3.9 - ukkonen @@ -6387,8 +6387,8 @@ packages: license_family: MIT purls: - pkg:pypi/identify?source=hash-mapping - size: 78415 - timestamp: 1736026672643 + size: 78562 + timestamp: 1737421654786 - conda: https://prefix.dev/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda sha256: d7a472c9fd479e2e8dcb83fb8d433fce971ea369d704ece380e876f9c3494e87 md5: 39a4f67be3286c86d696df570b1201b7 @@ -6411,18 +6411,18 @@ packages: - pkg:pypi/imagesize?source=hash-mapping size: 10164 timestamp: 1656939625410 -- conda: https://prefix.dev/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_1.conda - sha256: 13766b88fc5b23581530d3a0287c0c58ad82f60401afefab283bf158d2be55a9 - md5: 315607a3030ad5d5227e76e0733798ff +- conda: https://prefix.dev/conda-forge/noarch/importlib-metadata-8.6.1-pyha770c72_0.conda + sha256: 598951ebdb23e25e4cec4bbff0ae369cec65ead80b50bc08b441d8e54de5cf03 + md5: f4b39bf00c69f56ac01e020ebfac066c depends: - python >=3.9 - zipp >=0.5 license: Apache-2.0 license_family: APACHE purls: - - pkg:pypi/importlib-metadata?source=hash-mapping - size: 28623 - timestamp: 1733223207185 + - pkg:pypi/importlib-metadata?source=compressed-mapping + size: 29141 + timestamp: 1737420302391 - conda: https://prefix.dev/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda sha256: 0ec8f4d02053cd03b0f3e63168316530949484f80e16f5e2fb199a1d117a89ca md5: 6837f3eff7dcea42ecd714ce1ac2b108 @@ -10450,30 +10450,28 @@ packages: purls: [] size: 21796933 timestamp: 1734113054756 -- conda: https://prefix.dev/conda-forge/osx-arm64/nodejs-22.12.0-h02a13b7_0.conda - sha256: 0d6f31cf19a3671aa7a6473392447801b4231ec832c8526b9a975b5c01930343 - md5: 293e451c0590d9029036f58aa109bc5d +- conda: https://prefix.dev/conda-forge/osx-arm64/nodejs-22.13.0-h02a13b7_0.conda + sha256: d390651526630468e385a74474bb3f17849861182257c161bbca8fca7734d578 + md5: 93cd91b998422ebf2dace6c13c1842ce depends: - __osx >=11.0 - icu >=75.1,<76.0a0 - libcxx >=18 - - libuv >=1.49.2,<2.0a0 + - libuv >=1.50.0,<2.0a0 - libzlib >=1.3.1,<2.0a0 - openssl >=3.4.0,<4.0a0 - zlib license: MIT - license_family: MIT purls: [] - size: 15429539 - timestamp: 1734125056499 -- conda: https://prefix.dev/conda-forge/win-64/nodejs-22.12.0-hfeaa22a_0.conda - sha256: 43d728b5d56ffeea5e95308218d7045acabcd318ced6ad4f2e89f295666aadda - md5: 4e1fa4ec4147ec961f75a3b6f7a558af + size: 15490642 + timestamp: 1737401388520 +- conda: https://prefix.dev/conda-forge/win-64/nodejs-22.13.0-hfeaa22a_0.conda + sha256: 2e72f510715960a0579a2a5452104d20044e8ba74742b87899e24c11cb72d578 + md5: bd7dde69cfd032aec6ba645297315aff license: MIT - license_family: MIT purls: [] - size: 26256775 - timestamp: 1734108943224 + size: 26232097 + timestamp: 1737384238153 - conda: https://prefix.dev/conda-forge/noarch/nodejs-wheel-22.13.0-pyhd8ed1ab_0.conda sha256: 6c36ec2f56105bd6bfe572b8ce7b6b4eb770a2c3db3655d99ead99c5a8b1a5d7 md5: 17efc155ae707cdc9fe2970ebd74ee01 @@ -11385,9 +11383,9 @@ packages: - pkg:pypi/pluggy?source=hash-mapping size: 23595 timestamp: 1733222855563 -- conda: https://prefix.dev/conda-forge/noarch/pre-commit-4.0.1-pyha770c72_1.conda - sha256: 3cfe4c777f1bb3f869cefd732357c7c657df7f0bba5c11cd64ced21e0b0a2b5b - md5: d0ea6ed474bf7f6db88fc85e6dc809b1 +- conda: https://prefix.dev/conda-forge/noarch/pre-commit-4.1.0-pyha770c72_0.conda + sha256: b260b4b47956b654232f698be1b757935268830a808040aff2006d08953e9e32 + md5: 5353f5eb201a9415b12385e35ed1148d depends: - cfgv >=2.0.0 - identify >=1.0.0 @@ -11399,22 +11397,21 @@ packages: license_family: MIT purls: - pkg:pypi/pre-commit?source=hash-mapping - size: 193591 - timestamp: 1734267205422 -- conda: https://prefix.dev/conda-forge/noarch/prompt-toolkit-3.0.48-pyha770c72_1.conda - sha256: 79fb7d1eeb490d4cc1b79f781bb59fe302ae38cf0a30907ecde75a7d399796cc - md5: 368d4aa48358439e07a97ae237491785 + size: 195101 + timestamp: 1737408051494 +- conda: https://prefix.dev/conda-forge/noarch/prompt-toolkit-3.0.50-pyha770c72_0.conda + sha256: 0749c49a349bf55b8539ce5addce559b77592165da622944a51c630e94d97889 + md5: 7d823138f550b14ecae927a5ff3286de depends: - python >=3.9 - wcwidth constrains: - - prompt_toolkit 3.0.48 + - prompt_toolkit 3.0.50 license: BSD-3-Clause - license_family: BSD purls: - pkg:pypi/prompt-toolkit?source=hash-mapping - size: 269848 - timestamp: 1733302634979 + size: 271905 + timestamp: 1737453457168 - conda: https://prefix.dev/conda-forge/linux-64/psutil-6.1.1-py310ha75aee5_0.conda sha256: a643a57e5338fb3a154c5d57fdc72d80170cf7868f20acbbeedde014195f0d92 md5: 00838ea1d4e87b1e6e2552bba98cc899 @@ -12727,9 +12724,9 @@ packages: - pkg:pypi/pytz?source=hash-mapping size: 188538 timestamp: 1706886944988 -- conda: https://prefix.dev/conda-forge/linux-64/pyyaml-6.0.2-py310ha75aee5_1.conda - sha256: bf6002aef0fd9753fa6de54e82307b2d7e67a1d701dba018869471426078d5d1 - md5: 0d4c5c76ae5f5aac6f0be419963a19dd +- conda: https://prefix.dev/conda-forge/linux-64/pyyaml-6.0.2-py310h89163eb_2.conda + sha256: 5fba7f5babcac872c72f6509c25331bcfac4f8f5031f0102530a41b41336fce6 + md5: fd343408e64cf1e273ab7c710da374db depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 @@ -12739,12 +12736,12 @@ packages: license: MIT license_family: MIT purls: - - pkg:pypi/pyyaml?source=hash-mapping - size: 182609 - timestamp: 1725456280173 -- conda: https://prefix.dev/conda-forge/linux-64/pyyaml-6.0.2-py312h66e93f0_1.conda - sha256: a60705971e958724168f2ebbb8ed4853067f1d3f7059843df3903e3092bbcffa - md5: 549e5930e768548a89c23f595dac5a95 + - pkg:pypi/pyyaml?source=compressed-mapping + size: 182769 + timestamp: 1737454971552 +- conda: https://prefix.dev/conda-forge/linux-64/pyyaml-6.0.2-py312h178313f_2.conda + sha256: 159cba13a93b3fe084a1eb9bda0a07afc9148147647f0d437c3c3da60980503b + md5: cf2485f39740de96e2a7f2bb18ed2fee depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 @@ -12755,11 +12752,11 @@ packages: license_family: MIT purls: - pkg:pypi/pyyaml?source=hash-mapping - size: 206553 - timestamp: 1725456256213 -- conda: https://prefix.dev/conda-forge/osx-arm64/pyyaml-6.0.2-py310h493c2e1_1.conda - sha256: 04b7adb2f79264b2556c79924a523f8c5b297dfaa40f01c8b112f06e388001da - md5: 4b086c01e4c1ae219d1e139893841ae7 + size: 206903 + timestamp: 1737454910324 +- conda: https://prefix.dev/conda-forge/osx-arm64/pyyaml-6.0.2-py310hc74094e_2.conda + sha256: 0c46719507e1664b1085f2142b8250250c6aae01ec367d18068688efeba445ec + md5: b8be3d77488c580d2fd81c9bb3cacdf1 depends: - __osx >=11.0 - python >=3.10,<3.11.0a0 @@ -12770,11 +12767,11 @@ packages: license_family: MIT purls: - pkg:pypi/pyyaml?source=hash-mapping - size: 162312 - timestamp: 1725456439220 -- conda: https://prefix.dev/conda-forge/osx-arm64/pyyaml-6.0.2-py312h024a12e_1.conda - sha256: b06f1c15fb39695bbf707ae8fb554b9a77519af577b5556784534c7db10b52e3 - md5: 1ee23620cf46cb15900f70a1300bae55 + size: 166853 + timestamp: 1737454973579 +- conda: https://prefix.dev/conda-forge/osx-arm64/pyyaml-6.0.2-py312h998013c_2.conda + sha256: ad225ad24bfd60f7719709791345042c3cb32da1692e62bd463b084cf140e00d + md5: 68149ed4d4e9e1c42d2ba1f27f08ca96 depends: - __osx >=11.0 - python >=3.12,<3.13.0a0 @@ -12785,11 +12782,11 @@ packages: license_family: MIT purls: - pkg:pypi/pyyaml?source=hash-mapping - size: 187143 - timestamp: 1725456547263 -- conda: https://prefix.dev/conda-forge/win-64/pyyaml-6.0.2-py310ha8f682b_1.conda - sha256: b30056440fdff1d52e96303f539ba3b4a33c19070993a75cc15c5414cb2a8b1d - md5: 308f62d05cbcbc633eeab4843def3b51 + size: 192148 + timestamp: 1737454886351 +- conda: https://prefix.dev/conda-forge/win-64/pyyaml-6.0.2-py310h38315fa_2.conda + sha256: 49dd492bdf2c479118ca9d61a59ce259594853d367a1a0548926f41a6e734724 + md5: 9986c3731bb820db0830dd0825c26cf9 depends: - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 @@ -12801,11 +12798,11 @@ packages: license_family: MIT purls: - pkg:pypi/pyyaml?source=hash-mapping - size: 156987 - timestamp: 1725456772886 -- conda: https://prefix.dev/conda-forge/win-64/pyyaml-6.0.2-py312h4389bb4_1.conda - sha256: fa3ede1fa2ed6ea0a51095aeea398f6f0f54af036c4bc525726107cfb49229d5 - md5: afb7809721516919c276b45f847c085f + size: 157941 + timestamp: 1737455030235 +- conda: https://prefix.dev/conda-forge/win-64/pyyaml-6.0.2-py312h31fea79_2.conda + sha256: 76fec03ef7e67e37724873e1f805131fb88efb57f19e9a77b4da616068ef5c28 + md5: ba00a2e5059c1fde96459858537cc8f5 depends: - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 @@ -12817,8 +12814,8 @@ packages: license_family: MIT purls: - pkg:pypi/pyyaml?source=hash-mapping - size: 181227 - timestamp: 1725456516473 + size: 181734 + timestamp: 1737455207230 - conda: https://prefix.dev/conda-forge/linux-64/rdma-core-55.0-h5888daf_0.conda sha256: 3715a51f1ea6e3765f19b6db90a7edb77a3b5aa201a4f09cbd51a678e8609a88 md5: fd94951ea305bdfe6fb3939db3fb7ce2 @@ -13056,7 +13053,7 @@ packages: license: MIT license_family: MIT purls: - - pkg:pypi/setuptools?source=compressed-mapping + - pkg:pypi/setuptools?source=hash-mapping size: 775598 timestamp: 1736512753595 - conda: https://prefix.dev/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda From 42da174c2aff3806161912db6527acb2d96f4962 Mon Sep 17 00:00:00 2001 From: Lucas Colley Date: Tue, 21 Jan 2025 12:27:46 +0000 Subject: [PATCH 6/7] lint --- src/array_api_extra/_delegation.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/array_api_extra/_delegation.py b/src/array_api_extra/_delegation.py index 44c97ec..b7bc9a8 100644 --- a/src/array_api_extra/_delegation.py +++ b/src/array_api_extra/_delegation.py @@ -43,11 +43,11 @@ def isclose( Return a boolean array where two arrays are element-wise equal within a tolerance. The tolerance values are positive, typically very small numbers. The relative - difference ``(rtol * abs(b))`` and the absolute difference `atol` are added together to - compare against the absolute difference between `a` and `b`. + difference ``(rtol * abs(b))`` and the absolute difference `atol` are added together + to compare against the absolute difference between `a` and `b`. - NaNs are treated as equal if they are in the same place and if ``equal_nan=True``. Infs - are treated as equal if they are in the same place and of the same sign in both + NaNs are treated as equal if they are in the same place and if ``equal_nan=True``. + Infs are treated as equal if they are in the same place and of the same sign in both arrays. Parameters @@ -86,7 +86,8 @@ def isclose( absolute(a - b) <= (atol + rtol * absolute(b)) - Unlike the built-in `math.isclose`, the above equation is not symmetric in `a` and `b`, + Unlike the built-in `math.isclose`, + the above equation is not symmetric in `a` and `b`, so that ``isclose(a, b)`` might be different from ``isclose(b, a)`` in some rare cases. @@ -101,8 +102,8 @@ def isclose( `b` need not have the same shape in order for ``isclose(a, b)`` to evaluate to ``True``. - `isclose` is not defined for non-numeric data types. ``bool`` is considered a numeric - data-type for this purpose. + `isclose` is not defined for non-numeric data types. + ``bool`` is considered a numeric data-type for this purpose. """ xp = array_namespace(a, b) if xp is None else xp From 38e88b5425a830e8d88da6fd09d61cf11a39c073 Mon Sep 17 00:00:00 2001 From: crusaderky Date: Tue, 21 Jan 2025 14:12:40 +0000 Subject: [PATCH 7/7] code review --- src/array_api_extra/_lib/_funcs.py | 3 +-- src/array_api_extra/_lib/_testing.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/array_api_extra/_lib/_funcs.py b/src/array_api_extra/_lib/_funcs.py index 4b6769a..0af265e 100644 --- a/src/array_api_extra/_lib/_funcs.py +++ b/src/array_api_extra/_lib/_funcs.py @@ -312,10 +312,9 @@ def isclose( rtol: float = 1e-05, atol: float = 1e-08, equal_nan: bool = False, - xp: ModuleType | None = None, + xp: ModuleType, ) -> Array: # numpydoc ignore=PR01,RT01 """See docstring in array_api_extra._delegation.""" - xp = array_namespace(a, b) if xp is None else xp a_inexact = xp.isdtype(a.dtype, ("real floating", "complex floating")) b_inexact = xp.isdtype(b.dtype, ("real floating", "complex floating")) diff --git a/src/array_api_extra/_lib/_testing.py b/src/array_api_extra/_lib/_testing.py index 70152a4..cc0d055 100644 --- a/src/array_api_extra/_lib/_testing.py +++ b/src/array_api_extra/_lib/_testing.py @@ -136,7 +136,7 @@ def xp_assert_close( Notes ----- - The default `atol` and `rtol` differ from `xp.all(xpx.allclose(a, b))`. + The default `atol` and `rtol` differ from `xp.all(xpx.isclose(a, b))`. """ xp = _check_ns_shape_dtype(actual, desired)