From f28bddc5a2afb711df5934af9e7319a3e0f7bc83 Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Thu, 29 Oct 2026 10:44:41 +0100 Subject: [PATCH] Documentazione completa in docs/: assunzioni, algoritmi, moduli, configurazione, output, test, limiti - 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 --- CLAUDE.md | 4 +- README.md | 121 ++++---------------- docs/README.md | 18 +++ docs/SPEC.md | 74 ------------ docs/algoritmi.md | 220 ++++++++++++++++++++++++++++++++++++ docs/assunzioni.md | 71 ++++++++++++ docs/configurazione.md | 81 +++++++++++++ docs/img/domini_esempio.png | Bin 0 -> 158312 bytes docs/limiti.md | 38 +++++++ docs/moduli.md | 133 ++++++++++++++++++++++ docs/output.md | 80 +++++++++++++ docs/panoramica.md | 63 +++++++++++ docs/test.md | 83 ++++++++++++++ docs/uso.md | 76 +++++++++++++ src/fitseg/analisi.py | 2 +- src/fitseg/dati.py | 2 +- src/fitseg/modelli.py | 2 +- tests/test_selezione.py | 2 +- 18 files changed, 892 insertions(+), 178 deletions(-) create mode 100644 docs/README.md delete mode 100644 docs/SPEC.md create mode 100644 docs/algoritmi.md create mode 100644 docs/assunzioni.md create mode 100644 docs/configurazione.md create mode 100644 docs/img/domini_esempio.png create mode 100644 docs/limiti.md create mode 100644 docs/moduli.md create mode 100644 docs/output.md create mode 100644 docs/panoramica.md create mode 100644 docs/test.md create mode 100644 docs/uso.md diff --git a/CLAUDE.md b/CLAUDE.md index 1109018..de8acda 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co `fitseg` legge un CSV `t_ms,T_C,dT_C` (temperatura di un sensore IR), lo divide in **domini** (riposo / riscaldamento = la salita rapida di un gradino, 2–3 campioni / raffreddamento = il decadimento dopo ogni gradino) e trova per ogni (dominio, serie) l'equazione che meglio lo approssima. Documentazione, commenti e messaggi sono in italiano: mantieni la lingua. -Fonti di verità (non duplicarne il contenuto): [docs/SPEC.md](docs/SPEC.md) (algoritmi, limiti), [README.md](README.md) (uso, test), [config.toml](config.toml) (unica fonte dei valori: nessun default nel codice, ogni chiave nuova va in `CHIAVI` di [src/fitseg/config.py](src/fitseg/config.py) e in SPEC). +Fonti di verità (non duplicarne il contenuto): la documentazione in [docs/](docs/README.md) — [assunzioni](docs/assunzioni.md) (A1–A24, richiamate nel codice), [algoritmi](docs/algoritmi.md), [moduli](docs/moduli.md), [configurazione](docs/configurazione.md), [output](docs/output.md), [test](docs/test.md), [limiti](docs/limiti.md) — e [config.toml](config.toml), unica fonte dei valori: nessun default nel codice, ogni chiave nuova va in `CHIAVI` di [src/fitseg/config.py](src/fitseg/config.py) e in [docs/configurazione.md](docs/configurazione.md). Il [README](README.md) è solo un indice con l'avvio rapido. **Quando cambi comportamento aggiorna il file di docs corrispondente** (e le assunzioni se ne cambia una). ## Comandi @@ -24,7 +24,7 @@ uv venv .venv && uv pip install -e ".[dev]" - I domini derivano **solo dai gradini positivi** di `serie_riferimento` (`diff > soglia_gradino_c`): la salita (estesa ai campioni adiacenti ancora in salita) è un `riscaldamento`, il tratto dopo è un `raffreddamento`; con N gradini i domini sono 1 + 2N. `domini.toml` è modificabile e deve essere contiguo. - `analisi.analizza_domini`: il raffreddamento **finale** (ultimo) si fitta per primo, libero; i raffreddamenti intermedi fissano `c`/`tau` ai suoi valori (`raffreddamento_intermedio_fissa_da_finale`, `adatta(..., fissi=)`). Il `riscaldamento` non si fitta: `_gradino` ne riporta il salto (`vs["gradino"]`, `sigma` None). Modelli per tipo in `[fit.modelli_per_tipo]` (riposo, raffreddamento). - `fit.adatta`: `least_squares` con perdita robusta; l'esponenziale parte da una griglia su τ. "Non identificabile" = condizione > soglia **o** IC 95 % > |valore| per un parametro diverso da `a`/`c`. σ del rumore **per dominio** da `MAD(d²y)`; tempo traslato a `t' = t − t0`. -- Nuovo modello = voce in `modelli.py` + riga in SPEC §3 + nome in `[fit.modelli_per_tipo]`. Non ci sono partenze casuali: il programma è deterministico. +- Nuovo modello = voce in `modelli.py` + riga in [docs/algoritmi.md](docs/algoritmi.md) §5.1 e in [docs/moduli.md](docs/moduli.md) + nome in `[fit.modelli_per_tipo]`. Non ci sono partenze casuali: il programma è deterministico. ## Test diff --git a/README.md b/README.md index 87d8bab..dcb3fcd 100644 --- a/README.md +++ b/README.md @@ -1,106 +1,31 @@ # fitseg -Legge un CSV `t_ms,T_C,dT_C` (temperatura di un sensore IR che si scalda e si raffredda), riconosce **riposo**, **riscaldamento** (la salita rapida di ogni gradino, 2–3 campioni) e **raffreddamento** (il decadimento esponenziale tra un gradino e l'altro e dopo l'ultimo), lo divide in **domini** e trova per ogni dominio e ogni serie l'equazione che lo approssima meglio, con minimi quadrati non lineari. +Legge un CSV `t_ms,T_C,dT_C` (temperatura di un sensore IR che si scalda a gradini e si raffredda), lo divide in **domini** (riposo, riscaldamento, raffreddamento) e +trova per ognuno l'equazione che lo approssima meglio. Produce, per ogni CSV, i grafici della suddivisione, la sovrapposizione dei dati grezzi con le equazioni trovate +e un report in Markdown con formule in LaTeX. -Risultato per dominio: modello scelto, parametri con intervallo di confidenza al 95 %, metriche (RMSE, R², AICc, BIC, autocorrelazione dei residui), un grafico dati/equazioni e un report Markdown. +## Documentazione -## Installazione +Tutto è spiegato in **[docs/](docs/README.md)**: assunzioni, algoritmi, moduli, configurazione, output, test, limiti. -Python ≥ 3.11 e [uv](https://docs.astral.sh/uv/) (`curl -LsSf https://astral.sh/uv/install.sh | sh`): +| Se vuoi… | Vai a | +|---|---| +| capire cosa fa e vedere un esempio | [docs/panoramica.md](docs/panoramica.md) | +| installare e lanciare i comandi | [docs/uso.md](docs/uso.md) | +| sapere cosa si assume e quando non vale | [docs/assunzioni.md](docs/assunzioni.md) | +| capire come si ottengono le equazioni | [docs/algoritmi.md](docs/algoritmi.md) | +| orientarti nel codice | [docs/moduli.md](docs/moduli.md) | +| cambiare un valore (`config.toml`) | [docs/configurazione.md](docs/configurazione.md) | +| leggere i file e i grafici prodotti | [docs/output.md](docs/output.md) | +| eseguire i test | [docs/test.md](docs/test.md) | +| conoscere i limiti | [docs/limiti.md](docs/limiti.md) | + +## Avvio rapido ```bash -uv venv .venv -uv pip install -e ".[dev]" # numpy, scipy, matplotlib; pytest e ruff per lo sviluppo +uv venv .venv && uv pip install -e ".[dev]" # installazione: docs/uso.md §1 +.venv/bin/fitseg domini input/gen_1.csv # passo 1: come verrebbero suddivisi i dati +.venv/bin/fitseg analizza input/gen_1.csv # tutti i passi → output/gen_1/{01_domini,02_sovrapposizione,03_report} +.venv/bin/pytest # test: docs/test.md ``` - -## Dati di ingresso - -CSV con intestazione, tempo crescente e le serie indicate in [config.toml](config.toml) (`[dati]`: colonne, unità `ms`/`s`, separatore). I CSV di prova vengono dal generatore `../gen_data`; **metti i CSV da analizzare nella cartella `input/`** (la cartella è nel repository, i file `*.csv` no); i test di integrazione usano i seed 1–3: - -```bash -mkdir -p input -for s in 1 2 3; do - (cd ../gen_data && .venv/bin/python genera.py config.toml --seed $s -o ../EquationMiner/input/gen_$s.csv) -done -``` - -## Uso - -```bash -.venv/bin/fitseg domini input/gen_1.csv # passo 1: come verrebbero suddivisi i dati (tabella, domini.png, domini.toml), nessun fit -.venv/bin/fitseg analizza input/gen_1.csv # tutti i passi: domini, equazioni, sovrapposizione, report -``` -`--config PATH` (default `config.toml`) e `--output DIR` (default `output`) vanno **prima** del sottocomando. I file di ogni analisi vanno in `output//`. - -**Correggere i domini a mano**: apri `output/gen_1/01_domini/domini.toml`, modifica i tempi (i domini devono restare contigui) e rilancia -```bash -.venv/bin/fitseg analizza input/gen_1.csv --domini output/gen_1/01_domini/domini.toml -``` -```toml -[[dominio]] -tipo = "riscaldamento" # riposo | riscaldamento | raffreddamento -t0_s = 15.8 # incluso -t1_s = 17.76 # escluso (l'ultimo dominio arriva a fine dati) -``` - -### Cosa produce - -Una cartella per ogni CSV analizzato, con un passo per sottocartella (`output/` è ignorata da git): - -``` -output/gen_1/ -├── 01_domini/ suddivisione dei dati -│ ├── domini.png dati grezzi con i domini (colore per tipo, numerati) -│ └── domini.toml domini usati, modificabili a mano -├── 02_sovrapposizione/ dati grezzi contro le equazioni trovate -│ ├── sovrapposizione_.png serie intera con le equazioni a tratti e i residui -│ └── dominio_.png un dominio: dati, modello scelto, altri modelli validi, residui -└── 03_report/ - ├── report.md report completo con tutte le immagini incorporate - └── risultati.json tutti i fit con tutte le metriche -``` - -`report.md` contiene: (1) suddivisione in domini, (2) tabella delle equazioni con parametri ± IC 95 %, (3) sovrapposizione dati–equazioni, (4) dettaglio per dominio con metriche e tabella di tutti i modelli provati. Tutte le grandezze matematiche (equazioni con i valori sostituiti, parametri ± IC, $\sigma$, $R^2$, $\rho_1$, $\Delta$, $t_0$, criteri di scelta) sono scritte in **LaTeX** (`$…$`, `$$…$$`): servono un visualizzatore Markdown con supporto matematico (GitHub, VS Code, Obsidian) e le immagini come link relativi. - -Come leggere il report: **salto** = riscaldamento descritto ma non fittato; **non adeguato** = RMSE oltre 1.3 volte il rumore stimato (guarda l'ACF1 dei residui); **indeterminato** = nessun modello valido o dominio con meno di `n_min_punti` campioni. Nel raffreddamento `τ` è la costante di tempo e `c` l'asintoto (≈ ambiente per `T_C`, ≈ 0 per `dT_C`). - -## Come funziona - -1. **Domini** (`domini.py`): un salto positivo tra due campioni consecutivi sopra `soglia_gradino_c` è un gradino. I campioni della salita formano un dominio `riscaldamento` (pochi punti, esteso ai campioni adiacenti ancora in salita). Prima del primo gradino: `riposo`; dopo ogni gradino: `raffreddamento`, fino al gradino successivo o alla fine. Con 3 gradini i domini sono 7: riposo, (riscaldamento, raffreddamento) × 3. -2. **Modelli** (`modelli.py`): `riposo` → `costante`, `lineare`, `esp_decad`; `raffreddamento` → solo `esp_decad` `y = c + A·exp(−t'/τ)`, con `t' = t − t0` del dominio. Il `riscaldamento` ha troppo pochi campioni per un fit: è descritto come **salto** (Δ, da → a, numero di campioni, durata). -3. **Fit** (`fit.py`): `scipy.optimize.least_squares` con perdita robusta; partenza dell'esponenziale da una griglia su τ. Il raffreddamento finale (il più lungo) si fitta per primo con parametri liberi; i raffreddamenti intermedi, tra due gradini (12–25 campioni), **fissano `c` e `τ`** ai suoi valori (stesse costanti fisiche; in pochi secondi di dati con scarti casuali τ non si stima da solo), restando libera solo `A`. Gli IC di `A` sono condizionati ai valori fissi. -4. **Scelta** (`analisi.py`): scarta i modelli non convergenti, con parametri a limite o non identificabili; preferisce i modelli «adeguati» (RMSE ≤ `rmse_vs_rumore_max` × σ rumore); tra quelli entro `soglia_delta_aicc` dal miglior AICc vince chi ha meno parametri. - -Specifica e limiti: [docs/SPEC.md](docs/SPEC.md). - -## Configurazione - -Tutti i valori sono in [config.toml](config.toml), nessun default nel codice (chiave mancante = errore, exit 3). Le principali: `[domini] soglia_gradino_c` (20 °C), `scarta_dopo_salto_n` (campioni esclusi all'inizio di ogni raffreddamento), `n_min_punti`; `[fit.modelli_per_tipo]` (modelli per tipo di dominio); `[fit] raffreddamento_intermedio_fissa_da_finale` (`["c", "tau"]`, `[]` = parametri liberi), `soglia_delta_aicc`, `rmse_vs_rumore_max`. - -Nuovo modello: voce in `MODELLI` di [src/fitseg/modelli.py](src/fitseg/modelli.py), riga in SPEC §4 e nome in `[fit.modelli_per_tipo]`. - -## Test - -```bash -.venv/bin/pytest # tutti (≈ 4 s) -.venv/bin/pytest tests/test_fit.py # un file -.venv/bin/pytest -k raffreddamento # per nome -.venv/bin/ruff check src tests -``` - -| File | Verifica | Servono i CSV? | -|---|---|---| -| `test_domini.py` | 7 domini con 3 gradini (riscaldamento = pochi campioni), nessun gradino, un solo gradino, gradino sotto soglia, confini vicini ai gradini veri, `domini.toml` (andata e ritorno, non contiguo, tipo non valido) | no | -| `test_fit.py` | recupero di τ, `c`, `A` su segnale sintetico (τ = 16 s), parametri fissi, τ non identificabile su tratti corti e rumorosi, analisi completa, dominio troppo corto, riproducibilità | no | -| `test_selezione.py` | regola di scelta | no | -| `test_config_dati.py` | errori di config e CSV | no | -| `test_integrazione.py` | domini e τ su `input/gen_{1,2,3}.csv`; CLI `domini` e `analizza --domini`; codici di uscita | sì (altrimenti saltati) | - -I test unitari generano il segnale da soli (`tests/conftest.py`). Prova manuale: `fitseg analizza input/gen_1.csv` e controlla in `output/gen_1/03_report/report.md` 7 domini (riposo, poi riscaldamento e raffreddamento per ognuno dei 3 gradini) e τ ≈ 16 s nel raffreddamento finale; in `01_domini/domini.png` ogni raffreddamento ha un colore diverso. - -Codici di uscita: 0 ok · 3 configurazione non valida · 4 CSV illeggibile/non valido · 6 `domini.toml` non valido. - -## Limiti noti - -- Nel raffreddamento i residui sono autocorrelati (ACF1 ≈ 0.7) per scarti casuali e deriva lenta del sensore: i parametri sono buoni, ma gli IC sono troppo stretti e il dominio risulta «non adeguato». -- Ogni dominio è fittato in modo indipendente (nessun vincolo di continuità); provato su pochi seed e con la config d'esempio di `gen_data`. +I CSV da analizzare vanno in `input/` (dati di prova da `../gen_data`: [docs/uso.md](docs/uso.md) §2). Il report è `output//03_report/report.md`. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..ebf448b --- /dev/null +++ b/docs/README.md @@ -0,0 +1,18 @@ +# Documentazione di fitseg + +Un file per argomento. Per un primo giro leggi **panoramica**, poi **uso**; per capire perché i numeri sono quelli che sono, **assunzioni** e **algoritmi**. + +| File | Contenuto | Quando leggerlo | +|---|---|---| +| [panoramica.md](panoramica.md) | cosa fa il programma, il segnale, glossario, un esempio completo | per iniziare | +| [uso.md](uso.md) | installazione, dati di ingresso, comandi, correggere i domini a mano, codici di uscita | per usarlo | +| [assunzioni.md](assunzioni.md) | ogni assunzione (A1–A24): perché, dove nel codice, cosa succede se è falsa; origine dei dati di prova | per fidarsi (o no) dei risultati | +| [algoritmi.md](algoritmi.md) | rilevamento dei gradini, domini, fit, incertezze, metriche, scelta del modello, con le formule | per capire i numeri | +| [moduli.md](moduli.md) | un modulo alla volta: responsabilità, funzioni, strutture dati, come aggiungere un modello | per modificare il codice | +| [configurazione.md](configurazione.md) | ogni chiave di `config.toml`, formato di `domini.toml`, esempi | per cambiare un valore | +| [output.md](output.md) | file prodotti, colori, grafici, struttura del report, come leggere i messaggi | per leggere i risultati | +| [test.md](test.md) | come eseguire i test, cosa verifica ognuno, il segnale sintetico | per sviluppare | +| [limiti.md](limiti.md) | limiti noti, estensioni possibili, scelte progettuali | prima di estendere | + +Convenzioni: le assunzioni hanno identificativi **A1…A24** richiamati dagli altri file e dai commenti nel codice; le chiavi di configurazione sono scritte +`in_questo_formato`; le formule sono in LaTeX (servono un visualizzatore Markdown con supporto matematico: GitHub, VS Code, Obsidian). diff --git a/docs/SPEC.md b/docs/SPEC.md deleted file mode 100644 index 71fa696..0000000 --- a/docs/SPEC.md +++ /dev/null @@ -1,74 +0,0 @@ -# `fitseg` — specifica - -**Obiettivo**: leggere un CSV (`t_ms,T_C,dT_C`, p.es. da `gen_data`), riconoscere **riposo**, **riscaldamento** (flusso in entrata: la salita rapida di ogni gradino, pochi campioni) e **raffreddamento** (decadimento esponenziale dopo ogni gradino), dividere il tempo in **domini** e trovare per ogni (dominio, serie) l'equazione che lo approssima meglio. Valori configurabili: solo in [`config.toml`](../config.toml). Uso: [README](../README.md). - -## 1. Flusso - -``` -CSV ─► [1 DOMINI] gradini ⇒ riposo | (riscaldamento, raffreddamento) per ogni gradino ─► output//01_domini/ (domini.png, domini.toml modificabile) - ▼ - [2 FIT] per (dominio, serie): modelli del tipo ─► regola di scelta ─► 02_sovrapposizione/ (dati contro equazioni) - ▼ - [3 REPORT] 03_report/report.md (immagini incorporate, formule in LaTeX) e risultati.json -``` -Tutto il calcolo è deterministico (nessuna partenza casuale, nessun LLM). Ogni CSV ha la sua cartella `output//`. - -## 2. Domini (`domini.py`) - -Struttura fisica (vedi `gen_data/docs/SPEC.md`): T parte dall'ambiente, **gradini istantanei positivi** (2–4 s uno dall'altro): la salita dura 2–3 campioni ed è il **riscaldamento**; ognuno è seguito da un decadimento esponenziale, il **raffreddamento**, che dopo l'ultimo gradino prosegue fino all'ambiente. Compaiono anche scarti casuali (±4 °C, rientro < 1 s). - -1. `d = diff(y)` sulla `serie_riferimento`; gradino = `d > soglia_gradino_c`; rilevazioni più vicine di `distanza_min_salti_campioni` sono lo stesso gradino. -2. I campioni della salita formano il dominio `riscaldamento`: dal primo campione già in salita all'ultimo (si estende ai campioni adiacenti finché `d > soglia_bordo_sigma · σ_d`, σ_d = MAD dei salti). -3. Prima del primo gradino `riposo`; dopo ogni gradino `raffreddamento`, fino al gradino successivo o alla fine. Con N gradini i domini sono 1 + 2N. Senza gradini: un solo `riposo`. Un raffreddamento senza campioni (gradini adiacenti) non viene creato. -4. I primi `scarta_dopo_salto_n` campioni di ogni `raffreddamento` sono esclusi dal fit (sicurezza dopo la salita). - -Ipotesi: tempo crescente; rumore circa gaussiano, σ stimato per dominio da `1.4826·MAD(d²y)/√6`; le serie condividono i domini; i gradini sono salti positivi ≫ rumore e scarti casuali (`soglia_gradino_c` ≫ ampiezza degli scarti). - -`domini.toml` è modificabile (`tipo`, `t0_s` incluso, `t1_s` escluso; ultimo dominio fino a fine dati); i domini devono essere contigui, altrimenti exit 6. - -## 3. Modelli (`modelli.py`, t' = t − t0 del dominio) - -| Id | Equazione | Parametri | Tipi di dominio (`[fit.modelli_per_tipo]`) | -|---|---|---|---| -| `costante` | `y = a` | a | riposo | -| `lineare` | `y = a + b·t'` | a, b | riposo | -| `esp_decad` | `y = c + A·exp(−t'/τ)` | c, A, τ | riposo, raffreddamento | - -Il raffreddamento è esponenziale; il **riscaldamento** (2–3 campioni) non ha modelli: è descritto come salto (Δ = ultimo campione di salita − campione precedente, da → a, n campioni, durata). Nel raffreddamento `c` è l'asintoto (≈ ambiente) e `τ = 1/k`. Nuovo modello = voce in `MODELLI`, riga qui, nome in config. - -## 4. Fit (`fit.py`) - -1. Dati del dominio (raffreddamento: senza i primi `scarta_dopo_salto_n` campioni); tempo traslato. -2. Partenza: polinomi per minimi quadrati; esponenziale con griglia log-spaziata su τ (`tau_min_rel·L` … `tau_max_rel·L`, L = durata) e `c`, `A` lineari. -3. `scipy.optimize.least_squares`, perdita `perdita` (robusta `soft_l1`, scala `scala_perdita_sigma·σ`), limiti su τ. -4. IC 95 % = ±1.96·σ_p da `s²(JᵀJ)⁻¹`, moltiplicati per `√((1+ρ)/(1−ρ))` se ACF1 > `soglia_acf` (`correggi_autocorrelazione`). -5. Metriche: RSS, RMSE, R², AICc, BIC, ACF1, parametri a limite, numero di condizione. **Non identificabile** se condizione > `soglia_condizione` o un parametro (esclusi `a`, `c`) ha IC 95 % > |valore|. - -### 4.1 Costanti condivise tra i raffreddamenti (`analisi.py`) -Ogni raffreddamento segue la stessa legge (stessa costante di tempo, stesso asintoto ≈ ambiente). Il raffreddamento **finale** (ultimo dominio, il più lungo) si fitta per primo con parametri liberi; i raffreddamenti **intermedi** (tra due gradini, 12–25 campioni) fissano i parametri di `raffreddamento_intermedio_fissa_da_finale` (default `c`, `tau`) ai suoi valori e hanno libera solo `A`. Motivo: con gli scarti casuali del sensore, in pochi secondi di dati τ non si identifica da solo (su dati puliti sì). Gli IC di `A` sono condizionati ai valori fissi; i parametri fissi sono marcati «(fisso)» nel report. Se manca un finale valido, o la lista è vuota, i parametri restano liberi. - -### 4.2 Regola di scelta (`analisi.scegli`) -Scarta non convergenti, con parametri a limite o non identificabili. Se esistono modelli con `RMSE ≤ rmse_vs_rumore_max·σ` («adeguati») si sceglie tra questi, altrimenti tra tutti i validi; tra i modelli entro `soglia_delta_aicc` dal miglior AICc vince quello con meno parametri. Nessun modello valido ⇒ **indeterminato**; meno di `n_min_punti` campioni ⇒ non fittato. Scelto ma con RMSE > soglia·σ ⇒ «non adeguato» nel report. - -## 5. CLI e file - -| Comando | Funzione | -|---|---| -| `fitseg domini CSV` | passo 1: tabella, `01_domini/domini.png`, `01_domini/domini.toml` (nessun fit) | -| `fitseg analizza CSV [--domini FILE]` | tutti i passi; con `--domini` usa i domini corretti a mano al posto della proposta | - -File in `output//`: `01_domini/{domini.png, domini.toml}` · `02_sovrapposizione/{sovrapposizione_.png, dominio_.png}` · `03_report/{report.md, risultati.json}`. Il report scrive in LaTeX tutte le grandezze matematiche (`$…$` inline, `$$…$$` per l'equazione scelta con i valori sostituiti; il campo `equazione` di `MODELLI` è LaTeX). Il report (indice: 1 suddivisione, 2 equazioni, 3 sovrapposizione, 4 dettaglio per dominio) incorpora le immagini con percorsi relativi. - -Opzioni globali prima del sottocomando: `--config PATH`, `--output DIR` (default `[output] cartella_output`). Exit: 0 ok · 3 config non valida · 4 CSV illeggibile · 6 domini non validi. - -## 6. Test - -Unitari su un segnale sintetico (`tests/conftest.py`: τ = 16 s, 3 gradini, nessun disturbo a salto) e di integrazione su `input/gen_{1,2,3}.csv` (saltati se mancano). Dettaglio: README, «Test». Coperto: domini (3, 0, 1 gradino, sotto soglia, confini), recupero di τ/`c`/`A`, parametri fissi e ereditati dal raffreddamento, regola di scelta, errori di config/CSV/`domini.toml`, CLI. - -## 7. Limiti noti - -- **Residui autocorrelati nel raffreddamento** (ACF1 ≈ 0.7, RMSE ≈ 0.36 contro σ ≈ 0.15): scarti casuali e deriva lenta non sono nel modello; parametri buoni, IC troppo stretti, dominio «non adeguato». -- **Riscaldamento non fittato**: con 2–3 campioni si riporta solo il salto Δ; il valore dipende da quanta salita cade sul campione precedente (campioni di transizione), quindi è una stima per difetto dell'ampiezza reale del gradino. -- **Raffreddamenti intermedi**: ACF1 alto e RMSE ≈ 2σ per gli scarti: «non adeguati» anche se la forma è giusta. -- `soglia_gradino_c` va adattata se i gradini reali sono più piccoli di 20 °C o gli scarti più grandi. -- Nessun vincolo di continuità tra domini. Provato su pochi seed e con la config d'esempio di `gen_data`. diff --git a/docs/algoritmi.md b/docs/algoritmi.md new file mode 100644 index 0000000..afadb0a --- /dev/null +++ b/docs/algoritmi.md @@ -0,0 +1,220 @@ +# Algoritmi + +Tutti i passi, nell'ordine in cui il programma li esegue. Le assunzioni richiamate (A1, A2, …) sono in [assunzioni.md](assunzioni.md); le +chiavi di configurazione (`in_questo_formato`) in [configurazione.md](configurazione.md); il modulo che implementa ogni passo in +[moduli.md](moduli.md). + +## Visione d'insieme + +```mermaid +flowchart TD + CSV(["CSV in input/"]) --> P1 + + subgraph P1["PASSO 1 · Domini"] + direction LR + L["1 · Lettura
validazione"] --> S["2 · Rumore
stima di σ"] --> G["3 · Gradini
salti sopra soglia"] --> D["4 · Domini
divisione del tempo"] + end + + P1 --> RIP["Riposo"] + P1 --> RIS["Riscaldamento
2–3 campioni"] + P1 --> INT["Raffreddamenti intermedi
tra due gradini, corti"] + P1 --> FIN["Raffreddamento finale
l'ultimo, il più lungo"] + + subgraph P2["PASSO 2 · Equazioni"] + direction TB + F0["5 · Fit
costante, lineare,
esponenziale"] + F1["5 · Fit esponenziale
c, A, τ liberi"] + F2["5b · Fit esponenziale
c e τ fissi, solo A libera"] + SALTO["7 · Salto Δ
descritto, non fittato"] + SC["6 · Scelta del modello
validi → adeguati → meno parametri"] + F0 --> SC + F1 --> SC + F2 --> SC + end + + RIP --> F0 + FIN --> F1 + INT --> F2 + RIS --> SALTO + F1 -. "passa c, τ" .-> F2 + + SC --> REP + SALTO --> REP + REP(["PASSO 3 · Sovrapposizione e report
dati grezzi contro equazioni
report.md · grafici · risultati.json"]) + + classDef dato fill:#eceff1,stroke:#607d8b,color:#263238 + classDef calcolo fill:#e3f2fd,stroke:#1a73e8,color:#0d47a1 + classDef dominio fill:#fff8e1,stroke:#f9a825,color:#5d4037 + classDef uscita fill:#e8f5e9,stroke:#2e7d32,color:#1b5e20 + class CSV dato + class L,S,G,D,F0,F1,F2,SC,SALTO calcolo + class RIP,RIS,INT,FIN dominio + class REP uscita + + linkStyle 15 stroke:#e65100,stroke-width:2px,color:#e65100 +``` + +Come si legge: dall'alto in basso. I numeri nei riquadri sono i paragrafi di questo documento. Colori: **grigio** dati, **azzurro** calcoli, +**giallo** tipi di dominio, **verde** risultati. Il Passo 1 produce i domini; ogni tipo ha il suo trattamento nel Passo 2; la freccia +**arancione tratteggiata** indica che il raffreddamento finale **passa** $c$ e $\tau$ ai raffreddamenti intermedi (§5b), per questo si elabora per primo. +Il riscaldamento non si fitta: se ne riporta il salto (§7). + +## Simboli e chiavi di configurazione + +Nelle formule si usano questi simboli; a destra la chiave di [configurazione.md](configurazione.md) con il valore. + +| Simbolo | Chiave | Significato | +|---|---|---| +| $\theta_g$ | `soglia_gradino_c` | salto positivo minimo che segna un gradino [°C] | +| $\theta_b$ | `soglia_bordo_sigma` | soglia, in multipli di $\sigma_d$, per estendere la salita ai campioni adiacenti | +| $\theta_\rho$ | `soglia_acf` | soglia di $\rho_1$ oltre la quale si allargano gli intervalli | +| $\kappa$ | `scala_perdita_sigma` | scala della perdita robusta, in multipli di $\sigma$ | +| $\alpha_{\min},\ \alpha_{\max}$ | `tau_min_rel`, `tau_max_rel` | limiti di $\tau$ come frazione di $L$ | +| $\lambda$ | `rmse_vs_rumore_max` | un modello è «adeguato» se $\mathrm{RMSE} \le \lambda\,\sigma$ | +| $\delta$ | `soglia_delta_aicc` | tolleranza su $\Delta\mathrm{AICc}$ per preferire il modello più semplice | +| $L$ | — | durata del dominio, $t_\text{ultimo} - t_\text{primo}$ | +| $n$, $k$ | — | campioni usati nel fit; parametri **liberi** del modello | + + +## 1. Lettura dei dati (`dati.leggi_csv`) + +Legge l'intestazione, controlla che ci siano la colonna del tempo e tutte le serie (A2), converte il tempo in secondi (A3) e verifica che +sia strettamente crescente (A1). Restituisce `t` (array in secondi) e un dizionario `serie` {nome: array}. + +## 2. Stima del rumore (`domini.sigma_rumore`) + +Per un rumore bianco $\varepsilon_i$ di deviazione $\sigma$, la **derivata seconda discreta** $\Delta^2 y_i = y_{i+2} - 2y_{i+1} + y_i$ elimina +le componenti lisce (costante, lineare, e in buona parte l'esponenziale) e ha varianza $6\sigma^2$. Per renderla robusta agli outlier si usa la +deviazione mediana assoluta: + +$$\sigma = \frac{1.4826\,\mathrm{MAD}(\Delta^2 y)}{\sqrt{6}}, \qquad \mathrm{MAD}(x) = \mathrm{mediana}\,|x_i - \mathrm{mediana}(x)|$$ + +Il fattore 1.4826 rende la MAD uguale a $\sigma$ per dati gaussiani. La stima si calcola **per dominio** (A13) sui campioni usati nel fit +e serve come scala della perdita robusta e come soglia di «adeguatezza» (§6). Ignora la deriva lenta (A14) e restituisce `nan` con meno +di 4 campioni. + +## 3. Rilevamento dei gradini (`domini.trova_gradini`) + +Sulla `serie_riferimento`: + +1. **Candidati**: $d_i = y_{i+1} - y_i$; sono forti gli indici con $d_i > \theta_g$ (solo salti positivi, A5, A12). +2. **Raggruppamento**: candidati a meno di `distanza_min_salti_campioni` indici l'uno dall'altro appartengono allo stesso gradino (la salita + si distribuisce su 2 campioni, ognuno con un salto grande). +3. **Estensione ai bordi**: il gruppo si estende all'indietro e in avanti ai campioni adiacenti ancora in salita, cioè finché + $d_i > \theta_b\,\sigma_d$, con $\sigma_d = 1.4826\,\mathrm{MAD}(d)$ calcolata su **tutti** i salti (A6). Serve a + non lasciare un campione a metà salita dentro il tratto vicino. +4. Ogni gradino diventa la coppia $(i_0, i_1)$: $i_0$ è il primo campione già in salita e $i_1$ il campione successivo all'ultimo in salita + (estremo escluso). I **campioni di salita** sono quelli in $[i_0, i_1)$. + +## 4. Costruzione dei domini (`domini.proponi`) + +Dati $N$ gradini, in ordine di tempo: + +- `riposo`: dall'inizio al primo gradino (o tutto il segnale se non ci sono gradini); +- per ogni gradino: un dominio `riscaldamento` sui suoi campioni di salita, seguito da un dominio `raffreddamento` fino al gradino + successivo (o alla fine). Un raffreddamento senza campioni (gradini adiacenti) non viene creato. + +Con $N$ gradini i domini sono quindi $1 + 2N$. I domini sono contigui e coprono tutti i campioni. L'utente può sostituire la proposta con +`domini.toml` (A21), che viene validato: tipi ammessi, intervalli non vuoti, contiguità. + +## 5. Fit (`fit.adatta`) + +### 5.1 Modelli (`modelli.MODELLI`, $t' = t - t_0$) + +| Modello | Equazione | Parametri | Dove si prova | +|---|---|---|---| +| `costante` | $y = a$ | $a$ | riposo | +| `lineare` | $y = a + b\,t'$ | $a, b$ | riposo | +| `esp_decad` | $y = c + A\,e^{-t'/\tau}$ | $c, A, \tau$ | riposo, raffreddamento | + +Nel raffreddamento $c$ è l'asintoto (≈ ambiente per `T_C`, ≈ 0 per `dT_C`), $A$ l'ampiezza a $t'=0$ e $\tau = 1/k$ la costante di tempo. + +### 5.2 Partenza + +- Polinomi (`costante`, `lineare`): soluzione dei minimi quadrati lineari. +- `esp_decad`: **proiezione delle variabili**. Per $\tau$ fissato il modello è lineare in $(c, A)$. Si provano `n_griglia_tau` valori di $\tau$ + su una griglia logaritmica tra $\alpha_{\min} L$ e $\alpha_{\max} L$, per ognuno si + risolvono $(c, A)$ in modo lineare e si tiene il $\tau$ con $\mathrm{RSS}$ minima. Poi il raffinamento non lineare parte da lì: nessuna + partenza casuale (A22). +- Se $c$ e $\tau$ sono fissi e solo $A$ è libero: $A = \sum (y - c)\,e^{-t'/\tau} \big/ \sum e^{-2t'/\tau}$. + +### 5.3 Minimizzazione + +`scipy.optimize.least_squares` sui soli parametri liberi, con `x_scale="jac"`, limiti $\tau \in [\alpha_{\min} L,\ \alpha_{\max} L]$ e +**perdita robusta** (`perdita`, default `soft_l1`): + +$$\min_p \sum_i \rho\!\left(\frac{r_i^2}{s^2}\right), \quad \rho(z) = 2\left(\sqrt{1+z}-1\right), \quad s = \kappa\,\sigma, \quad r_i = f(t'_i; p) - y_i$$ + +Per $|r_i| \ll s$ si comporta come i minimi quadrati; per $|r_i| \gg s$ cresce linearmente, così gli **scarti casuali** pesano molto meno (A14). +Se $\sigma$ non è finita o è nulla la scala è 1 (A20). + +### 5.4 Incertezze + +$$s^2 = \frac{\mathrm{RSS}}{n-k}, \qquad \mathrm{cov}(p) = s^2\,(J^\top J)^{-1}, \qquad \mathrm{IC}_{95\%} = \pm 1.96\,\sqrt{\mathrm{diag}(\mathrm{cov})}\cdot f_\rho$$ + +$k$ è il numero di parametri **liberi**, $J$ lo jacobiano dei residui nella soluzione (con la pseudo-inversa di $J^\top J$), $\mathrm{RSS} = \sum r_i^2$ (A16). +Il fattore di autocorrelazione vale $f_\rho = \sqrt{(1+\rho_1)/(1-\rho_1)}$ se `correggi_autocorrelazione` è attivo e $\rho_1 > \theta_\rho$, altrimenti 1 (A17). +Per i parametri fissi l'incertezza è 0 (A18). + +### 5.5 Metriche + +| Metrica | Definizione | +|---|---| +| $\mathrm{RMSE}$ | $\sqrt{\mathrm{RSS}/n}$ | +| $R^2$ | $1 - \mathrm{RSS}/\mathrm{TSS}$, con $\mathrm{TSS} = \sum (y_i - \bar y)^2$ | +| $\rho_1$ | autocorrelazione dei residui a lag 1: $\sum_{i} \tilde r_{i+1}\tilde r_i \big/ \sum_i \tilde r_i^2$, con $\tilde r = r - \bar r$ | +| $\mathrm{AICc}$ | $n\ln(\mathrm{RSS}/n) + 2k + \dfrac{2k(k+1)}{n-k-1}$ (infinito se $n-k-1 \le 0$) | +| $\mathrm{BIC}$ | $n\ln(\mathrm{RSS}/n) + k\ln n$ | +| condizione | numero di condizione di $J$ con le colonne normalizzate in norma euclidea | + +AICc e BIC sono calcolati da $\mathrm{RSS}$, quindi senza peso robusto (A19). + +### 5.6 Validità e identificabilità + +Un fit è **valido** se converge, nessun parametro libero è sul limite (distanza dal limite inferiore a $10^{-3}$ volte il limite stesso: $\tau$ al bordo della griglia), +il numero di condizione non supera `soglia_condizione` e l'AICc è finito. Un fit è **non identificabile** se la condizione supera la +soglia **oppure** un parametro libero diverso da `a` e `c` ha $1.96\,\sigma_p > |p|$ (intervallo che include lo zero: il parametro non +è distinguibile da 0). + +## 5b. Costanti condivise (`analisi.analizza_domini`) + +Per A8 i raffreddamenti hanno lo stesso $\tau$ e lo stesso $c$. L'ordine di elaborazione è quindi: + +1. il **raffreddamento finale** (ultimo dominio di tipo `raffreddamento`) si fitta per primo con $c, A, \tau$ liberi; +2. se la scelta è valida, i valori di $c$ e $\tau$ (parametri in `raffreddamento_intermedio_fissa_da_finale`) sono salvati per serie; +3. ogni **raffreddamento intermedio** si fitta con quei valori **fissi**, e solo $A$ libera ($k = 1$). + +Motivo: un intermedio ha 12–25 campioni con scarti casuali, e $\tau$ da solo non è identificabile (su dati puliti lo sarebbe: i test lo +verificano). Se il finale non dà un fit valido o la lista è vuota gli intermedi restano liberi. + +## 6. Scelta del modello (`analisi.scegli`) + +Per ogni (dominio, serie) con più modelli candidati: + +1. **Scarto**: via i fit non validi (§5.6). Se non ne resta nessuno: `indeterminato`. +2. **Adeguatezza**: sono *adeguati* i fit con $\mathrm{RMSE} \le \lambda\,\sigma$. Se ce n'è almeno uno si sceglie tra questi, altrimenti tra + tutti i validi (e il dominio è segnato *non adeguato*). +3. **Parsimonia**: tra i candidati con $\mathrm{AICc} \le \mathrm{AICc}_{\min} + \delta$ vince quello con **meno parametri** + (a parità, AICc minore). + +Un dominio con meno di `n_min_punti` campioni non viene fittato (`indeterminato`); un modello con più parametri di $n - 2$ non viene provato. +Con un solo modello candidato (raffreddamento) la scelta è lui, se valido: il flag *adeguato* resta informativo. + +## 7. Riscaldamento come salto (`analisi._gradino`) + +Il dominio di riscaldamento ha 2–3 campioni: nessun fit. Si riporta, per ogni serie, con $i_0, i_1$ gli estremi del dominio: + +$$\Delta = y_{i_1-1} - y_{i_0-1}, \qquad \text{durata} = t_{i_1-1} - t_{i_0-1}$$ + +cioè il salto dal campione **prima** della salita all'ultimo campione di salita, e il tempo che separa i due (per 2 campioni di salita ≈ 2 +intervalli, circa 300 ms). Poiché parte della salita può cadere sul campione precedente, $\Delta$ è una stima per difetto dell'ampiezza del +gradino (A23). `sigma` vale `null` e `fit` è vuoto. + +## Esempio di calcolo completo (seed 1, `T_C`, raffreddamento finale) + +1. $n = 651$ campioni da $t_0 = 22.12$ s (dopo `scarta_dopo_salto_n = 1`, 650 usati nel fit); $\sigma = 0.145$ °C. +2. Griglia su $\tau \in [0.05 L,\ 50 L]$ con $L \approx 97.8$ s → $\tau_0$ vicino a 16 s, $c, A$ lineari. +3. Raffinamento `soft_l1` con scala $3\sigma = 0.44$ °C: $c = 23.00 \pm 0.12$, $A = 179.6 \pm 0.48$, $\tau = 16.08 \pm 0.07$ s. +4. Metriche: $\mathrm{RMSE} = 0.356$, $\rho_1 = 0.71$ (> 0.3: intervalli moltiplicati per $\sqrt{1.71/0.29} = 2.43$), $R^2 = 0.9999$. +5. $\mathrm{RMSE} > 1.3 \cdot 0.145 = 0.189$: **non adeguato** (residui autocorrelati per gli scarti casuali), ma unico modello valido → scelto. +6. Gli intermedi 3 e 5 usano $c = 23.00$, $\tau = 16.08$ e stimano solo $A$ ($84.3$ e $148.3$). diff --git a/docs/assunzioni.md b/docs/assunzioni.md new file mode 100644 index 0000000..479b810 --- /dev/null +++ b/docs/assunzioni.md @@ -0,0 +1,71 @@ +# 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_.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 | diff --git a/docs/configurazione.md b/docs/configurazione.md new file mode 100644 index 0000000..8d9e2ab --- /dev/null +++ b/docs/configurazione.md @@ -0,0 +1,81 @@ +# Configurazione + +Tutti i valori sono in [`config.toml`](../config.toml): **nessun default nel codice**, una chiave mancante è un errore (exit 3). Suffissi nei nomi: `_s` +secondi, `_c` gradi, `_rel` frazione della durata del dominio, `_sigma` multiplo di $\sigma$, `_n` / `_campioni` numero di campioni. Per aggiungere una chiave: +`config.toml`, `CHIAVI` in `config.py`, questo documento. Il modulo che la legge è indicato a destra. + +Si usa un altro file con `fitseg --config altro.toml …`. + +## `[dati]` — formato del CSV (`dati.py`) + +| Chiave | Valore | Significato | +|---|---|---| +| `colonna_tempo` | `"t_ms"` | nome della colonna del tempo | +| `unita_tempo` | `"ms"` \| `"s"` | unità del tempo nel CSV; internamente tutto è in secondi (A3) | +| `serie` | `["T_C", "dT_C"]` | colonne da approssimare; ognuna ha le sue equazioni, tutte condividono i domini (A4) | +| `separatore` | `","` | separatore dei campi | + +## `[domini]` — riconoscimento dei domini (`domini.py`, `analisi.py`) + +| Chiave | Default | Significato | Effetto se alzata / abbassata | +|---|---|---|---| +| `serie_riferimento` | `"T_C"` | serie in cui si cercano i gradini; deve essere in `serie` | — | +| `soglia_gradino_c` | 20.0 | salto positivo minimo tra due campioni consecutivi per essere un gradino | alta: gradini piccoli non rilevati; bassa: scarti scambiati per gradini (A5) | +| `soglia_bordo_sigma` | 6.0 | la salita si estende ai campioni adiacenti con salto $d_i > \theta_b\,\sigma_d$, dove $\theta_b$ è questa soglia e $\sigma_d = 1.4826\,\mathrm{MAD}$ dei salti ([algoritmi.md](algoritmi.md) §3) | alta: campioni di mezza salita restano nel raffreddamento; bassa: il riscaldamento si allarga troppo | +| `distanza_min_salti_campioni` | 3 | candidati più vicini di così appartengono allo stesso gradino | alta: gradini ravvicinati fusi; bassa: una salita su 2 campioni spezzata | +| `n_min_punti` | 6 | campioni minimi per fittare un dominio; sotto → `indeterminato` | alta: più domini non fittati | +| `scarta_dopo_salto_n` | 1 | campioni esclusi dal fit **all'inizio di ogni raffreddamento** (sicurezza dopo la salita) | 0: nessuna esclusione; alta: perde campioni del decadimento | + +## `[fit]` — regressione e scelta (`fit.py`, `analisi.py`) + +| Chiave | Default | Significato | +|---|---|---| +| `raffreddamento_intermedio_fissa_da_finale` | `["c", "tau"]` | parametri che i raffreddamenti intermedi prendono dal raffreddamento finale ([algoritmi.md](algoritmi.md) §5b). `[]` = tutti liberi. Nomi ammessi: quelli del modello (`c`, `A`, `tau`) | +| `perdita` | `"soft_l1"` | funzione di perdita di `least_squares`: `"linear"` (minimi quadrati), `"soft_l1"`, `"huber"` (robuste agli scarti, A14) | +| `scala_perdita_sigma` | 3.0 | scala $s$ della perdita robusta, in multipli di $\sigma$: sotto $s$ come i minimi quadrati, sopra cresce linearmente | +| `tau_min_rel`, `tau_max_rel` | 0.05, 50.0 | limiti di $\tau$ come frazione della durata del dominio $L$: $\tau \in [0.05L,\ 50L]$; un $\tau$ al bordo rende il fit non valido | +| `n_griglia_tau` | 40 | punti della griglia logaritmica su $\tau$ per la partenza dell'esponenziale | +| `soglia_condizione` | $10^8$ | numero di condizione dello jacobiano normalizzato oltre cui i parametri sono «non identificabili» | +| `correggi_autocorrelazione` | `true` | allarga gli intervalli di $\sqrt{(1+\rho_1)/(1-\rho_1)}$ se $\rho_1$ supera `soglia_acf` (A17) | +| `soglia_acf` | 0.3 | soglia di $\rho_1$ per la correzione | +| `soglia_delta_aicc` | 2.0 | tra i modelli entro questo $\Delta\mathrm{AICc}$ dal migliore vince quello con meno parametri | +| `rmse_vs_rumore_max` | 1.3 | un modello è «adeguato» se $\mathrm{RMSE} \le$ questo valore $\cdot\ \sigma$ | + +### `[fit.modelli_per_tipo]` — modelli provati per tipo di dominio + +| Chiave | Default | Significato | +|---|---|---| +| `riposo` | `["costante", "lineare", "esp_decad"]` | modelli provati sul riposo (A10) | +| `raffreddamento` | `["esp_decad"]` | modelli provati sui raffreddamenti (A7) | + +Entrambe le liste sono obbligatorie e non vuote. Non esiste la voce `riscaldamento`: quel dominio non si fitta (A23). Modelli ammessi: i nomi in +`modelli.MODELLI`. + +## `[output]` (`cli.py`) + +| Chiave | Default | Significato | +|---|---|---| +| `cartella_output` | `"output"` | cartella madre; ogni analisi scrive in `//`. `--output DIR` la sostituisce | + +## `domini.toml` — domini modificabili (`domini.py`) + +Scritto in `output//01_domini/domini.toml`, rilegibile con `fitseg analizza CSV --domini FILE` ([uso.md](uso.md)): + +```toml +[[dominio]] +tipo = "raffreddamento" # riposo | riscaldamento | raffreddamento +t0_s = 16.1000 # primo campione del dominio (incluso) +t1_s = 17.9100 # primo campione del dominio successivo (escluso); per l'ultimo: ultimo campione +``` +I domini devono essere contigui ($t_1$ di uno uguale a $t_0$ del successivo): altrimenti exit 6. + +## Esempi + +| Voglio… | Cambio | +|---|---| +| gradini più piccoli (es. 8 °C) | `[domini] soglia_gradino_c = 8.0` e verifica che gli scarti restino sotto la soglia (A5) | +| tutti i raffreddamenti con parametri liberi | `[fit] raffreddamento_intermedio_fissa_da_finale = []` | +| fissare solo la costante di tempo | `["tau"]` | +| fit ai minimi quadrati puri | `perdita = "linear"` | +| un'altra colonna del CSV | `[dati] serie = [...]` e, se serve, `[domini] serie_riferimento` | +| tempo in secondi | `[dati] unita_tempo = "s"` e `colonna_tempo` | diff --git a/docs/img/domini_esempio.png b/docs/img/domini_esempio.png new file mode 100644 index 0000000000000000000000000000000000000000..b8045e450131230368d4341d2328b3baeb098ef1 GIT binary patch literal 158312 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%0A#%+(%0{$A}3gNlob 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

$}ZRXF#vDK3Z_TzjpLe$Zd|&gd4(IALqWa97vk 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 literal 0 HcmV?d00001 diff --git a/docs/limiti.md b/docs/limiti.md new file mode 100644 index 0000000..c88dc28 --- /dev/null +++ b/docs/limiti.md @@ -0,0 +1,38 @@ +# Limiti noti ed estensioni + +## Limiti noti + +| Limite | Perché | Effetto | Dove | +|---|---|---|---| +| **Residui autocorrelati nei raffreddamenti** ($\rho_1 \approx 0.5$–$0.7$, $\mathrm{RMSE}$ 1.5–3 volte $\sigma$) | scarti casuali (±4 °C) e deriva lenta non sono nel modello (A14) | i parametri sono buoni ($\tau$ entro pochi % dal valore nominale), ma gli intervalli di confidenza sono troppo stretti nonostante la correzione AR(1) (A17) e i domini risultano «non adeguati» | [assunzioni.md](assunzioni.md) A14, A17 | +| **Il $\sigma$ stimato ignora la parte lenta del rumore** | la derivata seconda elimina la deriva (A13) | $\sigma$ sottostimata → «non adeguato» più frequente | A13 | +| **Il riscaldamento non è fittato** | 2–3 campioni, meno dei parametri utili (A23) | si riporta solo il salto $\Delta$, che sottostima l'ampiezza reale del gradino perché parte della salita cade sul campione precedente | [algoritmi.md](algoritmi.md) §7 | +| **Incertezze condizionate nei raffreddamenti intermedi** | $c$ e $\tau$ sono fissi, la loro incertezza non si propaga (A18) | gli intervalli di $A$ sono troppo stretti | A18 | +| **Nessun vincolo di continuità** | ogni dominio è indipendente (A11) | gli intermedi non sono vincolati a combaciare col gradino | A11 | +| **Un solo esponenziale per raffreddamento** | modello `esp_decad` (A7) | un raffreddamento con due processi (conduzione e convezione) non è descritto bene | A7 | +| **Gradini cercati su una sola serie** | `serie_riferimento` (A4) | serie con gradini diversi richiedono due analisi | A4 | +| **Soglia dei gradini fissa** | `soglia_gradino_c` in config (A5) | va adattata a gradini più piccoli o scarti più grandi | [configurazione.md](configurazione.md) | +| **Provato su pochi dati** | 3 seed di `gen_data` con la configurazione d'esempio | nessuna validazione su dati reali o su rumore molto diverso | [test.md](test.md) | + +## Estensioni possibili (non presenti) + +| Estensione | Cosa servirebbe | +|---|---| +| Modello a due esponenziali per il raffreddamento | voce in `modelli.MODELLI` con 5 parametri, partenza su griglia a due $\tau$, ordinamento $\tau_1 < \tau_2$, nome nelle liste di `modelli_per_tipo` ([moduli.md](moduli.md)) | +| Modelli di transizione liscia (logistica) o polinomi superiori | stessa procedura; limitare il centro della logistica al dominio per evitare che degeneri in esponenziale | +| Fit congiunto con continuità tra domini | un unico problema ai minimi quadrati con penalità sul salto residuo ai confini | +| Fit del riscaldamento | richiede più campioni (sensore più lento o campionamento più fitto) | +| Scelta dei modelli guidata da un agente (Claude Code) | era nel progetto iniziale e **è stata tolta**: la regola automatica ([algoritmi.md](algoritmi.md) §6) basta per questi dati | +| Rumore colorato nel fit (minimi quadrati generalizzati) | modello AR(1) dei residui per correggere intervalli e pesi invece del solo fattore $\sqrt{(1+\rho_1)/(1-\rho_1)}$ | + +## Scelte progettuali già fatte + +| Scelta | Alternativa scartata | Motivo | +|---|---|---| +| Domini dai gradini di `T_C` | PELT + fusione per BIC | i regimi qui sono determinati dai gradini; PELT con costo quadratico spezzerebbe gli esponenziali lisci | +| Riscaldamento non fittato, descritto come salto | fit su 2–3 punti | parametri non identificabili | +| $c$ e $\tau$ fissati negli intermedi | $\tau$ libero ovunque | $\tau$ non identificabile con 12–25 campioni e scarti casuali | +| Partenza per griglia su $\tau$ (proiezione delle variabili) | partenze casuali | deterministico e più robusto per $\tau$ | +| Perdita robusta `soft_l1` | minimi quadrati | gli scarti casuali sono outlier | +| Output per CSV in sottocartelle numerate | cartella piatta | ogni passo è ispezionabile separatamente | +| Un modulo per responsabilità, CLI a due comandi | script per fase, pacchetti `motore/` e `uscita/` | meno codice, nessuna duplicazione | diff --git a/docs/moduli.md b/docs/moduli.md new file mode 100644 index 0000000..ab65461 --- /dev/null +++ b/docs/moduli.md @@ -0,0 +1,133 @@ +# Moduli + +Mappa del codice in `src/fitseg/`. Un modulo ha una sola responsabilità; le dipendenze vanno in una sola direzione. + +```mermaid +flowchart TD + cli[cli.py] --> config[config.py] + cli --> dati[dati.py] + cli --> domini[domini.py] + cli --> analisi[analisi.py] + cli --> uscita[uscita.py] + analisi --> domini + analisi --> fit[fit.py] + analisi --> modelli[modelli.py] + fit --> modelli + uscita --> analisi + uscita --> modelli +``` +`config.py`, `dati.py`, `domini.py` e `modelli.py` non importano niente del pacchetto; `fit.py` importa solo `modelli`; `analisi.py` importa `domini`, `fit`, `modelli`; +`uscita.py` importa `analisi` (per `valido`) e `modelli`. Nessun modulo di calcolo scrive file. + +## Strutture dati comuni + +**Domini** (lista, ordinata e contigua): ogni elemento è `{"tipo": "riposo"|"riscaldamento"|"raffreddamento", "i0": int, "i1": int}`, con +`i0` primo campione e `i1` escluso. `domini[k]["i1"] == domini[k+1]["i0"]`. + +**Risultato** (lista, una voce per dominio; è anche il contenuto di `risultati.json`): + +| Campo | Significato | +|---|---| +| `k` | numero del dominio (da 1) | +| `tipo`, `t0_s`, `t1_s` | tipo; tempo del primo e dell'ultimo campione del dominio (in secondi) | +| `n`, `n_fit` | campioni del dominio; campioni usati nel fit (meno `scarta_dopo_salto_n` nei raffreddamenti) | +| `serie` | `{nome serie: voce}` (sotto) | + +**Voce di serie**: `sigma` (σ del rumore, `null` nel riscaldamento), `fit` (lista dei fit provati), `scelta` (nome del modello o `null`), `motivo` +(testo), `adeguato` (bool, assente nel riscaldamento e nei domini troppo corti); nel riscaldamento al posto dei fit c'è `gradino`: +`{n, da, a, delta, durata_s}`. + +**Fit** (elemento di `fit`): `modello`, `equazione` (LaTeX), `n_parametri` ($k$ liberi), `n`, `fissi` (nomi dei parametri fissi), `parametri` +`{nome: valore}`, `ic95` `{nome: ±}`, `rss`, `rmse`, `r2`, `aicc`, `bic`, `acf1` ($\rho_1$), `condizione`, `parametri_a_limite`, `non_identificabile`, +`convergenza`. Se la minimizzazione fallisce: solo `modello`, `convergenza: false`, `n_parametri`. + +## `cli.py` — punto d'ingresso + +| | | +|---|---| +| Responsabilità | argomenti da riga di comando, orchestrazione dei passi, codici di uscita | +| Funzioni | `main(argv)`, `cmd_domini`, `cmd_analizza`, `_passo_domini(args)`, `errore(msg, codice)` | +| Entra | `--config`, `--output`, sottocomando `domini CSV` o `analizza CSV [--domini FILE]` | +| Esce | codici 0 ok, 2 uso errato (argparse), 3 `ErroreConfig`, 4 `ErroreDati`, 6 `domini.toml` non valido | + +`_passo_domini` è il passo 1 comune ai due comandi: legge config e dati, crea `output//` con le tre sottocartelle, ricava i domini +(da `--domini` oppure dalla proposta), scrive `01_domini/domini.toml` e `domini.png`, stampa la tabella dei domini. `cmd_analizza` poi chiama +`analizza_domini` e scrive sovrapposizione e report. Con `--domini` il file passato viene riscritto normalizzato in `01_domini/`. + +## `config.py` — configurazione + +| | | +|---|---| +| Responsabilità | leggere `config.toml`, rifiutare chiavi mancanti (**nessun default nel codice**) | +| Funzioni | `leggi(percorso) → dict`; costante `CHIAVI` (elenco obbligatorio per sezione); eccezione `ErroreConfig` | +| Controlli | chiavi presenti; `unita_tempo` ∈ {ms, s}; `serie_riferimento` ∈ `serie`; `modelli_per_tipo` ha liste non vuote per `riposo` e `raffreddamento` | + +Una chiave nuova va aggiunta a `CHIAVI`, a `config.toml` e a [configurazione.md](configurazione.md). + +## `dati.py` — lettura del CSV + +| | | +|---|---| +| Funzioni | `leggi_csv(percorso, cfg) → (t, serie)`; eccezione `ErroreDati` | +| Garanzie | `t` in secondi, strettamente crescente (A1); `serie` = `{nome: array}` per le colonne di `[dati] serie`; nessun valore mancante | + +## `domini.py` — suddivisione in domini + +| Funzione | Cosa fa | Vedi | +|---|---|---| +| `sigma_robusto(d)` | $1.4826\,\mathrm{MAD}$ di un campione | [algoritmi.md](algoritmi.md) §2 | +| `sigma_rumore(y)` | $\sigma$ del rumore bianco da $\Delta^2 y$ | §2 | +| `trova_gradini(y, cfg)` | lista di $(i_0, i_1)$ dei campioni di salita | §3 | +| `proponi(t, serie, cfg)` | lista dei domini (riposo + riscaldamento/raffreddamento per gradino) | §4 | +| `scrivi_toml(percorso, domini, t)` | scrive `domini.toml` (tempi con 4 decimali) | [uso.md](uso.md) | +| `leggi_toml(percorso, t)` | rilegge e **valida** i domini (tipi, intervalli, contiguità); `ValueError` se non validi | [uso.md](uso.md) | + +Nel `domini.toml` `t1_s` è l'estremo **escluso** (tempo del primo campione del dominio successivo); per l'ultimo dominio è l'ultimo campione. Gli +indici si ricalcolano dal tempo con tolleranza $10^{-6}$ s. + +## `modelli.py` — registro dei modelli + +| | | +|---|---| +| Dati | `MODELLI = {nome: (funzione, [nomi parametri], equazione LaTeX, indice di τ o None)}` | +| Funzioni | `valuta(nome, t, p)` valuta un modello in $t$ con parametri `p` (lista nell'ordine dei nomi) | + +Contiene `costante`, `lineare`, `esp_decad`. **Aggiungere un modello**: una funzione, una voce in `MODELLI`, il nome nelle liste di +`[fit.modelli_per_tipo]`, una riga in [algoritmi.md](algoritmi.md) §5.1; se il modello ha un nuovo tipo di parametro-tempo, indicare l'indice +in `MODELLI` e la partenza in `fit._partenza`; in `uscita._eq_numerica` aggiungere il ramo per l'equazione con i valori. + +## `fit.py` — regressione di un modello + +| Funzione | Cosa fa | +|---|---| +| `adatta(t, y, nome, sigma, cfg, fissi=None) → dict` | fit di un modello su un dominio; `fissi = {parametro: valore}` tenuti costanti. Restituisce il dizionario *Fit* sopra | +| `_partenza(t, y, nome, cfg, L)` | minimi quadrati per i polinomi, griglia su $\tau$ (VARPRO) per l'esponenziale | +| `_acf1(r)` | autocorrelazione dei residui a lag 1 | + +Dettagli: [algoritmi.md](algoritmi.md) §5. Nessun accesso a file, nessuna casualità. + +## `analisi.py` — scelta e orchestrazione dei domini + +| Funzione | Cosa fa | +|---|---| +| `valido(fit)` | convergente, nessun parametro a limite, identificabile, AICc finito | +| `scegli(fit_list, sigma, cfg) → (fit, motivo, adeguato)` | regola di scelta ([algoritmi.md](algoritmi.md) §6) | +| `_gradino(t, y, d)` | descrive il riscaldamento come salto ([algoritmi.md](algoritmi.md) §7) | +| `_serie(ty, yy, sigma, tipo, fissi, cfg)` | fit di tutti i modelli del tipo per una serie e scelta; `indeterminato` se troppo corto | +| `analizza_domini(t, serie, domini, cfg) → list` | un *Risultato* per dominio; ordine: raffreddamento finale, poi gli altri ([algoritmi.md](algoritmi.md) §5b) | + +## `uscita.py` — cartelle, grafici, report + +| Funzione | Cosa produce | +|---|---| +| `cartelle(base)` | crea `01_domini`, `02_sovrapposizione`, `03_report` e le restituisce | +| `colori_domini(tipi)` | un colore per dominio (grigio riposo, rosso riscaldamento, colori diversi per ogni raffreddamento) | +| `grafico_domini(percorso, t, serie, domini)` | passo 1: `domini.png` | +| `sovrapposizione_generale(...)` | passo 2a: `sovrapposizione_.png` | +| `sovrapposizione_domini(...)` | passo 2b: `dominio_.png` | +| `scrivi_report(cartella, risultati, nome_csv, serie_nomi, cfg)` | passo 3: `report.md` e `risultati.json` | +| `_num`, `_params`, `_eq_numerica`, `_salto` | formattazione LaTeX (valori, parametri ± IC, equazione con i valori sostituiti, salto) | + +Il report incorpora le immagini con percorsi **relativi** (`../01_domini/domini.png`, …): cambiando nomi di file o cartelle vanno aggiornati +`PASSI` e i riferimenti. Le grandezze matematiche del report sono sempre in LaTeX e i `$` devono restare bilanciati per riga (c'è un test). +Descrizione di file e grafici: [output.md](output.md). diff --git a/docs/output.md b/docs/output.md new file mode 100644 index 0000000..7acadba --- /dev/null +++ b/docs/output.md @@ -0,0 +1,80 @@ +# Output + +Ogni analisi scrive in `output//` (la cartella `output/` è ignorata da git). Ogni passo ha la sua sottocartella, numerata +nell'ordine di esecuzione. Chi produce cosa: [moduli.md](moduli.md) (`uscita.py`). + +``` +output/gen_1/ +├── 01_domini/ passo 1 — suddivisione dei dati +│ ├── domini.png dati grezzi con i domini (colore per tipo, numerati) + zoom sui gradini +│ └── domini.toml domini usati, modificabili a mano ([uso.md](uso.md)) +├── 02_sovrapposizione/ passo 2 — dati grezzi contro le equazioni trovate +│ ├── sovrapposizione_.png serie intera con le equazioni a tratti, residui e zoom +│ └── dominio_.png un dominio: dati, modello scelto, altri modelli validi, residui +└── 03_report/ passo 3 + ├── report.md report completo, immagini incorporate, formule in LaTeX + └── risultati.json tutti i fit con tutte le metriche (schema in moduli.md) +``` +`fitseg domini` scrive solo `01_domini/`; `fitseg analizza` scrive tutto. + +## Colori + +| Dominio | Colore | +|---|---| +| riposo | grigio | +| riscaldamento | rosso (i suoi campioni sono evidenziati con pallini, perché la fascia è sottile) | +| raffreddamento | un colore **diverso per ognuno**, in ordine: blu, verde, viola, arancio, ciano (poi si ricomincia) | + +Le fasce sono numerate come nelle tabelle (il numero compare solo se la fascia è abbastanza larga per il grafico). Il raffreddamento *k*-esimo +ha lo stesso colore in tutti i grafici. + +## `01_domini/domini.png` + +Una riga per serie. A sinistra l'intero segnale con le fasce dei domini; a destra lo **zoom** sulla zona dei gradini (da 2 s prima del primo +riscaldamento a 8 s dopo l'ultimo), dove 7 domini in pochi secondi si distinguono. Serve a controllare la suddivisione *prima* di fidarsi +delle equazioni: se è sbagliata si corregge `domini.toml` e si rilancia ([uso.md](uso.md) §3). + +## `02_sovrapposizione/sovrapposizione_.png` + +Sopra: dati grezzi (punti grigi), fasce dei domini e **curva dell'equazione scelta** per ogni dominio nel colore del dominio. Sotto: residui +$y - \hat y$ dei soli campioni usati nel fit (i campioni esclusi all'inizio dei raffreddamenti non compaiono, per non schiacciare la scala). +A destra lo stesso con lo zoom. Il riscaldamento non ha curva (non si fitta) e i suoi campioni sono i pallini rossi. + +Come leggere i residui: attorno a zero e senza struttura = modello adeguato; ondulazioni o code verso il basso = **scarti casuali** del sensore +non descritti dal modello (A14), non un errore del fit. + +## `02_sovrapposizione/dominio_.png` + +- **Riposo e raffreddamento**: punti grigi = campioni esclusi dal fit; punti neri = campioni usati; **linea continua** = modello scelto + (nel colore del dominio, con $\mathrm{RMSE}$ in legenda); **tratteggiate** = gli altri modelli validi (confronto); sotto i residui del modello scelto. +- **Riscaldamento**: la salita (pallini rossi) con 8 campioni di contesto prima e dopo, e nel titolo $\Delta$, numero di campioni e durata. Nessuna curva. +- Dominio indeterminato: solo i dati, con «indeterminato» nel titolo. + +## `03_report/report.md` + +Le immagini sono link relativi (`../01_domini/domini.png`); le grandezze matematiche sono in LaTeX (`$…$` nel testo, `$$…$$` per l'equazione scelta +con i valori sostituiti): servono un visualizzatore Markdown con supporto matematico (GitHub, VS Code, Obsidian). Struttura: + +| Sezione | Contenuto | +|---|---| +| intestazione | nome del CSV, data, serie, definizione $t' = t - t_0$, livello degli intervalli (95 %) | +| 1. Suddivisione in domini | `domini.png` e tabella (#, tipo, $t_0$, $t_1$, campioni) | +| 2. Equazioni trovate | criterio di scelta con le soglie correnti; tabella (dominio, tipo, serie, modello, equazione e parametri ± IC) con una riga per dominio e serie; i riscaldamenti hanno il salto | +| 3. Sovrapposizione dati–equazioni | un grafico per serie | +| 4. Dettaglio per dominio | per ogni dominio e serie: grafico, modello scelto, equazione con i valori ($$…$$), parametri ± IC, $\sigma_\text{rumore}$, $\mathrm{RMSE}$, $R^2$, $\rho_1$, motivo, tabella di tutti i modelli provati ($k$, RMSE, AICc, BIC, $\rho_1$, valido) | + +### Come leggere i messaggi + +| Messaggio | Significato | Cosa fare | +|---|---|---| +| **salto** (riscaldamento) | salita rapida descritta, non fittata (A23) | niente: $\Delta$ è per difetto | +| **(fisso)** accanto a un parametro | tenuto costante al valore del raffreddamento finale (A8); l'IC di $A$ è condizionato (A18) | niente; per disattivarlo vedi [configurazione.md](configurazione.md) | +| **modello non adeguato** ($\mathrm{RMSE} > 1.3\,\sigma$) | il modello scelto è il migliore tra quelli validi ma non spiega tutta la varianza | guarda $\rho_1$ e i residui: tipicamente scarti casuali (A14) | +| **Indeterminato** | nessun modello valido, o meno di `n_min_punti` campioni | verifica la suddivisione (domini troppo corti?) | +| «valido: no» in tabella | fit non convergente, $\tau$ al limite o non identificabile ([algoritmi.md](algoritmi.md) §5.6) | un modello più semplice può essere preferito | + +## `03_report/risultati.json` + +Lista di domini con tutti i fit e tutte le metriche, anche dei modelli scartati. Schema dei campi: [moduli.md](moduli.md), «Strutture dati comuni». +È JSON valido (i campi non applicabili, come `sigma` del riscaldamento, sono `null`), adatto a elaborazioni successive (confronti tra +analisi, tabelle). diff --git a/docs/panoramica.md b/docs/panoramica.md new file mode 100644 index 0000000..6185349 --- /dev/null +++ b/docs/panoramica.md @@ -0,0 +1,63 @@ +# Panoramica + +## Cosa fa + +`fitseg` prende la serie temporale di un sensore IR che si scalda e si raffredda (CSV `t_ms,T_C,dT_C`) e per ogni **tratto** del segnale +trova l'equazione che lo descrive meglio. Il lavoro è in tre passi: + +1. **Domini**: divide il tempo in tratti omogenei (riposo, riscaldamento, raffreddamento). +2. **Equazioni**: in ogni tratto fitta i modelli adatti con minimi quadrati non lineari e sceglie il migliore con una regola fissa. +3. **Sovrapposizione e report**: confronta i dati grezzi con le equazioni trovate e scrive un report con grafici e formule. + +Tutto è deterministico: stessi dati e stessa configurazione danno gli stessi risultati. Non c'è nessun modello linguistico né nessuna +parte casuale. + +## Il segnale + +Il sensore parte dalla temperatura ambiente (**riposo**). Poi il campione viene scaldato a **gradini**: la temperatura sale in una frazione +di secondo (pochi campioni, **riscaldamento**) e subito dopo decade verso l'ambiente (**raffreddamento**). Con più gradini ravvicinati +(2–4 s) il decadimento di ognuno è interrotto dal successivo; dopo l'ultimo gradino il raffreddamento prosegue fino all'ambiente. + +![Segnale del seed 1: i 7 domini, lo zoom sui gradini e la tabella dei risultati](img/domini_esempio.png) + +*Figura: dati reali di `input/gen_1.csv` (serie `T_C`) con i domini riconosciuti da `fitseg` e le equazioni trovate. In alto il segnale completo, al +centro lo zoom sui tre gradini, in basso la tabella che associa a ogni numero tipo, intervallo ed equazione. Il riscaldamento è rosso, il riposo grigio e +ogni raffreddamento ha un colore diverso. Con 3 gradini i domini sono $1 + 2\cdot 3 = 7$.* + +Il generatore dei dati di prova è il repository fratello `../gen_data` (modello e parametri: `../gen_data/docs/SPEC.md`). Le sue +caratteristiche che contano per `fitseg` sono elencate in [assunzioni.md](assunzioni.md), sezione «Origine dei dati di prova». + +## Concetti + +| Termine | Significato | +|---|---| +| **serie** | una colonna del CSV da approssimare (`T_C` temperatura, `dT_C` differenza dall'ambiente). Tutte le serie condividono gli stessi domini | +| **gradino** | salto positivo di temperatura tra due campioni consecutivi, sopra `soglia_gradino_c` | +| **dominio** | tratto contiguo di campioni con un tipo: `riposo`, `riscaldamento` o `raffreddamento` | +| **riscaldamento** | la salita rapida di un gradino (2–3 campioni): troppo breve per un fit, è descritta come **salto** ($\Delta$, da → a, durata) | +| **raffreddamento** | il decadimento dopo un gradino, fino al gradino successivo o alla fine; modello esponenziale | +| **raffreddamento finale / intermedio** | il finale è l'ultimo dominio (il più lungo, parametri liberi); gli intermedi stanno tra due gradini (corti) e ricevono $c$ e $\tau$ dal finale | +| **modello** | una famiglia di equazioni (`costante`, `lineare`, `esp_decad`); vedi [algoritmi.md](algoritmi.md) §5.1 | +| **parametro fisso** | parametro tenuto costante nel fit (non conta nei gradi di libertà); marcato «(fisso)» nel report | +| **indeterminato** | nessun modello valido, oppure dominio con meno di `n_min_punti` campioni | +| **non adeguato** | modello scelto ma con $\mathrm{RMSE} > 1.3\,\sigma$: la forma non spiega tutto il rumore | +| **tempo del dominio** | $t' = t - t_0$, con $t_0$ il tempo del primo campione del dominio | + +## Esempio (seed 1 di `gen_data`) + +La figura sopra è il risultato di `fitseg analizza input/gen_1.csv` su `T_C`: sul raffreddamento finale $\tau = 16.08 \pm 0.07$ s, $c = 23.00$ °C, $A = 179.6$ °C; +i due raffreddamenti intermedi usano gli stessi $c$ e $\tau$ e stimano solo $A$ ($84.3$ e $148.3$ °C); i tre riscaldamenti sono salti di $+82.2$, $+71.1$ e $+60.8$ °C. +Il valore nominale del generatore è $\tau = 1/k = 15.9$ s (con $k$ perturbato del 2 % per seed): il fit lo recupera entro l'1 %. + +## Dove andare avanti + +| Per… | Leggi | +|---|---| +| lanciare i comandi, correggere i domini a mano | [uso.md](uso.md) | +| capire cosa si dà per vero e cosa succede se non lo è | [assunzioni.md](assunzioni.md) | +| sapere come funzionano rilevamento, fit e scelta, con le formule | [algoritmi.md](algoritmi.md) | +| orientarsi nel codice (un modulo alla volta) | [moduli.md](moduli.md) | +| cambiare un valore (ogni chiave di `config.toml`) | [configurazione.md](configurazione.md) | +| leggere i file prodotti e i grafici | [output.md](output.md) | +| eseguire e capire i test | [test.md](test.md) | +| conoscere limiti noti e possibili estensioni | [limiti.md](limiti.md) | diff --git a/docs/test.md b/docs/test.md new file mode 100644 index 0000000..9a748dd --- /dev/null +++ b/docs/test.md @@ -0,0 +1,83 @@ +# Test + +```bash +.venv/bin/pytest # tutti (≈ 7 s) +.venv/bin/pytest tests/test_fit.py # un file +.venv/bin/pytest -k raffreddamento # per nome +.venv/bin/pytest -x -q # ferma al primo errore, output breve +.venv/bin/pytest -rs # mostra i test saltati e il motivo +.venv/bin/ruff check src tests # stile e import (deve passare) +``` + +## Due livelli + +| Livello | File | Dati | Se i CSV mancano | +|---|---|---|---| +| **Unitari** | `test_domini`, `test_fit`, `test_selezione`, `test_config_dati` | segnale sintetico generato nei test (`conftest.py`) | girano comunque | +| **Integrazione** | `test_integrazione` | `input/gen_{1,2,3}.csv` di `gen_data`; CLI | **saltati** (non falliti) | + +Un clone nuovo non ha i CSV (`input/*.csv` è ignorato da git): i test unitari bastano per lavorare sul codice; per quelli di integrazione si generano +i CSV come in [uso.md](uso.md) §2. + +## Il segnale sintetico (`tests/conftest.py`) + +$$T(t) = T_\text{amb} + \sum_j A_j\,e^{-(t-t_j)/\tau}\,\mathbb{1}[t \ge t_j] + \varepsilon, \qquad \varepsilon \sim \mathcal{N}(0, 0.1^2)$$ + +con $T_\text{amb} = 23$, $\tau = 16$ s, gradini $(t_j, A_j) = (15, 80), (18, 70), (22, 60)$, campionamento 0.15 s, 120 s, **nessun scarto casuale**. La verità è nota, quindi si +possono controllare numericamente i parametri recuperati. La funzione `segnale(...)` accetta rumore, seme, gradini e durata per costruire casi particolari +(nessun gradino, un gradino, gradino sotto soglia). `dT_C` = $T - T_\text{amb}$. Fixture: `cfg` (config reale), `sintetico`, `csv_gen` (un CSV `gen_data`, saltato se assente). + +## `test_domini.py` — suddivisione (assunzioni A5, A6, A21) + +| Test | Verifica | +|---|---| +| `test_tre_gradini` | 3 gradini → 7 domini: riposo, (riscaldamento, raffreddamento) × 3 | +| `test_riscaldamento_e_la_salita_rapida` | ogni riscaldamento ha ≤ 3 campioni | +| `test_confini_vicini_agli_istanti_dei_gradini` | l'inizio di ogni riscaldamento è entro 0.5 s dal gradino vero | +| `test_domini_contigui_e_completi` | primo `i0 = 0`, ultimo `i1 = n`, nessun buco | +| `test_senza_gradini_un_solo_riposo` | nessun gradino → solo riposo | +| `test_un_solo_gradino` | riposo, riscaldamento, raffreddamento | +| `test_gradino_sotto_soglia_ignorato` | un salto di 10 °C (< soglia) non è un gradino | +| `test_domini_toml_andata_e_ritorno` | scrivere e rileggere `domini.toml` dà gli stessi domini | +| `test_domini_toml_non_contigui_rifiutati` | domini non contigui → `ValueError` | +| `test_domini_toml_tipo_non_valido` | tipo sconosciuto → `ValueError` | + +## `test_fit.py` — fit e analisi (A7, A8, A13, A18, A22, A23) + +| Test | Verifica | +|---|---| +| `test_modelli_valutazione` | valori dei modelli; il registro contiene esattamente `costante`, `lineare`, `esp_decad` | +| `test_esp_decad_recupera_parametri` (5 seed) | $\tau$ entro il 2 %, $c$ entro 0.2, $A$ entro il 2 %, $\mathrm{RMSE} \approx \sigma$, fit valido | +| `test_parametri_fissi_restano_fissi` | $c$ e $\tau$ fissi restano invariati, $k = 1$, $A$ recuperata entro il 3 % | +| `test_tau_libero_non_identificabile_su_tratto_corto_e_rumoroso` (5 seed) | su 3 s con disturbi di 2 °C, $\tau$ libero è «non identificabile» (motivo del fissaggio) | +| `test_analisi_completa_sintetico` | 7 domini; finale con $\tau$ entro il 3 % e nessun parametro fisso; intermedi con `c`, `tau` fissi uguali al finale; ampiezze $A$ degli intermedi entro il 5 % dal valore atteso | +| `test_riscaldamento_descritto_come_salto_non_fittato` | `fit` vuoto, `scelta` assente, $\Delta$ entro il 15 % dell'ampiezza vera, ≤ 3 campioni | +| `test_un_solo_raffreddamento_resta_libero` | un solo gradino → il raffreddamento (finale) non ha parametri fissi | +| `test_dominio_troppo_corto_indeterminato` | 5 campioni → `indeterminato` | +| `test_riproducibile` | due esecuzioni identiche danno risultati identici | + +## `test_selezione.py` — regola di scelta ([algoritmi.md](algoritmi.md) §6) + +Su fit finti costruiti a mano: vince il modello con meno parametri entro $\Delta\mathrm{AICc}$; vince il migliore oltre la soglia; si scartano non +convergenti, a limite, non identificabili; nessun valido → indeterminato; si preferiscono i modelli adeguati anche con AICc peggiore; nessun adeguato → +scelto ma segnalato. + +## `test_config_dati.py` — errori di configurazione e CSV (A1, A2) + +`config.toml` reale valido; chiave mancante; lista dei modelli vuota per un tipo; file di configurazione inesistente; CSV valido (conversione +ms → s); colonna mancante; tempo non crescente. + +## `test_integrazione.py` — su `gen_data` e CLI + +| Test | Verifica | +|---|---| +| `test_domini_su_csv_gen` (3 seed) | 7 domini nell'ordine atteso | +| `test_raffreddamento_tau_nominale` (3 seed) | raffreddamento finale: `esp_decad`, $\tau$ entro il 10 % di 15.9 s | +| `test_raffreddamenti_intermedi_ereditano_costanti` (3 seed) | intermedi con `c`, `tau` fissi uguali al finale, $A > 0$ | +| `test_cli_domini_e_analizza` | `fitseg domini` scrive solo `01_domini/`; `analizza --domini` scrive sovrapposizione e report; il report rimanda alle immagini giuste; formule principali in LaTeX e `$` bilanciati su ogni riga | +| `test_cli_errori` | CSV inesistente → exit 4; configurazione inesistente → exit 3 | + +## Cosa non è coperto + +Seed oltre i tre di `gen_data`; rumore e gradini molto diversi dalla configurazione d'esempio; dati reali; i grafici (si controllano a vista, [output.md](output.md)). +Prova manuale dopo una modifica: `fitseg analizza input/gen_1.csv` e controllare in `report.md` i 7 domini e $\tau \approx 16$ s nel finale. diff --git a/docs/uso.md b/docs/uso.md new file mode 100644 index 0000000..55612f9 --- /dev/null +++ b/docs/uso.md @@ -0,0 +1,76 @@ +# Uso + +## 1. Installazione + +Python ≥ 3.11 e [uv](https://docs.astral.sh/uv/) (`curl -LsSf https://astral.sh/uv/install.sh | sh`): + +```bash +uv venv .venv +uv pip install -e ".[dev]" # numpy, scipy, matplotlib; pytest e ruff per lo sviluppo +``` +Il comando si chiama `fitseg`: `.venv/bin/fitseg`, oppure dopo `source .venv/bin/activate` semplicemente `fitseg`. + +## 2. Dati di ingresso + +I CSV da analizzare vanno nella cartella **`input/`** (la cartella è nel repository, i file `*.csv` no). Formato: intestazione, tempo +strettamente crescente, una colonna per serie, numeri senza celle vuote (A1, A2); i nomi delle colonne e l'unità del tempo sono in +`[dati]` di [config.toml](../config.toml) ([configurazione.md](configurazione.md)). + +``` +t_ms,T_C,dT_C +151,22.90,0.20 +301,22.63,0.03 +``` + +I CSV di prova si generano con il repository fratello `../gen_data` (modello: `../gen_data/docs/SPEC.md`). I test di integrazione usano i seed 1–3: + +```bash +mkdir -p input +for s in 1 2 3; do + (cd ../gen_data && .venv/bin/python genera.py config.toml --seed $s -o ../EquationMiner/input/gen_$s.csv) +done +``` + +## 3. Comandi + +```bash +fitseg domini input/gen_1.csv # passo 1: come verrebbero suddivisi i dati (tabella, domini.png, domini.toml), nessun fit +fitseg analizza input/gen_1.csv # tutti i passi: domini, equazioni, sovrapposizione, report +fitseg analizza input/gen_1.csv --domini output/gen_1/01_domini/domini.toml # con i domini corretti a mano +``` + +| Opzione | Dove | Significato | +|---|---|---| +| `--config PATH` | prima del sottocomando | file di configurazione (default `config.toml`) | +| `--output DIR` | prima del sottocomando | cartella madre dell'output (default `[output] cartella_output`); i file vanno in `DIR//` | +| `--domini FILE` | solo `analizza` | usa questi domini invece della proposta automatica | + +**Flusso consigliato**: lancia `fitseg domini`, guarda `01_domini/domini.png` (in particolare lo zoom); se la suddivisione è giusta lancia +`fitseg analizza`, altrimenti correggi `domini.toml` e rilancia con `--domini`. + +### Correggere i domini a mano + +Apri `output//01_domini/domini.toml`, modifica `t0_s` / `t1_s` o il `tipo` (`riposo`, `riscaldamento`, `raffreddamento`), aggiungi o togli blocchi +`[[dominio]]`. I domini devono restare contigui. Formato e regole: [configurazione.md](configurazione.md) («domini.toml»). Con `--domini` il file viene +riscritto normalizzato e il grafico rigenerato, quindi `domini.png` mostra sempre la suddivisione effettivamente usata. + +## 4. Cosa esce + +Una cartella per CSV con `01_domini/`, `02_sovrapposizione/`, `03_report/report.md`: [output.md](output.md). Il report si apre con un visualizzatore Markdown +con supporto matematico (formule in LaTeX). + +## 5. Codici di uscita + +| Codice | Significato | +|---|---| +| 0 | ok | +| 2 | uso errato della riga di comando (argparse) | +| 3 | `config.toml` non valido: file illeggibile, TOML non valido, chiave mancante, valore non ammesso | +| 4 | CSV illeggibile o non valido (colonna mancante, valori non numerici, tempo non crescente) | +| 6 | `domini.toml` non valido (tipo non ammesso, intervallo vuoto, domini non contigui) | + +Gli errori sono stampati su stderr in italiano come `Errore: …`. + +## 6. Sviluppo + +Test e controllo dello stile: [test.md](test.md). Mappa del codice e come aggiungere un modello: [moduli.md](moduli.md). diff --git a/src/fitseg/analisi.py b/src/fitseg/analisi.py index bbc773b..0d8e822 100644 --- a/src/fitseg/analisi.py +++ b/src/fitseg/analisi.py @@ -1,4 +1,4 @@ -"""Analisi: fit dei modelli per ogni dominio e serie e scelta con la regola di SPEC §4.2; il riscaldamento è descritto come salto.""" +"""Analisi: fit dei modelli per ogni dominio e serie e scelta con la regola di docs/algoritmi.md §6; il riscaldamento è descritto come salto.""" import math diff --git a/src/fitseg/dati.py b/src/fitseg/dati.py index 70d828b..936f523 100644 --- a/src/fitseg/dati.py +++ b/src/fitseg/dati.py @@ -26,6 +26,6 @@ def leggi_csv(percorso, cfg): if d["unita_tempo"] == "ms": t = t / 1000.0 if np.any(np.diff(t) <= 0): - raise ErroreDati("il tempo non è strettamente crescente (ipotesi H1)") + raise ErroreDati("il tempo non è strettamente crescente (assunzione A1, docs/assunzioni.md)") serie = {s: righe[:, intestazione.index(s)] for s in d["serie"]} return t, serie diff --git a/src/fitseg/modelli.py b/src/fitseg/modelli.py index 690e37a..fe8fac4 100644 --- a/src/fitseg/modelli.py +++ b/src/fitseg/modelli.py @@ -1,4 +1,4 @@ -"""Registro dei modelli (t' = t − inizio del dominio). Nuovo modello = nuova voce qui, in docs/SPEC.md e in config.toml.""" +"""Registro dei modelli (t' = t − inizio del dominio). Nuovo modello = nuova voce qui, in docs/algoritmi.md (§5.1) e in config.toml.""" import numpy as np diff --git a/tests/test_selezione.py b/tests/test_selezione.py index 220304d..380c049 100644 --- a/tests/test_selezione.py +++ b/tests/test_selezione.py @@ -1,4 +1,4 @@ -"""Regola di scelta del modello (SPEC §5.3) su fit finti.""" +"""Regola di scelta del modello (docs/algoritmi.md §6) su fit finti.""" from fitseg.analisi import scegli