As a simple example of a Monte Carlo method, we will approximate the value of $\pi$:
import numpy as np
import pyopencl as cl
import pyopencl.array
import pyopencl.clrandom
ctx = cl.create_some_context()
queue = cl.CommandQueue(ctx)
generator_preamble = """
#include <pyopencl-random123/philox.cl>
typedef union {
uint4 v;
philox4x32_ctr_t c;
} philox4x32_ctr_vec_union;
uint4 philox4x32_bump(uint4 ctr)
{
if (++ctr.x == 0)
if (++ctr.y == 0)
++ctr.z;
return ctr;
}
uint4 philox4x32_gen(
uint4 ctr,
uint2 key,
uint4 *new_ctr)
{
philox4x32_ctr_vec_union result;
result.c = philox4x32(
*(philox4x32_ctr_t *) &ctr,
*(philox4x32_key_t *) &key);
*new_ctr = philox4x32_bump(ctr);
return result.v;
}
float4 philox4x32_f32(
uint4 ctr,
uint2 key,
uint4 *new_ctr)
{
*new_ctr = ctr;
return
convert_float4(philox4x32_gen(*new_ctr, key, new_ctr))
* 2.3283064365386963e-10f;
}
"""
Complete the sampler code:
mc_preamble_src = """
#include <pyopencl-complex.h>
float compute_sample(int i, unsigned int k1)
{
uint4 ctr = { 0, 1, 2, 3 };
uint2 key2 = { i, k1 };
float4 rng_res = philox4x32_f32(ctr, key2, &(ctr));
...
}
"""
mc_preamble_src = """
#include <pyopencl-complex.h>
float compute_sample(int i, unsigned int k1)
{
uint4 ctr = { 0, 1, 2, 3 };
uint2 key2 = { i, k1 };
float4 rng_res = philox4x32_f32(ctr, key2, &(ctr));
cfloat_t samp0 = cfloat_new(rng_res.s0, rng_res.s1);
cfloat_t samp1 = cfloat_new(rng_res.s2, rng_res.s3);
float result = 0;
if (cfloat_abs(samp0) <= 1)
result += 1;
if (cfloat_abs(samp1) <= 1)
result += 1;
return result;
}
"""
from pyopencl.reduction import ReductionKernel
Syntax:
ReductionKernel(context, dtype, netural, reduce_expr, map_expr, arguments)
rknl = ReductionKernel(ctx, np.float32,
neutral="0",
reduce_expr="a+b", map_expr="compute_sample(i, k1)",
arguments="unsigned int k1", preamble=generator_preamble+mc_preamble_src)
n = 100000000
nsamples_accepted = rknl(15, range=slice(n), queue=queue).get()
nsamples = 2*n
approx_pi = 4 * nsamples_accepted/nsamples
print(approx_pi)