### Test macro_clustering_finds_groups setup Source: https://docs.rs/clump/latest/src/clump/cluster/denstream.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Starts a test for macro clustering with a DenStream configured with macro_epsilon 3.0 and a long pruning period. ```rust #[test] fn macro_clustering_finds_groups() { let mut ds = DenStream::new(1.0, 2) .with_beta(0.2) .with_lambda(0.0001) .with_mu(1.0) .with_macro_epsilon(3.0) .with_pruning_period(10_000); ``` -------------------------------- ### Example for Result::is_ok Source: https://docs.rs/clump/latest/clump/error/type.Result.html?search=std%3A%3Avec Example demonstrating `is_ok()` which returns `true` if the result is `Ok`. ```rust let x: Result = Ok(-3); assert_eq!(x.is_ok(), true); let x: Result = Err("Some error message"); assert_eq!(x.is_ok(), false); ``` -------------------------------- ### Property-based test module setup and arbitrary data strategy Source: https://docs.rs/clump/latest/src/clump/cluster/kmeans.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Starts the proptest module, defining an arbitrary data strategy that generates vectors of vectors of f32 with values in [-100, 100] and a fixed dimension. ```rust #[cfg(test)] mod proptests { use super::*; use proptest::prelude::*; fn arb_data(max_n: usize, d: usize) -> impl Strategy>> { proptest::collection::vec( proptest::collection::vec(-100.0f32..100.0, d..=d), 3..=max_n, ) } proptest! { /// All labels must be in [0, k). #[test] ``` -------------------------------- ### Example for Result::is_ok_and Source: https://docs.rs/clump/latest/clump/error/type.Result.html?search=std%3A%3Avec Example demonstrating `is_ok_and()` which returns `true` if the result is `Ok` and the value matches a predicate. ```rust let x: Result = Ok(2); assert_eq!(x.is_ok_and(|x| x > 1), true); let x: Result = Ok(0); assert_eq!(x.is_ok_and(|x| x > 1), false); let x: Result = Err("hey"); assert_eq!(x.is_ok_and(|x| x > 1), false); let x: Result = Ok("ownership".to_string()); assert_eq!(x.as_ref().is_ok_and(|x| x.len() > 1), true); println!("still alive {:?}", x); ``` -------------------------------- ### iter example Source: https://docs.rs/clump/latest/clump/error/type.Result.html?search=std%3A%3Avec Shows how to use iter to get an iterator over the possibly contained value, yielding one value if Ok and none if Err. ```rust let x: Result = Ok(7); assert_eq!(x.iter().next(), Some(&7)); let x: Result = Err("nothing!"); assert_eq!(x.iter().next(), None); ``` -------------------------------- ### COP-Kmeans usage example with constraints Source: https://docs.rs/clump/latest/clump/cluster/constrained/struct.CopKmeans.html?search=u32+-%3E+bool Example showing how to create a CopKmeans clusterer, define must-link and cannot-link constraints, and fit the model. The example asserts that points 0 and 1 are in the same cluster and points 0 and 2 are in different clusters. ```rust use clump::cluster::constrained::{CopKmeans, Constraint}; let data = vec![ vec![0.0f32, 0.0], vec![0.1, 0.1], vec![10.0, 10.0], vec![10.1, 10.1], ]; // Force points 0 and 1 together, points 0 and 2 apart. let constraints = vec![ Constraint::MustLink(0, 1), Constraint::CannotLink(0, 2), ]; let labels = CopKmeans::new(2) .with_seed(42) .fit_predict_constrained(&data, &constraints) .unwrap(); assert_eq!(labels[0], labels[1]); assert_ne!(labels[0], labels[2]); ``` -------------------------------- ### MiniBatchKmeans usage example Source: https://docs.rs/clump/latest/clump/cluster/streaming/struct.MiniBatchKmeans.html?search=u32+-%3E+bool Example showing creation, seeding, batch updates, point updates, and cluster count retrieval. ```rust use clump::MiniBatchKmeans; let mut mbk = MiniBatchKmeans::new(2).with_seed(42); // First batch seeds centroids via k-means++ let batch1 = vec![ vec![0.0f32, 0.0], vec![0.1, 0.1], vec![10.0, 10.0], vec![10.1, 10.1], ]; let labels = mbk.update_batch(&batch1).unwrap(); assert_eq!(labels.len(), 4); // Subsequent updates refine centroids let label = mbk.update(&[0.05, 0.05]).unwrap(); assert_eq!(mbk.n_clusters(), 2); ``` -------------------------------- ### K-means fit: validation, initialization, and buffer setup Source: https://docs.rs/clump/latest/src/clump/cluster/kmeans.rs.html Validates k, dimension, cluster count, and uniform dimensionality; normalizes tolerance by data variance; initializes RNG, centroids (warm-start or k-means++), and working buffers including Hamerly bounds. Precomputes squared norms for expanded-form assignment, optionally sets up Metal GPU assigner (feature gpu, macOS) and BLAS GEMM (feature blas) for large problems, then starts the iteration loop. ```rust 273 return Err(Error::InvalidParameter { 274 name: "k", 275 message: "must be at least 1", 276 }); 277 } 278 279 let n = data.n(); 280 let d = data.d(); 281 if d == 0 { 282 return Err(Error::InvalidParameter { 283 name: "dimension", 284 message: "must be at least 1", 285 }); 286 } 287 288 if self.k > n { 289 return Err(Error::InvalidClusterCount { 290 requested: self.k, 291 n_items: n, 292 }); 293 } 294 295 // Validate uniform dimensionality. 296 for i in 0..n { 297 if data.row(i).len() != d { 298 return Err(Error::DimensionMismatch { 299 expected: d, 300 found: data.row(i).len(), 301 }); 302 } 303 } 304 305 util::validate_finite(data)?; 306 307 // Normalize tolerance by data variance so it scales with data magnitude. 308 // Without this, the raw tol is compared against the sum of k*d squared 309 // centroid shifts, which becomes meaninglessly tight for high-dimensional 310 // or large-scale data. (Matches scikit-learn's _tolerance approach.) 311 let effective_tol = (self.tol * util::mean_variance(data) * self.k as f64) as f32; 312 313 // Initialize RNG. 314 let mut rng = match self.seed { 315 Some(s) => StdRng::seed_from_u64(s), 316 None => StdRng::from_os_rng(), 317 }; 318 319 // Initialize centroids: use provided centroids (warm-start) or k-means++. 320 let mut centroids = if let Some(ref init) = self.init_centroids { 321 assert_eq!( 322 init.len(), 323 self.k, 324 "init_centroids length ({}) must equal k ({})", 325 init.len(), 326 self.k 327 ); 328 init.clone() 329 } else { 330 util::kmeanspp_init(data, self.k, &self.metric, self.seeding_alpha, &mut rng) 331 }; 332 let mut labels = vec![0usize; n]; 333 334 // Pre-allocate working buffers outside the iteration loop to avoid 335 // per-iteration allocation overhead (AMD ROCm pattern). 336 let mut new_centroids = vec![vec![0.0f32; d]; self.k]; 337 let mut counts = vec![0usize; self.k]; 338 // Hamerly bounds: per-point upper (dist to assigned centroid) and // lower (dist to second-nearest centroid). When upper <= lower, // the assignment cannot change and we skip distance computation. // O(n) extra memory (Hamerly, SDM 2010). let mut upper_bounds = vec![f32::MAX; n]; let mut lower_bounds = vec![0.0f32; n]; let mut centroid_shifts = vec![0.0f32; self.k]; let mut sums_f64 = vec![vec![0.0f64; d]; self.k]; let mut flat_buf: Vec = Vec::with_capacity(self.k * d); let mut inertia_trace: Vec = Vec::with_capacity(self.max_iter); // Precompute squared norms for expanded-form first-iteration assignment. // Only valid for SquaredEuclidean (||x-c||^2 = ||x||^2 + ||c||^2 - 2*x.c). let use_expanded = self.metric.supports_expanded_form(); let data_norms: Vec = if use_expanded { util::squared_norms(data) } else { Vec::new() }; // GPU acceleration: initialize Metal compute pipeline for assignment // when using SquaredEuclidean and problem is large enough to amortize // GPU setup overhead. Buffers for data, labels, and params are allocated // once here and reused across iterations. #[cfg(all(feature = "gpu", target_os = "macos"))] let gpu_assigner = if self.metric.supports_expanded_form() && n * self.k >= 500_000 { let data_flat = super::gpu::flatten(data); super::gpu::GpuAssigner::new(&data_flat, n, self.k, d) } else { None }; // BLAS GEMM for first-iteration assignment (when blas feature enabled). // matrixmultiply's optimized SGEMM beats per-point distance loops // for large n*k due to micro-kernel SIMD and cache optimization. #[cfg(feature = "blas")] let use_blas = n * self.k >= 100_000 && self.metric.supports_expanded_form(); #[cfg(feature = "blas")] let blas_data = if use_blas { let fd = super::flat::FlatMatrix::from_data(data); let xn = fd.row_norms_sq(); Some((fd, xn)) } else { None }; let mut iters = 0usize; for iter in 0..self.max_iter { iters = iter + 1; // Zero the accumulators for this iteration. ``` -------------------------------- ### expect example Source: https://docs.rs/clump/latest/clump/error/type.Result.html?search=std%3A%3Avec Shows how to use expect to return the contained Ok value, panicking with a message if the value is Err. The first example demonstrates a panic, and the second shows a recommended message style for environment variables. ```rust let x: Result = Err("emergency failure"); x.expect("Testing expect"); // panics with `Testing expect: emergency failure` ``` ```rust let path = std::env::var("IMPORTANT_PATH") .expect("env variable `IMPORTANT_PATH` should be set by `wrapper_script.sh`"); ``` -------------------------------- ### Example for Result::copied on &mut T Source: https://docs.rs/clump/latest/clump/error/type.Result.html?search=std%3A%3Avec Example demonstrating `copied()` on `Result<&mut T, E>` to copy the `Ok` value. ```rust let mut val = 12; let x: Result<&mut i32, i32> = Ok(&mut val); assert_eq!(x, Ok(&mut 12)); let copied = x.copied(); assert_eq!(copied, Ok(12)); ``` -------------------------------- ### Example for Result::flatten Source: https://docs.rs/clump/latest/clump/error/type.Result.html?search=std%3A%3Avec Example demonstrating `flatten()` which converts `Result, E>` to `Result`. ```rust let x: Result, u32> = Ok(Ok("hello")); assert_eq!(Ok("hello"), x.flatten()); let x: Result, u32> = Ok(Err(6)); assert_eq!(Err(6), x.flatten()); let x: Result, u32> = Err(6); assert_eq!(Err(6), x.flatten()); ``` -------------------------------- ### Example for Result::transpose Source: https://docs.rs/clump/latest/clump/error/type.Result.html?search=std%3A%3Avec Example demonstrating `transpose()` which maps `Result, E>` to `Option>`. ```rust #[derive(Debug, Eq, PartialEq)] struct SomeErr; let x: Result, SomeErr> = Ok(Some(5)); let y: Option> = Some(Ok(5)); assert_eq!(x.transpose(), y); ``` -------------------------------- ### Example for Result::copied on &T Source: https://docs.rs/clump/latest/clump/error/type.Result.html?search=std%3A%3Avec Example demonstrating `copied()` on `Result<&T, E>` to copy the `Ok` value. ```rust let val = 12; let x: Result<&i32, i32> = Ok(&val); assert_eq!(x, Ok(&12)); let copied = x.copied(); assert_eq!(copied, Ok(12)); ``` -------------------------------- ### Example for Result::cloned on &T Source: https://docs.rs/clump/latest/clump/error/type.Result.html?search=std%3A%3Avec Example demonstrating `cloned()` on `Result<&T, E>` to clone the `Ok` value. ```rust let val = 12; let x: Result<&i32, i32> = Ok(&val); assert_eq!(x, Ok(&12)); let cloned = x.cloned(); assert_eq!(cloned, Ok(12)); ``` -------------------------------- ### and_then example function Source: https://docs.rs/clump/latest/clump/error/type.Result.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Example function for and_then, showing control flow based on Result values. ```rust fn sq_then_to_string(x: u32) -> Result { x.checked_mul(x).map(|sq| sq.to_string()).ok_or("overflowed") } ``` -------------------------------- ### Example for Result::cloned on &mut T Source: https://docs.rs/clump/latest/clump/error/type.Result.html?search=std%3A%3Avec Example demonstrating `cloned()` on `Result<&mut T, E>` to clone the `Ok` value. ```rust let mut val = 12; let x: Result<&mut i32, i32> = Ok(&mut val); assert_eq!(x, Ok(&mut 12)); let cloned = x.cloned(); assert_eq!(cloned, Ok(12)); ``` -------------------------------- ### as_mut example Source: https://docs.rs/clump/latest/clump/error/type.Result.html?search=std%3A%3Avec Demonstrates using Result::as_mut to get mutable references to the contained values, allowing mutation of the Ok or Err value. ```rust fn mutate(r: &mut Result) { match r.as_mut() { Ok(v) => *v = 42, Err(e) => *e = 0, } } let mut x: Result = Ok(2); mutate(&mut x); assert_eq!(x.unwrap(), 42); let mut x: Result = Err(13); mutate(&mut x); assert_eq!(x.unwrap_err(), 0); ``` -------------------------------- ### Start main iteration loop Source: https://docs.rs/clump/latest/src/clump/cluster/kmeans.rs.html?search= Starts the main iteration loop, tracking the iteration count and zeroing accumulators at the beginning of each iteration. ```rust let mut iters = 0usize; for iter in 0..self.max_iter { iters = iter + 1; // Zero the accumulators for this iteration. ``` -------------------------------- ### flat_to_vecs usage example Source: https://docs.rs/clump/latest/clump/cluster/adapt/fn.flat_to_vecs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Example usage of flat_to_vecs, converting a flat vector of six f32 values into three vectors of dimension two. The example asserts the resulting length and the content of the second vector. ```rust use clump::cluster::adapt::flat_to_vecs; let flat = vec![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0]; let vecs = flat_to_vecs(&flat, 3, 2); assert_eq!(vecs.len(), 3); assert_eq!(vecs[1], vec![3.0, 4.0]); ``` -------------------------------- ### HDBSCAN clustering example with Hdbscan::new() Source: https://docs.rs/clump/latest/clump/cluster/struct.Hdbscan.html Example usage of Hdbscan to cluster two well-separated clusters. Uses with_min_samples(3) and with_min_cluster_size(5), then fit_predict. Asserts 30 labels and at least 2 non-noise clusters. ```rust use clump::{Hdbscan, NOISE}; // Two tight clusters, well-separated let mut data: Vec> = (0..15).map(|i| { vec![(i % 3) as f32 * 0.1, (i / 3) as f32 * 0.1] }).collect(); data.extend((0..15).map(|i| { vec![50.0 + (i % 3) as f32 * 0.1, 50.0 + (i / 3) as f32 * 0.1] })); let labels = Hdbscan::new() .with_min_samples(3) .with_min_cluster_size(5) .fit_predict(&data) .unwrap(); assert_eq!(labels.len(), 30); // At least 2 distinct non-noise clusters found let clusters: std::collections::HashSet<_> = labels.iter().copied().filter(|&l| l != NOISE).collect(); assert!(clusters.len() >= 2); ``` -------------------------------- ### map example Source: https://docs.rs/clump/latest/clump/error/type.Result.html?search=std%3A%3Avec Shows how to use Result::map to apply a function to the Ok value, leaving Err untouched. The example parses each line of a string and prints the doubled number. ```rust let line = "1\n2\n3\n4\n"; for num in line.lines() { match num.parse::().map(|i| i * 2) { Ok(n) => println!("{n}"), Err(..) => {} } } ``` -------------------------------- ### inspect example Source: https://docs.rs/clump/latest/clump/error/type.Result.html?search=std%3A%3Avec Shows how to use inspect to call a function with a reference to the contained value if Ok, returning the original result. The example parses a string to u8, inspects the value, and then maps it to its cube. ```rust let x: u8 = "4" .parse::() .inspect(|x| println!("original: {x}")) .map(|x| x.pow(3)) .expect("literal `4` should parse as a `u8`"); ``` -------------------------------- ### as_deref_mut example Source: https://docs.rs/clump/latest/clump/error/type.Result.html?search=std%3A%3Avec Shows how to use as_deref_mut to convert a Result to Result<&mut ::Target, &mut E>, coercing the Ok variant via DerefMut. The example demonstrates with String and u32 types, including mutation. ```rust let mut s = "HELLO".to_string(); let mut x: Result = Ok("hello".to_string()); let y: Result<&mut str, &mut u32> = Ok(&mut s); assert_eq!(x.as_deref_mut().map(|x| { x.make_ascii_uppercase(); x }), y); let mut i = 42; let mut x: Result = Err(42); let y: Result<&mut str, &mut u32> = Err(&mut i); assert_eq!(x.as_deref_mut().map(|x| { x.make_ascii_uppercase(); x }), y); ``` -------------------------------- ### Kmeans fit example Source: https://docs.rs/clump/latest/clump/cluster/struct.Kmeans.html Fits k-means and returns a KmeansFit containing centroids, labels, and iteration count. The example asserts the expected lengths and that iterations occurred. ```rust use clump::Kmeans; let data = vec![vec![0.0f32, 0.0], vec![0.1, 0.1], vec![5.0, 5.0], vec![5.1, 5.1]]; let fit = Kmeans::new(2).with_seed(42).fit(&data).unwrap(); assert_eq!(fit.centroids.len(), 2); assert_eq!(fit.labels.len(), 4); assert!(fit.iters > 0); ``` -------------------------------- ### as_deref example Source: https://docs.rs/clump/latest/clump/error/type.Result.html?search=std%3A%3Avec Shows how to use as_deref to convert a Result to Result<&::Target, &E>, coercing the Ok variant via Deref. The example demonstrates with String and u32 types. ```rust let x: Result = Ok("hello".to_string()); let y: Result<&str, &u32> = Ok("hello"); assert_eq!(x.as_deref(), y); let x: Result = Err(42); let y: Result<&str, &u32> = Err(&42); assert_eq!(x.as_deref(), y); ``` -------------------------------- ### DenStream usage example Source: https://docs.rs/clump/latest/clump/cluster/denstream/struct.DenStream.html Shows how to create a DenStream with epsilon 1.0 and min_pts 3, configure beta, lambda, and mu, feed points from two clusters, and assert that at least two clusters are found. ```rust use clump::DenStream; let mut ds = DenStream::new(1.0, 3) .with_beta(0.5) .with_lambda(0.01) .with_mu(1.0); // Feed points from two clusters. for i in 0..20 { let offset = i as f32 * 0.1; ds.update(&[offset, offset]).unwrap(); } for i in 0..20 { let offset = 50.0 + i as f32 * 0.1; ds.update(&[offset, offset]).unwrap(); } assert!(ds.n_clusters() >= 2); ``` -------------------------------- ### iter_mut example Source: https://docs.rs/clump/latest/clump/error/type.Result.html?search=std%3A%3Avec Shows how to use iter_mut to get a mutable iterator over the possibly contained value, allowing modification of the Ok value. ```rust let mut x: Result = Ok(7); match x.iter_mut().next() { Some(v) => *v = 40, None => {}, } assert_eq!(x, Ok(40)); let mut x: Result = Err("nothing!"); assert_eq!(x.iter_mut().next(), None); ``` -------------------------------- ### Usage example: K-means, DBSCAN, and EVoC clustering Source: https://docs.rs/clump/latest/clump/cluster/index.html Shows how to use `Kmeans`, `Dbscan`, and `EVoC` from `clump::cluster` on a small dataset. The example creates a 4x2 data matrix, fits K-means with 2 clusters, DBSCAN with epsilon 0.5 and min points 2, and EVoC with custom parameters. It asserts label lengths and relationships to verify clustering behavior. ```rust use clump::cluster::{Dbscan, EVoC, EVoCParams, Kmeans}; let data = vec![ vec![0.0, 0.0], vec![0.1, 0.1], vec![10.0, 10.0], vec![10.1, 10.1], ]; // Hard clustering with K-means let labels = Kmeans::new(2).fit_predict(&data).unwrap(); assert_eq!(labels[0], labels[1]); // First two together assert_ne!(labels[0], labels[2]); // Separate from last two // Density-based clustering with DBSCAN let labels = Dbscan::new(0.5, 2).fit_predict(&data).unwrap(); assert_eq!(labels.len(), data.len()); // Hierarchical clustering with EVōC (noise as `None`) let mut evoc = EVoC::new(EVoCParams { intermediate_dim: 1, min_cluster_size: 2, seed: Some(42), ..Default::default() }); let labels = evoc.fit_predict(&data).unwrap(); assert_eq!(labels.len(), data.len()); ``` -------------------------------- ### and_then: chaining fallible operations Source: https://docs.rs/clump/latest/clump/error/type.Result.html?search=std%3A%3Avec Shows how to chain operations that may return Err. The first example uses a helper function sq_then_to_string (not shown) to convert a number to a string or return an error on overflow. The second example uses metadata().and_then() to get file modification times, handling errors with ErrorKind::NotFound. ```rust assert_eq!(Ok(2).and_then(sq_then_to_string), Ok(4.to_string())); assert_eq!(Ok(1_000_000).and_then(sq_then_to_string), Err("overflowed")); assert_eq!(Err("not a number").and_then(sq_then_to_string), Err("not a number")); ``` ```rust use std::{io::ErrorKind, path::Path}; // Note: on Windows "/" maps to "C:\" let root_modified_time = Path::new("/").metadata().and_then(|md| md.modified()); assert!(root_modified_time.is_ok()); let should_fail = Path::new("/bad/path").metadata().and_then(|md| md.modified()); assert!(should_fail.is_err()); assert_eq!(should_fail.unwrap_err().kind(), ErrorKind::NotFound); ``` -------------------------------- ### Example for Result::is_err Source: https://docs.rs/clump/latest/clump/error/type.Result.html?search=std%3A%3Avec Example demonstrating `is_err()` which returns `true` if the result is `Err`. ```rust let x: Result = Ok(-3); assert_eq!(x.is_err(), false); let x: Result = Err("Some error message"); assert_eq!(x.is_err(), true); ``` -------------------------------- ### ok example Source: https://docs.rs/clump/latest/clump/error/type.Result.html?search=std%3A%3Avec Demonstrates converting a Result to an Option using Result::ok, which returns Some(value) for Ok and None for Err. ```rust let x: Result = Ok(2); assert_eq!(x.ok(), Some(2)); let x: Result = Err("Nothing here"); assert_eq!(x.ok(), None); ``` -------------------------------- ### CompositeDistance usage example Source: https://docs.rs/clump/latest/clump/cluster/distance/struct.CompositeDistance.html Example showing how to create a CompositeDistance with SquaredEuclidean and Euclidean metrics, and compute a distance. The result is 0.5 * 25.0 + 0.5 * 5.0 = 15.0. The example includes an attribute to allow deprecated code. ```rust #![allow(deprecated)] use clump::cluster::distance::{CompositeDistance, SquaredEuclidean, Euclidean, DistanceMetric}; let metric = CompositeDistance::new(SquaredEuclidean, Euclidean, 0.5, 0.5); let d = metric.distance(&[0.0, 0.0], &[3.0, 4.0]); // 0.5 * 25.0 + 0.5 * 5.0 = 15.0 assert!((d - 15.0).abs() < 1e-6); ``` -------------------------------- ### CorrelationClustering::edges_from_distances Source: https://docs.rs/clump/latest/clump/cluster/correlation/struct.CorrelationClustering.html?search=std%3A%3Avec Creates signed edges from a distance matrix and a threshold. Pairs closer than the threshold get positive weight, pairs farther get negative weight, and pairs at exactly the threshold get weight 0. ```APIDOC ## CorrelationClustering::edges_from_distances ### Description Creates signed edges from a distance matrix and a threshold. Pairs closer than the threshold get positive weight, pairs farther get negative weight, and pairs at exactly the threshold get weight 0. ### Method edges_from_distances ### Signature ```rust pub fn edges_from_distances( data: &[Vec], metric: &D, threshold: f32, ) -> Vec ``` ### Parameters - **data** (&[Vec]) - Required - The distance matrix or data points. - **metric** (&D) - Required - The distance metric used to compute distances. - **threshold** (f32) - Required - The threshold for determining positive or negative edges. ### Returns - **Vec** - A vector of signed edges. ### Example ```rust // Example usage with a distance metric let data = vec![vec![0.0, 1.0], vec![1.0, 0.0]]; let metric = EuclideanDistance; let edges = CorrelationClustering::edges_from_distances(&data, &metric, 0.5); ``` ``` -------------------------------- ### as_ref example Source: https://docs.rs/clump/latest/clump/error/type.Result.html?search=std%3A%3Avec Shows how to use Result::as_ref to convert a Result to a Result of references, leaving the original intact. ```rust let x: Result = Ok(2); assert_eq!(x.as_ref(), Ok(&2)); let x: Result = Err("Error"); assert_eq!(x.as_ref(), Err(&"Error")); ``` -------------------------------- ### CorrelationClustering::edges_from_distances Source: https://docs.rs/clump/latest/clump/cluster/correlation/struct.CorrelationClustering.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Creates signed edges from a distance matrix and a threshold. Pairs closer than the threshold get positive weight, pairs farther get negative weight, and pairs at exactly the threshold get weight 0. ```APIDOC ## CorrelationClustering::edges_from_distances ### Description Create signed edges from a distance matrix and a threshold. Pairs closer than `threshold` get positive weight `= threshold - distance`. Pairs farther than `threshold` get negative weight `= threshold - distance` (which is negative). Pairs at exactly `threshold` get weight 0 and are included but contribute no cost. ### Method `pub fn edges_from_distances(data: &[Vec], metric: &D, threshold: f32) -> Vec` ### Parameters - **data** (&[Vec]) - Required - The distance matrix (or feature vectors) used to compute distances. - **metric** (&D) - Required - The distance metric to use. - **threshold** (f32) - Required - The threshold for determining positive/negative edges. ### Returns A `Vec` containing the signed edges. ### Example ```rust // Example usage with a distance metric let data = vec![vec![0.0, 1.0], vec![1.0, 0.0]]; let metric = EuclideanDistance; let edges = CorrelationClustering::edges_from_distances(&data, &metric, 0.5); ``` ``` -------------------------------- ### expect example: panic on Err Source: https://docs.rs/clump/latest/clump/error/type.Result.html?search= Shows how to use expect to return the contained Ok value, panicking with a message if the result is Err. ```rust let x: Result = Err("emergency failure"); x.expect("Testing expect"); // panics with `Testing expect: emergency failure` ``` -------------------------------- ### CorrelationClustering::edges_from_distances Source: https://docs.rs/clump/latest/clump/cluster/correlation/struct.CorrelationClustering.html?search= Creates signed edges from a distance matrix and a threshold. Pairs closer than the threshold get positive weight, farther pairs get negative weight, and pairs at exactly the threshold get weight 0. ```APIDOC ## CorrelationClustering::edges_from_distances ### Description Creates signed edges from a distance matrix and a threshold. Pairs closer than `threshold` get positive weight `= threshold - distance`. Pairs farther than `threshold` get negative weight `= threshold - distance` (which is negative). Pairs at exactly `threshold` get weight 0 and are included but contribute no cost. ### Method `pub fn edges_from_distances(data: &[Vec], metric: &D, threshold: f32) -> Vec` ### Parameters - **data** (&[Vec]) - Required - The distance matrix. - **metric** (&D) - Required - The distance metric used to compute distances. - **threshold** (f32) - Required - The threshold for determining positive/negative edges. ### Returns - `Vec` - A vector of signed edges. ### Example ```rust // Assuming a DistanceMetric implementation exists let edges = CorrelationClustering::edges_from_distances(&data, &metric, 0.5); ``` ``` -------------------------------- ### Fit K-means and inspect centroids, labels, and iterations Source: https://docs.rs/clump/latest/src/clump/cluster/kmeans.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Demonstrates fitting a Kmeans clusterer with 2 clusters on a small dataset, then verifying the number of centroids, labels, and iteration count. ```rust use clump::Kmeans; let data = vec![vec![0.0f32, 0.0], vec![0.1, 0.1], vec![5.0, 5.0], vec![5.1, 5.1]]; let fit = Kmeans::new(2).with_seed(42).fit(&data).unwrap(); assert_eq!(fit.centroids.len(), 2); assert_eq!(fit.labels.len(), 4); assert!(fit.iters > 0); ``` -------------------------------- ### KMeans fit() method: validation and initialization Source: https://docs.rs/clump/latest/src/clump/cluster/kmeans.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E This is the start of the KMeans fit() method. It validates the k parameter, data dimension, cluster count, uniform dimensionality, and finite data. It then normalizes tolerance, initializes the RNG, centroids (warm-start or k-means++), and pre-allocates working buffers including Hamerly bounds, expanded-form norms, and optional GPU/BLAS acceleration. ```rust 273 return Err(Error::InvalidParameter { 274 name: "k", 275 message: "must be at least 1", 276 }); 277 } 278 279 let n = data.n(); 280 let d = data.d(); 281 if d == 0 { 282 return Err(Error::InvalidParameter { 283 name: "dimension", 284 message: "must be at least 1", 285 }); 286 } 287 288 if self.k > n { 289 return Err(Error::InvalidClusterCount { 290 requested: self.k, 291 n_items: n, 292 }); 293 } 294 295 // Validate uniform dimensionality. 296 for i in 0..n { 297 if data.row(i).len() != d { 298 return Err(Error::DimensionMismatch { 299 expected: d, 300 found: data.row(i).len(), 301 }); 302 } 303 } 304 305 util::validate_finite(data)?; 306 307 // Normalize tolerance by data variance so it scales with data magnitude. 308 // Without this, the raw tol is compared against the sum of k*d squared 309 // centroid shifts, which becomes meaninglessly tight for high-dimensional 310 // or large-scale data. (Matches scikit-learn's _tolerance approach.) 311 let effective_tol = (self.tol * util::mean_variance(data) * self.k as f64) as f32; 312 313 // Initialize RNG. 314 let mut rng = match self.seed { 315 Some(s) => StdRng::seed_from_u64(s), 316 None => StdRng::from_os_rng(), 317 }; 318 319 // Initialize centroids: use provided centroids (warm-start) or k-means++. 320 let mut centroids = if let Some(ref init) = self.init_centroids { 321 assert_eq!( 322 init.len(), 323 self.k, 324 "init_centroids length ({}) must equal k ({})", 325 init.len(), 326 self.k 327 ); 328 init.clone() 329 } else { 330 util::kmeanspp_init(data, self.k, &self.metric, self.seeding_alpha, &mut rng) 331 }; 332 let mut labels = vec![0usize; n]; 333 334 // Pre-allocate working buffers outside the iteration loop to avoid 335 // per-iteration allocation overhead (AMD ROCm pattern). 336 let mut new_centroids = vec![vec![0.0f32; d]; self.k]; 337 let mut counts = vec![0usize; self.k]; 338 339 // Hamerly bounds: per-point upper (dist to assigned centroid) and 340 // lower (dist to second-nearest centroid). When upper <= lower, 341 // the assignment cannot change and we skip distance computation. 342 // O(n) extra memory (Hamerly, SDM 2010). 343 let mut upper_bounds = vec![f32::MAX; n]; 344 let mut lower_bounds = vec![0.0f32; n]; 345 let mut centroid_shifts = vec![0.0f32; self.k]; 346 let mut sums_f64 = vec![vec![0.0f64; d]; self.k]; 347 let mut flat_buf: Vec = Vec::with_capacity(self.k * d); 348 let mut inertia_trace: Vec = Vec::with_capacity(self.max_iter); 349 350 // Precompute squared norms for expanded-form first-iteration assignment. 351 // Only valid for SquaredEuclidean (||x-c||^2 = ||x||^2 + ||c||^2 - 2*x.c). 352 let use_expanded = self.metric.supports_expanded_form(); 353 let data_norms: Vec = if use_expanded { 354 util::squared_norms(data) 355 } else { 356 Vec::new() 357 }; 358 359 // GPU acceleration: initialize Metal compute pipeline for assignment 360 // when using SquaredEuclidean and problem is large enough to amortize 361 // GPU setup overhead. Buffers for data, labels, and params are allocated 362 // once here and reused across iterations. 363 #[cfg(all(feature = "gpu", target_os = "macos"))] 364 let gpu_assigner = if self.metric.supports_expanded_form() && n * self.k >= 500_000 { 365 let data_flat = super::gpu::flatten(data); 366 super::gpu::GpuAssigner::new(&data_flat, n, self.k, d) 367 } else { 368 None 369 }; 370 371 // BLAS GEMM for first-iteration assignment (when blas feature enabled). 372 // matrixmultiply's optimized SGEMM beats per-point distance loops 373 // for large n*k due to micro-kernel SIMD and cache optimization. 374 #[cfg(feature = "blas")] 375 let use_blas = n * self.k >= 100_000 && self.metric.supports_expanded_form(); 376 #[cfg(feature = "blas")] 377 let blas_data = if use_blas { 378 let fd = super::flat::FlatMatrix::from_data(data); 379 let xn = fd.row_norms_sq(); 380 Some((fd, xn)) 381 } else { 382 None 383 }; 384 385 let mut iters = 0usize; 386 for iter in 0..self.max_iter { 387 iters = iter + 1; 388 389 // Zero the accumulators for this iteration. 390 } ``` -------------------------------- ### is_ok method Source: https://docs.rs/clump/latest/clump/error/type.Result.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Example for the `is_ok` method, showing it returns `true` for `Ok` and `false` for `Err`. ```rust let x: Result = Ok(-3); assert_eq!(x.is_ok(), true); let x: Result = Err("Some error message"); assert_eq!(x.is_ok(), false); ``` -------------------------------- ### Create signed edges from distance matrix and threshold Source: https://docs.rs/clump/latest/src/clump/cluster/correlation.rs.html?search= Builds a vector of SignedEdge for all pairs of items. Pairs closer than the threshold get positive weight (threshold - distance), farther pairs get negative weight, and pairs at exactly the threshold get weight 0 and are included but contribute no cost. ```rust pub fn edges_from_distances( data: &[Vec], metric: &D, threshold: f32, ) -> Vec { let n = data.len(); let mut edges = Vec::with_capacity(n * (n - 1) / 2); for i in 0..n { for j in (i + 1)..n { let dist = metric.distance(&data[i], &data[j]); let weight = threshold - dist; edges.push(SignedEdge { i, j, weight }); } } edges } ``` -------------------------------- ### fit() method: full clustering pipeline Source: https://docs.rs/clump/latest/src/clump/cluster/correlation.rs.html Main entry point for clustering. Validates edge indices, runs preclustering, PIVOT, merge, local search, and iterated flip on a contracted graph, then expands back to original vertices. Returns Error::InvalidParameter if any edge index exceeds n_items. Handles n_items == 0 by returning an empty result. ```rust pub fn fit(&self, n_items: usize, edges: &[SignedEdge]) -> Result { if n_items == 0 { return Ok(CorrelationResult { labels: Vec::new(), n_clusters: 0, cost: 0.0, }); } // Validate edge indices. for edge in edges { if edge.i >= n_items || edge.j >= n_items { return Err(Error::InvalidParameter { name: "edge index", message: "exceeds n_items", }); } } // Build adjacency list: for each item, list of (neighbor, weight). let adj = Self::build_adj(n_items, edges); // Phase 0: Preclustering -- merge vertices with high positive- // neighborhood overlap. let mapping = precluster(n_items, &adj, 0.5); let (n_contracted, contracted_edges) = contract_graph(n_items, edges, &mapping); let contracted_adj = Self::build_adj(n_contracted, &contracted_edges); // Phase 1: PIVOT + merge + local search on contracted graph. let mut labels1 = self.pivot(n_contracted, &contracted_adj); if self.max_iter > 0 { Self::merge_pass(n_contracted, &contracted_adj, &mut labels1); self.local_search(n_contracted, &contracted_adj, &mut labels1); } let cost1 = compute_cost(&labels1, &contracted_edges); // Phase 2: Iterated flip (Cohen-Addad et al. STOC 2024 warm-up). // Double weights of inter-cluster edges from phase 1, then re-run. let flipped_edges: Vec = contracted_edges .iter() .map(|e| { if labels1[e.i] != labels1[e.j] { SignedEdge { weight: e.weight * 2.0, ..*e } } else { *e } }) .collect(); let adj2 = Self::build_adj(n_contracted, &flipped_edges); let mut labels2 = self.pivot(n_contracted, &adj2); if self.max_iter > 0 { Self::merge_pass(n_contracted, &adj2, &mut labels2); self.local_search(n_contracted, &adj2, &mut labels2); } // Evaluate cost on contracted (not reweighted) edges. let cost2 = compute_cost(&labels2, &contracted_edges); // Take better solution on the contracted graph. let contracted_labels = if cost2 < cost1 { labels2 } else { labels1 }; // Expand back to original vertices. let mut labels = vec![0usize; n_items]; for i in 0..n_items { labels[i] = contracted_labels[mapping[i]]; } // Evaluate cost on ORIGINAL edges. let cost = compute_cost(&labels, edges); let (labels, n_clusters) = relabel_contiguous(&labels); Ok(CorrelationResult { labels, n_clusters, cost, }) } ``` -------------------------------- ### DBSCAN clustering usage example with noise detection Source: https://docs.rs/clump/latest/clump/cluster/struct.Dbscan.html?search= Demonstrates creating a Dbscan with epsilon 0.5 and min_pts 2 to cluster data, marking an outlier as noise via the NOISE sentinel. Shows that core cluster points share a label and the outlier is labeled as noise. ```rust use clump::{Dbscan, NOISE}; let data = vec![ vec![0.0f32, 0.0], vec![0.1, 0.0], vec![0.0, 0.1], // cluster vec![100.0, 100.0], // outlier ]; let labels = Dbscan::new(0.5, 2).fit_predict(&data).unwrap(); assert_eq!(labels[0], labels[1]); // same cluster assert_eq!(labels[3], NOISE); // outlier is noise ``` -------------------------------- ### inspect_err example Source: https://docs.rs/clump/latest/clump/error/type.Result.html?search=std%3A%3Avec Shows how to use inspect_err to call a function with a reference to the contained value if Err, returning the original result. The example reads a file and prints an error message if reading fails. ```rust use std::{fs, io}; fn read() -> io::Result { fs::read_to_string("address.txt") .inspect_err(|e| eprintln!("failed to read file: {e}")) } ``` -------------------------------- ### map_err example Source: https://docs.rs/clump/latest/clump/error/type.Result.html?search=std%3A%3Avec Shows how to use map_err to convert an Err value to a different type, leaving Ok untouched. The example defines a stringify function and asserts the results for both Ok and Err cases. ```rust fn stringify(x: u32) -> String { format!("error code: {x}") } let x: Result = Ok(2); assert_eq!(x.map_err(stringify), Ok(2)); let x: Result = Err(13); assert_eq!(x.map_err(stringify), Err("error code: 13".to_string())); ```