pylibann 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. pylibann-0.1.0/.github/FUNDING.yml +3 -0
  2. pylibann-0.1.0/.github/copilot-instructions.md +48 -0
  3. pylibann-0.1.0/.github/workflows/blas-bench.yml +96 -0
  4. pylibann-0.1.0/.github/workflows/wheels.yml +128 -0
  5. pylibann-0.1.0/.gitignore +217 -0
  6. pylibann-0.1.0/.gitmodules +3 -0
  7. pylibann-0.1.0/CLAUDE.md +61 -0
  8. pylibann-0.1.0/CMakeLists.txt +271 -0
  9. pylibann-0.1.0/LICENSE +21 -0
  10. pylibann-0.1.0/PKG-INFO +398 -0
  11. pylibann-0.1.0/README.md +366 -0
  12. pylibann-0.1.0/ann/.gitignore +114 -0
  13. pylibann-0.1.0/ann/.gitmodules +3 -0
  14. pylibann-0.1.0/ann/CMakeLists.txt +261 -0
  15. pylibann-0.1.0/ann/CODE_OF_CONDUCT.md +128 -0
  16. pylibann-0.1.0/ann/CONTRIBUTORS.md +5 -0
  17. pylibann-0.1.0/ann/LICENSE +21 -0
  18. pylibann-0.1.0/ann/Makefile +135 -0
  19. pylibann-0.1.0/ann/README.md +800 -0
  20. pylibann-0.1.0/ann/ann.c +3997 -0
  21. pylibann-0.1.0/ann/ann.h +1067 -0
  22. pylibann-0.1.0/ann/ann_config.h +43 -0
  23. pylibann-0.1.0/ann/ann_gpu_backend.h +138 -0
  24. pylibann-0.1.0/ann/ann_hypertune.c +1599 -0
  25. pylibann-0.1.0/ann/ann_hypertune.h +621 -0
  26. pylibann-0.1.0/ann/json.c +486 -0
  27. pylibann-0.1.0/ann/json.h +156 -0
  28. pylibann-0.1.0/ann/shaders.cu +333 -0
  29. pylibann-0.1.0/ann/shaders.metal +332 -0
  30. pylibann-0.1.0/ann/shaders_cuda.h +105 -0
  31. pylibann-0.1.0/ann/tensor.c +1531 -0
  32. pylibann-0.1.0/ann/tensor.h +442 -0
  33. pylibann-0.1.0/ann/tensor_cuda.cu +924 -0
  34. pylibann-0.1.0/ann/tensor_metal.m +1657 -0
  35. pylibann-0.1.0/benchmarks/bench_blas.py +78 -0
  36. pylibann-0.1.0/benchmarks/summarize.py +17 -0
  37. pylibann-0.1.0/build_lib.py +131 -0
  38. pylibann-0.1.0/examples/mnist_fashion.py +217 -0
  39. pylibann-0.1.0/pyproject.toml +87 -0
  40. pylibann-0.1.0/src/native/pyann_info.c +19 -0
  41. pylibann-0.1.0/src/pyann/__init__.py +36 -0
  42. pylibann-0.1.0/src/pyann/_bindings/__init__.py +5 -0
  43. pylibann-0.1.0/src/pyann/_bindings/ann_cdef.py +168 -0
  44. pylibann-0.1.0/src/pyann/_bindings/ffi.py +247 -0
  45. pylibann-0.1.0/src/pyann/_bindings/tensor_cdef.py +68 -0
  46. pylibann-0.1.0/src/pyann/activations.py +67 -0
  47. pylibann-0.1.0/src/pyann/callbacks.py +117 -0
  48. pylibann-0.1.0/src/pyann/core/__init__.py +7 -0
  49. pylibann-0.1.0/src/pyann/core/layers.py +114 -0
  50. pylibann-0.1.0/src/pyann/core/network.py +620 -0
  51. pylibann-0.1.0/src/pyann/core/tensor.py +244 -0
  52. pylibann-0.1.0/src/pyann/exceptions.py +55 -0
  53. pylibann-0.1.0/src/pyann/hypertune/__init__.py +6 -0
  54. pylibann-0.1.0/src/pyann/hypertune/search.py +772 -0
  55. pylibann-0.1.0/src/pyann/hypertune/space.py +156 -0
  56. pylibann-0.1.0/src/pyann/losses.py +59 -0
  57. pylibann-0.1.0/src/pyann/optimizers.py +132 -0
  58. pylibann-0.1.0/src/pyann/utils/__init__.py +6 -0
  59. pylibann-0.1.0/src/pyann/utils/compat.py +117 -0
  60. pylibann-0.1.0/src/pyann/utils/data.py +223 -0
  61. pylibann-0.1.0/tests/__init__.py +1 -0
  62. pylibann-0.1.0/tests/test_activations.py +58 -0
  63. pylibann-0.1.0/tests/test_backend.py +30 -0
  64. pylibann-0.1.0/tests/test_callbacks.py +96 -0
  65. pylibann-0.1.0/tests/test_compat.py +91 -0
  66. pylibann-0.1.0/tests/test_data.py +105 -0
  67. pylibann-0.1.0/tests/test_gpu.py +79 -0
  68. pylibann-0.1.0/tests/test_hypertune.py +146 -0
  69. pylibann-0.1.0/tests/test_layers.py +95 -0
  70. pylibann-0.1.0/tests/test_losses.py +50 -0
  71. pylibann-0.1.0/tests/test_optimizers.py +99 -0
@@ -0,0 +1,3 @@
1
+ # These are supported funding model platforms
2
+
3
+ github: mseminatore
@@ -0,0 +1,48 @@
1
+ # PyANN - Copilot Instructions
2
+
3
+ ## Architecture
4
+
5
+ PyANN is a Python wrapper around the [libann](https://github.com/mseminatore/ann) C neural network library, exposing a Keras-like API. It has two distinct layers:
6
+
7
+ - **C library** (`ann/` submodule) — core neural network engine built as a shared library (`.dll`/`.so`/`.dylib`). Bindings are declared in `src/pyann/_bindings/` using CFFI. The C library has its own guidelines in `ann/.github/copilot-instructions.md`.
8
+ - **Python package** (`src/pyann/`) — high-level API wrapping the C library. Key modules:
9
+ - `core/network.py` — `Sequential` model (the main entry point)
10
+ - `core/layers.py` — `Dense`, `Input` layers
11
+ - `core/tensor.py` — Tensor wrapper around C tensors
12
+ - `callbacks.py` — LR schedulers (`StepDecay`, `ExponentialDecay`, `CosineAnnealing`) and `EarlyStopping`
13
+ - `hypertune/` — hyperparameter search (`GridSearch`, `RandomSearch`, `BayesianSearch`, `TPESearch`)
14
+ - `utils/` — data loading, normalization, one-hot encoding, NumPy compatibility (`compat.py`)
15
+
16
+ NumPy is optional. The `utils/compat.py` module provides `has_numpy`, `to_array`, and `get_shape` helpers that work with plain Python lists as a fallback.
17
+
18
+ ## Build & Test
19
+
20
+ ```bash
21
+ # Build the C shared library (requires CMake)
22
+ python build_lib.py # Release build
23
+ python build_lib.py --cblas # With CBLAS acceleration
24
+ python build_lib.py --clean # Clean rebuild
25
+
26
+ # Install Python package in dev mode
27
+ pip install -e ".[dev]"
28
+
29
+ # Run all tests
30
+ pytest
31
+
32
+ # Run a single test file
33
+ pytest tests/test_layers.py
34
+
35
+ # Run a single test
36
+ pytest tests/test_layers.py::TestDense::test_basic_dense -v
37
+ ```
38
+
39
+ The built shared library is copied to `src/pyann/lib/`. You can also set `PYANN_LIB_PATH` to point to a custom library location.
40
+
41
+ ## Conventions
42
+
43
+ - **src layout**: Python package lives under `src/pyann/`, not at the project root.
44
+ - **Enum + string API**: Activations, losses, and optimizers accept both enum values (`Activation.RELU`) and string names (`"relu"`). Parsing helpers (`parse_optimizer`, `parse_loss`) handle conversion.
45
+ - **Error handling**: C library errors are translated via `raise_for_error_code()` in `exceptions.py`. Python-side validation uses standard `ValueError`/`TypeError`.
46
+ - **Test style**: Tests use pytest with class-based grouping (`class TestDense:`). Each test class covers one component.
47
+ - **Type hints**: All public APIs use type hints. `TYPE_CHECKING` guard is used for numpy imports.
48
+ - **C function prefixes**: `ann_*` for network functions, `tensor_*` for tensor operations (relevant when modifying `_bindings/` CFFI definitions).
@@ -0,0 +1,96 @@
1
+ name: BLAS benchmark
2
+
3
+ # Compares libann built with no BLAS, the project's CBLAS, OpenBLAS (and Accelerate on macOS)
4
+ # Manual only: a paths filter on pull_request matches the whole PR diff, so it
5
+ # would run on every push to a PR that ever touched benchmarks/
6
+ on:
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ bench:
11
+ name: ${{ matrix.name }}
12
+ runs-on: ${{ matrix.os }}
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ include:
17
+ - { name: macos-arm64, os: macos-14 }
18
+ - { name: linux-x86_64, os: ubuntu-latest }
19
+ - { name: linux-aarch64, os: ubuntu-24.04-arm }
20
+ env:
21
+ OUT: ${{ github.workspace }}/bench-libs
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+ with:
25
+ submodules: recursive
26
+
27
+ - uses: actions/setup-python@v5
28
+ with:
29
+ python-version: "3.12"
30
+
31
+ - name: Install OpenBLAS (Linux)
32
+ if: runner.os == 'Linux'
33
+ run: |
34
+ sudo apt-get update
35
+ sudo apt-get install -y libopenblas-dev
36
+ # libann looks for include/cblas.h and lib/libopenblas under one root.
37
+ # libopenblas-dev is a meta-package, so locate the real files with find.
38
+ hdr=$(find /usr/include -name cblas.h -path '*openblas*' | head -1)
39
+ lib=$(find /usr/lib -name 'libopenblas.so' | head -1)
40
+ echo "header: $hdr library: $lib"
41
+ sudo mkdir -p /opt/OpenBLAS/lib
42
+ sudo ln -sfn "$(dirname "$hdr")" /opt/OpenBLAS/include
43
+ sudo ln -sf "$lib" /opt/OpenBLAS/lib/libopenblas.so
44
+ ls -lL /opt/OpenBLAS/include/cblas.h /opt/OpenBLAS/lib/libopenblas.so
45
+
46
+ - name: Install OpenBLAS (macOS)
47
+ if: runner.os == 'macOS'
48
+ run: |
49
+ brew install openblas
50
+ sudo mkdir -p /opt
51
+ sudo ln -sfn "$(brew --prefix openblas)" /opt/OpenBLAS
52
+
53
+ - name: Build and install CBLAS from main
54
+ run: |
55
+ git clone --depth 1 https://github.com/mseminatore/cblas /tmp/cblas
56
+ echo "CBLAS commit: $(git -C /tmp/cblas rev-parse HEAD)"
57
+ # PIC is required to link the static library into libann's shared library
58
+ cmake -S /tmp/cblas -B /tmp/cblas/build -DCMAKE_BUILD_TYPE=Release \
59
+ -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_INSTALL_PREFIX=/opt/cblas
60
+ cmake --build /tmp/cblas/build --target cblas --config Release
61
+ sudo cmake --install /tmp/cblas/build --config Release
62
+
63
+ - name: Build libann variants
64
+ run: |
65
+ build() { # name, cmake flags...
66
+ name=$1; shift
67
+ cmake -S . -B build-$name -DCMAKE_BUILD_TYPE=Release "$@"
68
+ cmake --build build-$name --config Release
69
+ mkdir -p $OUT/$name
70
+ cp -L src/pyann/lib/libann.* $OUT/$name/
71
+ }
72
+ build none
73
+ build cblas -DUSE_CBLAS=ON
74
+ build openblas -DUSE_BLAS=ON
75
+ if [ "$RUNNER_OS" = macOS ]; then build accelerate -DUSE_ACCELERATE=ON; fi
76
+
77
+ - name: Run benchmarks
78
+ run: |
79
+ python -m pip install numpy cffi
80
+ # A crash must be visible: no pipes, per-variant exit codes, faulthandler
81
+ for dir in $OUT/*/; do
82
+ variant=$(basename $dir)
83
+ echo "::group::$variant"
84
+ PYANN_LIB_PATH=$dir PYTHONPATH=src python -X faulthandler benchmarks/bench_blas.py \
85
+ --variant $variant --out results.jsonl || echo "::error::$variant exited with status $?"
86
+ echo "::endgroup::"
87
+ done
88
+ {
89
+ echo "## ${{ matrix.name }} ($(python -c 'import os; print(os.cpu_count())') CPUs)"
90
+ python benchmarks/summarize.py < results.jsonl
91
+ } | tee -a "$GITHUB_STEP_SUMMARY"
92
+
93
+ - uses: actions/upload-artifact@v4
94
+ with:
95
+ name: bench-${{ matrix.name }}
96
+ path: results.jsonl
@@ -0,0 +1,128 @@
1
+ name: Build wheels
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ tags: ["v*"]
7
+ pull_request:
8
+ workflow_dispatch:
9
+
10
+ jobs:
11
+ sdist:
12
+ name: Source distribution
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ with:
17
+ submodules: recursive
18
+
19
+ - uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.12"
22
+
23
+ - name: Build sdist
24
+ run: |
25
+ python -m pip install build
26
+ python -m build --sdist
27
+
28
+ - name: Check package metadata
29
+ run: |
30
+ python -m pip install twine
31
+ twine check dist/*
32
+
33
+ # Prove the sdist is self-contained: build and test it away from the checkout
34
+ - name: Install and test sdist
35
+ env:
36
+ PYANN_EXPECT_BLAS: openblas
37
+ run: |
38
+ python -m pip install pytest numpy dist/*.tar.gz
39
+ mkdir /tmp/sdist-test && cp -r tests /tmp/sdist-test/
40
+ cd /tmp/sdist-test && python -m pytest tests
41
+
42
+ - uses: actions/upload-artifact@v4
43
+ with:
44
+ name: sdist
45
+ path: dist/*.tar.gz
46
+
47
+ wheels:
48
+ name: Wheel (${{ matrix.name }})
49
+ runs-on: ${{ matrix.os }}
50
+ strategy:
51
+ fail-fast: false
52
+ matrix:
53
+ include:
54
+ - { name: macos-arm64, os: macos-14, archs: arm64 }
55
+ - { name: linux-x86_64, os: ubuntu-latest, archs: x86_64 }
56
+ - { name: linux-aarch64, os: ubuntu-24.04-arm, archs: aarch64 }
57
+ - { name: windows-x86_64, os: windows-latest, archs: AMD64 }
58
+ steps:
59
+ - uses: actions/checkout@v4
60
+ with:
61
+ submodules: recursive
62
+
63
+ - uses: pypa/cibuildwheel@v2.23.3
64
+ env:
65
+ CIBW_ARCHS: ${{ matrix.archs }}
66
+
67
+ - uses: actions/upload-artifact@v4
68
+ with:
69
+ name: wheel-${{ matrix.name }}
70
+ path: wheelhouse/*.whl
71
+
72
+ # Releases are tag-driven: push vX.Y.Z to publish to PyPI, or a pre-release
73
+ # tag such as vX.Y.ZrcN to publish to TestPyPI.
74
+ release-meta:
75
+ name: Release metadata
76
+ if: startsWith(github.ref, 'refs/tags/v')
77
+ runs-on: ubuntu-latest
78
+ outputs:
79
+ prerelease: ${{ steps.meta.outputs.prerelease }}
80
+ steps:
81
+ - uses: actions/checkout@v4
82
+
83
+ - uses: actions/setup-python@v5
84
+ with:
85
+ python-version: "3.12"
86
+
87
+ - name: Check tag against package version
88
+ id: meta
89
+ run: |
90
+ python -m pip install packaging
91
+ python - <<'PY' >> "$GITHUB_OUTPUT"
92
+ import os, tomllib
93
+ from packaging.version import Version
94
+
95
+ tag = os.environ["GITHUB_REF_NAME"].removeprefix("v")
96
+ with open("pyproject.toml", "rb") as f:
97
+ version = tomllib.load(f)["project"]["version"]
98
+ if Version(tag) != Version(version):
99
+ raise SystemExit(f"::error::tag v{tag} does not match pyproject.toml version {version}")
100
+ print(f"prerelease={str(Version(version).is_prerelease).lower()}")
101
+ PY
102
+
103
+ publish:
104
+ name: Publish
105
+ needs: [sdist, wheels, release-meta]
106
+ runs-on: ubuntu-latest
107
+ # Each index has its own trusted publisher bound to one of these environments
108
+ environment: ${{ needs.release-meta.outputs.prerelease == 'true' && 'testpypi' || 'pypi' }}
109
+ permissions:
110
+ id-token: write # trusted publishing: no API token needed
111
+ steps:
112
+ - uses: actions/download-artifact@v4
113
+ with:
114
+ path: dist
115
+ merge-multiple: true
116
+
117
+ - name: List distributions
118
+ run: ls -l dist
119
+
120
+ - name: Publish to TestPyPI
121
+ if: needs.release-meta.outputs.prerelease == 'true'
122
+ uses: pypa/gh-action-pypi-publish@release/v1
123
+ with:
124
+ repository-url: https://test.pypi.org/legacy/
125
+
126
+ - name: Publish to PyPI
127
+ if: needs.release-meta.outputs.prerelease != 'true'
128
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,217 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # data files
7
+ fashion-mnist_train.csv
8
+ fashion-mnist_test.csv
9
+ fashion_mnist.nna
10
+
11
+ # C extensions
12
+ *.so
13
+ *.dll
14
+ *.dylib
15
+
16
+ # Built shared libraries
17
+ src/pyann/lib/
18
+
19
+ # Distribution / packaging
20
+ .Python
21
+ build/
22
+ develop-eggs/
23
+ dist/
24
+ downloads/
25
+ eggs/
26
+ .eggs/
27
+ lib/
28
+ lib64/
29
+ parts/
30
+ sdist/
31
+ var/
32
+ wheels/
33
+ share/python-wheels/
34
+ *.egg-info/
35
+ .installed.cfg
36
+ *.egg
37
+ MANIFEST
38
+
39
+ # PyInstaller
40
+ # Usually these files are written by a python script from a template
41
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
42
+ *.manifest
43
+ *.spec
44
+
45
+ # Installer logs
46
+ pip-log.txt
47
+ pip-delete-this-directory.txt
48
+
49
+ # Unit test / coverage reports
50
+ htmlcov/
51
+ .tox/
52
+ .nox/
53
+ .coverage
54
+ .coverage.*
55
+ .cache
56
+ nosetests.xml
57
+ coverage.xml
58
+ *.cover
59
+ *.py.cover
60
+ .hypothesis/
61
+ .pytest_cache/
62
+ cover/
63
+
64
+ # Translations
65
+ *.mo
66
+ *.pot
67
+
68
+ # Django stuff:
69
+ *.log
70
+ local_settings.py
71
+ db.sqlite3
72
+ db.sqlite3-journal
73
+
74
+ # Flask stuff:
75
+ instance/
76
+ .webassets-cache
77
+
78
+ # Scrapy stuff:
79
+ .scrapy
80
+
81
+ # Sphinx documentation
82
+ docs/_build/
83
+
84
+ # PyBuilder
85
+ .pybuilder/
86
+ target/
87
+
88
+ # Jupyter Notebook
89
+ .ipynb_checkpoints
90
+
91
+ # IPython
92
+ profile_default/
93
+ ipython_config.py
94
+
95
+ # pyenv
96
+ # For a library or package, you might want to ignore these files since the code is
97
+ # intended to run in multiple environments; otherwise, check them in:
98
+ # .python-version
99
+
100
+ # pipenv
101
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
102
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
103
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
104
+ # install all needed dependencies.
105
+ #Pipfile.lock
106
+
107
+ # UV
108
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
109
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
110
+ # commonly ignored for libraries.
111
+ #uv.lock
112
+
113
+ # poetry
114
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
115
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
116
+ # commonly ignored for libraries.
117
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
118
+ #poetry.lock
119
+ #poetry.toml
120
+
121
+ # pdm
122
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
123
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
124
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
125
+ #pdm.lock
126
+ #pdm.toml
127
+ .pdm-python
128
+ .pdm-build/
129
+
130
+ # pixi
131
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
132
+ #pixi.lock
133
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
134
+ # in the .venv directory. It is recommended not to include this directory in version control.
135
+ .pixi
136
+
137
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
138
+ __pypackages__/
139
+
140
+ # Celery stuff
141
+ celerybeat-schedule
142
+ celerybeat.pid
143
+
144
+ # SageMath parsed files
145
+ *.sage.py
146
+
147
+ # Environments
148
+ .env
149
+ .envrc
150
+ .venv
151
+ env/
152
+ venv/
153
+ ENV/
154
+ env.bak/
155
+ venv.bak/
156
+
157
+ # Spyder project settings
158
+ .spyderproject
159
+ .spyproject
160
+
161
+ # Rope project settings
162
+ .ropeproject
163
+
164
+ # mkdocs documentation
165
+ /site
166
+
167
+ # mypy
168
+ .mypy_cache/
169
+ .dmypy.json
170
+ dmypy.json
171
+
172
+ # Pyre type checker
173
+ .pyre/
174
+
175
+ # pytype static type analyzer
176
+ .pytype/
177
+
178
+ # Cython debug symbols
179
+ cython_debug/
180
+
181
+ # PyCharm
182
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
183
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
184
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
185
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
186
+ #.idea/
187
+
188
+ # Abstra
189
+ # Abstra is an AI-powered process automation framework.
190
+ # Ignore directories containing user credentials, local state, and settings.
191
+ # Learn more at https://abstra.io/docs
192
+ .abstra/
193
+
194
+ # Visual Studio Code
195
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
196
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
197
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
198
+ # you could uncomment the following to ignore the entire vscode folder
199
+ # .vscode/
200
+
201
+ # Ruff stuff:
202
+ .ruff_cache/
203
+
204
+ # PyPI configuration file
205
+ .pypirc
206
+
207
+ # Cursor
208
+ # Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
209
+ # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
210
+ # refer to https://docs.cursor.com/context/ignore-files
211
+ .cursorignore
212
+ .cursorindexingignore
213
+
214
+ # Marimo
215
+ marimo/_static/
216
+ marimo/_lsp/
217
+ __marimo__/
@@ -0,0 +1,3 @@
1
+ [submodule "ann"]
2
+ path = ann
3
+ url = https://github.com/mseminatore/ann.git
@@ -0,0 +1,61 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ PyANN is a Python wrapper around the [libann](https://github.com/mseminatore/ann) C neural network library, exposing a Keras-like API. It has two layers:
8
+
9
+ - **C library** (`ann/` git submodule) — core neural network engine built as a shared library via CMake. CFFI bindings are declared in `src/pyann/_bindings/`.
10
+ - **Python package** (`src/pyann/`, src layout) — high-level API wrapping the C library. `Sequential` is the main entry point.
11
+
12
+ NumPy is optional — `utils/compat.py` provides fallback helpers that work with plain Python lists.
13
+
14
+ ## Build & Test
15
+
16
+ ```bash
17
+ # Build the native library and install in dev mode (scikit-build-core runs CMake)
18
+ pip install -e ".[dev]"
19
+
20
+ # Pass CMake options through CMAKE_ARGS, e.g. CUDA or a plain CPU build
21
+ CMAKE_ARGS="-DUSE_CUDA=ON" pip install -e .
22
+ CMAKE_ARGS="-DUSE_METAL=OFF -DUSE_ACCELERATE=OFF" pip install -e .
23
+
24
+ # Or build only the shared library into src/pyann/lib/ (no install)
25
+ python build_lib.py # defaults: no BLAS (see flags below)
26
+ python build_lib.py --accelerate --metal # macOS
27
+ python build_lib.py --scipy-openblas # Linux/Windows (pip install scipy-openblas32 first)
28
+ python build_lib.py --cblas | --blas | --cuda | --clean
29
+
30
+ # Run tests
31
+ pytest # all tests
32
+ pytest tests/test_layers.py # single file
33
+ pytest tests/test_layers.py::TestDense::test_basic_dense -v # single test
34
+
35
+ # Build a wheel / sdist
36
+ pip wheel . --no-deps -w dist
37
+ python -m build --sdist
38
+ ```
39
+
40
+ The `ann/` submodule must be checked out (`git submodule update --init`). Wheels default to Accelerate+Metal on macOS and the bundled `scipy-openblas32` OpenBLAS on Linux/Windows (see `[[tool.scikit-build.overrides]]` in `pyproject.toml`). CI (`.github/workflows/wheels.yml`) builds and tests wheels with cibuildwheel; `tests/test_backend.py` fails a wheel that lost its BLAS. `.github/workflows/blas-bench.yml` (manual) compares BLAS libraries using `benchmarks/`.
41
+
42
+ The built shared library goes to `src/pyann/lib/` for dev builds. Override with `PYANN_LIB_PATH` env var.
43
+
44
+ ## Releasing
45
+
46
+ The PyPI distribution is `pylibann` (the name `pyann` is taken by an unrelated project); the import name is still `pyann`. Releases are tag-driven via `.github/workflows/wheels.yml`:
47
+
48
+ 1. Set `version` in `pyproject.toml` (PEP 440) and merge to `main`.
49
+ 2. Push a tag `vX.Y.Z`. The tag must equal the version or the `release-meta` job fails.
50
+ 3. A pre-release tag (`vX.Y.ZrcN`) publishes to TestPyPI via the `testpypi` environment; a final tag publishes to PyPI via the `pypi` environment. Both use trusted publishing (no tokens); each index needs a publisher pointing at this repo, `wheels.yml`, and the matching environment name.
51
+
52
+ Try a release candidate on TestPyPI first and install it in a clean venv before tagging the real release.
53
+
54
+ ## Conventions
55
+
56
+ - **Enum + string API**: Activations, losses, and optimizers accept both enum values (`Activation.RELU`) and strings (`"relu"`). Parsing helpers (`parse_optimizer`, `parse_loss`, `parse_activation`) handle conversion.
57
+ - **Error handling**: C errors are translated via `raise_for_error_code()` in `exceptions.py`. Python-side validation uses `ValueError`/`TypeError`.
58
+ - **Test style**: pytest with class-based grouping (`class TestDense:`). Each class covers one component.
59
+ - **Type hints**: All public APIs use type hints. `TYPE_CHECKING` guard for numpy imports.
60
+ - **C binding prefixes**: `ann_*` for network functions, `tensor_*` for tensor operations (in `_bindings/` CFFI definitions).
61
+ - **Python enum values** (`IntEnum`) must match C enum ordinals exactly.