Metadata-Version: 2.4
Name: anianns
Version: 0.7.1.post2
Summary: A Python package for annotating large tandem repeats
Author-email: Alex Sweeten <alex.sweeten@nih.gov>
License: MIT License
        
        Copyright (c) 2026 Alex Sweeten
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Keywords: genomics,bioinformatics,repeatmasker,tandem repeat
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: alive-progress<4,>=3.3
Requires-Dist: pysam
Requires-Dist: setproctitle
Requires-Dist: numpy
Requires-Dist: polars
Requires-Dist: mmh3
Requires-Dist: numba
Requires-Dist: matplotlib
Requires-Dist: scipy
Requires-Dist: seaborn
Provides-Extra: moddotplot
Requires-Dist: moddotplot; extra == "moddotplot"
Provides-Extra: dev
Requires-Dist: black; extra == "dev"
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Dynamic: license-file

[![Tests](https://img.shields.io/github/actions/workflow/status/marbl/anianns/tests.yml?branch=main&label=tests)](https://github.com/marbl/anianns/actions/workflows/tests.yml)
[![PyPI version](https://img.shields.io/pypi/v/anianns)](https://pypi.org/project/anianns/)
[![Python versions](https://img.shields.io/pypi/pyversions/anianns)](https://pypi.org/project/anianns/)

![](images/anianns_logo.png)

- [About](#about)
- [Installation](#installation)
- [Usage](#usage)
  - [Annotate Mode](#annotate-mode)
    - [Required args](#required-args)
    - [Optional args](#optional-args)
    - [Sample run](#sample-run)
    - [Repeat Masking](#repeat-masking)
  - [Build db](#build-db)
    - [Required args](#required-args-1)
    - [Optional Arguments](#optional-arguments)
    - [Sample database](#sample-database)
  - [NTRPrism](#ntrprism)
- [Questions](#questions)


## About
_AniAnn's_: __ANI__ Inferred **ANN**otation of Tandem Repeats

_AniAnn's_ is an _a priori_ satellite array detection and annotation software package. _AniAnn's_ uses a matrix of Average Nucleotide Identity (ANI) values similar to [ModDotPlot](https://github.com/marbl/ModDotPlot) in order to infer the location and orientation of satellite arrays. It introduces new downstream analysis to accurately annotate its contents and boundaries.

## Installation

You can download the current release from GitHub by using: 

```
git clone https://github.com/marbl/anianns.git
cd anianns
```

Although optional, we recommend setting up a virtual environment:

```
python -m venv venv
source venv/bin/activate
```

Once the virtual environment is activated, you can install the required dependencies:

```
python -m pip install .
```

By default, _AniAnn's_ installs **without** _ModDotPlot_ as a dependency. If you would like to include _ModDotPlot_ into the same venv for plotting, you can install using the following command:

```
python -m pip install .[moddotplot]
```

_AniAnn's_ is also available to install using PyPI:

```
pip install anianns
```

Once installed, confirm _AniAnn's_ was installed correctly by running `python -m anianns -h`, or simply with the shortcut `anianns -h`:

```

                                                 __       __
     _          _     _                        .'  `'._.'`  '.
    / \   _ __ (_)   / \   _ __  _ __  ' ___  |  .--;   ;--.  | 
   / _ \ | '_ \| |  / _ \ | '_ \| '_ \  / __| |  (  /   \  )  | 
  / ___ \| | | | | / ___ \| | | | | | | \__ |  \  ;` /^\ `;  / 
 /_/   \_\_| |_|_|/_/   \_\_| |_|_| |_| |___/   :` .'._.'. `;
                                                '-`'.___.'`-'

usage: anianns [-h] {annotate,build_db,ntrprism} ...

Ani Ann's: ANI Inferred ANNotation of Tandem Repeats

positional arguments:
  {annotate,build_db,ntrprism}  Choose mode: annotate, build_db, ntrprism
    annotate           Takes an input fasta and outputs an annotated bedfile of satellite arrays.
    build_db           Takes an input fasta, a bedfile of satellite coordinates, and an optional config file and outputs a db of satellite k-mers.
    ntrprism           Takes an input fasta, and a bedfile or region. Outputs k-mer histogram.


options:
  -h, --help       show this help message and exit
```

Note that _AniAnn's_ might take a while to run during your first usage. This is because the Python interpreter is compiling source code into the pycache directory. Subsequent runs will use the pre-compiled code and load much faster!

## Usage 

_AniAnn's_ must be run either in `annotate` mode, or `build_db` mode. 

### Annotate Mode

```
anianns annotate -f <FASTA_FILENAME(S)> <ARGS>
```

_AniAnn's_ requires at least one FASTA file as input. It generates one output annotation file (default BED) per sequence contained in the input FASTA file(s). Output annotation files are named based on the sequence identifier from the FASTA header.

Annotation of arrays into known satellite classes **must** be done through the use of a satellite _k_-mer database, using the command `--classify <directory>`. See [creating an annotation database](#creating-an-annotation-database) for more information.

#### Required args

`-f / --fasta <FILENAME(S)>`

Fasta file(s) to input. Multifasta files are accepted. 

#### Optional args

`-s / --seq_id <STR>`

Sequence ID to extract (multiple if using multifasta file). Will ignore if not found. **Default: None.**

`-d / --directory <DIR>`

Name of output directory. **Default: current working directory.**

`-o / --output-format <STR>`

Output annotation file format. Options are BED, GTF, GFF, CSV, TSV, JSON. **Default: BED.**

GTF and GFF output uses 1-based inclusive coordinates, `tandem_repeat` features,
and includes the AniAnn's repeat name and inferred monomer length in the attributes.

`-m / --mask <ALL>`

Name or repeat length of satellite arrays to mask. Replaces deteced satellites with N's. See [Repeat Masking](#repeat-masking) for more info. **Default: None.**

`--soft <BOOL>`

Softmask flag. Instead of N's, will force bases lower-case in detected satellite arrays. Must be used with `--mask`. **Default: None.**

`-c / --classify <DIR>`

Directory containing .db or .msh k-mer db files. Required for annotation into known satellites. **Default: None.**

`-t / --threshold <INT>`

Confidence threshold. This relates to the minimum percentage of _k_-mers in a satellite array required to be within that are contained within the selected. A lower number will be more sensitive, at the risk  **Default: 50.**

`-k / --kmer <INT>`

K-mer size to use. This should be large enough to distinguish unique k-mers with enough specificity, but not too large that sensitivity is removed. **Default: 21.**

`-i / --identity <INT>`

Minimum sequence identity cutoff threshold when running ModDotPlot. While it is possible to go as low as 50% sequence identity, anything below 80% is not recommended. **Default: 86.** 

`-w / --window <INT>`

The central dotplot window size. AniAnn's automatically scans half, the supplied
value, and double the supplied value. For example, `-w 5000` scans windows of
2500, 5000, and 10000 bp. AniAnn's hashes each sequence band once, scans the
additional resolutions without dense matrices, reconciles overlapping calls,
and boundary-refines only the winning call at each locus. If the initial
right-boundary range has no supported transition, refinement searches back to
the left using k-mers observed at least three times in the candidate core. This
prevents an unresolved call from defaulting to a matrix-band endpoint.
Boundary refinement runs NTRPrism at both k=6 and the user-selected k-mer
length. A significant k=6 result supplies the monomer estimate; the
user-selected k-mer result is used only when k=6 rejects. If both reject,
AniAnn's removes the candidate even when its matrix or distal signal is strong,
so final annotations never use a monomer score below 3 bp as a fallback.
Periodic rescues target long arrays whose repeat unit produces
several regularly spaced lines parallel to the main diagonal rather than one
solid diagonal block. AniAnn's scans a bounded set of diagonal lags without
materializing a full matrix, requires at least three high-contrast harmonics
with consistent row coverage, and independently confirms the inferred period
with NTRPrism before adding the candidate. Broad dense blocks, isolated
off-diagonal matches, and single parallel lines do not pass this rescue path.
Comparable spans favor the finer resolution; a coarser call wins when it
recovers materially more supported array sequence.
**Default: 2000 (scans 1000, 2000, and 4000).**

`--band <FLOAT>`

_AniAnn's_ streams the sequence in bands of this many megabases. Normal
annotation scans outward from the diagonal without materializing a full band
matrix; `--plot` or `--distal` creates the primary-resolution matrix needed for
visualization or distal-link detection. **Default: 2.**

`--cache-dir <DIR>`

Opt in to reusable canonical k-mer hashing. When the directory contains a
matching cache for the FASTA sequence and k-mer size, AniAnn's loads it;
otherwise AniAnn's creates the cache during the run. Without `--cache-dir`,
hashes are streamed in memory and no disk cache is read or created.
**Default: caching disabled.**

`-j / --threads <INT>`

Maximum total compute concurrency used by AniAnn's. By default, AniAnn's uses
all threads available to its Numba runtime. During an uncached multi-band run,
one worker is reserved for streaming k-mer hashes while the remaining workers
run the matrix kernels. With `--threads 1`, hashing and matrix processing run
synchronously so the limit is preserved. **Default: all available threads.**

Distal annotation runs also write `satellite_dsu.tsv` and a human-readable
`satellite_dsu.txt`; non-distal runs do not create either file. Each
boundary-refined satellite is a DSU node, including unlinked singletons.
Validated distal links union their two satellite nodes, so chains of distal
relationships share one stable `component_id`; the table also reports
component size and direct-link count. BED `itemRgb` values are shared only by
satellites that have both the same DSU component and the same retained
NTRPrism signature (monomer, periodicity, and HOR status). Missing NTRPrism
monomers are not grouped by color.

`--identifier <STR>`

Name of identifier. Used when no matches to a k-mer db are found, or if `--classify` is not provided. bed file to output to. **Default: None.**

`-p / --plot <bool>`

Save two PNGs for every `--band` length matrix under
`<output directory>/matrix_plots`; plots are not displayed interactively. The
standard filename is a hollow, edge-only view: cyan intensity shows the
normalized Sobel gradient response and green outlines show detected diagonal
satellites. With `--distal`, distal link rectangles are also highlighted in
red. The companion `_identity.png` file is a higher-resolution, inverted
11-level Spectral ANI heatmap with an identity colorbar; values below the ANI
cutoff are white, and it has no Sobel, satellite, distal, or annotation legend.
Adjacent matrix pairs receive the same two views. Sobel
is visualization-only and does not affect satellite or distal-link prediction.
**Default: disabled.**

`--distal`

Detect distal-satellite links independently of whether plots are requested.
Detected off-diagonal blocks are written to
`<sequence>_distal_links.bedpe`, and used to link satellites in the DSU. A
compact candidate-neighborhood matrix retaining two neighboring windows on
either side of each candidate is used in memory and is not written to disk. An
unmatched distal axis must pass NTR Prism and boundary refinement before it is
added to BED/CSV or linked in the DSU. Strong distal support cannot rescue an
endpoint rejected at both k values.
Neighboring bands are bridged with bounded candidate-to-all comparisons in both
directions plus a 50-window seam scan. With both `--distal --plot`, links are
outlined in red. When the bridge finds evidence, a sparse
two-band heatmap is saved under `matrix_pairs/`; adjacent pairs are then skipped
by the final global comparison to avoid duplicate work.
**Default: disabled.**

`--verbose`

Show stage timings, the number of potential candidates, concise acceptance or
removal reasons for each boundary-refined candidate, and resolved boundary
histograms. Per-matrix timing comparisons and unresolved extension histograms
are omitted. Each candidate begins with a labeled separator, and every boundary
histogram line is capped at 80 characters, including searches that resolve after
extending the initial boundary range. **Default: disabled.**

`--log`

Write the verbose output to a timestamped file in the selected output directory
while continuing to show it in the terminal. The filename records the local run
date and time, for example
`anianns_annotation_log_2026-08-04_14-37-52.txt`. This option implies
`--verbose` and cannot be combined with `--quiet`. A distal run also writes a
per-sequence `<sequence>_distal_summary.txt` containing the score, density, row
coverage, column coverage, and hit count for every accepted distal satellite
pair.
**Default: disabled.**

`--quiet`

Suppress all stdout and stderr output, including the AniAnn's banner, sequence
status, progress bars, warnings, and completion messages. Output files are
still written normally. **Default: disabled.**

#### Sample run

`anianns -f sample_sequences/sample_hap1_.fa`

Upon running the above command, you should see the following output in `sample_hap1.bed`:

```
#chrom	start	end	name	score	strand	thickStart	thickEnd	itemRgb
sample_hap1	67178	407613		147	.	67178	407613	31,119,180
sample_hap1	458676	466557		64	.	458676	466557	174,199,232
sample_hap1	634665	668075		5	.	634665	668075	255,127,14
sample_hap1	670071	1148691		68	.	670071	1148691	255,187,120
sample_hap1	1148878	1472729		5	.	1148878	1472729	44,160,44
sample_hap1	1472878	2702615		42	.	1472878	2702615	152,223,138
sample_hap1	2729748	2834698		5	.	2729748	2834698	214,39,40
sample_hap1	3044478	3208742		147	.	3044478	3208742	255,152,150
sample_hap1	3225278	3297085		48	.	3225278	3297085	148,103,189

```

This is a BED file containing inferred satellite intervals. The value in the `score` column indicates the periodicity of the satellite array. Since no k-mer database was used, _AniAnn's_ does not attempt to classify each array.

To classify each line of the BED file into a known satellite array, a database of _k_-mers must be used. See [creating an annotation database](#creating-an-annotation-database) for more information. We will use the following provided _k_-mer db for our sample run:

`anianns -f sample_sequences/sample_hap1.fa --classify sample_sequences/sample_kmer_db`

```
#chrom	start	end	name	score	strand	thickStart	thickEnd	itemRgb
sample_hap1	67178	407613	ACRO	147	.	67178	407613	31,119,180
sample_hap1	458676	466557	Walusat	64	.	458676	466557	174,199,232
sample_hap1	634665	668075	HSat3	5	.	634665	668075	255,127,14
sample_hap1	670071	1148691	bSat	68	.	670071	1148691	255,187,120
sample_hap1	1148878	1472729	HSat3	5	.	1148878	1472729	255,127,14
sample_hap1	1472878	2702615	HSat1A	42	.	1472878	2702615	152,223,138
sample_hap1	2729748	2834698	HSat3	5	.	2729748	2834698	255,127,14
sample_hap1	3044478	3208742	ACRO	147	.	3044478	3208742	31,119,180
sample_hap1	3225278	3297085	CER	48	.	3225278	3297085	148,103,189
```

Note that you *must* use the default k-mer value as the classification database. The default _k_ = 21. _AniAnn's_ will label arrays it determines to be related as the same color in `itemRgb`.

For a more thorough analysis of the region, run `anianns -f sample_sequences/sample_hap1_.fa --distal`

To visualize the matrix evidence behind the annotations, add `--plot`:

`anianns annotate -f sample_sequences/sample_hap1.fa -d sample_hap1_plots --plot`

This saves an annotated Sobel view and a high-resolution spectral identity view
for each sequence band under `sample_hap1_plots/matrix_plots`. Satellite
intervals and Sobel edges are labeled on the annotated view, with its legend
placed outside the matrix. Combining `--plot` with `--distal` also outlines
supported distal matches and saves any adjacent-band views under
`matrix_pairs`.

![](images/sample_hap1_matrices_0001_0002_identity.png)

![](images/sample_hap1_matrices_0001_0002.png)


#### Repeat Masking

```
anianns annotate -f <FASTA_FILENAME(S)> --mask <ARGS>
```

Use of _AniAnn's_ as a tool to mask satellite arrays from a given sequence is done through the `-m/--mask` argument. _AniAnn's_ will output each sequence into its own masked fasta file in the `--directory` ouptut folder. By default, running `--mask` with no parameters will mask everything deemed a satellite array.

- If string parameter(s) are provided (e.g., `hSat1 hSat2`), _AniAnn's_ will mask arrays that match the provided class. Note this **must** be used in conjunction with `--classify` in order to match names. Matches are case-insensitive.
- If integer parameter(s) are provided (e.g., `6 7 42`), _AniAnn's_ will mask arrays whose predominant monomer length is equal to that provided. 


### Build db

```
anianns build_db -f <FASTA_FILENAME(S)> -b <BED_FILENAME(S)> <ARGS>
```

Building a database of satellite _k_-mers is how _AniAnn's_ can match a detected satellite array into a known repeat class. _AniAnn's_ will extract and compress _k_-mers at the coordinates provided by a bed file. Running this will create a directory at `--directory`. 

#### Required args

`-f / --fasta <FILENAME(S)>`

Fasta file(s) to input. Multifasta files are accepted. 

`-b / --bed <FILENAME(S)>`

Bed file(s) to input. Must contain at least 4 columns: 1 chrom, 2 start, 3 end, 4 name.  

#### Optional Arguments

`-c / --config <FILENAME>`

Name of config file to use. See [Sample database for more info](#sample-database)**Default: None.**

`-k / --kmer <INT>`

K-mer size. Note that k-mers **must** be the same length when running `anianns annotate` in order for classification to work. **Default: 21.**

`-d / --directory <DIR>`

Specifies the output directory where results will be saved. If not provided, _AniAnn's_ automatically creates an output folder in the current working directory:

- If the BED file contains a track header, the folder name will be derived from that header.
- If no header is present, the folder name will default to the k-mer length (e.g., k_21).
**Default: current working directory.**

`-v / --verbose <BOOL>`

Verbose logging output. Will output a log file into `--directory`. **Default: False.**

`-q / --quiet <BOOL>`

Suppress all logging output. **Default: False.**

#### Sample database

Here, we will create a db of known satellite arrays using the HG002 human genome CenSat annotation track ([credit: Hailey Loucks](https://github.com/hloucks/CenSatData/tree/main)):

`wget https://raw.githubusercontent.com/hloucks/CenSatData/refs/heads/main/HG002/v1.1/hg002v1.1.cenSatv2.0.bed`

We want to remove any centromere transition regions `ct` from this BED file:

`awk 'NR==1 || $4!="ct"' hg002v1.1.cenSatv2.0.bed > hg002v1.1.cenSatv2.0.ctRemoved.bed`

Finally, we want to group related satellite arrays into the same class. This is done using a `--config` file. For this example, we will use the file provided in `config` folder of this repo: 

```
head config/sample_config.json 
{
    "gSat": [
      "gSat(GSAT,GSATX)",
      "gSat(GSAT)",
      "gSat(GSATII,GSATX)",
      "gSat(GSATII,TAR1)",
      "gSat(GSATII)",
      "gSat(GSATX)",
      "gSat(TAR1)"
    ],
```

This merges the _k_-mers of all variations of gSat into the same class. Anything not in the config file is not included in the *k*-mer db. The config file must be in standard JSON format. If a config file is not provided, each unique value in column 4 of the input bed file will become its own unique class. 

Creating a *k*-mer db for HG002 using the provided config file takes around 3 minutes. This results in 16.9 million unique _k_-mers, compressed down into a 53mb directory. Note that increasing the *k*-mer size will increase the directory size, as a more specific *k*-mer threshold will increase the total number of unique *k*-mers.

### NTRPrism

NTRPrism reports the most common distances between consecutive occurrences of
the same forward k-mer in one FASTA region. Nearby spacing values are combined
before ranking and plotting, so values such as 170 and 171 bp contribute to the
same peak by default.

```bash
anianns ntrprism \
  -f assembly.fa.gz \
  -s chr1_MATERNAL \
  -r 120000000 121000000 \
  -k 6
```

The region uses 0-based, half-open coordinates. By default, the command prints
the ten strongest merged spacing peaks, each peak's count as a percentage of
the requested interval, and a horizontal ASCII histogram. It does not create
files unless `--save` is supplied:

```bash
anianns ntrprism \
  -f assembly.fa.gz \
  -s chr1_MATERNAL \
  -r 120000000 121000000 \
  -k 11 \
  --save \
  -d chr1_ntrprism
```

With `--save`, AniAnn's also writes a top-ten text report and a PNG histogram.
The `-k / --kmer` option controls the k-mer length and defaults to 21, matching
the annotation pipeline. The
default nearby-value merge distance is 1 bp; use `--merge-distance` to change
it. Use `--quiet --save` for files without the terminal report. Missing or
unreadable FASTA files, unknown sequence IDs, invalid ranges, and regions
shorter than the selected k-mer size produce an error and a nonzero exit status.

## Questions

For bug reports or general usage questions, please raise a GitHub issue, or email alex ~dot~ sweeten ~at~ nih ~dot~ gov
