Browse Source

initial commit

main
Yasuhiro Yamaguchi 2 years ago
commit
71ccbac47c
Signed by: altescy GPG Key ID: 45C9FA6B031084CC
  1. 18
      .editorconfig
  2. 153
      .gitignore
  3. 13
      Cargo.toml
  4. 44
      Makefile
  5. 1
      README.md
  6. 89
      benchmark.py
  7. 658
      poetry.lock
  8. 67
      pyproject.toml
  9. 36
      setup.cfg
  10. 221
      src/lib.rs
  11. 139
      src/simd.rs
  12. 179
      src/tensor.rs
  13. 137
      src/util.rs
  14. 5
      tests/test_xsor.py
  15. 29
      xsor/__init__.py
  16. 103
      xsor/tensor.py
  17. 1
      xsor/types.py
  18. 35
      xsor/xsor.pyi

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

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

13
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"] }

44
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/

1
README.md

@ -0,0 +1 @@
# xsor

89
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()

658
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"

67
pyproject.toml

@ -0,0 +1,67 @@
[tool.poetry]
name = "xsor"
version = "0.0.1"
description = ""
authors = ["altescy <altescy@fastmail.com>"]
[[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"

36
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

221
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<f32>,
}
#[pymethods]
impl PyTensorF32 {
#[new]
fn __new__(data: Vec<f32>, shape: Vec<usize>) -> Self {
assert_eq!(data.len(), shape.iter().product());
Self {
tensor: Tensor { data, shape },
}
}
#[staticmethod]
fn zeros(shape: Vec<usize>) -> Self {
Self {
tensor: Tensor::zeros(&shape),
}
}
#[staticmethod]
fn ones(shape: Vec<usize>) -> 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<Tensor<f32>>;
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<Tensor<f32>>;
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<Tensor<f32>>;
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<Tensor<f32>>;
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<usize>) -> 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<usize> {
self.tensor.shape.clone()
}
#[getter]
fn data(&self) -> Vec<f32> {
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<usize>) -> Self {
Self {
tensor: self.tensor.reshape(&shape),
}
}
}
#[pymodule]
fn xsor(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_class::<PyTensorF32>()?;
Ok(())
}

139
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::<f32, 4>::splat(0.0);
for i in 0..chunks {
let a = Simd::<f32, 4>::from_slice(&a[i * chunk_size..]);
sum += a;
}
let mut result = sum.as_array().iter().sum::<f32>();
for i in (chunks * chunk_size)..a.len() {
result += a[i];
}
result
}
pub fn add_f32_vector_simd(a: &[f32], b: &[f32]) -> Vec<f32> {
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::<f32, 4>::from_slice(&a[i * chunk_size..]);
let b = Simd::<f32, 4>::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<f32> {
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::<f32, 4>::from_slice(&a[i * chunk_size..]);
let b = Simd::<f32, 4>::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<f32> {
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::<f32, 4>::from_slice(&a[i * chunk_size..]);
let b = Simd::<f32, 4>::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<f32> {
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::<f32, 4>::from_slice(&a[i * chunk_size..]);
let b = Simd::<f32, 4>::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<f32> {
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::<f32, 4>::splat(0.0);
for chunk in 0..chunks {
let a = Simd::<f32, 4>::from_slice(&arow[chunk * chunk_size..]);
let b = Simd::<f32, 4>::from_slice(&bcol[chunk * chunk_size..]);
sum += a * b;
}
let mut res = sum.as_array().iter().sum::<f32>();
for chunk in (chunks * chunk_size)..k {
res += arow[chunk] * bcol[chunk];
}
result[i * n + j] = res;
}
}
result
}

179
src/tensor.rs

@ -0,0 +1,179 @@
use num::Num;
use std::fmt::{self, Display, Formatter};
#[derive(Debug, PartialEq)]
pub struct Tensor<T> {
pub data: Vec<T>,
pub shape: Vec<usize>,
}
impl<T> Tensor<T>
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<T> Clone for Tensor<T>
where
T: Copy,
{
fn clone(&self) -> Self {
Self {
data: self.data.clone(),
shape: self.shape.clone(),
}
}
}
impl<T> Tensor<T>
where
T: Copy,
{
pub fn reshape(&self, shape: &[usize]) -> Self {
assert_eq!(self.data.len(), shape.iter().product());
Self::new(&self.data, shape)
}
}
impl<T> Display for Tensor<T>
where
T: Display,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", format_tensor(0, &self.shape, &self.data))
}
}
impl<T> Tensor<T>
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<T>(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::<Vec<_>>();
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::<Vec<_>>()
.join(&inner_delimiter),
outer_delimiter,
)
}

137
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::<usize>()
})
}
pub fn get_nested_indice(shape: &[usize], index: usize) -> Vec<usize> {
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<usize> {
let max_ndim = shapes.iter().map(|s| s.len()).max().unwrap();
let shapes: Vec<Vec<usize>> = 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<usize> = 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<T>(tensor: &Tensor<T>, shape: &[usize]) -> Tensor<T>
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<usize> = shape
.iter()
.enumerate()
.map(|(i, &v)| if i < diff_ndim { 1 } else { v })
.collect();
let dims_to_expand: HashSet<usize> = 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::<Vec<usize>>();
let flattened_index = get_flattened_indice(&expanded_shape, &nested_index);
data[index] = tensor.data[flattened_index];
}
Tensor { data, shape }
}
pub fn broadcast_tensors<T>(tensors: &[&Tensor<T>]) -> Vec<Tensor<T>>
where
T: Copy,
{
let shape = broadcast_shapes(
&tensors
.iter()
.map(|t| t.shape.as_slice())
.collect::<Vec<_>>(),
);
tensors
.iter()
.map(|t| broadcast_to(t, &shape))
.collect::<Vec<_>>()
}
#[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]),
);
}
}

5
tests/test_xsor.py

@ -0,0 +1,5 @@
import xsor
def test_version() -> None:
assert xsor.__version__ == "0.0.1"

29
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()

103
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))

1
xsor/types.py

@ -0,0 +1 @@

35
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: ...
Loading…
Cancel
Save