Files
EquationMiner/src/fitseg/analisi.py
T
davideandClaude Sonnet 5.5 8a4309364b Report con tutte le grandezze matematiche in LaTeX
- equazioni dei modelli in LaTeX, equazione scelta con i valori sostituiti (24092...24092)
- parametri ± IC, sigma, RMSE, R^2, rho_1, Delta, t_0/t_1 e criteri di scelta in formula
- motivo della scelta a parole (i criteri in LaTeX stanno nel report)
- test sulle formule e sul bilanciamento dei dollari

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-29 10:30:46 +01:00

80 lines
4.0 KiB
Python

"""Analisi: fit dei modelli per ogni dominio e serie e scelta con la regola di SPEC §4.2; il riscaldamento è descritto come salto."""
import math
from .domini import sigma_rumore
from .fit import adatta
from .modelli import MODELLI
def valido(fit):
return (fit.get("convergenza") and not fit["parametri_a_limite"] and not fit["non_identificabile"]
and math.isfinite(fit["aicc"]))
def scegli(fit_list, sigma, cfg):
"""(fit scelto, motivo, adeguato); (None, motivo, False) se nessun fit è valido."""
f = cfg["fit"]
validi = [x for x in fit_list if valido(x)]
if not validi:
return None, "indeterminato: nessun modello convergente, identificabile e lontano dai limiti", False
adeguati = [x for x in validi if x["rmse"] <= f["rmse_vs_rumore_max"] * sigma]
pool = adeguati or validi
miglior = min(x["aicc"] for x in pool)
scelto = min((x for x in pool if x["aicc"] <= miglior + f["soglia_delta_aicc"]),
key=lambda x: (x["n_parametri"], x["aicc"]))
motivo = (f"{len(validi)}/{len(fit_list)} modelli validi; "
+ ("modello adeguato" if adeguati else "nessun modello adeguato")
+ "; a parità di AICc, meno parametri")
return scelto, motivo, bool(adeguati)
def _gradino(t, y, d):
"""Descrizione del riscaldamento: salto dal campione prima della salita all'ultimo campione di salita."""
i0, i1 = d["i0"], d["i1"]
prima, dopo = float(y[i0 - 1]), float(y[i1 - 1])
g = {"n": int(i1 - i0), "da": prima, "a": dopo, "delta": dopo - prima, "durata_s": float(t[i1 - 1] - t[i0 - 1])}
return {"sigma": None, "fit": [], "scelta": None, "gradino": g,
"motivo": f"salita rapida di {g['n']} campioni ({g['durata_s'] * 1000:.0f} ms), non fittata"}
def _serie(ty, yy, sigma, tipo, fissi, cfg):
voce = {"sigma": float(sigma), "fit": [], "scelta": None}
if len(yy) < cfg["domini"]["n_min_punti"]:
voce["motivo"] = f"indeterminato: {len(yy)} campioni < n_min_punti"
return voce
for m in cfg["fit"]["modelli_per_tipo"][tipo]:
if len(MODELLI[m][1]) <= len(yy) - 2:
voce["fit"].append(adatta(ty, yy, m, sigma, cfg, fissi))
scelto, voce["motivo"], voce["adeguato"] = scegli(voce["fit"], sigma, cfg)
voce["scelta"] = scelto["modello"] if scelto else None
return voce
def analizza_domini(t, serie, domini, cfg):
"""Un risultato per dominio. Il raffreddamento finale (ultimo) si fitta per primo, con parametri liberi; i
raffreddamenti intermedi (tra due gradini, corti) fissano i parametri di `raffreddamento_intermedio_fissa_da_finale`
ai suoi valori (stesse costanti fisiche). Il riscaldamento non si fitta: è descritto come salto."""
scarta = cfg["domini"]["scarta_dopo_salto_n"]
raffreddamenti = [i for i, d in enumerate(domini) if d["tipo"] == "raffreddamento"]
finale = raffreddamenti[-1] if raffreddamenti else None
risultati = [None] * len(domini)
fissi = {nome: {} for nome in serie}
for idx in sorted(range(len(domini)), key=lambda i: i != finale):
d = domini[idx]
i0, i1, t0 = d["i0"] + (scarta if d["tipo"] == "raffreddamento" else 0), d["i1"], t[d["i0"]]
voce = {"k": idx + 1, "tipo": d["tipo"], "t0_s": float(t0), "t1_s": float(t[i1 - 1]),
"n": int(i1 - d["i0"]), "n_fit": int(i1 - i0), "serie": {}}
for nome, y in serie.items():
if d["tipo"] == "riscaldamento":
voce["serie"][nome] = _gradino(t, y, d)
continue
yy = y[i0:i1]
usa = fissi[nome] if d["tipo"] == "raffreddamento" and idx != finale else None
vs = voce["serie"][nome] = _serie(t[i0:i1] - t0, yy, sigma_rumore(yy), d["tipo"], usa, cfg)
if idx == finale and vs["scelta"]:
par = next(x for x in vs["fit"] if x["modello"] == vs["scelta"])["parametri"]
fissi[nome] = {k: par[k] for k in cfg["fit"]["raffreddamento_intermedio_fissa_da_finale"] if k in par}
risultati[idx] = voce
return risultati