Numpy: Rastreamento de problema para implementação de NEP-18 (__array_function__)

Criado em 25 set. 2018  ·  54Comentários  ·  Fonte: numpy/numpy

  • [x] Funcionalidade central para suporte a substituições:

    • [x] Implementação inicial em Python puro (# 12005)

    • [x] Validar funções do expedidor em array_function_dispatch (https://github.com/numpy/numpy/pull/12099)



      • Desative a validação quando não estiver testando NumPy (se houver um impacto mensurável nos tempos de importação) (desnecessário)



    • [x] Adicione um atributo de função .__skip_array_function__ para permitir pular __array_function__ despacho. (https://github.com/numpy/numpy/pull/13389)

  • [x] Reimplemente partes de numpy/core/overrides.py em C para aumentar a velocidade (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] Substituições de suporte para todas as funções públicas de NumPy

    • [x] numpy.core



      • [x] a parte fácil (https://github.com/numpy/numpy/pull/12115)


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


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



    • [x] numpy.lib



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


      • [x] parte 2 (# 12119)



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

    • [x] funções atualmente escritas inteiramente em C : empty_like, concatenate, inner, where, lexsort, can_cast, min_scalar_type, result_type, ponto, vdot, is_busday, busday_offset, busday_count, datetime_as_string (https://github.com/numpy/numpy/ pull / 12175)

    • [x] linspace

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

  • [x] Melhorias de usabilidade

    • [x] [melhor mensagem de erro] (https://github.com/numpy/numpy/issues/12213) para funções não implementadas (https://github.com/numpy/numpy/pull/12251)

    • [x] ndarray.__repr__ não deve contar com __array_function__ (https://github.com/numpy/numpy/pull/12212)

    • [x] stacklevel deve ser aumentado em 1 para funções agrupadas, então tracebacks apontam para o lugar certo (gh-13329)

  • [x] Corrigir todos os bugs conhecidos / falhas de teste downstream
  • [] Documentação

    • [x] Notas de lançamento (# 12028)

    • [x] Documentos narrativos

    • [] Documentação revisada para esclarecer argumentos sobrecarregados?

__array_function__

Comentários muito úteis

Existe uma proposta em torno de fazer esse tipo de alteração ou você está dizendo que apoiar o envio de np.array seria muito difícil e, portanto, não poderemos nunca chegar a 100% de suporte?

NEP 22 tem alguma discussão sobre as opções aqui. Não acho que possamos alterar com segurança a semântica de np.asarray() para retornar qualquer coisa diferente de um objeto numpy.ndarray - precisaremos de um novo protocolo para isso.

O problema é que np.asarray() é atualmente a forma idiomática de converter para um objeto array numpy, que usa pode e espera corresponder exatamente a numpy.ndarray , por exemplo, no layout de memória.

Certamente há muitos casos de uso em que esse não é o caso, mas mudar esse comportamento quebraria muitos códigos downstream, portanto, não é um iniciante. Os projetos downstream precisarão aceitar pelo menos esse aspecto da tipagem de pato de matriz.

Eu entendo que há uma compensação de desempenho / complexidade aqui e pode ser um bom motivo para não implementá-los. Mas pode forçar os usuários a explorar outros meios para obter a flexibilidade que desejam.

Sim. NEP 18 não pretende ser uma solução completa para alternativas NumPy drop-in, mas é um passo nessa direção.

Todos 54 comentários

Pode ser bom fundir um preliminar "Decorar todas as funções públicas NumPy com @array_function_dispatch" para algumas funções de alto perfil e solicitar aos consumidores downstream do protocolo para experimentá-lo

Assim que fundirmos https://github.com/numpy/numpy/pull/12099 , tenho outro PR pronto que adicionará decoradores de despacho para a maior parte de numpy.core . Vai ser muito fácil terminar as coisas - este levou menos de uma hora para montar.

cc @ eric-wieser @mrocklin @mhvk @hameerabbasi

Veja https://github.com/shoyer/numpy/tree/array-function-easy-impl para meu branch implementando todas as substituições "fáceis" em funções com wrappers Python. As partes restantes são np.block , np.einsum e um punhado de funções multiarray escritas inteiramente em C (por exemplo, np.concatenate ). Vou dividir isso em um monte de PRs quando terminarmos com o # 12099.

Observe que não escrevi testes para substituições em cada função individual. Gostaria de adicionar alguns testes de integração quando terminarmos (por exemplo, um array de pato que registra todas as operações aplicadas), mas não acho que seria produtivo escrever testes de despacho para cada função individual. As verificações em # 12099 devem capturar os erros mais comuns em despachantes, e cada linha de código em funções de despachante deve ser executada por testes existentes.

@shoyer - nos testes, concordo que não é particularmente útil escrever testes para cada um; em vez disso, dentro de numpy, pode fazer mais sentido começar a usar as substituições de forma relativamente rápida em MaskedArray .

@mhvk parece bom para mim, embora eu deixe alguém que usa / conhece MaskedArray assumir a liderança nisso.

Consulte https://github.com/numpy/numpy/pull/12115 , https://github.com/numpy/numpy/pull/12116 , # 12119 e https://github.com/numpy/numpy/pull/ 12117 para PRs implementando suporte a __array_function__ em funções definidas em Python.

@shoyer - vendo algumas das implementações, tenho duas preocupações:

  • Para algumas funções, como reshape , a funcionalidade original já fornecia uma maneira de sobrescrevê-la, definindo um método reshape . Estamos efetivamente descontinuando isso para qualquer classe que defina __array_function__ .
  • Para outras funções, como np.median , o uso cuidadoso de np.asanyarray e ufuncs garantiu que as subclasses já pudessem usá-los. Mas essa funcionalidade não pode mais ser acessada diretamente.

Acho que no geral essas duas coisas são provavelmente benefícios, já que simplificamos a interface e podemos otimizar as implementações para ndarray puros - embora o último sugira que ndarray.__array_function__ deve assumir a conversão de listas, etc., a ndarray , para que as implementações possam pular essa parte). Ainda assim, pensei em observá-lo, pois me faz temer a implementação disso por Quantity um pouco mais do que eu pensava - em termos de quantidade de trabalho e possível impacto no desempenho.

embora o último sugira que ndarray .__ array_function__ deve assumir a conversão de listas, etc., em ndarray, de modo que as implementações possam pular essa parte).

Não tenho certeza se estou seguindo aqui.

De fato, estamos efetivamente substituindo o método antigo de substituir funções como reshape e mean , embora o método antigo ainda suporte implementações incompletas da API do NumPy.

Não tenho certeza se estou seguindo aqui.

Acho que o problema é que, se implementarmos __array_function__ mesmo para uma única função, os mecanismos anteriores quebram completamente e não há como fazer o failover. É por isso que proponho que revisitemos minha proposta NotImplementedButCoercible .

@hameerabbasi - sim, esse é o problema. Embora precisemos ser cuidadosos aqui, como tornamos fácil confiar em soluções de fita adesiva das quais realmente preferiríamos nos livrar ... (é por isso que escrevi acima que meus "problemas" podem na verdade ser benefícios ...) . Talvez seja o caso de tentar como está em 1.16 e, em seguida, decidir sobre a experiência real se queremos fornecer um retorno de "ignorar meu __array_function__ para este caso".

Re: estilo do despachante: Minhas preferências de estilo são baseadas em considerações de tempo de memória / importação e verbosidade. Muito simplesmente, mescle os despachantes onde a assinatura provavelmente permanecerá a mesma. Dessa forma, criamos a menor quantidade de objetos e os acertos do cache também serão maiores.

Dito isso, não sou muito contra o estilo lambda.

O estilo para escrever funções de despachante agora surgiu em alguns PRs. Seria bom fazer uma escolha consistente em NumPy.

Temos algumas opções:


Opção 1 : escreva um despachante separado para cada função, por exemplo,

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):
    ...

Vantagens:

  • Muito legível
  • Definições fáceis de encontrar de funções de despachante
  • Limpe a mensagem de erro quando você fornecer os argumentos errados, por exemplo, sin(x=1) -> TypeError: _sin_dispatcher() got an unexpected keyword argument 'x' .

Desvantagens:

  • Muita repetição, mesmo quando muitas funções em um módulo têm exatamente a mesma assinatura.

Opção 2 : reutilizar funções do despachante dentro de um módulo, por exemplo,

def _unary_dispatcher(a):
    return (a,)


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


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

Vantagens:

  • Menos repetição
  • Legível

Desvantagens:

  • Pode ser um pouco mais difícil encontrar definições de funções de despachante
  • Mensagens de erro um pouco menos claras para argumentos ruins, por exemplo, sin(x=1) -> TypeError: _unary_dispatcher() got an unexpected keyword argument 'x'

Opção 3 : use funções lambda quando a definição do despachante caberia em uma linha, por exemplo,

# 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):
     ...

Vantagens:

  • Não há necessidade de procurar por definições de despachante, elas estão bem aqui.
  • Menos número de caracteres e linhas de códigos.
  • Parece muito bom para casos curtos (por exemplo, um argumento), especialmente quando o lambda é mais curto que o nome da função.

Desvantagens:

  • Código mais repetido do que a opção 2.
  • Parece muito confuso se houver mais do que alguns argumentos
  • Também tem mensagens de erro menos claras ( TypeError: <lambda>() got an unexpected keyword argument 'x' )

@shoyer : editado para adicionar o espaçamento PEP8 de duas linhas para tornar o aspecto de "linhas de código" mais realista

Observe que os problemas de mensagem de erro podem ser corrigidos reconstruindo o objeto de código , embora isso venha com algum custo de tempo de importação. Talvez valha a pena investigar e descobrir o atum de @nschloe para comparar algumas opções.

Sim, o módulo decorador também pode ser usado para gerar definição de função (ele usa uma abordagem um pouco diferente para a geração de código, um pouco mais como namedtuple porque usa exec() ).

Contanto que o erro não seja resolvido, acho que precisamos nos ater às opções com um despachante que tenha um nome claro. Gostaria de agrupar ligeiramente os despachantes (2) por motivos de memória, mas manteria a mensagem de erro muito em mente, então sugeriria chamar o despachante algo como _dispatch_on_x .

Porém, se pudermos mudar o erro, as coisas mudam. Por exemplo, pode ser tão simples quanto capturar exceções, substituindo <lambda> pelo nome da função no texto da exceção e, em seguida, levantando novamente. (Ou essa coisa de corrente hoje em dia?)

Concordo que a mensagem de erro deve ser clara, idealmente não deve mudar em nada.

OK, por enquanto acho melhor adiar o uso de lambda , a menos que tenhamos algum tipo de geração de código funcionando.

https://github.com/numpy/numpy/pull/12175 adiciona um rascunho de como as substituições para funções multiarray (escritas em C) poderiam parecer se adotarmos a abordagem do wrapper Python.

@mattip, onde estamos implementando matmul como um ufunc? Assim que terminarmos todas essas substituições de __array_function__ , acho que é a última coisa que precisamos para tornar a API pública do NumPy totalmente sobrecarregável. Seria bom ter tudo pronto para o NumPy 1.16!

PR # 11175, que implementa o NEP 20, tem progredido lentamente. É um bloqueador para PR # 11133, que contém o código de loop matmul. Esse ainda precisa ser atualizado e depois verificado através de benchmarks se o novo código não é mais lento que o antigo.

Tenho quatro PRs para revisão que devem completar o conjunto completo de substituições. Avaliações / aprovações / fusões finais serão apreciadas para que possamos começar a testar __array_function__ sério! https://github.com/numpy/numpy/pull/12154 , https://github.com/numpy/numpy/pull/12163 , https://github.com/numpy/numpy/pull/12119 , https: //github.com/numpy/numpy/pull/12175

Adicionar substituições a np.core causou a falha de alguns testes do pandas (https://github.com/pandas-dev/pandas/issues/23172). Não temos certeza do que está acontecendo ainda, mas devemos definitivamente descobrir antes de lançar.

Veja https://github.com/numpy/numpy/issues/12225 para meu melhor palpite sobre por que isso está causando falhas de teste no dask / pandas.

Alguns benchmarks de tempos de importação (no meu macbook pro com uma unidade de estado sólido):

  • NumPy 1.15.2: 152,451 ms
  • Mestre NumPy: 156,5745 ms
  • Usando decorator.decorate (# 12226): 183,694 ms

Meu script de benchmark

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)

Alguma ideia do uso de memória (antes / depois)? Isso também é útil, especialmente para aplicativos de IoT.

Você sabe como medir de forma confiável o uso de memória para um módulo?
No sábado, 20 de outubro de 2018 às 6h56 Hameer Abbasi [email protected]
escrevi:

Alguma ideia do uso de memória (antes / depois)? Isso é meio útil, pois
bem, especialmente para aplicativos IoT.

-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/numpy/numpy/issues/12028#issuecomment-431584123 ou mudo
o segmento
https://github.com/notifications/unsubscribe-auth/ABKS1k_IkrJ2YmYReaDrnkNvcH2X0-ZCks5umyuogaJpZM4W3kSC
.

Acho que escrever um script contendo import numpy as np , adicionar uma instrução sleep e rastrear a memória do processo deve ser suficiente. https://superuser.com/questions/581108/how-can-i-track-and-log-cpu-and-memory-usage-on-a-mac

Qualquer outro desenvolvedor central deseja dar uma olhada rápida (na verdade, inclui apenas duas funções!) Em https://github.com/numpy/numpy/pull/12163? É o último PR adicionando array_function_dispatch às funções numpy internas.

Para referência, aqui está a diferença de desempenho que vejo ao desativar __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.

consulte também https://docs.google.com/spreadsheets/d/15-AFI_cmZqfkU6mo2p1znsQF2E52PEXpF68QqYqEar4/edit#gid = 0 para uma planilha.

Não surpreendentemente, a maior diferença de desempenho é para funções que chamam outras funções numpy internamente muitas vezes, por exemplo, para np.block() .

@shoyer - Fiquei um pouco desconcertado com o tempo extra gasto ... Provavelmente, nós realmente deveríamos ter uma implementação C, mas nesse meio tempo fiz um PR com algumas pequenas mudanças que economizam algum tempo para o caso comum de apenas um tipo e para o caso em que o único tipo é ndarray . Veja # 12321.

@shoyer - Eu levantei duas questões na lista de e-mails que provavelmente também devem ser

  1. Todos os tipos exclusivos de argumentos semelhantes a matrizes devem ser incluídos em types ? (em vez de apenas aqueles de argumentos que fornecem uma substituição.) Parece útil saber para implementações. (consulte # 12327).
  2. A implementação de ndarray.__array_function__ aceitar subclasses mesmo que elas substituam __array_function__ ? Isso seria razoável, dado o princípio de substituição de Liskov e dado que a subclasse já teve a chance de sair. Isso implicaria chamar a implementação em vez da função pública dentro de ndarray.__array_function__ . (E algo semelhante em __array_ufunc__ ...) Veja # 12328 para um teste de __array_function__ apenas.

@shoyer - consulte # 12327 para uma implementação rápida de (1) - se seguirmos esse caminho, acho que também devemos ajustar o NEP.

E # 12328 para um teste de (2), principalmente para ver como fica.

Eu sou +1 em ambas as modificações aqui.

O nome das funções do despachante em mensagens de erro apareceu novamente em https://github.com/numpy/numpy/pull/12789 , onde alguém ficou surpreso ao ver TypeError: _pad_dispatcher missing 1 required positional argument

Além das alternativas descritas acima https://github.com/numpy/numpy/issues/12028#issuecomment -429377396 (atualmente usamos 2), adicionarei uma quarta opção:

Opção 4 : escreva um despachante separado para cada função, com o mesmo nome da função:

def sin(a):
    return (a,)


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


def cos(a):
    return (a,)


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

Vantagens:

  • Python agora sempre fornece os nomes de função corretos na mensagem de erro.

Desvantagens:

  • Mais repetição de código
  • Mais indireção - não está mais claro qual nome de função pad recebeu os argumentos errados (mas temos testes para verificar se eles são mantidos em sincronia).

Eu acho que para manter o código atual funcionando, a função real deve vir _após_ o despachante.

Certo, mas podemos dar a ele o mesmo nome do despachante. O nome do despachante será sobrescrito.

Seria ótimo poder definir o despacho personalizado para funções como np.arange ou np.empty.

Eu acho que uma opção seria para NumPy despachar em escalares, bem como em matrizes. Isso é incompatível com o NEP? Alguma coisa quebraria com essa mudança?

Para discussões sobre np.arange , consulte https://github.com/numpy/numpy/issues/12379.

Não vejo como np.empty() poderia fazer o despacho - não há nada para despachar, apenas uma forma e um tipo de d. Mas certamente np.empty_like() poderia despachar com uma forma sobrescrita - é exatamente isso que https://github.com/numpy/numpy/pull/13046 tem a ver com o suporte.

Opção 4 : escreva um despachante separado para cada função, com o mesmo nome da função:

Alguma objeção à adoção desta opção? Acho que é provavelmente a escolha mais amigável do ponto de vista do usuário.

Não vejo como np.empty () poderia fazer o despacho - não há nada para despachar, apenas uma forma e um dtype

Você pode querer despachar em qualquer um deles. Por exemplo, aqui está um objeto de forma personalizada, que podemos desejar enviar de forma diferente.

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

Este exemplo não é muito útil, mas a ideia é que eu tenho um objeto preguiçoso que se comporta como forma, mas não retorna inteiros, ele retorna expressões. Por exemplo, seria bom ser capaz de fazer algo assim:

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

numpy.empty(ExprShape())

Que eu gostaria de substituir para retornar algo como ExprArray('empty', ExprShape()) .

Sim, em princípio também poderíamos despachar na forma. Isso adicionaria complexidade / sobrecarga adicional ao protocolo. Você tem casos de uso em que usar um array como modelo (como empty_like com shape ) não seria suficiente?

Os outros casos em que consigo pensar são o argumento size para np.random.RandomState métodos, mas observe que atualmente não há suporte para eles - consulte http://www.numpy.org/ neps / nep-0018-array-function-protocol.html # callable -objects-generated-at-runtime

Você tem casos de uso em que usar um array como modelo (como empty_like com forma) não seria suficiente?

Se estivermos usando uma API existente que depende do NumPy e quisermos que funcione de forma transparente em um backend diferente, sem alterar o código-fonte existente.

Por exemplo, digamos que estivéssemos tentando chamar scipy.optimize.differential_evolution com NP como matrizes, que criam um gráfico de chamadas em vez de serem executados imediatamente.

Você pode ver aqui que seria útil se pudéssemos alterar np.full para criar uma matriz simbólica em vez de uma matriz numpy padrão, se a entrada passada também fosse simbólica.

Se estivermos usando uma API existente que depende do NumPy e quisermos que funcione de forma transparente em um backend diferente, sem alterar o código-fonte existente.

Em geral, isso não é possível. A construção de array explícito como np.array() definitivamente precisará ser reescrita para ser compatível com a digitação duck.

Nesse caso, mudar energies = np.full(num_members, np.inf) para energies = np.full_like(population, np.inf, shape=num_members) parece uma mudança fácil e legível.

Em geral, isso não é possível. A construção de array explícito como np.array () definitivamente precisará ser reescrita para ser compatível com a digitação duck.

Existe uma proposta em torno de fazer esse tipo de alteração ou você está dizendo que apoiar o envio de np.array seria muito difícil e, portanto, não poderemos nunca chegar a 100% de suporte?

Neste caso, trocar energias = np.full (num_members, np.inf) para energies = np.full_like (população, np.inf, forma = num_members) parece uma mudança fácil e legível.

Definitivamente. Mas há muitos casos em que você não controla o código-fonte ou deseja oferecer suporte aos usuários no uso das funções que eles conhecem e amam, tanto quanto possível.

Existem outras maneiras de fornecer essa experiência aos usuários, como:

  • Fornecer um novo módulo que atua como entorpecido, mas se comporta como você gostaria. Requer que os usuários alterem suas importações
  • Inspecione a fonte para entender o comportamento. ala numba ou tangente.

Ambas as opções podem ser necessárias em certos casos (como permitir que os usuários chamem np.full e retornem um resultado simbólico atualmente), mas se bem entendi, o objetivo do NEP-18 é tentar limitar quando eles são necessários e permitir que as pessoas usem o NumPy original em mais casos.

Eu entendo que há uma compensação de desempenho / complexidade aqui e pode ser um bom motivo para não implementá-los. Mas pode forçar os usuários a explorar outros meios para obter a flexibilidade que desejam.

Existe uma proposta em torno de fazer esse tipo de alteração ou você está dizendo que apoiar o envio de np.array seria muito difícil e, portanto, não poderemos nunca chegar a 100% de suporte?

NEP 22 tem alguma discussão sobre as opções aqui. Não acho que possamos alterar com segurança a semântica de np.asarray() para retornar qualquer coisa diferente de um objeto numpy.ndarray - precisaremos de um novo protocolo para isso.

O problema é que np.asarray() é atualmente a forma idiomática de converter para um objeto array numpy, que usa pode e espera corresponder exatamente a numpy.ndarray , por exemplo, no layout de memória.

Certamente há muitos casos de uso em que esse não é o caso, mas mudar esse comportamento quebraria muitos códigos downstream, portanto, não é um iniciante. Os projetos downstream precisarão aceitar pelo menos esse aspecto da tipagem de pato de matriz.

Eu entendo que há uma compensação de desempenho / complexidade aqui e pode ser um bom motivo para não implementá-los. Mas pode forçar os usuários a explorar outros meios para obter a flexibilidade que desejam.

Sim. NEP 18 não pretende ser uma solução completa para alternativas NumPy drop-in, mas é um passo nessa direção.

Elaborei uma revisão do NEP-18 para adicionar um atributo __numpy_implementation__ :
https://github.com/numpy/numpy/pull/13305

Ocorreu-me que esquecemos de distorcer as funções em numpy.testing : https://github.com/numpy/numpy/issues/13588

Eu vou fazer isso em breve ...

Há uma revisão que gostaria de ver no NEP, especificamente para esclarecer quais garantias o NEP-18 oferece aos autores de subclasses: https://github.com/numpy/numpy/pull/13633

Marquei as tarefas de usabilidade concluídas desde que o gh-13329 foi corrigido. Decidimos- # 13588 podemos esperar até depois do lançamento do 1.17. Isso deixa as melhorias na documentação e arange gh-12379 ainda abertas para inclusão no 1.17.

Também há # 13728 - um bug no despachante para histogram[2d]d

Isso deixa as melhorias na documentação e o arange gh-12379 ainda aberto para inclusão no 1.17.

Um problema de documentação estava faltando, então abri gh-13844. Acho que os documentos são muito mais importantes do que a questão aberta arange .

@shoyer podemos fechar isso?

Esta página foi útil?
0 / 5 - 0 avaliações