---
name: mc_random_walk_2d_gpu
kind: function
lang: cpp
domain: datascience
version: "1.0.0"
purity: impure
signature: "McRandomWalk2DGpu mc_rw2d_gpu_create(int m_walkers, int n_steps_per_run, const std::string& target_log_pdf_glsl); void mc_rw2d_gpu_reset(...); void mc_rw2d_gpu_run(...); void mc_rw2d_gpu_readback_trace(...); void mc_rw2d_gpu_readback_accepts(...); const Ssbo& mc_rw2d_gpu_trace_ssbo(const McRandomWalk2DGpu&); void mc_rw2d_gpu_destroy(...)"
description: "Random walk 2D MH paralelo en GPU. Cada thread es un walker independiente; trace_xy se almacena como float[2*N] xy-interleaved compatible directamente con gpu_histogram_2d. Para mcmc-visualizer y joint posteriors."
tags: [montecarlo, mcmc, random_walk, 2d, gpu, datascience, pendiente-usar]
uses_functions: ["gl_loader_cpp_gfx", "gpu_ssbo_cpp_gfx", "gpu_compute_program_cpp_gfx", "gpu_dispatch_cpp_gfx", "gpu_rng_glsl_cpp_gfx"]
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [GL/gl.h, GL/glext.h, vector, string, cstdio]
tested: false
tests: []
test_file_path: ""
file_path: "cpp/functions/datascience/mc_random_walk_2d_gpu.cpp"
framework: opengl
params:
  - name: m_walkers
    desc: "Numero de walkers en paralelo (1 thread por walker). Tipico 1024-65536 — saturar la GPU."
  - name: n_steps_per_run
    desc: "Steps por walker en cada llamada a run. trace_xy guarda los n_steps_per_run de cada walker."
  - name: target_log_pdf_glsl
    desc: "Snippet GLSL que define float target_log_pdf(vec2 p). Puede leer u_user[16]."
  - name: master_seed
    desc: "Semilla base, derivada via SplitMix64 a m_walkers seeds."
  - name: x0
    desc: "X inicial. Mismo para todos si initial_xys=nullptr."
  - name: y0
    desc: "Y inicial."
  - name: initial_xys
    desc: "(opcional) array m_walkers*2 floats (x0,y0,x1,y1,...). Useful para arranque disperso."
  - name: proposal_sigma
    desc: "Stddev del proposal Gaussian (mismo en X e Y). Tunear hasta accept ~0.2-0.4."
  - name: user_params
    desc: "(opcional) <= 16 floats. Sube a u_user[]."
output: "trace_xy[m_walkers * n_steps_per_run * 2] floats xy-interleaved. Layout: para walker w, step i, x = trace[(w*n + i) * 2], y = trace[(w*n + i) * 2 + 1]. accept_counts[m_walkers] uints."
---

# mc_random_walk_2d_gpu

Random walk 2D para `mcmc-visualizer` y joint posteriors (`mcmc-full`). El trace queda en GPU listo para alimentar `gpu_histogram_2d` sin readback.

## Patron tipico — densidad bimodal en vivo

```cpp
const std::string log_pdf = R"glsl(
float target_log_pdf(vec2 p) {
    // Bimodal del visualizer
    float d1 = (p.x - 2.0)*(p.x - 2.0) + (p.y - 2.0)*(p.y - 2.0);
    float d2 = (p.x + 2.0)*(p.x + 2.0) + (p.y + 2.0)*(p.y + 2.0);
    return log(exp(-0.5*d1) + exp(-0.5*d2));
}
)glsl";

auto rw = fn::ds::mc_rw2d_gpu_create(/*m=*/4096, /*n=*/256, log_pdf);
fn::ds::mc_rw2d_gpu_reset(rw, 0xC0FFEE, 0.0f, 0.0f);

auto h2d = fn::gfx::gpu_histogram_2d_create(128, 128);

// Render loop:
for (int frame = 0; frame < 60; ++frame) {
    fn::ds::mc_rw2d_gpu_run(rw, /*sigma=*/0.5f);

    // Sin readback — encadenamos al binner GPU directamente.
    int total = 4096 * 256;
    fn::gfx::gpu_histogram_2d_clear(h2d);
    fn::gfx::gpu_histogram_2d_accumulate(h2d, fn::ds::mc_rw2d_gpu_trace_ssbo(rw),
                                         total, -5.0f, 5.0f, -5.0f, 5.0f);

    std::vector<unsigned int> counts(128 * 128);
    fn::gfx::gpu_histogram_2d_readback(h2d, counts.data());

    std::vector<float> density(128 * 128);
    fn::gfx::gpu_histogram_2d_to_density(counts.data(), 128, 128, density.data());

    fn::viz::heatmap(density.data(), 128, 128, /*...*/);
}

fn::gfx::gpu_histogram_2d_destroy(h2d);
fn::ds::mc_rw2d_gpu_destroy(rw);
```

## Ventaja del pipeline GPU-only

`mc_rw2d_gpu_trace_ssbo` retorna el SSBO de la trayectoria sin readback. Pasarlo a `gpu_histogram_2d_accumulate` evita el round-trip GPU->CPU->GPU. Solo se hace readback al final, del binner ya reducido (16 KB para 64×64 grid). Para 4096 walkers × 256 steps = 1M samples, el frame entero (run + bin + readback) es <5 ms.

## Notas

- `mc_rw2d_gpu_run` emite `barrier_storage()` — adecuado para encadenar a otro compute. Si vas a hacer readback directo, llama `barrier_buffer_update()` antes (los `readback_*` ya lo hacen internamente).
- trace_xy se sobreescribe en cada run. curr_xy persiste — el walker continua. Para acumular trace de multiples runs, hacer readback entre runs y concat CPU-side, o reservar trace mas grande y escribir con offset (no incluido).
- target_log_pdf se evalua dos veces por step. Si tu pdf tiene logs/exps caros, optimizar dentro del shader (precomputar constantes en u_user, usar rsqrt nativo, etc.).
