From 7883d5cb95d4dcd19f5b15f7cd70998833531665 Mon Sep 17 00:00:00 2001 From: Robrecht Cannoodt Date: Fri, 21 Aug 2026 14:45:05 +0200 Subject: [PATCH] address review comments on bbknn_ts * renamed the component to `era_combat_bbknn`, after the ERA system and the two algorithms it combines * `method_types: [embedding, graph]`: the custom kNN graph is evaluated as is * `preferred_normalization: counts`: the method does its own normalisation * pass `--n_pca_components`, `--n_neighbors_per_batch` and `--total_k_neighbors` through to the integration code, they were unused * store a minimal output object instead of the mutated input * updated the reference to the Nature paper and the links to the renamed `google-research/era` repo * `openproblems/base_python:1` and `[hightime, highmem, midcpu]`, in line with the other methods --- CHANGELOG.md | 2 +- src/methods/bbknn_ts/config.vsh.yaml | 54 ---------------- src/methods/era_combat_bbknn/config.vsh.yaml | 64 +++++++++++++++++++ .../{bbknn_ts => era_combat_bbknn}/script.py | 47 +++++++++++--- src/workflows/run_benchmark/config.vsh.yaml | 2 +- src/workflows/run_benchmark/main.nf | 2 +- 6 files changed, 106 insertions(+), 65 deletions(-) delete mode 100644 src/methods/bbknn_ts/config.vsh.yaml create mode 100644 src/methods/era_combat_bbknn/config.vsh.yaml rename src/methods/{bbknn_ts => era_combat_bbknn}/script.py (90%) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf0fe0e5a..67c23175e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ * Added `ARI_batch` and `NMI_batch` to `metrics/clustering_overlap` (PR #68). -* Added `methods/bbknn_ts` component (PR #84). +* Added `methods/era_combat_bbknn` component (PR #84). * Added `metrics/cilisi` new metric component (PR #57). - ciLISI measures batch mixing in a cell type-aware manner by computing iLISI within each cell type and normalizing diff --git a/src/methods/bbknn_ts/config.vsh.yaml b/src/methods/bbknn_ts/config.vsh.yaml deleted file mode 100644 index 7ac3189a3..000000000 --- a/src/methods/bbknn_ts/config.vsh.yaml +++ /dev/null @@ -1,54 +0,0 @@ -__merge__: ../../api/comp_method.yaml - -name: bbknn_ts -label: BBKNN (TS) -summary: "A combination of ComBat and BBKNN discovered and implemented by Gemini." -description: | - "The BBKNN (TS) solution applies standard scRNA-seq preprocessing steps, including total count normalization, log-transformation, and scaling of gene expression data. Batch effect correction is performed using scanpy.pp.combat directly on the gene expression matrix (before dimensionality reduction). Dimensionality reduction is then applied using PCA on the ComBat-corrected data, and this PCA embedding (adata.obsm['X_pca']) is designated as the integrated embedding (adata.obsm['X_emb']). A custom batch-aware nearest neighbors graph is constructed based on this integrated embedding; for each cell, neighbors are independently identified within its own batch and other batches, up to n_neighbors_per_batch. These candidate neighbors are merged, keeping the minimum distance for duplicate entries, and the top total_k_neighbors are selected for each cell. Finally, a symmetric sparse distance matrix and a binary connectivities matrix are generated to represent the integrated neighborhood graph. This code was entirely written by the AI system described in the associated publication." -references: - bibtex: | - @misc{GoogleScienceAI2025, - title={An {AI} system to help scientists write expert-level empirical software}, - author={Eser Ayg\"un and Anastasiya Belyaeva and Gheorghe Comanici and Marc Coram and Hao Cui and Jake Garrison and Renee Johnston and Anton Kast and Cory Y. McLean and Peter Norgaard and Zahra Shamsi and David Smalling and James Thompson and Subhashini Venugopalan and Brian P. Williams and Chujun He and Sarah Martinson and Martyna Plomecka and Lai Wei and Yuchen Zhou and Qian-Ze Zhu and Matthew Abraham and Erica Brand and Anna Bulanova and Jeffrey A. Cardille and Chris Co and Scott Ellsworth and Grace Joseph and Malcolm Kane and Ryan Krueger and Johan Kartiwa and Dan Liebling and Jan-Matthis Lueckmann and Paul Raccuglia and Xuefei (Julie) Wang and Katherine Chou and James Manyika and Yossi Matias and John C. Platt and Lizzie Dorfman and Shibl Mourad and Michael P. Brenner}, - year={2025}, - eprint={2509.06503}, - archivePrefix={arXiv}, - primaryClass={cs.AI}, - url={https://arxiv.org/abs/2509.06503} - } -links: - documentation: https://google-research.github.io/score/ - repository: https://github.com/google-research/score - -info: - method_types: [embedding] - preferred_normalization: log_cp10k - -arguments: - - name: "--n_pca_components" - type: "integer" - default: 100 - description: "Number of PCA components." - - name: "--n_neighbors_per_batch" - type: "integer" - default: 10 - description: "Number of neighbors to use within each batch." - - name: "--total_k_neighbors" - type: "integer" - default: 50 - description: "Total number of nearest neighbors to retain for the final graph." - -resources: - - type: python_script - path: script.py - - path: /src/utils/read_anndata_partial.py - -engines: - - type: docker - image: openproblems/base_python:1.0.0 - -runners: - - type: executable - - type: nextflow - directives: - label: [midtime,midmem,midcpu] diff --git a/src/methods/era_combat_bbknn/config.vsh.yaml b/src/methods/era_combat_bbknn/config.vsh.yaml new file mode 100644 index 000000000..0c634e3b6 --- /dev/null +++ b/src/methods/era_combat_bbknn/config.vsh.yaml @@ -0,0 +1,64 @@ +__merge__: ../../api/comp_method.yaml + +name: era_combat_bbknn +label: ERA ComBat + BBKNN +summary: "A combination of ComBat and BBKNN discovered and implemented by the ERA AI system." +description: | + Standard scRNA-seq preprocessing steps are applied to the raw counts, including + total count normalization, log-transformation and scaling of the gene expression + data. Batch effect correction is performed using `scanpy.pp.combat` directly on the + gene expression matrix (before dimensionality reduction). Dimensionality reduction + is then applied using PCA on the ComBat-corrected data, and this PCA embedding is + returned as the integrated embedding. + + A custom batch-aware nearest neighbors graph is constructed based on this integrated + embedding: for each cell, neighbors are independently identified within its own batch + and other batches, up to `n_neighbors_per_batch`. These candidate neighbors are + merged, keeping the minimum distance for duplicate entries, and the top + `total_k_neighbors` are selected for each cell. Finally, a symmetric sparse distance + matrix and a binary connectivities matrix are generated to represent the integrated + neighborhood graph. + + The integration code was entirely written by the AI system described in the + associated publication, and is included here unmodified. +references: + # Aygün, E., Belyaeva, A., Comanici, G. et al. + # An AI system to help scientists write expert-level empirical software. + # Nature 654, 909-916 (2026). https://doi.org/10.1038/s41586-026-10658-6 + doi: 10.1038/s41586-026-10658-6 +links: + documentation: https://google-research.github.io/era/ + repository: https://github.com/google-research/era + +info: + method_types: [embedding, graph] + preferred_normalization: counts + +arguments: + - name: "--n_pca_components" + type: "integer" + default: 100 + description: "Number of PCA components." + - name: "--n_neighbors_per_batch" + type: "integer" + default: 10 + description: "Number of neighbors to use within each batch." + - name: "--total_k_neighbors" + type: "integer" + default: 50 + description: "Total number of nearest neighbors to retain for the final graph." + +resources: + - type: python_script + path: script.py + - path: /src/utils/read_anndata_partial.py + +engines: + - type: docker + image: openproblems/base_python:1 + +runners: + - type: executable + - type: nextflow + directives: + label: [hightime, highmem, midcpu] diff --git a/src/methods/bbknn_ts/script.py b/src/methods/era_combat_bbknn/script.py similarity index 90% rename from src/methods/bbknn_ts/script.py rename to src/methods/era_combat_bbknn/script.py index 7046f24b4..d519e2c86 100644 --- a/src/methods/bbknn_ts/script.py +++ b/src/methods/era_combat_bbknn/script.py @@ -1,17 +1,20 @@ -"""BBKNN (TS) code for removing batch effects. +"""ComBat + BBKNN code for removing batch effects. This is the top-performing batch correction implementation discovered by the AI -system described in https://arxiv.org/abs/2509.06503. +system described in https://doi.org/10.1038/s41586-026-10658-6. """ ## VIASH START # Note: this section is auto-generated by viash at runtime. To edit it, make changes # in config.vsh.yaml and then run `viash config inject config.vsh.yaml`. par = { - 'input': 'resources_test/.../input.h5ad', - 'output': 'output.h5ad' + 'input': 'resources_test/task_batch_integration/cxg_immune_cell_atlas/dataset.h5ad', + 'output': 'output.h5ad', + 'n_pca_components': 100, + 'n_neighbors_per_batch': 10, + 'total_k_neighbors': 50 } meta = { - 'name': 'bbknn_ts' + 'name': 'era_combat_bbknn' } ## VIASH END @@ -235,7 +238,7 @@ def eliminate_batch_effect_fn( from read_anndata_partial import read_anndata print('Read input', flush=True) -input_adata = read_anndata( +adata = read_anndata( par['input'], X='layers/counts', obs='obs', @@ -243,6 +246,34 @@ def eliminate_batch_effect_fn( uns='uns' ) -output = eliminate_batch_effect_fn(input_adata, config=config) -output.uns['method_id'] = 'bbknn_ts' +print('Run ComBat + BBKNN', flush=True) +integrated = eliminate_batch_effect_fn( + adata, + config={ + 'n_pca_components': par['n_pca_components'], + 'n_neighbors_per_batch': par['n_neighbors_per_batch'], + 'total_k_neighbors': par['total_k_neighbors'] + } +) + +print('Store output', flush=True) +output = ad.AnnData( + obs=adata.obs[[]], + var=adata.var[[]], + obsm={ + 'X_emb': integrated.obsm['X_emb'], + }, + obsp={ + 'connectivities': integrated.obsp['connectivities'], + 'distances': integrated.obsp['distances'], + }, + uns={ + 'dataset_id': adata.uns['dataset_id'], + 'normalization_id': adata.uns['normalization_id'], + 'method_id': meta['name'], + 'neighbors': integrated.uns['neighbors'], + } +) + +print('Write output to file', flush=True) output.write_h5ad(par['output'], compression='gzip') diff --git a/src/workflows/run_benchmark/config.vsh.yaml b/src/workflows/run_benchmark/config.vsh.yaml index b9b726327..6b4a0c980 100644 --- a/src/workflows/run_benchmark/config.vsh.yaml +++ b/src/workflows/run_benchmark/config.vsh.yaml @@ -91,8 +91,8 @@ dependencies: - name: methods/batchelor_fastmnn - name: methods/batchelor_mnn_correct - name: methods/bbknn - - name: methods/bbknn_ts - name: methods/combat + - name: methods/era_combat_bbknn - name: methods/geneformer - name: methods/harmony - name: methods/harmonypy diff --git a/src/workflows/run_benchmark/main.nf b/src/workflows/run_benchmark/main.nf index 0c77ef56d..f7754ffc4 100644 --- a/src/workflows/run_benchmark/main.nf +++ b/src/workflows/run_benchmark/main.nf @@ -19,8 +19,8 @@ methods = [ batchelor_fastmnn, batchelor_mnn_correct, bbknn, - bbknn_ts, combat, + era_combat_bbknn, geneformer, harmony, harmonypy,