.__skip_array_function__
pour permettre de sauter __array_function__
dispatch. (https://github.com/numpy/numpy/pull/13389)numpy/core/overrides.py
en C pour la vitesse (https://github.com/numpy/numpy/issues/12028):get_overloaded_types_and_args
array_function_implementation_or_override
ndarray.__array_function__
?array_function_dispatch
?numpy.core
np.core.defchararray
(# 12154)np.einsum
et np.block
(https://github.com/numpy/numpy/pull/12163)numpy.lib
numpy.fft
/ numpy.linalg
(https://github.com/numpy/numpy/pull/12117)arange?
] (https://github.com/numpy/numpy/issues/12379)ndarray.__repr__
ne doit pas compter sur __array_function__
(https://github.com/numpy/numpy/pull/12212)stacklevel
doit être augmenté de 1 pour les fonctions encapsulées, donc les retraits pointent vers le bon endroit (gh-13329)Il pourrait être bon de fusionner un préliminaire "Décorez toutes les fonctions NumPy publiques avec @array_function_dispatch" pour certaines fonctions de haut niveau et demandez aux consommateurs en aval du protocole de l'essayer
Une fois que nous fusionnons https://github.com/numpy/numpy/pull/12099, j'ai un autre PR prêt qui ajoutera des décorateurs d'expédition pour la plupart des numpy.core
. Il sera assez facile de terminer les choses - celui-ci a pris moins d'une heure à assembler.
cc @ eric-wieser @mrocklin @mhvk @hameerabbasi
Voir https://github.com/shoyer/numpy/tree/array-function-easy-impl pour ma branche implémentant tous les remplacements "faciles" sur les fonctions avec des wrappers Python. Les parties restantes sont np.block
, np.einsum
et une poignée de fonctions multiarray écrites entièrement en C (par exemple, np.concatenate
). Je vais diviser cela en un tas de PR une fois que nous aurons terminé avec # 12099.
Notez que je n'ai pas écrit de tests pour les remplacements sur chaque fonction individuelle. J'aimerais ajouter quelques tests d'intégration lorsque nous aurons terminé (par exemple, un tableau de canard qui enregistre toutes les opérations appliquées), mais je ne pense pas qu'il serait productif d'écrire des tests de répartition pour chaque fonction individuelle. Les vérifications dans # 12099 devraient détecter les erreurs les plus courantes sur les répartiteurs, et chaque ligne de code dans les fonctions du répartiteur devrait être exécutée par les tests existants.
@shoyer - sur les tests, je suis d'accord qu'il n'est pas particulièrement utile d'écrire des tests pour chacun d'eux; au lieu de cela, dans numpy, il peut être plus judicieux de commencer à utiliser les remplacements relativement rapidement dans MaskedArray
.
@mhvk me semble bien, bien que je laisse quelqu'un d'autre qui utilise / connaît MaskedArray prendre les devants à ce sujet.
Voir https://github.com/numpy/numpy/pull/12115 , https://github.com/numpy/numpy/pull/12116 , # 12119 et https://github.com/numpy/numpy/pull/ 12117 pour les PR implémentant le support __array_function__
sur les fonctions définies en Python.
@shoyer - voyant certaines implémentations, j'ai deux soucis:
reshape
, la fonctionnalité d'origine fournissait déjà un moyen de la remplacer, en définissant une méthode reshape
. Nous abandonnons effectivement cela pour toute classe qui définit __array_function__
.np.median
, une utilisation prudente de np.asanyarray
et ufuncs garantit que les sous-classes peuvent déjà les utiliser. Mais cette fonctionnalité n'est plus accessible directement.Je pense que dans l'ensemble, ces deux choses sont probablement des avantages, car nous simplifions l'interface et pouvons rendre les implémentations optimisées pour ndarray
- bien que ce dernier suggère que ndarray.__array_function__
devrait prendre en charge la conversion des listes, etc., à ndarray
, afin que les implémentations puissent sauter cette partie). Pourtant, j'ai pensé le noter car cela me fait redouter de l'implémenter pour Quantity
un peu plus que je ne le pensais - en termes à la fois de la quantité de travail et du succès possible en termes de performances.
bien que ce dernier suggère que ndarray .__ array_function__ devrait prendre en charge la conversion des listes, etc., en ndarray, afin que les implémentations puissent sauter cette partie).
Je ne suis pas sûr de suivre ici.
Nous sommes en effet en train de déprécier l'ancienne façon de remplacer des fonctions telles que reshape
et mean
, bien que l'ancienne méthode supporte toujours les implémentations incomplètes de l'API de NumPy.
Je ne suis pas sûr de suivre ici.
Je pense que le problème est que si nous implémentons __array_function__
même pour une seule fonction, les mécanismes précédents se cassent complètement et il n'y a aucun moyen de basculer. C'est pourquoi je propose de revoir ma proposition NotImplementedButCoercible
.
@hameerabbasi - oui, c'est le problème. Bien que nous devions faire attention ici à la facilité avec laquelle nous nous basons sur des solutions de ruban adhésif dont nous préférerions vraiment nous débarrasser ... (c'est pourquoi j'ai écrit ci-dessus que mes "problèmes" peuvent en fait être des avantages ...) . Il y a peut-être lieu d'essayer tel quel en 1.16 et de décider ensuite, en fonction de l'expérience réelle, si nous voulons fournir une solution de secours de "ignorer mon __array_function__
pour ce cas".
Re: style du répartiteur: Mes préférences en matière de style sont basées sur des considérations de mémoire / temps d'importation et de verbosité. Tout simplement, fusionnez les répartiteurs où la signature est susceptible de rester la même. De cette façon, nous créons le moins d'objets et les hits de cache seront également plus élevés.
Cela dit, je ne suis pas trop opposé au style lambda.
Le style d'écriture des fonctions de répartiteur est maintenant apparu dans quelques PR. Il serait bon de faire un choix cohérent dans NumPy.
Nous avons quelques options:
Option 1 : écrivez un répartiteur distinct pour chaque fonction, par exemple,
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):
...
Avantages:
sin(x=1)
-> TypeError: _sin_dispatcher() got an unexpected keyword argument 'x'
.Désavantages:
Option 2 : réutiliser les fonctions du répartiteur dans un module, par exemple,
def _unary_dispatcher(a):
return (a,)
@array_function_dispatch(_unary_dispatcher)
def sin(a):
...
@array_function_dispatch(_unary_dispatcher)
def cos(a):
...
Avantages:
Désavantages:
sin(x=1)
-> TypeError: _unary_dispatcher() got an unexpected keyword argument 'x'
Option 3 : Utilisez les fonctions lambda
lorsque la définition du répartiteur tient sur une seule ligne, par exemple,
# 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):
...
Avantages:
Désavantages:
TypeError: <lambda>() got an unexpected keyword argument 'x'
)@shoyer : modifié pour ajouter l'espacement PEP8 sur deux lignes pour rendre l'aspect "lignes de code" plus réaliste
Notez que les problèmes de message d'erreur peuvent être résolus en reconstruisant l'objet de code , bien que cela entraîne un certain coût en temps d'importation. Cela vaut peut-être la peine d'enquêter et de sortir le thon de
Oui, le module décorateur pourrait également être utilisé pour générer la définition de fonction (il utilise une approche légèrement différente pour la génération de code, un peu plus comme namedtuple en ce qu'il utilise exec()
).
Tant que l'erreur n'est pas résolue, je pense que nous devons nous en tenir aux options avec un répartiteur qui a un nom clair. Je vais légèrement regrouper les répartiteurs (2) pour des raisons de mémoire, mais je garderais alors le message d'erreur à l'esprit, donc je suggérerais d'appeler le répartiteur quelque chose comme _dispatch_on_x
.
Mais si nous pouvons changer l'erreur, les choses changent. Par exemple, cela peut être aussi simple que d'attraper des exceptions, de remplacer <lambda>
par le nom de la fonction dans le texte de l'exception, puis de le relancer. (Ou est-ce que cette chaîne est de nos jours?)
Je suis d'accord que le message d'erreur doit être clair, idéalement ne devrait pas changer du tout.
OK, pour l'instant, je pense qu'il est préférable de ne pas utiliser lambda
, sauf si nous obtenons une sorte de génération de code qui fonctionne.
https://github.com/numpy/numpy/pull/12175 ajoute un brouillon de ce à quoi pourraient ressembler les remplacements pour les fonctions multiarray (écrites en C) si nous adoptons l'approche wrapper Python.
@mattip où en sommes-nous sur l'implémentation de matmul
comme ufunc? Une fois que nous avons terminé tous ces remplacements __array_function__
, je pense que c'est la dernière chose dont nous avons besoin pour rendre l'API publique de NumPy entièrement surchargeable. Ce serait bien d'avoir tout prêt pour NumPy 1.16!
Le PR # 11175, qui met en œuvre le NEP 20, progresse lentement. C'est un bloqueur pour PR # 11133, qui a le code de boucle matmul. Celui-ci doit encore être mis à jour puis vérifié via des benchmarks que le nouveau code n'est pas plus lent que l'ancien.
J'ai quatre PR à examiner qui devraient compléter l'ensemble complet des dérogations. Les révisions / approbations / fusions finales seraient appréciées afin que nous puissions commencer à tester __array_function__
sérieusement! 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
L'ajout de remplacements à np.core
provoqué l'échec de quelques tests pandas (https://github.com/pandas-dev/pandas/issues/23172). Nous ne sommes pas encore tout à fait sûr de ce qui se passe, mais nous devons absolument le comprendre avant de sortir.
Voir https://github.com/numpy/numpy/issues/12225 pour ma meilleure estimation de la raison pour laquelle cela provoque des échecs de test dans dask / pandas.
Quelques repères des temps d'import (sur mon macbook pro avec un disque SSD):
decorator.decorate
(# 12226): 183,694 msMon script de référence
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)
Une idée de l'utilisation de la mémoire (avant / après)? C'est également très utile, en particulier pour les applications IoT.
Savez-vous comment mesurer de manière fiable l'utilisation de la mémoire pour un module?
Le sam 20 octobre 2018 à 6 h 56 Hameer Abbasi [email protected]
a écrit:
Une idée de l'utilisation de la mémoire (avant / après)? C'est un peu utile car
bien, en particulier pour les applications IoT.-
Vous recevez cela parce que vous avez été mentionné.
Répondez directement à cet e-mail, affichez-le sur GitHub
https://github.com/numpy/numpy/issues/12028#issuecomment-431584123 , ou muet
le fil
https://github.com/notifications/unsubscribe-auth/ABKS1k_IkrJ2YmYReaDrnkNvcH2X0-ZCks5umyuogaJpZM4W3kSC
.
Je pense qu'écrire un script contenant import numpy as np
, ajouter une instruction de sommeil et suivre la mémoire du processus devrait suffire. https://superuser.com/questions/581108/how-can-i-track-and-log-cpu-and-memory-usage-on-a-mac
Tous les autres développeurs de base veulent jeter un coup d'œil rapide (vraiment, cela ne comprend que deux fonctions!) À https://github.com/numpy/numpy/pull/12163? C'est le dernier PR ajoutant array_function_dispatch
aux fonctions numpy internes.
Pour référence, voici la différence de performances que je vois lors de la désactivation de __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.
voir également https://docs.google.com/spreadsheets/d/15-AFI_cmZqfkU6mo2p1znsQF2E52PEXpF68QqYqEar4/edit#gid = 0 pour une feuille de calcul.
Sans surprise, la plus grande différence de performances concerne les fonctions qui appellent plusieurs fois en interne d'autres fonctions numpy, par exemple pour np.block()
.
@shoyer - J'ai été un peu déconcerté par le temps supplémentaire pris ... Probablement, nous devrions vraiment avoir une implémentation C, mais en attendant j'ai fait un PR avec quelques petits changements qui font gagner du temps pour le cas courant d'un seul type, et pour le cas où le seul type est ndarray
. Voir # 12321.
@shoyer - J'ai soulevé deux problèmes sur la liste de diffusion qu'il est probablement bon de
types
? (plutôt que simplement ceux des arguments qui fournissent un remplacement.) Il semblerait utile de connaître les implémentations. (voir # 12327).ndarray.__array_function__
devrait-elle accepter des sous-classes même si elles remplacent __array_function__
? Cela serait raisonnable étant donné le principe de substitution de Liskov et étant donné que la sous-classe avait déjà une chance de se libérer. Cela impliquerait d'appeler l'implémentation plutôt que la fonction publique dans ndarray.__array_function__
. (Et quelque chose de similaire dans __array_ufunc__
...) Voir # 12328 pour un essai pour __array_function__
seulement.@shoyer - voir # 12327 pour une implémentation rapide de (1) - si nous
Et # 12328 pour un essai de (2), principalement pour voir à quoi il ressemble.
Je suis +1 sur les deux modifications ici.
Le nom des fonctions de répartiteur dans les messages d'erreur est revenu sur https://github.com/numpy/numpy/pull/12789 , où quelqu'un a été surpris de voir TypeError: _pad_dispatcher missing 1 required positional argument
En plus des alternatives décrites ci-dessus https://github.com/numpy/numpy/issues/12028#issuecomment -429377396 (nous utilisons actuellement 2), je vais ajouter une quatrième option:
Option 4 : Écrivez un répartiteur distinct pour chaque fonction, avec le même nom que la fonction:
def sin(a):
return (a,)
@array_function_dispatch(sin)
def sin(a):
...
def cos(a):
return (a,)
@array_function_dispatch(cos)
def cos(a):
...
Avantages:
Désavantages:
pad
reçu les mauvais arguments (mais nous avons des tests pour vérifier qu'ils sont synchronisés).Je pense que pour que le code actuel fonctionne, la fonction réelle doit venir _after_ le répartiteur.
D'accord, mais on peut lui donner le même nom que le répartiteur. Le nom du répartiteur sera écrasé.
Ce serait formidable de pouvoir définir une répartition personnalisée pour des fonctions telles que np.arange ou np.empty.
Je suppose qu'une option serait pour NumPy d'envoyer sur des scalaires ainsi que des tableaux. Est-ce incompatible avec le NEP? Quelque chose romprait-il avec ce changement?
Pour une discussion sur np.arange
, voir https://github.com/numpy/numpy/issues/12379.
Je ne vois pas comment np.empty()
pourrait faire le dispatching - il n'y a rien à envoyer, juste une forme et un type. Mais certainement np.empty_like()
pourrait faire la distribution avec une forme écrasée - c'est exactement ce que https://github.com/numpy/numpy/pull/13046 est sur le support.
Option 4 : Écrivez un répartiteur distinct pour chaque fonction, avec le même nom que la fonction:
Y a-t-il des objections à l'adoption de cette option? Je pense que c'est probablement le choix le plus convivial du point de vue de l'utilisateur.
Je ne vois pas comment np.empty () pourrait faire du dispatching - il n'y a rien à envoyer, juste une forme et un type
Vous voudrez peut-être envoyer sur l'un ou l'autre de ceux-ci. Par exemple, voici un objet de forme personnalisé sur lequel nous pourrions vouloir distribuer différemment.
Cet exemple n'est pas très utile, mais l'idée est que j'ai un objet paresseux qui se comporte comme une forme, mais ne renvoie pas d'entiers, il renvoie des expressions. Par exemple, ce serait bien de pouvoir faire quelque chose comme ceci:
class ExprShape:
def __getitem__(self, i):
return ('getitem', self, i)
def __len__(self):
return ('len', self)
numpy.empty(ExprShape())
Ce que je voudrais remplacer pour renvoyer quelque chose comme ExprArray('empty', ExprShape())
.
Oui, en principe, nous pourrions également expédier sur forme. Cela ajouterait une complexité / surcharge supplémentaire au protocole. Avez-vous des cas d'utilisation où l'utilisation d'un tableau comme modèle (comme empty_like
avec shape
) ne suffirait pas?
Les autres cas , je peux penser est le size
argument np.random.RandomState
méthodes, mais notez que nous ne soutenons pas actuellement ceux du tout - voir http://www.numpy.org/ neps / nep-0018-array-function-protocol.html # callable -objects-generated-at-runtime
Avez-vous des cas d'utilisation où l'utilisation d'un tableau comme modèle (comme empty_like avec forme) ne suffirait pas?
Si nous prenons une API existante qui dépend de NumPy et que nous souhaitons la faire fonctionner de manière transparente sur un backend différent, sans changer le code source existant.
Par exemple, disons que nous essayions d'appeler scipy.optimize.differential_evolution
avec des NP comme des tableaux, qui construisent un graphe d'appel au lieu de s'exécuter immédiatement.
Vous pouvez voir ici qu'il serait utile de changer np.full
pour créer un tableau symbolique au lieu d'un tableau numpy par défaut, si l'entrée qui y est passée était également symbolique.
Si nous prenons une API existante qui dépend de NumPy et que nous souhaitons la faire fonctionner de manière transparente sur un backend différent, sans changer le code source existant.
Ce n'est pas possible en général. La construction de tableaux explicites comme np.array()
va certainement devoir être réécrite pour être compatible avec le typage canard.
Dans ce cas, changer energies = np.full(num_members, np.inf)
en energies = np.full_like(population, np.inf, shape=num_members)
semble être un changement facile et lisible.
Ce n'est pas possible en général. La construction de tableaux explicites comme np.array () va certainement devoir être réécrite pour être compatible avec le typage canard.
Y a-t-il une proposition pour apporter ce type de changement ou est-ce que vous dites que soutenir la répartition de np.array
serait vraiment difficile et que nous ne pourrons donc jamais obtenir un soutien à 100%?
Dans ce cas, changer energies = np.full (num_members, np.inf) en energies = np.full_like (population, np.inf, shape = num_members) semble être un changement facile et lisible.
Absolument. Mais il existe de nombreux cas où vous ne contrôlez pas le code source ou vous souhaitez aider les utilisateurs à utiliser les fonctions qu'ils connaissent et aiment autant que possible.
Il existe d'autres moyens de fournir aux utilisateurs cette expérience, comme:
Ces deux options peuvent être nécessaires dans certains cas (comme permettre aux utilisateurs d'appeler np.full
et de renvoyer un résultat symbolique actuellement), mais si je comprends bien, le but de NEP-18 est d'essayer de limiter le moment où ceux-ci sont nécessaires et laissez les gens utiliser le NumPy original dans plus de cas.
Je comprends qu'il y a ici un compromis performance / complexité et cela pourrait être une bonne raison de ne pas les implémenter. Mais cela pourrait forcer les utilisateurs à explorer d'autres moyens pour obtenir la flexibilité qu'ils souhaitent.
Y a-t-il une proposition pour apporter ce type de changement ou est-ce que vous dites que soutenir la répartition de
np.array
serait vraiment difficile et que nous ne pourrons donc jamais obtenir un soutien à 100%?
NEP 22 a quelques discussions sur les options ici. Je ne pense pas que nous puissions changer en toute sécurité la sémantique de np.asarray()
pour renvoyer autre chose qu'un objet numpy.ndarray
- nous aurons besoin d'un nouveau protocole pour cela.
Le problème est que np.asarray()
est actuellement la manière idiomatique de transtyper en un objet tableau numpy, qui utilise peut et s'attend à correspondre exactement à numpy.ndarray
, par exemple jusqu'à la disposition de la mémoire.
Il y a certainement beaucoup de cas d'utilisation où ce n'est pas le cas, mais changer ce comportement briserait beaucoup de code en aval, donc ce n'est pas un démarreur. Les projets en aval devront accepter au moins cet aspect du typage de canard en réseau.
Je comprends qu'il y a ici un compromis performance / complexité et cela pourrait être une bonne raison de ne pas les implémenter. Mais cela pourrait forcer les utilisateurs à explorer d'autres moyens pour obtenir la flexibilité qu'ils souhaitent.
Oui. NEP 18 n'est pas destiné à être une solution complète pour les alternatives NumPy, mais c'est un pas dans cette direction.
J'ai rédigé une révision de NEP-18 pour ajouter un attribut __numpy_implementation__
:
https://github.com/numpy/numpy/pull/13305
Il me vient à l'esprit que l'on oublie de déformer les fonctions dans numpy.testing
: https://github.com/numpy/numpy/issues/13588
Je vais faire ça sous peu ...
Il y a une révision que j'aimerais voir dans le NEP, spécifiquement pour clarifier quelles garanties NEP-18 offre aux auteurs de sous-classes: https://github.com/numpy/numpy/pull/13633
J'ai marqué les tâches d'utilisabilité comme terminées depuis que gh-13329 a été corrigé. Nous avons décidé que # 13588 peut attendre la sortie de la 1.17. Cela laisse des améliorations de la documentation et arange
gh-12379 toujours ouverts pour inclusion dans 1.17.
Il y a aussi # 13728 - un bug dans le répartiteur pour histogram[2d]d
Cela laisse les améliorations de la documentation et la gamme gh-12379 toujours ouverte à l'inclusion dans la version 1.17.
Un problème de documentation manquait, j'ai donc ouvert gh-13844. Je pense que les documents sont beaucoup plus importants que le problème ouvert arange
.
@shoyer pouvons-nous fermer ça?
Commentaire le plus utile
NEP 22 a quelques discussions sur les options ici. Je ne pense pas que nous puissions changer en toute sécurité la sémantique de
np.asarray()
pour renvoyer autre chose qu'un objetnumpy.ndarray
- nous aurons besoin d'un nouveau protocole pour cela.Le problème est que
np.asarray()
est actuellement la manière idiomatique de transtyper en un objet tableau numpy, qui utilise peut et s'attend à correspondre exactement ànumpy.ndarray
, par exemple jusqu'à la disposition de la mémoire.Il y a certainement beaucoup de cas d'utilisation où ce n'est pas le cas, mais changer ce comportement briserait beaucoup de code en aval, donc ce n'est pas un démarreur. Les projets en aval devront accepter au moins cet aspect du typage de canard en réseau.
Oui. NEP 18 n'est pas destiné à être une solution complète pour les alternatives NumPy, mais c'est un pas dans cette direction.