@@ -73,8 +73,10 @@ def _brute_knn(
7373 X : cp_sparse .spmatrix | cp .ndarray ,
7474 Y : cp_sparse .spmatrix | cp .ndarray ,
7575 k : int ,
76+ * ,
7677 metric : _Metrics ,
7778 metric_kwds : Mapping ,
79+ algorithm_kwds : Mapping ,
7880) -> tuple [cp .ndarray , cp .ndarray ]:
7981 from cuml .neighbors import NearestNeighbors
8082
@@ -91,7 +93,13 @@ def _brute_knn(
9193
9294
9395def _cagra_knn (
94- X : cp .ndarray , Y : cp .ndarray , k : int , metric : _Metrics , metric_kwds : Mapping
96+ X : cp .ndarray ,
97+ Y : cp .ndarray ,
98+ k : int ,
99+ * ,
100+ metric : _Metrics ,
101+ metric_kwds : Mapping ,
102+ algorithm_kwds : Mapping ,
95103) -> tuple [cp .ndarray , cp .ndarray ]:
96104 if not _cuvs_switch ():
97105 try :
@@ -135,8 +143,20 @@ def _cagra_knn(
135143 return neighbors , distances
136144
137145
146+ def _compute_nlist (N ):
147+ base = math .sqrt (N )
148+ next_pow2 = 2 ** math .ceil (math .log2 (base ))
149+ return int (next_pow2 * 2 )
150+
151+
138152def _ivf_flat_knn (
139- X : cp .ndarray , Y : cp .ndarray , k : int , metric : _Metrics , metric_kwds : Mapping
153+ X : cp .ndarray ,
154+ Y : cp .ndarray ,
155+ k : int ,
156+ * ,
157+ metric : _Metrics ,
158+ metric_kwds : Mapping ,
159+ algorithm_kwds : Mapping ,
140160) -> tuple [cp .ndarray , cp .ndarray ]:
141161 if not _cuvs_switch ():
142162 from pylibraft .neighbors import ivf_flat
@@ -151,12 +171,16 @@ def _ivf_flat_knn(
151171 build_kwargs = {} # cuvs does not need handle/resources
152172 search_kwargs = {}
153173
154- n_lists = int (math .sqrt (X .shape [0 ]))
174+ # Extract n_lists and nprobes from algorithm_kwds, with defaults
175+ n_lists = algorithm_kwds .get ("n_lists" , _compute_nlist (X .shape [0 ]))
176+ n_probes = algorithm_kwds .get ("n_probes" , 20 )
177+ print (f"n_lists: { n_lists } , n_probes: { n_probes } " )
155178 index_params = ivf_flat .IndexParams (n_lists = n_lists , metric = metric )
156179 index = ivf_flat .build (index_params , X , ** build_kwargs )
157- distances , neighbors = ivf_flat .search (
158- ivf_flat .SearchParams (), index , Y , k , ** search_kwargs
159- )
180+
181+ # Create SearchParams with nprobes if provided
182+ search_params = ivf_flat .SearchParams (n_probes = n_probes )
183+ distances , neighbors = ivf_flat .search (search_params , index , Y , k , ** search_kwargs )
160184
161185 if resources is not None :
162186 resources .sync ()
@@ -168,7 +192,13 @@ def _ivf_flat_knn(
168192
169193
170194def _ivf_pq_knn (
171- X : cp .ndarray , Y : cp .ndarray , k : int , metric : _Metrics , metric_kwds : Mapping
195+ X : cp .ndarray ,
196+ Y : cp .ndarray ,
197+ k : int ,
198+ * ,
199+ metric : _Metrics ,
200+ metric_kwds : Mapping ,
201+ algorithm_kwds : Mapping ,
172202) -> tuple [cp .ndarray , cp .ndarray ]:
173203 if not _cuvs_switch ():
174204 from pylibraft .neighbors import ivf_pq
@@ -183,12 +213,16 @@ def _ivf_pq_knn(
183213 build_kwargs = {}
184214 search_kwargs = {}
185215
186- n_lists = int (math .sqrt (X .shape [0 ]))
216+ # Extract n_lists and nprobes from algorithm_kwds, with defaults
217+ n_lists = algorithm_kwds .get ("n_lists" , _compute_nlist (X .shape [0 ]))
218+ n_probes = algorithm_kwds .get ("n_probes" , 20 )
219+
187220 index_params = ivf_pq .IndexParams (n_lists = n_lists , metric = metric )
188221 index = ivf_pq .build (index_params , X , ** build_kwargs )
189- distances , neighbors = ivf_pq .search (
190- ivf_pq .SearchParams (), index , Y , k , ** search_kwargs
191- )
222+ print (f"n_lists: { n_lists } , n_probes: { n_probes } " )
223+ # Create SearchParams with nprobes if provided
224+ search_params = ivf_pq .SearchParams (n_probes = n_probes )
225+ distances , neighbors = ivf_pq .search (search_params , index , Y , k , ** search_kwargs )
192226 if resources is not None :
193227 resources .sync ()
194228
@@ -199,7 +233,13 @@ def _ivf_pq_knn(
199233
200234
201235def _nn_descent_knn (
202- X : cp .ndarray , Y : cp .ndarray , k : int , metric : _Metrics , metric_kwds : Mapping
236+ X : cp .ndarray ,
237+ Y : cp .ndarray ,
238+ k : int ,
239+ * ,
240+ metric : _Metrics ,
241+ metric_kwds : Mapping ,
242+ algorithm_kwds : Mapping ,
203243) -> tuple [cp .ndarray , cp .ndarray ]:
204244 from cuvs import __version__ as cuvs_version
205245
@@ -210,8 +250,13 @@ def _nn_descent_knn(
210250 )
211251 from cuvs .neighbors import nn_descent
212252
253+ # Extract intermediate_graph_degree from algorithm_kwds, with default
254+ intermediate_graph_degree = algorithm_kwds .get ("intermediate_graph_degree" , None )
255+
213256 idxparams = nn_descent .IndexParams (
214- graph_degree = k , metric = "sqeuclidean" if metric == "euclidean" else metric
257+ graph_degree = k ,
258+ intermediate_graph_degree = intermediate_graph_degree ,
259+ metric = "sqeuclidean" if metric == "euclidean" else metric ,
215260 )
216261 idx = nn_descent .build (
217262 idxparams ,
@@ -392,6 +437,7 @@ def neighbors(
392437 algorithm : _Algorithms = "brute" ,
393438 metric : _Metrics = "euclidean" ,
394439 metric_kwds : Mapping [str , Any ] = MappingProxyType ({}),
440+ algorithm_kwds : Mapping [str , Any ] = MappingProxyType ({}),
395441 key_added : str | None = None ,
396442 copy : bool = False ,
397443) -> AnnData | None :
@@ -437,6 +483,16 @@ def neighbors(
437483 A known metric's name or a callable that returns a distance.
438484 metric_kwds
439485 Options for the metric.
486+ algorithm_kwds
487+ Options for the algorithm. For 'ivfflat' and 'ivfpq' algorithms, the following
488+ parameters can be specified:
489+ * 'n_lists': Number of inverted lists for IVF indexing. Default is 2 * next_power_of_2(sqrt(n_samples)).
490+ * 'n_probes': Number of lists to probe during search. Default is 20. Higher values
491+ increase accuracy but reduce speed.
492+ For 'nn_descent' algorithm, the following parameters can be specified:
493+ * 'intermediate_graph_degree': The degree of the intermediate graph. Default is None.
494+ It is recommended to set it to `>= 1.5 * n_neighbors`.
495+
440496 key_added
441497 If not specified, the neighbors data is stored in .uns['neighbors'],
442498 distances and connectivities are stored in .obsp['distances'] and
@@ -484,6 +540,7 @@ def neighbors(
484540 k = n_neighbors ,
485541 metric = metric ,
486542 metric_kwds = metric_kwds ,
543+ algorithm_kwds = algorithm_kwds ,
487544 )
488545
489546 n_nonzero = n_obs * n_neighbors
@@ -516,6 +573,7 @@ def neighbors(
516573 random_state = random_state ,
517574 metric = metric ,
518575 ** ({"metric_kwds" : metric_kwds } if metric_kwds else {}),
576+ ** ({"algorithm_kwds" : algorithm_kwds } if algorithm_kwds else {}),
519577 ** ({"use_rep" : use_rep } if use_rep is not None else {}),
520578 ** ({"n_pcs" : n_pcs } if n_pcs is not None else {}),
521579 )
@@ -543,6 +601,7 @@ def bbknn(
543601 algorithm : _Algorithms_bbknn = "brute" ,
544602 metric : _Metrics = "euclidean" ,
545603 metric_kwds : Mapping [str , Any ] = MappingProxyType ({}),
604+ algorithm_kwds : Mapping [str , Any ] = MappingProxyType ({}),
546605 trim : int | None = None ,
547606 key_added : str | None = None ,
548607 copy : bool = False ,
@@ -588,6 +647,13 @@ def bbknn(
588647 A known metric's name or a callable that returns a distance.
589648 metric_kwds
590649 Options for the metric.
650+ algorithm_kwds
651+ Options for the algorithm. For 'ivfflat' and 'ivfpq' algorithms, the following
652+ parameters can be specified:
653+
654+ * 'n_lists': Number of inverted lists for IVF indexing. Default is 2 * next_power_of_2(sqrt(n_samples)).
655+ * 'nprobes': Number of lists to probe during search. Default is 1. Higher values
656+ increase accuracy but reduce speed.
591657 trim
592658 Trim the neighbours of each cell to these many top connectivities.
593659 May help with population independence and improve the tidiness of clustering.
@@ -660,6 +726,7 @@ def bbknn(
660726 k = neighbors_within_batch ,
661727 metric = metric ,
662728 metric_kwds = metric_kwds ,
729+ algorithm_kwds = algorithm_kwds ,
663730 )
664731
665732 col_range = cp .arange (
@@ -705,6 +772,7 @@ def bbknn(
705772 metric = metric ,
706773 trim = trim ,
707774 ** ({"metric_kwds" : metric_kwds } if metric_kwds else {}),
775+ ** ({"algorithm_kwds" : algorithm_kwds } if algorithm_kwds else {}),
708776 ** ({"use_rep" : use_rep } if use_rep is not None else {}),
709777 ** ({"n_pcs" : n_pcs } if n_pcs is not None else {}),
710778 )
0 commit comments