- 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>
80 lines
4.0 KiB
Python
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
|