just_dna_enricher.alphagenome_avi_build¶
just_dna_enricher.alphagenome_avi_build ¶
The AlphaGenome AVI artifact, re-encoded as an operator-built snapshot (RM191).
Nothing here fetches, and that is not the usual inject-only rule — this is the network tier, so
it is allowed to. The AVI artifact is 88.5 GB behind a sign-in whose eligibility clause bars whole
classes of holder (ALPHAGENOME_ATLAS.md § 2.1), so acquisition is the operator's act and this
builder reads the file they already have. --input is required for that reason and has no default
URL to fall back on.
What it writes. <out>/data/alphagenome_avi-<contig>.parquet — one row per (contig, position,
ref, alt) with raw_score as Int32 at a scale of 10⁵ — plus avi_knots.parquet, release.json
and LICENSE.txt.
Four shape decisions, each measured rather than argued (§§ 1.4, 4.4–4.9 of the probe):
Int32×10⁵, notFloat32. Both published columns print at most 5 decimals, so an integer scale is exactly lossless whileFloat32silently rounds the fifth — and is larger, 3.154 bytes/row against 2.413. Wherever a source publishes fixed decimals a float is the wrong container: its low mantissa bits are noise the source never had, and noise does not compress. The builder does not take that on trust —_scaled_scoresre-derives the scaling and refuses a value that does not land on the grid, so losslessness is checked over every row written rather than sampled.
Read the integer, do not divide it back. The exactness is about the decimal: raw_score_e5
is the printed value shifted five places, and nothing is lost. Recovering a float with
raw_score_e5 / 1e5 rounds a second time and lands one ulp off float(printed) on 53% of
rows — measured, not feared. Compare thresholds in the integer domain (score >= 0.1 becomes
raw_score_e5 >= 10_000) and the question never arises.
* PHRED is not stored. Measured over all 8,812,917,339 rows it is an exact within-corpus rank
(PHRED ≥ p keeps 10^(-p/10) of the corpus, to four significant figures across four decades),
so it is 24.7 GB of a number that is a function of raw_score. The knot table carries the curve
instead, in 466 KB.
* The knot table is rebuilt here, not copied. docs/probes/alphagenome_knots/avi_knots.parquet
is evidence; the lane's copy is the artifact, and sum(n) over its knots must equal the rows
this build wrote or the two halves describe different data.
* No threshold. The whole corpus is 34.2 GB measured keeping raw_score alone — inside any
stated budget, with the sign intact. 49.30% of rows are negative and a negative AVI is low
conservation, evidence against impact, not down-regulation (§ 4.7): abs() would discard what
half the corpus says. A threshold is a consumer's slice, not this artifact's shape.
Absence is row-absence. AVI covers about 95% of the assembly and writes 672,931 genuine
zeros, so a position with no row is unscored and a row with raw_score_e5 == 0 is scored zero.
Collapsing the two would be @unreachable-not-absent at nine-billion-row scale.
AlphaGenomeBuildError ¶
Bases: RuntimeError
The build could not produce a snapshot. Never raised for a legitimately empty contig.
ContigResult
dataclass
¶
One contig's parquet, and what went into it.
AviBuildResult
dataclass
¶
AviBuildResult(
contigs: tuple[ContigResult, ...],
rows: int,
knots: int,
negative_rows: int,
zero_rows: int,
source_path: Path,
source_sha256: str | None,
source_mtime: str | None,
dataset: str | None,
)
What the build produced, with every number it computed rather than only the ones it used.
A count computed and discarded is a count every consumer recomputes
(@dont-discard-computed), and here the recomputation costs a pass over 88.5 GB.
alts_for_ref ¶
The three ALT bases a locus with this REF carries, in the order the columns hold them.
{A,C,G,T} − ref, ascending. This is the whole reason the wide layout costs nothing to read:
a caller with (pos, ref, alt) finds its column as alts_for_ref(ref).index(alt), and a reader
reconstructing long rows walks the tuple. No lookup table travels with the artifact.
Source code in enricher/src/just_dna_enricher/alphagenome_avi_build.py
check_input_is_the_avi_artifact ¶
Refuse a file from the other two bulk artifacts, by name, before anything is read.
Not a courtesy check. The three artifacts are not one licence: only AVI is in the Permissive
class, and the merged splicing and SHAP files are non-commercial-only. A lane whose SourceRow
describes AVI, silently filled from the splicing file, would publish terms that do not govern
its bytes — which is the failure @a-hosts-terms-are-not-its-contents-terms names from the
other direction. The name is what an operator actually has; the schema check below catches the
rest.
Source code in enricher/src/just_dna_enricher/alphagenome_avi_build.py
list_contigs ¶
The contigs the tabix index knows, in the file's own order.
Read from the index rather than assembled from a chromosome list: what the artifact covers is a property of the artifact, and a hand-kept roster is how a build silently skips a contig.
Source code in enricher/src/just_dna_enricher/alphagenome_avi_build.py
to_long ¶
One row per (chrom, pos, ref, alt) from the artifact's wide rows (RM197).
The inverse of the layout, and it needs nothing but ref. The snapshot stores one row per
position with three score columns, and which base each column means is {A,C,G,T} − ref
ascending — so long form is recoverable with no stored alt and no lookup table travelling
beside the data. That is what made the wide layout adoptable rather than a schema break: a
consumer who wants (pos, ref, alt, score) rows calls this and gets them.
It lives here rather than in the reader because it is a fact about the artifact, and three
callers now need it to agree — alphagenome_check's join, the tests, and anyone converting a
pulled snapshot back to long after download.
Apply it to a filtered frame. Over the whole corpus it is 2.9 billion loci becoming 8.8 billion rows, which is the shape the artifact exists to avoid storing.
Source code in enricher/src/just_dna_enricher/alphagenome_avi_build.py
use_restrictions_text ¶
The Output Terms' "Use restrictions" section, verbatim, for LICENSE.txt.
Restriction 3b requires it to travel inside a derivative rather than as a link: anyone who
attaches their own terms — which a module's sources.csv is — must carry this section "as an
enforceable provision". So the bytes go beside the data, the way ClinPGx's bundled LICENSE.txt
already does (SNAPSHOT_LICENSE_FILENAME exists for exactly this).
Sliced from the pinned extraction rather than paraphrased, and bounded at the next heading so the disclaimer sections do not ride along under a name that says "use restrictions".
Source code in enricher/src/just_dna_enricher/alphagenome_avi_build.py
build_snapshot ¶
build_snapshot(
source: Path,
out_dir: Path,
*,
contigs: Sequence[str] | None = None,
workers: int = 12,
chunk_bytes: int = CHUNK_BYTES,
hash_source: bool = True,
terms_file: Path | None = None,
) -> AviBuildResult
Re-encode the AVI artifact into out_dir, and rebuild the knot table from the same pass.
contigs restricts the build — a test builds one, an operator builds all of them. workers is
the tabix fan-out: the measured passes ran 24 contigs in 41–46 minutes at twelve, and a
single-threaded pass over 8.8 billion rows takes roughly four times as long.
Threads rather than processes because every worker spends its time in a tabix subprocess and
in polars, both of which are outside the GIL; a process pool would buy nothing and cost the
chunk-sized copies.
Source code in enricher/src/just_dna_enricher/alphagenome_avi_build.py
691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 | |