vicinity 0.1.0__tar.gz → 0.2.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 (35) hide show
  1. vicinity-0.2.0/.github/workflows/ci.yaml +67 -0
  2. vicinity-0.2.0/Makefile +21 -0
  3. {vicinity-0.1.0 → vicinity-0.2.0}/PKG-INFO +56 -2
  4. vicinity-0.2.0/README.md +186 -0
  5. {vicinity-0.1.0 → vicinity-0.2.0}/pyproject.toml +16 -1
  6. vicinity-0.2.0/tests/conftest.py +65 -0
  7. vicinity-0.1.0/tests/test_nearest.py → vicinity-0.2.0/tests/test_vicinity.py +32 -18
  8. {vicinity-0.1.0 → vicinity-0.2.0}/uv.lock +260 -58
  9. {vicinity-0.1.0 → vicinity-0.2.0}/vicinity/backends/__init__.py +13 -0
  10. vicinity-0.2.0/vicinity/backends/annoy.py +128 -0
  11. {vicinity-0.1.0 → vicinity-0.2.0}/vicinity/backends/base.py +4 -6
  12. {vicinity-0.1.0 → vicinity-0.2.0}/vicinity/backends/basic.py +4 -6
  13. vicinity-0.2.0/vicinity/backends/faiss.py +233 -0
  14. {vicinity-0.1.0 → vicinity-0.2.0}/vicinity/backends/hnsw.py +6 -7
  15. vicinity-0.2.0/vicinity/backends/pynndescent.py +109 -0
  16. {vicinity-0.1.0 → vicinity-0.2.0}/vicinity/datatypes.py +3 -0
  17. {vicinity-0.1.0 → vicinity-0.2.0}/vicinity/version.py +1 -1
  18. {vicinity-0.1.0 → vicinity-0.2.0}/vicinity.egg-info/PKG-INFO +56 -2
  19. {vicinity-0.1.0 → vicinity-0.2.0}/vicinity.egg-info/SOURCES.txt +6 -2
  20. vicinity-0.2.0/vicinity.egg-info/requires.txt +28 -0
  21. vicinity-0.1.0/Makefile +0 -15
  22. vicinity-0.1.0/README.md +0 -146
  23. vicinity-0.1.0/tests/conftest.py +0 -39
  24. vicinity-0.1.0/vicinity.egg-info/requires.txt +0 -15
  25. {vicinity-0.1.0 → vicinity-0.2.0}/.gitignore +0 -0
  26. {vicinity-0.1.0 → vicinity-0.2.0}/.pre-commit-config.yaml +0 -0
  27. {vicinity-0.1.0 → vicinity-0.2.0}/LICENSE +0 -0
  28. {vicinity-0.1.0 → vicinity-0.2.0}/setup.cfg +0 -0
  29. {vicinity-0.1.0 → vicinity-0.2.0}/tests/test_utils.py +0 -0
  30. {vicinity-0.1.0 → vicinity-0.2.0}/vicinity/__init__.py +0 -0
  31. {vicinity-0.1.0 → vicinity-0.2.0}/vicinity/py.typed +0 -0
  32. {vicinity-0.1.0 → vicinity-0.2.0}/vicinity/utils.py +0 -0
  33. {vicinity-0.1.0 → vicinity-0.2.0}/vicinity/vicinity.py +0 -0
  34. {vicinity-0.1.0 → vicinity-0.2.0}/vicinity.egg-info/dependency_links.txt +0 -0
  35. {vicinity-0.1.0 → vicinity-0.2.0}/vicinity.egg-info/top_level.txt +0 -0
@@ -0,0 +1,67 @@
1
+ name: Run tests and upload coverage
2
+
3
+ on:
4
+ pull_request:
5
+ types: [opened, synchronize, reopened, ready_for_review]
6
+
7
+ jobs:
8
+ test:
9
+ name: Run tests with pytest
10
+ if: ${{ !github.event.pull_request.draft }} # Skip draft PRs
11
+ runs-on: ${{ matrix.os }}
12
+ strategy:
13
+ matrix:
14
+ os: ["ubuntu-latest", "windows-latest"]
15
+ python-version: ["3.9", "3.10", "3.11", "3.12"]
16
+ exclude:
17
+ - os: windows-latest
18
+ python-version: "3.9"
19
+ - os: windows-latest
20
+ python-version: "3.11"
21
+ - os: windows-latest
22
+ python-version: "3.12"
23
+ fail-fast: false
24
+
25
+ steps:
26
+ - uses: actions/checkout@v4
27
+
28
+ - name: Set up Python ${{ matrix.python-version }} on ${{ matrix.os }}
29
+ uses: actions/setup-python@v5
30
+ with:
31
+ python-version: ${{ matrix.python-version }}
32
+ allow-prereleases: true
33
+
34
+ # Step for Windows: Create and activate a virtual environment
35
+ - name: Create and activate a virtual environment (Windows)
36
+ if: ${{ runner.os == 'Windows' }}
37
+ run: |
38
+ irm https://astral.sh/uv/install.ps1 | iex
39
+ $env:Path = "C:\Users\runneradmin\.local\bin;$env:Path"
40
+ uv venv .venv
41
+ "VIRTUAL_ENV=.venv" | Out-File -FilePath $env:GITHUB_ENV -Append
42
+ "$PWD/.venv/Scripts" | Out-File -FilePath $env:GITHUB_PATH -Append
43
+
44
+ # Step for Unix: Create and activate a virtual environment
45
+ - name: Create and activate a virtual environment (Unix)
46
+ if: ${{ runner.os != 'Windows' }}
47
+ run: |
48
+ curl -LsSf https://astral.sh/uv/install.sh | sh
49
+ uv venv .venv
50
+ echo "VIRTUAL_ENV=.venv" >> $GITHUB_ENV
51
+ echo "$PWD/.venv/bin" >> $GITHUB_PATH
52
+
53
+ # Install dependencies using uv pip
54
+ - name: Install dependencies
55
+ run: make install-no-pre-commit
56
+
57
+ # Run tests with coverage
58
+ - name: Run tests under coverage
59
+ run: |
60
+ coverage run -m pytest
61
+ coverage report
62
+
63
+ # Upload results to Codecov
64
+ - name: Upload results to Codecov
65
+ uses: codecov/codecov-action@v4
66
+ with:
67
+ token: ${{ secrets.CODECOV_TOKEN }}
@@ -0,0 +1,21 @@
1
+ clean:
2
+
3
+
4
+ venv:
5
+ uv venv
6
+
7
+ install: venv
8
+ uv sync --all-extras
9
+ uv run pre-commit install
10
+
11
+ install-no-pre-commit:
12
+ uv pip install ".[dev,hnsw,pynndescent,annoy,faiss]"
13
+
14
+ install-base:
15
+ uv sync --extra dev
16
+
17
+ fix:
18
+ uv run pre-commit run --all-files
19
+
20
+ test:
21
+ uv run pytest --cov=vicinity --cov-report=term-missing
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vicinity
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Lightweight Nearest Neighbors with Flexible Backends
5
5
  Author-email: Stéphan Tulkens <stephantul@gmail.com>, Thomas van Dongen <thomas123@live.nl>
6
6
  License: MIT License
@@ -25,6 +25,9 @@ License: MIT License
25
25
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
26
  SOFTWARE.
27
27
 
28
+ Project-URL: Homepage, https://github.com/MinishLab
29
+ Project-URL: Bug Reports, https://github.com/MinishLab/vicinity/issues
30
+ Project-URL: Source, https://github.com/MinishLab/vicinity
28
31
  Classifier: Development Status :: 4 - Beta
29
32
  Classifier: Intended Audience :: Developers
30
33
  Classifier: Intended Audience :: Science/Research
@@ -32,10 +35,11 @@ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
32
35
  Classifier: Topic :: Software Development :: Libraries
33
36
  Classifier: License :: OSI Approved :: MIT License
34
37
  Classifier: Programming Language :: Python :: 3 :: Only
38
+ Classifier: Programming Language :: Python :: 3.9
35
39
  Classifier: Programming Language :: Python :: 3.10
36
40
  Classifier: Programming Language :: Python :: 3.11
37
41
  Classifier: Programming Language :: Python :: 3.12
38
- Requires-Python: >=3.10
42
+ Requires-Python: >=3.9
39
43
  Description-Content-Type: text/markdown
40
44
  License-File: LICENSE
41
45
  Requires-Dist: numpy
@@ -49,8 +53,18 @@ Requires-Dist: pre-commit; extra == "dev"
49
53
  Requires-Dist: pytest; extra == "dev"
50
54
  Requires-Dist: pytest-coverage; extra == "dev"
51
55
  Requires-Dist: ruff; extra == "dev"
56
+ Requires-Dist: setuptools; extra == "dev"
52
57
  Provides-Extra: hnsw
53
58
  Requires-Dist: hnswlib; extra == "hnsw"
59
+ Provides-Extra: pynndescent
60
+ Requires-Dist: pynndescent>=0.5.10; extra == "pynndescent"
61
+ Requires-Dist: numba>=0.59.0; extra == "pynndescent"
62
+ Requires-Dist: llvmlite>=0.42.0; extra == "pynndescent"
63
+ Requires-Dist: numpy>=1.24.0; extra == "pynndescent"
64
+ Provides-Extra: annoy
65
+ Requires-Dist: annoy; extra == "annoy"
66
+ Provides-Extra: faiss
67
+ Requires-Dist: faiss-cpu; extra == "faiss"
54
68
 
55
69
  <div align="center">
56
70
 
@@ -58,11 +72,28 @@ Requires-Dist: hnswlib; extra == "hnsw"
58
72
 
59
73
  </div>
60
74
 
75
+
76
+ <div align="center">
77
+ <h2>
78
+ <a href="https://pypi.org/project/vicinity/"><img src="https://img.shields.io/pypi/v/vicinity?color=%23007ec6&label=pypi%20package" alt="Package version"></a>
79
+ <a href="https://pypi.org/project/vicinity/"><img src="https://img.shields.io/pypi/pyversions/vicinity" alt="Supported Python versions"></a>
80
+ <a href="https://pepy.tech/project/vicinity">
81
+ <img src="https://static.pepy.tech/badge/vicinity" alt="Downloads">
82
+ </a>
83
+ <a href="https://app.codecov.io/gh/MinishLab/vicinity">
84
+ <img src="https://codecov.io/gh/MinishLab/vicinity/graph/badge.svg?token=0MQ2945OZL" alt="Codecov">
85
+ </a>
86
+ <a href="https://github.com/MinishLab/vicinity/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="License - MIT"></a>
87
+ </h2>
88
+ </div>
89
+
90
+
61
91
  ## Table of contents
62
92
 
63
93
  - [Quickstart](#quickstart)
64
94
  - [Main Features](#main-features)
65
95
  - [Supported Backends](#supported-backends)
96
+ - [Backend Parameters](#backend-parameters)
66
97
  - [Usage](#usage)
67
98
 
68
99
  Vicinity is the lightest-weight vector store. Just put in some vectors, calculate query vectors, and off you go. It provides a simple and intuitive API for nearest neighbor search, with support for different backends.
@@ -113,6 +144,29 @@ Vicinity provides the following features:
113
144
  The following backends are supported:
114
145
  - `BASIC`: A simple flat index for vector storage and search.
115
146
  - `HNSW`: Hierarchical Navigable Small World Graph for approximate nearest neighbor search.
147
+ - `FAISS`: All FAISS indexes for approximate nearest neighbor search are supported.
148
+ - `ANNOY`: "Approximate Nearest Neighbors Oh Yeah" for approximate nearest neighbor search.
149
+ - `PYNNDescent`: Approximate nearest neighbor search using PyNNDescent.
150
+
151
+ ### Backend Parameters
152
+
153
+ | Backend | Parameter | Description | Default Value |
154
+ |----------------|--------------------|-----------------------------------------------------------------------------------------------|---------------------|
155
+ | **Annoy** | `metric` | Similarity metric to use (`dot`, `euclidean`, `cosine`). | `"cosine"` |
156
+ | | `trees` | Number of trees to use for indexing. | `100` |
157
+ | | `length` | Optional length of the dataset. | `None` |
158
+ | **FAISS** | `index_type` | Type of FAISS index (`flat`, `ivf`, `hnsw`, `lsh`, `scalar`, `pq`, `ivf_scalar`, `ivfpq`, `ivfpqr`). | `"hnsw"` |
159
+ | | `metric` | Similarity metric to use (`cosine`, `l2`). | `"cosine"` |
160
+ | | `nlist` | Number of cells for IVF indexes. | `100` |
161
+ | | `m` | Number of subquantizers for PQ and HNSW indexes. | `8` |
162
+ | | `nbits` | Number of bits for LSH and PQ indexes. | `8` |
163
+ | | `refine_nbits` | Number of bits for the refinement stage in IVFPQR indexes. | `8` |
164
+ | **HNSW** | `space` | Similarity space to use (`cosine`, `l2`). | `"cosine"` |
165
+ | | `ef_construction` | Size of the dynamic list during index construction. | `200` |
166
+ | | `m` | Number of connections per layer. | `16` |
167
+ | **PyNNDescent**| `n_neighbors` | Number of neighbors to use for search. | `15` |
168
+ | | `metric` | Similarity metric to use (`cosine`, `euclidean`, `manhattan`). | `"cosine"` |
169
+
116
170
 
117
171
  ## Usage
118
172
 
@@ -0,0 +1,186 @@
1
+ <div align="center">
2
+
3
+ # Vicinity: The Lightweight Vector Store
4
+
5
+ </div>
6
+
7
+
8
+ <div align="center">
9
+ <h2>
10
+ <a href="https://pypi.org/project/vicinity/"><img src="https://img.shields.io/pypi/v/vicinity?color=%23007ec6&label=pypi%20package" alt="Package version"></a>
11
+ <a href="https://pypi.org/project/vicinity/"><img src="https://img.shields.io/pypi/pyversions/vicinity" alt="Supported Python versions"></a>
12
+ <a href="https://pepy.tech/project/vicinity">
13
+ <img src="https://static.pepy.tech/badge/vicinity" alt="Downloads">
14
+ </a>
15
+ <a href="https://app.codecov.io/gh/MinishLab/vicinity">
16
+ <img src="https://codecov.io/gh/MinishLab/vicinity/graph/badge.svg?token=0MQ2945OZL" alt="Codecov">
17
+ </a>
18
+ <a href="https://github.com/MinishLab/vicinity/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="License - MIT"></a>
19
+ </h2>
20
+ </div>
21
+
22
+
23
+ ## Table of contents
24
+
25
+ - [Quickstart](#quickstart)
26
+ - [Main Features](#main-features)
27
+ - [Supported Backends](#supported-backends)
28
+ - [Backend Parameters](#backend-parameters)
29
+ - [Usage](#usage)
30
+
31
+ Vicinity is the lightest-weight vector store. Just put in some vectors, calculate query vectors, and off you go. It provides a simple and intuitive API for nearest neighbor search, with support for different backends.
32
+
33
+ ## Quickstart
34
+
35
+ Install the package with:
36
+ ```bash
37
+ pip install vicinity
38
+ ```
39
+
40
+ The following code snippet demonstrates how to use Vicinity for nearest neighbor search:
41
+ ```python
42
+ import numpy as np
43
+ from vicinity import Vicinity
44
+ from vicinity.datatypes import Backend
45
+
46
+ # Create some dummy data
47
+ items = ["triforce", "master sword", "hylian shield", "boomerang", "hookshot"]
48
+ vectors = np.random.rand(len(items), 128)
49
+
50
+ # Initialize the Vicinity instance (using the basic backend)
51
+ vicinity = Vicinity.from_vectors_and_items(vectors=vectors, items=items, backend_type=Backend.BASIC)
52
+
53
+ # Query for nearest neighbors with a top-k search
54
+ query_vector = np.random.rand(128)
55
+ results = vicinity.query([query_vector], k=3)
56
+
57
+ # Query for nearest neighbors with a threshold search
58
+ results = vicinity.query_threshold([query_vector], threshold=0.9)
59
+
60
+ # Save the vector store
61
+ vicinity.save('my_vector_store')
62
+
63
+ # Load the vector store
64
+ vicinity = Vicinity.load('my_vector_store')
65
+ ```
66
+
67
+ ## Main Features
68
+ Vicinity provides the following features:
69
+ - Lightweight: Minimal dependencies and fast performance.
70
+ - Flexible Backend Support: Use different backends for vector storage and search.
71
+ - Dynamic Updates: Insert and delete items in the vector store.
72
+ - Serialization: Save and load vector stores for persistence.
73
+ - Easy to Use: Simple and intuitive API.
74
+
75
+ ## Supported Backends
76
+ The following backends are supported:
77
+ - `BASIC`: A simple flat index for vector storage and search.
78
+ - `HNSW`: Hierarchical Navigable Small World Graph for approximate nearest neighbor search.
79
+ - `FAISS`: All FAISS indexes for approximate nearest neighbor search are supported.
80
+ - `ANNOY`: "Approximate Nearest Neighbors Oh Yeah" for approximate nearest neighbor search.
81
+ - `PYNNDescent`: Approximate nearest neighbor search using PyNNDescent.
82
+
83
+ ### Backend Parameters
84
+
85
+ | Backend | Parameter | Description | Default Value |
86
+ |----------------|--------------------|-----------------------------------------------------------------------------------------------|---------------------|
87
+ | **Annoy** | `metric` | Similarity metric to use (`dot`, `euclidean`, `cosine`). | `"cosine"` |
88
+ | | `trees` | Number of trees to use for indexing. | `100` |
89
+ | | `length` | Optional length of the dataset. | `None` |
90
+ | **FAISS** | `index_type` | Type of FAISS index (`flat`, `ivf`, `hnsw`, `lsh`, `scalar`, `pq`, `ivf_scalar`, `ivfpq`, `ivfpqr`). | `"hnsw"` |
91
+ | | `metric` | Similarity metric to use (`cosine`, `l2`). | `"cosine"` |
92
+ | | `nlist` | Number of cells for IVF indexes. | `100` |
93
+ | | `m` | Number of subquantizers for PQ and HNSW indexes. | `8` |
94
+ | | `nbits` | Number of bits for LSH and PQ indexes. | `8` |
95
+ | | `refine_nbits` | Number of bits for the refinement stage in IVFPQR indexes. | `8` |
96
+ | **HNSW** | `space` | Similarity space to use (`cosine`, `l2`). | `"cosine"` |
97
+ | | `ef_construction` | Size of the dynamic list during index construction. | `200` |
98
+ | | `m` | Number of connections per layer. | `16` |
99
+ | **PyNNDescent**| `n_neighbors` | Number of neighbors to use for search. | `15` |
100
+ | | `metric` | Similarity metric to use (`cosine`, `euclidean`, `manhattan`). | `"cosine"` |
101
+
102
+
103
+ ## Usage
104
+
105
+ <details>
106
+ <summary> Creating a Vector Store
107
+ </summary>
108
+ <br>
109
+
110
+ You can create a Vicinity instance by providing items and their corresponding vectors:
111
+
112
+
113
+ ```python
114
+ from vicinity import Vicinity
115
+ import numpy as np
116
+
117
+ items = ["triforce", "master sword", "hylian shield", "boomerang", "hookshot"]
118
+ vectors = np.random.rand(len(items), 128)
119
+
120
+ vicinity = Vicinity.from_vectors_and_items(vectors=vectors, items=items)
121
+ ```
122
+
123
+ </details>
124
+
125
+ <details>
126
+ <summary> Querying
127
+ </summary>
128
+ <br>
129
+
130
+ Find the k nearest neighbors for a given vector:
131
+
132
+ ```python
133
+ query_vector = np.random.rand(128)
134
+ results = vicinity.query([query_vector], k=3)
135
+ ```
136
+
137
+ Find all neighbors within a given threshold:
138
+
139
+ ```python
140
+ query_vector = np.random.rand(128)
141
+ results = vicinity.query_threshold([query_vector], threshold=0.9)
142
+ ```
143
+ </details>
144
+
145
+ <details>
146
+
147
+ <summary> Inserting and Deleting Items
148
+ </summary>
149
+ <br>
150
+
151
+ Insert new items:
152
+
153
+ ```python
154
+ new_items = ["ocarina", "bow"]
155
+ new_vectors = np.random.rand(2, 128)
156
+ vicinity.insert(new_items, new_vectors)
157
+ ```
158
+
159
+ Delete items:
160
+
161
+ ```python
162
+ vicinity.delete(["hookshot"])
163
+ ```
164
+ </details>
165
+
166
+ <details>
167
+ <summary> Saving and Loading
168
+ </summary>
169
+ <br>
170
+
171
+ Save the vector store:
172
+
173
+ ```python
174
+ vicinity.save('my_vector_store')
175
+ ```
176
+
177
+ Load the vector store:
178
+
179
+ ```python
180
+ vicinity = Vicinity.load('my_vector_store')
181
+ ```
182
+ </details>
183
+
184
+ ## License
185
+
186
+ MIT
@@ -3,7 +3,7 @@ name = "vicinity"
3
3
  description = "Lightweight Nearest Neighbors with Flexible Backends"
4
4
  readme = { file = "README.md", content-type = "text/markdown" }
5
5
  dynamic = ["version"]
6
- requires-python = ">=3.10"
6
+ requires-python = ">=3.9"
7
7
  license = { file = "LICENSE" }
8
8
  authors = [{ name = "Stéphan Tulkens", email = "stephantul@gmail.com"}, {name = "Thomas van Dongen", email = "thomas123@live.nl"}]
9
9
 
@@ -15,6 +15,7 @@ classifiers = [
15
15
  "Topic :: Software Development :: Libraries",
16
16
  "License :: OSI Approved :: MIT License",
17
17
  "Programming Language :: Python :: 3 :: Only",
18
+ "Programming Language :: Python :: 3.9",
18
19
  "Programming Language :: Python :: 3.10",
19
20
  "Programming Language :: Python :: 3.11",
20
21
  "Programming Language :: Python :: 3.12",
@@ -39,8 +40,22 @@ dev = [
39
40
  "pytest",
40
41
  "pytest-coverage",
41
42
  "ruff",
43
+ "setuptools"
42
44
  ]
43
45
  hnsw = ["hnswlib"]
46
+ pynndescent = [
47
+ "pynndescent>=0.5.10",
48
+ "numba>=0.59.0",
49
+ "llvmlite>=0.42.0",
50
+ "numpy>=1.24.0"
51
+ ]
52
+ annoy = ["annoy"]
53
+ faiss = ["faiss-cpu"]
54
+
55
+ [project.urls]
56
+ "Homepage" = "https://github.com/MinishLab"
57
+ "Bug Reports" = "https://github.com/MinishLab/vicinity/issues"
58
+ "Source" = "https://github.com/MinishLab/vicinity"
44
59
 
45
60
  [tool.ruff]
46
61
  exclude = [".venv/"]
@@ -0,0 +1,65 @@
1
+ from __future__ import annotations
2
+
3
+ import numpy as np
4
+ import pytest
5
+
6
+ from vicinity import Vicinity
7
+ from vicinity.datatypes import Backend
8
+
9
+ random_gen = np.random.default_rng(42)
10
+
11
+ _faiss_index_types = ["flat", "ivf", "hnsw", "lsh", "scalar", "pq", "ivf_scalar", "ivfpq", "ivfpqr"]
12
+
13
+
14
+ @pytest.fixture(scope="session")
15
+ def items() -> list[str]:
16
+ """Fixture providing a list of item names."""
17
+ return [f"item{i}" for i in range(1, 10001)]
18
+
19
+
20
+ @pytest.fixture(scope="session")
21
+ def vectors() -> np.ndarray:
22
+ """Fixture providing an array of vectors corresponding to items."""
23
+ return random_gen.random((10000, 8))
24
+
25
+
26
+ @pytest.fixture(scope="session")
27
+ def query_vector() -> np.ndarray:
28
+ """Fixture providing a query vector."""
29
+ return random_gen.random(8)
30
+
31
+
32
+ BACKEND_PARAMS = [(Backend.FAISS, index_type) for index_type in _faiss_index_types] + [
33
+ (Backend.BASIC, None),
34
+ (Backend.HNSW, None),
35
+ (Backend.ANNOY, None),
36
+ (Backend.PYNNDESCENT, None),
37
+ ]
38
+
39
+ # Create human-readable ids for each backend type
40
+ BACKEND_IDS = [f"{backend.name}-{index_type}" if index_type else backend.name for backend, index_type in BACKEND_PARAMS]
41
+
42
+
43
+ @pytest.fixture(params=BACKEND_PARAMS)
44
+ def backend_type(request: pytest.FixtureRequest) -> Backend:
45
+ """Fixture parametrizing over all backend types defined in Backend."""
46
+ return request.param
47
+
48
+
49
+ @pytest.fixture(params=BACKEND_PARAMS, ids=BACKEND_IDS)
50
+ def vicinity_instance(request: pytest.FixtureRequest, items: list[str], vectors: np.ndarray) -> Vicinity:
51
+ """Fixture providing a Vicinity instance for each backend type."""
52
+ backend_type, index_type = request.param
53
+ # Handle FAISS backend with specific FAISS index types
54
+ if backend_type == Backend.FAISS:
55
+ if index_type in ("pq", "ivfpq", "ivfpqr"):
56
+ # Use smaller values for pq indexes since the dataset is small
57
+ return Vicinity.from_vectors_and_items(
58
+ vectors, items, backend_type=backend_type, index_type=index_type, m=2, nbits=4
59
+ )
60
+ else:
61
+ return Vicinity.from_vectors_and_items(
62
+ vectors, items, backend_type=backend_type, index_type=index_type, nlist=2, nbits=32
63
+ )
64
+
65
+ return Vicinity.from_vectors_and_items(vectors, items, backend_type=backend_type)
@@ -8,16 +8,19 @@ import pytest
8
8
  from vicinity import Vicinity
9
9
  from vicinity.datatypes import Backend
10
10
 
11
+ BackendType = tuple[Backend, str]
11
12
 
12
- def test_vicinity_init(backend_type: Backend, items: list[str], vectors: np.ndarray) -> None:
13
+
14
+ def test_vicinity_init(backend_type: BackendType, items: list[str], vectors: np.ndarray) -> None:
13
15
  """
14
16
  Test Vicinity.init.
15
17
 
16
- :param backend_type: The backend type to use (BASIC or HNSW).
18
+ :param backend_type: The backend type to use.
17
19
  :param items: A list of item names.
18
20
  :param vectors: An array of vectors.
19
21
  """
20
- vicinity = Vicinity.from_vectors_and_items(vectors, items, backend_type=backend_type)
22
+ backend = backend_type[0]
23
+ vicinity = Vicinity.from_vectors_and_items(vectors, items, backend_type=backend)
21
24
  assert len(vicinity) == len(items)
22
25
  assert vicinity.items == items
23
26
  assert vicinity.dim == vectors.shape[1]
@@ -25,18 +28,19 @@ def test_vicinity_init(backend_type: Backend, items: list[str], vectors: np.ndar
25
28
  vectors = np.random.default_rng(42).random((len(items) - 1, 5))
26
29
 
27
30
  with pytest.raises(ValueError):
28
- vicinity = Vicinity.from_vectors_and_items(vectors, items, backend_type=backend_type)
31
+ vicinity = Vicinity.from_vectors_and_items(vectors, items, backend_type=backend)
29
32
 
30
33
 
31
- def test_vicinity_from_vectors_and_items(backend_type: Backend, items: list[str], vectors: np.ndarray) -> None:
34
+ def test_vicinity_from_vectors_and_items(backend_type: BackendType, items: list[str], vectors: np.ndarray) -> None:
32
35
  """
33
36
  Test Vicinity.from_vectors_and_items.
34
37
 
35
- :param backend_type: The backend type to use (BASIC or HNSW).
38
+ :param backend_type: The backend type to use.
36
39
  :param items: A list of item names.
37
40
  :param vectors: An array of vectors.
38
41
  """
39
- vicinity = Vicinity.from_vectors_and_items(vectors, items, backend_type=backend_type)
42
+ backend = backend_type[0]
43
+ vicinity = Vicinity.from_vectors_and_items(vectors, items, backend_type=backend)
40
44
 
41
45
  assert len(vicinity) == len(items)
42
46
  assert vicinity.items == items
@@ -67,7 +71,7 @@ def test_vicinity_query_threshold(vicinity_instance: Vicinity, query_vector: np.
67
71
  assert len(results) >= 1
68
72
 
69
73
 
70
- def test_vicinity_insert(backend_type: Backend, vicinity_instance: Vicinity, query_vector: np.ndarray) -> None:
74
+ def test_vicinity_insert(vicinity_instance: Vicinity, query_vector: np.ndarray) -> None:
71
75
  """
72
76
  Test Vicinity.insert method.
73
77
 
@@ -75,18 +79,17 @@ def test_vicinity_insert(backend_type: Backend, vicinity_instance: Vicinity, que
75
79
  :param vicinity_instance: A Vicinity instance.
76
80
  :param query_vector: A query vector.
77
81
  """
78
- if backend_type == Backend.HNSW:
79
- # Don't test insert for HNSW backend
82
+ if vicinity_instance.backend.backend_type in {Backend.HNSW, Backend.ANNOY, Backend.PYNNDESCENT}:
83
+ # Skip insert for HNSW or Annoy backends.
80
84
  return
81
- new_item = ["item101"]
85
+ new_item = ["item10001"]
82
86
  new_vector = query_vector
83
-
84
87
  vicinity_instance.insert(new_item, new_vector[None, :])
85
88
 
86
- results = vicinity_instance.query(query_vector, k=1)
87
- returned_item = results[0][0][0]
89
+ results = vicinity_instance.query(query_vector, k=50)
88
90
 
89
- assert returned_item == "item101"
91
+ returned_items = [item for item, _ in results[0]]
92
+ assert "item10001" in returned_items
90
93
 
91
94
 
92
95
  def test_vicinity_delete(vicinity_instance: Vicinity, items: list[str], vectors: np.ndarray) -> None:
@@ -97,6 +100,17 @@ def test_vicinity_delete(vicinity_instance: Vicinity, items: list[str], vectors:
97
100
  :param items: List of item names.
98
101
  :param vectors: Array of vectors corresponding to items.
99
102
  """
103
+ if vicinity_instance.backend.backend_type in {Backend.ANNOY, Backend.PYNNDESCENT}:
104
+ # Skip delete for Annoy and Pynndescent backend
105
+ return
106
+
107
+ elif vicinity_instance.backend.backend_type == Backend.FAISS and vicinity_instance.backend.arguments.index_type in {
108
+ "hnsw",
109
+ "ivfpqr",
110
+ }:
111
+ # Skip delete test for FAISS index types that do not support deletion
112
+ return
113
+
100
114
  # Get the vector corresponding to "item2"
101
115
  item2_index = items.index("item2")
102
116
  item2_vector = vectors[item2_index]
@@ -150,7 +164,7 @@ def test_vicinity_delete_nonexistent(vicinity_instance: Vicinity) -> None:
150
164
  :raises ValueError: If deleting items that do not exist.
151
165
  """
152
166
  with pytest.raises(ValueError):
153
- vicinity_instance.delete(["item102"])
167
+ vicinity_instance.delete(["item10002"])
154
168
 
155
169
 
156
170
  def test_vicinity_insert_mismatched_lengths(vicinity_instance: Vicinity, query_vector: np.ndarray) -> None:
@@ -160,7 +174,7 @@ def test_vicinity_insert_mismatched_lengths(vicinity_instance: Vicinity, query_v
160
174
  :param vicinity_instance: A Vicinity instance.
161
175
  :raises ValueError: If tokens and vectors lengths differ.
162
176
  """
163
- new_items = ["item102", "item103"]
177
+ new_items = ["item10002", "item10003"]
164
178
  new_vector = query_vector
165
179
 
166
180
  with pytest.raises(ValueError):
@@ -174,7 +188,7 @@ def test_vicinity_insert_wrong_dimension(vicinity_instance: Vicinity) -> None:
174
188
  :param vicinity_instance: A Vicinity instance.
175
189
  :raises ValueError: If vectors have wrong dimension.
176
190
  """
177
- new_item = ["item102"]
191
+ new_item = ["item10002"]
178
192
  new_vector = np.array([[0.5, 0.5, 0.5]])
179
193
 
180
194
  with pytest.raises(ValueError):