Metadata-Version: 2.4
Name: otadtk
Version: 0.1.2
Summary: Optimal Transport Audio Distance Toolkit: a structural replacement for FAD/KAD with a learned Riemannian ground metric and per-sample diagnostics.
Author-email: Wonwoo Jeong <jeongwonwoo@sogang.ac.kr>
License: Creative Commons Attribution 4.0 International Public License
        
        Copyright (c) 2026 Wonwoo Jeong
        
        This work is licensed under the Creative Commons Attribution 4.0 International License.
        To view a copy of this license, visit http://creativecommons.org/licenses/by/4.0/ or
        send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
        
Project-URL: Homepage, https://github.com/wonwoo-jeong/otadtk
Project-URL: Repository, https://github.com/wonwoo-jeong/otadtk
Project-URL: Source, https://github.com/wonwoo-jeong/otadtk
Project-URL: Issues, https://github.com/wonwoo-jeong/otadtk/issues
Project-URL: Paper, https://arxiv.org/abs/2605.05554
Keywords: audio,evaluation,fad,kad,otad,optimal-transport,sinkhorn,riemannian-metric,generative-audio
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Multimedia :: Sound/Audio :: Analysis
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: <3.13,>=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.23
Requires-Dist: scipy>=1.10
Requires-Dist: torch>=2.1
Requires-Dist: torchaudio>=2.1
Requires-Dist: soundfile>=0.12
Requires-Dist: librosa>=0.10
Requires-Dist: tqdm>=4.65
Requires-Dist: pyyaml>=6.0
Requires-Dist: geomloss>=0.2.6
Requires-Dist: POT>=0.9.0
Requires-Dist: scikit-learn>=1.3
Provides-Extra: all-encoders
Requires-Dist: transformers>=4.40; extra == "all-encoders"
Requires-Dist: encodec>=0.1.1; extra == "all-encoders"
Requires-Dist: laion-clap>=1.1.4; extra == "all-encoders"
Requires-Dist: torchopenl3>=0.4.0; extra == "all-encoders"
Requires-Dist: resampy>=0.4.2; extra == "all-encoders"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0; extra == "dev"
Requires-Dist: ruff>=0.1.0; extra == "dev"
Dynamic: license-file

# otadtk — Optimal Transport Audio Distance Toolkit

A drop-in replacement for FAD / KAD that adds a **learned ground metric**
and **per-sample diagnostics**, with a CLI compatible with
[`fadtk`](https://github.com/microsoft/fadtk) /
[`kadtk`](https://github.com/YoonjinXD/kadtk).

## Features

- **Three metrics behind a uniform interface** — OTAD (default), FAD, KAD;
  swap with a single flag.
- **Three OTAD variants** — `agnostic` (recommended), `native`,
  `raw` (no adapter, ablation).
- **Nine bundled encoders** — VGGish, EnCodec, CLAP, OpenL3, AudioMAE,
  AST, BEATs, PANNs, PANNs-WGLM (lazy-loaded; register your own with
  `@register_encoder`).
- **Self-contained** — all 9 × 2 = 18 pretrained Riemannian adapters
  ship inside the wheel (`otadtk/checkpoints/`, ≈ 43 MB) with SHA-256
  hashes in `MANIFEST.json`. No network access at scoring time.
- **Per-sample diagnostics** — every evaluation file gets a marginal
  transport cost `cj`; the API exposes top-k offenders, contamination
  AUROC, separation ratio, and CSV export so you can plot or analyse
  with whatever tooling you prefer.
- **kadtk-compatible cache** — embeddings live in
  `<dir>/embedding/<model>/<basename>.npy` so `otadtk` and `kadtk` can
  share a working directory.
- **Three CLI entry points** — `otadtk`, `otadtk-embeds`,
  `otadtk-list-models`.
- **CPU & CUDA** — pure-PyTorch primitives; CUDA is auto-used when
  available.

## Install

```bash
pip install otadtk                     # core (numpy, torch, soundfile, …)
pip install "otadtk[all-encoders]"     # adds transformers, encodec, openl3, laion-clap
```

Python 3.10–3.12, PyTorch ≥ 2.1.

## CLI

```text
otadtk <model> <ref_dir> <eval_dir> [options]
```

| Option | Effect |
|---|---|
| `--variant {agnostic,native,raw}` | OTAD adapter variant (default `agnostic`) |
| `--epsilon FLOAT` | Sinkhorn regularisation (default `0.1`) |
| `--diagnose` | Compute per-sample `cj` and print the top offenders |
| `--diag-out PATH` | Write per-file `cj` to a CSV (implies `--diagnose`) |
| `--top-k INT` | How many offenders to print (default `10`) |
| `--indiv PATH` | Per-file `cj` CSV (sorted ascending) |
| `--fad` / `--kad` | Compute FAD / KAD instead of OTAD on the same cache |
| `--bandwidth FLOAT` | KAD bandwidth (defaults to test-set median) |
| `--csv PATH` | Append a single-row record `(metric, model, variant, ref, eval, score, time)` |
| `--device {cuda,cpu}` | Override device (default: auto) |
| `--workers INT` | Parallelism for the embedding cache |
| `--force-emb-encode` | Ignore an existing embedding cache |
| `--adapter-checkpoint PATH` | Use a custom adapter checkpoint |

Auxiliary commands:

```bash
otadtk-embeds  -m vggish -d ref/ eval/   # populate the cache only
otadtk-list-models                       # registered encoders + dim/sr
```

## Python

```python
from otadtk import OTAD

otad  = OTAD(model="panns", variant="agnostic", epsilon=0.10)
score = otad.score("ref/", "eval/")                     # scalar OTAD
diag  = otad.diagnose("ref/", "eval/")                  # OTADDiagnostics
print(diag.top_k(10))                                   # worst offenders
diag.to_csv("cj.csv")                                   # file,cj rows
# diag.cj is a numpy array; diag.files is a list[Path] — plot however you like.
otad.score_individual("ref/", "eval/", csv="per_file.csv")

# AUROC against a known contamination flag
flags = [f.name.startswith("CONTAMINATED_") for f in diag.files]
print(diag.auroc(flags), diag.separation_ratio(flags))

# Functional API on numpy banks
from otadtk import compute_otad, compute_fad, compute_kad
score, cj = compute_otad(ref_emb, eval_emb, return_sample_costs=True)
```

## Encoders

| `model` | dim | sr | source |
|---|---:|---:|---|
| `vggish`     |  128 | 16 kHz | torch.hub `harritaylor/torchvggish` |
| `panns`      | 2048 | 32 kHz | PANNs CNN14 |
| `panns_wglm` | 2048 | 32 kHz | PANNs Wavegram-LogMel-CNN14 (KAD-paper backbone) |
| `clap`       |  512 | 48 kHz | LAION-CLAP music branch |
| `openl3`     |  512 | 48 kHz | OpenL3 mel256-music |
| `audiomae`   |  768 | 16 kHz | AudioMAE-base |
| `ast`        |  768 | 16 kHz | AST-base |
| `beats`      |  768 | 16 kHz | BEATs-iter3+ |
| `encodec`    |  128 | 24 kHz | EnCodec 24 kHz |

## Variants

| Variant | Adapter | Use |
|---|---|---|
| `agnostic` | class-contrastive triplets | Default; cross-axis decompositions and MOS evaluation |
| `native`   | trained to minimise Sinkhorn divergence | High-sensitivity outlier scoring |
| `raw`      | identity (no adapter) | Ablation; reproduces the paper's "raw" baseline |

## Citation

```bibtex
@misc{jeong2026otad,
  author        = {Wonwoo Jeong},
  title         = {{OTAD}: Optimal Transport Audio Distance with Learned Riemannian Ground Metrics},
  year          = {2026},
  eprint        = {2605.05554},
  archivePrefix = {arXiv},
  primaryClass  = {eess.AS}
}
```

## License

CC-BY-4.0 — see [`LICENSE`](LICENSE).
