66 lines
2.3 KiB
Python
66 lines
2.3 KiB
Python
|
|
import sys
|
||
|
|
import os
|
||
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||
|
|
|
||
|
|
import torch
|
||
|
|
from engine import prepare_data
|
||
|
|
from model import HeatPINN, heat_pinn_loss
|
||
|
|
|
||
|
|
|
||
|
|
def test_data_to_model_forward():
|
||
|
|
"""prepare_data → forward: shape e device coerenti, nessun NaN."""
|
||
|
|
data = prepare_data(N_f=100, N_ic=50, N_bc=50)
|
||
|
|
model = HeatPINN().to(data['device'])
|
||
|
|
xt = torch.stack([data['x_f'], data['t_f']], dim=1)
|
||
|
|
out = model(xt)
|
||
|
|
assert out.shape == (data['x_f'].shape[0], 1)
|
||
|
|
assert out.device.type == data['device'].type
|
||
|
|
assert torch.isfinite(out).all()
|
||
|
|
|
||
|
|
|
||
|
|
def test_full_loss_pipeline():
|
||
|
|
"""prepare_data → heat_pinn_loss: tutti i componenti finiti e non-negativi."""
|
||
|
|
data = prepare_data(N_f=100, N_ic=50, N_bc=50)
|
||
|
|
model = HeatPINN().to(data['device'])
|
||
|
|
total, L_pde, L_ic, L_bc = heat_pinn_loss(
|
||
|
|
model, data['x_f'], data['t_f'], data['x_ic'], data['t_bc']
|
||
|
|
)
|
||
|
|
for name, v in [('total', total), ('L_pde', L_pde), ('L_ic', L_ic), ('L_bc', L_bc)]:
|
||
|
|
assert torch.isfinite(v), f"{name} non è finita"
|
||
|
|
assert v.item() >= 0.0, f"{name} è negativa"
|
||
|
|
|
||
|
|
|
||
|
|
def test_backward_gradients_finite():
|
||
|
|
"""Il backward della loss non produce NaN/Inf nei parametri."""
|
||
|
|
data = prepare_data(N_f=100, N_ic=50, N_bc=50)
|
||
|
|
model = HeatPINN().to(data['device'])
|
||
|
|
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
|
||
|
|
optimizer.zero_grad()
|
||
|
|
total, _, _, _ = heat_pinn_loss(
|
||
|
|
model, data['x_f'], data['t_f'], data['x_ic'], data['t_bc']
|
||
|
|
)
|
||
|
|
total.backward()
|
||
|
|
for p in model.parameters():
|
||
|
|
assert p.grad is not None
|
||
|
|
assert torch.isfinite(p.grad).all(), "gradiente NaN/Inf"
|
||
|
|
|
||
|
|
|
||
|
|
def test_training_loop_stable():
|
||
|
|
"""20 step di Adam non producono NaN/Inf nei parametri né nella loss."""
|
||
|
|
data = prepare_data(N_f=200, N_ic=100, N_bc=100)
|
||
|
|
model = HeatPINN().to(data['device'])
|
||
|
|
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
|
||
|
|
args = (model, data['x_f'], data['t_f'], data['x_ic'], data['t_bc'])
|
||
|
|
|
||
|
|
for _ in range(20):
|
||
|
|
optimizer.zero_grad()
|
||
|
|
loss, _, _, _ = heat_pinn_loss(*args)
|
||
|
|
loss.backward()
|
||
|
|
optimizer.step()
|
||
|
|
|
||
|
|
for p in model.parameters():
|
||
|
|
assert torch.isfinite(p).all(), "parametro NaN/Inf dopo training"
|
||
|
|
|
||
|
|
total, _, _, _ = heat_pinn_loss(*args)
|
||
|
|
assert torch.isfinite(total)
|