feat(Init): Initial upload of array + interoperability package#1
Conversation
There was a problem hiding this comment.
Pull request overview
Initial import of the decent_array package, providing an Array wrapper and a single-active-backend interoperability layer with NumPy/PyTorch/JAX/TensorFlow backends, plus CI, docs, benchmarks, and a comprehensive pytest suite.
Changes:
- Added backend manager + abstract backend contract, plus concrete backends (NumPy/PyTorch/JAX/TensorFlow) and module-level iop functions/RNG coordination.
- Added
Arraywrapper implementing operators, indexing, and common properties via the active backend. - Added tox/CI/Sphinx/ReadTheDocs scaffolding, benchmarks, and extensive tests.
Reviewed changes
Copilot reviewed 49 out of 52 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_iop_rng.py | Tests for RNG seeding, snapshot/restore, and distribution helpers. |
| tests/test_iop_functions.py | Tests for module-level interoperability functions (creation, math, linalg, indexing). |
| tests/test_decorators.py | Tests for the cost-method autodecorator argument unwrapping/rewrapping behavior. |
| tests/test_backend_manager.py | Tests backend activation, registration, listener behavior, and reset semantics. |
| tests/test_array.py | Tests Array operators/dunders, indexing, coercion, and properties across backends. |
| tests/conftest.py | Parametrized backend/device fixture with skip logic for unavailable combinations. |
| readthedocs.yaml | Read the Docs build configuration. |
| pyproject.toml | Project metadata, dependencies/extras, tox envs, ruff/mypy/pytest config, mypyc build hook. |
| LICENSE | Adds AGPL-3.0 license text. |
| docs/sphinx_theme.txt | Sphinx theme requirements list. |
| docs/source/user.rst | Initial user guide stub + installation snippet. |
| docs/source/index.rst | Sphinx landing page and toctree. |
| docs/source/developer.rst | Developer guide covering tox workflows, mypyc usage, and contribution process. |
| docs/source/conf.py | Sphinx configuration (theme, intersphinx, missing-reference fixups, sidebar config). |
| docs/source/background.rst | Background page stub. |
| docs/source/author.rst | Contributors page. |
| docs/source/api/decent_array.types.rst | API stub for decent_array.types. |
| docs/source/api/decent_array.rst | API toctree root. |
| docs/source/api/decent_array.interoperability.rst | API stub for decent_array.interoperability. |
| docs/source/api/decent_array.array.rst | API stub for decent_array (exports Array). |
| docs/source/_templates/package.rst.jinja | Custom sphinx-apidoc package template. |
| docs/source/_templates/module.rst.jinja | Custom sphinx-apidoc module template. |
| docs/source/_static/custom.css | Small theme CSS tweak for logo sizing. |
| docs/Makefile | Sphinx Makefile (unix). |
| docs/make.bat | Sphinx make script (windows). |
| decent_array/types.py | Shared type aliases + supported framework/device enums. |
| decent_array/interoperability/_tensorflow/tensorflow_backend.py | TensorFlow backend implementation + registration side-effect. |
| decent_array/interoperability/_tensorflow/init.py | TensorFlow backend package initializer/export. |
| decent_array/interoperability/_pytorch/pytorch_backend.py | PyTorch backend implementation + registration side-effect. |
| decent_array/interoperability/_pytorch/init.py | PyTorch backend package initializer/export. |
| decent_array/interoperability/_numpy/numpy_backend.py | NumPy backend implementation + registration side-effect. |
| decent_array/interoperability/_numpy/init.py | NumPy backend package initializer/export. |
| decent_array/interoperability/_jax/jax_backend.py | JAX backend implementation + registration side-effect. |
| decent_array/interoperability/_jax/init.py | JAX backend package initializer/export. |
| decent_array/interoperability/_iop/rng.py | Cross-backend RNG coordination (python random, numpy, backend) + distributions. |
| decent_array/interoperability/_iop/functions.py | Module-level iop function surface delegating to the active backend. |
| decent_array/interoperability/_iop/init.py | iop package marker/init (empty). |
| decent_array/interoperability/_decorators.py | Decorator to unwrap Array args for backend-native cost-method implementations. |
| decent_array/interoperability/_backend_manager.py | Backend registry/activation/reset + auto-import and listener mechanism. |
| decent_array/interoperability/_abstracts/backend.py | Abstract Backend API contract (creation/manipulation/linalg/math/RNG). |
| decent_array/interoperability/_abstracts/init.py | Abstracts package export (Backend). |
| decent_array/interoperability/init.py | Public interoperability API exports (functions + RNG helpers + decorator). |
| decent_array/_array.py | Array wrapper implementation (operators, indexing, properties) tied to active backend. |
| decent_array/init.py | Package exports (Array, interoperability, types). |
| benchmarks/profile_hotpath.py | cProfile-based hot-path profiler for wrapper vs function dispatch. |
| benchmarks/bench_iop.py | Microbenchmark for iop function-call overhead vs native frameworks. |
| benchmarks/bench_common.py | Shared benchmark helpers (backend discovery/activation + timing utilities). |
| benchmarks/bench_array.py | Microbenchmark for Array operator overhead vs native frameworks. |
| .gitignore | Python/build/dev artifact ignores. |
| .github/workflows/ci.yaml | CI pipeline running tox envs (mypy/pytest/ruff/sphinx/mypyc) on Py3.13 across OSes. |
| .github/CODEOWNERS | Sets default code owners for the repo. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| [project] | ||
| name = "decent-array" | ||
| version = "0.1.0" | ||
| authors = [{name = "Elias Ram"}, {name = "Simon Granström"}, {name = "Adriana Rodriguez"}, {name = "Nicola Bastianello"}] | ||
| maintainers = [{name = "Team Decent"}] | ||
| description = "A library of array operations and linear algebra primitives for interoperability across ML frameworks." | ||
| readme = "README.md" | ||
| requires-python = ">=3.13" | ||
| classifiers = [ | ||
| "Programming Language :: Python :: 3.13", | ||
| "Operating System :: OS Independent", | ||
| ] | ||
| license = "AGPL-3.0-only" | ||
| dependencies = [ | ||
| "numpy", | ||
| ] |
|
I will try to remember to upload benchmark results tomorrow evening |
nicola-bastianello
left a comment
There was a problem hiding this comment.
first batch of comments, mostly on docstrings and docs. a few of the comments on adding stuff to docs we can copy-paste to a new issue and address them later
nicola-bastianello
left a comment
There was a problem hiding this comment.
second batch of comments on repo set-up
nicola-bastianello
left a comment
There was a problem hiding this comment.
third batch of comments, this time on aligning to the array api
some of the signatures proposed by array api are a bit more complicated than what we currently have. so for now we could just make small changes (like introducing / where needed), and leave bigger changes on the type annotations for later
nicola-bastianello
left a comment
There was a problem hiding this comment.
other comments, on private vs. public
nicola-bastianello
left a comment
There was a problem hiding this comment.
comments on the iop api
my priority for now is to ensure compatibility with the array api. this means changing the names and signatures of many functions, but at this stage we don't need to change the behavior (e.g. we can ignore arguments that are not currently supported). my idea is that aligning the api right now is easier than doing it later, and that changing behavior is easier to do at a later time
| return _BACKEND_INSTANCE.argmin(array, axis, keepdims) | ||
|
|
||
|
|
||
| def set_item(array: Array, key: ArrayKey, value: Array) -> None: |
There was a problem hiding this comment.
set_item and get_item could also be private, since they are called by the corresponding dunder methods
the signature should also align with the api as much as possible, e.g. array arg to x and the other args align to
array.__setitem__(key: int | slice | ellipsis | Tuple[int | slice | ellipsis | array, ...] | array, value: int | float | complex | bool | array, /) -> None https://data-apis.org/array-api/latest/API_specification/generated/array_api.array.__setitem__.html#array_api.array.__setitem__
array.__getitem__(key: int | slice | ellipsis | None | Tuple[int | slice | ellipsis | array | None, ...] | array, /) -> array https://data-apis.org/array-api/latest/API_specification/generated/array_api.array.__getitem__.html#array_api.array.__getitem__
| return _BACKEND_INSTANCE.copy(array) | ||
|
|
||
|
|
||
| def to_numpy(array: Array) -> NDArray[Any]: |
There was a problem hiding this comment.
both torch and tf use tensor.numpy() for conversion to numpy. we could change this function to private and call it from Array.numpy(). jax is a bit different but I think it's fair to use .numpy() as the api choice
related #3, which would require implementing __array__ and __array_wrap__, but that's a discussion for later
|
another comment: I like that with the new version there is no need for this would allow removing all checks from the iop functions. again, I'm not sure this is much of an improvement in terms of performance, but it would at least cut out a lot of repeated code |
I've already tried this and it doesnt compile. _NoBackend would not be of type Backend so it is not assignable to backend instance. So we'd need to implement every abstract method. The current solution was the best I could come up with without having some very complex code |
I see, thank you! then let's keep the current implementation |
|
@nicola-bastianello Can you double check that I didnt miss anything |
|
looks great overall! just a few items left. I left them unresolved and here is a summary:
after these are addressed, I will make another pass of the whole PR |
I added expand_dims as an alias in _iop, its fine to add aliases just in _iop and not in the backend
Already added in _iop. Question is, should the backend name be dot or vecdot?
Does it make sense to have the exponent as x2? That could be interpreted as a vector which wouldn't make much sense?
Already added in _iop I'll upload the fixes to the others shortly |
|
I did not put too much thought into the split of the _iop methods. If anything is majorly wrong then let me know, otherwise we can change it once we start working on the docs. Since its only backend changes and not API, we can modify these without changing other packages in the decent-stack which may depend on it. I think its better that we ship it soon so we can implement the changes in decent-bench. |
sorry, I hadn't checked the iop for the aliases. it makes indeed more sense to have them there
vecdot is good
it looks like in the array api x2 can indeed be an array (broadcastable with x1), in which case each element of x1 is raised to the corresponding element of x2. I looked into it and all backends support this natively, so it should just be a matter of changing the name and adding array to the type of x2
I think it's good enough for now. as we add even more functions, I'll look into this again and we can make changes as needed. what I think is important is to keep a flat import structure, but present them in separate pages in the docs, to make them more readable one last thing I noticed: the args order and name for vector_norm are not aligned https://data-apis.org/array-api/latest/extensions/generated/array_api.linalg.vector_norm.html#array_api.linalg.vector_norm other than this, everything else looks great. thank you! |
I have never used that or needed to so I didn't know it was a feature. I'll fix it |
|
looks great, thank you! merging now |
Initial upload of array + interoperability modules, including tests, docs, type-checking and compilation. Better docs and a README is needed.