Julia: neue Syntax für transponieren

Erstellt am 15. März 2017  ·  103Kommentare  ·  Quelle: JuliaLang/julia

Nun, da .op im Allgemeinen die vektorisierte Form von op ist, ist es sehr verwirrend, dass .' Transposition bedeutet und nicht die vektorisierte Form von ' (adjungiert, auch bekannt als ctranspose ). In dieser Ausgabe werden alternative Syntaxen für Transponieren und/oder Adjungieren diskutiert.

linear algebra parser

Hilfreichster Kommentar

Sehr stark dagegen, dass tr(A) eine Matrixtransposition bedeutet - jeder wird denken, dass dies eine Matrixspur bedeutet: https://en.wikipedia.org/wiki/Trace_ (linear_algebra)

Alle 103 Kommentare

Andreas versuchte Aᵀ (und vielleicht Aᴴ ) in #19344, aber es wurde nicht sehr gut aufgenommen. Wir könnten auf ähnliche Weise ^ mit speziellen Exponententypen T (und vielleicht H ) spielen, sodass A^T transponiert ist, aber das ist auch ziemlich zwielichtig. Ich bin mir nicht sicher, ob es viele andere gute Optionen gibt, die immer noch irgendwie wie mathematische Notation aussehen.

Ich denke, dass t(A) vielleicht das Beste ist, aber es ist unglücklich, einen weiteren Ein-Buchstaben-Namen zu "stehlen".

Verschieben meines Kommentars vom anderen Problem (nicht, dass es irgendetwas löst, aber ...):

+1 für die Verwendung von etwas anderem als .' .

Ich konnte keine Sprachen mit einer speziellen Syntax für die Transposition finden, außer APL, das das nicht so offensichtliche verwendet, und Python, das *X verwendet (was für Julia verwirrend wäre). Mehrere Sprachen verwenden transpose(X) ; R verwendet t(X) . Das ist nicht schön, aber es ist nicht schlimmer als .' . Zumindest sind Sie weniger versucht, ' zu verwenden, indem Sie es mit .' verwechseln: Es wäre klar, dass dies sehr unterschiedliche Operationen sind.

Siehe Rosetta-Code . (Übrigens, das Julia-Beispiel veranschaulicht tatsächlich die konjugierte Transponierung ...)

Könnte einer der anderen Ticks verwendet werden? ` oder "

-100 zum Ändern von Adjoint, da es eines der großartigen Dinge ist, die das Schreiben von Julia-Code so klar machen wie das Schreiben von Mathematik, und außerdem ist die konjugierte Transponierung normalerweise das, was Sie sowieso wollen, sodass es sinnvoll ist, eine abgekürzte Syntax dafür zu haben.

Solange wir die schöne Syntax für die konjugierte Transponierung haben, scheint ein Postfix-Operator für die reguläre Transponierung größtenteils unnötig zu sein, also scheint es mir in Ordnung, wenn es nur ein regulärer Funktionsaufruf ist. transpose funktioniert bereits; könnten wir das nicht einfach verwenden? Ich finde den t(x) R-ismus schade, da aus dem Namen nicht ersichtlich ist, was er eigentlich bewirken soll.

Die Verwendung eines anderen Häkchens wäre etwas seltsam, z. B. kann A` A' aussehen, und A" sieht zu sehr wie A'' aus .

Wenn wir die Änderung in #20978 vornehmen, dann wird eine Postfix-Transponierung tatsächlich nützlicher als sie jetzt ist. zB wenn Sie zwei Vektoren x und y haben und f paarweise darauf anwenden wollen, können Sie zB f.(x, y.') ... mit #20978 machen , gilt dies für Arrays beliebiger Typen.

Ehrlich gesagt denke ich, dass unsere beste Option immer noch ist, es so zu lassen, wie es ist. Keiner der Vorschläge scheint mir eine klare Verbesserung zu sein. .' hat den Vorteil der Vertrautheit mit Matlab. Das . ist tatsächlich etwas kongruent mit der Dot-Call-Syntax in Beispielen wie f.(x, y.') und deutet (ziemlich korrekt) an, dass die Transponierung "verschmilzt" (es erzeugt dank RowVector keine temporäre Kopie

Tatsächlich könnten wir sogar noch weiter gehen und f.(x, g.(y).') zu einer Verschmelzungsoperation machen. dh wir ändern die Transponierung von .' so, dass sie nicht rekursiv ist, ala #20978, und wir erweitern ihre Semantik, um die Fusion mit anderen verschachtelten Punktaufrufen einzuschließen. (Wenn Sie die Version ohne Fusion wünschen, würden Sie transpose anrufen.)

Ich mag diesen Plan sehr, @stevengj.

Eine Falte: vermutlich wandelt das Makro @. y' nicht in y.' (da das falsch wäre). Es könnte jedoch y' in eine Art verschmolzene adjungierte Operation verwandeln.

Das Hauptproblem besteht darin, einen sauberen Weg zu finden, um f.(x, g.(y).') dazu zu bringen, Semantik zu verschmelzen. Eine Möglichkeit wäre, es in f.(x, g.(y.')) und damit in broadcast(x,y -> f(x, g(y)), x, y.') umzuwandeln?

Beachten Sie, dass wir, damit dies ordnungsgemäß funktioniert, möglicherweise die Fallback-Methode transpose(x) = x wiederherstellen müssen. In diesem Fall können wir die Transponierung genauso gut rekursiv bleiben lassen.

Ich denke, die Entscheidung, ob die Transponierung rekursiv sein soll oder nicht, ist orthogonal dazu, ob wir sie an der Punktsyntaxfusion teilnehmen lassen. Die Entscheidung, es nicht-rekursiv zu machen, ist nicht dadurch motiviert.

@StefanKarpinski , wenn ein Fallback transpose(x) = x wiederhergestellt wird, dann verschwindet der größte Teil der Motivation, es so zu ändern, dass es nicht rekursiv ist.

Was ist das Problem, wenn der Fallback wiederhergestellt wird, aber die Transponierung immer noch nicht rekursiv ist?

@jebej , rekursive Transponierung ist korrekter, wenn sie als mathematische Operation für lineare Operatoren verwendet wird. Wenn ich mich richtig erinnere, war der Hauptgrund dafür, es nicht rekursiv zu machen, dass wir den transpose(x) = x -Fallback nicht definieren müssen, anstatt einen MethodError auszulösen.

Aber es wäre nicht schlimm, den Fallback zu haben, aber immer noch nicht rekursiv zu sein.

Lassen Sie mich zwei Kommentare hinzufügen (ich habe die frühere Diskussion durchgesehen und sie nicht bemerkt - sorry, wenn ich etwas ausgelassen habe):

  • Dokumentation für permutedims besagt, dass dies eine Verallgemeinerung der Transponierung für mehrdimensionale Arrays ist. transpose -Funktion suggeriert, was es nicht ist.
  • Wie soll man einen Vektor x=["a", "b"] transponieren? Eigentlich funktioniert y=x.' und erstellt eine neue Variable, aber getindex scheitert daran. AFAIK, Sie müssen reshape(x, 1, :) oder viel langsamer hcat(x...) verwenden, um dies zu erreichen, aber es ist unnatürlich, eine andere Syntax für Vector zu haben ( permutedims funktioniert hier nicht ).

Was ist Ihr Anwendungsfall für die Transponierung eines Vektors von Zeichenfolgen?

Betrachten Sie zum Beispiel das folgende Szenario:

x = ["$(j+i)" for j in 1:3, i in 1:5]
y = ["$i" for i in 5:9]

und ich möchte y nach der letzten Zeile von x anhängen. Und der einfachste Weg ist vcat eine Transponierung von y .

Kommt in der Praxis vor, wenn Textdaten inkrementell in Matrix{String} protokolliert werden (ich könnte Vector{Vector{String}} verwenden), aber oft ist eine Matrix nützlicher (oder dann stellt sich wieder die Frage, wie man Vector{Vector{String}} umwandelt Matrix{String} durch vertikales Verketten aufeinanderfolgender Elemente).

Ein weiterer Anwendungsfall: Transponieren ist der einfachste Weg, zwei Vektoren orthogonal zueinander zu machen, um eine Funktion über das kartesische Produkt ( f.(v, w.') ) zu übertragen.

Datenpunkt: Gestern bin ich auf eine Partei gestoßen, die verwirrt war über den Postfix-Operator "Broadcast-Adjoint" und warum er sich wie eine Transponierung verhält. Am besten!

FWIW, ich bin der festen Überzeugung, dass wir die .' -Syntax loswerden sollten. Als jemand, der mit Julia besser vertraut ist als mit Matlab, erwartete ich, dass es vektorisiert adjungiert bedeutet, und ich bin wirklich gestolpert, als dies nicht der Fall war. Julia ist nicht Matlab und sollte nicht an die Konventionen von Matlab gebunden sein - wenn in Julia ein Punkt die Vektorisierung der angrenzenden Funktion bedeutet, dann sollte dies in der gesamten Sprache konsistent sein und nicht zufällig die eine schreckliche Ausnahme haben, die .' hat formal nichts mit ' zu tun.

Ich denke, es ist in Ordnung, nur transpose ohne spezielle "Tick"-Notation zu haben, da es die meiste Zeit über eine Matrix aus reellen Zahlen aufgerufen wird, also wäre ' äquivalent, wenn Sie es tun würden möchte mir echt tipperei sparen. Wenn wir eine verschmelzende Version von transponieren wollen, dann glaube ich wirklich nicht, dass .' die richtige Syntax ist.

Das ist ein guter Punkt. Wohl nur das Adjoint benötigt eine superkompakte Syntax.

Nennen wir es einfach transpose und verwerfen .' . In Zukunft können wir überlegen, ob wir .' als punktweise Adjunktion wollen oder ob wir es einfach dauerhaft veraltet lassen wollen, um zu vermeiden, dass Matlab-Benutzer gefangen werden.

Beachten Sie, dass ich gerade die registrierten Pakete durchsucht und mehr als 600 Verwendungen von .' gefunden habe, also ist es nicht sehr selten. Und mit Punktaufrufen / broadcast (die erst in 0.6 begannen, nicht-numerische Daten vollständig zu verarbeiten), wird der Wunsch, nicht-numerische Arrays (wo Adjoint weniger Sinn macht) träge zu transponieren , wahrscheinlich viel häufiger werden, also das Argument für eine kompakte Syntax wird etwas gestärkt.

Dann sollten wir .' so schnell wie möglich verwerfen, bevor noch mehr Code in einem schlechten Nutzungsmuster gefangen wird.

Warum ist es schlecht?

Das Problem ist, dass .' jetzt nicht mehr das bedeutet, was es als gepunkteter Operator zu bedeuten scheint.

Wie ich oben sagte, weil es gegen das allgemeine Muster verstößt, dass . Vektorisierung bedeutet, und es so aussieht, als ob es vektorisiertes Adjoint bedeutet (insbesondere für jemanden, der mit Matlab nicht vertraut ist).

Ich denke, @stevengj macht einen guten Punkt – dies hängt mit dem Wunsch nach einer einfachen nicht rekursiven Transponierung zusammen.

Ich weiß, es war unbeliebt, aber ich fange an, Andreas' Nr. 19344 für zu bevorzugen. An dieser Stelle würde ich es vorziehen, die Verwendung von _allen_ hochgestellten Zeichen als Bezeichner abzulehnen und _beliebige_ abschließende hochgestellte Zeichen als Postfix-Operatoren zu interpretieren. Dies bietet auch einen Weg, um einige der Ungenauigkeiten um literal_pow mit hochgestellten Zahlen zu beheben. Ja, es wäre traurig, χ² und solche Variablennamen zu verlieren, aber ich denke, die Vorteile würden die Nachteile überwiegen.

An dieser Stelle würde ich es vorziehen, die Verwendung von _allen_ hochgestellten Zeichen als Bezeichner abzulehnen und _beliebige_ abschließende hochgestellte Zeichen als Postfix-Operatoren zu interpretieren.

RIP meinen Code
screenshot from 2017-11-09 22-08-25

An dieser Stelle würde ich es vorziehen, die Verwendung aller hochgestellten Zeichen als Bezeichner abzulehnen

Ich glaube wirklich nicht, dass das nötig wäre, wenn wir nur T und vielleicht ein paar andere Dinge in der Zukunft wollen.

Eine törichte Konsistenz …

Ja, es ist etwas widersprüchlich, .' für die Transponierung zu verwenden, aber alle bisher vorgeschlagenen Alternativen scheinen schlechter zu sein. Es ist nicht das Schlimmste auf der Welt zu sagen: " .' ist transponiert, eine Ausnahme von der üblichen Regel über Punktoperatoren." Du lernst das und gehst weiter.

Eine Sache, die bei potenzieller Verwirrung darüber helfen kann, dass .' kein Dot-Broadcast ist, ist, dass es sich um einen Postfix-Operator handelt, während Präfix-Broadcasting op. und Infix .op ist. Wir können also sagen, dass . nicht Broadcast bedeutet, wenn es Postfix ist. Die andere Verwendung von Postfix . ist die Feldsuche, und getfield(x, ') ergibt keinen Sinn und unterscheidet sich daher von anderen Bedeutungen.

(Trotzdem ziehe ich transpose(x) dem Behalten .' vor.)

@stevengj Ich würde wetten, dass viele (vielleicht die meisten) der über 600 Verwendungen von .' in den registrierten Paketen, die Sie oben erwähnt haben, ohne Kosten für die Lesbarkeit und den Code durch ' ersetzt werden könnten würde weiter arbeiten.

Möglicherweise nicht beliebt, aber es könnte immer noch Postfix " und ` geben?

Verwendungen von .' in den registrierten Paketen, die Sie oben erwähnt haben, könnte ohne Kosten für die Lesbarkeit durch ' ersetzt werden, und der Code würde weiterhin funktionieren.

Beachten Sie, dass wir, sobald #23424 landet, transpose für String-Arrays usw. verwenden können, aber nicht adjoint . Best Practice für die Verwendung von x.' in der linearen Algebra wird höchstwahrscheinlich so etwas wie conj(x') werden (hoffentlich ist dies faul, dh kostenlos). Obwohl ich .' wegen seiner Kompaktheit gerne verwende, wird es Benutzer der linearen Algebra vielleicht dazu zwingen, das Richtige zu verwenden, und Benutzer von Arrays von Daten, das ausgeschriebene transpose zu verwenden.

es könnte noch postfix " und `?

Neue Syntax für transpose() erscheint ziemlich verfrüht. IMHO wäre es besser, .' einfach zu verwerfen, um es wie von Ihnen vorgeschlagen durch conj(x') und transpose nach Bedarf zu ersetzen.

Ich habe das Gefühl, dass .' in Matlab so nützlich ist, hauptsächlich weil Matlab darauf besteht, dass "alles eine Matrix ist", zusammen mit dem Mangel an kohärenten Slicing-Regeln, so dass Sie oft zufällige Transponierungen an verschiedenen Stellen einfügen müssen Dinge zum Laufen bringen.

Um die Argumente hier zusammenzufassen:

  1. .' ist jetzt der einzig herausragende Punkt-Operator, der nicht bedeutet „undpunktierten Operator elementweise anwenden“; neue Benutzer, die nicht von Matlab kommen, finden dies eine überraschende Falle.

  2. .' ist jetzt mehrdeutig: Meinten Sie transpose oder meinten Sie conj(x') ? Im Prinzip sollte jede Legacy-Nutzung von .' überprüft werden, um festzustellen, ob die Indizes eines zweidimensionalen Arrays permutiert werden oder ob es sich um eine "unkonjugierte Adjunktion" handelt.

Das erste Problem ist problematisch, aber an sich nicht fatal; Das zweite Problem ist das wirklich Schlimme – dies ist keine einzige kohärente Operation mehr, sondern wird in zwei getrennte Bedeutungen aufgeteilt.

Mir ist gerade aufgefallen, dass, wenn wir .' jemals in "elementweise adjungiert" ändern würden, dann conj(x') ungefähr äquivalent zu x'.' und conj(x)' ungefähr x.'' wäre x.' ist 😬.

Möglicherweise nicht beliebt, aber es könnte immer noch Postfix " und `?

Das Einfügen von Code in Slack zu kopieren und zu sehen, dass die Syntaxhervorhebung zerstört wird, wäre ...

In der Lage zu sein, alles zu transponieren, ist schön, weil es das "Cross-Product" über den Versandmechanismus und andere kurze, prägnante Anwendungsfälle wie diesen erleichtert. Das Problem, keinen einfachen Fallback für diese Art von Dingen zu haben, besteht darin, dass der Hack, den wir sehen werden, ausnahmslos darin besteht, einfach transpose(x) = x Fallbacks (oder auf Basistypen, also Typpiraterie in Paketen) zu definieren, um dies zu erreichen sowas geht problemlos. Das bringt mich zum Nachdenken: Warum ist Complex nicht das Ungewöhnliche? Adjoint der meisten Zahlen ist es selbst, also Adjoint von Komplex ist derjenige, auf den man sich spezialisieren sollte: Kann das nicht über Zahlen hinaus erweitert werden?

Ich sehe hier zwei sehr verwandte Dinge:

1) x' funktioniert nicht für Nicht-Zahlentypen, also wollen wir eine Möglichkeit, dies einfach für andere Daten zu tun
2) transpose(x) ist nicht so einfach wie x.' . Dies gilt hauptsächlich für die Fälle von (1), da die Anwendungsfälle zum Transponieren komplexer Matrizen viel seltener sind.

Aber anstatt (2) nach unten zu gehen, warum nicht versuchen, eine vernünftige Lösung für (1) zu finden?

Vielleicht ist eine vernünftige Lösung nur ein Makro, das ' transponiert statt adjungiert bedeutet?

Aber anstatt (2) nach unten zu gehen, warum nicht versuchen, eine vernünftige Lösung für (1) zu finden?

Wir sind diesen Weg und mehrere angrenzende bereits gegangen. Es gab eine Menge daraus resultierender Diskussionen, die vielleicht jemand anderes zusammenfassen kann, aber zusammenfassend funktioniert es nicht gut. Grundsätzlich macht die mathematische Operation adjoint bei Dingen, die keine Zahlen sind, keinen Sinn. Die Verwendung ' für Nicht-Zahlen, nur weil Ihnen die knappe Syntax gefällt, ist schlecht – es ist die schlimmste Art von Operator-Wortspiel, und es sollte nicht überraschen, dass aus dieser Art von Bedeutungsmissbrauch schlimme Dinge resultieren. Die adjoint -Funktion sollte nur für Dinge definiert werden, von denen es sinnvoll ist, das Adjoint zu nehmen, und ' sollte nur verwendet werden, um dies zu bedeuten.

Denken Sie daran, dass es sich .' , wie es derzeit verwendet wird, grundsätzlich um zwei verschiedene Operationen handelt: Array-Transposition und nicht konjugierte Adjunktion. Das Problem der rekursiven Transponierung hebt die Tatsache hervor, dass dies unterschiedliche Operationen sind und dass wir daher unterschiedliche Arten benötigen, sie auszudrücken. Die Mathy-Leute scheinen unnachgiebig zu sein, dass die nicht konjugierte adjungierte Operation (a) wichtig ist und (b) sich vom einfachen Austausch von Dimensionen unterscheidet. Um korrekt zu sein, sollte insbesondere nicht konjugierte Adjunktion rekursiv sein. Andererseits sollte das Austauschen von Dimensionen eines generischen Arrays eindeutig nicht rekursiv sein. Daher müssen diese Operationen anders geschrieben werden, und bestehende Verwendungen von .' müssen dahingehend disambiguiert werden, dass sie die eine oder andere Bedeutung haben. Das Ablehnen .' ist eine Möglichkeit, dies zu erzwingen.

Obwohl ich der festen Überzeugung bin, dass permutedims(x, (2, 1)) definitiv zu unpraktisch ist, um die Dimensionen eines 2D-Arrays auszutauschen, finde ich das Argument, dass transpose(x) zu unpraktisch ist, nicht überzeugend. Ist diese Operation so verbreitet, dass es zu viel ist, einen einfachen, klaren Funktionsnamen dafür zu haben? Wirklich? Ist das Austauschen der Dimensionen eines Arrays so viel häufiger oder wichtiger als all die anderen Dinge in der Sprache, für die wir Funktionsnamen und Funktionsaufrufsyntax verwenden? Die Haushaltsnotation macht adjoint zu etwas ganz Besonderem, da wir Dinge wie v'v , v*v' und v'A*v schreiben wollen. Deshalb bekommt adjoint eine wirklich schöne Syntax. Aber die Dimensionen eines Arrays tauschen? Es garantiert meiner Meinung nach keinen Betreiber.

Kein starkes Argument, aber ich verwende den Operator ' oft, um kompaktere Arrays zu drucken (wenn sie als einfache Container verwendet werden), beispielsweise wenn ich den Inhalt einiger Vektoren gleichzeitig auf meinem Bildschirm sehen möchte ( und unweigerlich frustriert, wenn es fehlschlägt, weil Elemente nicht transponiert werden können). Eine kurze Syntax für die REPL ist also auf jeden Fall praktisch. (Außerdem macht es dies für Leute, die an Row-Major-Arrays gewöhnt sind, einfacher, eine einfache Möglichkeit zu haben, "die Reihenfolge zu ändern", insbesondere wenn Algorithmen mit 2D-Arrays nach Julia portiert werden; aber definitiv auch kein starkes Argument). Nur um zu sagen, dass es eine nette knappe Syntax ist, die nicht nur für lineare Algebraiker nützlich ist.

Ich hatte einige Syntax-Ideen unter https://github.com/JuliaLang/julia/pull/19344#issuecomment -261621763 kommentiert, im Grunde war es:

julia> const ᵀ, ᴴ = transpose, ctranspose;

julia> for op in (ᵀ, ᴴ)
           <strong i="7">@eval</strong> Base.:*(x::AbstractArray{T}, f::typeof($op)) where {T<:Number} = f(x)
       end

julia> A = rand(2, 2)
2×2 Array{Float64,2}:
 0.919332  0.651938
 0.387085  0.16784

julia>  Aᵀ = (A)ᵀ    # variable definition and function application are both available!
2×2 Array{Float64,2}:
 0.919332  0.387085
 0.651938  0.16784

julia> Aᴴ = (A)ᴴ
2×2 Array{Float64,2}:
 0.919332  0.387085
 0.651938  0.16784

Aber ohne den Hack natürlich, nur die Idee, dass es eine Art "Postfix-Funktionsanwendung" geben kann und dass es Klammern (x)f erfordert, könnten gepunktete Versionen so aussehen (x).f ( xf wäre ein Bezeichner, selbst wenn f ein hochgestelltes Symbol ist).

Dieser Beispiel-Hack funktionierte früher auf 0.6, aber jetzt:

julia> Aᵀ = (A)ᵀ               
ERROR: syntax: invalid operator

julia> Aᵀ = (A)transpose       
2×2 Array{Float64,2}:          
 0.995848  0.549117            
 0.69401   0.908227            

julia> Aᴴ = (A)ᴴ               
ERROR: syntax: invalid operator

julia> Aᴴ = (A)ctranspose      # or adjoint or whatever
2×2 Array{Float64,2}:          
 0.995848  0.549117            
 0.69401   0.908227            

Was traurig ist, ich wollte das ursprünglich für Kräfte tun:

julia> square(n) = n^2; cube(n) = n^3;

julia> Base.:*(n, f::typeof(square)) = f(n)

julia> Base.:*(n, f::typeof(cube)) = f(n)

julia> const ² = square    # why?
syntax: invalid character "²"

julia> const ³ = cube    # why?
syntax: invalid character "³"

Was ich naiv dachte, würde eine Syntax wie: n² = (n)² und n³ = (n)³ ermöglichen. Aber jede Art von numerischer Kennung darf nicht an der ersten Position stehen, aber (A)⁻¹ hat auch funktioniert, wo ⁻¹ war const ⁻¹ = inv .

Ich habe einen ähnlichen Hack für InfixFunctions.jl implementiert .

Als Benutzer könnte ich einfach ein PostfixFunctions.jl -Paket machen und mit dem zufrieden sein, was Sie hier am besten finden. Aber derzeit diese Syntaxbeschränkungen:

  • Die Verwendung numerischer Superindizes am Anfang eines Bezeichners ist nicht zulässig
  • Superindex x * ᶠ im Postfix (implizite Multiplikation im Hack) (x)ᶠ nicht erlaubt

Scheint mir meiner Meinung nach ein bisschen zu viel, ich möchte zumindest in der Lage sein, Bezeichner zu definieren, die mit hochgestellten Ziffern beginnen können, oder allgemeiner, nur die tatsächlichen numerischen Zeichen 0-9 mit Zahlensemantik am Anfang von verbieten Kennung, das wäre genial. 😄

Prost!

Siehe #10762 für eine Diskussion anderer Zahlenzeichen als Bezeichner.

Das andere Problem bezieht sich auf #22089, Operator-Suffixe. +ᵀ ist jetzt ein gültiger Operator, der (wahrscheinlich versehentlich) Bezeichner, die nur aus der Kombination von Zeichen bestehen, in Kontexten, in denen ein Operator erwartet werden könnte, nicht erlaubte. Das scheint mir ein Bug zu sein. Es ist auch etwas seltsam, dass ein gültiger Bezeichner ist, -ᵀ aber nicht -(ᵀ) . Das ist jedoch nicht das Ende der Welt, und meiner Meinung nach wäre es nicht wert, andere mögliche Verwendungen von zu verlieren, wenn man es repariert.

Beachten Sie, dass die Verwendung .' als Postfix- Transpositionsoperator hier nicht einmal auf dem Tisch steht (ungeachtet dessen, was das Thema des Problems sagt), die Überlegung ist tatsächlich, ob wir .' als Postfix-Operator beibehalten sollten für nicht konjugierte Adjungierte, was rekursiv wäre. Dies ist oft dasselbe wie eine Transposition, ist aber im Allgemeinen nicht dieselbe Operation. Wenn die Leute der linearen Algebra bereit sind, .' eine generische Array-Transponierung bedeuten zu lassen, ist das eine andere Geschichte, aber mein Eindruck ist, dass das nicht akzeptabel ist.

@Ismael-VC, ich kann sehen, dass (x)ᵀ als Postfix-Funktionssyntax für hochgestellte Zeichen zulässig ist - denn was würde es sonst bedeuten? Ich denke, wo Ihr Vorschlag anfängt, die Leute in die falsche Richtung zu reiben, ist es zuzulassen, dass jede Kennung als Funktion in der Postfix-Syntax angewendet wird. Ich würde es auf hochgestellte Zeichen beschränken.

@StefanKarpinski , ich dachte, dass der Konsens genau darin bestand, .' nicht rekursive, nicht konjugierte Array-Transposition zuzulassen (falls wir diesen Operator überhaupt haben), während ' die rekursive, konjugierte ist Nebenbetrieb.

Ich hasse wirklich die Idee, für einen Postfix-Transpositionsoperator zu verwenden. Es ist viel zu nützlich, um es als hochgestelltes Zeichen in Variablennamen zu verwenden, wie aᵀa oder LᵀDL = ltdlfact(A) . (Abgesehen von der Tatsache, dass es seltsam wäre, nur für einen Operator zu verwenden, während andere hochgestellte Zeichen in Identifizierungen gültig sind.)

Das war überhaupt nicht mein Verständnis – ich dachte, dass Linalg-Leute dafür sind, a.' so zu lassen, wie es ist, dh conj(a)' bedeutet. .' , aber seine Bedeutung in Array-Transponieren zu ändern, ist etwas ganz anderes – ich bin mir nicht sicher, wie ich darüber denke. Ich stimme zu, dass es ärgerlich und inkonsistent wäre, nur als Postfix-Operator zu haben. Ich mag jedoch den (a)ᵀ -Vorschlag von @Ismael-VC, der die Verwendung aᵀ als Namen nicht verhindern würde.

Meine Erinnerung an diese Diskussionen spiegelt die von Steven wider. Die rekursive, nicht konjugierte Transponierung ist selten und im Allgemeinen ziemlich seltsam. Anständige Zusammenfassung hier: https://github.com/JuliaLang/julia/issues/20978#issuecomment -316141984.

Ich denke, wir sind uns alle einig, dass das Postfix ' adjungiert ist und bleiben sollte.
Ich denke, wir sind uns alle einig, dass Postfix .' eine suboptimale Syntax ist.
Ich denke, die meisten stimmen darin überein, dass eine nicht-rekursive (strukturelle) Transponierung nützlicher ist als eine rekursive Transponierung.

Ok, also die Punkte, in denen sich alle einig zu sein scheinen:

  1. Verwenden Sie a' für adjoint(a)
  2. Verwenden Sie conj(a)' oder conj(a') für die (nicht-)konjugierte Adjunktion.

Der einzige Streitpunkt ist also, wie die Array-Transponierung geschrieben wird:

  • Als a.' oder
  • Als transpose(a) oder
  • Als (a)ᵀ .

Ist diese Einschätzung richtig?

Ja, ich denke schon (wobei die "Array-Transponierung" nicht rekursiv ist).

So wie ich es verstehe, sind sich auch alle einig, dass transpose(a) definitiv eine gültige Syntax (und nicht rekursiv) sein sollte, und die einzigen Meinungsverschiedenheiten sind, ob .' und/oder (a)ᵀ sollte eine alternative (völlig gleichwertige) gültige Syntax sein.

Ansatz (1) von https://github.com/JuliaLang/julia/issues/20978#issuecomment -315902532, der ein gutes Stück Unterstützung erhielt (z. B. https://github.com/JuliaLang/julia/issues/20978# issuecomment-316080448), bleibt eine Möglichkeit. Ich habe einen Zweig, der diesen Ansatz realisiert (der flip(A) einführt), den ich posten kann.

Für das, was es wert ist, unterstütze ich die Ablehnung .' . Die Verwirrung und Mehrdeutigkeit in diesem Thread ist an sich schon ein starkes Argument dafür. Am besten!

Ich glaube, solange wir Postfix ' haben, werden die Leute es verwenden wollen, um f über ein kartesisches Produkt von Vektoren mit f.(v, w') zu übertragen. Und die Leute werden es verwenden wollen, um einen Vektor von Strings in einen Zeilenvektor von Headern für eine tabellenähnliche Struktur umzuformen. Daher ist es für mich überzeugend, einen einfachen und benutzerfreundlichen Ersatz zu haben, an den wir sie verweisen können.

Hier ist eine Option, die wir nicht in Betracht gezogen haben: A*' – ein neuer Bigraph. Die typische mathematische Notation könnte dies als conj(A)' interpretieren, was eigentlich ziemlich nah an dem liegt, was wir wollen. Es war in Version 0.6 verfügbar, aber in Version 0.7 erlauben wir die Verwendung * zum Verketten von Zeichen… funktioniert aber immer noch.

Ich glaube nicht, dass Postfix " und ` verfügbar sind, da benutzerdefinierte Zeichenfolgenliterale über das Ende einer Zeile hinaus analysiert werden. Postfix * selbst ist aus dem gleichen Grund ebenfalls nicht verfügbar. Postfix prime A′ ist wahrscheinlich einer der am häufigsten verwendeten Unicode-Bezeichner, also ist das noch mehr als Aᵀ .

Ehrlich gesagt, nachdem ich mir meinen Code angesehen habe, verwende ich .' überhaupt nicht, also ist transpose(a) wahrscheinlich in Ordnung.

Beachten Sie, dass ich gerade die registrierten Pakete durchsucht und über 600 Verwendungen von .' gefunden habe, also ist es nicht sehr selten.

Wurde diese Stelle überhaupt überprüft, um zu sehen, ob .' nicht verwendet wurde, wo ' in Ordnung gewesen wäre? Ich fange an zu glauben, dass das öfter als nicht wahr sein könnte. Ansonsten war der einzige Ort, an dem ich eine legitime Verwendung von .' gesehen habe, bevor Plots.jl-Labels einen Vektor zulassen würden (stattdessen wollte man einen Zeilenvektor von Zeichenfolgen), aber das wurde geändert. Für Codes, bei denen ich das wirklich oft brauche, würde ich anfangen, T = transpose lokal zu machen, oder ein Makro werfen, um ' in transpose zu ändern.

<strong i="17">@transpose</strong> A = A'*A*B'*B*C'*C

wäre für mich in diesem seltenen Fall in Ordnung.

Die Leute werden es verwenden wollen, um f über ein kartesisches Produkt von Vektoren mit f.(v, w') zu übertragen. Und die Leute werden es verwenden wollen, um einen Vektor von Strings in einen Zeilenvektor von Headern für eine tabellenähnliche Struktur umzuformen. Daher ist es für mich überzeugend, einen einfachen und benutzerfreundlichen Ersatz zu haben, an den wir sie verweisen können.

Wenn es nur einmal in einer Anweisung auftaucht, ist es dann nicht in Ordnung, einfach transpose zu verwenden?

Die a*' -Syntax für conjugate-adjoint ist ziemlich nett, obwohl es nicht wirklich so aussieht, als wäre das die Operation, für die wir eine bessere Syntax brauchen. &a wird bald verfügbar sein und schlägt vor, Dinge auszutauschen, obwohl es sich ziemlich von traditionellen Notationen dafür unterscheidet.

Vielleicht ist es Zeit für eine Strohumfrage?

Wie sollten wir die strukturelle Transponierung schreiben?

(ungefähr in der Reihenfolge des Vorschlags; hier keine Urteile zu Emoji-Namen)

  • 👍: A.' – ändere einfach die Bedeutung, behalte die Syntax bei
  • 👎: transpose(A) — keine spezielle Syntax
  • 😄: t(A) oder tr(A) — keine spezielle Syntax, aber einen kürzeren Namen exportieren
  • 🎉: Aᵀ – mit nur und vielleicht ein oder zwei hochgestellten Sonderzeichen von Bezeichnern
  • 😕: (A)ᵀ – wobei alle hochgestellten Zeichen von Bezeichnern getrennt sind, die sich wie Postfix-Operatoren verhalten
  • ❤️: A*' – beschönige dieses unheimliche Tal, es bedeutet eine strukturelle Transponierung
  • Wenn du &A bevorzugst, wirf ein 🎉 auf Stefans Post unmittelbar darüber (wir haben keine Emojis mehr)

In den LinAlg-Diskussionen wurde tatsächlich darüber gesprochen, .’ für die nicht-rekursive Transponierungsverwendung zu verschenken, da conj(x’) relativ ungewöhnlich ist. Allerdings ist eine mathematische Syntax und sollte wirklich die mathematische Bedeutung haben (wenn überhaupt).

Sehr stark dagegen, dass tr(A) eine Matrixtransposition bedeutet - jeder wird denken, dass dies eine Matrixspur bedeutet: https://en.wikipedia.org/wiki/Trace_ (linear_algebra)

Wenn hochgestellte Zeichen als Bezeichner nicht missbilligt werden (was wahrscheinlich vor 1.0 ernsthaft in Betracht gezogen werden muss), dann ist ᵀ(A) auch eine Möglichkeit.

In Bezug auf den Vorschlag (A)ᵀ entschuldige ich mich dafür, dass ich diese Diskussion mit der folgenden Bemerkung etwas entgleist habe:

Ich habe mich nie sehr darum gekümmert, als unären Operator zur Verfügung zu haben, zumal Sie am Ende sowieso √(...) eingeben werden, sobald Sie es auf eine Variable anwenden möchten, die größer als a ist ein oder ein paar Zeichen. Außerdem fand ich den Funktionsunterschied zwischen und √a immer sehr künstlich. Es ist wahrscheinlich sinnvoll, wenn Sie sich mit Unicode-Klassen usw. auskennen, aber für alle anderen muss dies absurd erscheinen. Sicherlich ist es nützlich, als gültigen Variablennamen zu haben, aber ebenso könnte √a ein nützlicher Variablenname sein, um die Quadratwurzel von a zu speichern, wenn Sie sie mehrfach verwenden müssen mal. Oder kompliziertere Ausdrücke wie a²b und seine Quadratwurzel a√b , wobei ersterer ein gültiger Bezeichner ist und letzterer nicht. Ich mag vor allem Konsistenz.

Aus Gründen der Konsistenz mag ich den Vorschlag, Postfix-Operatoren zu haben, wenn die Klammern (A)ᵀ , (a)² verwendet werden, in Kombination mit dem Entfernen des unären Unicode-Operators (und seiner Verwandten). es kann auch in Bezeichnern verwendet werden (während es immer noch als normaler Funktionsaufruf √(a) zugänglich ist).

Ich stimme zu 100% mit dem überein, was @Jutho gesagt hat, und habe es bei einer Reihe von Gelegenheiten gedacht. Würde es dir etwas ausmachen, ein Problem zu eröffnen, @Jutho? Vorschlag: Erlaube in Bezeichnernamen, fordere √(x) für den Aufruf als op.

nächste Frage -> was ist mit 2 |> √ ?

Lassen Sie uns in einem anderen Thread über diskutieren, aber kurz gesagt bedeutet 2 |> √ √(2) .

Eine andere Alternative, die keine Parser-Änderungen erfordern und einfach einzugeben wäre, wäre A^T für die Transponierung (indem T als Singleton-Typ mit einer Methode ^ definiert wird). … oh, ich sehe, dass @mbauman diese Idee auch hatte. Es ist ein bisschen hässlich, aber nicht mehr als A.' .

Ich habe keine Fachkenntnisse, aber ich interessiere mich sehr für das Ergebnis dieser Diskussion, denn wer wird im Laufe meiner Arbeit wahrscheinlich Tausende von Zeilen mit Matrixausdrücken eingeben.

transpose(A) # with no special syntax gewinnt die obige Abstimmung, tut aber weh für meine Augen und Finger.

In Python ist die übliche Verwendung wahrscheinlich mit numpy und einer Menge Zeug, das wie folgt aussieht und nicht so schlimm ist:

import numpy as np
# define matrix X of n columns, with m rows of observations
error = X.dot(Theta.T) - Y
gradient = (1 / m) * (X.dot(Theta.T) - Y).T.dot(X)

würde ich nicht machen wollen:

grad = 1/m * transpose(X * transpose(Theta) - Y)) * X

Es verändert die mentale Vorstellung von Transponierung völlig von der Konvention, auf die sich die mathematische Notation festgelegt hat, die ein Postfix-Signifikator ist, normalerweise Aᵀ oder Aᵗ .

Persönlich bin ich sehr zufrieden mit A' , das in Julia v.0.6 funktioniert, bevor es von Adjoint übernommen wurde. Wird Adjoint sehr oft verwendet?

Hier sind meine Kommentare in einer Tabelle:

Aᵀ or Aᵗ    if the world won't accept unicode operators, let them use transpose(A)
A'          close to math notation, easy to type and *especially* easy to read
A^'         this could signal `^` not to be parsed as Exponentiation.
A.'         conflicts with dotted operator syntax, but at face value OK
A^T or A^t  these are pretty good, but what if variable `T` is meant to be an exponent? 
A.T         same as numpy, same dotted operator collision
t(A)        nesting reverses semantics, 3 keystrokes and two of them with shift key.
transpose(A) with no special syntax     # please don't do this.

Persönlich bin ich sehr zufrieden mit A' , das in Julia v.0.6 funktioniert, bevor es von Adjoint übernommen wurde. Wird Adjoint sehr oft verwendet?

Ich verstehe nicht, A' war schon immer der Adjunkt von A . Früher nannten wir die zugrunde liegende Funktion ctranspose für die konjugierte Transponierung, aber wir haben sie ohne Änderung der Funktionalität in den entsprechenden Begriff adjoint umbenannt.

Wenn Sie mit linearer Algebra arbeiten, möchten Sie sowieso eher eine konjugierte Transponierung, also geben Sie A' statt transpose(A) ein. Die Popularität, keine spezielle Syntax für nicht-konjugierte Transponierungen zu definieren, ist (vermutlich) teilweise darauf zurückzuführen, dass es für die meisten linearen algebraischen Anwendungen wirklich nicht so üblich ist, die nicht-konjugierte Transponierung zu wollen.

Wenn Sie lineare Algebra machen, dann ...

Wenn dein Werkzeug Hammer ist dann ... :)

... Sie müssen über die Möglichkeit nachdenken, dass Julia zu einer allgemeinen Programmiersprache heranwächst.

Vielleicht nicht, vielleicht bleibt es beim Argot der linearen Algebra - das ist eine Möglichkeit, die Programmierer wie ich bedenken müssen. :)

@mahiki , du bist NumPy-Beispiel:

import numpy as np
# define matrix X of n columns, with m rows of observations
error = X.dot(Theta.T) - Y
gradient = (1 / m) * (X.dot(Theta.T) - Y).T.dot(X)

wörtlich in Julia geschrieben werden als:

error = X*Θ' - Y
gradient = (1/m) * (X*Θ' - Y)' * X

oder unter der Annahme, dass Vektoren in diesem NumPy-Beispiel Zeilen sind und in Julia Spalten wären:

error = X'Θ - Y
gradient = (1/m) * (X'Θ - Y) * X'

was ungefähr so ​​klar und mathematisch erscheint, wie es nur geht. Wenn Ihre Daten real sind, dann sind adjungiert und transponieren die gleiche Operation, weshalb Sie oben möglicherweise transponieren verwenden – aber mathematisch adjungiert ist die richtige Operation. Wie @ararslan sagte, bedeutete $#$ X' $#$ in Julia (und auch in Matlab) immer adjoint . Früher hieß er ctranspose , kurz für „ conjugate transpose “, aber dieser Name war eine Fehlbezeichnung, da die definierende Eigenschaft des Operators dies ist

dot(A*x, y) == dot(x, A'y)

Dies ist die definierende Eigenschaft der hermiteschen Adjungierten , wird aber zufällig durch die konjugierte Transponierte erfüllt, wenn A eine komplexe Matrix ist. Deshalb ist „adjungiert“ der richtige Oberbegriff für diesen Operator.

Alles in allem habe ich oben sowohl für transpose(a) als auch a.' gestimmt, da ich denke, dass es in Ordnung wäre, wenn a.' eine strukturelle Transponierung bedeuten würde. Es würde wie erwartet funktionieren, und obwohl es in einem generischen Code nicht rekursiv und daher nicht "mathematisch korrekt" wäre, scheint es gut genug zu sein, wenn es wie erwartet funktioniert. Und den Leuten zu sagen, dass sie conj(a') in generischem Code verwenden sollten, scheint eher eine Aufklärungssache zu sein als etwas, mit dem wir die Leute wirklich über den Kopf schlagen müssen.

@mahiki Wenn Sie aus irgendeinem Grund wirklich oft transpose anstelle von adjoint in Ihrem Code verwenden müssen, könnten Sie ein kürzeres Makro wie @t definieren, das transpose aliasiert

Sie müssen über die Möglichkeit nachdenken, dass Julia zu einer allgemeinen Programmiersprache heranwachsen könnte.

@ Liso77 Das ist es schon. Als nur eines von vielen Beispielen betreibt Nanosoldier einen Webserver, der GitHub-Ereignisse abhört und bei Bedarf Leistungsbenchmarks ausführt, alles in Julia. Das ist allerdings ein Exkurs, und ich möchte nicht, dass dieser Thread vom Thema abweicht.

Wenn Sie eine Art Matrix mit nicht numerischen Daten transponieren – was ein absolut gültiger Anwendungsfall ist – scheint die mathematische Transpositionsnotation tatsächlich wie ein schlechtes Wortspiel zu sein. In diesem Fall denke ich, dass es besser wäre, expliziter zu sagen, wonach Sie fragen, zB transpose (oder sogar permutedims , je nach Ihren spezifischen Bedürfnissen).

Wenn Sie eine Art Matrix mit nicht numerischen Daten transponieren – was ein absolut gültiger Anwendungsfall ist – scheint die mathematische Transpositionsnotation tatsächlich wie ein schlechtes Wortspiel zu sein.

Da A.' nicht wirklich "mathematische Transponierungsnotation" im üblichen Sinne ist, sehe ich kein Argument dafür oder dagegen.

Ich denke, dass @ararslan nicht gegen das bestehende .' argumentiert, sondern eher gegen die Einführung einer hochgestellten T-Syntax. Ich stimme eher zu - wenn Sie das Konzept der linearen Algebra von Adjoint meinen, sollten Sie ' verwenden (selbst wenn Ihre Matrix zufällig real ist). Und wenn Sie eine Matrix mit nicht numerischen Daten haben, dann ist es natürlich vollkommen legitim, die beiden Indizes zu permutieren, aber diese Operation ist nicht wirklich die "Transponierung", wie wir normalerweise darüber nachdenken, und die Verwendung der mathematischen hochgestellten T-Notation ist es wahrscheinlich eher verwirren als klären. Die einzige Situation, in der eine hochgestellte T-Notation wirklich angemessen wäre, ist, wenn Sie eine numerische Matrix haben, deren Indizes Sie permutieren möchten, aber den adjungierten linearen Operator wirklich nicht wollen. Solche Situationen existieren sicherlich, sind aber möglicherweise zu selten, um die Einführung einer neuen Syntax zu rechtfertigen.

... aber diese Operation ist nicht wirklich das "Transponieren", wie wir es normalerweise denken, ...

Wenn dies so ungewöhnlich ist, warum stimmen ararslan und viele andere für die Schreibweise der strukturellen Transponierung als transpose(A) ?

Danke @StefanKarpinski @ararslan @ttparker. Ich musste zu meinem Text zur linearen Algebra zurückkehren und das Adjoint wiederentdecken, es ist in Ordnung da drin. Ich habe das vor der Komplexen Analyse gemacht, wahrscheinlich deshalb habe ich es nicht beachtet.

Ich liebe es, das tun zu können
gradient = (1/m) * (X'Θ - Y) * X'

Meine Verwirrung rührt von der weit verbreiteten Verwendung von „transpose“ (als hochgestelltes T) in Referenzdokumenten, Artikeln, Lehrbüchern usw. her, zum Beispiel Andrew Ngs Stanford CS229-Vorlesungsunterlagen , wo der entsprechende Julia-Code adjoint als verwenden würde im sauber aussehenden Beispiel von @StefanKarpinski oben. Dies liegt daran, dass adjungiert und transponiert in ℝ äquivalent sind (richtig?) . update: jap

Jetzt ist meine bevorzugte Notation für die Transponierung einfach alles, was logisch konsistent ist. Offensichtlich liegt .' nicht an einem Konflikt mit der gepunkteten Operatorsyntax, und ich habe nichts gegen transpose(A) ohne spezielle Syntax einzuwenden, da außer einer Unicode-Hochstellung keine vernünftige spezielle Syntax verfügbar zu sein scheint.

Ich mag die Lösung von @ttparker , wenn ich viele transponierte Makros @t schreibe, die transpose aliasieren.

Wieder habe ich mich geirrt, als ich sagte:

transpose(A) with no special syntax # please don't do this.

Vielen Dank, dass Sie meine Kommentare trotz meiner schlechten Fähigkeiten in Mathematik auf Hochschulniveau ernst genommen haben.

(Aus dem Diskurs .)

Ich möchte, dass ' ein Post-Fix-Operator ist, der f' auf '(f) abbildet, wobei Base.:'(x::AbstractMatrix) = adjoint(x) und der Benutzer frei ist, andere Methoden hinzuzufügen, die haben nichts mit Adjunkten zu tun. (Zum Beispiel mögen einige Leute f' , um sich auf df/dt zu beziehen.)

Mit den in 0.7 eingeführten Operator-Suffixen wäre es dann natürlich, dass f'ᵃ auf 'ᵃ(f) wird, und so weiter, was es dem Benutzer ermöglicht, seine eigenen Postfix-Operatoren zu definieren. Dies würde es möglich machen, Base.:'ᵀ(x::AbstractMatrix) = transpose(x) und Base.:'⁻¹(x::Union{AbstractMatrix,Number}) = inv(x) usw. zu haben.

Das Schreiben A'ᵀ ist vielleicht nicht so sauber wie Aᵀ , aber es würde nicht erforderlich sein, Variablennamen, die auf enden, zu verwerfen.

Auf den ersten Blick sieht das so aus, als könnte es sich um eine nicht brechende Funktion handeln. Das ist ein sehr cleverer Kompromiss. Ich mag das.

Scheint mir vernünftig. Das Schwierigste ist, einen Namen für die Funktion ' zu finden – die Präfix-Syntax funktioniert in diesem Fall nicht.

Das Schwierigste ist, einen Namen für die '-Funktion zu finden

apostrophe ? Vielleicht zu wörtlich...

Ist es überhaupt möglich, die Präfix-Syntax zum Laufen zu bringen (z. B. mit einer expliziten (')(A) -Syntax?)? Wenn nicht, dann ist das ein Problem, da es die von https://github.com/JuliaLang eingeführte Regel „if-you-can-define-the-symbol-name-then-you-can-override-its-syntax“ verletzen würde

Edit: scheint verfügbar zu sein:

julia> (')(A)


ERROR: syntax: incomplete: invalid character literal

julia> (')(A) = 2


ERROR: syntax: incomplete: invalid character literal

Leider ist ' eines der am schwierigsten zu verwendenden Zeichen als Bezeichnername, da es eine andere Art von Atom (Zeichen) einführt, die einen sehr hohen Vorrang hat (gleich dem Vorrang von Bezeichnern selbst). Ist beispielsweise (')' eine Anwendung von ' auf sich selbst oder ein offener Klammersatz, gefolgt von einem ')' -Literal?

Eine Option, die kurzfristig nicht praktikabel ist, besteht darin, Zeichenliterale so zu deklarieren, dass sie keinen Wert ' haben, und stattdessen ein Zeichenfolgenmakro wie c"_" zu verwenden.

Wie wäre es, wenn ' als Bezeichner analysiert wird, wenn ein Punkt-Doppelpunkt vorangestellt ist, sodass Base.:' funktionieren würde?

Natürlich ist (@__MODULE__).:'(x) = function_body etwas umständlich zu schreiben, aber (x)' = function_body sollte genauso funktionieren. Bearbeiten: Nein, da (x)' dem Aufruf von ' in Base werden sollte. Das Definieren einer ' -Funktion im aktuellen Modul wäre umständlich, aber es gäbe auch keinen Grund dafür.

Oder wie wäre es, '' als Bezeichner ' #$ parsen zu lassen, wenn es sonst als leeres Zeichenliteral geparst worden wäre (was derzeit ein Fehler auf Parsing-Ebene ist). In ähnlicher Weise würde ''ᵃ als Bezeichner 'ᵃ usw. geparst werden.

Alles, was derzeit kein Syntaxfehler ist, würde immer noch wie zuvor geparst werden (zum Beispiel 2'' ist das Postfix ' zweimal auf 2 $ angewendet), aber 2*'' würde es jetzt tun parse als zweimal ' .

Es scheint verwirrend, dass wir a'' === a aber ''(a) === a' haben würden. Es scheint besser zu sein, stattdessen Base.apostrophe als Namen zu verwenden (oder so ähnlich).

Könnte es besser sein, diese Diskussion in ein neues Github-Problem aufzuteilen, da es um ' -Syntax geht, die nicht direkt mit der Matrixtransposition zusammenhängt?

Gibt es eine automatisierte Methode zum Aufteilen von Problemen oder sollte ich einfach ein neues öffnen und auf die Diskussion hier verlinken?

Letzteres

Die einzige Situation, in der eine hochgestellte T-Notation wirklich angemessen wäre, ist, wenn Sie eine numerische Matrix haben, deren Indizes Sie permutieren möchten, aber den adjungierten linearen Operator wirklich nicht wollen. Solche Situationen existieren sicherlich, sind aber möglicherweise zu selten, um die Einführung einer neuen Syntax zu rechtfertigen.

Ich denke, ich bin viel zu spät für die Diskussion, aber ich möchte auf eine Verwendung hinweisen, die meiner Meinung nach erwähnenswert ist: Die Anwendung der komplexen schrittweisen Differenzierung auf eine reellwertige Funktion, in der transpose enthalten ist es. (Ich persönlich habe herausgefunden, dass ich aus diesem besonderen Grund .' in MATLAB und Julia brauchte.)

Ich gebe ein Beispiel mit mehreren Vorkommen von transpose (vielleicht könnte ich es vermeiden, es auf diese Weise zu tun?)

using LinearAlgebra

# f : Rⁿ → R
#     x  ↦ f(x) = xᵀ * x / 2
f(x) = 0.5 * transpose(x) * x

# Fréchet derivative of f
# Df : Rⁿ → L(Rⁿ, R)
#      x  ↦ Df(x) : Rⁿ → R (linear, so expressed via multiplication)
#                   h  ↦ Df(x)(h) = Df(x) * h
Df(x) = transpose(x) 

# Complex-step method version of Df
function CSDf(x) 
    out = zeros(eltype(x), 1, length(x))
        for i = 1:length(x)
        x2 = copy(x) .+ 0im
        h = x[i] * 1e-50
        x2[i] += im * h
        out[i] = imag(f(x2)) / h
    end
    return out
end

# 2nd Fréchet derivative
# D2f : Rⁿ → L(Rⁿ ⊗ Rⁿ, R)
#       x  ↦ D2f(x) : Rⁿ ⊗ Rⁿ → R (linear, so expressed via multiplication)
#                     h₁ ⊗ h₂ ↦ D2f(x)(h₁ ⊗ h₂) = h₁ᵀ * D2f(x) * h₂
D2f(x) = Matrix{eltype(x)}(I, length(x), length(x))

# Complex-step method version of D2f
function CSD2f(x)
    out = zeros(eltype(x), length(x), length(x))
    for i = 1:length(x)
        x2 = copy(x) .+ 0im
        h = x[i] * 1e-50
        x2[i] += im * h
        out[i, :] .= transpose(imag(Df(x2)) / h)
    end
    return out
end 

# Test on random vector x of size n
n = 5
x = rand(n)
Df(x) ≈ CSDf(x)
D2f(x) ≈ CSD2f(x)

# test that the 1st derivative is correct Fréchet derivative
xϵ = √eps(norm(x))
for i = 1:10
    h = xϵ * randn(n) # random small y
    println(norm(f(x + h) - f(x) - Df(x) * h) / norm(h)) # Fréchet check
end

# test that the 2nd derivative is correct 2nd Fréchet derivative
for i = 1:10
    h₁ = randn(n) # random h₁
    h₂ = xϵ * randn(n) # random small h₂
    println(norm(Df(x + h₂) * h₁ - Df(x) * h₁ - transpose(h₁) * D2f(x) * h₂) / norm(h₂)) # Fréchet check
end
# Because f is quadratic, we can even check that f is equal to its Taylor expansion
h = rand(n)
f(x + h) ≈ f(x) + Df(x) * h + 0.5 * transpose(h) * D2f(x) * h

Der Punkt ist, dass f und Df mit transpose definiert werden müssen und nicht den Adjoint verwenden dürfen.

Ich glaube nicht, dass die komplexe Schrittmethode bei Julia besonders relevant ist. Ist es nicht ein netter Hack/Workaround, eine automatische Differenzierung in Fällen zu erhalten, in denen eine Sprache effiziente eingebaute komplexe Zahlen unterstützt, aber ein gleichwertig effizienter Dual -Zahlentyp nicht definiert werden kann? Das ist bei julia nicht der Fall, das wirklich schöne automatische Differenzierungsbibliotheken hat.

Ich stimme der Verwendung von Dual-Zahlen anstelle der Methode mit komplexen Schritten zu, und das ist ein sehr guter Punkt, den Sie ansprechen (ich persönlich habe bereits alle meine Auswertungen mit der Methode mit komplexen Schritten in Julia durch Einsen mit zwei Zahlen ersetzt). Ich denke jedoch, dass dies immer noch ein gültiger Anwendungsfall für Demonstrationszwecke, das Lehren von Tricks (siehe z. B. Nick Higham, der auf der Julia Con 2018 über die Methode der komplexen Schritte spricht ) und die Portabilität (mit anderen Worten, ich mache mir darüber Sorgen) ist Die MATLAB-Version des obigen Codes mit komplexen Zahlen wäre sauberer).

Aus der Welt der Ingenieure und möglicherweise Physiker kommend, die komplexe Arrays häufiger als echte Arrays verwenden, ist es ein bisschen mühsam, keinen Transponierungsoperator zu haben. (Komplexe Zeigerdarstellung für eine harmonische Zeitabhängigkeit ist in unserem Bereich allgegenwärtig.) Ich persönlich würde die numpy-Syntax von xH und xT bevorzugen, obwohl meine einzige Überlegung die Prägnanz ist.

Die Dichte des Transponierungsoperators relativ zur hermitischen Transponierung beträgt in meinem Code etwa 1 zu 1. Daher ist mir die unkonjugierte Transponierung ebenso wichtig. Ein Großteil der Verwendung von Transponieren besteht darin, äußere Produkte zu erstellen und Arrays für die Verbindung mit anderem Code oder für die Matrixmultiplikation richtig zu dimensionieren.

Ich beabsichtige vorerst, einfach ein Makro oder eine Ein-Zeichen-Funktion für die Operation bereitzustellen, aber was ist das richtige Äquivalent zur alten Funktionalität, transpose() oder permutedims()?

transpose ist für die lineare Algebra gedacht und ist rekursiv, und permutedims ist für die nicht-rekursive Anordnung von Daten jeglicher Art.

Es ist interessant, dass Sie sagen, dass Sie sowohl die Transponierung als auch die Adjunktion verwenden. Früher war ich genauso, aber hauptsächlich, weil ich dazu neigte, Fehler zu machen, wo meine Daten echt waren, also neigte ich dazu, zu transponieren, aber eigentlich war die Adjunktion die richtige Operation (verallgemeinert auf den komplexen Fall - Adjungation war die richtige Operation für meinen Algorithmus). Natürlich gibt es (viele) gültige Ausnahmen.

In allem, was mit Elektrodynamik zu tun hat, verwenden Sie häufig raumähnliche Vektoren und möchten Vektoroperationen in R ^ n (typischerweise n = 3) verwenden, dh insbesondere transpose , obwohl Ihre Vektoren komplexwertig sind, weil Sie habe eine Fourier-Transformation genommen. Es scheint, als würde @mattcbro über diese Art von Anwendungen sprechen.

Abgesehen davon denke ich beim Lesen von Syntaxdiskussionen oft darüber nach, dass ich mir persönlich nicht vorstellen könnte, dass eine etwas ausführlichere Syntax das ist, was meine Programmiergeschwindigkeit oder -effizienz verlangsamt. Das Nachdenken über den Algorithmus selbst und die natürlichste/effizienteste Art, ihn zu implementieren, erfordert viel mehr Zeit.

In allem, was mit Elektrodynamik zu tun hat, verwenden Sie häufig raumähnliche Vektoren und möchten Vektoroperationen in R ^ n (typischerweise n = 3) verwenden, dh insbesondere transponieren, obwohl Ihre Vektoren komplexwertig sind, weil Sie einen Fourier genommen haben verwandeln.

Nicht unbedingt. Oft möchte man zeitgemittelte Größen aus den Fourier-Amplituden, in diesem Fall verwendet man das komplexe Skalarprodukt, z. B. ist ½ℜ[𝐄*×𝐇] der zeitgemittelte Poynting-Fluss aus den komplexen Fourier-Komponenten und ¼ε₀|𝐄|² ist a zeitlich gemittelte Vakuumenergiedichte. Da andererseits der Maxwell-Operator (typischerweise) ein komplex-symmetrischer Operator („reziprok“) ist, verwendet man oft ein unkonjugiertes „inneres Produkt“ für die (unendlich-dimensionale) Algebra an den Körpern 𝐄(𝐱) etc. vorbei alle platz.

Das stimmt, ich hatte das Wort oft im ersten Satz, aber anscheinend entfernt :-).

Nun, wenn Sie dorthin gehen möchten, elektromagnetische Größen werden noch präziser in einer algebraischen Formulierung von Clifford geschrieben, die oft als geometrische Algebra bezeichnet wird. Diese Algebren haben mehrere Automorphismen und Antiautomorphismen, die bei der Formulierung der Theorie eine entscheidende Rolle spielen, insbesondere bei der Betrachtung von Streuproblemen.

Diese Algebren haben typischerweise eine prägnante Matrixdarstellung und diese Morphismen lassen sich oft leicht über komplexe Transponierung, Hermitesche Transponierung und Konjugation berechnen.

Nichtsdestotrotz, wie ich bereits erwähnt habe, besteht meine primäre Verwendung von transponieren darin, meine Arrays so anzuordnen, dass sie mit anderen Arrays und anderem Code verbunden sind, und um eine Matrixmultiplikation zu erhalten, die gegen die korrekte Dimension eines abgeflachten Arrays arbeitet.

Ich persönlich würde die numpy-Syntax von xH und xT bevorzugen

Jetzt in 1.0 einfach zu implementieren und sollte effizient sein:

function Base.getproperty(x::AbstractMatrix, name::Symbol)
    if name === :T
        return transpose(x) 
    #elseif name === :H # can also do this, though not sure why we'd want to overload with `'`
    #    return adjoint(x)
    else
        return getfield(x, name)
    end
end 

Das ist überraschend einfach und irgendwie ordentlich. Der Nachteil scheint zu sein, dass orthogonale Verwendungen von getproperty nicht miteinander komponieren. Jeder, der getproperty für seinen spezifischen Matrixtyp implementiert, muss das generische Verhalten von Hand implementieren.

orthogonale Verwendungen von getproperty werden nicht erstellt

Hmm. Ich frage mich, ob das impliziert, dass xT auf getproperty(x, Val(:T)) hätte gesenkt werden sollen. Ich schaudere bei dem Gedanken, was das mit dem armen Compiler machen würde.

Ich bin mir sicher, dass jeder seine Meinung hat - aber für mich ist es fast ein Merkmal, dass es schwierig ist, eine generische Schnittstelle aus der Punktsyntax zu erstellen. Verstehen Sie mich nicht falsch, es ist ein wirklich großartiges Feature und wunderbar zum Definieren benannter Tupel-ähnlicher Strukturen und so weiter.

(Es ist auch möglich, Ihren Typen ziemlich einfach eine Val Dispatch-Ebene hinzuzufügen).

Der Code von @ c42f funktioniert wie ein Zauber. Unglücklicherweise versuche ich, Code zu schreiben, der in den Versionen 0.64 und höher funktioniert, was mich dazu zwingt, entweder transpose oder meine eigene definierte Funktion T(A) = transpose(A) zu verwenden. Vielleicht wäre ein Makro etwas sauberer und etwas effizienter gewesen.

Um es klar zu sagen, ich schlage nicht vor, dass die Definition dieses speziellen getproperty eine gute Idee für Benutzercode ist. Es ist nur wahrscheinlich, dass die Dinge auf längere Sicht kaputt gehen ;-) Obwohl wir vielleicht eines Tages ein so gutes Gefühl für die Konsequenzen haben werden, dass wir x.T in Base $ definieren könnten.

Aber im Allgemeinen frage ich mich, warum diese Art der Verwendung von Eigenschaften zum Definieren von "Gettern" in generischen Schnittstellen eigentlich schlecht ist. Zum Beispiel haben generische Feld-Getter-Funktionen derzeit ein riesiges Namespace-Problem, das einfach durch vernünftige Verwendung von getproperty gelöst wird. Es ist viel schöner, x.A zu schreiben, als MyModule.A(x) zu schreiben, einen längeren hässlichen Funktionsnamen wie get_my_A(x) , oder den extrem allgemeinen Namen A von einem Benutzer zu exportieren Modul. Das einzige Problem, wie ich es sehe, ist die erwartete Fähigkeit, die Bedeutung von .B für Untertypen zu überschreiben, unabhängig davon, ob .A generisch für einen Supertyp definiert wird. Daher der halbernste Kommentar zu Val .

Lustige Idee:

julia> x'̄
ERROR: syntax: invalid character "̄"

Das Zeichen sieht aus wie ein T , ist aber eigentlich ein ' mit einem Balken darüber. Ich bin mir nicht sicher, ob es ernst gemeint ist...

screen shot 2018-09-10 at 11 29 56

Ja, sieht für mich auf GitHub auch so aus. Aber es ist ein Overbar. Kopieren und Einfügen in mein Terminal zeigt:

screen shot 2018-09-10 at 10 31 24 am

Zu schlau und süß. Ich mag aber immer noch die kombinierten Charaktere und ich finde 'ᵀ nett.

-100 zum Ändern von Adjoint, da es eines der großartigen Dinge ist, die das Schreiben von Julia-Code so klar machen wie das Schreiben von Mathematik, und außerdem ist die konjugierte Transponierung normalerweise das, was Sie sowieso wollen, sodass es sinnvoll ist, eine abgekürzte Syntax dafür zu haben.

Eine solche Aussage hat eine gewisse Arroganz. Bedenken Sie, dass ein begrenzter Anteil von Entwicklern ausdrücklich adjoint() _nicht_ will, aber $#$ transpose() $#$ _braucht.

Fall und Punkt für uns, wenn wir mit symbolischen Berechnungen zur Modellierung des Standardoperators ' arbeiten, würde beispielsweise die Pseudo-Inverse (A'*A)\(A *b) oder eine quadratische Form v'*A*v fälschlicherweise zurückgeben lange und komplexe Ergebnisse, die nicht reduziert werden können.

Vielleicht ist die Lösung eine Art Compiler-Direktive, die die Bedeutung von ' erklärt.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

m-j-w picture m-j-w  ·  3Kommentare

i-apellaniz picture i-apellaniz  ·  3Kommentare

wilburtownsend picture wilburtownsend  ·  3Kommentare

tkoolen picture tkoolen  ·  3Kommentare

TotalVerb picture TotalVerb  ·  3Kommentare