From 809a4480dc48ca2c5a948f31155518e0141171b2 Mon Sep 17 00:00:00 2001
From: Hameer Abbasi <2190658+hameerabbasi@users.noreply.github.com>
Date: Mon, 10 Mar 2025 11:41:33 +0100
Subject: [PATCH 1/7] Add specification for the `__binsparse__` protocol.
---
.gitignore | 4 ++
spec/draft/design_topics/data_interchange.rst | 33 +++++++++++++---
spec/draft/extensions/index.rst | 1 +
spec/draft/extensions/sparse_interchange.rst | 39 +++++++++++++++++++
src/array_api_stubs/_draft/array_object.py | 36 +++++++++++++++++
.../_draft/creation_functions.py | 27 +++++++++++++
6 files changed, 135 insertions(+), 5 deletions(-)
create mode 100644 spec/draft/extensions/sparse_interchange.rst
diff --git a/.gitignore b/.gitignore
index cc40a3b43..c261fc313 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,3 +35,7 @@ tmp/
*.egg
dist/
.DS_STORE
+
+# pixi environments
+.pixi
+*.egg-info
diff --git a/spec/draft/design_topics/data_interchange.rst b/spec/draft/design_topics/data_interchange.rst
index 3b3040672..041f2d4f8 100644
--- a/spec/draft/design_topics/data_interchange.rst
+++ b/spec/draft/design_topics/data_interchange.rst
@@ -85,17 +85,40 @@ page gives a high-level specification for data exchange in Python using DLPack.
below. They are not required to return an array object from ``from_dlpack``
which conforms to this standard.
+binsparse: Extending to sparse arrays
+-------------------------------------
+
+Sparse arrays can be represented in-memory by a collection of 1-dimensional and 2-dimensional
+dense arrays, alongside some metadata on how to interpret these arrays. This allows us to re-use
+the DLPack protocol for the storage of the constituent arrays. The work of specifying the
+accompanying metadata has already been performed by the
+`binsparse specification `_.
+
+While initially intended to target file formats, binsparse has relatively few requirements from
+back-ends:
+
+1. The ability to represent and parse JSON.
+2. To be able to represent/store a key-value store of 1-dimensional (and optionally 2-dimensional)
+ arrays.
+
+It is the only such specification for sparse representations to have these minimal requirements.
+We can satisfy both: The former with the ``json`` built-in Python module or a Python ``dict`` and
+the latter with the DLPack protocol.
+
+.. note::
+ See the `RFC to adopt binsparse `_
+ for discussion that preceded the adoption of the binsparse protocol.
+
+ See :ref:`sparse_interchange` for the Python specification of this protocol.
+
+
Non-supported use cases
-----------------------
Use of DLPack requires that the data can be represented by a strided, in-memory
layout on a single device. This covers usage by a large range of, but not all,
known and possible array libraries. Use cases that are not supported by DLPack
-include:
-
-- Distributed arrays, i.e., the data residing on multiple nodes or devices,
-- Sparse arrays, i.e., sparse representations where a data value (typically
- zero) is implicit.
+include distributed arrays, i.e., the data residing on multiple nodes or devices.
There may be other reasons why it is not possible or desirable for an
implementation to materialize the array as strided data in memory. In such
diff --git a/spec/draft/extensions/index.rst b/spec/draft/extensions/index.rst
index 3b9409954..d6deb4683 100644
--- a/spec/draft/extensions/index.rst
+++ b/spec/draft/extensions/index.rst
@@ -32,3 +32,4 @@ the array API standard. See :ref:`api-specification`.
fourier_transform_functions
linear_algebra_functions
+ sparse_interchange
diff --git a/spec/draft/extensions/sparse_interchange.rst b/spec/draft/extensions/sparse_interchange.rst
new file mode 100644
index 000000000..9e988d2eb
--- /dev/null
+++ b/spec/draft/extensions/sparse_interchange.rst
@@ -0,0 +1,39 @@
+.. _sparse_interchange:
+
+Sparse interchange
+==================
+
+ Array API specification for sparse interchange functions.
+
+Extension name and usage
+------------------------
+
+If implemented, this extension must be retrievable via::
+
+ >>> if hasattr(x, '__dlpack__'):
+ >>> # Use the extension
+
+Objects in API
+--------------
+
+A conforming implementation of this extension must provide and support the following
+functions/methods.
+
+.. currentmodule:: array_api
+
+..
+ NOTE: please keep the functions and their inverse together
+
+.. autosummary::
+ :toctree: generated
+ :template: method.rst
+
+ from_binsparse
+
+
+.. autosummary::
+ :toctree: generated
+ :template: property.rst
+
+ array.__binsparse__
+ array.__binsparse_descriptor__
diff --git a/src/array_api_stubs/_draft/array_object.py b/src/array_api_stubs/_draft/array_object.py
index 08d5c0b6e..8029da11c 100644
--- a/src/array_api_stubs/_draft/array_object.py
+++ b/src/array_api_stubs/_draft/array_object.py
@@ -1246,5 +1246,41 @@ def to_device(
Clarified behavior when a provided ``device`` object corresponds to the device on which an array instance resides.
"""
+ def __binsparse_descriptor__(self) -> dict:
+ """
+ Returns a `dict` equivalent to a parsed `binsparse JSON descriptor `_.
+
+ Parameters
+ ----------
+ self: array
+ array instance.
+
+ Returns
+ -------
+ out: dict
+ A ``dict`` equivalent to a parsed JSON binsparse descriptor of an array. See :ref:`sparse_interchange` for details.
+
+
+ .. versionadded:: 2025.12
+ """
+
+ def __binsparse__(self) -> dict[str, array]:
+ """
+ Returns a key-value store of the constituent arrays of a sparse array, as specified by the `binsparse specification `_.
+
+ Parameters
+ ----------
+ self: array
+ array instance.
+
+ Returns
+ -------
+ out: dict[str, array]
+ A ``dict`` equivalent to a parsed JSON binsparse descriptor of an array. See :ref:`sparse_interchange` for details.
+
+
+ .. versionadded:: 2025.12
+ """
+
array = _array
diff --git a/src/array_api_stubs/_draft/creation_functions.py b/src/array_api_stubs/_draft/creation_functions.py
index c09800783..20c427e7c 100644
--- a/src/array_api_stubs/_draft/creation_functions.py
+++ b/src/array_api_stubs/_draft/creation_functions.py
@@ -5,6 +5,7 @@
"empty_like",
"eye",
"from_dlpack",
+ "from_binsparse",
"full",
"full_like",
"linspace",
@@ -645,3 +646,29 @@ def zeros_like(
out: array
an array having the same shape as ``x`` and filled with zeros.
"""
+
+
+def from_binsparse(arrays: dict[str, array], descriptor: dict, /) -> array:
+ """
+ Returns a new array containing the data from another (array) object with a ``__binsparse__`` method.
+
+ Parameters
+ ----------
+ arrays: dict[str, array]
+ input constituent arrays.
+ descriptor: dict
+ The parsed binsparse descriptor of the array.
+
+ Returns
+ -------
+ out: array
+ an array containing the data in `arrays` with a format specified by `descriptor`.
+
+ .. admonition:: Note
+ :class: note
+
+ The returned array may be either a copy or a view. See :ref:`data-interchange` for details.
+
+
+ .. versionadded:: 2025.12
+ """
From a8513012c210123d7af9d6289f50de33ede26084 Mon Sep 17 00:00:00 2001
From: Athan
Date: Wed, 2 Apr 2025 23:32:11 -0700
Subject: [PATCH 2/7] Apply suggestions from code review
---
spec/draft/design_topics/data_interchange.rst | 4 ++--
src/array_api_stubs/_draft/array_object.py | 6 ------
src/array_api_stubs/_draft/creation_functions.py | 5 +----
3 files changed, 3 insertions(+), 12 deletions(-)
diff --git a/spec/draft/design_topics/data_interchange.rst b/spec/draft/design_topics/data_interchange.rst
index 041f2d4f8..f3d297c10 100644
--- a/spec/draft/design_topics/data_interchange.rst
+++ b/spec/draft/design_topics/data_interchange.rst
@@ -98,11 +98,11 @@ While initially intended to target file formats, binsparse has relatively few re
back-ends:
1. The ability to represent and parse JSON.
-2. To be able to represent/store a key-value store of 1-dimensional (and optionally 2-dimensional)
+2. The ability to represent/store a key-value store of 1-dimensional (and optionally 2-dimensional)
arrays.
It is the only such specification for sparse representations to have these minimal requirements.
-We can satisfy both: The former with the ``json`` built-in Python module or a Python ``dict`` and
+We can satisfy both: the former with the ``json`` built-in Python module or a Python ``dict`` and
the latter with the DLPack protocol.
.. note::
diff --git a/src/array_api_stubs/_draft/array_object.py b/src/array_api_stubs/_draft/array_object.py
index 8029da11c..a081e9c58 100644
--- a/src/array_api_stubs/_draft/array_object.py
+++ b/src/array_api_stubs/_draft/array_object.py
@@ -1259,9 +1259,6 @@ def __binsparse_descriptor__(self) -> dict:
-------
out: dict
A ``dict`` equivalent to a parsed JSON binsparse descriptor of an array. See :ref:`sparse_interchange` for details.
-
-
- .. versionadded:: 2025.12
"""
def __binsparse__(self) -> dict[str, array]:
@@ -1277,9 +1274,6 @@ def __binsparse__(self) -> dict[str, array]:
-------
out: dict[str, array]
A ``dict`` equivalent to a parsed JSON binsparse descriptor of an array. See :ref:`sparse_interchange` for details.
-
-
- .. versionadded:: 2025.12
"""
diff --git a/src/array_api_stubs/_draft/creation_functions.py b/src/array_api_stubs/_draft/creation_functions.py
index 20c427e7c..c97308f96 100644
--- a/src/array_api_stubs/_draft/creation_functions.py
+++ b/src/array_api_stubs/_draft/creation_functions.py
@@ -657,7 +657,7 @@ def from_binsparse(arrays: dict[str, array], descriptor: dict, /) -> array:
arrays: dict[str, array]
input constituent arrays.
descriptor: dict
- The parsed binsparse descriptor of the array.
+ the parsed binsparse descriptor of the array.
Returns
-------
@@ -668,7 +668,4 @@ def from_binsparse(arrays: dict[str, array], descriptor: dict, /) -> array:
:class: note
The returned array may be either a copy or a view. See :ref:`data-interchange` for details.
-
-
- .. versionadded:: 2025.12
"""
From e0d4d2826fa28fab369a5218e08fe01c3013989d Mon Sep 17 00:00:00 2001
From: Athan
Date: Wed, 2 Apr 2025 23:33:10 -0700
Subject: [PATCH 3/7] style: ensure alphabetical order
---
src/array_api_stubs/_draft/creation_functions.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/array_api_stubs/_draft/creation_functions.py b/src/array_api_stubs/_draft/creation_functions.py
index c97308f96..5c5b3f168 100644
--- a/src/array_api_stubs/_draft/creation_functions.py
+++ b/src/array_api_stubs/_draft/creation_functions.py
@@ -4,8 +4,8 @@
"empty",
"empty_like",
"eye",
- "from_dlpack",
"from_binsparse",
+ "from_dlpack",
"full",
"full_like",
"linspace",
From af51eb5e370ccc6f9b9c26890ec094d17a9a8a6b Mon Sep 17 00:00:00 2001
From: Hameer Abbasi <2190658+hameerabbasi@users.noreply.github.com>
Date: Thu, 3 Apr 2025 08:59:26 +0200
Subject: [PATCH 4/7] Move `from_binsparse` to `sparse` submodule.
---
spec/draft/extensions/sparse_interchange.rst | 4 +--
src/array_api_stubs/_draft/__init__.py | 1 +
.../_draft/creation_functions.py | 24 ----------------
src/array_api_stubs/_draft/sparse.py | 28 +++++++++++++++++++
4 files changed, 31 insertions(+), 26 deletions(-)
create mode 100644 src/array_api_stubs/_draft/sparse.py
diff --git a/spec/draft/extensions/sparse_interchange.rst b/spec/draft/extensions/sparse_interchange.rst
index 9e988d2eb..71bb1f80b 100644
--- a/spec/draft/extensions/sparse_interchange.rst
+++ b/spec/draft/extensions/sparse_interchange.rst
@@ -10,7 +10,7 @@ Extension name and usage
If implemented, this extension must be retrievable via::
- >>> if hasattr(x, '__dlpack__'):
+ >>> if hasattr(x, 'sparse'):
>>> # Use the extension
Objects in API
@@ -28,7 +28,7 @@ functions/methods.
:toctree: generated
:template: method.rst
- from_binsparse
+ sparse.from_binsparse
.. autosummary::
diff --git a/src/array_api_stubs/_draft/__init__.py b/src/array_api_stubs/_draft/__init__.py
index 537ea8f85..08dc15ad2 100644
--- a/src/array_api_stubs/_draft/__init__.py
+++ b/src/array_api_stubs/_draft/__init__.py
@@ -16,6 +16,7 @@
from .utility_functions import *
from . import linalg
from . import fft
+from . import sparse
from .info import __array_namespace_info__
diff --git a/src/array_api_stubs/_draft/creation_functions.py b/src/array_api_stubs/_draft/creation_functions.py
index 5c5b3f168..c09800783 100644
--- a/src/array_api_stubs/_draft/creation_functions.py
+++ b/src/array_api_stubs/_draft/creation_functions.py
@@ -4,7 +4,6 @@
"empty",
"empty_like",
"eye",
- "from_binsparse",
"from_dlpack",
"full",
"full_like",
@@ -646,26 +645,3 @@ def zeros_like(
out: array
an array having the same shape as ``x`` and filled with zeros.
"""
-
-
-def from_binsparse(arrays: dict[str, array], descriptor: dict, /) -> array:
- """
- Returns a new array containing the data from another (array) object with a ``__binsparse__`` method.
-
- Parameters
- ----------
- arrays: dict[str, array]
- input constituent arrays.
- descriptor: dict
- the parsed binsparse descriptor of the array.
-
- Returns
- -------
- out: array
- an array containing the data in `arrays` with a format specified by `descriptor`.
-
- .. admonition:: Note
- :class: note
-
- The returned array may be either a copy or a view. See :ref:`data-interchange` for details.
- """
diff --git a/src/array_api_stubs/_draft/sparse.py b/src/array_api_stubs/_draft/sparse.py
new file mode 100644
index 000000000..aa9575d81
--- /dev/null
+++ b/src/array_api_stubs/_draft/sparse.py
@@ -0,0 +1,28 @@
+from __future__ import annotations
+
+from ._types import array
+
+__all__ = ["from_binsparse"]
+
+
+def from_binsparse(arrays: dict[str, array], descriptor: dict, /) -> array:
+ """
+ Returns a new array containing the data from another (array) object with a ``__binsparse__`` method.
+
+ Parameters
+ ----------
+ arrays: dict[str, array]
+ input constituent arrays.
+ descriptor: dict
+ The parsed binsparse descriptor of the array.
+
+ Returns
+ -------
+ out: array
+ an array containing the data in `arrays` with a format specified by `descriptor`.
+
+ .. admonition:: Note
+ :class: note
+
+ The returned array may be either a copy or a view. See :ref:`data-interchange` for details.
+ """
From 0ad72df203782eff73c1fec4e5ee24eeeb544c7a Mon Sep 17 00:00:00 2001
From: Hameer Abbasi <2190658+hameerabbasi@users.noreply.github.com>
Date: Thu, 3 Apr 2025 13:04:30 +0200
Subject: [PATCH 5/7] Specify the functions further.
---
spec/draft/extensions/sparse_interchange.rst | 12 ++--
src/array_api_stubs/_draft/array_object.py | 11 +++-
src/array_api_stubs/_draft/sparse.py | 63 +++++++++++++++++---
3 files changed, 74 insertions(+), 12 deletions(-)
diff --git a/spec/draft/extensions/sparse_interchange.rst b/spec/draft/extensions/sparse_interchange.rst
index 71bb1f80b..3b740beaf 100644
--- a/spec/draft/extensions/sparse_interchange.rst
+++ b/spec/draft/extensions/sparse_interchange.rst
@@ -13,23 +13,27 @@ If implemented, this extension must be retrievable via::
>>> if hasattr(x, 'sparse'):
>>> # Use the extension
+.. currentmodule:: array_api
+
Objects in API
--------------
A conforming implementation of this extension must provide and support the following
-functions/methods.
-
-.. currentmodule:: array_api
+functions/methods. In addition, the ``asarray`` method must also be able to convert
+objects with supported formats which implement the protocol.
..
NOTE: please keep the functions and their inverse together
+.. currentmodule:: array_api.sparse
+
.. autosummary::
:toctree: generated
:template: method.rst
- sparse.from_binsparse
+ from_binsparse
+.. currentmodule:: array_api
.. autosummary::
:toctree: generated
diff --git a/src/array_api_stubs/_draft/array_object.py b/src/array_api_stubs/_draft/array_object.py
index a081e9c58..f48d52f19 100644
--- a/src/array_api_stubs/_draft/array_object.py
+++ b/src/array_api_stubs/_draft/array_object.py
@@ -1261,7 +1261,9 @@ def __binsparse_descriptor__(self) -> dict:
A ``dict`` equivalent to a parsed JSON binsparse descriptor of an array. See :ref:`sparse_interchange` for details.
"""
- def __binsparse__(self) -> dict[str, array]:
+ def __binsparse__(
+ self, /, *, descriptor: Optional[dict] = None
+ ) -> dict[str, array]:
"""
Returns a key-value store of the constituent arrays of a sparse array, as specified by the `binsparse specification `_.
@@ -1269,11 +1271,18 @@ def __binsparse__(self) -> dict[str, array]:
----------
self: array
array instance.
+ descriptor: Optional[dict]
+ If ``descriptor`` is not ``None``, the data returned must be in the format specified by it. If the format is unsupported, a ``TypeError`` must be raised.
Returns
-------
out: dict[str, array]
A ``dict`` equivalent to a parsed JSON binsparse descriptor of an array. See :ref:`sparse_interchange` for details.
+
+ Raises
+ ------
+ TypeError
+ If ``descriptor`` is not ``None``, and the array library does not support converting to it.
"""
diff --git a/src/array_api_stubs/_draft/sparse.py b/src/array_api_stubs/_draft/sparse.py
index aa9575d81..6359d39db 100644
--- a/src/array_api_stubs/_draft/sparse.py
+++ b/src/array_api_stubs/_draft/sparse.py
@@ -1,20 +1,47 @@
from __future__ import annotations
-from ._types import array
+from typing import Optional
+from ._types import array, device
__all__ = ["from_binsparse"]
-def from_binsparse(arrays: dict[str, array], descriptor: dict, /) -> array:
+def from_binsparse(
+ x: object,
+ /,
+ *,
+ descriptor: Optional[dict] = None,
+ device: Optional[device] = None,
+ copy: Optional[bool] = None,
+) -> array:
"""
- Returns a new array containing the data from another (array) object with a ``__binsparse__`` method.
+ Returns a new array containing the data from another (array) object with a ``__binsparse__`` method,
+ assuming the format specified in `descriptor` is supported in this library.
Parameters
----------
- arrays: dict[str, array]
- input constituent arrays.
- descriptor: dict
- The parsed binsparse descriptor of the array.
+ x: object
+ input (array) object.
+ descriptor: Optional[dict]
+ If ``descriptor`` is ``None``, the array must be returned in the format in which it is stored or materializable to.
+ Otherwise, it must be converted to the format specified by ``descriptor``.
+
+ If ``copy`` is ``False``, no conversion should be performed, and only stored data should be returned.
+
+ If the format specified by ``descriptor`` is unsupported by the library, a ``TypeError`` must be raised.
+ device: Optional[device]
+ device on which to place the created array. If ``device`` is ``None`` and ``x`` supports binsparse, the output array
+ must be on the same device as ``x``. Default: ``None``.
+
+ The v2023.12 standard only mandates that a compliant library should offer a way for ``from_binsparse`` to return an array
+ whose underlying memory is accessible to the Python interpreter, when the corresponding ``device`` is provided. If the
+ array library does not support such cases at all, the function must raise ``BufferError``. If a copy must be made to
+ enable this support but ``copy`` is set to ``False``, the function must raise ``ValueError``.
+
+ Other device kinds will be considered for standardization in a future version of this API standard.
+ copy: Optional[bool]
+ boolean indicating whether or not to copy the input. If ``True``, the function must always copy. If ``False``, the function must never copy, and raise ``BufferError`` in case a copy is deemed necessary (e.g. if a cross-device data movement is requested, and it is not possible without a copy). If ``None``, the function must reuse the existing memory buffer if possible and copy otherwise. Default: ``None``.
+
Returns
-------
@@ -25,4 +52,26 @@ def from_binsparse(arrays: dict[str, array], descriptor: dict, /) -> array:
:class: note
The returned array may be either a copy or a view. See :ref:`data-interchange` for details.
+
+ Raises
+ ------
+ BufferError
+ The ``__binsparse__``, ``__binsparse_descriptor__``, ``__dlpack__`` or ``__dlpack_device__``
+ methods on the input array or constituent arrays may raise ``BufferError`` when the data
+ cannot be exported as a binsparse-compatible array. (e.g., incompatible dtype, strides, or
+ device). It may also raise other errors when export fails for other reasons (e.g., not
+ enough memory available to materialize the data). ``from_dlpack`` must propagate such
+ exceptions.
+ AttributeError
+ If the ``__binsparse__`` and ``__binsparse_descriptor__`` methods are not present
+ on the input array. This may happen for libraries that are never able
+ to export their data with binsparse.
+ ValueError
+ If data exchange is possible via an explicit copy but ``copy`` is set to ``False``.
+ TypeError
+ If ``descriptor`` is ``None``, the data received from the source library is not guaranteed to
+ be in a format that the target array library supports. In this case, a ``TypeError`` must be raised.
+ Additionally, if ``descriptor`` is not ``None``, it must be passed along to ``__binsparse__``, which
+ may raise a ``TypeError`` if the conversion is unsupported by the source library, which
+ ``from_binsparse`` must propagate.
"""
From c8e4d60278194832f25b5481cc0d1294c8c0cd63 Mon Sep 17 00:00:00 2001
From: Hameer Abbasi <2190658+hameerabbasi@users.noreply.github.com>
Date: Thu, 3 Apr 2025 14:58:42 +0200
Subject: [PATCH 6/7] Modify protocol to be more like DLPack.
---
spec/draft/extensions/sparse_interchange.rst | 3 ++-
src/array_api_stubs/_draft/array_object.py | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/spec/draft/extensions/sparse_interchange.rst b/spec/draft/extensions/sparse_interchange.rst
index 3b740beaf..4e9581ab9 100644
--- a/spec/draft/extensions/sparse_interchange.rst
+++ b/spec/draft/extensions/sparse_interchange.rst
@@ -10,7 +10,8 @@ Extension name and usage
If implemented, this extension must be retrievable via::
- >>> if hasattr(x, 'sparse'):
+ >>> xp = x.__array_namespace__()
+ >>> if hasattr(xp, 'sparse'):
>>> # Use the extension
.. currentmodule:: array_api
diff --git a/src/array_api_stubs/_draft/array_object.py b/src/array_api_stubs/_draft/array_object.py
index f48d52f19..5ab9603c8 100644
--- a/src/array_api_stubs/_draft/array_object.py
+++ b/src/array_api_stubs/_draft/array_object.py
@@ -1282,7 +1282,7 @@ def __binsparse__(
Raises
------
TypeError
- If ``descriptor`` is not ``None``, and the array library does not support converting to it.
+ If ``descriptor`` is not ``None``, and the array library does not support converting to a format specified by it.
"""
From 3cc841041b908d915dac6fd39a3ae092e151a749 Mon Sep 17 00:00:00 2001
From: Hameer Abbasi <2190658+hameerabbasi@users.noreply.github.com>
Date: Thu, 3 Apr 2025 15:42:59 +0200
Subject: [PATCH 7/7] Give examples of the binsparse descriptor.
---
spec/draft/extensions/sparse_interchange.rst | 59 +++++++++++++++++++-
src/array_api_stubs/_draft/array_object.py | 10 +++-
src/array_api_stubs/_draft/sparse.py | 3 +-
3 files changed, 68 insertions(+), 4 deletions(-)
diff --git a/spec/draft/extensions/sparse_interchange.rst b/spec/draft/extensions/sparse_interchange.rst
index 4e9581ab9..dade7231a 100644
--- a/spec/draft/extensions/sparse_interchange.rst
+++ b/spec/draft/extensions/sparse_interchange.rst
@@ -3,7 +3,7 @@
Sparse interchange
==================
- Array API specification for sparse interchange functions.
+ Array API specification for sparse interchange functions using `binsparse `_.
Extension name and usage
------------------------
@@ -14,11 +14,66 @@ If implemented, this extension must be retrievable via::
>>> if hasattr(xp, 'sparse'):
>>> # Use the extension
-.. currentmodule:: array_api
+To convert an object from another library supporting also supporting the sparse interchange extension::
+
+ >>> xp1 = xp1.sparse.from_binsparse(xp2_array) # Convert with the same formats
+ >>> xp1 = xp1.sparse.from_binsparse(xp2_array, descriptor=binsparse_descriptor)
+
+.. _binsparse_descriptor_examples:
+
+Examples of binsparse descriptors
+---------------------------------
+
+While the `binsparse specification `_ uses JSON for its descriptor,
+we will work with equivalent Python objects instead. Here are some examples of binsparse descriptors::
+
+ >>> coo_2d_descriptor = {
+ "binsparse": {
+ "version": "0.1",
+ "format": "COOR",
+ "shape": [10, 12],
+ "number_of_stored_values": 20,
+ "data_types": {
+ "indices_0": "uint64",
+ "indices_1": "uint64",
+ "values": "float32",
+ },
+ },
+ "original_source": f"{library_name!s}, version {library_version!s}",
+ }
+ >>> csr_2d_descriptor = {
+ "binsparse": {
+ "version": "0.1",
+ "format": "CSR",
+ "shape": [20, 24],
+ "number_of_stored_values": 20,
+ "data_types": {
+ "pointers_to_1": "uint64",
+ "indices_1": "uint64",
+ "values": "float32",
+ },
+ },
+ "original_source": f"{library_name!s}, version {library_version!s}",
+ }
+ >>> compressed_vector_descriptor = {
+ "binsparse": {
+ "version": "0.1",
+ "format": "CVEC",
+ "shape": [30],
+ "number_of_stored_values": 3,
+ "data_types": {
+ "indices_0": "uint64",
+ "values": "float32",
+ },
+ },
+ "original_source": f"{library_name!s}, version {library_version!s}",
+ }
Objects in API
--------------
+.. currentmodule:: array_api
+
A conforming implementation of this extension must provide and support the following
functions/methods. In addition, the ``asarray`` method must also be able to convert
objects with supported formats which implement the protocol.
diff --git a/src/array_api_stubs/_draft/array_object.py b/src/array_api_stubs/_draft/array_object.py
index 5ab9603c8..1bae49bec 100644
--- a/src/array_api_stubs/_draft/array_object.py
+++ b/src/array_api_stubs/_draft/array_object.py
@@ -1272,7 +1272,7 @@ def __binsparse__(
self: array
array instance.
descriptor: Optional[dict]
- If ``descriptor`` is not ``None``, the data returned must be in the format specified by it. If the format is unsupported, a ``TypeError`` must be raised.
+ If ``descriptor`` is not ``None``, the data returned must be in the format specified by it.
Returns
-------
@@ -1283,6 +1283,14 @@ def __binsparse__(
------
TypeError
If ``descriptor`` is not ``None``, and the array library does not support converting to a format specified by it.
+ ValueError
+ If ``descriptor`` is not a valid binsparse descriptor.
+
+ Notes
+ -----
+
+ - ``x.__binsparse_descriptor__()["binsparse"]["data_types"].keys() == x.__binsparse__().keys()`` must hold.
+ - ``descriptor["binsparse"]["data_types"].keys() == x.__binsparse__(descriptor=descriptor).keys()`` must hold.
"""
diff --git a/src/array_api_stubs/_draft/sparse.py b/src/array_api_stubs/_draft/sparse.py
index 6359d39db..2dd1f0323 100644
--- a/src/array_api_stubs/_draft/sparse.py
+++ b/src/array_api_stubs/_draft/sparse.py
@@ -67,7 +67,8 @@ def from_binsparse(
on the input array. This may happen for libraries that are never able
to export their data with binsparse.
ValueError
- If data exchange is possible via an explicit copy but ``copy`` is set to ``False``.
+ If data exchange is possible via an explicit copy but ``copy`` is set to ``False``, or if the specified
+ descriptor is not valid.
TypeError
If ``descriptor`` is ``None``, the data received from the source library is not guaranteed to
be in a format that the target array library supports. In this case, a ``TypeError`` must be raised.