Skip to content

gcomneno/digit-probe

Repository files navigation

Digit-Probe

Analizzatore statistico/strutturale per sequenze numeriche.

Supporta:

  • cifre (digits): file di sole cifre 0..9 (eventuali spazi/newline vengono ignorati)
  • interi (integers): un intero per riga con alfabeto dichiarato (--alphabet M)

Pensato per diagnosticare random-like vs struttura in stream numerici e per ispezionare bucket prodotti da strumenti esterni: (es. Turbo-Bucketizer) (o Turbo-Bucketizer-2)


✨ Cosa misura

  • Distribuzione per simbolo, chi-square, z-score
  • Runs test (pari/dispari)
  • Gaps per simbolo (conteggio e gap medio)
  • Autocorrelazione (lag 1..5)
  • Compression ratio (zlib) come proxy di ripetizione/struttura
  • N-gram predictor (n=1..3, split 80/20)
  • SchurProbe (additività mod M)
    • Coppie i<j, indice k=(i+j) mod R
    • Verifica (seq[i]+seq[j]) % M == seq[k]
    • N_triples = C(R,2), atteso E = N_triples/M, varianza N p (1-p), z-score standard

Output: stampa leggibile + JSON opzionale con --report-json (compatibile con compare_reports.py).


🚀 Installazione

Richiede Python 3.11+ (ok anche 3.13).

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt  # leggero: usa solo stdlib + pochi pacchetti

Suggerito: tenere i dataset/risultati fuori dal versionamento (.gitignore già predisposto).


🧩 Struttura repo (essenziale)

src/
  digit_probe.py          # core analyzer (CLI)
  compare_reports.py      # confronto tra più JSON
  make_datasets.py        # generatori semplici (pi, e, gradienti, ecc.)
  generative/
    gen_rng_digits_zoo.py # RNG Zoo per test di regressione
    gen_rng_1_90.py       # (opzionale) generatori su 1..90

tests/
  basic.sh                # smoke test rapido
  advanced.sh             # test avanzati (gradienti, bucket, schur-stress)
  test_rng_digits_ci.py   # test di regressione RNG Zoo

datasets/
  pi_100k.txt             # esempi offline
  e_100k.txt
  ...
Makefile

⌨️ Uso rapido

Modalità digits (cifre senza spazi “logici”)

python3 src/digit_probe.py --file pi_100k.txt --report-json pi.json
  • L’input è trattato come stream di cifre: i caratteri 0..9 vengono letti, tutto il resto viene ignorato (spazi, newline, virgole…).
  • File tipico: una lunga stringa di cifre, opzionalmente con newline finali.

Modalità integers (un intero per riga, serve --alphabet)

# Esempio: bucket in [0..4095]
python3 src/digit_probe.py --file buckets_k12.txt --integers --alphabet 4096 --report-json buckets.json
  • Ogni riga deve contenere un singolo intero (con eventuali spazi iniziali/finali).
  • I valori sono usati mod M (M = --alphabet), quindi un valore 5000 con --alphabet 4096 diventa 5000 % 4096.

Opzioni principali

--file PATH             input (digits o integers)
--n N                   limita la lunghezza analizzata
--integers              abilita modalità "integers"
--alphabet M            alfabeto per integers (obbligatorio con --integers)
--report-json OUT.json  salva un report JSON
--schur-N R             R massimo per SchurProbe (default: 5000)

🧷 Guida rapida – come preparare un tuo dataset

1. Decidi il tipo di sequenza

  • Cifre (digits):
    • sequenze come cifre di π, e, costanti, output di funzioni hash, stream di cifre da log, cifre di estrazioni del Lotto, ecc.
  • Interi (integers):
    • bucket ID (0..M-1),
    • valori discreti (stati di un automa, classi, label),
    • output di PRNG personalizzati, ecc.

2. Preparazione file per modalità digits

Formato consigliato: file di testo con solo cifre (più eventuali newline).

Esempi:

  • hai un CSV con cifre miste ad altro, puoi “spremere” solo i numeri:

    # Estrai solo cifre e scrivi in mydigits.txt
    tr -cd '0-9' < raw_input.txt > mydigits.txt
  • ora puoi analizzare:

    python3 src/digit_probe.py --file mydigits.txt --report-json mydigits.json

3. Preparazione file per modalità integers

Formato: un intero per riga.

Esempi:

  • hai bucket ID 0..4095:

    17
    210
    3
    4095
    0
    ...
    

    analisi:

    python3 src/digit_probe.py     --file my_buckets.txt     --integers     --alphabet 4096     --report-json my_buckets.json
  • hai numeri 1..90 (es. estrazioni del Lotto) uno per riga:

    python3 src/digit_probe.py     --file lotto_2025_numbers.txt     --integers     --alphabet 90     --report-json lotto_2025_integers.json

    (internamente verranno usati mod 90, ma se i valori sono già in 1..90 l’effetto è nullo).

4. Confrontare più dataset

Una volta che hai i tuoi JSON (--report-json), puoi confrontarli:

python3 src/compare_reports.py   out/mio_dataset.json   out/rng_uniform.json   --baseline out/rng_uniform.json   --md out/compare_mio_vs_rng.md

Questo produce un Markdown con:

  • differenze sulle metriche chiave (chi-square, autocorr, compressione, Schur…),
  • un AnomalyScore sintetico per capire chi è più “strano” rispetto alla baseline.

🧪 RNG Zoo & test di regressione (CI)

Il progetto contiene una piccola RNG Zoo a cifre per verificare che gli strumenti diagnostici non si rompano nel tempo:

Dataset generati da src/generative/gen_rng_digits_zoo.py:

  • digits_rng_uniform.txt → cifre 0..9 da RNG uniforme “sano”
  • digits_rng_biased7.txt → distribuzione truccata con 7 iper-favorito (~40%)
  • digits_rng_lcg_mod10.txt → LCG modulo 10 marcio e periodico (solo 4 cifre usate)

La CI (GitHub Actions) lancia pytest e verifica che:

  • l’RNG uniforme risulti:
    • chi-square piccolo,
    • z-score per cifra vicino a 0,
    • compressione compatibile con random-like,
    • SchurProbe con z vicino a 0;
  • il dataset biased7 risulti fortemente non uniforme:
    • il 7 è iper-frequente,
    • chi-square e SchurProbe con z enormi,
    • gaps e compressione rivelano il trucco;
  • il dataset LCG mod10 venga visto come completamente non-random:
    • solo poche cifre usate,
    • chi-square mostruoso,
    • autocorrelazioni forti,
    • compressione quasi totale.

Se cambiano algoritmi/parametri interni e questi test iniziano a fallire, è un campanello d’allarme: qualcosa nel motore di analisi si è degradato.


📦 Dataset “famosi” (offline)

Genera 100k cifre di π o e senza rete:

python3 src/make_datasets.py --n 100000 --only pi --offline
python3 src/make_datasets.py --n 100000 --only e  --offline

Poi analizza:

python3 src/digit_probe.py --file pi_100k.txt --report-json pi.json
python3 src/digit_probe.py --file e_100k.txt  --report-json e.json

🧪 Suite di test

Comandi:

make test-basic     # random, pi (offline), sequenza costante
make test-advanced  # gradiente, bucket (sintetico o Turbo), schur-stress
make selftest       # aggrega i JSON in out/SELFTEST_SUMMARY.md

Se hai Turbo-Bucketizer e vuoi usarlo davvero nei test avanzati:

TURBO_BIN=/percorso/turbo-bucketizer make test-advanced

Risultati in out/ (JSON + Markdown di confronto).


🔍 Confronto report (più file)

Confronta due o più JSON:

python3 src/compare_reports.py out/pi.json out/e.json --baseline out/pi.json --md out/compare_pi_e.md

Output sintetico (ordinabile) con indicatori di severità e AnomalyScore.


📘 Esempi interpretativi (due dritte)

  • Compressione zlib

    • digits (M=10) random-like ⇒ ~0.46–0.50
    • valori molto bassi (≪0.44) ⇒ ripetizioni/strutture/periodicità
  • Autocorrelazione

    • random-like ⇒ |ρ| piccoli (≲0.02 con N grandi)
    • picchi stabili ⇒ dipendenze
  • SchurProbe (z)

    • z ≈ 0 ⇒ in linea con casualità mod M
    • |z| alto ⇒ struttura additiva (pattern, periodi, generazioni affini)

🧠 SchurProbe in due righe

Su R simboli (cap a --schur-N), testiamo tutte le coppie i<j e chiediamo se la “somma mod M” riappare in posizione k=(i+j) mod R. Atteso “casuale”: 1 volta su M. Misuriamo quanto te ne discosti con uno z-score binomiale standard.


🔗 Integrazione con Turbo-Bucketizer

  • Esporta bucket come interi (0..(2^k-1)) in txt/csv
  • Analizza con --integers --alphabet 2^k
  • Confronta con baseline random, gradienti e sequenze sintetiche (tests/advanced.sh lo fa per te)

🧾 JSON di output (schema minimo)

{
  "mode": "digits|integers",
  "N": 100000,
  "alphabet": 10,
  "chi_square": 4.093,
  "expected_per_bin": 10000.0,
  "counts": {"0":9999, "1":10137, ...},
  "runs": {"Z": 0.565, "p_two_tailed": 0.5724},
  "autocorr": {"1": -0.0025, "2": 0.0022, ...},
  "compress_ratio": 0.4817,
  "ngram": {"1": 0.1013, "2": 0.1026, "3": 0.0998},
  "schur": {
    "triples": 12497500,
    "count": 124749,
    "expected": 125777.4,
    "fraction": 0.00998,
    "z": -2.91,
    "first_violation_index": 59
  }
}

🛠️ Note pratiche

  • In integers mode i valori sono usati mod M (M=--alphabet).
  • --n può accelerare prove rapide (es. --n 20000).
  • --schur-N (default 5000) limita il costo di SchurProbe (crescita ~quadratica).

Case study


📄 Licenza

MIT. Vedi LICENSE.


💡 Motto

“Se è random-like, non lo è per sempre. Se è strutturato, lo becchiamo.”

About

Analizzatore statistico e strutturale per sequenze numeriche — valuta casualità, uniformità, compressione, autocorrelazione, prevedibilità n-gram e schemi additivi di tipo Schur su dati di cifre o interi.

Topics

Resources

License

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors