Before you begin
- qpAdm identifies models that are not rejected relative to chosen references; it does not discover true ancestors automatically.
- A p-value above 0.05 is not proof, and the highest p-value is not a source ranking.
- Freeze candidate sources and right populations before viewing results; rotate systematically instead of tuning until a favorite model passes.
- Consumer arrays are sparse, diploid views of one person. Treat cross-chip agreement as technical robustness, not biological replication.
What qpAdm Actually Tests
qpAdm evaluates whether a target can be represented as a mixture of specified left-side source populations relative to a set of right-side reference populations. Its tail probability comes from a rank test. A low value can reject the proposed model; a high value means the test did not find enough evidence to reject it under that design.
Three consequences follow:
- Different proxy populations can all be plausible because they share the drift qpAdm can see.
- A plausible model can still be historically wrong if the real source is unsampled or the demographic history violates the model assumptions.
- Changing right populations after inspecting results changes the question. Exploratory tuning must be labelled exploratory and confirmed on a frozen design.
The Reproducible Workflow
Evidence figure
A model-competition workflow, not a p-value hunt
- Audit build, strand, overlap, and privacy
- Freeze targets, sources, rights, and chronology
- Compute f2 and model-free f3/f4 checks
- Rotate models and apply deterministic gates
- Report all survivors and the resolution ceiling
1. Environment and Data
This tutorial uses ADMIXTOOLS 2 in R and the AADR v66.0 release. Use Linux, macOS, or WSL with current R and enough storage for the genotype panel and extracted f2 blocks.
# In R
install.packages('remotes')
remotes::install_github('uqrmaie1/admixtools')
library(admixtools)
packageVersion('admixtools')Record the AADR release DOI, genotype-panel type, ADMIXTOOLS version, operating system, input checksums, and every transformation command. A filename such as “latest” is not sufficient provenance. The code in this tutorial was verified against admixtools 2.0.10; the package API moves, so record the version you installed and re-check argument names against your release rather than trusting any tutorial, this one included.
2. Prepare Consumer Data Safely
Raw consumer genotypes are identifying data. Keep them out of public repositories, cloud build logs, screenshots, and browser-only ancestry calculators. Work locally and publish aggregate outputs only.
Before merging with AADR:
- Confirm the consumer file’s genome build and lift over only with a documented chain.
- Normalize chromosome names, reference/alternate alleles, and strand orientation.
- Remove ambiguous A/T and C/G SNPs unless strand orientation is independently known.
- Resolve duplicate positions and sample identifiers deterministically.
- Measure post-merge overlap; never infer it from the advertised chip size.
- Create a second view only when it represents a genuinely different overlap or QC condition.
A single modern individual can be used as a target for personal exploration, but standard errors and idiosyncratic family history limit population-level interpretation. Do not call one person a representative “Jatt,” “Punjabi,” or “Indo-Aryan population.”
Platform-bias check. Consumer arrays and ancient capture data were genotyped on different platforms, and that mismatch can bias f-statistics on its own. Fournier et al. (2026) publish a compatibility SNP panel selected to minimize cross-platform artifacts; re-fit your accepted models restricted to the intersection of your merge with that panel (keepsnps in extract_f2, or the equivalent genotype-direct filter) and report the shift alongside the restricted fit’s standard error and the post-restriction SNP count — the panel is itself a restriction, so re-apply your minimum-SNP gate. If a headline proportion moves by more than the restricted fit’s standard error, treat platform bias as live rather than assumed away. This is a predeclared screening flag, not a significance test: the restricted and full fits share data, so no single standard error is an exact yardstick for their difference. In the companion analysis this check moved no coefficient by more than two points, well inside each restricted standard error.
3. Freeze Hypotheses
Write a machine-readable manifest before running qpAdm. It should name the target, candidate source families, fixed rights, chronological constraints, minimum SNP count, coefficient bounds, p-value threshold, multiple-testing procedure, and sensitivity checks.
target: TARGET_SAMPLE
source_families:
aasi: [AASI_PROXY_1, AASI_PROXY_2]
farmer: [TURAN_PROXY_1, TURAN_PROXY_2]
steppe: [STEPPE_PROXY_1, STEPPE_PROXY_2]
right_fixed: [OUTGROUP_1, OUTGROUP_2, OUTGROUP_3]
gates:
p_min: 0.05
# Feasibility bound. A slightly negative estimate whose standard error
# comfortably overlaps zero is statistical noise, not infeasibility --
# gate on estimate AND uncertainty together, not the point value alone.
coefficients: [0, 1]
require_target_younger_than_sources: true
require_source_order_invariance: true
report_all_survivors: trueThe placeholder labels are intentional. Select actual AADR population labels only after checking their archaeological date ranges, sample composition, relatedness exclusions, coverage, and publication metadata.
The right set deserves as much care as the sources, because it does all the discriminating work. Four rules cover most failures: include deep outgroups (an African pair such as Mbuti and Mota anchors the tree); include one reference differentially related to each ancestral stream your sources draw from — a right set with no leverage along some axis is how the “unresolved fourth term” trap arises; include no population directly ancestral to, or descended from, a source; and keep every proxy out of the reference set it is judged against (Harney et al., 2021). The companion analysis’s twelve-population set is a worked instance of these rules, one anchor per stream.
4. Compute f2 Blocks
ADMIXTOOLS 2 auto-detects EIGENSTRAT, packed ancestry map, PLINK, and PLINK 2 prefixes. Restrict extraction to the populations needed for the frozen analysis; large, indiscriminate right sets can create conditioning problems.
library(admixtools)
geno_prefix <- '/private/genotypes/analysis_panel'
f2_dir <- '/private/derived/f2_blocks'
analysis_pops <- unique(c(target, sources, right_fixed, rotating_candidates))
extract_f2(
pref = geno_prefix,
outdir = f2_dir,
pops = analysis_pops,
overwrite = FALSE
)
f2_blocks <- f2_from_precomp(f2_dir, pops = analysis_pops)
count_snps(f2_blocks)The default missingness behavior is deliberately conservative. The choice that actually matters is SNP-set handling: the conservative default computes statistics on SNPs shared across the extracted populations, while allsnps = TRUE computes each statistic on the SNPs available for its own populations. These are different regimes, not interchangeable settings — results can move between them, especially when a sparse consumer target meets heterogeneous-coverage capture data. Choose the regime before inspecting results, state it wherever a number is reported, and confirm key results under the alternative treatment. The companion analysis runs genotype-direct allsnps = TRUE throughout for exactly this reason, states the regime in its Methodology, and stamps it in every raw model receipt.
5. Run Model-Free Checks
Before mixture fitting, ask whether the target displays the broad relationships your hypothesis requires. Admixture f3 can support mixture between populations related to two sources; focused f4 contrasts can test whether the target distinguishes competing proxies.
# Negative f3 with |Z| >= 3 supports admixture between
# populations related to source_a and source_b.
f3_results <- f3(
f2_blocks,
pop1 = target,
pop2 = source_a,
pop3 = source_b
)
# Focused proxy comparison; choose A, B, C, D from a frozen design.
f4_results <- f4(f2_blocks, pop1 = A, pop2 = B, pop3 = C, pop4 = D)A non-significant f4 result does not prove two sources are identical. It can indicate insufficient resolution at the available SNP overlap.
6. Run and Rotate qpAdm
Run a predeclared model first, then rotate candidates systematically. The target is not part of the source vector.
fit <- qpadm(
f2_blocks,
left = sources,
right = right_fixed,
target = target
)
fit$weights
fit$rankdrop
fit$popdropThese are the documented outputs: estimated weights with block-jackknife standard errors, the rank-test table the tail probability comes from, and the nested-model comparison. There is no packaged conditioning diagnostic; near-singular designs announce themselves through exploding standard errors and unstable rank tests, which is why the gates below watch for exactly those symptoms.
For a rotating design, split a frozen candidate pool between left and right while retaining fixed references:
rotation <- qpadm_rotate(
f2_blocks,
leftright = rotating_candidates,
target = target,
rightfix = right_fixed,
full_results = TRUE
)Use qpadm_multi() when the model grid is explicitly enumerated. Preserve the complete grid, including rejected and infeasible models.
7. Apply Acceptance Gates
| Gate | Reject or flag when | Why |
|---|---|---|
| Tail probability | Below the frozen threshold after the chosen multiplicity treatment | The specified model is rejected relative to the right set. |
| Coefficients | Outside [0,1], or uncertainty crosses implausible values | Weights are infeasible or poorly identified. |
| Chronology | A population offered as a literal ancestor is younger than the target or the event | A literal ancestor cannot postdate its descendant. A younger population may still stand in for an older related lineage — but then it must be declared a proxy, not an ancestor. |
| Numerical conditioning | Standard errors explode or rank tests destabilize under small design changes | These are the visible symptoms of a near-singular design; collinear rights can make weights appear more precise than they are. |
| Stability | The conclusion collapses under source order, chromosome blocks, or leave-one-out checks | A robust interpretation should not depend on an arbitrary representation. |
| Competition | A simpler or historically better proxy model survives equally well | One passing model does not identify a unique source. |
A note on multiplicity, because qpAdm screening cuts the opposite way from most testing: when you rotate many models, the risk you usually care about is falsely retaining inadequate models, and a conventional p-value correction (Bonferroni, Benjamini–Hochberg) lowers the rejection threshold — which retains more. Declare before running which error you are controlling. When in doubt, report unadjusted p-values with the complete grid, so readers can apply whatever rule they defend; a correction chosen after seeing the results is tuning by another name.
8. Interpret Without Overclaiming
Name the target, proxies, right set, p-value, weights, uncertainty, SNP count, and software version.
State whether chronology or another prior changed the rank and list statistically similar survivors.
qpAdm p-values are not posterior probabilities of cultural or genealogical identity.
Do not interpret negative coefficients as proof that a culture contributed nothing. Do not interpret failure of one Gonur group as absence of all BMAC-related ancestry. Do not combine two chips from the same person and call them independent replication.
Three further traps deserve their own warnings, because each produces a claim that looks rigorous and is not:
- Stability across collinear proxies proves nothing about history. If every tested source for one ancestry stream descends from the same gene pool, a stable coefficient across all of them is a property of that geometry, not evidence of a single migration event. Substituting correlated instruments and getting the same answer validates their correlation, never the process. Proportion constancy bounds the combined contribution; it cannot count arrival pulses or date them.
- An unresolved fourth term is a power statement, not an absence. A fourth source that returns a coefficient with a standard error of tens of points has not been shown to contribute nothing — the design lacked the leverage to see it separately. Before reporting a null, ask whether the right set contains any population that separates the candidate from the sources already in the model; if it does not, the honest phrasing is “not resolvable as a separate stream,” never “no contribution.”
- Proxy-measured estimates carry systematic error the standard errors do not include. When a source is a stand-in for an unsampled lineage — an Andamanese population for AASI, a single site for a whole horizon — the block-jackknife uncertainty is conditional on the proxy being right. Label such numbers as measured through the proxy, and expect them to shift if a better-placed reference is ever sampled.
9. Reproducible Reporting
A publishable result should include:
- AADR release DOI and annotation version.
- Target data source, build, merge procedure, final SNP overlap, and privacy statement.
- Every source and right population with dates and sample counts.
- The full attempted model grid, not only passing models.
- Weights, standard errors, p-values, rank information, and conditioning diagnostics.
- Frozen rejection gates, multiple-testing treatment, and all sensitivity checks.
- A plain-language claim ceiling naming what the analysis cannot distinguish.
- Checksums for scripts, manifests, and aggregate outputs.
Worked South Asian Example
In the screening campaign behind the companion Sohi analysis, broad AASI-related and Steppe-related admixture is supported by model-free tests, while a Central Asian farmer-related axis recurs across the qpAdm screens. Several Steppe proxies survive the rotation: Czech Corded Ware passes 14 of 15 panels and Samara Srubnaya 13 of 15, and only 5.5% of focused Steppe-proxy pairs are statistically separable — the collinearity trap from the previous section, live in real data.
The correct conclusion from the autosomes alone is therefore not “Srubnaya was the ancestor.” It is: a Steppe-related source is robust, a Bronze Age Steppe–Turan contact window is historically admissible, and the exact Steppe and farmer cultures remain autosomally unresolved. The published analysis then narrows further — to the Sintashta–Andronovo horizon as the best-supported source — but it does so by adding evidence qpAdm does not contain: a Bronze-Age-dated paternal lineage and corridor chronology. That two-stage structure is the honest shape of the inference, and the write-up says at each step which kind of evidence is doing the work. When your own analysis reaches this point, resist collapsing the two stages into “qpAdm identified the source”; it did not, and cannot.
Evidence figure
Broad signal, narrow identification
Primary references
- ADMIXTOOLS 2 documentation and vignettes.
- Harney et al. 2021, simulation-based qpAdm assessment and user guide.
- Yüncü et al. 2024, recent and complex demographies.
- Allen Ancient DNA Resource v66.0.