import type { DataMatrix, SpectralClusteringParams, BaseClustering } from './types'; import * as tf from '../backend/adapter'; import { SparseMatrix } from '../graph/sparse'; import type { ClusterRepresentations } from './representations'; export interface LaplacianResult { laplacian: tf.Tensor2D; /** D^{1/2} — square root of the degree vector, matching scipy's dd from csgraph_laplacian. */ degrees?: tf.Tensor1D; /** D^{-1/2} — inverse square root of the degree vector, as returned by normalised_laplacian. */ sqrt_degrees?: tf.Tensor1D; } export interface EmbeddingResult { embedding: tf.Tensor2D; eigenvalues: tf.Tensor1D; raw_eigenvectors?: tf.Tensor2D; scaling_factors?: tf.Tensor1D; } export interface IntermediateSteps { affinity: tf.Tensor2D; laplacian: LaplacianResult; embedding: EmbeddingResult; labels: number[]; } export interface DebugInfo { affinity_stats?: { shape: number[]; nnz: number; min: number; max: number; mean: number; }; laplacian_spectrum?: number[]; embedding_stats?: { shape: number[]; unique_values_per_dim: number[]; scaling_factors?: number[]; }; clustering_metrics?: { inertia: number; iterations: number; }; } /** * Spectral clustering estimator. * * Clusters by embedding the data into the eigenspace of its graph Laplacian and * running k-means on that embedding. The pipeline is: * 1. Build the similarity graph (affinity matrix) — `'rbf'`, * `'nearest_neighbors'`, `'cosine'`, `'precomputed'`, or a user callable. * 2. Form the normalised Laplacian and take its smallest eigenvectors. * 3. Row-normalise the embedding and cluster it with k-means. * * Precomputed and callable affinities are validated (square, symmetric, * non-negative). The estimator is transductive: it exposes no `predict` and no * JSON serialization (representatives are available via {@link compute_medoids}). * `dispose()` releases cached tensors; repeated `fit` calls clean up * automatically. */ export declare class SpectralClustering implements BaseClustering, ClusterRepresentations { readonly params: SpectralClusteringParams; labels_: number[] | null; /** * Index of the representative sample (medoid) per cluster, populated by * {@link compute_medoids}. Position `c` holds cluster `c`'s medoid index, or * `-1` if that cluster has no assigned samples. */ medoid_indices_: Int32Array | null; affinity_matrix_: tf.Tensor2D | null; sparse_affinity_matrix_: SparseMatrix | null; private debug_info_; private capture_debug_info; /** * The estimator instance can still be reused after calling `dispose()` by * invoking `fit` again. */ dispose(): void; private static readonly VALID_AFFINITIES; constructor(params: SpectralClusteringParams); /** * Note: Row normalization to unit length is only applied when using * assign_labels='discretize', not for the default k-means approach. */ fit(_X: DataMatrix): Promise; /** * @throws {Error} If n_clusters exceeds n_samples or n_samples exceeds max_samples. */ fit_predict(X: DataMatrix): Promise; /** * SpectralClustering has no synthetic centroids, so medoids are its * `ClusterRepresentations` surface. * * @throws {Error} If called before `fit()`. */ compute_medoids(X: DataMatrix): Promise; get_debug_info(): DebugInfo | null; fit_with_intermediate_steps(X: DataMatrix): Promise; private static validate_params; static compute_affinity_matrix(X: tf.Tensor2D, params: SpectralClusteringParams): tf.Tensor2D; static default_neighbors(params: SpectralClusteringParams, n_samples: number): number; private compute_embedding_from_affinity; /** * Validates that the provided tensor is a proper affinity / similarity * matrix suitable for spectral clustering. * • Must be 2-D & **square** * • Must be **symmetric** (within tolerance) * • Must be **non-negative** (entries ≥ 0) */ static validate_affinity_matrix(A: tf.Tensor2D): void; }