Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a49d627

Browse files
- added documentation for generalized ufuncs
- added matrix multiply generalized ufunc for Rational
1 parent 35a1e3b commit a49d627

2 files changed

Lines changed: 184 additions & 6 deletions

File tree

doc/ufuncs-draft.txt

Lines changed: 106 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ object representing the sum of those two rational numbers:
1414

1515
static NPY_INLINE rational
1616
rational_add(rational x, rational y) {
17+
// d(y) retrieves denominator for y
1718
return make_rational_fast((int64_t)x.n*d(y) + (int64_t)d(x)*y.n, (int64_t)d(x)*d(y));
1819
}
1920

@@ -89,7 +90,7 @@ with the following arguments:
8990

9091
int types[] = {npy_rational,npy_rational,npy_rational};
9192

92-
if (PyUFunc_RegisterLoopForType((PyUFuncObject*)ufunc,npy_rational,rational_ufunc_biggest,types,0) < 0) {
93+
if (PyUFunc_RegisterLoopForType((PyUFuncObject*)ufunc,npy_rational,rational_ufunc_add,types,0) < 0) {
9394
return;
9495
}
9596

@@ -128,8 +129,8 @@ The function parameters are:
128129
Creating New UFunc for Custom DType
129130

130131
The next example shows how to create a new ufunc for the Rational dtype. The ufunc
131-
example is called 'numerator' and generates an array of numerator values based
132-
rational numbers from the input array.
132+
example creates a ufunc called 'numerator' which generates an array of numerator
133+
values based rational numbers from the input array.
133134

134135

135136
1. A 1-d loop function is created as before which takes the numerator value from
@@ -170,7 +171,7 @@ You can also use the c MACRO provided in Rational for generating the above funct
170171
PyObject* ufunc = PyUFunc_FromFuncAndData(0,0,0,0,1,1,PyUFunc_None,(char*)"numerator",(char*)"rational number numerator",0);
171172

172173
In this use case, the first four parameters should be set to zero since we're
173-
creating a new ufunc instead of extending an existing one. The rest of the
174+
creating a new ufunc without support for existing dtypes. The rest of the
174175
parameters:
175176

176177
- number of inputs to function that the loop function calls for each pair of elements
@@ -214,4 +215,105 @@ parameters:
214215
NEW_UNARY_UFUNC(numerator,NPY_INT64,"rational number numerator");
215216

216217

218+
Creating New Generalized UFunc for Custom DType
219+
220+
The next example shows how to create a new generalized ufunc for the Rational dtype.
221+
The gufunc example creates a gufunc 'matrix_multiply' which loops over a pair of
222+
vectors or matrices and performs a matrix multiply on each pair of matrix elements.
223+
224+
225+
1. A loop function is created to loop through the outer or loop dimensions, performing a
226+
matrix multiply operation on the core dimensions for each loop:
227+
228+
static void
229+
rational_gufunc_matrix_multiply(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
230+
{
231+
// outer dimensions counter
232+
npy_intp N_;
233+
234+
// length of flattened outer dimensions
235+
npy_intp dN = dimensions[0];
236+
237+
// striding over flattened outer dimensions for input and output arrays
238+
npy_intp s0 = steps[0];
239+
npy_intp s1 = steps[1];
240+
npy_intp s2 = steps[2];
241+
242+
// loop through outer dimensions, performing matrix multiply on core dimensions for each loop
243+
for (N_ = 0; N_ < dN; N_++, args[0] += s0, args[1] += s1, args[2] += s2) {
244+
rational_matrix_multiply(args, dimensions+1, steps+3);
245+
}
246+
}
247+
248+
If the input matrices have more than one outer dimension, the outer dimensions are flattened from the
249+
perspective of the loop function.
250+
251+
The matrix multiply function:
252+
253+
static NPY_INLINE void
254+
rational_matrix_multiply(char **args, npy_intp *dimensions, npy_intp *steps)
255+
{
256+
// pointers to data for input and output arrays
257+
char *ip1 = args[0];
258+
char *ip2 = args[1];
259+
char *op = args[2];
260+
261+
// lengths of core dimensions
262+
npy_intp dm = dimensions[0];
263+
npy_intp dn = dimensions[1];
264+
npy_intp dp = dimensions[2];
265+
266+
// striding over core dimensions
267+
npy_intp is1_m = steps[0];
268+
npy_intp is1_n = steps[1];
269+
npy_intp is2_n = steps[2];
270+
npy_intp is2_p = steps[3];
271+
npy_intp os_m = steps[4];
272+
npy_intp os_p = steps[5];
273+
274+
// core dimensions counters
275+
npy_intp m, n, p;
276+
277+
// calculate dot product for each row/column vector pair
278+
for (m = 0; m < dm; m++) {
279+
for (p = 0; p < dp; p++) {
280+
npyrational_dot(ip1, is1_n, ip2, is2_n, op, dn, NULL);
281+
282+
ip2 += is2_p;
283+
op += os_p;
284+
}
285+
286+
ip2 -= is2_p * p;
287+
op -= os_p * p;
288+
289+
ip1 += is1_m;
290+
op += os_m;
291+
}
292+
}
293+
294+
295+
2. In the 'initrational' function used to initialize the Rational dtype with numpy,
296+
a new PyUFuncObject is created for the new 'matrix_multiply' generalized ufunc using the
297+
PyUFunc_FromFuncAndDataAndSignature function:
298+
299+
PyObject* gufunc = PyUFunc_FromFuncAndDataAndSignature(0,0,0,0,2,1,PyUFunc_None,(char*)"matrix_multiply",(char*)"return result of multiplying two matrices of rationals",0,"(m,n),(n,p)->(m,p)");
300+
301+
This is identical to the PyUFunc_FromFuncAndData function used to create a ufunc object in the examples above,
302+
with the addition of a ufunc signature describing the core dimensions of the input and output arrays. In this example,
303+
the generalized ufunc operates on pairs of matrices with dimensions (m,n) and (n,p), producing an
304+
output matrix of dimensions (m,p).
305+
306+
3. The loop function is registered using the loop function and the PyUFuncObject created in step 2:
307+
308+
int types2[3] = {npy_rational,npy_rational,npy_rational};
309+
if (PyUFunc_RegisterLoopForType((PyUFuncObject*)gufunc,npy_rational,rational_gufunc_matrix_multiply,types2,0) < 0) {
310+
return;
311+
}
312+
313+
4. Finally, a function called 'matrix_multiply' is added to the rational module which
314+
will call the numerator ufunc:
315+
316+
317+
PyModule_AddObject(m,"matrix_multiply",(PyObject*)gufunc);
318+
217319

npytypes/rational/rational.c

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44

55
#include <stdint.h>
66
#include <math.h>
7-
#include <Python/Python.h>
8-
#include <Python/structmember.h>
7+
//#include <Python/Python.h>
8+
//#include <Python/structmember.h>
9+
#include <Python.h>
10+
#include <structmember.h>
911
#include <numpy/arrayobject.h>
1012
#include <numpy/ufuncobject.h>
1113

@@ -952,6 +954,69 @@ UNARY_UFUNC(reciprocal,rational,rational_inverse(x))
952954
UNARY_UFUNC(numerator,int64_t,x.n)
953955
UNARY_UFUNC(denominator,int64_t,d(x))
954956

957+
static NPY_INLINE void
958+
rational_matrix_multiply(char **args, npy_intp *dimensions, npy_intp *steps)
959+
{
960+
// pointers to data for input and output arrays
961+
char *ip1 = args[0];
962+
char *ip2 = args[1];
963+
char *op = args[2];
964+
965+
// lengths of core dimensions
966+
npy_intp dm = dimensions[0];
967+
npy_intp dn = dimensions[1];
968+
npy_intp dp = dimensions[2];
969+
970+
// striding over core dimensions
971+
npy_intp is1_m = steps[0];
972+
npy_intp is1_n = steps[1];
973+
npy_intp is2_n = steps[2];
974+
npy_intp is2_p = steps[3];
975+
npy_intp os_m = steps[4];
976+
npy_intp os_p = steps[5];
977+
978+
// core dimensions counters
979+
npy_intp m, n, p;
980+
981+
// calculate dot product for each row/column vector pair
982+
for (m = 0; m < dm; m++) {
983+
for (p = 0; p < dp; p++) {
984+
npyrational_dot(ip1, is1_n, ip2, is2_n, op, dn, NULL);
985+
986+
ip2 += is2_p;
987+
op += os_p;
988+
}
989+
990+
ip2 -= is2_p * p;
991+
op -= os_p * p;
992+
993+
ip1 += is1_m;
994+
op += os_m;
995+
}
996+
}
997+
998+
999+
static void
1000+
rational_gufunc_matrix_multiply(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
1001+
{
1002+
// outer dimensions counter
1003+
npy_intp N_;
1004+
1005+
// length of flattened outer dimensions
1006+
npy_intp dN = dimensions[0];
1007+
1008+
// striding over flattened outer dimensions for input and output arrays
1009+
npy_intp s0 = steps[0];
1010+
npy_intp s1 = steps[1];
1011+
npy_intp s2 = steps[2];
1012+
1013+
// loop through outer dimensions, performing matrix multiply on core dimensions for each loop
1014+
for (N_ = 0; N_ < dN; N_++, args[0] += s0, args[1] += s1, args[2] += s2) {
1015+
rational_matrix_multiply(args, dimensions+1, steps+3);
1016+
}
1017+
}
1018+
1019+
9551020
PyMethodDef module_methods[] = {
9561021
{0} /* sentinel */
9571022
};
@@ -1088,6 +1153,17 @@ initrational(void) {
10881153
Py_INCREF(&PyRational_Type);
10891154
PyModule_AddObject(m,"rational",(PyObject*)&PyRational_Type);
10901155

1156+
/* Create matrix multiply generalized ufunc */
1157+
PyObject* gufunc = PyUFunc_FromFuncAndDataAndSignature(0,0,0,0,2,1,PyUFunc_None,(char*)"matrix_multiply",(char*)"return result of multiplying two matrices of rationals",0,"(m,n),(n,p)->(m,p)");
1158+
if (!gufunc) {
1159+
return;
1160+
}
1161+
int types2[3] = {npy_rational,npy_rational,npy_rational};
1162+
if (PyUFunc_RegisterLoopForType((PyUFuncObject*)gufunc,npy_rational,rational_gufunc_matrix_multiply,types2,0) < 0) {
1163+
return;
1164+
}
1165+
PyModule_AddObject(m,"matrix_multiply",(PyObject*)gufunc);
1166+
10911167
/* Create numerator and denominator ufuncs */
10921168
#define NEW_UNARY_UFUNC(name,type,doc) { \
10931169
PyObject* ufunc = PyUFunc_FromFuncAndData(0,0,0,0,1,1,PyUFunc_None,(char*)#name,(char*)doc,0); \

0 commit comments

Comments
 (0)