Skip to content

feat(Init): Initial upload of array + interoperability package#1

Merged
nicola-bastianello merged 13 commits into
team-decent:mainfrom
Simpag:dev1
May 27, 2026
Merged

feat(Init): Initial upload of array + interoperability package#1
nicola-bastianello merged 13 commits into
team-decent:mainfrom
Simpag:dev1

Conversation

@Simpag

@Simpag Simpag commented May 10, 2026

Copy link
Copy Markdown
Collaborator

Initial upload of array + interoperability modules, including tests, docs, type-checking and compilation. Better docs and a README is needed.

Copilot AI review requested due to automatic review settings May 10, 2026 18:22

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 Array wrapper 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.

Comment thread benchmarks/bench_common.py Outdated
Comment thread benchmarks/bench_common.py Outdated
Comment thread benchmarks/bench_iop.py Outdated
Comment thread decent_array/interoperability/_iop/functions.py Outdated
Comment thread decent_array/interoperability/_backend_manager.py Outdated
Comment thread docs/source/author.rst Outdated
Comment thread tests/test_iop_rng.py
Comment thread pyproject.toml
Comment on lines +1 to +16
[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",
]
Comment thread docs/source/developer.rst
Comment thread decent_array/_array.py Outdated
@Simpag

Simpag commented May 10, 2026

Copy link
Copy Markdown
Collaborator Author

I will try to remember to upload benchmark results tomorrow evening

@Simpag Simpag requested a review from nicola-bastianello May 10, 2026 21:33

@nicola-bastianello nicola-bastianello left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread benchmarks/bench_array.py
Comment thread decent_array/interoperability/_abstracts/backend.py Outdated
Comment thread decent_array/interoperability/_jax/jax_backend.py Outdated
Comment thread decent_array/interoperability/_tensorflow/tensorflow_backend.py
Comment thread decent_array/interoperability/_backend_manager.py Outdated
Comment thread docs/source/api/decent_array.rst
Comment thread docs/source/author.rst Outdated
Comment thread docs/source/developer.rst
Comment thread docs/source/user.rst Outdated
Comment thread docs/source/api/decent_array.interoperability.rst

@nicola-bastianello nicola-bastianello left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

second batch of comments on repo set-up

Comment thread .gitignore
Comment thread pyproject.toml Outdated
Comment thread pyproject.toml
Comment thread pyproject.toml Outdated

@nicola-bastianello nicola-bastianello left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread decent_array/_array.py
Comment thread decent_array/_array.py Outdated
Comment thread decent_array/_array.py Outdated
Comment thread decent_array/_array.py Outdated
Comment thread decent_array/_array.py Outdated
Comment thread decent_array/_array.py Outdated
Comment thread decent_array/_array.py Outdated
Comment thread decent_array/_array.py Outdated
Comment thread decent_array/_array.py Outdated
Comment thread decent_array/_array.py
@nicola-bastianello nicola-bastianello mentioned this pull request May 14, 2026

@nicola-bastianello nicola-bastianello left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

other comments, on private vs. public

Comment thread decent_array/interoperability/_backend_manager.py
Comment thread decent_array/interoperability/_iop/rng.py
Comment thread decent_array/interoperability/_iop/rng.py
Comment thread benchmarks/bench_array.py

@nicola-bastianello nicola-bastianello left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread decent_array/interoperability/_iop/functions.py Outdated
Comment thread decent_array/interoperability/_iop/functions.py Outdated
Comment thread decent_array/interoperability/_iop/functions.py Outdated
Comment thread decent_array/interoperability/_iop/functions.py Outdated
Comment thread decent_array/interoperability/_iop/functions.py Outdated
Comment thread decent_array/interoperability/_iop/functions.py Outdated
Comment thread decent_array/interoperability/_iop/functions.py Outdated
Comment thread decent_array/interoperability/_iop/functions.py Outdated
return _BACKEND_INSTANCE.argmin(array, axis, keepdims)


def set_item(array: Array, key: ArrayKey, value: Array) -> None:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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__

Comment thread decent_array/interoperability/_iop/rng.py

@nicola-bastianello nicola-bastianello left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comments on conversion to/from numpy, and on devices

Comment thread decent_array/interoperability/_iop/functions.py Outdated
Comment thread decent_array/interoperability/_iop/functions.py Outdated
return _BACKEND_INSTANCE.copy(array)


def to_numpy(array: Array) -> NDArray[Any]:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread decent_array/interoperability/_iop/functions.py Outdated
@nicola-bastianello

Copy link
Copy Markdown
Member

another comment: I like that with the new version there is no need for _NoBackend class that replicates the same api but always raises. however, having a is None check in every iop function might add a slight overhead (probably very little). so I've thought of an alternative: we go back to a custom class, and use __getattribute__ to raise with the custom error message

class _NoBackend:
    def __getattribute__(self, name):
        raise RuntimeError(...)

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

@Simpag

Simpag commented May 16, 2026

Copy link
Copy Markdown
Collaborator Author

another comment: I like that with the new version there is no need for _NoBackend class that replicates the same api but always raises. however, having a is None check in every iop function might add a slight overhead (probably very little). so I've thought of an alternative: we go back to a custom class, and use __getattribute__ to raise with the custom error message

class _NoBackend:
    def __getattribute__(self, name):
        raise RuntimeError(...)

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

@nicola-bastianello

Copy link
Copy Markdown
Member

another comment: I like that with the new version there is no need for _NoBackend class that replicates the same api but always raises. however, having a is None check in every iop function might add a slight overhead (probably very little). so I've thought of an alternative: we go back to a custom class, and use __getattribute__ to raise with the custom error message

class _NoBackend:
    def __getattribute__(self, name):
        raise RuntimeError(...)

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

@Simpag

Simpag commented May 24, 2026

Copy link
Copy Markdown
Collaborator Author

@nicola-bastianello Can you double check that I didnt miss anything

@nicola-bastianello

Copy link
Copy Markdown
Member

looks great overall! just a few items left. I left them unresolved and here is a summary:

  • expand_dims as alias for unsqueeze; does adding aliases as non-abstract methods in Backend work for compilation?
  • diag: adding diagonal only for extraction of diagonals like in array api; for creation of diagonal matrix we can keep diag
  • vecdot as alias of dot
  • norm as alias of vector_norm
  • name of arguments in power -> x1 and x2
  • abs as alias for absolute
  • value in set_item should allow for value: int | float | complex | bool | array
  • supporting Array.numpy() which calls to_numpy

after these are addressed, I will make another pass of the whole PR

@Simpag

Simpag commented May 25, 2026

Copy link
Copy Markdown
Collaborator Author

looks great overall! just a few items left. I left them unresolved and here is a summary:

  • expand_dims as alias for unsqueeze; does adding aliases as non-abstract methods in Backend work for compilation?

I added expand_dims as an alias in _iop, its fine to add aliases just in _iop and not in the backend

  • vecdot as alias of dot

Already added in _iop. Question is, should the backend name be dot or vecdot?

  • name of arguments in power -> x1 and x2

Does it make sense to have the exponent as x2? That could be interpreted as a vector which wouldn't make much sense?

  • abs as alias for absolute

Already added in _iop

I'll upload the fixes to the others shortly

@Simpag

Simpag commented May 25, 2026

Copy link
Copy Markdown
Collaborator Author

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.

@nicola-bastianello

Copy link
Copy Markdown
Member

I added expand_dims as an alias in _iop, its fine to add aliases just in _iop and not in the backend

sorry, I hadn't checked the iop for the aliases. it makes indeed more sense to have them there

Already added in _iop. Question is, should the backend name be dot or vecdot?

vecdot is good

Does it make sense to have the exponent as x2? That could be interpreted as a vector which wouldn't make much sense?

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

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!

@Simpag

Simpag commented May 26, 2026

Copy link
Copy Markdown
Collaborator Author

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 have never used that or needed to so I didn't know it was a feature. I'll fix it

@nicola-bastianello

Copy link
Copy Markdown
Member

looks great, thank you! merging now

@nicola-bastianello nicola-bastianello merged commit 6fbb220 into team-decent:main May 27, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants