Numpy: Masalah pelacakan untuk implementasi NEP-18 (__array_function__)

Dibuat pada 25 Sep 2018  ·  54Komentar  ·  Sumber: numpy/numpy

  • [x] Fungsi inti untuk mendukung penggantian:

    • [x] Implementasi awal dengan Python murni (# 12005)

    • [x] Validasi fungsi petugas operator di array_function_dispatch (https://github.com/numpy/numpy/pull/12099)



      • Nonaktifkan validasi saat tidak menguji NumPy (jika ada dampak terukur pada waktu impor) (tidak perlu)



    • [x] Tambahkan atribut fungsi .__skip_array_function__ untuk memungkinkan melewatkan pengiriman __array_function__ . (https://github.com/numpy/numpy/pull/13389)

  • [x] Implementasikan ulang bagian dari numpy/core/overrides.py dalam C untuk kecepatan (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] Dukungan menimpa semua fungsi NumPy publik

    • [x] numpy.core



      • [x] bagian yang mudah (https://github.com/numpy/numpy/pull/12115)


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


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



    • [x] numpy.lib



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


      • [x] bagian 2 (# 12119)



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

    • Fungsi [x] saat ini seluruhnya ditulis dalam C : empty_like, concatenate, 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/ tarik / 12175)

    • [x] spasi

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

  • [x] Peningkatan kegunaan

    • [x] [pesan error yang lebih baik] (https://github.com/numpy/numpy/issues/12213) untuk fungsi yang tidak diimplementasikan (https://github.com/numpy/numpy/pull/12251)

    • [x] ndarray.__repr__ tidak boleh bergantung pada __array_function__ (https://github.com/numpy/numpy/pull/12212)

    • [x] stacklevel harus ditingkatkan 1 untuk fungsi yang dibungkus, jadi traceback mengarah ke tempat yang benar (gh-13329)

  • [x] Perbaiki semua bug yang diketahui / kegagalan pengujian downstream
  • [] Dokumentasi

    • [x] Catatan rilis (# 12028)

    • [x] Dokumen naratif

    • [] Docstrings direvisi untuk mengklarifikasi argumen yang kelebihan beban?

__array_function__

Komentar yang paling membantu

Apakah ada usulan untuk membuat perubahan semacam ini atau apakah Anda mengatakan bahwa mendukung pengiriman np.array akan sangat sulit sehingga kami tidak bisa mendapatkan dukungan 100%?

NEP 22 membahas beberapa opsi di sini. Saya tidak berpikir kita dapat dengan aman mengubah semantik np.asarray() untuk mengembalikan apa pun selain objek numpy.ndarray - kita akan membutuhkan protokol baru untuk ini.

Masalahnya adalah bahwa np.asarray() saat ini merupakan cara idiomatis untuk mentransmisikan ke objek array numpy, yang menggunakan can dan memang mengharapkan untuk sama persis dengan numpy.ndarray , misalnya, ke tata letak memori.

Tentu ada banyak kasus penggunaan yang tidak terjadi, tetapi mengalihkan perilaku ini akan merusak banyak kode downstream, jadi ini bukan pemulai. Proyek hilir harus memilih setidaknya aspek pengetikan bebek array ini.

Saya memahami ada tradeoff kinerja / kompleksitas di sini dan itu mungkin alasan yang baik untuk tidak menerapkannya. Tapi mungkin memaksa pengguna untuk mengeksplorasi cara lain untuk mendapatkan fleksibilitas yang mereka inginkan.

Iya. NEP 18 tidak dimaksudkan sebagai solusi lengkap untuk alternatif NumPy drop-in, tetapi merupakan langkah ke arah itu.

Semua 54 komentar

Mungkin bagus untuk menggabungkan "Hiasi semua fungsi NumPy publik dengan @array_function_dispatch" awal untuk beberapa fungsi profil tinggi dan meminta konsumen hilir protokol untuk mencobanya

Setelah kami menggabungkan https://github.com/numpy/numpy/pull/12099 saya memiliki PR lain yang siap yang akan menambahkan dekorator pengiriman untuk sebagian besar numpy.core . Akan sangat mudah untuk menyelesaikan semuanya - yang ini membutuhkan waktu kurang dari satu jam untuk menyelesaikannya.

cc @ eric-werer @rocklin @hvk @hameerabbasi

Lihat https://github.com/shoyer/numpy/tree/array-function-easy-impl untuk cabang saya yang menerapkan semua penggantian "mudah" pada fungsi dengan pembungkus Python. Bagian yang tersisa adalah np.block , np.einsum dan beberapa fungsi multiarray yang seluruhnya ditulis dalam C (misal, np.concatenate ). Saya akan membagi ini menjadi beberapa PR setelah kita selesai dengan # 12099.

Perhatikan bahwa saya belum menulis tes untuk menimpa pada setiap fungsi individu. Saya ingin menambahkan beberapa tes integrasi ketika kita selesai (misalnya, array bebek yang mencatat semua operasi yang diterapkan), tetapi menurut saya tidak akan produktif untuk menulis tes pengiriman untuk setiap fungsi individu. Pemeriksaan di # 12099 harus menangkap kesalahan paling umum pada petugas operator, dan setiap baris kode dalam fungsi petugas operator harus dijalankan oleh pengujian yang ada.

@ shoyer - pada tes, saya setuju bahwa menulis tes untuk masing-masing tes tidak terlalu berguna; sebaliknya, dalam numpy, mungkin masuk akal untuk mulai menggunakan penggantian relatif cepat di MaskedArray .

@mhvk kedengarannya bagus untuk saya, meskipun saya akan membiarkan orang lain yang menggunakan / tahu MaskedArray memimpinnya.

Lihat https://github.com/numpy/numpy/pull/12115 , https://github.com/numpy/numpy/pull/12116 , # 12119 dan https://github.com/numpy/numpy/pull/ 12117 untuk PR yang mengimplementasikan __array_function__ dukungan pada fungsi yang ditentukan dengan Python.

@ shoyer - melihat beberapa implementasinya, saya punya dua kekhawatiran:

  • Untuk beberapa fungsi, seperti reshape , fungsionalitas asli telah menyediakan cara untuk menimpanya, dengan mendefinisikan metode reshape . Kami secara efektif menghentikan itu untuk setiap kelas yang mendefinisikan __array_function__ .
  • Untuk fungsi lain, seperti np.median , penggunaan np.asanyarray hati-hati dan ufuncs memastikan bahwa subclass sudah bisa menggunakannya. Tetapi fungsionalitas itu tidak dapat lagi diakses secara langsung.

Saya pikir secara keseluruhan kedua hal ini mungkin menguntungkan, karena kita menyederhanakan antarmuka dan dapat membuat implementasi dioptimalkan untuk ndarray murni - meskipun yang terakhir menyarankan bahwa ndarray.__array_function__ harus mengambil alih daftar konversi, dll., ke ndarray , sehingga implementasi dapat melewati bagian itu). Namun, saya pikir saya akan mencatatnya karena itu membuat saya takut menerapkan ini untuk Quantity sedikit lebih dari yang saya kira - dalam hal jumlah pekerjaan dan kemungkinan hit dalam kinerja.

meskipun yang terakhir menyarankan bahwa ndarray .__ array_function__ harus mengambil alih mengubah daftar, dll., ke ndarray, sehingga implementasi dapat melewati bagian itu).

Saya tidak yakin saya mengikuti di sini.

Kami memang secara efektif menghentikan cara lama untuk mengganti fungsi seperti reshape dan mean , meskipun cara lama masih mendukung implementasi API NumPy yang tidak lengkap.

Saya tidak yakin saya mengikuti di sini.

Saya pikir masalahnya adalah jika kita mengimplementasikan __array_function__ bahkan untuk satu fungsi, mekanisme sebelumnya rusak total dan tidak ada cara untuk melakukan fail-over. Itulah sebabnya saya mengusulkan agar kami mengunjungi kembali proposal NotImplementedButCoercible .

@hameerabbasi - ya, itulah masalahnya. Meskipun kita perlu berhati-hati di sini betapa mudahnya kita membuatnya bergantung pada larutan lakban yang sebenarnya ingin kita singkirkan ... (itulah sebabnya saya tulis di atas bahwa "masalah" saya sebenarnya bisa bermanfaat ...) . Mungkin ada kasus untuk mencoba seperti di 1.16 dan kemudian memutuskan pengalaman aktual apakah kami ingin memberikan fall-back "abaikan __array_function__ untuk kasus ini".

Re: dispatcher styling: Preferensi saya pada gaya didasarkan pada pertimbangan memori / waktu impor, dan verbositas. Sederhananya, gabungkan petugas operator di mana tanda tangannya kemungkinan besar tetap sama. Dengan cara ini, kami membuat objek paling sedikit dan cache yang ditemukan akan lebih tinggi juga.

Meski begitu, saya tidak terlalu menentang gaya lambda.

Gaya penulisan fungsi dispatcher kini muncul di beberapa PR. Sebaiknya buat pilihan yang konsisten di seluruh NumPy.

Kami memiliki beberapa opsi:


Opsi 1 : Tulis dispatcher terpisah untuk setiap fungsi, misalnya,

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

Keuntungan:

  • Sangat mudah dibaca
  • Definisi fungsi dispatcher yang mudah ditemukan
  • Hapus pesan kesalahan saat Anda memberikan argumen yang salah, misalnya, sin(x=1) -> TypeError: _sin_dispatcher() got an unexpected keyword argument 'x' .

Kekurangan:

  • Banyak pengulangan, meskipun banyak fungsi dalam modul memiliki tanda tangan yang sama persis.

Opsi 2 : Gunakan kembali fungsi operator dalam modul, misalnya,

def _unary_dispatcher(a):
    return (a,)


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


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

Keuntungan:

  • Lebih sedikit pengulangan
  • Dapat dibaca

Kekurangan:

  • Bisa sedikit lebih sulit untuk menemukan definisi dari fungsi dispatcher
  • Pesan kesalahan yang agak kurang jelas untuk argumen yang buruk, misalnya, sin(x=1) -> TypeError: _unary_dispatcher() got an unexpected keyword argument 'x'

Opsi 3 : Gunakan fungsi lambda ketika definisi dispatcher cocok di satu baris, misalnya,

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

Keuntungan:

  • Tidak perlu mencari definisi dispatcher, mereka ada di sana.
  • Jumlah karakter dan baris kode lebih sedikit.
  • Terlihat sangat bagus untuk kasus pendek (mis., Satu argumen), terutama saat lambda lebih pendek dari nama fungsi.

Kekurangan:

  • Kode yang lebih berulang daripada opsi 2.
  • Terlihat sangat berantakan jika ada lebih dari beberapa argumen
  • Juga memiliki pesan kesalahan yang kurang jelas ( TypeError: <lambda>() got an unexpected keyword argument 'x' )

@shoyer : diedit untuk menambahkan spasi dua baris PEP8 untuk membuat aspek "baris kode" lebih realistis

Perhatikan bahwa masalah pesan kesalahan dapat diperbaiki dengan merekonstruksi objek kode , meskipun itu akan menimbulkan beberapa biaya waktu impor. Mungkin perlu diselidiki, dan temukan tuna

Ya, modul dekorator juga dapat digunakan untuk menghasilkan definisi fungsi (ini menggunakan pendekatan yang sedikit berbeda untuk pembuatan kode, sedikit lebih mirip dengan nametuple yang menggunakan exec() ).

Selama kesalahan tidak terpecahkan, saya pikir kita harus tetap menggunakan opsi dengan dispatcher yang memiliki nama yang jelas. Saya akan sedikit membundel dispatcher bersama-sama (2) untuk alasan memori, meskipun kemudian akan mengingat pesan kesalahan sangat banyak, jadi saya akan menyarankan memanggil dispatcher sesuatu seperti _dispatch_on_x .

Meskipun jika kita bisa mengubah kesalahan, banyak hal berubah. Misalnya, ini mungkin sesederhana menangkap pengecualian, mengganti <lambda> dengan nama fungsi di teks pengecualian, dan kemudian menaikkan kembali. (Atau apakah itu rantai hari ini?)

Saya setuju bahwa pesan kesalahan harus jelas, idealnya tidak boleh berubah sama sekali.

OK, untuk saat ini saya pikir yang terbaik adalah menunda menggunakan lambda , kecuali kita mendapatkan semacam pembuatan kode yang berfungsi.

https://github.com/numpy/numpy/pull/12175 menambahkan draf tentang apa yang menimpa fungsi multiarray (ditulis dalam C) jika kita mengambil pendekatan pembungkus Python.

@ Mattip di mana kita sedang mengimplementasikan matmul sebagai ufunc? Setelah kita menyelesaikan semua penggantian __array_function__ , saya pikir itu adalah hal terakhir yang kita butuhkan untuk membuat API publik NumPy sepenuhnya dapat kelebihan beban. Akan menyenangkan jika semuanya siap untuk NumPy 1.16!

PR # 11175, yang menerapkan NEP 20, mengalami kemajuan yang lambat. Ini adalah pemblokir untuk PR # 11133, yang memiliki kode loop matmul. Yang itu masih perlu diperbarui dan kemudian diverifikasi melalui tolok ukur bahwa kode baru tidak lebih lambat dari yang lama.

Saya memiliki empat PR untuk ditinjau yang seharusnya menyelesaikan set penggantian lengkap. Ulasan akhir / persetujuan / penggabungan akan dihargai sehingga kita dapat mulai menguji __array_function__ dengan sungguh-sungguh! 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

Menambahkan penggantian ke np.core menyebabkan beberapa pengujian panda gagal (https://github.com/pandas-dev/pandas/issues/23172). Kami belum yakin apa yang sedang terjadi tetapi kami harus mencari tahu sebelum merilisnya.

Lihat https://github.com/numpy/numpy/issues/12225 untuk tebakan terbaik saya mengapa hal ini menyebabkan kegagalan pengujian di dask / pandas.

Beberapa tolok ukur waktu impor (di macbook pro saya dengan solid state drive):

  • Jumlah 1.15.2: 152.451 md
  • Master NumPy: 156,5745 md
  • Menggunakan decorator.decorate (# 12226): 183,694 md

Skrip patokan saya

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)

Ada gagasan tentang penggunaan memori (sebelum / sesudah)? Itu juga berguna, terutama untuk aplikasi IoT.

Apakah Anda tahu cara mengukur penggunaan memori untuk sebuah modul dengan andal?
Pada Sabtu, 20 Okt 2018 pukul 6:56 Hameer Abbasi [email protected]
menulis:

Ada gagasan tentang penggunaan memori (sebelum / sesudah)? Itu berguna sebagai
baik, terutama untuk aplikasi IoT.

-
Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/numpy/numpy/issues/12028#issuecomment-431584123 , atau nonaktifkan
utasnya
https://github.com/notifications/unsubscribe-auth/ABKS1k_IkrJ2YmYReaDrnkNvcH2X0-ZCks5umyuogaJpZM4W3kSC
.

Saya pikir menulis skrip berisi import numpy as np , menambahkan pernyataan tidur dan memori proses pelacakan harus cukup baik. https://superuser.com/questions/581108/how-can-i-track-and-log-cpu-and-memory-usage-on-a-mac

Pengembang inti lainnya ingin melihat sekilas (sungguh, ini hanya mencakup dua fungsi!) Di https://github.com/numpy/numpy/pull/12163? Ini PR terakhir yang menambahkan array_function_dispatch ke fungsi numpy internal.

Untuk referensi, inilah perbedaan kinerja yang saya lihat saat menonaktifkan __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.

lihat juga https://docs.google.com/spreadsheets/d/15-AFI_cmZqfkU6mo2p1znsQF2E52PEXpF68QqYqEar4/edit#gid = 0 untuk spreadsheet.

Tidak mengherankan, perbedaan performa terbesar adalah untuk fungsi yang memanggil fungsi numpy lainnya secara internal berkali-kali, misalnya untuk np.block() .

@shoyer - Saya sedikit bingung dengan waktu ekstra yang diambil ... Mungkin, kita benar-benar harus menerapkan C, tetapi sementara itu saya membuat PR dengan beberapa perubahan kecil yang memangkas beberapa waktu untuk kasus umum hanya satu jenis, dan untuk kasus di mana satu-satunya jenis adalah ndarray . Lihat # 12321.

@shoyer - Saya mengangkat dua masalah di milis yang mungkin bagus untuk

  1. Haruskah semua tipe unik dari argumen mirip larik disertakan dalam types ? (bukan hanya argumen yang memberikan timpaan.) Tampaknya bermanfaat untuk mengetahui implementasi. (lihat # 12327).
  2. Haruskah implementasi ndarray.__array_function__ menerima subclass meskipun mereka menimpa __array_function__ ? Hal ini masuk akal mengingat prinsip substitusi Liskov dan subclass tersebut sudah memiliki kesempatan untuk mendapatkan jaminan. Ini akan menyiratkan memanggil implementasi daripada fungsi publik di dalam ndarray.__array_function__ . (Dan sesuatu yang serupa di __array_ufunc__ ...) Lihat # 12328 untuk percobaan seharga __array_function__ saja.

@ shoyer - lihat # 12327 untuk implementasi cepat (1) - jika kita melakukan rute ini, saya pikir kita juga harus menyesuaikan NEP.

Dan # 12328 untuk uji coba (2), sebagian besar untuk melihat tampilannya.

Saya memberi +1 pada kedua modifikasi di sini.

Nama fungsi dispatcher dalam pesan kesalahan muncul lagi di https://github.com/numpy/numpy/pull/12789 , di mana seseorang terkejut melihat TypeError: _pad_dispatcher missing 1 required positional argument

Selain alternatif yang diuraikan di atas https://github.com/numpy/numpy/issues/12028#issuecomment -429377396 (saat ini kami menggunakan 2), saya akan menambahkan opsi keempat:

Opsi 4 : Tulis dispatcher terpisah untuk setiap fungsi, dengan nama yang sama dengan fungsinya:

def sin(a):
    return (a,)


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


def cos(a):
    return (a,)


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

Keuntungan:

  • Python sekarang selalu memberikan nama fungsi yang benar dalam pesan kesalahan.

Kekurangan:

  • Lebih banyak pengulangan kode
  • Lebih banyak tipuan - tidak lagi jelas nama fungsi mana pad menerima argumen yang salah (tetapi kami memiliki tes untuk memverifikasi bahwa mereka tetap sinkron).

Saya pikir untuk menjaga kode saat ini bekerja, fungsi sebenarnya harus datang _setelah_ operator.

Benar, tapi kita bisa memberinya nama yang sama dengan petugas operator. Nama petugas operator akan ditimpa.

Akan sangat bagus untuk dapat mendefinisikan pengiriman kustom untuk fungsi seperti np.arange atau np.empty.

Saya kira satu pilihan adalah untuk NumPy untuk mengirimkan skalar serta array. Apakah ini tidak sesuai dengan NEP? Apakah ada yang akan merusak perubahan ini?

Untuk diskusi tentang np.arange , lihat https://github.com/numpy/numpy/issues/12379.

Saya tidak melihat bagaimana np.empty() dapat melakukan pengiriman - tidak ada yang perlu dikirim, hanya bentuk dan tipe. Tapi tentu saja np.empty_like() dapat melakukan pengiriman dengan bentuk yang ditimpa - itulah yang https://github.com/numpy/numpy/pull/13046 tentang dukungan.

Opsi 4 : Tulis dispatcher terpisah untuk setiap fungsi, dengan nama yang sama dengan fungsinya:

Adakah keberatan untuk mengadopsi opsi ini? Saya pikir itu mungkin pilihan paling ramah dari sudut pandang pengguna.

Saya tidak melihat bagaimana np.empty () dapat melakukan pengiriman - tidak ada yang perlu dikirim, hanya bentuk dan tipe

Anda mungkin ingin mengirimkan salah satu dari itu. Misalnya, berikut adalah objek bentuk kustom, yang mungkin ingin kami kirim secara berbeda.

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

Contoh ini tidak terlalu berguna, tetapi idenya adalah saya memiliki objek malas yang berperilaku seperti bentuk, tetapi tidak mengembalikan bilangan bulat, ia mengembalikan ekspresi. Misalnya, alangkah baiknya bisa melakukan sesuatu seperti ini:

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

numpy.empty(ExprShape())

Yang mana yang ingin saya ganti untuk mengembalikan sesuatu seperti ExprArray('empty', ExprShape()) .

Ya, pada prinsipnya kami juga dapat mengirimkan sesuai bentuk. Itu akan menambah kompleksitas / overhead tambahan ke protokol. Apakah Anda memiliki kasus penggunaan di mana menggunakan array sebagai template (seperti empty_like dengan shape ) tidak cukup?

Kasus lain yang dapat saya pikirkan adalah argumen size menjadi np.random.RandomState metode, tetapi perhatikan bahwa saat ini kami tidak mendukungnya sama sekali - lihat http://www.numpy.org/ neps / nep-0018-array-function-protocol.html # callable -objects-generated-at-runtime

Apakah Anda memiliki kasus penggunaan di mana menggunakan array sebagai template (seperti empty_like with shape) tidak cukup?

Jika kita menggunakan API yang ada yang bergantung pada NumPy dan ingin membuatnya bekerja secara transparan pada backend yang berbeda, tanpa mengubah kode sumber yang ada.

Sebagai contoh, katakanlah kita mencoba memanggil scipy.optimize.differential_evolution dengan NP seperti array, yang membangun grafik panggilan alih-alih langsung dieksekusi.

Anda dapat melihat di sini akan sangat membantu jika kita dapat mengubah np.full untuk membuat array simbolik daripada array numpy default, jika input yang dilewatkan juga simbolik.

Jika kita menggunakan API yang ada yang bergantung pada NumPy dan ingin membuatnya bekerja secara transparan pada backend yang berbeda, tanpa mengubah kode sumber yang ada.

Ini tidak mungkin secara umum. Konstruksi array eksplisit seperti np.array() pasti perlu ditulis ulang agar kompatibel dengan pengetikan bebek.

Dalam kasus ini, mengalihkan energies = np.full(num_members, np.inf) ke energies = np.full_like(population, np.inf, shape=num_members) sepertinya merupakan perubahan yang mudah dan dapat dibaca.

Ini tidak mungkin secara umum. Konstruksi array eksplisit seperti np.array () pasti perlu ditulis ulang agar kompatibel dengan pengetikan bebek.

Apakah ada usulan untuk membuat perubahan semacam ini atau apakah Anda mengatakan bahwa mendukung pengiriman np.array akan sangat sulit sehingga kami tidak bisa mendapatkan dukungan 100%?

Dalam kasus ini, mengalihkan energi = np.full (num_members, np.inf) ke energi = np.full_like (populasi, np.inf, shape = num_members) tampak seperti perubahan yang mudah dan dapat dibaca.

Pastinya. Namun ada banyak kasus di mana Anda tidak mengontrol kode sumber, atau Anda ingin mendukung pengguna dalam menggunakan fungsi yang mereka kenal dan sukai sebanyak mungkin.

Ada cara lain untuk memberikan pengalaman tersebut kepada pengguna seperti:

  • Menyediakan modul baru yang berfungsi seperti numpy tetapi berperilaku seperti yang Anda inginkan. Mengharuskan pengguna untuk mengubah impor mereka
  • Periksa sumber untuk memahami perilakunya. ala numba atau tangen.

Kedua opsi tersebut mungkin diperlukan dalam kasus tertentu (seperti membiarkan pengguna memanggil np.full dan mengembalikan hasil simbolis saat ini), tetapi jika saya mengerti dengan benar, tujuan NEP-18 adalah mencoba membatasi kapan itu diperlukan dan biarkan orang menggunakan NumPy asli dalam lebih banyak kasus.

Saya memahami ada tradeoff kinerja / kompleksitas di sini dan itu mungkin alasan yang baik untuk tidak menerapkannya. Tapi mungkin memaksa pengguna untuk mengeksplorasi cara lain untuk mendapatkan fleksibilitas yang mereka inginkan.

Apakah ada usulan untuk membuat perubahan semacam ini atau apakah Anda mengatakan bahwa mendukung pengiriman np.array akan sangat sulit sehingga kami tidak bisa mendapatkan dukungan 100%?

NEP 22 membahas beberapa opsi di sini. Saya tidak berpikir kita dapat dengan aman mengubah semantik np.asarray() untuk mengembalikan apa pun selain objek numpy.ndarray - kita akan membutuhkan protokol baru untuk ini.

Masalahnya adalah bahwa np.asarray() saat ini merupakan cara idiomatis untuk mentransmisikan ke objek array numpy, yang menggunakan can dan memang mengharapkan untuk sama persis dengan numpy.ndarray , misalnya, ke tata letak memori.

Tentu ada banyak kasus penggunaan yang tidak terjadi, tetapi mengalihkan perilaku ini akan merusak banyak kode downstream, jadi ini bukan pemulai. Proyek hilir harus memilih setidaknya aspek pengetikan bebek array ini.

Saya memahami ada tradeoff kinerja / kompleksitas di sini dan itu mungkin alasan yang baik untuk tidak menerapkannya. Tapi mungkin memaksa pengguna untuk mengeksplorasi cara lain untuk mendapatkan fleksibilitas yang mereka inginkan.

Iya. NEP 18 tidak dimaksudkan sebagai solusi lengkap untuk alternatif NumPy drop-in, tetapi merupakan langkah ke arah itu.

Saya telah menyusun revisi NEP-18 untuk menambahkan atribut __numpy_implementation__ :
https://github.com/numpy/numpy/pull/13305

Terpikir oleh saya bahwa kita lupa membengkokkan fungsi di numpy.testing : https://github.com/numpy/numpy/issues/13588

Saya akan segera melakukannya ...

Ada satu revisi yang ingin saya lihat pada NEP, khususnya untuk mengklarifikasi jaminan apa yang ditawarkan NEP-18 kepada penulis subkelas: https://github.com/numpy/numpy/pull/13633

Saya menandai tugas kegunaan selesai sejak gh-13329 diperbaiki. Kami memutuskangh- # 13588 bisa menunggu sampai setelah rilis 1,17. Itu membuat perbaikan dokumentasi dan arange gh-12379 masih terbuka untuk dimasukkan dalam 1.17.

Ada juga # 13728 - bug di operator untuk histogram[2d]d

Itu membuat perbaikan dokumentasi dan arange gh-12379 masih terbuka untuk dimasukkan dalam 1,17.

Masalah dokumentasi hilang, jadi saya membuka gh-13844. Menurut saya dokumen jauh lebih penting daripada terbitan terbuka arange .

@ shoyer bisakah kita menutup ini?

Apakah halaman ini membantu?
0 / 5 - 0 peringkat