import * as tf from '../backend/adapter'; import type { BaseClustering, DataMatrix, SOMParams, SOMState, SOMClusterOptions } from './types'; /** * Self-Organizing Map (SOM) implementation using TensorFlow.js. * * SOMs create a low-dimensional (typically 2D) discrete representation * of high-dimensional input space while preserving topological properties. */ export declare class SOM implements BaseClustering { readonly params: SOMParams; weights_: tf.Tensor3D | null; labels_: number[] | null; bmus_: tf.Tensor2D | null; private grid_distance_matrix_; private learning_rate_scheduler_; private radius_scheduler_; private total_samples_learned_; private last_batch_size_; private current_epoch_; private quantization_errors_; private static readonly DEFAULT_TOPOLOGY; private static readonly DEFAULT_NEIGHBORHOOD; private static readonly DEFAULT_NUM_EPOCHS; private static readonly DEFAULT_LEARNING_RATE; private static readonly DEFAULT_INITIALIZATION; private static readonly DEFAULT_TOL; private static readonly DEFAULT_MINI_BATCH_SIZE; constructor(params: SOMParams); private validate_and_complete_params; private validate_initial_weights_shape; private make_initial_weights; private initialize_schedulers; fit(X: DataMatrix): Promise; private fit_tensor; private train_epoch; private update_weights; private compute_final_labels; fit_predict(X: DataMatrix): Promise; predict(X: DataMatrix): Promise; /** * SOM neurons outnumber the desired clusters (grid_width * grid_height >> n_clusters), * so raw BMU indices are not useful as cluster assignments. Applies agglomerative * clustering on the trained weight vectors to group neurons into `n_clusters` * macro-clusters, then maps each data point's BMU to its macro-cluster label. * * @throws Error if the SOM has not been fitted yet. * @throws Error if n_clusters is not a positive integer or exceeds grid_width * grid_height. * * @example * ```typescript * const som = new SOM({ * grid_width: 5, * grid_height: 5, * num_epochs: 100, * random_state: 42, * }); * * const data = [[0, 0], [1, 1], [5, 5], [6, 6], [10, 10], [11, 11]]; * await som.fit(data); * * // Get 3 meaningful clusters from the 25-neuron grid * const labels = await som.cluster(3); * // labels: [0, 0, 1, 2, 2, ...] — one per data point * * // With custom linkage * const labels2 = await som.cluster(4, { linkage: 'average' }); * ``` */ cluster(n_clusters: number, options?: SOMClusterOptions): Promise; /** * On the first call (when no weights exist), the input dimensionality establishes * the expected feature count. Subsequent calls must match that dimension. * * @throws Error if online_mode is not enabled. * @throws Error if n_features does not match the feature dimensionality of existing weights. */ partial_fit(X: DataMatrix): Promise; /** * The array has shape `[grid_height][grid_width][n_features]`; each element * `weights[row][col]` is the codebook vector for that neuron. * * Snapshot (deep copy): mutating it won't affect the SOM, and {@link dispose} * won't invalidate it. * * @throws Error if the SOM has not been fitted yet. */ get_weights(): number[][][]; get_u_matrix(): tf.Tensor2D; quantization_error(): number; topographic_error(X?: DataMatrix): Promise; private are_neighbors; get_total_samples_learned(): number; save_state(): SOMState; load_state(state: SOMState): void; save_to_json(): Promise; load_from_json(json: string): Promise; enable_streaming_mode(batch_size?: number): void; process_stream(sample: DataMatrix, auto_train?: boolean): Promise; get_streaming_stats(): { total_samples: number; virtual_epoch: number; current_learning_rate: number; current_radius: number; latest_quantization_error: number; }; private shuffle_indices; /** * Releases all GPU/WebGL memory held by this SOM instance. * * After calling `dispose()`, the SOM instance must not be used for any * further operations (fit, predict, cluster, get_weights, get_u_matrix, etc.). * * Values previously returned by {@link get_weights} (plain `number[][][]` * arrays) remain valid after disposal. However, any `tf.Tensor` values * previously returned by {@link get_u_matrix} that have not yet been disposed * by the caller are unaffected — the caller still owns those tensors and * must dispose them separately. * * Calling `dispose()` multiple times is safe (idempotent). */ dispose(): void; }