Julia: Menangani transposisi matriks dengan serius

Dibuat pada 10 Mar 2017  ·  141Komentar  ·  Sumber: JuliaLang/julia

Saat ini, transpose bersifat rekursif. Ini sangat tidak intuitif dan mengarah pada ketidakberuntungan ini:

julia> A = [randstring(3) for i=1:3, j=1:4]
3×4 Array{String,2}:
 "J00"  "oaT"  "JGS"  "Gjs"
 "Ad9"  "vkM"  "QAF"  "UBF"
 "RSa"  "znD"  "WxF"  "0kV"

julia> A.'
ERROR: MethodError: no method matching transpose(::String)
Closest candidates are:
  transpose(::BitArray{2}) at linalg/bitarray.jl:265
  transpose(::Number) at number.jl:100
  transpose(::RowVector{T,CV} where CV<:(ConjArray{T,1,V} where V<:(AbstractArray{T,1} where T) where T) where T) at linalg/rowvector.jl:80
  ...
Stacktrace:
 [1] transpose_f!(::Base.#transpose, ::Array{String,2}, ::Array{String,2}) at ./linalg/transpose.jl:54
 [2] transpose(::Array{String,2}) at ./linalg/transpose.jl:121

Untuk beberapa waktu sekarang, kami telah memberi tahu orang-orang untuk melakukan permutedims(A, (2,1)) sebagai gantinya. Tapi saya pikir kita semua tahu, jauh di lubuk hati, itu mengerikan. Bagaimana kita bisa sampai disini? Yah, cukup dipahami bahwa seseorang menginginkan ctranspose atau "adjoint" dari matriks matriks menjadi rekursif. Contoh yang memotivasi adalah Anda dapat merepresentasikan bilangan kompleks menggunakan matriks 2x2 , dalam hal ini "konjugasi" dari setiap "elemen" (sebenarnya matriks), adalah adjoint-nya sebagai matriks - dengan kata lain, jika transposisi bersifat rekursif, maka semuanya bekerja. Ini hanyalah sebuah contoh tetapi ini menggeneralisasi.

Alasannya tampaknya sebagai berikut:

  1. ctranspose harus rekursif
  2. ctranspose == conj ∘ transpose == conj ∘ transpose
  3. transpose karena itu juga harus rekursif

Saya pikir ada beberapa masalah di sini:

  • Tidak ada alasan bahwa ctranspose == conj ∘ transpose == conj ∘ transpose harus ditahan, meskipun namanya membuat ini tampak hampir tidak dapat dihindari.
  • Perilaku conj operasi elementwise pada array adalah semacam peninggalan yang tidak menguntungkan dari Matlab dan bukan operasi yang dapat dibenarkan secara matematis, sebanyak exp operasi elementwise tidak benar-benar baik secara matematis dan apa expm tidak akan menjadi definisi yang lebih baik.
  • Ini sebenarnya mengambil konjugasi (yaitu adjoint) dari setiap elemen yang menyiratkan bahwa ctranspose harus rekursif; dengan tidak adanya konjugasi, tidak ada alasan yang baik untuk transpos menjadi rekursif.

Oleh karena itu, saya akan mengusulkan perubahan berikut untuk memperbaiki situasi:

  1. Ubah nama ctranspose (alias ' ) menjadi adjoint - itulah yang sebenarnya dilakukan operasi ini, dan ini membebaskan kita dari implikasi bahwa ia harus setara dengan conj ∘ transpose .
  2. Hentikan penggunaan vektorisasi conj(A) pada array yang mendukung conj.(A) .
  3. Tambahkan argumen kata kunci recur::Bool=true ke adjoint (née ctranspose ) yang menunjukkan apakah ia harus memanggil dirinya sendiri secara rekursif. Secara default, itu benar.
  4. Tambahkan argumen kata kunci recur::Bool=false ke transpose menunjukkan apakah argumen tersebut harus memanggil dirinya sendiri secara rekursif. Secara default, tidak.

Paling tidak, ini akan memungkinkan kita menulis yang berikut:

julia> A.'
4×3 Array{String,2}:
 "J00"  "Ad9"  "RSa"
 "oaT"  "vkM"  "znD"
 "JGS"  "QAF"  "WxF"
 "Gjs"  "UBF"  "0kV"

Apakah kita dapat mempersingkatnya lebih jauh menjadi A' tergantung pada apa yang ingin kita lakukan dengan conj dan adjoint non-angka (atau lebih khusus lagi, non-real, non nilai -complex).

[Masalah ini adalah yang kedua dari seri ω₁-bagian.]

breaking decision linear algebra

Komentar yang paling membantu

(OT: Saya sudah menantikan untuk "Mengambil 7-tensor dengan serius," angsuran berikutnya dalam miniseri 6 bagian yang sangat sukses ...)

Semua 141 komentar

Penerus logis dari masalah sebelumnya ... 👍

Perilaku konj operasi elementwise pada array adalah semacam peninggalan yang tidak menguntungkan dari Matlab dan sebenarnya bukan operasi yang dapat dibenarkan secara matematis.

Ini sama sekali tidak benar, dan sama sekali tidak sejalan dengan exp . Ruang vektor kompleks, dan konjugasinya, adalah konsep matematika yang mapan dengan sempurna. Lihat juga https://github.com/JuliaLang/julia/pull/19996#issuecomment -272312876

Ruang vektor kompleks, dan konjugasinya, adalah konsep matematika yang mapan dengan sempurna.

Kecuali jika saya salah, operasi konjugasi matematika yang benar dalam konteks itu adalah ctranspose daripada conj (yang merupakan maksud saya):

julia> v = rand(3) + rand(3)*im
3-element Array{Complex{Float64},1}:
 0.0647959+0.289528im
  0.420534+0.338313im
  0.690841+0.150667im

julia> v'v
0.879291582684847 + 0.0im

julia> conj(v)*v
ERROR: DimensionMismatch("Cannot multiply two vectors")
Stacktrace:
 [1] *(::Array{Complex{Float64},1}, ::Array{Complex{Float64},1}) at ./linalg/rowvector.jl:180

Masalah dengan menggunakan kata kunci untuk recur adalah, terakhir saya memeriksa, ada penalti kinerja yang besar karena menggunakan kata kunci, yang merupakan masalah jika Anda akhirnya memanggil fungsi-fungsi ini secara rekursif dengan kasus dasar yang berakhir pada skalar.

Kami tetap harus memperbaiki masalah kinerja kata kunci di 1.0.

@StefanKarpinski , Anda salah. Anda dapat memiliki konjugasi kompleks dalam ruang vektor tanpa memiliki adjoin - adjoint adalah konsep yang memerlukan ruang Hilbert, dll., Bukan hanya ruang vektor yang dikomplekskan.

Selain itu, bahkan jika Anda memiliki ruang Hilbert, konjugasi kompleks berbeda dari adjoint. misalnya konjugasi dari vektor kolom kompleks di ℂⁿ adalah vektor kompleks lainnya, tetapi penyambungnya adalah operator linier ("vektor baris").

(Konjugasi kompleks tidak menyiratkan bahwa Anda dapat mengalikan conj(v)*v !)

Menghentikan penggunaan vektorisasi conj tidak bergantung pada proposal lainnya. Dapatkah Anda memberikan beberapa referensi untuk definisi konjugasi kompleks dalam ruang vektor?

https://en.wikipedia.org/wiki/Complexification#Complex_conjugation

(Perlakuan ini agak formal; tetapi jika Anda google "matriks konjugasi kompleks" atau "vektor konjugasi kompleks", Anda akan menemukan banyak sekali penggunaan.)

Jika memetakan vektor kompleks ke vektor konjugasi (apa yang dilakukan conj sekarang) adalah operasi penting yang berbeda dari memetakannya ke covector konjugasi (apa yang dilakukan ' ), maka kita pasti dapat menyimpan conj untuk menyatakan operasi itu pada vektor (dan matriks, dan array yang lebih tinggi, saya kira). Itu memberikan perbedaan antara conj dan adjoint karena mereka setuju pada skalar tetapi berperilaku berbeda pada array.

Dalam hal ini, haruskah conj(A) memanggil conj pada setiap elemen atau memanggil adjoint ? Representasi bilangan kompleks sebagai contoh matriks 2x2 akan menyarankan bahwa conj(A) seharusnya memanggil adjoint pada setiap elemen, daripada memanggil conj . Itu akan menghasilkan adjoint , conj dan conj. semua operasi yang berbeda:

  1. adjoint : tukar i dan j indeks dan secara rekursif memetakan adjoint atas elemen.
  2. conj : petakan adjoint atas elemen.
  3. conj. : petakan conj atas elemen.

conj(A) harus memanggil conj pada setiap elemen. Jika Anda merepresentasikan bilangan kompleks dengan matriks 2x2, Anda memiliki ruang vektor kompleks yang berbeda.

Misalnya, penggunaan konjugasi vektor yang umum terjadi dalam analisis nilai eigen dari matriks nyata : nilai eigen dan vektor eigen datang dalam pasangan konjugasi kompleks. Sekarang, misalkan Anda memiliki matriks blok yang diwakili oleh array 2d A dari matriks 2x2 nyata, yang bekerja pada vektor yang diblokir v diwakili oleh array 1d dari vektor 2-komponen. Untuk nilai eigen λ dari A dengan vektor eigen v , kami mengharapkan nilai eigen kedua conj(λ) dengan vektor eigen conj(v) . Ini tidak akan berfungsi jika conj memanggil adjoint secara rekursif.

(Perhatikan bahwa adjoint, bahkan untuk matriks, mungkin berbeda dari konjugasi-transpos, karena adjoint dari operator linier yang didefinisikan dalam cara yang paling umum juga bergantung pada pilihan produk dalam. Ada banyak aplikasi nyata di mana beberapa jenis produk dalam berbobot sesuai, dalam hal ini cara yang tepat untuk mengambil adjoint dari matriks berubah! Tapi saya setuju bahwa, dengan Matrix , kita harus mengambil adjoint yang sesuai dengan produk dalam standar yang diberikan oleh dot(::Vector,::Vector) . Namun, sangat mungkin bahwa beberapa jenis AbstractMatrix (atau operator linier lainnya) ingin menimpa adjoint untuk melakukan sesuatu yang berbeda.)

Sejalan dengan itu, ada juga beberapa kesulitan dalam mendefinisikan adjoint(A) secara aljabar masuk akal untuk misalnya array 3d, karena kita belum mendefinisikan array 3d sebagai operator linier (misalnya tidak ada operasi array3d * array2d bawaan) . Mungkin adjoint seharusnya hanya ditentukan secara default untuk skalar, larik 1d, dan 2d?

Pembaruan: Oh, bagus: kami tidak mendefinisikan ctranspose sekarang juga untuk array 3d. Lanjut.

(OT: Saya sudah menantikan untuk "Mengambil 7-tensor dengan serius," angsuran berikutnya dalam miniseri 6 bagian yang sangat sukses ...)

Jika Anda merepresentasikan bilangan kompleks dengan matriks 2x2, Anda memiliki ruang vektor kompleks yang berbeda.

Saya tidak mengikuti ini - representasi matriks 2x2 dari bilangan kompleks harus berperilaku persis seperti memiliki skalar kompleks sebagai elemen. Saya akan berpikir, misalnya jika kita mendefinisikan

m(z::Complex) = [z.re -z.im; z.im z.re]

dan kami memiliki vektor kompleks arbitrer v maka kami ingin identitas ini disimpan:

conj(m.(v)) == m.(conj(v))

Saya akan mengeja contoh lebih lanjut dan membuat perbandingan dengan ' yang seharusnya sudah bolak-balik dengan m tetapi saya tidak bisa karena https://github.com/JuliaLang/julia/ issues / 20979 , yang secara tidak sengaja merusak pergantian tersebut. Setelah bug itu diperbaiki, maka m.(v)' == m.(v') akan bertahan, tetapi jika saya memahami Anda dengan benar, @stevengj , maka conj(m.(v)) == m.(conj(v)) tidak boleh?

conj(m(z)) harus == m(z) .

m(z) adalah isomorfisme terhadap bilangan kompleks yang sedang dijumlahkan dan dikalikan, tetapi ini bukan objek yang sama dengan cara lain untuk aljabar linier, dan kita tidak boleh menganggapnya sebagai conj . Misalnya, jika z adalah skalar yang kompleks, maka eigvals(z) == [z] , tetapi eigvals(m(z)) == [z, conj(z)] . Ketika trik m(z) diperluas ke matriks kompleks, penggandaan spektrum ini memiliki konsekuensi rumit untuk misalnya metode iteratif (lihat misalnya makalah ini ).

Cara lain untuk menjelaskannya adalah bahwa z sudah menjadi ruang vektor kompleks (ruang vektor di atas bilangan kompleks), tetapi matriks nyata 2x2 m(z) adalah ruang vektor di atas bilangan real (Anda dapat mengalikan m(z) dengan bilangan real)… jika Anda "memperumit" m(z) dengan mengalikannya dengan bilangan kompleks misalnya m(z) * (2+3im) ( bukan m(z) * m(2+3im) ) maka Anda dapatkan ruang vektor kompleks yang berbeda yang tidak isomorfik lagi ke ruang vektor kompleks asli z .

Proposal ini sepertinya akan menghasilkan peningkatan yang nyata: +1: Saya sedang memikirkan sisi matematika:

  • seperti yang saya pahami, konjugasi adalah tindakan di atas ring (baca: angka) yang memberikan koefisien untuk ruang vektor / vektor kita. Tindakan ini menginduksi tindakan lain pada ruang vektor (dan pada pemetaannya, baca: matriks), juga disebut konjugasi, oleh linieritas konstruksi ruang vektor di atas cincin. Oleh karena itu, konjugasi harus bekerja dengan aplikasi elementwise ke koefisien. Untuk Julia, itu berarti bahwa inti dari vektor v , conj(v) = conj.(v) , dan itu adalah pilihan desain apakah conj memiliki metode juga untuk array atau hanya untuk skalar, keduanya sepertinya oke? (Poin Steven tentang contoh matriks-bilangan-kompleks-sebagai-2x2-matriks adalah bahwa ini adalah ruang vektor yang koefisiennya secara formal / nyata, jadi konjugasi tidak berpengaruh di sini.)
  • adjoint memiliki arti aljabar yang fundamental dalam banyak domain penting yang secara erat melibatkan aljabar linier (lihat 1 dan 2 dan 3 , semuanya dengan aplikasi besar dalam fisika juga). Jika adjoint ditambahkan, itu harus memungkinkan argumen kata kunci untuk tindakan yang sedang dipertimbangkan - dalam ruang vektor / analisis fungsional, tindakan ini biasanya diinduksi oleh produk dalam, maka argumen kata kunci dapat menjadi bentuknya. Apa pun yang dirancang untuk transpos Julia harus menghindari konflik dengan aplikasi ini, jadi menurut saya adjoint agak mahal?

@felixrehren , saya rasa Anda tidak akan menggunakan argumen kata kunci untuk menentukan produk dalam yang menginduksi adjoint. Saya pikir Anda hanya akan menggunakan tipe yang berbeda, sama seperti jika Anda ingin mengubah arti dot untuk vektor.

Preferensi saya akan sedikit lebih sederhana:

  • Pertahankan conj apa adanya (berelemen).
  • Membuat a' sesuai dengan adjoint(a) , dan membuatnya selalu berulang (dan karenanya gagal untuk array string dll di mana adjoint tidak ditentukan untuk elemen).
  • Buat a.' sesuai dengan transpose(a) , dan buat itu tidak pernah berulang (hanya permutedims ), dan karenanya abaikan jenis elemennya.

Saya benar-benar tidak melihat kasus penggunaan untuk adjoint non-rekursif. Sebagai peregangan, saya dapat membayangkan kasus penggunaan untuk rekursif transpose , tetapi dalam aplikasi apa pun di mana Anda harus mengubah a.' menjadi transpose(a, recur=true) tampaknya sama mudahnya panggil fungsi lain transposerecursive(a) . Kita bisa mendefinisikan transposerecursive di Base, tapi saya pikir kebutuhan akan hal ini akan sangat jarang sehingga kita harus menunggu untuk melihat apakah itu benar-benar muncul.

Saya pribadi merasa bahwa menjaga agar ini tetap sederhana akan menjadi yang paling mudah bagi pengguna (dan untuk implementasi), dan masih cukup dapat dipertahankan di bagian depan aljabar linier.

Sejauh ini (sebagian besar) rutinitas aljabar linier kita berakar kuat pada struktur array standar dan hasil kali dalam standar. Di setiap slot matriks atau vektor Anda, Anda menempatkan elemen "bidang" Anda. Saya akan berpendapat bahwa untuk elemen bidang , kita peduli tentang + , * , dll, dan conj , tetapi bukan transpose . Jika bidang Anda adalah representasi kompleks khusus yang terlihat sedikit seperti matriks 2x2 nyata tetapi perubahan di bawah conj , maka yang ini OK - itu adalah properti dari conj . Jika hanya 2x2 nyata AbstractMatrix yang tidak berubah di bawah conj , maka elemen matriks seperti itu tidak boleh berubah di bawah adjoint ( @stevengj mengatakannya lebih baik daripada yang bisa saya gambarkan - mungkin ada isomorfisme pada bilangan kompleks tetapi itu tidak berarti ia berperilaku sama dalam semua hal).

Bagaimanapun, contoh kompleks 2x2 terasa sedikit mengganggu bagi saya. Penyebab sebenarnya dari perilaku matriks rekursif adalah sebagai jalan pintas untuk melakukan aljabar linier pada matriks blok. Mengapa kita tidak menangani kasus khusus ini dengan hati-hati, dan menyederhanakan sistem yang mendasarinya?

Jadi, saran saya yang "disederhanakan" adalah:

  • Simpan conj apa adanya (atau buat tampilan seharga AbstractArray s)
  • Buat a' tampilan non-rekursif sehingga (a')[i,j] == conj(a[j,i])
  • Buat a.' tampilan non-rekursif sehingga (a.')[i,j] == a[j,i]
  • Perkenalkan tipe BlockArray untuk menangani matriks-blok dan seterusnya (dalam Base , atau mungkin dalam paket yang didukung dengan baik). Bisa dibilang, ini akan jauh lebih kuat dan fleksibel daripada Matrix{Matrix} untuk tujuan ini, tetapi sama-sama efisien.

Saya pikir aturan ini akan cukup sederhana bagi pengguna untuk dipeluk, dan dikembangkan.

PS - @StefanKarpinski Untuk alasan praktis, argumen kata kunci boolean untuk rekursi tidak akan berfungsi dengan view untuk transposisi. Jenis tampilan mungkin bergantung pada nilai boolean.

Juga, saya sebutkan di tempat lain, tetapi saya akan menambahkannya di sini untuk kelengkapan: tampilan transpose rekursif memiliki properti yang mengganggu bahwa tipe elemen dapat berubah dibandingkan dengan array yang dibungkusnya. Misalnya transpose(Vector{Vector}) -> RowVector{RowVector} . Saya belum memikirkan cara untuk mendapatkan jenis elemen ini seharga RowVector tanpa penalti run-time atau dengan memanggil inferensi untuk menghitung jenis keluaran. Saya menduga perilaku saat ini (meminta kesimpulan) tidak diinginkan dari sudut pandang bahasa.

NB: juga tidak ada yang menghentikan pengguna untuk mendefinisikan conj untuk mengembalikan tipe yang berbeda - jadi tampilan ConjArray juga mengalami masalah ini, apakah transposisi bersifat rekursif atau tidak.

@stevengj - Anda menunjuk tentang matriks 2x2 "kompleks" yang menjadi ruang vektor nyata secara formal daripada ruang vektor yang kompleks masuk akal bagi saya, tetapi hal itu menimbulkan pertanyaan bagi saya motivasi asli untuk penyambungan rekursif, yang membuat saya bertanya-tanya apakah Proposal @andyferris tidak akan lebih baik (transpos dan adjoint non-rekursif). Saya rasa fakta bahwa kedua contoh kompleks 2x2 dan representasi matriks blok "ingin" adjoint menjadi rekursif adalah sugestif, tetapi mengingat komentar Anda tentang contoh pertama itu, saya harus bertanya-tanya apakah tidak ada kasus lain di mana adjoint non-rekursif lebih tepat / nyaman.

Jika adjoint tidak rekursif, itu bukan adjoint. Itu salah.

Dapatkah Anda memberikan sedikit lebih banyak pembenaran untuk itu ketika Anda punya waktu?

Adjoint vektor harus menjadi operator linier yang memetakannya ke skalar. Artinya, a'*a harus skalar jika a adalah vektor. Dan ini menginduksi adjoint yang sesuai pada matriks, karena properti yang menentukan adalah a'*A*a == (A'*a)'*a .

Jika a adalah vektor vektor, ini berarti bahwa a' == adjoint(a) harus rekursif.

Oke, saya rasa saya mengikuti ini.

Kami juga memiliki produk dalam rekursif:

julia> norm([[3,4]])
5.0

julia> dot([[3,4]], [[3,4]])
25

Jelas, "adjoint" atau "dual" atau apa pun harus rekursif serupa.

Saya pikir pertanyaan utamanya adalah, apakah kita memerlukan a' * b == dot(a,b) untuk semua vektor a , b ?

Alternatifnya adalah dengan mengatakan bahwa ' tidak selalu mengembalikan adjoint - ini hanya operasi array yang mentransposisi elemen dan meneruskannya melalui conj . Ini kebetulan menjadi titik temu untuk elemen nyata atau kompleks.

Ada satu dan hanya satu alasan mengapa kita memiliki nama khusus dan simbol khusus untuk adjoin dalam aljabar linier, dan itu adalah hubungan dengan hasil kali dalam. Itulah alasan mengapa "transpos konjugasi" adalah operasi yang penting dan "rotasi konjugasi matriks sebesar 90 derajat" tidak. Tidak ada gunanya memiliki sesuatu yang "hanya operasi array yang menukar baris dan kolom dan konjugasi" jika tidak terhubung ke produk titik.

Seseorang dapat mendefinisikan hubungan serupa antara transpose dan "produk titik" tak terkonjugasi, yang akan memperdebatkan transposisi rekursif. Namun, "produk titik" tak terkonjugasi untuk data kompleks, yang sebenarnya bukan produk dalam sama sekali, tidak muncul sesering produk dalam sebenarnya - produk tersebut muncul dari hubungan bi-ortogonalitas ketika operator ditulis dalam kompleks- bentuk simetris (bukan Hermitian) - dan bahkan tidak memiliki fungsi Julia built-in atau terminologi umum dalam aljabar linier, sedangkan keinginan untuk menukar baris dan kolom dari array non-numerik yang sewenang-wenang tampaknya jauh lebih umum, terutama dengan penyiaran. Inilah mengapa saya dapat mendukung membuat transpose non-rekursif sementara menyisakan adjoint rekursif.

Saya melihat. Jadi mengganti nama ' menjadi adjoint akan menjadi bagian dari perubahan, untuk memperjelas bahwa ini bukan conj ∘ transpose ?

Hal yang selalu membingungkan saya dengan array rekursif adalah: apa itu skalar dalam konteks ini? Dalam semua kasus yang saya kenal, dikatakan bahwa elemen vektor adalah skalar. Bahkan dalam kasus di mana seorang matematikawan menulis struktur blok-vektor / matriks pada selembar kertas, kita masih tahu ini hanya singkatan dari vektor / matriks yang lebih besar di mana elemen-elemennya mungkin bilangan real atau kompleks (yaitu BlockArray ). Anda berharap skalar dapat berkembang biak di bawah * , dan jenis skalar biasanya tidak berbeda antara vektor dan dua.

@andyferris , untuk ruang vektor umum, skalar adalah cincin (angka) yang dapat digunakan untuk mengalikan vektor. Saya bisa melakukan 3 * [[1,2], [3,4]] , tapi saya tidak bisa melakukan [3,3] * [[1,2], [3,4]] . Jadi, meskipun untuk Array{Array{Number}} , jenis skalar yang benar adalah Number .

Setuju - tetapi elemen biasanya dikatakan skalar (sama), bukan?

Perawatan yang saya lihat, bagaimanapun, dimulai dengan cincin, dan membangun ruang vektor dari mereka. Cincin itu mendukung + , * , tetapi saya belum pernah melihat perawatan yang membutuhkannya untuk mendukung adjoint , dot , atau apa pun.

(Maaf, saya hanya mencoba memahami objek matematika yang mendasari yang kami modelkan).

Itu tergantung pada apa yang Anda maksud dengan "steno" dan apa yang Anda maksud dengan "elemen".

Misalnya, sangat umum untuk memiliki vektor fungsi berukuran terbatas, dan "matriks" dari operator berdimensi tak hingga. misalnya pertimbangkan bentuk persamaan makroskopik Maxwell berikut :

image

Dalam kasus ini, kita memiliki matriks 2x2 operator linier (misalnya ikal) yang bekerja pada vektor 2 komponen yang "elemen" -nya adalah bidang vektor 3 komponen. Ini adalah vektor di atas bidang skalar bilangan kompleks. Dalam beberapa hal, jika Anda menelusuri cukup jauh, "elemen" vektor adalah bilangan kompleks - komponen individual bidang pada setiap titik dalam ruang - tetapi itu agak suram.

Atau mungkin lebih tepatnya, tidak bisakah kita selalu menggunakan cincin untuk mendeskripsikan koefisien vektor dalam beberapa dasar (mengingat sifat array hal-hal di Julia, kita tidak akan bebas basis)?

Bagaimanapun, konsekuensinya adalah tetap bahwa adjoints harus rekursif, bukan? Saya tidak mengerti maksud Anda.

(Kami benar-benar dapat pergi tanpa basis, saya pikir, karena objek atau elemen dari array bisa menjadi ekspresi simbolik ala SymPy atau beberapa struktur data lain yang mewakili objek matematika abstrak, dan adjoint dapat mengembalikan objek lain yang ketika dikalikan menghitung integral, misalnya.)

Saya hanya mencoba untuk memahami lebih baik, tidak membuat poin tertentu :)

Tentu saja, di atas, adjointnya rekursif. Saya bertanya-tanya apakah misalnya di atas adalah yang terbaik untuk memikirkan [E, H] sebagai BlockVector yang (tak terhingga?) Banyak elemen yang kompleks, atau sebagai 2-vektor yang elemennya adalah bidang vektor. Saya pikir dalam kasus ini, pendekatan BlockVector akan bisa dijalankan.

Terima kasih.

Jadi, mungkin saya bisa menyaring pikiran saya dalam bentuk ini:

Untuk vektor v , saya berharap length(v) mendeskripsikan dimensi ruang vektor, yaitu jumlah koefisien skalar yang saya perlukan untuk (sepenuhnya) mendeskripsikan elemen ruang vektor, dan juga bahwa v[i] mengembalikan koefisien skalar i th. Sampai saat ini, saya belum memikirkan AbstractVector sebagai "vektor abstrak", melainkan sebuah objek yang menyimpan koefisien elemen dari beberapa ruang vektor dengan beberapa dasar yang diketahui oleh programmer.

Ini tampak seperti model mental yang sederhana & berguna, tetapi mungkin ini terlalu membatasi / tidak praktis.

(EDIT: Ini membatasi karena dalam Vector{T} , T harus berperilaku seperti skalar agar operasi aljabar linier berfungsi.)

tapi saya tidak bisa melakukan [3,3] * [[1,2], [3,4]]

Kami bisa memperbaikinya. Saya tidak yakin apakah itu ide yang bagus atau tidak, tapi pasti bisa berhasil.

Saya tidak yakin itu diinginkan ... dalam hal ini [3,3] sebenarnya bukan skalar. (Kami juga sudah memiliki kemampuan untuk melakukan [3,3]' * [[1,2], [3,4]] - yang saya benar-benar tidak tahu bagaimana menafsirkannya dalam pengertian aljabar linier).

Ini menunjukkan kasus sudut yang menarik: kita sepertinya mengatakan bahwa v1' * v2 adalah produk dalam (dan dengan demikian mengembalikan "skalar"), tetapi [3,3]' * [[1,2], [3,4]] == [12, 18] . Sebuah contoh yang dibuat-buat, saya kira.

Dalam hal ini [3,3] adalah skalar: skalar adalah elemen dalam cincin yang mendasarinya, dan ada banyak cara bagus untuk membuat elemen bentuk [a,b] menjadi cincin (dan dengan demikian mendefinisikan vektor / matriks di atasnya). Fakta bahwa mereka ditulis sebagai vektor, atau fakta bahwa cincin ini sendiri membentuk ruang vektor di atas cincin lain yang mendasari, tidak mengubah itu. (Anda dapat menggunakan notasi lain yang mungkin menyembunyikan vektor! Atau membuatnya terlihat sangat berbeda.) Seperti yang dijelaskan @andyferris , apa itu skalar bergantung pada konteks.

Secara formal, rekursi bukanlah bagian dari adjoint. Sebaliknya, konjugasi elemen skalar melakukan bagian pekerjaan itu - dan seringkali, jika skalar s direpresentasikan sebagai matriks, mengkonjugasikan s akan melibatkan pengalihan matriks yang kita gunakan untuk menunjukkan Itu. Tapi itu tidak selalu terjadi, itu tergantung pada struktur yang diberikan pengguna ke lingkaran skalar.

Saya pikir memaksa rekursi adjoint bisa menjadi kompromi praktis OK, khas untuk aplikasi jenis matlab. Tapi bagi saya, menganggapnya sangat serius berarti adjoint non-rekursif dan menggunakan skalar yang diketik + pengiriman untuk membiarkan conj melakukan keajaiban yang diperlukan pada apa pun struktur cincin yang mendasarinya.

Dalam hal ini [3,3] adalah skalar: skalar adalah elemen dalam cincin yang mendasarinya

Kecuali, skalar seperti itu bukanlah elemen cincin yang ditentukan oleh + , * . Itu tidak mendukung * ; Saya tidak bisa melakukan [3,3] * [3,3] .

Saya pikir memaksa rekursi adjoint bisa menjadi kompromi praktis OK, khas untuk aplikasi jenis matlab. Tapi bagi saya, menganggapnya sangat serius berarti adjoint non-rekursif dan menggunakan skalar yang diketik + pengiriman untuk membiarkan conj melakukan keajaiban yang diperlukan pada apa pun struktur cincin yang mendasarinya.

Saya setuju, jika kita ingin melakukan komposisi praktis ini baik-baik saja dan itulah yang telah kami lakukan sampai sekarang, tetapi bagi saya tampaknya skalar yang mendasarinya adalah skalar yang sepenuhnya rata dan itulah yang didefinisikan aljabar linier . Kami memiliki teknologi untuk membuat BlockVector dan BlockMatrix (dan BlockDiagonal , dll) yang menyajikan tampilan yang efisien dan rata dari larik yang diperluas sepenuhnya, sehingga "super serius "Pendekatan harus, minimal, bisa dilakukan. Saya juga kebetulan berpikir itu akan sama ramah pengguna seperti pendekatan rekursif (beberapa karakter tambahan untuk mengetik BlockMatrix([A B; C D]) dalam pertukaran untuk apa yang mungkin menjadi kode semantik yang lebih baik dan berpotensi lebih mudah dibaca - Saya yakin ini poin untuk diperdebatkan). Akan tetapi, akan lebih banyak pekerjaan untuk menerapkan semua itu.

@andyferris Anda benar, mereka bukan cincin karena * tidak ditentukan - tapi saya rasa kita berada di halaman yang sama bahwa * dapat dengan mudah didefinisikan untuknya, dan ada banyak cara berbeda untuk melakukannya yang akan menghasilkan struktur cincin. Jadi saya kira kita berada di halaman yang sama: mengetik adalah cara yang lebih luas untuk menyelesaikan ini.

(Tentang skalar: skalar belum tentu merupakan elemen dari struktur yang sepenuhnya rata! Alasannya adalah ini. Ruang vektor kompleks 1 dimensi adalah 1 dimensi di atas bilangan kompleks dan 2 dimensi di atas bilangan real; tidak ada representasi yang spesial. Kuarter adalah 2 dimensi di atas bilangan kompleks. Oktonion adalah 2 dimensi di atas kuarternion, 4 dimensi di atas bilangan kompleks, dan 8 dimensi di atas real. Tapi tidak ada alasan untuk bersikeras bahwa oktonion adalah "8 dimensi" lagi daripada bersikeras bahwa skalar mereka adalah bilangan real bukan kompleks; itu adalah pilihan yang tidak dipaksakan oleh logika. Ini murni pertanyaan representasi, pengguna harus diberi kebebasan ini karena aljabar linier sama dalam setiap kasus. Dan Anda mendapatkan rantai yang jauh lebih panjang dari ruang seperti itu dengan cincin multipolinomial [terpotong], atau ekstensi aljabar dari bidang angka, keduanya memiliki aplikasi yang hidup dengan matriks. Julia tidak harus pergi sejauh ini ke lubang kelinci - tetapi kami harus ingat itu ada, agar tidak memblokirnya. Mungkin topik wacana? :))

@felixrehren , vektor vektor di atas cincin R paling baik dipahami sebagai ruang penjumlahan langsung, yang juga ruang vektor di atas R. Tidak masuk akal untuk menyebut vektor itu sendiri "cincin yang mendasari", karena secara umum mereka bukan a cincin. Alasan itu berlaku sempurna untuk [[1,2], [3,4]] .

Konjugasi dan adjoin biasanya merupakan konsep yang terpisah; mengatakan bahwa "konjugasi elemen skalar" harus melakukan pekerjaan di sini tampaknya sama sekali tidak benar bagi saya - kebalikan dari "serius" - kecuali dalam kasus khusus yang disebutkan di bawah.

Pertimbangkan kasus "vektor kolom 2 komponen" (| u⟩, | v⟩) dari dua elemen | u⟩ dan | v⟩ di beberapa ruang Hilbert H di atas beberapa cincin (katakanlah bilangan kompleks ℂ), dalam notasi Dirac: "vektor vektor." Ini adalah penjumlahan langsung ruang Hilbert H⊕H dengan hasil kali dalam alami ⟨(| u⟩, | v⟩), (| w⟩, | z⟩)⟩ = ⟨u | w⟩ + ⟨v | z⟩. Oleh karena itu, adjoin harus menghasilkan operator linier yang terdiri dari "vektor baris" (⟨u | ⟨v |), yang elemennya sendiri adalah operator linier: adjoint adalah"rekursif" . Ini sama sekali berbeda dari konjugat kompleks, yang menghasilkan elemen ruang Hilbert yang sama H⊕H, yang diinduksi oleh konjugasi cincin bawahan.

Anda mengacaukan masalah ini dengan merujuk ke vektor di atas angka empat, dan sebagainya. Jika "elemen" dari vektor juga merupakan elemen dari cincin "kompleks" yang mendasarinya, maka tentu saja penyambung dan konjugasi elemen-elemen ini adalah sama. Namun, itu tidak berlaku untuk semua ruang produk langsung.

Dengan kata lain, tipe objek perlu menunjukkan dua informasi berbeda :

  • Cincin skalar (karena itu konjugasi, yang menghasilkan elemen dari ruang yang sama ).
  • Produk dalam (karena itu adjoint, yang menghasilkan elemen ruang ganda ).

Untuk Vector{T<:Number} , kita harus membacanya sebagai indikasi bahwa cincin skalar adalah T (atau complex(T) untuk ruang vektor kompleks), dan hasil kali dalam adalah produk Euclidean biasa .

Jika Anda memiliki vektor vektor, maka itu adalah ruang Hilbert penjumlahan langsung dan cincin tersebut adalah promosi dari cincin skalar dari masing-masing vektor, dan perkalian titik adalah jumlah perkalian titik dari elemen-elemen tersebut. (Jika skalar tidak dapat dipromosikan atau produk titik tidak dapat dijumlahkan, maka itu bukan vektor / ruang Hilbert sama sekali.)

Jika Anda memiliki skalar T<:Number , lalu conj(x::T) == adjoint(x::T) .

Jadi, jika Anda mencoba merepresentasikan bilangan kompleks z::Complex{T} dengan matriks 2x2 m(z)::Array{T,2} , maka jenis Anda menunjukkan cincin yang berbeda T dan hasil kali dalam yang berbeda dibandingkan dengan z , dan karenanya Anda seharusnya tidak mengharapkan conj atau adjoint untuk memberikan hasil yang setara.

Saya agak mengerti apa yang Anda katakan @felixrehren. Jika skalar adalah nyata, maka okterna dapat dianggap sebagai ruang vektor 8 dimensi. Tetapi jika koefisien skalar adalah octernian, maka ada basis dimensi-1 trivial yang mewakili semua octernian. Saya tidak yakin apakah ini berbeda dengan apa yang saya katakan (atau maksud saya: smile :), jika kita memiliki AbstractVector{T1} itu mungkin isomorfik ke AbstractVector{T2} dari dimensi yang berbeda ( length ). Tapi T1 dan T2 keduanya harus berdering di bawah operator Julia + dan * (dan isomorfisme mungkin mempertahankan perilaku + tetapi bukan * , atau produk dalam atau tambahan).

@stevengj Saya selalu memikirkan jumlah langsung persis seperti BlockVector . Anda memiliki dua (terputus-putus) basis yang tidak lengkap. Dalam setiap basis, Anda mungkin dapat membentuk superposisi seperti c₁ | X₁⟩ + c₂ | X₂⟩ di basis pertama atau c₃ | Y₁⟩ + c₄ | Y₂⟩ di basis kedua. "Jumlah langsung" mewakili ruang dari keadaan-keadaan yang terlihat seperti (c₁ | X₁⟩ + c₂ | X₂⟩) + (c₃ | Y₁⟩ + c₄ | Y.). Tampaknya bagi saya bahwa satu-satunya hal khusus yang memisahkan ini dari basis dimensi empat di atas bilangan kompleks (yaitu menyatakan seperti c₁ | X₁⟩ + c₂ | X₂⟩ + c₃ | Y₁⟩ + c₄ | Y₂⟩) adalah tanda kurung - bagi saya, itu tampak seperti notasional; jumlah langsung hanyalah cara mudah untuk menulis ini di atas kertas atau dengan array di komputer (dan mungkin berguna untuk misalnya membiarkan kami membuat katalog dan memanfaatkan simetri, atau untuk memecah masalah (mungkin untuk memparalelkannya), dll. ). Dalam contoh ini X⊕Y masih merupakan ruang vektor empat dimensi dengan skalar yang kompleks.

Inilah yang membuat saya menginginkan eltype(v) == Complex{...} dan length(v) == 4 daripada eltype(v) == Vector{Complex{...}} dan length(v) == 2 . Ini membantu saya untuk melihat dan bernalar tentang cincin yang mendasari dan ruang vektor. Bukankah ini juga ruang yang "diratakan" di mana Anda dapat melakukan transposisi "global" dan dengan elemen conj untuk menghitung adjoint?

(Anda mendapat dua posting ketika saya hanya menulis satu, @stevengj : smile :)

Tentu saja, jika kita lebih suka menggunakan array bersarang sebagai konvensi untuk jumlah langsung (seperti yang kita lakukan sekarang) daripada BlockArray , ini bisa bagus dan konsisten juga! Kita mungkin mendapat keuntungan dari beberapa fungsi tambahan untuk menentukan bidang skalar (semacam eltype rekursif) dan apa dimensi rata efektif yang mungkin (semacam ukuran rekursif) untuk membuatnya sedikit mudah untuk bernalar tentang aljabar linier apa yang kita lakukan.

Untuk lebih jelasnya, saya senang dengan kedua pendekatan tersebut dan saya telah belajar banyak dari diskusi ini. Terima kasih.

@andyferris , hanya karena dua spasi isomorfik tidak berarti mereka adalah ruang yang "sama", dan memperdebatkan yang mana yang "benar-benar" ruang vektor (atau apakah mereka "benar-benar" sama) adalah rawa metafisik dari mana kita tidak akan pernah melarikan diri. (Tetapi kasus penjumlahan langsung dari ruang vektor berdimensi-tak-hingga menggambarkan batasan dari konsep "diratakan" Anda tentang penjumlahan langsung sebagai "semua elemen dari satu diikuti oleh semua elemen yang lain".)

Sekali lagi, saya tidak yakin apa maksud Anda. Apakah Anda serius mengusulkan bahwa eltype(::Vector{Vector{Complex}}) di Julia harus Complex , atau bahwa length harus mengembalikan jumlah panjangnya? Bahwa setiap orang di Julia harus dipaksa untuk mengadopsi isomorfisme Anda yang "diratakan" untuk spasi penjumlahan langsung? Jika tidak, maka adjoint harus rekursif.

Dan jika Anda "hanya mencoba untuk memahami lebih baik, bukan membuat poin tertentu", dapatkah Anda membawanya ke forum lain? Masalah ini cukup membingungkan tanpa argumen metafisik tentang arti spasi penjumlahan langsung.

hanya karena dua spasi isomorfik tidak berarti mereka adalah ruang yang "sama"

Saya sama sekali tidak menyarankan itu.

Sekali lagi, saya tidak yakin apa maksud Anda

Saya dengan serius menyarankan bahwa jika Anda ingin mengerjakan aljabar linier dengan AbstractArray , maka eltype sebaiknya dijadikan cincin (dukung + , * dan conj ) yang mengesampingkan misalnya eltype dari Vector karena [1,2] * [3,4] tidak berfungsi. Dan length dari vektor akan benar-benar mewakili dimensi aljabar linier Anda. Ya, ini mengesampingkan ruang vektor berdimensi tak terhingga, tetapi umumnya di Julia AbstractVector tidak dapat berukuran tak terhingga. Saya merasa ini akan memudahkan untuk bernalar tentang bagaimana mengimplementasikan dan menggunakan fungsionalitas aljabar linier pada Julia. Namun, pengguna harus secara eksplisit menunjukkan jumlah langsung melalui BlockArray atau serupa daripada menggunakan struktur array bersarang.

Ini adalah saran yang cukup melanggar, dan memiliki kerugian yang signifikan (beberapa telah Anda sebutkan), jadi saya tidak akan kecewa jika kita mengatakan "itu ide yang bagus, tetapi itu tidak akan praktis". Namun, saya juga telah mencoba menunjukkan bahwa pendekatan array bersarang membuat beberapa hal tentang aljabar linier yang mendasarinya menjadi sedikit lebih buram.

Semua ini tampaknya relevan langsung dengan OP. Dengan pendekatan pipih yang dipaksakan, kita akan membuang transpos / adjoint rekursif, dan jika kita membuang transpose / adjoint rekursif, hanya struktur yang diratakan yang dapat digunakan untuk aljabar linier. Jika kita tidak memaksakan pendekatan yang diratakan, kita harus menyimpan adjoint rekursif. Bagi saya, ini sepertinya keputusan yang terkait.

Apakah Anda serius mengusulkan bahwa eltype(::Vector{Vector{Complex}}) di Julia harus Complex , atau bahwa length harus mengembalikan jumlah panjangnya?

Tidak, maaf, mungkin yang saya tulis di sana tidak jelas - saya hanya menyarankan bahwa dua fungsi kenyamanan baru untuk tujuan ini mungkin berguna dalam keadaan tertentu. Terkadang Anda mungkin menginginkan zero atau one cincin itu, misalnya.

Apakah Anda mengatakan bahwa setiap orang di Julia harus dipaksa untuk mengadopsi isomorfisme Anda yang "diratakan" untuk spasi penjumlahan langsung?

Saya menyarankan itu sebagai kemungkinan, ya. Saya tidak 100% yakin itu ide terbaik, tapi saya merasa ada beberapa keuntungan (dan beberapa kekurangan).

Mengembalikan ranah perubahan yang dapat ditindaklanjuti di sini, tampaknya versi sederhana dari proposal @stevengj adalah cara untuk menuju ke sini, yaitu:

  • Pertahankan conj apa adanya (berelemen).
  • Membuat a' sesuai dengan adjoint(a) , dan membuatnya selalu berulang (dan karenanya gagal untuk array string dll di mana adjoint tidak ditentukan untuk elemen).
  • Membuat a.' sesuai dengan transpose(a) , dan membuatnya tidak pernah rekursif (hanya permutedims ), dan karenanya mengabaikan jenis elemen.

Kata "todos" utama yang bisa diambil dari ini adalah:

  1. [] Ubah nama ctranspose menjadi adjoint .
  2. [] Ubah transpose menjadi non-rekursif.
  3. [] Cari tahu bagaimana hal ini cocok dengan vektor baris.

Saya menduga bahwa mengandalkan transpose rekursif cukup langka sehingga kita dapat mengubahnya di 1.0 dan mencantumkannya sebagai melanggar di NEWS. Mengubah ctranspose menjadi adjoint dapat melalui penghentian normal (namun kami melakukan ini untuk 1.0). Apakah ada hal lain yang perlu dilakukan?

@StefanKarpinski , kita juga perlu melakukan perubahan yang sesuai ke RowVector . Salah satu kemungkinannya adalah membagi RowVector menjadi Transpose dan Adjoint jenis untuk malas non-rekursif transpose dan rekursif malas adjoint , masing-masing, dan untuk menyingkirkan Conj (malas conj ).

Sedikit lagi perubahan yang terkait setidaknya secara tangensial

  • Beberapa jenis tampilan untuk transpose dan adjoint dari AbstractMatrix
  • Buat adjoint(::Diagonal) rekursif (bisa dibilang kita harus memperbaiki "bug" ini di Diagonal untuk transpose dan ctranspose sebelum Julia v0.6.0)
  • Gunakan jenis "skalar" yang tepat dalam hal-hal seperti: v = Vector{Vector{Float64}}(); dot(v,v) (saat ini terjadi kesalahan - mencoba mengembalikan zero(Vector{Float64}) )

OK, saya telah mencoba untuk mengambil matriks blok rekursif lebih serius, dan untuk mendukung ini saya juga mencoba untuk meningkatkan perilaku Diagonal sini (sudah ada beberapa metode yang mengantisipasi blok-diagonal struktur, seperti getindex , jadi tampaknya wajar untuk membuat transpose rekursif dalam kasus ini).

Di mana saya terjebak, dan apa yang langsung memotivasi semua poin diskusi saya di atas, adalah bagaimana melakukan operasi aljabar linier pada struktur seperti itu, termasuk inv , det , expm , eig , dan seterusnya. Misalnya, ada metode untuk det(::Diagonal{T}) where T yang hanya mengambil produk dari semua elemen diagonal. Berfungsi dengan sangat baik untuk T <: Number , dan juga berfungsi jika semua elemennya adalah matriks persegi dengan ukuran yang sama . (Tentu saja, jika mereka adalah matriks persegi dengan ukuran yang sama, maka elemen-elemennya membentuk cincin, dan semuanya cukup masuk akal - juga transposisi rekursif (edit: adjoint) adalah hal yang benar).

Namun, jika kita memikirkan matriks umum blok-diagonal Diagonal{Matrix{T}} , atau matriks-blok Matrix{Matrix{T}} , maka secara umum kita dapat berbicara tentang determinannya sebagai skalar T . Yaitu jika ukurannya (3 ⊕ 4) × (3 ⊕ 4) (matriks 2 × 2 dengan elemen diagonal yang merupakan matriks dengan dimensi 3 × 3, 4 × 4, dan elemen off-diagonal yang cocok), harus det mengembalikan determinan dari struktur 7 × 7 yang "diratakan", atau haruskah ia mencoba dan mengalikan elemen 2 × 2 sebagaimana adanya (dan error-out, dalam kasus ini)?

(edit: Saya mengubah dimensi di atas menjadi semua berbeda, yang seharusnya menghindari bahasa yang ambigu)

Saya tidak punya masalah dengan a' menjadi rekursif, tapi saya pribadi akan menemukan notasi a.' sangat membingungkan. Jika Anda menunjukkan sintaks a.' saya akan memberi tahu Anda, berdasarkan model mental Julia, bahwa ia melakukan transpose.(a) yaitu transposisi elemen a , dan saya akan sepenuhnya salah. Atau, jika Anda menunjukkan kepada saya bahwa a' dan a.' keduanya merupakan opsi untuk transpose, dan salah satunya juga berulang secara elementwise, saya akan memberi tahu Anda bahwa a.' harus memiliki rekursi elementwise, dan sekali lagi saya akan salah.

Tentu saja, model mental saya tentang apa artinya . salah dalam kasus ini. Tapi saya curiga saya bukan satu-satunya yang akan menafsirkan sintaks itu dengan cara yang salah. Untuk itu, saya sarankan menggunakan sesuatu selain .' untuk transpos non-rekursif.

@rdeits Saya khawatir sintaks ini berasal dari MATLAB. Ini menjadi sedikit disayangkan setelah sintaks siaran dot-call (cukup bagus) diperkenalkan untuk v0.5.

Kita bisa menempa jalur kita sendiri terpisah dari MATLAB dan mengubahnya - tapi saya yakin ada diskusi terpisah tentang itu (ada yang ingat di mana?).

Ah, itu sangat disayangkan. Terima kasih!

Hal lain yang harus dilakukan:

  • [] Perbaiki issymmetric dan ishermitian agar cocok dengan transpose dan adjoint - yang sebelumnya dari setiap pasangan bersifat non-rekursif, yang terakhir dari setiap pasangan bersifat rekursif.

Sintaks Matlab .' jelas sangat disayangkan dalam konteks sintaks . . Saya tidak akan menentang perubahan ini, tetapi kemudian kami memerlukan sintaks baru untuk transpos, dan saya tidak yakin kami memiliki yang tersedia. Ada yang punya saran untuk transpos?

Mari pindahkan diskusi sintaks transpose ke sini: https://github.com/JuliaLang/julia/issues/21037.

Saya tidak memiliki pendapat yang kuat tentang transpose / ctranspose / adjoint menjadi rekursif, tetapi saya lebih suka tidak memperlakukan A::Matrix{Matrix{T}} seperti matriks blok di rasa malas hvcat , yang tampaknya disiratkan @andyferris setidaknya sebagian. Artinya, jika elemen A adalah semua matriks persegi dengan ukuran yang sama (yaitu membentuk cincin), saya mengharapkan det(A) untuk mengembalikan kuadrat dengan ukuran itu lagi. Jika mereka persegi panjang atau ukuran yang berbeda, saya mengharapkan kesalahan.

Meskipun demikian, jenis matriks blok / kucing malas mungkin berguna, tetapi harus diterapkan sepenuhnya dan juga menentukan misalnya getindex untuk mengerjakan data yang diratakan. Tapi saya pasti tidak ingin konsep ini diserap oleh Matrix atau jenis matriks lain yang ada seperti Diagonal .

Ini melegakan, @martinholters. Apa yang saya panik sebelumnya di utas ini adalah gagasan bahwa kita harus dapat menerapkan seluruh kerangka kerja aljabar linier Julia ke Matrix{Matrix} untuk matriks blok arbitrer (di mana submatrices memiliki ukuran yang berbeda).

Apa yang saya perdebatkan dengan "perataan" adalah tidak melakukan introspeksi mewah apa pun elemennya, dan hanya memperlakukan elemen tersebut berdasarkan kemampuannya sendiri sebagai elemen cincin. Namun, rekursif ctranspose / adjoint memperluas ini untuk memungkinkan elemen yang bertindak seperti operator linier, yang tampaknya benar dan berguna. (Kasus lainnya adalah elemen vektor yang bertindak seperti vektor, di mana adjoint rekursif juga benar).

Untuk mundur lebih jauh - apa motivasi asli untuk menghapus perilaku tanpa operasi default untuk transpose(x) = x dan ctranpsose(x) = conj(x) ? Ini selalu tampak sangat berguna bagi saya.

Untuk kembali sedikit lebih jauh - apa motivasi asli untuk menghapus perilaku no-op default untuk transpose (x) = x dan ctranpsose (x) = conj (x)? Ini selalu tampak sangat berguna bagi saya.

Itu dimotivasi oleh jenis operator linier khusus (yang tidak bisa menjadi subtipe dari AbstractArray) yang gagal mengkhususkan ctranspose . Ini berarti bahwa mereka mewarisi perilaku tanpa operasi yang salah dari fallback. Kami telah mencoba menyusun pengiriman sedemikian rupa sehingga fallback tidak pernah salah secara diam-diam (tetapi mungkin memiliki kompleksitas pesimistis). https://github.com/JuliaLang/julia/issues/13171

Kami tampaknya memiliki dua opsi - di keduanya, transpose menjadi non-rekursif, jika tidak:

  1. Non-rekursif ctranspose .
  2. Rekursif ctranspose .

@stevengj, @jiahao, @andreasnoack - apa preferensi Anda di sini? Lainnya?

Saya telah banyak memikirkan hal ini sejak @jiahao 's JuliaCon 2017 bicara.

Bagi saya, saya masih merasa aljabar linier harus didefinisikan sehubungan dengan bidang "skalar" sebagai jenis elemen T . Jika T adalah bidang (mendukung + , * dan conj (juga - , / , .. .)) maka saya tidak mengerti mengapa metode di Base.LinAlg harus gagal.

OTOH sangat umum (dan valid) untuk berbicara tentang mengambil transpos dari matriks blok, misalnya. Saya pikir kita bisa belajar dari teori tipe "matematis" di sini, yang misalnya mencoba untuk menangani pernyataan aneh yang timbul dari diskusi set himpunan dengan memiliki set skalar "orde pertama", set skalar "orde dua", set skalar ketiga order "set set skalar, dan sebagainya. Saya pikir kita memiliki masalah yang sama (dan peluang) di sini ketika berurusan dengan array array - kita berpotensi dapat menggunakan sistem tipe Julia untuk menggambarkan array "urutan pertama" dari skalar "benar", array "urutan kedua" dari array skalar, dll. . Saya pikir diskusi panjang antara @stevengj , saya, dan orang lain, dihasilkan dari fakta bahwa, ya, Anda dapat menghasilkan serangkaian operasi yang konsisten pada array "urutan" yang sewenang-wenang, dan dalam kerangka ini (c) transpos adalah rekursif dengan jelas dan benar, tetapi bagaimanapun, Anda tidak perlu melakukan itu. Seperti yang ditunjukkan Jiahao dengan jelas, bahasa pemrograman / kerangka kerja membuat pilihan semantik tentang membedakan skalar dari array (atau tidak), membedakan vektor dari matriks (atau tidak) dan sebagainya, dan saya pikir ini adalah pilihan terkait yang dapat kita buat.

Bagi saya, poin penting di sini adalah agar array "urutan sewenang-wenang" berfungsi, Anda harus mengajarkan "skalar" Anda beberapa operasi yang biasanya hanya ditentukan pada vektor dan matriks. Metode Julia saat ini yang melakukan ini adalah transpose(x::Number) = x . Namun, saya lebih suka jika kita memperluas perbedaan semantik kita antara array dan skalar dengan menghapus metode ini - dan menurut saya itu mungkin cukup layak.

Langkah selanjutnya adalah mengajarkan Base.LinAlg perbedaan antara matriks orde pertama dan matriks orde kedua dan seterusnya. Saya telah memikirkan dua opsi yang layak, mungkin ada lebih banyak, saya benar-benar tidak tahu:

  • Memiliki semacam sifat keikutsertaan untuk "arrayness", sehingga transpose(::AbstractMatrix{T}) adalah rekursif ketika T adalah AbstractMatOrVec , dan bukan ketika T adalah a Number , dan membuatnya dapat dikonfigurasi (opt-in, opt-out, apa pun) sebaliknya. (Dalam beberapa hal ini telah dan saat ini dilakukan dengan mengontrol perilaku metode transpose untuk elemen, tapi saya pikir lebih bersih untuk lebih mengontrol perilaku metode transpose dari (luar ) Himpunan).
  • Cara lainnya, tegaskan bahwa sebagian besar AbstractArray s termasuk Array s adalah urutan pertama (elemennya adalah bidang skalar), dan gunakan jenis AbstractArray (misalnya NestedArray ) untuk membungkus array dan "menandai" bahwa elemennya akan diperlakukan sebagai array, bukan skalar. Saya mencoba bermain-main dengan ini sedikit, tetapi sepertinya opsi di atas mungkin kurang membebani pengguna.

Saya merasa Julia membuat pemisahan semantik yang kuat antara array dan skalar, dan situasi saat ini tampaknya (bagi saya) sedikit tidak konsisten dengan itu, karena skalar harus "diajarkan" tentang properti array transpose . Seorang pengguna dapat dengan jelas mengatakan bahwa Matrix{Float64} adalah matriks skalar (larik orde pertama) dan Matrix{Matrix{Float64}} adalah matriks blok (larik orde dua). Mungkin lebih konsisten bagi kita untuk menggunakan sistem tipe atau sifat untuk menentukan apakah sebuah array adalah "urutan pertama" atau apa pun. Saya juga berpikir opsi pertama yang saya daftarkan akan membuat Julia lebih ramah pengguna (jadi saya dapat melakukan ["abc", "def"].' ) namun tetap memiliki fleksibilitas untuk melakukan beberapa hal default yang masuk akal / berguna dengan matriks blok.

Maaf sudah terlalu lama berbicara, tetapi setelah berbicara dengan @jiahao di JuliaCon, saya merasa kami dapat melakukan sedikit lebih baik di sini. Saya yakin kita dapat membuat opsi lain yang disebutkan oleh @StefanKarpinski di atas "bekerja", tetapi bagi saya itu akan "bekerja" seperti matriks / vektor aljabar "bekerja" sebelum pengenalan RowVector .

TLDR adalah - jangan buat ( c ) transpose menjadi rekursif atau tidak - biarkan ia melakukan keduanya (yaitu dapat dikonfigurasi).

Itu adalah poin yang bagus tetapi hanya ingin menunjukkan bahwa hampir semua (jika tidak semua) keluhan tentang (c)transpose bersifat rekursif terkait dengan penggunaan non-matematis ' dan .' saat membentuk ulang misalnya Vector{String} dan Vector{PyObject} menjadi matriks 1xn. Mudah untuk memperbaiki tipe demi tipe tetapi saya dapat melihat bahwa itu mengganggu. Namun, pemikirannya adalah bahwa definisi rekursif adalah yang benar secara matematis dan bahwa "benar tetapi mengganggu dalam banyak kasus" lebih baik daripada "nyaman tetapi salah dalam kasus yang jarang terjadi".

Solusi yang mungkin bisa menjadi saran Anda di poin pertama, yaitu hanya untuk memiliki transposisi rekursif untuk elemen seperti array. Saya yakin T<:AbstractVecOrMat akan menutupi sebagian besar kasus yang mengubah status menjadi "nyaman tetapi salah dalam kasus yang sangat jarang". Alasan mengapa ini mungkin masih salah adalah bahwa beberapa jenis operator-like tidak akan cocok dalam kategori AbstractMatrix karena antarmuka AbstractArray terutama tentang semantik array ( getindex ) , bukan aljabar linier.

@andyferris , adjoint dan dual dari skalar didefinisikan dengan baik, dan ctranspose(x::Number) = conj(x) benar.

Perasaan saya adalah bahwa sebagian besar penggunaan transpose adalah "non-matematis", dan sebagian besar penggunaan ctranspose (yaitu penggunaan di mana perilaku konjugasi diinginkan) bersifat matematis (karena tidak ada alasan lain untuk mengkonjugasikan ). Oleh karena itu, saya akan cenderung mendukung non-rekursif transpose dan rekursif ctranspose .

Secara pribadi, saya pikir mencoba melihat array blok sebagai Arrays bersarang menjadi rumit karena alasan di sini dan mungkin lebih baik memiliki tipe khusus à la https://github.com/KristofferC/BlockArrays.jl untuk ini.

Kelihatannya bagus, @KoC.

Ada satu poin yang sangat valid yang dibuat oleh AbstractArray s. Kami pasti membutuhkan cara melakukan transpose rekursif (c) untuk ini - tidak yakin apakah Anda telah memikirkan hal ini atau tidak, tetapi saya pikir saya akan menyebutkannya.

Sorotan dari percakapan kendur / # linalg tentang topik ini. Rangkum beberapa utas di atas. Berfokus pada semantik, hindari ejaan. (Terima kasih untuk semua yang berpartisipasi dalam percakapan itu! :))

Ada tiga operasi yang berbeda secara semantik:
1) "adjoint matematis" (rekursif dan malas)
2) "transposisi matematis" (idealnya rekursif dan malas)
3) "transpose struktural" (idealnya non-rekursif dan? Bersemangat?)

Situasi saat ini: "penyambungan matematis" dipetakan ke ctranspose , "pengalihan matematis" ke transpose , dan "pengalihan struktural" ke permutedims(C, (2, 1)) untuk dua dimensi C dan reshape(C, 1, length(C)) untuk satu dimensi C . Masalah: "transposisi struktural" adalah operasi yang umum, dan dalam praktiknya kisah permutedims / reshape agak membingungkan / tidak wajar / mengganggu.

Bagaimana hal itu muncul: Sebelumnya "transpose struktural" digabungkan dengan "adjoint matematika" / "transpose matematis" melalui fallback tanpa operasi umum seperti transpose(x::Any) = x , ctranspose(x::Any) = conj(x) , dan conj(x::Any) = x . Fallback ini menghasilkan [c]transpose dan operator postfix terkait ' / .' berfungsi untuk "transpose struktural" dalam banyak kasus. Bagus. Tetapi mereka juga membuat operasi yang melibatkan [c]transpose pada beberapa tipe numerik yang ditentukan pengguna gagal secara diam-diam (mengembalikan hasil yang salah) definisi yang tidak ada dari [c]transpose spesialisasi untuk tipe tersebut. Aduh. Oleh karena itu, fallback no-op generik telah dihapus, menghasilkan situasi saat ini.

Pertanyaannya adalah apa yang harus dilakukan sekarang.

Hasil yang ideal: Memberikan mantra terpadu, alami, dan kompak untuk "transpos struktural". Secara bersamaan mendukung adjoint dan transpos matematika yang benar secara semantik. Hindari memperkenalkan kasus sudut rumit dalam penggunaan atau penerapan.

Ada dua proposal luas:

(1) Menyediakan adjoint matematika, transpos matematika, dan transpos struktural sebagai tiga operasi yang berbeda secara sintaksis dan semantik. Kelebihan: Membuat semuanya bekerja, memisahkan konsep dengan rapi, dan menghindari kasus sudut yang rumit. Kerugian: Tiga operasi untuk dijelaskan dan diterapkan.

(2) Siapkan tiga operasi menjadi dua. Ada tiga bentuk proposal ini:

(2a) Hasilkan transpose semantik "transposisi struktural", juga berfungsi sebagai "transposisi matematis" dalam kasus umum. Sisi atas: Dua operasi. Berfungsi seperti yang diharapkan untuk container dengan elemen skalar yang jelas. Kekurangan: Siapa pun yang mengharapkan semantik "transposisi matematis" diam-diam akan menerima hasil yang salah pada objek yang lebih kompleks daripada penampung dengan elemen skalar yang jelas. Jika pengguna memahami masalah itu, mencapai semantik "transpose matematis" memerlukan definisi jenis dan / atau metode baru.

(2b) Memperkenalkan sifat yang menunjukkan "matematis" suatu tipe. Saat menerapkan adjoint / transpose ke sebuah objek, jika container / elemen berjenis "mathy", gunakan recurse; jika tidak, jangan berulang. Sisi atas: Dua operasi. Bisa mencakup berbagai kasus umum. Kerugian: Menderita masalah yang sama dengan fallbacks no-op [c]transpose generik, yaitu diam-diam dapat menghasilkan hasil yang salah untuk tipe numerik yang ditentukan pengguna yang tidak memiliki definisi sifat yang diperlukan. Tidak mengaktifkan penerapan transpose struktural ke jenis "mathy" atau transpos matematika ke jenis "non-mathy". Tidak jelas bagaimana memutuskan apakah suatu objek "matematis" dalam semua kasus. (Misalnya, bagaimana jika tipe elemennya abstrak? Apakah keputusan rekursif / non-rekursif kemudian runtime dan elementwise, atau runtime dan memerlukan sapuan pertama atas semua elemen dalam container untuk memeriksa tipe kolektifnya?)

(2c) Simpan adjoint ( ctranspose ) "adjoint matematika" dan transpose "matematis transpose", dan perkenalkan metode khusus (bukan penggantian umum) untuk adjoint / transpose untuk jenis skalar non-numerik (mis. adjoint(s::AbstractString) = s ). Sisi atas: Dua operasi. Benar semantik matematika. Kerugian: Membutuhkan definisi sedikit demi sedikit dari adjoint / transpose untuk tipe non-numerik, menghalangi pemrograman generik. Tidak mengaktifkan penerapan transpos struktural ke tipe "mathy".

Proposal (1) dan (2a) menikmati dukungan yang jauh lebih luas daripada (2b) dan (2c).

Temukan lebih banyak diskusi di # 19344, # 21037, # 13171, dan slack / # linalg. Terbaik!

Terima kasih atas artikel yang bagus!

Saya ingin meletakkan kemungkinan lain di atas tabel yang akan cocok dengan opsi 1: Memiliki jenis wadah yang berbeda untuk matriks matematika dan data tabel. Maka arti dari A' bisa ditentukan oleh tipe A (catatan: bukan tipe elemennya seperti yang dibahas di atas). Kontra di sini adalah bahwa ini mungkin memerlukan banyak convert s antara keduanya (bukan?) Dan tentu saja, akan sangat mengganggu. Memang, saya lebih dari skeptis apakah manfaatnya akan membenarkan gangguan ini, tetapi saya tetap ingin menyebutkannya.

Terima kasih, artikel yang bagus. Saya memilih (1).

Tulisan yang bagus. Perhatikan bahwa untuk (1) ejaannya bisa jadi:

  • Adjoint: a' (rekursif, malas)
  • Transpos matematika: conj(a') (rekursif, malas)
  • Transpos struktural: a.' (non-rekursif, bersemangat)

Jadi tidak perlu ada operator baru yang diperkenalkan - transpos matematika hanya akan menjadi komposisi adjoint dan konjugasi (malas dan karena itu efisien). Masalah terbesar adalah bahwa itu membuat dua operator yang tampak serupa, yaitu postfix ' dan .' , sangat berbeda secara semantik. Namun, saya akan mengandaikan bahwa di sebagian besar kode umum yang benar apakah seseorang telah menggunakan ' atau .' adalah indikator akurat 99% apakah yang mereka maksud "beri saya penyambung dari benda ini" atau "tukar dimensi benda ini ". Selain itu, dalam kasus di mana seseorang telah menggunakan ' dan sebenarnya bermaksud "menukar dimensi benda ini", kode mereka akan menjadi salah untuk matriks apa pun dengan elemen yang adjoint skalarnya tidak sepele, misalnya bilangan kompleks. Dalam beberapa kasus yang tersisa di mana seseorang benar-benar bermaksud "beri saya konjugasi dari adjoint ini", saya berpendapat bahwa menulis conj(a') membuat arti itu jauh lebih jelas karena dalam praktiknya orang benar-benar menggunakan a.' untuk berarti "menukar dimensi a ".

Bekerja keluar semua jenis yang mendasari generik akan kami butuhkan untuk tetap ini akan ditentukan, namun @andyferris dan @andreasnoack sudah memiliki beberapa pemikiran tentang masalah ini dan tampaknya mungkin.

Saya pikir saya juga harus memperbarui, karena diskusi ini berlanjut di tempat lain. Saya akui bahwa ada satu hal (jelas?) Tentang proposal ini yang benar-benar saya lewatkan, yaitu saya berasumsi bahwa kami akan terus menggunakan .' untuk aljabar linier, tetapi saya seharusnya menyadari bahwa bukan ini masalahnya !

Misalnya, vector.' tidak lagi perlu menjadi RowVector (karena RowVector adalah konsep aljabar linier, percobaan pertama kita pada vektor "ganda") - dapat dengan mudah jadilah Matrix . Ketika saya ingin "non-konjugasi transpose" dalam aljabar linier, apa yang saya benar-benar lakukan adalah mengambil conj(adjoint(a)) , dan bahwa ini apa yang akan kita gunakan dan merekomendasikan semua pengguna aljabar linier untuk digunakan (sampai sekarang saya punya memiliki kebiasaan "buruk" yang sudah berlangsung lama sejak MATLAB hanya menggunakan a.' daripada a' untuk mentransposisi matriks (atau vektor) apa pun yang saya tahu nyata, padahal yang saya inginkan adalah adjoint (atau dual) - perubahan nama akan sangat membantu di sini).

Saya juga akan menunjukkan secara singkat bahwa ini membuka ruang yang menarik. Sebelumnya vector' dan vector.' harus memenuhi kebutuhan ganda melakukan "transpose data" dan mengambil sesuatu seperti vektor ganda. Sekarang .' adalah untuk memanipulasi ukuran array dan ' adalah konsep aljabar linier, kita mungkin dapat mengubah RowVector menjadi 1D DualVector atau yang lainnya sepenuhnya. (mungkin kita sebaiknya tidak membahas ini di sini - jika ada yang tertarik dengan hal ini, mari buat masalah terpisah.)

Terakhir, saya akan menyalin rencana tindakan yang diusulkan dari Slack:

1) pindahkan transpose dari LinAlg menjadi Base dan tambahkan depwarns (hanya 0.7) tentang hal itu tidak lagi menghasilkan RowVector atau menjadi rekursif (jika memungkinkan)
2) ganti nama ctranspose menjadi adjoint mana-mana
3) pastikan Vector , ConjVector dan RowVector berfungsi dengan kombinasi ' dan conj dan * , mungkin ganti nama RowVector . (disini kita juga menghasilkan conj(vector) malas).
4) perkenalkan adjoint matriks malas yang juga berinteraksi dengan baik dengan conj dan ConjMatrix
5) hapus A_mul_Bc dll. Ubah nama A_mul_B! menjadi mul! (atau *! ).
6) keuntungan

@stevengj menulis:

@andyferris , adjoint dan dual dari skalar didefinisikan dengan baik, dan ctranspose(x::Number) = conj(x) benar.

Sebagai catatan, saya sangat setuju dengan ini.

Perasaan saya adalah bahwa sebagian besar penggunaan transpose adalah "non-mathy", dan sebagian besar penggunaan ctranspose (...) adalah mathy

Jadi idenya adalah untuk memformalkan ini: semua penggunaan transpose menjadi "non-mathy" dan satu-satunya penggunaan adjoint akan menjadi "mathy".

Misalnya, vector.' tidak lagi perlu menjadi RowVector (karena RowVector adalah konsep aljabar linier, percobaan pertama kita pada vektor "ganda") - ini dapat dengan mudah jadilah Matrix .

Kami masih ingin .' menjadi malas. misal, X .= f.(x, y.') seharusnya tetap non-alokasi.

Inilah pertanyaannya: Bagaimana seharusnya kita menerapkan issymmetric(::AbstractMatrix{<:AbstractMatrix}) ? Apakah ini akan menjadi cek non-rekursif, untuk mencocokkan transpose ? Saya melihat implementasinya; itu mengasumsikan bahwa elemen dapat transpose . OTOH tampaknya cukup valid untuk memeriksa apakah issymmetric(::Matrix{String}) ...

Saat ini tidak rekursif jika dibungkus dengan Symmetric btw

julia> A = [rand(2, 2) for i in 1:2, j in 1:2]; A[1, 2] = A[2, 1]; As = Symmetric(A);

julia> issymmetric(A)
false

julia> issymmetric(As)
true

julia> A == As
true

Ya, saya sedang membuat PR untuk ini dan ada banyak inkonsistensi semacam ini. (Saya menemukan yang satu itu belum lama ini ketika mencari setiap contoh transpose , adjoint dan conj dalam kode array).

Kecuali jika diarahkan, saya akan menerapkan perilaku seperti issymmetric(a) == (a == a.') dan ishermitian(a) == (a == a') . Ini tampaknya cukup intuitif dalam dirinya sendiri dan penggunaan AFAICT yang ada dari issymmetric adalah untuk Number jenis elemen (atau sering kali memiliki kesalahan / asumsi lain yang tidak akan masuk akal untuk array bersarang) .

(Implementasi alternatifnya adalah issymmetric(a) == (a == conj(adjoint(a))) ... tidak sebagai "cantik" juga tidak akan berfungsi untuk array "data" (dari String , dll), tetapi bertepatan pada array Number )

Kecuali diarahkan sebaliknya, saya akan menerapkan perilaku seperti issymmetric(a) == (a == a.') dan ishermitian(a) == (a == a')

Sepertinya solusi yang tepat untuk saya.

Pembaruan: Berubah pikiran. Simetris mungkin terutama untuk aljabar linier

Hanya saran kecil: sering kali berguna untuk melakukan penjumlahan atas produk dua vektor ( dotu ), dan x.’y mengembalikan skalar adalah cara mudah untuk melakukan ini. Jadi saya lebih memilih untuk tidak mengembalikan Matrix dari transpose(::Vector)

Ya, itu akan menjadi RowVector jadi Anda harus mendapatkan skalar. (Perhatikan bahwa transpos tidak akan rekursif).

Maaf untuk komentar yang mungkin menyinggung, tetapi banyak orang di utas ini terus berbicara tentang "cincin skalar ruang vektor". Menurut definisi, skalar ruang vektor harus membentuk bidang, bukan sembarang cincin. Struktur aljabar yang sangat mirip dengan ruang vektor tetapi skalarnya hanya membentuk cincin daripada bidang disebut "modul", tetapi menurut saya modul agak terlalu esoteris untuk ditangani di Basis ... er. .. modul.

Karena kami mendukung array integer, kami secara efektif mendukung modul, bukan hanya spasi vektor. Tentu saja, kita juga bisa melihat array integer seperti yang disematkan dalam array floating-point secara perilaku, jadi mereka adalah representasi parsial dari ruang vektor. Tapi kita juga bisa membuat array integer modular (misalnya), dan dengan modulus non-prime kita akan bekerja dengan cincin yang tidak tertanam secara alami di bidang apa pun. Singkatnya, kami benar-benar ingin mempertimbangkan modul secara umum daripada hanya ruang vektor, tetapi menurut saya tidak ada perbedaan yang signifikan untuk tujuan kami (kami umumnya hanya berbicara tentang + dan * ) jadi "ruang vektor" berfungsi sebagai singkatan yang lebih dikenal untuk "modul" untuk tujuan kita.

Ya, Julia memang (dan harus) mendukung operasi aljabar pada modul umum serta ruang vektor yang sebenarnya. Tapi seperti yang saya pahami, filosofi umum komunitas adalah bahwa fungsi dalam Base harus dirancang dengan "rutinitas aljabar linier generik 'biasa'" dalam pikiran - jadi, misalnya, perhitungan aljabar linier yang tepat pada matriks bilangan bulat tidak tidak termasuk dalam Base - jadi ketika membuat keputusan desain yang mendasar, kita harus menganggap bahwa skalar membentuk sebuah field. (Meskipun seperti yang Anda katakan, secara praktis, itu tidak terlalu penting.)

Crossposting https://github.com/JuliaLang/julia/pull/23424#issuecomment -346678279

Saya sangat menghargai upaya untuk memajukan # 5332 dan # 20978 yang diwakili oleh permintaan tarik ini, dan saya setuju dengan sebagian besar rencana terkait. Saya sangat ingin juga memahami pilihan terminologi dan dampak hilir yang dimajukan oleh permintaan tarik ini, dan secara teratur mencoba meyakinkan diri sendiri bahwa pilihan tersebut menghasilkan rangkaian pengorbanan terbaik yang tersedia.

Setiap kali saya mencoba meyakinkan diri saya sendiri tentang posisi itu, saya kandas pada serangkaian perasaan was-was yang sama. Salah satu keraguan itu adalah kompleksitas implementasi substansial yang dipaksakan pilihan ini pada LinAlg : Transpos yang digabung dan gaya flip-array LinAlg untuk menangani keduanya, membutuhkan LinAlg mendukung set yang jauh lebih besar jenis kombinasi dalam operasi umum.

Sebagai ilustrasi, pertimbangkan mul(A, B) mana A dan B kosong, dibungkus adjoint, dibungkus transpose, atau dibungkus array Matrix s. Tanpa menggabungkan transpose dan array-flip, A bisa menjadi Matrix , Matrix dibungkus dengan adjoin, atau Matrix dibungkus transpose (dan juga B ). Jadi mul(A, B) membutuhkan dukungan sembilan jenis kombinasi. Tapi conflating transpos dan berbagai-flip, A bisa menjadi Matrix , sebuah adjoint dibungkus Matrix , sebuah transpose dibungkus Matrix , atau array- dibungkus balik Matrix (dan juga B ). Jadi sekarang mul(A, B) membutuhkan dukungan enam belas jenis kombinasi.

Masalah ini memburuk secara eksponensial dengan jumlah argumen. Misalnya, tanpa penggabungan mul!(C, A, B) memerlukan dukungan dua puluh tujuh jenis kombinasi, sedangkan dengan penggabungan mul!(C, A, B) membutuhkan dukungan enam puluh empat jenis kombinasi. Dan tentu saja menambahkan tipe Vector s dan non- Matrix matrix / operator ke dalam campuran semakin memperumit masalah.

Efek samping ini tampaknya patut dipertimbangkan sebelum melanjutkan perubahan ini. Terbaik!

Posting ini mengkonsolidasikan / meninjau diskusi terbaru tentang github, slack, dan triage, dan menganalisis kemungkinan jalur ke depan. (Penerus spiritual dari https://github.com/JuliaLang/julia/issues/20978#issuecomment-315902532 lahir dari pemikiran tentang cara mencapai 1.0.)

Tiga operasi yang berbeda secara semantik sedang dipermasalahkan:

  • adjoint (aljabar linier, rekursif, idealnya malas secara default)
  • mengubah urutan (aljabar linier, rekursif, idealnya malas secara default)
  • array-flip (abstrak-array-ic, non-rekursif, idealnya? malas? secara default)

Status operasi ini di master

  • Adjoint disebut adjoint / ' (tetapi sangat ingin terlepas dari ' - ekspresi yang terlibat secara khusus diturunkan menjadi A[c|t]_(mul|rdiv|ldiv)_B[c|t][!] panggilan, yang menghindari adjoint / transposes yang bersemangat menengah) .

  • Transposisi disebut transpose / .' (dengan peringatan yang sama seperti adjoint ).

  • Array-flip disebut permutedims(C, (2, 1)) untuk dua dimensi C dan reshape(C, 1, length(C)) untuk satu dimensi C .

Masalah yang relevan

  1. 5332: Penurunan khusus ke A[c|t]_(mul|rdiv|ldiv)_B[c|t] dan kumpulan kombinatorial terkait dari nama metode harus menghilang sebesar 1.0. Menghapus penurunan khusus / nama metode terkait membutuhkan penyambungan dan transposisi yang lambat.

  2. 13171: Adjoint (née ctranspose ) dan transpose sebelumnya digabungkan dengan array-flip melalui fallback no-op generik seperti transpose(x::Any) = x , ctranspose(x::Any) = conj(x) , dan conj(x::Any) = x . Fallback ini membuat operasi yang melibatkan [c]transpose pada beberapa tipe numerik yang ditentukan pengguna gagal secara diam-diam (mengembalikan hasil yang salah) tanpa spesialisasi [c]transpose untuk tipe tersebut. Mengembalikan hasil yang salah secara diam-diam adalah kabar buruk, jadi penggantian tersebut telah dihapus (# 17075). Tidak memperkenalkan lebih banyak kegagalan diam-diam akan lebih bagus.

  3. 17075, # 17374, # 19205: Menghapus fallback sebelumnya membuat array-flip kurang nyaman. Dan ditambah dengan (maaf, salah saya) peringatan penghentian terkait yang kurang bagus, bahwa penghapusan (sementara?) Menyebabkan keluhan. Mantra yang lebih nyaman untuk membalik larik akan menyenangkan.

  4. 21037: Menghapus sintaks .' untuk 1.0 yang sekarang membingungkan akan menyenangkan. Penurunan khusus yang disebutkan di atas harus dihilangkan untuk mengaktifkan perubahan ini.

Tujuan desain untuk menyelesaikan masalah ini

Hapus penurunan khusus dan nama metode terkait, hindari memperkenalkan kegagalan diam, berikan mantera yang intuitif dan nyaman untuk transpose dan array flip, dan hapus .' . Capai sebelumnya dengan kerumitan dan kerusakan tambahan yang minimal.

Proposal desain

Bidang tersebut telah dipangkas menjadi dua proposal desain:

  1. Panggil adjoint adjoint , ubah urutan conjadjoint ("conjugate adjoint"), dan array-flip transpose . adjoint dan conjadjoint tinggal di LinAlg , dan transpose tinggal di Base .

  2. Panggil adjoint adjoint , ubah urutan transpose , dan array-flip flip . adjoint dan transpose tinggal di LinAlg , dan flip tinggal di Base .

Sekilas proposal ini tampak hanya berbeda secara dangkal. Tetapi pemeriksaan lebih lanjut mengungkapkan perbedaan praktis yang dalam. Menghindari diskusi tentang manfaat relatif dangkal dari skema penamaan ini, mari kita lihat perbedaan praktis yang dalam tersebut.

Perbedaan, tampilan tingkat tinggi

  1. Kompleksitas:

    Proposal pertama, dengan memanggil array-flip transpose , memaksa LinAlg untuk menangani array-flip sebagai tambahan untuk transpose dan adjoint. Akibatnya, LinAlg harus secara substansial memperluas kumpulan kombinasi tipe yang didukung oleh operasi umum; https://github.com/JuliaLang/julia/pull/23424#issuecomment -346678279 menggambarkan kompleksitas tambahan ini sebagai contoh, dan pembahasan berikut secara implisit menegaskan keberadaan kompleksitas tambahan tersebut.

    Proposal dua membutuhkan LinAlg hanya mendukung transpose dan adjoint, yang sekarang digunakan LinAlg .

  2. Kerusakan:

    Proposal satu mengubah semantik dasar dari operasi yang ada: transpose menjadi array-flip daripada transpose, dan semua fungsionalitas yang berhubungan dengan transpose harus berubah. (Misalnya, semua operasi perkalian dan pembagian kiri / kanan di LinAlg terkait dengan nama transpose akan membutuhkan revisi semantik.) Bergantung pada bagaimana perubahan ini direalisasikan, perubahan ini menyebabkan kerusakan diam-diam dimanapun semantik saat ini diandalkan (sengaja atau tidak sengaja).

    Proposal dua mempertahankan semantik dasar dari semua operasi yang ada.

  3. Kopel:

    Proposal satu membawa konsep aljabar linier (transpos) menjadi Base , dan konsep array-abstrak (array-flip) menjadi LinAlg , menggabungkan Base dan LinAlg .

    Proposal dua dengan rapi memisahkan hal-hal larik abstrak dan aljabar linier, memungkinkan yang pertama untuk hidup hanya dalam basis dan yang terakhir hanya dalam LinAlg , tanpa kopling baru.

  4. Kegagalan senyap vs. keras:

    Proposal satu mengarah ke hasil yang salah secara diam-diam ketika seseorang memanggil transpose mengharapkan semantik transpose (tetapi sebaliknya mendapatkan semantik array-flip).

    Analog di bawah proposal dua memanggil transpose pada array non-numerik yang mengharapkan semantik array-flip. Dalam kasus ini, transpose dapat membuat kesalahan yang membantu mengarahkan pengguna ke flip .

  5. .' : Argumen utama untuk tidak membatalkan .' adalah panjang transpose . Proposal satu menggantikan .' dengan nama transpose dan conjadjoint , yang tidak memperbaiki situasi tersebut. Sebaliknya, proposal dua memberikan nama flip dan transpose , memperbaiki situasi tersebut.

Perbedaan jalur ke 1.0

Apa yang dilakukan untuk mencapai 1.0 dalam setiap proposal? Path ke 1.0 lebih sederhana pada proposal dua, jadi mari kita mulai dari sana.

Jalur ke 1.0 di bawah proposal dua

  1. Perkenalkan lazy adjoint dan transpose jenis pembungkus di LinAlg , katakan Adjoint dan Transpose . Perkenalkan mul[!] / ldiv[!] / rdiv[!] metode pengiriman pada jenis pembungkus dan berisi kode metode A[c|t]_{mul|ldiv|rdiv}_B[c|t][!] sesuai. Menerapkan kembali metode terakhir sebagai anak pendek dari metode sebelumnya.

    Langkah ini tidak merusak apa pun, dan segera mengaktifkan penghapusan penurunan dan penghentian khusus .' :

  2. Hapus penurunan khusus yang menghasilkan A[c|t]_{mul|ldiv|rdiv}_B[c|t] , alih-alih hanya menurunkan ' / .' menjadi Adjoint / Transpose ; ekspresi sebelumnya-khusus-diturunkan menghasilkan A[c|t]_{mul|ldiv|rdiv}_B[c|t] panggilan kemudian menjadi setara mul / ldiv / rdiv panggilan. Abaikan A[c|t]_{mul|ldiv|rdiv}_B[c|t][!] ke metode yang sesuai mul[!] / ldiv[!] / rdiv[!] . Singkirkan .' .

    Langkah-langkah ini bisa dilakukan di 0.7. Mereka hanya mematahkan dua hal: (1) Kode yang mengandalkan penurunan khusus untuk mencapai A[c|t]_{mul|ldiv|rdiv}_B[c|t] metode untuk non- Base / LinAlg akan rusak. Kode tersebut akan mengeluarkan MethodError s eksplisit yang menunjukkan ke mana hasil penurunan yang baru / ke mana kode yang rusak perlu dimigrasi. (2) Kode yang mengandalkan pada ' s / .' s yang diisolasi akan rusak. Mode kegagalan umum juga harus eksplisit MethodError s. Di sekelilingnya, kerusakan terbatas dan keras.

    Dan itu saja untuk perubahan yang sangat diperlukan untuk 1.0.

    Pada titik ini, Adjoint(A) / Transpose(A) akan menghasilkan adjoint dan transpose malas, dan adjoint(A) / transpose(A) akan menghasilkan adjoint dan transpose yang bersemangat. Nama yang terakhir dapat tetap tanpa batas waktu atau, jika diinginkan, tidak digunakan lagi untuk ejaan lain di 0.7, misalnya eagereval(Adjoint(A)) / eagereval(Transpose(A)) modulo ejaan eagereval atau eageradjoint(A) / eagertranspose(A) . Dalam kasus deprecation, adjoint / transpose kemudian dapat digunakan kembali di 1.0 (meskipun dengan Adjoint(A) / Transpose(A) , saya tidak yakin apakah itu akan menjadi perlu).

    Akhirnya...

  3. Perkenalkan flip dan / atau Flip dalam Base . Sebagai tambahan fitur, perubahan ini dapat terjadi di 1.x jika perlu.

Jalur ke 1.0 di bawah proposal satu

Bagaimana dengan proposal satu? Di bawah ini menguraikan dua jalur yang mungkin. Jalur pertama mengkonsolidasikan perubahan di 0,7 tetapi melibatkan kerusakan diam-diam. Jalur kedua menghindari kerusakan diam, tetapi melibatkan lebih banyak perubahan 0,7-> 1,0. Kedua garis besar terus mengembangkan basis kode; padanan yang kurang kontinu dapat mengkonsolidasikan / menghindari beberapa pekerjaan / churn, tetapi kemungkinan besar akan lebih menantang dan rawan kesalahan. Pekerjaan yang sedang berlangsung tampaknya mengikuti jalur pertama.

Jalur pertama di bawah proposal satu (dengan kerusakan diam-diam)
  1. Ubah semantik transpose dari transpose ke array-flip, dan juga semantik dari semua fungsionalitas yang berhubungan dengan transpose . Misalnya, semua metode A[c|t]_{mul|ldiv|rdiv}_B[c|t][!] mulai At_... atau diakhiri ..._Bt[!] kemungkinan memerlukan revisi semantik. Juga harus mengubah misalnya definisi dan perilaku Symmetric / issymmetric . Pindahkan transpose itu sendiri ke Base .

    Perubahan ini akan secara diam-diam dan meluas.

  2. Perkenalkan conjadjoint dalam LinAlg . Langkah ini memerlukan pemulihan semua metode yang disentuh pada langkah sebelumnya, tetapi dalam bentuk semantik aslinya, dan sekarang dengan nama berbeda yang dikaitkan dengan conjadjoint (katakanlah Aca_... dan ..._Bca[!] nama) . Juga memerlukan penambahan metode untuk kombinasi tipe tambahan yang secara bersamaan mendukung array-flip (sekarang transpose ), transpose (sekarang conjadjoint ), dan menambahkan LinAlg membutuhkan (misalnya ca varian antara A[c|t|ca]_{mul|ldiv|rdiv}_B[c|t|ca][!] ).

  3. Perkenalkan lazy adjoint dan transpose (disebut conjadjoint ) di LinAlg , katakan Adjoint dan ConjAdjoint . Perkenalkan tipe pembungkus lazy array-flip (disebut transpose ) di Base , katakan Transpose . Perkenalkan mul[!] / ldiv[!] / rdiv[!] metode pengiriman pada jenis pembungkus tersebut dan berisi kode metode A[c|t|ca]_{mul|ldiv|rdiv}_B[c|t|ca][!] sesuai. Menerapkan kembali metode terakhir sebagai anak pendek dari metode sebelumnya.

  4. Hapus penurunan khusus yang menghasilkan A[c|t]_{mul|ldiv|rdiv}_B[c|t] , alih-alih hanya menurunkan ' / .' menjadi Adjoint / Transpose ; ekspresi sebelumnya-khusus-diturunkan menghasilkan A[c|t]_{mul|ldiv|rdiv}_B[c|t] panggilan kemudian menjadi setara mul / ldiv / rdiv panggilan (meskipun ingat bahwa semantik akan diam-diam berubah). Abaikan A[c|t]_{mul|ldiv|rdiv}_B[c|t][!] ke metode yang sesuai mul[!] / ldiv[!] / rdiv[!] . Demikian pula, hapus metode Aca_... / ...Bca[!] yang baru-baru ini diperkenalkan untuk mendukung mul[!] / ldiv[!] / rdiv[!] setara. Singkirkan .' .

Perubahan ini harus dilakukan di 0,7. Perubahan semantik pada operasi yang ada akan menghasilkan kerusakan yang luas dan senyap. Penghapusan penurunan khusus akan menghasilkan kerusakan yang terbatas dan keras seperti yang dijelaskan di atas.

Pada titik ini Adjoint(A) / Transpose(A) / ConjAdjoint(A) masing-masing akan menghasilkan lazy adjoint, array-flip, dan transpose, dan adjoint(A) / transpose(A) / conjadjoint(A) masing-masing akan menghasilkan adjoint, array-flip, dan transpose yang bersemangat. Nama terakhir bisa tetap tanpa batas waktu atau, jika diinginkan, tidak digunakan lagi untuk beberapa ejaan lain juga di 0.7 (ref. Di atas).

Seseorang dapat memperkenalkan ConjAdjoint sebelumnya dalam proses ini untuk mengkonsolidasikan / menghindari beberapa pekerjaan / churn, meskipun kemungkinan pendekatan itu akan lebih menantang dan rawan kesalahan.

Jalur kedua di bawah proposal satu (menghindari kerusakan diam-diam)
  1. Perkenalkan bersemangat conjadjoint dalam LinAlg . Migrasikan semua fungsionalitas dan metode yang saat ini terkait dengan transpose (termasuk misalnya A[c|t]_{mul|ldiv|rdiv}_B[c|t][!] metode yang melibatkan t ) ke conjadjoint dan nama turunan. Buat semua nama yang berhubungan dengan transpose anak pendek dari setara conjadjoint . Abaikan semua nama yang berhubungan dengan transpose menjadi conjadjoint setara.

  2. Perkenalkan jenis pembungkus lazy adjoint dan transpose (disebut conjadjoint ) di LinAlg , katakan Adjoint dan ConjAdjoint . Perkenalkan mul[!] / ldiv[!] / rdiv[!] metode pengiriman pada jenis pembungkus tersebut dan berisi kode metode A[c|ca]_{mul|ldiv|rdiv}_B[c|ca][!] sesuai. Menerapkan kembali metode terakhir sebagai anak pendek dari metode sebelumnya.

  3. Perkenalkan tipe lazy array-flip wrapper di Base , yang disebut Transpose (agak membingungkan, karena secara bersamaan transpose tidak digunakan lagi menjadi conjadjoint dengan transpose semantik). Tambahkan semua metode umum yang diperlukan agar array-flip ( Transpose ) berfungsi ke Base . Kemudian tambahkan metode ke LinAlg untuk semua kombinasi tipe tambahan yang secara bersamaan mendukung array-flip (sekarang Transpose , tetapi bukan transpose ), ubah urutan (sekarang conjadjoint dan ConjAdjoint ), dan menambahkan LinAlg membutuhkan (misalnya mul[!] / rdiv[!] / ldiv[!] setara dengan A[c|t|ca]_{mul|ldiv|rdiv}_B[c|t|ca][!] yang saat ini tidak ada).

  4. Hapus penurunan khusus yang menghasilkan A[c|t]_{mul|ldiv|rdiv}_B[c|t] , alih-alih hanya menurunkan ' / .' menjadi Adjoint / Transpose ; ekspresi yang sebelumnya-khusus-diturunkan menghasilkan A[c|t]_{mul|ldiv|rdiv}_B[c|t] panggilan kemudian menjadi setara mul / ldiv / rdiv panggilan. Abaikan A[c|t]_{mul|ldiv|rdiv}_B[c|t][!] ke metode yang sesuai mul[!] / ldiv[!] / rdiv[!] . Singkirkan .' .

Perubahan sebelumnya harus dilakukan di 0,7. Tidak ada kerusakan diam, hanya kerusakan keras dari penghapusan penurunan khusus seperti dijelaskan di atas.

Pada titik ini Adjoint(A) / Transpose(A) / ConjAdjoint(A) masing-masing akan menghasilkan lazy adjoint, array-flip, dan transpose. adjoint(A) / conjadjoint(A) masing-masing akan menghasilkan adjoint dan transpose yang menarik. transpose(A) akan ditinggalkan menjadi conjadjoint ; transpose dapat digunakan kembali di 1.0 jika diinginkan. adjoint bisa tetap tanpa batas waktu atau tidak digunakan lagi untuk mendukung ejaan lain di 0.7. Cara lain untuk mengeja conjadjoint bisa langsung menjadi 0.7.

Seseorang bisa mengkonsolidasikan langkah 1 dan 2, menghindari beberapa pekerjaan / churn, meskipun kemungkinan pendekatan itu akan lebih menantang dan rawan kesalahan.

#

Terima kasih sudah membaca!

Terima kasih untuk artikel yang bagus. Saya biasanya juga setuju dengan proposal 2, dengan alasan tambahan bahwa adjoint konjugasi sama sekali bukan terminologi standar dan saya pribadi merasa sangat membingungkan (karena adjoint kadang-kadang digunakan sebagai terminologi untuk transpose polos di cabang matematika tertentu, dan konjugasi kata sifat tambahan tampaknya menyiratkan bahwa konjugasi terjadi).

Saya mencoba membaca diskusi rumit di atas, tetapi gagal untuk melihat pada titik mana diputuskan / dimotivasi bahwa matematika transpose harus rekursif. Ini sepertinya mengikuti dari beberapa diskusi pribadi (di suatu tempat antara 14 dan 18 Juli), setelah itu tiba-tiba transpose matematika dan struktural diperkenalkan.
@ Sacha0 mendeskripsikannya sebagai

Bagaimana itu muncul: Sebelumnya "transpose struktural" digabungkan dengan "adjoint matematika" / "transpos matematika"

Saya setuju (struktural) transpos digabungkan dengan "adjoint", tetapi di sini "transpos matematika" muncul entah dari mana? Untuk kelengkapan / kemandirian, dapatkah konsep itu dan mengapa harus rekursif diulang / diringkas secara singkat?

Sehubungan dengan itu, saya pikir tidak ada yang benar-benar menggunakan transpose dalam pengertian matematika abstrak, sebagai operasi pada peta linier , karena alasan sederhana bahwa transposisi matriks akan menjadi objek yang tidak berfungsi vektor tetapi pada RowVector s, yaitu akan memetakan RowVector menjadi RowVector . Mengingat kurangnya argumentasi untuk transpose rekursif, saya benar-benar tidak melihat perbedaan antara transpos matriks polos yang biasanya didefinisikan sebagai konsep (bergantung basis), dan operasi flip baru diusulkan.

Terima kasih @ Sacha0 untuk tulisan yang bagus. Saya mendukung proposal 2 (Panggil adjoint adjoint , ubah urutan transpose , dan array-flip flip . adjoint dan transpose tinggal di LinAlg , dan flip tinggal di Base .). Bagi saya ini tampaknya memberikan hasil akhir terbaik (yang seharusnya menjadi perhatian utama), dan juga memungkinkan jalan yang lebih bersih untuk v1.0. Saya setuju dengan poin Anda di atas jadi saya tidak akan mengulanginya, tetapi berikut adalah beberapa komentar tambahan.

Satu argumen yang mendukung non-rekursif transpose adalah bahwa .' -syntax sangat nyaman, dan dengan demikian dapat digunakan untuk misalnya Matrix{String} . Dengan asumsi bahwa sintaks akan hilang (# 21037) dan seseorang harus menggunakan transpose(A) alih-alih A.' , maka saya berpikir bahwa fungsi flip akan menjadi tambahan yang diterima (lebih pendek dan lebih jelas dari transpose(A) , dan pasti lebih bagus dari permutedims(A, (2,1)) ).

Pemisahan antara LinAlg dan Base yang diberikan proposal 2 juga sangat bagus. Tidak hanya karena LinAlg hanya perlu peduli tentang cara menangani Transpose dan Adjoint , tetapi juga menjelaskan bahwa kami mempertimbangkan transpose dan adjoint menjadi LinAlg operasi, dan flip menjadi AbstractMatrix generik yang hanya membalik dimensi matriks.

Terakhir, saya belum melihat banyak keluhan tentang transpose(::Matrix{String}) dll tidak berfungsi. Apakah ini benar-benar kasus penggunaan yang umum? Dalam kebanyakan kasus, akan lebih baik untuk membuat matriks yang dibalik dari awal.

Skema penamaan ini (proposal 2) mungkin memang lebih baik. Adjoint dan transpose adalah suku-suku yang cukup standar dalam aljabar linier, dan lebih sedikit putusnya ...

Tetapi gagal untuk melihat pada titik mana diputuskan / dimotivasi bahwa transpos matematika harus rekursif

@Jutho Ini adalah argumen yang sama dengan adjoint (adjoint dari bilangan kompleks adalah konjugatnya (karena bilangan kompleks adalah ruang vektor peringkat-satu yang valid dan operator linier, produk dalam dan konsep adjoint berlaku), dan adjoint dari matriks blok bersifat rekursif ...). Orang mungkin ingin mengerjakan aljabar linier dengan matriks riil bersarang, misalnya. Saya masih melihat banyak kode yang menggunakan .' untuk "adjoint of a real matrix or vector", dan saya juga biasa melakukan ini. Di sini, ' tidak hanya valid tetapi juga lebih umum (bekerja untuk array yang mungkin bernilai kompleks dan / atau bersarang). Penggunaan transpose rekursif yang lebih asli pada matriks kompleks bersarang terjadi ketika persamaan Anda memberi Anda conj(adjoint(a)) , yang relatif lebih jarang tetapi masih terjadi (cukup jarang bahwa saya senang jika tidak ada fungsi yang terkait dengannya - di diskusi di atas dan di tempat lain berbagai orang mulai menyebutnya hal-hal yang berbeda seperti "transpos matematika", saya memilih conjadoint , tetapi tidak satupun dari itu adalah terminologi yang bagus IMO, kecuali mungkin transpose itu sendiri). Dalam aljabar linier, operasi non-rekursif yang menyusun ulang blok-blok matriks blok tetapi tidak melakukan apa pun pada blok itu sendiri umumnya tidak muncul.

Terakhir, saya belum melihat banyak keluhan tentang transpose(::Matrix{String}) dll tidak berfungsi. Apakah ini benar-benar kasus penggunaan yang umum? Dalam kebanyakan kasus, akan lebih baik untuk membuat matriks yang dibalik dari awal.

Saya pikir hal semacam ini cukup diinginkan dengan operasi penyiaran Julia. Misalnya, sangat umum ingin mengambil produk luar (Kartesius) dari beberapa data dan mungkin memetakannya melalui suatu fungsi. Jadi alih-alih sesuatu seperti map(f, product(a, b)) kita dapat menggunakan broadcast(f, a, transpose(b)) atau cukup f.(a, b.') . Ada banyak kekuatan dalam beberapa karakter.

Pikiran saya: untuk menghindari menambahkan lebih banyak nama fungsi ke ruang ini (yaitu flip ), saya bertanya-tanya apakah kita dapat memiliki nilai default untuk permutasi di permutedims(a) = permutedims(a, (2,1)) . Sayangnya ini tidak sesingkat flip , tetapi secara signifikan kurang menjengkelkan daripada formulir permutedims(a, (2,1)) (dan memiliki efek samping mengajar pengguna tentang fungsi yang lebih umum).

( @ Sacha0 Terima kasih banyak atas tulisan Anda di sini. FYI dalam pilihan antara Adjoint dan Transpose pembungkus, atau RowVector + MappedArray + apa pun untuk membalik matriks seperti yang direncanakan sebelumnya (mungkin hanya PermutedDimsArray ), saya masih cenderung menyukai yang terakhir ...)

Penggunaan transpose rekursif yang lebih asli pada matriks kompleks bersarang terjadi ketika persamaan Anda memberi Anda conj(adjoint(a))

Setelah Anda mendapatkan conj(adjoint(a)) dalam persamaan Anda, bagaimana Anda tahu bahwa sebenarnya conj yang ingin Anda terapkan ke entri matriks Anda dan mungkin bukan adjoint , yaitu mungkin Anda benar-benar menginginkannya adjoint.(adjoint(a)) .

Ini mungkin akan membuat saya dilarang / dikeluarkan, tetapi saya bukan penggemar berat keseluruhan ide rekursif. Saya juga tidak menentangnya, saya hanya tidak berpikir ada alasan matematis yang ketat untuk ini, karena vektor dan operator / matriks tidak didefinisikan secara rekursif, mereka didefinisikan di atas bidang skalar (dan dengan cincin ekstensi jika Anda mau untuk juga bekerja dengan modul daripada ruang vektor). Jadi Base.LinAlg seharusnya berfungsi dalam kasus itu. Argumen utama

Adjoint vektor harus menjadi operator linier yang memetakannya ke skalar. Artinya, '* a harus berupa skalar jika a adalah vektor.

tidak sesuai dengan cara kerja Julia Base: Hasilkan a=[rand(2,2),rand(2,2)] dan amati a'*a . Ini bukan skalar. Jadi, apakah a adalah matriks sekarang, karena ini adalah vektor yang diisi dengan matriks? Di atas hanya skalar jika memang Anda bermaksud menggunakan cincin matriks 2x2 sebagai skalar yang Anda tentukan modulnya. Namun dalam konteks tersebut, tidak jelas bagi saya hasil di atas adalah yang diinginkan. Produk dalam Euclidean bagaimanapun juga jarang digunakan dalam konteks modul dll, jadi menurut saya Base tidak perlu repot-repot mendefinisikan perilaku yang benar untuknya.

Jadi sungguh, meskipun pernyataan di atas mungkin merupakan upaya untuk memperluas motivasi di luar menafsirkan matriks yang diisi dengan matriks sebagai matriks blok, pada akhirnya saya pikir interpretasi matriks blok adalah satu-satunya motivasi sejati untuk menghasilkan adjoint dan sekarang bahkan transpose (yang tidak pernah digunakan dalam pengertian matematis tetapi selalu dalam pengertian indeks matriks balik) rekursif di tempat pertama. Jika Anda menentukan sendiri jenis bidang skalar baru, itu hanya beban aneh bahwa Anda perlu menentukan adjoint dan transpose untuk itu selain conj , sebelum Anda dapat menggunakannya dalam matriks.

Jadi mengapa, dengan sistem tipe yang kuat seperti di Julia, tidak hanya memiliki tipe khusus untuk matriks blok. Dan dengan demikian, bermain sebagai pendukung iblis:

  • Berapa banyak orang yang benar-benar menggunakan / mengandalkan perilaku rekursif adjoint saat ini untuk pekerjaan praktis?
  • Apakah ada bahasa lain yang menggunakan pendekatan rekursif seperti itu? (Matlab, menggunakan sel matriks, tidak)

Seperti yang dikatakan, saya tidak selalu menentang, hanya mempertanyakan validitas (bahkan setelah membaca semua pembahasan di atas), terutama untuk kasus transpose .

Ini mungkin akan membuat saya dilarang / dikeluarkan

Saya menyadari ini adalah lelucon tetapi memiliki pendapat yang tidak populer sama sekali tidak akan membuat siapa pun dilarang. Hanya perilaku buruk yang berlangsung dalam waktu lama yang dapat menyebabkan pelarangan. Meski begitu, larangan tersebut tidak permanen .

Saya hanya tidak berpikir ada alasan matematis yang ketat untuk ini, karena vektor dan operator / matriks tidak didefinisikan secara rekursif, mereka ditentukan di atas bidang skalar (dan dengan cincin ekstensi jika Anda juga ingin bekerja dengan modul daripada ruang vektor ).

Pernyataan ini tidak masuk akal bagi saya. Vektor vektor, atau vektor fungsi, atau vektor matriks, masih membentuk ruang vektor (di atas bidang skalar yang mendasari) dengan + dan * scalar didefinisikan secara rekursif, dan dengan cara yang sama ia terbentuk ruang hasilkali dalam dengan hasilkali dalam didefinisikan secara rekursif. Gagasan bahwa vektor hanya dapat menjadi "kolom skalar" menentang definisi dan penggunaan umum dalam matematika, fisika, dan teknik.

Dapatkan a=[rand(2,2),rand(2,2)] dan amati a'*a . Ini bukan skalar.

Dalam hal ini "cincin skalar" dari a adalah cincin dari matriks 2x2. Jadi ini adalah masalah linguistik, bukan masalah dengan cara kerja adjoint.

Perdebatan tentang bagaimana seharusnya a'*a bekerja dari cara kerjanya saat ini (atau tidak berfungsi) di Julia tampaknya agak melingkar.

Jika Anda menentukan sendiri jenis bidang skalar baru, itu hanya beban aneh bahwa Anda perlu menentukan adjoint dan transpose untuk itu selain conj , sebelum Anda dapat menggunakannya dalam matriks

Menurut saya beban ini lebih estetis daripada praktis. Jika Anda tidak keberatan menjadikannya subtipe Number Anda tidak perlu mendefinisikan apa pun dan bahkan jika Anda yakin bahwa Number bukanlah hal yang tepat untuk Anda, definisinya sangat sepele sehingga menambahkan mereka bukanlah beban.

Jadi mengapa, dengan sistem tipe yang kuat seperti di Julia, tidak hanya memiliki tipe khusus untuk matriks blok.

https://github.com/KristofferC/BlockArrays.jl/ Kontributor dipersilakan :)

Saya minta maaf atas lelucon "larangan". Ini akan menjadi reaksi terakhir saya agar tidak menggagalkan diskusi ini lebih jauh. Jelas bahwa adjoint rekursif (tapi mungkin tidak transpose) diselesaikan, dan saya tidak mencoba membatalkan keputusan itu. Saya hanya mencoba menunjukkan beberapa ketidakkonsistenan.

@StefanKarpinski : Contoh cincin skalar persis bertentangan dengan rekursi: apakah matriks 2x2 itu sendiri adalah skalar, atau apakah definisinya rekursif dan apakah Number mendasari ketik skalar?

Dalam kasus sebelumnya, Anda sebenarnya memiliki modul alih-alih ruang vektor dan mungkin tergantung pada kasus penggunaan apakah Anda ingin conj atau adjoint pada 'skalar' itu, jika Anda mau menggunakan produk dalam dan sambungan sama sekali.

Saya baik-baik saja dengan menerima definisi yang terakhir. Saya menggunakan elemen arbitrer dalam subruang grup-invarian dari produk tensor bertingkat dari ruang vektor super dalam pekerjaan saya, jadi saya tidak benar-benar terbatas pada kolom angka dalam definisi saya tentang vektor. Tetapi vektor matriks, apakah mereka hanya vektor murni atau haruskah mereka juga mewarisi beberapa perilaku matriks. Dalam kasus sebelumnya, dot(v,w) harus menghasilkan skalar, mungkin dengan memanggil vecdot secara rekursif pada elemennya. Dalam kasus terakhir, setidaknya vecdot(v,w) harus menghasilkan skalar dengan bertindak secara rekursif. Jadi itu akan menjadi perbaikan minimal agar konsisten dengan adjoint rekursif.

Tetapi tampaknya lebih sederhana untuk tidak memiliki rekursif transpose dan hanya mengizinkannya digunakan untuk aplikasi non-matematika juga, karena saya pikir bahkan dalam persamaan matriks yang khas itu tidak ada hubungannya dengan transpos matematika yang sebenarnya. dari peta linier.

Saya pikir dot harus memanggil dot secara rekursif, bukan vecdot , jadi itu akan error untuk vektor matriks karena kita tidak mendefinisikan produk dalam kanonik untuk matriks.

Saya selalu terkejut bahwa transpos itu rekursif .. dan saya tidak bisa membayangkan ada orang yang tidak. (Saya menduga bahwa penayangan artikel Wikipedia "mengubah urutan peta linier" setidaknya tiga kali lipat karena diskusi ini.)

Saya juga sering menemukan definisi rekursif mengganggu, dan umumnya berbagi pandangan yang diungkapkan di atas oleh @Jutho (tetapi sekali lagi kami memiliki pelatihan yang sangat mirip).

Saya pikir saya telah menemukan ketidakkonsistenan yang mengganggu saya di sini - kami terus menggunakan cincin dan bidang seperti matriks 2x2 yang menyematkan bilangan kompleks sebagai contoh di sini, tetapi itu tidak benar-benar berhasil atau memotivasi transpose rekursif (dan adjoint ).

Biar saya jelaskan. Hal-hal yang saya setujui secara luas

  1. Scalars adalah operator linier peringkat-1 yang valid. Mengingat sistem tipe Julia yang kuat, tidak ada alasan mengapa LinAlg konsep seperti adjoint tidak berlaku, misalnya kita harus mendefinisikan adjoint(z::Complex) = conj(z) . (Di luar vektor dan matriks skalar, konsep LinAlg juga dapat diperluas (oleh pengguna, saya kira) ke objek lain - @stevengj disebutkan misalnya ruang vektor berukuran tak hingga (ruang Hilbert)).
  2. Kami harus bisa entah bagaimana berurusan dengan skalar dengan representasi yang berbeda. Contoh prototipe di sini adalah bilangan kompleks z = x + y*im dapat dimodelkan sebagai Z = x*[1 0; 0 1] + y*[0 1; -1 0] dan operasi + , - , * , / dan \ disimpan dalam isomorfisme ini. (Namun perlu dicatat bahwa conj(z) pergi ke adjoint(Z) / transpose(Z) / flip(Z) - lebih lanjut nanti).
  3. Matriks blok harus dimungkinkan entah bagaimana (Pendekatan saat ini bergantung pada rekursif adjoint secara default, dll).

Tampaknya masuk akal bahwa Base.LinAlg kompatibel dengan 1 dan 2, tetapi IMO 3 hanya boleh dilakukan di Base jika cocok secara alami (jika tidak, saya akan cenderung menunda paket eksternal seperti https: / /github.com/KristofferC/BlockArrays.jl).

Sekarang saya menyadari bahwa kita telah menggabungkan 2 dan 3 dan ini menyebabkan beberapa inkonsistensi ( @Jutho juga menunjukkan hal ini). Di bawah ini, saya ingin menunjukkan bahwa 3. menggunakan rekursif adjoint dan operasi lain sesuai matriks blok tidak memberi kita 2. Cara termudah adalah dengan memberi contoh. Mari kita tentukan matriks 2x2 dari bilangan kompleks sebagai m = [z1 z2; z3 z4] , representasi isomorfik M = [Z1 Z2; Z3 Z4] , dan matriks blok 2x2 standar b = [m1 m2; m3 m4] mana m1 dll berukuran sama persegi matriks Number . Jawaban semantik yang benar untuk operasi umum tercantum di bawah ini:

| operasi | z | Z | m | M | b |
| - | - | - | - | - | - |
| + , - , * | rekursif | rekursif | rekursif | rekursif | rekursif |
| conj | conj(z) | Z' atau Z.' atau flip(Z) | conj.(m) | adjoint.(M) (atau transpose.(M) ) | conj.(b) |
| adjoint | conj(z) | Z' atau Z.' atau flip(Z) | flip(conj.(m)) atau rekursif | flip(transpose.(m)) atau rekursif | rekursif |
| trace | z | Z | z1 + z4 | Z1 + Z4 | trace(m1) + trace(m4) |
| det | z | Z | z1*z4 - z2*z3 | Z1*Z3 - Z2*Z3 | det(m1) * det(m4 - m2*inv(m1)*m3) (jika m1 dapat dibalik, lihat misalnya Wikipedia ) |

Mempertimbangkan operasi seperti trace dan det yang mengembalikan skalar, menurut saya cukup jelas bahwa sistem tipe Julia untuk LinAlg tidak mungkin menangani penyematan matriks 2x2 sebesar Complex dengan cara "otomatis", menyimpulkan apa yang kami maksud dengan "skalar" pada saat tertentu. Contoh nyata adalah trace(Z) mana Z = [1 0; 0 1] adalah 2 , sedangkan ini adalah representasi kami dari 1 . Demikian pula untuk rank(Z) .

Salah satu cara untuk memulihkan konsistensi adalah dengan secara eksplisit mendefinisikan representasi 2x2 kami sebagai skalar, misalnya dengan subtipe Number seperti di bawah ini:

struct CNumber{T <: Real} <: Number
    m::Matrix{T}
end
CNumber(x::Real, y::Real) = CNumber([x y; -y x])

+(c1::CNumber, c2::CNumber) = CNumber(c1.m + c2.m)
-(c1::CNumber, c2::CNumber) = CNumber(c1.m - c2.m)
*(c1::CNumber, c2::CNumber) = CNumber(c1.m * c2.m)
/(c1::CNumber, c2::CNumber) = CNumber(c1.m / c2.m)
\(c1::CNumber, c2::CNumber) = CNumber(c1.m \ c2.m)
conj(c::CNumber) = CNumber(transpose(c.m))
zero(c::CNumber{T}) where {T} = CNumber(zero(T), zero(T))
one(c::CNumber{T}) where {T} = CNumber(one(T), one(T))

Dengan definisi ini, metode LinAlg mungkin akan bekerja dengan baik.

Kesimpulan yang saya tarik dari ini: rekursif adjoint adalah kemudahan untuk matriks blok dan vektor vektor. Ini tidak boleh didorong oleh ketepatan matematis untuk jenis matriks "skalar" 2x2 yang saya beri label Z atas. Saya melihatnya sebagai pilihan kami apakah kami mendukung matriks blok atau tidak secara default, dengan pro dan kontra berikut:

Pro

  • Kenyamanan bagi pengguna array blok
  • Vektor vektor adalah ruang vektor yang valid, dan matriks blok adalah operator linier yang valid, di bawah + , * , conj , dll. Jika memungkinkan untuk membuat isomorfisme natural (tidak seperti contoh Z atas, yang membutuhkan CNumber ), lalu mengapa tidak?

Kontra

  • Kompleksitas tambahan yang signifikan untuk implementasi kami LinAlg (yang berpotensi dapat hidup dalam paket lain).
  • Agak sulit (tetapi bukan tidak mungkin) untuk mendukung hal-hal seperti eig(block_matrix) . Jika kita mengatakan LinAlg mendukung eig dan LinAlg mendukung matriks blok, maka saya menganggap ini sebagai bug hingga diperbaiki. Mengingat banyaknya fungsionalitas yang disediakan oleh LinAlg , sulit untuk melihat bahwa kita akan pernah "selesai".
  • Ketidaknyamanan pengguna data yang ingin menggunakan operasi seperti transpose dengan cara non-rekursif,

Bagi saya pertanyaannya adalah - apakah kita memilih untuk mengatakan bahwa secara default LinAlg mengharapkan elemen AbstractArray s menjadi "skalar" (subtipe atau pengetikan bebek Number ) dan membuang kerumitan array blok ke paket eksternal? Atau apakah kita merangkul kerumitan dalam Base dan LinAlg ?

Rencananya sampai sehari yang lalu adalah yang terakhir.

@ Sacha0 Saya telah mencoba menyelesaikan pekerjaan RowVector untuk mendukung perubahan di sini (yang sebelumnya: non-rekursif transpose , RowVector string pendukung dan data lainnya) dan sekarang saya bertanya-tanya apa yang ada dalam pikiran Anda dalam hal desain.

Dari diskusi baru-baru ini, saya melihat kasus-kasus ini dengan perkiraan apa yang mungkin diinginkan

| | Vektor | Matriks |
| - | - | - |
| adjoint | RowVector dengan rekursif adjoint | AdjointMatrix atau TransposedMatrix dengan rekursif adjoint |
| transpose | RowVector dengan rekursif transpose | TransposeMatrix dengan rekursif transpose |
| flip | salinan atau PermutedDimsArray ? | salinan atau PermutedDimsArray ? |
| conj dari AbstractArray | malas atau bersemangat? | malas atau bersemangat? |
| conj dari RowVector atau TransposedMatrix | malas | malas |

(Upaya di atas untuk memisahkan masalah aljabar linier dari permutasi dimensi array data.)

Jadi beberapa pertanyaan dasar untuk membuat saya tidak terjebak:

  • Apakah kita melakukan rekursif transpose atau tidak? Bagaimana dengan adjoint ?
  • Jika demikian, apakah kita akan terus mengasumsikan conj(transpose(array)) == adjoint(array) ?
  • Tampaknya setidaknya beberapa konjugasi kompleks akan malas, untuk mendukung misalnya semua operasi BLAS saat ini tanpa salinan tambahan. Apakah kita membuat conj malas untuk semua array?
  • Jika kami memperkenalkan flip , apakah itu malas atau bersemangat?

FYI Saya mencoba pendekatan gesekan rendah untuk "membalik" dimensi matriks di # 24839, menggunakan bentuk yang lebih pendek untuk permutedims .

Saya sangat mendukung proposal @ Sacha0 2. Kami belum sepenuhnya memilah- conj∘adjoint ) dalam hal ini, jika diperlukan.

FWIW, Mathematica tidak melakukan rekursif Transpose atau ConjugateTranspose :

untitled

Saya mencoba membaca diskusi yang rumit di atas, tetapi gagal untuk melihat pada titik mana diputuskan / dimotivasi bahwa transpos matematika harus rekursif. [...] Untuk kelengkapan / kemandirian, dapatkah konsep itu dan mengapa harus rekursif diulang / diringkas sebentar?

Saya memahami dan menghargai sentimen ini, dan ingin mengatasinya sejauh waktu memungkinkan. Menjelaskan secara aksesibel dan gamblang mengapa adjoint dan transpose harus rekursif menurut definisi sayangnya paling menantang dan kemungkinan tidak mungkin secara singkat. Penulisan yang koheren dan komprehensif seperti di atas membutuhkan banyak waktu untuk dibuat. Waktu singkat, sepanjang hari saya akan mencoba untuk mengatasi beberapa kebingungan di beberapa posting sebelumnya dengan menanggapi poin / pertanyaan tertentu di dalamnya; tolong bersabarlah sementara saya melakukannya sedikit demi sedikit :). Terbaik!

menjelaskan mengapa adjoint ... harus rekursif menurut definisi ...

Saya cantik semua orang yang telah berkontribusi dalam diskusi ini setuju bahwa adjoint(A) harus rekursif. Satu-satunya poin perselisihan adalah apakah transpose(A) juga harus rekursif (dan apakah fungsi baru flip(A) harus diperkenalkan untuk transpose non-berulang).

Saya semua orang yang berkontribusi dalam diskusi ini setuju bahwa adjoint (A) harus rekursif.

Lihat, misalnya, https://github.com/JuliaLang/julia/issues/20978#issuecomment-347777577 dan komentar sebelumnya :). Terbaik!

Argumen untuk adjoint rekursif cukup mendasar untuk definisi. dot(x,y) harus berupa produk dalam, yaitu menghasilkan skalar, dan definisi adjoint adalah dot(A'*x, y) == dot(x, A*y) . Rekursi (untuk dot dan adjoint ) mengikuti dari dua properti ini.

(Hubungan dengan perkalian titik adalah alasan utama mengapa adjoint dan transpose adalah operasi penting dalam aljabar linier. Itulah mengapa kita memiliki nama untuk keduanya, dan bukan untuk operasi lain seperti memutar matriks sebesar 90 derajat ( rot90 di Matlab ).)

Perhatikan bahwa satu masalah dengan menggunakan flip untuk transpose non-rekursif adalah bahwa Matlab dan Numpy menggunakan flip untuk apa yang kita sebut flipdim .

Saya pikir tidak ada yang benar-benar menggunakan transpose dalam arti matematika abstrak, sebagai operasi pada peta linier, karena alasan sederhana bahwa transpose matriks akan menjadi objek yang tidak bekerja pada vektor tetapi pada RowVectors, yaitu akan memetakan RowVector ke RowVector.

Tetapi tampaknya lebih sederhana untuk tidak memiliki transpose rekursif dan hanya mengizinkannya untuk digunakan untuk aplikasi non-matematika juga, karena saya pikir bahkan dalam persamaan matriks yang khas itu tidak ada hubungannya dengan transpos matematika aktual dari peta linier.

transpose (yang tidak pernah digunakan dalam pengertian matematika tetapi selalu dalam pengertian indeks matriks balik)

Ini akan tampak agak bundar, jadi bersabarlah :).

adjoint Julia secara khusus mengacu pada adjoint Hermitian . Secara umum, untuk ruang vektor bernorma lengkap U dan V (ruang Banach) dengan masing-masing ruang ganda U * dan V , dan untuk peta linier A: U -> V, maka adjoint A, biasanya dilambangkan A , adalah peta linier A *: V * -> U * . Artinya, secara umum adjoint adalah peta linier antar ruang rangkap, seperti pada umumnya transpos A ^ t adalah peta linier antar ruang rangkap seperti yang disebutkan di atas. Jadi bagaimana seseorang mendamaikan definisi ini dengan gagasan akrab dari adjoint Hermitian? :)

Jawabannya terletak pada struktur tambahan yang dimiliki oleh ruang-ruang yang biasa dikerjakannya, yaitu ruang hasilkali dalam lengkap (ruang Hilbert). Hasil kali dalam menginduksi suatu norma, sehingga ruang hasilkali dalam lengkap (Hilbert) adalah ruang bernorma lengkap (Banach), dan dengan demikian mendukung konsep sambungan (Hermitian). Inilah kuncinya: Memiliki hasil kali dalam bukan sekadar norma, salah satu teorema terindah dalam aljabar linier berlaku, yaitu teorema representasi Riesz. Singkatnya, teorema representasi Riesz menyatakan bahwa ruang Hilbert secara alami bersifat isomorfik terhadap ruang gandanya. Akibatnya, saat bekerja dengan ruang Hilbert, kita biasanya mengidentifikasi ruang dan kembarannya dan menghilangkan perbedaannya. Membuat identifikasi itu adalah bagaimana Anda sampai pada gagasan yang sudah dikenal tentang adjoint Hermitian sebagai A *: V -> U daripada A *: V * -> U * .

Dan identifikasi yang sama umumnya dibuat untuk transpos ketika mempertimbangkan ruang Hilbert, seperti juga A ^ t: V -> U, menghasilkan pengertian transpos yang sudah dikenal. Jadi untuk memperjelas, ya, pengertian umum tentang transpose adalah pengertian umum tentang transpose yang diterapkan pada pengaturan (Hilbert) yang paling familiar, seperti pengertian umum dari adjoint Hermitian adalah pengertian umum tentang adjoint yang diterapkan pada pengaturan itu. Terbaik!

Permintaan maaf karena mengalahkan kuda mati, tapi saya pikir saya akan secara singkat merangkum masalah kebingungan matematika dengan cara yang berbeda. Titik kunci dari ketidaksepakatan adalah bagaimana menafsirkan secara matematis sebuah objek Vector{T} ketika T bukan hanya subtipe dari Number tanpa substruktur mirip larik.

Satu aliran pemikiran menerima klaim @stevengj itu

vektor vektor di atas cincin R paling baik dipahami sebagai ruang penjumlahan langsung, yang juga ruang vektor di atas R.

Modulo kehalusan tertentu mengenai ruang vektor berdimensi tak hingga, dll., Ini pada dasarnya hanya berarti bahwa kita harus memikirkan vektor vektor sebagai vektor blok. Jadi Vector{Vector{T<:Number}} harus secara mental "diratakan" menjadi Vector{T} . Dalam paradigma ini, operator linier yang direpresentasikan sebagai matriks matriks harus dianggap sama sebagai matriks blok, dan adjoint tentunya harus rekursif, sebagaimana seharusnya transpose jika kita menggunakan kata di pengertian matematika . Tolong koreksi saya jika saya salah, tetapi saya percaya bahwa orang-orang yang mengambil sudut pandang ini berpikir bahwa sesuatu seperti Vector{Matrix{T}} tidak memiliki interpretasi yang cukup alami sehingga kita harus merancangnya. (Secara khusus, kita tidak boleh hanya mengasumsikan bahwa Matrix adalah representasi matriks dari bilangan kompleks, karena seperti yang dikatakan @stevengj ,

Jika Anda merepresentasikan bilangan kompleks dengan matriks 2x2, Anda memiliki ruang vektor kompleks yang berbeda.

)

Aliran pemikiran lainnya adalah bahwa Vector{T} harus selalu dianggap sebagai representasi vektor dalam ruang vektor abstrak di atas skalar (dalam arti kata aljabar linier) dengan tipe T , terlepas dari jenis T . Dalam paradigma ini, Vector{Vector{T'}} tidak boleh dianggap sebagai elemen ruang penjumlahan langsung, tetapi sebagai vektor di atas skalar Vector{T'} . Dalam kasus ini, transpose(Matrix{T}) tidak boleh rekursif, tetapi cukup membalik matriks luar. Satu masalah dengan interpretasi ini adalah agar elemen tipe T membentuk cincin skalar yang valid, harus ada pengertian yang jelas tentang penjumlahan (komutatif) dan perkalian. Untuk vektor seperti Vector{Vector{T'}} , kita memerlukan aturan untuk mengalikan dua "skalar" Vector{T'} menjadi Vector{T'} . Meskipun seseorang pasti dapat menemukan aturan seperti itu (mis. Perkalian elemen, yang mempersoalkan masalah ke cara mengalikan T' ), tidak ada cara alami universal untuk melakukannya. Masalah lainnya adalah bagaimana adjoint akan bekerja di bawah interpretasi ini. Adjoint Hermitian hanya ditentukan pada operator linier di atas ruang Hilbert, yang bidang skalarnya menurut definisi harus berupa bilangan real atau kompleks. Jadi jika kita ingin menerapkan adjoint ke matriks Matrix{T} , kita harus berasumsi bahwa T adalah representasi dari bilangan kompleks (atau bilangan real, tapi saya akan tetap dengan kompleks karena kasing itu lebih halus). Dalam interpretasi ini, adjoint tidak boleh rekursif tetapi harus membalik matriks luar dan kemudian menerapkan conjugate . Tetapi ada lebih banyak masalah di sini, karena tindakan yang benar dari conjugate(T) bergantung pada sifat representasi. Jika ini adalah representasi 2x2 yang dijelaskan di Wikipedia, maka conjugate harus membalik matriks. Tetapi untuk alasan yang dijelaskan di atas, conjugate pasti tidak selalu membalik matriks. Jadi pendekatan ini akan sedikit berantakan untuk diterapkan.

Berikut adalah pemikiran saya: tidak ada jawaban yang "benar secara obyektif" apakah transpose harus rekursif ketika diterapkan pada array yang elemennya memiliki substruktur yang lebih rumit. Itu tergantung pada bagaimana tepatnya pengguna memilih untuk merepresentasikan struktur aljabar abstrak mereka di Julia. Namun demikian, mendukung cincin skalar yang sepenuhnya sewenang-wenang sepertinya akan sangat sulit, jadi saya pikir demi kepraktisan, kita tidak boleh mencoba menjadi begitu ambisius dan harus melupakan matematika esoterik modul di atas cincin non-standar. Kita pasti harus memiliki fungsi dalam Base (dengan sintaks yang lebih sederhana dari permutedims(A, (2,1)) ) yang tidak ada hubungannya dengan konsep aljabar linier transposisi dan hanya membalik matriks dan tidak melakukan rekursif, terlepas dari apakah itu disebut transpose atau flip atau apa. Akan lebih baik jika adjoint dan fungsi transpose terpisah (mungkin dengan nama yang berbeda) di LinAlg bersifat rekursif, karena mereka dapat menangani vektor / matriks blok dan implementasi sederhana dari penjumlahan langsung sebagai vektor vektor, tetapi ini tidak diwajibkan oleh "ketepatan matematis obyektif", dan akan baik-baik saja untuk membuat keputusan itu semata-mata atas dasar kemudahan implementasi.

Terlepas dari janji saya sebelumnya untuk tidak berkomentar lagi, sayangnya saya harus menanggapi yang ini.

Adjoin Julia merujuk secara khusus ke adjoint Hermitian. Secara umum, untuk ruang vektor bernorma lengkap U dan V (ruang Banach) dengan masing-masing ruang ganda U * dan V , dan untuk peta linier A: U -> V, maka adjoint Hermitian dari A, biasanya dilambangkan dengan A , adalah peta linier A *: V * -> U *. Artinya, secara umum adjoint Hermitian adalah peta linier antar ruang rangkap, seperti pada umumnya transpos A ^ t adalah peta linier antar ruang rangkap seperti yang disebutkan di atas. Jadi bagaimana Anda mendamaikan definisi ini dengan pengertian yang sudah dikenal tentang adjoint Hermitian? :)

Sungguh itu tidak benar. Apa yang Anda gambarkan di sini sebenarnya adalah transpos, tetapi (seperti yang telah saya sebutkan), dalam beberapa bidang ini disebut sebagai adjoint (tanpa Hermitian) dan dilambangkan sebagai A ^ t atau A ^ * (tidak pernah A ^ dagger). Faktanya, ini melampaui ruang vektor, dan dalam teori kategori, konsep seperti itu ada dalam kategori monoidal apa pun (mis. Cateogory Cob dari manifold berorientasi dimensi-n dengan cobordisme sebagai peta linier), di mana ia disebut sebagai adjoint mate (dalam Faktanya bisa ada dua ruang yang berbeda A dan A , karena ruang ganda kiri dan kanan belum tentu sama). Tetapi perhatikan, ini tidak pernah melibatkan konjugasi yang rumit. Unsur-unsur V * memang merupakan peta linier f: V-> Skalar, dan untuk peta linier A: U-> V dan vektor v dari U, kita memiliki f (Av) = (A ^ tf) (v) . Karena aksi f tidak melibatkan konjugasi kompleks, begitu pula definisi dari A ^ t.

Jawabannya terletak pada struktur tambahan yang dimiliki oleh ruang tempat Anda biasanya bekerja, yaitu ruang hasilkali dalam lengkap (ruang Hilbert). Hasil kali dalam menginduksi suatu norma, sehingga ruang hasilkali dalam lengkap (Hilbert) adalah ruang bernorma lengkap (Banach), dan dengan demikian mendukung konsep adjoint Hermitian. Inilah kuncinya: Memiliki hasil kali dalam bukan sekadar norma, salah satu teorema terindah dalam aljabar linier berlaku, yaitu teorema representasi Riesz. Singkatnya, teorema representasi Riesz menyatakan bahwa ruang Hilbert secara alami bersifat isomorfik terhadap ruang gandanya. Akibatnya, saat bekerja dengan ruang Hilbert, kita biasanya mengidentifikasi ruang dan kembarannya dan menghilangkan perbedaannya. Membuat identifikasi itu adalah bagaimana Anda sampai pada gagasan yang sudah dikenal tentang adjoint Hermitian sebagai A *: V -> U daripada A *: V * -> U *.

Sekali lagi, saya rasa itu tidak sepenuhnya benar. Dalam ruang hasil kali dalam, hasil kali dalam adalah bentuk seskuilinear dot dari konj (V) x V -> Skalar (dengan konj (V) ruang vektor konjugasi). Hal ini memungkinkan untuk membuat peta dari V ke V * (atau secara teknis dari konj (V) ke V *), yang memang merupakan teorema representasi Riesz. Namun, kami tidak membutuhkannya untuk memperkenalkan adjoint Hermitian. Sungguh, produk dalam dot itu sendiri sudah cukup, dan adjoint Hermitian dari peta linier A adalah sedemikian rupa sehingga
dot(w, Av) = dot(A' w, v) . Ini melibatkan konjugasi yang kompleks.

Sungguh itu tidak benar. Apa yang Anda gambarkan di sini sebenarnya adalah transpos, tetapi (seperti yang telah saya sebutkan), dalam beberapa bidang ini disebut sebagai adjoint (tanpa Hermitian) dan dilambangkan sebagai A ^ t atau A ^ * (tidak pernah A ^ dagger). [...]

@Jutho , silakan lihat, misalnya, halaman Wikipedia di adjoint Hermitian .

Mungkin ada ketidakkonsistenan antara berbagai bidang matematika, tetapi:
https://en.wikipedia.org/wiki/Transpose_of_a_linear_map
dan khususnya
https://en.wikipedia.org/wiki/Transpose_of_a_linear_map#Relation_to_the_Hermitian_adjoint
dan tak terhitung banyaknya referensi dalam teori kategori, misalnya
https://arxiv.org/pdf/0908.3347v1.pdf

https://en.wikipedia.org/wiki/Transpose_of_a_linear_map
dan khususnya
https://en.wikipedia.org/wiki/Transpose_of_a_linear_map#Relation_to_the_Hermitian_adjoint

@ Jutho , saya melihat tidak ada ketidakkonsistenan antara bagian halaman itu dan definisi yang diberikan pada halaman yang saya tautkan di atas, saya juga tidak melihat adanya ketidakkonsistenan dengan apa yang saya posting di atas. Terbaik!

Saya juga akan masuk ke proposal @ Sacha0 2. Saya permutedims untuk saat ini juga; Saya pikir itu lebih baik dari flip .

@ Sacha0 , maka kami memiliki cara berbeda untuk menafsirkannya. Saya membaca ini sebagai
Untuk A: U-> V tertentu,
transpose (A) = dual (A) = (terkadang juga) adjoint (A): V * -> U *
hermitian adjoint (A) = dagger (A) = (biasanya hanya) adjoint (A): V-> U
dan hubungan antara keduanya diperoleh secara tepat dengan menggunakan peta dari ruang ke ruang ganda (yaitu Riesz ...), yang melibatkan konjugasi kompleks. Oleh karena itu, adjoint hermitian melibatkan konjugasi, transpos tidak.

Jika Anda juga ingin memanggil yang pertama hermitian adjoint, lalu apa yang Anda sebut transpose? Anda tidak benar-benar menjelaskan apa yang ada dalam deskripsi Anda, Anda baru saja menyebutkannya

Adjoint Hermitian dari A, biasanya dilambangkan dengan A , adalah peta linier A : V * -> U *. Artinya, pada umumnya adjoint Hermitian adalah peta linier antar ruang rangkap, sama seperti pada umumnya transpos A ^ t adalah peta linier antar ruang rangkap.

Jadi, apakah transpos dan adjoint Hermitian dua cara berbeda untuk mengubah A: U-> V menjadi peta dari V -> U ? Saya akan sangat senang untuk mendiskusikan lebih lanjut tentang ini, tetapi saya rasa lebih baik kita melakukan ini di tempat lain. Tapi sungguh, hubungi saya karena saya sebenarnya sangat tertarik untuk mempelajari lebih lanjut tentang ini.

Juga lihat http://staff.um.edu.mt/jmus1/banach.pdf untuk referensi bahwa adjoint yang digunakan dalam konteks ruang Banach benar-benar transpos, dan bukan adjoint Hermitian (khususnya yang linier dan bukan antilinear transformasi). Wikipedia (dan referensi lainnya) benar-benar menggabungkan kedua konsep ini, menggunakan gagasan adjoint Hermitian di ruang Hilbert sebagai motivasi untuk definisi umum adjoint di ruang Banach. Namun, yang terakhir benar-benar mengubah urutan (dan tidak membutuhkan produk dalam, atau norma). Tapi itulah transpos yang saya bicarakan, yang tidak benar-benar digunakan dalam kode komputer.

Untuk Julia Base: Saya tidak menentang konjugasi Hermitian rekursif; Saya setuju itu sering kali menjadi hal yang benar untuk dilakukan. Saya hanya tidak yakin Base harus mencoba melakukan hal-hal pintar ketika tipe elemen bukan Number . Bahkan dengan T adalah angka, tidak ada dukungan di Base untuk penggunaan produk dalam non-Euclidean yang jauh lebih umum (dan definisi adjoint yang dimodifikasi terkait), dan menurut saya seharusnya tidak ada. Jadi saya pikir motivasi utamanya adalah matriks blok, tetapi di sana saya hanya berpikir jenis tujuan khusus (dalam Base atau dalam paket) jauh lebih banyak Julian, dan, seperti yang juga disebutkan @andyferris , itu tidak seperti sisa LinAlg mendukung gagasan tentang matriks blok, bahkan hal-hal sederhana seperti inv (apalagi faktorisasi matriks, dll).

Tetapi jika konjugasi Hermitian rekursif ada di sini untuk tetap (baik-baik saja oleh saya), maka saya pikir untuk konsistensi dot dan vecdot harus bertindak secara rekursif pada elemen. Saat ini, ini bukan masalahnya: dot memanggil x'y pada elemen (yang tidak sama ketika elemen adalah matriks) dan vecdot memanggil dot pada elemen. Jadi untuk sebuah vektor matriks, sebenarnya tidak ada cara untuk mendapatkan hasil skalar. Saya akan dengan senang hati menyiapkan PR jika orang setuju bahwa implementasi saat ini tidak benar-benar tidak konsisten dengan rekursif adjoint .

Adapun transpose , sepertinya lebih sederhana untuk membuatnya non-rekursif dan juga membiarkannya digunakan oleh mereka yang tidak bekerja dengan data numerik. Saya pikir kebanyakan orang yang bekerja dengan matriks tahu istilah transpose dan akan mencarinya. Masih ada conj(adjoint()) bagi mereka yang membutuhkan rekursif transpose .

Triage berpikir @ Sacha0 harus melanjutkan proposal 2 sehingga kami dapat mencobanya.

Saya sangat setuju dengan @ttparker bahwa

1 - hingga LinAlg , AbstractVector v adalah vektor dimensi length(v) dengan bobot dasar (skalar) v[1] , v[2] , ..., v[length(v)] .

(dan juga untuk AbstractMatrix ).

Ini mungkin akan menjadi asumsi yang dibuat oleh banyak orang dari library lain, dan memiliki definisi sederhana dari vektor basis, peringkat, dll, membantu menjaga implementasi tetap sederhana. (Banyak yang mungkin akan mengatakan bahwa aljabar linier numerik layak untuk diterapkan di komputer karena kita memiliki basis yang bagus untuk dikerjakan.)

Pendekatan kami saat ini lebih seperti:

2 - menjadi LinAlg , AbstractVector v adalah jumlah langsung dari length(v) vektor abstrak. Kami juga menyertakan definisi yang cukup pada jenis skalar seperti Number sehingga menjadi LinAlg mereka adalah operator / vektor linier satu dimensi yang valid.

dan juga untuk matriks (blok). Ini jauh lebih umum daripada implementasi aljabar linier di MATLAB, numpy, eigen, dll, dan ini adalah cerminan dari sistem tipe / pengiriman Julia yang kuat sehingga ini bahkan dapat dilakukan.

Alasan menyeluruh saya melihat opsi 2 sebagai yang diinginkan sekali lagi adalah bahwa jenis / sistem pengiriman Julia memungkinkan kita memiliki tujuan yang jauh lebih luas, yang secara samar-samar berjalan seperti:

3 - Dalam LinAlg , kami mencoba untuk membuat antarmuka aljabar linier generik yang bekerja untuk objek yang memenuhi linearitas (dll) di bawah + , * , conj (etc), memperlakukan objek seperti operator linier / anggota ruang Hilbert / apapun yang sesuai.

Yang merupakan tujuan yang sangat keren (pasti jauh melampaui bahasa pemrograman / pustaka lain yang saya ketahui), benar-benar memotivasi rekursif adjoint dan 2 (karena + , * dan conj itu sendiri rekursif) dan mengapa @ Sacha0 's usulan 2 dan keputusan triase adalah pilihan yang baik :)

Saya akan sangat senang untuk mendiskusikan lebih lanjut tentang ini, tetapi saya rasa lebih baik kita melakukan ini di tempat lain. Tapi sungguh, hubungi saya karena saya sebenarnya sangat tertarik untuk mempelajari lebih lanjut tentang ini.

Cheers, ayo lakukan! Saya berharap untuk mengobrol lebih lanjut secara offline :). Terbaik!

Ringkasan bagus Andy! :)

Andy sepenuhnya setuju, setidaknya untuk adjoint (yang merupakan topik komentar Anda).

Namun, satu permohonan terakhir untuk non-rekursif transpose , sebelum saya selamanya memegang kedamaian saya (semoga).
Saya melihat transpos non-rekursif memiliki keuntungan sebagai berikut:

  • Ini dapat digunakan oleh semua orang yang bekerja dengan matriks, bahkan berisi data non-numerik. Begitulah cara mereka kemungkinan besar akan mengetahui operasi ini dan mencarinya, dari bahasa lain dan dari matematika dasar yang diekstrapolasi ke kasus penggunaan non-numerik mereka.
  • Tidak perlu menulis kode tambahan untuk membuat tipe malas flip atau PermutedDimsArray berinteraksi dengan LinAlg . Bagaimana jika saya memiliki matriks numerik yang saya balik alih-alih dialihkan; apakah saya masih bisa mengalikannya dengan matriks lain (sebaiknya menggunakan BLAS)?

  • Dengan non-rekursif transpose dan rekursif adjoint , kita dapat dengan mudah memiliki adjoint non-rekursif sebagai conj(transpose(a)) dan transpose rekursif conj(adjoint(a)) . Dan tetap semuanya akan berinteraksi dengan baik dengan LinAlg .

Jadi apa saja kerugiannya. Saya tidak melihat apapun. Saya masih berpegang teguh pada poin saya bahwa sebenarnya tidak ada yang menggunakan transpose dalam pengertian matematisnya. Tetapi alih-alih mencoba berdebat lebih jauh, adakah yang bisa memberi saya contoh konkret di mana transpose rekursif diperlukan atau berguna, dan di mana sebenarnya itu adalah tanspose matematis? Ini tidak termasuk contoh di mana Anda sebenarnya bermaksud untuk menggunakan adjoint tetapi hanya memiliki bilangan real. Jadi aplikasi di mana ada alasan matematis untuk mengubah urutan vektor atau matriks yang diisi dengan lebih banyak matriks yang nilainya sendiri kompleks.

Saya dapat mengatakan bahwa setidaknya Mathematica (yang diharapkan telah mencurahkan banyak pemikiran untuk ini) tidak melakukan transposisi rekursif:

A = Array[1 &, {2, 3, 4, 5}];
Dimensions[A]  # returns {2, 3, 4, 5}
Dimensions[Transpose[A]] # returns {3, 2, 4, 5}

EDIT: Ups, ini juga dikomentari di atas, maaf

Jadi saya bingung. Tampaknya ada konsensus yang cukup kuat bahwa transpose harus dibuat non-rekursif - misalnya https://github.com/JuliaLang/julia/issues/20978#issuecomment -285865225, https://github.com/ JuliaLang / julia / issues / 20978 # Issuecomment -285942526, https://github.com/JuliaLang/julia/issues/20978#issuecomment -285993057, https://github.com/JuliaLang/julia/issues/20978#issuecomment - 348464449, dan https://github.com/JuliaLang/julia/pull/23424. Kemudian @ Sacha0 memberikan dua proposal, salah satunya akan meninggalkan transpose rekursif tetapi memperkenalkan fungsi non-rekursif flip , yang mendapat dukungan kuat meskipun (sejauh yang saya tahu) tidak benar-benar dimunculkan sebagai kemungkinan sebelumnya . Kemudian @JeffBezanson menyarankan agar kita tidak membutuhkan flip jika kita memberikan permutedims argumen kedua default, yang juga mendapat dukungan kuat.

Jadi sekarang konsensusnya tampaknya adalah bahwa satu-satunya perubahan nyata pada transpose harus "di belakang layar": mengenai penurunan khusus dan evaluasi malas vs. bersemangat, yang mungkin tidak akan diketahui atau dipedulikan oleh pengguna akhir pada umumnya. . Perubahan yang benar-benar terlihat pada dasarnya hanyalah perubahan ejaan (mendepresiasi .' dan memberikan permutedims argumen kedua default).

Jadi konsensus komunitas tampaknya telah berubah hampir 180 derajat dalam waktu yang sangat singkat (sekitar waktu posting @ Sacha0 https://github.com/JuliaLang/julia/issues/20978#issuecomment-347360279). Apakah postingan Sacha begitu fasih sehingga mengubah pikiran semua orang? (Tidak apa-apa jika demikian, saya hanya ingin memahami mengapa kita tampaknya bergerak maju di jalan yang beberapa hari lalu kita semua tampaknya setuju adalah jalan yang salah.)

Saya lupa jika ada yang menyarankan ini, tetapi dapatkah kita menghasilkan transpose(::AbstractMatrix{AbstractMatrix}) (dan mungkin juga transpose(::AbstractMatrix{AbstractVector}) ) rekursif dan transpose non-rekursif? Sepertinya itu akan mencakup semua basis dan saya tidak dapat memikirkan kasus penggunaan lain di mana Anda ingin tranpose menjadi rekursif.

Jadi konsensus komunitas tampaknya telah berubah hampir 180 derajat dalam waktu yang sangat singkat (sekitar waktu postingan @ Sacha0 # 20978 (komentar)). Apakah postingan Sacha begitu fasih sehingga mengubah pikiran semua orang? (Tidak apa-apa jika demikian, saya hanya ingin memahami mengapa kita tampaknya bergerak maju di jalan yang beberapa hari lalu kita semua tampaknya setuju adalah jalan yang salah.)

Kalau saja saya begitu fasih 😄. Apa yang Anda lihat adalah bahwa konsensus belum benar-benar terbentuk. Sebaliknya, (1) peserta yang mendukung status quo, tetapi menarik diri dari diskusi karena atrisi, kembali untuk mengungkapkan pendapat; dan (2) pihak lain yang tidak mempertimbangkan apa yang akan ditimbulkan dari perpindahan dari status quo dalam praktiknya (dan bagaimana hal itu mungkin bermain dengan pertimbangan pelepasan) membentuk opini yang lebih kuat untuk mendukung status quo dan menyatakan pendapat tersebut.

Harap pertimbangkan bahwa diskusi ini telah berlangsung dalam satu bentuk atau lainnya di github sejak 2014, dan mungkin lebih awal secara offline. Bagi peserta jangka panjang, diskusi seperti itu menjadi melelahkan dan berputar. Ada pekerjaan yang bermakna untuk dilakukan selain terlibat dalam diskusi ini - seperti menulis kode, yang lebih menyenangkan - hasilnya adalah gesekan di antara peserta jangka panjang tersebut. Akibatnya, percakapan tampak miring selama satu periode atau lainnya. Secara pribadi, saya hampir mencapai ambang gesekan itu, jadi saya akan fokus menulis kode sekarang daripada terus terlibat dalam diskusi ini. Terima kasih semuanya dan yang terbaik! :)

Saya akan memberikan suara kecil untuk mendukung transpose non-rekursif dan ctranspose untuk AbstractArrays, dengan keduanya menjadi rekursif pada AbstractArray {T} di mana T <: AbstractArray.

Saya setuju bahwa perilaku rekursif adalah 'benar' dalam beberapa kasus, dan saya melihat pertanyaannya adalah bagaimana kita mencapai perilaku yang benar dengan kejutan paling sedikit bagi mereka yang menggunakan dan mengembangkan paket.
Dalam proprosal, perilaku transpose rekursif untuk tipe kustom diikutsertakan: Anda ikut serta dengan membuat tipe Anda menjadi AbstractArray atau dengan menentukan metode yang sesuai
Base.transpose(AbstractArray{MyType}) atau Base.transpose(AbstractArray{T}) where T<: MyAbstractType .
Menurut saya, strategi pengetikan bebek untuk transposisi rekursif (hanya berulang tanpa bertanya) menghasilkan beberapa kejutan seperti yang didokumentasikan di atas. Jika Anda memperkenalkan ctranspose dan adjoint yang berbeda, atau proposal yang lebih rumit seperti conjadjoint dan flip, pengguna akan menemukan ini dan mencoba menggunakannya, dan pengelola paket akan mencoba mendukung semuanya.

Sebagai contoh dari sesuatu yang akan sulit untuk didukung di bawah proposal baru: array normal, transpose, ctranspose, dan konjungsi, semua harus dapat memiliki tampilan (atau evaluasi malas) yang interop dengan tampilan ReshapedArray dan SubArray. (Saya agnostik tentang apakah ini menghasilkan tampilan secara default atau hanya ketika menggunakan @view .) Ini menghubungkan ke pekerjaan pada penurunan A*_mul_B* dan pada panggilan BLAS tingkat yang lebih rendah dengan bendera 'N', 'T', dan 'C' untuk larik padat, seperti yang telah dicatat di tempat lain. Ini akan lebih mudah untuk dipikirkan jika mereka memperlakukan normal , transpose , ctranspose , dan conj
sejajar. Perhatikan bahwa BLAS sendiri hanya mendukung 'N' untuk normal, 'T' untuk transpose, dan 'C' untuk ctranspose dan tidak memiliki flag untuk conj, yang menurut saya merupakan kesalahan.

Akhirnya, untuk konsistensi dengan Array dan bentuk ulang dimensi yang lebih tinggi, saya percaya generalisasi yang tepat dari transpose dan ctranspose adalah membalikkan semua dimensi, yaitu
transpose (A :: Array {T, 3}) = permutedims (A, (3, 2, 1)).

Bersulang!

Saya sangat menghargai orang-orang yang benar-benar melakukan pekerjaan ini. Apa yang telah dibahas terlalu panjang adalah vektor adjoints / transpose (tapi tidak pernah aspek rekursifnya), sampai @andyferris meningkatkan dan menerapkan ini, dan itu bekerja dengan sangat baik. Demikian pula, saya juga sangat menghargai desain ulang konstruktor array yang sedang berlangsung. Jempol untuk semua itu.

Namun demikian, matriks transpose dan adjoint / ctranspose tidak pernah mendapat banyak diskusi, terutama aspek rekursifnya, yang hampir diam-diam diperkenalkan di https://github.com/JuliaLang/julia/pull/7244 dengan matriks blok motivasi tunggal. . Berbagai alasan dan motivasi untuk adjoint rekursif telah diberikan (setelah fakta), dan kebanyakan orang dapat menyetujui bahwa menjadi pilihan yang baik (tetapi bukan satu-satunya). Namun transpose tidak memiliki motivasi atau kasus penggunaan yang sebenarnya.

Ada beberapa hal terpisah yang terjadi dalam diskusi ini, dan itu terjadi sekarang karena kami memerlukan rencana yang dapat dilaksanakan dengan cepat.

  • Kita telah membahas apakah matriks blok pendukung (dan struktur yang lebih eksotis) bermanfaat dalam LinAlg . Pilihan implementasinya adalah: tidak ada hal rekursif sama sekali (kecuali + , * dan conj karena itulah sifat fungsi generik di Julia), rekursif semuanya (status quo), atau mencoba beberapa jenis pemeriksaan atau sifat untuk mengetahui apakah suatu elemen harus melakukan aljabar linier rekursif atau diperlakukan sebagai skalar.
  • Kami menginginkan cara yang bagus bagi pengguna untuk mengubah dimensi array data 2D. Kami memiliki sintaks non-rekursif transpose , flip , disingkat permutedims sintaks (PR itu dikirim terlebih dahulu murni karena itu adalah karakter paling sedikit untuk diterapkan, dan mungkin masuk akal bahkan jika kita melakukan sesuatu yang lain juga), semacam pemeriksaan tipe atau sifat apakah suatu elemen harus melakukan transposisi rekursif (bahkan mungkin memperkenalkan kembali transpose(x::Any) = x ...).
  • Pengurai Julia memiliki perilaku aneh seperti x' * y -> Ac_mul_B(x, y) yang merupakan kutil, yang idealnya tidak akan ada di v1.0. Ini belum terlihat layak sampai kami dapat mendukung BLAS cepat (tanpa salinan tambahan) tanpa itu, sehingga matriks malas mengubah urutan & adjoint.
  • Kode di LinAlg cukup besar dan dibangun selama beberapa tahun. Banyak hal seperti perkalian matriks dapat direfraktorisasi menjadi lebih ramah sifat, mungkin dengan sistem pengiriman yang lebih seperti broadcast . Saya pikir di sinilah kita dapat membuatnya lebih mudah untuk mengirim array yang tepat (saya berpikir PermuteDimsArray dari pandangan ajdointed terkonjugasi dibentuk ulang dari matriks melangkah) ke BLAS. Namun, ini tidak akan membuat v1.0 dan kami juga mencoba untuk menghindari regresi kinerja tanpa membuat kode menjadi lebih buruk. Seperti yang ditunjukkan Sacha (dan saya menemukan) memiliki tampilan transpose dengan berbagai perilaku pada elemen (adjoint rekursif, transpos rekursif, konjugasi, tidak ada) membuat kompleksitas tambahan dan banyak metode baru untuk menjaga agar semuanya berfungsi saat mereka. adalah.

Jika kita berpikir tentang v1.0 sebagai sesuatu yang menstabilkan bahasa, maka dalam beberapa hal prioritas terbesar untuk membuat perubahan perilaku adalah yang ketiga. Saya akan mengatakan: bahasa (termasuk parser) harus paling stabil, diikuti oleh Base , diikuti oleh stdlib (yang mungkin atau mungkin tidak termasuk LinAlg , tetapi saya pikir hampir pasti akan menyertakan BLAS , Sparse , dll suatu hari). Ini adalah perubahan yang tidak terlalu memengaruhi pengguna (kebanyakan pengembang perpustakaan), jadi saya tidak akan terkejut jika pendapat orang berbeda di sini.

Tepat pada Andy! :)

Saya pikir satu-satunya hal yang tersisa untuk dilakukan di sini adalah membuat adjoint dan transpose malas secara default?

Bisakah ini ditutup sekarang?

Selanjutnya: "Menangani transposisi skalar dengan serius"

Namun serius, dapatkah kita memiliki antarmuka yang baik untuk menentukan transposes 3D dan perkalian tensor yang berbeda yang digunakan dalam pemecah PDE? Agak serius, tapi saya tidak yakin apakah saya bisa menangani menjadi OP untuk iterasi berikutnya dari kegilaan ini.

tidak

:)

dapatkah kita memiliki antarmuka yang baik untuk menentukan transpos 3D yang berbeda dan perkalian tensor yang digunakan dalam pemecah PDE

Sepertinya subjek yang bagus untuk sebuah paket.

antarmuka yang baik untuk menentukan transpos 3D yang berbeda dan perkalian tensor

Apakah TensorOperations.jl tidak melakukan yang Anda butuhkan di sini? (Perhatikan bahwa pada level ini "antarmuka yang baik" berarti sesuatu seperti diagram jaringan tensor, yang sedikit menantang untuk menulis kode secara lebih ringkas daripada sintaks TensorOperations ).

Ya, TensorOperations.jl terlihat bagus. Saya sedikit bercanda, tetapi saya mendapatkan apa yang saya butuhkan darinya 👍.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat