1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378 |
- """
- python _generate_pyx.py
- Generate Ufunc definition source files for scipy.special. Produces
- files '_ufuncs.c' and '_ufuncs_cxx.c' by first producing Cython.
- This will generate both calls to PyUFunc_FromFuncAndData and the
- required ufunc inner loops.
- The functions signatures are contained in 'functions.json', the syntax
- for a function signature is
- <function>: <name> ':' <input> '*' <output>
- '->' <retval> '*' <ignored_retval>
- <input>: <typecode>*
- <output>: <typecode>*
- <retval>: <typecode>?
- <ignored_retval>: <typecode>?
- <headers>: <header_name> [',' <header_name>]*
- The input parameter types are denoted by single character type
- codes, according to
- 'f': 'float'
- 'd': 'double'
- 'g': 'long double'
- 'F': 'float complex'
- 'D': 'double complex'
- 'G': 'long double complex'
- 'i': 'int'
- 'l': 'long'
- 'v': 'void'
- If multiple kernel functions are given for a single ufunc, the one
- which is used is determined by the standard ufunc mechanism. Kernel
- functions that are listed first are also matched first against the
- ufunc input types, so functions listed earlier take precedence.
- In addition, versions with casted variables, such as d->f,D->F and
- i->d are automatically generated.
- There should be either a single header that contains all of the kernel
- functions listed, or there should be one header for each kernel
- function. Cython pxd files are allowed in addition to .h files.
- Cython functions may use fused types, but the names in the list
- should be the specialized ones, such as 'somefunc[float]'.
- Function coming from C++ should have ``++`` appended to the name of
- the header.
- Floating-point exceptions inside these Ufuncs are converted to
- special function errors --- which are separately controlled by the
- user, and off by default, as they are usually not especially useful
- for the user.
- The C++ module
- --------------
- In addition to ``_ufuncs`` module, a second module ``_ufuncs_cxx`` is
- generated. This module only exports function pointers that are to be
- used when constructing some of the ufuncs in ``_ufuncs``. The function
- pointers are exported via Cython's standard mechanism.
- This mainly avoids build issues --- Python distutils has no way to
- figure out what to do if you want to link both C++ and Fortran code in
- the same shared library.
- """
- from __future__ import division, print_function, absolute_import
- #---------------------------------------------------------------------------------
- # Extra code
- #---------------------------------------------------------------------------------
- UFUNCS_EXTRA_CODE_COMMON = """\
- # This file is automatically generated by _generate_pyx.py.
- # Do not edit manually!
- from __future__ import absolute_import
- include "_ufuncs_extra_code_common.pxi"
- """
- UFUNCS_EXTRA_CODE = """\
- include "_ufuncs_extra_code.pxi"
- """
- UFUNCS_EXTRA_CODE_BOTTOM = """\
- #
- # Aliases
- #
- jn = jv
- """
- CYTHON_SPECIAL_PXD = """\
- # This file is automatically generated by _generate_pyx.py.
- # Do not edit manually!
- """
- CYTHON_SPECIAL_PYX = """\
- # This file is automatically generated by _generate_pyx.py.
- # Do not edit manually!
- \"\"\"
- .. highlight:: cython
- ================================
- Cython API for Special Functions
- ================================
- Scalar, typed versions of many of the functions in ``scipy.special``
- can be accessed directly from Cython; the complete list is given
- below. Functions are overloaded using Cython fused types so their
- names match their ufunc counterpart. The module follows the following
- conventions:
- - If a function's ufunc counterpart returns multiple values, then the
- function returns its outputs via pointers in the final arguments
- - If a function's ufunc counterpart returns a single value, then the
- function's output is returned directly.
- The module is usable from Cython via::
- cimport scipy.special.cython_special
- Error Handling
- ==============
- Functions can indicate an error by returning ``nan``; however they
- cannot emit warnings like their counterparts in ``scipy.special``.
- Available Functions
- ===================
- FUNCLIST
- \"\"\"
- from __future__ import absolute_import
- include "_cython_special.pxi"
- """
- #---------------------------------------------------------------------------------
- # Code generation
- #---------------------------------------------------------------------------------
- import os
- import optparse
- import re
- import textwrap
- import itertools
- import numpy
- import json
- BASE_DIR = os.path.abspath(os.path.dirname(__file__))
- add_newdocs = __import__('add_newdocs')
- CY_TYPES = {
- 'f': 'float',
- 'd': 'double',
- 'g': 'long double',
- 'F': 'float complex',
- 'D': 'double complex',
- 'G': 'long double complex',
- 'i': 'int',
- 'l': 'long',
- 'v': 'void',
- }
- C_TYPES = {
- 'f': 'npy_float',
- 'd': 'npy_double',
- 'g': 'npy_longdouble',
- 'F': 'npy_cfloat',
- 'D': 'npy_cdouble',
- 'G': 'npy_clongdouble',
- 'i': 'npy_int',
- 'l': 'npy_long',
- 'v': 'void',
- }
- TYPE_NAMES = {
- 'f': 'NPY_FLOAT',
- 'd': 'NPY_DOUBLE',
- 'g': 'NPY_LONGDOUBLE',
- 'F': 'NPY_CFLOAT',
- 'D': 'NPY_CDOUBLE',
- 'G': 'NPY_CLONGDOUBLE',
- 'i': 'NPY_INT',
- 'l': 'NPY_LONG',
- }
- CYTHON_SPECIAL_BENCHFUNCS = {
- 'airy': ['d*dddd', 'D*DDDD'],
- 'beta': ['dd'],
- 'erf': ['d', 'D'],
- 'exprel': ['d'],
- 'gamma': ['d', 'D'],
- 'jv': ['dd', 'dD'],
- 'loggamma': ['D'],
- 'logit': ['d'],
- 'psi': ['d', 'D'],
- }
- def underscore(arg):
- return arg.replace(" ", "_")
- def cast_order(c):
- return ['ilfdgFDG'.index(x) for x in c]
- # These downcasts will cause the function to return NaNs, unless the
- # values happen to coincide exactly.
- DANGEROUS_DOWNCAST = set([
- ('F', 'i'), ('F', 'l'), ('F', 'f'), ('F', 'd'), ('F', 'g'),
- ('D', 'i'), ('D', 'l'), ('D', 'f'), ('D', 'd'), ('D', 'g'),
- ('G', 'i'), ('G', 'l'), ('G', 'f'), ('G', 'd'), ('G', 'g'),
- ('f', 'i'), ('f', 'l'),
- ('d', 'i'), ('d', 'l'),
- ('g', 'i'), ('g', 'l'),
- ('l', 'i'),
- ])
- NAN_VALUE = {
- 'f': 'NPY_NAN',
- 'd': 'NPY_NAN',
- 'g': 'NPY_NAN',
- 'F': 'NPY_NAN',
- 'D': 'NPY_NAN',
- 'G': 'NPY_NAN',
- 'i': '0xbad0bad0',
- 'l': '0xbad0bad0',
- }
- def generate_loop(func_inputs, func_outputs, func_retval,
- ufunc_inputs, ufunc_outputs):
- """
- Generate a UFunc loop function that calls a function given as its
- data parameter with the specified input and output arguments and
- return value.
- This function can be passed to PyUFunc_FromFuncAndData.
- Parameters
- ----------
- func_inputs, func_outputs, func_retval : str
- Signature of the function to call, given as type codes of the
- input, output and return value arguments. These 1-character
- codes are given according to the CY_TYPES and TYPE_NAMES
- lists above.
- The corresponding C function signature to be called is:
- retval func(intype1 iv1, intype2 iv2, ..., outtype1 *ov1, ...);
- If len(ufunc_outputs) == len(func_outputs)+1, the return value
- is treated as the first output argument. Otherwise, the return
- value is ignored.
- ufunc_inputs, ufunc_outputs : str
- Ufunc input and output signature.
- This does not have to exactly match the function signature,
- as long as the type casts work out on the C level.
- Returns
- -------
- loop_name
- Name of the generated loop function.
- loop_body
- Generated C code for the loop.
- """
- if len(func_inputs) != len(ufunc_inputs):
- raise ValueError("Function and ufunc have different number of inputs")
- if len(func_outputs) != len(ufunc_outputs) and not (
- func_retval != "v" and len(func_outputs)+1 == len(ufunc_outputs)):
- raise ValueError("Function retval and ufunc outputs don't match")
- name = "loop_%s_%s_%s_As_%s_%s" % (
- func_retval, func_inputs, func_outputs, ufunc_inputs, ufunc_outputs
- )
- body = "cdef void %s(char **args, np.npy_intp *dims, np.npy_intp *steps, void *data) nogil:\n" % name
- body += " cdef np.npy_intp i, n = dims[0]\n"
- body += " cdef void *func = (<void**>data)[0]\n"
- body += " cdef char *func_name = <char*>(<void**>data)[1]\n"
- for j in range(len(ufunc_inputs)):
- body += " cdef char *ip%d = args[%d]\n" % (j, j)
- for j in range(len(ufunc_outputs)):
- body += " cdef char *op%d = args[%d]\n" % (j, j + len(ufunc_inputs))
- ftypes = []
- fvars = []
- outtypecodes = []
- for j in range(len(func_inputs)):
- ftypes.append(CY_TYPES[func_inputs[j]])
- fvars.append("<%s>(<%s*>ip%d)[0]" % (
- CY_TYPES[func_inputs[j]],
- CY_TYPES[ufunc_inputs[j]], j))
- if len(func_outputs)+1 == len(ufunc_outputs):
- func_joff = 1
- outtypecodes.append(func_retval)
- body += " cdef %s ov0\n" % (CY_TYPES[func_retval],)
- else:
- func_joff = 0
- for j, outtype in enumerate(func_outputs):
- body += " cdef %s ov%d\n" % (CY_TYPES[outtype], j+func_joff)
- ftypes.append("%s *" % CY_TYPES[outtype])
- fvars.append("&ov%d" % (j+func_joff))
- outtypecodes.append(outtype)
- body += " for i in range(n):\n"
- if len(func_outputs)+1 == len(ufunc_outputs):
- rv = "ov0 = "
- else:
- rv = ""
- funcall = " %s(<%s(*)(%s) nogil>func)(%s)\n" % (
- rv, CY_TYPES[func_retval], ", ".join(ftypes), ", ".join(fvars))
- # Cast-check inputs and call function
- input_checks = []
- for j in range(len(func_inputs)):
- if (ufunc_inputs[j], func_inputs[j]) in DANGEROUS_DOWNCAST:
- chk = "<%s>(<%s*>ip%d)[0] == (<%s*>ip%d)[0]" % (
- CY_TYPES[func_inputs[j]], CY_TYPES[ufunc_inputs[j]], j,
- CY_TYPES[ufunc_inputs[j]], j)
- input_checks.append(chk)
- if input_checks:
- body += " if %s:\n" % (" and ".join(input_checks))
- body += " " + funcall
- body += " else:\n"
- body += " sf_error.error(func_name, sf_error.DOMAIN, \"invalid input argument\")\n"
- for j, outtype in enumerate(outtypecodes):
- body += " ov%d = <%s>%s\n" % (
- j, CY_TYPES[outtype], NAN_VALUE[outtype])
- else:
- body += funcall
- # Assign and cast-check output values
- for j, (outtype, fouttype) in enumerate(zip(ufunc_outputs, outtypecodes)):
- if (fouttype, outtype) in DANGEROUS_DOWNCAST:
- body += " if ov%d == <%s>ov%d:\n" % (j, CY_TYPES[outtype], j)
- body += " (<%s *>op%d)[0] = <%s>ov%d\n" % (
- CY_TYPES[outtype], j, CY_TYPES[outtype], j)
- body += " else:\n"
- body += " sf_error.error(func_name, sf_error.DOMAIN, \"invalid output\")\n"
- body += " (<%s *>op%d)[0] = <%s>%s\n" % (
- CY_TYPES[outtype], j, CY_TYPES[outtype], NAN_VALUE[outtype])
- else:
- body += " (<%s *>op%d)[0] = <%s>ov%d\n" % (
- CY_TYPES[outtype], j, CY_TYPES[outtype], j)
- for j in range(len(ufunc_inputs)):
- body += " ip%d += steps[%d]\n" % (j, j)
- for j in range(len(ufunc_outputs)):
- body += " op%d += steps[%d]\n" % (j, j + len(ufunc_inputs))
- body += " sf_error.check_fpe(func_name)\n"
- return name, body
- def generate_fused_type(codes):
- """
- Generate name of and cython code for a fused type.
- Parameters
- ----------
- typecodes : str
- Valid inputs to CY_TYPES (i.e. f, d, g, ...).
- """
- cytypes = map(lambda x: CY_TYPES[x], codes)
- name = codes + "_number_t"
- declaration = ["ctypedef fused " + name + ":"]
- for cytype in cytypes:
- declaration.append(" " + cytype)
- declaration = "\n".join(declaration)
- return name, declaration
- def generate_bench(name, codes):
- tab = " "*4
- top, middle, end = [], [], []
- tmp = codes.split("*")
- if len(tmp) > 1:
- incodes = tmp[0]
- outcodes = tmp[1]
- else:
- incodes = tmp[0]
- outcodes = ""
- inargs, inargs_and_types = [], []
- for n, code in enumerate(incodes):
- arg = "x{}".format(n)
- inargs.append(arg)
- inargs_and_types.append("{} {}".format(CY_TYPES[code], arg))
- line = "def {{}}(int N, {}):".format(", ".join(inargs_and_types))
- top.append(line)
- top.append(tab + "cdef int n")
- outargs = []
- for n, code in enumerate(outcodes):
- arg = "y{}".format(n)
- outargs.append("&{}".format(arg))
- line = "cdef {} {}".format(CY_TYPES[code], arg)
- middle.append(tab + line)
- end.append(tab + "for n in range(N):")
- end.append(2*tab + "{}({})")
- pyfunc = "_bench_{}_{}_{}".format(name, incodes, "py")
- cyfunc = "_bench_{}_{}_{}".format(name, incodes, "cy")
- pytemplate = "\n".join(top + end)
- cytemplate = "\n".join(top + middle + end)
- pybench = pytemplate.format(pyfunc, "_ufuncs." + name, ", ".join(inargs))
- cybench = cytemplate.format(cyfunc, name, ", ".join(inargs + outargs))
- return pybench, cybench
- def generate_doc(name, specs):
- tab = " "*4
- doc = ["- :py:func:`~scipy.special.{}`::\n".format(name)]
- for spec in specs:
- incodes, outcodes = spec.split("->")
- incodes = incodes.split("*")
- intypes = list(map(lambda x: CY_TYPES[x], incodes[0]))
- if len(incodes) > 1:
- types = map(lambda x: "{} *".format(CY_TYPES[x]), incodes[1])
- intypes.extend(types)
- outtype = CY_TYPES[outcodes]
- line = "{} {}({})".format(outtype, name, ", ".join(intypes))
- doc.append(2*tab + line)
- doc[-1] = "{}\n".format(doc[-1])
- doc = "\n".join(doc)
- return doc
- def npy_cdouble_from_double_complex(var):
- """Cast a cython double complex to a numpy cdouble."""
- res = "_complexstuff.npy_cdouble_from_double_complex({})".format(var)
- return res
- def double_complex_from_npy_cdouble(var):
- """Cast a numpy cdouble to a cython double complex."""
- res = "_complexstuff.double_complex_from_npy_cdouble({})".format(var)
- return res
- def iter_variants(inputs, outputs):
- """
- Generate variants of UFunc signatures, by changing variable types,
- within the limitation that the corresponding C types casts still
- work out.
- This does not generate all possibilities, just the ones required
- for the ufunc to work properly with the most common data types.
- Parameters
- ----------
- inputs, outputs : str
- UFunc input and output signature strings
- Yields
- ------
- new_input, new_output : str
- Modified input and output strings.
- Also the original input/output pair is yielded.
- """
- maps = [
- # always use long instead of int (more common type on 64-bit)
- ('i', 'l'),
- ]
- # float32-preserving signatures
- if not ('i' in inputs or 'l' in inputs):
- # Don't add float32 versions of ufuncs with integer arguments, as this
- # can lead to incorrect dtype selection if the integer arguments are
- # arrays, but float arguments are scalars.
- # For instance sph_harm(0,[0],0,0).dtype == complex64
- # This may be a Numpy bug, but we need to work around it.
- # cf. gh-4895, https://github.com/numpy/numpy/issues/5895
- maps = maps + [(a + 'dD', b + 'fF') for a, b in maps]
- # do the replacements
- for src, dst in maps:
- new_inputs = inputs
- new_outputs = outputs
- for a, b in zip(src, dst):
- new_inputs = new_inputs.replace(a, b)
- new_outputs = new_outputs.replace(a, b)
- yield new_inputs, new_outputs
- class Func(object):
- """
- Base class for Ufunc and FusedFunc.
- """
- def __init__(self, name, signatures):
- self.name = name
- self.signatures = []
- self.function_name_overrides = {}
- for header in signatures.keys():
- for name, sig in signatures[header].items():
- inarg, outarg, ret = self._parse_signature(sig)
- self.signatures.append((name, inarg, outarg, ret, header))
- def _parse_signature(self, sig):
- m = re.match(r"\s*([fdgFDGil]*)\s*\*\s*([fdgFDGil]*)\s*->\s*([*fdgFDGil]*)\s*$", sig)
- if m:
- inarg, outarg, ret = [x.strip() for x in m.groups()]
- if ret.count('*') > 1:
- raise ValueError("{}: Invalid signature: {}".format(self.name, sig))
- return inarg, outarg, ret
- m = re.match(r"\s*([fdgFDGil]*)\s*->\s*([fdgFDGil]?)\s*$", sig)
- if m:
- inarg, ret = [x.strip() for x in m.groups()]
- return inarg, "", ret
- raise ValueError("{}: Invalid signature: {}".format(self.name, sig))
- def get_prototypes(self, nptypes_for_h=False):
- prototypes = []
- for func_name, inarg, outarg, ret, header in self.signatures:
- ret = ret.replace('*', '')
- c_args = ([C_TYPES[x] for x in inarg]
- + [C_TYPES[x] + ' *' for x in outarg])
- cy_args = ([CY_TYPES[x] for x in inarg]
- + [CY_TYPES[x] + ' *' for x in outarg])
- c_proto = "%s (*)(%s)" % (C_TYPES[ret], ", ".join(c_args))
- if header.endswith("h") and nptypes_for_h:
- cy_proto = c_proto + "nogil"
- else:
- cy_proto = "%s (*)(%s) nogil" % (CY_TYPES[ret], ", ".join(cy_args))
- prototypes.append((func_name, c_proto, cy_proto, header))
- return prototypes
- def cython_func_name(self, c_name, specialized=False, prefix="_func_",
- override=True):
- # act on function name overrides
- if override and c_name in self.function_name_overrides:
- c_name = self.function_name_overrides[c_name]
- prefix = ""
- # support fused types
- m = re.match(r'^(.*?)(\[.*\])$', c_name)
- if m:
- c_base_name, fused_part = m.groups()
- else:
- c_base_name, fused_part = c_name, ""
- if specialized:
- return "%s%s%s" % (prefix, c_base_name, fused_part.replace(' ', '_'))
- else:
- return "%s%s" % (prefix, c_base_name,)
- class Ufunc(Func):
- """
- Ufunc signature, restricted format suitable for special functions.
- Parameters
- ----------
- name
- Name of the ufunc to create
- signature
- String of form 'func: fff*ff->f, func2: ddd->*i' describing
- the C-level functions and types of their input arguments
- and return values.
- The syntax is 'function_name: inputparams*outputparams->output_retval*ignored_retval'
- Attributes
- ----------
- name : str
- Python name for the Ufunc
- signatures : list of (func_name, inarg_spec, outarg_spec, ret_spec, header_name)
- List of parsed signatures
- doc : str
- Docstring, obtained from add_newdocs
- function_name_overrides : dict of str->str
- Overrides for the function names in signatures
- """
- def __init__(self, name, signatures):
- super(Ufunc, self).__init__(name, signatures)
- self.doc = add_newdocs.get("scipy.special." + name)
- if self.doc is None:
- raise ValueError("No docstring for ufunc %r" % name)
- self.doc = textwrap.dedent(self.doc).strip()
- def _get_signatures_and_loops(self, all_loops):
- inarg_num = None
- outarg_num = None
- seen = set()
- variants = []
- def add_variant(func_name, inarg, outarg, ret, inp, outp):
- if inp in seen:
- return
- seen.add(inp)
- sig = (func_name, inp, outp)
- if "v" in outp:
- raise ValueError("%s: void signature %r" % (self.name, sig))
- if len(inp) != inarg_num or len(outp) != outarg_num:
- raise ValueError("%s: signature %r does not have %d/%d input/output args" % (
- self.name, sig,
- inarg_num, outarg_num))
- loop_name, loop = generate_loop(inarg, outarg, ret, inp, outp)
- all_loops[loop_name] = loop
- variants.append((func_name, loop_name, inp, outp))
- # First add base variants
- for func_name, inarg, outarg, ret, header in self.signatures:
- outp = re.sub(r'\*.*', '', ret) + outarg
- ret = ret.replace('*', '')
- if inarg_num is None:
- inarg_num = len(inarg)
- outarg_num = len(outp)
- inp, outp = list(iter_variants(inarg, outp))[0]
- add_variant(func_name, inarg, outarg, ret, inp, outp)
- # Then the supplementary ones
- for func_name, inarg, outarg, ret, header in self.signatures:
- outp = re.sub(r'\*.*', '', ret) + outarg
- ret = ret.replace('*', '')
- for inp, outp in iter_variants(inarg, outp):
- add_variant(func_name, inarg, outarg, ret, inp, outp)
- # Then sort variants to input argument cast order
- # -- the sort is stable, so functions earlier in the signature list
- # are still preferred
- variants.sort(key=lambda v: cast_order(v[2]))
- return variants, inarg_num, outarg_num
- def generate(self, all_loops):
- toplevel = ""
- variants, inarg_num, outarg_num = self._get_signatures_and_loops(all_loops)
- loops = []
- funcs = []
- types = []
- for func_name, loop_name, inputs, outputs in variants:
- for x in inputs:
- types.append(TYPE_NAMES[x])
- for x in outputs:
- types.append(TYPE_NAMES[x])
- loops.append(loop_name)
- funcs.append(func_name)
- toplevel += "cdef np.PyUFuncGenericFunction ufunc_%s_loops[%d]\n" % (self.name, len(loops))
- toplevel += "cdef void *ufunc_%s_ptr[%d]\n" % (self.name, 2*len(funcs))
- toplevel += "cdef void *ufunc_%s_data[%d]\n" % (self.name, len(funcs))
- toplevel += "cdef char ufunc_%s_types[%d]\n" % (self.name, len(types))
- toplevel += 'cdef char *ufunc_%s_doc = (\n "%s")\n' % (
- self.name,
- self.doc.replace("\\", "\\\\").replace('"', '\\"').replace('\n', '\\n\"\n "')
- )
- for j, function in enumerate(loops):
- toplevel += "ufunc_%s_loops[%d] = <np.PyUFuncGenericFunction>%s\n" % (self.name, j, function)
- for j, type in enumerate(types):
- toplevel += "ufunc_%s_types[%d] = <char>%s\n" % (self.name, j, type)
- for j, func in enumerate(funcs):
- toplevel += "ufunc_%s_ptr[2*%d] = <void*>%s\n" % (self.name, j,
- self.cython_func_name(func, specialized=True))
- toplevel += "ufunc_%s_ptr[2*%d+1] = <void*>(<char*>\"%s\")\n" % (self.name, j,
- self.name)
- for j, func in enumerate(funcs):
- toplevel += "ufunc_%s_data[%d] = &ufunc_%s_ptr[2*%d]\n" % (
- self.name, j, self.name, j)
- toplevel += ('@ = np.PyUFunc_FromFuncAndData(ufunc_@_loops, '
- 'ufunc_@_data, ufunc_@_types, %d, %d, %d, 0, '
- '"@", ufunc_@_doc, 0)\n' % (len(types)/(inarg_num+outarg_num),
- inarg_num, outarg_num)
- ).replace('@', self.name)
- return toplevel
- class FusedFunc(Func):
- """
- Generate code for a fused-type special function that can be
- cimported in cython.
- """
- def __init__(self, name, signatures):
- super(FusedFunc, self).__init__(name, signatures)
- self.doc = "See the documentation for scipy.special." + self.name
- # "codes" are the keys for CY_TYPES
- self.incodes, self.outcodes = self._get_codes()
- self.fused_types = set()
- self.intypes, infused_types = self._get_types(self.incodes)
- self.fused_types.update(infused_types)
- self.outtypes, outfused_types = self._get_types(self.outcodes)
- self.fused_types.update(outfused_types)
- self.invars, self.outvars = self._get_vars()
- def _get_codes(self):
- inarg_num, outarg_num = None, None
- all_inp, all_outp = [], []
- for _, inarg, outarg, ret, _ in self.signatures:
- outp = re.sub(r'\*.*', '', ret) + outarg
- if inarg_num is None:
- inarg_num = len(inarg)
- outarg_num = len(outp)
- inp, outp = list(iter_variants(inarg, outp))[0]
- all_inp.append(inp)
- all_outp.append(outp)
- incodes = []
- for n in range(inarg_num):
- codes = unique(map(lambda x: x[n], all_inp))
- codes.sort()
- incodes.append(''.join(codes))
- outcodes = []
- for n in range(outarg_num):
- codes = unique(map(lambda x: x[n], all_outp))
- codes.sort()
- outcodes.append(''.join(codes))
- return tuple(incodes), tuple(outcodes)
- def _get_types(self, codes):
- all_types = []
- fused_types = set()
- for code in codes:
- if len(code) == 1:
- # It's not a fused type
- all_types.append((CY_TYPES[code], code))
- else:
- # It's a fused type
- fused_type, dec = generate_fused_type(code)
- fused_types.add(dec)
- all_types.append((fused_type, code))
- return all_types, fused_types
- def _get_vars(self):
- invars = []
- for n in range(len(self.intypes)):
- invars.append("x{}".format(n))
- outvars = []
- for n in range(len(self.outtypes)):
- outvars.append("y{}".format(n))
- return invars, outvars
- def _get_conditional(self, types, codes, adverb):
- """Generate an if/elif/else clause that selects a specialization of
- fused types.
- """
- clauses = []
- seen = set()
- for (typ, typcode), code in zip(types, codes):
- if len(typcode) == 1:
- continue
- if typ not in seen:
- clauses.append("{} is {}".format(typ, underscore(CY_TYPES[code])))
- seen.add(typ)
- if clauses and adverb != "else":
- line = "{} {}:".format(adverb, " and ".join(clauses))
- elif clauses and adverb == "else":
- line = "else:"
- else:
- line = None
- return line
- def _get_incallvars(self, intypes, c):
- """Generate pure input variables to a specialization,
- i.e. variables that aren't used to return a value.
- """
- incallvars = []
- for n, intype in enumerate(intypes):
- var = self.invars[n]
- if c and intype == "double complex":
- var = npy_cdouble_from_double_complex(var)
- incallvars.append(var)
- return incallvars
- def _get_outcallvars(self, outtypes, c):
- """Generate output variables to a specialization,
- i.e. pointers that are used to return values.
- """
- outcallvars, tmpvars, casts = [], [], []
- # If there are more out variables than out types, we want the
- # tail of the out variables
- start = len(self.outvars) - len(outtypes)
- outvars = self.outvars[start:]
- for n, (var, outtype) in enumerate(zip(outvars, outtypes)):
- if c and outtype == "double complex":
- tmp = "tmp{}".format(n)
- tmpvars.append(tmp)
- outcallvars.append("&{}".format(tmp))
- tmpcast = double_complex_from_npy_cdouble(tmp)
- casts.append("{}[0] = {}".format(var, tmpcast))
- else:
- outcallvars.append("{}".format(var))
- return outcallvars, tmpvars, casts
- def _get_nan_decs(self):
- """Set all variables to nan for specializations of fused types for
- which don't have signatures.
- """
- # Set non fused-type variables to nan
- tab = " "*4
- fused_types, lines = [], [tab + "else:"]
- seen = set()
- for outvar, outtype, code in zip(self.outvars, self.outtypes, self.outcodes):
- if len(code) == 1:
- line = "{}[0] = {}".format(outvar, NAN_VALUE[code])
- lines.append(2*tab + line)
- else:
- fused_type = outtype
- name, _ = fused_type
- if name not in seen:
- fused_types.append(fused_type)
- seen.add(name)
- if not fused_types:
- return lines
- # Set fused-type variables to nan
- all_codes = []
- for fused_type in fused_types:
- _, codes = fused_type
- all_codes.append(codes)
- all_codes = tuple(all_codes)
- codelens = list(map(lambda x: len(x), all_codes))
- last = numpy.product(codelens) - 1
- for m, codes in enumerate(itertools.product(*all_codes)):
- fused_codes, decs = [], []
- for n, fused_type in enumerate(fused_types):
- code = codes[n]
- fused_codes.append(underscore(CY_TYPES[code]))
- for nn, outvar in enumerate(self.outvars):
- if self.outtypes[nn] == fused_type:
- line = "{}[0] = {}".format(outvar, NAN_VALUE[code])
- decs.append(line)
- if m == 0:
- adverb = "if"
- elif m == last:
- adverb = "else"
- else:
- adverb = "elif"
- cond = self._get_conditional(fused_types, codes, adverb)
- lines.append(2*tab + cond)
- lines.extend(map(lambda x: 3*tab + x, decs))
- return lines
- def _get_tmp_decs(self, all_tmpvars):
- """Generate the declarations of any necessary temporary
- variables.
- """
- tab = " "*4
- tmpvars = list(all_tmpvars)
- tmpvars.sort()
- tmpdecs = []
- for tmpvar in tmpvars:
- line = "cdef npy_cdouble {}".format(tmpvar)
- tmpdecs.append(tab + line)
- return tmpdecs
- def _get_python_wrap(self):
- """Generate a python wrapper for functions which pass their
- arguments as pointers.
- """
- tab = " "*4
- body, callvars = [], []
- for (intype, _), invar in zip(self.intypes, self.invars):
- callvars.append("{} {}".format(intype, invar))
- line = "def _{}_pywrap({}):".format(self.name, ", ".join(callvars))
- body.append(line)
- for (outtype, _), outvar in zip(self.outtypes, self.outvars):
- line = "cdef {} {}".format(outtype, outvar)
- body.append(tab + line)
- addr_outvars = map(lambda x: "&{}".format(x), self.outvars)
- line = "{}({}, {})".format(self.name, ", ".join(self.invars),
- ", ".join(addr_outvars))
- body.append(tab + line)
- line = "return {}".format(", ".join(self.outvars))
- body.append(tab + line)
- body = "\n".join(body)
- return body
- def _get_common(self, signum, sig):
- """Generate code common to all the _generate_* methods."""
- tab = " "*4
- func_name, incodes, outcodes, retcode, header = sig
- # Convert ints to longs; cf. iter_variants()
- incodes = incodes.replace('i', 'l')
- outcodes = outcodes.replace('i', 'l')
- retcode = retcode.replace('i', 'l')
- if header.endswith("h"):
- c = True
- else:
- c = False
- if header.endswith("++"):
- cpp = True
- else:
- cpp = False
- intypes = list(map(lambda x: CY_TYPES[x], incodes))
- outtypes = list(map(lambda x: CY_TYPES[x], outcodes))
- retcode = re.sub(r'\*.*', '', retcode)
- if not retcode:
- retcode = 'v'
- rettype = CY_TYPES[retcode]
- if cpp:
- # Functions from _ufuncs_cxx are exported as a void*
- # pointers; cast them to the correct types
- func_name = "scipy.special._ufuncs_cxx._export_{}".format(func_name)
- func_name = "(<{}(*)({}) nogil>{})"\
- .format(rettype, ", ".join(intypes + outtypes), func_name)
- else:
- func_name = self.cython_func_name(func_name, specialized=True)
- if signum == 0:
- adverb = "if"
- else:
- adverb = "elif"
- cond = self._get_conditional(self.intypes, incodes, adverb)
- if cond:
- lines = [tab + cond]
- sp = 2*tab
- else:
- lines = []
- sp = tab
- return func_name, incodes, outcodes, retcode, \
- intypes, outtypes, rettype, c, lines, sp
- def _generate_from_return_and_no_outargs(self):
- tab = " "*4
- specs, body = [], []
- for signum, sig in enumerate(self.signatures):
- func_name, incodes, outcodes, retcode, intypes, outtypes, \
- rettype, c, lines, sp = self._get_common(signum, sig)
- body.extend(lines)
- # Generate the call to the specialized function
- callvars = self._get_incallvars(intypes, c)
- call = "{}({})".format(func_name, ", ".join(callvars))
- if c and rettype == "double complex":
- call = double_complex_from_npy_cdouble(call)
- line = sp + "return {}".format(call)
- body.append(line)
- sig = "{}->{}".format(incodes, retcode)
- specs.append(sig)
- if len(specs) > 1:
- # Return nan for signatures without a specialization
- body.append(tab + "else:")
- outtype, outcodes = self.outtypes[0]
- last = len(outcodes) - 1
- if len(outcodes) == 1:
- line = "return {}".format(NAN_VALUE[outcodes])
- body.append(2*tab + line)
- else:
- for n, code in enumerate(outcodes):
- if n == 0:
- adverb = "if"
- elif n == last:
- adverb = "else"
- else:
- adverb = "elif"
- cond = self._get_conditional(self.outtypes, code, adverb)
- body.append(2*tab + cond)
- line = "return {}".format(NAN_VALUE[code])
- body.append(3*tab + line)
- # Generate the head of the function
- callvars, head = [], []
- for n, (intype, _) in enumerate(self.intypes):
- callvars.append("{} {}".format(intype, self.invars[n]))
- (outtype, _) = self.outtypes[0]
- dec = "cpdef {} {}({}) nogil".format(outtype, self.name, ", ".join(callvars))
- head.append(dec + ":")
- head.append(tab + '"""{}"""'.format(self.doc))
- src = "\n".join(head + body)
- return dec, src, specs
- def _generate_from_outargs_and_no_return(self):
- tab = " "*4
- all_tmpvars = set()
- specs, body = [], []
- for signum, sig in enumerate(self.signatures):
- func_name, incodes, outcodes, retcode, intypes, outtypes, \
- rettype, c, lines, sp = self._get_common(signum, sig)
- body.extend(lines)
- # Generate the call to the specialized function
- callvars = self._get_incallvars(intypes, c)
- outcallvars, tmpvars, casts = self._get_outcallvars(outtypes, c)
- callvars.extend(outcallvars)
- all_tmpvars.update(tmpvars)
- call = "{}({})".format(func_name, ", ".join(callvars))
- body.append(sp + call)
- body.extend(map(lambda x: sp + x, casts))
- if len(outcodes) == 1:
- sig = "{}->{}".format(incodes, outcodes)
- specs.append(sig)
- else:
- sig = "{}*{}->v".format(incodes, outcodes)
- specs.append(sig)
- if len(specs) > 1:
- lines = self._get_nan_decs()
- body.extend(lines)
- if len(self.outvars) == 1:
- line = "return {}[0]".format(self.outvars[0])
- body.append(tab + line)
- # Generate the head of the function
- callvars, head = [], []
- for invar, (intype, _) in zip(self.invars, self.intypes):
- callvars.append("{} {}".format(intype, invar))
- if len(self.outvars) > 1:
- for outvar, (outtype, _) in zip(self.outvars, self.outtypes):
- callvars.append("{} *{}".format(outtype, outvar))
- if len(self.outvars) == 1:
- outtype, _ = self.outtypes[0]
- dec = "cpdef {} {}({}) nogil".format(outtype, self.name, ", ".join(callvars))
- else:
- dec = "cdef void {}({}) nogil".format(self.name, ", ".join(callvars))
- head.append(dec + ":")
- head.append(tab + '"""{}"""'.format(self.doc))
- if len(self.outvars) == 1:
- outvar = self.outvars[0]
- outtype, _ = self.outtypes[0]
- line = "cdef {} {}".format(outtype, outvar)
- head.append(tab + line)
- head.extend(self._get_tmp_decs(all_tmpvars))
- src = "\n".join(head + body)
- return dec, src, specs
- def _generate_from_outargs_and_return(self):
- tab = " "*4
- all_tmpvars = set()
- specs, body = [], []
- for signum, sig in enumerate(self.signatures):
- func_name, incodes, outcodes, retcode, intypes, outtypes, \
- rettype, c, lines, sp = self._get_common(signum, sig)
- body.extend(lines)
- # Generate the call to the specialized function
- callvars = self._get_incallvars(intypes, c)
- outcallvars, tmpvars, casts = self._get_outcallvars(outtypes, c)
- callvars.extend(outcallvars)
- all_tmpvars.update(tmpvars)
- call = "{}({})".format(func_name, ", ".join(callvars))
- if c and rettype == "double complex":
- call = double_complex_from_npy_cdouble(call)
- call = "{}[0] = {}".format(self.outvars[0], call)
- body.append(sp + call)
- body.extend(map(lambda x: sp + x, casts))
- sig = "{}*{}->v".format(incodes, outcodes + retcode)
- specs.append(sig)
- if len(specs) > 1:
- lines = self._get_nan_decs()
- body.extend(lines)
- # Generate the head of the function
- callvars, head = [], []
- for invar, (intype, _) in zip(self.invars, self.intypes):
- callvars.append("{} {}".format(intype, invar))
- for outvar, (outtype, _) in zip(self.outvars, self.outtypes):
- callvars.append("{} *{}".format(outtype, outvar))
- dec = "cdef void {}({}) nogil".format(self.name, ", ".join(callvars))
- head.append(dec + ":")
- head.append(tab + '"""{}"""'.format(self.doc))
- head.extend(self._get_tmp_decs(all_tmpvars))
- src = "\n".join(head + body)
- return dec, src, specs
- def generate(self):
- _, _, outcodes, retcode, _ = self.signatures[0]
- retcode = re.sub(r'\*.*', '', retcode)
- if not retcode:
- retcode = 'v'
- if len(outcodes) == 0 and retcode != 'v':
- dec, src, specs = self._generate_from_return_and_no_outargs()
- elif len(outcodes) > 0 and retcode == 'v':
- dec, src, specs = self._generate_from_outargs_and_no_return()
- elif len(outcodes) > 0 and retcode != 'v':
- dec, src, specs = self._generate_from_outargs_and_return()
- else:
- raise ValueError("Invalid signature")
- if len(self.outvars) > 1:
- wrap = self._get_python_wrap()
- else:
- wrap = None
- return dec, src, specs, self.fused_types, wrap
- def get_declaration(ufunc, c_name, c_proto, cy_proto, header, proto_h_filename):
- """
- Construct a Cython declaration of a function coming either from a
- pxd or a header file. Do sufficient tricks to enable compile-time
- type checking against the signature expected by the ufunc.
- """
- defs = []
- defs_h = []
- var_name = c_name.replace('[', '_').replace(']', '_').replace(' ', '_')
- if header.endswith('.pxd'):
- defs.append("from .%s cimport %s as %s" % (
- header[:-4], ufunc.cython_func_name(c_name, prefix=""),
- ufunc.cython_func_name(c_name)))
- # check function signature at compile time
- proto_name = '_proto_%s_t' % var_name
- defs.append("ctypedef %s" % (cy_proto.replace('(*)', proto_name)))
- defs.append("cdef %s *%s_var = &%s" % (
- proto_name, proto_name, ufunc.cython_func_name(c_name, specialized=True)))
- else:
- # redeclare the function, so that the assumed
- # signature is checked at compile time
- new_name = "%s \"%s\"" % (ufunc.cython_func_name(c_name), c_name)
- defs.append("cdef extern from \"%s\":" % proto_h_filename)
- defs.append(" cdef %s" % (cy_proto.replace('(*)', new_name)))
- defs_h.append("#include \"%s\"" % header)
- defs_h.append("%s;" % (c_proto.replace('(*)', c_name)))
- return defs, defs_h, var_name
- def generate_ufuncs(fn_prefix, cxx_fn_prefix, ufuncs):
- filename = fn_prefix + ".pyx"
- proto_h_filename = fn_prefix + '_defs.h'
- cxx_proto_h_filename = cxx_fn_prefix + '_defs.h'
- cxx_pyx_filename = cxx_fn_prefix + ".pyx"
- cxx_pxd_filename = cxx_fn_prefix + ".pxd"
- toplevel = ""
- # for _ufuncs*
- defs = []
- defs_h = []
- all_loops = {}
- # for _ufuncs_cxx*
- cxx_defs = []
- cxx_pxd_defs = [
- "from . cimport sf_error",
- "cdef void _set_action(sf_error.sf_error_t, sf_error.sf_action_t) nogil"
- ]
- cxx_defs_h = []
- ufuncs.sort(key=lambda u: u.name)
- for ufunc in ufuncs:
- # generate function declaration and type checking snippets
- cfuncs = ufunc.get_prototypes()
- for c_name, c_proto, cy_proto, header in cfuncs:
- if header.endswith('++'):
- header = header[:-2]
- # for the CXX module
- item_defs, item_defs_h, var_name = get_declaration(ufunc, c_name, c_proto, cy_proto,
- header, cxx_proto_h_filename)
- cxx_defs.extend(item_defs)
- cxx_defs_h.extend(item_defs_h)
- cxx_defs.append("cdef void *_export_%s = <void*>%s" % (
- var_name, ufunc.cython_func_name(c_name, specialized=True, override=False)))
- cxx_pxd_defs.append("cdef void *_export_%s" % (var_name,))
- # let cython grab the function pointer from the c++ shared library
- ufunc.function_name_overrides[c_name] = "scipy.special._ufuncs_cxx._export_" + var_name
- else:
- # usual case
- item_defs, item_defs_h, _ = get_declaration(ufunc, c_name, c_proto, cy_proto, header,
- proto_h_filename)
- defs.extend(item_defs)
- defs_h.extend(item_defs_h)
- # ufunc creation code snippet
- t = ufunc.generate(all_loops)
- toplevel += t + "\n"
- # Produce output
- toplevel = "\n".join(sorted(all_loops.values()) + defs + [toplevel])
- with open(filename, 'w') as f:
- f.write(UFUNCS_EXTRA_CODE_COMMON)
- f.write(UFUNCS_EXTRA_CODE)
- f.write("\n")
- f.write(toplevel)
- f.write(UFUNCS_EXTRA_CODE_BOTTOM)
- defs_h = unique(defs_h)
- with open(proto_h_filename, 'w') as f:
- f.write("#ifndef UFUNCS_PROTO_H\n#define UFUNCS_PROTO_H 1\n")
- f.write("\n".join(defs_h))
- f.write("\n#endif\n")
- cxx_defs_h = unique(cxx_defs_h)
- with open(cxx_proto_h_filename, 'w') as f:
- f.write("#ifndef UFUNCS_PROTO_H\n#define UFUNCS_PROTO_H 1\n")
- f.write("\n".join(cxx_defs_h))
- f.write("\n#endif\n")
- with open(cxx_pyx_filename, 'w') as f:
- f.write(UFUNCS_EXTRA_CODE_COMMON)
- f.write("\n")
- f.write("\n".join(cxx_defs))
- f.write("\n# distutils: language = c++\n")
- with open(cxx_pxd_filename, 'w') as f:
- f.write("\n".join(cxx_pxd_defs))
- def generate_fused_funcs(modname, ufunc_fn_prefix, fused_funcs):
- pxdfile = modname + ".pxd"
- pyxfile = modname + ".pyx"
- proto_h_filename = ufunc_fn_prefix + '_defs.h'
- sources = []
- declarations = []
- # Code for benchmarks
- bench_aux = []
- fused_types = set()
- # Parameters for the tests
- doc = []
- defs = []
- for func in fused_funcs:
- if func.name.startswith("_"):
- # Don't try to deal with functions that have extra layers
- # of wrappers.
- continue
- # Get the function declaration for the .pxd and the source
- # code for the .pyx
- dec, src, specs, func_fused_types, wrap = func.generate()
- declarations.append(dec)
- sources.append(src)
- if wrap:
- sources.append(wrap)
- fused_types.update(func_fused_types)
- # Declare the specializations
- cfuncs = func.get_prototypes(nptypes_for_h=True)
- for c_name, c_proto, cy_proto, header in cfuncs:
- if header.endswith('++'):
- # We grab the c++ functions from the c++ module
- continue
- item_defs, _, _ = get_declaration(func, c_name, c_proto,
- cy_proto, header,
- proto_h_filename)
- defs.extend(item_defs)
- # Add a line to the documentation
- doc.append(generate_doc(func.name, specs))
- # Generate code for benchmarks
- if func.name in CYTHON_SPECIAL_BENCHFUNCS:
- for codes in CYTHON_SPECIAL_BENCHFUNCS[func.name]:
- pybench, cybench = generate_bench(func.name, codes)
- bench_aux.extend([pybench, cybench])
- fused_types = list(fused_types)
- fused_types.sort()
- with open(pxdfile, 'w') as f:
- f.write(CYTHON_SPECIAL_PXD)
- f.write("\n")
- f.write("\n\n".join(fused_types))
- f.write("\n\n")
- f.write("\n".join(declarations))
- with open(pyxfile, 'w') as f:
- header = CYTHON_SPECIAL_PYX
- header = header.replace("FUNCLIST", "\n".join(doc))
- f.write(header)
- f.write("\n")
- f.write("\n".join(defs))
- f.write("\n\n")
- f.write("\n\n".join(sources))
- f.write("\n\n")
- f.write("\n\n".join(bench_aux))
- def unique(lst):
- """
- Return a list without repeated entries (first occurrence is kept),
- preserving order.
- """
- seen = set()
- new_lst = []
- for item in lst:
- if item in seen:
- continue
- seen.add(item)
- new_lst.append(item)
- return new_lst
- def all_newer(src_files, dst_files):
- from distutils.dep_util import newer
- return all(os.path.exists(dst) and newer(dst, src)
- for dst in dst_files for src in src_files)
- def main():
- p = optparse.OptionParser(usage=(__doc__ or '').strip())
- options, args = p.parse_args()
- if len(args) != 0:
- p.error('invalid number of arguments')
- pwd = os.path.dirname(__file__)
- src_files = (os.path.abspath(__file__),
- os.path.abspath(os.path.join(pwd, 'functions.json')),
- os.path.abspath(os.path.join(pwd, 'add_newdocs.py')))
- dst_files = ('_ufuncs.pyx',
- '_ufuncs_defs.h',
- '_ufuncs_cxx.pyx',
- '_ufuncs_cxx.pxd',
- '_ufuncs_cxx_defs.h',
- 'cython_special.pyx',
- 'cython_special.pxd')
- os.chdir(BASE_DIR)
- if all_newer(src_files, dst_files):
- print("scipy/special/_generate_pyx.py: all files up-to-date")
- return
- ufuncs, fused_funcs = [], []
- with open('functions.json') as data:
- functions = json.load(data)
- for f, sig in functions.items():
- ufuncs.append(Ufunc(f, sig))
- fused_funcs.append(FusedFunc(f, sig))
- generate_ufuncs("_ufuncs", "_ufuncs_cxx", ufuncs)
- generate_fused_funcs("cython_special", "_ufuncs", fused_funcs)
- if __name__ == "__main__":
- main()
|