Metadata-Version: 2.4
Name: polygenic-pgx
Version: 2.5.31
Summary: Polygenic score toolkit
Home-page: https://github.com/marpiech/polygenic
Author: Marcin Piechota, Wojciech Galan
Author-email: piechota@intelliseq.com
License: Intelliseq dual licenses this package. For commercial use, please contact [contact @ intelliseq.com](mailto:contact@intelliseq.com). For non-commercial use, this license permits use of the software only by government agencies, schools, universities, non-profit organizations or individuals on projects that do not receive external funding other than government research grants and contracts. Any other use requires a commercial license. For the full license, please see [LICENSE.md](https://github.com/intelliseq/polygenic/blob/master/LICENSE.md), in this source repository.
Classifier: Development Status :: 5 - Production/Stable
Classifier: License :: Free for non-commercial use
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: Unix
Classifier: Operating System :: MacOS
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy
Requires-Dist: scipy
Requires-Dist: progressbar2
Requires-Dist: python-utils
Requires-Dist: pysam
Requires-Dist: pytabix
Requires-Dist: pandas
Requires-Dist: plotly
Requires-Dist: kaleido
Requires-Dist: DotMap
Requires-Dist: pyyaml
Requires-Dist: tqdm
Requires-Dist: plotnine
Requires-Dist: polars
Requires-Dist: importlib-resources
Requires-Dist: logdecorator
Requires-Dist: jinja2
Requires-Dist: matplotlib
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: license
Dynamic: license-file
Dynamic: requires-dist
Dynamic: requires-python
Dynamic: summary

# polygenic - the polygenic scores toolkit

## Basic info
[![PyPI pyversions](https://img.shields.io/pypi/pyversions/polygenic.svg)](https://pypi.python.org/pypi/polygenic/)  
[![PyPI](https://img.shields.io/pypi/v/polygenic.svg)](https://pypi.python.org/pypi/polygenic)  
[![Maintainer]](https://img.shields.io/badge/maintainer-marpiech-blue)  

## Downloads
- pip [![PyPI download month](https://img.shields.io/pypi/dm/polygenic.svg)](https://pypi.python.org/pypi/polygenic/) 
- docker with data [![Docker](https://img.shields.io/docker/pulls/marpiech/polygenictk.svg)](https://hub.docker.com/repository/docker/marpiech/polygenictk) 
- docker without data [![Docker](https://img.shields.io/docker/pulls/intelliseq/polygenic.svg)](https://hub.docker.com/repository/docker/intelliseq/polygenic)

## Index
* [Summary](#summary)
* [Diplotyping Algorithm](#diplotyping-algorithm)
* [Installation](#installation)
  * [With pip](#with-pip)
  * [With conda](#with-conda)
  * [With docker](#with-docker)
* [Quick start guide](#quick-start-guide)
* [Manual](#manual)
  * [Tools](#tools)
    * [pgs-compute](#pgs_compute)
    * [pgs-build](#pgs_build)
    * [pgs-validate](#pgs_validate)
    * [vcf-index](#vcf_index)
    * [vcf-validate](#vcf_validate)
    * [vcf-stat](#vcf_stat)
    * [model-biobankuk](#model_biobankuk)
    * [model-pgscat](#model_pgscat)
    * [model-gbe](#model_gbe)
    * [model-pharmvar](#model_pharmvar)
  * [Docker images](#docker_images)
  * [Building models](#building_models)
  * [Example models](#example_models)
  * [Usecases](#usecases)
    * [pgx](#pgx)
* [License](#license)
* [Updates](#updates)

## Summary
Polygenic is a toolkit for a wide range of polygenic scores analysis tasks. The most important use cases include computing scores for samples in vcf files, building scores for GWAS results or fetching scores from repositories.

## Diplotyping Algorithm

We begin by reading individual genetic variants (genotypes) from the patient's VCF file, where each genotype carries two alleles — one per chromosome. The system supports phasing using a custom reference panel to resolve which alleles sit on the same chromosome; when phased data is available, the algorithm preserves this linkage information, while for unphased data both chromosomes are treated symmetrically. We define haplotypes as specific combinations of co-inherited variants that form recognized gene versions, such as pharmacogenomic star alleles, where each haplotype definition distinguishes core defining variants (weighted at 1.0) from supportive sub-lineage variants (weighted at 0.05). The algorithm scores every candidate haplotype against the patient's alleles, then selects the best-matching haplotype for the first chromosome — keeping all candidates within a 2% margin of the top score. The matched alleles are then "claimed" by that haplotype, and the remaining unmatched alleles (leftovers) are passed into a second round, where up to 100 candidate haplotypes are re-scored against only those residual alleles to identify the second haplotype. Each first/second haplotype pair is ranked by combined match percentage and filtered by total missing data, producing the final diplotype call — e.g., CYP2D6 \*1/\*4. Every variant in the result carries a source label — direct genotyping, LD proxy, imputation flag from VCF, allele-frequency-based imputation, reference, or missing — enabling granular quality control and full traceability of the diplotype call.

### Missing variant handling (reference fill + `--no-ref-fallback` / `--no-indeterminate`)

By **default**, variants called `./.` in the input VCF are **filled with homozygous reference** (`source: "reference"`) so a best-effort call and scores always compute. Pass **`--no-ref-fallback`** for strict behavior: `./.` is then treated as **missing** (`source: "missing"`) and subtracted from both the numerator and the denominator of the haplotype match score (the [PharmCAT Named Allele Matcher](https://pharmcat.clinpgx.org/methods/NamedAlleleMatcher-101/) approach of dropping missing positions), which can leave a sparse panel with no confident call. *(There is no `--ref-fallback` flag — filling is the default as of 2.5.19.)*

Filling alone could assert a wild-type call you can't justify, so two safeguards ride alongside it:

- **Indeterminate verdict** (on by default; disable with `--no-indeterminate`). The reference fill is recorded as `source: "reference"` and treated as *uncovered* by the verdict — i.e. it is computed on the pre-fill state. The headline call is `"Indeterminate"` **only when ALL defining (function-level / `core`) variants are uncovered** (missing, low-quality, or reference-filled). Otherwise the best-effort call is emitted, and **which star alleles cannot be ruled in/out is reported per-allele in `undeterminable`** (an allele is listed when any of its defining variants is uncovered and it is not contradicted by a covered slot). The best-effort call for a triggered (all-uncovered) case is kept as `call_filled`.

- **`reference_defining_variants`** (per-model annotation). Positions where the GRCh38 **reference** base itself defines an allele are **never** reference-filled, and when unassayed they surface in `undeterminable`. The canonical case is **CYP2C19**: rs3758581 (chr10:94842866) has reference `A`, but `G` is the global major allele and PharmVar v5 defines every "real" star allele (\*1, \*2, \*17 …) as requiring `G` there, reserving CYP2C19\*38 (= the empty \*1.001) for the minority carrying reference `A` (see [PharmVar GeneFocus: CYP2C19](https://pmc.ncbi.nlm.nih.gov/articles/PMC7769975/)). Without the annotation, filling an unassayed rs3758581 with `A` would collapse every sample toward \*38; the annotation keeps the real alleles callable from their own variants and flags the rs3758581-dependent alleles instead. (To author such a model, list the position under `reference_defining_variants` — see the guidance in `CLAUDE.md`.)

### Match-confidence gate (`--top-n`)

After scoring all candidate haplotype pairs, the algorithm returns a `haplotype_id` when **either** of these holds:
- the best pair's per-chromosome `max_percent_match` is `≥ 50%` (high-confidence call), **or**
- the total scored-candidate pool size is `≤ top_n` (default **15**) — a small, non-ambiguous candidate space is itself a confidence signal.

Otherwise `haplotype_id` is `None` (the caller refuses to guess). This is more conservative than [PharmCAT](https://pharmcat.clinpgx.org/methods/NamedAlleleMatcher-101/), which always returns the top-scoring diplotype with deterministic tie-breaking, and roughly comparable to [Aldy 4](https://pmc.ncbi.nlm.nih.gov/articles/PMC9977157/), which reports "no more than three diplotypes" when it cannot fully disambiguate. Tune `--top-n` higher (laxer) or lower (stricter) depending on how much ambiguity is acceptable downstream. Set `--top-n 0` to rely solely on the 50% threshold.

### CYP2C19*38 worked example

On a clinical panel that does **not** assay rs3758581, `cyp2c19-pharmvar-5.1.8.yml` lists it under `reference_defining_variants`, so it is never reference-filled. A sample's actual informative variants drive the call, and the rs3758581-dependent alleles (`*38` vs `*1`) are reported in `undeterminable` rather than miscalled:

| Sample | Non-ref CYP2C19 variants | Call | Note |
|---|---|---|---|
| Sample A | none | `*1`-like / not a confident `*38` | `*1` vs `*38` cannot be distinguished without rs3758581 → flagged in `undeterminable` |
| Sample B | rs12769205, rs4244285 (het) | contains `*2` ✓ | driven by rs4244285 |
| Sample C | rs12248560 (hom) | `*17/*17` ✓ | driven by rs12248560 |
| Sample D | rs12769205, rs4244285 (het) | contains `*2` ✓ | driven by rs4244285 |

The point of the annotation: were rs3758581 blindly filled with reference `A/A`, it would fail the `G` required by \*1/\*2/\*17 … and collapse every sample to the empty \*1.001 (= \*38). Because it is *not* filled, the real variants still call \*2/\*17, and the inability to tell \*1 from \*38 surfaces as an `undeterminable` entry — never a spurious confident \*38.

## Installation

### Runtime system dependency: `tabix`
Beyond the pip dependencies, `pgs-compute` needs the **`tabix`** binary (from **htslib**)
available on `PATH` at runtime. rsID-keyed models — including the diplotype models
(APOE, MTHFR, COMT, F2, F5, IFNL4, OPRM1) — resolve genotypes through `rsidx`, which shells
out to `tabix`; without it those lookups fail with `FileNotFoundError: 'tabix'`. (Positional
`chrom-pos-ref-alt` keyed models use the bundled `pytabix` library and do not need the binary,
but installing `tabix` is required for full functionality.) The input VCF must also be
bgzipped and tabix-indexed (`.tbi`).

Install it with your package manager, e.g.:
```
apt-get install -y tabix          # Debian/Ubuntu (htslib)
# or: conda install -c bioconda htslib   |   brew install htslib
```

### With pip
#### Install for user account
```
python3 -m pip install --upgrade polygenic
```
#### Install globally
```
sudo -H python3 -m pip install polygenic
```
### With conda
Run conda image
```
docker run -it conda/miniconda3 /bin/bash
```
Create python3.8 environment and install polygenic
```
yes | conda create --name py38 python=3.8
eval "$(conda shell.bash hook)"
conda activate py38
### should be 3.8
python --version

### gcc is missing to build pytabix
apt -qq update
apt -y install build-essential tabix

pip install polygenic
```
### With docker
#### Large image with all data included
```
docker run intelliseq:polygenictk:2.1.0 *command*
```
#### Thin image with just polygenic package installed
```
docker run intelliseq:polygenic:2.1.0 *command*
```
## Quick start guide
```
mkdir polygenic && cd polygenic # create working directory
wget https://downloads.intelliseq.com/public/polygenic/gbe-INI78-bone-density.yml # download model
wget https://downloads.intelliseq.com/public/polygenic/illu_merged-imputed.vcf.gz # download genotypes
wget https://downloads.intelliseq.com/public/polygenic/illu_merged-imputed.vcf.gz.tbi # download position index
wget https://downloads.intelliseq.com/public/polygenic/illu_merged-imputed.vcf.gz.idx.db # download rsid index
docker run -v $(pwd):/data intelliseq/polygenic:latest --vcf /data/illu_merged-imputed.vcf.gz --model /data/gbe-INI78-bone-density.yml --output-directory /data # compute model
```
## Manual
### Tools
#### pgs-compute
```
usage: pgstk [-h] -i VCF [-m MODEL [MODEL ...]] [-p PARAMETERS] [-s SAMPLE_NAME] [-o OUTPUT_DIRECTORY] [-n OUTPUT_NAME_APPENDIX] [-l LOG_FILE] [--af AF] [--af-field AF_FIELD]
             [-v] [--print]

pgs-compute computes polygenic scores for genotyped sample in vcf format

optional arguments:
  -h, --help            show this help message and exit
  -i, --vcf VCF         vcf.gz file with genotypes
  -m, --model MODEL [MODEL ...]
                        path to .yml model (can be specified multiple times with space as separator)
  -p, --parameters PARAMETERS
                        parameters json (to be used in formula models)
  -s, --sample-name SAMPLE_NAME
                        sample name in vcf.gz to calculate
  -o, --output-directory OUTPUT_DIRECTORY
                        output directory
  -n, --output-name-appendix OUTPUT_NAME_APPENDIX
                        appendix for output file names
  -l, --log-file LOG_FILE
                        path to log file
  --af AF               vcf file containing allele freq data
  --af-field AF_FIELD   name of the INFO field to be used as allele frequency
  -v, --version         show program's version number and exit
  --print               Print output to stdout
```
### Arguments
#### Required
- `--vcf` vcf.gz file with genotypes (tabix index should be available)
- `--model` path to model file
#### Optional
- `--log_file` log file
- `--out_dir` directory for result jsons
- `--population` population code
- `--models_path` path to a directory containing models
- `--af` an indexed vcf.gz file containing allele freq data
- `--version` prints version of package

## Building models in yml

Index:
[Model structure](#model_structure)
[Model types](#model_types)
[Parameters](#parameters)


### Model structure
##### Core structure
Models have two properties which is `model` and `description`. `model` is a specification of computation to be performed and `description` is additional information to be included in the result.
```
model:
description:
```
##### Object keys
Each object that is not collection has a set of predefined keys (required or optional) that can be used for computation. For example: `diplotype_model` object has a required `diplotypes` key.
```
diplotype_model:
  diplotypes:
```
The computation is first delegated to key specified objects and later aggregated by the top level object itself.
##### Collections
There is special category of objects that don't have predefined keys but are collections. Each key within collection becomes element of collection. Collections are easy to recognize, because they are specified in plural form like `diplotypes` or `variants`. Each element of collection will be defined as singular object of collection type. For example key in `variants` collection will becomes objects of `variant` type.
```
      variants:
        rs7041: {diplotype: C/C}
        rs4588: {diplotype: T/T}
```
##### Variants
Variants can be identified by rsid. Variant value will be computed basing on information provided: `diplotype` or `effect_allele`.
Accepted sets of fields are:
- diplotypes
    - `diplotype`
    - `symbol`
- score
    - `effect_allele`
    - `effect_size`
    - `symbol`

### Model types
There are currently implemented four types of models:  
- `score_model`
- `diplotype_model`
- `haplotype_model`
- `formula_model`
The type of model can be specified at the top of yml structure or within the `model` field.  
##### Specification of model type at the top of yml structure
```
diplotype_model:
description:
```
##### Specification of model type within the `model` field
```
model:
  diplotype_model:
description:
```
### Parameters
External parameters can be used in `formula_model` through `@parameters` keyword.  
Example parameters file in `.json` format:
```
{"sex": "F"}
```
Path to file can be provided as argument to polygenic tool:
```
--parameters /path/to/parameters.json
```
Example of use of parameters in the `formula_model`:
```
formula_model:
  formula:
    value: "@female.score_model.value if @parameters.sex == 'F' else @male.score_model.value"
  male:
    score_model:
      variants:
        ...
  female:
    score_model:
      variants:
```
## Example models
### Example diplotype model
This example diplotype model is based on [Randolph 2014](https://pubmed.ncbi.nlm.nih.gov/24447085/).
```
diplotype_model:
  diplotypes:
    1/1:
      variants:
        rs7041: {diplotype: C/C}
        rs4588: {diplotype: T/T}
    1/1s:
      variants:
        rs7041: {diplotype: C/C}
        rs4588: {diplotype: T/G}
    1/1f:
      variants:
        rs7041: {diplotype: C/A}
        rs4588: {diplotype: T/G}
    1/2:
      variants:
        rs7041: {diplotype: C/A}
        rs4588: {diplotype: T/T}
    1s/1s:
      variants:
        rs7041: {diplotype: C/C}
        rs4588: {diplotype: G/G}
    1s/1f:
      variants: 
        rs7041: {diplotype: C/A}
        rs4588: {diplotype: G/G}
    1s/2:
      variants: 
        rs7041: {diplotype: C/A}
        rs4588: {diplotype: G/T}
    1f/1f: 
      variants: 
        rs7041: {diplotype: A/A}
        rs4588: {diplotype: G/G}
    1f/2: 
      variants: 
        rs7041: {diplotype: A/A}
        rs4588: {diplotype: G/T}
    2/2: 
      variants: 
        rs7041: {diplotype: A/A}
        rs4588: {diplotype: T/T}
description:
  pmid: 24447085
  genes: [GC]
  result_diplotype_choice:
    1/1: Moderate
    1/1s: High
    1/1f: High
    1/2: Low
    1s/1s: Very high
    1s/1f: Very high
    1s/2: Moderate
    1f/1f: Very high
    1f/2: Moderate
    2/2: Very low
```

### Example haplotype model

Haplotype model can be used for HLA and PGx.  
To define haplotype models a list of alleles is required (called `variants` in this case, to be consistent with othe rypes of models). Each allele has associated list of defining mutations (alternative SNV alles) defined by Gnomad ID along with `ref`, `alt` and `effect_allele` properties. One star allele should be empty (containing only reference SNV alleles). The algorithm will utilised any phasing information in the vcf.

```
haplotype_model:
  variants:
    CYP2D6*1.001:
    CYP2D6*1.002:
      22-42126963-C-T: {ref: "C", alt: "T", effect_allele: "T"}
    CYP2D6*1.003:
      22-42128813-G-A: {ref: "G", alt: "A", effect_allele: "A"}
    CYP2D6*1.004:
      22-42128216-G-T: {ref: "G", alt: "T", effect_allele: "T"}
    CYP2D6*1.005:
      22-42128922-A-G: {ref: "A", alt: "G", effect_allele: "G"}
    CYP2D6*1.006:
      22-42129726-A-C: {ref: "A", alt: "C", effect_allele: "C"}
      22-42129950-A-C: {ref: "A", alt: "C", effect_allele: "C"}
      22-42130482-C-A: {ref: "C", alt: "A", effect_allele: "A"}
```

For copy-number star alleles (CYP2D6 `*5`/`*1xN`, CYP2C19 `*36`/`*37`) the model gains a
`copy_number:` block and `structural:` haplotypes — see **[docs/pgx-cnv.md](docs/pgx-cnv.md)**
for the VCF contract and YAML schema.

### Example score model with categories rescaling
```
score_model:
  variants:
    rs10012: {effect_allele: G, effect_size: 0.369215857410143}
    rs1014971: {effect_allele: T, effect_size: 0.075546961392531}
    rs10936599: {effect_allele: C, effect_size: 0.086359830674748}
    rs11892031: {effect_allele: C, effect_size: -0.552841968657781}
    rs1495741: {effect_allele: A, effect_size: 0.05307844348342}
    rs17674580: {effect_allele: C, effect_size: 0.187520720836463}
    rs2294008: {effect_allele: T, effect_size: 0.08278537031645}
    rs798766: {effect_allele: T, effect_size: 0.093421685162235}
    rs9642880: {effect_allele: G, effect_size: 0.093421685162235}
  categories:
    High risk: {from: 1.371624087, to: 2.581880425, scale_from: 2, scale_to: 3}
    Potential risk: {from: 1.169616034, to: 1.371624087, scale_from: 1, scale_to: 2}
    Average risk: {from: -0.346748358, to: 1.169616034, scale_from: 0, scale_to: 1}
    Low risk: {from: -1.657132197, to: -0.346748358, scale_from: -1, scale_to: 0}
description:
  about: 
  genes: []
  result_statement_choice:
    Average risk: Avg
    Potential risk: Pot
    High risk: Hig
    Low risk: Low
  science_behind_the_test:
  test_type: Polygenic Risk Score
  trait: Breast cancer
  trait_authors:
    - taken from the PGS catalog
  trait_copyright: Intelliseq all rights reserved
  trait_explained: None
  trait_heritability: None
  trait_pgs_id: PGS000001
  trait_pmids:
    - 25855707
  trait_snp_heritability: None
  trait_title: Breast_Cancer
  trait_version: 1.0
  what_you_can_do_choice:
    Average risk:
    High risk:
    Low risk:
  what_your_result_means_choice:
    Average risk:
    High risk:
    Low risk:
 ```

#### Example Formula Model
```
formula_model:
  formula:
    brownexp: "math.exp(@brown.score_model.value - 2.0769)"
    redexp: "math.exp(@red.score_model.value - 6.3953)"
    blackexp: "math.exp(@black.score_model.value - 2.4029)"
    sumexp: "@brownexp + @redexp + @blackexp"
    brown_prob: "@brownexp / (1 + @sumexp)"
    red_prob: "@redexp / (1 + @sumexp)"
    black_prob: "@blackexp / (1 + @sumexp)"
    blonde_prob: "1 - (@brown_prob + @red_prob + @black_prob)"
  brown:
    score_model:
      variants:
        rs796296176: {effect_allele: CA, effect_size: 1.2522}
        rs11547464: {effect_allele: A, effect_size: -0.61155}
        rs885479: {effect_allele: T, effect_size: 0.2937}
        rs1805008: {effect_allele: T, effect_size: -0.50143}
        rs1805005: {effect_allele: T, effect_size: 0.21172}
        rs1805006: {effect_allele: A, effect_size: 1.9293}
        rs1805007: {effect_allele: T, effect_size: -0.32318}
        rs1805009: {effect_allele: C, effect_size: 0.60861}
        rs1805009: {effect_allele: A, effect_size: 0.25624}
        rs2228479: {effect_allele: A, effect_size: -0.054143}
        rs1110400: {effect_allele: C, effect_size: -0.56315}
        rs28777: {effect_allele: C, effect_size: 0.52168}
        rs16891982: {effect_allele: C, effect_size: 0.75284}
        rs12821256: {effect_allele: G, effect_size: -0.34957}
        rs4959270: {effect_allele: A, effect_size: -0.19171}
        rs12203592: {effect_allele: T, effect_size: 1.6475}
        rs1042602: {effect_allele: T, effect_size: 0.16092}
        rs1800407: {effect_allele: A, effect_size: -0.19111}
        rs2402130: {effect_allele: G, effect_size: 0.35821}
        rs12913832: {effect_allele: T, effect_size: 1.214}
        rs2378249: {effect_allele: C, effect_size: 0.12669}
        rs683: {effect_allele: C, effect_size: 0.21172}
  red:
    score_model:
      variants:
        rs796296176: {effect_allele: CA, effect_size: 25.508}
        rs11547464: {effect_allele: A, effect_size: 2.5381}
        rs885479: {effect_allele: T, effect_size: -0.20889}
        rs1805008: {effect_allele: T, effect_size: 2.801}
        rs1805005: {effect_allele: T, effect_size: 0.93493}
        rs1805006: {effect_allele: A, effect_size: 3.65}
        rs1805007: {effect_allele: T, effect_size: 3.4408}
        rs1805009: {effect_allele: C, effect_size: 4.5868}
        rs1805009: {effect_allele: A, effect_size: 22.107}
        rs2228479: {effect_allele: A, effect_size: 0.62307}
        rs1110400: {effect_allele: C, effect_size: 1.4453}
        rs28777: {effect_allele: C, effect_size: 0.70401}
        rs16891982: {effect_allele: C, effect_size: -0.41869}
        rs12821256: {effect_allele: G, effect_size: -0.57964}
        rs4959270: {effect_allele: A, effect_size: 0.24861}
        rs12203592: {effect_allele: T, effect_size: 0.90233}
        rs1042602: {effect_allele: T, effect_size: 0.45003}
        rs1800407: {effect_allele: A, effect_size: -0.27606}
        rs2402130: {effect_allele: G, effect_size: 0.28313}
        rs12913832: {effect_allele: T, effect_size: -0.093776}
        rs2378249: {effect_allele: C, effect_size: 0.76634}
        rs683: {effect_allele: C, effect_size: -0.053427}
  black:
    score_model:
      variants:
        rs796296176: {effect_allele: CA, effect_size: 2.732}
        rs11547464: {effect_allele: A, effect_size: -16.969}
        rs885479: {effect_allele: T, effect_size: 0.39983}
        rs1805008: {effect_allele: T, effect_size: -0.86062}
        rs1805005: {effect_allele: T, effect_size: -0.0029013}
        rs1805006: {effect_allele: A, effect_size: -16.088}
        rs1805007: {effect_allele: T, effect_size: -1.3757}
        rs1805009: {effect_allele: C, effect_size: 0.060631}
        rs1805009: {effect_allele: A, effect_size: 3.9824}
        rs2228479: {effect_allele: A, effect_size: 0.17012}
        rs1110400: {effect_allele: C, effect_size: 0.29143}
        rs28777: {effect_allele: C, effect_size: 0.82228}
        rs16891982: {effect_allele: C, effect_size: 1.1617}
        rs12821256: {effect_allele: G, effect_size: -0.89824}
        rs4959270: {effect_allele: A, effect_size: -0.36359}
        rs12203592: {effect_allele: T, effect_size: 1.997}
        rs1042602: {effect_allele: T, effect_size: 0.065432}
        rs1800407: {effect_allele: A, effect_size: -0.49601}
        rs2402130: {effect_allele: G, effect_size: 0.26536}
        rs12913832: {effect_allele: T, effect_size: 1.9391}
        rs2378249: {effect_allele: C, effect_size: -0.089509}
        rs683: {effect_allele: C, effect_size: 0.15796}
description:
  name: HirisPlex

```

### Description
### Model keys glossary
- `model` - generic model that can aggregate results of other model types  
- `diplotype_model` 
    Required keys:
    - `diplotypes`
- `description` - all properties to be included in the final results  

### Usecases

#### PGX

```
python3 -m pip install polygenic
pgstk pgs-compute --vcf [PATH_TO_VCF_GZ] --model cyp2d6-pharmvar.yml --print | jq .haplotype_model.haplotypes.match
```

## License
Proprietary (contact@intelliseq.pl)

## Updates
### 2.5.31
- **Diplotype no-match with a no-call is now `Indeterminate`, not `null`.** When a `diplotype_model` matches no entry *because* an in-VCF defining variant is a no-call (missing / `LOW_QUAL` / `./.`), the call is flagged `Indeterminate` (culprit in `undeterminable.failed_slots`) instead of a silent `null`. A fully-covered no-match (a genotype that fits no entry, nothing uncovered) still returns `null`. Fixes e.g. MTHFR when rs1801133 (677) is `LOW_QUAL`/`./.` while rs1801131 (1298) is covered. Complements the existing verdict, which only fires when ALL defining variants are uncovered.
### 2.5.30
- **Haploid (chrM) support** - mitochondrial calls are reported as a single haplotype rather than a diploid pair.
### 2.5.29
- **Indel key normalization** - left-align (normalize) indel variant keys in PharmVar models to the 1000G/GATK convention so panel indels match.
### 2.5.28
- **Wild-type / het no-call rescue** - the reference (`*1`) pair, and a fully-covered contradiction-free pair on a densely-covered panel, resolve rather than falling to a silent no-call (trustworthiness is re-gated by the Indeterminate verdict). Plus CYP3A4 rebuilt from PharmVar 6.2.26.
### 2.5.27
- **CYP2D6 unphased CN gain — duplication now visible in the headline.** `haplotype_id` shows a deterministic **canonical arrangement** (`xN` on the higher-numbered allele, e.g. `CYP2D6*1/CYP2D6*2x2`) instead of the bare base diplotype, so a consumer reading only `haplotype_id` no longer mistakes an ultrarapid duplication for a normal `*1/*2`. Both arrangements stay in `copy_number.possible_calls` (unchanged list-of-strings, backward-compatible), and a new **`copy_number.possible_calls_scored`** adds `{call, score}` per arrangement (score = the base diplotype's assembler `match_sum`). `allele_specific_unphased` unchanged; the side is still never *resolved* without phase (it flips the activity score, `*1x2/*4`=2 vs `*1/*4x2`=1).
### 2.5.26
- **CYP2D6 copy-number gains (BT-1481):** an unphased / allele-specific-unknown duplication no longer produces the malformed `" (CN3)"`. The engine now emits the **base diplotype** as `haplotype_id` plus `copy_number.possible_calls` listing **every xN arrangement** (e.g. `*1/*2` + CN=3 -> `['*1/*2x2','*1x2/*2']`) with the `allele_specific_unphased` flag. The duplicated side drives the activity score (`*1x2/*4`=AS 2 vs `*1/*4x2`=AS 1), so the ambiguity is made explicit rather than guessed. When phase/`AD` resolves the side, the single arrangement is named as before. Gains resolve as the `xN` multiplier; losses remain the structural star allele (`*5`) — documented in the model's `copy_number`/`multiplication` block.
- **Sparse-panel pairing fix:** the reference (`*1`) lineage is always kept as a second-haplotype candidate, so a het call pairs `*1/*2` instead of a spurious `*2/*2` on targeted panels; the CNV path falls back to the best matching pair when the SNV assembler gates the base to no-call (a consumed structural event is itself coverage).
### 2.5.25
- Diplotype models support an `optional: true` flag on a variant: when that variant is **absent / no-call**, it no longer blocks the diplotype match (a required variant still fails as before). This lets a panel that doesn't assay a rare modifier still get the base call. Used for **APOE Christchurch (rs121918393)** and **F5 Cambridge (rs118203906)** — flagged only on the non-carrier entries, so a microarray without those probes calls e.g. APOE `E2/E3` / F5 `Reference/Reference` instead of `null`, while carrier detection is unchanged when the modifier IS assayed. Backward-compatible: models without the flag are unaffected; an older engine simply ignores the key.
### 2.5.24
- (BT-1478) A `*1/*1` (wild-type) star-allele call is now flagged **Indeterminate when >=1 defining (core) variant present in the VCF is low-quality or `./.`** (filtered/no-call/assumed-reference) - the "normal" call could be a false negative hiding a *2/*4/... behind a failed slot. The best-effort `*1/*1` is kept as `call_filled`; `indeterminate.wildtype_low_quality` flags this case. Applies to star-allele (haplotype) models; folded into the verdict (`--no-indeterminate` disables). Absent (not-in-VCF) positions don't trigger it.
- Low-quality FILTER recognition is now **case-insensitive** and the default set adds `LOW_QUALITY`/`LOWQUALITY` (so `lowqual`/`LOW_QUAL`/`lowquality`/... in any case are all treated as filtered).
### 2.5.23
- (BT-1478) The Indeterminate verdict + `undeterminable` list now consider **only variants PRESENT in the input VCF by default** (any record - genotyped, `./.`, or LOW_QUAL); model positions the panel never assays (absent from the VCF) are ignored, so targeted panels no longer get flooded with model-only positions. Pass **`--all-model-variants`** to consider every model variant (previous behavior). A new `in_vcf` flag on each genotype drives the scope; the legacy zero-coverage `safety_lock` is unchanged.
- The two alleles of an assembled diplotype call are now ordered by star-allele number ascending (e.g. `*9/*1` is reported as `*1/*9`), for both haplotype and CNV calls. Author-defined diplotype-model labels (e.g. APOE `E3/E4`) are unchanged.
### 2.5.22
- (docs) Refresh the PyPI page: rewrite the "Missing variant handling" section + CYP2C19*38 worked example to the current 2.5.19+ behavior (reference fill is the default; `--no-ref-fallback` for strict; the Indeterminate verdict fires only when ALL defining variants are uncovered, else a best-effort call + per-allele `undeterminable`; the `reference_defining_variants` annotation). No code change.
### 2.5.21
- (docs) Declare **`tabix` (htslib)** as a runtime system dependency: `pgs-compute` shells out to the `tabix` binary via `rsidx` for rsID-keyed models (the diplotype models APOE/MTHFR/COMT/F2/F5/IFNL4/OPRM1). See the new README "Runtime system dependency: tabix". `Dockerfile.test` now installs `tabix` so the full test suite runs in a clean image. (Test-suite maintenance: drop the removed `--ref-fallback` flag from legacy CLI tests.)
### 2.5.20
- (BT-1478) REFINED the Indeterminate rule. The headline call is now "Indeterminate" **only when ALL defining (function-level/core) variants are uncovered** (nothing can be called); otherwise the engine emits the best-effort call and reports **which star alleles cannot be ruled in/out per-allele in `undeterminable`**. Previously (2.5.19) any single missing defining variant blanket-flagged the whole call Indeterminate, which was too aggressive - e.g. CYP3A5 went Indeterminate only because the `*8`/`*9` positions weren't assayed, even though `*3`/`*6`/`*7` were. `reference_defining_variants` (CYP2C19 rs3758581 -> `*38`) are still never reference-filled (verdict computed on the pre-fill state).
### 2.5.19
- (BT-1478) Defining-variant **Indeterminate verdict** + default reference fill. A call is now "Indeterminate" when a DEFINING (function-level/core) variant is not really covered - missing, low-quality, or reference-FILLED - replacing the coarse zero-coverage-only rule. The best-effort filled call is kept as `call_filled`.
- (BT-1478) Reference fill is now the DEFAULT (`./.` -> homozygous reference so scores/calls always compute); disable with `--no-ref-fallback`. The `--ref-fallback` flag is removed (it's the default); added `--no-indeterminate` to disable the verdict. This resolves sparse-panel samples silently returning `null` (e.g. a 12/358-covered CYP2D6 now returns `Indeterminate` + a `*2/*1` `call_filled`, not a bare `null`).
- (BT-1478) New model annotation `reference_defining_variants` for positions whose GRCh38 reference base itself defines an allele (CYP2C19 rs3758581 -> `*38`): never reference-filled and, when missing, forces Indeterminate - so a panel that doesn't assay rs3758581 is no longer miscalled as `*38`. `cyp2c19-pharmvar-5.1.8.yml` annotated; see CLAUDE.md.
### 2.5.18
- (cli) FIX: `pgstk pgs-compute --no-ref-fallback` was rejected as an unrecognized argument (only `--ref-fallback`, a `store_true`, existed). Added the `--no-ref-fallback` counterpart (3.8-compatible pair sharing one dest; default off) so workflows can state the safe default explicitly.
- (docs/test) Clarified the **match-confidence gate**: `haplotype_id` is `null` - with `matching_haplotypes` still listing the leading candidate(s) - when no diplotype reaches the 50%% per-chromosome match AND the candidate pool exceeds `--top-n` (default 15). This is a deliberate "refuse to guess", distinct from `"Indeterminate"` (the zero-coverage safety lock). Added `test_confidence_gate.py` and an end-to-end `pgs-compute --no-ref-fallback` run (+ assertions for both `--no-*` flags) to the docker install test.
### 2.5.17
- (packaging) FIX: `import polygenic.pgstk` crashed on a clean install. Removed the dead `weasyprint` and `markdown` imports from `pgscompute.py` (their usage was commented out, yet they crashed the import and pulled in the `gobject`/pango system libraries). Declared the genuinely-used `jinja2` and `matplotlib`; removed the unused `pyarrow` and de-duplicated `kaleido`.
- (packaging) FIX: `pgs-compute` opened its templates via a cwd-relative path and the templates were not shipped in the wheel. Templates are now packaged (`package_data` + `MANIFEST.in` + `include_package_data`) and resolved package-relative; matplotlib uses the headless `Agg` backend.
- (test) Added `Dockerfile.test` + `test/docker_install_test.sh`: a fresh `python:3.8-slim` install smoke test (import `pgstk`, assert templates present, run the CLI) with NO graphics system libs, so this class of regression fails loudly before release.
### 2.5.16
- (BT-1481) CYP2A6 full PharmVar star-allele set: replaced the CNV-focused starter with `cyp2a6-pharmvar-6.2.25.yml`, generated from the authoritative PharmVar 6.2.25 GRCh38 allele definitions - 126 suballeles / 182 variants + the empty `*1.001` reference, retaining the `copy_number` block and `*4` whole-gene deletion. SNV calling validated (rs28399433 -> `*9/*1`) alongside CNV (`*1/*4`). NOTE: CYP2A6 was added to PharmVar after 5.1.8, so this model is pinned to 6.2.25 (newer than the 5.1.8 the other genes use).
### 2.5.15
- (BT-1481) PERF: `compute_haplotypes` early-out — when no panel slot is covered (every variant missing or filtered), return a no-call immediately instead of O(n^2) tie-scoring all candidates. Deletion-only / zero-coverage CYP2D6 (428 haplotypes) now resolves in <1s (was >9 min). General rule across all haplotype models; also avoids a bogus reference-ish call from no data (safety lock / CNV resolution handle it downstream). Normal covered calls are unchanged.
### 2.5.14
- (BT-1481) PRODUCTIONIZED CNV calling in the real PharmVar models: `cyp2d6-pharmvar-5.1.8.yml` (adds `*5` whole-gene deletion + `copy_number`/`multiplication` for `*1xN`), `cyp2c19-pharmvar-5.1.8.yml` (adds `*36` whole / `*37` partial deletion + `copy_number`), and a new `cyp2a6-pharmvar-5.1.8.yml` (CNV-focused: `*4` whole-gene deletion + `*2`/`*7`/`*9`; full PharmVar SNV set is a follow-up). Previously CNV existed only in test models.
- (BT-1481) FIX: a confirmed het deletion (CN=1) with no surviving-allele SNV call now defaults the surviving allele to the gene reference (`*1/*5`, `*1/*36`, `*1/*4`) instead of returning Indeterminate - the assembler returns no haplotype_id on large panels when all SNV sites are absent.
### 2.5.13
- (BT-1481) interpretation table: `Recommendation_tag` column constrained to a controlled vocabulary of three terms - **Standard dosing**, **Use with caution**, **Consider alternatives** (full detail remains in the Recommendation column). Mapped by clinical actionability (e.g. BChE PM / Factor V Leiden+Cambridge carriers / prothrombin carriers -> Consider alternatives; e4 ARIA / reduced-activity / monitor -> Use with caution; normal/non-carrier -> Standard dosing).
### 2.5.12
- (BT-1481) MTHFR diplotypes simplified to genotype-led notation (matching F5): `0/0` (both normal), `677 0/1`, `677 1/1`, `1298 0/1`, `1298 1/1`, and compounds like `677 0/1; 1298 0/1` - replacing the cDNA-style `677CT/1298AC`. Activity phenotype stays in the category text.
### 2.5.11
- (BT-1481) F5 diplotypes now genotype-led with the variant named in the ID/category: `0/0`, `Leiden 0/1`, `Leiden 1/1`, `Cambridge 0/1`, `Cambridge 1/1`, `Leiden 0/1; Cambridge 0/1` - easier for bioinformaticians while keeping both Factor V Leiden and Cambridge detectable (a single-locus 0/0/0/1/1/1 would miss Cambridge carriers). IFNL4 already emits `0/0`/`0/1`/`1/1` via the rs12979860 proxy; the causal dG indel stays documentation-only.
### 2.5.10
- (BT-1481) COMT diplotypes now reported in genotype notation `0/0` / `0/1` / `1/1` (Val/Val, Val/Met, Met/Met meaning retained in the category text), consistent with F2/F5(Leiden)/IFNL4/OPRM1.
- (BT-1481) FIX: interpretation table — corrected three column-shifted rows (APOE Christchurch, F5 Cambridge, IFNL4 dG indel were missing the Activity_score field) and refreshed the F5 rows to match the model's diplotype IDs (WildType / Leiden_het / Leiden_hom / Cambridge_het / Cambridge_hom / Leiden_Cambridge). All 84 rows now have a consistent 16-column layout.
### 2.5.9
- (BT-1481) FEATURE: added ClinVar/literature-validated variants to four genes (coordinates GRCh38 genomic, verified via dbSNP). **BCHE** expanded to 8 alleles — atypical `*2`, `*K` + new `*F1`, `*F2`, `*S1`, `*L335P`, `*G143D`, `*Y156C` — as a fully-enumerated `diplotype_model` (45 diplotypes) emitting star-allele pairs with NM/IM/PM by severity. **F5** now models Factor V **Cambridge** (rs118203906, R306T) alongside Leiden. **APOE** adds the **Christchurch** modifier (rs121918393, R136S) reported as a suffix on the ε-diplotype (and ε1 already complete). Interpretation table grown to 82 rows + a 20-variant genomic sheet.
- (BT-1481) NOTE: the IFNL4 causal dG/TT indel (rs368234815) is documented in the table/variants sheet but not wired as a model variant — the engine derives result genotypes from the last diplotype entry, so mixing proxy- and indel-keyed entries breaks the safety-lock coverage count; wiring it needs a genotypes-union engine fix (deferred). IFNL4 continues to call via the rs12979860 proxy.
- BCHE/F5 multi-variant models require all their panel sites present in the VCF (0/0 for non-carried); missing sites yield no diplotype match.
### 2.5.8
- (BT-1481) CHANGE: single-SNP genes now report the diplotype in genotype notation `0/0` / `0/1` / `1/1` (F2, F5, IFNL4, OPRM1) instead of WildType/Heterozygous/Homozygous, CC/CT/TT, AA/AG/GG. The descriptive meaning stays in the phenotype/category text and the interpretation table. (APOE `E1/E3` is intentionally not a separate diplotype — it is genotypically identical to the `E2/E4` double-het and reported as `E2/E4` by convention.)
### 2.5.7
- (BT-1481) FEATURE: APOE model now enumerates ALL 9 genotype combinations including the rare ε1 diplotypes (`E1/E1`, `E1/E2`, `E1/E4`) — previously these returned no-call. The double-het stays `E2/E4` by convention.
- (BT-1481) DATA: interpretation table (`docs/pgx-v2-interpretation.tsv` + `.xlsx`) is now complete — **every diplotype tuple for all 8 genes** (43 rows: APOE 10, MTHFR 9, BCHE 9, COMT/F2/F5/IFNL4/OPRM1 3 each), each with GRCh38 genomic CHR/POS/REF/ALT. Added a `Single_rsID_fallback` column (and an XLSX "Single-rsID fallback" sheet) describing the partial interpretation available when only one rsID of a multi-rsID gene (APOE/MTHFR/BCHE) is genotyped.
### 2.5.6
- (BT-1478) FEATURE: result now carries an `undeterminable` object — `failed_slots` (panel rsIDs whose genotype is missing or low-quality/filtered) and, for haplotype star-allele models, `alleles`: which star alleles cannot be ruled in OR out, with a `count`. Precise rule: since a star allele requires ALL its defining variants, an allele is undeterminable only if it has a missing/low-QC defining slot AND no covered slot already contradicts it — a high-quality covered slot lacking the effect allele rules the allele OUT (determinable absent) even when other slots are missing. Per-allele precursor to BT-1478 strict mode. Scoped to model variants today; becomes panel-scoped once the panel rsID contract is defined.
### 2.5.5
- (BT-1478) FEATURE: safety lock — haplotype and diplotype models now return `Indeterminate` (instead of a default wild-type / `*1/*1` call) when the panel has **zero covered positions** (every slot `missing` or `LOW_QUAL`-filtered), with a `safety_lock` block (`triggered`, `covered_slots`, `total_slots`, `reason`) in the result. A consumed copy-number/structural call counts as coverage (a whole-gene deletion stays a valid `*5/*5`, not Indeterminate). Default on; disable with `--no-safety-lock`.
### 2.5.4
- (BT-1481) FEATURE: 8 V2 PGx gene models (`models/pgx/`): APOE, MTHFR, BCHE, COMT, F2, F5, IFNL4, OPRM1 — uniform `diplotype_model` (single-SNP and compound-het rules; minus-strand alleles resolved from dbSNP). APOE double-het called ε2/ε4 by convention.
- FEATURE: interpretation table `docs/pgx-v2-interpretation.tsv` — drug×gene rows with diplotype→recommendation, source, PMID citation, and a 5-star evidence/actionability rating.
- TEST: `test_pgxv2_diplotypes.py` with per-gene fixtures (`resources/vcf/pgxv2/`) asserting the diplotype call for every genotype.
### 2.5.3
- TEST: added CNV deletion fixtures + tests over colleague-provided VCFs (`resources/vcf/testcnvvcfs/`) — CYP2D6 `*5` het/hom whole-gene deletion, CYP2A6 `*4` deletion, CYP2C19 `*36` (whole) vs `*37` (partial) — with coordinate-matched models.
### 2.5.2
- FEATURE: VCF records whose `FILTER` carries `LOW_QUAL` are ignored — treated as missing (dropped from haplotype numerator and denominator), reported as the new `source: "filtered"`. Structural/CNV records are skipped too. Configurable via `--ignore-filter` (default `LOW_QUAL`; pass one or more names).
### 2.5.1
- PACKAGING: render the README on the PyPI project page (`long_description` from README.md, `text/markdown`).
### 2.5.0
- FEATURE: copy-number (CNV) star-allele calling for CYP2D6 (`*5`, `*1xN`) and CYP2C19 (`*36`/`*37`). `polygenic` consumes copy number resolved by an upstream caller (symbolic `<DEL>`/`<DUP>` ALT + range + `FORMAT/CN` + phase) and emits non-diploid calls (`*5/*5`, `*1/*5`, `*1/*4x2`). It does no depth/ratio math — allele-specific duplication side is read from phase; unphased input yields `*a/*b (CNx)` + `allele_specific_unphased`. See [docs/pgx-cnv.md](docs/pgx-cnv.md).
- FEATURE: `haplotype_model` gains a `copy_number:` block, `structural:` haplotype definitions (`scope: whole|partial`), and a CYP2D6-only `multiplication:` block.
- TEST: structural VcfRecord unit tests and CNV deletion/duplication/regression integration tests (`test_vcfrecord_structural.py`, `test_haplotypemodel_cnv.py`).
### 2.4.0
- FEATURE: `--ref-fallback` flag (opt-in; default off). `./.` genotypes now stay `source="missing"` by default — matches PharmCAT behavior and fixes spurious CYP2C19*38 calls on panels that don't assay rs3758581.
- FEATURE: `--top-n` flag (default 15). Haplotype caller returns a call when either the match ≥50% OR the scored candidate pool is ≤top_n, enabling calls on sparse panels without ambiguity risk.
- BUG: ScoreModel.compute no longer KeyErrors on `adjusted_score` when a variant has no `af` field (model.py:549).
- BUG: Diplotype QC now correctly counts variant sources (previously miscounted every variant as "missing" due to structural mismatch in compute_qc).
- BUG: ScoreModel handles models without `args` section (previously AttributeError on `self.get("args").get("prevalence")`).
- TEST: Added PGx truth-set integration tests against 1000 Genomes phased GRCh38 slices (NA12878, NA18507, NA19240 × {CYP2C19, CYP2C9, CYP2B6, CYP2D6, SLCO1B1}); rebuildable via `scripts/build_pgx_fixtures.py`.
- TEST: Added PharmVar-named haplotype tests and NA18507 CYP2B6/SLCO1B1 regression tests.
### 2.3.17
- BUG: resolved bug with low weight for missing genotypes
### 2.3.16
- BUG: resolved bug with weight of genotypes in haplotypes
### 2.3.15
- BUG: resolved bug with not enough haplotypes to check
### 2.3.14
- BUG: resolved bug with wrong leftover genotypes
### 2.3.12
- FEATURE: added gene names to genotypes if available
### 2.3.11
- BUG: resolved bug with wrong genotype sources counts
### 2.3.10
- BUG: resolved bug with missing genotype sources counts
### 2.3.9
- FEATURE: add reference as a genotyping source
### 2.3.8
- BUG: resolved bugs inside mobigen wdl task
### 2.3.7
- FEATURE: added ldproxy imputation source
### 2.3.6
- BUG: resolved bug with missing polars package after installation
### 2.3.5
- BUG: resolved bug with 'type' object is not subscriptable running pgstk
### 2.3.4
- BUG: resolved bug with where model does not provide to or from category fields
### 2.3.3
- BUG: resolved bug with missing pyarrow package after installation
### 2.3.2
- BUG: renamed jpg to jpeg outputs from vcfstat
### 2.3.1
- BUG: resolved bug with missing importlib-resources package after installation
### 2.3.0
- FEATURE: added vcf stat tool for zygosities
- FEATURE: added vcf stat tool for baf computation
### 2.2.15
- UPDATE: updated parsing for new version of pan biobankuk
- DEV: updated numpy version to 1.23.4
### 2.2.14
- FEATURE: added module for ldproxy imputing
- FEATURE: added option for merging output as an array instead of dictionary in pgs-compute
### 2.2.13
- BUG: resolved bug with missing score in haplotype model
- DEV: cleaned up test resources
### 2.2.12
- BUG: resolved bug with empty argument in executable
### 2.2.11
- BUG: resolved bug with naming of multiple models in one file
### 2.2.10
- DOC: improved diploty model documentation
### 2.2.9
- BUG: missing effect allele in diplotyp models
### 2.2.8
- BUG: imputed source is based on IMP tag in the INFO field or GT:DS in format field
### 2.2.7
- BUG: repaired bug with missing math library in eval
### 2.2.6
- FEATURE: added qc to model results
### 2.2.5
- ENHACEMENT: libraries updates
### 2.2.0
- ENHANCEMENT: better computing of haplotype models. First one haplotype is identified and further the second haplotype is identified from leftover genotypes
- ENHANCEMENT: moved argparse from tools to pgstk
### 2.1.10
- BUG: resolved bug with wrong plink.clumped path in clumping
### 2.1.9
- BUG: resolved bug with missing index in biobankuk model
### 2.1.8
- BUG: resolved bug with biobankuk model for codenames with special characters
### 2.1.7
- BUG: resolved bug with haplotype model where none of haplotypes matched genotype. Most probable genotype is provided
### 2.1.6
- DOC: added docker badges
- FEATURE: added posibility to output all pgs results in one json file `--merge-outputs`
- FEATURE: added category to diplotype model
- FEATURE: added caching in genotyping module
### 2.1.5
- BUG: biobankuk model output files now contain only alphanumeric characters
- BUG: biobankuk model code names with special characters are now being downloaded
### 2.1.4
- FEATURE: added model_name and sample_name to description
### 2.1.3
- FEATURE: added support for multiple models in pgs-compute
- FEATURE: added missing variants count to haplotype in haplotype model
- BUG: id field in haplotype model
### 2.1.2
- FEATURE: allow gnomadid for variant in yml models
- FEATURE: added printing output option in pgs-compute
### 2.1.1
- BUG: resolved NoneType bug with empty haplotype
### 2.1.0
- FEATURE: haplotype model now works with phased data
### 2.0.0
- FEATURE: switched to yaml model definitions
- FEATURE: implemented formula, score, haplotype and diplotype model types
- FEATURE: added gene symbols to description
- DEVOPS: prepared docker image with resources for building models
