- docs/SPEC.md sostituito da un file per argomento (indice in docs/README.md) - assunzioni A1-A24 con dove nel codice e cosa succede se false - formule in LaTeX corrette (nessuno spazio prima del dollaro di chiusura), simboli e chiavi in tabella - diagrammi Mermaid per il flusso degli algoritmi e le dipendenze dei moduli - figura dei domini sul seed 1 (docs/img/domini_esempio.png) - README ridotto a indice con avvio rapido; CLAUDE.md e docstring allineati Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
72 lines
8.3 KiB
Markdown
72 lines
8.3 KiB
Markdown
# Assunzioni
|
||
|
||
Ogni assunzione ha un identificativo (A1, A2, …) richiamato nel codice e negli altri documenti. Per ognuna: cosa si assume, perché è
|
||
ragionevole, **dove** il codice ne dipende e **cosa succede se è falsa**. Le assunzioni sul segnale valgono per i dati del generatore
|
||
`../gen_data` (sezione finale): su dati reali vanno verificate.
|
||
|
||
## Dati
|
||
|
||
| ID | Assunzione | Dove | Se è falsa |
|
||
|---|---|---|---|
|
||
| **A1** | Il tempo è **strettamente crescente**, anche non uniforme (jitter del campionamento) | `dati.leggi_csv` | il CSV è rifiutato (exit 4). Il programma non riordina né deduplica |
|
||
| **A2** | Il CSV ha l'intestazione e tutte le colonne richieste da `[dati]` (tempo e serie), tutte numeriche, senza celle vuote | `dati.leggi_csv` | exit 4 con il nome della colonna mancante |
|
||
| **A3** | Il tempo è in `ms` o `s` come dichiarato in `[dati] unita_tempo` | `dati.leggi_csv` | tutti i tempi e le costanti $\tau$ risultano scalati di 1000 senza avvisi: controlla l'unità |
|
||
| **A4** | Le serie condividono gli **stessi domini** (stessi istanti di rottura) | `analisi.analizza_domini`, `domini.proponi` | i domini sono cercati solo su `serie_riferimento` (default `T_C`); una serie con gradini diversi non è gestita: si lancia l'analisi due volte |
|
||
|
||
## Struttura del segnale
|
||
|
||
| ID | Assunzione | Dove | Se è falsa |
|
||
|---|---|---|---|
|
||
| **A5** | I **gradini di riscaldamento** sono salti positivi tra due campioni consecutivi molto più grandi del rumore **e** degli scarti casuali: `soglia_gradino_c` (20 °C) $\gg$ ampiezza massima degli scarti (≤ 4 °C) | `domini.trova_gradini` | gradini più piccoli della soglia non sono visti (il segnale resta un unico tratto); scarti più grandi della soglia sono scambiati per gradini. Soluzione: adattare la soglia o correggere `domini.toml` a mano |
|
||
| **A6** | La **salita dura pochi campioni** (2–3): il sensore ha costante di tempo ($\tau_\text{sensore} \approx 0.07$ s) minore dell'intervallo di campionamento (≈ 0.15 s) | `domini.trova_gradini` (estensione ai campioni adiacenti), `analisi._gradino` | con un sensore più lento la salita avrebbe più campioni: verrebbe comunque raggruppata finché il salto resta sopra `soglia_bordo_sigma`$\cdot\sigma$, ma non sarebbe fittata |
|
||
| **A7** | Dopo ogni gradino la temperatura **decade esponenzialmente** verso un asintoto: $y = c + A\,e^{-t'/\tau}$ | `modelli.MODELLI["esp_decad"]` | residui sistematici, `ρ₁` alto, «modello non adeguato». Con più processi di raffreddamento servirebbe un modello a più esponenziali (non presente, vedi [limiti.md](limiti.md)) |
|
||
| **A8** | Tutti i raffreddamenti hanno la **stessa costante di tempo $\tau$ e lo stesso asintoto $c$** (stesso processo fisico: dispersione verso l'ambiente). Anche nel generatore il decadimento dopo più gradini è la somma di esponenziali con lo stesso $k$, quindi un'unica esponenziale | `analisi.analizza_domini` (parametri `raffreddamento_intermedio_fissa_da_finale`) | gli intermedi sono forzati a $\tau$ e $c$ del finale: se in realtà differiscono, il fit dell'ampiezza $A$ è distorto e i residui lo mostrano. Per rimuovere il vincolo: `raffreddamento_intermedio_fissa_da_finale = []` |
|
||
| **A9** | Il **raffreddamento finale** è l'ultimo dominio ed è abbastanza lungo (centinaia di campioni) da stimare $c$, $A$, $\tau$ **liberamente** | `analisi.analizza_domini` | se il CSV finisce subito dopo un gradino, il finale è corto e $\tau$ può risultare non identificabile; allora anche gli intermedi restano liberi e possono essere indeterminati |
|
||
| **A10** | Il **riposo** (prima del primo gradino) è una baseline costante, lineare o esponenziale lenta (l'ambiente che si assesta) | `[fit.modelli_per_tipo] riposo` | il modello scelto è solo descrittivo: con una deriva lenta vince `lineare` o `esp_decad` |
|
||
| **A11** | Ogni dominio è **indipendente**: nessun vincolo di continuità tra domini | `analisi` | i modelli adiacenti possono non combaciare ai confini; il «salto» tra due raffreddamenti è il gradino |
|
||
| **A12** | Non esistono gradini **discendenti** (un calo brusco non è un evento): si cercano solo salti positivi | `domini.trova_gradini` (`d > soglia`) | un calo brusco resterebbe dentro un raffreddamento, aumentando RMSE e $\rho_1$ |
|
||
|
||
## Rumore
|
||
|
||
| ID | Assunzione | Dove | Se è falsa |
|
||
|---|---|---|---|
|
||
| **A13** | Il rumore è circa **gaussiano bianco**, con $\sigma$ stimabile per dominio da $\sigma = 1.4826\,\mathrm{MAD}(\Delta^2 y)/\sqrt{6}$ | `domini.sigma_rumore` | $\sigma$ sottostimata: più domini risultano «non adeguati». La stima ignora la parte lenta del rumore (vedi A14) |
|
||
| **A14** | Sul rumore bianco si aggiungono **disturbi che il modello non descrive**: deriva lenta (Ornstein–Uhlenbeck, $\tau \approx 3.5$ s) e **scarti casuali** (±4 °C, rientro < 1 s) | `fit.adatta` (perdita robusta), `uscita` (residui) | sono trattati come outlier dalla perdita `soft_l1` (non spostano molto il fit) ma lasciano residui autocorrelati ($\rho_1 \approx 0.7$) e intervalli di confidenza troppo stretti. Non è un errore del fit |
|
||
| **A15** | La **quantizzazione** (T a 0.01 °C, ambiente a 0.1 °C) è trascurabile rispetto al rumore | — | nessun trattamento specifico |
|
||
|
||
## Statistica
|
||
|
||
| ID | Assunzione | Dove | Se è falsa |
|
||
|---|---|---|---|
|
||
| **A16** | Gli intervalli al 95 % sono **gaussiani asintotici**: $\pm 1.96\,\sigma_p$ con $\mathrm{cov} = s^2 (J^\top J)^{-1}$, $s^2 = \mathrm{RSS}/(n-k)$ | `fit.adatta` | con pochi campioni o residui non gaussiani sono approssimati |
|
||
| **A17** | L'autocorrelazione dei residui è di tipo AR(1): gli errori si allargano di $\sqrt{(1+\rho_1)/(1-\rho_1)}$ se $\rho_1$ supera `soglia_acf` | `fit.adatta` | correzione approssimata; con autocorrelazione di altra forma gli intervalli restano stretti |
|
||
| **A18** | Per i parametri **fissi** l'incertezza non si propaga: gli intervalli di $A$ nei raffreddamenti intermedi sono **condizionati** a $c$ e $\tau$ del finale | `fit.adatta`, `uscita._params` | sono sottostimati rispetto all'incertezza totale |
|
||
| **A19** | AICc e BIC si calcolano da $\mathrm{RSS}$ come se i residui fossero gaussiani, anche se il fit usa la perdita robusta | `fit.adatta` | confronto tra modelli approssimato; sensibile agli outlier |
|
||
| **A20** | Le serie con $\sigma$ non finita o nulla (dominio troppo corto) usano scala 1 per la perdita robusta | `fit.adatta` | solo robustezza ridotta |
|
||
|
||
## Progetto
|
||
|
||
| ID | Assunzione | Dove |
|
||
|---|---|---|
|
||
| **A21** | La **suddivisione in domini la decide l'utente**: la proposta automatica è modificabile (`domini.toml`) e il fit non la cambia mai | `cli._passo_domini`, `domini.leggi_toml` |
|
||
| **A22** | Il programma è **deterministico**: nessuna partenza casuale, nessun seme | `fit._partenza` (griglia su $\tau$, minimi quadrati lineari) |
|
||
| **A23** | Il **riscaldamento non si fitta** (2–3 campioni): si riporta il salto $\Delta = y_\text{fine salita} - y_\text{prima}$, che è una stima per difetto del gradino reale perché parte della salita può cadere sul campione precedente | `analisi._gradino` |
|
||
| **A24** | I **modelli provati** sono fissati per tipo di dominio in configurazione, non scelti dal programma | `[fit.modelli_per_tipo]` |
|
||
|
||
## Origine dei dati di prova (`../gen_data`)
|
||
|
||
I CSV in `input/gen_<seed>.csv` sono prodotti dal generatore con la sua configurazione d'esempio. Caratteristiche che fondano le
|
||
assunzioni sopra (dettaglio: `../gen_data/docs/SPEC.md` e `../gen_data/config.toml`):
|
||
|
||
| Aspetto | Valore nel generatore | Assunzioni |
|
||
|---|---|---|
|
||
| intervallo di campionamento | 150.5 ms con jitter ±0.6 ms | A1, A6 |
|
||
| gradini di riscaldamento | 3, a ≈ 15.7, 17.8, 21.7 s, ampiezza ≈ 88, 75, 65 °C (perturbati per seed) | A5, A7 |
|
||
| raffreddamento | $k = 0.063\ \mathrm{s^{-1}}$, cioè $\tau = 15.9$ s (perturbato del 2 % per seed) | A7, A8 |
|
||
| risposta del sensore | filtro del primo ordine, $\tau_\text{sensore} = 0.07$ s | A6 |
|
||
| rumore bianco | $\sigma = 0.11$ °C a freddo, $0.20$ a caldo; la stima per dominio dà ≈ 0.08–0.3 | A13 |
|
||
| deriva lenta | Ornstein–Uhlenbeck, $\sigma = 0.04$ °C, $\tau = 3.5$ s | A14 |
|
||
| scarti casuali | in riscaldamento: 0.8 Hz, $\sigma$ 2.5 °C; in raffreddamento: 0.25 Hz, media −1 °C; ampiezza ≤ 4 °C, rientro 0.3–0.7 s | A5, A14 |
|
||
| ambiente | $23.1 - 0.45\,e^{-t/58}$ °C, non sale quando T è alta | A10; $c$ di `dT_C` ≈ 0 |
|
||
| `dT_C` | $T - T_\text{amb}$ calcolato dopo rumore e quantizzazione di entrambi i canali | A4 |
|