Julia: API Perkalian Matriks

Dibuat pada 28 Sep 2017  ·  208Komentar  ·  Sumber: JuliaLang/julia

Saat ini, terdapat baris berikut dalam kode matmult jarang:

https://github.com/JuliaLang/julia/blob/056b374919e11977d5a8d57b446ad1c72f3e6b1d/base/sparse/linalg.jl#L94 -L95

Saya berasumsi bahwa ini berarti kita ingin memiliki metode A_mul_B*!(α,A,B,β,C) = αAB + βC yang lebih umum yang menimpa C (API BLAS gemm ) untuk array padat. Apakah masih demikian? (Tampaknya juga mungkin untuk menyimpan kedua API, yaitu tetap menggunakan metode A_mul_B*!(C,A,B) , yang hanya akan didefinisikan sebagai A_mul_B*!(C,A,B) = A_mul_B*!(1,A,B,0,C) .)

Saya pribadi ingin memiliki API gemm ditentukan untuk semua tipe array (ini telah diekspresikan di tempat lain ). Menerapkan metode ini untuk array padat tampaknya cukup sederhana, karena mereka hanya akan langsung memanggil gemm et al. Kasus renggang sudah diterapkan. Modifikasi nyata hanya akan ke matmult julia α dan β .

Ini akan menghasilkan kode umum yang bekerja dengan semua jenis larik / angka. Saat ini saya memiliki implementasi sederhana dari expm (setelah melakukan modifikasi ke _generic_matmatmult! ) yang bekerja dengan bigfloats dan sparse arrays.

linear algebra

Komentar yang paling membantu

Saya menyarankan satu putaran voting persetujuan dengan tenggat waktu 10 hari dari sekarang. Pemungutan suara persetujuan berarti: Setiap orang memberikan suara untuk semua opsi yang mereka anggap lebih disukai daripada melanjutkan diskusi. Orang-orang yang lebih memilih nama yang paling tidak disukai sekarang daripada diskusi lanjutan harus memilih ketiganya. Jika tidak ada opsi yang mendapat persetujuan luas, atau skema pemungutan suara itu sendiri gagal mendapatkan persetujuan luas, maka kita harus melanjutkan diskusi. Jika hampir ada hubungan antara opsi yang disetujui, @tkf akan memutuskan (hak istimewa penulis PR).

+1: Saya setuju dengan skema pemungutan suara ini dan telah memberikan suara persetujuan saya.
-1: Saya tidak setuju dengan skema pemungutan suara ini. Jika terlalu banyak atau terlalu orang penting memilih opsi ini, maka pemungutan suara bisa diperdebatkan.

Hati: mul! lebih disukai daripada diskusi lanjutan.
Roket: muladd! lebih disukai daripada diskusi lanjutan.
Hore: addmul! lebih disukai untuk melanjutkan diskusi.

Saya secara tentatif menyarankan bahwa 75% persetujuan dan 5 orang harus memenuhi kuorum (yaitu 75% orang yang telah memberikan suara sama sekali, termasuk ketidaksepakatan dengan seluruh prosedur pemungutan suara, dan setidaknya 5 orang telah menyetujui opsi menang; jika partisipasi rendah , maka 5/6 atau 6/8 buat kuorum tetapi dengan suara bulat 4/4 dapat dianggap gagal).

Semua 208 komentar

Ref. # 9930, # 20053, # 23552. Terbaik!

Terima kasih atas referensinya. Saya kira masalah ini lebih berkaitan dengan menambahkan metode gaya gemm daripada perombakan API, tetapi masalah ini dapat ditutup jika menurut kami masih terlalu mirip dengan # 9930.

Sebagai titik awal, apakah akan ada dukungan jika _generic_matmatmul! memiliki API gemm ? Ini adalah perubahan yang cukup sederhana, dan murni aditif / nonbreaking, karena metode saat ini hanya akan diimplementasikan dengan memiliki α=1 dan β=0 . Saya bisa membuat PR. Saya mungkin akan mirip dengan versi ini (dalam versi itu saya memotong semua hal transpose karena saya tidak membutuhkannya, saya tidak akan melakukannya di sini).

Iya. Itu akan menjadi awal yang baik. Kita perlu mempertimbangkan urutan argumennya. Awalnya, saya pikir lebih alami untuk mengikuti pemesanan BLAS tetapi kami cukup konsisten tentang memiliki argumen keluaran terlebih dahulu yang juga merupakan kasus untuk tiga argumen saat ini A_mul_B! . Selanjutnya dan seperti yang telah Anda tunjukkan, versi tiga argumen akan sesuai dengan versi lima argumen dengan α=1 dan β=0 dan argumen nilai default adalah yang terakhir. Tentu saja, kami tidak harus menggunakan sintaks nilai default untuk ini tetapi akan masuk akal untuk menggunakannya di sini.

Mengapa tidak memperkenalkan saja fungsi gemm umum?

Iya. Itu akan menjadi awal yang baik. Kita perlu mempertimbangkan urutan argumennya. Awalnya, saya pikir lebih wajar untuk mengikuti pengurutan BLAS tetapi kami cukup konsisten tentang memiliki argumen keluaran terlebih dahulu yang juga merupakan kasus untuk tiga argumen saat ini A_mul_B !. Selanjutnya dan seperti yang telah Anda tunjukkan, versi tiga argumen akan sesuai dengan versi lima argumen dengan α = 1 dan β = 0 dan argumen nilai default adalah yang terakhir. Tentu saja, kami tidak harus menggunakan sintaks nilai default untuk ini tetapi akan masuk akal untuk menggunakannya di sini.

Kedengarannya bagus. Kita bisa melanjutkan diskusi tentang pengurutan argumen aktual dan penggantian nama metode di # 9930. Ini lebih tentang hanya memiliki versi lima-argumen yang tersedia, jadi aku akan menjaga arus Ax_mul_Bx!(α,A,B,β,C) antarmuka.

Mengapa tidak hanya memperkenalkan fungsi gemm generik?

Apakah Anda menyarankan untuk mengganti nama _generic_matmatmul! menjadi gemm! selain perubahan di atas?

Agar lebih jelas, saya pikir kita harus memiliki satu metode mul(C,A,B,α=1,β=0) , bersama dengan jenis transpose / adjoint yang malas untuk dikirim, tetapi itu akan menjadi PR lainnya.

Mengapa tidak hanya memperkenalkan fungsi gemm generik?

Saya pikir gemm adalah istilah yang salah di Julia. Dalam BLAS, bagian ge menunjukkan bahwa matriksnya umum , yaitu tidak memiliki struktur khusus, m adalah kalikan dan daftar m adalah matriks . Di Julia, bagian ge (umum) dikodekan dalam tanda tangan seperti yang terakhir m (matriks) jadi saya pikir kita harus menyebutnya mul! .

Adalah notasi mul!(α, A, B, β, C) dari SparseArrays.jl

https://github.com/JuliaLang/julia/blob/160a46704fd1b349b5425f104a4ac8b323ea85af/stdlib/SparseArrays/src/linalg.jl#L32

"diselesaikan" sebagai sintaks resmi? Dan ini akan menjadi tambahan untuk mul!(C, A, B) , lmul!(A, B) , dan rmul!(A,B) ?

Saya tidak terlalu suka memiliki A , B , dan C dalam pesanan yang berbeda.

Apakah notasi mul!(α, A, B, β, C) dari SparseArrays.jl "diselesaikan" sebagai sintaks resmi?

Saya akan mengatakan tidak. Awalnya, saya menyukai ide mengikuti BLAS (dan urutannya juga cocok dengan bagaimana ini biasanya ditulis secara matematis) tetapi sekarang saya pikir masuk akal untuk menambahkan argumen penskalaan sebagai argumen opsional keempat dan kelima.

Jadi untuk memperjelas, Anda ingin argumen opsional dalam arti

function mul!(C, A, B, α=1, β=0)
 ...
end

Opsi lainnya adalah argumen kata kunci opsional

function mul!(C, A, B; α=1, β=0)
...
end

tapi saya tidak yakin orang terlalu senang dengan unicode.

Saya sangat senang dengan unicode tetapi memang benar bahwa kami mencoba selalu memiliki opsi ascii dan itu mungkin terjadi di sini. Nama α dan β juga tidak super intuitif kecuali Anda tahu BLAS jadi saya pikir menggunakan argumen posisi solusi yang lebih baik di sini.

Menurut pendapat saya, nomenklatur yang lebih logis adalah membiarkan muladd!(A, B, C, α=1, β=1) memetakan ke berbagai rutinitas BLAS yang melakukan perkalian dan penjumlahan . ( gemm seperti di atas, tetapi juga misalnya axpy bila A adalah skalar.)

Fungsi mul! kemudian dapat memiliki antarmuka seperti mul!(Y, A, B, ...) mengambil sejumlah argumen (seperti yang dilakukan * ) selama semua hasil antara dapat disimpan di Y. ( Sebuah kwarg opsional dapat menentukan urutan perkalian, dengan default yang masuk akal)

mul!(Y, A::AbstractVecOrMat, B:AbstractVecOrMat, α::Number) akan memiliki implementasi default muladd!(A, B, Y, α=α, β=0) , seperti halnya permutasi lain dari dua matriks / vektor dan skalar.

Pemungutan suara lain agar mul!(C, A, B, α, β) ditentukan untuk matriks padat. Ini akan memungkinkan penulisan kode generik untuk matriks padat dan jarang. Saya ingin mendefinisikan fungsi seperti itu dalam paket kuadrat terkecil non linier tetapi saya rasa ini adalah jenis pembajakan.

Saya juga tergoda untuk menulis mul!(C, A, B, α, β) metode untuk paket MixedModels dan terlibat dalam sedikit pembajakan tipe, tetapi akan jauh lebih baik jika metode seperti itu ada di LinearAlgebra paket. Memiliki metode untuk muladd! generik untuk operasi ini akan baik-baik saja bagi saya juga.

Saya setuju, meskipun saya pikir itu mungkin harus memiliki nama yang berbeda dari hanya mul! . muladd! tampaknya masuk akal, tetapi saya pasti terbuka untuk saran.

Mungkin mulinc! untuk kelipatan-kenaikan?

Mungkin kita dapat memiliki sesuatu seperti addmul!(C, A, B, α=1, β=1) ?

Bukankah ini bentuk muladd! ? Atau ide di balik menyebutnya addmul! yang mengubah argumen add daripada argumen multiply? Akankah seseorang mengubah argumen perkalian?

Perhatikan bahwa kami melakukan mutasi elemen non-pertama dalam beberapa kasus, misalnya lmul! dan ldiv! , jadi kami dapat melakukannya dalam urutan "muladd" yang biasa (yaitu muladd!(A,B,C) ). Pertanyaannya adalah, urutan apa yang seharusnya α dan β pergi? Salah satu opsinya adalah membuat argumen kata kunci?

Bukankah lebih baik jika Anda memberikan opsi kepada pelaksana untuk mengirimkan jenis skalar α dan β? Sangat mudah untuk menambahkan gula bagi pengguna akhir.

Saya pikir kami sudah menetapkan mul!(C, A, B, α, β) dengan nilai default untuk α , β . Kami menggunakan versi ini di https://github.com/JuliaLang/julia/blob/b8ca1a499ff4044b9cb1ba3881d8c6fbb1f3c03b/stdlib/SparseArrays/src/linalg.jl#L32 -L50. Saya pikir beberapa paket juga menggunakan formulir ini tetapi saya tidak ingat yang mana di atas kepala saya.

Terima kasih! Alangkah baiknya jika itu didokumentasikan.

Saya pikir kami sudah menetapkan mul!(C, A, B, α, β) dengan nilai default untuk α , β .

SparseArays menggunakannya, tapi saya tidak ingat itu dibahas di mana pun.

Dalam beberapa hal, nama muladd! lebih natural karena merupakan perkalian yang diikuti dengan penambahan. Namun, nilai default α dan β, muladd!(C, A, B, α=1, β=0) (perhatikan bahwa default untuk β adalah nol, bukan satu), ubah kembali menjadi mul!(C, A, B) .

Tampaknya menjadi kasus kesederhanaan vs konsistensi apakah akan memanggil ini mul! atau muladd! dan saya pikir kasus metode yang ada di SparseArrays akan berdebat untuk mul! . Saya menemukan diri saya dalam kasus yang aneh untuk memperdebatkan konsistensi meskipun kutipan favorit Oscar Wilde saya, "Konsistensi adalah perlindungan terakhir dari yang tidak imajinatif."

Dalam beberapa hal, nama muladd! lebih natural karena merupakan perkalian yang diikuti dengan penambahan. Namun, nilai default α dan β, muladd!(C, A, B, α=1, β=0) (perhatikan bahwa default untuk β adalah nol, bukan satu), ubah kembali menjadi mul!(C, A, B) .

Ada pengecualian menarik untuk ini ketika C berisi Infs atau NaNs: secara teoritis, jika β==0 , hasilnya tetap NaN. Ini tidak terjadi dalam praktik karena BLAS dan kode matriks jarang kami secara eksplisit memeriksa β==0 lalu menggantinya dengan nol.

Anda dapat mempertimbangkan bahwa memiliki default α=true, β=false karena true dan false -masing "kuat" 1 dan 0, dalam arti bahwa true*x selalu x dan false*x selalu zero(x) .

lmul! juga harus memiliki perilaku yang luar biasa: https://github.com/JuliaLang/julia/issues/28972

true dan false -masing "kuat" 1 dan 0, dalam arti bahwa true*x selalu x dan false*x selalu zero(x) .

Saya tidak tahu itu !:

julia> false*NaN
0.0

FWIW, saya cukup senang dengan keterbacaan sintaks LazyArrays.jl untuk operasi ini:

y .= α .* Mul(A,x) .+ β .* y

Di belakang layar, nilainya turun menjadi mul!(y, A, x, α, β) , untuk array yang kompatibel dengan BLAS (berpita dan melangkah).

Saya tidak tahu itu!

Itu bagian dari apa yang membuat im = Complex(false, true) berfungsi.

SparseArays menggunakannya, tapi saya tidak ingat itu dibahas di mana pun.

Itu telah dibahas di atas di https://github.com/JuliaLang/julia/issues/23919#issuecomment -365463941 dan diimplementasikan di https://github.com/JuliaLang/julia/pull/26117 tanpa ada keberatan. Kami tidak memiliki versi α,β dalam kasus padat jadi satu-satunya tempat di repo ini di mana keputusan akan berdampak langsung adalah SparseArrays .

Bagaimana dengan LinearAlgebra.BLAS.gemm! ? Bukankah seharusnya itu dibungkus sebagai 5-ary mul! juga?

Seharusnya, tetapi belum ada yang melakukannya. Ada banyak metode di matmul.jl .

Itu telah dibahas di atas di # 23919 (komentar) dan diterapkan di # 26117 tanpa keberatan apa pun.

Nah, pertimbangkan ini keberatan saya. Saya lebih suka nama yang berbeda.

Mengapa namanya berbeda? Baik dalam kasus padat maupun jarang, algoritme dasar melakukan perkalian dan penjumlahan.

Jika kita memberi fungsi tersebut nama yang berbeda, kita akan memiliki mul!(C,A,B) = dgemm(C,A,B,1,0) dan muladd!(C,A,B,α, β) = dgemm(C,A,B,α, β) .

Satu-satunya keuntungan yang saya lihat adalah jika kita benar-benar membagi metode dan menyimpan panggilan if β==0 dalam kasus C = A*B .

FYI, saya mulai mengerjakannya di # 29634 untuk menambahkan antarmuka ke matmul.jl . Saya berharap dapat menyelesaikannya pada saat nama dan tanda tangan ditentukan :)

Keuntungan dari muladd! adalah kita dapat memiliki terner muladd!(A, B, C) (atau muladd!(C, A, B) ?) Dengan default α = β = true (seperti yang disebutkan dalam saran asli https: //github.com/JuliaLang/julia/issues/23919#issuecomment-402953987). Metode muladd!(A, B, C) mirip dengan muladd untuk Number s jadi saya rasa itu nama yang lebih alami terutama jika Anda sudah tahu muladd .

@andreasnoack Tampaknya diskusi Anda sebelumnya adalah tentang tanda tangan metode dan lebih memilih argumen posisi daripada argumen kata kunci, bukan tentang nama metode. Apakah Anda keberatan dengan nama muladd! ? (Keberadaan 5-ary mul! dalam SparseArrays mungkin salah satunya, tetapi mendefinisikan pembungkus yang kompatibel dengan versi sebelumnya tidaklah sulit.)

Memiliki mul! dan muladd! tampaknya mubazir ketika yang pertama hanya yang terakhir dengan nilai default untuk α dan β . Selanjutnya, bagian add telah dikanonikalisasi oleh BLAS. Jika kita dapat membuat aplikasi aljabar linier generik yang kredibel dengan harga muladd! , saya ingin mendengarnya tetapi jika tidak, saya lebih suka menghindari redundansi.

Selain itu, saya sangat ingin agar kita tetap memisahkan properti strong-zero dari false dari pembahasan mul! . IMO nilai nol β apa pun harus kuat seperti di BLAS dan seperti dalam metode lima argumen mul! . Yaitu perilaku ini harus merupakan konsekuensi dari mul! dan bukan tipe β . Alternatifnya akan sulit untuk dikerjakan. Misalnya mul!(Matrix{Float64}, Matrix{Float64}, Matrix{Float64}, 1.0, 0.0) ~ bisa ~ tidak bisa menggunakan BLAS.

Kita tidak bisa mengubah apa yang BLAS lakukan, tapi perilaku nol yang kuat _requiring_ untuk float berarti bahwa setiap implementasi akan membutuhkan cabang untuk memeriksa nol.

Jika kita dapat membuat aplikasi aljabar linier generik yang kredibel dengan harga muladd!

@andreasnoack Dengan ini, saya kira yang Anda maksud adalah "aplikasi untuk _three-argument_ muladd! " karena jika tidak, Anda tidak akan setuju untuk menyertakan lima argumen mul! ?

Tapi saya masih bisa memberikan contoh di mana muladd!(A, B, C) berguna. Misalnya, jika Anda ingin membangun jaringan "dunia kecil", sebaiknya memiliki penjumlahan "malas" dari matriks berpita dan matriks renggang. Anda kemudian dapat menulis sesuatu seperti:

A :: SparseMatrixCSC
B :: BandedMatrix
x :: Vector  # input
y :: Vector  # output

# Compute `y .= (A .+ B) * x` efficiently:
fill!(y, 0)
muladd!(x, A, y)  # y .+= A * x
muladd!(x, B, y)  # y .+= B * x

Tetapi saya tidak keberatan menulis true s secara manual di sana karena saya dapat membungkusnya untuk penggunaan saya. Memiliki fungsi lima argumen sebagai API terdokumentasi yang stabil adalah tujuan terpenting di sini.

Kembali ke intinya:

Memiliki mul! dan muladd! tampaknya mubazir ketika yang pertama hanya yang terakhir dengan nilai default untuk α dan β .

Tetapi kita memiliki beberapa * diimplementasikan dalam bentuk mul! dengan "nilai default" dari larik keluaran yang diinisialisasi dengan tepat. Saya pikir mungkin ada contoh "jalan pintas" di Base dan pustaka standar? Saya pikir masuk akal untuk memiliki mul! dan muladd! meskipun mul! hanyalah jalan pintas dari muladd! .

Saya sangat lebih suka jika kita menjaga properti nol-kuat false terpisah dari pembahasan mul!

Saya setuju bahwa akan membangun untuk fokus membahas nama versi lima argumen dari multiply-add terlebih dahulu ( mul! vs muladd! ).

Saya tidak melakukan pekerjaan dengan baik ketika saya meminta kasus penggunaan umum di mana Anda memerlukan muladd untuk bekerja secara umum di seluruh matriks dan angka. Versi nomor akan menjadi muladd tanpa tanda seru jadi apa yang saya tanyakan tidak benar-benar masuk akal.

Contoh Anda bisa saja ditulis sebagai

mul!(y, A, x, 1, 1)
mul!(y, B, x, 1, 1)

jadi saya masih tidak melihat perlunya muladd! . Apakah hanya karena menurut Anda kasus ini begitu umum sehingga menulis 1, 1 terlalu bertele-tele?

Tetapi kita memiliki beberapa * diimplementasikan dalam bentuk mul! dengan "nilai default" dari larik keluaran yang diinisialisasi dengan tepat. Saya pikir mungkin ada contoh "jalan pintas" di Base dan pustaka standar?

Saya tidak mengerti yang ini. Bisakah Anda mencoba menjelaskan? Pintasan apa yang Anda bicarakan di sini?

jadi saya masih belum merasa perlu muladd! . Apakah hanya karena menurut Anda kasus ini begitu umum sehingga menulis 1, 1 terlalu bertele-tele?

Saya pikir muladd! juga lebih deskriptif tentang apa yang sebenarnya dilakukannya (meskipun mungkin seharusnya addmul! ).

Saya tidak punya masalah dengan nama muladd! . Pada dasarnya, saya hanya tidak berpikir kita harus berfungsi untuk ini dan kedua saya tidak berpikir mencela mul! demi muladd! / addmul! itu sepadan.

Apakah hanya karena menurut Anda kasus ini begitu umum sehingga penulisan 1, 1 terlalu bertele-tele?

Tidak. Saya benar-benar baik-baik saja dengan memanggil fungsi lima argumen selama itu adalah API publik. Saya hanya mencoba memberi contoh di mana saya hanya membutuhkan tiga versi argumen (karena saya pikir itu permintaan Anda).

Pintasan apa yang Anda bicarakan di sini?

https://github.com/JuliaLang/julia/blob/f068f21d6099632bd5543ad065d5de96943c9181/stdlib/LinearAlgebra/src/matmul.jl#L140 -L143

Saya pikir * didefinisikan di sini dapat dianggap sebagai jalan pintas dari mul! . Ini "hanya" mul! dengan nilai default. Jadi, mengapa tidak membiarkan mul! menjadi muladd! / addmul! dengan nilai default?

Ada rmul! dan lmul! didefinisikan sebagai "pintasan" serupa:

https://github.com/JuliaLang/julia/blob/f068f21d6099632bd5543ad065d5de96943c9181/stdlib/LinearAlgebra/src/triangular.jl#L478 -L479

mencela mul!

Saya pikir pembahasannya tentang menambahkan antarmuka baru atau tidak. Jika kita perlu menghentikan mul! untuk menambahkan API baru, menurut saya itu tidak sepadan.

Argumen utama yang dapat saya pikirkan adalah:

  • secara konseptual bentuk 5-argumen tidak lebih dari sekedar "mengalikan", dan menyampaikannya dengan lebih jelas.
  • Anda kemudian dapat menulis addmul!(C, A, B) alih-alih mul!(C,A,B,1,1) atau mul!(C,A,B,true,true) .

Saya pikir * didefinisikan di sini dapat dianggap sebagai jalan pintas dari mul! . Ini "hanya" mul! dengan nilai default. Jadi, kenapa tidak biarkan mul! menjadi muladd! / addmul! dengan nilai default?

Karena * adalah cara default untuk mengalikan matriks dan bagaimana sebagian besar pengguna akan melakukannya. Sebagai perbandingan, muladd! tidak akan mendekati penggunaan * . Selain itu, ini bahkan operator yang sudah ada sedangkan muladd! / addmul! akan menjadi fungsi baru.

Jangan berpikir rmul! dan lmul! cocok dengan pola ini karena mereka umumnya bukan versi nilai default dari metode mul! tempatnya.

Simon merangkum manfaatnya dengan baik di postingan tepat di atas. Pertanyaannya adalah apakah manfaatnya cukup besar untuk membenarkan fungsi tambahan dari penggantian nama (yang berarti penghentian mul! ). Di situlah kami tidak setuju. Saya tidak berpikir itu sepadan.

Ketika Anda mengatakan bahwa mengganti nama tidak sepadan, apakah Anda memperhitungkan bahwa API tidak sepenuhnya bersifat publik? Maksud saya, itu tidak ada dalam dokumentasi Julia.

Saya tahu LazyArrays.jl (dan paket lain?) Sudah menggunakannya secara membabi buta mengikuti semver tidak akan baik. Tapi tetap saja, ini tidak publik seperti fungsi lainnya.

mul! diekspor dari LinearAlgebra dan digunakan secara luas sehingga kami pasti harus menghentikannya pada saat ini. Sayang sekali kami tidak melakukan diskusi ini ketika A_mul_B! menjadi mul! atau setidaknya sebelum 0,7 karena itu akan menjadi waktu yang jauh lebih baik untuk mengganti nama fungsi.

Bagaimana jika menggunakan mul! untuk saat ini dan memperbarui nama untuk LinearAlgebra v2.0 ketika kita dapat memperbarui stdlib secara terpisah?

LazyArrays.jl tidak menggunakan mul! karena tidak fleksibel untuk banyak jenis matriks (dan memicu bug kelambatan kompilator saat Anda mengganti dengan StridedArray s). Ini memberikan konstruksi alternatif dari bentuk

y .= Mul(A, x)

yang menurut saya lebih deskriptif. Analog 5 argumen adalah

y .= a .* Mul(A, x) .+ b .* y

Saya akan berdebat mendukung deprecating mul! dan pindah ke pendekatan LazyArrays.jl di LinearAlgebra.jl, tapi itu akan menjadi kasus yang sulit untuk dibuat.

LowRankApprox.jl memang menggunakan mul! , tetapi saya mungkin mengubahnya untuk menggunakan pendekatan LazyArrays.jl dan dengan demikian menghindari bug compiler.

BAIK. Saya pikir hanya ada dua proposal. Tapi ternyata ada sekitar tiga proposal ?:

  1. tiga dan lima argumen mul!
  2. tiga dan lima argumen muladd!
  3. tiga argumen mul! dan lima argumen muladd!

( muladd! bisa disebut addmul! )

Saya berpikir kita sedang membandingkan 1 dan 3. Pemahaman saya sekarang adalah bahwa @andreasnoack telah membandingkan 1 dan 2.

Saya akan mengatakan 2 bukanlah pilihan sama sekali karena tiga argumen mul! adalah API publik dan banyak digunakan. Apa yang saya maksud dengan "API tidak sepenuhnya publik" adalah bahwa lima argumen mul! tidak didokumentasikan .

Ya, rencana saya adalah menyimpan mul! (sebagai bentuk argumen 3 dan mungkin 4 argumen). Saya pikir ini bermanfaat karena 3 arg mul! dan addmul! akan memiliki perilaku yang berbeda, yaitu jika diberikan addmul!(C, A, B, α, β) , kita akan memiliki:

mul!(C, A, B) = addmul!(C, A, B, 1, 0)
mul!(C, A, B, α) = addmul!(C, A, B, α, 0)
addmul!(C, A, B) = addmul!(C, A, B, 1, 1)
addmul!(C, A, B, α) = addmul!(C, A, B, α, 1)

Namun Anda mungkin tidak ingin benar-benar menerapkannya dengan cara ini dalam praktiknya, misalnya mungkin lebih sederhana hanya dengan 4-arg mul! dan addmul! secara terpisah, dan tentukan 5-arg addmul! sebagai:

addmul!(C, A, B, α, β) = addmul!(C .= β .* C, A, B, α)

Menabrak!

Namun Anda mungkin tidak ingin benar-benar menerapkannya dengan cara ini dalam praktiknya, misalnya mungkin lebih sederhana hanya dengan 4-arg mul! dan addmul! secara terpisah, dan tentukan addmul 5-arg! sebagai:
addmul!(C, A, B, α, β) = addmul!(C .= β .* C, A, B, α)

Mengapa tidak langsung dilakukan secara optimal? Inti dari tidak melakukannya adalah Anda hanya perlu mengunjungi elemen C sekali, yang pasti lebih efisien untuk matriks besar. Selain itu, saya hampir tidak percaya bahwa kode akan lebih panjang dengan mendefinisikan hanya 5-arg addmul! versus 4-arg mul! dan addmul! secara terpisah.

Untuk diketahui, saya telah memodifikasi implementasi LinearAlgebra _generic_matmatmul! untuk mengambil 5-argumen di LazyArrays: https://github.com/JuliaArrays/LazyArrays.jl/blob/8a50250fc6cf3f2402758088227769cf2de2e053/#L70/linalg/blasmul

Ini namanya melalui:

materialize!(MulAdd(α, A, b, β, c)) 

tetapi kode sebenarnya (dalam tiled_blasmul! ) akan mudah diterjemahkan kembali ke LinearAlgebra.

Apa yang dapat dilakukan untuk mencoba mempercepat proses ini? Hal-hal yang saya kerjakan akan benar-benar mendapat manfaat dari API perkalian matriks terpadu dengan mul + add di tempat

Versi terbaru Strided.jl sekarang juga mendukung 5 argumen mul!(C,A,B,α,β) , mengirim ke BLAS bila memungkinkan, dan menggunakan implementasinya sendiri (multithread) sebaliknya.

@Jutho paket hebat! apakah ada peta jalan untuk selanjutnya? mungkinkah rencananya untuk akhirnya bergabung dengan LinearAlgebra?

Ini tidak pernah menjadi niat saya tetapi saya tidak menentangnya jika itu pada suatu saat diminta. Namun, saya pikir penggunaan liberal saya dari fungsi @generated (walaupun hanya ada satu) dalam fungsi umum mapreduce mungkin tidak cocok untuk Base.

Peta jalan pribadi saya: ini sebagian besar adalah paket tingkat rendah untuk digunakan oleh paket tingkat yang lebih tinggi, yaitu versi baru TensorOperations, dan beberapa paket lain yang sedang saya kerjakan. Namun, beberapa dukungan lagi untuk aljabar linier dasar akan lebih baik (misalnya menerapkan norm menjadi StridedView saat ini kembali ke implementasi yang agak lambat dari norm di Julia Base). Dan jika saya punya waktu dan belajar bekerja dengan GPU, coba terapkan mapreducekernel sama umum untuk GPUArray s.

Saya pikir konsensusnya sejauh ini adalah:

  1. Kita harus menyimpan mul!(C, A, B)
  2. Kita membutuhkan fungsi _some_ 5-argumen untuk penambahan di tempat C = αAB + βC

Saya sarankan untuk pertama-tama fokus pada apa nama fungsi 5-argumen itu dan diskusikan API tambahan nanti (seperti 3- dan 4-argumen addmul! ). Tapi ini adalah "fitur" yang kami dapatkan dari _not_ menggunakan mul! jadi sulit untuk tidak digabungkan.

@andreasnoack Apakah kekhawatiran Anda tentang penghentian / penggantian nama diselesaikan dengan komentar @simonbyrne di atas https://github.com/JuliaLang/julia/issues/23919#issuecomment -431046516? Saya pikir tidak perlu ada penghentian.

FYI, saya baru saja menyelesaikan implementasi # 29634. Saya menghargai jika seseorang yang akrab dengan LinearAlgebra dapat memeriksanya.

Saya pikir itu lebih sederhana dan lebih baik untuk menamai semuanya mul! . Ini juga menghindari penghentian. Jika kita benar-benar menginginkan nama yang berbeda, muladd lebih baik.

Hal lain yang mungkin perlu diperhatikan saat mendiskusikan API mul! :

Ketika scale! hilang dan terserap dalam transisi 0,6 -> 0,7, saya agak sedih karena bagi saya, perkalian skalar (properti ruang vektor) sangat berbeda daripada mengalikan objek sendiri (properti aljabar) ). Meskipun demikian, saya telah sepenuhnya menganut pendekatan mul! , dan sangat menghargai kemampuan untuk rmul!(vector,scalar) dan lmul!(scalar,vector) ketika perkalian skalar tidak komutatif. Tapi sekarang saya setiap hari lebih terganggu oleh nama un-Julian dari dua operasi ruang vektor lainnya di tempat: axpy! dan generalisasinya axpby! . Mungkinkah ini juga diserap menjadi mul! / muladd! / addmul! . Meskipun agak aneh, jika salah satu dari dua faktor dalam A*B sudah menjadi skalar, tidak diperlukan faktor skalar tambahan α .
Tapi mungkin kemudian, dalam analogi dengan

mul!(C, A, B, α, β)

mungkin juga ada

add!(Y, X, α, β)

untuk mengganti axpby! .

@andreasnoack Apakah kekhawatiran Anda tentang penghentian / penggantian nama diselesaikan dengan komentar @simonbyrne di atas # 23919 (komentar)? Saya pikir tidak perlu ada penghentian.

Lihat paragraf terakhir https://github.com/JuliaLang/julia/issues/23919#issuecomment -430952179. Saya masih berpikir memperkenalkan fungsi baru tidak sepadan. Jika kita tetap melakukannya, saya pikir kita harus menghentikan 5-argumen saat ini mul! .

@Jutho Saya pikir mengganti nama acp(b)y! menjadi add! akan menjadi ide yang bagus.

Lihat paragraf terakhir # 23919 (komentar) . Saya masih berpikir memperkenalkan fungsi baru tidak sepadan.

Ya, saya telah membacanya dan menjawab bahwa lima argumen mul! tidak didokumentasikan dan itu bukan bagian dari API publik. Jadi, _technically_ tidak perlu ada penghentian. Lihat paragraf terakhir https://github.com/JuliaLang/julia/issues/23919#issuecomment -430975159 (Tentu saja, akan lebih baik jika ada penghentian jadi saya sudah menerapkannya di # 29634.)

Di sini, saya berasumsi bahwa deklarasi API publik karena dokumentasi satu tanda tangan (misalnya, mul!(C, A, B) ) tidak berlaku untuk tanda tangan lain (misalnya, mul!(C, A, B, α, β) ). Jika _bukan_ masalahnya, saya pikir Julia dan stdlib-nya terlalu banyak mengekspos internal. Misalnya, berikut adalah tanda tangan yang didokumentasikan dari Pkg.add

https://github.com/JuliaLang/julia/blob/0d713926f85dfa3e4e0962215b909b8e47e94f48/stdlib/Pkg/src/Pkg.jl#L76 -L79

sedangkan definisi sebenarnya adalah

https://github.com/JuliaLang/julia/blob/0d713926f85dfa3e4e0962215b909b8e47e94f48/stdlib/Pkg/src/API.jl#L69 -L70

https://github.com/JuliaLang/julia/blob/0d713926f85dfa3e4e0962215b909b8e47e94f48/stdlib/Pkg/src/API.jl#L27 -L33

Jika keberadaan dokumentasi setidaknya satu tanda tangan Pkg.add menyiratkan bahwa tanda tangan lain adalah API publik, Pkg.jl tidak dapat menghapus perilaku karena detail implementasi tanpa menabrak versi mayor, misalnya ,: Pkg.add(...; mode = :develop) berjalan Pkg.develop(...) ; semua argumen kata kunci untuk Context! didukung (yang sebenarnya mungkin dimaksudkan).

Tapi ini hanya kesan saya. Apakah Anda menganggap mul!(C, A, B, α, β) telah dijadikan publik seperti mul!(C, A, B) ?

Saya pikir kita berbicara melewati satu sama lain. Apa yang saya katakan adalah bahwa saya (masih) tidak berpikir bahwa memperkenalkan fungsi lain itu sepadan. Oleh karena itu referensi saya untuk komentar saya sebelumnya. Ini terpisah dari pembahasan penghentian lima argumen mul! .

Namun, jika kita memutuskan untuk menambahkan fungsi lain maka saya pikir akan lebih baik untuk menghentikan lima argumen mul! daripada hanya memecahnya. Tentu saja, ini tidak biasa digunakan sebagai tiga argumen mul! , tapi mengapa tidak mencela alih-alih hanya memecahnya?

Ini terpisah dari pembahasan penghentian lima argumen mul! .

Interpretasi saya tentang paragraf terakhir dari komentar Anda https://github.com/JuliaLang/julia/issues/23919#issuecomment -430952179 adalah bahwa Anda mengakui manfaat yang terdaftar @simonbyrne https://github.com/JuliaLang/julia/issues / 23919 # Issuecomment -430809383 untuk fungsi lima argumen baru tetapi dianggap kurang berharga dibandingkan dengan mempertahankan _public API_ (seperti yang Anda sebutkan "penggantian nama" dan "deprecation"). Itulah mengapa saya berpikir mempertimbangkan apakah lima argumen mul! telah publik atau tidak penting.

Tetapi Anda juga menyebutkan pembenaran memiliki "fungsi tambahan" yang, saya kira, adalah apa yang Anda maksud sekarang. Apakah Anda berpendapat bahwa komputasi _C = AB_ dan _C = αAB + βC_ cukup mirip sehingga nama yang sama dapat mendeskripsikan keduanya? Saya sebenarnya tidak setuju karena ada cara lain untuk menggeneralisasi tiga argumen mul! : misalnya, mengapa tidak mul!(y, A₁, A₂, ..., Aₙ, x) untuk _y = A₁ A₂ ⋯ Aₙ x_ https://github.com/JuliaLang/julia / issues / 23919 # Issuecomment -402953987?

mengapa tidak mencela alih-alih hanya merusaknya?

Seperti yang saya katakan di komentar sebelumnya, saya setuju mencela lima argumen mul! adalah hal yang benar untuk dilakukan _ jika kami akan memperkenalkan fungsi lain. Kode ini sudah ada di PR # 29634 saya.

Apakah Anda berpendapat bahwa perhitungan C = AB dan C = αAB + βC cukup mirip sehingga nama yang sama dapat mendeskripsikan keduanya?

Ya, karena yang pertama hanya yang terakhir dengan β=0 . Cukup adil untuk menyatakan bahwa muladd! / addmul! adalah nama yang lebih tepat untuk C = αAB + βC tetapi untuk mencapainya akan membutuhkan pengenalan fungsi perkalian matriks lainnya ( muladd! / addmul! ) atau mengganti nama mul! dan saya rasa itu tidak layak sekarang. Jika hal ini terjadi pada musim semi maka akan lebih mudah untuk mempertimbangkan perubahan.

Saya sebenarnya tidak setuju karena ada cara lain untuk menggeneralisasi mul tiga argumen !:

Julia kebetulan mendefinisikan metode perkalian matriks di tempat tanpa argumen α dan β tetapi tradisi perkalian matriks benar-benar didasarkan pada BLAS-3 dan di sana fungsi perkalian matriks umum adalah C = αAB + βC .

mengganti nama mul!

Apakah maksud Anda mengganti namanya di stdlib atau di modul / kode pengguna hilir? Jika yang Anda maksud yang pertama, itu sudah selesai (untuk LinearAlgebra dan SparseArrays) di # 29634 jadi saya rasa Anda tidak perlu khawatir tentang itu. Jika yang Anda maksud yang terakhir, saya pikir itu lagi-lagi bermuara pada diskusi publik atau tidak.

tradisi perkalian matriks benar-benar didasarkan pada BLAS-3

Namun Julia sudah menyimpang dari konvensi penamaan BLAS. Jadi, bukankah menyenangkan memiliki nama yang lebih deskriptif?

Apakah maksud Anda mengganti namanya di stdlib atau di modul / kode pengguna hilir?

29634 tidak mengganti nama fungsi mul! . Ini menambahkan fungsi baru addmul! .

Namun Julia sudah menyimpang dari konvensi penamaan BLAS.

Saya tidak berbicara tentang penamaan. Setidaknya tidak persis karena Fortran 77 memiliki beberapa batasan yang tidak kami miliki baik dalam hal nama fungsi dan pengiriman. Saya sedang berbicara tentang apa yang sedang dihitung. Fungsi perkalian matriks umum di BLAS-3 menghitung C = αAB + βC dan di Julia, nilainya menjadi mul! (fka A_mul_B! ).

Jadi, bukankah menyenangkan memiliki nama yang lebih deskriptif?

Itu akan, dan saya telah mengatakan itu beberapa kali. Masalahnya adalah tidak jauh lebih baik bahwa kita harus memiliki dua fungsi perkalian matriks yang pada dasarnya melakukan hal yang sama.

29634 tidak mengganti nama fungsi mul! . Ini menambahkan fungsi baru addmul! .

Yang ingin saya katakan adalah bahwa lima argumen mul! telah diganti namanya menjadi addmul! .

Masalahnya adalah tidak jauh lebih baik bahwa kita harus memiliki dua fungsi perkalian matriks yang pada dasarnya melakukan hal yang sama.

Saya merasa jika mereka pada dasarnya sama atau tidak agak subjektif. Saya pikir _C = αAB + βC_ dan _Y = A₁ A₂ ⋯ Aₙ X_ adalah generalisasi yang valid secara matematis dari _C = AB_. Kecuali _C = αAB + βC_ adalah generalisasi yang unik, menurut saya argumennya tidak cukup kuat. Itu juga tergantung pada apakah Anda tahu BLAS API dan saya tidak yakin apakah itu pengetahuan dasar untuk pengguna Julia pada umumnya.

Juga, _C = AB_ dan _C = αAB + βC_ secara komputasi sangat berbeda dalam hal konten C digunakan atau tidak. Ini adalah parameter hanya keluaran untuk yang pertama dan parameter masukan-keluaran untuk yang terakhir. Saya pikir perbedaan ini membutuhkan petunjuk visual. Jika saya melihat mul!(some_func(...), ...) dan mul! memiliki bentuk lima argumen, saya harus menghitung jumlah argumen (yang sulit ketika itu adalah hasil dari pemanggilan fungsi karena Anda harus mencocokkan tanda kurung) untuk melihat apakah some_func melakukan beberapa perhitungan atau hanya alokasi. Jika kita memiliki addmul! maka saya dapat langsung mengharapkan bahwa some_func dalam mul!(some_func(...), ...) hanya melakukan alokasi.

Saya merasa jika mereka pada dasarnya sama atau tidak agak subjektif. Saya pikir C = αAB + βC dan Y = A₁ A₂ ⋯ Aₙ X adalah generalisasi yang valid secara matematis dari C = AB. Kecuali C = αAB + βC adalah generalisasi yang unik, menurut saya argumennya tidak cukup kuat.

Ini mungkin bukan generalisasi yang unik, namun itu adalah generalisasi yang dapat dihitung dengan biaya yang hampir sama, dan yang membentuk primitif yang berguna untuk membangun algoritma aljabar linier lainnya. Dalam banyak kesempatan saya ingin memiliki beta bukan nol ketika menerapkan berbagai algoritma terkait aljabar linier, dan selalu harus kembali ke BLAS.gemm! . Generalisasi lain seperti yang Anda sebutkan tidak dapat dihitung dalam satu kesempatan tanpa perantara sementara, jadi versi di tempat jauh lebih berguna. Selain itu, mereka tidak berguna secara umum seperti operasi primitif.

Itu juga tergantung pada apakah Anda tahu BLAS API dan saya tidak yakin apakah itu pengetahuan dasar untuk pengguna Julia pada umumnya.

Selama argumen default α=1 dan β=0 masih ada, ketiga argumen mul! akan melakukan apa yang diharapkan pengguna Julia tanpa latar belakang BLAS. Untuk opsi yang lebih maju, seseorang harus membaca manual, seperti yang harus dilakukan dengan bahasa dan fungsi apa pun. Selain itu, panggilan tunggal mul! ini tidak hanya menggantikan gemm tetapi juga gemv dan trmv (yang anehnya tidak memiliki α dan β parameter di BLAS API) dan mungkin banyak lainnya.

Saya setuju BLAS-3 adalah generalisasi yang tepat dalam hal komputasi dan komposisinya sangat baik. Saya mengemukakan kemungkinan generalisasi lain hanya karena menurut saya itu tidak "cukup unik" untuk membenarkan penggunaan nama yang sama. Lihat juga argumen output-only vs input-output di paragraf terakhir https://github.com/JuliaLang/julia/issues/23919#issuecomment -441267056. Saya pikir nama yang berbeda membuat membaca / meninjau kode lebih mudah.

Selain itu, panggilan tunggal mul! ini tidak hanya menggantikan gemm tetapi juga gemv dan trmv (yang anehnya tidak memiliki α dan β parameter di BLAS API) dan mungkin banyak lainnya.

Ya, sudah diterapkan di # 29634 dan siap digunakan setelah namanya ditentukan (dan ditinjau)!

Saya mengalami kesulitan mengikuti percakapan ini (ini agak panjang dan panjang ... maaf!), Apakah proposal utama kira-kira mul!(C, A, B; α=true, β=false) ?

Saya tidak berpikir argumen kata kunci untuk α dan β ada di atas meja. Misalnya, @andreasnoack diberhentikan argumen kata kunci di https://github.com/JuliaLang/julia/issues/23919#issuecomment -365.762.889. @simonbyrne menyebutkan argumen kata kunci di https://github.com/JuliaLang/julia/issues/23919#issuecomment -426881998 tetapi saran terbarunya https://github.com/JuliaLang/julia/issues/23919#issuecomment -431046516 bersifat posisional argumen.

Kami belum memutuskan namanya (yaitu, mul! vs addmul! vs muladd! ) dan saya pikir itulah topik utama (atau setidaknya itulah keinginan saya).

Bagaimana Anda biasanya menyelesaikan kontroversi semacam ini? Pemungutan suara? Triage?

apakah proposal terdepan seperti mul! (C, A, B; α = true, β = false)?

Saya suka ini, tapi tanpa kwarg.

Tidak melihat hal kata kunci. Saya juga ragu untuk menambahkan kata kunci unicode. Saya pikir argumen posisi dengan nilai default ini baik-baik saja. Adapun pemungutan suara yang akan datang, milik saya adalah mul! . Saya rasa ini adalah generalisasi dari mul! yang cukup spesifik dan berguna untuk tidak memerlukan nama baru.

Sekadar mengumpulkan data (setidaknya saat ini), ayo lakukan voting:

Apa nama fungsi favorit Anda untuk _C = αAB + βC_?

  • : +1: mul!
  • : -1: addmul!
  • : smile: muladd!
  • : tada: sesuatu yang lain

Bagi saya addmul! sepertinya menggambarkan (A+B)C bukan AB + C .

Pada awalnya saya memilih mul! lalu saya melihat operasinya dan berpikir "ini melakukan penggandaan dan kemudian menambahkan, jelas kita harus menyebutnya muladd! . Sekarang saya tidak terpikir untuk menyebutnya apa pun. Fakta bahwa itu ada di tempat dengan jelas ditunjukkan oleh ! dan bagian penskalaan tampaknya cocok untuk kata kunci args.

itu melakukan perkalian dan kemudian menambahkan, jelas kita harus menyebutnya muladd!

Hanya jika Anda menggunakan nilai default β=true , tetapi untuk nilai lain itu hanya sesuatu yang lebih umum lagi. Jadi apa gunanya tidak menyebutnya mul! , di mana nilai lain selain nilai default β=false juga hanya memberi Anda sesuatu yang lebih umum? Dan bagaimana Anda mengurutkan argumen, dibandingkan dengan muladd(x,y,z) = x*y + z ? Akan agak membingungkan, bukan?

Saya pikir muladd! memiliki kelemahan dari terdengar deskriptif padahal bukan: nama deskriptif akan menjadi sesuatu seperti scalemuladd! untuk menyebutkan bagian penskalaan.

Jadi saya lebih suka mul! karena tidak cukup jelas untuk tidak menghasilkan harapan.

Karena itu, saya menyebut versi malas di LazyArays.jl MulAdd .

Saya lebih suka muladd! ke mul! karena bagus untuk tetap membedakan fungsi yang tidak pernah menggunakan nilai C ( mul! ) dari fungsi yang menggunakannya ( muladd! ).

  • Secara teknis ini adalah perkalian matriks: [AC] * [Bα; Iβ] atau, lihat komentar di bawah, [αA βC] * [B; SAYA]
  • ~ Kita sudah memiliki 5-arg mul! untuk matriks renggang, hal yang sama untuk linalg padat akan konsisten ~ (bukan argumen baru)

Jadi saya lebih suka menyebutnya mul! .

  • Secara teknis itu adalah perkalian matriks: [AC] * [Bα; Iβ]

... jika eltype memiliki perkalian komutatif

... jika eltype bersifat komutatif.

IIRC dari diskusi dengan @andreasnoack Julia baru saja mendefinisikan gemm / gemv sebagai y <- A * x * α + y * β karena itu paling masuk akal.

@haampie Itu bagus, tahu! Saat saya menerapkannya sebaliknya di # 29634.

Ini bantuan terbatas, tapi

       C = α*A*B + β*C

adalah cara terbaik yang bisa saya lakukan untuk mengekspresikan operasi dan karenanya mungkin makro <strong i="8">@call</strong> C = α*A*B + β*C atau <strong i="10">@call_specialized</strong> ... atau sesuatu di sepanjang baris ini akan menjadi antarmuka alami - juga untuk situasi serupa. Kemudian fungsi yang mendasarinya bisa disebut apa saja.

@mschauer LazyArrays.jl oleh @dlfivefifty memiliki sintaks yang bagus untuk memanggil 5-argumen mul! seperti sintaks Anda (dan banyak lagi!).

Saya pikir kita perlu membangun API berbasis fungsi terlebih dahulu dan agar pembuat paket dapat mulai membebani untuk matriks khusus mereka. Kemudian komunitas Julia bisa mulai bereksperimen dengan menyebutnya gula.

Hanya jika Anda menggunakan nilai default β=true , tetapi untuk nilai lain itu hanya sesuatu yang lebih umum lagi. Jadi apa gunanya tidak menyebutnya mul! , di mana nilai lain selain nilai default β=false juga hanya memberi Anda sesuatu yang lebih umum? Dan bagaimana Anda mengurutkan argumen, dibandingkan dengan muladd(x,y,z) = x*y + z ? Akan agak membingungkan, bukan?

Tentu ada beberapa penskalaan di sana tetapi "tulang" dari operasi itu jelas berlipat ganda dan bertambah. Saya juga akan baik-baik saja dengan muladd!(A, B, C, α=true, β=false) untuk mencocokkan tanda tangan muladd . Harus didokumentasikan, tentu saja, tapi tidak perlu dikatakan lagi. Itu membuat saya berharap bahwa muladd mengambil bagian aditif terlebih dahulu, tetapi kapal telah berlayar dengan yang itu.

Dan bagaimana Anda mengurutkan argumen, dibandingkan dengan muladd(x,y,z) = x*y + z ? Akan agak membingungkan, bukan?

Inilah alasan mengapa saya lebih suka addmul! daripada muladd! . Kami dapat memastikan urutan argumen tidak ada hubungannya dengan skalar muladd . (Meskipun saya lebih suka muladd! daripada mul! )

FWIW di sini adalah ringkasan dari argumen sejauh ini. (Saya mencoba untuk bersikap netral tetapi saya pro- muladd! / addmul! jadi ingatlah itu ...)

Perselisihan utama adalah apakah _C = AB_ dan _C = αAB + βC_ cukup berbeda untuk memberikan nama baru untuk yang terakhir.

Mereka cukup mirip karena ...

  1. Ini BLAS-3 dan dapat disusun dengan baik. Jadi, _C = αAB + βC_ adalah generalisasi yang jelas dari _C = AB_ (https://github.com/JuliaLang/julia/issues/23919#issuecomment-441246606, https://github.com/JuliaLang/julia/issues/ 23919 # Issuecomment-441312375, dll.)

  2. _ " muladd! memiliki kekurangan dari terdengar deskriptif jika bukan: nama deskriptif akan menjadi sesuatu seperti scalemuladd! untuk menyebutkan bagian penskalaan." _ --- https://github.com/ JuliaLang / julia / issues / 23919 # issue -441819470

  3. _ "Secara teknis ini adalah perkalian matriks: [AC] * [Bα; Iβ]" _ --- https://github.com/JuliaLang/julia/issues/23919#issuecomment -441825009

Mereka cukup berbeda karena ...

  1. _C = αAB + βC_ lebih dari mengalikan (https://github.com/JuliaLang/julia/issues/23919#issuecomment-430809383, https://github.com/JuliaLang/julia/issues/23919#issuecomment-427075792, https://github.com/JuliaLang/julia/issues/23919#issuecomment-441813176, dll.).

  2. Mungkin ada generalisasi lain dari mul! seperti Y = A₁ A₂ ⋯ Aₙ X (https://github.com/JuliaLang/julia/issues/23919#issuecomment-402953987 dll.)

  3. Parameter input-only vs Input-output: Membingungkan memiliki fungsi yang menggunakan data di C berdasarkan jumlah argumen (https://github.com/JuliaLang/julia/issues/23919#issuecomment -441267056, https://github.com/JuliaLang/julia/issues/23919#issuecomment-441824982)

Alasan lain mengapa mul! lebih baik karena ...:

  1. Matriks renggang sudah memilikinya. Jadi bagus untuk kompatibilitas ke belakang. Argumen kontra: lima argumen mul! tidak didokumentasikan jadi kita tidak perlu menganggapnya sebagai API publik.

dan mengapa muladd! / addmul! lebih baik karena ...:

  1. Kita dapat memiliki tiga atau empat argumen "fungsi praktis" yang berbeda untuk mul! dan muladd! / addmul! secara terpisah (https://github.com/JuliaLang/julia/issues / 23919 # Issuecomment-402953987, https://github.com/JuliaLang/julia/issues/23919#issuecomment-431046516, dll.). Argumen balasan: menulis mul!(y, A, x, 1, 1) tidak terlalu bertele-tele dibandingkan dengan mul!(y, A, x) (https://github.com/JuliaLang/julia/issues/23919#issuecomment-430674934, dll.)

Terima kasih atas ringkasan objektif @tkf

Saya juga akan baik-baik saja dengan muladd! (A, B, C, α = true, β = false) agar sesuai dengan tanda tangan muladd.

Saya berharap untuk fungsi yang disebut mulladd! defaultnya adalah β=true . Namun, menurut saya urutan argumen ini, yang ditentukan dari muladd , akan sangat membingungkan dalam kaitannya dengan mul!(C,A,B)

Mungkin saya salah, tetapi saya akan berpikir bahwa kebanyakan orang / aplikasi / kode tingkat tinggi (yang belum puas hanya dengan operator perkalian * ) perlu mul! . Kemampuan untuk juga mencampur βC , dengan β=1 ( true ) atau sebaliknya, akan digunakan dalam kode tingkat yang lebih rendah, oleh orang-orang yang mengetahui bahwa BLAS API untuk perkalian matriks memungkinkan ini. Saya akan menebak bahwa orang-orang ini akan mencari fungsi ini di bawah mul! , yang merupakan antarmuka Julia yang mapan menjadi gemm , gemv , ... Menambahkan nama baru (yang membingungkan urutan argumen yang berlawanan) tampaknya tidak layak; Saya gagal melihat keuntungannya?

Saya akan menebak bahwa orang-orang ini akan mencari fungsi ini di bawah mul! , yang merupakan antarmuka Julia yang sudah mapan menjadi gemm , gemv , ... Menambahkan nama baru (yang membingungkan urutan argumen yang berlawanan) tampaknya tidak sepadan; Saya gagal melihat keuntungannya?

Saya pikir kemampuan untuk dapat ditemukan bukanlah masalah besar karena kita cukup menyebutkan muladd! dalam mul! docstring. Mereka yang cukup ahli untuk mengetahui BLAS pasti tahu di mana mencari API, bukan?

Mengenai argumen posisi vs kata kunci: Ini belum dibahas di sini tetapi saya pikir C = αAB + βC dengan α menjadi matriks diagonal dapat diimplementasikan secara efisien dan semudah skalar α . Ekstensi semacam itu mengharuskan kita dapat mengirimkan tipe α yang tidak mungkin dilakukan dengan argumen kata kunci.

Selain itu, untuk eltype non-komutatif, Anda mungkin ingin menghitung C = ABα + Cβ secara efisien dengan memanggil muladd!(α', B', A', β', C') (urutan argumen hipotetis). Ini mungkin mengharuskan Anda untuk mengirimkan pembungkus malas Adjoint(α) dan Adjoint(β) . (Saya tidak menggunakan nomor non-komutatif di Julia secara pribadi jadi ini mungkin sangat hipotetis.)

Saya setuju dengan poin @Jutho bahwa fungsi multiply-add ini adalah API tingkat rendah untuk programmer terampil seperti pelaksana perpustakaan. Saya pikir ekstensibilitas memiliki prioritas tinggi untuk API ini dan argumen posisi adalah cara yang tepat.

Argumen lain untuk menghindari argumen kata kunci adalah apa @andreasnoack katakan sebelumnya https://github.com/JuliaLang/julia/issues/23919#issuecomment -365.762.889:

Nama α dan β juga tidak super intuitif kecuali Anda tahu BLAS

@tkf , tentu, argumen saya agak: jumlah penggunaan sebenarnya dari β != 0 akan lebih kecil daripada penggunaan β == 0 , dan mereka yang membutuhkan tidak akan terkejut menemukan ini sedikit lebih umum perilaku di bawah mul! . Oleh karena itu, saya tidak melihat keuntungan dari memisahkan ini dengan nama baru, terutama karena urutan argumen kacau (setidaknya dengan muladd! ). Jika itu harus menjadi metode baru, saya juga bersimpati dengan argumen Anda untuk addmul! .

mereka yang membutuhkan tidak akan terkejut menemukan perilaku yang sedikit lebih umum ini di bawah mul! .

Saya setuju dengan poin ini.

Oleh karena itu, saya tidak melihat keuntungan dari memisahkan ini dengan nama baru,

Kecuali jika Anda melihat beberapa kerugian, saya pikir ini adalah keuntungan global karena ada orang lain yang melihat manfaatnya.

terutama karena urutan argumen kacau (setidaknya dengan muladd! )

Saya kira Anda akan menganggap ini sebagai bahaya dan saya mengerti maksudnya. Hanya saja menurut saya manfaat lain untuk muladd! / addmul! lebih penting.

Saya kira Anda akan menganggap ini sebagai bahaya dan saya mengerti maksudnya. Cuma menurut saya manfaat lain buat muladd! / Addmul! lebih penting.

Itu memang salahnya, bersama fakta bahwa mul! selalu menjadi single entry point ke dalam beberapa operasi BLAS yang berhubungan dengan perkalian, baik itu dibatasi dengan tidak memberikan akses penuh ke α dan β. Dan sekarang dengan muladd! , akan ada dua titik masuk yang berbeda, bergantung pada sedikit perbedaan dalam operasi yang diminta yang dapat dengan mudah ditangkap oleh argumen (dan memang, yang ditangkap oleh argumen di BLAS API) . Saya pikir itu adalah kesalahan di Julia di tempat pertama untuk tidak menawarkan akses penuh ke API BLAS (jadi terima kasih telah memperbaiki @tkf itu). Meskipun itu konvensi penamaan fortran yang mengerikan, saya rasa orang-orang ini tahu mengapa mereka melakukan hal-hal seperti ini. Tetapi juga, saya pikir keluarga operasi ini (yaitu keluarga 2 parameter operasi yang diparameterisasi oleh α dan β) milik bersama di bawah satu titik masuk, karena mereka berada di BLAS.

Argumen balasan yang paling valid dalam pandangan saya adalah perbedaan antara apakah data asli di C akan diakses atau tidak. Tetapi mengingat fakta bahwa Julia telah merangkul mengalikan dengan false sebagai cara untuk menjamin hasil nol, bahkan ketika faktor lainnya adalah NaN , saya pikir ini juga diurus. Tapi mungkin fakta ini perlu dikomunikasikan / didokumentasikan dengan lebih baik (sudah lama saya tidak membaca dokumentasinya), dan saya juga baru mempelajarinya. (Itulah mengapa di KrylovKit.jl, saya memerlukan keberadaan metode fill! , untuk menginisialisasi tipe pengguna mirip vektor dengan angka nol. Tapi sekarang saya tahu saya hanya bisa rmul!(x,false) sebagai gantinya, jadi saya tidak perlu memaksakan bahwa fill! diimplementasikan).

Cuma menurut saya manfaat lain buat muladd! / Addmul! lebih penting.

Jadi izinkan saya membalikkan pertanyaan, apakah manfaat lain dari memiliki metode baru? Saya telah membaca ringkasan Anda lagi, tetapi saya hanya melihat inti dari mengakses C , yang baru saja saya komentari.

Saya menyebutkan kepada istri saya pagi ini bahwa ada percakapan selama dua bulan di komunitas Julia tentang penamaan operasi. Dia menyarankan agar itu disebut "fred!" - tidak ada akronim, tidak ada makna yang dalam, hanya nama yang bagus. Hanya meletakkan ini di sana atas namanya.

Bagus bahwa dia menyertakan tanda seru! 😄

Pertama-tama, untuk berjaga-jaga, izinkan saya mengklarifikasi bahwa kekhawatiran saya hampir hanya berasal dari keterbacaan kode, bukan kemampuan menulis atau dapat ditemukan. Anda menulis kode sekali tetapi membaca berkali-kali.

Apa keuntungan lain dari memiliki metode baru?

Seperti yang Anda komentari, saya pikir argumen parameter output vs input-output adalah yang paling penting. Tapi ini hanya salah satu alasan mengapa menurut saya _C = αAB + βC_ berbeda dari _C = AB_. Saya juga berpikir bahwa fakta sederhana bahwa mereka berbeda dalam arti bahwa ungkapan pertama adalah "superset" yang ketat dari yang terakhir memerlukan indikasi visual yang jelas dalam kode. Nama yang berbeda membantu programmer perantara (atau programmer tingkat lanjut yang tidak fokus) membaca kode skimming dan memperhatikan bahwa itu menggunakan sesuatu yang lebih aneh dari mul! .

Saya baru saja memeriksa jajak pendapat (Anda perlu mengklik "Muat lebih banyak" di atas) lagi dan sepertinya beberapa suara dipindahkan dari mul! menjadi muladd! ? Terakhir kali saya melihatnya, mul! menang. Mari kita rekam di sini sebelum mereka pindah: tertawa:

  • mul! : 6
  • addmul! : 2
  • muladd! : 8
  • sesuatu yang lain: 1

Sedikit lebih serius, menurut saya data ini tidak menunjukkan mul! atau muladd! lebih jelas dari yang lain. (Meskipun itu menunjukkan bahwa addmul! adalah minoritas: sob :)

Rasanya kita terjebak. Bagaimana kita melanjutkan?

Sebut saja gemm! saja?

Sebut saja gemm! sebagai gantinya?

Saya harap ini adalah lelucon ... kecuali Anda mengusulkan gemm!(α, A::Matrix, x::Vector, β, y::Vector) = gemv!(α, A, x, β, y) untuk matriks * vektor.

Bisakah kita membiarkan antarmuka mul! yang sudah ada (dengan matriks renggang) untuk saat ini sehingga kita dapat menggabungkan PR dan melakukan perbaikan, dan khawatir tentang apakah kita ingin menambahkan muladd! dalam PR lain ?

Mungkin sudah jelas bagi semua orang di sini tetapi saya hanya ingin menekankan bahwa pemungutan suara tidak

  • mul! vs muladd!

tapi

  • mul! vs ( mul! dan muladd! )

yaitu memiliki dua fungsi perkalian yang bermutasi, bukan satu.

Saya memutuskan untuk tidak memposting lagi karena setiap kali saya memposting yang mendukung mul! , suara sepertinya berpindah dari mul! menjadi ( mul! dan muladd! ).

Namun, saya punya pertanyaan? Jika kami memilih suara mayoritas saat ini, dan kami secara bersamaan memiliki mul!(C,A,B) dan muladd!(A,B,C,α=true,β=true) , dan saya ingin menyiapkan PR yang menggantikan axpy! dan axpby! dengan nama yang lebih julian add! , seharusnya add!(y, x, α=true, β=true) atau add!(x, y, α=true, β=true) (dimana, untuk kejelasan, y dimutasi). Atau sesuatu yang lain?

Jika tidak jelas, muladd!(A,B,C) akan melanggar konvensi bahwa argumen yang

Bisakah kita membiarkan antarmuka mul! yang sudah ada

@ Jebej Saya pikir argumen "kompatibilitas mundur" ini dibahas secara ekstensif. Namun, itu tidak meyakinkan siapa pun (dengan melihat jajak pendapat, bukan hanya saya).

khawatir apakah kita ingin menambahkan muladd! di PR lain?

Tidak baik merusak API publik. Jadi jika kita mengatakan mul! maka itu mul! selamanya (meskipun dalam teori LinearAlgebra dapat mengubah versi utamanya untuk memecahkan API).

Saya ingin menyiapkan PR yang menggantikan axpy! dan axpby! dengan nama yang lebih julian add! , harusnya add!(y, x, α=true, β=true) atau add!(x, y, α=true, β=true)

@Jutho Terima kasih, itu akan luar biasa! Saya pikir memilih urutan argumen akan mudah setelah kami memutuskan tanda panggilan dari API multiply-add.

muladd!(A,B,C) akan melanggar konvensi bahwa argumen yang

@simonbyrne Tapi (seperti yang telah Anda sebutkan di https://github.com/JuliaLang/julia/issues/23919#issuecomment-426881998), lmul! dan ldiv! mutasi argumen non-pertama. Jadi saya pikir kita tidak perlu mengecualikan muladd!(A,B,C,α,β) dari pilihan melainkan menghitungnya sebagai poin negatif untuk tanda tangan ini.

(Tapi saya akan mengatakan untuk menggunakan muladd!(α, A, B, β, C) jika kita akan memiliki API "urutan tekstual".)

Ngomong-ngomong, satu hal yang saya tidak mengerti dari hasil pemungutan suara adalah asimetri muladd! dan addmul! . Jika Anda menulis C = βC + αAB , menurut saya sepertinya addmul! lebih natural.

@tkf Ini tentang operasi apa yang Anda lakukan pertama kali. Bagi saya addmul! menyarankan agar Anda melakukan penjumlahan terlebih dahulu, lalu mengalikannya, seperti pada (A+B)C . Tentu saja subjektif. Tapi nama baik harus menarik intuisi.

Ah, saya mengerti maksudnya.

Karena ini masih macet, proposal saya akan memiliki pola penggunaan yang terdiri dari definisi fungsi dengan (pergi dengan @callexpr untuk yang kedua)

@callexpr(C .= β*C + α*A*B) = implementation(C, β, α, A, B)
@callexpr(C .= β*C + A*B) = implementation(C, β, true, A, B)

dan mungkin bentuk yang lebih ramah pengiriman (pergi dengan @callname untuk yang kedua)

function @callname(β*C + A*B)(C::Number, β::Number, A::Number, B::Number)
     β*C + A*B
end

dan panggilan

@callexpr(A .= 2*C + A*B)
@callexpr(2*3 + 3*2)

dan tidak ada yang perlu khawatir (atau tahu) bagaimana callexpr mengubah operasi aljabar menjadi nama fungsi yang unik (yang tidak bergantung pada simbol argumen, hanya pada operasi dan urutan operasi.)
Saya berpikir sedikit tentang implementasinya dan itu harus dilakukan dengan baik.

@mschauer Saya pikir itu arah yang menarik. Bisakah Anda membuka terbitan baru? API yang Anda usulkan dapat menyelesaikan banyak masalah lainnya. Saya pikir itu perlu melalui proses desain yang cermat daripada menyelesaikan satu contoh dari masalah yang dapat dipecahkannya.

Jadi saya pernah mendengar desas-desus bahwa pembekuan fitur 1.1 minggu depan. Meskipun rilis minor berikutnya "hanya" empat bulan lagi, akan sangat menyenangkan jika kami dapat memilikinya di 1.1 ...

Bagaimanapun, kita juga perlu memutuskan tanda panggil (urutan argumen dan kata kunci atau tidak) sebelum menggabungkan PR.

Jadi mari kita lakukan pemungutan suara lagi (karena saya telah menemukan bahwa ini adalah stimulan yang bagus).

_Jika_ kami menggunakan muladd! untuk _C = ABα + Cβ_, apa tanda panggilan favorit Anda?

  • : +1: muladd!(C, A, B, α, β)
  • : -1: muladd!(A, B, C, α, β)
  • : smile: muladd!(C, A, B; α, β) (seperti: +1 :, tetapi dengan kata kunci argumentbs)
  • : tada: muladd!(A, B, C; α, β) (seperti: -1 :, tetapi dengan argumen kata kunci)
  • : bingung: muladd!(A, B, α, C, β)
  • : hati: sesuatu yang lain

Jika Anda memiliki nama argumen kata kunci lain, pilih yang menggunakan α dan β dan kemudian komentari nama apa yang lebih baik.

Karena kami belum memutuskan apa namanya, kami perlu melakukannya dengan mul! juga:

_Jika_ kami menggunakan mul! untuk _C = ABα + Cβ_, apa tanda panggilan favorit Anda?

  • : +1: mul!(C, A, B, α, β)
  • : -1: mul!(A, B, C, α, β)
  • : smile: mul!(C, A, B; α, β) (seperti: +1 :, tetapi dengan kata kunci argumentbs)
  • : tada: mul!(A, B, C; α, β) (ini tidak mungkin)
  • : bingung: mul!(A, B, α, C, β)
  • : hati: sesuatu yang lain

CATATAN: Kami tidak mengubah API mul!(C, A, B)

CATATAN: Kami tidak mengubah API mul!(C, A, B)

Saya kurang memperhatikan fakta ini — kami sudah memiliki mul! dan inilah artinya:

mul!(Y, A, B) -> Y

Menghitung matriks-matriks atau matriks-vektor produk A*B dan menyimpan hasilnya dalam Y , menimpa nilai yang ada dari Y . Perhatikan bahwa Y tidak boleh alias dengan A atau B .

Mengingat hal itu, tampaknya sangat wajar untuk mengembangkannya seperti ini:

mul!(Y, A, B) -> Y
mul!(Y, A, B, α) -> Y
mul!(Y, A, B, α, β) -> Y

Menghitung matriks-matriks atau matriks-vektor produk A*B dan menyimpan hasilnya dalam Y , menimpa nilai yang ada dari Y . Perhatikan bahwa Y tidak boleh alias dengan A atau B . Jika nilai skalar, α , diberikan, maka α*A*B dihitung dan bukan A*B . Jika nilai skalar, β disediakan, maka α*A*B + β*Y dihitung sebagai gantinya. Pembatasan aliasing yang sama berlaku untuk varian ini.

Namun, saya pikir ada masalah besar dengan ini: tampaknya paling tidak wajar untuk mul!(Y, A, B, C, D) untuk menghitung A*B*C*D di tempatkan menjadi Y —dan gagasan umum itu sangat berbenturan dengan mul!(Y, A, B, α, β) komputasi α*A*B + β*C . Selain itu, menurut saya menghitung A*B*C*D menjadi Y adalah hal yang berguna dan mungkin untuk dilakukan secara efisien, menghindari alokasi perantara, jadi saya benar-benar tidak ingin memblokir makna itu .

Dengan generalisasi alami lainnya dari mul! , inilah pemikiran lain:

mul!(Y, α, A, B) # Y .= α*A*B

Ini cocok dengan model umum mul!(out, args...) mana Anda menghitung dan menulis menjadi out dengan mengalikan args . Ini bergantung pada pengiriman untuk menangani α menjadi skalar alih-alih menjadikannya kasus khusus — itu hanya hal lain yang Anda gandakan. Ketika α adalah skalar dan A , B dan Y adalah matriks, kami dapat mengirimkan ke BLAS untuk melakukan ini dengan sangat efisien. Jika tidak, kita bisa memiliki implementasi generik.

Selain itu, jika Anda berada di bidang non-komutatif (misalnya angka empat), maka Anda dapat mengontrol sisi mana penskalaan dengan α terjadi pada: mul!(Y, A, B, α) skala dengan α pada kanan bukan kiri:

mul!(Y, A, B, α) # Y .= A*B*α

Ya, kami tidak dapat memanggil BLAS untuk angka empat, tetapi ini umum dan kami mungkin masih dapat melakukannya dengan cukup efisien (bahkan mungkin mengubahnya menjadi beberapa panggilan BLAS).

Dengan asumsi pendekatan itu untuk Y .= α*A*B pertanyaan berikutnya menjadi: bagaimana dengan penskalaan dan peningkatan Y ? Saya mulai memikirkan kata kunci untuk itu, tetapi kemudian muncul bidang non-komutatif yang terasa terlalu canggung dan terbatas. Jadi, saya mulai memikirkan API ini — yang pada awalnya tampak agak aneh, tetapi bersabarlah:

mul!((β, Y), α, A, B) # Y .= β*Y .+ α*A*B

Agak aneh, tapi berhasil. Dan di bidang non-komutatif, Anda dapat meminta untuk mengalikan Y dengan β di sebelah kanan seperti ini:

mul!((Y, β), α, A, B) # Y .= Y*β .+ α*A*B

Secara umum penuh di bidang non-komutatif, Anda bisa menskalakan di kiri dan kanan seperti ini:

mul!((β₁, Y, β₂), α₁, A, B, α₂) # Y .= β₁*Y*β₂ + α₁*A*B*α₂

Sekarang, tentu saja, ini sedikit aneh dan tidak ada operasi BLAS untuk ini, tetapi ini adalah generalisasi GEMM yang memungkinkan kami mengekspresikan _ banyak_ hal dan yang dapat kami kirim ke operasi BLAS secara sepele tanpa melakukan tindakan buruk jika / lain ranting.

Saya sangat menyukai saran @StefanKarpinski sebagai panggilan API yang mendasarinya, tetapi saya juga memikirkan apakah ini cara yang sebenarnya kami ingin tampilkan kepada pengguna. IMO, pada akhirnya akan terlihat sederhana, seperti makro terkait:

@affine! Y = β₁*Y*β₂ + α₁*A*B*α₂

Fungsi yang mendasarinya kemudian akan menjadi sesuatu seperti yang diusulkan @StefanKarpinski .

Tapi kita harus melangkah lebih jauh di sini. Saya benar-benar berpikir, jika Anda membuat API untuk itu dan fungsi generik, seseorang akan membuat perpustakaan Julia yang melakukannya secara efisien, jadi saya setuju bahwa kita tidak boleh hanya menggunakan BLAS di sini. Hal-hal seperti MatrixChainMultiply.jl sudah membangun DSL untuk beberapa komputasi matriks dan DiffEq melakukan hal sendiri dengan ekspresi operator affine. Jika kita hanya memiliki satu representasi untuk ekspresi affine di Base, kita dapat mendefinisikan semua pekerjaan kita pada hal yang sama.

@dlfivefifty melihat ke aljabar linier malas sebelumnya, saya pikir itu harus benar-benar dihidupkan kembali di sini. Membangun representasi siaran yang malas sangat penting untuk mendapatkan operasi yang bijak dari elemen yang bekerja pada array abstrak dan perangkat keras komputasi alternatif. Kita membutuhkan hal yang sama untuk aljabar linier. Representasi ekspresi aljabar linier akan memungkinkan kita untuk menentukan kernel BLAS baru dengan cepat dari Julia BLAS atau mentransfer persamaan tersebut ke GPU / TPU.

Pada dasarnya semua komputasi dalam komputasi ilmiah bermuara pada operasi aljabar linier dan bijaksana elemen, sehingga memiliki deskripsi tingkat tinggi dari keduanya tampak penting untuk membangun perkakas untuk metaprogram dan mengeksplorasi desain baru.

Saya harus memikirkan lebih banyak tentang proposal ini tetapi, untuk saat ini, saya hanya akan berkomentar bahwa menurut saya Anda tidak ingin menghitung A*B*C tanpa sementara. Menurut saya, Anda harus membayar dengan banyak operasi aritmatika untuk menghindari operasi sementara.

Saya tidak berpikir Anda ingin menghitung A*B*C tanpa sementara.

Untuk mul! Anda sudah memiliki larik keluaran. Saya tidak yakin apakah itu membantu atau tidak. Bagaimanapun, ini tampak seperti detail implementasi. API mul!(Y, A, B, C...) mengekspresikan apa yang ingin Anda hitung dan memungkinkan penerapan memilih cara terbaik untuk melakukannya, yang merupakan tujuan umum di sini.

Saya sangat menyukai saran @StefanKarpinski sebagai panggilan API yang mendasarinya, tetapi saya juga memikirkan apakah ini cara yang sebenarnya kami ingin tampilkan kepada pengguna.

@ChrisRackauckas : Saya pikir hal-hal yang Anda hadapi dapat dan harus dieksplorasi dalam paket eksternal — kemalasan, menulis perhitungan yang Anda inginkan dan membiarkan beberapa jenis pengoptimalan lulus memilih bagian yang cocok dengan pola aljabar tertentu yang tahu cara dioptimalkan, dll. Menggunakan mul! seperti ini sepertinya hanya jenis operasi umum tetapi mudah dipahami yang kami inginkan pada level ini.

Perhatikan bahwa tidak ada perdebatan nyata yang bisa didapat tentang mul!(Y, α, A, B) — hampir pasti berarti Y .= α*A*B karena apa lagi artinya? Jadi bagi saya satu-satunya pertanyaan terbuka di sini adalah apakah menggunakan tupel dengan matriks dan skalar kiri dan / atau kanan adalah cara yang masuk akal untuk menyatakan bahwa kita ingin menaikkan dan menskalakan larik keluaran. Kasus umumnya adalah:

  1. mul!(Y::Matrx, args...) : Y .= *(args...)
  2. mul!((β, Y)::{Number, Matrix}, args...) : Y .= β*Y + *(args...)
  3. mul!((Y, β)::{Matrix, Number}, args...) : Y .= Y*β + *(args...)
  4. mul!((β₁, Y, β₂)::{Number, Matrix, Number}, args...) : Y .= β₁*Y*β₂ + *(args...)

Tidak ada lagi yang diizinkan untuk argumen pertama. Ini dapat diadopsi sebagai konvensi yang lebih umum untuk operasi lain di mana masuk akal untuk menimpa atau mengakumulasi ke dalam larik keluaran, secara opsional dikombinasikan dengan penskalaan.

Tidak terpikir oleh saya untuk "menggabungkan" mul!(out, args...) dan antarmuka seperti GEMM! Saya suka ekstensibilitasnya (tapi kemudian mulai menulis balasan di bawah dan sekarang saya tidak yakin ...)

Tapi kekhawatiran saya adalah jika mudah digunakan sebagai antarmuka yang kelebihan beban. Kita perlu mengandalkan sistem tipe agar bekerja dengan baik untuk tupel bersarang. Apakah tupel bersarang berfungsi sebaik tupel datar dalam sistem tipe Julia? Saya bertanya-tanya apakah sesuatu seperti " Tuple{Tuple{A1,B1},C1,D1} lebih spesifik daripada Tuple{Tuple{A2,B2},C2,D2} iff Tuple{A1,B1,C1,D1} lebih spesifik daripada Tuple{A2,B2,C2,D2} ". Jika tidak, akan sulit untuk digunakan sebagai API yang kelebihan muatan.

Perhatikan bahwa kita perlu mengirimkan jenis skalar untuk menggunakan peretasan interpretasi ulang untuk matriks kompleks (ini dari PR # 29634 jadi jangan perhatikan nama fungsinya):

https://github.com/JuliaLang/julia/blob/fae1a7a3ae646c7ea1c08982976b57096fb0ae8d/stdlib/LinearAlgebra/src/matmul.jl#L157 -L169

Kekhawatiran lain adalah bahwa ini adalah antarmuka yang agak terbatas untuk pelaksana grafik komputasi. Saya pikir tujuan utama dari multiply-add interface adalah untuk menyediakan API yang berlebihan untuk memungkinkan pelaksana perpustakaan menentukan kernel komputasi kecil yang dapat digunakan kembali yang dapat diimplementasikan secara efisien. Artinya, kami hanya dapat mengimplementasikan _C = ABα_ dan tidak, misalnya, _αAB_ (lihat https://github.com/JuliaLang/julia/pull/29634#issuecomment-443103667). Mendukung _α₁ABα₂_ untuk eltype non-komutatif membutuhkan larik sementara atau menambah jumlah operasi aritmatika. Tidak jelas yang mana yang diinginkan pengguna dan idealnya ini harus dapat dikonfigurasi. Pada titik ini kita membutuhkan representasi grafik komputasi yang terpisah dari mekanisme eksekusi. Saya pikir ini lebih baik dieksplorasi dalam paket eksternal (misalnya, LazyArrays.jl, MappedArrays.jl). Namun, jika kita dapat menemukan strategi implementasi yang mencakup sebagian besar kasus penggunaan di beberapa titik, menggunakan mul! sebagai titik masuk utama akan masuk akal. Saya pikir ini sebenarnya adalah alasan lain untuk memilih muladd! ; mengalokasikan ruang untuk API pemanggil di masa mendatang.

Saya harus lebih memikirkan proposal ini, tetapi untuk saat ini, saya hanya akan berkomentar bahwa saya rasa Anda tidak ingin menghitung A B C tanpa sementara. Menurut saya, Anda harus membayar dengan banyak operasi aritmatika untuk menghindari operasi sementara.

Anda memang dapat membuktikan bahwa setiap kontraksi sejumlah tensor yang berubah-ubah, cara paling efisien untuk mengevaluasi semuanya adalah selalu menggunakan kontraksi berpasangan. Jadi mengalikan beberapa matriks hanyalah kasus khusus dari itu, Anda harus mengalikannya secara berpasangan (urutan terbaik tentu saja adalah soal yang tidak sepele). Itulah mengapa saya pikir mul!(Y,X1,X2,X3...) bukanlah primitif yang berguna. Dan pada akhirnya, itulah yang menurut saya mul! , ini adalah operasi primitif yang dapat kelebihan beban pengembang untuk tipe spesifik mereka. Operasi yang lebih rumit kemudian dapat ditulis menggunakan konstruksi tingkat yang lebih tinggi, misalnya menggunakan makro, dan misalnya akan membangun grafik komputasi, yang pada akhirnya dievaluasi dengan memanggil operasi primitif seperti mul! . Tentu saja, kasus primitif itu bisa jadi cukup umum untuk menyertakan kasus-kasus seperti kasus non-komutatif yang disebutkan @StefanKarpinski .

Selama tidak ada perkalian matriks / kontraksi tensor yang terlibat, memang benar bahwa berpikir dalam istilah operasi primitif tidak begitu berguna dan dapat bermanfaat untuk menggabungkan semuanya seperti halnya penyiaran.

Secara umum, saya setuju bahwa akan lebih baik untuk memiliki tipe grafik representasi / komputasi malas default di Base, tetapi menurut saya mul! adalah cara untuk membangunnya.

@tp :

Tapi kekhawatiran saya adalah jika mudah digunakan sebagai antarmuka yang kelebihan beban. Kita perlu mengandalkan sistem tipe agar bekerja dengan baik untuk tupel bersarang. Apakah tupel bersarang berfungsi sebaik tupel datar dalam sistem tipe Julia?

Ya, kita semua baik-baik saja di depan itu. Saya tidak yakin dari mana masuknya nesting, tetapi meneruskan beberapa hal dalam tupel sama efisiennya dengan meneruskan semuanya sebagai argumen langsung — ini diterapkan dengan cara yang persis sama.

Artinya kita hanya dapat mengimplementasikan _C = ABα_ dan tidak, misalnya, _αAB_

Saya bingung ... Anda bisa menulis mul!(C, A, B, α) dan mul!(C, α, A, B) . Anda bahkan dapat menulis mul!(C, α₁, A, α₂, B, α₃) . Ini sepertinya API perkalian matriks generik paling fleksibel yang telah diusulkan sejauh ini.

Kekhawatiran lain adalah bahwa ini adalah antarmuka yang agak terbatas untuk pelaksana grafik komputasi. Saya pikir tujuan utama dari multiply-add interface adalah untuk menyediakan API yang berlebihan untuk memungkinkan pelaksana perpustakaan menentukan kernel komputasi kecil yang dapat digunakan kembali yang dapat diimplementasikan secara efisien.

Pada titik ini kita membutuhkan representasi grafik komputasi yang terpisah dari mekanisme eksekusi.

Mungkin itu masalahnya, tetapi ini bukan tempatnya — yang dapat dan harus dikembangkan dalam paket eksternal. Semua yang kita butuhkan untuk menyelesaikan masalah khusus ini adalah API perkalian matriks yang menggeneralisasi apa yang dapat dikirim ke operasi BLAS — yang hampir persis seperti yang dilakukannya.

@Tokopedia

Jadi mengalikan beberapa matriks hanyalah kasus khusus dari itu, Anda harus mengalikannya secara berpasangan (urutan terbaik tentu saja adalah soal yang tidak sepele). Itulah mengapa saya pikir mul!(Y,X1,X2,X3...) bukanlah primitif yang berguna.

Operasi mul! akan memungkinkan implementasi untuk memilih urutan perkalian, yang merupakan properti yang berguna. Memang, kemampuan untuk berpotensi melakukan itu adalah mengapa kami membuat parse operasi * sebagai n-ary di tempat pertama dan alasan yang sama berlaku bahkan lebih untuk mul! karena jika Anda menggunakannya , Anda mungkin cukup peduli dengan kinerja.

Secara umum, saya tidak tahu apakah Anda memperdebatkan atau menentang proposal saya untuk mul! .

Saya tidak yakin di mana masuknya nesting tetapi meneruskan beberapa hal dalam tupel sama efisiennya dengan meneruskan semuanya sebagai argumen langsung

Saya tidak khawatir tentang efisiensi melainkan pengiriman dan metode ambigu karena bahkan LinearAljabar saat ini agak rapuh (yang mungkin karena kurangnya pemahaman saya tentang sistem tipe; kadang-kadang mengejutkan saya). Saya menyebutkan tupel bersarang karena saya pikir resolusi metode dilakukan dengan memotong jenis tupel dari semua argumen posisi. Itu memberi Anda tupel datar. Jika Anda menggunakan tupel dalam argumen pertama, Anda memiliki tupel bersarang.

Artinya kita hanya dapat mengimplementasikan _C = ABα_ dan tidak, misalnya, _αAB_

Saya bingung ... Anda bisa menulis mul!(C, A, B, α) dan mul!(C, α, A, B) .

Maksud saya, "kami hanya dapat mengimplementasikan _C = ABα_ seefisien _C = AB_ dan kandidat lain seperti _αAB_ tidak dapat diterapkan secara efisien di semua kombinasi jenis matriks." (Dengan efisiensi, maksud saya kompleksitas waktu O besar.) Saya tidak terlalu yakin bahwa ini benar-benar masalahnya, tetapi setidaknya untuk matriks renggang, dua opsi lain sudah keluar.

Pada titik ini kita membutuhkan representasi grafik komputasi yang terpisah dari mekanisme eksekusi.

Mungkin itu masalahnya, tetapi ini bukan tempatnya — yang dapat dan harus dikembangkan dalam paket eksternal.

Itulah maksud saya. Saya menyarankan untuk melihat API ini sebagai blok bangunan minimal untuk penggunaan seperti itu (tentu saja, itu bukan tujuan keseluruhan). Implementasi dan desain vararg mul! dapat dilakukan setelah orang menjelajahi ruang desain dalam paket eksternal.

Pengiriman jenis bahkan untuk mul! ini sudah "rusak": ada pertumbuhan kombinatorial dalam ambiguitas menimpa yang diperlukan untuk bekerja dengan jenis array yang dapat disusun seperti SubArray dan Adjoint .

Solusinya adalah dengan menggunakan sifat, dan LazyArrays.jl memiliki bukti versi konsep mul! dengan sifat.

Tapi ini lebih merupakan diskusi tentang implementasi daripada API. Tetapi menggunakan tupel untuk mengelompokkan istilah terasa salah: bukankah ini untuk apa sistem tipe ada? Dalam hal ini Anda sampai pada solusi LazyArrays.jl.

Mul! operasi akan memungkinkan implementasi untuk memilih urutan perkalian, yang merupakan properti yang berguna. Memang, kemampuan untuk berpotensi melakukan itu adalah mengapa kami membuat * operasi parse sebagai n-ary di tempat pertama dan alasan yang sama berlaku bahkan lebih untuk mul! karena jika Anda menggunakannya, Anda mungkin cukup peduli dengan kinerja.

* mem-parsing sebagai n -ary sangat berguna. Saya menggunakannya di TensorOperations.jl untuk mengimplementasikan makro @tensoropt , yang memang mengoptimalkan urutan kontraksi. Bahwa saya menemukan versi n -ary dari mul! kurang berguna adalah karena ada gunanya, dari perspektif efisiensi, dalam menyediakan tempat yang telah dialokasikan sebelumnya untuk meletakkan hasilnya, jika semua menengah array masih harus dialokasikan di dalam fungsi dan kemudian di-gc'ed. Faktanya, di TensorOperations.jl, beberapa orang memperhatikan bahwa alokasi temporaries yang besar adalah salah satu tempat di mana gc Julia berkinerja sangat buruk (cukup sering mengarah ke waktu gc 50%).

Oleh karena itu, saya akan membatasi mul! untuk apa yang benar-benar merupakan operasi primitif, seperti yang juga dianjurkan oleh @tkf jika saya mengerti dengan benar: mengalikan dua matriks menjadi yang ketiga, dengan kemungkinan koefisien skalar. Ya, kita dapat memikirkan cara paling umum untuk melakukan ini untuk aljabar non-komutatif, tetapi untuk saat ini saya pikir kebutuhan mendesak adalah akses mudah ke fungsionalitas yang disediakan oleh BLAS (gemm, gemv, ...) bahwa Julia mul! wrapper kurang. Saya tidak keberatan memikirkan tentang API terbaik untuk memastikan itu adalah bukti masa depan untuk memang mendukung operasi primitif ini untuk tipe angka yang lebih umum.

Saya tidak menyukai proposal Anda dengan tupel, tetapi dapat meramalkan potensi kebingungan
Membatasi dari kasus 4 ke kasus 2 dari 3 tampaknya menyiratkan nilai default β₁ = 1 dan β₂ = 1 (atau sebenarnya true ). Tapi kemudian, jika tidak ada yang ditentukan, tiba-tiba berarti β₁ = β₂ = 0 ( false ). Tentu, sintaksnya sedikit berbeda, karena Anda menulis mul!(Y, args...) , bukan mul!((Y,), args...) . Pada akhirnya, ini masalah dokumentasi, jadi saya hanya ingin menunjukkannya.

Singkatnya, tidak, saya tidak terlalu menentang sintaks ini, meskipun ini adalah jenis paradigma baru yang sedang diperkenalkan, dan mungkin juga harus diikuti di tempat lain. Apa yang saya lawan adalah segera ingin menggeneralisasi ini ke perkalian sejumlah matriks, yang, seperti yang dikemukakan di atas, saya tidak melihat manfaatnya.

@dlfivefifty : Tapi ini lebih merupakan diskusi tentang implementasi daripada API. Tetapi menggunakan tupel untuk mengelompokkan istilah terasa salah: bukankah ini untuk apa sistem tipe ada? Dalam hal ini Anda sampai pada solusi LazyArrays.jl.

Tapi kita tidak akan menggunakan lazy array di sini — sudah ada LazyArrays untuk itu. Sementara itu, kami membutuhkan cara untuk mengekspresikan penskalaan Y . Menggunakan tupel tampak seperti pendekatan yang sederhana dan ringan untuk mengekspresikan struktur dalam jumlah kecil. Apakah ada yang punya saran lain? Kami dapat memiliki lscale dan / atau rscale kata kunci untuk β₁ dan β₂ , tetapi itu tidak terasa lebih elegan dan kami akan kehilangan kemampuan untuk mengirimkan itu, yang tidak penting tetapi menyenangkan untuk dimiliki.

@ Jutho : Oleh karena itu, saya akan membatasi mul! untuk operasi yang benar-benar primitif, seperti yang juga dianjurkan oleh @tkf jika saya mengerti dengan benar: mengalikan dua matriks menjadi yang ketiga, dengan kemungkinan koefisien skalar. Ya, kita dapat memikirkan cara paling umum untuk melakukan ini untuk aljabar non-komutatif, tetapi untuk saat ini saya pikir kebutuhan mendesak adalah akses mudah ke fungsionalitas yang disediakan oleh BLAS (gemm, gemv, ...) bahwa Julia mul! wrapper kurang.

Saya baik-baik saja dengan hanya mendefinisikan sebagian kecil operasi untuk mul! , bahkan mungkin hanya yang sesuai secara struktural dengan panggilan BLAS yang valid. Itu akan menjadi:

# gemm: alpha = 1.0, beta = 0.0
mul!(Y::Matrix, A::Matrix, B::Matrix) # gemm! Y, A

# gemm: alpha = α, beta = 0.0 (these all do the same thing for BLAS types)
mul!(Y::Matrix, α::Number, A::Matrix, B::Matrix)
mul!(Y::Matrix, A::Matrix, α::Number, B::Matrix)
mul!(Y::Matrix, A::Matrix, B::Matrix, α::Number)

# gemm: alpha = α, beta = β (these all do the same thing for BLAS types)
mul!((β::Number, Y::Matrix), α::Number, A::Matrix, B::Matrix)
mul!((β::Number, Y::Matrix), A::Matrix, α::Number, B::Matrix)
mul!((β::Number, Y::Matrix), A::Matrix, B::Matrix, α::Number)
mul!((Y::Matrix, β::Number), α::Number, A::Matrix, B::Matrix)
mul!((Y::Matrix, β::Number), A::Matrix, α::Number, B::Matrix)
mul!((Y::Matrix, β::Number), A::Matrix, B::Matrix, α::Number)

# gemm: alpha = α, beta = β₁*β₂ (these all do the same thing for BLAS types)
mul!((β₁::Number, Y::Matrix, β₂::Number), α::Number, A::Matrix, B::Matrix)
mul!((β₁::Number, Y::Matrix, β₂::Number), A::Matrix, α::Number, B::Matrix)
mul!((β₁::Number, Y::Matrix, β₂::Number), A::Matrix, B::Matrix, α::Number)

Ke ujung Apa? Mengapa ada begitu banyak variasi dalam cara mengekspresikan operasi BLAS?

  1. Karena memungkinkan orang untuk mengekspresikan niat mereka — jika tujuannya adalah untuk berkembang biak di kiri atau kanan atau keduanya, mengapa tidak mengizinkan orang untuk mengekspresikannya dan memilih implementasi yang tepat?

  2. Kita bisa memiliki fallback umum yang melakukan hal yang benar bahkan untuk jenis elemen non-komutatif.

Inti dari masalah ini adalah memiliki generalisasi perkalian matriks di tempat yang memasukkan gemm! sementara lebih umum dari gemm !. Jika tidak, mengapa tidak terus menulis gemm! ?

Tapi kita tidak akan menjadi array malas penuh di sini

Saya tidak mengatakan "array malas penuh", saya menyarankan malas dalam arti yang sama seperti Broadcasted , yang pada akhirnya dihapus pada waktu kompilasi. Pada dasarnya saya akan menambahkan Applied untuk mewakili aplikasi malas dari suatu fungsi dan daripada menggunakan tupel (yang tidak mengandung konteks) Anda akan memiliki sesuatu seperti

materialize!(applied(+, applied(*, α, A, B), applied(*, β, C)))

Ini bisa dilapisi gula seperti notasi siaran .* agar lebih mudah dibaca, tapi saya pikir ini segera jelas apa yang diinginkan, tidak seperti proposal berbasis tupel.

@StefanKarpinski , seperti yang dikatakan sebelumnya, saya tentu setuju bahwa kita harus merenungkan tentang antarmuka yang merupakan bukti masa depan dan menggeneralisasi dengan benar ke jenis angka lain. Satu-satunya masalah, menurut saya, adalah daftar Anda tidak lengkap. Pada prinsipnya Anda dapat memiliki:

mul!((β₁::Number, Y::Matrix, β₂::Number), α₁::Number, A::Matrix, α₂::Number, B::Matrix, α₃::Number)

dan semua versi yang dikurangi darinya, yaitu jika semua dari 5 argumen skalar bisa tidak ada, itu berarti 2 ^ 5 = 32 kemungkinan yang berbeda. Dan ini dikombinasikan dengan semua kemungkinan matriks atau vektor yang berbeda.

Saya setuju dengan @dlfivefifty bahwa pendekatan seperti siaran lebih mungkin dilakukan.

Ya, saya menyadari bahwa saya meninggalkan beberapa opsi, tetapi 32 metode tampaknya tidak terlalu gila bagi saya, lagipula kita tidak perlu menulisnya dengan tangan. Menambahkan "sistem seperti penyiaran" atau sistem evaluasi malas yang memungkinkan kita menulis materialize!(applied(+, applied(*, α, A, B), applied(*, β, C))) sepertinya merupakan tambahan yang jauh lebih besar dan jalan keluar untuk masalah ini. Yang kami inginkan hanyalah cara mengeja perkalian matriks umum yang bersifat umum dan memungkinkan kami mengirimkannya ke BLAS. Jika kita tidak bisa semua menyetujui itu maka saya cenderung membiarkan orang terus menelepon gemm! secara langsung.

Ya, itu mungkin benar; Saya berasumsi bahwa akan lebih mudah dengan argumen skalar di belakang, untuk dengan mudah memberikan default. Tetapi jika dengan beberapa @eval metaprogramming kita dapat dengan mudah menghasilkan semua 32 definisi, itu sama baiknya. (Perhatikan, seperti yang Anda ketahui, mul tidak hanya gemm! tetapi juga gemv dan trmm dan ...).

Izinkan saya menambahkan bahwa ini bukan hanya pembungkus BLAS. Ada metode khusus murni-Julia lainnya di stdlib. Juga, penting untuk memiliki ini sebagai API yang membebani: pembuat paket dapat mendefinisikan mul! untuk tipe matriks khusus mereka.

Saya kira ini adalah pendirian saya:

  1. Kami mungkin juga mendukung mul!(C, A, B, a, b) sekarang karena sudah ada di SparseArrays.jl
  2. Kita tidak boleh melakukan apa pun karena pengiriman pada jenis matriks tidak berskala dengan baik. (Sebagai pengelola BandedMatrices.jl, BlockArrays.jl, LowRankApprox.jl, dll. Saya dapat menyatakannya dari pengalaman.)
  3. Trait based dirancang memang berskala dengan baik, tetapi akan lebih baik untuk masuk semua dan melakukan siaran seperti Applied , karena pola desain sudah ditetapkan. Ini harus menunggu hingga Julia 2.0, dengan prototipe yang terus dikembangkan di LazyArrays.jl yang sesuai dengan kebutuhan saya.

@dlfivefifty Menurut Anda, apakah kesulitan dalam disambiguasi mul!((Y, β), α, A, B) API sama dengan di mul!(Y, A, B, α, β) ? Mempertimbangkan pembungkus matriks seperti Transpose memperkenalkan kesulitan, termasuk 2- dan 3-tupel terdengar seperti meningkatkan kesulitan lebih banyak (meskipun saya tahu tuple adalah kasus khusus dalam sistem tipe Julia).

  1. Kami mungkin juga mendukung mul!(C, A, B, a, b) sekarang karena sudah ada di SparseArrays.jl

Fakta bahwa seseorang memutuskan bahwa mul!(C, A, B, a, b) seharusnya berarti C .= b*C + a*A*B tanpa memikirkannya sepenuhnya bukanlah alasan yang baik untuk menggandakan ini. Jika mul! adalah versi di tempat dari * maka saya tidak melihat bagaimana mul!(out, args...) dapat berarti apa pun selain out .= *(args...) . Sejujurnya, ini adalah bagaimana Anda berakhir dengan sistem yang berantakan dari API yang tidak dipikirkan dengan baik dan tidak konsisten yang hanya ada secara kebetulan. Fungsi mul! tidak diekspor dari SparseArrays _and_ metode tertentu itu tidak didokumentasikan, jadi ini benar-benar alasan yang paling mudah untuk memasukkan metode yang salah dipahami yang mungkin hanya ditambahkan karena fungsinya tidak t publik! Saya mengusulkan agar kita membatalkan kesalahan itu dan menghapus / mengganti nama metode mul! sebagai gantinya.

Dari sisa diskusi ini sepertinya kita tidak boleh melakukan hal lain karena semua pemangku kepentingan ingin melakukan sesuatu yang lebih menarik dengan sifat dan / atau kemalasan di luar perpustakaan standar. Saya baik-baik saja dengan itu karena menghapus sesuatu selalu menyenangkan.

Dari sisa diskusi ini sepertinya kita tidak boleh melakukan hal lain karena semua pemangku kepentingan ingin melakukan sesuatu yang lebih menarik dengan sifat dan / atau kemalasan di luar perpustakaan standar. Saya baik-baik saja dengan itu karena menghapus sesuatu selalu menyenangkan.

Sepertinya Anda agak muak, itu bisa dimengerti. Namun, menurut saya kesimpulan itu tidak benar. Jika Anda yakin bahwa saran saat ini dapat diimplementasikan dengan cara yang dapat diskalakan dan tetap memudahkan pengembang paket untuk membebani definisi tersebut untuk jenis matriks dan vektor mereka sendiri (seperti yang disebutkan oleh @tkf), itu akan menjadi cara yang bagus meneruskan.

Secara khusus, saya akan berpikir bahwa pengembang paket hanya perlu mengimplementasikan:

mul!((β₁, Y::MyVecOrMat, β₂), α₁, A::MyMat, α₂, B:: MyVecOrMat, α₃)

dan mungkin, misalnya,

mul!((β₁, Y::MyVecOrMat, β₂), α₁, A::Adjoint{<:MyMat}, α₂, B:: MyVecOrMat, α₃)
...

sementara Julia Base (atau lebih tepatnya, perpustakaan standar LinearAlgebra) menangani penanganan semua nilai default, dll.

Saya akan berpikir bahwa pengembang paket hanya perlu mengimplementasikan:

mul!((β₁, Y::MyVecOrMat, β₂), α₁, A::MyMat, α₂, B:: MyVecOrMat, α₃)

Saya sarankan untuk mendokumentasikan

mul!((Y, β), A, B, α)

sebagai tanda tangan yang akan dibebani. Ini karena lokasi lain untuk α mengubah kompleksitas waktu O besar. Lihat: https://github.com/JuliaLang/julia/pull/29634#issuecomment -443103667. Ini memberi bilangan non-komutatif perlakuan non-kelas satu. Tapi AFAICT tidak ada orang di sini yang benar-benar menggunakan nomor non-komutatif dan saya pikir kita harus menunggu sampai ada kebutuhan yang sebenarnya.

Satu hal yang saya suka tentang pendekatan @StefanKarpinski adalah kita dapat menerapkan metode khusus untuk mul!((Y, β), α::Diagonal, A, B) untuk jenis matriks _some_ A (misalnya, Adjoint{_,<:SparseMatrixCSC} ) tanpa mengubah kerumitan waktu . (Ini penting untuk aplikasi saya.) Tentu saja, menggunakan cara ini akan memerlukan diskusi lebih lanjut di API, terutama untuk cara menanyakan keberadaan metode khusus. Tetap saja, memiliki kesempatan untuk memperluas API itu bagus.

Jika seseorang menjelaskan kekhawatiran saya tentang ketidakjelasan metode, saya akan mendukung pendekatan grup-demi-tupel.

Ini karena lokasi lain untuk α mengubah kompleksitas waktu O yang besar.

Apakah ini matriks yang jarang secara spesifik? Saya tidak cukup berdebat, terutama untuk matriks yang padat. Dalam implementasi yang Anda tautkan, Anda menunjukkan kasus di mana α antara A dan B ?

Saya akan berpikir bahwa pengembang paket hanya perlu mengimplementasikan ...

Ini sangat disederhanakan. Misalkan kita memiliki matriks yang berperilaku seperti matriks langkah, seperti PseudoBlockMatrix dari BlockArrays.jl. Untuk sepenuhnya mendukung gemm! kita perlu mengganti setiap permutasi PseudoBlockMatrix dengan (1) itu sendiri, (2) StridedMatrix , (3) Adjoint s itu sendiri , (4) Transpose s itu sendiri, (5) Adjoint s dari StridedMatrix , (6) Transpose s dari StridedMatrix , dan mungkin lainnya. Ini sudah 6 ^ 3 = 216 kombinasi yang berbeda. Kemudian Anda ingin mendukung trmm! dan Anda harus melakukan hal yang sama dengan UpperTriangular , UnitUpperTriangular , adjointnya, transposenya, dan seterusnya. Kemudian gsmm! dengan Symmetric dan Hermitian .

Tetapi dalam banyak aplikasi kami tidak hanya ingin bekerja dengan matriks, tetapi juga subview mereka, terutama untuk matriks blok di mana kami ingin bekerja dengan blok. Sekarang kita perlu menambahkan setiap permuation view dari matriks kita bersama dengan 6 kombinasi di atas.

Sekarang kami memiliki ribuan penggantian, yang melibatkan StridedMatrix , yang merupakan jenis penyatuan yang sangat rumit. Ini terlalu banyak untuk kompilator, membuat waktu using membutuhkan waktu beberapa menit, bukan detik.

Pada titik ini seseorang menyadari bahwa mul! , dan ekstensi yang diusulkan dari mul! , cacat oleh desain dan pengembang paket tidak perlu repot-repot dengan itu. Untungnya LazyArrays.jl menyediakan solusi sementara menggunakan sifat.

Jadi saya agak setuju dengan @StefanKarpinski dengan membiarkan hal-hal apa adanya sampai desain ulang yang lebih substansial dikejar, karena menghabiskan upaya untuk sesuatu yang cacat oleh desain bukanlah penggunaan waktu yang baik bagi siapa pun.

@dlfivefifty , saya hanya mengacu pada bagaimana argumen skalar ditangani. Semua komplikasi dengan tipe matriks berbeda yang saat ini sudah ada untuk mul!(C,A,B) tentu saja akan tetap ada.

Ini karena lokasi lain untuk α mengubah kompleksitas waktu O yang besar.

Apakah ini matriks yang jarang secara spesifik? Saya tidak cukup berdebat, terutama untuk matriks yang padat. Dalam implementasi yang Anda tautkan, Anda menunjukkan kasus di mana α antara A dan B ?

@ Jutho Saya pikir secara umum Anda tidak dapat menempatkan α di posisi loop paling dalam. Misalnya, dalam hal ini Anda dapat mendukung α₁*A*B*α₃ tetapi tidak mendukung A*α₂*B

https://github.com/JuliaLang/julia/blob/11c5680d5620b0b64420055e8474a2b8cf757010/stdlib/LinearAlgebra/src/matmul.jl#L661 -L670

Saya pikir setidaknya α₁ atau α₂ di α₁*A*α₂*B*α₃ harus 1 untuk menghindari peningkatan kompleksitas waktu asimtotik.

@dlfivefifty Tapi bahkan LazyArrays.jl membutuhkan beberapa fungsi primitif untuk dikirim, bukan? Pemahaman saya adalah bahwa ini memecahkan "neraka pengiriman" tetapi tidak mengurangi jumlah "kernel" komputasi yang harus diterapkan orang.

Tidak, tidak ada kata "primitif" dengan cara yang sama Broadcasted tidak memiliki "primitif". Tetapi ya untuk saat ini, ini tidak menyelesaikan pertanyaan "kernel". Saya pikir langkah selanjutnya adalah mendesain ulang untuk menggunakan tipe malas Applied dengan ApplyStyle . Kemudian mungkin ada MulAddStyle untuk mengenali operasi mirip BLAS, dengan cara yang tidak menjadi masalah.

Saya akan menyebut materialize! atau copyto! primitif. Setidaknya itu adalah blok bangunan untuk mekanisme penyiaran. Demikian juga, saya kira LazyArrays.jl harus menurunkan representasi malasnya ke fungsi dengan loop atau ccall s ke perpustakaan eksternal di beberapa titik, bukan? Apakah buruk jika nama fungsi tersebut adalah mul! ?

Ini sangat disederhanakan. Misalkan kita memiliki matriks yang berperilaku seperti matriks langkah, seperti PseudoBlockMatrix dari BlockArrays.jl. Untuk sepenuhnya mendukung gemm! kita perlu mengganti setiap permutasi PseudoBlockMatrix dengan (1) itu sendiri, (2) StridedMatrix, (3) Adjoints itu sendiri, (4) Transposes sendiri, (5) Adjoints of StridedMatrix, (6) Transposes dari StridedMatrix, dan mungkin lainnya . Ini sudah 6 ^ 3 = 216 kombinasi yang berbeda. Maka Anda ingin mendukung trmm! dan Anda harus melakukan hal yang sama dengan UpperTriangular, UnitUpperTriangular, adjointsnya, transposenya, dan seterusnya. Lalu gsmm! dengan Symmetric dan Hermitian.
Tetapi dalam banyak aplikasi kami tidak hanya ingin bekerja dengan matriks, tetapi juga subview mereka, terutama untuk matriks blok di mana kami ingin bekerja dengan blok. Sekarang kita perlu menambahkan setiap permuation view dari matriks kita bersama dengan 6 kombinasi di atas.
Sekarang kami memiliki ribuan penggantian, yang melibatkan StridedMatrix, yang merupakan jenis penyatuan yang sangat rumit. Ini terlalu banyak untuk kompilator, sehingga waktu penggunaan membutuhkan waktu beberapa menit, bukan detik.

Saya tentu setuju bahwa penyatuan tipe StridedArray ini adalah cacat desain utama. Saya mendukung upaya Anda untuk memperbaikinya di beberapa titik.

Selama di Strided.jl, saya hanya mengimplementasikan mul! ketika semua matriks yang terlibat adalah tipe kustom saya sendiri (Abstract)StridedView , setiap kali ada campuran dalam tipe A, B dan C, saya biarkan Julia Base / LinearAlgebra menangani ini. Tentu saja, ini akan digunakan di lingkungan makro @strided , yang mencoba mengonversi semua kemungkinan jenis Basis menjadi jenis StridedView . Di sini, StridedView dapat mewakili subview, transposes dan adjoints, dan bentuk ulang tertentu, semua dengan tipe (parametrik) yang sama. Secara keseluruhan, kode perkalian lengkap sekitar 100 baris:
https://github.com/Jutho/Strided.jl/blob/master/src/abstractstridedview.jl#L46 -L147
Pengganti Julia asli jika BLAS tidak berlaku diimplementasikan menggunakan fungsionalitas mapreducedim! lebih umum yang disediakan oleh paket itu, dan tidak kurang efisiennya daripada yang ada di LinearAlgebra ; tetapi juga multithread.

Saya pikir setidaknya α₁ atau α₂ dalam α₁*A*α₂*B*α₃ harus 1 untuk menghindari peningkatan kompleksitas waktu asimtotik.

@ tkf , saya akan berasumsi bahwa jika koefisien skalar ini mengambil nilai default one(T) , atau lebih baik lagi, true , propagasi konstan dan pengoptimalan kompiler akan secara otomatis menghilangkan perkalian itu. di loop paling dalam saat itu adalah no-op. Jadi, akan lebih mudah jika kita hanya perlu mendefinisikan bentuk yang paling umum.

Saya tidak yakin apakah kita dapat mengandalkan propagasi konstan untuk menghilangkan semua perkalian dengan 1 ( true ). Misalnya, eltype mungkin Matrix . Dalam hal ini, saya pikir true * x (di mana x::Matrix ) harus membuat salinan yang dialokasikan untuk tumpukan x . Bisakah Julia melakukan sihir untuk menghilangkan itu?

@Jutho Menurut saya, tolok ukur ini menunjukkan bahwa Julia tidak dapat menghilangkan perkalian antara dalam beberapa kasus:

function simplemul!((β₁, Y, β₂), α₁, A, α₂, B, α₃)
    <strong i="7">@assert</strong> size(Y, 1) == size(A, 1)
    <strong i="8">@assert</strong> size(Y, 2) == size(B, 2)
    <strong i="9">@assert</strong> size(A, 2) == size(B, 1)
    <strong i="10">@inbounds</strong> for i in 1:size(A, 1), j = 1:size(B, 2)
        acc = zero(α₁ * A[i, 1] * α₂ * B[1, j] * α₃ +
                   α₁ * A[i, 1] * α₂ * B[1, j] * α₃)
        for k = 1:size(A, 2)
            acc += A[i, k] * α₂ * B[k, j]
        end
        Y[i, j] = α₁ * acc * α₃ + β₁ * Y[i, j] * β₂
    end
    return Y
end

function simplemul!((Y, β), A, B, α)
    <strong i="11">@assert</strong> size(Y, 1) == size(A, 1)
    <strong i="12">@assert</strong> size(Y, 2) == size(B, 2)
    <strong i="13">@assert</strong> size(A, 2) == size(B, 1)
    <strong i="14">@inbounds</strong> for i in 1:size(A, 1), j = 1:size(B, 2)
        acc = zero(A[i, 1] * B[1, j] * α +
                   A[i, 1] * B[1, j] * α)
        for k = 1:size(A, 2)
            acc += A[i, k] * B[k, j]
        end
        Y[i, j] = acc * α + Y[i, j] * β
    end
    return Y
end

fullmul!(Y, A, B) = simplemul!((false, Y, false), true, A, true, B, true)
minmul!(Y, A, B) = simplemul!((Y, false), A, B, true)

using LinearAlgebra
k = 50
n = 50
A = [randn(k, k) for _ in 1:n, _ in 1:n]
B = [randn(k, k) for _ in 1:n]
Y = [zeros(k, k) for _ in 1:n]
<strong i="15">@assert</strong> mul!(copy(Y), A, B) == fullmul!(copy(Y), A, B) == minmul!(copy(Y), A, B)

using BenchmarkTools
<strong i="16">@btime</strong> mul!($Y, $A, $B)     # 63.845 ms (10400 allocations: 99.74 MiB)
<strong i="17">@btime</strong> fullmul!($Y, $A, $B) # 80.963 ms (16501 allocations: 158.24 MiB)
<strong i="18">@btime</strong> minmul!($Y, $A, $B)  # 64.017 ms (10901 allocations: 104.53 MiB)

Tolok ukur yang bagus. Saya juga telah memperhatikan bahwa itu memang tidak akan menghilangkan alokasi ini dengan beberapa percobaan serupa. Untuk kasus seperti itu, mungkin berguna untuk mendefinisikan tipe tujuan khusus One singleton yang hanya mendefinisikan *(::One, x::Any) = x dan *(x::Any, ::One) = x , dan tidak ada tipe pengguna yang diperlukan sekarang. Maka nilai default, setidaknya untuk α₂ , bisa menjadi One() .

Ah, ya, itu pintar! Saya pertama kali berpikir saya sekarang baik-baik saja dengan mendukung α₁ * A * α₂ * B * α₃ tetapi kemudian saya pikir saya menemukan masalah lain: Secara matematis ambigu apa yang harus kita lakukan ketika (katakanlah) A adalah matriks-matriks dan α₁ adalah matriks. Tidak akan menjadi masalah jika kami _never_ mendukung argumen non-skalar di posisi α . Namun, tidak mungkin menampilkan Y .= β₁*Y*β₂ + *(args...) sebagai model mental mul!((β₁, Y, β₂), args...) . Selain itu, akan sangat baik jika matriks diagonal dapat diubah menjadi α₁ atau α₂ karena terkadang dapat dihitung hampir "gratis" (dan penting dalam aplikasi). Menurut saya ada dua rute:

(1) Gunakan mul!((β₁, Y, β₂), α₁, A, α₂, B, α₃) tetapi ketika membebani metode ini, argumen α dan β harus menerima Diagonal . Sangat mudah untuk menentukan jalur panggilan sehingga kode pengguna akhir masih dapat memanggilnya melalui nilai skalar. Tetapi agar ini bekerja secara efisien, "O (1) version" dari Diagonal(fill(λ, n)) https://github.com/JuliaLang/julia/pull/30298#discussion_r239845163 harus diimplementasikan di LinearAlgebra. Perhatikan bahwa implementasi untuk skalar dan diagonal α tidak jauh berbeda; seringkali hanya menukar α dan α.diag[i] . Jadi menurut saya itu tidak terlalu membebani pembuat paket.

Ini memecahkan ambiguitas yang saya sebutkan di atas karena sekarang Anda dapat memanggil mul!(Y, α * I, A, B) ketika A adalah matriks-matriks dan α adalah matriks yang harus diperlakukan sebagai eltype dari A .

(2) Mungkinkah rute di atas (1) masih terlalu rumit? Jika demikian, gunakan muladd! sebagai gantinya untuk saat ini. Jika kami ingin mendukung mul!((β₁, Y, β₂), α₁, A, α₂, B, α₃) , bermigrasi ke sana sambil menjaga kompatibilitas ke belakang tidaklah sulit.

Pada titik ini saya harus bertanya-tanya apakah kita seharusnya tidak hanya memiliki matmul!(C, A, B, α, β) yang sangat terbatas dan terspesialisasi yang hanya didefinisikan untuk berfungsi untuk tanda tangan umum ini:

matmul!(
    C :: VecOrMatT,
    A :: Matrix{T},
    B :: VecOrMatT,
    α :: Union{Bool,T} = true,
    β :: Union{Bool,T} = false,
) where {
    T <: Number,
    VecOrMatT <: VecOrMat{T},
}

Juga, sungguh menakjubkan bahwa tanda tangan ini dapat ditulis dan digunakan.

Itu pada dasarnya adalah saran saya (2), bukan? (Saya menebak A :: Matrix{T} tidak secara harfiah berarti Core.Array{T,2} ; jika tidak, kurang lebih hanya gemm! )

Saya akan senang dengan itu sebagai solusi sementara, dan dapat mendukung sebagian dalam paket yang saya pertahankan ("sebagian" karena masalah sifat yang luar biasa), meskipun itu menambahkan nama lain ke dalam campuran: mul! , muladd! dan sekarang matmul! .

... Bukankah sudah waktunya bagi seseorang untuk memposting "kata triase ..." dan menyebutnya?

Juga, sungguh menakjubkan bahwa tanda tangan ini dapat ditulis dan digunakan.

Bukankah fakta bahwa Anda dapat dengan rapi mengirimkan tanda tangan ini dengan tepat argumen untuk hanya menjadikan ini metode mul! . Ini juga dapat dengan bersih tidak digunakan lagi jika solusi yang lebih umum muncul.

menjadikan ini metode mul!

Jika kita menggunakan mul!(C, A, B, α, β) tidak ada cara membuatnya untuk menggeneralisasikannya menjadi mul!((β₁, Y, β₂), α₁, A, α₂, B, α₃) dan sejenisnya tanpa merusak kompatibilitas. (Mungkin ini adalah "fitur" karena kita bebas dari diskusi ini selamanya: smile :).

Juga, izinkan saya mencatat bahwa jenis elemen pembatas ke Number _and_ menjaga kompatibilitas dengan 3-arg mul! (yang sudah mendukung jenis elemen non- Number ) akan memperkenalkan banyak hal. duplikasi.

Saya tidak tahu apa yang Anda harapkan dari mul!((β₁, Y, β₂), α₁, A, α₂, B, α₃) ... jadi menurut saya ini adalah fitur.

Menabrak. Saya sedih harus menggunakan fungsi BLAS di mana-mana untuk mendapatkan kinerja di tempat yang lebih baik.

@StefanKarpinski Bisakah Anda

Kita dapat berdiskusi meskipun saya tidak yakin apa hasil yang akan didapat tanpa beberapa orang linalg di telepon, yang biasanya tidak ada hari ini. Sejujurnya, saya menghabiskan cukup banyak waktu dan upaya untuk mencoba membuat diskusi ini menjadi semacam penyelesaian dan setiap gagasan tampaknya telah ditolak dengan tegas dengan satu atau lain cara, jadi saya baru saja memeriksa masalah ini di titik ini. Jika seseorang dapat membuat ringkasan masalah dan mengapa berbagai proposal tidak memadai, itu akan membantu untuk melakukan diskusi triase yang produktif. Kalau tidak, saya tidak berpikir kita akan bisa berbuat banyak.

Saya pikir kurangnya konsensus berarti ini bukan waktu yang tepat untuk memasukkan ini ke dalam StdLib.

Mengapa tidak hanya paket MatMul.jl yang mengimplementasikan salah satu saran yang dapat digunakan pengguna down? Saya tidak mengerti mengapa berada di StdLib begitu penting dalam praktiknya. Saya akan dengan senang hati mendukung ini dalam paket yang saya pertahankan.

Saya sedang berpikir hanya permata versi Julian yang bagus! dan gemv! mencocokkan apa yang sudah kita miliki di SparseArrays. Per @andreasnoack di atas:

Saya pikir kami sudah menetapkan mul!(C, A, B, α, β) dengan nilai default untuk α , β . Kami menggunakan versi ini di

julia / stdlib / SparseArrays / src / linalg.jl

Baris 32 sampai 50 di b8ca1a4

...
Saya pikir beberapa paket juga menggunakan formulir ini tetapi saya tidak ingat yang mana di atas kepala saya.

Saran itu ada 7 jempol dan tidak ada yang jempol. Mengapa kita tidak mengimplementasikan fungsi itu untuk vektor / matriks padat? Itu akan menjadi solusi sederhana yang mencakup kasus penggunaan paling umum, bukan?

BAIK. Jadi saya rasa bahkan tidak ada konsensus jika ada konsensus: sweat_smile:

_I_ berpikir bahwa hampir semua orang [*] menginginkan API ini dan ini hanya masalah nama fungsi dan tanda tangannya. Dibandingkan dengan _not_ memiliki API ini, saya pikir semua orang senang dengan opsi apa pun (katakanlah mul!((β₁, Y, β₂), α₁, A, α₂, B, α₃) , muladd!(C, A, B, α, β) , dan mul!(C, A, B, α, β) ). Kecuali jika seseorang dapat membuat argumen yang meyakinkan bahwa API tertentu jauh lebih buruk daripada _tidak_ memilikinya, saya akan senang dengan keputusan triase apa pun.

@StefanKarpinski Tapi jangan ragu untuk menghapus tag triage jika menurut Anda diskusi belum cukup terkonsolidasi.

[*] Oke, @dlfivefifty , saya rasa Anda ragu bahkan tentang 3-arg mul! . Tapi itu akan membutuhkan perubahan antarmuka 3-arg mul! dari awal jadi saya pikir itu jauh di luar cakupan diskusi ini (yang, saya telah menafsirkan sebagai tentang _menambahkan_ beberapa bentuk varian 5-arg). Saya pikir kita membutuhkan sesuatu yang bekerja "cukup" sampai LazyArays.jl matang.

Mengapa tidak hanya paket MatMul.jl yang mengimplementasikan salah satu saran yang dapat digunakan pengguna down?

@dlfivefifty Saya pikir memilikinya di LinearAlgebra.jl penting karena ini adalah fungsi antarmuka (API yang dapat dibebani). Selain itu, karena mul!(C::AbstractMatrix, A::AbstractVecOrMat, B::AbstractVecOrMat) diimplementasikan di LinearAlgebra.jl, kita tidak dapat mendefinisikan mul! dalam kaitannya dengan MatMul.muladd! . Tentu saja ada beberapa solusi, tetapi jauh lebih baik untuk menerapkan secara langsung, terutama mengingat itu "hanya" perlu memutuskan nama dan tanda tangan.

Mengapa kita tidak mengimplementasikan fungsi itu untuk vektor / matriks padat?

@chriscoey Sayangnya ini bukan satu-satunya favorit untuk semua orang: https://github.com/JuliaLang/julia/issues/23919#issuecomment -441717678. Berikut adalah ringkasan saya untuk pro dan kontra untuk ini dan opsi lainnya https://github.com/JuliaLang/julia/issues/23919#issuecomment -441865841. (Lihat juga komentar orang lain)

Dari triase: Ada rencana jangka panjang untuk memiliki API kontraksi tensor generik termasuk dukungan kompilator dan pemilihan untuk BLAS, tetapi untuk jangka menengah, pilih saja API apa saja yang memenuhi kebutuhan mendesak Anda. Jika cocok dengan BLAS, memilih nama BLAS sepertinya masuk akal.

@Keno , ada informasi yang dapat Anda bagikan tentang API kontraksi tensor generik dan dukungan compiler? Saya mungkin memiliki beberapa informasi menarik untuk dibagikan juga, meskipun tidak untuk umum (belum).

Tidak ada desain API yang dilakukan pada semua itu, hanya pengertian umum bahwa kita harus memilikinya. Saya sadar bahwa Anda telah mengerjakan beberapa hal ini, jadi akan lebih baik jika memiliki sesi desain pada saat yang tepat, tetapi saya rasa kita belum sampai di sana.

Jika cocok dengan BLAS, memilih nama BLAS sepertinya masuk akal.

Ini sepenuhnya bertentangan dengan apa yang telah kita lakukan sejauh ini untuk nama fungsi aljabar linier generik.

Apa rencana untuk kuat / lemah β == 0 dalam versi umum yang diusulkan BLAS.gemm!(α, A, B, β, C) ?

Jika kita menurunkan ke BLAS panggilan itu akan berperilaku seperti nol kuat, meskipun ini sekarang tidak konsisten dengan lmul! . Saya tidak dapat memikirkan solusi untuk ini selain jatuh kembali ke generic_muladd! jika β == 0 .

Apa rencana untuk kuat / lemah β == 0

Itu hanya dibahas sebentar seputar komentar saya di https://github.com/JuliaLang/julia/issues/23919#issuecomment -430139849 jadi triase mungkin tidak menjawab pertanyaan itu.

@Keno Meskipun belum ada desain API, apakah Anda membayangkan bahwa "API termasuk dukungan compiler dan pemilihan keluar untuk BLAS" akan didefinisikan sebagai mutasi atau akan tetap, seperti aljabar linier XLA, untuk membantu penyusun? Yaitu menurut Anda mul! dan / atau muladd! akan menjadi bagian dari API semacam itu?

ping @Keno untuk @andreasnoack 's pertanyaan pada https://github.com/JuliaLang/julia/issues/23919#issuecomment -475.534.454

Maaf, saya seharusnya kembali ke masalah ini (terutama saya meminta triase) tetapi saya tidak tahu tindakan apa selanjutnya yang akan diberikan keputusan dari triase.

Jika cocok dengan BLAS, memilih nama BLAS sepertinya masuk akal.

Seperti yang ditunjukkan @andreasnoack , kami tidak dapat menggunakan (katakanlah) gemm! karena kami ingin mendukung perkalian matriks-vektor, dll. Tapi saya rasa kita bisa mengabaikan keputusan triase ini (yang hanya mengatakan "Jika cocok dengan BLAS" ; itu tidak).

pilih saja API yang memenuhi kebutuhan mendesak Anda.

Jadi, saya kira kita bisa mengikuti arah ini. Saya pikir itu berarti melupakan API berbasis tuple yang disarankan oleh @StefanKarpinski dan "hanya" memilih salah satu dari mul! / muladd! / addmul! .

Kami seperti kembali ke diskusi awal. Tapi menurut saya bagus karena ada kendala untuk tidak membahas API lagi.

Ada ide bagaimana memilih nama dari mul! / muladd! / addmul! ?


@chriscoey Saya pikir lebih baik membahas API masa depan di tempat lain. Masalah ini sudah sangat panjang dan kami tidak akan dapat membuat kemajuan apa pun kecuali kami fokus pada solusi jangka menengah. Bagaimana dengan membuka masalah baru (atau utas wacana)?

Saya menyarankan satu putaran voting persetujuan dengan tenggat waktu 10 hari dari sekarang. Pemungutan suara persetujuan berarti: Setiap orang memberikan suara untuk semua opsi yang mereka anggap lebih disukai daripada melanjutkan diskusi. Orang-orang yang lebih memilih nama yang paling tidak disukai sekarang daripada diskusi lanjutan harus memilih ketiganya. Jika tidak ada opsi yang mendapat persetujuan luas, atau skema pemungutan suara itu sendiri gagal mendapatkan persetujuan luas, maka kita harus melanjutkan diskusi. Jika hampir ada hubungan antara opsi yang disetujui, @tkf akan memutuskan (hak istimewa penulis PR).

+1: Saya setuju dengan skema pemungutan suara ini dan telah memberikan suara persetujuan saya.
-1: Saya tidak setuju dengan skema pemungutan suara ini. Jika terlalu banyak atau terlalu orang penting memilih opsi ini, maka pemungutan suara bisa diperdebatkan.

Hati: mul! lebih disukai daripada diskusi lanjutan.
Roket: muladd! lebih disukai daripada diskusi lanjutan.
Hore: addmul! lebih disukai untuk melanjutkan diskusi.

Saya secara tentatif menyarankan bahwa 75% persetujuan dan 5 orang harus memenuhi kuorum (yaitu 75% orang yang telah memberikan suara sama sekali, termasuk ketidaksepakatan dengan seluruh prosedur pemungutan suara, dan setidaknya 5 orang telah menyetujui opsi menang; jika partisipasi rendah , maka 5/6 atau 6/8 buat kuorum tetapi dengan suara bulat 4/4 dapat dianggap gagal).

Pembekuan fitur untuk 1.3 sekitar tanggal 15 Agustus: https://discourse.julialang.org/t/release-1-3-branch-date-approaching-aug-15/27233?u=chriscoey. Saya harap kami dapat menggabungkannya saat itu 😃. Terima kasih untuk semua yang telah memilih!

Kami juga masih perlu memutuskan perilaku β == 0 https://github.com/JuliaLang/julia/issues/23919#issuecomment -475420149 yang ortogonal untuk menentukan nama. Juga, konflik penggabungan dalam PR saya harus diselesaikan dan PR memerlukan beberapa tinjauan pada detail implementasi (misalnya, pendekatan yang saya gunakan di sana untuk menangani undef s dalam larik tujuan). Kami mungkin menemukan masalah lain selama peninjauan. Jadi, saya tidak yakin apakah itu bisa masuk ke 1,3 ....

Re: β == 0 , saya pikir @andreasnoack 's komentar https://github.com/JuliaLang/julia/issues/23919#issuecomment -430.139.849 (ringkasan saya: β == 0 -handling harus BLAS -kompatibel untuk memanfaatkan BLAS sebanyak mungkin) masuk akal. Sulit untuk menemukan opini yang berlawanan selain argumen @simonbyrne tepat di bawah ("setiap implementasi akan membutuhkan cabang untuk memeriksa nol"). Apakah ada argumen lain yang menentang penanganan BLAS seperti β == 0 ?

@simonbyrne Mengenai komentar Anda https://github.com/JuliaLang/julia/issues/23919#issuecomment -430375349, menurut saya percabangan eksplisit bukan masalah besar karena sebagian besar hanya satu kali β != 0 ? rmul!(C, β) : fill!(C, zero(eltype(C))) . Selain itu, untuk implementasi yang sangat umum di mana Anda perlu menangani, misalnya, C = Matrix{Any}(undef, 2, 2) , implementasinya memerlukan penanganan "nol kuat" yang eksplisit (lihat fungsi pembantu _modify! di https PR saya: // github .com / JuliaLang / julia / pull / 29634 / files # diff-e5541a621163d78812e05b4ec9c33ef4R37). Jadi, menurut saya handling seperti BLAS adalah pilihan terbaik di sini. Bagaimana menurut anda?

Apakah mungkin memiliki angka nol lemah berkinerja tinggi? Dalam arti yang kami inginkan:

julia> A = [NaN 0;
                     1 0]

julia> b = [0.0,0];

julia> 0.0*A*b
2-element Array{Float64,1}:
 NaN  
   0.0

julia> false*A*b
2-element Array{Float64,1}:
 0.0
 0.0

Artinya, kita perlu secara manual menentukan baris mana yang dimaksudkan menjadi NaN jika kita menurunkan ke BLAS (yang menggunakan kuat 0).

@dlfivefifty Saya pikir BLAS dapat menangani NaN di A dan B , tetapi tidak di C ?

Saya kira tidak ada cara untuk melakukan NaN-aware _C = α * A * B + 0 * C_ secara efisien menggunakan BLAS.gemm! dll. [1] dan dari sanalah argumen @andreasnoack berasal.

[1] Anda perlu menghemat isnan.(C) suatu tempat dan "mencemari" C sesudahnya

@chethega sudah lebih dari 10 hari sejak pemungutan suara

@chriscoey Anda benar, pemungutan suara ditutup.

Saya terlalu buruk di github untuk mendapatkan daftar lengkap orang yang memilih (yang akan diperlukan untuk menghitung berapa banyak orang yang memberikan suara sama sekali). Namun, ketika saya mengamati angkanya, tampak cukup jelas bahwa mul! memiliki dukungan yang luar biasa (mungkin mengelola 75% kuorum persetujuan), dan pesaing kedua muladd! jauh di bawah 50%.

Tidak ada satu pun keberatan atas skema pemungutan suara tersebut. Saya menyebut pemungutan suara, mul! menang, dan nama sudah ditentukan. @tkf bisa terus membuatnya terbang :)

@chethega Terima kasih, ini adalah skema yang bagus untuk memutuskan ini!

BTW, saya tidak bisa langsung melakukan rebase (tapi mungkin dalam beberapa minggu) jadi jika seseorang ingin melakukan rebase atau reimplementation tolong jangan tunggu saya.

Sayangnya, kami tidak memiliki suara untuk semantik NaN. Pembekuan fitur akan dilakukan minggu depan, dan kami tidak punya cukup waktu untuk memberikan suara yang berarti.

Saya mengusulkan agar kita memiliki referendum yang tidak mengikat untuk mengumpulkan gambaran tentang suasana hati di utas.

Saya melihat opsi berikut:

  1. Lanjutkan berdiskusi, berharap konsensus tercapai sebelum tenggat waktu, atau orang-orang inti memberi kami perpanjangan, atau semacamnya. : tada: (edit untuk klarifikasi: Ini adalah opsi default, yaitu apa yang terjadi jika kita tidak dapat mencapai konsensus pada opsi yang berbeda. Hasil yang paling mungkin adalah 5-arg mul! ditunda hingga 1.4.)
  2. Gabungkan fitur baru, dengan perilaku NaN yang tidak ditentukan sebagai penghentian. Segera setelah kami mencapai konsensus, kami memperbarui kode dan / atau dokumen untuk mendapatkan perilaku NaN yang ditentukan (nol kuat vs nol). Penghenti dari perilaku NaN yang ditentukan implementasi yang tidak ditentukan dapat berakhir tanpa batas, tetapi itu tidak untuk pemungutan suara hari ini. (terapkan apa pun yang tercepat; pengguna yang peduli perlu memanggil metode yang berbeda). :jempolan:
  3. Gemm artinya Gemm! Gabungkan untuk 1.3 dengan komitmen terdokumentasi ke angka nol yang kuat. :jantung:
  4. NaN berarti NaN ! Gabungkan untuk 1,3 dengan komitmen terdokumentasi ke nol lemah. : mata:
  5. Lakukan sesuatu, apa saja, sebelum mencapai tebing 1,3. :roket:
  6. Tolak jajak pendapat. :jempol ke bawah:
  7. Tulis di
  8. Usulan downthread : Pengaturan backstop alternatif. Cobalah untuk menggabungkan dengan cepat, dan buat divergensi nol yang lemah / kuat menjadi kesalahan untuk 1,3-alfa, sampai kita dapat mencapai keputusan yang lebih dipertimbangkan nanti. Artinya, kami menggabungkan dengan !(alpha === false) && iszero(alpha) && !all(isfinite, C) && throw(ArgumentError()) , dan mendokumentasikan bahwa pemeriksaan kesalahan ini kemungkinan besar akan dihentikan demi sesuatu yang lain. :bingung:

Jangan ragu untuk memilih beberapa, opsi yang mungkin bertentangan, dan @tkf / triage jangan ragu untuk berpotensi mengabaikan jajak pendapat.

Sunting: Saat ini hanya: tada: (sabar) dan: roket: (ketidaksabaran) yang kontradiktif, tetapi keduanya kompatibel dengan yang lain. Seperti yang dijelaskan di bawah, triase diharapkan akan menghitung hasil jajak pendapat pada tanggal yang tidak ditentukan antara tanggal 14 Rabu dan 15 Kamis, dan mempertimbangkannya dengan cara yang tidak ditentukan. Ini sekali lagi dimaksudkan sebagai "pemungutan suara persetujuan", yaitu pilih semua opsi yang Anda suka, bukan hanya yang favorit Anda; Dapat dipahami bahwa: roket: tidak menolak: jempol :,: hati :,: mata: dan: bingung :

Saya akan memilih strong zero (: heart :) jika itu adalah "Gabung untuk 1.x" daripada "Gabung untuk 1.3". Bukankah opsi ini masuk akal jika semua "Gabung untuk 1.3" adalah "Gabung 1.x"?

Terima kasih @chethega. @tkf Saya dalam posisi benar-benar membutuhkan mul baru! SECEPATNYA tanpa terlalu peduli tentang keputusan NaN (selama kinerja tidak terganggu).

Apakah Anda sudah memeriksa LazyArrays.jl? FYI, ini memiliki dukungan perkalian matriks gabungan yang sangat bagus. Anda juga dapat menggunakan BLAS.gemm! dll dengan aman karena ini adalah metode publik https://docs.julialang.org/en/latest/stdlib/LinearAlgebra/#LinearAlgebra.BLAS.gemm!

Saya benar-benar membutuhkan mul generik! karena kami menggunakan berbagai macam matriks padat lama terstruktur dan jarang dan polos, untuk merepresentasikan masalah pengoptimalan yang berbeda dengan paling efisien. Saya di sini untuk kemurahan hati dan kecepatan.

Saya melihat. Dan saya baru ingat bahwa kita pernah membahas berbagai hal di LazyArrays.jl jadi tentunya Anda sudah mengetahuinya ...

Mengenai "ASAP", siklus rilis empat bulan Julia , setidaknya sebagai desain, untuk menghindari "merge rush" sebelum fitur dibekukan. Saya tahu tidak adil bagi saya untuk mengatakan ini karena saya telah mencoba hal yang sama sebelumnya ... Tapi saya pikir seseorang perlu menyebutkan ini sebagai pengingat. Sisi baiknya adalah Julia sangat mudah dibuat. Anda dapat mulai menggunakannya segera setelah digabungkan hingga rilis berikutnya.

Edit: lepaskan -> gabungkan

Terima kasih. Saya menemukan tenggat waktu untuk menjadi motivator yang membantu, dan saya ingin menghindari hal ini menjadi basi lagi. Jadi saya akan mengusulkan agar kita mencoba menggunakan tenggat waktu sebagai tujuan.

Sangat menyenangkan bahwa Anda secara aktif menyuntikkan energi ke utas ini!

Saya benar-benar membutuhkan mul generik! karena kami menggunakan berbagai macam matriks padat lama terstruktur dan jarang dan polos, untuk merepresentasikan masalah pengoptimalan yang berbeda dengan paling efisien. Saya di sini untuk kemurahan hati dan kecepatan.

Sebuah 5-argumen mul! tidak akan bekerja dengan baik ketika Anda memiliki banyak tipe yang berbeda: Anda akan membutuhkan banyak override secara kombinatorial untuk menghindari ambiguitas. Ini adalah salah satu motivasi di balik sistem LazyArrays.jl MemoryLayout . Ini digunakan untuk matriks "terstruktur dan jarang" tepatnya untuk alasan ini di BandedMatrices.jl dan BlockBandedMatrices.jl. (Di sini bahkan sub tampilan dari matriks berpita yang dikirim ke rutinitas BLAS terikat.)

Terima kasih, saya akan mencoba LazyArrays lagi.

Karena 5-arg mul tampaknya umumnya dianggap sebagai sementara sementara (sampai beberapa solusi seperti LazyArrays dapat digunakan di 2.0), saya pikir kita dapat bertujuan untuk menggabungkannya tanpa harus menjadi solusi ideal atau sempurna untuk jangka panjang.

@chethega, kapan menurut Anda kita harus menghitung penghitungan untuk suara tidak mengikat yang baru?

@tkf Tentu, Anda benar bahwa nol kuat / lemah / undef masuk akal untuk 1.x juga.

Namun, saya pikir ada beberapa orang yang lebih suka memiliki 1,3 mul! sekarang daripada menunggu sampai 1,4 untuk mendapatkan 5-arg mul! . Jika tidak ada tenggat waktu, maka saya akan menunggu lebih lama dan meluangkan lebih banyak waktu untuk memikirkan bagaimana melakukan pemungutan suara yang tepat (setidaknya 10 hari untuk pemungutan suara). Yang terpenting, kita tidak dapat memperoleh suara yang berarti tanpa terlebih dahulu menyajikan dan membandingkan implementasi yang bersaing pada kecepatan dan keanggunan dari angka nol yang lemah / kuat. Saya pribadi menduga bahwa angka nol yang lemah dapat dibuat hampir secepat nol kuat, dengan terlebih dahulu memeriksa iszero(alpha) , kemudian memindai matriks untuk nilai !isfinite , dan baru kemudian menggunakan jalur lambat dengan alokasi ekstra; tapi saya lebih suka semantik nol yang kuat.

@chethega, kapan menurut Anda kita harus menghitung penghitungan untuk suara tidak mengikat yang baru?

Triage harus membuat keputusan (delay / strong / weak / backstop) minggu ini untuk 1,3 alpha. Saya pikir Kamis tanggal 15 atau Rabu tanggal 14 adalah pilihan yang masuk akal untuk melakukan triase untuk dihitung, dan mempertimbangkannya. Saya mungkin tidak bisa bergabung pada hari Kamis, jadi orang lain harus menghitung.

Secara realistis, tidak apa-apa menjadi konservatif di sini, dan melewatkan tenggat waktu, dan melanjutkan diskusi dan menunggu 1.4.

Di sisi lain, kita mungkin sudah mencapai konsensus tanpa menyadarinya: @andreasnoack membuat beberapa argumen kuat bahwa koefisien nol harus menjadi nol yang kuat. Mungkin saja dia berhasil meyakinkan semua pendukung nol yang lemah. Mungkin ada sebagian besar yang benar-benar menginginkan 5-arg mul !, sebaiknya tahun lalu, dan tidak terlalu peduli dengan detail kecil ini. Jika itu masalahnya, maka akan sangat disayangkan untuk menunda fitur ini, hanya karena tidak ada yang mau menutup diskusi.

Mengapa tidak membuat kesalahan untuk saat ini:

β == 0.0 && any(isnan,C) && throw(ArgumentError("use β = false"))

Mengapa tidak melempar kesalahan saja untuk saat ini

Saya menambahkan opsi itu ke jajak pendapat. Ide kompromi yang bagus!

Hanya untuk menetapkan ekspektasi: fitur pembekuan untuk 1,3 dalam tiga hari jadi pada dasarnya tidak mungkin ini bisa tepat waktu. Kami cukup ketat tentang pembekuan dan percabangan fitur karena itu satu-satunya bagian dari siklus rilis yang benar-benar dapat kami kontrol waktunya.

Pekerjaan sudah dilakukan di https://github.com/JuliaLang/julia/pull/29634 . Hanya perlu disesuaikan dan di-rebased.

@tkf untuk #

TODO yang dapat saya pikirkan tentang ATM adalah:

PR saya mengimplementasikan semantik BLAS dari penanganan β = 0 . Jadi penanganan lainnya, seperti melakukan kesalahan, juga harus diterapkan.

PR saya mengimplementasikan semantik BLAS dari penanganan β = 0 .

Maaf, ingatan saya basi; implementasi saya tidak konsisten dan menyebarkan NaN _sometimes_. Jadi TODO tambahan adalah membuat perilaku β = 0.0 konsisten.

Jenis MulAddMul hanya untuk penggunaan internal, bukan?

Ya, ini benar-benar detail internal. Kekhawatiran saya adalah (1) mungkin ada terlalu banyak spesialisasi (beta = 0 dll. Dikodekan dalam parameter tipe) dan (2) itu menurunkan keterbacaan kode sumber.

Itu adalah masalah yang valid. Kita sudah menghasilkan banyak spesialisasi dalam kode aljabar linier, jadi ada baiknya kita memikirkan apakah kita benar-benar perlu mengkhususkan di sini. Pemikiran saya secara umum adalah bahwa kita harus mengoptimalkan untuk matriks kecil karena tidak gratis (seperti yang Anda katakan, ini mempersulit kode sumber dan dapat meningkatkan waktu kompilasi) dan orang-orang lebih baik menggunakan StaticArrays untuk perkalian matriks kecil. Oleh karena itu, saya bersandar hanya memeriksa nilai pada waktu proses tetapi kita selalu dapat menyesuaikan ini nanti jika kita berubah pikiran sehingga kita tidak boleh membiarkan ini menyebabkan penundaan.

FYI nol lunak memang memiliki implementasi sederhana:

if iszero(β) && β !== false && !iszero(α)
   lmul!(zero(T),y) # this handles soft zeros correctly
   BLAS.gemv!(α, A, x, one(T), y) # preserves soft zeros
elseif iszero(α) && iszero(β)
   BLAS.gemv!(one(T), A, x, one(T), y) # puts NaNs in the correct place
   lmul!(zero(T), y) # everything not NaN should be zero
elseif iszero(α) && !iszero(β)
   BLAS.gemv!(one(T), A, x, β, y) # puts NaNs in the correct place
   BLAS.gemv!(-one(T), A, x, one(T), y) # subtracts out non-NaN changes
end

@andreasnoack Maaf, saya lupa bahwa sebenarnya kami memerlukan spesialisasi untuk mengoptimalkan loop paling dalam untuk beberapa matriks terstruktur seperti mul!(C, A::BiTriSym, B, α, β) https://github.com/JuliaLang/julia/pull/29634#issuecomment -440510551. Beberapa spesialisasi dapat dihapus tetapi itu sebenarnya lebih banyak pekerjaan (jadi penundaan).

Oleh karena itu, saya bersandar hanya memeriksa nilai pada waktu proses tetapi kita selalu dapat menyesuaikan ini nanti jika kita berubah pikiran sehingga kita tidak boleh membiarkan ini menyebabkan penundaan.

Bagus!

@andreasnoack Terima kasih banyak telah meninjau dan menggabungkan ini tepat waktu!

Sekarang setelah digabungkan untuk 1.3, itu membuat saya sangat gugup tentang implementasinya: smile :

Tidak perlu khawatir, akan ada banyak waktu untuk 1,3 RC + PkgEval untuk menghilangkan bug.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

iamed2 picture iamed2  ·  3Komentar

StefanKarpinski picture StefanKarpinski  ·  3Komentar

musm picture musm  ·  3Komentar

Keno picture Keno  ·  3Komentar

dpsanders picture dpsanders  ·  3Komentar