khtst-rust / src /acoplador.rs
PowerMachine's picture
v0.6.0: verificador NCM calibrado (sem oráculo, V-1..V-4) + treino conjunto Pensamento↔backbone (fase C, PCGrad, acoplador duplex AC) + MoEs redirecionáveis c/ atenção própria (M-R1..M-R5, JEPA+SOM) + autoajuste por telemetria (A-1..A-5, UCB1) — 46/46 gates, provas r-23..r-27
bb2363d verified
Raw History Blame Contribute Delete
7.94 kB
//! **Acoplador Duplo Pensamento↔backbone** (v0.6.0 — "conexão bidirecional
//! e paralela modelo ↔ modelo" estendida ao treino conjunto).
//!
//! Envolve a `PonteDupla` (r-05: P.1 sem deadlock por potencial, P.2
//! simultaneidade medida, P.3 filas limitadas) num protocolo lag-1
//! síncrono entre os dois MODELOS:
//!
//! - **A = backbone (gerador NCM):** envia o estado `(a, b, c)` —
//! conhecimento (resumo do tronco), estado de inferência (ẑ corrompido)
//! e sonda do nível — JUNTO com a confiança calibrada da sua distribuição
//! preditiva (o feedback que treina o gate do Pensamento);
//! - **B = Pensamento:** envia a correção `δ` agregada das N tentativas
//! (o valor que o backbone injeta no condicionamento) JUNTO com o
//! auto-acordo das tentativas (estatística de diversidade — insumo do
//! autoajuste de N, lema do acordo par-a-par).
//!
//! Em CADA rodada as duas direções carregam cargas REAIS e a troca é
//! simultânea (P.2 herdado + medido aqui). O lag-1 é a consequência lógica
//! da simultaneidade: `δ_k` depende do estado `s_k`, que chega a B na mesma
//! rodada em que A recebe `δ_{k−1}` — causalidade por filtração
//! (δ_k é mensurável w.r.t. F_k = σ(s_0..s_k)).
//!
//! **Provas (r-24):**
//! - **AC-1 (causalidade do protocolo lag-1):** a sequência trocada é
//! `(s_k, δ_{k−1})`; δ_k = P(s_k) com P mensurável ⇒ nenhuma mensagem
//! contém informação do futuro (filtração crescente).
//! - **AC-2 (terminação):** potencial Φ = R − k (orçamento de rodadas) —
//! decresce estritamente; herdado de P.1/T-P4.
//! - **AC-3 (consistência valor↔grafo):** no TREINO conjunto, a correção
//! injetada no grafo de autograd é o MESMO tensor δ cujo valor cruza a
//! ponte (cópia destacada apenas para o protocolo/telemetria) — o
//! gradiente não precisa cruzar canais; mora no grafo. Em inferência,
//! o protocolo carrega tudo (sem gradientes).
use crate::ponte::{EstatisticasPonte, PonteDupla};
use std::time::Instant;
#[derive(Debug, Clone)]
pub struct EstadoModelo {
/// conhecimento: resumo do tronco (projetado em d_lat).
pub a: Vec<f32>,
/// estado de inferência corrente: ẑ_{ℓ−1} (possivelmente corrompido).
pub b: Vec<f32>,
/// sonda da requisição corrente (nível/consulta).
pub c: Vec<f32>,
/// confiança calibrada do NCM na predição corrente (feedback A→B).
pub confianca: f64,
pub nivel: usize,
pub passo: usize,
}
#[derive(Debug, Clone)]
pub struct Correcao {
/// correção agregada δ (d_lat) a injetar em ẑ.
pub delta: Vec<f32>,
/// auto-acordo par-a-par das N tentativas (diversidade p/ autoajuste).
pub acordo: f64,
/// confiança do verificador NCM na resposta agregada (B→A informativo).
pub confianca_agregada: f64,
pub iteracao: usize,
pub pesos: Vec<f64>,
}
#[derive(Debug, Clone, Default)]
pub struct TelemetriaAcoplador {
pub rodadas: u64,
pub timeouts: u64,
pub soma_norma_delta: f64,
pub soma_confianca: f64,
pub soma_acordo: f64,
pub maior_sobreposicao_us: u128,
pub tempo_total_ms: f64,
}
pub struct AcopladorDuplo {
pub ponte: PonteDupla,
correcao_anterior: Correcao,
pub telemetria: TelemetriaAcoplador,
}
impl AcopladorDuplo {
pub fn nova(capacidade: usize, timeout_ms: u64) -> Self {
AcopladorDuplo {
ponte: PonteDupla::nova(capacidade, timeout_ms),
correcao_anterior: Correcao {
delta: Vec::new(),
acordo: 0.0,
confianca_agregada: 0.0,
iteracao: 0,
pesos: Vec::new(),
},
telemetria: TelemetriaAcoplador::default(),
}
}
/// UMA rodada duplex: A→B estado (a,b,c,conf); B→A correção ANTERIOR.
/// Devolve a correção RECEBIDA (para o backbone aplicar/inspecionar).
pub fn rodada(&mut self, estado: EstadoModelo) -> Result<Correcao, String> {
let inicio = Instant::now();
// payload A→B: [a; b; c; conf] | payload B→A: [δ; acordo; conf_agr]
let mut carga_a: Vec<f32> = Vec::with_capacity(
estado.a.len() + estado.b.len() + estado.c.len() + 3,
);
carga_a.extend(estado.a.iter().copied());
carga_a.extend(estado.b.iter().copied());
carga_a.extend(estado.c.iter().copied());
carga_a.push(estado.confianca as f32);
let mut carga_b: Vec<f32> = self.correcao_anterior.delta.clone();
carga_b.push(self.correcao_anterior.acordo as f32);
carga_b.push(self.correcao_anterior.confianca_agregada as f32);
// troca simultânea (P.2) com timeout (P.1)
let (msg_de_b, _msg_de_a) = self.ponte.rodada(carga_a, carga_b, estado.nivel)?;
// reconstrói a correção recebida de B
let recebida = Correcao {
delta: msg_de_b.carga.clone(),
acordo: self.correcao_anterior.acordo,
confianca_agregada: self.correcao_anterior.confianca_agregada,
iteracao: self.correcao_anterior.iteracao,
pesos: self.correcao_anterior.pesos.clone(),
};
// telemetria
let norm: f64 = msg_de_b
.carga
.iter()
.map(|x| (*x as f64) * (*x as f64))
.sum::<f64>()
.sqrt();
let t = self.ponte.estatisticas();
self.telemetria.rodadas += 1;
self.telemetria.timeouts += t.quedas_por_timeout;
self.telemetria.soma_norma_delta += norm;
self.telemetria.soma_confianca += estado.confianca;
self.telemetria.soma_acordo += recebida.acordo;
self.telemetria.maior_sobreposicao_us =
self.telemetria.maior_sobreposicao_us.max(t.maior_sobreposicao_us);
self.telemetria.tempo_total_ms += inicio.elapsed().as_secs_f64() * 1000.0;
Ok(recebida)
}
/// Registra a correção fresca (calculada do estado recebido nesta
/// rodada) — será o payload B→A da PRÓXIMA rodada (lag-1 — AC-1).
pub fn registrar_correcao(&mut self, c: Correcao) {
self.correcao_anterior = c;
}
pub fn estatisticas(&self) -> EstatisticasPonte {
self.ponte.estatisticas()
}
pub fn encerrar(&self) {
self.ponte.encerrar();
}
}
pub fn versao() -> &'static str {
"acoplador-0.6.0"
}
#[cfg(test)]
mod testes {
use super::*;
#[test]
fn ac1_ac2_rodadas_causais_terminam() {
// 60 rodadas com estados crescentes — sem deadlock, sem timeout
let mut ac = AcopladorDuplo::nova(8, 500);
let mut conf = 0.1f64;
for k in 0..60 {
let estado = EstadoModelo {
a: vec![0.1 * k as f32; 8],
b: vec![0.2 * k as f32; 8],
c: vec![0.3; 4],
confianca: conf,
nivel: k % 7,
passo: k,
};
let recebida = ac.rodada(estado).expect("rodada sem deadlock");
assert!(recebida.delta.len() >= 2, "correção recebida não vazia após 1ª rodada");
// δ_k = função do estado s_k (causal): registra correção fresca
let delta: Vec<f32> = (0..8).map(|i| (i as f32 + k as f32) * 0.01).collect();
ac.registrar_correcao(Correcao {
delta,
acordo: 0.5,
confianca_agregada: conf,
iteracao: k,
pesos: vec![0.25; 4],
});
conf = (conf * 1.02).min(0.99);
}
let t = &ac.telemetria;
assert_eq!(t.rodadas, 60);
assert_eq!(t.timeouts, 0, "nenhum timeout");
assert!(t.soma_norma_delta > 0.0);
// P.2: sobreposição medida nas duas direções
let p = ac.estatisticas();
assert!(p.envios_a_para_b == 60 && p.envios_b_para_a == 60);
assert!(p.quedas_por_timeout == 0);
}
}