Numpy: NEP-18(__array_function__)实施的跟踪问题

创建于 2018-09-25  ·  54评论  ·  资料来源: numpy/numpy

  • [x]支持替代的核心功能:

    • [x]纯Python(#12005)的初始实现

    • [x]验证array_function_dispatch中的调度程序功能(https://github.com/numpy/numpy/pull/12099)



      • 在不测试NumPy时禁用验证(如果对导入时间有可衡量的影响) (不必要)



    • [x]添加.__skip_array_function__函数属性以允许跳过__array_function__调度。 (https://github.com/numpy/numpy/pull/13389)

  • [x]在C中重新实现numpy/core/overrides.py以提高速度(https://github.com/numpy/numpy/issues/12028):

    • [x] get_overloaded_types_and_args

    • [x] array_function_implementation_or_override

    • [x] ndarray.__array_function__吗?

    • [x] array_function_dispatch

  • [x]所有公共NumPy函数的支持替代

    • [x] numpy.core



      • [x]简单部分(https://github.com/numpy/numpy/pull/12115)


      • [x] np.core.defchararray (#12154)


      • [x] np.einsumnp.block (https://github.com/numpy/numpy/pull/12163)



    • [x] numpy.lib



      • [x]第1部分(https://github.com/numpy/numpy/pull/12116)


      • [x]第2部分(#12119)



    • [x] numpy.fft / numpy.linalg (https://github.com/numpy/numpy/pull/12117)

    • [x]当前完全用C语言编写的函数:empty_like,catecatenate,inner,where,lexsort,can_cast,min_scalar_type,result_type,dot,vdot,is_busday,busday_offset,busday_count,datetime_as_string(https://github.com/numpy/numpy/拉/ 12175)

    • [x] linspace

    • [] [ arange? ](https://github.com/numpy/numpy/issues/12379)

  • [x]可用性改进

    • [x] [更好的错误消息](https://github.com/numpy/numpy/issues/12213),用于未实现的功能(https://github.com/numpy/numpy/pull/12251)

    • [x] ndarray.__repr__不应该依赖__array_function__ (https://github.com/numpy/numpy/pull/12212)

    • [x]对于包装函数, stacklevel应该增加1,因此回溯指向正确的位置(gh-13329)

  • [x]修复所有已知的错误/下游测试失败

    • [x]熊猫测试失败: https :

    • [x]快速测试失败:array_function_dispatch()破坏了inspect.getargspec / getfullargspec#12225

    • [x] scipy测试失败:hstack和column_stack不再接受生成器

  • []文档

    • [x]发行说明(#12028)

    • [x]叙事文档

    • []修订文档字符串以澄清重载参数?

__array_function__

最有用的评论

是否有关于进行此类更改的建议,或者您是说要支持调度np.array真的很困难,所以我们永远也无法获得100%的支持?

NEP 22在这里对这些选项进行了一些讨论。 我认为我们无法安全地更改np.asarray()的语义以返回除numpy.ndarray对象之外的任何内容-为此,我们将需要一个新的协议。

问题在于np.asarray()当前是转换为numpy数组对象的惯用方式,该对象使用can并且确实希望与numpy.ndarray完全匹配,例如向下匹配到内存布局。

当然,有很多用例不是这种情况,但是切换此行为会破坏很多下游代码,因此这不是入门。 下游项目将需要至少参与阵列鸭子类型的这一方面。

我知道这里存在性能/复杂性的折衷,这可能是不实施这些的很好理由。 但这可能会迫使用户探索其他方式来获得他们所希望的灵活性。

是。 NEP 18并非旨在成为NumPy替代方案的完整解决方案,而是朝着这个方向迈出的一步。

所有54条评论

对于一些引人注目的功能,可以合并一个初步的“用@array_function_dispatch装饰所有公共NumPy函数”的初步建议,并请协议的下游使用者尝试一下

一旦我们合并https://github.com/numpy/numpy/pull/12099,我就会准备好另一个PR,它将为numpy.core大部分添加调度装饰器。 完成事情将非常容易-这个过程花了不到一个小时的时间。

cc @ eric-wieser @mrocklin @mhvk @hameerabbasi

请参阅https://github.com/shoyer/numpy/tree/array-function-easy-impl作为我的分支机构,以Python包装器在函数上实现所有“简单”替代。 剩下的部分是np.blocknp.einsum和少数几个完全用C编写的多数组函数(例如np.concatenate )。 #12099完成后,我将其分成一堆PR。

请注意,我尚未针对每个函数的重写编写测试。 我想在完成后添加一些集成测试(例如,记录所有应用操作的鸭子数组),但是我认为为每个单独的函数编写调度测试不会产生任何效果。 #12099中的检查应捕获调度程序上最常见的错误,并且调度程序功能中的每一行代码都应由现有测试执行。

@shoyer-关于测试,我同意为每个测试编写代码不是特别有用; 取而代之的是,在numpy中,以相对较快的速度在MaskedArray开始使用覆盖可能是最有意义的。

@mhvk对我来说听起来不错,尽管我会让使用/了解MaskedArray的其他人带头。

https://github.com/numpy/numpy/pull/12115https://github.com/numpy/numpy/pull/12116 ,#12119和https://github.com/numpy/numpy/pull/ PR实现对Python中定义的函数的__array_function__支持的PR的12117

@shoyer-看到一些实现,我有两个担心:

  • 对于某些功能,例如reshape ,原始功能已经提供了一种通过定义reshape方法来覆盖它的方法。 对于任何定义__array_function__类,我们实际上都反对这样做。
  • 对于其他功能,例如np.median ,请谨慎使用np.asanyarray和ufuncs以确保子类可以使用它们。 但是该功能不再可以直接访问。

我认为总体而言,这两件事可能是有益的,因为我们简化了界面并可以针对纯ndarray进行优化的实现-尽管后者建议ndarray.__array_function__来接管转换列表等,到ndarray ,以便实现可以跳过该部分)。 不过,我认为我会注意到它,因为这使我不敢以Quantity

尽管后者建议ndarray .__ array_function__应该将列表等转换为ndarray,以便实现可以跳过该部分)。

我不确定我是否会遵循这里。

实际上,我们确实不赞成使用覆盖reshapemean之类的函数的旧方法,尽管该旧方法仍支持NumPy API的不完整实现。

我不确定我是否会遵循这里。

我认为问题在于,即使我们为单个函数实现__array_function__ ,以前的机制也会完全中断,并且无法进行故障转移。 这就是为什么我建议我们重新审查我的NotImplementedButCoercible建议。

@hameerabbasi-是的,这就是问题所在。 尽管我们在这里需要小心,但要使我们更容易依赖于胶带解决方案却是我们真正希望摆脱的……(这就是为什么我在上面写道我的“问题”实际上可能是有益的……) 。 也许有一个案例可以像1.16那样尝试,然后根据实际经验来决定是否要回退“忽略此案例的__array_function__ ”。

回复:调度员样式:我对样式的偏爱是基于内存/导入时间的考虑因素和详细程度。 很简单,合并可能保持签名不变的调度程序。 这样,我们创建的对象最少,缓存命中率也将更高。

也就是说,我不太反对lambda样式。

现在,一些PR中出现了编写调度程序功能的样式。 最好在NumPy中做出一致的选择。

我们有几种选择:


选项1 :为每个功能编写单独的调度程序,例如,

def _sin_dispatcher(a):
    return (a,)


@array_function_dispatch(_sin_dispatcher)
def sin(a):
     ...


def _cos_dispatcher(a):
    return (a,)


@array_function_dispatch(_cos_dispatcher)
def cos(a):
    ...

优点:

  • 可读性强
  • 易于找到调度程序功能的定义
  • 提供错误的参数时清除错误消息,例如sin(x=1) -> TypeError: _sin_dispatcher() got an unexpected keyword argument 'x'

缺点:

  • 即使模块中的许多功能具有完全相同的签名,也要进行大量重复。

选项2 :在模块内重用调度程序功能,例如,

def _unary_dispatcher(a):
    return (a,)


@array_function_dispatch(_unary_dispatcher)
def sin(a):
     ...


@array_function_dispatch(_unary_dispatcher)
def cos(a):
    ...

优点:

  • 减少重复
  • 可读的

缺点:

  • 可能很难找到调度程序功能的定义
  • 对于错误参数的错误消息稍少一些,例如sin(x=1) -> TypeError: _unary_dispatcher() got an unexpected keyword argument 'x'

选项3 :当调度程序定义适合一行时,请使用lambda函数,例如,

# inline style (shorter)
@array_function_dispatch(lambda a: (a,))
def sin(a):
     ...


@array_function_dispatch(lambda a, n=None, axis=None, norm=None: (a,))
def fft(a, n=None, axis=-1, norm=None):
     ...
# multiline style (more readable?)
@array_function_dispatch(
    lambda a: (a,)
)
def sin(a):
     ...


@array_function_dispatch(
    lambda a, n=None, axis=None, norm=None: (a,)
)
def fft(a, n=None, axis=-1, norm=None):
     ...

优点:

  • 无需寻找调度程序定义,它们就在那里。
  • 更少的字符和代码行。
  • 在短情况下(例如,一个参数)看起来非常好,尤其是当lambda短于函数名时。

缺点:

  • 与选项2相比,重复代码更多。
  • 如果有多个参数,看起来很混乱
  • 也有不太清晰的错误消息( TypeError: <lambda>() got an unexpected keyword argument 'x'

@shoyer :已编辑以添加两行PEP8间距,以使“代码行”方面更加逼真

请注意,错误消息问题可以通过重构代码对象来解决,尽管这会花费一些导入时间。 也许值得研究,并打破@nschloe的金枪鱼来比较一些选择。

是的,装饰器模块也可以用于生成函数定义(它使用稍有不同的代码生成方法,有点类似于namedtuple,因为它使用exec() )。

只要错误没有解决,我认为我们需要使用名称明确的调度程序来坚持这些选项。 出于内存原因,我会稍微将调度程序捆绑在一起(2),尽管这样会非常牢记错误消息,因此建议您像_dispatch_on_x这样调用调度程序。

尽管如果我们可以更改错误,情况也会改变。 例如,它可能很简单,例如捕获异常,将<lambda>替换

我同意错误消息必须清晰明了,理想情况下根本不应该更改。

好的,就目前而言,我认为最好不要使用lambda ,除非我们能进行某种代码生成。

https://github.com/numpy/numpy/pull/12175添加了一份草案,说明如果采用Python包装方法,多数组函数(用C编写)的替代可能看起来像什么。

@mattip我们在哪里将matmul为ufunc? 一旦完成所有这些__array_function__覆盖,我认为这是使NumPy的公共API完全可重载的最后一件事。 为NumPy 1.16做好一切准备真是太好了!

实施NEP 20的PR#11175进展缓慢。 它是PR#11133的阻止程序,它具有matmul循环代码。 仍然需要对其进行更新,然后通过基准测试来验证新代码的运行速度并不比旧代码慢。

我有四个PR待审核,这些PR应该能完成全部替代操作。 最后的评论/批准/合并将不胜感激,因此我们可以开始认真测试__array_function__https://github.com/numpy/numpy/pull/12154https://github.com/numpy/numpy/pull/12163https://github.com/numpy/numpy/pull/12119HTTPS: //github.com/numpy/numpy/pull/12175

np.core添加替代导致一些熊猫测试失败(https://github.com/pandas-dev/pandas/issues/23172)。 我们不太确定发生了什么,但是在发布之前,我们一定要弄清楚。

关于为什么这会导致dask / pandas中的测试失败的最佳猜测,请参阅https://github.com/numpy/numpy/issues/12225

导入时间的一些基准(在我的固态硬盘Macbook Pro上):

  • NumPy 1.15.2:152.451毫秒
  • NumPy主站:156.5745毫秒
  • 使用decorator.decorate (#12226):183.694毫秒

我的基准脚本

import numpy as np
import subprocess

times = []
for _ in range(100):
    result = subprocess.run("python -X importtime -c 'import numpy'",
                            shell=True, capture_output=True)
    last_line = result.stderr.rstrip().split(b'\n')[-1]
    time = float(last_line.decode('ascii')[-15:-7].strip().rstrip())
    times.append(time)

print(np.median(times) / 1e3)

对内存使用情况有任何想法(之前/之后)吗? 这也很有用,尤其是对于物联网应用。

您知道如何可靠地测量模块的内存使用情况吗?
2018年10月20日,星期六,上午6:56 Hameer Abbasi [email protected]
写道:

对内存使用情况有任何想法(之前/之后)吗? 这是很有用的
好,尤其是对于物联网应用。

-
您收到此邮件是因为有人提到您。
直接回复此电子邮件,在GitHub上查看
https://github.com/numpy/numpy/issues/12028#issuecomment-431584123或静音
线程
https://github.com/notifications/unsubscribe-auth/ABKS1k_IkrJ2YmYReaDrnkNvcH2X0-ZCks5umyuogaJpZM4W3kSC

我认为编写包含import numpy as np的脚本,添加一个sleep语句和跟踪进程内存应该足够好。 https://superuser.com/questions/581108/how-can-i-track-and-log-cpu-and-memory-usage-on-a-mac

是否有其他核心开发人员希望在https://github.com/numpy/numpy/pull/12163进行快速浏览(真的,它仅包含两个功能!) array_function_dispatch的最后一个PR。

作为参考,这是禁用__array_function__时看到的性能差异:

       before           after         ratio
     [45718fd7]       [4e5aa2cd]
     <master>         <disable-array-function>
+        72.5±2ms         132±20ms     1.82  bench_io.LoadtxtCSVdtypes.time_loadtxt_dtypes_csv('complex128', 10000)
-        44.9±2μs       40.8±0.6μs     0.91  bench_ma.Concatenate.time_it('ndarray', 2)
-      15.3±0.3μs       13.3±0.7μs     0.87  bench_core.CountNonzero.time_count_nonzero_multi_axis(2, 100, <type 'object'>)
-        38.4±1μs         32.7±2μs     0.85  bench_linalg.Linalg.time_op('norm', 'longfloat')
-        68.7±3μs         56.5±3μs     0.82  bench_linalg.Linalg.time_op('norm', 'complex256')
-        80.6±4μs         65.9±1μs     0.82  bench_function_base.Median.time_even
-        82.4±2μs         66.8±3μs     0.81  bench_shape_base.Block.time_no_lists(100)
-        73.5±3μs         59.3±3μs     0.81  bench_function_base.Median.time_even_inplace
-      15.2±0.3μs       12.2±0.6μs     0.80  bench_core.CountNonzero.time_count_nonzero_multi_axis(3, 100, <type 'str'>)
-      2.20±0.1ms      1.76±0.04ms     0.80  bench_shape_base.Block2D.time_block2d((1024, 1024), 'uint64', (4, 4))
-        388±20μs         310±10μs     0.80  bench_lib.Pad.time_pad((10, 10, 10), 3, 'linear_ramp')
-        659±20μs         524±20μs     0.80  bench_linalg.Linalg.time_op('det', 'float32')
-      22.9±0.7μs       18.2±0.8μs     0.79  bench_function_base.Where.time_1
-        980±50μs         775±20μs     0.79  bench_shape_base.Block2D.time_block2d((1024, 1024), 'uint32', (4, 4))
-        36.6±1μs         29.0±1μs     0.79  bench_ma.Concatenate.time_it('unmasked', 2)
-      16.4±0.7μs       12.9±0.6μs     0.79  bench_core.CountNonzero.time_count_nonzero_axis(3, 100, <type 'str'>)
-      16.4±0.5μs       12.9±0.4μs     0.79  bench_core.CountNonzero.time_count_nonzero_axis(2, 100, <type 'object'>)
-         141±5μs          110±4μs     0.78  bench_lib.Pad.time_pad((10, 100), (0, 5), 'linear_ramp')
-      18.0±0.6μs       14.1±0.6μs     0.78  bench_core.CountNonzero.time_count_nonzero_axis(3, 100, <type 'object'>)
-      11.9±0.6μs       9.28±0.5μs     0.78  bench_core.CountNonzero.time_count_nonzero_axis(1, 100, <type 'int'>)
-        54.6±3μs         42.4±2μs     0.78  bench_function_base.Median.time_odd_small
-        317±10μs          246±7μs     0.78  bench_lib.Pad.time_pad((10, 10, 10), 1, 'linear_ramp')
-      13.8±0.5μs       10.7±0.7μs     0.77  bench_reduce.MinMax.time_min(<type 'numpy.float64'>)
-        73.3±6μs         56.6±4μs     0.77  bench_lib.Pad.time_pad((1000,), (0, 5), 'mean')
-      14.7±0.7μs       11.4±0.3μs     0.77  bench_core.CountNonzero.time_count_nonzero_axis(2, 100, <type 'str'>)
-        21.5±2μs       16.5±0.6μs     0.77  bench_reduce.MinMax.time_min(<type 'numpy.int64'>)
-         117±4μs         89.2±3μs     0.76  bench_lib.Pad.time_pad((1000,), 3, 'linear_ramp')
-        43.7±1μs         33.4±1μs     0.76  bench_linalg.Linalg.time_op('norm', 'complex128')
-      12.6±0.6μs       9.55±0.2μs     0.76  bench_core.CountNonzero.time_count_nonzero_multi_axis(2, 100, <type 'int'>)
-        636±20μs         482±20μs     0.76  bench_ma.MA.time_masked_array_l100
-        86.6±4μs         65.6±4μs     0.76  bench_lib.Pad.time_pad((1000,), (0, 5), 'linear_ramp')
-         120±4μs         90.4±2μs     0.75  bench_lib.Pad.time_pad((1000,), 1, 'linear_ramp')
-         160±5μs          119±8μs     0.74  bench_ma.Concatenate.time_it('ndarray+masked', 100)
-      14.4±0.6μs       10.7±0.3μs     0.74  bench_core.CountNonzero.time_count_nonzero_multi_axis(1, 100, <type 'str'>)
-      15.7±0.4μs       11.7±0.6μs     0.74  bench_core.CountNonzero.time_count_nonzero_multi_axis(2, 100, <type 'str'>)
-        21.8±2μs       16.1±0.7μs     0.74  bench_reduce.MinMax.time_max(<type 'numpy.int64'>)
-      11.9±0.6μs       8.79±0.3μs     0.74  bench_core.CountNonzero.time_count_nonzero_axis(2, 100, <type 'bool'>)
-        53.8±3μs         39.4±2μs     0.73  bench_function_base.Median.time_even_small
-        106±20μs         76.7±4μs     0.73  bench_function_base.Select.time_select
-        168±10μs          122±4μs     0.72  bench_shape_base.Block2D.time_block2d((512, 512), 'uint32', (2, 2))
-      12.5±0.5μs       8.96±0.4μs     0.72  bench_core.CountNonzero.time_count_nonzero_multi_axis(1, 100, <type 'int'>)
-        162±10μs          115±5μs     0.71  bench_function_base.Percentile.time_percentile
-        12.9±1μs       9.12±0.4μs     0.71  bench_random.Random.time_rng('normal')
-      9.71±0.4μs       6.88±0.3μs     0.71  bench_core.CorrConv.time_convolve(1000, 10, 'full')
-      15.1±0.8μs       10.7±0.4μs     0.71  bench_reduce.MinMax.time_max(<type 'numpy.float64'>)
-         153±9μs          108±7μs     0.71  bench_shape_base.Block2D.time_block2d((1024, 1024), 'uint8', (2, 2))
-         109±5μs         76.9±5μs     0.71  bench_ma.Concatenate.time_it('ndarray+masked', 2)
-        34.3±1μs       24.2±0.6μs     0.71  bench_linalg.Linalg.time_op('norm', 'complex64')
-      9.80±0.2μs       6.84±0.5μs     0.70  bench_core.CorrConv.time_convolve(1000, 10, 'same')
-        27.4±6μs         19.1±2μs     0.70  bench_core.CountNonzero.time_count_nonzero_axis(1, 10000, <type 'bool'>)
-      9.35±0.4μs       6.50±0.3μs     0.70  bench_core.CorrConv.time_convolve(50, 100, 'full')
-        65.2±4μs         45.2±1μs     0.69  bench_shape_base.Block.time_block_simple_row_wise(100)
-        12.9±1μs       8.89±0.3μs     0.69  bench_core.CountNonzero.time_count_nonzero_axis(3, 100, <type 'bool'>)
-        19.6±3μs       13.5±0.4μs     0.69  bench_core.CountNonzero.time_count_nonzero_multi_axis(3, 100, <type 'object'>)
-        75.6±2μs         52.1±3μs     0.69  bench_lib.Pad.time_pad((10, 10, 10), (0, 5), 'reflect')
-        12.4±1μs       8.51±0.4μs     0.69  bench_core.CountNonzero.time_count_nonzero_multi_axis(3, 100, <type 'bool'>)
-        172±30μs          117±4μs     0.68  bench_ma.Concatenate.time_it('unmasked+masked', 100)
-      23.1±0.5μs       15.8±0.9μs     0.68  bench_linalg.Linalg.time_op('norm', 'int16')
-      8.18±0.9μs       5.57±0.1μs     0.68  bench_core.CorrConv.time_correlate(1000, 10, 'full')
-         153±5μs          103±3μs     0.68  bench_function_base.Percentile.time_quartile
-       758±100μs         512±20μs     0.68  bench_linalg.Linalg.time_op('det', 'int16')
-        55.4±6μs         37.4±1μs     0.68  bench_ma.Concatenate.time_it('masked', 2)
-        234±30μs          157±5μs     0.67  bench_shape_base.Block.time_nested(100)
-         103±4μs         69.3±3μs     0.67  bench_linalg.Eindot.time_dot_d_dot_b_c
-      19.2±0.4μs       12.9±0.6μs     0.67  bench_core.Core.time_tril_l10x10
-         122±7μs         81.7±4μs     0.67  bench_lib.Pad.time_pad((10, 10, 10), 3, 'edge')
-        22.9±1μs       15.3±0.5μs     0.67  bench_linalg.Linalg.time_op('norm', 'int32')
-        16.6±2μs       11.0±0.3μs     0.66  bench_core.CountNonzero.time_count_nonzero_multi_axis(1, 100, <type 'object'>)
-      9.98±0.3μs       6.58±0.1μs     0.66  bench_core.CorrConv.time_convolve(1000, 10, 'valid')
-         118±6μs         77.9±4μs     0.66  bench_shape_base.Block2D.time_block2d((512, 512), 'uint16', (2, 2))
-        212±50μs          140±8μs     0.66  bench_lib.Pad.time_pad((10, 10, 10), (0, 5), 'mean')
-      21.9±0.7μs       14.4±0.5μs     0.66  bench_linalg.Linalg.time_op('norm', 'int64')
-         131±5μs         85.9±5μs     0.65  bench_lib.Pad.time_pad((10, 10, 10), 3, 'constant')
-        56.8±2μs         37.0±3μs     0.65  bench_lib.Pad.time_pad((1000,), (0, 5), 'constant')
-        58.9±3μs         38.1±1μs     0.65  bench_lib.Pad.time_pad((10, 100), (0, 5), 'reflect')
-        72.1±2μs         46.5±3μs     0.64  bench_lib.Pad.time_pad((10, 100), (0, 5), 'constant')
-      8.66±0.3μs       5.58±0.2μs     0.64  bench_core.CorrConv.time_correlate(50, 100, 'full')
-        300±30μs         193±10μs     0.64  bench_shape_base.Block2D.time_block2d((1024, 1024), 'uint8', (4, 4))
-        15.9±5μs       10.2±0.3μs     0.64  bench_core.CountNonzero.time_count_nonzero_axis(3, 100, <type 'int'>)
-      13.7±0.5μs       8.80±0.1μs     0.64  bench_random.Random.time_rng('uniform')
-      8.60±0.5μs       5.50±0.2μs     0.64  bench_core.CorrConv.time_correlate(1000, 10, 'same')
-        44.7±2μs       28.5±0.7μs     0.64  bench_lib.Pad.time_pad((1000,), 1, 'reflect')
-        72.7±3μs         46.2±2μs     0.64  bench_lib.Pad.time_pad((10, 10, 10), 3, 'wrap')
-        567±50μs         360±40μs     0.63  bench_shape_base.Block2D.time_block2d((512, 512), 'uint64', (2, 2))
-        58.0±3μs         36.7±2μs     0.63  bench_lib.Pad.time_pad((10, 100), 3, 'reflect')
-        219±30μs          138±7μs     0.63  bench_lib.Pad.time_pad((10, 100), 1, 'mean')
-        261±60μs         164±10μs     0.63  bench_lib.Pad.time_pad((10, 100), 1, 'linear_ramp')
-       825±100μs         519±30μs     0.63  bench_shape_base.Block2D.time_block2d((512, 512), 'uint64', (4, 4))
-         121±5μs         75.7±2μs     0.63  bench_lib.Pad.time_pad((10, 10, 10), 1, 'constant')
-      8.16±0.2μs       5.08±0.4μs     0.62  bench_core.CorrConv.time_convolve(50, 100, 'same')
-        66.6±3μs         41.3±2μs     0.62  bench_lib.Pad.time_pad((1000,), 3, 'constant')
-        53.1±3μs       32.9±0.8μs     0.62  bench_lib.Pad.time_pad((10, 100), 3, 'wrap')
-        285±60μs         177±10μs     0.62  bench_lib.Pad.time_pad((10, 10, 10), (0, 5), 'linear_ramp')
-      8.30±0.9μs       5.14±0.1μs     0.62  bench_core.CorrConv.time_correlate(1000, 10, 'valid')
-         115±3μs         71.2±3μs     0.62  bench_shape_base.Block2D.time_block2d((256, 256), 'uint64', (2, 2))
-      19.1±0.5μs       11.8±0.6μs     0.62  bench_linalg.Linalg.time_op('norm', 'float64')
-        95.3±5μs         58.6±2μs     0.62  bench_lib.Pad.time_pad((10, 100), 1, 'constant')
-        44.6±1μs       27.2±0.9μs     0.61  bench_lib.Pad.time_pad((1000,), (0, 5), 'edge')
-        447±20μs         270±10μs     0.61  bench_shape_base.Block2D.time_block2d((1024, 1024), 'uint16', (4, 4))
-        53.9±2μs         32.6±2μs     0.60  bench_lib.Pad.time_pad((10, 100), 1, 'wrap')
-        11.6±1μs       6.97±0.4μs     0.60  bench_reduce.MinMax.time_max(<type 'numpy.float32'>)
-        95.9±5μs         57.7±2μs     0.60  bench_lib.Pad.time_pad((10, 100), 3, 'constant')
-        47.2±2μs         28.2±2μs     0.60  bench_lib.Pad.time_pad((1000,), (0, 5), 'reflect')
-      5.51±0.2μs      3.27±0.07μs     0.59  bench_core.CountNonzero.time_count_nonzero(3, 100, <type 'object'>)
-        74.3±3μs         44.0±2μs     0.59  bench_lib.Pad.time_pad((10, 10, 10), (0, 5), 'wrap')
-        76.2±3μs       45.0±0.8μs     0.59  bench_lib.Pad.time_pad((10, 10, 10), 1, 'reflect')
-        57.1±1μs         33.5±2μs     0.59  bench_lib.Pad.time_pad((10, 100), (0, 5), 'wrap')
-        52.0±2μs         30.4±1μs     0.58  bench_lib.Pad.time_pad((1000,), 1, 'edge')
-        42.6±2μs       24.9±0.9μs     0.58  bench_lib.Pad.time_pad((1000,), 3, 'wrap')
-        15.0±3μs       8.73±0.3μs     0.58  bench_core.CountNonzero.time_count_nonzero_multi_axis(1, 100, <type 'bool'>)
-        16.0±3μs       9.29±0.3μs     0.58  bench_core.CountNonzero.time_count_nonzero_multi_axis(3, 100, <type 'int'>)
-        53.1±1μs         30.9±2μs     0.58  bench_lib.Pad.time_pad((1000,), 3, 'edge')
-        88.0±8μs         51.1±3μs     0.58  bench_lib.Pad.time_pad((10, 10, 10), 3, 'reflect')
-        44.6±2μs         25.9±1μs     0.58  bench_lib.Pad.time_pad((1000,), (0, 5), 'wrap')
-        90.3±5μs         51.9±1μs     0.57  bench_shape_base.Block2D.time_block2d((512, 512), 'uint8', (2, 2))
-      15.6±0.5μs       8.93±0.3μs     0.57  bench_linalg.Linalg.time_op('norm', 'float32')
-         102±6μs       58.3±0.9μs     0.57  bench_lib.Pad.time_pad((10, 10, 10), 1, 'edge')
-        80.1±4μs         45.6±3μs     0.57  bench_lib.Pad.time_pad((10, 100), 3, 'edge')
-        44.2±2μs         24.9±1μs     0.56  bench_lib.Pad.time_pad((1000,), 1, 'wrap')
-        71.6±8μs         39.5±1μs     0.55  bench_lib.Pad.time_pad((10, 10, 10), 1, 'wrap')
-       81.7±10μs         44.8±2μs     0.55  bench_lib.Pad.time_pad((10, 100), 1, 'edge')
-        420±90μs         230±10μs     0.55  bench_shape_base.Block.time_3d(10, 'block')
-        114±20μs         62.3±2μs     0.55  bench_lib.Pad.time_pad((10, 10, 10), (0, 5), 'constant')
-      5.76±0.1μs      3.13±0.08μs     0.54  bench_core.CorrConv.time_convolve(50, 10, 'same')
-      5.30±0.1μs      2.84±0.08μs     0.54  bench_core.CorrConv.time_correlate(50, 100, 'valid')
-        92.5±4μs         49.3±1μs     0.53  bench_shape_base.Block2D.time_block2d((256, 256), 'uint32', (2, 2))
-        13.5±3μs       7.07±0.2μs     0.52  bench_reduce.MinMax.time_min(<type 'numpy.float32'>)
-        7.66±1μs       3.88±0.2μs     0.51  bench_core.CorrConv.time_convolve(50, 100, 'valid')
-        29.0±3μs       14.5±0.8μs     0.50  bench_shape_base.Block.time_no_lists(10)
-      6.62±0.3μs       3.30±0.2μs     0.50  bench_core.CorrConv.time_convolve(1000, 1000, 'valid')
-        74.2±7μs       36.2±0.9μs     0.49  bench_shape_base.Block2D.time_block2d((256, 256), 'uint16', (2, 2))
-      5.55±0.3μs       2.70±0.2μs     0.49  bench_core.CorrConv.time_convolve(50, 10, 'valid')
-       73.9±20μs         35.8±2μs     0.48  bench_lib.Pad.time_pad((10, 100), 1, 'reflect')
-        224±20μs          107±7μs     0.48  bench_shape_base.Block2D.time_block2d((256, 256), 'uint64', (4, 4))
-      3.87±0.1μs      1.83±0.06μs     0.47  bench_core.CountNonzero.time_count_nonzero(2, 100, <type 'str'>)
-        109±30μs         51.5±3μs     0.47  bench_lib.Pad.time_pad((10, 10, 10), (0, 5), 'edge')
-        240±20μs          112±4μs     0.47  bench_shape_base.Block2D.time_block2d((512, 512), 'uint16', (4, 4))
-        337±40μs          158±7μs     0.47  bench_shape_base.Block2D.time_block2d((512, 512), 'uint32', (4, 4))
-         188±8μs         88.0±2μs     0.47  bench_shape_base.Block2D.time_block2d((512, 512), 'uint8', (4, 4))
-      4.39±0.2μs      2.04±0.09μs     0.47  bench_core.CountNonzero.time_count_nonzero(3, 10000, <type 'bool'>)
-        73.2±4μs       33.9±0.5μs     0.46  bench_shape_base.Block2D.time_block2d((128, 128), 'uint64', (2, 2))
-        5.48±1μs       2.44±0.1μs     0.45  bench_core.CountNonzero.time_count_nonzero(2, 100, <type 'object'>)
-      4.46±0.1μs      1.97±0.08μs     0.44  bench_core.CorrConv.time_correlate(50, 10, 'full')
-        30.4±9μs       13.3±0.3μs     0.44  bench_shape_base.Block.time_no_lists(1)
-      7.05±0.2μs      3.05±0.06μs     0.43  bench_reduce.SmallReduction.time_small
-        7.35±1μs       3.12±0.2μs     0.42  bench_core.CorrConv.time_convolve(50, 10, 'full')
-      4.36±0.1μs      1.84±0.07μs     0.42  bench_core.CorrConv.time_correlate(50, 10, 'same')
-      3.51±0.2μs      1.46±0.05μs     0.42  bench_core.CountNonzero.time_count_nonzero(1, 100, <type 'object'>)
-     4.03±0.05μs       1.66±0.1μs     0.41  bench_core.CorrConv.time_correlate(1000, 1000, 'valid')
-        199±10μs         80.1±3μs     0.40  bench_shape_base.Block2D.time_block2d((256, 256), 'uint32', (4, 4))
-      3.98±0.2μs      1.60±0.08μs     0.40  bench_core.CountNonzero.time_count_nonzero(2, 10000, <type 'bool'>)
-        61.8±2μs         24.8±1μs     0.40  bench_shape_base.Block2D.time_block2d((256, 256), 'uint8', (2, 2))
-      4.13±0.1μs      1.62±0.05μs     0.39  bench_core.CorrConv.time_correlate(50, 10, 'valid')
-        61.6±2μs         23.9±1μs     0.39  bench_shape_base.Block2D.time_block2d((128, 128), 'uint32', (2, 2))
-        184±10μs         70.5±3μs     0.38  bench_shape_base.Block2D.time_block2d((256, 256), 'uint16', (4, 4))
-        56.1±4μs       21.0±0.9μs     0.38  bench_shape_base.Block2D.time_block2d((64, 64), 'uint64', (2, 2))
-        40.0±2μs       15.0±0.6μs     0.37  bench_shape_base.Block.time_block_simple_column_wise(10)
-         121±2μs         45.1±2μs     0.37  bench_shape_base.Block.time_nested(1)
-         179±4μs         66.1±4μs     0.37  bench_shape_base.Block2D.time_block2d((128, 128), 'uint64', (4, 4))
-        59.8±2μs         22.0±1μs     0.37  bench_shape_base.Block2D.time_block2d((128, 128), 'uint16', (2, 2))
-     3.19±0.05μs      1.17±0.02μs     0.37  bench_core.CountNonzero.time_count_nonzero(1, 100, <type 'str'>)
-        54.0±3μs         19.7±1μs     0.37  bench_shape_base.Block2D.time_block2d((32, 32), 'uint64', (2, 2))
-        56.9±1μs       20.7±0.7μs     0.36  bench_shape_base.Block2D.time_block2d((64, 64), 'uint32', (2, 2))
-      3.14±0.1μs      1.14±0.04μs     0.36  bench_core.CountNonzero.time_count_nonzero(1, 10000, <type 'bool'>)
-        92.7±2μs         33.7±2μs     0.36  bench_shape_base.Block.time_block_complicated(1)
-         104±4μs         37.8±1μs     0.36  bench_shape_base.Block.time_block_complicated(10)
-         128±5μs         45.5±2μs     0.36  bench_shape_base.Block.time_nested(10)
-       196±100μs         69.4±3μs     0.35  bench_ma.Concatenate.time_it('unmasked+masked', 2)
-         153±5μs         53.9±2μs     0.35  bench_shape_base.Block2D.time_block2d((128, 128), 'uint16', (4, 4))
-        39.4±2μs       13.8±0.5μs     0.35  bench_shape_base.Block.time_block_simple_column_wise(1)
-        53.5±2μs         18.7±1μs     0.35  bench_shape_base.Block2D.time_block2d((32, 32), 'uint8', (2, 2))
-        55.2±2μs       19.3±0.6μs     0.35  bench_shape_base.Block2D.time_block2d((32, 32), 'uint16', (2, 2))
-        16.9±1μs       5.89±0.5μs     0.35  bench_core.Core.time_dstack_l
-        60.6±3μs       21.1±0.6μs     0.35  bench_shape_base.Block2D.time_block2d((128, 128), 'uint8', (2, 2))
-      25.5±0.2μs       8.88±0.3μs     0.35  bench_shape_base.Block.time_block_simple_row_wise(10)
-        54.6±3μs       19.0±0.6μs     0.35  bench_shape_base.Block2D.time_block2d((16, 16), 'uint64', (2, 2))
-        52.6±2μs       18.2±0.7μs     0.35  bench_shape_base.Block2D.time_block2d((16, 16), 'uint16', (2, 2))
-        6.57±2μs      2.25±0.08μs     0.34  bench_core.CountNonzero.time_count_nonzero(3, 100, <type 'str'>)
-        24.3±1μs       8.30±0.6μs     0.34  bench_shape_base.Block.time_block_simple_row_wise(1)
-         148±3μs         50.0±3μs     0.34  bench_shape_base.Block2D.time_block2d((16, 16), 'uint32', (4, 4))
-         171±8μs         57.9±4μs     0.34  bench_shape_base.Block2D.time_block2d((256, 256), 'uint8', (4, 4))
-         159±5μs         53.8±1μs     0.34  bench_shape_base.Block2D.time_block2d((64, 64), 'uint64', (4, 4))
-        171±20μs         57.7±2μs     0.34  bench_shape_base.Block2D.time_block2d((128, 128), 'uint32', (4, 4))
-      3.15±0.3μs      1.06±0.03μs     0.34  bench_core.CountNonzero.time_count_nonzero(3, 100, <type 'int'>)
-        55.7±5μs       18.7±0.2μs     0.34  bench_shape_base.Block2D.time_block2d((16, 16), 'uint8', (2, 2))
-         158±7μs         52.6±3μs     0.33  bench_shape_base.Block2D.time_block2d((128, 128), 'uint8', (4, 4))
-         153±4μs         50.7±1μs     0.33  bench_shape_base.Block2D.time_block2d((32, 32), 'uint64', (4, 4))
-         152±7μs         50.3±1μs     0.33  bench_shape_base.Block2D.time_block2d((16, 16), 'uint8', (4, 4))
-        53.6±3μs       17.7±0.4μs     0.33  bench_shape_base.Block2D.time_block2d((16, 16), 'uint32', (2, 2))
-         156±4μs         51.4±3μs     0.33  bench_shape_base.Block2D.time_block2d((64, 64), 'uint8', (4, 4))
-         148±3μs         48.2±2μs     0.33  bench_shape_base.Block2D.time_block2d((16, 16), 'uint16', (4, 4))
-        160±10μs         52.0±1μs     0.33  bench_shape_base.Block2D.time_block2d((64, 64), 'uint32', (4, 4))
-         159±8μs         51.4±3μs     0.32  bench_shape_base.Block2D.time_block2d((64, 64), 'uint16', (4, 4))
-        59.8±3μs         19.3±1μs     0.32  bench_shape_base.Block2D.time_block2d((32, 32), 'uint32', (2, 2))
-         153±4μs         49.4±2μs     0.32  bench_shape_base.Block2D.time_block2d((32, 32), 'uint32', (4, 4))
-      15.6±0.6μs       5.03±0.3μs     0.32  bench_core.Core.time_vstack_l
-         154±7μs         49.7±2μs     0.32  bench_shape_base.Block2D.time_block2d((32, 32), 'uint8', (4, 4))
-        59.6±6μs       19.1±0.8μs     0.32  bench_shape_base.Block2D.time_block2d((64, 64), 'uint8', (2, 2))
-      3.03±0.4μs         969±30ns     0.32  bench_core.CountNonzero.time_count_nonzero(2, 100, <type 'int'>)
-        120±10μs         38.4±2μs     0.32  bench_shape_base.Block.time_3d(1, 'block')
-         156±5μs         49.3±1μs     0.32  bench_shape_base.Block2D.time_block2d((16, 16), 'uint64', (4, 4))
-        164±10μs         49.3±2μs     0.30  bench_shape_base.Block2D.time_block2d((32, 32), 'uint16', (4, 4))
-       65.7±10μs       19.6±0.7μs     0.30  bench_shape_base.Block2D.time_block2d((64, 64), 'uint16', (2, 2))
-     2.82±0.08μs         732±30ns     0.26  bench_core.CountNonzero.time_count_nonzero(1, 100, <type 'int'>)
-     2.77±0.07μs         664±30ns     0.24  bench_core.CountNonzero.time_count_nonzero(2, 100, <type 'bool'>)
-      2.61±0.1μs         624±20ns     0.24  bench_core.CountNonzero.time_count_nonzero(1, 100, <type 'bool'>)
-        16.8±3μs       3.97±0.2μs     0.24  bench_core.Core.time_hstack_l
-      2.78±0.1μs         637±20ns     0.23  bench_core.CountNonzero.time_count_nonzero(3, 100, <type 'bool'>)
-      2.36±0.2μs          207±5ns     0.09  bench_overrides.ArrayFunction.time_mock_broadcast_to_numpy
-      2.68±0.1μs          221±7ns     0.08  bench_overrides.ArrayFunction.time_mock_concatenate_numpy
-      2.58±0.1μs         201±10ns     0.08  bench_overrides.ArrayFunction.time_mock_broadcast_to_duck
-      3.02±0.2μs          222±6ns     0.07  bench_overrides.ArrayFunction.time_mock_concatenate_duck
-      4.29±0.3μs          216±6ns     0.05  bench_overrides.ArrayFunction.time_mock_concatenate_mixed
-        142±20μs          213±8ns     0.00  bench_overrides.ArrayFunction.time_mock_concatenate_many

SOME BENCHMARKS HAVE CHANGED SIGNIFICANTLY.

另请参见https://docs.google.com/spreadsheets/d/15-AFI_cmZqfkU6mo2p1znsQF2E52PEXpF68QqYqEar4/edit#gid = 0。

毫不奇怪,最大的性能差异是内部多次调用其他numpy函数的函数,例如np.block()

@shoyer-我对所花的额外时间感到有些不安...可能我们确实应该有一个C实现,但是与此同时,我进行了一些小的更改,使PR仅在单个情况下就节省了一些时间。类型,并且对于唯一类型为ndarray 。 请参阅#12321。

@shoyer-我在邮件列表中提出了两个可能也可以在此处说明的问题:

  1. 是否应将所有唯一类型的类似数组的参数包含在types ? (而不仅仅是提供覆盖的参数。)了解实现似乎很有帮助。 (请参阅#12327)。
  2. ndarray.__array_function__实现是否应该接受子类,即使它们重写__array_function__ ? 考虑到Liskov替代原则,并且子类已经有保释的机会,这将是合理的。 这意味着将调用ndarray.__array_function__内部的实现而不是公共函数。 (以及__array_ufunc__类似内容...)请参阅#12328以获取仅__array_function__的试用版。

@ shoyer-有关(1)的快速实现方法,请参见#12327-如果我们采用这种方法,我认为我们也应该调整NEP。

和#12328(2)试用版,主要是看它的外观。

我对这两个修改都+1。

错误消息中的调度程序函数的名称再次出现在https://github.com/numpy/numpy/pull/12789中,有人惊讶地看到TypeError: _pad_dispatcher missing 1 required positional argument

除了上面概述的替代方案https://github.com/numpy/numpy/issues/12028#issuecomment -429377396(我们目前使用2)之外,我还将添加第四个选项:

选项4 :为每个函数编写一个单独的调度程序,名称与函数相同:

def sin(a):
    return (a,)


@array_function_dispatch(sin)
def sin(a):
     ...


def cos(a):
    return (a,)


@array_function_dispatch(cos)
def cos(a):
    ...

优点:

  • Python现在总是在错误消息中提供正确的函数名称。

缺点:

  • 更多代码重复
  • 更多间接性-不再清楚哪个函数名称pad接收了错误的参数(但是我们确实有测试来验证它们是否保持同步)。

我认为为了保持当前代码正常工作,实际的功能必须在调度程序之后。

是的,但是我们可以给它起调度员的名字。 调度员名称将被覆盖。

能够为np.arange或np.empty之类的函数定义自定义调度将非常有用。

我猜一个选择是让NumPy在标量和数组上分派。 这与NEP不兼容吗? 这种变化会破坏一切吗?

有关np.arange ,请参阅https://github.com/numpy/numpy/issues/12379。

我看不到np.empty()可以如何进行分派-没有要分派的内容,只有形状和dtype。 但是肯定np.empty_like()可以以覆盖的形状进行调度-这正是https://github.com/numpy/numpy/pull/13046要支持的内容。

选项4 :为每个函数编写一个单独的调度程序,名称与函数相同:

是否对采用此选项有异议? 从用户角度来看,我认为这可能是最友好的选择。

我看不到np.empty()如何执行分派-没有要分派的内容,只有形状和dtype

您可能要分派其中任何一个。 例如,这是一个自定义形状对象,我们可能希望对其进行分派。

Screen Shot 2019-04-03 at 1 06 46 PM

这个例子不是很有用,但是我的想法是我有一个懒惰的对象,其行为类似于形状,但不返回整数,而是返回表达式。 例如,最好能够执行以下操作:

class ExprShape:
    def __getitem__(self, i):
        return ('getitem', self, i)
    def __len__(self):
        return ('len', self)

numpy.empty(ExprShape())

我想重写哪个以返回类似ExprArray('empty', ExprShape())

是的,原则上我们也可以按形状调度。 这将增加协议的额外复杂性/开销。 您是否有用例将数组用作模板(例如empty_likeshape )不够用的情况?

我能想到的其他情况是np.random.RandomState方法的size参数,但是请注意,我们目前根本不支持这些方法-请访问http://www.numpy.org/ neps / nep-0018-array-function-protocol.html#callable -objects-generated-at-runtime

您是否有用例无法将数组用作模板(如带有形状的empty_like)的情况?

如果我们采用的是依赖于NumPy的现有API,并且希望透明地使它在其他后端上工作,而无需更改现有源代码。

例如,假设我们尝试使用类似NP的数组调用scipy.optimize.differential_evolution ,这会建立一个调用图而不是立即执行。

您可以在这里看到,如果我们可以更改np.full来创建符号数组而不是默认的numpy数组,则很有用,如果传入的输入也是符号的。

如果我们采用的是依赖于NumPy的现有API,并且希望透明地使它在其他后端上工作,而无需更改现有源代码。

通常这是不可能的。 像np.array()这样的显式数组构造肯定需要重写才能与鸭子类型兼容。

在这种情况下,将energies = np.full(num_members, np.inf)切换energies = np.full_like(population, np.inf, shape=num_members)似乎是一个容易理解的更改。

通常这是不可能的。 像np.array()这样的显式数组构造肯定需要重写以与鸭子类型兼容。

是否有关于进行此类更改的建议,或者您是说要支持调度np.array真的很困难,所以我们永远也无法获得100%的支持?

在这种情况下,将能量= np.full(num_members,np.inf)切换为能量= np.full_like(population,np.inf,shape = num_members)似乎是一个容易理解的更改。

绝对是但是在很多情况下,要么您不控制源代码,要么想要支持用户尽可能多地使用他们所知道和喜欢的功能。

还有其他方式可以为用户提供这种体验,例如:

  • 提供一个新的模块,其功能类似于numpy,但行为与您希望的一样。 要求用户更改其进口
  • 检查源以了解行为。 ala numba或切线。

在某些情况下(例如,让用户调用np.full并当前返回符号结果)可能都需要这两个选项,但是如果我理解正确,NEP-18的目标是尝试限制何时需要这些选项并让人们在更多情况下使用原始的NumPy。

我知道这里存在性能/复杂性的折衷,这可能是不实施这些的很好理由。 但这可能会迫使用户探索其他方式来获得他们所希望的灵活性。

是否有关于进行此类更改的建议,或者您是说要支持调度np.array真的很困难,所以我们永远也无法获得100%的支持?

NEP 22在这里对这些选项进行了一些讨论。 我认为我们无法安全地更改np.asarray()的语义以返回除numpy.ndarray对象之外的任何内容-为此,我们将需要一个新的协议。

问题在于np.asarray()当前是转换为numpy数组对象的惯用方式,该对象使用can并且确实希望与numpy.ndarray完全匹配,例如向下匹配到内存布局。

当然,有很多用例不是这种情况,但是切换此行为会破坏很多下游代码,因此这不是入门。 下游项目将需要至少参与阵列鸭子类型的这一方面。

我知道这里存在性能/复杂性的折衷,这可能是不实施这些的很好理由。 但这可能会迫使用户探索其他方式来获得他们所希望的灵活性。

是。 NEP 18并非旨在成为NumPy替代方案的完整解决方案,而是朝着这个方向迈出的一步。

我为NEP-18起草了修订,以添加__numpy_implementation__属性:
https://github.com/numpy/numpy/pull/13305

在我看来,我们忘记了扭曲numpy.testing的函数: https :

我要马上做...

我想对NEP进行一个修订,以明确阐明NEP-18对子类作者的保证: https :

自gh-13329修复以来,我将可用性任务标记为完成。 我们决定gh-#13588可以等到1.17发布之后。 这就留下了文档方面的改进, arange gh-12379仍然可以在1.17中使用。

还有#13728-调度程序中的histogram[2d]d

这就留下了文档方面的改进,而gh-12379系列仍然可以在1.17中使用。

缺少文档问题,因此我打开了gh-13844。 我认为文档比arange公开问题重要得多。

@shoyer我们可以关闭它吗?

此页面是否有帮助?
0 / 5 - 0 等级