2014-10-09 26 views
3

我想使用numpy API進行泛化的ufunc。輸入是一個(n x m)矩陣和一個標量,輸出是兩個矩陣((n x p)(p x m))。但我不知道該怎麼做。有人可以幫助我嗎? 在初始化函數我使用PyUFunc_FromFuncAndDataAndSignature功能與簽名:numpy中的廣義通用函數

"(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); 
} 

此代碼編譯和作品。的Python腳本是在這裏:

#/usr/bin/python 

import numpy as np 
import nmf 

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.nmf(x,2) 
print "Shapes of outputs: ", y.shape, z.shape 

和端子輸出爲:

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中,我沒有設置它。

+0

請顯示原始數組,標量和最終數組的最小示例。 – wwii 2014-10-09 18:57:23

+1

不幸的是,這是不可能的。通過gufuncs的簽名解析器工作,你的輸出數組總是有'p == 1'。如果你需要其他值,唯一的方法就是預先分配輸出數組,並使用你的gufunc的'out'關鍵字參數傳遞它們。 NumPy 1.10可能會帶來額外的功能,可能使你想要做的事情。 – Jaime 2014-10-09 19:13:22

+3

你是說你想'p'由標量輸入的*值*決定嗎?如果是這樣,那麼你一個gufunc不是你想要的 - 只是做一個常規功能。 gufunc的想法是它可以在參數上進行廣播,但是假設我想傳遞兩個不同的標量[3,4] - 輸出形狀不能是(2,n,3),(2,3) ,m)和(2,n,4),(2,4,m)。 (2來自廣播輸入矢量。) – 2014-10-09 19:57:09

回答

2

除了提供一定大小的數組之外,沒有辦法在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簽名on the numpy-dev mailing list recently提供的功能的一些正在進行的討論。你所描述的與我所謂的「計算維度」有一些相似之處。如果你希望看到在1.10版本中實現的東西,那麼如果你能夠更詳細地解釋你的用例,那就太好了:我們不知道很多(任何?)gufunc編碼器在外面!

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); 
} 

這裏是Python示例代碼和終端輸出。

#/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]] 

現在我可以繼續程序。

我還使用Python/C API(無numpy)編寫了非ufunc,就像@ nathaniel-j-smith的建議(這就是我所知道的)。我希望得到一些結果,並簡要比較兩種方法。

+1

你應該用這個編輯你的問題,而不是用額外的細節來回答它。 – Jaime 2014-10-09 23:08:41