import type { DataMatrix, AgglomerativeClusteringParams, BaseClustering } from './types'; import type { ClusterRepresentations } from './representations'; /** * Agglomerative (hierarchical) clustering using nearest-neighbor chain merges * with Lance–Williams distance updates. * * Builds the full reducible-linkage tree in O(n²) time for single, complete, * average, and Ward linkage, then cuts that tree by `n_clusters` or * `distance_threshold`. * * Distances are computed in float64 with the same definitions as * scikit-learn's `pairwise_distances`, and the Ward update is arranged to round * bit-identically to scipy. Ward, complete, and average linkage therefore * reproduce scikit-learn's partitions exactly, including the resolution of * exactly-tied merge distances on degenerate data such as integer grids or * duplicate points. * * Single linkage produces a correct single-linkage tree, but its resolution of * tied distances can differ from scikit-learn's: single linkage admits several * equally-valid merge orders, and scikit-learn, scipy, and the nearest-neighbor * chain each break exact ties differently. Partitions agree whenever the data * contains no exactly-tied distances (the usual case for continuous inputs). */ export declare class AgglomerativeClustering implements BaseClustering, ClusterRepresentations { readonly params: AgglomerativeClusteringParams; /** * 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; labels_: number[] | null; /** * Children recorded for each merge performed during agglomeration. * Shape: `(n_merges, 2)` where `n_merges = n_samples - n_clusters` (merging * stops once `n_clusters` clusters remain, so the full tree is not built), or * the number of merges strictly below `distance_threshold` when that * stopping criterion is used. Each row gives the global ids of the two merged * clusters (sklearn convention: original samples are `0..n-1`, each merge * creates id `n, n+1, ...`). Populated by `fit`. */ children_: number[][] | null; /** * Distance at which each merge in `children_` occurred. Aligned 1:1 with * `children_` (same length and order). */ distances_: number[] | null; n_leaves_: number | null; private static readonly VALID_LINKAGES; private static readonly VALID_METRICS; constructor(params: AgglomerativeClusteringParams); fit(_X: DataMatrix): Promise; fit_predict(_X: DataMatrix): Promise; /** * @param X Must be the data the model was fitted on (same row order as `labels_`). * @throws {Error} If called before `fit()`. */ compute_medoids(X: DataMatrix): Promise; private static validate_params; /** * Computes the full `n×n` pairwise distance matrix in float64, laid out * row-major (`i*n+j`) to match the NN-chain engine. * * **Symmetry**: for every `(i, j)` pair the same scalar `dist` is assigned * to both `D[i*n+j]` and `D[j*n+i]`, so `D[i,j] ≡ D[j,i]` exactly — * not approximately. `(D+Dᵀ)/2` symmetrisation is unnecessary here * because asymmetry cannot arise from this mirror pattern. * * **Zero diagonal**: `Float64Array` zero-initialises; the inner loop * iterates `j > i` and never writes `D[i*n+i]`. * * **float64 only**: `pairwise_distance_matrix` in `distance/pairwise_distance.ts` * operates on float32 TensorFlow tensors and applies Gram-matrix shortcuts * that require clamping and explicit symmetrisation. This method uses * direct scalar arithmetic at float64 precision, which the NN-chain * tie-resolution requires to reproduce sklearn's merge order exactly. * Delegating to the tensor path would silently degrade precision. * * Distances use the same definitions as scikit-learn's `pairwise_distances`, * so results are bit-identical to sklearn for the same coordinates. */ private static compute_distance_matrix; /** * Converts raw active-slot merge records into sklearn/scipy-style children * node ids (`0..n-1` leaves, `n..` internal nodes). */ private static build_children; private static validate_precomputed; }