triohap 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. triohap-0.4.0/CHANGELOG.md +46 -0
  2. triohap-0.4.0/GVCF.md +103 -0
  3. triohap-0.4.0/LICENSE +21 -0
  4. triohap-0.4.0/MANIFEST.in +14 -0
  5. triohap-0.4.0/PKG-INFO +345 -0
  6. triohap-0.4.0/PUBLISHING.md +98 -0
  7. triohap-0.4.0/README.md +312 -0
  8. triohap-0.4.0/SCN5A.md +112 -0
  9. triohap-0.4.0/VALIDATION.txt +53 -0
  10. triohap-0.4.0/examples/gvcf/README.md +36 -0
  11. triohap-0.4.0/examples/gvcf/all-sites.vcf +12 -0
  12. triohap-0.4.0/examples/gvcf/child.vcf +12 -0
  13. triohap-0.4.0/examples/gvcf/father.vcf +12 -0
  14. triohap-0.4.0/examples/gvcf/joint.vcf +12 -0
  15. triohap-0.4.0/examples/gvcf/mother.vcf +12 -0
  16. triohap-0.4.0/examples/gvcf/panel.tsv +4 -0
  17. triohap-0.4.0/examples/individual/child.vcf +16 -0
  18. triohap-0.4.0/examples/individual/father.vcf +16 -0
  19. triohap-0.4.0/examples/individual/mother.vcf +16 -0
  20. triohap-0.4.0/examples/panel.tsv +10 -0
  21. triohap-0.4.0/examples/trio.vcf +16 -0
  22. triohap-0.4.0/examples/unfazed.vcf +8 -0
  23. triohap-0.4.0/pyproject.toml +48 -0
  24. triohap-0.4.0/reference-data/scn5a-promoter-pangenome/CITATION.cff +40 -0
  25. triohap-0.4.0/reference-data/scn5a-promoter-pangenome/LICENSE +21 -0
  26. triohap-0.4.0/reference-data/scn5a-promoter-pangenome/README.md +21 -0
  27. triohap-0.4.0/reference-data/scn5a-promoter-pangenome/config/config.yaml +68 -0
  28. triohap-0.4.0/reference-data/scn5a-promoter-pangenome/results/provenance.yaml +26 -0
  29. triohap-0.4.0/reference-data/scn5a-promoter-pangenome/results/tables/haplotype_assignments.core.tsv +464 -0
  30. triohap-0.4.0/reference-data/scn5a-promoter-pangenome/results/tables/haplotype_matrix.core.tsv +464 -0
  31. triohap-0.4.0/reference-data/scn5a-promoter-pangenome/results/tables/haplotypes.core.tsv +47 -0
  32. triohap-0.4.0/reference-data/scn5a-promoter-pangenome/results/tables/summary.core.json +16 -0
  33. triohap-0.4.0/reference-data/scn5a-promoter-pangenome/results/tables/variant_sites.core.tsv +46 -0
  34. triohap-0.4.0/scn5a-panel/audit.json +151 -0
  35. triohap-0.4.0/scn5a-panel/catalogue.json +5756 -0
  36. triohap-0.4.0/scn5a-panel/markers.snps.bed +37 -0
  37. triohap-0.4.0/scn5a-panel/panel.snps.tsv +38 -0
  38. triohap-0.4.0/scn5a-panel/validation.json +12 -0
  39. triohap-0.4.0/scripts/validate_distribution.py +227 -0
  40. triohap-0.4.0/scripts/validate_gvcf.py +186 -0
  41. triohap-0.4.0/scripts/validate_synthetic.py +168 -0
  42. triohap-0.4.0/setup.cfg +4 -0
  43. triohap-0.4.0/tests/test_catalogue.py +121 -0
  44. triohap-0.4.0/tests/test_gvcf.py +373 -0
  45. triohap-0.4.0/tests/test_individual_vcfs.py +253 -0
  46. triohap-0.4.0/tests/test_inference.py +312 -0
  47. triohap-0.4.0/tests/test_origin.py +48 -0
  48. triohap-0.4.0/tests/test_source_provenance.py +16 -0
  49. triohap-0.4.0/triohap/__init__.py +2 -0
  50. triohap-0.4.0/triohap/__main__.py +3 -0
  51. triohap-0.4.0/triohap/catalogue.py +297 -0
  52. triohap-0.4.0/triohap/catalogue_plot.py +248 -0
  53. triohap-0.4.0/triohap/cli.py +73 -0
  54. triohap-0.4.0/triohap/core.py +320 -0
  55. triohap-0.4.0/triohap/gvcf.py +261 -0
  56. triohap-0.4.0/triohap/origin.py +85 -0
  57. triohap-0.4.0/triohap/report.py +261 -0
  58. triohap-0.4.0/triohap/vcf.py +431 -0
  59. triohap-0.4.0/triohap.egg-info/PKG-INFO +345 -0
  60. triohap-0.4.0/triohap.egg-info/SOURCES.txt +64 -0
  61. triohap-0.4.0/triohap.egg-info/dependency_links.txt +1 -0
  62. triohap-0.4.0/triohap.egg-info/entry_points.txt +2 -0
  63. triohap-0.4.0/triohap.egg-info/requires.txt +8 -0
  64. triohap-0.4.0/triohap.egg-info/top_level.txt +1 -0
  65. triohap-0.4.0/validation/gvcf_summary.json +14 -0
  66. triohap-0.4.0/validation/synthetic_summary.json +13 -0
@@ -0,0 +1,46 @@
1
+ # Changelog
2
+
3
+ ## 0.4.0
4
+
5
+ - License TrioHap under MIT and include the licence in both PyPI distributions.
6
+ - Prepare the first PyPI distribution under the name `triohap` (previous local
7
+ wheels used `triohap-prototype`); the Python import and command stay `triohap`.
8
+ - Add Trusted Publishing, wheel/source-archive validation, project metadata,
9
+ complete source-distribution test fixtures, and `triohap --version`.
10
+ - Automatically detect VCF and conventional END-based gVCF input from content,
11
+ using the existing joint and three-file arguments; mixed input is supported.
12
+ - Materialize explicit homozygous-reference block evidence only at panel SNPs
13
+ and explicit trio SNP anchors, with per-sample quality and coverage checks.
14
+ - Preserve unknown calls, explicit-record precedence, wildcard allele indices,
15
+ and overlapping unsupported-variant exclusions. Reject ambiguous blocks and
16
+ unsupported sample-specific FORMAT/LEN blocks rather than guessing calls.
17
+ - Report detected formats, reference-block processing counts, and call sources.
18
+ - Add synthetic VCF/gVCF equivalence tests and reproducible examples.
19
+
20
+ ## 0.3.0
21
+
22
+ - Preserve the original SCN5A source-table line endings so saved byte checksums
23
+ remain reproducible after checkout; test exact catalogue reimport.
24
+
25
+ - Establish `comparativechrono/TrioHap` as the project repository.
26
+ - Accept either a joint VCF or three individual VCFs with explicit role/sample
27
+ selections. Decode each file's REF/ALT allele dictionary independently.
28
+ - Preserve child phase sets and the same quality filters in both input modes.
29
+ Absent records remain unknown; only complete usable trio sites are scored.
30
+ - Report input files, per-file metadata and exclusions. Reject incompatible
31
+ references and contig lengths before combining individual inputs.
32
+ - Add deterministic synthetic equivalence and truth-recovery validation, plus
33
+ automated unit tests and report-generation checks.
34
+ - Result schema is now `0.2`: individual inputs use a structured `sources.trio`
35
+ object; joint inputs retain the original path string.
36
+
37
+ ## 0.2.0
38
+
39
+ - Import the SCN5A source catalogue, retaining all 46 class identities and
40
+ full-variant graph topology while matching on the 37 supported SNP records.
41
+ - Preserve SNP-equivalent candidate sets and panel provenance.
42
+
43
+ ## 0.1.0
44
+
45
+ - Initial joint-trio SNP matcher, phase-block handling, ambiguity-aware ranking,
46
+ optional Unfazed annotation, and self-contained reports.
triohap-0.4.0/GVCF.md ADDED
@@ -0,0 +1,103 @@
1
+ # Automatic VCF and gVCF input
2
+
3
+ The same `--trio` or `--child-vcf` / `--mother-vcf` / `--father-vcf` arguments
4
+ accept ordinary VCFs and conventional gVCFs. No format flag is required. The
5
+ three individual inputs may use different encodings, and compressed input is
6
+ supported. Explicit child GT order and PS blocks retain their existing meaning.
7
+
8
+ ## Detection and supported records
9
+
10
+ Detection uses observed `<NON_REF>` or `<*>` alleles, GVCFBlock metadata, and
11
+ nonvariant END records. A filename or a standalone NON_REF ALT declaration in
12
+ the header is insufficient. Genotyped VCFs can retain that declaration without
13
+ containing any gVCF blocks.
14
+
15
+ The supported compressed representation is a nonvariant record with one
16
+ reference base, `INFO/END`, and no literal alternate allele: `ALT=<NON_REF>`,
17
+ `ALT=<*>`, or `ALT=.`. Both block endpoints are included. Unbanded wildcard
18
+ reference records can also contribute evidence at their explicit position.
19
+ END on a deletion or another structural variant is not a reference block.
20
+
21
+ A SNP record with `ALT=C,<NON_REF>` and `GT=0/1` has a literal A/C call when
22
+ REF is A. The unused symbolic allele is not scored. `GT=0/2` at the same record
23
+ is unresolved and excluded. Allele indices are decoded before symbols are
24
+ handled; they are never renumbered by stripping the ALT list. Bare `*` is a
25
+ spanning-deletion allele and is not interchangeable with `<*>`.
26
+
27
+ ## Reference-call requirements
28
+
29
+ Each selected sample is evaluated independently, including samples in a joint
30
+ file. An expanded call requires:
31
+
32
+ - A complete diploid homozygous-reference GT (`0/0` or `0|0`).
33
+ - FILTER and sample FT equal to PASS, dot, or absent where allowed.
34
+ - At least one supplied, finite, positive GQ, RGQ, or GQX value. The minimum
35
+ available value must meet `--min-gq`; zero-quality reference calls remain
36
+ unknown even when the requested threshold is zero.
37
+ - No known zero DP or MIN_DP. Missing depth can be retained with usable
38
+ quality, but its absence is reported in provenance. MED_DP is not used as a
39
+ substitute for minimum depth.
40
+
41
+ Malformed quality, depth, and interval fields are errors rather than invented
42
+ confidence. Missing quality, failed filters, incomplete/non-reference GTs,
43
+ and low quality produce exclusions. The program does not estimate missing
44
+ genotypes from likelihoods or population frequency.
45
+
46
+ For explicit SNP records, GQX is also honored alongside GQ, using the lower
47
+ available value. RGQ applies to homozygous-reference genotypes. As in earlier
48
+ versions, explicit SNP calls lacking quality fields are retained but counted
49
+ as lacking quality; a threshold cannot validate missing quality.
50
+
51
+ ## Coordinates, precedence, and phasing
52
+
53
+ Only panel SNPs and explicit SNPs from the trio are considered as targets.
54
+ This retains trio-only phase anchors without expanding every base in a
55
+ reference block. A block starting before the selected region can cover a
56
+ target inside it. END is inclusive.
57
+
58
+ The block REF identifies the nucleotide at its start only. For an interior
59
+ target, the reference base comes from the panel or an explicit SNP record at
60
+ that position. Inputs must share reference build and contig naming. Known
61
+ build/contig-length conflicts and REF disagreements are rejected; no FASTA
62
+ validation or liftover is performed.
63
+
64
+ An explicit record always takes precedence at its position, including a
65
+ no-call, failed filter, or unsupported variant. Unsupported variant spans
66
+ also prevent a reference block from filling affected positions. Overlapping
67
+ reference blocks in one input are rejected so a conflicting quality or call
68
+ cannot be selected arbitrarily.
69
+
70
+ Reference blocks provide homozygous calls, not new heterozygous phase links.
71
+ Existing child variant GT/PS evidence is preserved. A trio-only SNP may orient
72
+ a child phase block when the other members have usable covering reference
73
+ blocks; a missing or rejected parental call cannot provide that anchor.
74
+
75
+ ## Reporting and API
76
+
77
+ `input_metadata` in `matches.json` records each file's detected format,
78
+ detection evidence, block count, and materialized/excluded reference-call
79
+ counts. These input-processing counts precede the final shared-marker and
80
+ Mendelian filters. Retained SNP records with expanded calls include
81
+ `call_sources` identifying the contributing block. The HTML report summarizes
82
+ input detection; detailed exclusions remain available in JSON and TSV.
83
+
84
+ Python callers can pass `reference_sites=panel['records']` to either
85
+ `read_trio_vcf` or `read_trio_vcfs`. Without this argument, only explicit trio
86
+ SNP targets can be recovered; reference blocks do not supply an entire local
87
+ reference sequence. The CLI passes the panel's sites automatically.
88
+
89
+ ## Limits and sources
90
+
91
+ Newer VCF 4.5 sample-specific FORMAT/LEN blocks and implicit GT inheritance are
92
+ not implemented and produce an explicit unsupported-encoding error. This
93
+ restriction avoids applying sample-specific intervals as if they were shared
94
+ END blocks. BCF and indexed random access remain future work.
95
+
96
+ The implementation uses hard genotypes and confidence fields; it is not a
97
+ replacement for a joint variant caller. It still matches diploid autosomal
98
+ SNP backgrounds and has been tested only on synthetic trios.
99
+
100
+ - [GATK gVCF format](https://gatk.broadinstitute.org/hc/en-us/articles/360035531812-GVCF-Genomic-Variant-Call-Format)
101
+ - [DeepVariant gVCF support](https://github.com/google/deepvariant/blob/r1.10/docs/deepvariant-gvcf-support.md)
102
+ - [Illumina gVCF conventions](https://help.basespace.illumina.com/files-used-by-basespace/gvcf-files)
103
+ - [VCF 4.5 specification, reference blocks](https://samtools.github.io/hts-specs/VCFv4.5.pdf)
triohap-0.4.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 comparativechrono and TrioHap contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,14 @@
1
+ include README.md CHANGELOG.md GVCF.md SCN5A.md PUBLISHING.md VALIDATION.txt
2
+ include MANIFEST.in LICENSE
3
+ recursive-include tests *.py
4
+ recursive-include scripts *.py
5
+ recursive-include examples *.vcf *.tsv *.md
6
+ recursive-include reference-data/scn5a-promoter-pangenome *.md *.cff *.yaml *.tsv *.json
7
+ include reference-data/scn5a-promoter-pangenome/LICENSE
8
+ include scn5a-panel/catalogue.json
9
+ include scn5a-panel/panel.snps.tsv
10
+ include scn5a-panel/markers.snps.bed
11
+ include scn5a-panel/audit.json
12
+ include scn5a-panel/validation.json
13
+ recursive-include validation *.json
14
+ global-exclude __pycache__ *.py[cod]
triohap-0.4.0/PKG-INFO ADDED
@@ -0,0 +1,345 @@
1
+ Metadata-Version: 2.4
2
+ Name: triohap
3
+ Version: 0.4.0
4
+ Summary: Trio-aware matching of VCF and gVCF genotypes to regional pangenome haplotypes
5
+ Maintainer: comparativechrono
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/comparativechrono/TrioHap
8
+ Project-URL: Documentation, https://github.com/comparativechrono/TrioHap/blob/main/README.md
9
+ Project-URL: Source, https://github.com/comparativechrono/TrioHap
10
+ Project-URL: Issues, https://github.com/comparativechrono/TrioHap/issues
11
+ Project-URL: Changelog, https://github.com/comparativechrono/TrioHap/blob/main/CHANGELOG.md
12
+ Keywords: bioinformatics,pangenome,haplotype,trio,VCF,gVCF
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
23
+ Requires-Python: >=3.10
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Provides-Extra: plots
27
+ Requires-Dist: numpy>=1.24; extra == "plots"
28
+ Requires-Dist: matplotlib>=3.7; extra == "plots"
29
+ Provides-Extra: dev
30
+ Requires-Dist: build>=1.2.2; extra == "dev"
31
+ Requires-Dist: twine>=6.1; extra == "dev"
32
+ Dynamic: license-file
33
+
34
+ # TrioHap
35
+
36
+ Rank assembly-derived regional haplotype matches for a child using the child,
37
+ mother and father's SNP genotypes. Show the maternal and paternal assignments
38
+ on a marker-similarity network, preserving ambiguous matches and extra variants.
39
+
40
+ This is a small, inspectable research prototype, not a clinically validated
41
+ genotyper. Development lives in
42
+ [comparativechrono/TrioHap](https://github.com/comparativechrono/TrioHap).
43
+ The matching engine uses Python's standard library. Plots require NumPy and
44
+ Matplotlib. The PyPI distribution is named **`triohap`**.
45
+
46
+ Version 0.4 automatically detects VCF and conventional gVCF input, including
47
+ mixed individual files. Both input modes feed the same trio inference model
48
+ and produce the same rankings for equivalent evidence. SCN5A catalogue import
49
+ preserves full haplotype identities and graph topology; see [SCN5A.md](https://github.com/comparativechrono/TrioHap/blob/main/SCN5A.md).
50
+ All trio testing so far is synthetic.
51
+
52
+ ## Installation
53
+
54
+ Install with Python 3.10 or newer:
55
+
56
+ ```bash
57
+ python -m pip install 'triohap[plots]'
58
+ triohap --version
59
+ triohap --help
60
+ ```
61
+
62
+ For the standard-library matching engine without graph dependencies, install
63
+ `triohap` and pass `--no-plot`. Plotting is optional, but enabled by default in
64
+ the command, so the `[plots]` installation is recommended for normal use.
65
+
66
+ The package name, command, and Python import are all `triohap`. Older local
67
+ prototype wheels were called `triohap-prototype`; uninstall that distribution
68
+ before installing `triohap`, because both contain the same Python package.
69
+ The wheel contains the application. The source distribution also contains the
70
+ examples, tests, documentation, and SCN5A provenance fixtures; these are not
71
+ installed into Python's package directory. The source archive can be unpacked
72
+ to run the examples below, or use a repository checkout.
73
+
74
+ ## Run the synthetic example
75
+
76
+ From this directory, with Python 3.10 or newer:
77
+
78
+ ```bash
79
+ python -m pip install '.[plots]'
80
+ python -m triohap \
81
+ --trio examples/trio.vcf --panel examples/panel.tsv \
82
+ --child child --mother mother --father father \
83
+ --region chr1:100-290 --build synthetic \
84
+ --unfazed-vcf examples/unfazed.vcf \
85
+ --title 'Synthetic example: two inherited backgrounds' \
86
+ --out example-run
87
+ python -m unittest discover -s tests -v
88
+ python scripts/validate_synthetic.py
89
+ python scripts/validate_gvcf.py
90
+ ```
91
+
92
+ The expected result is maternal **H1/H1_like** (indistinguishable on observed
93
+ SNPs), paternal **H2**, with an inherited mismatch at position 200. Position
94
+ 220 is Mendelian-inconsistent and excluded from the matching score; the
95
+ separate synthetic Unfazed file places its alternate allele on the paternal
96
+ background. Position 240 is present only in the trio file. Position 260 is
97
+ present only in the panel and must not distinguish H1 from H1_like. Position
98
+ 280 has a missing father genotype and is excluded.
99
+
100
+ ## Inputs
101
+
102
+ 1. A joint trio VCF/gVCF (`--trio`), or three individual files (`--child-vcf`,
103
+ `--mother-vcf`, `--father-vcf`), with explicit diploid GTs. VCF versus gVCF
104
+ is detected automatically from content; no type flag is required. Plain and
105
+ gzip-compressed inputs are supported. Specify sample names through `--child`,
106
+ `--mother` and `--father`. Genotype quality filtering is available with
107
+ `--min-gq`; genotype likelihoods are not used.
108
+ 2. A panel VCF with phased diploid or haploid samples, or a tab-separated allele
109
+ matrix. Each panel column identifies an assembly haplotype. In a diploid VCF,
110
+ the two phased sample chromosomes become separate panel entries.
111
+ 3. One autosomal region, using `CHROM:START-END` (1-based, inclusive).
112
+ 4. `--build` records the coordinate reference you assert the inputs use. It is
113
+ provenance, **not** a liftover or reference-sequence validation procedure.
114
+
115
+ Panel TSV example:
116
+
117
+ ```text
118
+ CHROM POS REF assembly_A_hap1 assembly_B_hap2
119
+ chr1 100 A G A
120
+ chr1 120 C C T
121
+ ```
122
+
123
+ Use real tabs, A/C/G/T alleles, and `.` for missing. The same locus/reference
124
+ coordinates must be used in both files. A VCF REF disagreement is an error.
125
+ Allele order may differ: actual nucleotides, not numeric ALT indices, are
126
+ compared. Duplicate positions must be reconciled upstream; this prototype
127
+ does not silently combine split multiallelic records.
128
+
129
+ Only shared usable SNP calls enter the matching calculation. Missing VCF
130
+ records are **unknown**, never automatically homozygous reference. For gVCFs,
131
+ explicit reference-confidence blocks can supply calls at the panel's SNP
132
+ positions and explicit trio SNP anchors. Blocks must pass the checks described
133
+ below; uncovered positions remain unknown.
134
+ The panel must supply complete calls at each used marker across all candidate
135
+ haplotypes; incomplete markers are dropped globally, with diagnostics.
136
+
137
+ ### Three individual VCFs
138
+
139
+ ```bash
140
+ python -m triohap \
141
+ --child-vcf examples/individual/child.vcf \
142
+ --mother-vcf examples/individual/mother.vcf \
143
+ --father-vcf examples/individual/father.vcf \
144
+ --child child --mother mother --father father \
145
+ --panel examples/panel.tsv \
146
+ --region chr1:100-290 --build synthetic \
147
+ --out example-run-individual
148
+ ```
149
+
150
+ Supply all three paths together; they cannot be mixed with `--trio`. Each
151
+ sample ID selects a column in its corresponding file. Files may contain
152
+ additional samples. The same sample name (for example `SAMPLE`) is allowed
153
+ in different files; the CLI role specifies which family member it represents.
154
+
155
+ The reader compares coordinates and REF alleles, decodes each file's ALT
156
+ indices independently, and preserves the child's GT order and PS values.
157
+ Different ALT ordering or different called alternate alleles do not require
158
+ a joint-genotyping step. Known build/contig-length conflicts or disagreeing
159
+ REF alleles are errors; no liftover or allele normalization is performed.
160
+
161
+ Only sites with usable evidence for **all three** people enter the
162
+ current model. A missing parent record, `./.`, a failed FILTER/FT, or a call
163
+ below the requested GQ threshold cannot be replaced by `0/0`. Exclusions are
164
+ reported. This means variant-only individual VCFs can provide much less
165
+ evidence than joint VCFs with reference calls. Supply gVCFs, explicit all-sites
166
+ VCFs, or targeted genotypes when reference evidence is needed. An explicit
167
+ failed or missing call takes precedence over a covering reference block.
168
+
169
+ The three-file mode preserves parental uncertainty in exactly the same way
170
+ as the joint mode. It does not infer parental phase from the order of alleles
171
+ in unphased GTs, and it does not equate phase-set numbers across family members.
172
+
173
+ ### Automatic gVCF input
174
+
175
+ Use the same arguments for `.vcf`, `.g.vcf`, `.gvcf`, or their gzip-compressed
176
+ versions. The filename does not decide the input type. For example:
177
+
178
+ ```bash
179
+ triohap \
180
+ --child-vcf child.g.vcf.gz --mother-vcf mother.g.vcf.gz --father-vcf father.vcf.gz \
181
+ --child CHILD --mother MOTHER --father FATHER \
182
+ --panel scn5a-panel/catalogue.json \
183
+ --region chr3:38649188-38652187 --build GRCh38 \
184
+ --min-gq 20 --out family-scn5a
185
+ ```
186
+
187
+ Conventional `INFO/END` blocks with `<NON_REF>`, `<*>`, or nonvariant `ALT=.`
188
+ are supported. Block calls require diploid `0/0`, usable FILTER/FT, and a
189
+ present positive quality meeting `--min-gq`. The most conservative available
190
+ GQ/RGQ/GQX is used for blocks. Known zero DP or MIN_DP excludes a block call;
191
+ missing depth is recorded but does not by itself override usable quality.
192
+ Explicit SNP records may contain an unused wildcard ALT without losing their
193
+ literal genotype calls. Calling the wildcard itself remains unresolved.
194
+
195
+ Blocks are queried only at relevant SNP positions, including blocks beginning
196
+ before the requested region. Interior reference bases come from the same-build
197
+ panel or explicit SNP records, not the block's first base. The report shows
198
+ detected formats and reference-call counts; JSON records each expanded call's
199
+ source. See [GVCF.md](https://github.com/comparativechrono/TrioHap/blob/main/GVCF.md) for the precise rules and supported encodings.
200
+
201
+ This consumes hard-called evidence; it does not perform joint genotyping or
202
+ use PL/GL likelihoods. Newer VCF 4.5 sample-specific `FORMAT/LEN` blocks are
203
+ currently rejected with an explicit error. Indel/SV genotypes remain outside
204
+ the matching model.
205
+
206
+ The prototype accepts neither raw unaligned haplotype FASTA nor a GFA graph.
207
+ First project assembly haplotypes to comparable SNP coordinates, retaining
208
+ their original assembly IDs. FASTA-to-panel preparation is a separate proposed
209
+ module; it must resolve orthology, coordinate mapping and paralog ambiguity.
210
+
211
+ ## Inference
212
+
213
+ At each Mendelian-consistent SNP, enumerate possible maternal and paternal
214
+ transmitted alleles conditional on the trio GTs. Do not force origin when all
215
+ three are heterozygous. For each ordered pair of panel haplotypes, sum over
216
+ the possible transmissions using mismatch-tolerant emissions. A match has
217
+ weight `1-epsilon`; a different nucleotide has weight `epsilon/3`.
218
+
219
+ For externally phased child SNPs, marginalize two **whole-block** orientations.
220
+ Informative trio SNPs anchor a block to parental origin. The first GT allele
221
+ is not inherently maternal. Separate PS blocks orient independently. Following
222
+ VCF semantics, phased GTs without PS share an implicit phase set on a chromosome.
223
+ Contradictory Mendelian anchors in one phase block cause an explicit error;
224
+ `--ignore-phase` deliberately discards external phase when investigating it.
225
+ Parent GT phase blocks are not modeled in this first version.
226
+
227
+ Panel haplotypes identical at the observed, scored markers form one equivalence
228
+ class. Ranking gives each distinct observed class equal prior weight; adding
229
+ duplicate assemblies does not inflate support. Pair scores are normalized
230
+ across **all** ordered class pairs before the top-N rows are reported.
231
+
232
+ The resulting relative weights are conditional on the chosen panel, markers,
233
+ hard calls, phase and mismatch parameter. They are **not calibrated clinical
234
+ posterior probabilities**. SNPs in LD are not independent observations of an
235
+ ancestral history. The score does not establish unobserved SVs or prove identity
236
+ to an entire assembly sequence. There is no 'none of the above' model: inspect
237
+ absolute mismatches and marker coverage even when relative weight is high.
238
+
239
+ ## Outputs
240
+
241
+ - `matches.json`: model, provenance, classes, top ordered pairs, marginal
242
+ rankings, marker evidence and diagnostics. It records input mode and file
243
+ provenance. Under result schema `0.2`, `sources.trio` is a path for joint
244
+ input and a role/file/sample object for individual input.
245
+ - `ranked_pairs.tsv`: best maternal/paternal class combinations.
246
+ - `diagnostics.tsv`: omitted, unmatched and problematic records.
247
+ - `mismatches.tsv`: observed alleles differing from the first best-scoring pair.
248
+ - `haplotype_graph.svg` / `.png`: SNP similarity network and observed-allele
249
+ comparison, with two virtual proband chromosome nodes.
250
+ - `report.html`: self-contained report containing the graph and results.
251
+
252
+ Graph edges between panel nodes show a deterministic minimum spanning tree
253
+ of Hamming distances over the scored marker signatures. Edge labels count
254
+ different marker alleles. This is a compact similarity network, not a
255
+ genealogy, recombination graph or sequence-level pangenome graph. Dashed colored
256
+ links show leading marginal placements of each proband chromosome, including
257
+ ties. For large panels the image displays a stated subset; the JSON retains
258
+ all classes and marginals. The first listed pair in a tie is not a unique call.
259
+
260
+ ## Optional Unfazed integration
261
+
262
+ The PyPI package matching the discussion appears to be
263
+ [unfazed-bioinformatics](https://pypi.org/project/unfazed-bioinformatics/), the
264
+ maintained fork of Jonathan Belyeu's Unfazed. Its identity as the user's package
265
+ has not been independently confirmed. It phases de novo variants to parental
266
+ origin; it is not a general regional haplotype matching program.
267
+
268
+ `--unfazed-vcf` accepts its documented parent-ordered VCF output as **annotation**
269
+ of additional variants. A provenance header stating `1|0 is paternal, 0|1 is
270
+ maternal` is required. Only confident evidence types UET 0/1/2 with phased GTs
271
+ are assigned to a parental background. Ambiguous evidence is retained without
272
+ an assignment. Sex-chromosome autophasing is outside this prototype. UOPS is
273
+ reported as a count, not converted to probability. These annotations do not
274
+ alter the panel ranking, avoiding double-counting the same reads/markers.
275
+ Sample FT filters, obvious declared build conflicts, and disagreements with
276
+ overlapping trio REF/GT calls are checked. Where metadata or trio calls are
277
+ absent, annotation consistency cannot be established from this file alone.
278
+
279
+ ## Scope and next development steps
280
+
281
+ The current model is diploid, autosomal, SNP-based and fixed to one panel
282
+ haplotype per parental chromosome across the selected region. It excludes
283
+ Mendelian-inconsistent sites rather than estimating de novo or genotyping-error
284
+ probabilities. Indels/SVs/CNVs, sex chromosomes, mosaicism, PL/GL integration,
285
+ within-region recombination, parent phase propagation and a sparse-panel
286
+ missingness model remain future work. FASTA/GFA panel preparation and indexed
287
+ BCF/VCF access are also future work. Large files are streamed but not region
288
+ indexed. The pair search scales quadratically in distinct haplotype classes. Deterministic
289
+ phase contributions are cached; ambiguous-block work is capped at 25 million
290
+ pair/block combinations in this prototype.
291
+
292
+ Before clinical use: test on independent trios with assembly/long-read truth;
293
+ leave entire families out of the panel; evaluate private variants, phase-switch
294
+ errors, ancestry representation and missingness; calibrate no-call/ambiguity
295
+ criteria and sensitivity to epsilon. Do not infer clinical correctness from
296
+ the synthetic unit tests.
297
+
298
+ ## Development and reproducibility
299
+
300
+ Run `python -m unittest discover -s tests -v` and
301
+ `python scripts/validate_synthetic.py` from the repository root. The latter
302
+ constructs known inherited haplotypes and independently encodes equivalent
303
+ joint and individual VCFs, then checks truth recovery and matching equivalence.
304
+ It also checks deliberate missingness. These are software correctness checks,
305
+ not estimates of performance on clinical data.
306
+
307
+ GitHub Actions runs the test suite on Python 3.10–3.13 and exercises graph
308
+ generation separately. `demo-output/` and `demo-individual-output/` contain equivalent synthetic
309
+ reports from the two input modes;
310
+ `scn5a-panel/` contains the imported reference catalogue and its panel audit.
311
+ Source tables used to create that catalogue are retained under
312
+ `reference-data/scn5a-promoter-pangenome/`, with their supplied license and
313
+ provenance. See [CHANGELOG.md](https://github.com/comparativechrono/TrioHap/blob/main/CHANGELOG.md)
314
+ and [VALIDATION.txt](https://github.com/comparativechrono/TrioHap/blob/main/VALIDATION.txt).
315
+
316
+ Release preparation and the exact PyPI Trusted Publisher settings are in
317
+ [PUBLISHING.md](https://github.com/comparativechrono/TrioHap/blob/main/PUBLISHING.md).
318
+ Packaging CI builds a wheel from the source archive, checks PyPI metadata,
319
+ and tests the installed wheel outside the checkout, including graph generation.
320
+ The project version is maintained in `triohap/__init__.py`.
321
+
322
+ TrioHap is licensed under the
323
+ [MIT licence](https://github.com/comparativechrono/TrioHap/blob/main/LICENSE).
324
+ The supplied SCN5A reference-data subset retains its own licence and attribution
325
+ under `reference-data/scn5a-promoter-pangenome/`.
326
+
327
+ The gVCF examples are in `examples/gvcf/`, with a rendered report in
328
+ `demo-gvcf-output/`. Run `python scripts/validate_gvcf.py` to compare known
329
+ synthetic families across ordinary VCF, joint gVCF, individual gVCF, and mixed
330
+ inputs, including deliberate reference-block gaps. The saved summary is
331
+ `validation/gvcf_summary.json`.
332
+
333
+ ## Related components and specifications
334
+
335
+ - [Osprey](https://github.com/broadinstitute/Osprey): phased SNP reference
336
+ haplotype matching for CNV/VNTR imputation. Its documented matching uses SNPs
337
+ outside the target interval; it does not supply this combined trio workflow.
338
+ - [trioPhaser](https://pmc.ncbi.nlm.nih.gov/articles/PMC8607709/): trio-based
339
+ phasing using Mendelian rules plus statistical phasing.
340
+ - [VCF 4.5 specification](https://samtools.github.io/hts-specs/VCFv4.5.pdf): phase
341
+ set and genotype semantics.
342
+ - [Unfazed paper](https://academic.oup.com/bioinformatics/article/37/24/4860/6306405)
343
+ and the maintained PyPI distribution linked above.
344
+
345
+ No external tool's implementation is incorporated into the matching engine.
@@ -0,0 +1,98 @@
1
+ # Publishing TrioHap to PyPI
2
+
3
+ The first PyPI release is `triohap` version `0.4.0`. The older
4
+ GitHub-only wheels used the distribution name `triohap-prototype`. The import
5
+ package and executable remain `triohap`. Preparing or merging this workflow
6
+ does not publish a package.
7
+
8
+ ## Register the pending publisher
9
+
10
+ At <https://pypi.org/manage/account/publishing/>, select **GitHub** and enter:
11
+
12
+ | Field | Exact value |
13
+ | --- | --- |
14
+ | PyPI project name | `triohap` |
15
+ | Owner | `comparativechrono` |
16
+ | Repository name | `TrioHap` |
17
+ | Workflow name | `publish.yml` |
18
+ | Environment name | `pypi` |
19
+
20
+ Enter only `publish.yml`, not `.github/workflows/publish.yml` and not the workflow
21
+ display title. The workflow lives at `.github/workflows/publish.yml`.
22
+ There is no PyPI API token or GitHub secret to create. The pending publisher
23
+ creates the PyPI project on its first successful use; it does not reserve the
24
+ name. The project name in PyPI must match the distribution metadata.
25
+
26
+ The GitHub environment `pypi` is configured to allow tags matching `v*` only.
27
+ No secrets are needed. The current private-repository settings do not offer a
28
+ required-reviewer gate; publishing a GitHub release is the deliberate publishing
29
+ action. Keep release and workflow changes restricted to trusted maintainers.
30
+
31
+ ## Validate without publishing
32
+
33
+ The **Build and publish to PyPI** workflow offers **Run workflow**. A manual run
34
+ builds and validates the distributions and saves them as a workflow artifact;
35
+ its publishing job is always skipped. Pushes and pull requests also run the
36
+ distribution checks in the ordinary CI workflow.
37
+
38
+ To run the same checks locally, from a clean checkout and virtual environment:
39
+
40
+ ```bash
41
+ python -m pip install '.[dev]'
42
+ python -m build
43
+ python -m twine check --strict dist/*
44
+ python scripts/validate_distribution.py --dist-dir dist --tag v0.4.0 --plots
45
+ ```
46
+
47
+ Use an empty `dist/` directory for each version. `python -m build` creates a
48
+ source archive and builds the wheel from that archive. Validation checks
49
+ metadata, the entry point, runtime version, source/wheel module equality,
50
+ reproducibility fixtures, and installed command behavior. It runs the complete
51
+ unit suite against an isolated wheel installation outside the checkout,
52
+ exercises joint VCF and individual gVCF inputs, and checks plot generation.
53
+ The source archive is rebuilt independently, compared with the release wheel,
54
+ and checked in a separate installation with both CLI examples. Plot dependencies
55
+ are optional at runtime.
56
+
57
+ ## Publish a version
58
+
59
+ 1. Register the pending publisher above before publishing the first release.
60
+ 2. Preserve the root MIT `LICENSE` and the `MIT` SPDX expression in project
61
+ metadata. The source and wheel archives must include that licence. The
62
+ supplied reference-data subset retains its separate MIT licence and
63
+ attribution under `reference-data/scn5a-promoter-pangenome/`.
64
+ 3. Set `__version__` in `triohap/__init__.py` and update `CHANGELOG.md`. Package
65
+ metadata reads that version automatically. For the first release it is
66
+ `0.4.0`, and the tag must be exactly `v0.4.0`.
67
+ 4. Merge the release changes into `main` and confirm CI passes.
68
+ 5. Publish a GitHub release with the matching tag, targeting the tested commit.
69
+ A draft does not publish. Both published releases and published GitHub
70
+ prereleases trigger uploading; the Python version controls whether PyPI
71
+ treats the distribution as a prerelease. Use a version such as `0.4.1rc1`
72
+ for an actual Python prerelease, with tag `v0.4.1rc1`.
73
+ 6. Confirm the **Build and publish to PyPI** run succeeds and inspect the new
74
+ PyPI project/version. Then test installation in a fresh environment:
75
+
76
+ ```bash
77
+ python -m pip install 'triohap[plots]==0.4.0'
78
+ triohap --version
79
+ ```
80
+
81
+ The build job checks the release tag against the built metadata and runtime
82
+ version. Only its validated wheel and source archive are handed to the separate
83
+ publishing job. That job runs in `pypi`, requests a short-lived OIDC identity,
84
+ and uses PyPA's publishing action with attestations enabled by default. It does
85
+ not execute the source checkout or hold a persistent PyPI token. Actions are
86
+ pinned to reviewed upstream commits.
87
+
88
+ Pushing a tag alone, pushing a branch, opening a pull request, and manually
89
+ running the validation workflow do not publish. The existing `v0.3.0` release
90
+ does not trigger retroactively. Do not reuse a version already uploaded to
91
+ PyPI; publish a new version for corrections. No TestPyPI publisher is configured.
92
+
93
+ ## References
94
+
95
+ - [PyPI pending publishers](https://docs.pypi.org/trusted-publishers/creating-a-project-through-oidc/)
96
+ - [PyPA release workflow guidance](https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/)
97
+ - [PyPI Trusted Publishing security model](https://docs.pypi.org/trusted-publishers/security-model/)
98
+ - [PyPA publishing action](https://github.com/pypa/gh-action-pypi-publish)