@@ -1,4 +1,5 @@
#include <Python.h>
#include <structmember.h>
#include <numpy/arrayobject.h>
#include "kernels.h"
#include "gp.h"
@@ -7,54 +8,11 @@
using namespace Eigen;


#if PY_MAJOR_VERSION >= 3
#define IS_PY3K
#endif
//
// Shortcut function for parsing input numpy arrays and vectors.
//


static char module_doc[] = "GP Module";
static char evaluate_doc[] = "Evaluate a GP.";


static PyObject *gp_evaluate(PyObject *self, PyObject *args);


static PyMethodDef module_methods[] = {
{"evaluate", gp_evaluate, METH_VARARGS, evaluate_doc},
{NULL, NULL, 0, NULL}
};


#ifdef IS_PY3K
#define INITERROR return NULL

static struct PyModuleDef moduledef =
{PyModuleDef_HEAD_INIT, "_gp", module_doc, -1, module_methods, };

extern "C" PyObject *PyInit__gp(void)
{
PyObject *m = PyModule_Create(&moduledef);

#else
#define INITERROR return

extern "C" void init_gp(void)
{
PyObject *m = Py_InitModule3("_gp", module_methods, module_doc);
#endif

if (m == NULL)
INITERROR;

import_array();

#ifdef IS_PY3K
return m;
#endif
}

template <class T>
int nparray2matrix(PyObject *obj, T *m)
template <class T> int nparray2matrix(PyObject *obj, T *m)
{
// Load the array.
PyObject *array = PyArray_FROM_OTF(obj, NPY_DOUBLE, NPY_IN_ARRAY);
@@ -90,57 +48,94 @@ int nparray2matrix(PyObject *obj, T *m)
}


static PyObject *gp_evaluate(PyObject *self, PyObject *args)
//
// The ``_gp`` type definition.
//

typedef struct {
PyObject_HEAD
GaussianProcess gp;
} _gp;

static void _gp_dealloc(_gp *self)
{
self->ob_type->tp_free((PyObject*)self);
}

static PyObject *_gp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
_gp *self;

self = (_gp*)type->tp_alloc(type, 0);

return (PyObject *)self;
}

static int _gp_init(_gp *self, PyObject *args, PyObject *kwds)
{
PyObject *xobj, *yobj, *yerrobj, *x0obj, *parsobj;
PyObject *parsobj = NULL;
int kerneltype;
if (!PyArg_ParseTuple(args, "OOOOOi", &xobj, &yobj, &yerrobj, &x0obj,
&parsobj, &kerneltype))
return NULL;

// Parse the input numpy arrays and cast them as Eigen objects.
MatrixXd x, x0;
VectorXd y, yerr, pars;
nparray2matrix<MatrixXd>(xobj, &x);
nparray2matrix<VectorXd>(yobj, &y);
nparray2matrix<VectorXd>(yerrobj, &yerr);
nparray2matrix<MatrixXd>(x0obj, &x0);
nparray2matrix<VectorXd>(parsobj, &pars);
if (!PyArg_ParseTuple(args, "Oi", &parsobj, &kerneltype))
return -1;

VectorXd pars;
nparray2matrix<VectorXd>(parsobj, &pars);
if (PyErr_Occurred() != NULL)
return NULL;
return -1;

// Which type of kernel?
double (*k)(VectorXd, VectorXd, VectorXd);
if (kerneltype == 1) {
if (pars.rows() != 2) {
PyErr_SetString(PyExc_RuntimeError,
"The isotropic kernel requires 2 parameters.");
return NULL;
return -1;
}

k = isotropicKernel;
} else if (kerneltype == 2) {
if (pars.rows() != 1 + x.cols()) {
PyErr_SetString(PyExc_RuntimeError,
"The diagonal kernel requires 1 + ndim parameters.");
return NULL;
}

/* if (pars.rows() != 1 + x.cols()) { */
/* PyErr_SetString(PyExc_RuntimeError, */
/* "The diagonal kernel requires 1 + ndim parameters."); */
/* return -1; */
/* } */
k = diagonalKernel;
} else {
PyErr_SetString(PyExc_RuntimeError, "Unknown kernel type.");
return NULL;
return -1;
}

// Run the inference.
VectorXd mean(1);
MatrixXd variance(1, 1);
self->gp = GaussianProcess(pars, k);

return 0;
}

static PyMemberDef _gp_members[] = {
{NULL} /* Sentinel */
};

GaussianProcess gp(pars, k);
static PyObject *_gp_fit(_gp *self, PyObject *args)
{
PyObject *xobj, *yobj, *yerrobj;
if (!PyArg_ParseTuple(args, "OOO", &xobj, &yobj, &yerrobj))
return NULL;

int ret = gp.fit(x, y, yerr);
if (ret == -1) {
// Parse the input numpy arrays and cast them as Eigen objects.
MatrixXd x;
VectorXd y, yerr;
nparray2matrix<MatrixXd>(xobj, &x);
nparray2matrix<VectorXd>(yobj, &y);
nparray2matrix<VectorXd>(yerrobj, &yerr);
if (PyErr_Occurred() != NULL)
return NULL;

// Do the fit.
int ret = self->gp.fit(x, y, yerr);
if (ret == 1 || ret == 2) {
PyErr_SetString(PyExc_TypeError, "Dimension mismatch.");
return NULL;
} if (ret == -1) {
PyErr_SetString(PyExc_RuntimeError, "Couldn't factorize K.");
return NULL;
} else if (ret == -2) {
@@ -151,39 +146,138 @@ static PyObject *gp_evaluate(PyObject *self, PyObject *args)
return NULL;
}

double lnlike = gp.evaluate();
Py_INCREF(Py_None);
return Py_None;
}

static PyObject *_gp_predict(_gp *self, PyObject *args)
{
PyObject *xobj;
if (!PyArg_ParseTuple(args, "O", &xobj))
return NULL;

// Parse the input numpy arrays and cast them as Eigen objects.
MatrixXd x;
nparray2matrix<MatrixXd>(xobj, &x);
if (PyErr_Occurred() != NULL)
return NULL;

ret = gp.predict(x0, &mean, &variance);
if (ret != 0) {
PyErr_SetString(PyExc_RuntimeError, "Couldn't solve for variance.");
// Do the prediction.
VectorXd mu(1);
MatrixXd cov(1, 1);
int ret = self->gp.predict(x, &mu, &cov);
if (ret == -1) {
PyErr_SetString(PyExc_RuntimeError, "You have to fit the GP first.");
return NULL;
} else if (ret != 0) {
PyErr_SetString(PyExc_RuntimeError, "Couldn't solve for the mean/covariance.");
return NULL;
}

// Build the empty numpy arrays.
int n = mean.rows();
// Build the empty numpy arrays for output.
int n = mu.rows();
npy_intp dim[] = {n};
PyObject *mean_array = PyArray_EMPTY(1, dim, NPY_DOUBLE, 0);
PyObject *mu_array = PyArray_EMPTY(1, dim, NPY_DOUBLE, 0);

npy_intp dims[] = {n, n};
PyObject *variance_array = PyArray_EMPTY(2, dims, NPY_DOUBLE, 0);
PyObject *cov_array = PyArray_EMPTY(2, dims, NPY_DOUBLE, 0);

// Fill the data pointer.
double *mean_data = (double*)PyArray_DATA(mean_array);
double *variance_data = (double*)PyArray_DATA(variance_array);
// Fill in the output data.
double *mu_data = (double*)PyArray_DATA(mu_array);
double *cov_data = (double*)PyArray_DATA(cov_array);
for (int i = 0; i < n; ++i) {
mean_data[i] = mean[i];
mu_data[i] = mu[i];
for (int j = 0; j < n; ++j)
variance_data[i * n + j] = variance(i, j);
cov_data[i * n + j] = cov(i, j);
}

// Build the full output tuple.
PyObject *tuple_out = Py_BuildValue("NNd", mean_array, variance_array,
lnlike);
PyObject *tuple_out = Py_BuildValue("NN", mu_array, cov_array);
if (tuple_out == NULL) {
Py_DECREF(mean_array);
Py_DECREF(variance_array);
Py_DECREF(mu_array);
Py_DECREF(cov_array);
return NULL;
}

return tuple_out;
}

static PyObject *_gp_evaluate(_gp *self)
{
double lnlike = self->gp.evaluate();
PyObject *tuple_out = Py_BuildValue("d", lnlike);
return tuple_out;
}

static PyMethodDef _gp_methods[] = {
{"fit", (PyCFunction)_gp_fit, METH_VARARGS, "Fit the GP."},
{"evaluate", (PyCFunction)_gp_evaluate, METH_NOARGS, "Evaluate the GP."},
{"predict", (PyCFunction)_gp_predict, METH_VARARGS, "Predict new values."},
{NULL} /* Sentinel */
};

static PyTypeObject _gp_type = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"_gp._gp", /*tp_name*/
sizeof(_gp), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)_gp_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
"The _gp object", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
_gp_methods, /* tp_methods */
_gp_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)_gp_init, /* tp_init */
0, /* tp_alloc */
_gp_new, /* tp_new */
};


//
// Initialize the module.
//

static char module_doc[] = "GP Module";
static PyMethodDef module_methods[] = {{NULL}};
extern "C" void init_gp(void)
{
PyObject *m;

if (PyType_Ready(&_gp_type) < 0)
return;

m = Py_InitModule3("_gp", module_methods, module_doc);
if (m == NULL)
return;

Py_INCREF(&_gp_type);
PyModule_AddObject(m, "_gp", (PyObject *)&_gp_type);

import_array();
}