Everything qpAdm, the method behind a decade of ancient-DNA papers
and our paid analysis, is free software: the qpadm() function of the ADMIXTOOLS 2 R
package (Maier et al. 2023). This is a working tutorial for running it yourself: the setup, the
calls, the output tables, and the part no README covers, the arguments and protocol choices that
silently change your results.
If you want the modelling without the pipeline, that exists too: the free AdmixTools 2 Lab runs f-statistics, qpWave and qpAdm against a curated ancient panel in the browser, and the Ancestry Workbench does it on your own genome merged into AADR. This post is for the readers who want the R console.
Setup#
ADMIXTOOLS 2 installs from GitHub (uqrmaie1/admixtools); on most systems
devtools::install_github("uqrmaie1/admixtools") plus a compiler is the whole story. Data must
be EIGENSTRAT or PACKEDANCESTRYMAP genotypes; in practice that means the
AADR download, with your own genotypes
merged in if you intend to model yourself. The merge is its
own craft (position intersection, strand hygiene, build discipline);
sanity-check any consumer file first with the free file check.
The two-step workflow#
ADMIXTOOLS 2's speed comes from precomputing f2-statistics once, then reusing them across thousands of models:
library(admixtools)
extract_f2("path/to/geno_prefix", "f2_dir",
pops = my_pops, maxmiss = 0) # once, slow
f2 <- f2_from_precomp("f2_dir") # fast forever after
left <- c("Anatolia_N", "Yamnaya_Samara", "WHG")
right <- c("Mbuti.DG", "Ust_Ishim.DG", "Kostenki14", "MA1",
"Han.DG", "Papuan.DG", "Onge.DG", "Karitiana.DG",
"EHG", "Iran_N", "Levant_N")
qpadm(f2, left, right, target = "MyKit")The right set above is the standard spine plus era contrasts: not a decoration to copy blindly but the half of the model that decides whether it can be tested at all.
The sparse-data exception, immediately: precomputed f2 blocks restrict every statistic to
sites present across all populations. With low-coverage ancients that discards most of the data,
and the measured penalty reaches
SE 9.994 versus 0.035 at 90% missingness. The fix is
classic qpAdm's allsnps behaviour, which in ADMIXTOOLS 2 requires skipping the f2 directory and
passing the genotype prefix directly:
qpadm("path/to/geno_prefix", left, right, target = "MyKit",
allsnps = TRUE)Slower, and usually right for real ancient panels. Whichever you choose, hold it constant across every model you intend to compare.
Reading the output#
qpadm() returns the tables a model record is built from:
weights:weight,seandzfor each source. The three-number reading: a weight whose 2-SE interval covers 0 is a source the data cannot tell apart from absent; negative weights are a diagnosis, not a nuisance.rankdrop: the rank test, with chi-square, dof (outgroups minus sources for the full model) andpfor each tested rank, plusp_nestedcomparing adjacent ranks.popdrop: every sub-model obtained by dropping sources, with its p, weights and afeasibleflag. The discipline it encodes: if a simpler model survives, prefer the simpler model, as parsimony relative to your right set rather than as a count of historical sources.
The arguments that silently change results#
allsnps: see above; the largest single lever on sparse data.fudge_twice = TRUE: applies the numerical ridge a second time, matching classic qpAdm's p-values. Set it when comparing against published numbers; either way, consistently.constrained = TRUE: forces weights non-negative. Never for screening: an unconstrained negative weight is information about a wrong or missing source, and constraining hides it.blgsize: jackknife block size, default 0.05 Morgans (5 cM). The convention behind every published SE; change it only knowingly.boot: bootstrap instead of jackknife resampling. Jackknife SEs are the ones comparable to the published literature.
Every knob above has a fuller treatment (what it does, the measured stakes, when to deviate) in the parameters reference, with its section on matching classic ADMIXTOOLS covering the settings that reproduce published numbers from the original software. And if your own file is not yet merged into the AADR, the data-preparation guide is the missing chapter before this one.
Batch helpers exist (qpadm_rotate(), qpadm_multi()), and they make it trivial to test thousands
of models, which is where the false-discovery hazard lives: the more implausible models a search
contains, the more of its passes are wrong, whatever the protocol
(the pre-study odds arithmetic). On simulated admixture-graph
histories, unstratified rotating screens ran false-discovery rates of 72.5 to 100%; on simulated
stepping-stone landscapes the protocols did not differ significantly, and the odds decided.
Enumerate to map the space; never let the enumeration pick the winner.
The discipline the code will not enforce#
qpadm() will happily run models that violate every assumption behind the statistics. The
protocol, from the audit literature, is the difference between output and results:
- Constrain the hypotheses first: choose a few candidate sources for reasons outside qpAdm (archaeology, chronology, geography, earlier genetic work). A search's false-discovery rate depends on how plausible its candidates were before anything ran.
- Temporal stratification: no source younger than the target. A modern kit against ancient sources satisfies this by construction, which protects against one class of errors on graph-like histories and is not a guarantee on landscape-like ones.
- Simplest adequate model first: one-source
explanations before two, two before three, with
p_nestedas referee. That is parsimony relative to your right set, not a count of the migrations in the target's history. - Never rank surviving models by p: the best p identifies the true model 48% of the time. Margins, feasibility and stability across right-set variations do the choosing.
- Composite feasibility: p above threshold and weights bounded inside (0,1) within error and trailing simpler models rejected. These criteria cut false positives more than fourfold in simulations; on their own they do not rescue a search with poor pre-study odds.
- Report the family, not the winner: state which models also passed and what they agree on; single-winner claims are the overfit the auditors keep finding.
- Read a fit as a position, not an event: on landscape-like histories, a fitting model can summarise where the target sits between sampled groups without those groups ever having mixed.
That protocol is what the paid analysis applies to your file, plus the merge, the curated panels and a model record at the end. Candidate sources are chosen for your declared origins before any model runs, our analyst tests models from that pool, and a person reviews the results and approves the one that is published, measured against one house bar (p > 0.05, every |Z| > 3, every SE < 0.10) that is our own convention. Run it yourself, buy it run, or both: the method is the same, which is exactly the point.
Terms used here are defined in the glossary.
References#
- Maier, R. et al. (2023). On the limits of fitting complex models of population history to f-statistics. eLife, 12, e85492. (ADMIXTOOLS 2.)
- Harney, É., Patterson, N., Reich, D. & Wakeley, J. (2021). Assessing the performance of qpAdm. Genetics, 217(4), iyaa045.
- Williams, M. P. et al. (2024). Testing times: disentangling admixture histories in recent and complex demographies using ancient DNA. Genetics, 228(1), iyae110.
- Flegontova, O. et al. (2025). Performance of qpAdm-based screens for genetic admixture on graph-shaped histories and stepping stone landscapes. Genetics, 230(1), iyaf047.
- ADMIXTOOLS 2 documentation and source: uqrmaie1.github.io/admixtools.
Editorial note: revised on 24 September 2026. An earlier version quoted the admixture-graph false-discovery rates without their regime, overstated what composite criteria do to the false-discovery rate (they cut false positives; the false-discovery rate moves far less), and described our own analysis in terms that no longer matched how it runs.



