I have tried to understand Clyther. I have failed because the code is quite 
complex. But I have some understanding of the AST, so I was able to implement 
my own python -> C compiler which makes OpenCL friendly code and works with 
pyOpenCL.

The idea is that of writing kernels in Python, not C, and adding a decorator to 
compile them to C99.

The code can be found at https://github.com/mdipierro/mdpcl and it is very 
short (all in one file, device.py and one example file).
It requires pyopencl, https://github.com/srossross/Meta, matplotlib and 
https://github.com/mdipierro/canvas

Here for example is a 2D laplace solver in pure python (with pyopencl of course)

from device import Device
from canvas import Canvas
import random
import numpy

n = 300
q = numpy.zeros((n,n), dtype=numpy.float32)
u = numpy.zeros((n,n), dtype=numpy.float32)
w = numpy.zeros((n,n), dtype=numpy.float32)

for k in range(n):
    q[random.randint(1,n-1),random.randint(1,n-1)] = random.choice((-1,+1))

device = Device()
q_buffer = device.buffer(source=q, mode=device.flags.READ_ONLY)
u_buffer = device.buffer(source=u)
w_buffer = device.buffer(source=w)

@device.define('kernel',
               w='global:ptr_float',
               u='global:const:ptr_float',
               q='global:const:ptr_float')
def solve(w,u,q):
    x = new_int(get_global_id(0))
    y = new_int(get_global_id(1))
    n = new_int(get_global_size(0))
    site = new_int(x*n+y)
    if y!=0 and y!=n-1 and x!=0 and x!=n-1:
        up = new_int(site-n)
        down = new_int(site+n)
        left = new_int(site-1)
        right = new_int(site+1)
        w[site] = 1.0/4*(u[up]+u[down]+u[left]+u[right] - q[site])

program = device.compile(device.define.code)

for k in range(3000):
    program.solve(device.queue, [n,n], None, w_buffer, u_buffer, q_buffer)
    (u_buffer, w_buffer) = (w_buffer, u_buffer)
    
u = device.retrieve(u_buffer,shape=(n,n))
Canvas(title='').imshow(u).save(filename='plot.png')


Notice the solve function. It is converted at runtime to the following C99 code:


"""
__kernel void solve(__global float* w, __global const float* u, __global const 
float* q);

__kernel void solve(__global float* w, __global const float* u, __global const 
float* q) {
    int right;
    int site;
    int up;
    int down;
    int y;
    int x;
    int n;
    int left;
    x = get_global_id(0);
    y = get_global_id(1);
    n = get_global_size(0);
    site = ((x * n) + y);
    if(((y != 0) && ((y != (n - 1)) && ((x != 0) && (x != (n - 1)))))) {
        up = (site - n);
        down = (site + n);
        left = (site - 1);
        right = (site + 1);
        w[site] = ((1.0 / 4) * ((((u[up] + u[down]) + u[left]) + u[right]) - 
q[site]));
    }
}
""""

If a function is decorated with @device.define(…) it means that all functions 
called inside that function are defined on the opencl side. If the decorator 
says kernel it adds the "__kernel" modifier. You can decorate more than one 
function and they are all compiled together. Variables must be declared with 
new_int, new_float, new_float2, etc.

It seems to work but I could use some feedback.

Massimo
_______________________________________________
PyOpenCL mailing list
[email protected]
http://lists.tiker.net/listinfo/pyopencl

Reply via email to