vicinity 0.3.4__tar.gz → 0.4.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 (34) hide show
  1. {vicinity-0.3.4 → vicinity-0.4.0}/PKG-INFO +1 -1
  2. {vicinity-0.3.4 → vicinity-0.4.0}/uv.lock +1 -1
  3. {vicinity-0.3.4 → vicinity-0.4.0}/vicinity/backends/annoy.py +5 -4
  4. {vicinity-0.3.4 → vicinity-0.4.0}/vicinity/backends/base.py +1 -1
  5. {vicinity-0.3.4 → vicinity-0.4.0}/vicinity/backends/basic.py +9 -6
  6. {vicinity-0.3.4 → vicinity-0.4.0}/vicinity/backends/faiss.py +7 -5
  7. {vicinity-0.3.4 → vicinity-0.4.0}/vicinity/backends/hnsw.py +5 -4
  8. {vicinity-0.3.4 → vicinity-0.4.0}/vicinity/backends/pynndescent.py +6 -6
  9. {vicinity-0.3.4 → vicinity-0.4.0}/vicinity/backends/usearch.py +9 -6
  10. {vicinity-0.3.4 → vicinity-0.4.0}/vicinity/backends/voyager.py +5 -4
  11. {vicinity-0.3.4 → vicinity-0.4.0}/vicinity/version.py +1 -1
  12. {vicinity-0.3.4 → vicinity-0.4.0}/vicinity/vicinity.py +12 -8
  13. {vicinity-0.3.4 → vicinity-0.4.0}/vicinity.egg-info/PKG-INFO +1 -1
  14. {vicinity-0.3.4 → vicinity-0.4.0}/.github/workflows/ci.yaml +0 -0
  15. {vicinity-0.3.4 → vicinity-0.4.0}/.gitignore +0 -0
  16. {vicinity-0.3.4 → vicinity-0.4.0}/.pre-commit-config.yaml +0 -0
  17. {vicinity-0.3.4 → vicinity-0.4.0}/LICENSE +0 -0
  18. {vicinity-0.3.4 → vicinity-0.4.0}/Makefile +0 -0
  19. {vicinity-0.3.4 → vicinity-0.4.0}/README.md +0 -0
  20. {vicinity-0.3.4 → vicinity-0.4.0}/assets/images/vicinity_logo.png +0 -0
  21. {vicinity-0.3.4 → vicinity-0.4.0}/pyproject.toml +0 -0
  22. {vicinity-0.3.4 → vicinity-0.4.0}/setup.cfg +0 -0
  23. {vicinity-0.3.4 → vicinity-0.4.0}/tests/conftest.py +0 -0
  24. {vicinity-0.3.4 → vicinity-0.4.0}/tests/test_utils.py +0 -0
  25. {vicinity-0.3.4 → vicinity-0.4.0}/tests/test_vicinity.py +0 -0
  26. {vicinity-0.3.4 → vicinity-0.4.0}/vicinity/__init__.py +0 -0
  27. {vicinity-0.3.4 → vicinity-0.4.0}/vicinity/backends/__init__.py +0 -0
  28. {vicinity-0.3.4 → vicinity-0.4.0}/vicinity/datatypes.py +0 -0
  29. {vicinity-0.3.4 → vicinity-0.4.0}/vicinity/py.typed +0 -0
  30. {vicinity-0.3.4 → vicinity-0.4.0}/vicinity/utils.py +0 -0
  31. {vicinity-0.3.4 → vicinity-0.4.0}/vicinity.egg-info/SOURCES.txt +0 -0
  32. {vicinity-0.3.4 → vicinity-0.4.0}/vicinity.egg-info/dependency_links.txt +0 -0
  33. {vicinity-0.3.4 → vicinity-0.4.0}/vicinity.egg-info/requires.txt +0 -0
  34. {vicinity-0.3.4 → vicinity-0.4.0}/vicinity.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vicinity
3
- Version: 0.3.4
3
+ Version: 0.4.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
@@ -1087,7 +1087,7 @@ wheels = [
1087
1087
 
1088
1088
  [[package]]
1089
1089
  name = "vicinity"
1090
- version = "0.3.1"
1090
+ version = "0.3.4"
1091
1091
  source = { editable = "." }
1092
1092
  dependencies = [
1093
1093
  { name = "numpy" },
@@ -126,9 +126,10 @@ class AnnoyBackend(AbstractBackend[AnnoyArgs]):
126
126
  """Delete vectors from the backend."""
127
127
  raise NotImplementedError("Deletion is not supported in Annoy backend.")
128
128
 
129
- def threshold(self, vectors: npt.NDArray, threshold: float) -> list[npt.NDArray]:
129
+ def threshold(self, vectors: npt.NDArray, threshold: float, max_k: int) -> QueryResult:
130
130
  """Threshold the backend."""
131
- out: list[npt.NDArray] = []
132
- for x, y in self.query(vectors, 100):
133
- out.append(x[y < threshold])
131
+ out: QueryResult = []
132
+ for x, y in self.query(vectors, max_k):
133
+ mask = y < threshold
134
+ out.append((x[mask], y[mask]))
134
135
  return out
@@ -92,7 +92,7 @@ class AbstractBackend(ABC, Generic[ArgType]):
92
92
  raise NotImplementedError()
93
93
 
94
94
  @abstractmethod
95
- def threshold(self, vectors: npt.NDArray, threshold: float) -> list[npt.NDArray]:
95
+ def threshold(self, vectors: npt.NDArray, threshold: float, max_k: int) -> QueryResult:
96
96
  """Threshold the backend."""
97
97
  raise NotImplementedError()
98
98
 
@@ -150,22 +150,25 @@ class BasicBackend(BasicVectorStore, AbstractBackend[BasicArgs], ABC):
150
150
  self,
151
151
  vectors: npt.NDArray,
152
152
  threshold: float,
153
- ) -> list[npt.NDArray]:
153
+ max_k: int,
154
+ ) -> QueryResult:
154
155
  """
155
156
  Batched distance thresholding.
156
157
 
157
158
  :param vectors: The vectors to threshold.
158
159
  :param threshold: The threshold to use.
159
- :return: A list of lists of indices of vectors that are below the threshold
160
+ :param max_k: The maximum number of neighbors to consider.
161
+ :return: A list of tuples with the indices and distances.
160
162
  """
161
- out: list[npt.NDArray] = []
163
+ out: QueryResult = []
162
164
  for i in range(0, len(vectors), 1024):
163
165
  batch = vectors[i : i + 1024]
164
166
  distances = self._dist(batch)
165
167
  for dists in distances:
166
- indices = np.flatnonzero(dists <= threshold)
167
- sorted_indices = indices[np.argsort(dists[indices])]
168
- out.append(sorted_indices)
168
+ mask = dists <= threshold
169
+ indices = np.flatnonzero(mask)
170
+ filtered_distances = dists[mask]
171
+ out.append((indices, filtered_distances))
169
172
  return out
170
173
 
171
174
  def query(
@@ -164,9 +164,9 @@ class FaissBackend(AbstractBackend[FaissArgs]):
164
164
  """Delete vectors from the backend."""
165
165
  raise NotImplementedError("Deletion is not supported in FAISS backends.")
166
166
 
167
- def threshold(self, vectors: npt.NDArray, threshold: float) -> list[npt.NDArray]:
167
+ def threshold(self, vectors: npt.NDArray, threshold: float, max_k: int) -> QueryResult:
168
168
  """Query vectors within a distance threshold, using range_search if supported."""
169
- out: list[npt.NDArray] = []
169
+ out: QueryResult = []
170
170
  if self.arguments.metric == "cosine":
171
171
  vectors = normalize(vectors)
172
172
 
@@ -179,13 +179,15 @@ class FaissBackend(AbstractBackend[FaissArgs]):
179
179
  dist = D[start:end]
180
180
  if self.arguments.metric == "cosine":
181
181
  dist = 1 - dist
182
- out.append(idx[dist < threshold])
182
+ mask = dist < threshold
183
+ out.append((idx[mask], dist[mask]))
183
184
  else:
184
- distances, indices = self.index.search(vectors, 100)
185
+ distances, indices = self.index.search(vectors, max_k)
185
186
  for dist, idx in zip(distances, indices):
186
187
  if self.arguments.metric == "cosine":
187
188
  dist = 1 - dist
188
- out.append(idx[dist < threshold])
189
+ mask = dist < threshold
190
+ out.append((idx[mask], dist[mask]))
189
191
 
190
192
  return out
191
193
 
@@ -104,10 +104,11 @@ class HNSWBackend(AbstractBackend[HNSWArgs]):
104
104
  """Delete vectors from the backend."""
105
105
  raise NotImplementedError("Deletion is not supported in HNSW backend.")
106
106
 
107
- def threshold(self, vectors: npt.NDArray, threshold: float) -> list[npt.NDArray]:
107
+ def threshold(self, vectors: npt.NDArray, threshold: float, max_k: int) -> QueryResult:
108
108
  """Threshold the backend."""
109
- out: list[npt.NDArray] = []
110
- for x, y in self.query(vectors, 100):
111
- out.append(x[y < threshold])
109
+ out: QueryResult = []
110
+ for x, y in self.query(vectors, max_k):
111
+ mask = y < threshold
112
+ out.append((x[mask], y[mask]))
112
113
 
113
114
  return out
@@ -80,15 +80,15 @@ class PyNNDescentBackend(AbstractBackend[PyNNDescentArgs]):
80
80
  """Delete vectors from the backend."""
81
81
  raise NotImplementedError("Deletion is not supported in PyNNDescent backend.")
82
82
 
83
- def threshold(self, vectors: npt.NDArray, threshold: float) -> list[npt.NDArray]:
83
+ def threshold(self, vectors: npt.NDArray, threshold: float, max_k: int) -> QueryResult:
84
84
  """Find neighbors within a distance threshold."""
85
85
  normalized_vectors = normalize_or_copy(vectors)
86
- indices, distances = self.index.query(normalized_vectors, k=100)
87
- result = []
86
+ indices, distances = self.index.query(normalized_vectors, k=max_k)
87
+ out: QueryResult = []
88
88
  for idx, dist in zip(indices, distances):
89
- within_threshold = idx[dist < threshold]
90
- result.append(within_threshold)
91
- return result
89
+ mask = dist < threshold
90
+ out.append((idx[mask], dist[mask]))
91
+ return out
92
92
 
93
93
  def save(self, base_path: Path) -> None:
94
94
  """Save the vectors and configuration to a specified path."""
@@ -129,9 +129,12 @@ class UsearchBackend(AbstractBackend[UsearchArgs]):
129
129
  """Delete vectors from the index (not supported by Usearch)."""
130
130
  raise NotImplementedError("Dynamic deletion is not supported in Usearch.")
131
131
 
132
- def threshold(self, vectors: npt.NDArray, threshold: float) -> list[npt.NDArray]:
133
- """Threshold the backend and return filtered keys."""
134
- return [
135
- np.array(keys_row)[np.array(distances_row, dtype=np.float32) < threshold]
136
- for keys_row, distances_row in self.query(vectors, 100)
137
- ]
132
+ def threshold(self, vectors: npt.NDArray, threshold: float, max_k: int) -> QueryResult:
133
+ """Query vectors within a distance threshold and return keys and distances."""
134
+ out: QueryResult = []
135
+ for keys_row, distances_row in self.query(vectors, max_k):
136
+ keys_row = np.array(keys_row)
137
+ distances_row = np.array(distances_row, dtype=np.float32)
138
+ mask = distances_row < threshold
139
+ out.append((keys_row[mask], distances_row[mask]))
140
+ return out
@@ -94,11 +94,12 @@ class VoyagerBackend(AbstractBackend[VoyagerArgs]):
94
94
  """Delete vectors from the backend."""
95
95
  raise NotImplementedError("Deletion is not supported in Voyager backend.")
96
96
 
97
- def threshold(self, vectors: npt.NDArray, threshold: float) -> list[npt.NDArray]:
97
+ def threshold(self, vectors: npt.NDArray, threshold: float, max_k: int) -> QueryResult:
98
98
  """Threshold the backend."""
99
- out: list[npt.NDArray] = []
100
- for x, y in self.query(vectors, len(self)):
101
- out.append(x[y < threshold])
99
+ out: list[tuple[npt.NDArray, npt.NDArray]] = []
100
+ for x, y in self.query(vectors, max_k):
101
+ mask = y < threshold
102
+ out.append((x[mask], y[mask]))
102
103
 
103
104
  return out
104
105
 
@@ -1,2 +1,2 @@
1
- __version_triple__ = (0, 3, 4)
1
+ __version_triple__ = (0, 4, 0)
2
2
  __version__ = ".".join(map(str, __version_triple__))
@@ -14,7 +14,7 @@ from numpy import typing as npt
14
14
 
15
15
  from vicinity import Metric
16
16
  from vicinity.backends import AbstractBackend, BasicBackend, BasicVectorStore, get_backend_class
17
- from vicinity.datatypes import Backend, PathLike
17
+ from vicinity.datatypes import Backend, PathLike, QueryResult
18
18
 
19
19
  logger = logging.getLogger(__name__)
20
20
 
@@ -114,7 +114,7 @@ class Vicinity:
114
114
  self,
115
115
  vectors: npt.NDArray,
116
116
  k: int = 10,
117
- ) -> list[list[tuple[str, float]]]:
117
+ ) -> list[QueryResult]:
118
118
  """
119
119
  Find the nearest neighbors to some arbitrary vector.
120
120
 
@@ -140,22 +140,26 @@ class Vicinity:
140
140
  self,
141
141
  vectors: npt.NDArray,
142
142
  threshold: float = 0.5,
143
- ) -> list[list[str]]:
143
+ max_k: int = 100,
144
+ ) -> list[QueryResult]:
144
145
  """
145
- Find the nearest neighbors to some arbitrary vector with some threshold.
146
+ Find the nearest neighbors to some arbitrary vector with some threshold. Note: the output is not sorted.
146
147
 
147
148
  :param vectors: The vectors to find the most similar vectors to.
148
149
  :param threshold: The threshold to use.
150
+ :param max_k: The maximum number of neighbors to consider for the threshold query.
149
151
 
150
- :return: For each item in the input, all items above the threshold are returned.
152
+ :return: For each item in the input, the items above the threshold are returned in the form of
153
+ (NAME, SIMILARITY) tuples.
151
154
  """
152
- vectors = np.array(vectors)
155
+ vectors = np.asarray(vectors)
153
156
  if np.ndim(vectors) == 1:
154
157
  vectors = vectors[None, :]
155
158
 
156
159
  out = []
157
- for indexes in self.backend.threshold(vectors, threshold):
158
- out.append([self.items[idx] for idx in indexes])
160
+ for indices, distances in self.backend.threshold(vectors, threshold, max_k=max_k):
161
+ distances.clip(min=0, out=distances)
162
+ out.append([(self.items[idx], dist) for idx, dist in zip(indices, distances)])
159
163
 
160
164
  return out
161
165
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vicinity
3
- Version: 0.3.4
3
+ Version: 0.4.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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes