khtst-rust / src /python /demos_qwen.rs
PowerMachine's picture
v0.10.0: análise/prova/teste das críticas de engenharia Qwen3.8 lote 3 (r-35) — divisão SRP dos monólitos jepa/pensamento/moe/python em 26 submódulos (API estável via re-exports; 'Amdahl de compilação' refutada por experimento controlado, builds frios neutros), checkpoints atômicos SHA-256 puro FIPS 180-4 + fsync + rename + manifesto (CP-1..CP-3; correções da proposta: sem destruir estado anterior, manifesto com barreira, sem bincode/unwrap), TemperaturaInversa T-V3 (fronteira tipada da convexidade N-2; alegação compile-time corrigida), trait CamadaKhtst+CamadaHomeostatica (premissa do controlador refutada no fonte; Vec<Var>; adaptador MoeComPapel bit-a-bit), CLI Typer 75 (fachada aditiva com resume verificado por hash) — 123/123 cargo, 107/107 gates, treino real Wikipédia PT-BR com checkpoints íntegros
06e4b6f verified
Raw History Blame Contribute Delete
16 kB
//! `python::demos_qwen` — extraído do monólito na v0.10.0
//! (divisão SRP, crítica Qwen3.8 lote 3 nº 1; r-35 §2).
use super::*;
/// **v0.8.0 — análise/prova/teste das 4 propostas Qwen3.8** (r-33): mede
/// custo e exatidão de (1) Newton analítico vs golden-section (verificador),
/// (2) Cayley vs polar (FNH), (3) árvore pós-ordem iterativa vs recursiva,
/// (4) MoE vetorizada vs referência (valores + gradiente restaurado — bug
/// v080-1), e re-executa a ablação JEPA de 3 braços com a correção.
#[pyfunction]
#[allow(deprecated)] // mede o bug v080-1 intencionalmente (fwd = oráculo)
pub(crate) fn demo_qwen_v080(py: Python) -> PyResult<PyObject> {
use candle_core::{Device, Tensor};
use std::time::Instant;
let dev = Device::Cpu;
// ---------- (1) Newton vs golden-section (verificador) ----------
let mut rng = crate::rng::RngDeterministico::novo(808);
let (n, k) = (1024usize, 32usize);
let mut lv = Vec::with_capacity(n * k);
let mut av = Vec::with_capacity(n);
for _ in 0..n {
let alvo = rng.inteiro(k) as u32;
av.push(alvo);
for j in 0..k {
let base = if j as u32 == alvo { 3.0 } else { 0.0 };
lv.push(base + rng.normal(0.0, 1.0) as f32);
}
}
let lt = Tensor::from_vec(lv, (n, k), &dev).unwrap();
let at = Tensor::from_vec(av, (n,), &dev).unwrap();
let t0 = Instant::now();
let (t_g, _, nll_g) = verificador::calibrar_temperatura(&lt, &at).unwrap();
let tempo_golden = t0.elapsed().as_secs_f64();
let t0 = Instant::now();
let (t_n, _, nll_n, n_aval) =
verificador::calibrar_temperatura_newton(&lt, &at, 40).unwrap();
let tempo_newton = t0.elapsed().as_secs_f64();
// ---------- (2) Cayley vs polar (FNH) ----------
let n_rot = 16usize;
let r = fnh::RotacaoSkew::nova(n_rot, &dev).unwrap();
let th: Vec<f32> = (0..n_rot * (n_rot - 1) / 2)
.map(|i| ((i as f32) * 0.11) % 0.5)
.collect();
r.theta
.set(&Tensor::from_vec(th, (n_rot * (n_rot - 1) / 2,), &dev).unwrap())
.unwrap();
let rep = 25usize;
let t0 = Instant::now();
let mut delta_polar = 0.0;
for _ in 0..rep {
delta_polar = crate::stiefel::erro_ortogonalidade(&r.rotacao_polar().unwrap()).unwrap();
}
let tempo_polar = t0.elapsed().as_secs_f64() / rep as f64;
let t0 = Instant::now();
let mut delta_cayley = 0.0;
for _ in 0..rep {
delta_cayley = crate::stiefel::erro_ortogonalidade(&r.rotacao_cayley().unwrap()).unwrap();
}
let tempo_cayley = t0.elapsed().as_secs_f64() / rep as f64;
// ---------- (3) árvore iterativa vs recursiva ----------
let mut arv = arvore_pesos::ArvorePesos::nova(&std::collections::HashMap::new());
let mut pai = 1usize;
for i in 0..20_000u32 {
pai = arv.anexar_filho(pai, &format!("c{i}"), 1);
}
arv.nos[pai].metricas.insert("m".into(), 1.0);
let t0 = Instant::now();
let total_it = arv.agregar_fundo_topo("m");
let tempo_arv_it = t0.elapsed().as_secs_f64();
// repõe TODAS as métricas locais (a agregação sobrescreveu com os
// agregados) — só a folha tem valor; o resto volta a 0 — e roda a
// recursiva (profundidade 20k ok na thread principal do Python)
for no in arv.nos.iter_mut() {
no.metricas.insert("m".into(), 0.0);
}
arv.nos[pai].metricas.insert("m".into(), 1.0);
let t0 = Instant::now();
let total_rec = arv.agregar_fundo_topo_rec("m");
let tempo_arv_rec = t0.elapsed().as_secs_f64();
// ---------- (4) MoE vetorizada: valores + gradiente + custo ----------
let (nm, dm, em) = (256usize, 32usize, 8usize);
let xv: Vec<f32> = (0..nm * dm).map(|_| rng.normal(0.0, 1.0) as f32).collect();
let x = Tensor::from_vec(xv, (nm, dm), &dev).unwrap();
let moe = moe_redirecionavel::MoeRedirecionavel::novo(dm, em, 2, nm, &dev).unwrap();
let s_ref = moe.fwd(&x, moe_redirecionavel::Papel::Codificador).unwrap();
let s_vet = moe.forward(&x, moe_redirecionavel::Papel::Codificador).unwrap();
let dif_moe = (&s_ref.saida - &s_vet.saida)
.unwrap()
.abs()
.unwrap()
.max_all()
.unwrap()
.to_scalar::<f32>()
.unwrap() as f64;
let rep_moe = 10usize;
let t0 = Instant::now();
for _ in 0..rep_moe {
let _ = moe.fwd(&x, moe_redirecionavel::Papel::Codificador).unwrap();
}
let tempo_moe_ref = t0.elapsed().as_secs_f64() / rep_moe as f64;
let t0 = Instant::now();
for _ in 0..rep_moe {
let _ = moe.forward(&x, moe_redirecionavel::Papel::Codificador).unwrap();
}
let tempo_moe_vet = t0.elapsed().as_secs_f64() / rep_moe as f64;
// normas de gradiente (bug v080-1: fwd = 0; vetorizada > 0)
let norma = |vet: bool| -> (f64, f64) {
let s = if vet {
moe.forward(&x, moe_redirecionavel::Papel::Codificador).unwrap()
} else {
moe.fwd(&x, moe_redirecionavel::Papel::Codificador).unwrap()
};
let perda = s.saida.sqr().unwrap().sum_all().unwrap();
let gs = perda.backward().unwrap();
let mut nr = 0.0;
for v in moe.roteador.vars() {
if let Some(g) = gs.get(&v) {
nr += g.sqr().unwrap().sum_all().unwrap().to_scalar::<f32>().unwrap() as f64;
}
}
let mut ne = 0.0;
for e in &moe.especialistas {
for v in e.vars() {
if let Some(g) = gs.get(&v) {
ne += g.sqr().unwrap().sum_all().unwrap().to_scalar::<f32>().unwrap() as f64;
}
}
}
(nr, ne)
};
let (grad_ref_r, grad_ref_e) = norma(false);
let (grad_vet_r, grad_vet_e) = norma(true);
// ---------- (5) re-ablação JEPA 3 braços (gradiente restaurado) ----------
let xv: Vec<f32> = (0..64 * 12).map(|_| rng.normal(0.0, 1.0) as f32).collect();
let xj = Tensor::from_vec(xv, (64, 12), &dev).unwrap();
let mut ablac = serde_json::Map::new();
for modo in ["pequena", "densa", "moe"] {
let rj = moe_redirecionavel::treinar_jepa_com_moe(&xj, modo, 120, 0.5, 42).unwrap();
ablac.insert(
modo.into(),
serde_json::json!({
"perda_inicial": rj.perda_pred_inicial,
"perda_final": rj.perda_pred_final,
"queda_pct": 100.0 * (1.0 - rj.perda_pred_final / rj.perda_pred_inicial.max(1e-9)),
}),
);
}
let d_ = serde_json::json!({
"versao": "qwen-v080-1.0",
"newton_vs_golden": {
"t_golden": t_g, "t_newton": t_n, "dif_t": (t_g - t_n).abs(),
"nll_golden": nll_g, "nll_newton": nll_n,
"nll_dif": (nll_g - nll_n).abs(),
"n_aval_newton": n_aval,
"tempo_golden_s": tempo_golden, "tempo_newton_s": tempo_newton,
"speedup": tempo_golden / tempo_newton.max(1e-9),
"gates": {
"mesmo_t": (t_g - t_n).abs() < 1e-3,
"nll_nao_piora": nll_n <= nll_g + 1e-4,
"menos_avaliacoes": n_aval < 36,
},
},
"cayley_vs_polar": {
"delta_ort_polar": delta_polar, "delta_ort_cayley": delta_cayley,
"tempo_polar_s": tempo_polar, "tempo_cayley_s": tempo_cayley,
"speedup": tempo_polar / tempo_cayley.max(1e-9),
"gates": {
"ambos_exatos": delta_polar < 1e-6 && delta_cayley < 1e-6,
"cayley_mais_rapido": tempo_cayley < tempo_polar,
},
},
"arvore_iter_vs_rec": {
"n_nos": arv.n_nos(),
"total_iter": total_it, "total_rec": total_rec,
"bit_a_bit": total_it.to_bits() == total_rec.to_bits(),
"tempo_iter_s": tempo_arv_it, "tempo_rec_s": tempo_arv_rec,
"gates": { "iguais_bit_a_bit": total_it == total_rec },
},
"moe_vetorizada": {
"max_dif_valores": dif_moe,
"tempo_ref_s": tempo_moe_ref, "tempo_vet_s": tempo_moe_vet,
"grad_roteador_ref": grad_ref_r, "grad_especialistas_ref": grad_ref_e,
"grad_roteador_vet": grad_vet_r, "grad_especialistas_vet": grad_vet_e,
"gates": {
"valores_iguais": dif_moe < 1e-4,
"bug_v080_1_confirmado": grad_ref_r == 0.0 && grad_ref_e == 0.0,
"gradiente_restaurado": grad_vet_r > 0.0 && grad_vet_e > 0.0,
},
},
"ablacao_jepa_3_bracos_v080": ablac,
});
Ok(pythonize::pythonize(py, &d_)?.into())
}
/// **v0.9.0 — veredito executável das críticas Qwen3.8 (r-34):** mede os
/// aprimoramentos contra as referências v0.8.0 (oráculos preservados):
/// (1) skew O(n²) via index_add — bit-a-bit + tempo + memória analítica;
/// (2) calibrar vetorial — bit-a-bit em 3 passos EMA + tempo;
/// (3) forward canônico (ex-fwd_vetorizado) — regressão de valores e
/// gradiente;
/// (4) newtypes de domínio — rejeição de fora-de-domínio.
#[pyfunction]
pub(crate) fn demo_criticas_v090(py: Python) -> PyResult<PyObject> {
use candle_core::{Device, Tensor};
use std::time::Instant;
let dev = Device::Cpu;
let mut rng = crate::rng::RngDeterministico::novo(909);
// ---------- (1) skew: index_add O(n²) vs matmul de seleção O(n⁴) ----------
let mut skew_rel = serde_json::Map::new();
let mut bits_todos = true;
for &n in &[16usize, 64, 128] {
let r = fnh::RotacaoSkew::nova(n, &dev).unwrap();
let m = n * (n - 1) / 2;
let th: Vec<f32> = (0..m).map(|_| rng.uniforme_intervalo(-0.9, 0.9) as f32).collect();
r.theta.set(&Tensor::from_vec(th, (m,), &dev).unwrap()).unwrap();
// bit-a-bit (F-13)
let an = r.skew().unwrap().flatten_all().unwrap().to_vec1::<f32>().unwrap();
let ar = r.skew_matmul_referencia().unwrap().flatten_all().unwrap().to_vec1::<f32>().unwrap();
let bits_iguais = an.iter().zip(ar.iter()).all(|(a, b)| a.to_bits() == b.to_bits());
bits_todos &= bits_iguais;
// tempo (referência esquentada 1×; média de 5)
let _ = r.skew_matmul_referencia().unwrap();
let t0 = Instant::now();
for _ in 0..5 { let _ = r.skew_matmul_referencia().unwrap(); }
let t_ref = t0.elapsed().as_secs_f64() / 5.0;
let _ = r.skew().unwrap();
let t0 = Instant::now();
for _ in 0..5 { let _ = r.skew().unwrap(); }
let t_novo = t0.elapsed().as_secs_f64() / 5.0;
let bytes_ref = (m * n * n * 4) as f64;
let bytes_novo = ((n * n + 3 * m) * 4 + 2 * m * 4) as f64;
skew_rel.insert(
format!("n_{n}"),
serde_json::json!({
"tempo_ref_s": t_ref, "tempo_novo_s": t_novo,
"speedup": t_ref / t_novo.max(1e-12),
"bytes_ref_selecao": bytes_ref, "bytes_novo": bytes_novo,
"razao_memoria": bytes_ref / bytes_novo,
"bits_iguais": bits_iguais,
}),
);
}
// ---------- (2) calibrar: tensor vs laços nativos ----------
let (d_out, d_in, n_lin) = (512usize, 512usize, 4096usize);
let wv: Vec<f32> = (0..d_out * d_in).map(|_| rng.uniforme_intervalo(-2.0, 2.0) as f32).collect();
let w = Tensor::from_vec(wv, (d_out, d_in), &dev).unwrap();
let xv: Vec<f32> = (0..n_lin * d_in).map(|_| rng.uniforme_intervalo(-3.0, 3.0) as f32).collect();
let x = Tensor::from_vec(xv, (n_lin, d_in), &dev).unwrap();
let mut s_novo = fnh::SuavizadorQuant::novo(0.5, 0.98);
let mut s_tens = fnh::SuavizadorQuant::novo(0.5, 0.98);
let mut s_ref = fnh::SuavizadorQuant::novo(0.5, 0.98);
let mut bits_cal = true;
for _ in 0..3 {
s_novo.calibrar(&w, &x).unwrap();
s_tens.calibrar_tensor(&w, &x).unwrap();
s_ref.calibrar_referencia(&w, &x).unwrap();
for (a, b) in s_novo.w_max.as_ref().unwrap().iter().zip(s_ref.w_max.as_ref().unwrap()) {
bits_cal &= a.to_bits() == b.to_bits();
}
for (a, b) in s_novo.x_max.as_ref().unwrap().iter().zip(s_ref.x_max.as_ref().unwrap()) {
bits_cal &= a.to_bits() == b.to_bits();
}
}
// tempo (esquentadas 1×; média de 5)
let _ = s_ref.calibrar_referencia(&w, &x);
let t0 = Instant::now();
for _ in 0..5 { s_ref.calibrar_referencia(&w, &x).unwrap(); }
let t_cal_ref = t0.elapsed().as_secs_f64() / 5.0;
let _ = s_novo.calibrar(&w, &x);
let t0 = Instant::now();
for _ in 0..5 { s_novo.calibrar(&w, &x).unwrap(); }
let t_cal_novo = t0.elapsed().as_secs_f64() / 5.0;
let _ = s_tens.calibrar_tensor(&w, &x);
let t0 = Instant::now();
for _ in 0..5 { s_tens.calibrar_tensor(&w, &x).unwrap(); }
let t_cal_tens = t0.elapsed().as_secs_f64() / 5.0;
// ---------- (3) forward: regressão de valores + gradiente ----------
let (nm, dm, em) = (256usize, 32usize, 8usize);
let xv2: Vec<f32> = (0..nm * dm).map(|_| rng.normal(0.0, 1.0) as f32).collect();
let x2 = Tensor::from_vec(xv2, (nm, dm), &dev).unwrap();
#[allow(deprecated)] // oráculo do bug v080-1 (r-33 §5)
let moe = moe_redirecionavel::MoeRedirecionavel::novo(dm, em, 2, nm, &dev).unwrap();
#[allow(deprecated)]
let s_refm = moe.fwd(&x2, moe_redirecionavel::Papel::Codificador).unwrap();
let s_fwd = moe.forward(&x2, moe_redirecionavel::Papel::Codificador).unwrap();
let dif_moe = (&s_refm.saida - &s_fwd.saida).unwrap().abs().unwrap()
.max_all().unwrap().to_scalar::<f32>().unwrap() as f64;
let perda = s_fwd.saida.sqr().unwrap().sum_all().unwrap();
let gs = perda.backward().unwrap();
let mut gr = 0.0f64;
for v in moe.roteador.vars() {
if let Some(g) = gs.get(&v) {
gr += g.sqr().unwrap().sum_all().unwrap().to_scalar::<f32>().unwrap() as f64;
}
}
let mut ge = 0.0f64;
for e in &moe.especialistas {
for v in e.vars() {
if let Some(g) = gs.get(&v) {
ge += g.sqr().unwrap().sum_all().unwrap().to_scalar::<f32>().unwrap() as f64;
}
}
}
// ---------- (4) newtypes de domínio (T-V1) ----------
let rejeita = crate::tipos::Probabilidade::novo(7.0).is_err()
&& crate::tipos::Probabilidade::novo(-0.5).is_err()
&& crate::tipos::Probabilidade::novo(f64::NAN).is_err()
&& crate::tipos::GateAtivacao::novo(1.5).is_err();
let aceita = crate::tipos::Probabilidade::novo(0.37).unwrap().valor() == 0.37;
let gate_tipado = fnh::GatePunicao::default()
.valor_tipado(&crate::tipos::Probabilidade::novo(0.9).unwrap())
.valor();
let d_ = serde_json::json!({
"versao": "criticas-v090-1.0",
"skew_index_add": {
"por_n": skew_rel,
"gates": {
"bits_iguais_todos_n": bits_todos,
},
},
"calibrar_zero_copia": {
"bits_iguais_3_passos": bits_cal,
"tempo_ref_s": t_cal_ref, "tempo_novo_s": t_cal_novo,
"tempo_tensor_s": t_cal_tens,
"speedup_zero_copia": t_cal_ref / t_cal_novo.max(1e-12),
"razao_tensor_vs_zero_copia": t_cal_tens / t_cal_novo.max(1e-12),
"gates": { "bit_a_bit": bits_cal, "nao_regride": t_cal_novo <= t_cal_ref * 1.05 },
},
"forward_canonico": {
"max_dif_valores_vs_oraculo": dif_moe,
"grad_roteador": gr, "grad_especialistas": ge,
"gates": {
"valores_iguais": dif_moe < 1e-4,
"gradiente_ativo": gr > 0.0 && ge > 0.0,
},
},
"newtypes_dominio": {
"rejeita_fora_do_dominio": rejeita,
"aceita_dentro": aceita,
"gate_tipado_em_0_9": gate_tipado,
"gates": { "fronteira_validada": rejeita && aceita },
},
});
Ok(pythonize::pythonize(py, &d_)?.into())
}