File size: 3,571 Bytes
63dd946
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# Copyright (c) 2026 The Anuvani-Diarization authors
# SPDX-License-Identifier: MIT OR Apache-2.0
"""check-exact: the byte-identity checks on cached front-end records and on audio inputs, on a stratified set, in about
a minute.

  source env/canonical.sh && python -s tools/check_exact.py [--devices cuda:0,cuda:1] [--sample 20]

1. Every GPU's networks against the release's own outputs on every batch shape (the start-up self-test,
   assets/nn_golden.json).
2. Through `Diarizer` (decode, windows, both networks, masks, records, clustering, decoder, RTTM): the 12 golden files,
   every robustness input (formats, rates, channels, cut lengths, silence, NaN, empty), the 1 h file and a stratified
   sample of benchmark files, against the raw-audio oracle (the release's own code on the same audio): every front-end
   record, every RTTM and every error byte-identical (tests/test_identity_audio.py's comparison).
3. 40 cached front-end records through the Rust post-processing against the cache oracle
   (tests/test_identity_cached.py).

Exit 0 only if every compared byte is identical. Prints one line per part and the time it took.
"""
import argparse
import json
import sys
import time
from pathlib import Path

import numpy as np

PROJ = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(PROJ / "tests"))


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--devices", default=None, help="comma-separated torch devices (default: every visible GPU)")
    ap.add_argument("--sample", type=int, default=20, help="extra benchmark files from the stratified sample")
    a = ap.parse_args()
    import records
    import test_identity_audio as g2
    import test_identity_cached as g1
    from anuvani_diar import Diarizer, _core
    failures = 0

    t0 = time.monotonic()
    d = Diarizer(keep_records=True, devices=a.devices.split(",") if a.devices else None, strict=True)
    print(f"1. self-test: every batch shape bit-identical on {len(d.nn.devices)} device(s) "
          f"({len(json.loads((PROJ / 'assets/nn_golden.json').read_text())['digests'])} shapes) — {time.monotonic() - t0:.1f} s",
          flush=True)

    t0 = time.monotonic()
    keys = [f"bench/{i['sid']}" for i in json.loads((PROJ / "artifacts/golden12_inputs.json").read_text())]
    keys += [k for k, i in g2.INPUTS.items() if i["set"] == "robust"]
    keys += ["long/long_1h"]
    keys += [f"{s}/{p.stem}" for s, p in records.sample(a.sample)]
    keys = [k for k in dict.fromkeys(keys) if k in g2.INPUTS]
    hours = sum(g2.INPUTS[k].get("seconds") or 0 for k in keys) / 3600
    bad = g2.compare(keys, d)
    failures += len(bad)
    print(f"2. engine vs raw-audio oracle: {len(keys) - len({b[0] for b in bad})}/{len(keys)} inputs identical "
          f"(records, RTTMs, errors; {hours:.2f} h of audio) — {time.monotonic() - t0:.1f} s", flush=True)
    for b in bad[:10]:
        print("   MISMATCH", b)

    t0 = time.monotonic()
    ok = 0
    sample = records.sample(40)
    for set_name, path in sample:
        rttm = g1.run(_core, set_name, path)
        want = (PROJ / "artifacts/oracle_cache/canonical" / set_name / "indic" / f"{path.stem}.rttm").read_bytes()
        ok += rttm.encode() == want
    failures += len(sample) - ok
    print(f"3. Rust post-processing vs cache oracle: {ok}/{len(sample)} RTTMs identical — {time.monotonic() - t0:.1f} s",
          flush=True)
    print("check-exact: PASS" if failures == 0 else f"check-exact: FAIL ({failures})")
    return 0 if failures == 0 else 1


if __name__ == "__main__":
    sys.exit(main())