pyglass-portable 2.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.
- pyglass_portable-2.1.0/LICENSE +21 -0
- pyglass_portable-2.1.0/PKG-INFO +68 -0
- pyglass_portable-2.1.0/README.md +155 -0
- pyglass_portable-2.1.0/README_portable.md +43 -0
- pyglass_portable-2.1.0/pyproject.toml +48 -0
- pyglass_portable-2.1.0/python/ann_dataset/__init__.py +3 -0
- pyglass_portable-2.1.0/python/ann_dataset/dataset.py +93 -0
- pyglass_portable-2.1.0/python/bindings.cc +295 -0
- pyglass_portable-2.1.0/python/pyglass_portable.egg-info/PKG-INFO +68 -0
- pyglass_portable-2.1.0/python/pyglass_portable.egg-info/SOURCES.txt +14 -0
- pyglass_portable-2.1.0/python/pyglass_portable.egg-info/dependency_links.txt +1 -0
- pyglass_portable-2.1.0/python/pyglass_portable.egg-info/requires.txt +1 -0
- pyglass_portable-2.1.0/python/pyglass_portable.egg-info/top_level.txt +2 -0
- pyglass_portable-2.1.0/setup.cfg +4 -0
- pyglass_portable-2.1.0/setup.py +24 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 zh Wang
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyglass-portable
|
|
3
|
+
Version: 2.1.0
|
|
4
|
+
Summary: Portable wheels of pyglass (zilliztech) — a fast HNSW/NSG graph ANN library that builds and runs on macOS (Apple Silicon and Intel) and non-AVX-512 x86 (AMD, older Intel). Imported as `glass`.
|
|
5
|
+
Author: Zihao Wang
|
|
6
|
+
Maintainer: Ian Driver
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/iandriver/pyglass
|
|
9
|
+
Project-URL: Upstream, https://github.com/zilliztech/pyglass
|
|
10
|
+
Project-URL: Issues, https://github.com/iandriver/pyglass/issues
|
|
11
|
+
Keywords: nearest-neighbors,ann,hnsw,nsg,vector-search,pyglass,similarity-search
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
16
|
+
Classifier: Operating System :: MacOS
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: C++
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: numpy
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# pyglass-portable
|
|
27
|
+
|
|
28
|
+
Portable prebuilt wheels of [**pyglass**](https://github.com/zilliztech/pyglass)
|
|
29
|
+
(zilliztech) — a fast HNSW / NSG graph-based approximate-nearest-neighbor library.
|
|
30
|
+
|
|
31
|
+
Upstream publishes only an **AVX-512** x86_64 wheel, which crashes (`SIGILL`) on
|
|
32
|
+
CPUs without AVX-512 — Apple Silicon, AMD, older Intel. pyglass's *source* is
|
|
33
|
+
portable (AVX2 / NEON / scalar kernels); this repackage builds it with a portable
|
|
34
|
+
baseline so it installs and runs on those machines too. **The import name stays
|
|
35
|
+
`glass`** — it's a drop-in for upstream, not a fork of the algorithm.
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
uv pip install pyglass-portable # or: pip install pyglass-portable
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Prebuilt wheels: Linux x86_64 (AVX2) and macOS arm64 (NEON), Python 3.9+. Intel
|
|
42
|
+
Macs and ARM Linux build from the sdist — `pip install` compiles it portably (it
|
|
43
|
+
needs a C++20 compiler; on macOS, `brew install libomp`).
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
import numpy as np, glass
|
|
47
|
+
|
|
48
|
+
X = np.random.rand(100_000, 128).astype("float32")
|
|
49
|
+
index = glass.Index("HNSW", "L2", "FP32", R=32, L=200)
|
|
50
|
+
graph = index.build(X)
|
|
51
|
+
searcher = glass.Searcher(graph, X, "L2", "FP32")
|
|
52
|
+
searcher.set_ef(64); searcher.optimize()
|
|
53
|
+
ids, dists = searcher.batch_search(X[:1000], 10)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
It also works as a `scanpy` neighbors backend via
|
|
57
|
+
[`pipnn.contrib.GlassTransformer`](https://github.com/iandriver/pipnn-scanpy).
|
|
58
|
+
|
|
59
|
+
## Relationship to upstream
|
|
60
|
+
|
|
61
|
+
This is an unofficial repackage for platform coverage only — **no algorithm
|
|
62
|
+
changes**. The portability fixes are proposed upstream in
|
|
63
|
+
[zilliztech/pyglass#24](https://github.com/zilliztech/pyglass/pull/24); if they
|
|
64
|
+
land and upstream ships portable wheels, prefer `glassppy`. Version tracks the
|
|
65
|
+
upstream source version.
|
|
66
|
+
|
|
67
|
+
Credit to the pyglass authors (Zihao Wang / zilliztech). MIT licensed, same as
|
|
68
|
+
upstream.
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Graph Library for Approximate Similarity Search
|
|
2
|
+
|
|
3
|
+
pyglass is a library for fast inference of graph index for approximate similarity search.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Supports multiple graph algorithms, like [**HNSW**](https://github.com/nmslib/hnswlib) and [**NSG**](https://github.com/ZJULearning/nsg).
|
|
8
|
+
- Supports multiple hardware platforms, like **X86** and **ARM**. Support for **GPU** is on the way
|
|
9
|
+
- No third-party library dependencies, does not rely on OpenBLAS / MKL or any other computing framework.
|
|
10
|
+
- Sophisticated memory management and data structure design, very low memory footprint.
|
|
11
|
+
- It's high performant.
|
|
12
|
+
|
|
13
|
+
## Getting Started
|
|
14
|
+
|
|
15
|
+
Install pyglass with pip
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install -v -e "python"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Using uv
|
|
22
|
+
|
|
23
|
+
It is recommended to use `uv` for setting up the development environment.
|
|
24
|
+
|
|
25
|
+
1. **Install `uv`**
|
|
26
|
+
```bash
|
|
27
|
+
pip install uv
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
2. **Create and Activate Virtual Environment**
|
|
31
|
+
```bash
|
|
32
|
+
# Create the virtual environment in a .venv directory
|
|
33
|
+
uv venv
|
|
34
|
+
# Activate the environment (on bash/zsh)
|
|
35
|
+
source .venv/bin/activate
|
|
36
|
+
```
|
|
37
|
+
*Note: For other shells like Fish or Powershell, use the corresponding activation script in the `.venv/bin/` directory.*
|
|
38
|
+
|
|
39
|
+
3. **Install the Project**
|
|
40
|
+
```bash
|
|
41
|
+
# Install the project and its dependencies in editable mode
|
|
42
|
+
uv pip install -v -e "python"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Development
|
|
46
|
+
|
|
47
|
+
For C++ development, you might want to generate a `compile_commands.json` file for your editor's language server. You can use [Bear](https://github.com/rizsotto/Bear) to do this.
|
|
48
|
+
|
|
49
|
+
**1. Install Bear**
|
|
50
|
+
|
|
51
|
+
On Debian-based systems (like Ubuntu), you can install Bear with:
|
|
52
|
+
```bash
|
|
53
|
+
apt install bear
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**2. Generate `compile_commands.json`**
|
|
57
|
+
|
|
58
|
+
Run the installation command from within `bear`:
|
|
59
|
+
```bash
|
|
60
|
+
bear -- uv pip install -v -e "python"
|
|
61
|
+
```
|
|
62
|
+
This will create a `compile_commands.json` file in the project's root directory.
|
|
63
|
+
|
|
64
|
+
## Quick Tour
|
|
65
|
+
A runnable demo is at [examples/demo.ipynb](https://github.com/zilliztech/pyglass/blob/master/examples/demo.ipynb). It's highly recommended to try it.
|
|
66
|
+
|
|
67
|
+
## Usage
|
|
68
|
+
**Import library**
|
|
69
|
+
```python
|
|
70
|
+
>>> import glass
|
|
71
|
+
```
|
|
72
|
+
**Load Data**
|
|
73
|
+
```python
|
|
74
|
+
>>> import numpy as np
|
|
75
|
+
>>> n, d = 10000, 128
|
|
76
|
+
>>> X = np.random.randn(n, d)
|
|
77
|
+
>>> Y = np.random.randn(d)
|
|
78
|
+
```
|
|
79
|
+
**Create Index**
|
|
80
|
+
pyglass supports **HNSW** and **NSG** index currently, with different quantization support
|
|
81
|
+
```python
|
|
82
|
+
>>> index = glass.Index(index_type="HNSW", metric="L2", R=32, L=50)
|
|
83
|
+
>>> index = glass.Index(index_type="NSG", metric="L2", R=32, L=50, quant="SQ8U")
|
|
84
|
+
```
|
|
85
|
+
**Build Graph**
|
|
86
|
+
```python
|
|
87
|
+
>>> graph = index.build(X)
|
|
88
|
+
```
|
|
89
|
+
**Create Searcher**
|
|
90
|
+
```python
|
|
91
|
+
>>> searcher = glass.Searcher(graph=graph, data=X, metric="L2", quantizer="SQ4U")
|
|
92
|
+
>>> searcher.set_ef(32)
|
|
93
|
+
```
|
|
94
|
+
**(Optional) Optimize Searcher**
|
|
95
|
+
```python
|
|
96
|
+
>>> searcher.optimize()
|
|
97
|
+
```
|
|
98
|
+
**Searching**
|
|
99
|
+
```python
|
|
100
|
+
>>> ret = searcher.search(query=Y, k=10)
|
|
101
|
+
>>> print(ret)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Supported Quantization Methods
|
|
105
|
+
|
|
106
|
+
- FP8_E5M2
|
|
107
|
+
- PQ8
|
|
108
|
+
- SQ8
|
|
109
|
+
- SQ8U
|
|
110
|
+
- SQ6
|
|
111
|
+
- SQ4
|
|
112
|
+
- SQ4U
|
|
113
|
+
- SQ4UA
|
|
114
|
+
- SQ2U
|
|
115
|
+
- BinaryQuant
|
|
116
|
+
|
|
117
|
+
**Rule of Thumb**: Use SQ8U for indexing, and SQ4U for searching is almost always a good choice.
|
|
118
|
+
|
|
119
|
+
## Performance
|
|
120
|
+
|
|
121
|
+
Glass is among one of the top performant ann algorithms on [ann-benchmarks](https://ann-benchmarks.com/)
|
|
122
|
+
|
|
123
|
+
### fashion-mnist-784-euclidean
|
|
124
|
+

|
|
125
|
+
### gist-960-euclidean
|
|
126
|
+

|
|
127
|
+
### sift-128-euclidean
|
|
128
|
+

|
|
129
|
+
|
|
130
|
+
### Quick Benchmark
|
|
131
|
+
|
|
132
|
+
1. Change configuration file `examples/config.json`
|
|
133
|
+
2. Run benchmark
|
|
134
|
+
```
|
|
135
|
+
python3 examples/main.py
|
|
136
|
+
```
|
|
137
|
+
If you encounter network issues when downloading datasets, you can try setting the `HF_ENDPOINT` environment variable to `https://hf-mirror.com` or something else.
|
|
138
|
+
|
|
139
|
+
## Citation
|
|
140
|
+
|
|
141
|
+
You can cite the PyGlass repo as follows:
|
|
142
|
+
```bibtex
|
|
143
|
+
@misc{PyGlass,
|
|
144
|
+
author = {Zihao Wang},
|
|
145
|
+
title = {Graph Library for Approximate Similarity Search},
|
|
146
|
+
url = {https://github.com/zilliztech/pyglass},
|
|
147
|
+
year = {2025},
|
|
148
|
+
month = {4},
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Star History
|
|
153
|
+
|
|
154
|
+
[](https://www.star-history.com/#zilliztech/pyglass&Date)
|
|
155
|
+
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# pyglass-portable
|
|
2
|
+
|
|
3
|
+
Portable prebuilt wheels of [**pyglass**](https://github.com/zilliztech/pyglass)
|
|
4
|
+
(zilliztech) — a fast HNSW / NSG graph-based approximate-nearest-neighbor library.
|
|
5
|
+
|
|
6
|
+
Upstream publishes only an **AVX-512** x86_64 wheel, which crashes (`SIGILL`) on
|
|
7
|
+
CPUs without AVX-512 — Apple Silicon, AMD, older Intel. pyglass's *source* is
|
|
8
|
+
portable (AVX2 / NEON / scalar kernels); this repackage builds it with a portable
|
|
9
|
+
baseline so it installs and runs on those machines too. **The import name stays
|
|
10
|
+
`glass`** — it's a drop-in for upstream, not a fork of the algorithm.
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
uv pip install pyglass-portable # or: pip install pyglass-portable
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Prebuilt wheels: Linux x86_64 (AVX2) and macOS arm64 (NEON), Python 3.9+. Intel
|
|
17
|
+
Macs and ARM Linux build from the sdist — `pip install` compiles it portably (it
|
|
18
|
+
needs a C++20 compiler; on macOS, `brew install libomp`).
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
import numpy as np, glass
|
|
22
|
+
|
|
23
|
+
X = np.random.rand(100_000, 128).astype("float32")
|
|
24
|
+
index = glass.Index("HNSW", "L2", "FP32", R=32, L=200)
|
|
25
|
+
graph = index.build(X)
|
|
26
|
+
searcher = glass.Searcher(graph, X, "L2", "FP32")
|
|
27
|
+
searcher.set_ef(64); searcher.optimize()
|
|
28
|
+
ids, dists = searcher.batch_search(X[:1000], 10)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
It also works as a `scanpy` neighbors backend via
|
|
32
|
+
[`pipnn.contrib.GlassTransformer`](https://github.com/iandriver/pipnn-scanpy).
|
|
33
|
+
|
|
34
|
+
## Relationship to upstream
|
|
35
|
+
|
|
36
|
+
This is an unofficial repackage for platform coverage only — **no algorithm
|
|
37
|
+
changes**. The portability fixes are proposed upstream in
|
|
38
|
+
[zilliztech/pyglass#24](https://github.com/zilliztech/pyglass/pull/24); if they
|
|
39
|
+
land and upstream ships portable wheels, prefer `glassppy`. Version tracks the
|
|
40
|
+
upstream source version.
|
|
41
|
+
|
|
42
|
+
Credit to the pyglass authors (Zihao Wang / zilliztech). MIT licensed, same as
|
|
43
|
+
upstream.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel", "numpy", "pybind11"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pyglass-portable"
|
|
7
|
+
version = "2.1.0"
|
|
8
|
+
description = "Portable wheels of pyglass (zilliztech) — a fast HNSW/NSG graph ANN library that builds and runs on macOS (Apple Silicon and Intel) and non-AVX-512 x86 (AMD, older Intel). Imported as `glass`."
|
|
9
|
+
readme = "README_portable.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "Zihao Wang" }]
|
|
13
|
+
maintainers = [{ name = "Ian Driver" }]
|
|
14
|
+
keywords = ["nearest-neighbors", "ann", "hnsw", "nsg", "vector-search", "pyglass", "similarity-search"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Intended Audience :: Science/Research",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Operating System :: POSIX :: Linux",
|
|
20
|
+
"Operating System :: MacOS",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: C++",
|
|
23
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
24
|
+
]
|
|
25
|
+
dependencies = ["numpy"]
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
Homepage = "https://github.com/iandriver/pyglass"
|
|
29
|
+
Upstream = "https://github.com/zilliztech/pyglass"
|
|
30
|
+
Issues = "https://github.com/iandriver/pyglass/issues"
|
|
31
|
+
|
|
32
|
+
# Wheels are built with cibuildwheel (see .github/workflows/release.yml).
|
|
33
|
+
[tool.cibuildwheel]
|
|
34
|
+
build = "cp39-* cp310-* cp311-* cp312-* cp313-*"
|
|
35
|
+
skip = "*-musllinux* *_i686 pp*"
|
|
36
|
+
build-frontend = "build"
|
|
37
|
+
test-command = 'python -c "import glass, numpy as np; g=glass.Index(\"HNSW\",\"L2\",\"FP32\",32,200).build(np.random.rand(2000,32).astype(\"float32\")); print(\"glass OK\")"'
|
|
38
|
+
|
|
39
|
+
# GLASS_MARCH (the portable x86 AVX2 baseline for redistributable wheels — without
|
|
40
|
+
# it the build uses -march=native and bakes in the CI runner's ISA) is set per-job
|
|
41
|
+
# in the release workflow, so it applies to x86 targets only and not arm64/NEON.
|
|
42
|
+
[tool.cibuildwheel.linux]
|
|
43
|
+
# manylinux_2_28 has a new-enough toolchain for C++20.
|
|
44
|
+
manylinux-x86_64-image = "manylinux_2_28"
|
|
45
|
+
|
|
46
|
+
[tool.cibuildwheel.macos]
|
|
47
|
+
# libomp for OpenMP (build_extension.py picks it up from Homebrew).
|
|
48
|
+
before-all = "brew install libomp"
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import h5py
|
|
3
|
+
import numpy as np
|
|
4
|
+
from sklearn import preprocessing
|
|
5
|
+
import requests
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
HF_ENDPOINT = os.getenv("HF_ENDPOINT", "https://huggingface.co")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Dataset:
|
|
12
|
+
def __init__(self, name, metric="L2", normalize=False, url="", data_dir="datasets"):
|
|
13
|
+
self.name = name
|
|
14
|
+
self.metric = metric
|
|
15
|
+
self._normalize = normalize
|
|
16
|
+
self.file_path = Path(data_dir) / f"{self.name}.hdf5"
|
|
17
|
+
self._file = None
|
|
18
|
+
self._download_url = url
|
|
19
|
+
|
|
20
|
+
self._load_or_download_file()
|
|
21
|
+
|
|
22
|
+
def _load_or_download_file(self):
|
|
23
|
+
if not self.file_path.exists():
|
|
24
|
+
self.file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
25
|
+
self._download_file()
|
|
26
|
+
self._file = h5py.File(self.file_path, "r")
|
|
27
|
+
|
|
28
|
+
def _download_file(self):
|
|
29
|
+
url = (
|
|
30
|
+
f"{HF_ENDPOINT}/datasets/{self._download_url}/resolve/main/{self.name}.hdf5"
|
|
31
|
+
)
|
|
32
|
+
print(f"Downloading {self.name} from {url} to {self.file_path}...")
|
|
33
|
+
response = requests.get(url, stream=True)
|
|
34
|
+
response.raise_for_status()
|
|
35
|
+
with open(self.file_path, "wb") as f:
|
|
36
|
+
for chunk in response.iter_content(chunk_size=8192):
|
|
37
|
+
f.write(chunk)
|
|
38
|
+
print("Download complete.")
|
|
39
|
+
|
|
40
|
+
def _preprocess(self, data):
|
|
41
|
+
if self._normalize:
|
|
42
|
+
return preprocessing.normalize(data, norm="l2", axis=1)
|
|
43
|
+
return data
|
|
44
|
+
|
|
45
|
+
def get_database(self):
|
|
46
|
+
return self._preprocess(np.array(self._file["train"]))
|
|
47
|
+
|
|
48
|
+
def get_queries(self):
|
|
49
|
+
return self._preprocess(np.array(self._file["test"]))
|
|
50
|
+
|
|
51
|
+
def get_groundtruth(self, k):
|
|
52
|
+
return np.array(self._file["neighbors"])[:, :k]
|
|
53
|
+
|
|
54
|
+
def evaluate(self, pred, k=None):
|
|
55
|
+
nq, topk = pred.shape
|
|
56
|
+
if k is not None:
|
|
57
|
+
topk = k
|
|
58
|
+
gt = self.get_groundtruth(topk)
|
|
59
|
+
cnt = 0
|
|
60
|
+
for i in range(nq):
|
|
61
|
+
cnt += np.intersect1d(pred[i], gt[i]).size
|
|
62
|
+
return cnt / nq / k
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
hhy3_url = "hhy3/ann-datasets"
|
|
66
|
+
vibe_url = "vector-index-bench/vibe"
|
|
67
|
+
|
|
68
|
+
dataset_configs = {
|
|
69
|
+
"sift-128-euclidean": {"metric": "L2", "normalize": False, "url": hhy3_url},
|
|
70
|
+
"fashion-mnist-784-euclidean": {
|
|
71
|
+
"metric": "L2",
|
|
72
|
+
"normalize": False,
|
|
73
|
+
"url": hhy3_url,
|
|
74
|
+
},
|
|
75
|
+
"nytimes-256-angular": {"metric": "IP", "normalize": True, "url": hhy3_url},
|
|
76
|
+
"glove-100-angular": {"metric": "IP", "normalize": False, "url": hhy3_url},
|
|
77
|
+
"glove-25-angular": {"metric": "IP", "normalize": True, "url": hhy3_url},
|
|
78
|
+
"lastfm-64-dot": {"metric": "IP", "normalize": True, "url": hhy3_url},
|
|
79
|
+
"gist-960-euclidean": {"metric": "L2", "normalize": False, "url": hhy3_url},
|
|
80
|
+
"cohere-768-angular": {"metric": "IP", "normalize": True, "url": hhy3_url},
|
|
81
|
+
"llama-128-ip": {"metric": "IP", "normalize": False, "url": vibe_url},
|
|
82
|
+
"yi-128-ip": {"metric": "IP", "normalize": False, "url": vibe_url},
|
|
83
|
+
"agnews-mxbai-1024-euclidean": {
|
|
84
|
+
"metric": "L2",
|
|
85
|
+
"normalize": False,
|
|
86
|
+
"url": vibe_url,
|
|
87
|
+
},
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
dataset_dict = {
|
|
91
|
+
name: lambda n=name, cfg=config: Dataset(n, **cfg)
|
|
92
|
+
for name, config in dataset_configs.items()
|
|
93
|
+
}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
#include <pybind11/functional.h>
|
|
2
|
+
#include <pybind11/numpy.h>
|
|
3
|
+
#include <pybind11/pybind11.h>
|
|
4
|
+
#include <pybind11/stl.h>
|
|
5
|
+
|
|
6
|
+
#include <thread>
|
|
7
|
+
|
|
8
|
+
#include <omp.h>
|
|
9
|
+
|
|
10
|
+
#include "glass/algorithms/clustering.hpp"
|
|
11
|
+
#include "glass/builder.hpp"
|
|
12
|
+
#include "glass/hnsw/hnsw.hpp"
|
|
13
|
+
#include "glass/nsg/nsg.hpp"
|
|
14
|
+
#include "glass/nsg/rnndescent.hpp"
|
|
15
|
+
#include "glass/searcher/graph_searcher.hpp"
|
|
16
|
+
|
|
17
|
+
namespace py = pybind11;
|
|
18
|
+
using namespace pybind11::literals; // needed to bring in _a literal
|
|
19
|
+
|
|
20
|
+
inline void get_input_array_shapes(const py::buffer_info &buffer, size_t *rows,
|
|
21
|
+
size_t *features) {
|
|
22
|
+
if (buffer.ndim != 2 && buffer.ndim != 1) {
|
|
23
|
+
char msg[256];
|
|
24
|
+
snprintf(msg, sizeof(msg),
|
|
25
|
+
"Input vector data wrong shape. Number of dimensions %d. Data "
|
|
26
|
+
"must be a 1D or 2D array.",
|
|
27
|
+
buffer.ndim);
|
|
28
|
+
}
|
|
29
|
+
if (buffer.ndim == 2) {
|
|
30
|
+
*rows = buffer.shape[0];
|
|
31
|
+
*features = buffer.shape[1];
|
|
32
|
+
} else {
|
|
33
|
+
*rows = 1;
|
|
34
|
+
*features = buffer.shape[0];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
void set_num_threads(int num_threads) { omp_set_num_threads(num_threads); }
|
|
39
|
+
|
|
40
|
+
struct Graph {
|
|
41
|
+
glass::Graph<int> graph;
|
|
42
|
+
|
|
43
|
+
Graph() = default;
|
|
44
|
+
|
|
45
|
+
explicit Graph(Graph &&rhs) : graph(std::move(rhs.graph)) {}
|
|
46
|
+
|
|
47
|
+
Graph(const std::string &filename, const std::string &format = "glass") {
|
|
48
|
+
graph.load(filename, format);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
explicit Graph(glass::Graph<int> graph) : graph(std::move(graph)) {}
|
|
52
|
+
|
|
53
|
+
void save(const std::string &filename) { graph.save(filename); }
|
|
54
|
+
|
|
55
|
+
void load(const std::string &filename, const std::string &format = "glass") {
|
|
56
|
+
graph.load(filename, format);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
struct Index {
|
|
61
|
+
std::unique_ptr<glass::Builder> index = nullptr;
|
|
62
|
+
|
|
63
|
+
Index(const std::string &index_type, const std::string &metric,
|
|
64
|
+
const std::string &quant = "FP32", int R = 32, int L = 200) {
|
|
65
|
+
if (index_type == "HNSW") {
|
|
66
|
+
index = glass::create_hnsw(metric, quant, R, L);
|
|
67
|
+
} else if (index_type == "NSG") {
|
|
68
|
+
index = glass::create_nsg(quant, R, L, false);
|
|
69
|
+
} else if (index_type == "SSG") {
|
|
70
|
+
index = glass::create_nsg(quant, R, L, true);
|
|
71
|
+
} else if (index_type == "RNNDESCENT") {
|
|
72
|
+
index = glass::create_rnndescent(quant, R);
|
|
73
|
+
} else {
|
|
74
|
+
printf("Unknown index type\n");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
Graph build(py::object input) {
|
|
79
|
+
py::array_t<float, py::array::c_style | py::array::forcecast> items(input);
|
|
80
|
+
auto buffer = items.request();
|
|
81
|
+
size_t rows, features;
|
|
82
|
+
get_input_array_shapes(buffer, &rows, &features);
|
|
83
|
+
float *vector_data = (float *)items.data(0);
|
|
84
|
+
return Graph(index->Build(vector_data, rows, features));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
double get_construction_time() const { return index->GetConstructionTime(); }
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
struct Searcher {
|
|
91
|
+
|
|
92
|
+
std::unique_ptr<glass::GraphSearcherBase> searcher;
|
|
93
|
+
|
|
94
|
+
Searcher(Graph &graph, py::object input, const std::string &metric,
|
|
95
|
+
const std::string &quantizer, const std::string &refine_quant = "")
|
|
96
|
+
: searcher(
|
|
97
|
+
std::unique_ptr<glass::GraphSearcherBase>(glass::create_searcher(
|
|
98
|
+
std::move(graph.graph), metric, quantizer, refine_quant))) {
|
|
99
|
+
py::array_t<float, py::array::c_style | py::array::forcecast> items(input);
|
|
100
|
+
auto buffer = items.request();
|
|
101
|
+
size_t rows, features;
|
|
102
|
+
get_input_array_shapes(buffer, &rows, &features);
|
|
103
|
+
float *vector_data = (float *)items.data(0);
|
|
104
|
+
searcher->SetData(vector_data, rows, features);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
py::object search(py::object query, int k) {
|
|
108
|
+
py::array_t<float, py::array::c_style | py::array::forcecast> items(query);
|
|
109
|
+
int *ids;
|
|
110
|
+
float *dis;
|
|
111
|
+
{
|
|
112
|
+
py::gil_scoped_release l;
|
|
113
|
+
ids = new int[k];
|
|
114
|
+
dis = new float[k];
|
|
115
|
+
searcher->Search(items.data(0), k, ids, dis);
|
|
116
|
+
}
|
|
117
|
+
py::capsule free_when_done(ids, [](void *f) { delete[] f; });
|
|
118
|
+
py::capsule free_when_done_dis(dis, [](void *f) { delete[] f; });
|
|
119
|
+
return py::make_tuple(
|
|
120
|
+
py::array_t<int32_t>({(size_t)k}, {sizeof(int32_t)}, ids, free_when_done),
|
|
121
|
+
py::array_t<float>({(size_t)k}, {sizeof(float)}, dis, free_when_done_dis));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
py::object batch_search(py::object query, int k, int num_threads = 0) {
|
|
125
|
+
py::array_t<float, py::array::c_style | py::array::forcecast> items(query);
|
|
126
|
+
auto buffer = items.request();
|
|
127
|
+
int32_t *ids;
|
|
128
|
+
float *dis;
|
|
129
|
+
size_t nq, dim;
|
|
130
|
+
{
|
|
131
|
+
py::gil_scoped_release l;
|
|
132
|
+
get_input_array_shapes(buffer, &nq, &dim);
|
|
133
|
+
ids = new int[nq * k];
|
|
134
|
+
dis = new float[nq * k];
|
|
135
|
+
if (num_threads != 0) {
|
|
136
|
+
omp_set_num_threads(num_threads);
|
|
137
|
+
}
|
|
138
|
+
searcher->SearchBatch(items.data(0), nq, k, ids, dis);
|
|
139
|
+
}
|
|
140
|
+
py::capsule free_when_done(ids, [](void *f) { delete[] f; });
|
|
141
|
+
py::capsule free_when_done_dis(dis, [](void *f) { delete[] f; });
|
|
142
|
+
return py::make_tuple(
|
|
143
|
+
py::array_t<int32_t>({(size_t)nq, (size_t)k}, {k * sizeof(int32_t), sizeof(int32_t)}, ids, free_when_done),
|
|
144
|
+
py::array_t<float>({(size_t)nq, (size_t)k}, {k * sizeof(float), sizeof(float)}, dis, free_when_done_dis));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
void set_ef(int ef) { searcher->SetEf(ef); }
|
|
148
|
+
|
|
149
|
+
void optimize(int num_threads = 0) { searcher->Optimize(num_threads); }
|
|
150
|
+
|
|
151
|
+
double get_last_search_avg_dist_cmps() const {
|
|
152
|
+
return searcher->GetLastSearchAvgDistCmps();
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
struct Clustering {
|
|
157
|
+
|
|
158
|
+
glass::Clustering internal;
|
|
159
|
+
|
|
160
|
+
Clustering(int32_t n_cluster, int32_t epochs = 10,
|
|
161
|
+
const std::string &init = "random")
|
|
162
|
+
: internal(n_cluster, epochs, init) {}
|
|
163
|
+
|
|
164
|
+
void fit(py::object data) {
|
|
165
|
+
py::array_t<float, py::array::c_style | py::array::forcecast> items(data);
|
|
166
|
+
auto buffer = items.request();
|
|
167
|
+
size_t n, dim;
|
|
168
|
+
{
|
|
169
|
+
py::gil_scoped_release l;
|
|
170
|
+
get_input_array_shapes(buffer, &n, &dim);
|
|
171
|
+
internal.fit(items.data(0), n, dim);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
py::object predict(py::object data) {
|
|
176
|
+
py::array_t<float, py::array::c_style | py::array::forcecast> items(data);
|
|
177
|
+
auto buffer = items.request();
|
|
178
|
+
std::vector<int32_t> ids;
|
|
179
|
+
size_t n, dim;
|
|
180
|
+
{
|
|
181
|
+
py::gil_scoped_release l;
|
|
182
|
+
get_input_array_shapes(buffer, &n, &dim);
|
|
183
|
+
ids = internal.predict(items.data(0), n, dim);
|
|
184
|
+
}
|
|
185
|
+
return py::array(ids.size(), ids.data());
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
py::object transform(py::object data, bool inplace = false) {
|
|
189
|
+
py::array_t<float, py::array::c_style | py::array::forcecast> items(data);
|
|
190
|
+
auto buffer = items.request();
|
|
191
|
+
size_t n, dim;
|
|
192
|
+
{
|
|
193
|
+
py::gil_scoped_release l;
|
|
194
|
+
get_input_array_shapes(buffer, &n, &dim);
|
|
195
|
+
if (inplace) {
|
|
196
|
+
internal.transform((float *)items.data(0), n, dim);
|
|
197
|
+
return py::none();
|
|
198
|
+
} else {
|
|
199
|
+
// TODO
|
|
200
|
+
return py::none();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
#define DECLARE_QUANT(py_name, cxx_name) \
|
|
207
|
+
struct py_name { \
|
|
208
|
+
glass::cxx_name<glass::Metric::L2> quant; \
|
|
209
|
+
py_name(int32_t dim) : quant(dim) {} \
|
|
210
|
+
\
|
|
211
|
+
void train(py::object data) { \
|
|
212
|
+
py::array_t<float, py::array::c_style | py::array::forcecast> items( \
|
|
213
|
+
data); \
|
|
214
|
+
auto buffer = items.request(); \
|
|
215
|
+
size_t n, dim; \
|
|
216
|
+
{ \
|
|
217
|
+
py::gil_scoped_release l; \
|
|
218
|
+
get_input_array_shapes(buffer, &n, &dim); \
|
|
219
|
+
quant.train(items.data(0), n); \
|
|
220
|
+
} \
|
|
221
|
+
} \
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
DECLARE_QUANT(quant_fp32, FP32Quantizer)
|
|
225
|
+
DECLARE_QUANT(quant_bf16, BF16Quantizer)
|
|
226
|
+
DECLARE_QUANT(quant_fp16, FP16Quantizer)
|
|
227
|
+
DECLARE_QUANT(quant_e5m2, E5M2Quantizer)
|
|
228
|
+
DECLARE_QUANT(quant_sq8, SQ8Quantizer)
|
|
229
|
+
DECLARE_QUANT(quant_sq8u, SQ8QuantizerUniform)
|
|
230
|
+
|
|
231
|
+
double build_graph(const std::string &index_type, py::object input,
|
|
232
|
+
const std::string &graph_file, const std::string &metric,
|
|
233
|
+
const std::string &quant = "FP32", int R = 32, int L = 200) {
|
|
234
|
+
Index index(index_type, metric, quant, R, L);
|
|
235
|
+
index.build(input).save(graph_file);
|
|
236
|
+
return index.get_construction_time();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
PYBIND11_MODULE(glass, m) {
|
|
240
|
+
m.def("set_num_threads", &set_num_threads, py::arg("num_threads"));
|
|
241
|
+
|
|
242
|
+
py::class_<Graph>(m, "Graph")
|
|
243
|
+
.def(py::init<>())
|
|
244
|
+
.def(py::init<const std::string &, const std::string &>(),
|
|
245
|
+
py::arg("filename"), py::arg("format") = "glass")
|
|
246
|
+
.def("save", &Graph::save, py::arg("filename"))
|
|
247
|
+
.def("load", &Graph::load, py::arg("filename"),
|
|
248
|
+
py::arg("format") = "glass");
|
|
249
|
+
|
|
250
|
+
py::class_<Index>(m, "Index")
|
|
251
|
+
.def(py::init<const std::string &, const std::string &,
|
|
252
|
+
const std::string &, int, int>(),
|
|
253
|
+
py::arg("index_type"), py::arg("metric"), py::arg("quant") = "FP32",
|
|
254
|
+
py::arg("R") = 32, py::arg("L") = 0)
|
|
255
|
+
.def("build", &Index::build, py::arg("data"))
|
|
256
|
+
.def("get_construction_time", &Index::get_construction_time);
|
|
257
|
+
|
|
258
|
+
py::class_<Searcher>(m, "Searcher")
|
|
259
|
+
.def(py::init<Graph &, py::object, const std::string &,
|
|
260
|
+
const std::string &, const std::string &>(),
|
|
261
|
+
py::arg("graph"), py::arg("data"), py::arg("metric"),
|
|
262
|
+
py::arg("quantizer"), py::arg("refine_quant") = "")
|
|
263
|
+
.def("set_ef", &Searcher::set_ef, py::arg("ef"))
|
|
264
|
+
.def("search", &Searcher::search, py::arg("query"), py::arg("k"))
|
|
265
|
+
.def("batch_search", &Searcher::batch_search, py::arg("query"),
|
|
266
|
+
py::arg("k"), py::arg("num_threads") = 0)
|
|
267
|
+
.def("optimize", &Searcher::optimize, py::arg("num_threads") = 0)
|
|
268
|
+
.def("get_last_search_dist_cmps",
|
|
269
|
+
&Searcher::get_last_search_avg_dist_cmps);
|
|
270
|
+
|
|
271
|
+
py::class_<Clustering>(m, "Clustering")
|
|
272
|
+
.def(py::init<int32_t, int32_t, const std::string &>(),
|
|
273
|
+
py::arg("n_cluster"), py::arg("epochs") = 10,
|
|
274
|
+
py::arg("init") = "random")
|
|
275
|
+
.def("fit", &Clustering::fit, py::arg("data"))
|
|
276
|
+
.def("predict", &Clustering::predict, py::arg("data"))
|
|
277
|
+
.def("transform", &Clustering::transform, py::arg("data"),
|
|
278
|
+
py::arg("inplace") = false);
|
|
279
|
+
|
|
280
|
+
#define PYDECLARE_QUANT(py_name) \
|
|
281
|
+
py::class_<py_name>(m, #py_name) \
|
|
282
|
+
.def(py::init<int32_t>(), py::arg("dim")) \
|
|
283
|
+
.def("train", &py_name::train, py::arg("data"));
|
|
284
|
+
|
|
285
|
+
PYDECLARE_QUANT(quant_fp32)
|
|
286
|
+
PYDECLARE_QUANT(quant_bf16)
|
|
287
|
+
PYDECLARE_QUANT(quant_fp16)
|
|
288
|
+
PYDECLARE_QUANT(quant_e5m2)
|
|
289
|
+
PYDECLARE_QUANT(quant_sq8)
|
|
290
|
+
PYDECLARE_QUANT(quant_sq8u)
|
|
291
|
+
|
|
292
|
+
m.def("build_graph", &build_graph, py::arg("index_type"), py::arg("input"),
|
|
293
|
+
py::arg("graph_file"), py::arg("metric"), py::arg("quant"),
|
|
294
|
+
py::arg("R"), py::arg("L"));
|
|
295
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyglass-portable
|
|
3
|
+
Version: 2.1.0
|
|
4
|
+
Summary: Portable wheels of pyglass (zilliztech) — a fast HNSW/NSG graph ANN library that builds and runs on macOS (Apple Silicon and Intel) and non-AVX-512 x86 (AMD, older Intel). Imported as `glass`.
|
|
5
|
+
Author: Zihao Wang
|
|
6
|
+
Maintainer: Ian Driver
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/iandriver/pyglass
|
|
9
|
+
Project-URL: Upstream, https://github.com/zilliztech/pyglass
|
|
10
|
+
Project-URL: Issues, https://github.com/iandriver/pyglass/issues
|
|
11
|
+
Keywords: nearest-neighbors,ann,hnsw,nsg,vector-search,pyglass,similarity-search
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
16
|
+
Classifier: Operating System :: MacOS
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: C++
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: numpy
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# pyglass-portable
|
|
27
|
+
|
|
28
|
+
Portable prebuilt wheels of [**pyglass**](https://github.com/zilliztech/pyglass)
|
|
29
|
+
(zilliztech) — a fast HNSW / NSG graph-based approximate-nearest-neighbor library.
|
|
30
|
+
|
|
31
|
+
Upstream publishes only an **AVX-512** x86_64 wheel, which crashes (`SIGILL`) on
|
|
32
|
+
CPUs without AVX-512 — Apple Silicon, AMD, older Intel. pyglass's *source* is
|
|
33
|
+
portable (AVX2 / NEON / scalar kernels); this repackage builds it with a portable
|
|
34
|
+
baseline so it installs and runs on those machines too. **The import name stays
|
|
35
|
+
`glass`** — it's a drop-in for upstream, not a fork of the algorithm.
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
uv pip install pyglass-portable # or: pip install pyglass-portable
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Prebuilt wheels: Linux x86_64 (AVX2) and macOS arm64 (NEON), Python 3.9+. Intel
|
|
42
|
+
Macs and ARM Linux build from the sdist — `pip install` compiles it portably (it
|
|
43
|
+
needs a C++20 compiler; on macOS, `brew install libomp`).
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
import numpy as np, glass
|
|
47
|
+
|
|
48
|
+
X = np.random.rand(100_000, 128).astype("float32")
|
|
49
|
+
index = glass.Index("HNSW", "L2", "FP32", R=32, L=200)
|
|
50
|
+
graph = index.build(X)
|
|
51
|
+
searcher = glass.Searcher(graph, X, "L2", "FP32")
|
|
52
|
+
searcher.set_ef(64); searcher.optimize()
|
|
53
|
+
ids, dists = searcher.batch_search(X[:1000], 10)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
It also works as a `scanpy` neighbors backend via
|
|
57
|
+
[`pipnn.contrib.GlassTransformer`](https://github.com/iandriver/pipnn-scanpy).
|
|
58
|
+
|
|
59
|
+
## Relationship to upstream
|
|
60
|
+
|
|
61
|
+
This is an unofficial repackage for platform coverage only — **no algorithm
|
|
62
|
+
changes**. The portability fixes are proposed upstream in
|
|
63
|
+
[zilliztech/pyglass#24](https://github.com/zilliztech/pyglass/pull/24); if they
|
|
64
|
+
land and upstream ships portable wheels, prefer `glassppy`. Version tracks the
|
|
65
|
+
upstream source version.
|
|
66
|
+
|
|
67
|
+
Credit to the pyglass authors (Zihao Wang / zilliztech). MIT licensed, same as
|
|
68
|
+
upstream.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
README_portable.md
|
|
4
|
+
pyproject.toml
|
|
5
|
+
setup.py
|
|
6
|
+
./python/bindings.cc
|
|
7
|
+
python/bindings.cc
|
|
8
|
+
python/ann_dataset/__init__.py
|
|
9
|
+
python/ann_dataset/dataset.py
|
|
10
|
+
python/pyglass_portable.egg-info/PKG-INFO
|
|
11
|
+
python/pyglass_portable.egg-info/SOURCES.txt
|
|
12
|
+
python/pyglass_portable.egg-info/dependency_links.txt
|
|
13
|
+
python/pyglass_portable.egg-info/requires.txt
|
|
14
|
+
python/pyglass_portable.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
numpy
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Root build shim for the `pyglass-portable` repackage.
|
|
2
|
+
|
|
3
|
+
Upstream's setup.py lives in `python/` and references `../glass` / `../third_party`
|
|
4
|
+
relative to that directory. Building from the repo root instead makes the project
|
|
5
|
+
self-contained (`glass/` and `third_party/` sit right here), which is what
|
|
6
|
+
cibuildwheel needs. `python/build_extension.py` already resolves its sources and
|
|
7
|
+
include dirs relative to the current working directory, so this shim just calls
|
|
8
|
+
into it from the root.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
import sys
|
|
13
|
+
|
|
14
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "python"))
|
|
15
|
+
|
|
16
|
+
from build_extension import create_extension, get_build_ext_cmdclass # noqa: E402
|
|
17
|
+
from setuptools import setup # noqa: E402
|
|
18
|
+
|
|
19
|
+
setup(
|
|
20
|
+
ext_modules=create_extension(os.environ.get("GLASS_VERSION", "2.1.0")),
|
|
21
|
+
cmdclass=get_build_ext_cmdclass(),
|
|
22
|
+
package_dir={"": "python"},
|
|
23
|
+
packages=["ann_dataset"],
|
|
24
|
+
)
|