从Python调用快速C Mersenne Twister实现(SFMT)

时间:2017-02-25 16:44:38

标签: random python-c-api mersenne-twister

我正试图从Python调用SFMT Mersenne Twister实现(在http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/找到)。我这样做是因为我希望能够从具有4个概率的离散pdf中快速采样。我正在编写一些模拟,这是我代码中最慢的一部分。

我设法编写了一些有效的C代码,并使用SFMT算法创建的[0,1]中的随机数对输入PDF进行采样。

然而我不知道如何在从Python 调用时正确初始化SFMT随机数生成器。我当然只想初始化它一次,然后我需要将用于初始化它的结构的地址(sfmt)传递给我对sfmt_genrand_real1的调用。

因此,一些示例代码将是:

// Create a struct which stores the Mersenne Twister's state
sfmt_t sfmt;

// Initialise the MT with a random seed from the system time
// NOTE: Only want to do this once
int seed = time(NULL);
sfmt_init_gen_rand(&sfmt, seed);

// Get a random number
double random_number = sfmt_genrand_real1(&sfmt);

问题是我不知道如何在从Python调用此代码时仅为SFMT随机数生成器播种一次。如果我只是用C编写所有内容,我会在main()函数中进行初始化,然后将&sfmt参数传递给所有后续的sfmt_genrand_real1()调用。

但是因为我正在编译这个C代码,然后从Python调用它,我不能初始化该变量一次。就目前而言,我已经在sfmt_genrand_real1()调用之前停留了初始化,因为这是我能够让代码甚至编译并且能够在随机调用中访问sfmt变量的唯一方法数字生成器。我试图以某种方式使sfmt变量“全局”适得其反。

所以我的问题是:有没有办法初始化C SFMT随机数生成器一次,然后在sfmt的所有后续调用中访问c_random_sample struct用于初始化来自Python?

非常感谢任何可以提供帮助的人。

这是我的C代码。 (要编译,您需要将所有SMFT .c.h文件放在同一文件夹中,然后使用python setup.py build_ext --inplace进行编译。

#include "Python.h"
#include <stdio.h>
#include <time.h>
#include "SFMT.h"


static double*
get_double_array(PyObject* data)
{
    int i, size;
    double* out;
    PyObject* seq;

    seq = PySequence_Fast(data, "expected a sequence");
    if (!seq)
        return NULL;

    size = PySequence_Size(seq);
    if (size < 0)
        return NULL;

    out = (double*) PyMem_Malloc(size * sizeof(double));
    if (!out) {
        Py_DECREF(seq);
        PyErr_NoMemory();
        return NULL;
    }

    for (i=0; i<size; i++) {
        out[i] = PyFloat_AsDouble(PySequence_Fast_GET_ITEM(seq, i));
    }

    Py_DECREF(seq);

    if (PyErr_Occurred()) {
        PyMem_Free(out);
        out = NULL;
    }

    return out;
}


static PyObject*
c_random_sample(PyObject* self, PyObject* args)
{
    int i;
    double* pdf;

    PyObject* pdf_in;

    if (!PyArg_ParseTuple(args, "O:c_random_sample", &pdf_in))
        return NULL;

    pdf = get_double_array(pdf_in);
    if (!pdf)
        return NULL;

    // Create a struct which stores the Mersenne Twister's state
    sfmt_t sfmt;
    int seed = time(NULL);

    // Initialise the MT with a random seed from the system time
    sfmt_init_gen_rand(&sfmt, seed);
    // NOTE: This simply re-initialises the random number generator
    // on every call. We need to only initialise it once...

    double r = sfmt_genrand_real1(&sfmt);
    for (i=0; i<4; i++) {
        r -= pdf[i];
        if (r < 0) {
            break;
        }
     }
     PyMem_Free(pdf);
     return PyInt_FromLong(i);
}


static PyMethodDef functions[] = {
    {"c_random_sample", c_random_sample, METH_VARARGS},
    {NULL, NULL}
};


PyMODINIT_FUNC initc_random_sample(void)
{
    Py_InitModule4(
        "c_random_sample", functions, "Trying to implement random number sampling in C", NULL, PYTHON_API_VERSION
    );
}

2 个答案:

答案 0 :(得分:0)

sfmt位于C:

的全球范围内
static sfmt_t sfmt; /* outside any functions */

初始化进入模块init函数 - 当模块首次导入Python时,调用一次:

PyMODINIT_FUNC initc_random_sample(void)
{
    /* Py_InitModule4 to initialize the module as before */ 

    int seed = time(NULL);
    sfmt_init_gen_rand(&sfmt, seed);
}

答案 1 :(得分:0)

这并不是你想要的,但如果你不介意依赖(主要是Numpy和Cython),你会发现有用的ng-numpy-randomstate库有很多快速随机数生成器(特别是dSFMT)。

如果你可以使用它,它将为你节省编写C包装器的工作。

此外,此模块(或至少某些功能?)将来可能会在Numpy中合并(参见Numpy open issue #6967)。