summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjaimefrio <jaime.frio@gmail.com>2014-10-15 23:16:51 -0700
committerjaimefrio <jaime.frio@gmail.com>2014-10-15 23:16:58 -0700
commit140c50537087dccc764cd7540b46b039cb934530 (patch)
tree434923c99a0c3ffd01e6ad9ac7b23a1cbf632ab7
parent3a827637c9eca604315491e8e017b72f2d1f8837 (diff)
downloadnumpy-140c50537087dccc764cd7540b46b039cb934530.tar.gz
TST: Stricter checks for gufunc signatures
Added euclidean_pdist to umath/umath_tests.c.src. Modified tests to reflect new, stricter gufunc signature behavior.
-rw-r--r--numpy/core/src/umath/umath_tests.c.src137
-rw-r--r--numpy/core/tests/test_ufunc.py28
2 files changed, 112 insertions, 53 deletions
diff --git a/numpy/core/src/umath/umath_tests.c.src b/numpy/core/src/umath/umath_tests.c.src
index 46d06288d..e5bf5aa07 100644
--- a/numpy/core/src/umath/umath_tests.c.src
+++ b/numpy/core/src/umath/umath_tests.c.src
@@ -38,6 +38,10 @@
INIT_OUTER_LOOP_3 \
npy_intp s3 = *steps++;
+#define BEGIN_OUTER_LOOP_2 \
+ for (N_ = 0; N_ < dN; N_++, args[0] += s0, args[1] += s1) {
+
+
#define BEGIN_OUTER_LOOP_3 \
for (N_ = 0; N_ < dN; N_++, args[0] += s0, args[1] += s1, args[2] += s2) {
@@ -58,13 +62,14 @@ char *inner1d_signature = "(i),(i)->()";
/**begin repeat
#TYPE=LONG,DOUBLE#
- #typ=npy_long, npy_double#
+ #typ=npy_long,npy_double#
*/
/*
* This implements the function
* out[n] = sum_i { in1[n, i] * in2[n, i] }.
*/
+
static void
@TYPE@_inner1d(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
{
@@ -91,7 +96,7 @@ char *innerwt_signature = "(i),(i),(i)->()";
/**begin repeat
#TYPE=LONG,DOUBLE#
- #typ=npy_long, npy_double#
+ #typ=npy_long,npy_double#
*/
@@ -127,7 +132,7 @@ char *matrix_multiply_signature = "(m,n),(n,p)->(m,p)";
/**begin repeat
#TYPE=FLOAT,DOUBLE,LONG#
- #typ=npy_float, npy_double,npy_long#
+ #typ=npy_float,npy_double,npy_long#
*/
/*
@@ -135,7 +140,6 @@ char *matrix_multiply_signature = "(m,n),(n,p)->(m,p)";
* out[k, m, p] = sum_n { in1[k, m, n] * in2[k, n, p] }.
*/
-
static void
@TYPE@_matrix_multiply(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
{
@@ -177,27 +181,66 @@ static void
/**end repeat**/
-/* The following lines were generated using a slightly modified
- version of code_generators/generate_umath.py and adding these
- lines to defdict:
-
-defdict = {
-'inner1d' :
- Ufunc(2, 1, None_,
- r'''inner on the last dimension and broadcast on the rest \n"
- " \"(i),(i)->()\" \n''',
- TD('ld'),
- ),
-'innerwt' :
- Ufunc(3, 1, None_,
- r'''inner1d with a weight argument \n"
- " \"(i),(i),(i)->()\" \n''',
- TD('ld'),
- ),
-}
+char *euclidean_pdist_signature = "(n,d)->(p)";
+
+/**begin repeat
+ #TYPE=FLOAT,DOUBLE#
+ #typ=npy_float,npy_double#
+ #sqrt_func=sqrtf,sqrt#
*/
+/*
+ * This implements the function
+ * out[j*(2*n-3-j)+k-1] = sum_d { (in1[j, d] - in1[k, d])^2 }
+ * with k > j > n, i.e. computes all unique pairwise euclidean distances.
+ */
+
+static void
+@TYPE@_euclidean_pdist(char **args, npy_intp *dimensions, npy_intp *steps,
+ void *NPY_UNUSED(func))
+{
+ INIT_OUTER_LOOP_2
+ npy_intp len_n = *dimensions++;
+ npy_intp len_d = *dimensions++;
+ npy_intp len_p = *dimensions;
+ npy_intp stride_n = *steps++;
+ npy_intp stride_d = *steps++;
+ npy_intp stride_p = *steps;
+
+ assert(len_n * (len_n - 1) / 2 == len_p);
+
+ BEGIN_OUTER_LOOP_2
+ const char *data_this = (const char *)args[0];
+ char *data_out = args[1];
+ npy_intp n;
+ for (n = 0; n < len_n; ++n) {
+ const char *data_that = data_this + stride_n;
+ npy_intp nn;
+ for (nn = n + 1; nn < len_n; ++nn) {
+ const char *ptr_this = data_this;
+ const char *ptr_that = data_that;
+ @typ@ out = 0;
+ npy_intp d;
+ for (d = 0; d < len_d; ++d) {
+ const @typ@ delta = *(const @typ@ *)ptr_this -
+ *(const @typ@ *)ptr_that;
+ out += delta * delta;
+ ptr_this += stride_d;
+ ptr_that += stride_d;
+ }
+ *(@typ@ *)data_out = @sqrt_func@(out);
+ data_that += stride_n;
+ data_out += stride_p;
+ }
+ data_this += stride_n;
+ }
+ END_OUTER_LOOP
+}
+
+/**end repeat**/
+
+
static PyUFuncGenericFunction inner1d_functions[] = { LONG_inner1d, DOUBLE_inner1d };
static void * inner1d_data[] = { (void *)NULL, (void *)NULL };
static char inner1d_signatures[] = { NPY_LONG, NPY_LONG, NPY_LONG, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE };
@@ -208,39 +251,49 @@ static PyUFuncGenericFunction matrix_multiply_functions[] = { LONG_matrix_multip
static void *matrix_multiply_data[] = { (void *)NULL, (void *)NULL, (void *)NULL };
static char matrix_multiply_signatures[] = { NPY_LONG, NPY_LONG, NPY_LONG, NPY_FLOAT, NPY_FLOAT, NPY_FLOAT, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE };
+static PyUFuncGenericFunction euclidean_pdist_functions[] =
+ { FLOAT_euclidean_pdist, DOUBLE_euclidean_pdist };
+static void *eucldiean_pdist_data[] = { (void *)NULL, (void *)NULL };
+static char euclidean_pdist_signatures[] = { NPY_FLOAT, NPY_FLOAT,
+ NPY_DOUBLE, NPY_DOUBLE };
+
+
static void
addUfuncs(PyObject *dictionary) {
PyObject *f;
- f = PyUFunc_FromFuncAndDataAndSignature(inner1d_functions, inner1d_data, inner1d_signatures, 2,
- 2, 1, PyUFunc_None, "inner1d",
- "inner on the last dimension and broadcast on the rest \n"\
- " \"(i),(i)->()\" \n",
- 0, inner1d_signature);
+ f = PyUFunc_FromFuncAndDataAndSignature(inner1d_functions, inner1d_data,
+ inner1d_signatures, 2, 2, 1, PyUFunc_None, "inner1d",
+ "inner on the last dimension and broadcast on the rest \n"
+ " \"(i),(i)->()\" \n",
+ 0, inner1d_signature);
PyDict_SetItemString(dictionary, "inner1d", f);
Py_DECREF(f);
- f = PyUFunc_FromFuncAndDataAndSignature(innerwt_functions, innerwt_data, innerwt_signatures, 2,
- 3, 1, PyUFunc_None, "innerwt",
- "inner1d with a weight argument \n"\
- " \"(i),(i),(i)->()\" \n",
- 0, innerwt_signature);
+ f = PyUFunc_FromFuncAndDataAndSignature(innerwt_functions, innerwt_data,
+ innerwt_signatures, 2, 3, 1, PyUFunc_None, "innerwt",
+ "inner1d with a weight argument \n"
+ " \"(i),(i),(i)->()\" \n",
+ 0, innerwt_signature);
PyDict_SetItemString(dictionary, "innerwt", f);
Py_DECREF(f);
f = PyUFunc_FromFuncAndDataAndSignature(matrix_multiply_functions,
- matrix_multiply_data, matrix_multiply_signatures,
- 3, 2, 1, PyUFunc_None, "matrix_multiply",
- "matrix multiplication on last two dimensions \n"\
- " \"(m,n),(n,p)->(m,p)\" \n",
- 0, matrix_multiply_signature);
+ matrix_multiply_data, matrix_multiply_signatures,
+ 3, 2, 1, PyUFunc_None, "matrix_multiply",
+ "matrix multiplication on last two dimensions \n"
+ " \"(m,n),(n,p)->(m,p)\" \n",
+ 0, matrix_multiply_signature);
PyDict_SetItemString(dictionary, "matrix_multiply", f);
Py_DECREF(f);
+ f = PyUFunc_FromFuncAndDataAndSignature(euclidean_pdist_functions,
+ eucldiean_pdist_data, euclidean_pdist_signatures,
+ 2, 1, 1, PyUFunc_None, "euclidean_pdist",
+ "pairwise euclidean distance on last two dimensions \n"
+ " \"(n,d)->(p)\" \n",
+ 0, euclidean_pdist_signature);
+ PyDict_SetItemString(dictionary, "euclidean_pdist", f);
+ Py_DECREF(f);
}
-/*
- End of auto-generated code.
-*/
-
-
static PyObject *
UMath_Tests_test_signature(PyObject *NPY_UNUSED(dummy), PyObject *args)
diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py
index eacc266be..135059dd7 100644
--- a/numpy/core/tests/test_ufunc.py
+++ b/numpy/core/tests/test_ufunc.py
@@ -388,21 +388,18 @@ class TestUfunc(TestCase):
msg = "extend & broadcast loop dimensions"
b = np.arange(4).reshape((2, 2))
assert_array_equal(umt.inner1d(a, b), np.sum(a*b, axis=-1), err_msg=msg)
- msg = "broadcast in core dimensions"
+ # Broadcast in core dimensions
a = np.arange(8).reshape((4, 2))
b = np.arange(4).reshape((4, 1))
- assert_array_equal(umt.inner1d(a, b), np.sum(a*b, axis=-1), err_msg=msg)
- msg = "extend & broadcast core and loop dimensions"
+ assert_raises(ValueError, umt.inner1d, a, b)
+ # Extend core dimensions
a = np.arange(8).reshape((4, 2))
b = np.array(7)
- assert_array_equal(umt.inner1d(a, b), np.sum(a*b, axis=-1), err_msg=msg)
- msg = "broadcast should fail"
+ assert_raises(ValueError, umt.inner1d, a, b)
+ # Broadcast should fail"
a = np.arange(2).reshape((2, 1, 1))
b = np.arange(3).reshape((3, 1, 1))
- try:
- ret = umt.inner1d(a, b)
- assert_equal(ret, None, err_msg=msg)
- except ValueError: None
+ assert_raises(ValueError, umt.inner1d, a, b)
def test_type_cast(self):
msg = "type cast"
@@ -542,8 +539,8 @@ class TestUfunc(TestCase):
a2 = d2.transpose(p2)[s2]
ref = ref and a1.base != None
ref = ref and a2.base != None
- if broadcastable(a1.shape[-1], a2.shape[-2]) and \
- broadcastable(a1.shape[0], a2.shape[0]):
+ if (a1.shape[-1] == a2.shape[-2] and
+ broadcastable(a1.shape[0], a2.shape[0])):
assert_array_almost_equal(
umt.matrix_multiply(a1, a2),
np.sum(a2[..., np.newaxis].swapaxes(-3, -1) *
@@ -553,6 +550,15 @@ class TestUfunc(TestCase):
assert_equal(ref, True, err_msg="reference check")
+ def test_euclidean_pdist(self):
+ a = np.arange(12, dtype=np.float).reshape(4, 3)
+ out = np.empty((a.shape[0] * (a.shape[0] - 1) // 2,), dtype=a.dtype)
+ umt.euclidean_pdist(a, out)
+ b = np.sqrt(np.sum((a[:, None] - a)**2, axis=-1))
+ b = b[~np.tri(a.shape[0], dtype=bool)]
+ assert_almost_equal(out, b)
+ assert_raises(ValueError, umt.euclidean_pdist ,a)
+
def test_object_logical(self):
a = np.array([3, None, True, False, "test", ""], dtype=object)
assert_equal(np.logical_or(a, None),