jaxknn 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.
- jaxknn-0.1.0/.github/workflows/build_docs.yml +86 -0
- jaxknn-0.1.0/.github/workflows/cd.yml +53 -0
- jaxknn-0.1.0/.github/workflows/ci.yml +145 -0
- jaxknn-0.1.0/.gitignore +14 -0
- jaxknn-0.1.0/.pre-commit-config.yaml +42 -0
- jaxknn-0.1.0/.python-version +1 -0
- jaxknn-0.1.0/LICENSE +22 -0
- jaxknn-0.1.0/PKG-INFO +97 -0
- jaxknn-0.1.0/README.md +66 -0
- jaxknn-0.1.0/conftest.py +19 -0
- jaxknn-0.1.0/docs/_overrides/partials/source.html +10 -0
- jaxknn-0.1.0/docs/_static/custom_css.css +167 -0
- jaxknn-0.1.0/docs/api/jaxknn.md +17 -0
- jaxknn-0.1.0/docs/api/tree.md +17 -0
- jaxknn-0.1.0/docs/how-it-works.md +28 -0
- jaxknn-0.1.0/docs/how-to/find-the-nearest-point-with-another-label.md +23 -0
- jaxknn-0.1.0/docs/how-to/tune-for-large-k-or-high-dimension.md +19 -0
- jaxknn-0.1.0/docs/how-to/use-under-jit-vmap-grad.md +49 -0
- jaxknn-0.1.0/docs/index.md +37 -0
- jaxknn-0.1.0/docs/when-to-use-scipy-instead.md +19 -0
- jaxknn-0.1.0/mkdocs.yml +82 -0
- jaxknn-0.1.0/pyproject.toml +159 -0
- jaxknn-0.1.0/src/jaxknn/__init__.py +21 -0
- jaxknn-0.1.0/src/jaxknn/_api.py +249 -0
- jaxknn-0.1.0/src/jaxknn/_version.py +24 -0
- jaxknn-0.1.0/src/jaxknn/tree/__init__.py +20 -0
- jaxknn-0.1.0/src/jaxknn/tree/_brute.py +48 -0
- jaxknn-0.1.0/src/jaxknn/tree/_build.py +116 -0
- jaxknn-0.1.0/src/jaxknn/tree/_layout.py +36 -0
- jaxknn-0.1.0/src/jaxknn/tree/_query.py +446 -0
- jaxknn-0.1.0/src/jaxknn/tree/_select.py +140 -0
- jaxknn-0.1.0/tests/_compat.py +8 -0
- jaxknn-0.1.0/tests/benchmarks/test_tree.py +69 -0
- jaxknn-0.1.0/tests/test_api.py +397 -0
- jaxknn-0.1.0/tests/test_tree.py +569 -0
- jaxknn-0.1.0/tests/test_tree_properties.py +93 -0
- jaxknn-0.1.0/uv.lock +1364 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
name: Build docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
pull_request:
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
# Each push to main gets its own group, so merges never cancel each other
|
|
12
|
+
# and main keeps a complete CI record. All other events keep the per-ref
|
|
13
|
+
# group and still supersede older runs.
|
|
14
|
+
group: >-
|
|
15
|
+
${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
|
|
16
|
+
cancel-in-progress: true
|
|
17
|
+
|
|
18
|
+
permissions:
|
|
19
|
+
contents: read
|
|
20
|
+
|
|
21
|
+
env:
|
|
22
|
+
FORCE_COLOR: 3
|
|
23
|
+
|
|
24
|
+
jobs:
|
|
25
|
+
build:
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
29
|
+
with:
|
|
30
|
+
fetch-depth: 0
|
|
31
|
+
|
|
32
|
+
- name: Install uv
|
|
33
|
+
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
34
|
+
with:
|
|
35
|
+
python-version: "3.14"
|
|
36
|
+
|
|
37
|
+
- name: Install dependencies
|
|
38
|
+
run: uv sync --no-default-groups --group docs --locked
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
- name: Build docs
|
|
42
|
+
run: uv run --frozen zensical build --clean
|
|
43
|
+
|
|
44
|
+
upload:
|
|
45
|
+
needs: build
|
|
46
|
+
if: github.ref == 'refs/heads/main'
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
permissions:
|
|
49
|
+
contents: read
|
|
50
|
+
actions: write
|
|
51
|
+
steps:
|
|
52
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
53
|
+
with:
|
|
54
|
+
fetch-depth: 0
|
|
55
|
+
|
|
56
|
+
- name: Install uv
|
|
57
|
+
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
58
|
+
with:
|
|
59
|
+
python-version: "3.14"
|
|
60
|
+
|
|
61
|
+
- name: Install dependencies
|
|
62
|
+
run: uv sync --no-default-groups --group docs --locked
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
- name: Build docs
|
|
66
|
+
run: uv run --frozen zensical build --clean
|
|
67
|
+
|
|
68
|
+
- name: Upload pages artifact
|
|
69
|
+
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
|
|
70
|
+
with:
|
|
71
|
+
path: site
|
|
72
|
+
|
|
73
|
+
deploy:
|
|
74
|
+
needs: upload
|
|
75
|
+
if: github.ref == 'refs/heads/main'
|
|
76
|
+
runs-on: ubuntu-latest
|
|
77
|
+
permissions:
|
|
78
|
+
pages: write
|
|
79
|
+
id-token: write
|
|
80
|
+
environment:
|
|
81
|
+
name: github-pages
|
|
82
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
83
|
+
steps:
|
|
84
|
+
- name: Deploy to GitHub Pages
|
|
85
|
+
id: deployment
|
|
86
|
+
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
name: CD
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
push:
|
|
6
|
+
tags:
|
|
7
|
+
- "v*"
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: false
|
|
12
|
+
|
|
13
|
+
env:
|
|
14
|
+
FORCE_COLOR: 3
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
build:
|
|
18
|
+
name: Build & inspect package
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
permissions:
|
|
21
|
+
contents: read
|
|
22
|
+
id-token: write
|
|
23
|
+
attestations: write
|
|
24
|
+
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
27
|
+
with:
|
|
28
|
+
fetch-depth: 0
|
|
29
|
+
persist-credentials: false
|
|
30
|
+
|
|
31
|
+
- uses: hynek/build-and-inspect-python-package@2abe76da66d0a6a4a227101f9348ee855797cfa5 # v3.0.1
|
|
32
|
+
with:
|
|
33
|
+
attest-build-provenance-github: true
|
|
34
|
+
|
|
35
|
+
publish-pypi:
|
|
36
|
+
name: Publish to PyPI
|
|
37
|
+
needs: [build]
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
environment:
|
|
40
|
+
name: pypi
|
|
41
|
+
url: https://pypi.org/p/jaxknn
|
|
42
|
+
permissions:
|
|
43
|
+
id-token: write
|
|
44
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
45
|
+
|
|
46
|
+
steps:
|
|
47
|
+
- name: Download built artifact to dist/
|
|
48
|
+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
49
|
+
with:
|
|
50
|
+
name: Packages
|
|
51
|
+
path: dist
|
|
52
|
+
|
|
53
|
+
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
pull_request:
|
|
6
|
+
push:
|
|
7
|
+
branches:
|
|
8
|
+
- main
|
|
9
|
+
schedule:
|
|
10
|
+
- cron: "0 0 * * 4" # Every Thursday at midnight UTC
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
|
|
15
|
+
concurrency:
|
|
16
|
+
# Each push to main gets its own group, so merges never cancel each other
|
|
17
|
+
# and main keeps a complete CI record. All other events keep the per-ref
|
|
18
|
+
# group and still supersede older runs.
|
|
19
|
+
group: >-
|
|
20
|
+
${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
|
|
21
|
+
cancel-in-progress: true
|
|
22
|
+
|
|
23
|
+
env:
|
|
24
|
+
FORCE_COLOR: 3
|
|
25
|
+
|
|
26
|
+
jobs:
|
|
27
|
+
lint:
|
|
28
|
+
name: Lint
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
32
|
+
|
|
33
|
+
- name: Install uv
|
|
34
|
+
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
35
|
+
|
|
36
|
+
- name: Install
|
|
37
|
+
run: uv sync --no-default-groups --group lint --locked
|
|
38
|
+
|
|
39
|
+
- name: Checks with prek
|
|
40
|
+
# Not a real commit -- no-commit-to-branch would always fail here.
|
|
41
|
+
run: uv run --frozen prek run --all-files
|
|
42
|
+
env:
|
|
43
|
+
SKIP: no-commit-to-branch
|
|
44
|
+
|
|
45
|
+
tests:
|
|
46
|
+
name: Python ${{ matrix.python-version }} on ${{ matrix.os }}
|
|
47
|
+
runs-on: ${{ matrix.os }}
|
|
48
|
+
needs: [lint]
|
|
49
|
+
permissions:
|
|
50
|
+
contents: read
|
|
51
|
+
id-token: write # Codecov OIDC upload
|
|
52
|
+
strategy:
|
|
53
|
+
fail-fast: false
|
|
54
|
+
matrix:
|
|
55
|
+
python-version: ["3.12", "3.14"]
|
|
56
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
59
|
+
with:
|
|
60
|
+
fetch-depth: 0
|
|
61
|
+
|
|
62
|
+
- name: Install uv
|
|
63
|
+
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
64
|
+
with:
|
|
65
|
+
python-version: ${{ matrix.python-version }}
|
|
66
|
+
|
|
67
|
+
- name: Install
|
|
68
|
+
run: uv sync --no-default-groups --group tests --locked
|
|
69
|
+
|
|
70
|
+
- name: Test with pytest
|
|
71
|
+
run: uv run --frozen pytest --cov --cov-report=xml --cov-report=term
|
|
72
|
+
|
|
73
|
+
- name: Upload coverage report
|
|
74
|
+
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
|
|
75
|
+
with:
|
|
76
|
+
use_oidc: true
|
|
77
|
+
|
|
78
|
+
oldest-supported:
|
|
79
|
+
name: Oldest supported deps
|
|
80
|
+
runs-on: ubuntu-latest
|
|
81
|
+
needs: [lint]
|
|
82
|
+
steps:
|
|
83
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
84
|
+
with:
|
|
85
|
+
fetch-depth: 0
|
|
86
|
+
|
|
87
|
+
- name: Install uv
|
|
88
|
+
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
89
|
+
with:
|
|
90
|
+
python-version: "3.12"
|
|
91
|
+
|
|
92
|
+
- name: Install the project
|
|
93
|
+
run: uv sync --no-default-groups --group tests --resolution lowest-direct
|
|
94
|
+
|
|
95
|
+
- name: Test package
|
|
96
|
+
run: uv run --no-sync pytest
|
|
97
|
+
|
|
98
|
+
newest-supported:
|
|
99
|
+
name: Newest supported deps
|
|
100
|
+
runs-on: ubuntu-latest
|
|
101
|
+
needs: [lint]
|
|
102
|
+
steps:
|
|
103
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
104
|
+
with:
|
|
105
|
+
fetch-depth: 0
|
|
106
|
+
|
|
107
|
+
- name: Install uv
|
|
108
|
+
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
109
|
+
with:
|
|
110
|
+
python-version: "3.14"
|
|
111
|
+
|
|
112
|
+
- name: Install the project
|
|
113
|
+
run: uv sync --no-default-groups --group tests --upgrade
|
|
114
|
+
|
|
115
|
+
- name: Test package
|
|
116
|
+
run: uv run --no-sync pytest
|
|
117
|
+
|
|
118
|
+
benchmark-smoke:
|
|
119
|
+
name: Benchmark smoke test
|
|
120
|
+
runs-on: ubuntu-latest
|
|
121
|
+
needs: [lint]
|
|
122
|
+
steps:
|
|
123
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
124
|
+
with:
|
|
125
|
+
fetch-depth: 0
|
|
126
|
+
|
|
127
|
+
- name: Install uv
|
|
128
|
+
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
129
|
+
|
|
130
|
+
- name: Install
|
|
131
|
+
run: uv sync --no-default-groups --group bench --locked
|
|
132
|
+
|
|
133
|
+
- name: Run the benchmarks once
|
|
134
|
+
run: uv run --frozen pytest tests/benchmarks --override-ini="addopts=" -p no:randomly
|
|
135
|
+
|
|
136
|
+
ci-pass:
|
|
137
|
+
name: CI Pass
|
|
138
|
+
runs-on: ubuntu-latest
|
|
139
|
+
needs: [lint, tests, oldest-supported, newest-supported, benchmark-smoke]
|
|
140
|
+
if: always()
|
|
141
|
+
steps:
|
|
142
|
+
- name: All required jobs passed
|
|
143
|
+
uses: re-actors/alls-green@b5b5b37504aa4183270bd3d855c52a67f212be35 # v1.3.0
|
|
144
|
+
with:
|
|
145
|
+
jobs: ${{ toJSON(needs) }}
|
jaxknn-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: "v6.0.0"
|
|
4
|
+
hooks:
|
|
5
|
+
- id: no-commit-to-branch
|
|
6
|
+
args: ["--branch", "main"]
|
|
7
|
+
always_run: true
|
|
8
|
+
- id: check-added-large-files
|
|
9
|
+
- id: check-case-conflict
|
|
10
|
+
- id: check-merge-conflict
|
|
11
|
+
- id: check-yaml
|
|
12
|
+
- id: debug-statements
|
|
13
|
+
- id: end-of-file-fixer
|
|
14
|
+
- id: mixed-line-ending
|
|
15
|
+
- id: name-tests-test
|
|
16
|
+
args: ["--pytest-test-first"]
|
|
17
|
+
exclude: ^tests/(benchmarks/|_compat\.py$)
|
|
18
|
+
- id: trailing-whitespace
|
|
19
|
+
|
|
20
|
+
- repo: https://github.com/python-jsonschema/check-jsonschema
|
|
21
|
+
rev: 0.36.0
|
|
22
|
+
hooks:
|
|
23
|
+
- id: check-github-workflows
|
|
24
|
+
|
|
25
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
26
|
+
rev: v0.15.14
|
|
27
|
+
hooks:
|
|
28
|
+
- id: ruff-check
|
|
29
|
+
types_or: [python, pyi]
|
|
30
|
+
args: [--fix, --show-fixes]
|
|
31
|
+
- id: ruff-format
|
|
32
|
+
types_or: [python, pyi]
|
|
33
|
+
|
|
34
|
+
- repo: https://github.com/codespell-project/codespell
|
|
35
|
+
rev: "v2.4.1"
|
|
36
|
+
hooks:
|
|
37
|
+
- id: codespell
|
|
38
|
+
|
|
39
|
+
- repo: https://github.com/ComPWA/taplo-pre-commit
|
|
40
|
+
rev: v0.9.3
|
|
41
|
+
hooks:
|
|
42
|
+
- id: taplo-format
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
jaxknn-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026, Nathaniel Starkman.
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
jaxknn-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: jaxknn
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Exact k-nearest neighbours in JAX: a kd-tree that traces under jit, vmap and grad.
|
|
5
|
+
Project-URL: Bug Tracker, https://github.com/nstarman/jaxknn/issues
|
|
6
|
+
Project-URL: Changelog, https://github.com/nstarman/jaxknn/releases
|
|
7
|
+
Project-URL: Documentation, https://nstarkman.space/jaxknn
|
|
8
|
+
Project-URL: Source Code, https://github.com/nstarman/jaxknn
|
|
9
|
+
Author-email: Nathaniel Starkman <nstarman@users.noreply.github.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: jax,kd-tree,knn,nearest-neighbors
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Natural Language :: English
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Requires-Python: >=3.12
|
|
26
|
+
Requires-Dist: equinox>=0.13.5
|
|
27
|
+
Requires-Dist: jax>=0.7.2
|
|
28
|
+
Requires-Dist: jaxtyping>=0.3.5
|
|
29
|
+
Requires-Dist: numpy>=2.0
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# jaxknn
|
|
33
|
+
|
|
34
|
+
Exact k-nearest neighbours in [JAX](https://github.com/jax-ml/jax): a kd-tree that traces under `jit`, `vmap` and `grad`.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install jaxknn
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
>>> import jax
|
|
42
|
+
>>> import jax.numpy as jnp
|
|
43
|
+
>>> import jaxknn
|
|
44
|
+
|
|
45
|
+
>>> points = jax.random.normal(jax.random.key(0), (1000, 3))
|
|
46
|
+
>>> idx, dist = jaxknn.knn(points, 5) # each point's 5 nearest *other* points
|
|
47
|
+
>>> idx.shape, dist.shape
|
|
48
|
+
((1000, 5), (1000, 5))
|
|
49
|
+
|
|
50
|
+
>>> queries = jnp.zeros((2, 3))
|
|
51
|
+
>>> idx, dist = jax.jit(lambda p, q: jaxknn.knn(p, 1, queries=q))(points, queries)
|
|
52
|
+
>>> idx.shape
|
|
53
|
+
(2, 1)
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## What it gives you
|
|
58
|
+
|
|
59
|
+
- **Exact results.** No approximation. Ties go to the lower index, identically eagerly and under `jit`.
|
|
60
|
+
- **Fully traceable.** `knn` works inside `jit`, `vmap` and `grad`. Distances are differentiable with respect to the coordinates, with the neighbour selection held fixed, and the gradient is 0 rather than NaN at coincident points.
|
|
61
|
+
- **Robust inputs.**
|
|
62
|
+
- NaN or inf input is an error, not a silently wrong answer: `ValueError` eagerly, a runtime error under `jit`.
|
|
63
|
+
- Integers and bfloat16 are promoted to float32.
|
|
64
|
+
- Coordinates are divided by an exact power of two when float32 would overflow or underflow, so values from about 1e-38 to 3e38 keep their neighbours. Typical cases are metres at kiloparsec scale, or tiny lengths.
|
|
65
|
+
- **Compiles once per size bucket.** Eager calls pad `n` to a bucket (2ʲ or 1.5·2ʲ), so inputs of different sizes share compiled code.
|
|
66
|
+
- **A raw tree for custom queries.** `jaxknn.tree` exposes `build_tree`, `knn`, `all_knn` and label-excluding queries (`knn(tree, q, k, exclude=(point_labels, query_labels))`: the nearest point with a *different* label, with whole same-label subtrees pruned).
|
|
67
|
+
|
|
68
|
+
`jaxknn.brute_knn` has the same contract by brute force. It's faster for small inputs.
|
|
69
|
+
|
|
70
|
+
## When to use it
|
|
71
|
+
|
|
72
|
+
Use jaxknn when the kNN has to live inside a traced JAX program: under `jit`, `vmap` or `grad`, or on an accelerator.
|
|
73
|
+
|
|
74
|
+
On a CPU, eagerly, SciPy's `cKDTree` is faster, because it queries with every core. Here's all-points kNN (k=10, a 3-D stream with 2% interlopers), comparing jaxknn compiled under `jit` with SciPy called eagerly. It was measured on an Apple M2 Pro under load, so the ratios matter more than the absolute times.
|
|
75
|
+
|
|
76
|
+
| n | `jit` compile (first call) | jaxknn under `jit`, steady | SciPy, eager | jaxknn / SciPy |
|
|
77
|
+
|---|---|---|---|---|
|
|
78
|
+
| 2,000 | 9.4 s | 17.8 ms | 6.0 ms | 2.9× |
|
|
79
|
+
| 10,000 | 11.6 s | 106 ms | 16.4 ms | 6.5× |
|
|
80
|
+
| 100,000 | 13.2 s | 1,261 ms | 148 ms | 8.5× |
|
|
81
|
+
| 1,000,000 | 26.8 s | 11,719 ms | 1,990 ms | 5.9× |
|
|
82
|
+
|
|
83
|
+
Against [jaxkd](https://github.com/dodgebc/jaxkd) at n = 100,000 (all-points, k = 10, CPU):
|
|
84
|
+
|
|
85
|
+
| data | jaxknn | jaxkd | SciPy |
|
|
86
|
+
|---|---|---|---|
|
|
87
|
+
| stream | 307 ms | 815 ms | 73 ms |
|
|
88
|
+
| blob | 355 ms | 1,161 ms | 80 ms |
|
|
89
|
+
| stream + interlopers | 513 ms | ~140 s | 78 ms |
|
|
90
|
+
|
|
91
|
+
## Origin
|
|
92
|
+
|
|
93
|
+
jaxknn was extracted from [phasecurvefit](https://github.com/GalacticDynamics/phasecurvefit), where it is the traceable kNN behind `MSTOrderer`, with its history. See [phasecurvefit#197](https://github.com/GalacticDynamics/phasecurvefit/issues/197).
|
|
94
|
+
|
|
95
|
+
## License
|
|
96
|
+
|
|
97
|
+
MIT
|
jaxknn-0.1.0/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# jaxknn
|
|
2
|
+
|
|
3
|
+
Exact k-nearest neighbours in [JAX](https://github.com/jax-ml/jax): a kd-tree that traces under `jit`, `vmap` and `grad`.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install jaxknn
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
>>> import jax
|
|
11
|
+
>>> import jax.numpy as jnp
|
|
12
|
+
>>> import jaxknn
|
|
13
|
+
|
|
14
|
+
>>> points = jax.random.normal(jax.random.key(0), (1000, 3))
|
|
15
|
+
>>> idx, dist = jaxknn.knn(points, 5) # each point's 5 nearest *other* points
|
|
16
|
+
>>> idx.shape, dist.shape
|
|
17
|
+
((1000, 5), (1000, 5))
|
|
18
|
+
|
|
19
|
+
>>> queries = jnp.zeros((2, 3))
|
|
20
|
+
>>> idx, dist = jax.jit(lambda p, q: jaxknn.knn(p, 1, queries=q))(points, queries)
|
|
21
|
+
>>> idx.shape
|
|
22
|
+
(2, 1)
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## What it gives you
|
|
27
|
+
|
|
28
|
+
- **Exact results.** No approximation. Ties go to the lower index, identically eagerly and under `jit`.
|
|
29
|
+
- **Fully traceable.** `knn` works inside `jit`, `vmap` and `grad`. Distances are differentiable with respect to the coordinates, with the neighbour selection held fixed, and the gradient is 0 rather than NaN at coincident points.
|
|
30
|
+
- **Robust inputs.**
|
|
31
|
+
- NaN or inf input is an error, not a silently wrong answer: `ValueError` eagerly, a runtime error under `jit`.
|
|
32
|
+
- Integers and bfloat16 are promoted to float32.
|
|
33
|
+
- Coordinates are divided by an exact power of two when float32 would overflow or underflow, so values from about 1e-38 to 3e38 keep their neighbours. Typical cases are metres at kiloparsec scale, or tiny lengths.
|
|
34
|
+
- **Compiles once per size bucket.** Eager calls pad `n` to a bucket (2ʲ or 1.5·2ʲ), so inputs of different sizes share compiled code.
|
|
35
|
+
- **A raw tree for custom queries.** `jaxknn.tree` exposes `build_tree`, `knn`, `all_knn` and label-excluding queries (`knn(tree, q, k, exclude=(point_labels, query_labels))`: the nearest point with a *different* label, with whole same-label subtrees pruned).
|
|
36
|
+
|
|
37
|
+
`jaxknn.brute_knn` has the same contract by brute force. It's faster for small inputs.
|
|
38
|
+
|
|
39
|
+
## When to use it
|
|
40
|
+
|
|
41
|
+
Use jaxknn when the kNN has to live inside a traced JAX program: under `jit`, `vmap` or `grad`, or on an accelerator.
|
|
42
|
+
|
|
43
|
+
On a CPU, eagerly, SciPy's `cKDTree` is faster, because it queries with every core. Here's all-points kNN (k=10, a 3-D stream with 2% interlopers), comparing jaxknn compiled under `jit` with SciPy called eagerly. It was measured on an Apple M2 Pro under load, so the ratios matter more than the absolute times.
|
|
44
|
+
|
|
45
|
+
| n | `jit` compile (first call) | jaxknn under `jit`, steady | SciPy, eager | jaxknn / SciPy |
|
|
46
|
+
|---|---|---|---|---|
|
|
47
|
+
| 2,000 | 9.4 s | 17.8 ms | 6.0 ms | 2.9× |
|
|
48
|
+
| 10,000 | 11.6 s | 106 ms | 16.4 ms | 6.5× |
|
|
49
|
+
| 100,000 | 13.2 s | 1,261 ms | 148 ms | 8.5× |
|
|
50
|
+
| 1,000,000 | 26.8 s | 11,719 ms | 1,990 ms | 5.9× |
|
|
51
|
+
|
|
52
|
+
Against [jaxkd](https://github.com/dodgebc/jaxkd) at n = 100,000 (all-points, k = 10, CPU):
|
|
53
|
+
|
|
54
|
+
| data | jaxknn | jaxkd | SciPy |
|
|
55
|
+
|---|---|---|---|
|
|
56
|
+
| stream | 307 ms | 815 ms | 73 ms |
|
|
57
|
+
| blob | 355 ms | 1,161 ms | 80 ms |
|
|
58
|
+
| stream + interlopers | 513 ms | ~140 s | 78 ms |
|
|
59
|
+
|
|
60
|
+
## Origin
|
|
61
|
+
|
|
62
|
+
jaxknn was extracted from [phasecurvefit](https://github.com/GalacticDynamics/phasecurvefit), where it is the traceable kNN behind `MSTOrderer`, with its history. See [phasecurvefit#197](https://github.com/GalacticDynamics/phasecurvefit/issues/197).
|
|
63
|
+
|
|
64
|
+
## License
|
|
65
|
+
|
|
66
|
+
MIT
|
jaxknn-0.1.0/conftest.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Doctest the code blocks in the README, the docs and the docstrings."""
|
|
2
|
+
|
|
3
|
+
from doctest import ELLIPSIS, NORMALIZE_WHITESPACE
|
|
4
|
+
|
|
5
|
+
from sybil import Sybil
|
|
6
|
+
from sybil.parsers import markdown, rest
|
|
7
|
+
|
|
8
|
+
_flags = ELLIPSIS | NORMALIZE_WHITESPACE
|
|
9
|
+
|
|
10
|
+
pytest_collect_file = (
|
|
11
|
+
Sybil(
|
|
12
|
+
parsers=[
|
|
13
|
+
markdown.PythonCodeBlockParser(doctest_optionflags=_flags),
|
|
14
|
+
markdown.SkipParser(),
|
|
15
|
+
],
|
|
16
|
+
patterns=["*.md"],
|
|
17
|
+
)
|
|
18
|
+
+ Sybil(parsers=[rest.DocTestParser(optionflags=_flags)], patterns=["*.py"])
|
|
19
|
+
).pytest()
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{% import "partials/language.html" as lang with context %}
|
|
2
|
+
<a href="{{ config.repo_url }}" title="{{ lang.t('source.link.title') }}" class="md-source" data-md-component="source">
|
|
3
|
+
<div class="md-source__icon md-icon">
|
|
4
|
+
{% set icon = config.theme.icon.repo or "fontawesome/brands/git-alt" %}
|
|
5
|
+
{% include ".icons/" ~ icon ~ ".svg" %}
|
|
6
|
+
</div>
|
|
7
|
+
<div class="md-source__repository">
|
|
8
|
+
{{ config.repo_name }}
|
|
9
|
+
</div>
|
|
10
|
+
</a>
|