//! `python::pensamento_demos` — 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::*; /// Classificação de erro por taxonomia (diagnóstico de refatoração). #[pyfunction] pub(crate) fn classificar_erro(msg: String) -> Vec<(String, String, String)> { diagnostico::classificar_erro(&msg) .into_iter() .map(|(a, b, c)| (a.to_string(), b.to_string(), c.to_string())) .collect() } /// Confiança de uma distribuição (T-P1): `1 − H(softmax(z))/log K`. #[pyfunction] pub(crate) fn confianca_de_logits(logits: Vec) -> PyResult { let dev = candle_core::Device::Cpu; let k = logits.len(); let t = candle_core::Tensor::from_vec(logits, (1, k), &dev) .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; pensamento::confianca_de_logits(&t).map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}"))) } /// Projeção ridge (T-P3 — montador algébrico): `r* = (WᵀW+εI)⁻¹Wᵀr`. #[pyfunction] pub(crate) fn projetar_ridge(w: Vec>, r_alvo: Vec>, eps: f64) -> PyResult>> { let dev = candle_core::Device::Cpu; let wt = tensor_de_2d(w, &dev).map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; let rt = tensor_de_2d(r_alvo, &dev).map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; let r = pensamento::projetar_ridge(&wt, &rt, eps) .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; let out = r.to_vec2::().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; Ok(out.into_iter().map(|l| l.into_iter().map(|v| v as f64).collect()).collect()) } /// Demo completa da tecnologia Pensamento: treino cooperativo das N redes /// (item a) + ciclo d com verificador externo simulado + telemetria. #[pyfunction] pub(crate) fn demo_pensamento( py: Python, d_cond: usize, d_saida: usize, n_tentativas: usize, passos_treino: usize, limiar_conf: f64, orcamento: usize, semente: u64, ) -> PyResult { use candle_core::Tensor; let dev = candle_core::Device::Cpu; crate::rng::definir_semente(semente); let mut motor = pensamento::MotorPensamento::novo(d_cond, d_saida, n_tentativas, limiar_conf, orcamento, &dev) .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; let mut otim = crate::treino::AdamWManual::novo(motor.vars(), 5e-3, 0.0); let mut rng = crate::rng::RngDeterministico::novo(semente); // dados sintéticos: alvo = função determinística não-linear de cond (d=a+b+c) let n_dados = 256usize; let conds: Vec> = (0..n_dados) .map(|_| (0..d_cond).map(|_| rng.normal(0.0, 1.0) as f32).collect()) .collect(); let alvos: Vec> = conds .iter() .map(|c| { (0..d_saida) .map(|j| { let cj = c[j % d_cond]; let ck = c[(j + 1) % d_cond]; 0.5 * cj + 0.3 * cj * ck + 0.1 }) .collect() }) .collect(); let cond_t = Tensor::from_vec(conds.into_iter().flatten().collect(), (n_dados, d_cond), &dev) .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; let alvo_t = Tensor::from_vec(alvos.into_iter().flatten().collect(), (n_dados, d_saida), &dev) .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; // item a: treino cooperativo let mut perda_hist = Vec::new(); for passo in 0..passos_treino { let perda = motor.perda_treino(&cond_t, &alvo_t) .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; perda_hist.push(perda.to_scalar::().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))? as f64); otim.passo(crate::treino::grads_achatados(&perda, &motor.vars()) .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?); let _ = passo; } // item c+d: consulta fora do padrão (OOD) — confiança baixa ⇒ Pensamento let consulta: Vec = (0..d_cond).map(|j| 3.0 + (j as f32) * 0.1).collect(); let consulta_t = Tensor::from_vec(consulta, (d_cond,), &dev) .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; // verificador externo: negativo do MSE contra o alvo verdadeiro da consulta let alvo_verdadeiro: Vec = (0..d_saida) .map(|j| { let cj = 3.0 + (j % d_cond) as f32 * 0.1; let ck = 3.0 + ((j + 1) % d_cond) as f32 * 0.1; 0.5 * cj + 0.3 * cj * ck + 0.1 }) .collect(); let alvo_verd = Tensor::from_vec(alvo_verdadeiro.clone(), (d_saida,), &dev) .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; let verifica = |resp: &Tensor| -> f64 { // confiança = 1/(1+10·MSE) — medível e causaL (só da resposta) let dif = (resp.broadcast_as((1, d_saida)).unwrap() - &alvo_verd.broadcast_as((1, d_saida)).unwrap()).unwrap(); let mse = dif.sqr().unwrap().mean_all().unwrap().to_scalar::().unwrap() as f64; 1.0 / (1.0 + 10.0 * mse) }; let mut rng2 = crate::rng::RngDeterministico::novo(semente + 1); let res = motor.raciocinar(&consulta_t, &verifica, &mut rng2) .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; // linha de base SEM pensamento: uma tentativa isolada, sem agregação let base = motor.tentativas[0].fwd(&consulta_t.broadcast_as((1, d_cond)).unwrap()) .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; let conf_base = verifica(&base); let conf_pensamento = res.confianca; let d_ = serde_json::json!({ "versao": pensamento::versao(), "n_tentativas": n_tentativas, "perda_treino_inicial": perda_hist.first(), "perda_treino_final": perda_hist.last(), "treino_aprendeu": perda_hist.last().copied().unwrap_or(f64::INFINITY) < perda_hist.first().copied().map(|p| p * 0.5).unwrap_or(f64::INFINITY), "iteracoes_ciclo_d": res.iteracoes, "confianca_base_1_rede": conf_base, "confianca_pensamento": conf_pensamento, "ganho_confianca": conf_pensamento - conf_base, "orcamento_esgotado": res.telemetria.get("orcamento_esgotado").copied().unwrap_or(0.0), "telemetria": res.telemetria, "eventos_ciclo": res.trajetoria.iter().map(|e| match e { pensamento::EventoCiclo::Planejamento { iteracao, temperatura } => serde_json::json!({"tipo": "planejamento", "iteracao": iteracao, "temperatura": temperatura}), pensamento::EventoCiclo::DistribuicaoCompetencias { por_tentativa } => serde_json::json!({"tipo": "distribuicao", "pesos": por_tentativa}), pensamento::EventoCiclo::RePlanejamento { motivo } => serde_json::json!({"tipo": "replanejamento", "motivo": motivo}), pensamento::EventoCiclo::Resposta { confianca, iteracoes } => serde_json::json!({"tipo": "resposta", "confianca": confianca, "iteracoes": iteracoes}), }).collect::>(), }); Ok(pythonize::pythonize(py, &d_)?.into()) } /// **Ciclo d do Pensamento com o VERIFICADOR NCM** (produção, sem oráculo): /// backbone CABEÇA TREINADA (alvo → classes) + verificador calibrado em /// dados retidos; N tentativas agregadas; ganho medido em consultas retidas /// EM DISTRIBUIÇÃO (onde a calibração vale — V-2) e OOD reportado à parte. #[pyfunction] pub(crate) fn demo_pensamento_ncm( d_cond: usize, d_saida: usize, n_tentativas: usize, passos_treino: usize, limiar_conf: f64, orcamento: usize, semente: u64, ) -> PyResult { Python::with_gil(|py| { use candle_core::Tensor; let dev = candle_core::Device::Cpu; crate::rng::definir_semente(semente); let mut motor = pensamento::MotorPensamento::novo( d_cond, d_saida, n_tentativas, limiar_conf, orcamento, &dev) .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; let mut otim = treino::AdamWManual::novo(motor.vars(), 5e-3, 0.0); let mut rng = crate::rng::RngDeterministico::novo(semente); // ---- dados sintéticos (item a: treino cooperativo) ---- let n_dados = 320usize; let conds: Vec> = (0..n_dados) .map(|_| (0..d_cond).map(|_| rng.normal(0.0, 1.0) as f32).collect()) .collect(); let alvos: Vec> = conds.iter().map(|c| { (0..d_saida).map(|j| { let cj = c[j % d_cond]; let ck = c[(j + 1) % d_cond]; 0.5 * cj + 0.3 * cj * ck + 0.1 }).collect() }).collect(); let mk = |c: &[f32], j: usize| -> f32 { let cj = c[j % d_cond]; let ck = c[(j + 1) % d_cond]; 0.5 * cj + 0.3 * cj * ck + 0.1 }; let cond_t = Tensor::from_vec(conds.iter().flatten().cloned().collect(), (n_dados, d_cond), &dev) .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; let alvo_t = Tensor::from_vec(alvos.iter().flatten().cloned().collect(), (n_dados, d_saida), &dev) .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; // ---- BACKBONE (cabeça treinada): alvo → classes K ---- // classe = faixa do 1º componente do alvo (aprendível, mensurável) let k_classes = 12usize; let classes: Vec = alvos .iter() .map(|a| { let v = a[0]; let f = (((v + 2.0) / 4.0) * (k_classes as f32)).floor(); (f.clamp(0.0, (k_classes - 1) as f32)) as u32 }) .collect(); let cabeca = crate::jepa::MlpJepa::novo(d_saida, 4 * d_saida, k_classes, &dev) .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; let classe_t = Tensor::from_vec(classes.clone(), (n_dados,), &dev) .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; let mut otim_cab = treino::AdamWManual::novo(cabeca.vars(), 1e-2, 0.0); for _ in 0..200 { let logits = cabeca.fwd(&alvo_t) .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; let ce = candle_nn::loss::cross_entropy(&logits, &classe_t) .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; otim_cab.passo(treino::grads_achatados(&ce, &cabeca.vars()) .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?); } // ---- VERIFICADOR calibrado em RETIDOS (metade final) ---- let n_ret = n_dados / 2; let (alvo_ret, clas_ret) = ( alvo_t.narrow(0, n_ret, n_dados - n_ret).map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?, classe_t.narrow(0, n_ret, n_dados - n_ret).map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?, ); let logits_ret = cabeca.fwd(&alvo_ret).map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; let mut verif = verificador::VerificadorNcm::novo(); let (ece_antes, ece_depois) = verif.ajustar(&logits_ret, &clas_ret) .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; // logits_fn: resposta (1, d_saida) → logits do backbone (1, K) let logits_fn = |resp: &Tensor| -> candle_core::Result { cabeca.fwd(resp) }; // ---- treino cooperativo do motor (item a) ---- let cond_tr = cond_t.narrow(0, 0, n_ret).map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; let alvo_tr = alvo_t.narrow(0, 0, n_ret).map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; let mut hist = Vec::new(); for _ in 0..passos_treino { let perda = motor.perda_treino(&cond_tr, &alvo_tr) .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; let pv = perda.to_scalar::() .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))? as f64; hist.push(pv); otim.passo(treino::grads_achatados(&perda, &motor.vars()) .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?); } // ---- consultas RETIDAS em distribuição (ganho medido AQUI) ---- let n_consultas = 8usize; let mut confs_base = Vec::new(); let mut confs_pens = Vec::new(); let mut iteracoes = Vec::new(); for qi in 0..n_consultas { let consulta: Vec = conds[n_ret + qi].clone(); let consulta_t = Tensor::from_vec(consulta, (d_cond,), &dev) .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; let mut rng2 = crate::rng::RngDeterministico::novo(semente + 1 + qi as u64); let res = motor.raciocinar_ncm(&consulta_t, &verif, &logits_fn, &mut rng2) .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; // base: 1 tentativa isolada, mesma verificação NCM let base = motor.tentativas[0] .fwd(&consulta_t.broadcast_as((1, d_cond)).unwrap()).unwrap(); let conf_base = verif.confianca_1d(&logits_fn(&base).unwrap()).unwrap(); confs_base.push(conf_base); confs_pens.push(res.confianca); iteracoes.push(res.iteracoes); } let media = |v: &[f64]| v.iter().sum::() / v.len().max(1) as f64; let (conf_base_m, conf_pens_m) = (media(&confs_base), media(&confs_pens)); let iteracoes_media = iteracoes.iter().sum::() as f64 / iteracoes.len().max(1) as f64; // ---- consulta OOD (reporte honesto — sem gate de ganho) ---- let consulta_ood: Vec = (0..d_cond).map(|j| 3.0 + (j as f32) * 0.1).collect(); let consulta_ood_t = Tensor::from_vec(consulta_ood, (d_cond,), &dev) .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; let mut rng3 = crate::rng::RngDeterministico::novo(semente + 99); let res_ood = motor.raciocinar_ncm(&consulta_ood_t, &verif, &logits_fn, &mut rng3) .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(format!("{e}")))?; let base_ood = motor.tentativas[0] .fwd(&consulta_ood_t.broadcast_as((1, d_cond)).unwrap()).unwrap(); let conf_base_ood = verif.confianca_1d(&logits_fn(&base_ood).unwrap()).unwrap(); let _ = mk; let d_ = serde_json::json!({ "versao": "pensamento-0.6.0-ncm", "n_tentativas": n_tentativas, "perda_treino_inicial": hist.first(), "perda_treino_final": hist.last(), "treino_aprendeu": hist.last().copied() < hist.first().map(|p| p * 0.5), "ece_verificador_antes": ece_antes, "ece_verificador_depois": ece_depois, "temperatura_calibrada": verif.temperatura, "iteracoes_ciclo_d_media": iteracoes_media, "confianca_base_1_rede": conf_base_m, "confianca_pensamento": conf_pens_m, "ganho_confianca": conf_pens_m - conf_base_m, "confianca_ood_base": conf_base_ood, "confianca_ood_pensamento": res_ood.confianca, "iteracoes_ood": res_ood.iteracoes, "orcamento_esgotado_ood": res_ood.telemetria.get("orcamento_esgotado").copied().unwrap_or(0.0), "verificador": "ncm-calibrado (sem oraculo)", "gates": { "gate_verificador_sem_oraculo": true, "gate_calibrado": ece_depois <= ece_antes, "gate_ganho_nao_negativo": conf_pens_m >= conf_base_m - 1e-3, "gate_terminou": iteracoes_media <= orcamento as f64, }, }); Ok(pythonize::pythonize(py, &d_)?.into()) }) } /// **Autoajuste do Pensamento por telemetria (A-1..A-5) — demo:** alimenta /// o controlador com sinais sintéticos medidos e verifica cada regra. #[pyfunction] pub(crate) fn demo_autoajuste(semente: u64) -> PyResult { Python::with_gil(|py| { let mut hub = telemetria::TelemetryHub::novo(None); let mut rng = crate::rng::RngDeterministico::novo(semente); let mut auto = controlador::AutoAjustePensamento::novo(0.5, 8, 2); // A-1: confianças com mistura (60% altas ~0.85–0.95, 40% baixas ~0.25–0.40) // ⇒ quantil 0.75 cai no grupo alto (posições 0.6–1.0) ≈ 0.85+0.375·0.1 ≈ 0.89 for _ in 0..200 { let c = if rng.uniforme() < 0.6 { 0.85 + rng.uniforme() * 0.1 } else { 0.25 + rng.uniforme() * 0.15 }; auto.confs_retidas.push(c); } let tau = auto.ajustar_tau_c(0.25, &mut hub); // A-3: acordos altos (0.88 ⇒ p̂ = (1+√0.76)/2 ≈ 0.936 ⇒ N = ln(20)/(2·0.436²) ≈ 7.9 ⇒ 8) for _ in 0..32 { auto.acordos.push(0.88); } let n_ajustado = auto.ajustar_n(0.05, 16, &mut hub); // A-5: ganhos estáveis positivos (n=64 ⇒ 3º termo Bernstein ≈ 0.14 < 0.30) ⇒ gate sobe for _ in 0..64 { auto.ajustar_gate(0.30, 0.05, &mut hub); } let g_final = auto.gate_g; // A-2: ganhos decaindo geometricamente for k in 0..12 { auto.ganhos_por_iteracao.push(0.25 * 0.6f64.powi(k)); } let b_ajustado = auto.ajustar_orcamento(0.02, &mut hub); // A-4: UCB1 com 3 braços let mut u = controlador::Ucb1::novo(vec!["n1".into(), "n4_moe".into(), "n4".into()]); for _ in 0..200 { let braco = u.escolher(); let media = match braco { 0 => 0.35, 1 => 0.80, _ => 0.55 }; let r = (media + rng.normal(0.0, 0.06) as f64).clamp(0.0, 1.0); u.registrar(braco, r); } let medias = u.medias(); let melhor = medias.iter().enumerate().max_by(|a, b| a.1.partial_cmp(b.1).unwrap()).map(|(i, _)| i).unwrap_or(0); let d_ = serde_json::json!({ "versao": "autoajuste-0.6.0", "tau_c_final": tau, "n_tentativas_final": n_ajustado, "gate_final": g_final, "orcamento_b_final": b_ajustado, "ajustes_auditados": hub.eventos_ajuste.len(), "ucb1_medias": medias, "ucb1_melhor_braco": u.nomes[melhor], "ucb1_arrependimento_medio": u.arrependimento_empirico() / 200.0, "gates": { "gate_a1_tau_quantil": (tau - 0.89).abs() < 0.08, "gate_a3_n_reduzido": n_ajustado <= 8, "gate_a5_gate_subiu": g_final > 0.0, "gate_a2_b_finito": b_ajustado >= 1 && b_ajustado <= 16, "gate_a4_ucb1_achou": u.nomes[melhor] == "n4_moe", }, }); Ok(pythonize::pythonize(py, &d_)?.into()) }) }