Python numpy中的广义泛函数

Python numpy中的广义泛函数,python,c,numpy,python-c-api,Python,C,Numpy,Python C Api,我正在尝试使用numpy API创建一个通用的ufunc。输入是一个(nx m)矩阵和一个标量,输出是两个矩阵((nx p)和(px m))。但我不知道怎么做。有人能帮我吗? 在init函数中,我使用带有签名的PyUFunc\u FromFuncAndDataAndSignature函数: "(n,m),()->(n,p),(p,m)" 我可以读取输入(矩阵和标量),但我想使用标量输入,就像签名中的维度p一样。可能吗 以下是仅打印输入的示例代码: #include "Python.h"

我正在尝试使用numpy API创建一个通用的ufunc。输入是一个
(nx m)
矩阵和一个标量,输出是两个矩阵(
(nx p)
(px m)
)。但我不知道怎么做。有人能帮我吗? 在init函数中,我使用带有签名的
PyUFunc\u FromFuncAndDataAndSignature
函数:

"(n,m),()->(n,p),(p,m)"
我可以读取输入(矩阵和标量),但我想使用标量输入,就像签名中的维度p一样。可能吗

以下是仅打印输入的示例代码:

#include "Python.h"
#include "math.h"
#include "numpy/ndarraytypes.h"
#include "numpy/ufuncobject.h"

static PyMethodDef nmfMethods[] = {
        {NULL, NULL, 0, NULL}
};


static void double_nmf(char **args, npy_intp *dimensions,
                            npy_intp* steps, void* data)
{
    npy_intp i, j, 
             n = dimensions[1], //dimensions of input matrix
             m = dimensions[2]; //

    printf("scalar: %d\n",*(int*)args[1]); // input scalar

    // just print input matrix
    printf("Input matrix:\n");
    for(i=0;i<n;i++){
        for(j=0;j<m;j++){
            printf("%.1f ",*(double*)(args[0]+8*(i*m+j)));
        }
    printf("\n");
    }
    return;

}

static PyUFuncGenericFunction nmf_functions[] = { double_nmf };
static void * nmf_data[] = { (void *)NULL };
static char nmf_signatures[] = { PyArray_DOUBLE, PyArray_INT, PyArray_DOUBLE, PyArray_DOUBLE };
char *nmf_signature = "(n,m),()->(n,p),(p,m)";

PyMODINIT_FUNC initnmf(void)
{
    PyObject *m, *d, *version, *nmf;

    m = Py_InitModule("nmf", nmfMethods);
    if (m == NULL) {
        return;
    }

    import_array();
    import_umath();
    d = PyModule_GetDict(m);
    version = PyString_FromString("0.1");
    PyDict_SetItemString(d, "__version__", version);
    Py_DECREF(version);

    nmf = PyUFunc_FromFuncAndDataAndSignature(nmf_functions, nmf_data, nmf_signatures, 1,
                                    2, 2, PyUFunc_None, "nmf",
                                    "", 0, nmf_signature);
    PyDict_SetItemString(d, "nmf", nmf);
    Py_DECREF(nmf);
}
终端输出为:

scalar: 2
Input matrix:
1.0 2.0 3.0 4.0 5.0 
6.0 7.0 8.0 9.0 10.0 
11.0 12.0 13.0 14.0 15.0 
16.0 17.0 18.0 19.0 20.0 
Shapes of outputs:  (4, 1) (1, 5)

我的疑问是如何使用输出矩阵的标量输入(在这种情况下为2)如维数p。在示例p=1中,我没有设置它。

谢谢@jaime的回答,帮了我很多忙。我做了你建议的改变,效果很好。 这里是C示例代码。它只是将一些输入矩阵元素复制到输出

#include "Python.h"
#include "numpy/ndarraytypes.h"
#include "numpy/ufuncobject.h"

static PyMethodDef nmfMethods[] = {
        {NULL, NULL, 0, NULL}
};


static void double_nmf(char **args, npy_intp *dimensions,
                            npy_intp* steps, void* data)
{
    npy_intp i, j,
            n = dimensions[1],
            m = dimensions[2],
            p = dimensions[3];
    char *in = args[0], *out1 = args[1], *out2 = args[2];

    for(i=0; i<n; i++){
        for(j=0; j<p; j++){
            *(double*)(out1 + 8*(j + p*i)) = *(double*)(in + 8*(j + m*i));
        }
    }
    for(i=0; i<p; i++){
        for(j=0; j<m; j++){
            *(double*)(out2 + 8*(j + m*i)) = *(double*)(in + 8*(j + m*i));
        }
    }
    return;

}

static PyUFuncGenericFunction nmf_functions[] = { double_nmf };
static void * nmf_data[] = { (void *)NULL };
static char nmf_signatures[] = { PyArray_DOUBLE, PyArray_DOUBLE, PyArray_DOUBLE };
char *nmf_signature = "(n,m)->(n,p),(p,m)";

PyMODINIT_FUNC initnmf(void)
{
    PyObject *m, *d, *version, *nmf;

    m = Py_InitModule("nmf", nmfMethods);
    if (m == NULL) {
        return;
    }

    import_array();
    import_umath();
    d = PyModule_GetDict(m);
    version = PyString_FromString("0.1");
    PyDict_SetItemString(d, "__version__", version);
    Py_DECREF(version);

    nmf = PyUFunc_FromFuncAndDataAndSignature(nmf_functions, nmf_data, nmf_signatures, 1,
                                    1, 2, PyUFunc_None, "nmf",
                                    "", 0, nmf_signature);
    PyDict_SetItemString(d, "nmf", nmf);
    Py_DECREF(nmf);
}

现在我可以继续编程了


正如@nathaniel-j-smith所建议的(这就是我得到的),我也在使用Python/CAPI(无numpy)编写非ufunc。我希望得到一些结果,并对这两种方法进行简要比较。

除了提供一定大小的数组之外,没有其他方法可以在gufunc中设置维度。
1
是所有维度在内部初始化为的值,您不应该依赖于该值不改变。我个人的观点是,未定义的维度应该引起错误

设置
p
的唯一方法是创建一个具有正确形状的空数组,并将其作为输出数组传入。要实现它,您需要重新定义您的
nmf
,使其具有签名
“(m,n)->(m,p),(p,n)”
,并使用类似于以下内容的Python对其进行包装:

def nmf_wrap(x, p):
    x = np.asarray(x)
    assert x.ndim >= 2
    shape = x.shape[:-2]
    m, n = x.shape[-2:]
    out1 = np.empty(shape+(m, p), dtype=x.dtype)
    out2 = np.empty(shape+(p, n), dtype=x.dtype)
    return nmf.nmf(x, out1, out2)

关于扩展gufuncs签名提供的功能,目前正在进行一些讨论。你所描述的与我所说的“计算维度”有一些相似之处。如果你想看到numpy 1.10中实现的东西,最好能在列表中更详细地解释你的用例:我们不知道有多少(任何?)gufunc编码器在野外

请显示原始数组、标量数组和最终数组的最小示例。遗憾的是,不可能。按照gufuncs签名解析器的工作方式,您的输出数组将始终具有
p==1
。如果需要任何其他值,实现该值的唯一方法是预先分配输出数组,并使用gufunc的
out
关键字参数将它们传入。NumPy 1.10可能会带来额外的功能,可以启用您正在尝试执行的操作。您是说希望由标量输入的值确定
p
?如果是这样的话,那么gufunc就不是你想要的了——只是做一个正则函数。gufunc的思想是它可以在参数上广播,但是假设我想传入两个不同的标量[3,4]——输出形状不能同时是(2,n,3)、(2,3,m)和(2,n,4)、(2,4,m)。(2来自输入向量的广播。)你应该用这个来编辑你的问题,而不是用额外的细节来回答。
#/usr/bin/python

import numpy as np
import nmf

def nmf_wrap(x,p):
    x = np.asarray(x)
    assert x.ndim >=2
    shape = x.shape[-2:]
    n,m = shape[-2:]
    out1 = np.empty((n, p), dtype=x.dtype)
    out2 = np.empty((p, m), dtype=x.dtype)
    return nmf.nmf(x, out1, out2)

x = np.array([[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15],[16,17,18,19,20]])
y,z = nmf_wrap(x,2)
print 'Input:\n', x
print 'Output 1:\n', y
print 'Output 2:\n', z
Input:
[[ 1  2  3  4  5]
 [ 6  7  8  9 10]
 [11 12 13 14 15]
 [16 17 18 19 20]]
Output 1:
[[ 1  2]
 [ 6  7]
 [11 12]
 [16 17]]
Output 2:
[[ 1  2  3  4  5]
 [ 6  7  8  9 10]]
def nmf_wrap(x, p):
    x = np.asarray(x)
    assert x.ndim >= 2
    shape = x.shape[:-2]
    m, n = x.shape[-2:]
    out1 = np.empty(shape+(m, p), dtype=x.dtype)
    out2 = np.empty(shape+(p, n), dtype=x.dtype)
    return nmf.nmf(x, out1, out2)