summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--numpy/core/include/numpy/ndarraytypes.h2
-rw-r--r--numpy/core/include/numpy/ufuncobject.h43
-rw-r--r--numpy/core/src/multiarray/arraytypes.c.src162
-rw-r--r--numpy/core/src/multiarray/datetime.c56
-rw-r--r--numpy/core/src/umath/ufunc_object.c266
-rw-r--r--numpy/core/src/umath/umathmodule.c.src32
-rw-r--r--numpy/core/tests/test_datetime.py29
7 files changed, 294 insertions, 296 deletions
diff --git a/numpy/core/include/numpy/ndarraytypes.h b/numpy/core/include/numpy/ndarraytypes.h
index 17e739380..0b07ad762 100644
--- a/numpy/core/include/numpy/ndarraytypes.h
+++ b/numpy/core/include/numpy/ndarraytypes.h
@@ -809,7 +809,7 @@ typedef int (PyArray_FinalizeFunc)(PyArrayObject *, PyObject *);
/*
* Size of internal buffers used for alignment Make BUFSIZE a multiple
- * of sizeof(cdouble) -- ususally 16 so that ufunc buffers are aligned
+ * of sizeof(cdouble) -- usually 16 so that ufunc buffers are aligned
*/
#define NPY_MIN_BUFSIZE ((int)sizeof(cdouble))
#define NPY_MAX_BUFSIZE (((int)sizeof(cdouble))*1000000)
diff --git a/numpy/core/include/numpy/ufuncobject.h b/numpy/core/include/numpy/ufuncobject.h
index 34cd72707..ae8f06827 100644
--- a/numpy/core/include/numpy/ufuncobject.h
+++ b/numpy/core/include/numpy/ufuncobject.h
@@ -9,7 +9,41 @@ extern "C" {
typedef void (*PyUFuncGenericFunction) (char **, npy_intp *, npy_intp *, void *);
-typedef struct {
+/* Forward declaration for the type resolution function */
+struct _tagPyUFuncObject;
+
+/*
+ * Given the operands for calling a ufunc, should determine the
+ * calculation input and output data types and return an inner loop function.
+ * This function should validate that the casting rule is being followed,
+ * and fail if it is not.
+ *
+ * ufunc: The ufunc object.
+ * casting: The 'casting' parameter provided to the ufunc.
+ * operands: An array of length (ufunc->nin + ufunc->nout),
+ * with the output parameters possibly NULL.
+ * type_tup: Either NULL, or the type_tup passed to the ufunc.
+ * out_dtypes: An array which should be populated with new
+ * references to (ufunc->nin + ufunc->nout) new
+ * dtypes, one for each input and output.
+ * out_innerloop: Should be populated with the correct ufunc inner
+ * loop for the given type.
+ * out_innerloopdata: Should be populated with the void* data to
+ * be passed into the out_innerloop function.
+ *
+ * Should return 0 on success, -1 on failure (with exception set),
+ * or -2 if Py_NotImplemented should be returned.
+ */
+typedef int (PyUFunc_TypeResolutionFunc)(
+ struct _tagPyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes,
+ PyUFuncGenericFunction *out_innerloop,
+ void **out_innerloopdata);
+
+typedef struct _tagPyUFuncObject {
PyObject_HEAD
/*
* nin: Number of inputs
@@ -70,6 +104,13 @@ typedef struct {
int *core_offsets;
/* signature string for printing purpose */
char *core_signature;
+
+ /*
+ * A function which resolves the types and returns an inner loop.
+ * This is used by the regular ufunc, the reduction operations
+ * have a different set of rules.
+ */
+ PyUFunc_TypeResolutionFunc *type_resolution_function;
} PyUFuncObject;
#include "arrayobject.h"
diff --git a/numpy/core/src/multiarray/arraytypes.c.src b/numpy/core/src/multiarray/arraytypes.c.src
index ac3da4558..e4fe2c000 100644
--- a/numpy/core/src/multiarray/arraytypes.c.src
+++ b/numpy/core/src/multiarray/arraytypes.c.src
@@ -782,28 +782,6 @@ fail:
/*
- * Return a Python Datetime Object from a number representing the number of
- * units since the epoch (1970-01-01T00:00:00Z) ignoring leap seconds.
- */
-
-NPY_NO_EXPORT PyObject *
-PyDateTime_FromNormalized(npy_datetime val, NPY_DATETIMEUNIT base)
-{
- npy_datetimestruct ydate;
-
- /* Must be here to use PyDateTime_FromDateAndTime */
- PyDateTime_IMPORT;
-
- /* We just truncate the unused variables and don't wory about overflow */
- PyArray_DatetimeToDatetimeStruct(val, base, &ydate);
-
- /* FIXME?: We discard ydate.ns, ydate.ps, ydate.fs, and ydate.as */
- return PyDateTime_FromDateAndTime(ydate.year, ydate.month, ydate.day,
- ydate.hour, ydate.min, ydate.sec,
- ydate.us);
-}
-
-/*
* We also can lose precision and range here. Ignored.
* Don't use this function if you care.
*/
@@ -820,53 +798,6 @@ PyTimeDelta_FromNormalized(npy_timedelta val, NPY_DATETIMEUNIT base)
return PyDelta_FromDSU(td.day, td.sec, td.us);
}
-
-NPY_NO_EXPORT PyObject *
-PyDateTime_FromInt64(datetime val, PyArray_Descr *descr)
-{
- PyArray_DatetimeMetaData *meta;
-
- meta = PyDataType_GetDatetimeMetaData(descr);
- if (meta == NULL) {
- PyErr_SetString(PyExc_RuntimeError,
- "metadata not set for descriptor");
- return NULL;
- }
-
- if (meta->events > 1) {
- int events, rem, div;
- PyObject *obj;
-
- obj = PyTuple_New(2);
- events = meta->events;
- div = val/events;
- rem = val % events;
- PyTuple_SET_ITEM(obj, 1, PyInt_FromLong(rem));
- /* This resets meta->events for recursive call */
- meta->events = 1;
- PyTuple_SET_ITEM(obj, 0, PyDateTime_FromInt64(div, descr));
- meta->events = events;
- if (PyErr_Occurred()) {
- Py_DECREF(obj);
- return NULL;
- }
- return obj;
- }
-
- /*
- * We normalize the number to a base-unit and then return a
- * Python Datetime Object
- *
- * FIXME? : We silently truncate if it doesn't fit, either too
- * wide (e.g. 10 BC) or too narrow (nanoseconds)
- */
-
- /* Normalization and then conversion to Datetime */
- /* FIXME? : Check for Overflow... */
- return PyDateTime_FromNormalized(val*meta->num, meta->base);
-}
-
-
NPY_NO_EXPORT PyObject *
PyTimeDelta_FromInt64(timedelta val, PyArray_Descr *descr)
{
@@ -902,46 +833,6 @@ PyTimeDelta_FromInt64(timedelta val, PyArray_Descr *descr)
return PyTimeDelta_FromNormalized(val*meta->num, meta->base);
}
-
-
-NPY_NO_EXPORT npy_datetime
-PyDateTime_AsNormalized(PyObject *obj, NPY_DATETIMEUNIT base)
-{
- npy_datetimestruct ydate;
-
- /* Must be here to use PyDateTime_FromDateAndTime */
- PyDateTime_IMPORT;
-
- if (!PyDateTime_Check(obj) && !PyDate_Check(obj)) {
- PyErr_SetString(PyExc_ValueError,
- "Must be a datetime.date or datetime.datetime object");
- return -1;
- }
-
- ydate.year = PyDateTime_GET_YEAR(obj);
- ydate.month = PyDateTime_GET_MONTH(obj);
- ydate.day = PyDateTime_GET_DAY(obj);
-
- if (PyDateTime_Check(obj)) {
- ydate.hour = PyDateTime_DATE_GET_HOUR(obj);
- ydate.min = PyDateTime_DATE_GET_MINUTE(obj);
- ydate.sec = PyDateTime_DATE_GET_SECOND(obj);
- ydate.us = PyDateTime_DATE_GET_MICROSECOND(obj);
- }
- else {
- ydate.hour = 0;
- ydate.min = 0;
- ydate.sec = 0;
- ydate.us = 0;
- }
-
- ydate.ps = 0;
- ydate.as = 0;
-
- /* We just truncate the unused variables and don't wory about overflow */
- return PyArray_DatetimeStructToDatetime(base, &ydate);
-}
-
NPY_NO_EXPORT npy_timedelta
PyTimeDelta_AsNormalized(PyObject *obj, NPY_DATETIMEUNIT base)
{
@@ -964,59 +855,6 @@ PyTimeDelta_AsNormalized(PyObject *obj, NPY_DATETIMEUNIT base)
return PyArray_TimedeltaStructToTimedelta(base, &td);
}
-
-/*
- * These expect a 2-tuple if meta->events > 1 (baseobj, num-counts)
- * where baseobj is a datetime object or a timedelta object respectively.
- *
- */
-
-NPY_NO_EXPORT npy_datetime
-PyDateTime_AsInt64(PyObject *obj, PyArray_Descr *descr)
-{
- PyArray_DatetimeMetaData *meta;
- npy_datetime res;
-
- meta = PyDataType_GetDatetimeMetaData(descr);
- if (meta == NULL) {
- PyErr_SetString(PyExc_RuntimeError,
- "metadata not set for descriptor");
- return -1;
- }
-
-
- if (meta->events > 1) {
- datetime tmp;
- int events;
-
- if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 2) {
- PyErr_SetString(PyExc_ValueError,
- "need a 2-tuple on setting if events > 1");
- return -1;
- }
- /* Alter the dictionary and call again */
- /* FIXME: not thread safe */
- events = meta->events;
- meta->events = 1;
- tmp = PyDateTime_AsInt64(PyTuple_GET_ITEM(obj, 0), descr);
- meta->events = events;
- if (PyErr_Occurred()) {
- return -1;
- }
- /* FIXME: Check for overflow */
- tmp *= events;
- tmp += MyPyLong_AsLongLong(PyTuple_GET_ITEM(obj, 1));
- if (PyErr_Occurred()) {
- return -1;
- }
- return tmp;
- }
-
- res = PyDateTime_AsNormalized(obj, meta->base);
- return res/meta->num;
-}
-
-
NPY_NO_EXPORT timedelta
PyTimeDelta_AsInt64(PyObject *obj, PyArray_Descr *descr)
{
diff --git a/numpy/core/src/multiarray/datetime.c b/numpy/core/src/multiarray/datetime.c
index 0d1ed563e..a73e55e6c 100644
--- a/numpy/core/src/multiarray/datetime.c
+++ b/numpy/core/src/multiarray/datetime.c
@@ -1852,7 +1852,8 @@ datetimestruct_timezone_offset(npy_datetimestruct *dts, int minutes)
* + Doesn't (yet) handle the "YYYY-DDD" or "YYYY-Www" formats.
* + Doesn't handle leap seconds (seconds value has 60 in these cases).
* + Doesn't handle 24:00:00 as synonym for midnight (00:00:00) tomorrow
- * + Accepts special values "NaT" (not a time), "Today", and "Now".
+ * + Accepts special values "NaT" (not a time), "Today", (current
+ * day according to local time) and "Now" (current time in UTC).
*
* 'str' must be a NULL-terminated string, and 'len' must be its length.
*
@@ -1915,7 +1916,7 @@ parse_iso_8601_date(char *str, int len, npy_datetimestruct *out)
return 0;
}
- /* The string "now" resolves to the current time */
+ /* The string "now" resolves to the current UTC time */
if (len == 3 && tolower(str[0]) == 'n' &&
tolower(str[1]) == 'o' &&
tolower(str[2]) == 'w') {
@@ -2163,10 +2164,53 @@ parse_iso_8601_date(char *str, int len, npy_datetimestruct *out)
parse_timezone:
if (sublen == 0) {
- /* TODO: In this case, ISO 8601 states to treat
- * it as a local time, but we are leaving
- * it as a UTC time for now.
- */
+ /* Only do this timezone adjustment for recent and future years */
+ if (out->year > 1900 && out->year < 10000) {
+ time_t rawtime = 0;
+ struct tm tm_;
+ /*
+ * ISO 8601 states to treat date-times without a timezone offset
+ * or 'Z' for UTC as local time. The C standard libary functions
+ * mktime and gmtime allow us to do this conversion.
+ */
+ tm_.tm_sec = out->sec;
+ tm_.tm_min = out->min;
+ tm_.tm_hour = out->hour;
+ tm_.tm_mday = out->day;
+ tm_.tm_mon = out->month - 1;
+ tm_.tm_year = out->year - 1900;
+ tm_.tm_isdst = -1;
+
+ /* mktime converts a local 'struct tm' into a time_t */
+ rawtime = mktime(&tm_);
+ if (rawtime == -1) {
+ PyErr_SetString(PyExc_OSError, "Failed to use mktime to "
+ "convert local time to UTC");
+ goto error;
+ }
+
+ /* gmtime converts a 'time_t' into a UTC 'struct tm' */
+#if defined(_WIN32)
+ if (gmtime_s(&tm_, &rawtime) != 0) {
+ PyErr_SetString(PyExc_OSError, "Failed to use gmtime_s to "
+ "get a UTC time");
+ goto error;
+ }
+#else
+ /* Other platforms may require something else */
+ if (gmtime_r(&rawtime, &tm_) == NULL) {
+ PyErr_SetString(PyExc_OSError, "Failed to use gmtime_r to "
+ "get a UTC time");
+ goto error;
+ }
+#endif
+ out->sec = tm_.tm_sec;
+ out->min = tm_.tm_min;
+ out->hour = tm_.tm_hour;
+ out->day = tm_.tm_mday;
+ out->month = tm_.tm_mon + 1;
+ out->year = tm_.tm_year + 1900;
+ }
goto finish;
}
diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c
index 930c91ca1..d93375b8e 100644
--- a/numpy/core/src/umath/ufunc_object.c
+++ b/numpy/core/src/umath/ufunc_object.c
@@ -577,12 +577,12 @@ _parse_signature(PyUFuncObject *self, const char *signature)
}
len = strlen(signature);
- self->core_signature = _pya_malloc(sizeof(char) * (len+1));
+ self->core_signature = PyArray_malloc(sizeof(char) * (len+1));
if (self->core_signature) {
strcpy(self->core_signature, signature);
}
/* Allocate sufficient memory to store pointers to all dimension names */
- var_names = _pya_malloc(sizeof(char const*) * len);
+ var_names = PyArray_malloc(sizeof(char const*) * len);
if (var_names == NULL) {
PyErr_NoMemory();
return -1;
@@ -590,9 +590,9 @@ _parse_signature(PyUFuncObject *self, const char *signature)
self->core_enabled = 1;
self->core_num_dim_ix = 0;
- self->core_num_dims = _pya_malloc(sizeof(int) * self->nargs);
- self->core_dim_ixs = _pya_malloc(sizeof(int) * len); /* shrink this later */
- self->core_offsets = _pya_malloc(sizeof(int) * self->nargs);
+ self->core_num_dims = PyArray_malloc(sizeof(int) * self->nargs);
+ self->core_dim_ixs = PyArray_malloc(sizeof(int) * len); /* shrink this later */
+ self->core_offsets = PyArray_malloc(sizeof(int) * self->nargs);
if (self->core_num_dims == NULL || self->core_dim_ixs == NULL
|| self->core_offsets == NULL) {
PyErr_NoMemory();
@@ -677,24 +677,24 @@ _parse_signature(PyUFuncObject *self, const char *signature)
parse_error = "incomplete signature: not all arguments found";
goto fail;
}
- self->core_dim_ixs = _pya_realloc(self->core_dim_ixs,
+ self->core_dim_ixs = PyArray_realloc(self->core_dim_ixs,
sizeof(int)*cur_core_dim);
/* check for trivial core-signature, e.g. "(),()->()" */
if (cur_core_dim == 0) {
self->core_enabled = 0;
}
- _pya_free((void*)var_names);
+ PyArray_free((void*)var_names);
return 0;
fail:
- _pya_free((void*)var_names);
+ PyArray_free((void*)var_names);
if (parse_error) {
- char *buf = _pya_malloc(sizeof(char) * (len + 200));
+ char *buf = PyArray_malloc(sizeof(char) * (len + 200));
if (buf) {
sprintf(buf, "%s at position %d in \"%s\"",
parse_error, i, signature);
PyErr_SetString(PyExc_ValueError, signature);
- _pya_free(buf);
+ PyArray_free(buf);
}
else {
PyErr_NoMemory();
@@ -720,8 +720,7 @@ static int get_ufunc_arguments(PyUFuncObject *self,
NPY_CASTING *out_casting,
PyObject **out_extobj,
PyObject **out_typetup,
- int *out_subok,
- int *out_any_object)
+ int *out_subok)
{
npy_intp i, nargs, nin = self->nin;
PyObject *obj, *context;
@@ -938,8 +937,6 @@ static int get_ufunc_arguments(PyUFuncObject *self,
}
}
- *out_any_object = any_object;
-
Py_XDECREF(str_key_obj);
return 0;
@@ -1066,25 +1063,49 @@ ufunc_loop_matches(PyUFuncObject *self,
static int
set_ufunc_loop_data_types(PyUFuncObject *self, PyArrayObject **op,
PyArray_Descr **out_dtype,
- int *types,
- npy_intp buffersize, int *out_trivial_loop_ok)
+ int *types)
{
- npy_intp i, nin = self->nin, nop = nin + self->nout;
+ int i, nin = self->nin, nop = nin + self->nout;
- *out_trivial_loop_ok = 1;
/* Fill the dtypes array */
for (i = 0; i < nop; ++i) {
out_dtype[i] = PyArray_DescrFromType(types[i]);
if (out_dtype[i] == NULL) {
+ while (--i >= 0) {
+ Py_DECREF(out_dtype[i]);
+ out_dtype[i] = NULL;
+ }
return -1;
}
+ }
+
+ return 0;
+}
+
+/*
+ * This checks whether a trivial loop is ok,
+ * making copies of scalar and one dimensional operands if that will
+ * help.
+ *
+ * Returns 1 if a trivial loop is ok, 0 if it is not, and
+ * -1 if there is an error.
+ */
+static int
+check_for_trivial_loop(PyUFuncObject *self,
+ PyArrayObject **op,
+ PyArray_Descr **dtype,
+ npy_intp buffersize)
+{
+ npy_intp i, nin = self->nin, nop = nin + self->nout;
+
+ for (i = 0; i < nop; ++i) {
/*
* If the dtype doesn't match, or the array isn't aligned,
* indicate that the trivial loop can't be done.
*/
- if (*out_trivial_loop_ok && op[i] != NULL &&
+ if (op[i] != NULL &&
(!PyArray_ISALIGNED(op[i]) ||
- !PyArray_EquivTypes(out_dtype[i], PyArray_DESCR(op[i]))
+ !PyArray_EquivTypes(dtype[i], PyArray_DESCR(op[i]))
)) {
/*
* If op[j] is a scalar or small one dimensional
@@ -1095,9 +1116,9 @@ set_ufunc_loop_data_types(PyUFuncObject *self, PyArrayObject **op,
(PyArray_NDIM(op[i]) == 1 &&
PyArray_DIM(op[i],0) <= buffersize))) {
PyArrayObject *tmp;
- Py_INCREF(out_dtype[i]);
+ Py_INCREF(dtype[i]);
tmp = (PyArrayObject *)
- PyArray_CastToType(op[i], out_dtype[i], 0);
+ PyArray_CastToType(op[i], dtype[i], 0);
if (tmp == NULL) {
return -1;
}
@@ -1105,12 +1126,12 @@ set_ufunc_loop_data_types(PyUFuncObject *self, PyArrayObject **op,
op[i] = tmp;
}
else {
- *out_trivial_loop_ok = 0;
+ return 0;
}
}
}
- return 0;
+ return 1;
}
/*
@@ -1121,13 +1142,11 @@ find_ufunc_matching_userloop(PyUFuncObject *self,
PyArrayObject **op,
NPY_CASTING input_casting,
NPY_CASTING output_casting,
- npy_intp buffersize,
int any_object,
int use_min_scalar,
PyArray_Descr **out_dtype,
PyUFuncGenericFunction *out_innerloop,
void **out_innerloopdata,
- int *out_trivial_loop_ok,
int *out_no_castable_output,
char *out_err_src_typecode,
char *out_err_dst_typecode)
@@ -1168,8 +1187,7 @@ find_ufunc_matching_userloop(PyUFuncObject *self,
return -1;
/* Found a match */
case 1:
- set_ufunc_loop_data_types(self, op, out_dtype, types,
- buffersize, out_trivial_loop_ok);
+ set_ufunc_loop_data_types(self, op, out_dtype, types);
/* Save the inner loop and its data */
*out_innerloop = funcdata->func;
@@ -1199,13 +1217,11 @@ find_ufunc_specified_userloop(PyUFuncObject *self,
int *specified_types,
PyArrayObject **op,
NPY_CASTING casting,
- npy_intp buffersize,
int any_object,
int use_min_scalar,
PyArray_Descr **out_dtype,
PyUFuncGenericFunction *out_innerloop,
- void **out_innerloopdata,
- int *out_trivial_loop_ok)
+ void **out_innerloopdata)
{
npy_intp i, j, nin = self->nin, nop = nin + self->nout;
PyUFunc_Loop1d *funcdata;
@@ -1261,8 +1277,7 @@ find_ufunc_specified_userloop(PyUFuncObject *self,
&err_dst_typecode)) {
/* It works */
case 1:
- set_ufunc_loop_data_types(self, op, out_dtype, types,
- buffersize, out_trivial_loop_ok);
+ set_ufunc_loop_data_types(self, op, out_dtype, types);
/* Save the inner loop and its data */
*out_innerloop = funcdata->func;
@@ -1365,10 +1380,6 @@ should_use_min_scalar(PyArrayObject **op, int nop)
/*
* Does a linear search for the best inner loop of the ufunc.
- * When op[i] is a scalar or a one dimensional array smaller than
- * the buffersize, and needs a dtype conversion, this function
- * may substitute op[i] with a version cast to the correct type. This way,
- * the later trivial loop detection has a higher chance of being triggered.
*
* Note that if an error is returned, the caller must free the non-zero
* references in out_dtype. This function does not do its own clean-up.
@@ -1378,12 +1389,10 @@ find_best_ufunc_inner_loop(PyUFuncObject *self,
PyArrayObject **op,
NPY_CASTING input_casting,
NPY_CASTING output_casting,
- npy_intp buffersize,
int any_object,
PyArray_Descr **out_dtype,
PyUFuncGenericFunction *out_innerloop,
- void **out_innerloopdata,
- int *out_trivial_loop_ok)
+ void **out_innerloopdata)
{
npy_intp i, j, nin = self->nin, nop = nin + self->nout;
int types[NPY_MAXARGS];
@@ -1401,9 +1410,8 @@ find_best_ufunc_inner_loop(PyUFuncObject *self,
if (self->userloops) {
switch (find_ufunc_matching_userloop(self, op,
input_casting, output_casting,
- buffersize, any_object, use_min_scalar,
+ any_object, use_min_scalar,
out_dtype, out_innerloop, out_innerloopdata,
- out_trivial_loop_ok,
&no_castable_output, &err_src_typecode,
&err_dst_typecode)) {
/* Error */
@@ -1452,8 +1460,7 @@ find_best_ufunc_inner_loop(PyUFuncObject *self,
return -1;
/* Found a match */
case 1:
- set_ufunc_loop_data_types(self, op, out_dtype, types,
- buffersize, out_trivial_loop_ok);
+ set_ufunc_loop_data_types(self, op, out_dtype, types);
/* Save the inner loop and its data */
*out_innerloop = self->functions[i];
@@ -1494,10 +1501,6 @@ find_best_ufunc_inner_loop(PyUFuncObject *self,
/*
* Does a linear search for the inner loop of the ufunc specified by type_tup.
- * When op[i] is a scalar or a one dimensional array smaller than
- * the buffersize, and needs a dtype conversion, this function
- * may substitute op[i] with a version cast to the correct type. This way,
- * the later trivial loop detection has a higher chance of being triggered.
*
* Note that if an error is returned, the caller must free the non-zero
* references in out_dtype. This function does not do its own clean-up.
@@ -1507,12 +1510,10 @@ find_specified_ufunc_inner_loop(PyUFuncObject *self,
PyObject *type_tup,
PyArrayObject **op,
NPY_CASTING casting,
- npy_intp buffersize,
int any_object,
PyArray_Descr **out_dtype,
PyUFuncGenericFunction *out_innerloop,
- void **out_innerloopdata,
- int *out_trivial_loop_ok)
+ void **out_innerloopdata)
{
npy_intp i, j, n, nin = self->nin, nop = nin + self->nout;
int n_specified = 0;
@@ -1619,9 +1620,8 @@ find_specified_ufunc_inner_loop(PyUFuncObject *self,
switch (find_ufunc_specified_userloop(self,
n_specified, specified_types,
op, casting,
- buffersize, any_object, use_min_scalar,
- out_dtype, out_innerloop, out_innerloopdata,
- out_trivial_loop_ok)) {
+ any_object, use_min_scalar,
+ out_dtype, out_innerloop, out_innerloopdata)) {
/* Error */
case -1:
return -1;
@@ -1673,8 +1673,7 @@ find_specified_ufunc_inner_loop(PyUFuncObject *self,
return -1;
/* It worked */
case 1:
- set_ufunc_loop_data_types(self, op, out_dtype, types,
- buffersize, out_trivial_loop_ok);
+ set_ufunc_loop_data_types(self, op, out_dtype, types);
/* Save the inner loop and its data */
*out_innerloop = self->functions[i];
@@ -1706,6 +1705,49 @@ find_specified_ufunc_inner_loop(PyUFuncObject *self,
return -1;
}
+int generic_ufunc_type_resolution(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes,
+ PyUFuncGenericFunction *out_innerloop,
+ void **out_innerloopdata)
+{
+ int i, nop = ufunc->nin + ufunc->nout;
+ int retval = 0, any_object = 0;
+ NPY_CASTING input_casting;
+
+ for (i = 0; i < nop; ++i) {
+ if (operands[i] != NULL &&
+ PyTypeNum_ISOBJECT(PyArray_DESCR(operands[i])->type_num)) {
+ any_object = 1;
+ break;
+ }
+ }
+
+ /*
+ * Decide the casting rules for inputs and outputs. We want
+ * NPY_SAFE_CASTING or stricter, so that the loop selection code
+ * doesn't choose an integer loop for float inputs, for example.
+ */
+ input_casting = (casting > NPY_SAFE_CASTING) ? NPY_SAFE_CASTING : casting;
+
+ if (type_tup == NULL) {
+ /* Find the best ufunc inner loop, and fill in the dtypes */
+ retval = find_best_ufunc_inner_loop(ufunc, operands,
+ input_casting, casting, any_object,
+ out_dtypes, out_innerloop, out_innerloopdata);
+ } else {
+ /* Find the specified ufunc inner loop, and fill in the dtypes */
+ retval = find_specified_ufunc_inner_loop(ufunc, type_tup,
+ operands, casting, any_object, out_dtypes,
+ out_innerloop, out_innerloopdata);
+ }
+
+ return retval;
+}
+
+
static void
trivial_two_operand_loop(PyArrayObject **op,
PyUFuncGenericFunction innerloop,
@@ -2136,8 +2178,7 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *self,
int nin, nout;
int i, idim, nop;
char *ufunc_name;
- int retval = -1, any_object = 0, subok = 1;
- NPY_CASTING input_casting;
+ int retval = -1, subok = 1;
PyArray_Descr *dtype[NPY_MAXARGS];
@@ -2174,8 +2215,6 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *self,
*/
PyObject *arr_prep_args = NULL;
- int trivial_loop_ok = 0;
-
NPY_ORDER order = NPY_KEEPORDER;
/*
* Many things in NumPy do unsafe casting (doing int += float, etc).
@@ -2210,7 +2249,7 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *self,
/* Get all the arguments */
retval = get_ufunc_arguments(self, args, kwds,
- op, &order, &casting, &extobj, &type_tup, &subok, &any_object);
+ op, &order, &casting, &extobj, &type_tup, &subok);
if (retval < 0) {
goto fail;
}
@@ -2292,25 +2331,9 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *self,
NPY_UF_DBG_PRINT("Finding inner loop\n");
- /*
- * Decide the casting rules for inputs and outputs. We want
- * NPY_SAFE_CASTING or stricter, so that the loop selection code
- * doesn't choose an integer loop for float inputs, for example.
- */
- input_casting = (casting > NPY_SAFE_CASTING) ? NPY_SAFE_CASTING : casting;
- if (type_tup == NULL) {
- /* Find the best ufunc inner loop, and fill in the dtypes */
- retval = find_best_ufunc_inner_loop(self, op, input_casting, casting,
- buffersize, any_object, dtype,
- &innerloop, &innerloopdata, &trivial_loop_ok);
- } else {
- /* Find the specified ufunc inner loop, and fill in the dtypes */
- retval = find_specified_ufunc_inner_loop(self, type_tup,
- op, casting,
- buffersize, any_object, dtype,
- &innerloop, &innerloopdata, &trivial_loop_ok);
- }
+ retval = self->type_resolution_function(self, casting,
+ op, type_tup, dtype, &innerloop, &innerloopdata);
if (retval < 0) {
goto fail;
}
@@ -2408,7 +2431,7 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *self,
* Set up the inner strides array. Because we're not doing
* buffering, the strides are fixed throughout the looping.
*/
- inner_strides = (npy_intp *)_pya_malloc(
+ inner_strides = (npy_intp *)PyArray_malloc(
NPY_SIZEOF_INTP * (nop+core_dim_ixs_size));
/* The strides after the first nop match core_dim_ixs */
core_dim_ixs = self->core_dim_ixs;
@@ -2504,7 +2527,7 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *self,
goto fail;
}
- _pya_free(inner_strides);
+ PyArray_free(inner_strides);
NpyIter_Deallocate(iter);
/* The caller takes ownership of all the references in op */
for (i = 0; i < nop; ++i) {
@@ -2522,7 +2545,7 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *self,
fail:
NPY_UF_DBG_PRINT1("Returning failure code %d\n", retval);
if (inner_strides) {
- _pya_free(inner_strides);
+ PyArray_free(inner_strides);
}
if (iter != NULL) {
NpyIter_Deallocate(iter);
@@ -2553,8 +2576,7 @@ PyUFunc_GenericFunction(PyUFuncObject *self,
int nin, nout;
int i, nop;
char *ufunc_name;
- int retval = -1, any_object = 0, subok = 1;
- NPY_CASTING input_casting;
+ int retval = -1, subok = 1;
PyArray_Descr *dtype[NPY_MAXARGS];
@@ -2577,7 +2599,6 @@ PyUFunc_GenericFunction(PyUFuncObject *self,
int trivial_loop_ok = 0;
- /* TODO: For 1.6, the default should probably be NPY_CORDER */
NPY_ORDER order = NPY_KEEPORDER;
/*
* Many things in NumPy do unsafe casting (doing int += float, etc).
@@ -2593,7 +2614,6 @@ PyUFunc_GenericFunction(PyUFuncObject *self,
return -1;
}
- /* TODO: support generalized ufunc */
if (self->core_enabled) {
return PyUFunc_GeneralizedFunction(self, args, kwds, op);
}
@@ -2617,7 +2637,7 @@ PyUFunc_GenericFunction(PyUFuncObject *self,
/* Get all the arguments */
retval = get_ufunc_arguments(self, args, kwds,
- op, &order, &casting, &extobj, &type_tup, &subok, &any_object);
+ op, &order, &casting, &extobj, &type_tup, &subok);
if (retval < 0) {
goto fail;
}
@@ -2640,26 +2660,19 @@ PyUFunc_GenericFunction(PyUFuncObject *self,
NPY_UF_DBG_PRINT("Finding inner loop\n");
+ retval = self->type_resolution_function(self, casting,
+ op, type_tup, dtype, &innerloop, &innerloopdata);
+ if (retval < 0) {
+ goto fail;
+ }
+
/*
- * Decide the casting rules for inputs and outputs. We want
- * NPY_SAFE_CASTING or stricter, so that the loop selection code
- * doesn't choose an integer loop for float inputs, for example.
+ * This checks whether a trivial loop is ok,
+ * making copies of scalar and one dimensional operands if that will
+ * help.
*/
- input_casting = (casting > NPY_SAFE_CASTING) ? NPY_SAFE_CASTING : casting;
-
- if (type_tup == NULL) {
- /* Find the best ufunc inner loop, and fill in the dtypes */
- retval = find_best_ufunc_inner_loop(self, op, input_casting, casting,
- buffersize, any_object, dtype,
- &innerloop, &innerloopdata, &trivial_loop_ok);
- } else {
- /* Find the specified ufunc inner loop, and fill in the dtypes */
- retval = find_specified_ufunc_inner_loop(self, type_tup,
- op, casting,
- buffersize, any_object, dtype,
- &innerloop, &innerloopdata, &trivial_loop_ok);
- }
- if (retval < 0) {
+ trivial_loop_ok = check_for_trivial_loop(self, op, dtype, buffersize);
+ if (trivial_loop_ok < 0) {
goto fail;
}
@@ -2681,6 +2694,7 @@ PyUFunc_GenericFunction(PyUFuncObject *self,
}
}
+
#if NPY_UF_DBG_TRACING
printf("input types:\n");
for (i = 0; i < nin; ++i) {
@@ -4424,7 +4438,7 @@ PyUFunc_FromFuncAndDataAndSignature(PyUFuncGenericFunction *func, void **data,
{
PyUFuncObject *self;
- self = _pya_malloc(sizeof(PyUFuncObject));
+ self = PyArray_malloc(sizeof(PyUFuncObject));
if (self == NULL) {
return NULL;
}
@@ -4444,6 +4458,8 @@ PyUFunc_FromFuncAndDataAndSignature(PyUFuncGenericFunction *func, void **data,
self->obj = NULL;
self->userloops=NULL;
+ self->type_resolution_function = &generic_ufunc_type_resolution;
+
if (name == NULL) {
self->name = "?";
}
@@ -4484,8 +4500,12 @@ PyUFunc_SetUsesArraysAsData(void **data, size_t i)
return 0;
}
-/* Return 1 if the given data pointer for the loop specifies that it needs the
+/*
+ * Return 1 if the given data pointer for the loop specifies that it needs the
* arrays as the data pointer.
+ *
+ * NOTE: This is easier to specify with the type_resolution_function
+ * in the ufunc object.
*/
static int
_does_loop_use_arrays(void *data)
@@ -4535,8 +4555,8 @@ _free_loop1d_list(PyUFunc_Loop1d *data)
{
while (data != NULL) {
PyUFunc_Loop1d *next = data->next;
- _pya_free(data->arg_types);
- _pya_free(data);
+ PyArray_free(data->arg_types);
+ PyArray_free(data);
data = next;
}
}
@@ -4586,11 +4606,11 @@ PyUFunc_RegisterLoopForType(PyUFuncObject *ufunc,
if (key == NULL) {
return -1;
}
- funcdata = _pya_malloc(sizeof(PyUFunc_Loop1d));
+ funcdata = PyArray_malloc(sizeof(PyUFunc_Loop1d));
if (funcdata == NULL) {
goto fail;
}
- newtypes = _pya_malloc(sizeof(int)*ufunc->nargs);
+ newtypes = PyArray_malloc(sizeof(int)*ufunc->nargs);
if (newtypes == NULL) {
goto fail;
}
@@ -4645,8 +4665,8 @@ PyUFunc_RegisterLoopForType(PyUFuncObject *ufunc,
/* just replace it with new function */
current->func = function;
current->data = data;
- _pya_free(newtypes);
- _pya_free(funcdata);
+ PyArray_free(newtypes);
+ PyArray_free(funcdata);
}
else {
/*
@@ -4669,8 +4689,8 @@ PyUFunc_RegisterLoopForType(PyUFuncObject *ufunc,
fail:
Py_DECREF(key);
- _pya_free(funcdata);
- _pya_free(newtypes);
+ PyArray_free(funcdata);
+ PyArray_free(newtypes);
if (!PyErr_Occurred()) PyErr_NoMemory();
return -1;
}
@@ -4682,23 +4702,23 @@ static void
ufunc_dealloc(PyUFuncObject *self)
{
if (self->core_num_dims) {
- _pya_free(self->core_num_dims);
+ PyArray_free(self->core_num_dims);
}
if (self->core_dim_ixs) {
- _pya_free(self->core_dim_ixs);
+ PyArray_free(self->core_dim_ixs);
}
if (self->core_offsets) {
- _pya_free(self->core_offsets);
+ PyArray_free(self->core_offsets);
}
if (self->core_signature) {
- _pya_free(self->core_signature);
+ PyArray_free(self->core_signature);
}
if (self->ptr) {
- _pya_free(self->ptr);
+ PyArray_free(self->ptr);
}
Py_XDECREF(self->userloops);
Py_XDECREF(self->obj);
- _pya_free(self);
+ PyArray_free(self);
}
static PyObject *
@@ -4956,7 +4976,7 @@ ufunc_get_types(PyUFuncObject *self)
if (list == NULL) {
return NULL;
}
- t = _pya_malloc(no+ni+2);
+ t = PyArray_malloc(no+ni+2);
n = 0;
for (k = 0; k < nt; k++) {
for (j = 0; j<ni; j++) {
@@ -4972,7 +4992,7 @@ ufunc_get_types(PyUFuncObject *self)
str = PyUString_FromStringAndSize(t, no + ni + 2);
PyList_SET_ITEM(list, k, str);
}
- _pya_free(t);
+ PyArray_free(t);
return list;
}
diff --git a/numpy/core/src/umath/umathmodule.c.src b/numpy/core/src/umath/umathmodule.c.src
index 8d081f85b..b4cece358 100644
--- a/numpy/core/src/umath/umathmodule.c.src
+++ b/numpy/core/src/umath/umathmodule.c.src
@@ -43,6 +43,32 @@
static PyUFuncGenericFunction pyfunc_functions[] = {PyUFunc_On_Om};
+static int object_ufunc_type_resolution(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes,
+ PyUFuncGenericFunction *out_innerloop,
+ void **out_innerloopdata)
+{
+ int i, nop = ufunc->nin + ufunc->nout;
+
+ out_dtypes[0] = PyArray_DescrFromType(NPY_OBJECT);
+ if (out_dtypes[0] == NULL) {
+ return -1;
+ }
+
+ for (i = 1; i < nop; ++i) {
+ out_dtypes[i] = out_dtypes[0];
+ Py_INCREF(out_dtypes[0]);
+ }
+
+ *out_innerloop = ufunc->functions[0];
+ *out_innerloopdata = ufunc->data[0];
+
+ return 0;
+}
+
static PyObject *
ufunc_frompyfunc(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *NPY_UNUSED(kwds)) {
/* Keywords are ignored for now */
@@ -62,7 +88,7 @@ ufunc_frompyfunc(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *NPY_UNUS
PyErr_SetString(PyExc_TypeError, "function must be callable");
return NULL;
}
- self = _pya_malloc(sizeof(PyUFuncObject));
+ self = PyArray_malloc(sizeof(PyUFuncObject));
if (self == NULL) {
return NULL;
}
@@ -85,6 +111,8 @@ ufunc_frompyfunc(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *NPY_UNUS
self->core_offsets = NULL;
self->core_signature = NULL;
+ self->type_resolution_function = &object_ufunc_type_resolution;
+
pyname = PyObject_GetAttrString(function, "__name__");
if (pyname) {
(void) PyString_AsStringAndSize(pyname, &fname, &fname_len);
@@ -115,7 +143,7 @@ ufunc_frompyfunc(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *NPY_UNUS
if (i) {
offset[1] += (sizeof(void *)-i);
}
- self->ptr = _pya_malloc(offset[0] + offset[1] + sizeof(void *) +
+ self->ptr = PyArray_malloc(offset[0] + offset[1] + sizeof(void *) +
(fname_len + 14));
if (self->ptr == NULL) {
Py_XDECREF(pyname);
diff --git a/numpy/core/tests/test_datetime.py b/numpy/core/tests/test_datetime.py
index 79f3e9e41..a9ff39c7d 100644
--- a/numpy/core/tests/test_datetime.py
+++ b/numpy/core/tests/test_datetime.py
@@ -117,7 +117,7 @@ class TestDateTime(TestCase):
assert_equal(b.astype(object).astype(unit), b,
"Error roundtripping unit %s" % unit)
- def test_month_trucation(self):
+ def test_month_truncation(self):
# Make sure that months are truncating correctly
assert_equal(np.array('1945-03-01', dtype='M8[M]'),
np.array('1945-03-31', dtype='M8[M]'))
@@ -130,6 +130,33 @@ class TestDateTime(TestCase):
assert_equal(np.array('1980-02-01', dtype='M8[M]'),
np.array('1980-02-29T23:59:59.999999Z', dtype='M8[M]'))
+ def test_different_unit_comparison(self):
+ # Check some years
+ for unit1 in ['Y', 'M', 'D', '6h', 'h', 'm', 's', '10ms',
+ 'ms', 'us', 'ps']:
+ dt1 = np.dtype('M8[%s]' % unit1)
+ for unit2 in ['Y', 'M', 'D', 'h', 'm', 's', 'ms', 'us', 'ps']:
+ dt2 = np.dtype('M8[%s]' % unit2)
+ assert_equal(np.array('1945', dtype=dt1),
+ np.array('1945', dtype=dt2))
+ assert_equal(np.array('1970', dtype=dt1),
+ np.array('1970', dtype=dt2))
+ assert_equal(np.array('9999', dtype=dt1),
+ np.array('9999', dtype=dt2))
+ assert_equal(np.array('10000', dtype=dt1),
+ np.array('10000-01-01', dtype=dt2))
+ # Check some days
+ for unit1 in ['D', '12h', 'h', 'm', 's', '4s', 'ms', 'us', 'ps']:
+ dt1 = np.dtype('M8[%s]' % unit1)
+ for unit2 in ['D', 'h', 'm', 's', 'ms', 'us', 'ps']:
+ dt2 = np.dtype('M8[%s]' % unit2)
+ assert_equal(np.array('1932-02-17', dtype=dt1),
+ np.array('1932-02-17T00:00:00', dtype=dt2))
+ assert_equal(np.array('10000-04-27', dtype=dt1),
+ np.array('10000-04-27T00:00:00', dtype=dt2))
+ assert_equal(np.array('today', dtype=dt1),
+ np.array('today', dtype=dt2))
+
def test_hours(self):
t = np.ones(3, dtype='M8[s]')
t[0] = 60*60*24 + 60*60*10