#!/usr/bin/env python3 """48_suite_som.py — Suíte SOM v0.5.0: 4D-SOM, T-SOM, GNG, H-SOM. Demos com estrutura topológica CONHECIDA (critérios de falsificação das provas r-19): - 4D-SOM: 4 aglomerados em 4D — EQ deve cair, TE deve cair (Teos S4-1/S4-2) - T-SOM: deriva linear — EQ temporal estabiliza (Teo T-1, rastreio) - GNG: 3 aglomerados — crescimento + componentes conexas (Teo G-1) - H-SOM: decomposição — cota do Teo H-1 respeitada E QE_hier ≤ QE_grosso Saída: resultados/suite_som_v050.json """ import json, os, random, sys, time def main(): import khtst_rust.nucleo_rust as nr pasta = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) res = {"versao": "0.5.0", "modulo": nr.versao(), "suites": {}} # ---------------------------------------------------------------- 4D-SOM print("=== 4D-SOM (grade neuronal 4D) ===", flush=True) rng = random.Random(7) dados = [] for _ in range(240): bloco = rng.randrange(4) c = [[-3, -3, -3, -3], [3, -3, 3, -3], [-3, 3, -3, 3], [3, 3, 3, 3]][bloco] dados.append([v + rng.gauss(0, 0.4) for v in c]) t0 = time.time() som = nr.treinar_som4d(dados, json.dumps({ "nx": 4, "ny": 4, "nz": 2, "nt": 2, "epocas": 150, "sigma0": 1.2, "sigma1": 0.4, "eta0": 0.6, "eta1": 0.1, "espalhada": True, })) dt = time.time() - t0 queda_eq = 100.0 * (1 - som["eq_final"] / som["eq_inicial"]) print(f" EQ: {som['eq_inicial']:.4f} -> {som['eq_final']:.4f} (-{queda_eq:.1f}%) | TE: {som['te_final']:.4f} | {dt:.1f}s") res["suites"]["som4d"] = { **{k: som[k] for k in ("eq_inicial", "eq_final", "te_final", "celulas", "grade", "massa_soma")}, "queda_eq_pct": queda_eq, "tempo_s": dt, "gate_eq_cai": som["eq_final"] < som["eq_inicial"], "gate_te_baixo": som["te_final"] < 0.3, "gate_massa_valida": abs(som["massa_soma"] - 1.0) < 1e-6, } # ---------------------------------------------------------------- T-SOM print("=== T-SOM (Time SOM 4D com esquecimento) ===", flush=True) rng = random.Random(11) iniciais = [[rng.gauss(0, 0.5) for _ in range(3)] for _ in range(96)] tsom = nr.demo_tsom(iniciais, [[rng.gauss(0, 0.5) for _ in range(3)] for _ in range(32)], 25, 0.9) hist = tsom["eq_temporal_historico"] # com deriva simulada DENTRO da janela (recência pondera mais), o EQ # temporal fica limitado — o filtro acompanha (Teo T-1) print(f" EQ temporal: inicio {hist[0]:.4f} | fim {hist[-1]:.4f} | max {max(hist):.4f}") res["suites"]["tsom"] = { "gamma": tsom["gamma"], "eq_temporal_historico": hist, "gate_rastreio_limitado": max(hist) < 2.0 and hist[-1] < hist[0] * 1.5 + 0.5, } # ---------------------------------------------------------------- GNG print("=== GNG (Growing Neural Gas) ===", flush=True) rng = random.Random(3) dados_gng = [] for c in [[-5, 0, 0, 0], [5, 0, 0, 0], [0, 5, 0, 0]]: for _ in range(70): dados_gng.append([v + rng.gauss(0, 0.3) for v in c]) t0 = time.time() gng = nr.treinar_gng(dados_gng, json.dumps({"passos": 8000, "max_vertices": 45, "lambda": 60})) dt = time.time() - t0 print(f" vertices: {gng['vertices']} | arestas: {gng['arestas']} | EQ medio: {gng['eq_medio']:.4f} | {dt:.1f}s") res["suites"]["gng"] = { **{k: gng[k] for k in ("vertices", "arestas", "componentes_conexas", "insercoes", "eq_medio")}, "tempo_s": dt, "gate_cresceu": gng["vertices"] >= 10, "gate_eq_baixo": gng["eq_medio"] < 0.8, } # ---------------------------------------------------------------- H-SOM print("=== H-SOM (hierárquico) ===", flush=True) rng = random.Random(21) dados_h = [] for c in [[-2, -2, 0], [2, -2, 0], [-2, 2, 0], [2, 2, 0]]: for _ in range(60): dados_h.append([v + rng.gauss(0, 0.5) for v in c]) t0 = time.time() hsom = nr.treinar_hsom(dados_h, json.dumps({"celulas_finas": 9, "epocas_finas": 20})) dt = time.time() - t0 print(f" QE hier: {hsom['eq_hierarquico']:.4f} | QE grosso: {hsom['eq_grosso']:.4f} | R_fino: {hsom['r_fino']:.4f} | {dt:.1f}s") res["suites"]["hsom"] = { **{k: hsom[k] for k in ("eq_hierarquico", "eq_grosso", "r_fino", "cota_teo_h1", "cota_respeitada")}, "tempo_s": dt, "gate_hier_melhora": hsom["eq_hierarquico"] <= hsom["eq_grosso"] + 1e-9, } # ---------------------------------------------------------------- resumo gates = [] for nome, s in res["suites"].items(): gates += [(f"{nome}.{g[5:]}", v) for g, v in s.items() if g.startswith("gate_")] ok = sum(1 for _, v in gates if v) res["resumo"] = {"gates_totais": len(gates), "gates_ok": ok, "todos_verdes": ok == len(gates)} print(f"\n=== RESUMO: {ok}/{len(gates)} gates verdes ===") for g, v in gates: print(f" {'OK ' if v else 'FALHOU'} {g}") os.makedirs(os.path.join(pasta, "resultados"), exist_ok=True) with open(os.path.join(pasta, "resultados", "suite_som_v050.json"), "w") as f: json.dump(res, f, indent=1, ensure_ascii=False) print("salvo: resultados/suite_som_v050.json") if not res["resumo"]["todos_verdes"]: sys.exit(1) if __name__ == "__main__": main()