Kavya Manjula Gurubasavaiah

Single-Cell & Perturbation Bioinformatics · Machine Learning

CRISPRi perturbation screens, cell foundation models, and out-of-core pipelines for single-cell data that will not fit in memory. Two years at the bench before moving to computation, so I know how the data was made — and I measure a dataset before I model it.

  • Perturbation modelling. Fine-tuning cell foundation models — STATE, 242M parameters, ESM2 embeddings — for zero-shot prediction in cell contexts the model has never seen.
  • Out-of-core engineering. Sparse pipelines for data that does not fit in memory: a 1.29-billion-entry prediction written from an 8 GB laptop.
  • Measurement before modelling. Noise floors and ceilings established from the data itself, so a real improvement can be told from sampling noise.

MSc Bioinformatics, University of Edinburgh · based in Edinburgh, UK, open to relocating · UK Graduate visa, valid to December 2027 · available immediately

Kavya Manjula Gurubasavaiah

Selected work

Four shipped, two in progress

Arc Virtual Cell Challenge 2026

Ongoing Zero-shot Python · STATE · GPU

Zero-shot generalisation under distribution shift. Before building a model I established what a good answer could even look like.

The task

Given
ABC

Unperturbed cells from three anonymised human cell lines. None appears in any training data.

Predict

How each of 300 CRISPRi gene knockdowns changes the transcriptome, in every context.

Output
360,000

cells × 18,533 genes, as raw integer counts. Scored on six metrics.

The model has to answer for cell types it has never seen, using only what it learned from other lines — zero-shot generalisation under distribution shift.

Where a score can land

my submission−0.0317 · at the no-change mark 0.19leader at the time 0.30ceiling · measured 1.0a repeat experiment

Nobody can reach 1.0. I measured the ceiling at roughly 0.30 by running the organisers' own scorer sideways on control cells, scoring them against each other. The experiment cannot repeat itself more precisely than that. Everything real happens in the band between 0 and 0.30 — which is why knowing where 0.30 sat mattered more than any single submission.

What truncation cost

Top 2,000 genes per cell
−0.9976rank 455
All 18,533 genes
−0.0317rank 274

Same model, same data. The only change is how many genes each predicted cell is allowed to express. Keeping the top 2,000 of 18,533 zeroes 89% of the transcriptome — and nmae went from its floor of −6 to −0.0117 when that stopped.

0.0132noise floor, measured from 46 control guides
≈0.30ceiling — no model can score above it
A · B · Call three cell contexts identified from marker genes
SD 0.0caught subsampling the documentation never mentioned
221,273cells the released training config had excluded
242Mparameters fine-tuned — STATE, with ESM2 embeddings
How each of those was arrived at

Measured the noise floor before modelling anything

46 non-targeting guides, 400 cells each — cells where nothing was perturbed. Comparing each guide's mean profile against the other 45 runs exactly the comparison the challenge scores, on a group with no signal in it. MAE 0.0132. Replicated across all three contexts at 0.0132 / 0.0121 / 0.0131, within 9%. Below that number, a difference is sampling noise.

Identified all three anonymised cell contexts from marker genes alone

Per-gene detection rates with np.bincount, then log2 fold change with a pseudocount — chosen over plain difference, which rewards abundance rather than specificity. A: immature T-lymphoid, TRBC1 in 100.0% of cells, IKZF1 and LEF1 as master regulators, RAG1 and CD1E present. B: RPE1-like, CLU / FOLR1 / HTRA1, CDKN2A intact. C: basal squamous, TP63 and KRT5.

Caught curation that the documentation did not mention

Exactly 400 cells per guide, standard deviation 0.0. Real experiments are never that even, so the release had been subsampled before publication. Guide identifiers run to 99 while only 46 exist — an identifier is a name, not a count. Separately, median depth of 20,109 counts per cell matched the organisers' documentation exactly, and all three contexts were depth-matched, which means differences between them are biological rather than technical.

Diagnosed a metric pinned at its floor

Two early submissions scored about −1.0. Five of six metrics sat at ~0, exactly as the scale defines for a no-change prediction — but nmae was pinned at its floor of −6. The cause was truncation: keeping only the strongest genes per cell sends every other gene to zero. Predicting at full density moved the score to −0.0317 and the rank from 455 to 274, and nmae from −6 to −0.0117. The lesson was about the objective rather than the model — normalised error is dominated by what you predict as zero, so a representation choice made before any learning happens can cost more than the model can recover.

Read the library source rather than guessing

The official packaging tool was killed three times by the memory limit. Instead of retrying with smaller inputs, I read its source: every stored entry is held three times over — once on load, once in an unconditional type conversion, once in the integrality check. Instrumenting a run confirmed it: ~30 bytes per entry, peak 31.6 GB. That converts an unknown into a formula — available memory ÷ 30 is the ceiling.

Removed the memory ceiling entirely

The submission format turned out to be tar(zstd(h5ad)) with a single entry, and every step of that streams. I built the container by hand: it scored −0.03174760868365253, identical to the official tool's output to fifteen decimal places. The ceiling was a choice the validator made, not a property of the format.

Fine-tuned a 242M-parameter model and built the inference path end to end

Arc's STATE transformer with ESM2 perturbation embeddings and an optimal-transport loss over cell sets. Converting its log-space output back to raw integer counts needed care: each predicted cell is scaled to the library size of the control cell that seeded it, then Poisson-sampled rather than rounded — at ~20,000 counts across 18,533 genes, rounding sends everything below 0.5 to zero, which is the same truncation that scored −6. Result: median 20,007 counts per cell against a raw median of 19,940.

Traced a generalisation gap to distribution shift in the released configuration

The training set had been cut to fit a memory limit, dropping competition_train and leaving four reference Perturb-seq datasets — about 80,000 cells, none of them from the distribution being evaluated. The model was learning entirely out of domain and then being asked to generalise into it. Restored, competition_train is 221,273 cells: three quarters of the training set, and the only part drawn from the competition's own experiment. A larger handicap than the training budget was. I also vectorised a quadratic loop in the inference path that assigned rows one at a time into a DataFrame across 120,000 iterations.

Trained inside a fixed compute budget, and measured rather than assumed

30 GPU-hours per week, a 12-hour ceiling per session, and no disk that survives between sessions. I measured throughput instead of estimating it — 0.29 it/s, which turned an eleven-hour guess into a five-hour plan — checkpointed every 500 steps so a lost session costs 500 steps rather than all of them, and moved the trained model into a versioned artefact so it never needs retraining. The analysis that preceded it ran on an 8 GB laptop, streaming sparse chunks rather than loading matrices, on data that is 17 GB dense.

Quantifying data leakage in relapse prediction

Python · scikit-learn · XGBoost Human transcriptomics

Breast cancer relapse classification, built to measure how much a common methodological shortcut exaggerates reported performance.

Cross-validated AUC — same data, same models

The shortcut
all 286 samples rank probes, keep top 200 5-fold cross-validation
Done properly
5-fold split first rank and keep 200 inside each fold validate on a truly unseen fold
Random Forest
0.723
0.806
SVM (RBF)
0.699
0.833
Logistic regression
0.693
0.828
XGBoost
0.674
0.800
AUC
0.50 0.60 0.70 0.80
selection inside each fold selection once, on all the data

SVM tops the leaky ranking and comes third when done properly. The shortcut does not only inflate the score — it picks the wrong model.

How the comparison was built

Selecting features before cross-validation inflates ROC-AUC by +0.120

With 22,215 features and 286 samples, p >> n and the univariate filter is doing most of the work — so where it sits decides the reported score. Rank probes once on the full dataset and every fold's held-out samples have already helped choose the features; the fold is not held out at all. Put scaling and selection inside a Pipeline and both refit per fold. Both are implemented, so the gap is measured rather than asserted.

The shortcut does not just inflate the number — it selects the wrong model

Four models compared under both protocols. SVM looks like the best model under leakage and is third without it. Run the comparison the wrong way and the ranking changes, not only the magnitude — which is the part that actually costs you.

Reported the weak result rather than the flattering one

Test AUC 0.61–0.64 against a 24% base rate: weak but non-zero, and consistent with the literature on single-cohort microarray classifiers. The PCA shows why — relapse status is not separable along the dominant axes of variation. The README also records that the endpoint is bone relapse rather than the paper's distant-metastasis-free survival, because that clinical table was never deposited in GEO.

+0.120mean AUC inflation from leakage
4 modelsRF, SVM, logistic regression, XGBoost
0.723best honest CV AUC, Random Forest
286 × 22,215p >> n, the regime where this matters

Kvass fermentation — 16S and shotgun metagenomics

MSc dissertation · 2025 QIIME2 · Nextflow DSL2 · R

How dietary fibre changes a fermenting microbial community, in composition and in function. Four taxonomic profilers run against the same samples and compared for agreement rather than picking one and trusting it.

One fermentation, two sequencing layers

16S amplicon
QIIME2 2024.2 import → denoise → taxonomy → alpha and beta diversity
Shotgun
MetaPhlAn4 · Kraken2/Bracken · Kaiju three independent profilers, identical reads
compared for agreement, not averaged
Function
HUMAnN3 · DRAM2 pathways, gene families and CAZy shifts under fibre treatment. The DRAM2 stage runs as a Nextflow workflow.
Reporting
R 4.3 — vegan, pheatmap, ggplot2 PERMANOVA, ordination and heatmaps, generated from versioned manifests

Where the profilers disagree is itself a result. Establish how much of an answer is method before attributing any of it to biology — the same instinct as measuring a noise floor.

4taxonomic profilers, compared head to head
16S + shotgunintegrated, not analysed separately
DSL2modules, Docker and a Slurm profile — one config, three targets
Manifestsversioned metadata to results, fully traceable

LabHelpr

Deployed Built and maintained solo

Built for people at the bench. A tool that runs a statistical test without telling you the test was inappropriate is worse than no tool at all — so this one checks first.

LabHelpr labhelpr.com
13 calculatorsmolarity, dilutions, buffers — the maths a bench day actually needs
Statistics modulechecks its own assumptions before it runs a test
Electronic lab notebookentries that persist, even with no connection
Installable PWAworks offline, on a phone at the bench
In productionin use by wet-lab scientists, not a demo
Shipped solodesign, code, deployment and upkeep

Single-cell atlas analysis, end to end

In progress Scanpy · Cell Ranger

A complete standard workflow on a public 10x dataset, containerised and documented.

  • QC — mitochondrial fraction, ambient RNA, doublet scoring
  • Normalisation and highly-variable gene selection
  • PCA, neighbourhood graph, Leiden clustering
  • Marker genes and cell-type annotation
  • Containerised, reproducible from raw output
  • QC thresholds justified, not copied from a tutorial

Why this one. I have worked with single-cell data through h5py and CSR directly, which taught me the structure but skipped the standard toolchain. This closes that gap, on a dataset anyone can download and check.

The Challenge pipeline, containerised and on cloud

In progress Nextflow DSL2 · Docker · AWS

The perturbation pipeline rebuilt so anyone can run it, not just me.

  • Nextflow DSL2 workflow, modules per stage
  • Pinned Docker image, versions fixed
  • AWS Batch with S3 for data
  • IAM scoped to least privilege
  • Runtime benchmarks per stage, published
  • Cost per run, in the repository not in my head

Why this one. Everything I built for the Challenge runs on my machine and nowhere else. I have the runtime figures already — but until someone else can reproduce them, they are my numbers rather than evidence.

How I approach a problem

Where each of these is evidenced above

01

Bound it first

whena result has nothing to compare it against thenmeasure the floor and the ceiling first 0.0132  /  ≈0.30
02

Read the source

whenbehaviour repeats and stays unexplained thenread the implementation 30 B per entry
03

Verify from outside

whena check shares assumptions with what it checks thencompare against an independent implementation agreement to 15 dp
04

Keep the failures

whena method cannot be reproduced by anyone else thenkeep the runs that scored −1.0, and the reasoning both −1.0 runs kept

Skills

What I can do today, and what I am closing next

Single cell & perturbation

AnnData / h5adCSR sparse matricesout-of-core HDF5 (h5py)CRISPRi Perturb-seqdifferential expression (Wilcoxon, BH)marker-gene annotation

Machine learning

scikit-learnRandom Forest / SVM / XGBoostleakage-safe cross-validationfeature selection under p >> nROC-AUC, PR-AUC, confusion matricesarc-state (STATE) fine-tuningzero-shot inferenceESM2 embeddingsWeights & Biases

Wet lab

DNA/RNA extractionPCR / qPCRcloning and plasmid workcell culture

I know how the data I analyse was generated, and can design the experiment that produces it.

Statistics

GLMs (Poisson, binomial, overdispersion)multiple-testing correction and FDRnonparametric and rank-based testslinear models and ANOVA

Bulk transcriptomics

RNA-seq end to endFastQC / MultiQCSTAR / HISAT2DESeq2 / edgeRTPM / TMM / VST normalisationGO / KEGG enrichment

Engineering & reproducibility

PythonRBashnumpy / pandas / scipy.sparseNextflow DSL2 (modules, profiles)DockerSlurm / HPCGitLinux

In progress

Scanpy, Seurat and Cell Ranger — the standard single-cell stack, and the clearest distance between what I have built and the roles I want. AWS (S3, Batch, IAM, cost control), to run the Nextflow pipelines I already write somewhere other than one machine. PyTorch from first principles rather than through a framework. scVI and trajectory inference.

Publications

Both peer-reviewed, with DOIs

Experience

Two years at the bench, then computation

  1. 2026 — present

    Bioinformatics Domain Expert · Mercor

    AI evaluation across single-cell genomics, metagenomics and microbiology — judging whether a model’s biological reasoning holds. 1,000+ tasks, each in a pinned container.computational

  2. 2026 — present

    Bioinformatics Consultant · KreatBio

    End-to-end metagenomic analysis for client sample sets, from raw sequencing through to interpreted results.computational

  3. 2024 — 2025

    MSc Bioinformatics · University of Edinburgh

    Where the work moved from the bench to computation.the move

  4. 2023 — 2024

    Project Associate, gut health & prebiotics · CSIR–CFTRI

    Ran the molecular workstream — extraction, PCR, microbiome assays — then analysed the sequencing it produced. Mentored MSc students at the bench.bench and computational

  5. 2022

    Junior Research Fellow · University of Hyderabad

    Drug-resistance mechanisms in Leishmania donovani — PCR, cloning, protein expression, western blotting.bench

  6. 2022

    Academic Intern, Biochemistry · JSS College

    Led a KSCST-funded project on methane from biodegradable plastics. It became my first publication.bench

  7. 2020 — 2022

    MSc Biochemistry · JSS College

  8. 2017 — 2020

    BSc Chemistry, Botany & Biotechnology · University of Mysore