numpy  2.0.0
src/umath/ufunc_object.c File Reference
#include "Python.h"
#include "npy_config.h"
#include "npy_pycompat.h"
#include "numpy/arrayobject.h"
#include "numpy/ufuncobject.h"
#include "numpy/arrayscalars.h"
#include "lowlevel_strided_loops.h"
#include "ufunc_type_resolution.h"
#include "reduction.h"
#include "ufunc_object.h"
#include "ufunc_override.h"

Data Structures

struct  _simple_cobj

Defines

#define _UMATHMODULE
#define NPY_NO_DEPRECATED_API   NPY_API_VERSION
#define PY_ARRAY_UNIQUE_SYMBOL   _npy_umathmodule_ARRAY_API
#define NO_IMPORT_ARRAY
#define NPY_UF_DBG_TRACING   0
#define NPY_UF_DBG_PRINT(s)
#define NPY_UF_DBG_PRINT1(s, p1)
#define NPY_UF_DBG_PRINT2(s, p1, p2)
#define NPY_UF_DBG_PRINT3(s, p1, p2, p3)
#define USE_USE_DEFAULTS   1
#define HANDLEIT(NAME, str)
#define _SETCPTR(cobj, val)   ((_simple_cobj *)(cobj))->c_obj = (val)

Functions

static int _does_loop_use_arrays (void *data)
static int _extract_pyvals (PyObject *ref, const char *name, int *bufsize, int *errmask, PyObject **errobj)
static int assign_reduce_identity_zero (PyArrayObject *result, void *data)
static int assign_reduce_identity_minusone (PyArrayObject *result, void *data)
static int assign_reduce_identity_one (PyArrayObject *result, void *data)
static int _error_handler (int method, PyObject *errobj, char *errtype, int retstatus, int *first)
NPY_NO_EXPORT int PyUFunc_getfperr (void)
NPY_NO_EXPORT int PyUFunc_handlefperr (int errmask, PyObject *errobj, int retstatus, int *first)
NPY_NO_EXPORT int PyUFunc_checkfperr (int errmask, PyObject *errobj, int *first)
NPY_NO_EXPORT void PyUFunc_clearfperr ()
static PyObject * get_global_ext_obj (void)
static int _get_bufsize_errmask (PyObject *extobj, const char *ufunc_name, int *buffersize, int *errormask)
static void _find_array_prepare (PyObject *args, PyObject *kwds, PyObject **output_prep, int nin, int nout, int check_subok)
NPY_NO_EXPORT int PyUFunc_GetPyValues (char *name, int *bufsize, int *errmask, PyObject **errobj)
static int _next_non_white_space (const char *str, int offset)
static int _is_alpha_underscore (char ch)
static int _is_alnum_underscore (char ch)
static int _get_end_of_name (const char *str, int offset)
static int _is_same_name (const char *s1, const char *s2)
static int _parse_signature (PyUFuncObject *ufunc, const char *signature)
static int _set_out_array (PyObject *obj, PyArrayObject **store)
static int get_ufunc_arguments (PyUFuncObject *ufunc, PyObject *args, PyObject *kwds, PyArrayObject **out_op, NPY_ORDER *out_order, NPY_CASTING *out_casting, PyObject **out_extobj, PyObject **out_typetup, int *out_subok, PyArrayObject **out_wheremask)
static int check_for_trivial_loop (PyUFuncObject *ufunc, PyArrayObject **op, PyArray_Descr **dtype, npy_intp buffersize)
static void trivial_two_operand_loop (PyArrayObject **op, PyUFuncGenericFunction innerloop, void *innerloopdata)
static void trivial_three_operand_loop (PyArrayObject **op, PyUFuncGenericFunction innerloop, void *innerloopdata)
static int prepare_ufunc_output (PyUFuncObject *ufunc, PyArrayObject **op, PyObject *arr_prep, PyObject *arr_prep_args, int i)
static int iterator_loop (PyUFuncObject *ufunc, PyArrayObject **op, PyArray_Descr **dtype, NPY_ORDER order, npy_intp buffersize, PyObject **arr_prep, PyObject *arr_prep_args, PyUFuncGenericFunction innerloop, void *innerloopdata)
static int execute_legacy_ufunc_loop (PyUFuncObject *ufunc, int trivial_loop_ok, PyArrayObject **op, PyArray_Descr **dtypes, NPY_ORDER order, npy_intp buffersize, PyObject **arr_prep, PyObject *arr_prep_args)
static int execute_fancy_ufunc_loop (PyUFuncObject *ufunc, PyArrayObject *wheremask, PyArrayObject **op, PyArray_Descr **dtypes, NPY_ORDER order, npy_intp buffersize, PyObject **arr_prep, PyObject *arr_prep_args)
static PyObject * make_arr_prep_args (npy_intp nin, PyObject *args, PyObject *kwds)
static int _check_ufunc_fperr (int errmask, PyObject *extobj, const char *ufunc_name)
static int PyUFunc_GeneralizedFunction (PyUFuncObject *ufunc, PyObject *args, PyObject *kwds, PyArrayObject **op)
NPY_NO_EXPORT int PyUFunc_GenericFunction (PyUFuncObject *ufunc, PyObject *args, PyObject *kwds, PyArrayObject **op)
static int get_binary_op_function (PyUFuncObject *ufunc, int *otype, PyUFuncGenericFunction *out_innerloop, void **out_innerloopdata)
static int reduce_type_resolver (PyUFuncObject *ufunc, PyArrayObject *arr, PyArray_Descr *odtype, PyArray_Descr **out_dtype)
static int assign_reduce_identity_zero (PyArrayObject *result, void *NPY_UNUSED(data))
static int assign_reduce_identity_one (PyArrayObject *result, void *NPY_UNUSED(data))
static int assign_reduce_identity_minusone (PyArrayObject *result, void *NPY_UNUSED(data))
static int reduce_loop (NpyIter *iter, char **dataptrs, npy_intp *strides, npy_intp *countptr, NpyIter_IterNextFunc *iternext, int needs_api, npy_intp skip_first_count, void *data)
static PyArrayObjectPyUFunc_Reduce (PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, int naxes, int *axes, PyArray_Descr *odtype, int keepdims)
static PyObject * PyUFunc_Accumulate (PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, int axis, int otype)
static PyObject * PyUFunc_Reduceat (PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *ind, PyArrayObject *out, int axis, int otype)
static PyObject * PyUFunc_GenericReduction (PyUFuncObject *ufunc, PyObject *args, PyObject *kwds, int operation)
static PyObject * _get_out_wrap (PyObject *out, PyObject *wrap)
static void _find_array_wrap (PyObject *args, PyObject *kwds, PyObject **output_wrap, int nin, int nout)
static PyObject * ufunc_generic_call (PyUFuncObject *ufunc, PyObject *args, PyObject *kwds)
NPY_NO_EXPORT PyObject * ufunc_geterr (PyObject *NPY_UNUSED(dummy), PyObject *args)
static int ufunc_update_use_defaults (void)
NPY_NO_EXPORT PyObject * ufunc_seterr (PyObject *NPY_UNUSED(dummy), PyObject *args)
NPY_NO_EXPORT int PyUFunc_ReplaceLoopBySignature (PyUFuncObject *func, PyUFuncGenericFunction newfunc, int *signature, PyUFuncGenericFunction *oldfunc)
NPY_NO_EXPORT PyObject * PyUFunc_FromFuncAndData (PyUFuncGenericFunction *func, void **data, char *types, int ntypes, int nin, int nout, int identity, const char *name, const char *doc, int unused)
NPY_NO_EXPORT PyObject * PyUFunc_FromFuncAndDataAndSignature (PyUFuncGenericFunction *func, void **data, char *types, int ntypes, int nin, int nout, int identity, const char *name, const char *doc, int unused, const char *signature)
NPY_NO_EXPORT int PyUFunc_SetUsesArraysAsData (void **data, size_t i)
static int cmp_arg_types (int *arg1, int *arg2, int n)
static NPY_INLINE void _free_loop1d_list (PyUFunc_Loop1d *data)
static void _loop1d_list_free (void *ptr)
NPY_NO_EXPORT int PyUFunc_RegisterLoopForDescr (PyUFuncObject *ufunc, PyArray_Descr *user_dtype, PyUFuncGenericFunction function, PyArray_Descr **arg_dtypes, void *data)
NPY_NO_EXPORT int PyUFunc_RegisterLoopForType (PyUFuncObject *ufunc, int usertype, PyUFuncGenericFunction function, int *arg_types, void *data)
static void ufunc_dealloc (PyUFuncObject *ufunc)
static PyObject * ufunc_repr (PyUFuncObject *ufunc)
static PyObject * ufunc_outer (PyUFuncObject *ufunc, PyObject *args, PyObject *kwds)
static PyObject * ufunc_reduce (PyUFuncObject *ufunc, PyObject *args, PyObject *kwds)
static PyObject * ufunc_accumulate (PyUFuncObject *ufunc, PyObject *args, PyObject *kwds)
static PyObject * ufunc_reduceat (PyUFuncObject *ufunc, PyObject *args, PyObject *kwds)
static NPY_INLINE PyArrayObjectnew_array_op (PyArrayObject *op_array, char *data)
static PyObject * ufunc_at (PyUFuncObject *ufunc, PyObject *args)
static PyObject * _makeargs (int num, char *ltr, int null_if_none)
static char _typecharfromnum (int num)
static PyObject * ufunc_get_doc (PyUFuncObject *ufunc)
static PyObject * ufunc_get_nin (PyUFuncObject *ufunc)
static PyObject * ufunc_get_nout (PyUFuncObject *ufunc)
static PyObject * ufunc_get_nargs (PyUFuncObject *ufunc)
static PyObject * ufunc_get_ntypes (PyUFuncObject *ufunc)
static PyObject * ufunc_get_types (PyUFuncObject *ufunc)
static PyObject * ufunc_get_name (PyUFuncObject *ufunc)
static PyObject * ufunc_get_identity (PyUFuncObject *ufunc)
static PyObject * ufunc_get_signature (PyUFuncObject *ufunc)

Variables

static int PyUFunc_NUM_NODEFAULTS = 0
static struct PyMethodDef ufunc_methods []
static PyGetSetDef ufunc_getset []
NPY_NO_EXPORT PyTypeObject PyUFunc_Type

Define Documentation

#define _SETCPTR (   cobj,
  val 
)    ((_simple_cobj *)(cobj))->c_obj = (val)
#define _UMATHMODULE
#define HANDLEIT (   NAME,
  str 
)
Value:
{if (retstatus & NPY_FPE_##NAME) {          \
            handle = errmask & UFUNC_MASK_##NAME;                       \
            if (handle &&                                               \
                _error_handler(handle >> UFUNC_SHIFT_##NAME,            \
                               errobj, str, retstatus, first) < 0)      \
                return -1;                                              \
        }}
#define NO_IMPORT_ARRAY
#define NPY_NO_DEPRECATED_API   NPY_API_VERSION
#define NPY_UF_DBG_PRINT (   s)

Referenced by prepare_ufunc_output().

#define NPY_UF_DBG_PRINT1 (   s,
  p1 
)
#define NPY_UF_DBG_PRINT2 (   s,
  p1,
  p2 
)
#define NPY_UF_DBG_PRINT3 (   s,
  p1,
  p2,
  p3 
)
#define NPY_UF_DBG_TRACING   0
**** PRINTF DEBUG TRACING *********
#define PY_ARRAY_UNIQUE_SYMBOL   _npy_umathmodule_ARRAY_API
#define USE_USE_DEFAULTS   1
System Message: ERROR/3 (<string>, line 1) Document or section may not begin with a transition.

System Message: ERROR/3 (<string>, line 1) Document may not end with a transition.
System Message: ERROR/3 (<string>, line 1) Document or section may not begin with a transition.

System Message: ERROR/3 (<string>, line 1) Document may not end with a transition.

Function Documentation

static int _check_ufunc_fperr ( int  errmask,
PyObject *  extobj,
const char *  ufunc_name 
) [static]
check the floating point status
  • errmask: mask of status to check

  • extobj: ufunc pyvals object

    may be null, in which case the thread global one is fetched

  • ufunc_name: name of ufunc

Get error object globals
static int _does_loop_use_arrays ( void *  data) [static]
System Message: ERROR/3 (<string>, line 1) Document or section may not begin with a transition.

System Message: ERROR/3 (<string>, line 1) Document may not end with a transition.
System Message: ERROR/3 (<string>, line 1) Document or section may not begin with a transition.

System Message: ERROR/3 (<string>, line 1) Document may not end with a transition.
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_resolver
in the ufunc object.
TODO: Remove this, since this is already basically broken
with the addition of the masked inner loops and not worth fixing since the new loop selection functions have access to the full dtypes and can dynamically allocate arbitrary auxiliary data.

Referenced by prepare_ufunc_output().

static int _error_handler ( int  method,
PyObject *  errobj,
char *  errtype,
int  retstatus,
int *  first 
) [static]
fpstatus is the ufunc_formatted hardware status errmask is the handling mask specified by the user. errobj is a Python object with (string, callable object or None) or NULL
2. for each of the flags determine whether to ignore, warn, raise error, or call Python function. If ignore, do nothing If warn, print a warning and continue If raise return an error If call, call a user-defined function with string
don't need C API for a simple print
static int _extract_pyvals ( PyObject *  ref,
const char *  name,
int *  bufsize,
int *  errmask,
PyObject **  errobj 
) [static]
Extracts some values from the global pyvals tuple. all destinations may be NULL, in which case they are not retrieved ref - should hold the global tuple name - is the name of the ufunc (ufuncobj->name)
bufsize - receives the buffer size to use errmask - receives the bitmask for error handling errobj - receives the python object to call with the error,

System Message: ERROR/3 (<string>, line 9) Unexpected indentation.

<blockquote> if an error handling method is 'call'</blockquote>

default errobj case, skips dictionary lookup
static void _find_array_prepare ( PyObject *  args,
PyObject *  kwds,
PyObject **  output_prep,
int  nin,
int  nout,
int  check_subok 
) [static]
This function analyzes the input arguments and determines an appropriate __array_prepare__ function to call for the outputs. Assumes subok is already true if check_subok is false.
If an output argument is provided, then it is prepped with its own __array_prepare__ not with the one determined by the input arguments.
if the provided output argument is already an ndarray, the prepping function is None (which means no prepping will be done --- not even PyArray_Return).
A NULL is placed in output_prep for outputs that should just have PyArray_Return called.
If a 'subok' parameter is passed and isn't True, don't wrap if check_subok is false it assumed subok in kwds keyword is True
If we have some preps defined, find the one of highest priority
Here prep is the prepping function determined from the input arrays (could be NULL).
For all the output arrays decide what to do.
1) Use the prep function determined from the input arrays This is the default if the output array is not passed in.
2) Use the __array_prepare__ method of the output object. This is special cased for exact ndarray so that no PyArray_Return is done in that case.
Output argument one may also be in a keyword argument
Output argument one may also be in a keyword argument
None signals to not call any wrapping
static void _find_array_wrap ( PyObject *  args,
PyObject *  kwds,
PyObject **  output_wrap,
int  nin,
int  nout 
) [static]
This function analyzes the input arguments and determines an appropriate __array_wrap__ function to call for the outputs.
If an output argument is provided, then it is wrapped with its own __array_wrap__ not with the one determined by the input arguments.
if the provided output argument is already an array, the wrapping function is None (which means no wrapping will be done --- not even PyArray_Return).
A NULL is placed in output_wrap for outputs that should just have PyArray_Return called.
If a 'subok' parameter is passed and isn't True, don't wrap but put None into slots with out arguments which means return the out argument
skip search for wrap members
If we have some wraps defined, find the one of highest priority
Here wrap is the wrapping function determined from the input arrays (could be NULL).
For all the output arrays decide what to do.
1) Use the wrap function determined from the input arrays This is the default if the output array is not passed in.
2) Use the __array_wrap__ method of the output object passed in. -- this is special cased for exact ndarray so that no PyArray_Return is done in that case.
Default is using positional arguments
There may be a keyword argument we can use instead
No, go back to positional (even though there aren't any)
If a tuple, must have all nout items
If the kwarg is not a tuple then it is an array (or None)
static NPY_INLINE void _free_loop1d_list ( PyUFunc_Loop1d data) [static]
This frees the linked-list structure when the CObject is destroyed (removed from the internal dictionary)
static int _get_bufsize_errmask ( PyObject *  extobj,
const char *  ufunc_name,
int *  buffersize,
int *  errormask 
) [static]
Get the buffersize and errormask
static int _get_end_of_name ( const char *  str,
int  offset 
) [static]
Return the ending position of a variable name
static PyObject* _get_out_wrap ( PyObject *  out,
PyObject *  wrap 
) [static]
Returns an incref'ed pointer to the proper wrapping object for a ufunc output argument, given the output argument 'out', and the input's wrapping function, 'wrap'.
Iterator allocated outputs get the input's wrapping
None signals to not call any wrapping
For array subclasses use their __array_wrap__ method, or the input's wrapping if not available
static int _is_alnum_underscore ( char  ch) [static]
static int _is_alpha_underscore ( char  ch) [static]
static int _is_same_name ( const char *  s1,
const char *  s2 
) [static]
Returns 1 if the dimension names pointed by s1 and s2 are the same, otherwise returns 0.
static void _loop1d_list_free ( void *  ptr) [static]

References PyArray_ITER_NEXT.

static PyObject* _makeargs ( int  num,
char *  ltr,
int  null_if_none 
) [static]
System Message: SEVERE/4 (<string>, line 1)
Title overline & underline mismatch.

                           UFUNC GETSET                                 ***
 
construct the string y1,y2,...,yn
static int _next_non_white_space ( const char *  str,
int  offset 
) [static]
Return the position of next non-white-space char in the string

Referenced by _is_alnum_underscore(), and _parse_signature().

static int _parse_signature ( PyUFuncObject ufunc,
const char *  signature 
) [static]
Sets core_num_dim_ix, core_num_dims, core_dim_ixs, core_offsets, and core_signature in PyUFuncObject "ufunc". Returns 0 unless an error occurred.

<

number of dimension of the current argument

<

index into core_num_dims&core_offsets

<

index into core_dim_ixs
Allocate sufficient memory to store pointers to all dimension names

<

shrink this later
loop over input/output arguments
expect "->"
parse core dimensions of one argument, e.g. "()", "(i)", or "(i,j)"
loop over core dimensions
The list of input arguments (or output arguments) was only read partially
check for trivial core-signature, e.g. "(),()->()"

References _next_non_white_space().

static int _set_out_array ( PyObject *  obj,
PyArrayObject **  store 
) [static]
Checks if 'obj' is a valid output array for a ufunc, i.e. it is either None or a writeable array, increments its reference count and stores a pointer to it in 'store'. Returns 0 on success, sets an exception and returns -1 on failure.
Translate None to NULL
If it's an array, store it

References _loop1d_info::arg_dtypes, _loop1d_info::next, NpyCapsule_AsVoidPtr(), and _tagPyUFuncObject::userloops.

static char _typecharfromnum ( int  num) [static]
static int assign_reduce_identity_minusone ( PyArrayObject result,
void *  data 
) [static]
static int assign_reduce_identity_minusone ( PyArrayObject result,
void *  NPY_UNUSEDdata 
) [static]
static int assign_reduce_identity_one ( PyArrayObject result,
void *  data 
) [static]
static int assign_reduce_identity_one ( PyArrayObject result,
void *  NPY_UNUSEDdata 
) [static]
static int assign_reduce_identity_zero ( PyArrayObject result,
void *  data 
) [static]
static int assign_reduce_identity_zero ( PyArrayObject result,
void *  NPY_UNUSEDdata 
) [static]

References NPY_UF_DBG_PRINT1.

static int check_for_trivial_loop ( PyUFuncObject ufunc,
PyArrayObject **  op,
PyArray_Descr **  dtype,
npy_intp  buffersize 
) [static]
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.
If the dtype doesn't match, or the array isn't aligned, indicate that the trivial loop can't be done.
If op[j] is a scalar or small one dimensional array input, make a copy to keep the opportunity for a trivial loop.

References NPY_ITER_ALIGNED, NPY_ITER_READONLY, NPY_ITER_READWRITE, NPY_ITER_WRITEONLY, and _tagPyUFuncObject::op_flags.

static int cmp_arg_types ( int *  arg1,
int *  arg2,
int  n 
) [static]
return 1 if arg1 > arg2, 0 if arg1 == arg2, and -1 if arg1 < arg2

References PyArrayMapIterObject::dataptr.

static int execute_fancy_ufunc_loop ( PyUFuncObject ufunc,
PyArrayObject wheremask,
PyArrayObject **  op,
PyArray_Descr **  dtypes,
NPY_ORDER  order,
npy_intp  buffersize,
PyObject **  arr_prep,
PyObject *  arr_prep_args 
) [static]
nin - number of inputs nout - number of outputs wheremask - if not NULL, the 'where=' parameter to the ufunc. op - the operands (nin + nout of them) order - the loop execution order/output memory order buffersize - how big of a buffer to use arr_prep - the __array_prepare__ functions for the outputs innerloop - the inner loop function innerloopdata - data to pass to the inner loop
Set up the flags
If READWRITE flag has been set for this operand, then clear default READONLY flag
Allocate the iterator. Because the types of the inputs were already checked, we use the casting rule 'unsafe' which is faster to calculate.
Copy any allocated outputs
Call the __array_prepare__ functions where necessary
Only do the loop if the iteration size is non-zero
Validate that the prepare_ufunc_output didn't mess with pointers
Get the inner loop, with the possibility of specialization based on the fixed strides.
Get the variables needed for the loop
Execute the loop
static int execute_legacy_ufunc_loop ( PyUFuncObject ufunc,
int  trivial_loop_ok,
PyArrayObject **  op,
PyArray_Descr **  dtypes,
NPY_ORDER  order,
npy_intp  buffersize,
PyObject **  arr_prep,
PyObject *  arr_prep_args 
) [static]
trivial_loop_ok - 1 if no alignment, data conversion, etc required nin - number of inputs nout - number of outputs op - the operands (nin + nout of them) order - the loop execution order/output memory order buffersize - how big of a buffer to use arr_prep - the __array_prepare__ functions for the outputs innerloop - the inner loop function innerloopdata - data to pass to the inner loop
If the loop wants the arrays, provide them.
First check for the trivial cases that don't need an iterator
Call the __prepare_array__ if necessary
Call the __prepare_array__ if necessary
Have to choose the input with more dimensions to clone, as one of them could be a scalar.
Call the __prepare_array__ if necessary
Call the __prepare_array__ if necessary
If no trivial loop matched, an iterator is required to resolve broadcasting, etc
static int get_binary_op_function ( PyUFuncObject ufunc,
int *  otype,
PyUFuncGenericFunction out_innerloop,
void **  out_innerloopdata 
) [static]
Given the output type, finds the specified binary op. The ufunc must have nin==2 and nout==1. The function may modify otype if the given type isn't found.
Returns 0 on success, -1 on failure.
If the type is custom and there are userloops, search for it here
Search for a function with compatible inputs
If the signature is "xx->x", we found the loop
Otherwise, we found the natural type of the reduction, replace otype and search again
Search for the exact function
Since the signature is "xx->x", we found the loop
static PyObject* get_global_ext_obj ( void  ) [static]
static int get_ufunc_arguments ( PyUFuncObject ufunc,
PyObject *  args,
PyObject *  kwds,
PyArrayObject **  out_op,
NPY_ORDER out_order,
NPY_CASTING out_casting,
PyObject **  out_extobj,
PyObject **  out_typetup,
int *  out_subok,
PyArrayObject **  out_wheremask 
) [static]
*** GENERIC UFUNC USING ITERATOR ****
Parses the positional and keyword arguments for a generic ufunc call.
Note that if an error is returned, the caller must free the non-zero references in out_op. This function does not do its own clean-up.
Check number of arguments
Get input arguments
TODO: There should be a comment here explaining what
context does.
If any operand is a flexible dtype, check to see if any struct dtype ufuncs are registered. A ufunc has been registered for a struct dtype if ufunc's arg_dtypes array is not NULL.
Traditionally, we return -2 here (meaning "NotImplemented") anytime we hit the above condition.
This condition basically means "we are doomed", b/c the "flexible" dtypes -- strings and void -- cannot have their own ufunc loops registered (except via the special "flexible userloops" mechanism), and they can't be cast to anything except object (and we only cast to object if any_object is true). So really we should do nothing here and continue and let the proper error be raised. But, we can't quite yet, b/c of backcompat.
Most of the time, this NotImplemented either got returned directly to the user (who can't do anything useful with it), or got passed back out of a special function like __mul__. And fortunately, for almost all special functions, the end result of this was a TypeError. Which is also what we get if we just continue without this special case, so this special case is unnecessary.
The only thing that actually depended on the NotImplemented is array_richcompare, which did two things with it. First, it needed to see this NotImplemented in order to implement the special-case comparisons for <blockquote> string < <= == != >= > string void == != void</blockquote>
Now it checks for those cases first, before trying to call the ufunc, so that's no problem. What it doesn't handle, though, is cases like <blockquote> float < string</blockquote>
or <blockquote> float == void</blockquote>
For those, it just let the NotImplemented bubble out, and accepted Python's default handling. And unfortunately, for comparisons, Python's default is not to raise an error. Instead, it returns something that depends on the operator: <blockquote>
== return False != return True < <= >= > Python 2: use "fallback" (= weird and broken) ordering

System Message: ERROR/3 (<string>, line 45) Unexpected indentation.

<blockquote> Python 3: raise TypeError (hallelujah)</blockquote> </blockquote>

In most cases this is straightforwardly broken, because comparison of two arrays should always return an array, and here we end up returning a scalar. However, there is an exception: if we are comparing two scalars for equality, then it actually is correct to return a scalar bool instead of raising an error. If we just removed this special check entirely, then "np.float64(1) == 'foo'" would raise an error instead of returning False, which is genuinely wrong.

The proper end goal here is:
  1. == and != should be implemented in a proper vectorized way for all types. The short-term hack for this is just to add a special case to PyUFunc_DefaultLegacyInnerLoopSelector where if it can't find a comparison loop for the given types, and the ufunc is np.equal or np.not_equal, then it returns a loop that just fills the output array with False (resp. True). Then array_richcompare could trust that whenever its special cases don't apply, simply calling the ufunc will do the right thing, even without this special check.
  2. < <= >= > should raise an error if no comparison function can be found. array_richcompare already handles all string <> string cases, and void dtypes don't have ordering, so again this would mean that array_richcompare could simply call the ufunc and it would do the right thing (i.e., raise an error), again without needing this special check.
So this means that for the transition period, our goal is:
== and != on scalars should simply return NotImplemented like
they always did, since everything ends up working out correctly in this case only
== and != on arrays should issue a FutureWarning and then return
NotImplemented
< <= >= > on all flexible dtypes on py2 should raise a
DeprecationWarning, and then return NotImplemented. On py3 we skip the warning, though, b/c it would just be immediately be followed by an exception anyway.
And for all other operations, we let things continue as normal.
strcmp() is a hack but I think we can get away with it for this temporary measure.
Warn on non-scalar, return NotImplemented regardless
Get positional output arguments
Get keyword output and other arguments. Raise an error if anything else is present in the keyword dictionary.
Provides a policy for allowed casting
Another way to specify 'sig'
Allow this parameter to be None
Overrides the global parameters buffer size, error mask, and error object
Output arrays may be specified as a keyword argument, either as a single array or None for single output ufuncs, or as a tuple of arrays and Nones.
'out' must be a tuple of arrays and Nones
Can be an array if it only has one output
If the deprecated behavior is ever removed, keep only the else branch of this if-else
The future error message
Allows the default output layout to be overridden
Allows a specific function inner loop to be selected
Provides a boolean array 'where=' mask if out_wheremask is supplied.

References DEPRECATE_FUTUREWARNING, and PyArray_NDIM.

static int iterator_loop ( PyUFuncObject ufunc,
PyArrayObject **  op,
PyArray_Descr **  dtype,
NPY_ORDER  order,
npy_intp  buffersize,
PyObject **  arr_prep,
PyObject *  arr_prep_args,
PyUFuncGenericFunction  innerloop,
void *  innerloopdata 
) [static]
Set up the flags
If READWRITE flag has been set for this operand, then clear default READONLY flag
Allocate the iterator. Because the types of the inputs were already checked, we use the casting rule 'unsafe' which is faster to calculate.
Copy any allocated outputs
Call the __array_prepare__ functions where necessary
Only do the loop if the iteration size is non-zero
Reset the iterator with the base pointers from the wrapped outputs
Get the variables needed for the loop
Execute the loop
static PyObject* make_arr_prep_args ( npy_intp  nin,
PyObject *  args,
PyObject *  kwds 
) [static]
Copy the tuple, but set the nin-th item to the keyword arg
static NPY_INLINE PyArrayObject* new_array_op ( PyArrayObject op_array,
char *  data 
) [static]
Helper for ufunc_at, below
static int prepare_ufunc_output ( PyUFuncObject ufunc,
PyArrayObject **  op,
PyObject *  arr_prep,
PyObject *  arr_prep_args,
int  i 
) [static]
Calls the given __array_prepare__ function on the operand *op, substituting it in place if a new array is returned and matches the old one.

System Message: WARNING/2 (<string>, line 1); backlink Inline emphasis start-string without end-string.
This requires that the dimensions, strides and data type remain exactly the same, which may be more strict than before.
If the same object was returned, nothing to do
If the result doesn't match, throw an error
Replace the op value

References _does_loop_use_arrays(), _tagPyUFuncObject::legacy_inner_loop_selector, _tagPyUFuncObject::nin, _tagPyUFuncObject::nout, NPY_ANYORDER, NPY_ARRAY_F_CONTIGUOUS, NPY_KEEPORDER, NPY_UF_DBG_PRINT, PyArray_DIMS, PyArray_ISFORTRAN, PyArray_NDIM, PyArray_NewFromDescr(), PyArray_TRIVIALLY_ITERABLE, PyArray_Type, and trivial_two_operand_loop().

static PyObject* PyUFunc_Accumulate ( PyUFuncObject ufunc,
PyArrayObject arr,
PyArrayObject out,
int  axis,
int  otype 
) [static]
The selected inner loop
These parameters come from extobj= or from a TLS global
Take a reference to out for later returning
Set up the output data type, using the input's exact data type if the type number didn't change to preserve metadata
Set up the op_axes for the outer loop
The per-operand flags for the outer loop
We can't buffer, so must do UPDATEIFCOPY
The way accumulate is set up, we can't do buffering, so make a copy instead when necessary.
Add some more flags
In case COPY or UPDATEIFCOPY occurred
Get the output
If the reduction axis has size zero, either return the reduction unit for UFUNC_REDUCE, or return the zero-sized output array for UFUNC_ACCUMULATE.
Get the variables needed for the loop
Execute the loop with just the outer iterator
Copy the first element to start the reduction.
Output (dataptr[0]) and input (dataptr[1]) may point to the same memory, e.g. np.add.accumulate(a, out=a).
Incref before decref to avoid the possibility of the reference count being zero temporarily.
Turn the two items into three for the inner loop
Execute the loop with no iterators
Turn the two items into three for the inner loop
Copy the first element to start the reduction.
Output (dataptr[0]) and input (dataptr[1]) may point to the same memory, e.g. np.add.accumulate(a, out=a).
Incref before decref to avoid the possibility of the reference count being zero temporarily.
NPY_NO_EXPORT int PyUFunc_checkfperr ( int  errmask,
PyObject *  errobj,
int *  first 
)
clearing is done for backward compatiblity
Checking the status flag clears it
NPY_NO_EXPORT PyObject* PyUFunc_FromFuncAndData ( PyUFuncGenericFunction func,
void **  data,
char *  types,
int  ntypes,
int  nin,
int  nout,
int  identity,
const char *  name,
const char *  doc,
int  unused 
)
NPY_NO_EXPORT PyObject* PyUFunc_FromFuncAndDataAndSignature ( PyUFuncGenericFunction func,
void **  data,
char *  types,
int  ntypes,
int  nin,
int  nout,
int  identity,
const char *  name,
const char *  doc,
int  unused,
const char *  signature 
)
static int PyUFunc_GeneralizedFunction ( PyUFuncObject ufunc,
PyObject *  args,
PyObject *  kwds,
PyArrayObject **  op 
) [static]
Use remapped axes for generalized ufunc
These parameters come from extobj= or from a TLS global
The selected inner loop
The dimensions which get passed to the inner loop
The strides which get passed to the inner loop
The sizes of the core dimensions (# entries is ufunc->core_num_dim_ix)
The __array_prepare__ function to call for each output
This is either args, or args with the out= parameter from kwds added appropriately.
Use the default assignment casting rule
When provided, extobj and typetup contain borrowed references
Initialize all the operands and dtypes to NULL
Get all the arguments
Figure out the number of iteration dimensions, which is the broadcast result of all the input non-core dimensions.
Figure out the number of iterator creation dimensions, which is the broadcast dimensions + all the core dimensions of the outputs, so that the iterator can allocate those output dimensions following the rules of order='F', for example.
Validate the core dimensions of all the operands, and collect all of the labelled core dimensions into 'core_dim_sizes'.
The behavior has been changed in NumPy 1.10.0, and the following requirements must be fulfilled or an error will be raised:

System Message: ERROR/3 (<string>, line 6) Unexpected indentation.

<blockquote>

  • Arguments, both input and output, must have at least as many dimensions as the corresponding number of core dimensions. In previous versions, 1's were prepended to the shape as needed.
  • Core dimensions with same labels must have exactly matching sizes. In previous versions, core dimensions of size 1 would broadcast against other core dimensions with the same label.
  • All core dimensions must have their size specified by a passed in input or output argument. In previous versions, core dimensions in an output argument that were not specified in an input argument, and whose size could not be inferred from a passed in output argument, would have their size set to 1.

</blockquote>

Check if operands have enough dimensions
Make sure every core dimension exactly matches all other core dimensions with the same label.
Make sure no core dimension is unspecified.
There is at least one core dimension missing, find in which operand it comes up first (it has to be an output operand).
Change index offsets for error message
Fill in the initial part of 'iter_shape'
Fill in op_axes for all the operands
Note that n may be negative if broadcasting extends into the core dimensions.
Broadcast all the unspecified dimensions normally
Any output core dimensions shape should be ignored
Except for when it belongs to this output
Fill in 'iter_shape' and 'op_axes' for this output
Get the buffersize and errormask
For the generalized ufunc, we get the loop right away too
Get the appropriate __array_prepare__ function to call for each output
Set up arr_prep_args if a prep function was needed
If the loop wants the arrays, provide them
Set up the iterator per-op flags. For generalized ufuncs, we can't do buffering, so must COPY or UPDATEIFCOPY.
If READWRITE flag has been set for this operand, then clear default READONLY flag
Create the iterator
Fill in any allocated outputs
Set up the inner strides array. Because we're not doing buffering, the strides are fixed throughout the looping.
Copy the strides after the first nop
Need to use the arrays in the iterator, not op, because a copy with a different-sized type may have been made.
Force the stride to zero when the shape is 1, sot that the broadcasting works right.
Only used for threading, if negative (this means that it is larger then ssize_t before axes removal) assume that the actual problem is large enough to be threaded usefully.
Remove all the core output dimensions from the iterator
The first nop strides are for the inner loop (but only can copy them after removing the core axes
Start with the floating-point exception flags cleared
Do the ufunc loop
Get the variables needed for the loop
For each output operand, check if it has non-zero size, and assign the identity if it does. For example, a dot product of two zero-length arrays will be a scalar, which has size one.
Check whether any errors occurred during the loop
The caller takes ownership of all the references in op
NPY_NO_EXPORT int PyUFunc_GenericFunction ( PyUFuncObject ufunc,
PyObject *  args,
PyObject *  kwds,
PyArrayObject **  op 
)
This generic function is called with the ufunc object, the arguments to it,

and an array of (pointers to) PyArrayObjects which are NULL.

'op' is an array of at least NPY_MAXARGS PyArrayObject *.

System Message: WARNING/2 (<string>, line 4); backlink Inline emphasis start-string without end-string.
These parameters come from extobj= or from a TLS global
The mask provided in the 'where=' parameter
The __array_prepare__ function to call for each output
This is either args, or args with the out= parameter from kwds added appropriately.
Use the default assignment casting rule
When provided, extobj and typetup contain borrowed references
Initialize all the operands and dtypes to NULL
Get all the arguments
Use the masked loop if a wheremask was specified.
Get the buffersize and errormask
Only do the trivial loop check for the unmasked version.
This checks whether a trivial loop is ok, making copies of scalar and one dimensional operands if that will help.
Get the appropriate __array_prepare__ function to call for each output
Set up arr_prep_args if a prep function was needed
Start with the floating-point exception flags cleared
Do the ufunc loop
Check whether any errors occurred during the loop
The caller takes ownership of all the references in op
static PyObject* PyUFunc_GenericReduction ( PyUFuncObject ufunc,
PyObject *  args,
PyObject *  kwds,
int  operation 
) [static]
This code handles reduce, reduceat, and accumulate (accumulate and reduce are special cases of the more general reduceat but they are handled separately for speed)
Until removed outright by https://github.com/numpy/numpy/pull/8187
Ensure input is an array
Check to see that type (and otype) is not FLEXIBLE
Convert the 'axis' parameter into a list of axes
Convert 'None' into all the axes
Try to interpret axis as an integer
TODO: PyNumber_Index would be good to use here
Special case letting axis={0 or -1} slip through for scalars
Check to see if input is zero-dimensional.
A reduction with no axes is still valid but trivial. As a special case for backwards compatibility in 'sum', 'prod', et al, also allow a reduction where axis=0, even though this is technically incorrect.
If out is specified it determines otype unless otype already specified.
For integer types --- make sure at least a long is used for add and multiply reduction to avoid overflow
If an output parameter was provided, don't wrap it

References NPY_BUFSIZE, npy_um_str_pyvals_name, and UFUNC_ERR_DEFAULT.

Referenced by ufunc_geterr().

non-clearing get was only added in 1.9 so this function always cleared keep it so just in case third party code relied on the clearing

References npy_clear_floatstatus().

NPY_NO_EXPORT int PyUFunc_GetPyValues ( char *  name,
int *  bufsize,
int *  errmask,
PyObject **  errobj 
)
On return, if errobj is populated with a non-NULL value, the caller
owns a new reference to errobj.
NPY_NO_EXPORT int PyUFunc_handlefperr ( int  errmask,
PyObject *  errobj,
int  retstatus,
int *  first 
)
static PyArrayObject* PyUFunc_Reduce ( PyUFuncObject ufunc,
PyArrayObject arr,
PyArrayObject out,
int  naxes,
int *  axes,
PyArray_Descr odtype,
int  keepdims 
) [static]
The implementation of the reduction operators with the new iterator turned into a bit of a long function here, but I think the design of this part needs to be changed to be more like einsum, so it may not be worth refactoring it too much. Consider this timing:

>>> a = arange(10000)
>>> timeit sum(a)
10000 loops, best of 3: 17 us per loop
>>> timeit einsum("i->",a)
100000 loops, best of 3: 13.5 us per loop
The axes must already be bounds-checked by the calling function, this function does not validate them.
These parameters come from a TLS global
Create an array of flags for reduction
The identity for a dynamic dtype like object arrays can't be used in general
The identity for a dynamic dtype like object arrays can't be used in general
The identity for a dynamic dtype like object arrays can't be used in general
Get the reduction dtype
static PyObject* PyUFunc_Reduceat ( PyUFuncObject ufunc,
PyArrayObject arr,
PyArrayObject ind,
PyArrayObject out,
int  axis,
int  otype 
) [static]
Reduceat performs a reduce over an axis using the indices as a guide
op.reduceat(array,indices) computes op.reduce(array[indices[i]:indices[i+1]] for i=0..end with an implicit indices[i+1]=len(array) assumed when i=end-1
if indices[i+1] <= indices[i]+1 then the result is array[indices[i]] for that value
op.accumulate(array) is the same as op.reduceat(array,indices)[::2] where indices is range(len(array)-1) with a zero placed in every other sample indices = zeros(len(array)*2-1) indices[1::2] = range(1,len(array))
output shape is based on the size of indices
The reduceat indices - ind must be validated outside this call
The selected inner loop
These parameters come from extobj= or from a TLS global
Check for out-of-bounds values in indices array
Take a reference to out for later returning
Set up the output data type, using the input's exact data type if the type number didn't change to preserve metadata
Set up the op_axes for the outer loop
Use the i-th iteration dimension to match up ind
Special case when the index array's size is zero
Allow any zero-sized output array in this case
The way reduceat is set up, we can't do buffering, so make a copy instead when necessary using the UPDATEIFCOPY flag
The per-operand flags for the outer loop
Remove the inner loop axis from the outer iterator
In case COPY or UPDATEIFCOPY occurred
Allocate the output for when there's no outer iterator
If the output has zero elements, return now.
Get the variables needed for the loop
Execute the loop with just the outer iterator
Copy the first element to start the reduction.
Output (dataptr[0]) and input (dataptr[1]) may point to the same memory, e.g. np.add.reduceat(a, np.arange(len(a)), out=a).
Incref before decref to avoid the possibility of the reference count being zero temporarily.
Inner loop like REDUCE
Execute the loop with no iterators
Copy the first element to start the reduction.
Output (dataptr[0]) and input (dataptr[1]) may point to the same memory, e.g. np.add.reduceat(a, np.arange(len(a)), out=a).
Incref before decref to avoid the possibility of the reference count being zero temporarily.
Inner loop like REDUCE
NPY_NO_EXPORT int PyUFunc_RegisterLoopForDescr ( PyUFuncObject ufunc,
PyArray_Descr user_dtype,
PyUFuncGenericFunction  function,
PyArray_Descr **  arg_dtypes,
void *  data 
)
This function allows the user to register a 1-d loop with an already created ufunc. This function is similar to RegisterLoopForType except that it allows a 1-d loop to be registered with PyArray_Descr objects instead of dtype type num values. This allows a 1-d loop to be registered for a structured array dtype or a custom dtype. The ufunc is called whenever any of it's input arguments match the user_dtype argument. ufunc - ufunc object created from call to PyUFunc_FromFuncAndData user_dtype - dtype that ufunc will be registered with function - 1-d loop function pointer arg_dtypes - array of dtype objects describing the ufunc operands data - arbitrary data pointer passed in to loop function
NPY_NO_EXPORT int PyUFunc_RegisterLoopForType ( PyUFuncObject ufunc,
int  usertype,
PyUFuncGenericFunction  function,
int *  arg_types,
void *  data 
)
Get entry for this user-defined type
If it's not there, then make one and return.
There is already at least 1 loop. Place this one in lexicographic order. If the next one signature is exactly like this one, then just replace. Otherwise insert.
just replace it with new function
insert it before the current one by hacking the internals of cobject to replace the function pointer --- can't use CObject API because destructor is set.
place this at front

References _tagPyUFuncObject::name, and PyUString_FromFormat.

NPY_NO_EXPORT int PyUFunc_ReplaceLoopBySignature ( PyUFuncObject func,
PyUFuncGenericFunction  newfunc,
int *  signature,
PyUFuncGenericFunction oldfunc 
)
Find the location of the matching signature
NPY_NO_EXPORT int PyUFunc_SetUsesArraysAsData ( void **  data,
size_t  i 
)
Specify that the loop specified by the given index should use the array of input and arrays as the data pointer to the loop.

References NPY_ITER_ALIGNED, NPY_ITER_ALLOCATE, NPY_ITER_NO_BROADCAST, NPY_ITER_NO_SUBTYPE, and NPY_ITER_WRITEONLY.

static int reduce_loop ( NpyIter iter,
char **  dataptrs,
npy_intp strides,
npy_intp countptr,
NpyIter_IterNextFunc iternext,
int  needs_api,
npy_intp  skip_first_count,
void *  data 
) [static]
The normal selected inner loop
Get the inner loop
Skip any first-visit elements
Turn the two items into three for the inner loop
Jump to the faster loop when skipping is done
Turn the two items into three for the inner loop
static int reduce_type_resolver ( PyUFuncObject ufunc,
PyArrayObject arr,
PyArray_Descr odtype,
PyArray_Descr **  out_dtype 
) [static]
If odtype is specified, make a type tuple for the type resolution.
Use the type resolution function to find our loop
The first two type should be equivalent. Because of how reduce has historically behaved in NumPy, the return type could be different, and it is the return type on which the reduction occurs.
static void trivial_three_operand_loop ( PyArrayObject **  op,
PyUFuncGenericFunction  innerloop,
void *  innerloopdata 
) [static]
static void trivial_two_operand_loop ( PyArrayObject **  op,
PyUFuncGenericFunction  innerloop,
void *  innerloopdata 
) [static]

Referenced by prepare_ufunc_output().

static PyObject* ufunc_accumulate ( PyUFuncObject ufunc,
PyObject *  args,
PyObject *  kwds 
) [static]
<cite>nin</cite>, the last arg, is unused. So we put 0.
static PyObject* ufunc_at ( PyUFuncObject ufunc,
PyObject *  args 
) [static]
Call ufunc only on selected array items and store result in first operand. For add ufunc, method call is equivalent to op1[idx] += op2 with no buffering of the first operand. Arguments: op1 - First operand to ufunc idx - Indices that are applied to first operand. Equivalent to op1[idx]. op2 - Second operand to ufunc (if needed). Must be able to broadcast

System Message: ERROR/3 (<string>, line 8) Unexpected indentation.

<blockquote> over first operand.</blockquote>

override vars
<cite>nin</cite>, the last arg, is unused. So we put 0.
Create second operand from number array if needed.
May need to swap axes so that second operand is iterated over correctly
Create array iter object for second operand that "matches" the map iter object for the first operand. Then we can just iterate over the first and second operands at the same time and not have to worry about picking the correct elements from each operand to apply the ufunc to.
Create dtypes array for either one or two input operands. The output operand is set to the first input operand
Set up the flags
Create NpyIter object to "iterate" over single element of each input operand. This is an easy way to reuse the NpyIter logic for dealing with certain cases like casting operands to correct dtype. On each iteration over the MapIterArray object created above, we'll take the current data pointers from that and reset this NpyIter object using those data pointers, and then trigger a buffer copy. The buffer data pointers from the NpyIter object will then be passed to the inner loop function.
Iterate over first and second operands and call ufunc for each pair of inputs
one element at a time, no stride required but read by innerloop
Set up data pointers for either one or two input operands. The output data pointer points to the first operand data.
Reset NpyIter data pointers which will trigger a buffer copy
Call to iternext triggers copy from buffer back to output array after innerloop puts result in buffer.
static PyObject* ufunc_generic_call ( PyUFuncObject ufunc,
PyObject *  args,
PyObject *  kwds 
) [static]
Initialize all array objects to NULL to make cleanup easier if something goes wrong.
For array_richcompare's benefit -- see the long comment in get_ufunc_arguments.
Free the input references
Use __array_wrap__ on all outputs if present on one of the input arguments. If present for multiple inputs: use __array_wrap__ of input object with largest __array_priority__ (default = 0.0)
Exception: we should not wrap outputs for items already passed in as output-arguments. These items should either be left unwrapped or wrapped by calling their own __array_wrap__ routine.
For each output argument, wrap will be either NULL --- call PyArray_Return() -- default if no output arguments given None --- array-object passed in don't call PyArray_Return method --- the __array_wrap__ method to call.
wrap outputs
Handle __array_wrap__ that does not accept a context argument
default behavior
static PyObject* ufunc_get_doc ( PyUFuncObject ufunc) [static]
Put docstring first or FindMethod finds it... could so some introspection on name and nin + nout to automate the first part of it the doc string shouldn't need the calling convention construct name(x1, x2, ...,[ out1, out2, ...]) __doc__
static PyObject* ufunc_get_identity ( PyUFuncObject ufunc) [static]

Referenced by ufunc_dealloc().

static PyObject* ufunc_get_name ( PyUFuncObject ufunc) [static]

Referenced by ufunc_dealloc().

static PyObject* ufunc_get_nargs ( PyUFuncObject ufunc) [static]
static PyObject* ufunc_get_nin ( PyUFuncObject ufunc) [static]
static PyObject* ufunc_get_nout ( PyUFuncObject ufunc) [static]
static PyObject* ufunc_get_ntypes ( PyUFuncObject ufunc) [static]
static PyObject* ufunc_get_signature ( PyUFuncObject ufunc) [static]

Referenced by ufunc_dealloc().

static PyObject* ufunc_get_types ( PyUFuncObject ufunc) [static]
return a list with types grouped input->output

Referenced by ufunc_dealloc().

NPY_NO_EXPORT PyObject* ufunc_geterr ( PyObject *  NPY_UNUSEDdummy,
PyObject *  args 
)
Construct list of defaults

References PyUFunc_CheckOverride(), PyUFunc_GenericReduction(), and UFUNC_REDUCE.

Referenced by intern_strings().

static PyObject* ufunc_outer ( PyUFuncObject ufunc,
PyObject *  args,
PyObject *  kwds 
) [static]
System Message: SEVERE/4 (<string>, line 1)
Title overline & underline mismatch.

                          UFUNC METHODS                                 ***
 
op.outer(a,b) is equivalent to op(a[:,NewAxis,NewAxis,etc.],b) where a has b.ndim NewAxis terms appended.
The result has dimensions a.ndim + b.ndim
<cite>nin</cite>, the last arg, is unused. So we put 0.
Construct new shape tuple
static PyObject* ufunc_reduce ( PyUFuncObject ufunc,
PyObject *  args,
PyObject *  kwds 
) [static]
<cite>nin</cite>, the last arg, is unused. So we put 0.
static PyObject* ufunc_reduceat ( PyUFuncObject ufunc,
PyObject *  args,
PyObject *  kwds 
) [static]
<cite>nin</cite>, the last arg, is unused. So we put 0.
static PyObject* ufunc_repr ( PyUFuncObject ufunc) [static]
NPY_NO_EXPORT PyObject* ufunc_seterr ( PyObject *  NPY_UNUSEDdummy,
PyObject *  args 
)

References NPY_MAXARGS.

Referenced by intern_strings().

static int ufunc_update_use_defaults ( void  ) [static]
This is a strategy to buy a little speed up and avoid the dictionary look-up in the default case. It should work in the presence of threads. If it is deemed too complicated or it doesn't actually work it could be taken out.

Variable Documentation

int PyUFunc_NUM_NODEFAULTS = 0 [static]
System Message: SEVERE/4 (<string>, line 1)
Title overline & underline mismatch.

                        UFUNC TYPE OBJECT                               ***
 

Referenced by ufunc_frompyfunc().

PyGetSetDef ufunc_getset[] [static]
Initial value:
 {
    {"__doc__",
        (getter)ufunc_get_doc,
        NULL, NULL, NULL},
    {"nin",
        (getter)ufunc_get_nin,
        NULL, NULL, NULL},
    {"nout",
        (getter)ufunc_get_nout,
        NULL, NULL, NULL},
    {"nargs",
        (getter)ufunc_get_nargs,
        NULL, NULL, NULL},
    {"ntypes",
        (getter)ufunc_get_ntypes,
        NULL, NULL, NULL},
    {"types",
        (getter)ufunc_get_types,
        NULL, NULL, NULL},
    {"__name__",
        (getter)ufunc_get_name,
        NULL, NULL, NULL},
    {"identity",
        (getter)ufunc_get_identity,
        NULL, NULL, NULL},
    {"signature",
        (getter)ufunc_get_signature,
        NULL, NULL, NULL},
    {NULL, NULL, NULL, NULL, NULL},  
}
Docstring is now set from python static char *Ufunctype__doc__ = NULL;

System Message: WARNING/2 (<string>, line 1); backlink Inline emphasis start-string without end-string.
struct PyMethodDef ufunc_methods[] [static]
Initial value:
 {
    {"reduce",
        (PyCFunction)ufunc_reduce,
        METH_VARARGS | METH_KEYWORDS, NULL },
    {"accumulate",
        (PyCFunction)ufunc_accumulate,
        METH_VARARGS | METH_KEYWORDS, NULL },
    {"reduceat",
        (PyCFunction)ufunc_reduceat,
        METH_VARARGS | METH_KEYWORDS, NULL },
    {"outer",
        (PyCFunction)ufunc_outer,
        METH_VARARGS | METH_KEYWORDS, NULL},
    {"at",
        (PyCFunction)ufunc_at,
        METH_VARARGS, NULL},
    {NULL, NULL, 0, NULL}           
}