Complete rewrite with C++ backend (Armadillo + nanobind), matching the R harmony2 package step-by-step.
- C++ backend using Armadillo for BLAS-accelerated dense matrix operations (Accelerate on macOS, OpenBLAS on Linux). Custom scatter/gather kernels replace all sparse matrix operations by exploiting Phi's one-hot structure.
- Pre-built wheels for Linux (x86_64, aarch64) and macOS (x86_64, arm64), Python 3.9–3.13. Armadillo headers fetched at build time — no system install required.
- K-means initialization matches R exactly: Gumbel-max cosine-distance
sampling followed by
arma::kmeansrefinement. ncoresparameter to control BLAS thread count (0 = all cores, default).batch_prop_cutoffparameter (default 1e-5) excludes underrepresented batches from correction in each cluster.- Arrowhead matrix inverse for fast single-covariate batch correction.
- Accepts pandas DataFrame, dict of arrays, or NumPy array for
meta_data. - Non-numeric DataFrame columns (e.g. barcodes) are dropped automatically.
- Stricter input validation with clear error messages for shape mismatches.
- C++ progress messages (e.g. "Iteration 1 of 10") now go through Python's
loggingmodule instead ofstd::cout, so they appear immediately and integrate with downstream packages' logging configuration. Thanks to Yakir Reshef (@yakirr) for reporting this.
lambnow defaults to automatic lambda estimation (was fixed1). Passlamb=1explicitly to restore previous behavior.- Default parameters changed to match R harmony2:
max_iter_kmeans20→4,epsilon_cluster1e-5→1e-3,epsilon_harmony1e-4→1e-2. - Requires a C++ compiler and BLAS library for building from source (pre-built wheels are available on PyPI).
- Only
numpyis required at runtime (previously required pandas, scipy, scikit-learn, torch).
- 858k cells in ~36s on Apple M1 Ultra (vs ~340s in v0.1.0, ~38s in R harmony2).
- Correlation with R harmony2: ≥0.998 across all PCs on test data.
- No sparse matrices allocated — memory usage ~50% lower than sparse approach.
- PyTorch backend with GPU acceleration (CUDA, Apple Silicon MPS) and optimized CPU execution.
- Requires Python >= 3.9.
- Pure NumPy implementation matching R package v1.2.4 formulas for improved accuracy.
- Correlation with R harmony results: >0.95 for all PCs.
- Performance benchmarks:
- Small (3.5k cells): 1.88s
- Medium (69k cells): 56.22s
- Large (858k cells): 340.32s
- Migrate to hatch to ease development and include multiple authors.
- Add @johnarevalo to the author list.
- Stop excluding
README.mdfrom the build, because setup.py depends on this file.
- Replace
scipy.cluster.vq.kmeans2with the faster functionsklearn.cluster.KMeans. Thanks to @johnarevalo for providing details about the running time with both functions in PR #20.
- Replace
scipy.cluster.vq.kmeanswithscipy.cluster.vq.kmeans2to address issue #10 where we learned that kmeans does not always return k centroids, but kmeans2 does return k centroids. Thanks to @onionpork and @DennisPost10 for reporting this.
- Expose
max_iter_harmonyas a new top-level argument, in addition to the previously exposedmax_iter_kmeans. This more closely resembles the original interface in the harmony R package. Thanks to @pinin4fjords for pull request #8
-
Fix a bug in the LISI code that sometimes causes computation to break. Thanks to @tariqdaouda for reporting it in issue #1
-
Fix a bug that prevents controlling the number of iterations. Thanks to @liboxun for reporting it in issue #3
-
Fix a bug causing slightly different results than expected. Thanks to @bli25broad for pull request #2
-
Add support for multiple categorical batch variables.
- Speed up the Harmony algorithm. It should now be as fast as the R package.
- Add Local Inverse Simpson Index (LISI) functions from the lisi R package. https://github.com/immunogenomics/LISI
- Initial release. Code ported directly from the harmony R package. https://github.com/immunogenomics/harmony