feat(cpp/datascience): CPU stats + MCMC primitives

Nuevo dominio cpp/functions/datascience con primitivas puras CPU para post-
proceso de samples Monte Carlo y diagnostico de cadenas MCMC. Diseñadas como
gemelas CPU de los kernels GPU (rng pareja con gpu_rng_glsl, MH 1D/ND con
mc_metropolis_hastings_gpu) para validar numericamente y para datasets
pequeños donde el dispatch GPU no compensa.

- rng: xoshiro256++ con uniform / normal (Box-Muller) / below (Lemire) /
  categorical. Determinista bit-exacto dado seed.
- stats_summary: sum (Kahan), mean, var/std (Welford one-pass), min, max,
  quantile / percentile (R type-7).
- autocorr: r(k), ACF, tau_int (Sokal) — diagnostico ACF y ESS.
- rhat_ess: Gelman-Rubin clasico y split + ESS basico (multi-chain).
- beta_dist: lgamma (Lanczos), beta_pdf, beta_cdf (continued fraction),
  beta_quantile, mean/var/std — para inferencia Beta-Binomial.
- drawdown: max_dd absoluto/pct + underwater series para sesiones
  simuladas y backtests.
- samples_to_grid_2d: binning 2D CPU para alimentar heatmap_cpp_viz /
  contour_cpp_viz desde samples (x[], y[]).
- metropolis_hastings: MH 1D y ND con target log-pdf como std::function
  (no normalizada).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-04 11:52:26 +02:00
parent c74fd4ae0d
commit d76c831247
24 changed files with 1566 additions and 0 deletions
@@ -0,0 +1,93 @@
---
name: samples_to_grid_2d
kind: function
lang: cpp
domain: datascience
version: "1.0.0"
purity: pure
signature: "void samples_to_grid_2d_counts(const double* x, const double* y, size_t n, double xmin, double xmax, double ymin, double ymax, int nx, int ny, unsigned int* out_counts); void samples_to_grid_2d_density(...float* out_density); void counts_to_density(const unsigned int* counts, int nx, int ny, float* out_density)"
description: "Binning 2D CPU para alimentar heatmap_cpp_viz / contour_cpp_viz / surface_plot_3d desde un set de samples (x[], y[]). Variante counts (uint, acumulable) y density (float [0,1] normalizado a max). Pareja CPU del gpu_histogram_2d."
tags: [binning, histogram_2d, density, heatmap, contour, datascience]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: [cstddef, cmath, vector]
tested: false
tests: []
test_file_path: ""
file_path: "cpp/functions/datascience/samples_to_grid_2d.cpp"
params:
- name: samples_x
desc: "Array de coordenadas X de los samples."
- name: samples_y
desc: "Array de coordenadas Y de los samples."
- name: n
desc: "Numero de samples (longitud de samples_x y samples_y)."
- name: xmin
desc: "Limite inferior X. Samples con x fuera de [xmin, xmax) se descartan."
- name: xmax
desc: "Limite superior X."
- name: ymin
desc: "Limite inferior Y."
- name: ymax
desc: "Limite superior Y."
- name: nx
desc: "Bins en X."
- name: ny
desc: "Bins en Y."
- name: out_counts
desc: "Buffer destino unsigned int[nx*ny] row-major (idx = y*nx + x). NO se inicializa: el caller decide si poner a cero (acumular sobre llamadas previas)."
- name: out_density
desc: "Buffer destino float[nx*ny] normalizado a max=1.0. Variante density auto-rellena."
- name: counts
desc: "(counts_to_density) input uint[nx*ny]."
output: "out_counts incrementado con los conteos; out_density normalizado [0, 1]. Si todos los counts son 0, density se llena con 0."
---
# samples_to_grid_2d
Binning 2D para densidades de muestras (joint posteriors, walks 2D, scatter density). Versión CPU canónica — usar GPU `gpu_histogram_2d` cuando los samples ya viven en GPU.
## Patron tipico (mcmc-visualizer / mcmc-full)
```cpp
constexpr int NX = 128, NY = 128;
std::vector<float> density(NX * NY);
fn::ds::samples_to_grid_2d_density(
chain_x.data(), chain_y.data(), chain_x.size(),
-5.0, 5.0, -5.0, 5.0,
NX, NY,
density.data()
);
fn::viz::heatmap(density.data(), NX, NY, /*...*/);
```
## Acumulado a lo largo de iteraciones
Para visualizar la cadena creciendo en vivo sin recomputar todo:
```cpp
std::vector<unsigned int> counts(NX * NY, 0u);
// Cada iteracion del MCMC:
fn::ds::samples_to_grid_2d_counts(
new_x.data(), new_y.data(), new_x.size(),
-5.0, 5.0, -5.0, 5.0,
NX, NY,
counts.data() // sigue acumulando
);
std::vector<float> density(NX * NY);
fn::ds::counts_to_density(counts.data(), NX, NY, density.data());
fn::viz::heatmap(density.data(), NX, NY, /*...*/);
```
## Notas
- Samples fuera del rango `[xmin, xmax) x [ymin, ymax)` se descartan, no se clampean. Si quieres clamp, pre-clamp los samples antes de llamar.
- O(n) sobre samples + O(nx*ny) para to_density. Para n = 10^7, nx=ny=256: ~70 ms CPU. Para refresh interactivo a 60 FPS, downsample a n=10^5 o usar la version GPU.
- Layout row-major idx = y*nx + x — coincide con lo que esperan `heatmap_cpp_viz` y `contour_cpp_viz`.