Created
August 2, 2013 02:48
-
-
Save cgranade/6137168 to your computer and use it in GitHub Desktop.
This module demonstrates the use of OpenCL to accelerate sequential Monte Carlo
by implementing a simple cosine-likelihood model as an OpenCL kernel. When run
as a script, this module then compares the performance of the OpenCL-accelerated
model to the pure-NumPy model implemented in the QInfer project.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# -*- coding: utf-8 -*- | |
## | |
# cl_cos_model.py: Demonstration of OpenCL-powered SMC. | |
## | |
# © 2013 Christopher E. Granade (cgranade@gmail.com). | |
# | |
# Licensed under the AGPL version 3. | |
## | |
# This program is free software: you can redistribute it and/or modify | |
# it under the terms of the GNU Affero General Public License as published by | |
# the Free Software Foundation, either version 3 of the License, or | |
# (at your option) any later version. | |
# | |
# This program is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
# GNU Affero General Public License for more details. | |
# | |
# You should have received a copy of the GNU Affero General Public License | |
# along with this program. If not, see <http://www.gnu.org/licenses/>. | |
## | |
""" | |
This module demonstrates the use of OpenCL to accelerate sequential Monte Carlo | |
by implementing a simple cosine-likelihood model as an OpenCL kernel. When run | |
as a script, this module then compares the performance of the OpenCL-accelerated | |
model to the pure-NumPy model implemented in the QInfer project. | |
""" | |
## FEATURES #################################################################### | |
from __future__ import division | |
## IMPORTS ##################################################################### | |
import pyopencl as cl | |
import numpy as np | |
import numpy.linalg as la | |
import time | |
from qinfer.abstract_model import Model | |
from qinfer.test_models import SimplePrecessionModel | |
from qinfer.smc import SMCUpdater | |
from qinfer.distributions import UniformDistribution | |
## KERNELS ##################################################################### | |
OCL_SOURCE = """ | |
__kernel void cos_model( | |
int n_experiments, | |
__global const float *models, | |
__global const float *expparams, | |
__global float *likelihoods | |
) { | |
// Assuming two-outcome model, and finding Pr(0 | model; expparams). | |
int idx_model = get_global_id(0); | |
int idx_experiment = get_global_id(1); | |
likelihoods[idx_model * n_experiments + idx_experiment] = pow(cospi( | |
models[idx_model] * expparams[idx_experiment] / 2 | |
), 2); | |
} | |
""" | |
## CLASSES ##################################################################### | |
class AcceleratedPrecessionModel(Model): | |
r""" | |
Reimplementation of `qinfer.test_models.SimplePrecessionModel`, using OpenCL | |
to accelerate computation. | |
""" | |
def __init__(self): | |
super(AcceleratedPrecessionModel, self).__init__() | |
self._ctx = cl.create_some_context() | |
self._queue = cl.CommandQueue(self._ctx) | |
self._prg = cl.Program(self._ctx, OCL_SOURCE).build() | |
## PROPERTIES ## | |
@property | |
def n_modelparams(self): | |
return 1 | |
@property | |
def expparams_dtype(self): | |
return 'float' | |
@property | |
def is_n_outcomes_constant(self): | |
""" | |
Returns ``True`` if and only if the number of outcomes for each | |
experiment is independent of the experiment being performed. | |
This property is assumed by inference engines to be constant for | |
the lifetime of a Model instance. | |
""" | |
return True | |
## METHODS ## | |
@staticmethod | |
def are_models_valid(modelparams): | |
return modelparams > 0 | |
def n_outcomes(self, expparams): | |
""" | |
Returns an array of dtype ``uint`` describing the number of outcomes | |
for each experiment specified by ``expparams``. | |
:param numpy.ndarray expparams: Array of experimental parameters. This | |
array must be of dtype agreeing with the ``expparams_dtype`` | |
property. | |
""" | |
return 2 | |
def likelihood(self, outcomes, modelparams, expparams): | |
# By calling the superclass implementation, we can consolidate | |
# call counting there. | |
super(AcceleratedPrecessionModel, self).likelihood(outcomes, modelparams, expparams) | |
# Possibly add a second axis to modelparams. | |
if len(modelparams.shape) == 1: | |
modelparams = modelparams[..., np.newaxis] | |
# Convert to float32 if needed. | |
mps = modelparams.astype(np.float32) | |
eps = expparams.astype(np.float32) | |
# Allocating a buffer for the pr0 returns. | |
pr0 = np.empty((mps.shape[0], eps.shape[0]), dtype=mps.dtype) | |
# Move buffers to the GPU. | |
mf = cl.mem_flags | |
mps_buf = cl.Buffer(self._ctx, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=mps) | |
eps_buf = cl.Buffer(self._ctx, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=eps) | |
dest_buf = cl.Buffer(self._ctx, mf.WRITE_ONLY, pr0.nbytes) | |
# Run the kernel with global worksize (n_models, n_experiments). | |
self._prg.cos_model(self._queue, pr0.shape, None, np.int32(eps.shape[0]), mps_buf, eps_buf, dest_buf) | |
# Copy the buffer back from the GPU and free memory there. | |
cl.enqueue_copy(self._queue, pr0, dest_buf) | |
mps_buf.release() | |
eps_buf.release() | |
dest_buf.release() | |
# Now we concatenate over outcomes. | |
return Model.pr0_to_likelihood_array(outcomes, pr0) | |
## SCRIPT ###################################################################### | |
if __name__ == "__main__": | |
for model in [AcceleratedPrecessionModel(), SimplePrecessionModel()]: | |
true = np.random.random(1) | |
updater = SMCUpdater(model, 100000, UniformDistribution([0, 1])) | |
tic = time.time() | |
for idx_exp in xrange(200): | |
if not (idx_exp % 20): | |
print idx_exp | |
expparams = np.array([(9 / 8) ** idx_exp]) | |
updater.update(model.simulate_experiment(true, expparams), expparams) | |
print model, updater.est_mean(), true, time.time() - tic | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment