#!/usr/bin/env python3 """62_suite_fnh.py — v0.7.0: gates da FNH + ablação rotação/ortogonalidade EM TREINO (r-29 §3; requisitos: "construir em rust fnh_layer quando a punição estiver alta" + "testar e verificar uso de rotação e ortogonalidade em treinamento"). Gates: F1 rotação SO(n) exata (δ_ort ≤ 1e-6) — cargo test f1; F2 nascimento neutro bit-a-bit — cargo test f2; F3 gate zona morta exata + monotonia — cargo test f3; F4 homeostase (gradiente a θ estrangulado com g=0) — cargo test f4; F6 migração compensada identidade — cargo f6; F7 balanço AM-GM — cargo f7; A1 ablação: π alta ⇒ θ aprende; π baixa ⇒ θ congelado (demo_fnh); A2 δ_ort do codebook da fase A mantido (rotação no treino real). """ import json, os, sys def main(): pasta = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, pasta) import khtst_rust as K rel = {"versao": "fnh-suite-0.7.0", "gates": {}} # ablação sintética controlada (mecanismo) fnh = K.demo_fnh() alta, baixa = fnh["resumo"]["punicao_alta"], fnh["resumo"]["punicao_baixa"] rel["gates"]["f1_so_n_exato"] = alta["delta_ort"] < 1e-6 rel["gates"]["f3_gate_zona_morta"] = baixa["gate_final"] == 0.0 rel["gates"]["f3_gate_abre"] = alta["gate_final"] > 0.5 rel["gates"]["f4_homeostase_theta_congelado"] = baixa["theta_norma"] == 0.0 rel["gates"]["f4_rotacao_aprende_com_punicao"] = alta["theta_norma"] > 0.0 rel["ablaton"] = {"alta": alta, "baixa": baixa} # treino real: δ_ort do CODEBOOK TREINADO (medido pelo 63 no checkpoint # da fase A — rotação Stiefel EM treino real) + punição da fase C t70 = os.path.join(pasta, "resultados", "treino_v070.json") aq = os.path.join(pasta, "resultados", "arvore_quebra_v070.json") d_ort = None if os.path.exists(aq): with open(aq) as f: d_ort = json.load(f).get("quebra_cabeca_real", {}).get("delta_ort_codebook") if d_ort is None: # mede direto (o 63 roda depois se preciso — aqui executa a medição) ck_tok = os.path.join(pasta, "resultados", "tokenizador_conceitos.json") ck_ger = os.path.join(pasta, "resultados", "gerador_conceitos.json") if os.path.exists(ck_tok) and os.path.exists(ck_ger): cfg = K.Config(os.path.join(pasta, "configs", "base_rust.json")) qc = K.quebra_cabeca_real(cfg, ck_tok, ck_ger, n_pecas=2, n_rodadas=1, tau_c=1.1, nivel=0, seed=1) d_ort = qc.get("delta_ort_codebook") if d_ort is not None: rel["gates"]["a2_delta_ort_treino_real"] = d_ort < 1e-5 rel["treino_real"] = {"delta_ort_codebook": d_ort} if os.path.exists(t70): with open(t70) as f: tr = json.load(f) pun = tr.get("fnh", {}).get("punicao_normalizada_fase_c", 0.0) rel["gates"]["a3_punicao_real_medida"] = 0.0 <= pun <= 1.0 # veredito n_ok = sum(1 for v in rel["gates"].values() if v) rel["veredito"] = f"{n_ok}/{len(rel['gates'])}" saida = os.path.join(pasta, "resultados", "suite_fnh_v070.json") with open(saida, "w") as f: json.dump(rel, f, indent=1, ensure_ascii=False) print(json.dumps(rel["gates"], indent=1)) print("VEREDITO FNH:", rel["veredito"]) assert n_ok == len(rel["gates"]), "gate FNH falhou" if __name__ == "__main__": main()