package tutorial; import com.nativelibs4java.opencl.*; import com.nativelibs4java.opencl.CLPlatform.DeviceFeature; import com.nativelibs4java.util.*; import java.io.IOException; import java.nio.DoubleBuffer; import org.bridj.Pointer; public class DFT { final CLQueue queue; final CLContext context; final CLProgram program; final CLKernel kernel; public DFT(CLQueue queue) throws IOException, CLBuildException { this.queue = queue; this.context = queue.getContext(); String source = IOUtils.readText(DFT.class.getResource("DiscreteFourierTransformProgram.cl")); program = context.createProgram(source); kernel = program.createKernel("dft"); } /** * Method that takes complex values in input (sequence of pairs of real and imaginary values) and * returns the Discrete Fourier Transform of these values if forward == true or the inverse * transform if forward == false. */ public synchronized Pointer<Double> dft(Pointer<Double> in, boolean forward) { assert in.getValidElements() % 2 == 0; int length = (int)in.getValidElements() / 2; // Create an input CLBuffer that will be a copy of the NIO buffer : CLBuffer<Double> inBuf = context.createDoubleBuffer(CLMem.Usage.Input, in, true); // true = copy // Create an output CLBuffer : CLBuffer<Double> outBuf = context.createDoubleBuffer(CLMem.Usage.Output, length * 2); // Set the args of the kernel : kernel.setArgs(inBuf, outBuf, length, forward ? 1 : -1); // Ask for `length` parallel executions of the kernel in 1 dimension : CLEvent dftEvt = kernel.enqueueNDRange(queue, new int[]{ length }); // Return an NIO buffer read from the output CLBuffer : return outBuf.read(queue, dftEvt); } /// Wrapper method that takes and returns double arrays public double[] dft(double[] complexValues, boolean forward) { Pointer<Double> outBuffer = dft(Pointer.pointerToDoubles(complexValues), forward); return outBuffer.getDoubles(); } public static void main(String[] args) throws IOException, CLBuildException { // Create a context with the best double numbers support possible : // (try using DeviceFeature.GPU, DeviceFeature.CPU...) CLContext context = JavaCL.createBestContext(DeviceFeature.DoubleSupport); // Create a command queue, if possible able to execute multiple jobs in parallel // (out-of-order queues will still respect the CLEvent chaining) CLQueue queue = context.createDefaultOutOfOrderQueueIfPossible(); DFT dft = new DFT(queue); //DFT2 dft = new DFT2(queue); // Create some fake test data : double[] in = createTestDoubleData(); // Transform the data (spatial -> frequency transform) : double[] transformed = dft.dft(in, true); for (int i = 0; i < transformed.length / 2; i++) { // Print the transformed complex values (real + i * imaginary) System.out.println(transformed[i * 2] + "\t + \ti * " + transformed[i * 2 + 1]); } // Reverse-transform the transformed data (frequency -> spatial transform) : double[] backTransformed = dft.dft(transformed, false); // Check the transform + inverse transform give the original data back : double precision = 1e-5; for (int i = 0; i < in.length; i++) { if (Math.abs(in[i] - backTransformed[i]) > precision) throw new RuntimeException("Different values in back-transformed array than in original array !"); } } static double[] createTestDoubleData() { int n = 32; double[] in = new double[2 * n]; for (int i = 0; i < n; i++) { in[i * 2] = 1 / (double) (i + 1); in[i * 2 + 1] = 0; } return in; } }