From 71ccbac47c751534d41c8d1b0eb0274763ff7391 Mon Sep 17 00:00:00 2001 From: altescy Date: Mon, 15 Jan 2024 01:11:12 +0900 Subject: [PATCH] initial commit --- .editorconfig | 18 ++ .gitignore | 153 +++++++++++ Cargo.toml | 13 + Makefile | 44 +++ README.md | 1 + benchmark.py | 89 ++++++ poetry.lock | 658 +++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 67 +++++ setup.cfg | 36 +++ src/lib.rs | 221 +++++++++++++++ src/simd.rs | 139 ++++++++++ src/tensor.rs | 179 ++++++++++++ src/util.rs | 137 ++++++++++ tests/test_xsor.py | 5 + xsor/__init__.py | 29 ++ xsor/tensor.py | 103 +++++++ xsor/types.py | 1 + xsor/xsor.pyi | 35 +++ 18 files changed, 1928 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 Makefile create mode 100644 README.md create mode 100644 benchmark.py create mode 100644 poetry.lock create mode 100644 pyproject.toml create mode 100644 setup.cfg create mode 100644 src/lib.rs create mode 100644 src/simd.rs create mode 100644 src/tensor.rs create mode 100644 src/util.rs create mode 100644 tests/test_xsor.py create mode 100644 xsor/__init__.py create mode 100644 xsor/tensor.py create mode 100644 xsor/types.py create mode 100644 xsor/xsor.pyi diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..83fdbb7 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{json,jsonnet,libsonnet,yml,yaml}] +indent_size = 2 + +[*.tsv] +indent_style = tab + +[Makefile] +indent_style = tab diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a0ee87a --- /dev/null +++ b/.gitignore @@ -0,0 +1,153 @@ +## +## Python +## + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +## +## Rust +## + +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..309c17f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "xsor" +version = "0.1.0" +edition = "2021" + +[lib] +name = "xsor" +crate-type = ["cdylib"] + +[dependencies] +anyhow = "1.0.79" +num = "0.4.1" +pyo3 = { version = "0.20.2", features = ["extension-module"] } diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c50060b --- /dev/null +++ b/Makefile @@ -0,0 +1,44 @@ +PWD := $(shell pwd) +POETRY := poetry +PYTHON := $(POETRY) run python +PYTEST := $(POETRY) run pytest +PYSEN := $(POETRY) run pysen +MODULE := xsor + +.PHONY: all +all: format lint test + +.PHONY: build +build: + $(POETRY) run maturin build --release + +.PHONY: test +test: + PYTHONPATH=$(PWD) $(PYTEST) + +.PHONY: lint +lint: + PYTHONPATH=$(PWD) $(PYSEN) run lint + +.PHONY: format +format: + PYTHONPATH=$(PWD) $(PYSEN) run format + +.PHONY: clean +clean: clean-pyc clean-build + +.PHONY: clean-pyc +clean-pyc: + rm -rf .pytest_cache + rm -rf .mypy_cache + find . -name '*.pyc' -exec rm -f {} + + find . -name '*.pyo' -exec rm -f {} + + find . -name '*~' -exec rm -f {} + + find . -name '__pycache__' -exec rm -fr {} + + +.PHONY: clean-build +clean-build: + rm -rf build/ + rm -rf dist/ + rm -rf *.egg-info/ + rm -rf pip-wheel-metadata/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..e61b974 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# xsor diff --git a/benchmark.py b/benchmark.py new file mode 100644 index 0000000..8686a93 --- /dev/null +++ b/benchmark.py @@ -0,0 +1,89 @@ +import time + +import numpy + +import xsor + + +def numpy_add() -> None: + a = numpy.array(range(100000), dtype=numpy.float32) + b = numpy.array(range(100000), dtype=numpy.float32) + a + b + + +def numpy_mul() -> None: + a = numpy.array(range(100000), dtype=numpy.float32) + b = numpy.array(range(100000), dtype=numpy.float32) + a * b + + +def numpy_sum() -> None: + a = numpy.array(range(100000), dtype=numpy.float32) + a.sum() + + +def numpy_matmul() -> None: + a = numpy.array(range(100000), dtype=numpy.float32).reshape((100, 1000)) + b = numpy.array(range(100000), dtype=numpy.float32).reshape((1000, 100)) + a @ b + + +def numpy_add_scalar() -> None: + a = numpy.array(range(100000), dtype=numpy.float32) + a + 1.0 + + +def xsor_add() -> None: + a = xsor.tensor(range(100000)) + b = xsor.tensor(range(100000)) + a + b + + +def xsor_mul() -> None: + a = xsor.tensor(range(100000)) + b = xsor.tensor(range(100000)) + a * b + + +def xsor_sum() -> None: + a = xsor.tensor(range(100000)) + a.sum() + + +def xsor_matmul() -> None: + a = xsor.tensor(range(100000), (100, 1000)) + b = xsor.tensor(range(100000), (1000, 100)) + a @ b + + +def xsor_add_scalar() -> None: + a = xsor.tensor(range(100000)) + a + 1.0 + + +N = 1000 + +for title, numpy_fn, xsor_fn in ( + ("ADD", numpy_add, xsor_add), + ("MUL", numpy_mul, xsor_mul), + ("SUM", numpy_sum, xsor_sum), + ("MATMUL", numpy_matmul, xsor_matmul), + ("ADD_SCALAR", numpy_add_scalar, xsor_add_scalar), +): + print(f"[ {title} ]") + + print(" numpy ... ", end="") + start = time.time() + for _ in range(N): + numpy_fn() + end = time.time() + print(f"{end - start:.3f}s") + + print(" xsor ... ", end="") + start = time.time() + for _ in range(N): + xsor_fn() + end = time.time() + print(f"{end - start:.3f}s") + + print() diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..1255eb7 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,658 @@ +# This file is automatically @generated by Poetry 1.7.0 and should not be changed by hand. + +[[package]] +name = "black" +version = "23.12.1" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-23.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2"}, + {file = "black-23.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba"}, + {file = "black-23.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0"}, + {file = "black-23.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3"}, + {file = "black-23.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba"}, + {file = "black-23.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b"}, + {file = "black-23.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59"}, + {file = "black-23.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50"}, + {file = "black-23.12.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e"}, + {file = "black-23.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec"}, + {file = "black-23.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e"}, + {file = "black-23.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9"}, + {file = "black-23.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f"}, + {file = "black-23.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d"}, + {file = "black-23.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a"}, + {file = "black-23.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e"}, + {file = "black-23.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055"}, + {file = "black-23.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54"}, + {file = "black-23.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea"}, + {file = "black-23.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2"}, + {file = "black-23.12.1-py3-none-any.whl", hash = "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e"}, + {file = "black-23.12.1.tar.gz", hash = "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "colorlog" +version = "4.8.0" +description = "Log formatting with colors!" +optional = false +python-versions = "*" +files = [ + {file = "colorlog-4.8.0-py2.py3-none-any.whl", hash = "sha256:3dd15cb27e8119a24c1a7b5c93f9f3b455855e0f73993b1c25921b2f646f1dcd"}, + {file = "colorlog-4.8.0.tar.gz", hash = "sha256:59b53160c60902c405cdec28d38356e09d40686659048893e026ecbd589516b1"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} + +[[package]] +name = "dacite" +version = "1.8.1" +description = "Simple creation of data classes from dictionaries." +optional = false +python-versions = ">=3.6" +files = [ + {file = "dacite-1.8.1-py3-none-any.whl", hash = "sha256:cc31ad6fdea1f49962ea42db9421772afe01ac5442380d9a99fcf3d188c61afe"}, +] + +[package.extras] +dev = ["black", "coveralls", "mypy", "pre-commit", "pylint", "pytest (>=5)", "pytest-benchmark", "pytest-cov"] + +[[package]] +name = "exceptiongroup" +version = "1.2.0" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, + {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "flake8" +version = "6.1.0" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"}, + {file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.11.0,<2.12.0" +pyflakes = ">=3.1.0,<3.2.0" + +[[package]] +name = "gitdb" +version = "4.0.11" +description = "Git Object Database" +optional = false +python-versions = ">=3.7" +files = [ + {file = "gitdb-4.0.11-py3-none-any.whl", hash = "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4"}, + {file = "gitdb-4.0.11.tar.gz", hash = "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b"}, +] + +[package.dependencies] +smmap = ">=3.0.1,<6" + +[[package]] +name = "gitpython" +version = "3.1.41" +description = "GitPython is a Python library used to interact with Git repositories" +optional = false +python-versions = ">=3.7" +files = [ + {file = "GitPython-3.1.41-py3-none-any.whl", hash = "sha256:c36b6634d069b3f719610175020a9aed919421c87552185b085e04fbbdb10b7c"}, + {file = "GitPython-3.1.41.tar.gz", hash = "sha256:ed66e624884f76df22c8e16066d567aaa5a37d5b5fa19db2c6df6f7156db9048"}, +] + +[package.dependencies] +gitdb = ">=4.0.1,<5" + +[package.extras] +test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "sumtypes"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[package]] +name = "jedi" +version = "0.17.2" +description = "An autocompletion tool for Python that can be used for text editors." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "jedi-0.17.2-py2.py3-none-any.whl", hash = "sha256:98cc583fa0f2f8304968199b01b6b4b94f469a1f4a74c1560506ca2a211378b5"}, + {file = "jedi-0.17.2.tar.gz", hash = "sha256:86ed7d9b750603e4ba582ea8edc678657fb4007894a12bcf6f4bb97892f31d20"}, +] + +[package.dependencies] +parso = ">=0.7.0,<0.8.0" + +[package.extras] +qa = ["flake8 (==3.7.9)"] +testing = ["Django (<3.1)", "colorama", "docopt", "pytest (>=3.9.0,<5.0.0)"] + +[[package]] +name = "maturin" +version = "1.4.0" +description = "Build and publish crates with pyo3, rust-cpython and cffi bindings as well as rust binaries as python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "maturin-1.4.0-py3-none-linux_armv6l.whl", hash = "sha256:b84bee85620e1b7b662a7af71289f7f6c23df8269e42c0f76882676dfc9c733f"}, + {file = "maturin-1.4.0-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:076970a73da7fa3648204a584cd347b899c1ea67f8124b212bccd06728e63ed9"}, + {file = "maturin-1.4.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f8eded83abdb30b2b6ae6d32c80b8192bdd8bcfec0ebfacee6ac02434aa499d6"}, + {file = "maturin-1.4.0-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:ff95a4494d9e57b6e74d4d7f8a9a2ee8ed29bd7f0e61855656ad959a432c0efc"}, + {file = "maturin-1.4.0-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:16239a7648ef17976585353e381840c18e650d352576ed9545abca407d65e534"}, + {file = "maturin-1.4.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:77428c043d585f038f4b056c4d617e00a8027b49598ab6d065b8f6b9b9b8d144"}, + {file = "maturin-1.4.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:b4b2f006db1e92687c814576029157dcc2d97b5750fd35fd4f3aabb97e36444f"}, + {file = "maturin-1.4.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:ffe4e967080ceb83c156e73a37d3974b30cad01c376a86dc39a76a0c6bccf9b0"}, + {file = "maturin-1.4.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:01473dc30aed8f2cee3572b3e99e3ea75bf09c84b028bf6077f7643a189699c8"}, + {file = "maturin-1.4.0-py3-none-win32.whl", hash = "sha256:e669ba5984c15e29b8545b295ba6738974180b44f47f5d9e75569a5ce6b8add5"}, + {file = "maturin-1.4.0-py3-none-win_amd64.whl", hash = "sha256:e2c1b157397ef3721b9c2f3f24d9a5a60bd84322aac13b4dd0704a80448741b0"}, + {file = "maturin-1.4.0-py3-none-win_arm64.whl", hash = "sha256:2979175a7eee837dc3a6931980b37ddc86b9ced54d600856668fc074ca2530ef"}, + {file = "maturin-1.4.0.tar.gz", hash = "sha256:ed12e1768094a7adeafc3a74ebdb8dc2201fa64c4e7e31f14cfc70378bf93790"}, +] + +[package.dependencies] +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} + +[package.extras] +patchelf = ["patchelf"] +zig = ["ziglang (>=0.10.0,<0.11.0)"] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "mypy" +version = "1.8.0" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3"}, + {file = "mypy-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4"}, + {file = "mypy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d"}, + {file = "mypy-1.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9"}, + {file = "mypy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410"}, + {file = "mypy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae"}, + {file = "mypy-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3"}, + {file = "mypy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817"}, + {file = "mypy-1.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d"}, + {file = "mypy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835"}, + {file = "mypy-1.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd"}, + {file = "mypy-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55"}, + {file = "mypy-1.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218"}, + {file = "mypy-1.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3"}, + {file = "mypy-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e"}, + {file = "mypy-1.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6"}, + {file = "mypy-1.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66"}, + {file = "mypy-1.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6"}, + {file = "mypy-1.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d"}, + {file = "mypy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02"}, + {file = "mypy-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8"}, + {file = "mypy-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259"}, + {file = "mypy-1.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b"}, + {file = "mypy-1.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592"}, + {file = "mypy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a"}, + {file = "mypy-1.8.0-py3-none-any.whl", hash = "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d"}, + {file = "mypy-1.8.0.tar.gz", hash = "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=4.1.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "numpy" +version = "1.26.3" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "numpy-1.26.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:806dd64230dbbfaca8a27faa64e2f414bf1c6622ab78cc4264f7f5f028fee3bf"}, + {file = "numpy-1.26.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02f98011ba4ab17f46f80f7f8f1c291ee7d855fcef0a5a98db80767a468c85cd"}, + {file = "numpy-1.26.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d45b3ec2faed4baca41c76617fcdcfa4f684ff7a151ce6fc78ad3b6e85af0a6"}, + {file = "numpy-1.26.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdd2b45bf079d9ad90377048e2747a0c82351989a2165821f0c96831b4a2a54b"}, + {file = "numpy-1.26.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:211ddd1e94817ed2d175b60b6374120244a4dd2287f4ece45d49228b4d529178"}, + {file = "numpy-1.26.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1240f767f69d7c4c8a29adde2310b871153df9b26b5cb2b54a561ac85146485"}, + {file = "numpy-1.26.3-cp310-cp310-win32.whl", hash = "sha256:21a9484e75ad018974a2fdaa216524d64ed4212e418e0a551a2d83403b0531d3"}, + {file = "numpy-1.26.3-cp310-cp310-win_amd64.whl", hash = "sha256:9e1591f6ae98bcfac2a4bbf9221c0b92ab49762228f38287f6eeb5f3f55905ce"}, + {file = "numpy-1.26.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b831295e5472954104ecb46cd98c08b98b49c69fdb7040483aff799a755a7374"}, + {file = "numpy-1.26.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9e87562b91f68dd8b1c39149d0323b42e0082db7ddb8e934ab4c292094d575d6"}, + {file = "numpy-1.26.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c66d6fec467e8c0f975818c1796d25c53521124b7cfb760114be0abad53a0a2"}, + {file = "numpy-1.26.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f25e2811a9c932e43943a2615e65fc487a0b6b49218899e62e426e7f0a57eeda"}, + {file = "numpy-1.26.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af36e0aa45e25c9f57bf684b1175e59ea05d9a7d3e8e87b7ae1a1da246f2767e"}, + {file = "numpy-1.26.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:51c7f1b344f302067b02e0f5b5d2daa9ed4a721cf49f070280ac202738ea7f00"}, + {file = "numpy-1.26.3-cp311-cp311-win32.whl", hash = "sha256:7ca4f24341df071877849eb2034948459ce3a07915c2734f1abb4018d9c49d7b"}, + {file = "numpy-1.26.3-cp311-cp311-win_amd64.whl", hash = "sha256:39763aee6dfdd4878032361b30b2b12593fb445ddb66bbac802e2113eb8a6ac4"}, + {file = "numpy-1.26.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a7081fd19a6d573e1a05e600c82a1c421011db7935ed0d5c483e9dd96b99cf13"}, + {file = "numpy-1.26.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12c70ac274b32bc00c7f61b515126c9205323703abb99cd41836e8125ea0043e"}, + {file = "numpy-1.26.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f784e13e598e9594750b2ef6729bcd5a47f6cfe4a12cca13def35e06d8163e3"}, + {file = "numpy-1.26.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f24750ef94d56ce6e33e4019a8a4d68cfdb1ef661a52cdaee628a56d2437419"}, + {file = "numpy-1.26.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:77810ef29e0fb1d289d225cabb9ee6cf4d11978a00bb99f7f8ec2132a84e0166"}, + {file = "numpy-1.26.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8ed07a90f5450d99dad60d3799f9c03c6566709bd53b497eb9ccad9a55867f36"}, + {file = "numpy-1.26.3-cp312-cp312-win32.whl", hash = "sha256:f73497e8c38295aaa4741bdfa4fda1a5aedda5473074369eca10626835445511"}, + {file = "numpy-1.26.3-cp312-cp312-win_amd64.whl", hash = "sha256:da4b0c6c699a0ad73c810736303f7fbae483bcb012e38d7eb06a5e3b432c981b"}, + {file = "numpy-1.26.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1666f634cb3c80ccbd77ec97bc17337718f56d6658acf5d3b906ca03e90ce87f"}, + {file = "numpy-1.26.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:18c3319a7d39b2c6a9e3bb75aab2304ab79a811ac0168a671a62e6346c29b03f"}, + {file = "numpy-1.26.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b7e807d6888da0db6e7e75838444d62495e2b588b99e90dd80c3459594e857b"}, + {file = "numpy-1.26.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4d362e17bcb0011738c2d83e0a65ea8ce627057b2fdda37678f4374a382a137"}, + {file = "numpy-1.26.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b8c275f0ae90069496068c714387b4a0eba5d531aace269559ff2b43655edd58"}, + {file = "numpy-1.26.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cc0743f0302b94f397a4a65a660d4cd24267439eb16493fb3caad2e4389bccbb"}, + {file = "numpy-1.26.3-cp39-cp39-win32.whl", hash = "sha256:9bc6d1a7f8cedd519c4b7b1156d98e051b726bf160715b769106661d567b3f03"}, + {file = "numpy-1.26.3-cp39-cp39-win_amd64.whl", hash = "sha256:867e3644e208c8922a3be26fc6bbf112a035f50f0a86497f98f228c50c607bb2"}, + {file = "numpy-1.26.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3c67423b3703f8fbd90f5adaa37f85b5794d3366948efe9a5190a5f3a83fc34e"}, + {file = "numpy-1.26.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46f47ee566d98849323f01b349d58f2557f02167ee301e5e28809a8c0e27a2d0"}, + {file = "numpy-1.26.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a8474703bffc65ca15853d5fd4d06b18138ae90c17c8d12169968e998e448bb5"}, + {file = "numpy-1.26.3.tar.gz", hash = "sha256:697df43e2b6310ecc9d95f05d5ef20eacc09c7c4ecc9da3f235d39e71b7da1e4"}, +] + +[[package]] +name = "packaging" +version = "23.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, +] + +[[package]] +name = "parso" +version = "0.7.1" +description = "A Python Parser" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "parso-0.7.1-py2.py3-none-any.whl", hash = "sha256:97218d9159b2520ff45eb78028ba8b50d2bc61dcc062a9682666f2dc4bd331ea"}, + {file = "parso-0.7.1.tar.gz", hash = "sha256:caba44724b994a8a5e086460bb212abc5a8bc46951bf4a9a1210745953622eb9"}, +] + +[package.extras] +testing = ["docopt", "pytest (>=3.0.7)"] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "platformdirs" +version = "4.1.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"}, + {file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"}, +] + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] + +[[package]] +name = "pluggy" +version = "1.3.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, + {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pycodestyle" +version = "2.11.1" +description = "Python style guide checker" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, + {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, +] + +[[package]] +name = "pyflakes" +version = "3.1.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"}, + {file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, +] + +[[package]] +name = "pysen" +version = "0.10.5" +description = "Python linting made easy. Also a casual yet honorific way to address individuals who have entered an organization prior to you." +optional = false +python-versions = "*" +files = [ + {file = "pysen-0.10.5-py3-none-any.whl", hash = "sha256:4e8a83263f04585807e3754622bb635d4a0ccd88ec1a4f324e8c9efba300237f"}, + {file = "pysen-0.10.5.tar.gz", hash = "sha256:61a6674e0b8a0c6b837b878310bd4117c5d4108dd95db572caa31c5b311d85bb"}, +] + +[package.dependencies] +colorlog = ">=4.0.0,<5.0.0" +dacite = ">=1.1.0,<2.0.0" +GitPython = ">=3.0.0,<4.0.0" +tomlkit = ">=0.5.11,<1.0.0" +unidiff = ">=0.6.0,<1.0.0" + +[package.extras] +lint = ["black (>=19.10b0,<=22.10)", "flake8 (>=3.7,<5)", "flake8-bugbear", "isort (>=4.3,<5.2.0)", "mypy (>=0.770,<0.800)"] + +[[package]] +name = "pytest" +version = "7.4.4" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "python-jsonrpc-server" +version = "0.4.0" +description = "JSON RPC 2.0 server library" +optional = false +python-versions = "*" +files = [ + {file = "python-jsonrpc-server-0.4.0.tar.gz", hash = "sha256:62c543e541f101ec5b57dc654efc212d2c2e3ea47ff6f54b2e7dcb36ecf20595"}, + {file = "python_jsonrpc_server-0.4.0-py3-none-any.whl", hash = "sha256:e5a908ff182e620aac07db5f57887eeb0afe33993008f57dc1b85b594cea250c"}, +] + +[package.dependencies] +ujson = ">=3.0.0" + +[package.extras] +test = ["coverage", "mock", "pycodestyle", "pyflakes", "pylint", "pytest", "pytest-cov", "versioneer"] + +[[package]] +name = "python-language-server" +version = "0.36.2" +description = "Python Language Server for the Language Server Protocol" +optional = false +python-versions = "*" +files = [ + {file = "python-language-server-0.36.2.tar.gz", hash = "sha256:9984c84a67ee2c5102c8e703215f407fcfa5e62b0ae86c9572d0ada8c4b417b0"}, + {file = "python_language_server-0.36.2-py2.py3-none-any.whl", hash = "sha256:a0ad0aca03f4a20c1c40f4f230c6773eac82c9b7cdb026cb09ba10237f4815d5"}, +] + +[package.dependencies] +jedi = ">=0.17.2,<0.18.0" +pluggy = "*" +python-jsonrpc-server = ">=0.4.0" +ujson = {version = ">=3.0.0", markers = "python_version > \"3\""} + +[package.extras] +all = ["autopep8", "flake8 (>=3.8.0)", "mccabe (>=0.6.0,<0.7.0)", "pycodestyle (>=2.6.0,<2.7.0)", "pydocstyle (>=2.0.0)", "pyflakes (>=2.2.0,<2.3.0)", "pylint (>=2.5.0)", "rope (>=0.10.5)", "yapf"] +autopep8 = ["autopep8"] +flake8 = ["flake8 (>=3.8.0)"] +mccabe = ["mccabe (>=0.6.0,<0.7.0)"] +pycodestyle = ["pycodestyle (>=2.6.0,<2.7.0)"] +pydocstyle = ["pydocstyle (>=2.0.0)"] +pyflakes = ["pyflakes (>=2.2.0,<2.3.0)"] +pylint = ["pylint (>=2.5.0)"] +rope = ["rope (>0.10.5)"] +test = ["coverage", "flaky", "matplotlib", "mock", "numpy", "pandas", "pylint (>=2.5.0)", "pyqt5", "pytest", "pytest-cov", "versioneer"] +yapf = ["yapf"] + +[[package]] +name = "smmap" +version = "5.0.1" +description = "A pure Python implementation of a sliding window memory map manager" +optional = false +python-versions = ">=3.7" +files = [ + {file = "smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da"}, + {file = "smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62"}, +] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "tomlkit" +version = "0.12.3" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomlkit-0.12.3-py3-none-any.whl", hash = "sha256:b0a645a9156dc7cb5d3a1f0d4bab66db287fcb8e0430bdd4664a095ea16414ba"}, + {file = "tomlkit-0.12.3.tar.gz", hash = "sha256:75baf5012d06501f07bee5bf8e801b9f343e7aac5a92581f20f80ce632e6b5a4"}, +] + +[[package]] +name = "typing-extensions" +version = "4.9.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, + {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, +] + +[[package]] +name = "ujson" +version = "5.9.0" +description = "Ultra fast JSON encoder and decoder for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ujson-5.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ab71bf27b002eaf7d047c54a68e60230fbd5cd9da60de7ca0aa87d0bccead8fa"}, + {file = "ujson-5.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a365eac66f5aa7a7fdf57e5066ada6226700884fc7dce2ba5483538bc16c8c5"}, + {file = "ujson-5.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e015122b337858dba5a3dc3533af2a8fc0410ee9e2374092f6a5b88b182e9fcc"}, + {file = "ujson-5.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:779a2a88c53039bebfbccca934430dabb5c62cc179e09a9c27a322023f363e0d"}, + {file = "ujson-5.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10ca3c41e80509fd9805f7c149068fa8dbee18872bbdc03d7cca928926a358d5"}, + {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a566e465cb2fcfdf040c2447b7dd9718799d0d90134b37a20dff1e27c0e9096"}, + {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f833c529e922577226a05bc25b6a8b3eb6c4fb155b72dd88d33de99d53113124"}, + {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b68a0caab33f359b4cbbc10065c88e3758c9f73a11a65a91f024b2e7a1257106"}, + {file = "ujson-5.9.0-cp310-cp310-win32.whl", hash = "sha256:7cc7e605d2aa6ae6b7321c3ae250d2e050f06082e71ab1a4200b4ae64d25863c"}, + {file = "ujson-5.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:a6d3f10eb8ccba4316a6b5465b705ed70a06011c6f82418b59278fbc919bef6f"}, + {file = "ujson-5.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b23bbb46334ce51ddb5dded60c662fbf7bb74a37b8f87221c5b0fec1ec6454b"}, + {file = "ujson-5.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6974b3a7c17bbf829e6c3bfdc5823c67922e44ff169851a755eab79a3dd31ec0"}, + {file = "ujson-5.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5964ea916edfe24af1f4cc68488448fbb1ec27a3ddcddc2b236da575c12c8ae"}, + {file = "ujson-5.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ba7cac47dd65ff88571eceeff48bf30ed5eb9c67b34b88cb22869b7aa19600d"}, + {file = "ujson-5.9.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6bbd91a151a8f3358c29355a491e915eb203f607267a25e6ab10531b3b157c5e"}, + {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:829a69d451a49c0de14a9fecb2a2d544a9b2c884c2b542adb243b683a6f15908"}, + {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:a807ae73c46ad5db161a7e883eec0fbe1bebc6a54890152ccc63072c4884823b"}, + {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8fc2aa18b13d97b3c8ccecdf1a3c405f411a6e96adeee94233058c44ff92617d"}, + {file = "ujson-5.9.0-cp311-cp311-win32.whl", hash = "sha256:70e06849dfeb2548be48fdd3ceb53300640bc8100c379d6e19d78045e9c26120"}, + {file = "ujson-5.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:7309d063cd392811acc49b5016728a5e1b46ab9907d321ebbe1c2156bc3c0b99"}, + {file = "ujson-5.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:20509a8c9f775b3a511e308bbe0b72897ba6b800767a7c90c5cca59d20d7c42c"}, + {file = "ujson-5.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b28407cfe315bd1b34f1ebe65d3bd735d6b36d409b334100be8cdffae2177b2f"}, + {file = "ujson-5.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d302bd17989b6bd90d49bade66943c78f9e3670407dbc53ebcf61271cadc399"}, + {file = "ujson-5.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f21315f51e0db8ee245e33a649dd2d9dce0594522de6f278d62f15f998e050e"}, + {file = "ujson-5.9.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5635b78b636a54a86fdbf6f027e461aa6c6b948363bdf8d4fbb56a42b7388320"}, + {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:82b5a56609f1235d72835ee109163c7041b30920d70fe7dac9176c64df87c164"}, + {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5ca35f484622fd208f55041b042d9d94f3b2c9c5add4e9af5ee9946d2d30db01"}, + {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:829b824953ebad76d46e4ae709e940bb229e8999e40881338b3cc94c771b876c"}, + {file = "ujson-5.9.0-cp312-cp312-win32.whl", hash = "sha256:25fa46e4ff0a2deecbcf7100af3a5d70090b461906f2299506485ff31d9ec437"}, + {file = "ujson-5.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:60718f1720a61560618eff3b56fd517d107518d3c0160ca7a5a66ac949c6cf1c"}, + {file = "ujson-5.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d581db9db9e41d8ea0b2705c90518ba623cbdc74f8d644d7eb0d107be0d85d9c"}, + {file = "ujson-5.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ff741a5b4be2d08fceaab681c9d4bc89abf3c9db600ab435e20b9b6d4dfef12e"}, + {file = "ujson-5.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdcb02cabcb1e44381221840a7af04433c1dc3297af76fde924a50c3054c708c"}, + {file = "ujson-5.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e208d3bf02c6963e6ef7324dadf1d73239fb7008491fdf523208f60be6437402"}, + {file = "ujson-5.9.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4b3917296630a075e04d3d07601ce2a176479c23af838b6cf90a2d6b39b0d95"}, + {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0c4d6adb2c7bb9eb7c71ad6f6f612e13b264942e841f8cc3314a21a289a76c4e"}, + {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0b159efece9ab5c01f70b9d10bbb77241ce111a45bc8d21a44c219a2aec8ddfd"}, + {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0cb4a7814940ddd6619bdce6be637a4b37a8c4760de9373bac54bb7b229698b"}, + {file = "ujson-5.9.0-cp38-cp38-win32.whl", hash = "sha256:dc80f0f5abf33bd7099f7ac94ab1206730a3c0a2d17549911ed2cb6b7aa36d2d"}, + {file = "ujson-5.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:506a45e5fcbb2d46f1a51fead991c39529fc3737c0f5d47c9b4a1d762578fc30"}, + {file = "ujson-5.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0fd2eba664a22447102062814bd13e63c6130540222c0aa620701dd01f4be81"}, + {file = "ujson-5.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bdf7fc21a03bafe4ba208dafa84ae38e04e5d36c0e1c746726edf5392e9f9f36"}, + {file = "ujson-5.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2f909bc08ce01f122fd9c24bc6f9876aa087188dfaf3c4116fe6e4daf7e194f"}, + {file = "ujson-5.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd4ea86c2afd41429751d22a3ccd03311c067bd6aeee2d054f83f97e41e11d8f"}, + {file = "ujson-5.9.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:63fb2e6599d96fdffdb553af0ed3f76b85fda63281063f1cb5b1141a6fcd0617"}, + {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:32bba5870c8fa2a97f4a68f6401038d3f1922e66c34280d710af00b14a3ca562"}, + {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:37ef92e42535a81bf72179d0e252c9af42a4ed966dc6be6967ebfb929a87bc60"}, + {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f69f16b8f1c69da00e38dc5f2d08a86b0e781d0ad3e4cc6a13ea033a439c4844"}, + {file = "ujson-5.9.0-cp39-cp39-win32.whl", hash = "sha256:3382a3ce0ccc0558b1c1668950008cece9bf463ebb17463ebf6a8bfc060dae34"}, + {file = "ujson-5.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:6adef377ed583477cf005b58c3025051b5faa6b8cc25876e594afbb772578f21"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ffdfebd819f492e48e4f31c97cb593b9c1a8251933d8f8972e81697f00326ff1"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4eec2ddc046360d087cf35659c7ba0cbd101f32035e19047013162274e71fcf"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbb90aa5c23cb3d4b803c12aa220d26778c31b6e4b7a13a1f49971f6c7d088e"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba0823cb70866f0d6a4ad48d998dd338dce7314598721bc1b7986d054d782dfd"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4e35d7885ed612feb6b3dd1b7de28e89baaba4011ecdf995e88be9ac614765e9"}, + {file = "ujson-5.9.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b048aa93eace8571eedbd67b3766623e7f0acbf08ee291bef7d8106210432427"}, + {file = "ujson-5.9.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:323279e68c195110ef85cbe5edce885219e3d4a48705448720ad925d88c9f851"}, + {file = "ujson-5.9.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ac92d86ff34296f881e12aa955f7014d276895e0e4e868ba7fddebbde38e378"}, + {file = "ujson-5.9.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:6eecbd09b316cea1fd929b1e25f70382917542ab11b692cb46ec9b0a26c7427f"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:473fb8dff1d58f49912323d7cb0859df5585cfc932e4b9c053bf8cf7f2d7c5c4"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f91719c6abafe429c1a144cfe27883eace9fb1c09a9c5ef1bcb3ae80a3076a4e"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b1c0991c4fe256f5fdb19758f7eac7f47caac29a6c57d0de16a19048eb86bad"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a8ea0f55a1396708e564595aaa6696c0d8af532340f477162ff6927ecc46e21"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:07e0cfdde5fd91f54cd2d7ffb3482c8ff1bf558abf32a8b953a5d169575ae1cd"}, + {file = "ujson-5.9.0.tar.gz", hash = "sha256:89cc92e73d5501b8a7f48575eeb14ad27156ad092c2e9fc7e3cf949f07e75532"}, +] + +[[package]] +name = "unidiff" +version = "0.7.5" +description = "Unified diff parsing/metadata extraction library." +optional = false +python-versions = "*" +files = [ + {file = "unidiff-0.7.5-py2.py3-none-any.whl", hash = "sha256:c93bf2265cc1ba2a520e415ab05da587370bc2a3ae9e0414329f54f0c2fc09e8"}, + {file = "unidiff-0.7.5.tar.gz", hash = "sha256:2e5f0162052248946b9f0970a40e9e124236bf86c82b70821143a6fc1dea2574"}, +] + +[metadata] +lock-version = "2.0" +python-versions = ">=3.10,<4.0" +content-hash = "a344b17318ae47d45ed500dc385ae16ecade1b3f1c125fb50eeb9f02934f7b15" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..3b810d1 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,67 @@ +[tool.poetry] +name = "xsor" +version = "0.0.1" +description = "" +authors = ["altescy "] + +[[tool.poetry.source]] +name = "pypi" +priority = "primary" + +[tool.poetry.dependencies] +python = ">=3.10,<4.0" + +[tool.poetry.group.dev.dependencies] +python-language-server = "^0.36.2" +pytest = "^7.3.1" +pysen = "^0.10.4" +black = "^23.3.0" +isort = "^5.12.0" +flake8 = "^6.0.0" +mypy = "^1.2.0" +maturin = "^1.4.0" + + +[tool.poetry.group.benchmark.dependencies] +numpy = "*" + +[tool.pysen] +version = "0.10" + +[tool.pysen-cli] +settings_dir = "." + +[tool.pysen.lint] +enable_black = true +enable_flake8 = true +enable_isort = true +enable_mypy = true +mypy_preset = "strict" +line_length = 120 +py_version = "py310" +[[tool.pysen.lint.mypy_targets]] + paths = ["."] + +[tool.pysen.lint.source] + includes = ["."] + excludes = [".venv/"] + + +[tool.black] # automatically generated by pysen +# pysen ignores and overwrites any modifications +line-length = 120 +target-version = ["py310"] + +[tool.isort] # automatically generated by pysen +# pysen ignores and overwrites any modifications +default_section = "THIRDPARTY" +ensure_newline_before_comments = true +force_grid_wrap = 0 +force_single_line = false +include_trailing_comma = true +line_length = 120 +multi_line_output = 3 +use_parentheses = true +[build-system] +requires = ["maturin>=0.12,<0.13"] +build-backend = "maturin" diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..d51cb03 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,36 @@ +[flake8] +# automatically generated by pysen +# pysen ignores and overwrites any modifications +# e203: black treats : as a binary operator +# e231: black doesn't put a space after , +# e501: black may exceed the line-length to follow other style rules +# w503 or w504: either one needs to be disabled to select w error codes +ignore = E203,E231,E501,W503 +max-line-length = 120 +select = B,B950,C,E,F,W + +[mypy] +# automatically generated by pysen +# pysen ignores and overwrites any modifications +check_untyped_defs = True +disallow_any_decorated = False +disallow_any_generics = False +disallow_any_unimported = False +disallow_incomplete_defs = True +disallow_subclassing_any = True +disallow_untyped_calls = True +disallow_untyped_decorators = False +disallow_untyped_defs = True +ignore_errors = False +ignore_missing_imports = True +no_implicit_optional = True +python_version = 3.10 +show_error_codes = True +strict_equality = True +strict_optional = True +warn_redundant_casts = True +warn_return_any = True +warn_unreachable = True +warn_unused_configs = True +warn_unused_ignores = False + diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..27435cd --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,221 @@ +#![feature(portable_simd)] +#![feature(iter_collect_into)] + +use pyo3::prelude::*; + +mod simd; +mod tensor; +mod util; + +use simd::{ + add_f32_vector_simd, div_f32_tensor_simd, matmul_f32_tensor_simd, mul_f32_tensor_simd, + sub_f32_vector_simd, sum_f32_vector_simd, +}; +use tensor::Tensor; +use util::broadcast_tensors; + +#[pyclass(name = "TensorF32")] +struct PyTensorF32 { + tensor: Tensor, +} + +#[pymethods] +impl PyTensorF32 { + #[new] + fn __new__(data: Vec, shape: Vec) -> Self { + assert_eq!(data.len(), shape.iter().product()); + Self { + tensor: Tensor { data, shape }, + } + } + + #[staticmethod] + fn zeros(shape: Vec) -> Self { + Self { + tensor: Tensor::zeros(&shape), + } + } + + #[staticmethod] + fn ones(shape: Vec) -> Self { + Self { + tensor: Tensor::ones(&shape), + } + } + + #[staticmethod] + fn eye(size: usize) -> Self { + Self { + tensor: Tensor::eye(size), + } + } + + fn __repr__(&self) -> String { + format!("{}", &self.tensor) + } + + fn neg(&self) -> Self { + Self { + tensor: self.tensor.neg(), + } + } + + fn add_tensor(&self, other: &Self) -> Self { + let tensors: Vec>; + let (left, right) = if self.tensor.shape == other.tensor.shape { + (&self.tensor, &other.tensor) + } else { + tensors = broadcast_tensors(&[&self.tensor, &other.tensor]); + (&tensors[0], &tensors[1]) + }; + Self { + tensor: Tensor { + data: add_f32_vector_simd(&left.data, &right.data), + shape: left.shape.clone(), + }, + } + } + + fn sub_tensor(&self, other: &Self) -> Self { + let tensors: Vec>; + let (left, right) = if self.tensor.shape == other.tensor.shape { + (&self.tensor, &other.tensor) + } else { + tensors = broadcast_tensors(&[&self.tensor, &other.tensor]); + (&tensors[0], &tensors[1]) + }; + Self { + tensor: Tensor { + data: sub_f32_vector_simd(&left.data, &right.data), + shape: left.shape.clone(), + }, + } + } + + fn mul_tensor(&self, other: &Self) -> Self { + let tensors: Vec>; + let (left, right) = if self.tensor.shape == other.tensor.shape { + (&self.tensor, &other.tensor) + } else { + tensors = broadcast_tensors(&[&self.tensor, &other.tensor]); + (&tensors[0], &tensors[1]) + }; + Self { + tensor: Tensor { + data: mul_f32_tensor_simd(&left.data, &right.data), + shape: left.shape.clone(), + }, + } + } + + fn div_tensor(&self, other: &Self) -> Self { + let tensors: Vec>; + let (left, right) = if self.tensor.shape == other.tensor.shape { + (&self.tensor, &other.tensor) + } else { + tensors = broadcast_tensors(&[&self.tensor, &other.tensor]); + (&tensors[0], &tensors[1]) + }; + Self { + tensor: Tensor { + data: div_f32_tensor_simd(&left.data, &right.data), + shape: left.shape.clone(), + }, + } + } + + fn add_scalar(&self, scalar: f32) -> Self { + Self { + tensor: self.tensor.add_scalar(scalar), + } + } + + fn sub_scalar(&self, scalar: f32) -> Self { + Self { + tensor: self.tensor.sub_scalar(scalar), + } + } + + fn mul_scalar(&self, scalar: f32) -> Self { + Self { + tensor: self.tensor.mul_scalar(scalar), + } + } + + fn div_scalar(&self, scalar: f32) -> Self { + Self { + tensor: self.tensor.div_scalar(scalar), + } + } + + fn matmul(&self, other: &Self) -> Self { + let (left, right) = (&self.tensor, &other.tensor); + assert_eq!(left.shape.len(), 2, "Left tensor must be 2D"); + assert_eq!(right.shape.len(), 2, "Right tensor must be 2D"); + assert_eq!(left.shape[1], right.shape[0], "Inner dimensions must match"); + Self { + tensor: Tensor { + data: matmul_f32_tensor_simd(&left.data, &right.data, &left.shape, &right.shape), + shape: vec![left.shape[0], right.shape[1]], + }, + } + } + + fn sum(&self) -> Self { + Self { + tensor: Tensor { + data: vec![sum_f32_vector_simd(&self.tensor.data)], + shape: vec![], + }, + } + } + + fn get(&self, index: Vec) -> Self { + let flattened_index = util::get_flattened_indice(&self.tensor.shape, &index); + let value = self.tensor.data[flattened_index]; + Self { + tensor: Tensor { + data: vec![value], + shape: vec![], + }, + } + } + + #[getter] + fn shape(&self) -> Vec { + self.tensor.shape.clone() + } + + #[getter] + fn data(&self) -> Vec { + self.tensor.data.clone() + } + + #[getter] + fn size(&self) -> usize { + self.tensor.data.len() + } + + #[getter] + fn ndim(&self) -> usize { + self.tensor.shape.len() + } + + #[getter] + fn value(&self) -> f32 { + assert_eq!(self.tensor.data.len(), 1); + self.tensor.data[0] + } + + fn reshape(&self, shape: Vec) -> Self { + Self { + tensor: self.tensor.reshape(&shape), + } + } +} + +#[pymodule] +fn xsor(_py: Python<'_>, m: &PyModule) -> PyResult<()> { + m.add_class::()?; + Ok(()) +} diff --git a/src/simd.rs b/src/simd.rs new file mode 100644 index 0000000..302204c --- /dev/null +++ b/src/simd.rs @@ -0,0 +1,139 @@ +use std::simd::Simd; + +pub fn sum_f32_vector_simd(a: &[f32]) -> f32 { + let chunk_size = 4; + let chunks = a.len() / chunk_size; + let mut sum = Simd::::splat(0.0); + for i in 0..chunks { + let a = Simd::::from_slice(&a[i * chunk_size..]); + sum += a; + } + let mut result = sum.as_array().iter().sum::(); + for i in (chunks * chunk_size)..a.len() { + result += a[i]; + } + result +} + +pub fn add_f32_vector_simd(a: &[f32], b: &[f32]) -> Vec { + assert_eq!(a.len(), b.len(), "Vectors must be of the same length"); + let mut result = Vec::with_capacity(a.len()); + if a.is_empty() { + return result; + } + let chunk_size = 4; + let chunks = a.len() / chunk_size; + for i in 0..chunks { + let a = Simd::::from_slice(&a[i * chunk_size..]); + let b = Simd::::from_slice(&b[i * chunk_size..]); + let c = a + b; + result.extend_from_slice(c.as_array()); + } + for i in (chunks * chunk_size)..a.len() { + result.push(a[i] + b[i]); + } + result +} + +pub fn sub_f32_vector_simd(a: &[f32], b: &[f32]) -> Vec { + assert_eq!(a.len(), b.len(), "Vectors must be of the same length"); + let mut result = Vec::with_capacity(a.len()); + if a.is_empty() { + return result; + } + let chunk_size = 4; + let chunks = a.len() / chunk_size; + for i in 0..chunks { + let a = Simd::::from_slice(&a[i * chunk_size..]); + let b = Simd::::from_slice(&b[i * chunk_size..]); + let c = a - b; + result.extend_from_slice(c.as_array()); + } + for i in (chunks * chunk_size)..a.len() { + result.push(a[i] - b[i]); + } + result +} + +pub fn mul_f32_tensor_simd(a: &[f32], b: &[f32]) -> Vec { + assert_eq!(a.len(), b.len(), "Vectors must be of the same length"); + let mut result = Vec::with_capacity(a.len()); + if a.is_empty() { + return result; + } + let chunk_size = 4; + let chunks = a.len() / chunk_size; + for i in 0..chunks { + let a = Simd::::from_slice(&a[i * chunk_size..]); + let b = Simd::::from_slice(&b[i * chunk_size..]); + let c = a * b; + result.extend_from_slice(c.as_array()); + } + for i in (chunks * chunk_size)..a.len() { + result.push(a[i] * b[i]); + } + result +} + +pub fn div_f32_tensor_simd(a: &[f32], b: &[f32]) -> Vec { + assert_eq!(a.len(), b.len(), "Vectors must be of the same length"); + let mut result = Vec::with_capacity(a.len()); + if a.is_empty() { + return result; + } + let chunk_size = 4; + let chunks = a.len() / chunk_size; + for i in 0..chunks { + let a = Simd::::from_slice(&a[i * chunk_size..]); + let b = Simd::::from_slice(&b[i * chunk_size..]); + let c = a / b; + result.extend_from_slice(c.as_array()); + } + for i in (chunks * chunk_size)..a.len() { + result.push(a[i] / b[i]); + } + result +} + +pub fn matmul_f32_tensor_simd( + a: &[f32], + b: &[f32], + a_shape: &[usize], + b_shape: &[usize], +) -> Vec { + assert_eq!( + a_shape[1], b_shape[0], + "Shapes do not align for matrix multiplication" + ); + + let m = a_shape[0]; + let n = b_shape[1]; + let k = a_shape[1]; + + let chunk_size = 4; + let chunks = k / chunk_size; + + let mut result = vec![0.0; m * n]; + let mut bcol = vec![0.0; k]; + for j in 0..n { + for (i, bcol) in bcol.iter_mut().enumerate() { + *bcol = b[i * n + j]; + } + for i in 0..m { + let arow = &a[i * k..(i + 1) * k]; + let mut sum = Simd::::splat(0.0); + for chunk in 0..chunks { + let a = Simd::::from_slice(&arow[chunk * chunk_size..]); + let b = Simd::::from_slice(&bcol[chunk * chunk_size..]); + sum += a * b; + } + let mut res = sum.as_array().iter().sum::(); + for chunk in (chunks * chunk_size)..k { + res += arow[chunk] * bcol[chunk]; + } + result[i * n + j] = res; + } + } + + result +} diff --git a/src/tensor.rs b/src/tensor.rs new file mode 100644 index 0000000..1d341ca --- /dev/null +++ b/src/tensor.rs @@ -0,0 +1,179 @@ +use num::Num; +use std::fmt::{self, Display, Formatter}; + +#[derive(Debug, PartialEq)] +pub struct Tensor { + pub data: Vec, + pub shape: Vec, +} + +impl Tensor +where + T: Copy, +{ + pub fn new(data: &[T], shape: &[usize]) -> Self { + assert_eq!(data.len(), shape.iter().product()); + Self { + data: data.to_vec(), + shape: shape.to_vec(), + } + } +} + +impl Clone for Tensor +where + T: Copy, +{ + fn clone(&self) -> Self { + Self { + data: self.data.clone(), + shape: self.shape.clone(), + } + } +} + +impl Tensor +where + T: Copy, +{ + pub fn reshape(&self, shape: &[usize]) -> Self { + assert_eq!(self.data.len(), shape.iter().product()); + Self::new(&self.data, shape) + } +} + +impl Display for Tensor +where + T: Display, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}", format_tensor(0, &self.shape, &self.data)) + } +} + +impl Tensor +where + T: Num + Copy, +{ + pub fn zeros(shape: &[usize]) -> Self { + let size = shape.iter().product(); + let data = vec![T::zero(); size]; + Self::new(&data, shape) + } + + pub fn ones(shape: &[usize]) -> Self { + let size = shape.iter().product(); + let data = vec![T::one(); size]; + Self::new(&data, shape) + } + + pub fn eye(size: usize) -> Self { + let mut data = vec![T::zero(); size * size]; + for i in 0..size { + data[i * size + i] = T::one(); + } + Self::new(&data, &[size, size]) + } + + pub fn neg(&self) -> Self { + let data: Vec<_> = self.data.iter().map(|a| T::zero() - *a).collect(); + Self::new(&data, &self.shape) + } + + pub fn add(&self, other: &Self) -> Self { + let data: Vec<_> = self + .data + .iter() + .zip(other.data.iter()) + .map(|(a, b)| *a + *b) + .collect(); + Self::new(&data, &self.shape) + } + + pub fn sub(&self, other: &Self) -> Self { + let data: Vec<_> = self + .data + .iter() + .zip(other.data.iter()) + .map(|(a, b)| *a - *b) + .collect(); + Self::new(&data, &self.shape) + } + + pub fn mul(&self, other: &Self) -> Self { + let data: Vec<_> = self + .data + .iter() + .zip(other.data.iter()) + .map(|(a, b)| *a * *b) + .collect(); + Self::new(&data, &self.shape) + } + + pub fn div(&self, other: &Self) -> Self { + let data: Vec<_> = self + .data + .iter() + .zip(other.data.iter()) + .map(|(a, b)| *a / *b) + .collect(); + Self::new(&data, &self.shape) + } + + pub fn add_scalar(&self, scalar: T) -> Self { + let data: Vec<_> = self.data.iter().map(|a| *a + scalar).collect(); + Self::new(&data, &self.shape) + } + + pub fn sub_scalar(&self, scalar: T) -> Self { + let data: Vec<_> = self.data.iter().map(|a| *a - scalar).collect(); + Self::new(&data, &self.shape) + } + + pub fn mul_scalar(&self, scalar: T) -> Self { + let data: Vec<_> = self.data.iter().map(|a| *a * scalar).collect(); + Self::new(&data, &self.shape) + } + + pub fn div_scalar(&self, scalar: T) -> Self { + let data: Vec<_> = self.data.iter().map(|a| *a / scalar).collect(); + Self::new(&data, &self.shape) + } +} + +fn format_tensor(level: usize, shape: &[usize], data: &[T]) -> String +where + T: Display, +{ + if shape.is_empty() { + return format!("{}", data[0]); + } + let dim = shape[0]; + let extra_size: usize = shape[1..].iter().product(); + let substrings = (0..dim) + .map(|i| { + format_tensor( + level + 1, + &shape[1..], + &data[i * extra_size..(i + 1) * extra_size], + ) + }) + .collect::>(); + if shape.len() == 1 { + return format!("[{}]", substrings.join(", ")); + } + let newline = "\n"; + let indent = " ".repeat(level); + let inner_delimiter = format!(",{}", newline); + let outer_delimiter = format!("{}{}", newline, indent); + format!( + "[{}{}{}]", + newline, + substrings + .iter() + .map(|s| format!("{} {}", &indent, &s)) + .collect::>() + .join(&inner_delimiter), + outer_delimiter, + ) +} diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..422ba13 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,137 @@ +use std::collections::HashSet; + +use super::tensor::Tensor; + +pub fn get_flattened_indice(shape: &[usize], index: &[usize]) -> usize { + assert_eq!(shape.len(), index.len()); + index.iter().enumerate().fold(0, |acc, (i, &v)| { + acc + v * shape[i + 1..].iter().product::() + }) +} + +pub fn get_nested_indice(shape: &[usize], index: usize) -> Vec { + let mut index = index; + let mut nested_index = vec![0; shape.len()]; + for i in (0..shape.len()).rev() { + let size = shape[i]; + nested_index[i] = index % size; + index /= size; + } + nested_index +} + +pub fn broadcast_shapes(shapes: &[&[usize]]) -> Vec { + let max_ndim = shapes.iter().map(|s| s.len()).max().unwrap(); + let shapes: Vec> = shapes + .iter() + .map(|s| { + let mut s = s.to_vec(); + while s.len() < max_ndim { + s.insert(0, 1); + } + s + }) + .collect(); + let mut shape = vec![1; max_ndim]; + for dim in 0..max_ndim { + let sizes: HashSet = shapes.iter().map(|s| s[dim]).collect(); + assert!( + sizes.len() <= 2 || (sizes.len() == 2 && sizes.contains(&1)), + "Shapes are not broadcastable" + ); + let max_size = sizes.iter().max().unwrap(); + shape[dim] = *max_size; + } + shape +} + +pub fn broadcast_to(tensor: &Tensor, shape: &[usize]) -> Tensor +where + T: Copy, +{ + assert!(tensor.shape.len() <= shape.len(), "Shape is too small"); + if tensor.shape == shape { + return tensor.clone(); + } + if tensor.shape.is_empty() { + return Tensor { + data: vec![tensor.data[0]; shape.iter().product()], + shape: shape.to_vec(), + }; + } + let shape = broadcast_shapes(&[&tensor.shape, shape]); + let diff_ndim = shape.len() - tensor.shape.len(); + let expanded_shape: Vec = shape + .iter() + .enumerate() + .map(|(i, &v)| if i < diff_ndim { 1 } else { v }) + .collect(); + let dims_to_expand: HashSet = shape + .iter() + .enumerate() + .filter(|(i, &v)| *i < diff_ndim || v != tensor.shape[*i - diff_ndim]) + .map(|(i, _)| i) + .collect(); + let size = shape.iter().product(); + let mut data = vec![tensor.data[0]; size]; + for index in 0..size { + let nested_index = get_nested_indice(&shape, index) + .iter() + .enumerate() + .map(|(i, &v)| if dims_to_expand.contains(&i) { 0 } else { v }) + .collect::>(); + let flattened_index = get_flattened_indice(&expanded_shape, &nested_index); + data[index] = tensor.data[flattened_index]; + } + Tensor { data, shape } +} + +pub fn broadcast_tensors(tensors: &[&Tensor]) -> Vec> +where + T: Copy, +{ + let shape = broadcast_shapes( + &tensors + .iter() + .map(|t| t.shape.as_slice()) + .collect::>(), + ); + tensors + .iter() + .map(|t| broadcast_to(t, &shape)) + .collect::>() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_get_flattened_indice() { + assert_eq!(get_flattened_indice(&[2, 3], &[1, 2]), 5); + assert_eq!(get_flattened_indice(&[2, 3], &[0, 0]), 0); + assert_eq!(get_flattened_indice(&[2, 3], &[1, 0]), 3); + } + + #[test] + fn test_get_nested_indice() { + assert_eq!(get_nested_indice(&[2, 3], 5), vec![1, 2]); + assert_eq!(get_nested_indice(&[2, 3], 0), vec![0, 0]); + assert_eq!(get_nested_indice(&[2, 3], 3), vec![1, 0]); + } + + #[test] + fn test_broadcast_shapes() { + assert_eq!(broadcast_shapes(&[&[1, 2, 3], &[1, 2, 3]]), vec![1, 2, 3]); + assert_eq!(broadcast_shapes(&[&[2, 3], &[3, 2, 1]]), vec![3, 2, 3]); + } + + #[test] + fn test_broadcast_to() { + let tensor = Tensor::new(&[1, 2, 3], &[3]); + assert_eq!( + broadcast_to(&tensor, &[2, 3]), + Tensor::new(&[1, 2, 3, 1, 2, 3], &[2, 3]), + ); + } +} diff --git a/tests/test_xsor.py b/tests/test_xsor.py new file mode 100644 index 0000000..81de8dd --- /dev/null +++ b/tests/test_xsor.py @@ -0,0 +1,5 @@ +import xsor + + +def test_version() -> None: + assert xsor.__version__ == "0.0.1" diff --git a/xsor/__init__.py b/xsor/__init__.py new file mode 100644 index 0000000..e4d425c --- /dev/null +++ b/xsor/__init__.py @@ -0,0 +1,29 @@ +from typing import Sequence + +from xsor.tensor import Tensor + +__version__ = "0.0.1" +__all__ = ["Tensor", "tensor", "zeros", "ones", "eye"] + + +def tensor( + data: Sequence[float], + shape: Sequence[int] | None = None, +) -> Tensor: + return Tensor(data, shape) + + +def zeros(shape: Sequence[int]) -> Tensor: + return Tensor.zeros(shape) + + +def ones(shape: Sequence[int]) -> Tensor: + return Tensor.ones(shape) + + +def eye(size: int) -> Tensor: + return Tensor.eye(size) + + +def sum(tensor: Tensor) -> Tensor: + return tensor.sum() diff --git a/xsor/tensor.py b/xsor/tensor.py new file mode 100644 index 0000000..f559bff --- /dev/null +++ b/xsor/tensor.py @@ -0,0 +1,103 @@ +from __future__ import annotations + +from typing import Sequence, Tuple + +from .xsor import TensorF32 + + +class Tensor: + def __init__( + self, + data: Sequence[float], + shape: Sequence[int] | None = None, + ): + if shape is None: + shape = (len(data),) + self._tensor = TensorF32(data, shape) + + @classmethod + def _from_tensor(cls, tensor: TensorF32) -> Tensor: + output = cls((0,), ()) # type: ignore[arg-type] + output._tensor = tensor + return output + + @property + def data(self) -> Sequence[float]: + return self._tensor.data + + @property + def size(self) -> int: + return self._tensor.size + + @property + def shape(self) -> Tuple[int, ...]: + return tuple(self._tensor.shape) + + @property + def ndim(self) -> int: + return self._tensor.ndim + + @property + def value(self) -> float: + if self.size != 1: + raise ValueError("Tensor.value is only available for scalar tensors") + return self._tensor.value + + def __repr__(self) -> str: + return f"Tensor({self._tensor}, shape={self.shape})" + + def __neg__(self) -> Tensor: + return self._from_tensor(self._tensor.neg()) + + def __add__(self, other: int | float | Tensor) -> Tensor: + if isinstance(other, (int, float)): + return self._from_tensor(self._tensor.add_scalar(float(other))) + return self._from_tensor(self._tensor.add_tensor(other._tensor)) + + def __radd__(self, other: int | float | Tensor) -> Tensor: + return self + other + + def __sub__(self, other: int | float | Tensor) -> Tensor: + if isinstance(other, (int, float)): + return self._from_tensor(self._tensor.sub_scalar(float(other))) + return self._from_tensor(self._tensor.sub_tensor(other._tensor)) + + def __rsub__(self, other: int | float | Tensor) -> Tensor: + return -(self - other) + + def __mul__(self, other: int | float | Tensor) -> Tensor: + if isinstance(other, (int, float)): + return self._from_tensor(self._tensor.mul_scalar(float(other))) + return self._from_tensor(self._tensor.mul_tensor(other._tensor)) + + def __rmul__(self, other: int | float | Tensor) -> Tensor: + return self * other + + def __truediv__(self, other: int | float | Tensor) -> Tensor: + if isinstance(other, (int, float)): + return self._from_tensor(self._tensor.div_scalar(float(other))) + return self._from_tensor(self._tensor.div_tensor(other._tensor)) + + def __rtruediv__(self, other: int | float | Tensor) -> Tensor: + raise NotImplementedError("Tensor.__rtruediv__ is not implemented") + + def __matmul__(self, other: Tensor) -> Tensor: + return self._from_tensor(self._tensor.matmul(other._tensor)) + + def sum(self) -> Tensor: + return self._from_tensor(self._tensor.sum()) + + def __getitem__(self, index: Sequence[int]) -> Tensor: + return self._from_tensor(self._tensor.get(index)) + + @classmethod + def zeros(cls, shape: Sequence[int]) -> Tensor: + return cls._from_tensor(TensorF32.zeros(shape)) + + @classmethod + def ones(cls, shape: Sequence[int]) -> Tensor: + return cls._from_tensor(TensorF32.ones(shape)) + + @classmethod + def eye(cls, n: int) -> Tensor: + return cls._from_tensor(TensorF32.eye(n)) diff --git a/xsor/types.py b/xsor/types.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/xsor/types.py @@ -0,0 +1 @@ + diff --git a/xsor/xsor.pyi b/xsor/xsor.pyi new file mode 100644 index 0000000..1fcf45a --- /dev/null +++ b/xsor/xsor.pyi @@ -0,0 +1,35 @@ +from __future__ import annotations + +from typing import Sequence + +class TensorF32: + def __init__(self, data: Sequence[float], shape: Sequence[int]) -> None: ... + def neg(self) -> TensorF32: ... + def add_tensor(self, other: TensorF32) -> TensorF32: ... + def sub_tensor(self, other: TensorF32) -> TensorF32: ... + def mul_tensor(self, other: TensorF32) -> TensorF32: ... + def div_tensor(self, other: TensorF32) -> TensorF32: ... + def add_scalar(self, other: float) -> TensorF32: ... + def sub_scalar(self, other: float) -> TensorF32: ... + def mul_scalar(self, other: float) -> TensorF32: ... + def div_scalar(self, other: float) -> TensorF32: ... + def matmul(self, other: TensorF32) -> TensorF32: ... + def sum(self) -> TensorF32: ... + def reshape(self, shape: Sequence[int]) -> TensorF32: ... + def get(self, index: Sequence[int]) -> TensorF32: ... + @property + def data(self) -> Sequence[float]: ... + @property + def size(self) -> int: ... + @property + def shape(self) -> list[int]: ... + @property + def ndim(self) -> int: ... + @property + def value(self) -> float: ... + @staticmethod + def zeros(shape: Sequence[int]) -> TensorF32: ... + @staticmethod + def ones(shape: Sequence[int]) -> TensorF32: ... + @staticmethod + def eye(size: int) -> TensorF32: ...