""" Generate the code to build all the internal ufuncs. At the base is the defdict: a dictionary ofUfunc classes. This is fed to make_code to generate __umath_generated.c """ import argparse import os import re import textwrap # identity objects Zero = "PyLong_FromLong(0)" One = "PyLong_FromLong(1)" True_ = "(Py_INCREF(Py_True), Py_True)" False_ = "(Py_INCREF(Py_False), Py_False)" None_ = object() AllOnes = "PyLong_FromLong(-1)" MinusInfinity = 'PyFloat_FromDouble(-NPY_INFINITY)' ReorderableNone = "(Py_INCREF(Py_None), Py_None)" class docstrings: @staticmethod def get(place): """ Returns the C #definition name of docstring according to ufunc place. C #definitions are generated by generate_umath_doc.py in a separate C header. """ return 'DOC_' + place.upper().replace('.', '_') # Sentinel value to specify using the full type description in the # function name class FullTypeDescr: pass class FuncNameSuffix: """Stores the suffix to append when generating functions names. """ def __init__(self, suffix): self.suffix = suffix class TypeDescription: """Type signature for a ufunc. Attributes ---------- type : str Character representing the nominal type. func_data : str or None or FullTypeDescr or FuncNameSuffix, optional The string representing the expression to insert into the data array, if any. in_ : str or None, optional The typecode(s) of the inputs. out : str or None, optional The typecode(s) of the outputs. astype : dict or None, optional If astype['x'] is 'y', uses PyUFunc_x_x_As_y_y/PyUFunc_xx_x_As_yy_y instead of PyUFunc_x_x/PyUFunc_xx_x. cfunc_alias : str or none, optional Appended to inner loop C function name, e.g., FLOAT_{cfunc_alias}. See make_arrays. NOTE: it doesn't support 'astype' dispatch : str or None, optional Dispatch-able source name without its extension '.dispatch.c' that contains the definition of ufunc, dispatched at runtime depending on the specified targets of the dispatch-able source. NOTE: it doesn't support 'astype' """ def __init__(self, type, f=None, in_=None, out=None, astype=None, cfunc_alias=None, dispatch=None): self.type = type self.func_data = f if astype is None: astype = {} self.astype_dict = astype if in_ is not None: in_ = in_.replace('P', type) self.in_ = in_ if out is not None: out = out.replace('P', type) self.out = out self.cfunc_alias = cfunc_alias self.dispatch = dispatch def finish_signature(self, nin, nout): if self.in_ is None: self.in_ = self.type * nin assert len(self.in_) == nin if self.out is None: self.out = self.type * nout assert len(self.out) == nout self.astype = self.astype_dict.get(self.type, None) def _check_order(types1, types2): """ Helper to check that the loop types are ordered. The legacy type resolver (and potentially downstream) may pick use the first loop to which operands can be cast safely. """ # Insert kK (int64) after all other ints (assumes long long isn't larger) dtype_order = bints + 'kK' + times + flts + cmplxP + "O" for t1, t2 in zip(types1, types2): # We have no opinion on object or time ordering for now: if t1 in "OP" or t2 in "OP": return True if t1 in "mM" or t2 in "mM": return True t1i = dtype_order.index(t1) t2i = dtype_order.index(t2) if t1i < t2i: return if t2i > t1i: break if types1 == "QQ?" and types2 == "qQ?": # Explicitly allow this mixed case, rather than figure out what order # is nicer or how to encode it. return raise TypeError( f"Input dtypes are unsorted or duplicate: {types1} and {types2}") def check_td_order(tds): # A quick check for whether the signatures make sense, it happened too # often that SIMD additions added loops that do not even make some sense. # TODO: This should likely be a test and it would be nice if it rejected # duplicate entries as well (but we have many as of writing this). signatures = [t.in_ + t.out for t in tds] for prev_i, sign in enumerate(signatures[1:]): if sign in signatures[:prev_i + 1]: continue # allow duplicates... _check_order(signatures[prev_i], sign) _floatformat_map = { "e": 'npy_%sf', "f": 'npy_%sf', "d": 'npy_%s', "g": 'npy_%sl', "F": 'nc_%sf', "D": 'nc_%s', "G": 'nc_%sl' } def build_func_data(types, f): func_data = [_floatformat_map.get(t, '%s') % (f,) for t in types] return func_data def TD(types, f=None, astype=None, in_=None, out=None, cfunc_alias=None, dispatch=None): """ Generate a TypeDescription instance for each item in types """ if f is not None: if isinstance(f, str): func_data = build_func_data(types, f) elif len(f) != len(types): raise ValueError("Number of types and f do not match") else: func_data = f else: func_data = (None,) * len(types) if isinstance(in_, str): in_ = (in_,) * len(types) elif in_ is None: in_ = (None,) * len(types) elif len(in_) != len(types): raise ValueError("Number of types and inputs do not match") if isinstance(out, str): out = (out,) * len(types) elif out is None: out = (None,) * len(types) elif len(out) != len(types): raise ValueError("Number of types and outputs do not match") tds = [] for t, fd, i, o in zip(types, func_data, in_, out): # [(dispatch file name without extension '.dispatch.c*', list of types)] if dispatch: dispt = ([k for k, v in dispatch if t in v] + [None])[0] else: dispt = None tds.append(TypeDescription( t, f=fd, in_=i, out=o, astype=astype, cfunc_alias=cfunc_alias, dispatch=dispt )) return tds class Ufunc: """Description of a ufunc. Attributes ---------- nin : number of input arguments nout : number of output arguments identity : identity element for a two-argument function (like Zero) docstring : docstring for the ufunc typereso: type resolver function of type PyUFunc_TypeResolutionFunc type_descriptions : TypeDescription objects signature: a generalized ufunc signature (like for matmul) indexed: add indexed loops (ufunc.at) for these type characters """ def __init__(self, nin, nout, identity, docstring, typereso, *type_descriptions, signature=None, indexed=''): self.nin = nin self.nout = nout if identity is None: identity = None_ self.identity = identity self.docstring = docstring self.typereso = typereso self.type_descriptions = [] self.signature = signature self.indexed = indexed for td in type_descriptions: self.type_descriptions.extend(td) for td in self.type_descriptions: td.finish_signature(self.nin, self.nout) check_td_order(self.type_descriptions) # String-handling utilities to avoid locale-dependence. import string UPPER_TABLE = bytes.maketrans(bytes(string.ascii_lowercase, "ascii"), bytes(string.ascii_uppercase, "ascii")) def english_upper(s): """ Apply English case rules to convert ASCII strings to all upper case. This is an internal utility function to replace calls to str.upper() such that we can avoid changing behavior with changing locales. In particular, Turkish has distinct dotted and dotless variants of the Latin letter "I" in both lowercase and uppercase. Thus, "i".upper() != "I" in a "tr" locale. Parameters ---------- s : str Returns ------- uppered : str Examples -------- >>> import numpy as np >>> from numpy.lib.utils import english_upper >>> s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_' >>> english_upper(s) 'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_' >>> english_upper('') '' """ uppered = s.translate(UPPER_TABLE) return uppered # each entry in defdict is a Ufunc object. # name: [string of chars for which it is defined, # string of characters using func interface, # tuple of strings giving funcs for data, # (in, out), or (instr, outstr) giving the signature as character codes, # identity, # docstring, # output specification (optional) # ] chartoname = { '?': 'bool', 'b': 'byte', 'B': 'ubyte', 'h': 'short', 'H': 'ushort', 'i': 'int', 'I': 'uint', 'l': 'long', 'L': 'ulong', # We sometimes need int64, but we have no obvious char for it, use k and # define it as `int64` below. 'k': 'int64', 'K': 'uint64', 'q': 'longlong', 'Q': 'ulonglong', 'e': 'half', 'f': 'float', 'd': 'double', 'g': 'longdouble', 'F': 'cfloat', 'D': 'cdouble', 'G': 'clongdouble', 'M': 'datetime', 'm': 'timedelta', 'O': 'OBJECT', # '.' is like 'O', but calls a method of the object instead # of a function 'P': 'OBJECT', } no_obj_bool = 'bBhHiIlLqQefdgFDGmM' noobj = '?' + no_obj_bool all = '?bBhHiIlLqQefdgFDGOmM' O = 'O' P = 'P' ints = 'bBhHiIlLqQ' sints = 'bhilq' uints = 'BHILQ' times = 'Mm' timedeltaonly = 'm' intsO = ints + O bints = '?' + ints bintsO = bints + O flts = 'efdg' fltsO = flts + O fltsP = flts + P cmplx = 'FDG' cmplxvec = 'FD' cmplxO = cmplx + O cmplxP = cmplx + P inexact = flts + cmplx inexactvec = 'fd' noint = inexact + O nointP = inexact + P allP = bints + times + flts + cmplxP nobool_or_obj = noobj[1:] nobool_or_datetime = noobj[1:-1] + O # includes m - timedelta64 intflt = ints + flts intfltcmplx = ints + flts + cmplx nocmplx = bints + times + flts nocmplxO = nocmplx + O nocmplxP = nocmplx + P notimes_or_obj = bints + inexact nodatetime_or_obj = bints + inexact no_bool_times_obj = ints + inexact # Find which code corresponds to int64. int64 = 'k' uint64 = 'K' # This dictionary describes all the ufunc implementations, generating # all the function names and their corresponding ufunc signatures. TD is # an object which expands a list of character codes into an array of # TypeDescriptions. defdict = { 'add': Ufunc(2, 1, Zero, docstrings.get('numpy._core.umath.add'), 'PyUFunc_AdditionTypeResolver', TD('?', cfunc_alias='logical_or', dispatch=[('loops_logical', '?')]), TD(no_bool_times_obj, dispatch=[ ('loops_arithm_fp', 'fdFD'), ('loops_autovec', ints), ]), [TypeDescription('M', FullTypeDescr, 'Mm', 'M'), TypeDescription('m', FullTypeDescr, 'mm', 'm'), TypeDescription('M', FullTypeDescr, 'mM', 'M'), ], TD(O, f='PyNumber_Add'), indexed=intfltcmplx ), 'subtract': Ufunc(2, 1, None, # Zero is only a unit to the right, not the left docstrings.get('numpy._core.umath.subtract'), 'PyUFunc_SubtractionTypeResolver', TD(no_bool_times_obj, dispatch=[ ('loops_arithm_fp', 'fdFD'), ('loops_autovec', ints), ]), [TypeDescription('M', FullTypeDescr, 'Mm', 'M'), TypeDescription('m', FullTypeDescr, 'mm', 'm'), TypeDescription('M', FullTypeDescr, 'MM', 'm'), ], TD(O, f='PyNumber_Subtract'), indexed=intfltcmplx ), 'multiply': Ufunc(2, 1, One, docstrings.get('numpy._core.umath.multiply'), 'PyUFunc_MultiplicationTypeResolver', TD('?', cfunc_alias='logical_and', dispatch=[('loops_logical', '?')]), TD(no_bool_times_obj, dispatch=[ ('loops_arithm_fp', 'fdFD'), ('loops_autovec', ints), ]), [TypeDescription('m', FullTypeDescr, 'mq', 'm'), TypeDescription('m', FullTypeDescr, 'qm', 'm'), TypeDescription('m', FullTypeDescr, 'md', 'm'), TypeDescription('m', FullTypeDescr, 'dm', 'm'), ], TD(O, f='PyNumber_Multiply'), indexed=intfltcmplx ), # 'true_divide' : aliased to divide in umathmodule.c:initumath 'floor_divide': Ufunc(2, 1, None, # One is only a unit to the right, not the left docstrings.get('numpy._core.umath.floor_divide'), 'PyUFunc_DivisionTypeResolver', TD(ints, cfunc_alias='divide', dispatch=[('loops_arithmetic', 'bBhHiIlLqQ')]), TD(flts), [TypeDescription('m', FullTypeDescr, 'mq', 'm'), TypeDescription('m', FullTypeDescr, 'md', 'm'), TypeDescription('m', FullTypeDescr, 'mm', 'q'), ], TD(O, f='PyNumber_FloorDivide'), indexed=flts + ints ), 'divide': Ufunc(2, 1, None, # One is only a unit to the right, not the left docstrings.get('numpy._core.umath.divide'), 'PyUFunc_TrueDivisionTypeResolver', TD(flts + cmplx, cfunc_alias='divide', dispatch=[('loops_arithm_fp', 'fd')]), [TypeDescription('m', FullTypeDescr, 'mq', 'm', cfunc_alias='divide'), TypeDescription('m', FullTypeDescr, 'md', 'm', cfunc_alias='divide'), TypeDescription('m', FullTypeDescr, 'mm', 'd', cfunc_alias='divide'), ], TD(O, f='PyNumber_TrueDivide'), indexed=flts ), 'conjugate': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.conjugate'), None, TD(ints + flts + cmplx, dispatch=[ ('loops_arithm_fp', 'FD'), ('loops_autovec', ints), ]), TD(P, f='conjugate'), ), 'fmod': Ufunc(2, 1, None, docstrings.get('numpy._core.umath.fmod'), None, TD(ints, dispatch=[('loops_modulo', ints)]), TD(flts, f='fmod', astype={'e': 'f'}), TD(P, f='fmod'), ), 'square': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.square'), None, TD(ints + inexact, dispatch=[ ('loops_unary_fp', 'fd'), ('loops_arithm_fp', 'FD'), ('loops_autovec', ints), ]), TD(O, f='Py_square'), ), 'reciprocal': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.reciprocal'), None, TD(ints + inexact, dispatch=[ ('loops_unary_fp', 'fd'), ('loops_autovec', ints), ]), TD(O, f='Py_reciprocal'), ), # This is no longer used as numpy.ones_like, however it is # still used by some internal calls. '_ones_like': Ufunc(1, 1, None, docstrings.get('numpy._core.umath._ones_like'), 'PyUFunc_OnesLikeTypeResolver', TD(noobj), TD(O, f='Py_get_one'), ), 'power': Ufunc(2, 1, None, docstrings.get('numpy._core.umath.power'), None, TD(ints), TD('e', f='pow', astype={'e': 'f'}), TD('fd', dispatch=[('loops_umath_fp', 'fd')]), TD(inexact, f='pow', astype={'e': 'f'}), TD(O, f='npy_ObjectPower'), ), 'float_power': Ufunc(2, 1, None, docstrings.get('numpy._core.umath.float_power'), None, TD('dgDG', f='pow'), ), 'absolute': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.absolute'), 'PyUFunc_AbsoluteTypeResolver', TD(bints + flts + timedeltaonly, dispatch=[ ('loops_unary_fp', 'fd'), ('loops_logical', '?'), ('loops_autovec', ints + 'e'), ]), TD(cmplx, dispatch=[('loops_unary_complex', 'FD')], out=('f', 'd', 'g')), TD(O, f='PyNumber_Absolute'), ), '_arg': Ufunc(1, 1, None, docstrings.get('numpy._core.umath._arg'), None, TD(cmplx, out=('f', 'd', 'g')), ), 'negative': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.negative'), 'PyUFunc_NegativeTypeResolver', TD(ints + flts + timedeltaonly, dispatch=[('loops_unary', ints + 'fdg')]), TD(cmplx, f='neg'), TD(O, f='PyNumber_Negative'), ), 'positive': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.positive'), 'PyUFunc_SimpleUniformOperationTypeResolver', TD(ints + flts + timedeltaonly), TD(cmplx, f='pos'), TD(O, f='PyNumber_Positive'), ), 'sign': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.sign'), 'PyUFunc_SimpleUniformOperationTypeResolver', TD(nobool_or_datetime, dispatch=[('loops_autovec', ints)]), ), 'greater': Ufunc(2, 1, None, docstrings.get('numpy._core.umath.greater'), 'PyUFunc_SimpleBinaryComparisonTypeResolver', TD(bints, out='?'), [TypeDescription('q', FullTypeDescr, 'qQ', '?'), TypeDescription('q', FullTypeDescr, 'Qq', '?')], TD(inexact + times, out='?', dispatch=[('loops_comparison', bints + 'fd')]), TD('O', out='?'), [TypeDescription('O', FullTypeDescr, 'OO', 'O')], ), 'greater_equal': Ufunc(2, 1, None, docstrings.get('numpy._core.umath.greater_equal'), 'PyUFunc_SimpleBinaryComparisonTypeResolver', TD(bints, out='?'), [TypeDescription('q', FullTypeDescr, 'qQ', '?'), TypeDescription('q', FullTypeDescr, 'Qq', '?')], TD(inexact + times, out='?', dispatch=[('loops_comparison', bints + 'fd')]), TD('O', out='?'), [TypeDescription('O', FullTypeDescr, 'OO', 'O')], ), 'less': Ufunc(2, 1, None, docstrings.get('numpy._core.umath.less'), 'PyUFunc_SimpleBinaryComparisonTypeResolver', TD(bints, out='?'), [TypeDescription('q', FullTypeDescr, 'qQ', '?'), TypeDescription('q', FullTypeDescr, 'Qq', '?')], TD(inexact + times, out='?', dispatch=[('loops_comparison', bints + 'fd')]), TD('O', out='?'), [TypeDescription('O', FullTypeDescr, 'OO', 'O')], ), 'less_equal': Ufunc(2, 1, None, docstrings.get('numpy._core.umath.less_equal'), 'PyUFunc_SimpleBinaryComparisonTypeResolver', TD(bints, out='?'), [TypeDescription('q', FullTypeDescr, 'qQ', '?'), TypeDescription('q', FullTypeDescr, 'Qq', '?')], TD(inexact + times, out='?', dispatch=[('loops_comparison', bints + 'fd')]), TD('O', out='?'), [TypeDescription('O', FullTypeDescr, 'OO', 'O')], ), 'equal': Ufunc(2, 1, None, docstrings.get('numpy._core.umath.equal'), 'PyUFunc_SimpleBinaryComparisonTypeResolver', TD(bints, out='?'), [TypeDescription('q', FullTypeDescr, 'qQ', '?'), TypeDescription('q', FullTypeDescr, 'Qq', '?')], TD(inexact + times, out='?', dispatch=[('loops_comparison', bints + 'fd')]), TD('O', out='?'), [TypeDescription('O', FullTypeDescr, 'OO', 'O')], ), 'not_equal': Ufunc(2, 1, None, docstrings.get('numpy._core.umath.not_equal'), 'PyUFunc_SimpleBinaryComparisonTypeResolver', TD(bints, out='?'), [TypeDescription('q', FullTypeDescr, 'qQ', '?'), TypeDescription('q', FullTypeDescr, 'Qq', '?')], TD(inexact + times, out='?', dispatch=[('loops_comparison', bints + 'fd')]), TD('O', out='?'), [TypeDescription('O', FullTypeDescr, 'OO', 'O')], ), 'logical_and': Ufunc(2, 1, True_, docstrings.get('numpy._core.umath.logical_and'), 'PyUFunc_SimpleBinaryComparisonTypeResolver', TD(nodatetime_or_obj, out='?', dispatch=[ ('loops_logical', '?'), ('loops_autovec', ints), ]), TD(O, f='npy_ObjectLogicalAnd'), ), 'logical_not': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.logical_not'), None, TD(nodatetime_or_obj, out='?', dispatch=[ ('loops_logical', '?'), ('loops_autovec', ints), ]), TD(O, f='npy_ObjectLogicalNot'), ), 'logical_or': Ufunc(2, 1, False_, docstrings.get('numpy._core.umath.logical_or'), 'PyUFunc_SimpleBinaryComparisonTypeResolver', TD(nodatetime_or_obj, out='?', dispatch=[ ('loops_logical', '?'), ('loops_autovec', ints), ]), TD(O, f='npy_ObjectLogicalOr'), ), 'logical_xor': Ufunc(2, 1, False_, docstrings.get('numpy._core.umath.logical_xor'), 'PyUFunc_SimpleBinaryComparisonTypeResolver', TD('?', out='?', cfunc_alias='not_equal', dispatch=[('loops_comparison', '?')]), TD(no_bool_times_obj, out='?', dispatch=[ ('loops_autovec', ints), ]), # TODO: using obj.logical_xor() seems pretty much useless: TD(P, f='logical_xor'), ), 'maximum': Ufunc(2, 1, ReorderableNone, docstrings.get('numpy._core.umath.maximum'), 'PyUFunc_SimpleUniformOperationTypeResolver', TD('?', cfunc_alias='logical_or', dispatch=[('loops_logical', '?')]), TD(no_obj_bool, dispatch=[('loops_minmax', ints + 'fdg')]), TD(O, f='npy_ObjectMax'), indexed=flts + ints, ), 'minimum': Ufunc(2, 1, ReorderableNone, docstrings.get('numpy._core.umath.minimum'), 'PyUFunc_SimpleUniformOperationTypeResolver', TD('?', cfunc_alias='logical_and', dispatch=[('loops_logical', '?')]), TD(no_obj_bool, dispatch=[('loops_minmax', ints + 'fdg')]), TD(O, f='npy_ObjectMin'), indexed=flts + ints, ), 'clip': Ufunc(3, 1, ReorderableNone, docstrings.get('numpy._core.umath.clip'), 'PyUFunc_SimpleUniformOperationTypeResolver', TD(noobj), [TypeDescription('O', 'npy_ObjectClip', 'OOO', 'O')] ), 'fmax': Ufunc(2, 1, ReorderableNone, docstrings.get('numpy._core.umath.fmax'), 'PyUFunc_SimpleUniformOperationTypeResolver', TD('?', cfunc_alias='logical_or', dispatch=[('loops_logical', '?')]), TD(no_obj_bool, dispatch=[('loops_minmax', 'fdg')]), TD(O, f='npy_ObjectMax'), indexed=flts + ints, ), 'fmin': Ufunc(2, 1, ReorderableNone, docstrings.get('numpy._core.umath.fmin'), 'PyUFunc_SimpleUniformOperationTypeResolver', TD('?', cfunc_alias='logical_and', dispatch=[('loops_logical', '?')]), TD(no_obj_bool, dispatch=[('loops_minmax', 'fdg')]), TD(O, f='npy_ObjectMin'), indexed=flts + ints, ), 'logaddexp': Ufunc(2, 1, MinusInfinity, docstrings.get('numpy._core.umath.logaddexp'), None, TD(flts, f="logaddexp", astype={'e': 'f'}) ), 'logaddexp2': Ufunc(2, 1, MinusInfinity, docstrings.get('numpy._core.umath.logaddexp2'), None, TD(flts, f="logaddexp2", astype={'e': 'f'}) ), 'bitwise_and': Ufunc(2, 1, AllOnes, docstrings.get('numpy._core.umath.bitwise_and'), None, TD('?', cfunc_alias='logical_and', dispatch=[('loops_logical', '?')]), TD(ints, dispatch=[('loops_autovec', ints)]), TD(O, f='PyNumber_And'), ), 'bitwise_or': Ufunc(2, 1, Zero, docstrings.get('numpy._core.umath.bitwise_or'), None, TD('?', cfunc_alias='logical_or', dispatch=[('loops_logical', '?')]), TD(ints, dispatch=[('loops_autovec', ints)]), TD(O, f='PyNumber_Or'), ), 'bitwise_xor': Ufunc(2, 1, Zero, docstrings.get('numpy._core.umath.bitwise_xor'), None, TD('?', cfunc_alias='not_equal', dispatch=[('loops_comparison', '?')]), TD(ints, dispatch=[('loops_autovec', ints)]), TD(O, f='PyNumber_Xor'), ), 'invert': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.invert'), None, TD('?', cfunc_alias='logical_not', dispatch=[('loops_logical', '?')]), TD(ints, dispatch=[('loops_autovec', ints)]), TD(O, f='PyNumber_Invert'), ), 'left_shift': Ufunc(2, 1, None, docstrings.get('numpy._core.umath.left_shift'), None, TD(ints, dispatch=[('loops_autovec', ints)]), TD(O, f='PyNumber_Lshift'), ), 'right_shift': Ufunc(2, 1, None, docstrings.get('numpy._core.umath.right_shift'), None, TD(ints, dispatch=[('loops_autovec', ints)]), TD(O, f='PyNumber_Rshift'), ), 'heaviside': Ufunc(2, 1, None, docstrings.get('numpy._core.umath.heaviside'), None, TD(flts, f='heaviside', astype={'e': 'f'}), ), 'degrees': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.degrees'), None, TD(fltsP, f='degrees', astype={'e': 'f'}), ), 'rad2deg': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.rad2deg'), None, TD(fltsP, f='rad2deg', astype={'e': 'f'}), ), 'radians': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.radians'), None, TD(fltsP, f='radians', astype={'e': 'f'}), ), 'deg2rad': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.deg2rad'), None, TD(fltsP, f='deg2rad', astype={'e': 'f'}), ), 'arccos': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.arccos'), None, TD('efd', dispatch=[('loops_umath_fp', 'fd'), ('loops_half', 'e')]), TD(inexact, f='acos', astype={'e': 'f'}), TD(P, f='arccos'), ), 'arccosh': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.arccosh'), None, TD('efd', dispatch=[('loops_umath_fp', 'fd'), ('loops_half', 'e')]), TD(inexact, f='acosh', astype={'e': 'f'}), TD(P, f='arccosh'), ), 'arcsin': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.arcsin'), None, TD('efd', dispatch=[('loops_umath_fp', 'fd'), ('loops_half', 'e')]), TD(inexact, f='asin', astype={'e': 'f'}), TD(P, f='arcsin'), ), 'arcsinh': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.arcsinh'), None, TD('efd', dispatch=[('loops_umath_fp', 'fd'), ('loops_half', 'e')]), TD(inexact, f='asinh', astype={'e': 'f'}), TD(P, f='arcsinh'), ), 'arctan': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.arctan'), None, TD('efd', dispatch=[('loops_umath_fp', 'fd'), ('loops_half', 'e')]), TD(inexact, f='atan', astype={'e': 'f'}), TD(P, f='arctan'), ), 'arctanh': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.arctanh'), None, TD('efd', dispatch=[('loops_umath_fp', 'fd'), ('loops_half', 'e')]), TD(inexact, f='atanh', astype={'e': 'f'}), TD(P, f='arctanh'), ), 'cos': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.cos'), None, TD('e', dispatch=[('loops_half', 'e')]), TD('f', dispatch=[('loops_trigonometric', 'f')]), TD('d', dispatch=[('loops_trigonometric', 'd')]), TD('g' + cmplx, f='cos'), TD(P, f='cos'), ), 'sin': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.sin'), None, TD('e', dispatch=[('loops_half', 'e')]), TD('f', dispatch=[('loops_trigonometric', 'f')]), TD('d', dispatch=[('loops_trigonometric', 'd')]), TD('g' + cmplx, f='sin'), TD(P, f='sin'), ), 'tan': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.tan'), None, TD('efd', dispatch=[('loops_umath_fp', 'fd'), ('loops_half', 'e')]), TD(inexact, f='tan', astype={'e': 'f'}), TD(P, f='tan'), ), 'cosh': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.cosh'), None, TD('efd', dispatch=[('loops_umath_fp', 'fd'), ('loops_half', 'e')]), TD(inexact, f='cosh', astype={'e': 'f'}), TD(P, f='cosh'), ), 'sinh': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.sinh'), None, TD('efd', dispatch=[('loops_umath_fp', 'fd'), ('loops_half', 'e')]), TD(inexact, f='sinh', astype={'e': 'f'}), TD(P, f='sinh'), ), 'tanh': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.tanh'), None, TD('e', dispatch=[('loops_half', 'e')]), TD('fd', dispatch=[('loops_hyperbolic', 'fd')]), TD(inexact, f='tanh', astype={'e': 'f'}), TD(P, f='tanh'), ), 'exp': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.exp'), None, TD('e', dispatch=[('loops_half', 'e')]), TD('fd', dispatch=[('loops_exponent_log', 'fd')]), TD('fdg' + cmplx, f='exp'), TD(P, f='exp'), ), 'exp2': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.exp2'), None, TD('efd', dispatch=[('loops_umath_fp', 'fd'), ('loops_half', 'e')]), TD(inexact, f='exp2', astype={'e': 'f'}), TD(P, f='exp2'), ), 'expm1': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.expm1'), None, TD('efd', dispatch=[('loops_umath_fp', 'fd'), ('loops_half', 'e')]), TD(inexact, f='expm1', astype={'e': 'f'}), TD(P, f='expm1'), ), 'log': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.log'), None, TD('e', dispatch=[('loops_half', 'e')]), TD('fd', dispatch=[('loops_exponent_log', 'fd')]), TD('fdg' + cmplx, f='log'), TD(P, f='log'), ), 'log2': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.log2'), None, TD('efd', dispatch=[('loops_umath_fp', 'fd'), ('loops_half', 'e')]), TD(inexact, f='log2', astype={'e': 'f'}), TD(P, f='log2'), ), 'log10': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.log10'), None, TD('efd', dispatch=[('loops_umath_fp', 'fd'), ('loops_half', 'e')]), TD(inexact, f='log10', astype={'e': 'f'}), TD(P, f='log10'), ), 'log1p': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.log1p'), None, TD('efd', dispatch=[('loops_umath_fp', 'fd'), ('loops_half', 'e')]), TD(inexact, f='log1p', astype={'e': 'f'}), TD(P, f='log1p'), ), 'sqrt': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.sqrt'), None, TD('e', f='sqrt', astype={'e': 'f'}), TD(inexactvec, dispatch=[('loops_unary_fp', 'fd')]), TD('fdg' + cmplx, f='sqrt'), TD(P, f='sqrt'), ), 'cbrt': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.cbrt'), None, TD('efd', dispatch=[('loops_umath_fp', 'fd'), ('loops_half', 'e')]), TD(flts, f='cbrt', astype={'e': 'f'}), TD(P, f='cbrt'), ), 'ceil': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.ceil'), None, TD(bints), TD('e', f='ceil', astype={'e': 'f'}), TD(inexactvec, dispatch=[('loops_unary_fp', 'fd')]), TD('fdg', f='ceil'), TD(O, f='npy_ObjectCeil'), ), 'trunc': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.trunc'), None, TD(bints), TD('e', f='trunc', astype={'e': 'f'}), TD(inexactvec, dispatch=[('loops_unary_fp', 'fd')]), TD('fdg', f='trunc'), TD(O, f='npy_ObjectTrunc'), ), 'fabs': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.fabs'), None, TD(flts, f='fabs', astype={'e': 'f'}), TD(P, f='fabs'), ), 'floor': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.floor'), None, TD(bints), TD('e', f='floor', astype={'e': 'f'}), TD(inexactvec, dispatch=[('loops_unary_fp', 'fd')]), TD('fdg', f='floor'), TD(O, f='npy_ObjectFloor'), ), 'rint': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.rint'), None, TD('e', f='rint', astype={'e': 'f'}), TD(inexactvec, dispatch=[('loops_unary_fp', 'fd')]), TD('fdg' + cmplx, f='rint'), TD(P, f='rint'), ), 'arctan2': Ufunc(2, 1, None, docstrings.get('numpy._core.umath.arctan2'), None, TD('e', f='atan2', astype={'e': 'f'}), TD('fd', dispatch=[('loops_umath_fp', 'fd')]), TD('g', f='atan2', astype={'e': 'f'}), TD(P, f='arctan2'), ), 'remainder': Ufunc(2, 1, None, docstrings.get('numpy._core.umath.remainder'), 'PyUFunc_RemainderTypeResolver', TD(ints, dispatch=[('loops_modulo', ints)]), TD(flts), [TypeDescription('m', FullTypeDescr, 'mm', 'm')], TD(O, f='PyNumber_Remainder'), ), 'divmod': Ufunc(2, 2, None, docstrings.get('numpy._core.umath.divmod'), 'PyUFunc_DivmodTypeResolver', TD(ints, dispatch=[('loops_modulo', ints)]), TD(flts), [TypeDescription('m', FullTypeDescr, 'mm', 'qm')], # TD(O, f='PyNumber_Divmod'), # gh-9730 ), 'hypot': Ufunc(2, 1, Zero, docstrings.get('numpy._core.umath.hypot'), None, TD(flts, f='hypot', astype={'e': 'f'}), TD(P, f='hypot'), ), 'isnan': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.isnan'), 'PyUFunc_IsFiniteTypeResolver', TD(noobj, out='?', dispatch=[ ('loops_unary_fp_le', inexactvec), ('loops_autovec', bints), ]), ), 'isnat': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.isnat'), 'PyUFunc_IsNaTTypeResolver', TD(times, out='?'), ), 'isinf': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.isinf'), 'PyUFunc_IsFiniteTypeResolver', TD(noobj, out='?', dispatch=[ ('loops_unary_fp_le', inexactvec), ('loops_autovec', bints + 'mM'), ]), ), 'isfinite': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.isfinite'), 'PyUFunc_IsFiniteTypeResolver', TD(noobj, out='?', dispatch=[ ('loops_unary_fp_le', inexactvec), ('loops_autovec', bints), ]), ), 'signbit': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.signbit'), None, TD(flts, out='?', dispatch=[('loops_unary_fp_le', inexactvec)]), ), 'copysign': Ufunc(2, 1, None, docstrings.get('numpy._core.umath.copysign'), None, TD(flts), ), 'nextafter': Ufunc(2, 1, None, docstrings.get('numpy._core.umath.nextafter'), None, TD(flts), ), 'spacing': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.spacing'), None, TD(flts), ), 'modf': Ufunc(1, 2, None, docstrings.get('numpy._core.umath.modf'), None, TD(flts), ), 'ldexp': Ufunc(2, 1, None, docstrings.get('numpy._core.umath.ldexp'), None, [TypeDescription('e', None, 'ei', 'e'), TypeDescription('f', None, 'fi', 'f', dispatch='loops_exponent_log'), TypeDescription('e', FuncNameSuffix('int64'), 'e' + int64, 'e'), TypeDescription('f', FuncNameSuffix('int64'), 'f' + int64, 'f'), TypeDescription('d', None, 'di', 'd', dispatch='loops_exponent_log'), TypeDescription('d', FuncNameSuffix('int64'), 'd' + int64, 'd'), TypeDescription('g', None, 'gi', 'g'), TypeDescription('g', FuncNameSuffix('int64'), 'g' + int64, 'g'), ], ), 'frexp': Ufunc(1, 2, None, docstrings.get('numpy._core.umath.frexp'), None, [TypeDescription('e', None, 'e', 'ei'), TypeDescription('f', None, 'f', 'fi', dispatch='loops_exponent_log'), TypeDescription('d', None, 'd', 'di', dispatch='loops_exponent_log'), TypeDescription('g', None, 'g', 'gi'), ], ), 'gcd': Ufunc(2, 1, Zero, docstrings.get('numpy._core.umath.gcd'), "PyUFunc_SimpleUniformOperationTypeResolver", TD(ints), TD('O', f='npy_ObjectGCD'), ), 'lcm': Ufunc(2, 1, None, docstrings.get('numpy._core.umath.lcm'), "PyUFunc_SimpleUniformOperationTypeResolver", TD(ints), TD('O', f='npy_ObjectLCM'), ), 'bitwise_count': Ufunc(1, 1, None, docstrings.get('numpy._core.umath.bitwise_count'), None, TD(ints, dispatch=[('loops_autovec', ints)], out='B'), TD(P, f='bit_count'), ), 'matmul': Ufunc(2, 1, None, docstrings.get('numpy._core.umath.matmul'), "PyUFunc_SimpleUniformOperationTypeResolver", TD(notimes_or_obj), TD(O), signature='(n?,k),(k,m?)->(n?,m?)', ), 'vecdot': Ufunc(2, 1, None, docstrings.get('numpy._core.umath.vecdot'), "PyUFunc_SimpleUniformOperationTypeResolver", TD(notimes_or_obj), TD(O), signature='(n),(n)->()', ), 'matvec': Ufunc(2, 1, None, docstrings.get('numpy._core.umath.matvec'), "PyUFunc_SimpleUniformOperationTypeResolver", TD(notimes_or_obj), TD(O), signature='(m,n),(n)->(m)', ), 'vecmat': Ufunc(2, 1, None, docstrings.get('numpy._core.umath.vecmat'), "PyUFunc_SimpleUniformOperationTypeResolver", TD(notimes_or_obj), TD(O), signature='(n),(n,m)->(m)', ), 'str_len': Ufunc(1, 1, Zero, docstrings.get('numpy._core.umath.str_len'), None, ), 'isalpha': Ufunc(1, 1, False_, docstrings.get('numpy._core.umath.isalpha'), None, ), 'isdigit': Ufunc(1, 1, False_, docstrings.get('numpy._core.umath.isdigit'), None, ), 'isspace': Ufunc(1, 1, False_, docstrings.get('numpy._core.umath.isspace'), None, ), 'isalnum': Ufunc(1, 1, False_, docstrings.get('numpy._core.umath.isalnum'), None, ), 'islower': Ufunc(1, 1, False_, docstrings.get('numpy._core.umath.islower'), None, ), 'isupper': Ufunc(1, 1, False_, docstrings.get('numpy._core.umath.isupper'), None, ), 'istitle': Ufunc(1, 1, False_, docstrings.get('numpy._core.umath.istitle'), None, ), 'isdecimal': Ufunc(1, 1, False_, docstrings.get('numpy._core.umath.isdecimal'), None, ), 'isnumeric': Ufunc(1, 1, False_, docstrings.get('numpy._core.umath.isnumeric'), None, ), 'find': Ufunc(4, 1, None, docstrings.get('numpy._core.umath.find'), None, ), 'rfind': Ufunc(4, 1, None, docstrings.get('numpy._core.umath.rfind'), None, ), 'count': Ufunc(4, 1, None, docstrings.get('numpy._core.umath.count'), None, ), 'index': Ufunc(4, 1, None, docstrings.get('numpy._core.umath.index'), None, ), 'rindex': Ufunc(4, 1, None, docstrings.get('numpy._core.umath.rindex'), None, ), '_replace': Ufunc(4, 1, None, docstrings.get('numpy._core.umath._replace'), None, ), 'startswith': Ufunc(4, 1, False_, docstrings.get('numpy._core.umath.startswith'), None, ), 'endswith': Ufunc(4, 1, False_, docstrings.get('numpy._core.umath.endswith'), None, ), '_strip_chars': Ufunc(2, 1, None, docstrings.get('numpy._core.umath._strip_chars'), None, ), '_lstrip_chars': Ufunc(2, 1, None, docstrings.get('numpy._core.umath._lstrip_chars'), None, ), '_rstrip_chars': Ufunc(2, 1, None, docstrings.get('numpy._core.umath._rstrip_chars'), None, ), '_strip_whitespace': Ufunc(1, 1, None, docstrings.get('numpy._core.umath._strip_whitespace'), None, ), '_lstrip_whitespace': Ufunc(1, 1, None, docstrings.get('numpy._core.umath._lstrip_whitespace'), None, ), '_rstrip_whitespace': Ufunc(1, 1, None, docstrings.get('numpy._core.umath._rstrip_whitespace'), None, ), '_expandtabs_length': Ufunc(2, 1, None, docstrings.get('numpy._core.umath._expandtabs_length'), None, ), '_expandtabs': Ufunc(2, 1, None, docstrings.get('numpy._core.umath._expandtabs'), None, ), '_center': Ufunc(3, 1, None, docstrings.get('numpy._core.umath._center'), None, ), '_ljust': Ufunc(3, 1, None, docstrings.get('numpy._core.umath._ljust'), None, ), '_rjust': Ufunc(3, 1, None, docstrings.get('numpy._core.umath._rjust'), None, ), '_zfill': Ufunc(2, 1, None, docstrings.get('numpy._core.umath._zfill'), None, ), '_partition_index': Ufunc(3, 3, None, docstrings.get('numpy._core.umath._partition_index'), None, ), '_rpartition_index': Ufunc(3, 3, None, docstrings.get('numpy._core.umath._rpartition_index'), None, ), '_partition': Ufunc(2, 3, None, docstrings.get('numpy._core.umath._partition'), None, ), '_rpartition': Ufunc(2, 3, None, docstrings.get('numpy._core.umath._rpartition'), None, ), '_slice': Ufunc(4, 1, None, docstrings.get('numpy._core.umath._slice'), None, ), } def indent(st, spaces): indentation = ' ' * spaces indented = indentation + st.replace('\n', '\n' + indentation) # trim off any trailing spaces indented = re.sub(r' +$', r'', indented) return indented # maps [nin, nout][type] to a suffix arity_lookup = { (1, 1): { 'e': 'e_e', 'f': 'f_f', 'd': 'd_d', 'g': 'g_g', 'F': 'F_F', 'D': 'D_D', 'G': 'G_G', 'O': 'O_O', 'P': 'O_O_method', }, (2, 1): { 'e': 'ee_e', 'f': 'ff_f', 'd': 'dd_d', 'g': 'gg_g', 'F': 'FF_F', 'D': 'DD_D', 'G': 'GG_G', 'O': 'OO_O', 'P': 'OO_O_method', }, (3, 1): { 'O': 'OOO_O', } } # for each name # 1) create functions, data, and signature # 2) fill in functions and data in InitOperators # 3) add function. def make_arrays(funcdict): # functions array contains an entry for every type implemented NULL # should be placed where PyUfunc_ style function will be filled in # later code1list = [] code2list = [] dispdict = {} names = sorted(funcdict.keys()) for name in names: uf = funcdict[name] funclist = [] datalist = [] siglist = [] sub = 0 for k, t in enumerate(uf.type_descriptions): cfunc_alias = t.cfunc_alias or name cfunc_fname = None if t.func_data is FullTypeDescr: tname = english_upper(chartoname[t.type]) datalist.append('(void *)NULL') if t.out == "?": cfunc_fname = f"{tname}_{t.in_}_bool_{cfunc_alias}" else: cfunc_fname = f"{tname}_{t.in_}_{t.out}_{cfunc_alias}" elif isinstance(t.func_data, FuncNameSuffix): datalist.append('(void *)NULL') tname = english_upper(chartoname[t.type]) cfunc_fname = f"{tname}_{cfunc_alias}_{t.func_data.suffix}" elif t.func_data is None: datalist.append('(void *)NULL') tname = english_upper(chartoname[t.type]) cfunc_fname = f"{tname}_{cfunc_alias}" else: try: thedict = arity_lookup[uf.nin, uf.nout] except KeyError as e: raise ValueError( f"Could not handle {name}[{t.type}] " f"with nin={uf.nin}, nout={uf.nout}" ) from None astype = '' if t.astype is not None: astype = f'_As_{thedict[t.astype]}' astr = ('%s_functions[%d] = PyUFunc_%s%s;' % (name, k, thedict[t.type], astype)) code2list.append(astr) if t.type == 'O': astr = ('%s_data[%d] = (void *) %s;' % (name, k, t.func_data)) code2list.append(astr) datalist.append('(void *)NULL') elif t.type == 'P': datalist.append(f'(void *)"{t.func_data}"') else: astr = ('%s_data[%d] = (void *) %s;' % (name, k, t.func_data)) code2list.append(astr) datalist.append('(void *)NULL') #datalist.append('(void *)%s' % t.func_data) sub += 1 if cfunc_fname: funclist.append(cfunc_fname) if t.dispatch: dispdict.setdefault(t.dispatch, []).append( (name, k, cfunc_fname, t.in_ + t.out) ) else: funclist.append('NULL') for x in t.in_ + t.out: siglist.append(f'NPY_{english_upper(chartoname[x])}') if funclist or siglist or datalist: funcnames = ', '.join(funclist) signames = ', '.join(siglist) datanames = ', '.join(datalist) code1list.append( "static PyUFuncGenericFunction %s_functions[] = {%s};" % (name, funcnames)) code1list.append("static void * %s_data[] = {%s};" % (name, datanames)) code1list.append("static const char %s_signatures[] = {%s};" % (name, signames)) uf.empty = False else: uf.empty = True for dname, funcs in dispdict.items(): code2list.append(textwrap.dedent(f""" #include "{dname}.dispatch.h" """)) for (ufunc_name, func_idx, cfunc_name, inout) in funcs: code2list.append(textwrap.dedent(f"""\ NPY_CPU_DISPATCH_TRACE("{ufunc_name}", "{''.join(inout)}"); NPY_CPU_DISPATCH_CALL_XB({ufunc_name}_functions[{func_idx}] = {cfunc_name}); """)) return "\n".join(code1list), "\n".join(code2list) def make_ufuncs(funcdict): code3list = [] names = sorted(funcdict.keys()) for name in names: uf = funcdict[name] mlist = [] if uf.signature is None: sig = "NULL" else: sig = f'"{uf.signature}"' fmt = textwrap.dedent("""\ identity = {identity_expr}; if ({has_identity} && identity == NULL) {{ return -1; }} f = PyUFunc_FromFuncAndDataAndSignatureAndIdentity( {funcs}, {data}, {signatures}, {nloops}, {nin}, {nout}, {identity}, "{name}", {doc}, 0, {sig}, identity ); if ({has_identity}) {{ Py_DECREF(identity); }} if (f == NULL) {{ return -1; }} """) args = { "name": name, "funcs": f"{name}_functions" if not uf.empty else "NULL", "data": f"{name}_data" if not uf.empty else "NULL", "signatures": f"{name}_signatures" if not uf.empty else "NULL", "nloops": len(uf.type_descriptions), "nin": uf.nin, "nout": uf.nout, "has_identity": '0' if uf.identity is None_ else '1', "identity": 'PyUFunc_IdentityValue', "identity_expr": uf.identity, "doc": uf.docstring, "sig": sig, } # Only PyUFunc_None means don't reorder - we pass this using the old # argument if uf.identity is None_: args['identity'] = 'PyUFunc_None' args['identity_expr'] = 'NULL' mlist.append(fmt.format(**args)) if uf.typereso is not None: mlist.append( r"((PyUFuncObject *)f)->type_resolver = &%s;" % uf.typereso) for c in uf.indexed: # Handle indexed loops by getting the underlying ArrayMethodObject # from the list in f._loops and setting its field appropriately fmt = textwrap.dedent(""" {{ PyArray_DTypeMeta *dtype = PyArray_DTypeFromTypeNum({typenum}); PyObject *info = get_info_no_cast((PyUFuncObject *)f, dtype, {count}); if (info == NULL) {{ return -1; }} if (info == Py_None) {{ PyErr_SetString(PyExc_RuntimeError, "cannot add indexed loop to ufunc " "{name} with {typenum}"); return -1; }} if (!PyObject_TypeCheck(info, &PyArrayMethod_Type)) {{ PyErr_SetString(PyExc_RuntimeError, "Not a PyArrayMethodObject in ufunc " "{name} with {typenum}"); }} ((PyArrayMethodObject*)info)->contiguous_indexed_loop = {funcname}; /* info is borrowed, no need to decref*/ }} """) mlist.append(fmt.format( typenum=f"NPY_{english_upper(chartoname[c])}", count=uf.nin + uf.nout, name=name, funcname=f"{english_upper(chartoname[c])}_{name}_indexed", )) mlist.append(r"""PyDict_SetItemString(dictionary, "%s", f);""" % name) mlist.append(r"""Py_DECREF(f);""") code3list.append('\n'.join(mlist)) return '\n'.join(code3list) def make_code(funcdict, filename): code1, code2 = make_arrays(funcdict) code3 = make_ufuncs(funcdict) code2 = indent(code2, 4) code3 = indent(code3, 4) code = textwrap.dedent(r""" /** Warning this file is autogenerated!!! Please make changes to the code generator program (%s) **/ #include "ufunc_object.h" #include "ufunc_type_resolution.h" #include "loops.h" #include "matmul.h" #include "clip.h" #include "dtypemeta.h" #include "dispatching.h" #include "_umath_doc_generated.h" %s static int InitOperators(PyObject *dictionary) { PyObject *f, *identity; %s %s return 0; } """) % (os.path.basename(filename), code1, code2, code3) return code def main(): parser = argparse.ArgumentParser() parser.add_argument( "-o", "--outfile", type=str, help="Path to the output directory" ) args = parser.parse_args() # used to insert the name of this file into the generated file filename = __file__ code = make_code(defdict, filename) if not args.outfile: # This is the distutils-based build outfile = '__umath_generated.c' else: outfile = os.path.join(os.getcwd(), args.outfile) with open(outfile, 'w') as f: f.write(code) if __name__ == "__main__": main()