@@ -145,7 +145,7 @@ def spectral_embedding(
145
145
n_components = 8 ,
146
146
eigen_solver = None ,
147
147
random_state = None ,
148
- eigen_tol = 0.0 ,
148
+ eigen_tol = "auto" ,
149
149
norm_laplacian = True ,
150
150
drop_first = True ,
151
151
):
@@ -197,9 +197,22 @@ def spectral_embedding(
197
197
https://github.com/pyamg/pyamg/issues/139 for further
198
198
information.
199
199
200
- eigen_tol : float, default=0.0
201
- Stopping criterion for eigendecomposition of the Laplacian matrix
202
- when using arpack eigen_solver.
200
+ eigen_tol : float, default="auto"
201
+ Stopping criterion for eigendecomposition of the Laplacian matrix.
202
+ If `eigen_tol="auto"` then the passed tolerance will depend on the
203
+ `eigen_solver`:
204
+
205
+ - If `eigen_solver="arpack"`, then `eigen_tol=0.0`;
206
+ - If `eigen_solver="lobpcg"` or `eigen_solver="amg"`, then
207
+ `eigen_tol=None` which configures the underlying `lobpcg` solver to
208
+ automatically resolve the value according to their heuristics. See,
209
+ :func:`scipy.sparse.linalg.lobpcg` for details.
210
+
211
+ Note that when using `eigen_solver="amg"` values of `tol<1e-5` may lead
212
+ to convergence issues and should be avoided.
213
+
214
+ .. versionadded:: 1.2
215
+ Added 'auto' option.
203
216
204
217
norm_laplacian : bool, default=True
205
218
If True, then compute symmetric normalized Laplacian.
@@ -293,10 +306,11 @@ def spectral_embedding(
293
306
try :
294
307
# We are computing the opposite of the laplacian inplace so as
295
308
# to spare a memory allocation of a possibly very large array
309
+ tol = 0 if eigen_tol == "auto" else eigen_tol
296
310
laplacian *= - 1
297
311
v0 = _init_arpack_v0 (laplacian .shape [0 ], random_state )
298
312
_ , diffusion_map = eigsh (
299
- laplacian , k = n_components , sigma = 1.0 , which = "LM" , tol = eigen_tol , v0 = v0
313
+ laplacian , k = n_components , sigma = 1.0 , which = "LM" , tol = tol , v0 = v0
300
314
)
301
315
embedding = diffusion_map .T [n_components ::- 1 ]
302
316
if norm_laplacian :
@@ -338,7 +352,9 @@ def spectral_embedding(
338
352
X = random_state .standard_normal (size = (laplacian .shape [0 ], n_components + 1 ))
339
353
X [:, 0 ] = dd .ravel ()
340
354
X = X .astype (laplacian .dtype )
341
- _ , diffusion_map = lobpcg (laplacian , X , M = M , tol = 1.0e-5 , largest = False )
355
+
356
+ tol = None if eigen_tol == "auto" else eigen_tol
357
+ _ , diffusion_map = lobpcg (laplacian , X , M = M , tol = tol , largest = False )
342
358
embedding = diffusion_map .T
343
359
if norm_laplacian :
344
360
# recover u = D^-1/2 x from the eigenvector output x
@@ -371,8 +387,9 @@ def spectral_embedding(
371
387
)
372
388
X [:, 0 ] = dd .ravel ()
373
389
X = X .astype (laplacian .dtype )
390
+ tol = None if eigen_tol == "auto" else eigen_tol
374
391
_ , diffusion_map = lobpcg (
375
- laplacian , X , tol = 1e-5 , largest = False , maxiter = 2000
392
+ laplacian , X , tol = tol , largest = False , maxiter = 2000
376
393
)
377
394
embedding = diffusion_map .T [:n_components ]
378
395
if norm_laplacian :
@@ -444,6 +461,23 @@ class SpectralEmbedding(BaseEstimator):
444
461
to be installed. It can be faster on very large, sparse problems.
445
462
If None, then ``'arpack'`` is used.
446
463
464
+ eigen_tol : float, default="auto"
465
+ Stopping criterion for eigendecomposition of the Laplacian matrix.
466
+ If `eigen_tol="auto"` then the passed tolerance will depend on the
467
+ `eigen_solver`:
468
+
469
+ - If `eigen_solver="arpack"`, then `eigen_tol=0.0`;
470
+ - If `eigen_solver="lobpcg"` or `eigen_solver="amg"`, then
471
+ `eigen_tol=None` which configures the underlying `lobpcg` solver to
472
+ automatically resolve the value according to their heuristics. See,
473
+ :func:`scipy.sparse.linalg.lobpcg` for details.
474
+
475
+ Note that when using `eigen_solver="lobpcg"` or `eigen_solver="amg"`
476
+ values of `tol<1e-5` may lead to convergence issues and should be
477
+ avoided.
478
+
479
+ .. versionadded:: 1.2
480
+
447
481
n_neighbors : int, default=None
448
482
Number of nearest neighbors for nearest_neighbors graph building.
449
483
If None, n_neighbors will be set to max(n_samples/10, 1).
@@ -516,6 +550,7 @@ def __init__(
516
550
gamma = None ,
517
551
random_state = None ,
518
552
eigen_solver = None ,
553
+ eigen_tol = "auto" ,
519
554
n_neighbors = None ,
520
555
n_jobs = None ,
521
556
):
@@ -524,6 +559,7 @@ def __init__(
524
559
self .gamma = gamma
525
560
self .random_state = random_state
526
561
self .eigen_solver = eigen_solver
562
+ self .eigen_tol = eigen_tol
527
563
self .n_neighbors = n_neighbors
528
564
self .n_jobs = n_jobs
529
565
@@ -641,6 +677,7 @@ def fit(self, X, y=None):
641
677
affinity_matrix ,
642
678
n_components = self .n_components ,
643
679
eigen_solver = self .eigen_solver ,
680
+ eigen_tol = self .eigen_tol ,
644
681
random_state = random_state ,
645
682
)
646
683
return self
0 commit comments