@@ -756,6 +756,15 @@ _set_out_array(PyObject *obj, PyArrayObject **store)
756
756
757
757
/********* GENERIC UFUNC USING ITERATOR *********/
758
758
759
+ /*
760
+ * Produce a name for the ufunc, if one is not already set
761
+ * This is used in the PyUFunc_handlefperr machinery, and in error messages
762
+ */
763
+ static const char *
764
+ _get_ufunc_name (PyUFuncObject * ufunc ) {
765
+ return ufunc -> name ? ufunc -> name : "<unnamed ufunc>" ;
766
+ }
767
+
759
768
/*
760
769
* Parses the positional and keyword arguments for a generic ufunc call.
761
770
*
@@ -779,14 +788,12 @@ get_ufunc_arguments(PyUFuncObject *ufunc,
779
788
int nout = ufunc -> nout ;
780
789
PyObject * obj , * context ;
781
790
PyObject * str_key_obj = NULL ;
782
- const char * ufunc_name ;
791
+ const char * ufunc_name = _get_ufunc_name ( ufunc ) ;
783
792
int type_num ;
784
793
785
794
int any_flexible = 0 , any_object = 0 , any_flexible_userloops = 0 ;
786
795
int has_sig = 0 ;
787
796
788
- ufunc_name = ufunc -> name ? ufunc -> name : "<unnamed ufunc>" ;
789
-
790
797
* out_extobj = NULL ;
791
798
* out_typetup = NULL ;
792
799
if (out_wheremask != NULL ) {
@@ -1983,6 +1990,123 @@ _check_ufunc_fperr(int errmask, PyObject *extobj, const char *ufunc_name) {
1983
1990
return ret ;
1984
1991
}
1985
1992
1993
+ /*
1994
+ * Validate the core dimensions of all the operands, and collect all of
1995
+ * the labelled core dimensions into 'core_dim_sizes'.
1996
+ *
1997
+ * Returns 0 on success, and -1 on failure
1998
+ *
1999
+ * The behavior has been changed in NumPy 1.10.0, and the following
2000
+ * requirements must be fulfilled or an error will be raised:
2001
+ * * Arguments, both input and output, must have at least as many
2002
+ * dimensions as the corresponding number of core dimensions. In
2003
+ * previous versions, 1's were prepended to the shape as needed.
2004
+ * * Core dimensions with same labels must have exactly matching sizes.
2005
+ * In previous versions, core dimensions of size 1 would broadcast
2006
+ * against other core dimensions with the same label.
2007
+ * * All core dimensions must have their size specified by a passed in
2008
+ * input or output argument. In previous versions, core dimensions in
2009
+ * an output argument that were not specified in an input argument,
2010
+ * and whose size could not be inferred from a passed in output
2011
+ * argument, would have their size set to 1.
2012
+ */
2013
+ static int
2014
+ _get_coredim_sizes (PyUFuncObject * ufunc , PyArrayObject * * op ,
2015
+ npy_intp * core_dim_sizes ) {
2016
+ int i ;
2017
+ int nin = ufunc -> nin ;
2018
+ int nout = ufunc -> nout ;
2019
+ int nop = nin + nout ;
2020
+
2021
+ for (i = 0 ; i < ufunc -> core_num_dim_ix ; ++ i ) {
2022
+ core_dim_sizes [i ] = -1 ;
2023
+ }
2024
+ for (i = 0 ; i < nop ; ++ i ) {
2025
+ if (op [i ] != NULL ) {
2026
+ int idim ;
2027
+ int dim_offset = ufunc -> core_offsets [i ];
2028
+ int num_dims = ufunc -> core_num_dims [i ];
2029
+ int core_start_dim = PyArray_NDIM (op [i ]) - num_dims ;
2030
+
2031
+ /* Check if operands have enough dimensions */
2032
+ if (core_start_dim < 0 ) {
2033
+ PyErr_Format (PyExc_ValueError ,
2034
+ "%s: %s operand %d does not have enough "
2035
+ "dimensions (has %d, gufunc core with "
2036
+ "signature %s requires %d)" ,
2037
+ _get_ufunc_name (ufunc ), i < nin ? "Input" : "Output" ,
2038
+ i < nin ? i : i - nin , PyArray_NDIM (op [i ]),
2039
+ ufunc -> core_signature , num_dims );
2040
+ return -1 ;
2041
+ }
2042
+
2043
+ /*
2044
+ * Make sure every core dimension exactly matches all other core
2045
+ * dimensions with the same label.
2046
+ */
2047
+ for (idim = 0 ; idim < num_dims ; ++ idim ) {
2048
+ int core_dim_index = ufunc -> core_dim_ixs [dim_offset + idim ];
2049
+ npy_intp op_dim_size =
2050
+ PyArray_DIM (op [i ], core_start_dim + idim );
2051
+
2052
+ if (core_dim_sizes [core_dim_index ] == -1 ) {
2053
+ core_dim_sizes [core_dim_index ] = op_dim_size ;
2054
+ }
2055
+ else if (op_dim_size != core_dim_sizes [core_dim_index ]) {
2056
+ PyErr_Format (PyExc_ValueError ,
2057
+ "%s: %s operand %d has a mismatch in its "
2058
+ "core dimension %d, with gufunc "
2059
+ "signature %s (size %zd is different "
2060
+ "from %zd)" ,
2061
+ _get_ufunc_name (ufunc ), i < nin ? "Input" : "Output" ,
2062
+ i < nin ? i : i - nin , idim ,
2063
+ ufunc -> core_signature , op_dim_size ,
2064
+ core_dim_sizes [core_dim_index ]);
2065
+ return -1 ;
2066
+ }
2067
+ }
2068
+ }
2069
+ }
2070
+
2071
+ /*
2072
+ * Make sure no core dimension is unspecified.
2073
+ */
2074
+ for (i = 0 ; i < ufunc -> core_num_dim_ix ; ++ i ) {
2075
+ if (core_dim_sizes [i ] == -1 ) {
2076
+ break ;
2077
+ }
2078
+ }
2079
+ if (i != ufunc -> core_num_dim_ix ) {
2080
+ /*
2081
+ * There is at least one core dimension missing, find in which
2082
+ * operand it comes up first (it has to be an output operand).
2083
+ */
2084
+ const int missing_core_dim = i ;
2085
+ int out_op ;
2086
+ for (out_op = nin ; out_op < nop ; ++ out_op ) {
2087
+ int first_idx = ufunc -> core_offsets [out_op ];
2088
+ int last_idx = first_idx + ufunc -> core_num_dims [out_op ];
2089
+ for (i = first_idx ; i < last_idx ; ++ i ) {
2090
+ if (ufunc -> core_dim_ixs [i ] == missing_core_dim ) {
2091
+ break ;
2092
+ }
2093
+ }
2094
+ if (i < last_idx ) {
2095
+ /* Change index offsets for error message */
2096
+ out_op -= nin ;
2097
+ i -= first_idx ;
2098
+ break ;
2099
+ }
2100
+ }
2101
+ PyErr_Format (PyExc_ValueError ,
2102
+ "%s: Output operand %d has core dimension %d "
2103
+ "unspecified, with gufunc signature %s" ,
2104
+ _get_ufunc_name (ufunc ), out_op , i , ufunc -> core_signature );
2105
+ return -1 ;
2106
+ }
2107
+ return 0 ;
2108
+ }
2109
+
1986
2110
1987
2111
static int
1988
2112
PyUFunc_GeneralizedFunction (PyUFuncObject * ufunc ,
@@ -1992,7 +2116,7 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc,
1992
2116
int nin , nout ;
1993
2117
int i , j , idim , nop ;
1994
2118
const char * ufunc_name ;
1995
- int retval = -1 , subok = 1 ;
2119
+ int retval = 0 , subok = 1 ;
1996
2120
int needs_api = 0 ;
1997
2121
1998
2122
PyArray_Descr * dtypes [NPY_MAXARGS ];
@@ -2046,7 +2170,7 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc,
2046
2170
nout = ufunc -> nout ;
2047
2171
nop = nin + nout ;
2048
2172
2049
- ufunc_name = ufunc -> name ? ufunc -> name : "<unnamed ufunc>" ;
2173
+ ufunc_name = _get_ufunc_name ( ufunc ) ;
2050
2174
2051
2175
NPY_UF_DBG_PRINT1 ("\nEvaluating ufunc %s\n" , ufunc_name );
2052
2176
@@ -2098,110 +2222,9 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc,
2098
2222
goto fail ;
2099
2223
}
2100
2224
2101
- /*
2102
- * Validate the core dimensions of all the operands, and collect all of
2103
- * the labelled core dimensions into 'core_dim_sizes'.
2104
- *
2105
- * The behavior has been changed in NumPy 1.10.0, and the following
2106
- * requirements must be fulfilled or an error will be raised:
2107
- * * Arguments, both input and output, must have at least as many
2108
- * dimensions as the corresponding number of core dimensions. In
2109
- * previous versions, 1's were prepended to the shape as needed.
2110
- * * Core dimensions with same labels must have exactly matching sizes.
2111
- * In previous versions, core dimensions of size 1 would broadcast
2112
- * against other core dimensions with the same label.
2113
- * * All core dimensions must have their size specified by a passed in
2114
- * input or output argument. In previous versions, core dimensions in
2115
- * an output argument that were not specified in an input argument,
2116
- * and whose size could not be inferred from a passed in output
2117
- * argument, would have their size set to 1.
2118
- */
2119
- for (i = 0 ; i < ufunc -> core_num_dim_ix ; ++ i ) {
2120
- core_dim_sizes [i ] = -1 ;
2121
- }
2122
- for (i = 0 ; i < nop ; ++ i ) {
2123
- if (op [i ] != NULL ) {
2124
- int dim_offset = ufunc -> core_offsets [i ];
2125
- int num_dims = ufunc -> core_num_dims [i ];
2126
- int core_start_dim = PyArray_NDIM (op [i ]) - num_dims ;
2127
-
2128
- /* Check if operands have enough dimensions */
2129
- if (core_start_dim < 0 ) {
2130
- PyErr_Format (PyExc_ValueError ,
2131
- "%s: %s operand %d does not have enough "
2132
- "dimensions (has %d, gufunc core with "
2133
- "signature %s requires %d)" ,
2134
- ufunc_name , i < nin ? "Input" : "Output" ,
2135
- i < nin ? i : i - nin , PyArray_NDIM (op [i ]),
2136
- ufunc -> core_signature , num_dims );
2137
- retval = -1 ;
2138
- goto fail ;
2139
- }
2140
-
2141
- /*
2142
- * Make sure every core dimension exactly matches all other core
2143
- * dimensions with the same label.
2144
- */
2145
- for (idim = 0 ; idim < num_dims ; ++ idim ) {
2146
- int core_dim_index = ufunc -> core_dim_ixs [dim_offset + idim ];
2147
- npy_intp op_dim_size =
2148
- PyArray_DIM (op [i ], core_start_dim + idim );
2149
-
2150
- if (core_dim_sizes [core_dim_index ] == -1 ) {
2151
- core_dim_sizes [core_dim_index ] = op_dim_size ;
2152
- }
2153
- else if (op_dim_size != core_dim_sizes [core_dim_index ]) {
2154
- PyErr_Format (PyExc_ValueError ,
2155
- "%s: %s operand %d has a mismatch in its "
2156
- "core dimension %d, with gufunc "
2157
- "signature %s (size %zd is different "
2158
- "from %zd)" ,
2159
- ufunc_name , i < nin ? "Input" : "Output" ,
2160
- i < nin ? i : i - nin , idim ,
2161
- ufunc -> core_signature , op_dim_size ,
2162
- core_dim_sizes [core_dim_index ]);
2163
- retval = -1 ;
2164
- goto fail ;
2165
- }
2166
- }
2167
- }
2168
- }
2169
-
2170
- /*
2171
- * Make sure no core dimension is unspecified.
2172
- */
2173
- for (i = 0 ; i < ufunc -> core_num_dim_ix ; ++ i ) {
2174
- if (core_dim_sizes [i ] == -1 ) {
2175
- break ;
2176
- }
2177
- }
2178
- if (i != ufunc -> core_num_dim_ix ) {
2179
- /*
2180
- * There is at least one core dimension missing, find in which
2181
- * operand it comes up first (it has to be an output operand).
2182
- */
2183
- const int missing_core_dim = i ;
2184
- int out_op ;
2185
- for (out_op = nin ; out_op < nop ; ++ out_op ) {
2186
- int first_idx = ufunc -> core_offsets [out_op ];
2187
- int last_idx = first_idx + ufunc -> core_num_dims [out_op ];
2188
- for (i = first_idx ; i < last_idx ; ++ i ) {
2189
- if (ufunc -> core_dim_ixs [i ] == missing_core_dim ) {
2190
- break ;
2191
- }
2192
- }
2193
- if (i < last_idx ) {
2194
- /* Change index offsets for error message */
2195
- out_op -= nin ;
2196
- i -= first_idx ;
2197
- break ;
2198
- }
2199
- }
2200
- PyErr_Format (PyExc_ValueError ,
2201
- "%s: Output operand %d has core dimension %d "
2202
- "unspecified, with gufunc signature %s" ,
2203
- ufunc_name , out_op , i , ufunc -> core_signature );
2204
- retval = -1 ;
2225
+ /* Collect the lengths of the labelled core dimensions */
2226
+ retval = _get_coredim_sizes (ufunc , op , core_dim_sizes );
2227
+ if (retval < 0 ) {
2205
2228
goto fail ;
2206
2229
}
2207
2230
@@ -2212,7 +2235,6 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc,
2212
2235
2213
2236
/* Fill in op_axes for all the operands */
2214
2237
j = broadcast_ndim ;
2215
- core_dim_ixs_size = 0 ;
2216
2238
for (i = 0 ; i < nop ; ++ i ) {
2217
2239
int n ;
2218
2240
if (op [i ]) {
@@ -2254,7 +2276,6 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc,
2254
2276
}
2255
2277
2256
2278
op_axes [i ] = op_axes_arrays [i ];
2257
- core_dim_ixs_size += ufunc -> core_num_dims [i ];
2258
2279
}
2259
2280
2260
2281
/* Get the buffersize and errormask */
@@ -2369,6 +2390,10 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc,
2369
2390
* Set up the inner strides array. Because we're not doing
2370
2391
* buffering, the strides are fixed throughout the looping.
2371
2392
*/
2393
+ core_dim_ixs_size = 0 ;
2394
+ for (i = 0 ; i < nop ; ++ i ) {
2395
+ core_dim_ixs_size += ufunc -> core_num_dims [i ];
2396
+ }
2372
2397
inner_strides = (npy_intp * )PyArray_malloc (
2373
2398
NPY_SIZEOF_INTP * (nop + core_dim_ixs_size ));
2374
2399
if (inner_strides == NULL ) {
@@ -2607,7 +2632,7 @@ PyUFunc_GenericFunction(PyUFuncObject *ufunc,
2607
2632
nout = ufunc -> nout ;
2608
2633
nop = nin + nout ;
2609
2634
2610
- ufunc_name = ufunc -> name ? ufunc -> name : "<unnamed ufunc>" ;
2635
+ ufunc_name = _get_ufunc_name ( ufunc ) ;
2611
2636
2612
2637
NPY_UF_DBG_PRINT1 ("\nEvaluating ufunc %s\n" , ufunc_name );
2613
2638
@@ -2847,7 +2872,7 @@ reduce_type_resolver(PyUFuncObject *ufunc, PyArrayObject *arr,
2847
2872
int i , retcode ;
2848
2873
PyArrayObject * op [3 ] = {arr , arr , NULL };
2849
2874
PyArray_Descr * dtypes [3 ] = {NULL , NULL , NULL };
2850
- const char * ufunc_name = ufunc -> name ? ufunc -> name : "(unknown)" ;
2875
+ const char * ufunc_name = _get_ufunc_name ( ufunc ) ;
2851
2876
PyObject * type_tup = NULL ;
2852
2877
2853
2878
* out_dtype = NULL ;
@@ -3036,7 +3061,7 @@ PyUFunc_Reduce(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out,
3036
3061
PyArray_Descr * dtype ;
3037
3062
PyArrayObject * result ;
3038
3063
PyArray_AssignReduceIdentityFunc * assign_identity = NULL ;
3039
- const char * ufunc_name = ufunc -> name ? ufunc -> name : "(unknown)" ;
3064
+ const char * ufunc_name = _get_ufunc_name ( ufunc ) ;
3040
3065
/* These parameters come from a TLS global */
3041
3066
int buffersize = 0 , errormask = 0 ;
3042
3067
@@ -3144,7 +3169,7 @@ PyUFunc_Accumulate(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out,
3144
3169
PyUFuncGenericFunction innerloop = NULL ;
3145
3170
void * innerloopdata = NULL ;
3146
3171
3147
- const char * ufunc_name = ufunc -> name ? ufunc -> name : "(unknown)" ;
3172
+ const char * ufunc_name = _get_ufunc_name ( ufunc ) ;
3148
3173
3149
3174
/* These parameters come from extobj= or from a TLS global */
3150
3175
int buffersize = 0 , errormask = 0 ;
@@ -3511,7 +3536,7 @@ PyUFunc_Reduceat(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *ind,
3511
3536
PyUFuncGenericFunction innerloop = NULL ;
3512
3537
void * innerloopdata = NULL ;
3513
3538
3514
- const char * ufunc_name = ufunc -> name ? ufunc -> name : "(unknown)" ;
3539
+ const char * ufunc_name = _get_ufunc_name ( ufunc ) ;
3515
3540
char * opname = "reduceat" ;
3516
3541
3517
3542
/* These parameters come from extobj= or from a TLS global */
0 commit comments