import importlib import inspect import numpy _footnotes = {} def _get_functions(obj, exclude=None): return set([ n for n, target in [(n, getattr(obj, n)) for n in dir(obj)] if ( # not in exclude list (exclude is None or n not in exclude) # not module: and not inspect.ismodule(target) # not constant: and not isinstance(target, (int, float, bool, str, numpy.bool_)) # not exceptions or warning classes: and (not inspect.isclass(target) or not issubclass(target, (BaseException,))) # not private/special method: and not n.startswith('_') ) ]) def _import(mod, klass): obj = importlib.import_module(mod) if klass: obj = getattr(obj, klass) return obj, ':obj:`{}.{}.{{}}`'.format(mod, klass) else: # ufunc is not a function return obj, ':obj:`{}.{{}}`'.format(mod) def _generate_comparison_rst( base_mod, cupy_mod, base_type, klass, exclude_mod, exclude, footnotes=None): base_obj, base_fmt = _import(base_mod, klass) base_funcs = _get_functions(base_obj, exclude) cp_obj, cp_fmt = _import(cupy_mod, klass) cp_funcs = _get_functions(cp_obj) if exclude_mod: exclude_obj, _ = _import(exclude_mod, klass) exclude_funcs = _get_functions(exclude_obj) base_funcs -= exclude_funcs cp_funcs -= exclude_funcs buf = [] buf += [ '.. csv-table::', ' :header: {}, CuPy'.format(base_type), '', ] for f in sorted(base_funcs): footnote_id = None if footnotes is not None and f in footnotes: footnote_id = _footnotes.setdefault( footnotes[f], f'f{len(_footnotes)}') base_cell = base_fmt.format(f) cp_cell = r'\-' if f in cp_funcs: cp_cell = cp_fmt.format(f) if getattr(base_obj, f) is getattr(cp_obj, f): cp_cell = '{} (*alias of* {})'.format(cp_cell, base_cell) if footnote_id is not None: cp_cell += f' [#{footnote_id}]_' line = ' {}, {}'.format(base_cell, cp_cell) buf.append(line) buf += [ '', '.. Summary:', ' Number of NumPy functions: {}'.format(len(base_funcs)), ' Number of functions covered by CuPy: {}'.format( len(cp_funcs & base_funcs)), ' CuPy specific functions:', ] + [ ' - {}'.format(f) for f in sorted(cp_funcs - base_funcs) ] return buf def _section( header, base_mod, cupy_mod, base_type='NumPy', klass=None, exclude_mod=None, exclude=None, footnotes=None, header_char='~'): return [ header, header_char * len(header), '', ] + _generate_comparison_rst( base_mod, cupy_mod, base_type, klass, exclude_mod, exclude, footnotes ) + [ '', ] _deprecated = 'Not supported as it has been deprecated in NumPy.' _np_matrix = ( 'Use of :class:`numpy.matrix` is discouraged in NumPy and thus' ' we have no plan to add it to CuPy.') _np_poly1d = ( 'Use of :class:`numpy.poly1d` is discouraged in NumPy and thus' ' we have stopped adding functions with the interface.') _dtype_na = ( '`object` and string dtypes are not supported in GPU and thus' ' left unimplemented in CuPy.') _dtype_time = ( '`datetime64` and `timedelta64` dtypes are currently unsupported.') _dtype_rec = ( 'Structured arrays and record arrays are currently unsupported.') _float_error_state = ( 'Floating point error handling depends on CPU-specific features' ' which is not available in GPU.') _byte_order = 'Not supported as GPUs only support little-endian byte-encoding.' def generate(): buf = [] buf += [ 'NumPy / CuPy APIs', '-----------------', '', ] buf += _section( 'Module-Level', 'numpy', 'cupy', exclude=[ 'add_docstring', 'add_newdoc', 'add_newdoc_ufunc', '_add_newdoc_ufunc', 'fastCopyAndTranspose', 'kernel_version', 'test', 'Tester', ], footnotes={ 'Datetime64': _deprecated, # NumPy 1.20 'Uint64': _deprecated, # NumPy 1.20 'mafromtxt': _deprecated, # NumPy 1.17 'alen': _deprecated, # NumPy 1.18 'asscalar': _deprecated, # NumPy 1.16 'loads': _deprecated, # NumPy 1.15 'ndfromtxt': _deprecated, # NumPy 1.17 'set_numeric_ops': _deprecated, # NumPy 1.16 'asmatrix': _np_matrix, 'bmat': _np_matrix, 'mat': _np_matrix, 'matrix': _np_matrix, 'poly': _np_poly1d, 'polyder': _np_poly1d, 'polydiv': _np_poly1d, 'polyint': _np_poly1d, 'Bytes0': _dtype_na, # also deprecated in NumPy 1.20 'bytes0': _dtype_na, 'bytes_': _dtype_na, 'character': _dtype_na, 'chararray': _dtype_na, 'compare_chararrays': _dtype_na, 'flexible': _dtype_na, 'object0': _dtype_na, 'object_': _dtype_na, 'Str0': _dtype_na, # also deprecated in NumPy 1.20 'str0': _dtype_na, 'str_': _dtype_na, 'string_': _dtype_na, 'unicode_': _dtype_na, 'void': _dtype_na, 'void0': _dtype_na, 'busday_count': _dtype_time, 'busday_offset': _dtype_time, 'busdaycalendar': _dtype_time, 'is_busday': _dtype_time, 'isnat': _dtype_time, 'datetime64': _dtype_time, 'datetime_as_string': _dtype_time, 'datetime_data': _dtype_time, 'timedelta64': _dtype_time, 'fromregex': _dtype_rec, 'recarray': _dtype_rec, 'recfromcsv': _dtype_rec, 'recfromtxt': _dtype_rec, 'record': _dtype_rec, 'errstate': _float_error_state, 'geterr': _float_error_state, 'geterrcall': _float_error_state, 'geterrobj': _float_error_state, 'seterr': _float_error_state, 'seterrcall': _float_error_state, 'seterrobj': _float_error_state, }) buf += _section( 'Multi-Dimensional Array', 'numpy', 'cupy', klass='ndarray', footnotes={ 'tostring': _deprecated, 'byteswap': _byte_order, 'newbyteorder': _byte_order, }) buf += _section( 'Linear Algebra', 'numpy.linalg', 'cupy.linalg', exclude=['test']) buf += _section( 'Discrete Fourier Transform', 'numpy.fft', 'cupy.fft', exclude=['test']) buf += _section( 'Random Sampling', 'numpy.random', 'cupy.random', exclude=['test']) buf += _section( 'Polynomials', 'numpy.polynomial', 'cupy.polynomial', exclude=['test']) buf += _section( 'Power Series', 'numpy.polynomial.polynomial', 'cupy.polynomial.polynomial', header_char='"') buf += _section( 'Polyutils', 'numpy.polynomial.polyutils', 'cupy.polynomial.polyutils', header_char='"') buf += [ 'SciPy / CuPy APIs', '-----------------', '', ] buf += _section( 'Discrete Fourier Transform', 'scipy.fft', 'cupyx.scipy.fft', 'SciPy', exclude=['test']) buf += _section( 'Legacy Discrete Fourier Transform', 'scipy.fftpack', 'cupyx.scipy.fftpack', 'SciPy', exclude=['test']) buf += _section( 'Interpolation', 'scipy.interpolate', 'cupyx.scipy.interpolate', 'SciPy', exclude=['test']) buf += _section( 'Advanced Linear Algebra', 'scipy.linalg', 'cupyx.scipy.linalg', 'SciPy', exclude_mod='numpy.linalg', exclude=['test']) buf += _section( 'Multidimensional Image Processing', 'scipy.ndimage', 'cupyx.scipy.ndimage', 'SciPy', exclude=['test']) buf += _section( 'Signal processing', 'scipy.signal', 'cupyx.scipy.signal', 'SciPy', exclude=['test']) buf += _section( 'Sparse Matrices', 'scipy.sparse', 'cupyx.scipy.sparse', 'SciPy', exclude=['test']) buf += _section( 'Sparse Linear Algebra', 'scipy.sparse.linalg', 'cupyx.scipy.sparse.linalg', 'SciPy', exclude=['test']) buf += _section( 'Compressed sparse graph routines', 'scipy.sparse.csgraph', 'cupyx.scipy.sparse.csgraph', 'SciPy', exclude=['test']) buf += _section( 'Special Functions', 'scipy.special', 'cupyx.scipy.special', 'SciPy', exclude=['test']) buf += _section( 'Statistical Functions', 'scipy.stats', 'cupyx.scipy.stats', 'SciPy', exclude=['test']) # numpy.array_api is not ready yet... # buf += [ # 'NumPy / CuPy Array APIs', # '-----------------------', # '', # ] # buf += _section( # 'Python array API compliance', # 'numpy.array_api', 'cupy.array_api', 'NumPy') buf += [ '', '.. rubric:: Footnotes', '', ] + [ f'.. [#{footnote_id}] {footnote}' for footnote, footnote_id in _footnotes.items() ] return '\n'.join(buf)